diff --git a/pom.xml b/pom.xml index ea20f2fd105..2cf26a1480f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,12 @@ org.rascalmpl rascal - 0.43.0-RC16-SNAPSHOT + 0.43.0-RC17-BOOT-PREVIEW-SYNTAX-ROLES-SNAPSHOT jar scm:git:ssh://git@github.com/usethesource/rascal.git - HEAD + v0.43.0-RC16-BOOT-PREVIEW-SYNTAX-ROLES diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxDeclarationTCTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxDeclarationTCTests.rsc index aa7ac31be9d..3a005704703 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxDeclarationTCTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxDeclarationTCTests.rsc @@ -566,78 +566,6 @@ test bool Issue442() = checkModuleOK(" // https://github.com/cwi-swat/rascal/issues/465 -test bool Issue465a(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return checkModuleOK(" - module Issue465a - import MMM; - data Exp = con(int n); - "); -} - -test bool Issue465b(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return checkModuleOK(" - module Issue465b - import MMM; - data Exp = con(int n); - void main() { c = con(5); } - "); -} - -test bool Issue465c(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return checkModuleOK(" - module Issue465c - import MMM; - data Exp = con(int n); - void main() { data[Exp] c = con(5); } - "); -} - -test bool Issue465c2(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return unexpectedTypeInModule(" - module Issue465c - import MMM; - data Exp = con(int n); - void main() { Exp c = con(5); } // should trigger an error - "); -} - - -test bool Issue465d(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return checkModuleOK(" - module Issue465d - import MMM; - data Exp = con(int n); - void main() { MMM::Exp c = [MMM::Exp] \"3\"; } // should work because data Exp is not in MMM - "); -} - -test bool Issue465d2(){ - writeModule("module MMM - lexical IntegerLiteral = [0-9]+; - start syntax Exp = con: IntegerLiteral;"); - return checkModuleOK(" - module Issue465d - import MMM; - data Exp = con(int n); - void main() { syntax[Exp] c = [Exp] \"3\"; } - "); -} - // https://github.com/usethesource/rascal/issues/1353 test bool Issue1353() { writeModule("module MC @@ -676,4 +604,4 @@ test bool srcFromExpressionFieldsOK(){ e.rhs.src = |unknown:///|; } "); -} \ No newline at end of file +} diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxRoleTCTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxRoleTCTests.rsc deleted file mode 100644 index 85d5a2842eb..00000000000 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/SyntaxRoleTCTests.rsc +++ /dev/null @@ -1,129 +0,0 @@ -@license{ -Copyright (c) 2018-2025, NWO-I CWI, Swat.engineering and Paul Klint -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -} -module lang::rascalcore::check::tests::SyntaxRoleTCTests - -import lang::rascalcore::check::tests::StaticTestingUtils; - -private str exampleGrammar - = "lexical Z = [a-z]+; - 'syntax A = \"a\" | \"b\" \"b\"; - 'layout L = [\\ ]*; - 'syntax E = \"e\"; - 'data A = a(); - 'data E = e(); - '"; - -test bool MiniTest() = checkModuleOK( - "module MiniTest - ' data E = e(); - ' data[E] exData = e(); - '" -); - -test bool MiniTest2() = unexpectedTypeInModule( - "module MiniTest2 - ' syntax E = \"foo\"; - ' data E = e(); - ' E exData = e(); // E is not uniquely resolvable - '" -); - -test bool MiniTest3() = unexpectedTypeInModule( - "module MiniTest2 - ' syntax E = \"foo\"; - ' data E = e(); - ' E exData = (E) `foo`; // E is not uniquely resolvable - '" -); - - -test bool OverloadingTest1() = checkModuleOK( - "module OverloadingTest1 - ' syntax E = \"foo\"; - ' data E = e(); - ' data[E] ex1 = e(); // e() should have type data[E] not syntax[E] - "); - -test bool OverloadingTest2() = checkModuleOK( - "module OverloadingTest2 - ' syntax E = \"e\"; - ' data E = e(); - ' syntax[E] ex2 = (E) `e`; - "); - -test bool OverloadingTest3() = unexpectedTypeInModule( - "module OverloadingTest3 - ' syntax E = \"e\"; - ' data E = e(); - ' syntax[E] ex2 = e(); // this is not a compatible assignment - "); - -test bool OverloadingTest4() = unexpectedTypeInModule( - "module OverloadingTest4 - ' syntax E = \"e\"; - ' data E = e(); - ' data[E] ex2 = (E) `e`; // this is not a compatible assignment - "); - -test bool TestSimpleModificationOfRoles() = checkModuleOK( - "module TestSimpleModificationOfRoles - ' - ' data[E] exData = e(); - ' syntax[E] exSyntax = (E) `e`; - " -); - -test bool NoGenericUseOfModifiers() = checkModuleOK( - "module NoGenericUseOfModifiers - ' syntax E = \"e\"; - ' data E = e(); - ' data[E] implode(type[data[E]] grammar, syntax[E] tree) = e(); - ' data[E] example = implode(#data[E], [E] \"e\"); - "); - -test bool GenericUseOfModifiers() = checkModuleOK( - "module GenericUseOfModifiers - ' syntax E = \"e\"; - ' data E = e(); - ' data[E] tst = e(); - ' data[&T] id(data[&T] a, syntax[&T] b) = a; - ' data[E] example = id(e(), (E) `e`); - "); - -test bool WrongGenericUseOfModifiers1() = unexpectedTypeInModule( - "module GenericUseOfModifiers - ' &T id(data[&T] a, syntax[&T] _b) = a; // return type is unmodified - "); - -test bool WrongGenericUseOfModifiers2() = unexpectedTypeInModule( - "module GenericUseOfModifiers - ' syntax E = \"e\"; - ' data E = e(); - ' &T id(data[&T] a, syntax[&T] _b) = a; // return type is unmodified - ' data[E] example = id(e(), (E) `e`); - "); - diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/util/ExplodeTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/util/ExplodeTests.rsc deleted file mode 100644 index 827fd31a9a9..00000000000 --- a/src/org/rascalmpl/library/lang/rascal/tests/library/util/ExplodeTests.rsc +++ /dev/null @@ -1,84 +0,0 @@ -module lang::rascal::tests::library::util::ExplodeTests - -import util::Explode; -import analysis::m3::AST; -import IO; -import lang::json::IO; -import util::IDEServices; - -data Record(loc src=|unknown:///|) = record(Name name, Age age); -data Name(loc src=|unknown:///|) = name(str x); -data Age(loc src=|unknown:///|) = age(str a); -data Rolodex(loc src=|unknown:///|) = rolodex(list[Record] records); - -Rolodex exampleAsTerm = rolodex([ - record(name("Aap"), age("1")), - record(name("Noot"), age("2")), - record(name("Mies"), age("3")) -]); - -public loc exampleFile = |memory://ExplodeTests/example.json|; - -Rolodex setupExample() { - writeJSON(exampleFile, exampleAsTerm, indent=4); - Rolodex result = readJSON(#Rolodex, exampleFile, trackOrigins=true); - assert astNodeSpecification(result); - return result; -} - -test bool explodeDeepMatch() { - Rolodex ast = setupExample(); - syntax[Rolodex] tree = explode(ast); - return (0 | it + 1 | /syntax[Record] _ := tree) == (0 | it + 1 | /data[Record] _ := ast); -} - -test bool explodeVisit() { - Rolodex ast = setupExample(); - int astRecordCount = 0; - visit (ast) { - case data[Record] _ : astRecordCount += 1; - } - - syntax[Rolodex] tree = explode(ast); - int treeRecordCount = 0; - visit (tree) { - case syntax[Record] _ : treeRecordCount += 1; - } - - return astRecordCount == treeRecordCount; -} - -@synopsis{prints to strings next to each other in html for debugging purposes} -bool showDiff(str a, str b) { - showInteractiveContent(html("\\\ - '\ - ' - '\\\ - '\ - ' - '\\\\")); - return a == b; -} - -@synopsis{Makes sure each sub-ast aligns with each sub-tree and with each sub-string} -test bool explodeYieldContract() { - Rolodex ast = setupExample(); - syntax[Rolodex] tree = explode(ast); - - // get all the AST nodes - asts = [a | /node a := ast]; - - // get all the Tree nodes can coincide with abstract data-type nodes - trees = [x | /Tree x := tree, syntax[&T] _ := x]; - - together = zip2(asts, trees); - - // abstract nodes and concrete nodes align per src field - assert (true | it && a.src == b.src | <- together); - - // the concrete nodes yield equals to the substring of the file that is indicated by the src field - assert (true | it && readFile(b.src) == "" | b <- trees); - - return size(asts) == size(trees); -} - diff --git a/src/org/rascalmpl/library/lang/sexp/AST.rsc b/src/org/rascalmpl/library/lang/sexp/AST.rsc index 828ded3ef5d..e742e6ce3f0 100644 --- a/src/org/rascalmpl/library/lang/sexp/AST.rsc +++ b/src/org/rascalmpl/library/lang/sexp/AST.rsc @@ -1,10 +1,7 @@ @synopsis{AST model for S-Expressions.} @contributor{Tijs van der Storm - storm@cwi.nl (CWI)} -module lang::sexp::SExp - -import lang::sexp::\syntax::SExp; -import ParseTree; +module lang::sexp::AST data SExp = string(String \str) @@ -27,6 +24,3 @@ data SimpleString data Raw = raw(int size, str bytes) ; - -public SExp parseSExp(str src, loc l) - = implode(#SExp, parse(#lang::sexp::\syntax::SExp::SExp, src, l)); diff --git a/src/org/rascalmpl/library/lang/sexp/Parse.rsc b/src/org/rascalmpl/library/lang/sexp/Parse.rsc index 0ceb0674314..97e56ca55fe 100644 --- a/src/org/rascalmpl/library/lang/sexp/Parse.rsc +++ b/src/org/rascalmpl/library/lang/sexp/Parse.rsc @@ -1,8 +1,20 @@ +@license{ +Copyright (c) 2025, NWO-I Centrum Wiskunde & Informatica (CWI) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +} module lang::sexp::Parse import lang::sexp::AST; import lang::sexp::Syntax; import ParseTree; -public data[SExp] parseSExp(str src, loc l) +data[SExp] parseSExp(str src, loc l) = implode(#data[SExp], parse(#syntax[SExp], src, l)); diff --git a/src/org/rascalmpl/library/lang/sexp/Syntax.rsc b/src/org/rascalmpl/library/lang/sexp/Syntax.rsc index a437139e1b0..91e38f2b885 100644 --- a/src/org/rascalmpl/library/lang/sexp/Syntax.rsc +++ b/src/org/rascalmpl/library/lang/sexp/Syntax.rsc @@ -2,7 +2,7 @@ @synopsis{Syntax definition for S-Expressions, based on http://people.csail.mit.edu/rivest/Sexp.txt} @contributor{Tijs van der Storm - storm@cwi.nl (CWI)} -module lang::sexp::\syntax::SExp +module lang::sexp::Syntax import String; import IO; diff --git a/src/org/rascalmpl/library/util/Explode.rsc b/src/org/rascalmpl/library/util/Explode.rsc deleted file mode 100644 index 391b89c77b4..00000000000 --- a/src/org/rascalmpl/library/util/Explode.rsc +++ /dev/null @@ -1,221 +0,0 @@ -@licence{ -Copyright (c) 2023, NWO-I Centrum Wiskunde & Informatica (CWI) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -} -@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl} -@synopsis{Explode lifts abstract syntax trees to parse trees} -@description{ -The explode function is used to lift abstract syntax trees to concrete syntax trees. -The important difference is that all whitespace and comments are retrieved from the original -files and placed inside of the parse tree. -} -@benefits{ -* when analyzing a parse tree all information including layout and comments is preserved. -* when transforming parse trees, the original layout and comments are transformed along; moreover things that remain the same, remain the same. -* unparsing a parse tree returns the exact original input file -* using the `explode` function we can reuse an external parser that produces ASTs, and still have Hi-fidelity source code analyses and transformations. -* the explode function is "type name preserving", such that a data-type named "Expression" becomes a concrete syntax tree type named "Expression" -} -@pitfalls{ -* exploding only works correctly if the AST implements the AST specification from analysis::m3::AST. -* exploding takes about as much time as parsing a file -} -module util::Explode - -extend ParseTree; -import IO; -import Node; -import List; -import Location; -import String; - -@synopsis{Turn an AST into a ParseTree, while preserving the name of the type, and the entire input string} -syntax[&T] explode(data[&T] ast) { - assert readFile(ast.src.top) == readFile(ast.src); - Production cons = getConstructor(ast); - - if (syntax[&T] r := explode(ast, unlabel(cons.def), readFile(ast.src.top), ast.src.top, ast.src.offset, ast.src.length)) { - return r[src=ast.src]; - } - - throw "unexpected problem while exploding "; -} - -private Symbol allChars = \char-class([range(1,1114111)]); -private Symbol allCharsStar = \iter-star(allChars); - -@synopsis{singleton str nodes are lexicals (identifiers and constants)} -Tree explode(data[&T] ast:str name(str _), Symbol def, str contents, loc _top, int offset, int length) { - - rule = prod(\syntax(def),[allCharsStar],{}); - - return appl(rule, - [appl(regular(allCharsStar), [char(ch) | ch <- chars(contents[offset..offset+length])])]); -} - -@synopsis{Special case for empty lists} -Tree explode([], Symbol def, str contents, loc top, int offset, int length) - = appl(regular(\syntax(def), [])); - -@synopsis{Abstract lists become concrete lists} -Tree explode(list[value] children, Symbol def, str contents, loc top, int offset, int length) { - list[loc] pox = positions(top(offset, length), children); - Symbol elem = def.symbol; - - rule = regular(\syntax(def)); - - work = zipi(zip2(children, pox)); - count = size(work); - - children = [ - explode(c, elem, contents, top, pos.offset, pos.length)[src=pos], // element - *[separatorTree(contents, pos.offset + pos.length, next.offset) | i + 1 < count, <_, <_, loc next>> := work[i + 1]], // middle - | > <- work - ]; - - return appl(rule, children); -} - -@synopsis{do not further explode parse trees} -Tree explode(Tree t, Symbol _, str _, int _, int _) = t; - -@synopsis{Nullary constructor} -Tree explode(data[&T] ast: _(), Symbol def, str contents, loc _pos, int offset, int length) { - rule = prod(\syntax(def), [layouts("*seps*")], {}); - - return appl(rule, [separatorTree(contents, offset, offset + length)]); -} - -private Tree emptyTree(loc src) = appl(regular(empty()), [], src=src); - -@synopsis{AST nodes with a single child} -Tree explode(data[&T] ast:str label(value child), Symbol _def, str contents, loc top, int offset, int length) { - list[value] children = [child]; - list[loc] pox = positions(ast.src, children); - Production cons = getConstructor(ast); - list[Symbol] symbols = cons.symbols; - - rule = prod(\syntax(cons.def), [empty(), layouts("*seps*"), \syntax(symbols[0]), layouts("*seps"), empty()], {}); - - return appl(rule, [ - emptyTree(top(offset, 0)), - separatorTree(contents, offset, pox[0].offset), - explode(child, unlabel(cons.symbols[0]), contents, top, pox[0].offset, pox[0].length)[src=pox[0]], - separatorTree(contents, pox[0].offset + pox[0].length, offset + length), - emptyTree(top(offset+length, 0)) - ], src=top(offset, length)); -} - - -@synopsis{main workhorse for AST nodes with more than one child} -default Tree explode(data[&T] ast, Symbol _def, str contents, loc top, int offset, int length) { - list[value] children = getChildren(ast); - list[loc] pox = positions(ast.src, children); - Production cons = getConstructor(ast); - list[Symbol] symbols = cons.symbols; - - rule = prod(\syntax(cons.def), [empty(), layouts("*seps*"), *[\syntax(c), layouts("*seps*") | Symbol c <- symbols], empty()], {}); - - work = zipi(zip3(children, symbols, pox)); - count = size(work); - - children = [ - emptyTree(top(offset, 0)), - separatorTree(contents, offset, pos.offset), - *[ - explode(c, s, contents, top, pos.offset, pos.length)[src=pos], // element - *[separatorTree(contents, pos.offset + pos.length, next.offset) | i + 1 < count, <_, <_, _, loc next>> := work[i + 1]], // middle - | > <- work - ], - separatorTree(contents, pos.offset + pos.length, offset + length) - emptyTree(top(offset+length, 0)) - ]; - - return appl(rule, children); -} - -@synopsis{Generate a layout tree with the separator content} -Tree separatorTree(str contents, int \start, int end) - = appl(prod(layouts("*seps*"),[allCharsStar],{}), - [appl(regular(allCharsStar), - [char(ch) | int ch <- chars(contents[\start..end])]) - ] - ); - -@synopsis{Helper function to convert AST notions to their ParseTree equivalent.} -@description{ -* argument labels are kept for field access purposes later -* string constants represent (flat) lexical syntax -* abstract lists become concrete layout-separated nullable lists. -} -Symbol \syntax(label(str x, Symbol s)) = label(x, \syntax(s)); -Symbol \syntax(\str()) = \lex("*lexical*"); -Symbol \syntax(\list(Symbol s)) = \iter-star-seps(\syntax(s),[layouts("*seps*")]); - -private Symbol unlabel(label(str _, Symbol s)) = unlabel(s); -private Symbol unlabel(conditional(Symbol s, set[Condition] _)) = unlabel(s); -private default Symbol unlabel(Symbol s) = s; - -@synopsis{Give every element an exact and true location for later processing.} -@description{ -For every AST element in a list, the function returns a list of the same length, -with each inferred fully-specified location in the place of the respective AST element. - -There are strings, nodes, empty lists and non-empty lists to consider. Only nodes have -a `.src` field. For the other values a `loc` value is computed from the surrounding -siblings and the parent span. - -This algorithm runs in 2 steps: -1. `pos` first positions every type of possible abstract element - * for lexical strings it becomes the entire span - * empty lists are not resolvable in this stage, deferred with `empty:///` - * nodes with src annotations; that is used - * non-empty lists take the cover of the first and last element. -2. The second step is a fixed-point computation that incrementally replaces `empty:///` instances -by using the information of the already resolved siblings, until all `empty:///` spots have been resolved. - * `empty:///` at the start means we can use the parent span for the left border. - * `empty:///` at the end means we can use the parent span for the right border. - * `empty:///` after a resolved location can take over the right border of that sibling. - * `empty:///` before a resolved location can take over the left border of that sibling. - -Due to the semantics of list matching, the algorithm typically replaces `empty:///` in the list -going from left to right to find instances of the above cases. -} -private list[loc] positions(loc span, list[value] l) = infer(span, [pos(span, x) | x <- l]); - -@synopsis{Replaces all |empty:///| with a correct loc inferred from the surroundings} -private list[loc] infer(loc span, [loc l, *loc rest]) = infer(span, [span[length=0], *rest]) when l == |empty:///|; -private list[loc] infer(loc span, [*loc rest, loc l]) = infer(span, [*rest, span[offset=span.offset+span.length-1][length=0]]) when l == |empty:///|; -private list[loc] infer(loc span, [*loc pre, loc before, loc l, *loc post]) = infer(span, [*pre, before, before[offset=before.offset+before.length][length = 0], *post]) when l == |empty:///|; -private list[loc] infer(loc span, [*loc pre, loc l, loc after, *loc post]) = infer(span, [*pre, after[offset=after.offset][length = 0], after, *post]) when l == |empty:///|; -private default list[loc] infer(loc _span, list[loc] done) = done; - -@synsopsis{Take the src field or infer the position from context} -@description{ -This function applies the `span` and any directly available `.src` fields -to do a first estimate at solving the location of an AST element. -In particular it fails to do so for empty lists `[]`, which is left for -the later `infer` stage. -} -@pitfalls{ -* This is where we have to assume that `str` fields are always singletons, otherwise we could not -put the entire `span` around them. -} -private loc pos(loc span, str _) = span; -private loc pos(loc _span, []) = |empty:///|; -private loc pos(loc _span, node n) = \loc(n); -private loc pos(loc _span, [node n]) = \loc(n); -private loc pos(loc _span, [node a, *_, node b]) = cover([\loc(a), \loc(b)]); - -@synopsis{Waiting for `node.src` to be available in Rascal for good...} -private loc \loc(node n) = l when loc l := n.src; - -@synopsis{Infer positions of separators} \ No newline at end of file