Search code examples
rascal

UnsupportedOperationException when I try translate a expression


I defined a syntax for a language of expressions, actually it's more complex, but I simplified to put here, and I defined some functions to translate this expressions for Java (I'm not using the Java syntax, I just translate to strings). I defined some constants in the syntax (I don't know if it's the correct name for this) that calls "MAXINT" and "MININT", and some functions that calls translateExp to translate each expression that I defined in the syntax to a string. The most expressions that I try translate works, but when "MAXINT" or "MININT" appears in the expression don't work and throws UnsupportedOperationException and I don't know why, for example "MAXINT - 1". Somebody knows why and can help me? Another problem that throws UnsupportedOperationException too is when I try translate some expression that have more than one plus (+) or minus (-), like "1+1+1", again, somebody knows why?

My module with the syntax and the functions:

module ExpSyntax

import String;
import ParseTree;

layout Whitespaces = [\t\n\ \r\f]*;

lexical Ident = [a-z A-Z 0-9 _] !<< [a-z A-Z][a-z A-Z 0-9 _]* !>> [a-z A-Z 0-9 _] \ Keywords;
lexical Integer_literal = [0-9]+;

keyword Keywords = "MAXINT" | "MININT";

start syntax Expression
= Expressions_primary
| Expressions_arithmetical;

syntax Expressions_primary
= Data: Ident+ id
| bracket Expr_bracketed: "(" Expression e ")"
;

syntax Expressions_arithmetical
= Integer_lit
| left Addition: Expression e1 "+" Expression e2
| left Difference: Expression e1 "-" Expression e2 
;

syntax Integer_lit
= Integer_literal il
| MAX_INT: "MAXINT"
| MIN_INT: "MININT"
;

public str translate(str txt) = translateExp(parse(#Expression, txt));

public str translateExp((Expression) `<Integer_literal i>`) = "<i>";
public str translateExp((Expression) `MAXINT`) = "java.lang.Integer.MAX_VALUE";
public str translateExp((Expression) `MININT`) = "java.lang.Integer.MIN_VALUE";
public str translateExp((Expression) `<Expression e1>+<Expression e2>`) = "<translateExp(e1)> + <translateExp(e2)>";
public str translateExp((Expression) `<Expression e1>-<Expression e2>`) = "<translateExp(e1)> - <translateExp(e2)>";
public str translateExp((Expression) `<Ident id>`) = "<id>";
public str translateExp((Expression) `(<Expression e>)`) = "(<translateExp(e)>)";

This is what happens:

rascal>import ExpSyntax;

ok

rascal>translate("(test + 1) - test2");

str: "(test + 1) - test2"

rascal>translate("MAXINT - 1");

java.lang.UnsupportedOperationException(internal error) at $shell$(|main://$shell$|)
java.lang.UnsupportedOperationException
    at org.rascalmpl.ast.Expression.getKeywordArguments(Expression.java:214)
    at org.rascalmpl.interpreter.matching.NodePattern.<init>(NodePattern.java:84)
    at org.rascalmpl.semantics.dynamic.Tree$Amb.buildMatcher(Tree.java:351)
    at org.rascalmpl.ast.AbstractAST.getMatcher(AbstractAST.java:173)
    at org.rascalmpl.interpreter.result.RascalFunction.prepareFormals(RascalFunction.java:503)
    at org.rascalmpl.interpreter.result.RascalFunction.call(RascalFunction.java:365)
    at org.rascalmpl.interpreter.result.OverloadedFunction.callWith(OverloadedFunction.java:327)
    at org.rascalmpl.interpreter.result.OverloadedFunction.call(OverloadedFunction.java:305)
    at org.rascalmpl.semantics.dynamic.Expression$CallOrTree.interpret(Expression.java:486)
    at org.rascalmpl.semantics.dynamic.Statement$Expression.interpret(Statement.java:355)
    at org.rascalmpl.semantics.dynamic.Statement$Return.interpret(Statement.java:773)
    at org.rascalmpl.interpreter.result.RascalFunction.runBody(RascalFunction.java:467)
    at org.rascalmpl.interpreter.result.RascalFunction.call(RascalFunction.java:413)
    at org.rascalmpl.interpreter.result.OverloadedFunction.callWith(OverloadedFunction.java:327)
    at org.rascalmpl.interpreter.result.OverloadedFunction.call(OverloadedFunction.java:305)
    at org.rascalmpl.semantics.dynamic.Expression$CallOrTree.interpret(Expression.java:486)
    at org.rascalmpl.semantics.dynamic.Statement$Expression.interpret(Statement.java:355)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:936)
    at org.rascalmpl.semantics.dynamic.Command$Statement.interpret(Command.java:115)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:1147)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:1107)
    at org.rascalmpl.eclipse.console.RascalScriptInterpreter.execCommand(RascalScriptInterpreter.java:446)
    at org.rascalmpl.eclipse.console.RascalScriptInterpreter.run(RascalScriptInterpreter.java:239)
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:53)

rascal>translate("1+1+1");

java.lang.UnsupportedOperationException(internal error) at $shell$(|main://$shell$|)
java.lang.UnsupportedOperationException
    at org.rascalmpl.ast.Expression.getKeywordArguments(Expression.java:214)
    at org.rascalmpl.interpreter.matching.NodePattern.<init>(NodePattern.java:84)
    at org.rascalmpl.semantics.dynamic.Tree$Amb.buildMatcher(Tree.java:351)
    at org.rascalmpl.ast.AbstractAST.getMatcher(AbstractAST.java:173)
    at org.rascalmpl.interpreter.result.RascalFunction.prepareFormals(RascalFunction.java:503)
    at org.rascalmpl.interpreter.result.RascalFunction.call(RascalFunction.java:365)
    at org.rascalmpl.interpreter.result.OverloadedFunction.callWith(OverloadedFunction.java:327)
    at org.rascalmpl.interpreter.result.OverloadedFunction.call(OverloadedFunction.java:305)
    at org.rascalmpl.semantics.dynamic.Expression$CallOrTree.interpret(Expression.java:486)
    at org.rascalmpl.semantics.dynamic.Statement$Expression.interpret(Statement.java:355)
    at org.rascalmpl.semantics.dynamic.Statement$Return.interpret(Statement.java:773)
    at org.rascalmpl.interpreter.result.RascalFunction.runBody(RascalFunction.java:467)
    at org.rascalmpl.interpreter.result.RascalFunction.call(RascalFunction.java:413)
    at org.rascalmpl.interpreter.result.OverloadedFunction.callWith(OverloadedFunction.java:327)
    at org.rascalmpl.interpreter.result.OverloadedFunction.call(OverloadedFunction.java:305)
    at org.rascalmpl.semantics.dynamic.Expression$CallOrTree.interpret(Expression.java:486)
    at org.rascalmpl.semantics.dynamic.Statement$Expression.interpret(Statement.java:355)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:936)
    at org.rascalmpl.semantics.dynamic.Command$Statement.interpret(Command.java:115)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:1147)
    at org.rascalmpl.interpreter.Evaluator.eval(Evaluator.java:1107)
    at org.rascalmpl.eclipse.console.RascalScriptInterpreter.execCommand(RascalScriptInterpreter.java:446)
    at org.rascalmpl.eclipse.console.RascalScriptInterpreter.run(RascalScriptInterpreter.java:239)
    at org.eclipse.core.internal.jobs.Worker.run(Worker.java:53)

Solution

  • The error message is a bug, since it should report something more clear, but it appears there is still an ambiguity in your syntax definition. The run-time is trying to build a pattern matcher for Tree$Amb (org.rascalmpl.semantics.dynamic.Tree$Amb.buildMatcher) which we did not implement and will not implement.

    From looking at the definition (I did not try it), it seems that the cause is this rule:

    lexical Ident = [a-z A-Z 0-9 _] !<< [a-z A-Z][a-z A-Z 0-9 _]* !>> [a-z A-Z 0-9 _] \ Keywords;
    

    Because !>> and \ bind stronger than juxtapositioning, the \ keyword reservation is applied only to the tail of an Ident and not the whole. Please add brackets (a sequence operator) to disambiguate:

    lexical Ident = [a-z A-Z 0-9 _] !<< ([a-z A-Z][a-z A-Z 0-9 _]*) !>> [a-z A-Z 0-9 _] \ Keywords; 
    

    This should get you a step further.

    Then your expression grammar is still ambiguous and can be simplified to this:

    start syntax Expression
      = Data: Ident+ id
      | Integer: Integer_lit
      | bracket bracketed: "(" Expression e ")"
      > left
          ( Addition: Expression e1 "+" Expression e2
          | Difference: Expression e1 "-" Expression e2 
          )
      ;
    
    syntax Integer_lit
      = Integer_literal il
      | MAX_INT: "MAXINT"
      | MIN_INT: "MININT"
      ;
    

    Short explanation: left only works on directly recursive non-terminals. Since you defined Expressions_arithmatical in terms of Expression there was no direct recursion. A next will support indirect recursion, but for this grammar this would be unnecessary.

    Also, I added that + and - are mutually left recursive by putting them in a group, otherwise 1+1-1 would have remained ambiguous.