-
Notifications
You must be signed in to change notification settings - Fork 34
Add Ghost Dot Syntax #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Ghost Dot Syntax #142
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Ghost; | ||
| import liquidjava.specification.StateRefinement; | ||
|
|
||
| @Ghost("int n") | ||
| public class CorrectDotNotationIncrementOnce { | ||
|
|
||
| // explicit this | ||
| @StateRefinement(to="this.n() == 0") | ||
| public CorrectDotNotationIncrementOnce() {} | ||
|
|
||
| // implicit this | ||
| @StateRefinement(from="n() == 0", to="n() == old(this).n() + 1") | ||
| public void incrementOnce() {} | ||
|
|
||
| public static void main(String[] args) { | ||
| CorrectDotNotationIncrementOnce t = new CorrectDotNotationIncrementOnce(); | ||
| t.incrementOnce(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.StateRefinement; | ||
| import liquidjava.specification.StateSet; | ||
|
|
||
| @StateSet({"green", "amber", "red"}) | ||
| public class CorrectDotNotationTrafficLight { | ||
|
|
||
| @StateRefinement(to="this.green()") | ||
| public CorrectDotNotationTrafficLight() {} | ||
|
|
||
| @StateRefinement(from="this.green()", to="this.amber()") | ||
| public void transitionToAmber() {} | ||
|
|
||
| @StateRefinement(from="red()", to="green()") | ||
| public void transitionToGreen() {} | ||
|
|
||
| @StateRefinement(from="this.amber()", to="red()") | ||
| public void transitionToRed() {} | ||
|
|
||
| public static void main(String[] args) { | ||
| CorrectDotNotationTrafficLight tl = new CorrectDotNotationTrafficLight(); | ||
| tl.transitionToAmber(); | ||
| tl.transitionToRed(); | ||
| tl.transitionToGreen(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // State Refinement Error | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Ghost; | ||
| import liquidjava.specification.StateRefinement; | ||
|
|
||
| @Ghost("int n") | ||
| public class ErrorDotNotationIncrementOnce { | ||
|
|
||
| @StateRefinement(to="this.n() == 0") | ||
| public ErrorDotNotationIncrementOnce() {} | ||
|
|
||
| @StateRefinement(from="n() == 0", to="n() == old(this).n() + 1") | ||
| public void incrementOnce() {} | ||
|
|
||
| public static void main(String[] args) { | ||
| ErrorDotNotationIncrementOnce t = new ErrorDotNotationIncrementOnce(); | ||
| t.incrementOnce(); | ||
| t.incrementOnce(); // error | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Syntax Error | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.Ghost; | ||
| import liquidjava.specification.Refinement; | ||
| import liquidjava.specification.StateRefinement; | ||
|
|
||
| @Ghost("int size") | ||
| public class ErrorDotNotationMultiple { | ||
|
|
||
| @StateRefinement(to="size() == 0") | ||
| public ErrorDotNotationMultiple() {} | ||
|
|
||
| void test() { | ||
| @Refinement("_ == this.not.size()") | ||
| int x = 0; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // State Refinement Error | ||
| package testSuite; | ||
|
|
||
| import liquidjava.specification.StateRefinement; | ||
| import liquidjava.specification.StateSet; | ||
|
|
||
| @StateSet({"green", "amber", "red"}) | ||
| public class ErrorDotNotationTrafficLight { | ||
|
|
||
| @StateRefinement(to="this.green()") | ||
| public ErrorDotNotationTrafficLight() {} | ||
|
|
||
| @StateRefinement(from="this.green()", to="this.amber()") | ||
| public void transitionToAmber() {} | ||
|
|
||
| @StateRefinement(from="red()", to="green()") | ||
| public void transitionToGreen() {} | ||
|
|
||
| @StateRefinement(from="this.amber()", to="red()") | ||
| public void transitionToRed() {} | ||
|
|
||
| public static void main(String[] args) { | ||
| ErrorDotNotationTrafficLight tl = new ErrorDotNotationTrafficLight(); | ||
| tl.transitionToAmber(); | ||
| tl.transitionToGreen(); // error | ||
| tl.transitionToRed(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,11 +19,13 @@ | |
| import liquidjava.rj_language.ast.UnaryExpression; | ||
| import liquidjava.rj_language.ast.Var; | ||
| import liquidjava.utils.Utils; | ||
| import liquidjava.utils.constants.Keys; | ||
|
|
||
| import org.antlr.v4.runtime.tree.ParseTree; | ||
| import org.apache.commons.lang3.NotImplementedException; | ||
| import rj.grammar.RJParser.AliasCallContext; | ||
| import rj.grammar.RJParser.ArgsContext; | ||
| import rj.grammar.RJParser.DotCallContext; | ||
| import rj.grammar.RJParser.ExpBoolContext; | ||
| import rj.grammar.RJParser.ExpContext; | ||
| import rj.grammar.RJParser.ExpGroupContext; | ||
|
|
@@ -51,9 +53,7 @@ | |
| import rj.grammar.RJParser.ProgContext; | ||
| import rj.grammar.RJParser.StartContext; | ||
| import rj.grammar.RJParser.StartPredContext; | ||
| import rj.grammar.RJParser.TargetInvocationContext; | ||
| import rj.grammar.RJParser.VarContext; | ||
| import spoon.reflect.cu.SourcePosition; | ||
| import liquidjava.diagnostics.errors.ArgumentMismatchError; | ||
|
|
||
| /** | ||
|
|
@@ -82,6 +82,8 @@ else if (rc instanceof OperandContext) | |
| return operandCreate(rc); | ||
| else if (rc instanceof LiteralExpressionContext) | ||
| return literalExpressionCreate(rc); | ||
| else if (rc instanceof DotCallContext) | ||
| return dotCallCreate((DotCallContext) rc); | ||
| else if (rc instanceof FunctionCallContext) | ||
| return functionCallCreate((FunctionCallContext) rc); | ||
| else if (rc instanceof LiteralContext) | ||
|
|
@@ -156,9 +158,7 @@ else if (rc instanceof LitContext) | |
| return create(((LitContext) rc).literal()); | ||
| else if (rc instanceof VarContext) { | ||
| return new Var(((VarContext) rc).ID().getText()); | ||
| } else if (rc instanceof TargetInvocationContext) { | ||
| // TODO Finish Invocation with Target (a.len()) | ||
| return null; | ||
|
|
||
| } else { | ||
| return create(((InvocationContext) rc).functionCall()); | ||
| } | ||
|
|
@@ -171,15 +171,57 @@ private Expression functionCallCreate(FunctionCallContext rc) throws LJError { | |
| String name = Utils.qualifyName(prefix, ref); | ||
| List<Expression> args = getArgs(gc.args()); | ||
| if (args.isEmpty()) | ||
| throw new ArgumentMismatchError("Ghost call cannot have empty arguments"); | ||
| args.add(new Var(Keys.THIS)); // implicit this: size() => this.size() | ||
|
|
||
| return new FunctionInvocation(name, args); | ||
| } else { | ||
| } else if (rc.aliasCall() != null) { | ||
| AliasCallContext gc = rc.aliasCall(); | ||
| String ref = gc.ID_UPPER().getText(); | ||
| List<Expression> args = getArgs(gc.args()); | ||
| if (args.isEmpty()) | ||
| throw new ArgumentMismatchError("Alias call cannot have empty arguments"); | ||
|
|
||
| return new AliasInvocation(ref, args); | ||
| } else { | ||
| return dotCallCreate(rc.dotCall()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Handles both cases of dot calls: this.func(args) and targetFunc(this).func(args) Converts them to func(this, | ||
| * args) and func(targetFunc(this), args) respectively | ||
| */ | ||
| private Expression dotCallCreate(DotCallContext rc) throws LJError { | ||
| if (rc.OBJECT_TYPE() != null) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are a couple more cases here right?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep! |
||
| String text = rc.OBJECT_TYPE().getText(); | ||
|
|
||
| // check if there are multiple fields (e.g. this.a.b) | ||
| if (text.chars().filter(ch -> ch == '.').count() > 1) | ||
| throw new SyntaxError("Multiple dot notation is not allowed", text); | ||
|
|
||
| // this.func(args) => func(this, args) | ||
| int dot = text.indexOf('.'); | ||
| String target = text.substring(0, dot); | ||
| String simpleName = text.substring(dot + 1); | ||
| String name = Utils.qualifyName(prefix, simpleName); | ||
| List<Expression> args = getArgs(rc.args(0)); | ||
| if (!args.isEmpty() && args.get(0)instanceof Var v && v.getName().equals(Keys.THIS) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. picky space betwee
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also don't like it, blame the formatter 😂 |
||
| && target.equals(Keys.THIS)) | ||
| throw new SyntaxError("Cannot use both dot notation and explicit 'this' argument. Use either 'this." | ||
| + simpleName + "()' or '" + simpleName + "(this)'", text); | ||
|
|
||
| args.add(0, new Var(target)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add comment here saying this transforms the call |
||
| return new FunctionInvocation(name, args); | ||
|
|
||
| } else { | ||
| // targetFunc(this).func(args) => func(targetFunc(this), args) | ||
| String targetFunc = rc.ID(0).getText(); | ||
| String func = rc.ID(1).getText(); | ||
| String name = Utils.qualifyName(prefix, func); | ||
| List<Expression> targetArgs = getArgs(rc.args(0)); | ||
| List<Expression> funcArgs = getArgs(rc.args(1)); | ||
| funcArgs.add(0, new FunctionInvocation(targetFunc, targetArgs)); | ||
| return new FunctionInvocation(name, funcArgs); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also add comment here saying what the transformation is |
||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We def need documentation here explaining what the function does
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I add documentation for all other functions in this file then?