diff --git a/ChangeLog b/ChangeLog index 836d2fbecc3305a169e3282958e4af7716167fee..6be4b5de854b714cbeae57430bf2b9c8500f2169 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,8 @@ attributes are now generated only for node types that have a rewrite, and the attribute relies on the rewrite condition to decide when the rewrite has reached a final result. - * Removed list rewrites. List rewrites were an undocumented feature that + * List rewrite have been deprecated in legacy rewrite mode and removed in + circular NTA rewrite mode. List rewrites were an undocumented feature that caused problems for attribute correctness by allowing nodes to change position in a list after the AST was constructed. * Removed staged rewrites. Rewrite stages were an undocumented, diff --git a/src/jastadd/ast/Ast.ast b/src/jastadd/ast/Ast.ast index 16c35bb5feefc8131099f16a6691d50c1f47e7ac..15f99974e82c56bedd28042cdf91907cfdf293c2 100644 --- a/src/jastadd/ast/Ast.ast +++ b/src/jastadd/ast/Ast.ast @@ -58,6 +58,8 @@ ClassBodyDecl ::= <Name> <FileName> <StartLine:int> <EndLine:int> <AspectName:St Rewrite ::= <FileName> <StartLine:int> <EndLine:int> <AspectName>; +RewriteList : Rewrite; + /** A component of an AST node type declaration. */ abstract Component; diff --git a/src/jastadd/ast/AttributeProblems.jrag b/src/jastadd/ast/AttributeProblems.jrag index e507ab641a06100bbe73afff632a5e5fe6d57d1c..8964ee3ba245cc42287eeb4a33594fdecaee71e2 100644 --- a/src/jastadd/ast/AttributeProblems.jrag +++ b/src/jastadd/ast/AttributeProblems.jrag @@ -140,6 +140,16 @@ aspect AttributeProblems { .sourceLine(getStartLine()) .buildError(); + /** + * Create a new error object with relevant file name and line number. + */ + syn Problem Rewrite.error(String message) = + Problem.builder() + .message(message) + .sourceFile(getFileName()) + .sourceLine(getStartLine()) + .buildError(); + /** * Create a new error object with relevant file name and line number. */ @@ -160,6 +170,14 @@ aspect AttributeProblems { return warning(String.format(messagefmt, args)); } + /** Create a new warning with the relevant file name and line number. */ + syn Problem Rewrite.warning(String message) = + Problem.builder() + .message(message) + .sourceFile(getFileName()) + .sourceLine(getStartLine()) + .buildWarning(); + /** @return Previous equation for same synthesized attribute. */ syn SynEq SynEq.prevEq() = hostClass().lookupSynEq(signature()); @@ -482,4 +500,16 @@ aspect AttributeProblems { when root != null && root() == null to TypeDecl.attributeProblems() for hostClass(); + + RewriteList contributes + error("list rewrites are not supported when using cnta rewrites") + when config().rewriteCircularNTA() + to TypeDecl.attributeProblems() + for hostClass(); + + RewriteList contributes + warning("list rewrites are deprecated and will be removed in a future version of JastAdd") + when config().legacyRewrite() + to TypeDecl.attributeProblems() + for hostClass(); } diff --git a/src/jastadd/ast/Attributes.jrag b/src/jastadd/ast/Attributes.jrag index 6d374556fee31780a89cbef856a348e8de525706..28306eaff07e2a62c57733b493e6693ea04e11dd 100644 --- a/src/jastadd/ast/Attributes.jrag +++ b/src/jastadd/ast/Attributes.jrag @@ -85,6 +85,45 @@ public aspect Attributes { aspectMap.put(name, comment); } + // Add attributes to AST. + public void Grammar.addRewriteList( + String className, + org.jastadd.jrag.AST.SimpleNode condition, + org.jastadd.jrag.AST.SimpleNode result, + String type, + String fileName, + int startLine, + int endLine, + String parentName, + String childName, + String aspectName) { + + if (!config().rewriteEnabled()) { + error("can not use rewrites while rewrites are disabled (enable with --rewrite=regular)", + fileName, startLine); + return; + } + + TypeDecl c = lookup(className); + if (c != null && c instanceof ASTDecl) { + RewriteList r = new RewriteList(); + r.setFileName(fileName); + r.setStartLine(startLine); + r.setEndLine(endLine); + r.setCondition(condition); + r.setResult(result); + r.setReturnType(type); + r.setParentName(parentName); + r.setChildName(childName); + r.setAspectName(aspectName); + ((ASTDecl)c).addRewrite(r); + } else if (c != null) { + error("can not rewrite to non AST class '" + className + "'", fileName, startLine); + } else { + error("can not rewrite to unknown class '" + className + "'", fileName, startLine); + } + } + public void Grammar.addRewrite( String className, org.jastadd.jrag.AST.SimpleNode condition, @@ -1170,4 +1209,24 @@ public aspect Attributes { public void Rewrite.setReturnType(String type) { returnType = type; } + + public String RewriteList.parentName; + + public String RewriteList.getParentName() { + return parentName; + } + + public void RewriteList.setParentName(String name) { + parentName = name; + } + + public String RewriteList.childName; + + public String RewriteList.getChildName() { + return childName; + } + + public void RewriteList.setChildName(String name) { + childName = name; + } } diff --git a/src/jastadd/ast/JaddCodeGen.jrag b/src/jastadd/ast/JaddCodeGen.jrag index 089facb2803a3daf8a1e2224a09f400d49a0a29d..00f431c8945d031e1bddf86c2032937a15429c87 100644 --- a/src/jastadd/ast/JaddCodeGen.jrag +++ b/src/jastadd/ast/JaddCodeGen.jrag @@ -349,6 +349,12 @@ aspect JaddCodeGen { } } + /** + * @return <code>true</code> if the list$touched field is needed for this + * ASTDecl. + */ + syn boolean ASTDecl.needsListTouched() = config().legacyRewrite(); + /** * Generate implicit aspect declarations for the List type. * @@ -473,7 +479,9 @@ aspect JaddCodeGen { out.println(ind + " * @apilevel internal"); out.println(ind + " */"); out.println(ind + "public boolean " + name() + ".mayHaveRewrite() {"); - if (hasRewrites()) { + if (config().legacyRewrite() && name().equals(config().listType())) { + out.println(ind2 + "return true;"); + } else if (hasRewrites()) { out.println(ind2 + "return true;"); } else { out.println(ind2 + "return false;"); @@ -524,17 +532,6 @@ aspect JaddCodeGen { genIncremental(out); } - syn boolean ASTDecl.rewriteWithNoPhaseCondition() { - for (int i = 0; i < getNumRewrite(); i++) { - if (getRewrite(i).getCondition() == null) - return true; - String condition = Unparser.unparse(getRewrite(i).getCondition()); - if (condition.indexOf("inRewritePhase") == -1 && condition.indexOf("inExactRewritePhase") == -1) - return true; - } - return superClass() instanceof ASTDecl && ((ASTDecl)superClass()).rewriteWithNoPhaseCondition(); - } - public abstract void Component.jaddGen(int index, boolean publicModifier, ASTDecl decl); public void ListComponent.jaddGen(int index, boolean publicModifier, ASTDecl decl) { diff --git a/src/jastadd/ast/Rewrites.jrag b/src/jastadd/ast/Rewrites.jrag index 1a84c52702d97d77d3837365a9babb2818a822e3..9f70384527ef426caba3d1cfe5778f3d0de128fa 100644 --- a/src/jastadd/ast/Rewrites.jrag +++ b/src/jastadd/ast/Rewrites.jrag @@ -37,6 +37,9 @@ aspect Rewrites { tt.expand("ASTDecl.rewriteTo:begin", out); + if (config().legacyRewrite() && name().equals(config().listType())) { + tt.expand("ASTDecl.emitRewrites.touch_list", out); + } for (int i = 0; i < getNumRewrite(); i++) { Rewrite r = getRewrite(i); if (r.genRewrite(out, i)) { @@ -87,6 +90,18 @@ aspect Rewrites { } } + public boolean RewriteList.genRewrite(PrintStream out, int index) { + TemplateContext tt = templateContext(); + tt.expand("Rewrite.declaredat", out); + if (getCondition() != null) { + tt.bind("Condition", " && " + Unparser.unparse(getCondition())); + } else { + tt.bind("Condition", ""); + } + tt.expand("RewriteList.genRewrite", out); + return false; + } + public void Rewrite.genRewriteCondition(PrintStream out, int index) { TemplateContext tt = templateContext(); tt.bind("RewriteIndex", "" + index); @@ -114,6 +129,23 @@ aspect Rewrites { } } + public void RewriteList.genRewritesExtra(PrintStream out, int index) { + String ind = config().indent; + String ind2 = config().ind(2); + if (getResult() instanceof org.jastadd.jrag.AST.ASTBlock) { + templateContext().expand("Rewrite.javaDoc:internal", out); + out.println(ind + "private " + getReturnType() + " rewrite" + + getParentName() + "_" + getChildName() + "() {"); + out.print(Unparser.unparse(getResult())); + out.println(ind + "}"); + } else { + templateContext().expand("Rewrite.javaDoc:internal", out); + out.println(ind + "private " + getReturnType() + " rewrite" + getParentName() + "_" + getChildName() + "() {"); + out.println(ind2 + "return " + Unparser.unparse(getResult()) + ";"); + out.println(ind + "}"); + } + } + syn lazy boolean ASTDecl.hasRewrites() = getNumRewrite() > 0 || (superClass() != null && superClass().hasRewrites()); @@ -169,4 +201,6 @@ aspect Rewrites { "", "", new List<Annotation>()); + + inh TypeDecl Rewrite.hostClass(); } diff --git a/src/javacc/jrag/Jrag.jjt b/src/javacc/jrag/Jrag.jjt index 7b86d44b2482c06c94089e86fd7c112e7d3655d4..9bbcd42428a3c6e644f20c5dca99ed81e48fcbde 100644 --- a/src/javacc/jrag/Jrag.jjt +++ b/src/javacc/jrag/Jrag.jjt @@ -1138,15 +1138,22 @@ void AspectRewrite() : SimpleNode eq; Token first, last; + Token parent = null; + Token child = null; String type; } { "rewrite" {cond = null; first = token;} t = <IDENTIFIER> { className = t.image; } + [<IDENTIFIER> parent = <IDENTIFIER> "." child = <IDENTIFIER> <LPAREN> <RPAREN>] <LBRACE> ( ["when" { first = token; } <LPAREN> cond = Expression() <RPAREN>] "to" type = AspectType() ( (eq = Expression() ";" { last = token; }) | eq = Block() { last = token; } ) { - root.addRewrite(className, cond, eq, type, fileName, first.beginLine, last.endLine, enclosingAspect); + if (parent != null && child != null) { + root.addRewriteList(className, cond, eq, type, fileName, first.beginLine, last.endLine, parent.image, child.image, enclosingAspect); + } else { + root.addRewrite(className, cond, eq, type, fileName, first.beginLine, last.endLine, enclosingAspect); + } cond = null; } )+ diff --git a/src/template/ast/ASTNode.tt b/src/template/ast/ASTNode.tt index ec28d309143f3faafd220f6302adf68b4def2f80..4962dba55901a99b97d03898922b74dc2c45ecd5 100644 --- a/src/template/ast/ASTNode.tt +++ b/src/template/ast/ASTNode.tt @@ -52,7 +52,12 @@ $if(RewriteCircularNTA) } } $else + $if(LegacyRewrite) + // Legacy rewrites with rewrite in list can change child position, so update may be needed. + if (node.childIndex >= 0 && node.childIndex < numChildren && node == children[node.childIndex]) { + $else if (node.childIndex >= 0) { + $endif return node.childIndex; } for (int i = 0; children != null && i < children.length; i++) { diff --git a/src/template/ast/List.tt b/src/template/ast/List.tt index c4adae5ff531c11b65701ae9e440b4bd05312fba..b253a75714e8ed3a4559e8b1fb5ee469f8efe97d 100644 --- a/src/template/ast/List.tt +++ b/src/template/ast/List.tt @@ -30,6 +30,10 @@ List.implicitAspectDecls [[ +$if(#needsListTouched) + private boolean $List.list$$touched = true; +$endif + public $List<T> $List.add(T node) { $SynchBegin $if(DebugMode) @@ -66,12 +70,18 @@ $endif public void $List.insertChild($ASTNode node, int i) { $SynchBegin +$if(#needsListTouched) + list$$touched = true; +$endif super.insertChild(node, i); $SynchEnd } public void $List.addChild(T node) { $SynchBegin +$if(#needsListTouched) + list$$touched = true; +$endif super.addChild(node); $SynchEnd } @@ -79,12 +89,23 @@ $endif /** @apilevel low-level */ public void $List.removeChild(int i) { $SynchBegin +$if(#needsListTouched) + list$$touched = true; +$endif super.removeChild(i); $SynchEnd } public int $List.getNumChild() { $SynchBegin +$if(#needsListTouched) + if (list$$touched) { + for (int i = 0; i < getNumChildNoTransform(); i++) { + getChild(i); + } + list$$touched = false; + } +$endif return getNumChildNoTransform(); $SynchEnd } diff --git a/src/template/ast/Rewrites.tt b/src/template/ast/Rewrites.tt index 1ec844a86d55b53db68101c7cc2fd6e4e76517f2..916e3bccc5d0c55cbf417c91ebead6776e0d28b3 100644 --- a/src/template/ast/Rewrites.tt +++ b/src/template/ast/Rewrites.tt @@ -30,6 +30,22 @@ ASTDecl.rewriteTo:begin [[ public $ASTNode rewriteTo() { ]] +ASTDecl.emitRewrites.touch_list = [[ + if (list$$touched) { + for(int i = 0 ; i < getNumChildNoTransform(); i++) { + getChild(i); + } + list$$touched = false; +$if(RewriteCircularNTA) + if (state().changeInCycle()) { + return this; + } +$else + return this; +$endif + } +]] + ASTDecl.rewriteTo:end:ASTNode [[ $if(LegacyRewrite) if (state().peek() == $StateClass.REWRITE_CHANGE) { @@ -85,6 +101,20 @@ Rewrite.genRewrite:unconditional [[ return rewriteRule$RewriteIndex(); ]] +RewriteList.genRewrite [[ + if (getParent().getParent() instanceof #getParentName && + ((#getParentName)getParent().getParent()).#(getChildName)ListNoTransform() == getParent()$Condition) { + $List list = ($List) getParent(); + int i = list.getIndexOfChild(this); + $List newList = rewrite#(getParentName)_#getChildName(); + // The first child is set by the normal rewrite loop. + for(int j = 1; j < newList.getNumChildNoTransform(); j++) { + list.insertChild(newList.getChildNoTransform(j), ++i); + } + return newList.getChildNoTransform(0); + } +]] + Rewrite.javaDoc:internal [[ /** * #declaredat