You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, base on the CalculatorParser3 example I create a parser to evaluate expressions like
key=='abc'
It works fin in java. However, when I converted the code to kotlin, then I see an error:
Exception in thread "main" java.lang.RuntimeException: Error creating extended parser class: An ACTION or Var initializer in rule method 'Identifier' contains illegal writes to a local variable or parameter
at org.parboiled.Parboiled.createParser(Parboiled.java:58)
at pl.tfij.copy.CalculatorParser3copy$Companion.main(CalculatorParser3copy.kt:144)
at pl.tfij.copy.CalculatorParser3copy.main(CalculatorParser3copy.kt)
Caused by: org.parboiled.errors.GrammarException: An ACTION or Var initializer in rule method 'Identifier' contains illegal writes to a local variable or parameter
at org.parboiled.support.Checks.ensure(Checks.java:37)
at org.parboiled.transform.InstructionGroupCreator.verify(InstructionGroupCreator.java:135)
at org.parboiled.transform.InstructionGroupCreator.process(InstructionGroupCreator.java:58)
at org.parboiled.transform.ParserTransformer.runMethodTransformers(ParserTransformer.java:62)
at org.parboiled.transform.ParserTransformer.extendParserClass(ParserTransformer.java:45)
at org.parboiled.transform.ParserTransformer.transformParser(ParserTransformer.java:39)
at org.parboiled.Parboiled.createParser(Parboiled.java:54)
... 2 more
Any idea why?
parboiled-java version : 1.4.1
My java code:
package pl.tfij;
import org.parboiled.BaseParser;
import org.parboiled.Parboiled;
import org.parboiled.Rule;
import org.parboiled.annotations.BuildParseTree;
import org.parboiled.parserunners.RecoveringParseRunner;
import org.parboiled.support.ParsingResult;
import org.parboiled.trees.ImmutableBinaryTreeNode;
import static org.parboiled.errors.ErrorUtils.printParseErrors;
import static pl.tfij.CalculatorParser3.CalcNode;
@BuildParseTree
public class CalculatorParser3 extends BaseParser<CalcNode> {
public Rule InputLine() {
return Sequence(Expression(), EOI);
}
Rule Expression() {
return EqualityExpression();
}
Rule EqualityExpression() {
return FirstOf(
Sequence(
Identifier(),
"== ",
StringLiteral(),
push(new CalcNode("==", pop(1), pop()))
),
Sequence(
Identifier(),
"!= ",
StringLiteral(),
push(new CalcNode("!=", pop(1), pop()))
)
);
}
Rule Identifier() {
return Sequence(
Sequence(
OneOrMore(
Letter(),
ZeroOrMore(FirstOf(Letter(), Digit()))
),
WhiteSpace()
),
push(new CalcNode("Identifier", match().trim()))
);
}
Rule Letter() {
return FirstOf(CharRange('a', 'z'), CharRange('A', 'Z'), '_', '$');
}
Rule StringLiteral() {
return Sequence("'", StringContent(), "'", WhiteSpace());
}
Rule StringContent() {
return Sequence(
ZeroOrMore(Sequence(TestNot(AnyOf("\r\n'")), FirstOf(EscapedChar(), ANY))),
push(new CalcNode("String", escapeString(matchOrDefault(""))))
);
}
protected String escapeString(String string) {
StringBuilder result = new StringBuilder();
var i = 0;
while (i < string.length()) {
if (string.charAt(i) == '\\') {
i++;
}
result.append(string.charAt(i));
i++;
}
return result.toString();
}
Rule EscapedChar() {
return Sequence("\\", ANY);
}
Rule Digit() {
return CharRange('0', '9');
}
Rule WhiteSpace() {
return ZeroOrMore(AnyOf(" \t\f"));
}
// we redefine the rule creation for string literals to automatically match trailing whitespace if the string
// literal ends with a space character, this way we don't have to insert extra whitespace() rules after each
// character or string literal
@Override
protected Rule fromStringLiteral(String string) {
return string.endsWith(" ") ?
Sequence(String(string.substring(0, string.length() - 1)), WhiteSpace()) :
String(string);
}
//****************************************************************
/**
* The AST node for the calculators. The type of the node is carried as a Character that can either contain
* an operator char or be null. In the latter case the AST node is a leaf directly containing a value.
*/
public static class CalcNode extends ImmutableBinaryTreeNode<CalcNode> {
private Object value;
private String type;
public CalcNode(String type, Object value) {
super(null, null);
this.type = type;
this.value = value;
}
public CalcNode(String type, CalcNode left, CalcNode right) {
super(left, right);
this.type = type;
}
public Object getValue() {
switch (type) {
case "==":
return left().getValue().equals(right().getValue());
case "!=":
return !left().getValue().equals(right().getValue());
case "Identifier":
return "abc"; //TODO
case "String":
return value;
default:
throw new IllegalStateException(type);
}
}
@Override
public String toString() {
return (type == null ? "Value " + value : "Operator '" + type + '\'') + " | " + getValue();
}
}
//**************** MAIN ****************
public static void main(String[] args) {
CalculatorParser3 parser = Parboiled.createParser(CalculatorParser3.class);
String input = "key=='abc'";
ParsingResult<CalcNode> result = new RecoveringParseRunner<CalcNode>(parser.InputLine()).run(input);
if (result.hasErrors()) {
System.out.println("\nParse Errors:\n" + printParseErrors(result));
}
CalcNode value = result.resultValue;
System.out.println("value: " + value.getValue());
}
}
and Kotlin version:
package pl.tfij
import org.parboiled.BaseParser
import org.parboiled.Parboiled
import org.parboiled.Rule
import org.parboiled.annotations.BuildParseTree
import org.parboiled.errors.ErrorUtils
import org.parboiled.parserunners.RecoveringParseRunner
import org.parboiled.trees.ImmutableBinaryTreeNode
import pl.tfij.copy.CalculatorParser3copy
@BuildParseTree
open class CalculatorParser3copy : BaseParser<CalculatorParser3copy.CalcNode?>() {
open fun InputLine(): Rule {
return Sequence(Expression(), EOI)
}
open fun Expression(): Rule {
return EqualityExpression()
}
open fun EqualityExpression(): Rule {
return FirstOf(
Sequence(
Identifier(),
"== ",
StringLiteral(),
push(CalcNode("==", pop(1), pop()))
),
Sequence(
Identifier(),
"!= ",
StringLiteral(),
push(CalcNode("!=", pop(1), pop()))
)
)
}
open fun Identifier(): Rule {
return Sequence(
Sequence(
OneOrMore(
Letter(),
ZeroOrMore(FirstOf(Letter(), Digit()))
),
WhiteSpace()
),
push(CalcNode("Identifier", match().trim()))
)
}
open fun Letter(): Rule {
return FirstOf(CharRange('a', 'z'), CharRange('A', 'Z'), '_', '$')
}
open fun StringLiteral(): Rule {
return Sequence("'", StringContent(), "'", WhiteSpace())
}
open fun StringContent(): Rule {
return Sequence(
ZeroOrMore(Sequence(TestNot(AnyOf("\r\n'")), FirstOf(EscapedChar(), ANY))),
push(CalcNode("String", escapeString(matchOrDefault(""))))
)
}
protected fun escapeString(string: String): String {
val result = StringBuilder()
var i = 0
while (i < string.length) {
if (string[i] == '\\') {
i++
}
result.append(string[i])
i++
}
return result.toString()
}
open fun EscapedChar(): Rule {
return Sequence("\\", ANY)
}
open fun Digit(): Rule {
return CharRange('0', '9')
}
open fun WhiteSpace(): Rule {
return ZeroOrMore(AnyOf(" \t\u000c"))
}
// we redefine the rule creation for string literals to automatically match trailing whitespace if the string
// literal ends with a space character, this way we don't have to insert extra whitespace() rules after each
// character or string literal
override fun fromStringLiteral(string: String): Rule {
return if (string.endsWith(" ")) Sequence(
String(string.substring(0, string.length - 1)),
WhiteSpace()
) else String(string)
}
//****************************************************************
/**
* The AST node for the calculators. The type of the node is carried as a Character that can either contain
* an operator char or be null. In the latter case the AST node is a leaf directly containing a value.
*/
class CalcNode : ImmutableBinaryTreeNode<CalcNode?> {
private var value: Any? = null
private var type: String?
constructor(type: String?, value: Any?) : super(null, null) {
this.type = type
this.value = value
}
constructor(type: String?, left: CalcNode?, right: CalcNode?) : super(left, right) {
this.type = type
}
fun getValue(): Any? {
return when (type) {
"==" -> left()!!.getValue() == right()!!.getValue()
"!=" -> left()!!.getValue() != right()!!.getValue()
"Identifier" -> "abc" //TODO
"String" -> value
else -> throw IllegalStateException(type)
}
}
override fun toString(): String {
return (if (type == null) "Value $value" else "Operator '$type'") + " | " + getValue()
}
}
companion object {
//**************** MAIN ****************
@JvmStatic
fun main(args: Array<String>) {
val parser = Parboiled.createParser(
CalculatorParser3copy::class.java
)
val input = "key=='abc'"
val result = RecoveringParseRunner<CalcNode>(parser.InputLine()).run(input)
if (result.hasErrors()) {
println(
"""
Parse Errors:
${ErrorUtils.printParseErrors(result)}
""".trimIndent()
)
}
val value = result.resultValue
println("value: " + value.getValue())
}
}
}
The text was updated successfully, but these errors were encountered:
Hi, base on the CalculatorParser3 example I create a parser to evaluate expressions like
It works fin in java. However, when I converted the code to kotlin, then I see an error:
Any idea why?
parboiled-java version : 1.4.1
My java code:
and Kotlin version:
The text was updated successfully, but these errors were encountered: