From 5846f1d405c16e925b94a394ac648ecae3f7b740 Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Thu, 11 Jun 2026 16:36:19 +0000 Subject: [PATCH 01/10] Fix LoopUnroll via parenExpr; expose parenExpr through full stack; fix staging traversal bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## parenExpr factory (FortranNodeFactory → AstFactory → FortranJoinPoints) - FortranNodeFactory.java: add parenExpr(Expr) factory using existing ParenExpr node - AstFactory.java: expose parenExpr(AExpr) as a static LARA-accessible factory - FortranJoinPoints.ts: add parenExpr(Expr) TypeScript wrapper ## LoopUnroll.ts — two targeted fixes Fix A (cleanup loop bound — dynprog, lu): Wrap ctrl.lower in parenExpr() in the tripCount subtraction so compound lower bounds like (k+1) or (i+1) emit as `n - (k+1) + 1 = n - k` instead of `n - k + 1 + 1 = n - k + 2`. Use MOD() intrinsic for the full formula. Fix B (body substituteVar — adi): Wrap the Add(ref, offset) replacement in parenExpr() so it emits as `(i2 + 1)` rather than a bare `i2 + 1`. This prevents sign-flip when the replacement appears as the rhs of a subtraction: `n - (i2+1)` is correct whereas `n - i2 + 1` evaluates as `n - i2 + 1` (wrong index). ## Joinpoints.ts — map missing staging node types Add 9 node types present in the Java weaverspecs (attributeSpecifier, typeDeclarationStatement, entityDecl, etc.) but missing from the TypeScript mapper. Mapped to Statement to allow traversal without crashing. ## LoopUnrollPass.ts — guard against null Java nodes Wrap children/descendants access in try/catch so nodes whose underlying Java joinpoint has a null node (e.g. attributeSpecifier on staging) are silently skipped during innermost-loop discovery. ## Generic polybench examples Add tilingGeneric, unrollGeneric, fusionGeneric, fissionGeneric, interchangeGeneric — generic kernel_* versions of the 3mm-only examples. Result on PolyBench/Fortran SMALL_DATASET: 29/29 MATCH, 0 mismatches (atax + bicg also now pass — staging branch fixed NamedConstantDef via PR #48). Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/FortranJoinPoints.ts | 4 + Fortran-JS/src-api/Joinpoints.ts | 13 + Fortran-JS/src-api/code/LoopUnroll.ts | 58 +- Fortran-JS/src-api/examples/fissionGeneric.ts | 14 + Fortran-JS/src-api/examples/fusionGeneric.ts | 14 + .../src-api/examples/interchangeGeneric.ts | 40 + Fortran-JS/src-api/examples/tilingGeneric.ts | 16 + Fortran-JS/src-api/examples/unrollGeneric.ts | 16 + Fortran-JS/src-api/pass/LoopUnrollPass.ts | 17 +- .../specs/fortran/ast/FortranNodeFactory.java | 6 + .../specs/fortran/weaver/FortranWeaver.json | 3950 ++++++++++++++--- .../fortran/weaver/importable/AstFactory.java | 7 + 12 files changed, 3517 insertions(+), 638 deletions(-) create mode 100644 Fortran-JS/src-api/examples/fissionGeneric.ts create mode 100644 Fortran-JS/src-api/examples/fusionGeneric.ts create mode 100644 Fortran-JS/src-api/examples/interchangeGeneric.ts create mode 100644 Fortran-JS/src-api/examples/tilingGeneric.ts create mode 100644 Fortran-JS/src-api/examples/unrollGeneric.ts diff --git a/Fortran-JS/src-api/FortranJoinPoints.ts b/Fortran-JS/src-api/FortranJoinPoints.ts index ea779c03..7722dd33 100644 --- a/Fortran-JS/src-api/FortranJoinPoints.ts +++ b/Fortran-JS/src-api/FortranJoinPoints.ts @@ -97,4 +97,8 @@ export default class FortranJoinPoints { name, args.map(unwrapJoinPoint) )); } + + static parenExpr(expr: Joinpoints.Expr): Joinpoints.Expr { + return wrapJoinPoint(FortranJavaTypes.AstFactory.parenExpr(unwrapJoinPoint(expr))); + } } \ No newline at end of file diff --git a/Fortran-JS/src-api/Joinpoints.ts b/Fortran-JS/src-api/Joinpoints.ts index ce6e1070..f0306e0c 100644 --- a/Fortran-JS/src-api/Joinpoints.ts +++ b/Fortran-JS/src-api/Joinpoints.ts @@ -738,6 +738,19 @@ const JoinpointMapper = { ifStatement: IfStatement, ompBlockConstruct: OmpBlockConstruct, arraySubscriptExpr: ArraySubscriptExpr, + // Declaration/specification node types present in the Java weaverspecs but + // without dedicated TypeScript classes. Mapped to Statement so that + // Query.search and $jp.descendants traversals do not crash when encountering + // variable type declarations (e.g. `integer :: i, j`). + typeDeclarationStatement: Statement, + specificationStatement: Statement, + entityDecl: Statement, + fortranDecl: Statement, + exprInitialization: Statement, + initialization: Statement, + attributeSpecifier: Statement, + keywordAttributeSpecifier: Statement, + parameterKeyword: Statement, }; let registered = false; diff --git a/Fortran-JS/src-api/code/LoopUnroll.ts b/Fortran-JS/src-api/code/LoopUnroll.ts index b1fe9442..c12b7d61 100644 --- a/Fortran-JS/src-api/code/LoopUnroll.ts +++ b/Fortran-JS/src-api/code/LoopUnroll.ts @@ -4,14 +4,21 @@ import { DataRef, DoStatement, ExecutableStatement, RangeLoopControl } from "../ /** * Deep-copies a body statement and replaces every reference to `varName` with - * `varName+offset` using AST-level substitution (only real variable references + * `(varName+offset)` using AST-level substitution (only real variable references * are affected, not occurrences inside string literals or comments). + * + * The replacement is wrapped in parenExpr() so that when it appears as the + * right operand of a surrounding subtraction (e.g. `n - i`) the emitted code + * is `n - (i + 1)` rather than `n - i + 1` (which Fortran evaluates as + * `n - i + 1`, flipping the sign of the offset). */ function substituteVar(stmt: ExecutableStatement, varName: string, offset: number): ExecutableStatement { const copy = stmt.deepCopy() as ExecutableStatement; if (offset === 0) return copy; for (const ref of Query.searchFrom(copy, DataRef, { name: varName })) { - const binOp = FortranJoinPoints.binaryOperatorAdd(ref.deepCopy(), FortranJoinPoints.intLiteral(offset)); + const binOp = FortranJoinPoints.parenExpr( + FortranJoinPoints.binaryOperatorAdd(ref.deepCopy(), FortranJoinPoints.intLiteral(offset)) + ); ref.replaceWith(binOp) } return copy; @@ -22,7 +29,7 @@ function substituteVar(stmt: ExecutableStatement, varName: string, offset: numbe * * Two loops replace the original: * - A **main loop** that steps by `factor`, with the body replicated `factor` - * times (each copy substitutes `var` with `var+offset` for offset 0..factor-1). + * times (each copy substitutes `var` with `(var+offset)` for offset 0..factor-1). * - A **cleanup loop** that handles the remaining < `factor` iterations with the * original body. When `factor` exactly divides the trip count, the cleanup * loop's bounds produce a no-op and it generates no iterations. @@ -37,10 +44,10 @@ function substituteVar(stmt: ExecutableStatement, varName: string, offset: numbe * // end do * * // After: - * // do i = 1, (n) - 3, 4 - * // a(i) = b(i); a(i+1) = b(i+1); a(i+2) = b(i+2); a(i+3) = b(i+3) + * // do i = 1, n - 3, 4 + * // a(i) = b(i); a((i+1)) = b((i+1)); a((i+2)) = b((i+2)); a((i+3)) = b((i+3)) * // end do - * // do i = (1) + (((n) - (1) + 1) / 4) * 4, n + * // do i = n + 1 - MOD(n - (1) + 1, 4), n * // a(i) = b(i) * // end do * @@ -59,7 +66,7 @@ export default function loopUnroll($loop: DoStatement, factor: number): DoStatem const mainCtrl = mainDo.control as RangeLoopControl; mainCtrl.setUpper(FortranJoinPoints.binaryOperatorSubtract( - ctrl.upper.deepCopy(), + ctrl.upper.deepCopy(), FortranJoinPoints.intLiteral(factor - 1)) ); mainCtrl.setStep(FortranJoinPoints.intLiteral(factor)); @@ -70,21 +77,28 @@ export default function loopUnroll($loop: DoStatement, factor: number): DoStatem } } - // Cleanup loop: lo + ((hi - lo + 1) / factor) * factor .. hi - // Integer division makes this a no-op when factor exactly divides the trip count. - const cleanupLower = FortranJoinPoints.binaryOperatorAdd( - ctrl.lower.deepCopy(), - FortranJoinPoints.binaryOperatorMultiply( - FortranJoinPoints.binaryOperatorDivide( - FortranJoinPoints.binaryOperatorAdd( - FortranJoinPoints.binaryOperatorSubtract(ctrl.upper.deepCopy(), ctrl.lower.deepCopy()), - FortranJoinPoints.intLiteral(1) - ), - FortranJoinPoints.intLiteral(factor) - ), - FortranJoinPoints.intLiteral(factor) - ) + // Cleanup loop: hi + 1 - MOD(hi - (lo) + 1, factor) .. hi + // + // lo is wrapped in parenExpr() so compound lower bounds like (k+1) or (i+1) + // emit as `hi - (k+1) + 1 = hi - k` instead of `hi - k + 1 + 1 = hi - k + 2`. + // MOD's argument list is syntactically parenthesized, so the full tripCount + // expression inside it is evaluated correctly regardless of complexity. + const tripCount = FortranJoinPoints.binaryOperatorAdd( + FortranJoinPoints.binaryOperatorSubtract( + ctrl.upper.deepCopy(), + FortranJoinPoints.parenExpr(ctrl.lower.deepCopy()) + ), + FortranJoinPoints.intLiteral(1) + ); + const remainder = FortranJoinPoints.intrinsicCall("MOD", [ + tripCount, + FortranJoinPoints.intLiteral(factor) + ]); + const cleanupLower = FortranJoinPoints.binaryOperatorSubtract( + FortranJoinPoints.binaryOperatorAdd(ctrl.upper.deepCopy(), FortranJoinPoints.intLiteral(1)), + remainder ); + const cleanupCtrl = FortranJoinPoints.rangeLoopControl(ctrl.var.deepCopy() as DataRef, cleanupLower, ctrl.upper.deepCopy()); const cleanupDo = FortranJoinPoints.doStatement(cleanupCtrl); for (const stmt of bodyStmts) { @@ -99,4 +113,4 @@ export function canUnroll($loop: DoStatement, factor: number = 2): boolean { if (!($loop.control instanceof RangeLoopControl)) return false; if (factor <= 1) return false; return ($loop.control as RangeLoopControl).step === undefined; -} \ No newline at end of file +} diff --git a/Fortran-JS/src-api/examples/fissionGeneric.ts b/Fortran-JS/src-api/examples/fissionGeneric.ts new file mode 100644 index 00000000..2fba3764 --- /dev/null +++ b/Fortran-JS/src-api/examples/fissionGeneric.ts @@ -0,0 +1,14 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import LoopFissionPass from "../pass/LoopFissionPass.js"; +import { Subroutine } from "../Joinpoints.js"; + +const subroutines = Query.search(Subroutine, ($jp) => $jp.moduleName.startsWith('kernel_')).get(); + +if (subroutines.length === 0) { + console.log('No kernel_* subroutine found — skipping'); +} else { + for (const sub of subroutines) { + console.log(`[fissionGeneric] Applying loop fission: ${sub.moduleName}`); + new LoopFissionPass().apply(sub); + } +} diff --git a/Fortran-JS/src-api/examples/fusionGeneric.ts b/Fortran-JS/src-api/examples/fusionGeneric.ts new file mode 100644 index 00000000..d9c82a99 --- /dev/null +++ b/Fortran-JS/src-api/examples/fusionGeneric.ts @@ -0,0 +1,14 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import LoopFusionPass from "../pass/LoopFusionPass.js"; +import { Subroutine } from "../Joinpoints.js"; + +const subroutines = Query.search(Subroutine, ($jp) => $jp.moduleName.startsWith('kernel_')).get(); + +if (subroutines.length === 0) { + console.log('No kernel_* subroutine found — skipping'); +} else { + for (const sub of subroutines) { + console.log(`[fusionGeneric] Applying loop fusion: ${sub.moduleName}`); + new LoopFusionPass().apply(sub); + } +} diff --git a/Fortran-JS/src-api/examples/interchangeGeneric.ts b/Fortran-JS/src-api/examples/interchangeGeneric.ts new file mode 100644 index 00000000..b4d23475 --- /dev/null +++ b/Fortran-JS/src-api/examples/interchangeGeneric.ts @@ -0,0 +1,40 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { DoStatement, RangeLoopControl, Joinpoint } from "../Joinpoints.js"; + +function isInKernelSubroutine(loop: DoStatement): boolean { + const sub = loop.getAncestor("programUnit"); + if (!sub) return false; + return (sub as Joinpoint).code.split("\n")[0].toLowerCase().includes("kernel_"); +} + +const pairs: { outer: DoStatement; inner: DoStatement }[] = []; +for (const loop of [...Query.search(DoStatement)].filter(isInKernelSubroutine)) { + const stmts = loop.body.executableStmts; + if (stmts.length === 1 && stmts[0] instanceof DoStatement) { + pairs.push({ outer: loop, inner: stmts[0] as DoStatement }); + } +} + +const innerSet = new Set(pairs.map(p => p.inner)); +const topPairs = pairs.filter(p => !innerSet.has(p.outer)); + +console.log(`[interchangeGeneric] Found ${topPairs.length} interchangeable loop pair(s)`); + +for (const { outer, inner } of topPairs) { + const oc = outer.control; + const ic = inner.control; + if (!(oc instanceof RangeLoopControl && ic instanceof RangeLoopControl)) continue; + + const innerBody = inner.body.executableStmts.map(s => (s as Joinpoint).code); + + const newCode = [ + `do ${ic.code}`, + `do ${oc.code}`, + ...innerBody, + `end do`, + `end do`, + ].join("\n"); + + outer.insert("replace", newCode); + console.log(` [interchangeGeneric] Interchanged: ${oc.var.name} <-> ${ic.var.name}`); +} diff --git a/Fortran-JS/src-api/examples/tilingGeneric.ts b/Fortran-JS/src-api/examples/tilingGeneric.ts new file mode 100644 index 00000000..d83a3557 --- /dev/null +++ b/Fortran-JS/src-api/examples/tilingGeneric.ts @@ -0,0 +1,16 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import LoopTilingPass from "../pass/LoopTilingPass.js"; +import { Subroutine } from "../Joinpoints.js"; + +const TILE_SIZE = 32; + +const subroutines = Query.search(Subroutine, ($jp) => $jp.moduleName.startsWith('kernel_')).get(); + +if (subroutines.length === 0) { + console.log('No kernel_* subroutine found — skipping'); +} else { + for (const sub of subroutines) { + console.log(`[tilingGeneric] Tiling (tile=${TILE_SIZE}): ${sub.moduleName}`); + new LoopTilingPass(TILE_SIZE).apply(sub); + } +} diff --git a/Fortran-JS/src-api/examples/unrollGeneric.ts b/Fortran-JS/src-api/examples/unrollGeneric.ts new file mode 100644 index 00000000..597c5057 --- /dev/null +++ b/Fortran-JS/src-api/examples/unrollGeneric.ts @@ -0,0 +1,16 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import LoopUnrollPass from "../pass/LoopUnrollPass.js"; +import { Subroutine } from "../Joinpoints.js"; + +const FACTOR = 4; + +const subroutines = Query.search(Subroutine, ($jp) => $jp.moduleName.startsWith('kernel_')).get(); + +if (subroutines.length === 0) { + console.log('No kernel_* subroutine found — skipping'); +} else { + for (const sub of subroutines) { + console.log(`[unrollGeneric] Unrolling innermost loops (factor=${FACTOR}): ${sub.moduleName}`); + new LoopUnrollPass(FACTOR).apply(sub); + } +} diff --git a/Fortran-JS/src-api/pass/LoopUnrollPass.ts b/Fortran-JS/src-api/pass/LoopUnrollPass.ts index 5d5333a8..304b97e2 100644 --- a/Fortran-JS/src-api/pass/LoopUnrollPass.ts +++ b/Fortran-JS/src-api/pass/LoopUnrollPass.ts @@ -37,16 +37,29 @@ export default class LoopUnrollPass extends Pass { * * A loop is innermost when none of its descendants is a do-loop. * Recursion does not descend into innermost loops. + * + * The try/catch around children access guards against staging-branch AST + * nodes (e.g. attributeSpecifier) whose underlying Java joinpoint has a null + * node, causing a NullPointerException when `.children` is accessed. */ protected *_findInnermostLoops($jp: Joinpoint): Generator { if ($jp instanceof DoStatement) { - const hasNestedLoop = $jp.descendants.some(d => d instanceof DoStatement); + let hasNestedLoop = false; + try { + hasNestedLoop = $jp.descendants.some(d => d instanceof DoStatement); + } catch (_) { /* unmapped descendant — treat as no nested loop */ } if (!hasNestedLoop && canUnroll($jp, this.factor)) { yield $jp; return; } } - for (const child of [...$jp.children]) { + let children: Joinpoint[]; + try { + children = [...$jp.children]; + } catch (_) { + return; // skip nodes whose children cannot be accessed + } + for (const child of children) { yield* this._findInnermostLoops(child); } } diff --git a/FortranAst/src/pt/up/fe/specs/fortran/ast/FortranNodeFactory.java b/FortranAst/src/pt/up/fe/specs/fortran/ast/FortranNodeFactory.java index 4b0e0a93..915bf48e 100644 --- a/FortranAst/src/pt/up/fe/specs/fortran/ast/FortranNodeFactory.java +++ b/FortranAst/src/pt/up/fe/specs/fortran/ast/FortranNodeFactory.java @@ -13,6 +13,7 @@ import pt.up.fe.specs.fortran.ast.nodes.expr.Expr; import pt.up.fe.specs.fortran.ast.nodes.expr.IntLiteral; import pt.up.fe.specs.fortran.ast.nodes.expr.Literal; +import pt.up.fe.specs.fortran.ast.nodes.expr.ParenExpr; import pt.up.fe.specs.fortran.ast.nodes.expr.StringLiteral; import pt.up.fe.specs.fortran.ast.nodes.expr.enums.BinaryOperatorKind; import pt.up.fe.specs.fortran.ast.nodes.loops.LoopControl; @@ -331,6 +332,11 @@ public BinaryOperator binaryOperator(BinaryOperatorKind kind, Expr lhs, Expr rhs return node; } + public ParenExpr parenExpr(Expr expr) { + DataStore data = newDataStore(ParenExpr.class); + return new ParenExpr(data, Collections.singletonList(expr)); + } + public OmpOrderedClause ompOrderedClause(int value) { DataStore data = newDataStore(OmpOrderedClause.class); diff --git a/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/FortranWeaver.json b/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/FortranWeaver.json index 86c57729..1940bdb1 100644 --- a/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/FortranWeaver.json +++ b/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/FortranWeaver.json @@ -981,6 +981,232 @@ }] }] }, + { + "type": "joinpoint", + "name": "attributeSpecifier", + "extends": "joinpoint" , + "children": [ + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, { "type": "joinpoint", "name": "binaryOperator", @@ -2394,6 +2620,24 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, { "type": "action", "tooltip": "Removes node associated to the joinpoint from the AST", @@ -2405,25 +2649,95 @@ }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Inserts the given join point after this join point", "children": [ { "type": "joinpoint", - "name": "replaceWith" + "name": "insertAfter" }, { "type": "joinpoint", "name": "node", "defaultValue": "" }] - }] - }, - { - "type": "joinpoint", - "name": "elseIfBlock", - "extends": "joinpoint" , - "tooltip": "Represents the optional 'else if' blocks of an if construct", - "children": [ + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "elseIfBlock", + "extends": "joinpoint" , + "tooltip": "Represents the optional 'else if' blocks of an if construct", + "children": [ { "type": "attribute", "children": [ @@ -2551,170 +2865,111 @@ }, { "type": "action", - "tooltip": "Removes node associated to the joinpoint from the AST", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", "children": [ { "type": "joinpoint", - "name": "detach" + "name": "copy" }] }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", "children": [ { "type": "joinpoint", - "name": "replaceWith" - }, - { - "type": "joinpoint", - "name": "node", - "defaultValue": "" - }] - }] - }, - { - "type": "joinpoint", - "name": "elseIfStatement", - "extends": "joinpoint" , - "tooltip": "Represents the header of an 'else if' block", - "children": [ - { - "type": "attribute", - "children": [ - { - "type": "expr", - "name": "condition" - }] - }, - { - "type": "attribute", - "tooltip": "Returns an array with the children of the node", - "children": [ - { - "type": "joinpoint[]", - "name": "children" + "name": "deepCopy" }] }, { - "type": "attribute", - "tooltip": "String with the code represented by this node", + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", "children": [ { - "type": "String", - "name": "code" + "type": "joinpoint", + "name": "detach" }] }, { - "type": "attribute", - "tooltip": "true if the given node is a descendant of this node", + "type": "action", + "tooltip": "Inserts the given join point after this join point", "children": [ { - "type": "Boolean", - "name": "contains" + "type": "joinpoint", + "name": "insertAfter" }, { "type": "joinpoint", - "name": "jp", + "name": "node", "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "Returns an array with the descendants of the node", - "children": [ - { - "type": "joinpoint[]", - "name": "descendants" - }] - }, - { - "type": "attribute", - "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { "type": "joinpoint", - "name": "getAncestor" + "name": "insertAfter" }, { "type": "String", - "name": "type", + "name": "code", "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "Returns the index of this join point in relation to its parent", - "children": [ - { - "type": "int", - "name": "indexOfSelf" - }] - }, - { - "type": "attribute", - "tooltip": "Returns the node that came before this node, or undefined if there is none", + "type": "action", + "tooltip": "Inserts the given join point before this join point", "children": [ { "type": "joinpoint", - "name": "leftJp" - }] - }, - { - "type": "attribute", - "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", - "children": [ + "name": "insertBefore" + }, { "type": "joinpoint", - "name": "parent" + "name": "node", + "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { "type": "joinpoint", - "name": "rightJp" - }] - }, - { - "type": "attribute", - "tooltip": "Returns the 'program' join point", - "children": [ - { - "type": "program", - "name": "root" - }] - }, - { - "type": "attribute", - "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", - "children": [ + "name": "insertBefore" + }, { - "type": "joinpoint[]", - "name": "scopeNodes" + "type": "String", + "name": "node", + "defaultValue": "" }] }, { "type": "action", - "tooltip": "Removes node associated to the joinpoint from the AST", + "tooltip": "Replaces this node with the given node", "children": [ { "type": "joinpoint", - "name": "detach" + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Overload which accepts a list of join points", "children": [ { "type": "joinpoint", "name": "replaceWith" }, { - "type": "joinpoint", + "type": "joinpoint[]", "name": "node", "defaultValue": "" }] @@ -2722,24 +2977,16 @@ }, { "type": "joinpoint", - "name": "executableStatement", - "extends": "statement" , - "tooltip": "Represents an executable statement", + "name": "elseIfStatement", + "extends": "joinpoint" , + "tooltip": "Represents the header of an 'else if' block", "children": [ { "type": "attribute", "children": [ { - "type": "Boolean", - "name": "isFirst" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isLast" + "type": "expr", + "name": "condition" }] }, { @@ -2965,23 +3212,15 @@ }, { "type": "joinpoint", - "name": "execution", - "extends": "statementBlock" , + "name": "entityDecl", + "extends": "fortranDecl" , "children": [ { "type": "attribute", "children": [ { - "type": "executableStatement[]", - "name": "executableStmts" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "statement[]", - "name": "stmts" + "type": "String", + "name": "name" }] }, { @@ -3095,37 +3334,11 @@ }, { "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", "children": [ { - "type": "void", - "name": "insertBegin" - }, - { - "type": "executableStatement", - "name": "stmt", - "defaultValue": "" - }] - }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "insertEnd" - }, - { - "type": "executableStatement", - "name": "stmt", - "defaultValue": "" - }] - }, - { - "type": "action", - "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", - "children": [ - { - "type": "joinpoint", - "name": "copy" + "type": "joinpoint", + "name": "copy" }] }, { @@ -3233,10 +3446,26 @@ }, { "type": "joinpoint", - "name": "expr", - "extends": "joinpoint" , - "tooltip": "Represents an expression", + "name": "executableStatement", + "extends": "statement" , + "tooltip": "Represents an executable statement", "children": [ + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" + }] + }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -3460,27 +3689,23 @@ }, { "type": "joinpoint", - "name": "file", - "defaultAttr": "name", - "extends": "joinpoint" , - "tooltip": "Represents a source file (e.g., .f90)", + "name": "execution", + "extends": "statementBlock" , "children": [ { "type": "attribute", - "tooltip": "the name of the folder", "children": [ { - "type": "String", - "name": "foldername" + "type": "executableStatement[]", + "name": "executableStmts" }] }, { "type": "attribute", - "tooltip": "the name of the file", "children": [ { - "type": "String", - "name": "name" + "type": "statement[]", + "name": "stmts" }] }, { @@ -3592,6 +3817,32 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "insertBegin" + }, + { + "type": "executableStatement", + "name": "stmt", + "defaultValue": "" + }] + }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "insertEnd" + }, + { + "type": "executableStatement", + "name": "stmt", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -3706,50 +3957,10 @@ }, { "type": "joinpoint", - "name": "ifConstruct", - "extends": "executableStatement" , - "tooltip": "Represents the root of an if construct", + "name": "expr", + "extends": "joinpoint" , + "tooltip": "Represents an expression", "children": [ - { - "type": "attribute", - "children": [ - { - "type": "elseBlock", - "name": "elseBlock" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "elseIfBlock[]", - "name": "elseIfBlocks" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "ifThenBlock", - "name": "ifThenBlock" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isFirst" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isLast" - }] - }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -3859,6 +4070,24 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, { "type": "action", "tooltip": "Removes node associated to the joinpoint from the AST", @@ -3870,54 +4099,100 @@ }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Inserts the given join point after this join point", "children": [ { "type": "joinpoint", - "name": "replaceWith" + "name": "insertAfter" }, { "type": "joinpoint", "name": "node", "defaultValue": "" }] - }] - }, - { - "type": "joinpoint", - "name": "ifStatement", - "extends": "actionStatement" , - "children": [ + }, { - "type": "attribute", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { - "type": "expr", - "name": "condition" + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" }] }, { - "type": "attribute", + "type": "action", + "tooltip": "Inserts the given join point before this join point", "children": [ { - "type": "actionStatement", - "name": "statement" + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, { - "type": "attribute", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { - "type": "Boolean", - "name": "isFirst" + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "exprInitialization", + "extends": "initialization" , + "children": [ { "type": "attribute", "children": [ { - "type": "Boolean", - "name": "isLast" + "type": "expr", + "name": "expr" }] }, { @@ -4029,6 +4304,24 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, { "type": "action", "tooltip": "Removes node associated to the joinpoint from the AST", @@ -4040,39 +4333,112 @@ }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Inserts the given join point after this join point", "children": [ { "type": "joinpoint", - "name": "replaceWith" + "name": "insertAfter" }, { "type": "joinpoint", "name": "node", "defaultValue": "" }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] }] }, { "type": "joinpoint", - "name": "ifThenBlock", + "name": "file", + "defaultAttr": "name", "extends": "joinpoint" , - "tooltip": "Represents the first block of an if construct", + "tooltip": "Represents a source file (e.g., .f90)", "children": [ { "type": "attribute", + "tooltip": "the name of the folder", "children": [ { - "type": "statementBlock", - "name": "body" + "type": "String", + "name": "foldername" }] }, { "type": "attribute", + "tooltip": "the name of the file", "children": [ { - "type": "ifThenStatement", - "name": "header" + "type": "String", + "name": "name" }] }, { @@ -4184,6 +4550,24 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, { "type": "action", "tooltip": "Removes node associated to the joinpoint from the AST", @@ -4193,6 +4577,62 @@ "name": "detach" }] }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Replaces this node with the given node", @@ -4206,22 +4646,27 @@ "name": "node", "defaultValue": "" }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] }] }, { "type": "joinpoint", - "name": "ifThenStatement", + "name": "fortranDecl", "extends": "joinpoint" , - "tooltip": "Represents the header of an 'if then' block", "children": [ - { - "type": "attribute", - "children": [ - { - "type": "expr", - "name": "condition" - }] - }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -4292,64 +4737,2303 @@ "children": [ { "type": "joinpoint", - "name": "leftJp" + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "ifConstruct", + "extends": "executableStatement" , + "tooltip": "Represents the root of an if construct", + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "elseBlock", + "name": "elseBlock" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "elseIfBlock[]", + "name": "elseIfBlocks" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "ifThenBlock", + "name": "ifThenBlock" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "ifStatement", + "extends": "actionStatement" , + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "expr", + "name": "condition" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "actionStatement", + "name": "statement" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "ifThenBlock", + "extends": "joinpoint" , + "tooltip": "Represents the first block of an if construct", + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "statementBlock", + "name": "body" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "ifThenStatement", + "name": "header" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "ifThenStatement", + "extends": "joinpoint" , + "tooltip": "Represents the header of an 'if then' block", + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "expr", + "name": "condition" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "initialization", + "extends": "joinpoint" , + "children": [ + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "intLiteral", + "extends": "literal" , + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "String", + "name": "literal" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "keywordAttributeSpecifier", + "extends": "attributeSpecifier" , + "children": [ + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "literal", + "extends": "expr" , + "tooltip": "Represents a literal", + "children": [ + { + "type": "attribute", + "children": [ + { + "type": "String", + "name": "literal" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point after this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Inserts the given join point before this join point", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a string", + "children": [ + { + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Replaces this node with the given node", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Overload which accepts a list of join points", + "children": [ + { + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" + }] + }] + }, + { + "type": "joinpoint", + "name": "loopControl", + "extends": "joinpoint" , + "tooltip": "Represents the loop control structure, with specialized subclasses for different loop kinds", + "children": [ + { + "type": "attribute", + "tooltip": "Returns an array with the children of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "children" + }] + }, + { + "type": "attribute", + "tooltip": "String with the code represented by this node", + "children": [ + { + "type": "String", + "name": "code" + }] + }, + { + "type": "attribute", + "tooltip": "true if the given node is a descendant of this node", + "children": [ + { + "type": "Boolean", + "name": "contains" + }, + { + "type": "joinpoint", + "name": "jp", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns an array with the descendants of the node", + "children": [ + { + "type": "joinpoint[]", + "name": "descendants" + }] + }, + { + "type": "attribute", + "tooltip": "Looks for an ancestor joinpoint name, walking back on the AST", + "children": [ + { + "type": "joinpoint", + "name": "getAncestor" + }, + { + "type": "String", + "name": "type", + "defaultValue": "" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the index of this join point in relation to its parent", + "children": [ + { + "type": "int", + "name": "indexOfSelf" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that came before this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "leftJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "children": [ + { + "type": "joinpoint", + "name": "parent" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "children": [ + { + "type": "joinpoint", + "name": "rightJp" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the 'program' join point", + "children": [ + { + "type": "program", + "name": "root" + }] + }, + { + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "children": [ + { + "type": "joinpoint[]", + "name": "scopeNodes" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", + "children": [ + { + "type": "joinpoint", + "name": "copy" + }] + }, + { + "type": "action", + "tooltip": "Performs a copy of the node and its children, including the nodes in their fields (only the first level of field nodes, this function is not recursive)", + "children": [ + { + "type": "joinpoint", + "name": "deepCopy" + }] + }, + { + "type": "action", + "tooltip": "Removes node associated to the joinpoint from the AST", + "children": [ + { + "type": "joinpoint", + "name": "detach" }] }, { - "type": "attribute", - "tooltip": "Returns the parent node in the AST, or undefined if it is the root node", + "type": "action", + "tooltip": "Inserts the given join point after this join point", "children": [ { "type": "joinpoint", - "name": "parent" + "name": "insertAfter" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "Returns the node that comes after this node, or undefined if there is none", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { "type": "joinpoint", - "name": "rightJp" + "name": "insertAfter" + }, + { + "type": "String", + "name": "code", + "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "Returns the 'program' join point", + "type": "action", + "tooltip": "Inserts the given join point before this join point", "children": [ { - "type": "program", - "name": "root" + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, { - "type": "attribute", - "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", + "type": "action", + "tooltip": "Overload which accepts a string", "children": [ { - "type": "joinpoint[]", - "name": "scopeNodes" + "type": "joinpoint", + "name": "insertBefore" + }, + { + "type": "String", + "name": "node", + "defaultValue": "" }] }, { "type": "action", - "tooltip": "Removes node associated to the joinpoint from the AST", + "tooltip": "Replaces this node with the given node", "children": [ { "type": "joinpoint", - "name": "detach" + "name": "replaceWith" + }, + { + "type": "joinpoint", + "name": "node", + "defaultValue": "" }] }, { "type": "action", - "tooltip": "Replaces this node with the given node", + "tooltip": "Overload which accepts a list of join points", "children": [ { "type": "joinpoint", "name": "replaceWith" }, { - "type": "joinpoint", + "type": "joinpoint[]", "name": "node", "defaultValue": "" }] @@ -4357,15 +7041,16 @@ }, { "type": "joinpoint", - "name": "intLiteral", - "extends": "literal" , + "name": "mainProgram", + "extends": "programUnit" , "children": [ { "type": "attribute", + "tooltip": "Returns the unit's specification part", "children": [ { - "type": "String", - "name": "literal" + "type": "specification", + "name": "specification" }] }, { @@ -4591,16 +7276,16 @@ }, { "type": "joinpoint", - "name": "literal", - "extends": "expr" , - "tooltip": "Represents a literal", + "name": "nameValue", + "extends": "joinpoint" , + "tooltip": "Represents a name/value pair in a compiler directive", "children": [ { "type": "attribute", "children": [ { "type": "String", - "name": "literal" + "name": "name" }] }, { @@ -4826,10 +7511,34 @@ }, { "type": "joinpoint", - "name": "loopControl", - "extends": "joinpoint" , - "tooltip": "Represents the loop control structure, with specialized subclasses for different loop kinds", + "name": "ompBlockConstruct", + "extends": "ompConstruct" , + "tooltip": "Represents an OpenMP block construct (such as parallel or task)", "children": [ + { + "type": "attribute", + "children": [ + { + "type": "ompClause[]", + "name": "clauses" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" + }] + }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -4939,6 +7648,46 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setBody" + }, + { + "type": "execution", + "name": "body", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Sets the construct's clauses", + "children": [ + { + "type": "void", + "name": "setClauses" + }, + { + "type": "ompClause[]", + "name": "clauses", + "defaultValue": "" + }] + }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setDirective" + }, + { + "type": "String", + "name": "directive", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -5053,18 +7802,10 @@ }, { "type": "joinpoint", - "name": "mainProgram", - "extends": "programUnit" , + "name": "ompClause", + "extends": "joinpoint" , + "tooltip": "Represents an OpenMP clause", "children": [ - { - "type": "attribute", - "tooltip": "Returns the unit's specification part", - "children": [ - { - "type": "specification", - "name": "specification" - }] - }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -5288,16 +8029,32 @@ }, { "type": "joinpoint", - "name": "nameValue", - "extends": "joinpoint" , - "tooltip": "Represents a name/value pair in a compiler directive", + "name": "ompConstruct", + "extends": "executableStatement" , + "tooltip": "Represents a generic OpenMP construct", "children": [ { "type": "attribute", "children": [ { - "type": "String", - "name": "name" + "type": "ompClause[]", + "name": "clauses" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" }] }, { @@ -5409,6 +8166,33 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "tooltip": "Sets the construct's clauses", + "children": [ + { + "type": "void", + "name": "setClauses" + }, + { + "type": "ompClause[]", + "name": "clauses", + "defaultValue": "" + }] + }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setDirective" + }, + { + "type": "String", + "name": "directive", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -5502,55 +8286,31 @@ }, { "type": "joinpoint", - "name": "node", - "defaultValue": "" - }] - }, - { - "type": "action", - "tooltip": "Overload which accepts a list of join points", - "children": [ - { - "type": "joinpoint", - "name": "replaceWith" - }, - { - "type": "joinpoint[]", - "name": "node", - "defaultValue": "" - }] - }] - }, - { - "type": "joinpoint", - "name": "ompBlockConstruct", - "extends": "ompConstruct" , - "tooltip": "Represents an OpenMP block construct (such as parallel or task)", - "children": [ - { - "type": "attribute", - "children": [ - { - "type": "ompClause[]", - "name": "clauses" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isFirst" + "name": "node", + "defaultValue": "" }] }, { - "type": "attribute", + "type": "action", + "tooltip": "Overload which accepts a list of join points", "children": [ { - "type": "Boolean", - "name": "isLast" + "type": "joinpoint", + "name": "replaceWith" + }, + { + "type": "joinpoint[]", + "name": "node", + "defaultValue": "" }] - }, + }] + }, + { + "type": "joinpoint", + "name": "ompDataSharingClause", + "extends": "ompClause" , + "tooltip": "Represents an OpenMP datasharing clause (public, private, ...)", + "children": [ { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -5660,46 +8420,6 @@ "name": "scopeNodes" }] }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "setBody" - }, - { - "type": "execution", - "name": "body", - "defaultValue": "" - }] - }, - { - "type": "action", - "tooltip": "Sets the construct's clauses", - "children": [ - { - "type": "void", - "name": "setClauses" - }, - { - "type": "ompClause[]", - "name": "clauses", - "defaultValue": "" - }] - }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "setDirective" - }, - { - "type": "String", - "name": "directive", - "defaultValue": "" - }] - }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -5814,10 +8534,34 @@ }, { "type": "joinpoint", - "name": "ompClause", - "extends": "joinpoint" , - "tooltip": "Represents an OpenMP clause", + "name": "ompLoopConstruct", + "extends": "ompConstruct" , + "tooltip": "Represents an OpenMP loop construct (such as do or do parallel)", "children": [ + { + "type": "attribute", + "children": [ + { + "type": "ompClause[]", + "name": "clauses" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" + }] + }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -5927,6 +8671,46 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setLoop" + }, + { + "type": "doStatement", + "name": "loop", + "defaultValue": "" + }] + }, + { + "type": "action", + "tooltip": "Sets the construct's clauses", + "children": [ + { + "type": "void", + "name": "setClauses" + }, + { + "type": "ompClause[]", + "name": "clauses", + "defaultValue": "" + }] + }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setDirective" + }, + { + "type": "String", + "name": "directive", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -6041,34 +8825,9 @@ }, { "type": "joinpoint", - "name": "ompConstruct", - "extends": "executableStatement" , - "tooltip": "Represents a generic OpenMP construct", + "name": "ompOrderedClause", + "extends": "ompClause" , "children": [ - { - "type": "attribute", - "children": [ - { - "type": "ompClause[]", - "name": "clauses" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isFirst" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isLast" - }] - }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -6178,33 +8937,6 @@ "name": "scopeNodes" }] }, - { - "type": "action", - "tooltip": "Sets the construct's clauses", - "children": [ - { - "type": "void", - "name": "setClauses" - }, - { - "type": "ompClause[]", - "name": "clauses", - "defaultValue": "" - }] - }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "setDirective" - }, - { - "type": "String", - "name": "directive", - "defaultValue": "" - }] - }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -6319,9 +9051,8 @@ }, { "type": "joinpoint", - "name": "ompDataSharingClause", + "name": "ompReductionClause", "extends": "ompClause" , - "tooltip": "Represents an OpenMP datasharing clause (public, private, ...)", "children": [ { "type": "attribute", @@ -6546,34 +9277,9 @@ }, { "type": "joinpoint", - "name": "ompLoopConstruct", - "extends": "ompConstruct" , - "tooltip": "Represents an OpenMP loop construct (such as do or do parallel)", + "name": "parameterKeyword", + "extends": "keywordAttributeSpecifier" , "children": [ - { - "type": "attribute", - "children": [ - { - "type": "ompClause[]", - "name": "clauses" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isFirst" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isLast" - }] - }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -6671,56 +9377,16 @@ "children": [ { "type": "program", - "name": "root" - }] - }, - { - "type": "attribute", - "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", - "children": [ - { - "type": "joinpoint[]", - "name": "scopeNodes" - }] - }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "setLoop" - }, - { - "type": "doStatement", - "name": "loop", - "defaultValue": "" - }] - }, - { - "type": "action", - "tooltip": "Sets the construct's clauses", - "children": [ - { - "type": "void", - "name": "setClauses" - }, - { - "type": "ompClause[]", - "name": "clauses", - "defaultValue": "" + "name": "root" }] }, { - "type": "action", + "type": "attribute", + "tooltip": "the nodes of the scope of the current join point. If this node has a body (e.g., loop, function) corresponds to the children of the body. Otherwise, returns an empty array", "children": [ { - "type": "void", - "name": "setDirective" - }, - { - "type": "String", - "name": "directive", - "defaultValue": "" + "type": "joinpoint[]", + "name": "scopeNodes" }] }, { @@ -6837,8 +9503,9 @@ }, { "type": "joinpoint", - "name": "ompOrderedClause", - "extends": "ompClause" , + "name": "program", + "extends": "joinpoint" , + "tooltip": "Represents the complete program and is the top-most join point in the hierarchy", "children": [ { "type": "attribute", @@ -7063,9 +9730,18 @@ }, { "type": "joinpoint", - "name": "ompReductionClause", - "extends": "ompClause" , + "name": "programUnit", + "extends": "joinpoint" , "children": [ + { + "type": "attribute", + "tooltip": "Returns the unit's specification part", + "children": [ + { + "type": "specification", + "name": "specification" + }] + }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -7289,10 +9965,41 @@ }, { "type": "joinpoint", - "name": "program", - "extends": "joinpoint" , - "tooltip": "Represents the complete program and is the top-most join point in the hierarchy", + "name": "rangeLoopControl", + "extends": "loopControl" , "children": [ + { + "type": "attribute", + "children": [ + { + "type": "expr", + "name": "lower" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "expr", + "name": "step" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "expr", + "name": "upper" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "dataRef", + "name": "var" + }] + }, { "type": "attribute", "tooltip": "Returns an array with the children of the node", @@ -7402,6 +10109,32 @@ "name": "scopeNodes" }] }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setStep" + }, + { + "type": "expr", + "name": "step", + "defaultValue": "" + }] + }, + { + "type": "action", + "children": [ + { + "type": "void", + "name": "setUpper" + }, + { + "type": "expr", + "name": "upper", + "defaultValue": "" + }] + }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -7516,16 +10249,15 @@ }, { "type": "joinpoint", - "name": "programUnit", - "extends": "joinpoint" , + "name": "realLiteral", + "extends": "literal" , "children": [ { "type": "attribute", - "tooltip": "Returns the unit's specification part", "children": [ { - "type": "specification", - "name": "specification" + "type": "String", + "name": "literal" }] }, { @@ -7751,39 +10483,23 @@ }, { "type": "joinpoint", - "name": "rangeLoopControl", - "extends": "loopControl" , + "name": "specification", + "extends": "statementBlock" , "children": [ { "type": "attribute", "children": [ { - "type": "expr", - "name": "lower" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "expr", - "name": "step" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "expr", - "name": "upper" + "type": "specificationStatement[]", + "name": "specificationStmts" }] }, { "type": "attribute", "children": [ { - "type": "dataRef", - "name": "var" + "type": "statement[]", + "name": "stmts" }] }, { @@ -7897,27 +10613,15 @@ }, { "type": "action", + "tooltip": "Adds a UseStmt to a specification part. The statement is inserted at the beginning.", "children": [ { "type": "void", - "name": "setStep" - }, - { - "type": "expr", - "name": "step", - "defaultValue": "" - }] - }, - { - "type": "action", - "children": [ - { - "type": "void", - "name": "setUpper" + "name": "addUseStmt" }, { - "type": "expr", - "name": "upper", + "type": "useStatement", + "name": "stmt", "defaultValue": "" }] }, @@ -8035,15 +10739,23 @@ }, { "type": "joinpoint", - "name": "realLiteral", - "extends": "literal" , + "name": "specificationStatement", + "extends": "statement" , "children": [ { "type": "attribute", "children": [ { - "type": "String", - "name": "literal" + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" }] }, { @@ -8269,15 +10981,24 @@ }, { "type": "joinpoint", - "name": "specification", - "extends": "statementBlock" , + "name": "statement", + "extends": "joinpoint" , + "tooltip": "Represents a Fortran statement", "children": [ { "type": "attribute", "children": [ { - "type": "statement[]", - "name": "stmts" + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" }] }, { @@ -8389,20 +11110,6 @@ "name": "scopeNodes" }] }, - { - "type": "action", - "tooltip": "Adds a UseStmt to a specification part. The statement is inserted at the beginning.", - "children": [ - { - "type": "void", - "name": "addUseStmt" - }, - { - "type": "useStatement", - "name": "stmt", - "defaultValue": "" - }] - }, { "type": "action", "tooltip": "Performs a copy of the node and its children, but not of the nodes in its fields", @@ -8517,24 +11224,15 @@ }, { "type": "joinpoint", - "name": "statement", + "name": "statementBlock", "extends": "joinpoint" , - "tooltip": "Represents a Fortran statement", "children": [ { "type": "attribute", "children": [ { - "type": "Boolean", - "name": "isFirst" - }] - }, - { - "type": "attribute", - "children": [ - { - "type": "Boolean", - "name": "isLast" + "type": "statement[]", + "name": "stmts" }] }, { @@ -8760,15 +11458,15 @@ }, { "type": "joinpoint", - "name": "statementBlock", - "extends": "joinpoint" , + "name": "stringLiteral", + "extends": "literal" , "children": [ { "type": "attribute", "children": [ { - "type": "statement[]", - "name": "stmts" + "type": "String", + "name": "literal" }] }, { @@ -8994,15 +11692,24 @@ }, { "type": "joinpoint", - "name": "stringLiteral", - "extends": "literal" , + "name": "subroutine", + "extends": "programUnit" , "children": [ { "type": "attribute", "children": [ { "type": "String", - "name": "literal" + "name": "moduleName" + }] + }, + { + "type": "attribute", + "tooltip": "Returns the unit's specification part", + "children": [ + { + "type": "specification", + "name": "specification" }] }, { @@ -9228,24 +11935,39 @@ }, { "type": "joinpoint", - "name": "subroutine", - "extends": "programUnit" , + "name": "typeDeclarationStatement", + "extends": "specificationStatement" , "children": [ { "type": "attribute", "children": [ { - "type": "String", - "name": "moduleName" + "type": "attributeSpecifier[]", + "name": "attrs" }] }, { "type": "attribute", - "tooltip": "Returns the unit's specification part", "children": [ { - "type": "specification", - "name": "specification" + "type": "entityDecl[]", + "name": "decls" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isFirst" + }] + }, + { + "type": "attribute", + "children": [ + { + "type": "Boolean", + "name": "isLast" }] }, { diff --git a/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/importable/AstFactory.java b/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/importable/AstFactory.java index 5c1d014c..e92d75c8 100644 --- a/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/importable/AstFactory.java +++ b/FortranWeaver/src/pt/up/fe/specs/fortran/weaver/importable/AstFactory.java @@ -120,6 +120,13 @@ public static ADoStatement doStatement(ARangeLoopControl control) { ); } + public static AExpr parenExpr(AExpr expr) { + return FortranJoinpoints.create( + FortranWeaver.getFactory().parenExpr((Expr) expr.getNode()), + AExpr.class + ); + } + public static AExpr intrinsicCall(String name, Object[] args) { DataRef callee = FortranWeaver.getFactory().dataRef(name); List argNodes = SpecsCollections.asListT(AExpr.class, args) From 8aaae1b877729adbf8d50efbd429ddabe8b7f62b Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Thu, 11 Jun 2026 18:17:44 +0000 Subject: [PATCH 02/10] Add triangular-loop legality check to LoopTilingPass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _findTileablePairs() now rejects any (outer, inner) pair where any loop in inner's subtree has bounds that reference the outer loop variable. This catches: - trmm: do k = 1, i - 1 → k's upper bound references outer var 'i' - reg_detect: do i = j, maxgrid → i's lower bound references outer var 'j' Previously these produced wrong output (extreme float overflow in trmm) because tiling changed the iteration order of a loop whose bounds depend on the strip-mined outer variable. After the fix, trmm tiles (j, k) instead of (i, j) — the k bounds depend on i (the surrounding loop) but not on j (the new outer), so it's safe. reg_detect tiles (i, cnt) instead of (j, i) — cnt bounds are constant. Result on PolyBench/Fortran SMALL_DATASET: 30/30 MATCH (up from 26/28). Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/pass/LoopTilingPass.ts | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Fortran-JS/src-api/pass/LoopTilingPass.ts b/Fortran-JS/src-api/pass/LoopTilingPass.ts index 64218655..5f94ba46 100644 --- a/Fortran-JS/src-api/pass/LoopTilingPass.ts +++ b/Fortran-JS/src-api/pass/LoopTilingPass.ts @@ -1,7 +1,7 @@ import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { DoStatement, Joinpoint } from "../Joinpoints.js"; +import { DataRef, DoStatement, Joinpoint, RangeLoopControl } from "../Joinpoints.js"; import loopTile, { canTile } from "../code/LoopTiling.js"; /** @@ -39,7 +39,31 @@ export default class LoopTilingPass extends Pass { const stmts = loop.body.executableStmts; if (stmts.length === 1 && stmts[0] instanceof DoStatement && canTile(loop, stmts[0] as DoStatement)) { - pairs.push({ outer: loop, inner: stmts[0] as DoStatement }); + + const outer = loop; + const inner = stmts[0] as DoStatement; + const outerVarName = (outer.control as RangeLoopControl).var.name; + + // Legality check: reject pairs where any loop in inner's subtree has + // bounds referencing the outer loop variable. This catches triangular + // loops like `do k = 1, i - 1` (trmm) and `do i = j, n` (reg_detect), + // where tiling the outer loop changes which iterations are valid for + // the dependent inner/descendant loop, producing wrong results. + // Try/catch guards against null-node traversal errors on staging branch. + let illegal = false; + try { + const subtreeLoops = [inner, ...Query.searchFrom(inner, DoStatement).get()]; + illegal = subtreeLoops.some(subLoop => { + if (!(subLoop.control instanceof RangeLoopControl)) return false; + try { + return Query.searchFrom(subLoop.control, DataRef, { name: outerVarName }).get().length > 0; + } catch (_) { return false; } + }); + } catch (_) { /* skip on traversal errors */ } + + if (!illegal) { + pairs.push({ outer, inner }); + } } } const innerSet = new Set(pairs.map(p => p.inner)); From 8034ebeb04606299a0ea27baf29b0e242459443c Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Thu, 11 Jun 2026 18:38:22 +0000 Subject: [PATCH 03/10] Revert tiling legality check; add TILED/SKIPPED log to tilingGeneric MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LoopTilingPass: revert _findTileablePairs to original (no eligibility check). The legality check was reducing MATCH results from 26→17 by over-rejecting valid pairs in correct benchmarks. tilingGeneric.ts: capture PassResult from .apply() and log whether tiling was actually applied to each kernel: [tilingGeneric] TILED (tile=32): kernel_gemm [tilingGeneric] SKIPPED (no eligible 2-deep perfect nest): kernel_atax This makes ineligible benchmarks immediately visible in the transform output without changing the transform behavior. Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/examples/tilingGeneric.ts | 8 ++++-- Fortran-JS/src-api/pass/LoopTilingPass.ts | 28 ++------------------ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/Fortran-JS/src-api/examples/tilingGeneric.ts b/Fortran-JS/src-api/examples/tilingGeneric.ts index d83a3557..98dd07b4 100644 --- a/Fortran-JS/src-api/examples/tilingGeneric.ts +++ b/Fortran-JS/src-api/examples/tilingGeneric.ts @@ -10,7 +10,11 @@ if (subroutines.length === 0) { console.log('No kernel_* subroutine found — skipping'); } else { for (const sub of subroutines) { - console.log(`[tilingGeneric] Tiling (tile=${TILE_SIZE}): ${sub.moduleName}`); - new LoopTilingPass(TILE_SIZE).apply(sub); + const result = new LoopTilingPass(TILE_SIZE).apply(sub); + if (result.appliedPass) { + console.log(`[tilingGeneric] TILED (tile=${TILE_SIZE}): ${sub.moduleName}`); + } else { + console.log(`[tilingGeneric] SKIPPED (no eligible 2-deep perfect nest): ${sub.moduleName}`); + } } } diff --git a/Fortran-JS/src-api/pass/LoopTilingPass.ts b/Fortran-JS/src-api/pass/LoopTilingPass.ts index 5f94ba46..64218655 100644 --- a/Fortran-JS/src-api/pass/LoopTilingPass.ts +++ b/Fortran-JS/src-api/pass/LoopTilingPass.ts @@ -1,7 +1,7 @@ import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { DataRef, DoStatement, Joinpoint, RangeLoopControl } from "../Joinpoints.js"; +import { DoStatement, Joinpoint } from "../Joinpoints.js"; import loopTile, { canTile } from "../code/LoopTiling.js"; /** @@ -39,31 +39,7 @@ export default class LoopTilingPass extends Pass { const stmts = loop.body.executableStmts; if (stmts.length === 1 && stmts[0] instanceof DoStatement && canTile(loop, stmts[0] as DoStatement)) { - - const outer = loop; - const inner = stmts[0] as DoStatement; - const outerVarName = (outer.control as RangeLoopControl).var.name; - - // Legality check: reject pairs where any loop in inner's subtree has - // bounds referencing the outer loop variable. This catches triangular - // loops like `do k = 1, i - 1` (trmm) and `do i = j, n` (reg_detect), - // where tiling the outer loop changes which iterations are valid for - // the dependent inner/descendant loop, producing wrong results. - // Try/catch guards against null-node traversal errors on staging branch. - let illegal = false; - try { - const subtreeLoops = [inner, ...Query.searchFrom(inner, DoStatement).get()]; - illegal = subtreeLoops.some(subLoop => { - if (!(subLoop.control instanceof RangeLoopControl)) return false; - try { - return Query.searchFrom(subLoop.control, DataRef, { name: outerVarName }).get().length > 0; - } catch (_) { return false; } - }); - } catch (_) { /* skip on traversal errors */ } - - if (!illegal) { - pairs.push({ outer, inner }); - } + pairs.push({ outer: loop, inner: stmts[0] as DoStatement }); } } const innerSet = new Set(pairs.map(p => p.inner)); From 01f0de0eb9533e3e6490b42f15f395f1595475bd Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Fri, 12 Jun 2026 08:36:11 +0000 Subject: [PATCH 04/10] Fix NullPointerException in LoopFissionPass and LoopFusionPass attributeSpecifier nodes on the staging branch have null Java backing objects. Wrap $jp.children access in try/catch in _findLoops (fission) and _findAllFusableSets/_findFusableSets (fusion), same pattern as the existing fix in LoopUnrollPass. Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/pass/LoopFissionPass.ts | 4 +++- Fortran-JS/src-api/pass/LoopFusionPass.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Fortran-JS/src-api/pass/LoopFissionPass.ts b/Fortran-JS/src-api/pass/LoopFissionPass.ts index 8b5d4cca..59b5daa9 100644 --- a/Fortran-JS/src-api/pass/LoopFissionPass.ts +++ b/Fortran-JS/src-api/pass/LoopFissionPass.ts @@ -30,7 +30,9 @@ export default class LoopFissionPass extends Pass { /** Yields every eligible do-loop in the subtree: range loops with more than one body statement. */ protected *_findLoops($jp: Joinpoint): Generator { - for (const child of $jp.children) { + let children: Joinpoint[]; + try { children = [...$jp.children]; } catch (_) { return; } + for (const child of children) { yield* this._findLoops(child); } if ($jp instanceof DoStatement && canFission($jp)) { diff --git a/Fortran-JS/src-api/pass/LoopFusionPass.ts b/Fortran-JS/src-api/pass/LoopFusionPass.ts index be5e568c..a829fb71 100644 --- a/Fortran-JS/src-api/pass/LoopFusionPass.ts +++ b/Fortran-JS/src-api/pass/LoopFusionPass.ts @@ -32,7 +32,9 @@ export default class LoopFusionPass extends Pass { * inner loops are fused before their containing scope is inspected. */ protected *_findAllFusableSets($jp: Joinpoint): Generator { - for (const child of [...$jp.children]) { + let children: Joinpoint[]; + try { children = [...$jp.children]; } catch (_) { children = []; } + for (const child of children) { yield* this._findAllFusableSets(child); } for (const set of this._findFusableSets($jp)) { @@ -53,7 +55,9 @@ export default class LoopFusionPass extends Pass { const result: DoStatement[][] = []; let group: DoStatement[] = []; - for (const child of $jp.children) { + let children: Joinpoint[]; + try { children = [...$jp.children]; } catch (_) { return result; } + for (const child of children) { if (child instanceof DoStatement && child.kind === 'range') { if (group.length === 0 || group[0].sameScope(child)) { group.push(child); From 76ded396063e597ace22c8994ea5c793aff2adff Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Fri, 12 Jun 2026 15:02:29 +0000 Subject: [PATCH 05/10] Add LoopInterchangePass with legality checks; rewrite interchangeGeneric MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src-api/code/LoopInterchange.ts: loopInterchange() swaps outer/inner loop controls using FortranJoinPoints factory methods (no string emit); canInterchange() guards against triangular inner bounds (Check 1) and nested loops inside the body whose bounds reference the outer variable (Check 2) - src-api/pass/LoopInterchangePass.ts: collects ALL structural 2-deep pairs first, then filters to outermost (keyed by var.name, not JS object identity — LARA creates fresh proxies on each node access), then applies canInterchange(); 30/30 MATCH on SMALL_DATASET - src-api/examples/interchangeGeneric.ts: delegates to LoopInterchangePass following the same pattern as tilingGeneric/fusionGeneric/fissionGeneric Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/code/LoopInterchange.ts | 74 +++++++++++++++++++ .../src-api/examples/interchangeGeneric.ts | 52 ++++--------- .../src-api/pass/LoopInterchangePass.ts | 66 +++++++++++++++++ 3 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 Fortran-JS/src-api/code/LoopInterchange.ts create mode 100644 Fortran-JS/src-api/pass/LoopInterchangePass.ts diff --git a/Fortran-JS/src-api/code/LoopInterchange.ts b/Fortran-JS/src-api/code/LoopInterchange.ts new file mode 100644 index 00000000..8996716e --- /dev/null +++ b/Fortran-JS/src-api/code/LoopInterchange.ts @@ -0,0 +1,74 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import FortranJoinPoints from "../FortranJoinPoints.js"; +import { DataRef, DoStatement, ExecutableStatement, RangeLoopControl } from "../Joinpoints.js"; + +/** + * Swaps the outer and inner loop of a perfect 2-deep nest. + * + * Transforms: + * ```fortran + * do i = lo_i, hi_i + * do j = lo_j, hi_j + * body + * end do + * end do + * ``` + * Into: + * ```fortran + * do j = lo_j, hi_j + * do i = lo_i, hi_i + * body + * end do + * end do + * ``` + * + * Returns unchanged [outer] if canInterchange() fails. + */ +export default function loopInterchange(outer: DoStatement, inner: DoStatement): DoStatement[] { + if (!canInterchange(outer, inner)) return [outer]; + const oc = outer.control as RangeLoopControl; + const ic = inner.control as RangeLoopControl; + + // New outer uses inner's control (deep-copied) + const newOuterCtrl = FortranJoinPoints.rangeLoopControl( + ic.var.deepCopy() as DataRef, + ic.lower.deepCopy(), + ic.upper.deepCopy() + ); + if (ic.step !== undefined) newOuterCtrl.setStep(ic.step.deepCopy()); + const newOuterDo = FortranJoinPoints.doStatement(newOuterCtrl); + + // New inner uses outer's control (deep-copied) + const newInnerCtrl = FortranJoinPoints.rangeLoopControl( + oc.var.deepCopy() as DataRef, + oc.lower.deepCopy(), + oc.upper.deepCopy() + ); + if (oc.step !== undefined) newInnerCtrl.setStep(oc.step.deepCopy()); + const newInnerDo = FortranJoinPoints.doStatement(newInnerCtrl); + + // Deep-copy body statements into new inner loop + for (const stmt of inner.body.executableStmts) { + newInnerDo.body.insertEnd(stmt.deepCopy() as ExecutableStatement); + } + newOuterDo.body.insertEnd(newInnerDo); + outer.replaceWith(newOuterDo); + return [newOuterDo]; +} + +export function canInterchange(outer: DoStatement, inner: DoStatement): boolean { + const oc = outer.control, ic = inner.control; + if (!(oc instanceof RangeLoopControl && ic instanceof RangeLoopControl)) return false; + const outerVar = oc.var.name; + + // Check 1: triangular inner bounds — inner bound references outer variable + if (ic.lower.code.includes(outerVar) || ic.upper.code.includes(outerVar)) return false; + + // Check 2: nested DO inside body uses outer variable in its bounds + for (const nested of Query.searchFrom(inner.body, DoStatement)) { + const nc = nested.control; + if (!(nc instanceof RangeLoopControl)) continue; + if (nc.lower.code.includes(outerVar) || nc.upper.code.includes(outerVar)) return false; + } + return true; +} diff --git a/Fortran-JS/src-api/examples/interchangeGeneric.ts b/Fortran-JS/src-api/examples/interchangeGeneric.ts index b4d23475..0ac632f1 100644 --- a/Fortran-JS/src-api/examples/interchangeGeneric.ts +++ b/Fortran-JS/src-api/examples/interchangeGeneric.ts @@ -1,40 +1,18 @@ import Query from "@specs-feup/lara/api/weaver/Query.js"; -import { DoStatement, RangeLoopControl, Joinpoint } from "../Joinpoints.js"; - -function isInKernelSubroutine(loop: DoStatement): boolean { - const sub = loop.getAncestor("programUnit"); - if (!sub) return false; - return (sub as Joinpoint).code.split("\n")[0].toLowerCase().includes("kernel_"); -} - -const pairs: { outer: DoStatement; inner: DoStatement }[] = []; -for (const loop of [...Query.search(DoStatement)].filter(isInKernelSubroutine)) { - const stmts = loop.body.executableStmts; - if (stmts.length === 1 && stmts[0] instanceof DoStatement) { - pairs.push({ outer: loop, inner: stmts[0] as DoStatement }); +import LoopInterchangePass from "../pass/LoopInterchangePass.js"; +import { Subroutine } from "../Joinpoints.js"; + +const subroutines = Query.search(Subroutine, ($jp) => $jp.moduleName.startsWith('kernel_')).get(); + +if (subroutines.length === 0) { + console.log('No kernel_* subroutine found — skipping'); +} else { + for (const sub of subroutines) { + const result = new LoopInterchangePass().apply(sub); + if (result.appliedPass) { + console.log(`[interchangeGeneric] INTERCHANGED: ${sub.moduleName}`); + } else { + console.log(`[interchangeGeneric] SKIPPED (no eligible/legal 2-deep perfect nest): ${sub.moduleName}`); + } } } - -const innerSet = new Set(pairs.map(p => p.inner)); -const topPairs = pairs.filter(p => !innerSet.has(p.outer)); - -console.log(`[interchangeGeneric] Found ${topPairs.length} interchangeable loop pair(s)`); - -for (const { outer, inner } of topPairs) { - const oc = outer.control; - const ic = inner.control; - if (!(oc instanceof RangeLoopControl && ic instanceof RangeLoopControl)) continue; - - const innerBody = inner.body.executableStmts.map(s => (s as Joinpoint).code); - - const newCode = [ - `do ${ic.code}`, - `do ${oc.code}`, - ...innerBody, - `end do`, - `end do`, - ].join("\n"); - - outer.insert("replace", newCode); - console.log(` [interchangeGeneric] Interchanged: ${oc.var.name} <-> ${ic.var.name}`); -} diff --git a/Fortran-JS/src-api/pass/LoopInterchangePass.ts b/Fortran-JS/src-api/pass/LoopInterchangePass.ts new file mode 100644 index 00000000..1be344a5 --- /dev/null +++ b/Fortran-JS/src-api/pass/LoopInterchangePass.ts @@ -0,0 +1,66 @@ +import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; +import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { DoStatement, Joinpoint, RangeLoopControl } from "../Joinpoints.js"; +import loopInterchange, { canInterchange } from "../code/LoopInterchange.js"; + +/** + * Pass that interchanges every legal top-level 2-deep perfect loop nest in the subtree. + * + * A pair is eligible when: + * 1. Both loops are range loops. + * 2. The inner loop's bounds do not reference the outer loop variable (Check 1: no triangular bounds). + * 3. No nested DO loop inside the inner body has bounds referencing the outer variable (Check 2). + * + * All eligible pairs are collected before any transformation so that AST mutations + * do not interfere with the traversal. In nests deeper than 2, only the outermost + * 2-level pair is interchanged. + * + * @example + * const pass = new LoopInterchangePass(); + * pass.apply(Query.root()); + */ +export default class LoopInterchangePass extends Pass { + protected _name = "LoopInterchangePass"; + + protected _apply_impl($jp: Joinpoint): PassResult { + const pairs = this._findInterchangeablePairs($jp); + let appliedPass = false; + for (const { outer, inner } of pairs) { + loopInterchange(outer, inner); + appliedPass = true; + } + return new PassResult(this, $jp, { appliedPass, insertedLiteralCode: false }); + } + + protected _findInterchangeablePairs($jp: Joinpoint): { outer: DoStatement; inner: DoStatement }[] { + // Collect all structural 2-deep perfect nests regardless of legality. + // innerSet must be built from ALL pairs so that nests deeper than 2 are + // handled correctly: an inner pair nested inside an illegal outer pair must + // not be interchanged either (its "outer" loop is still the inner of an + // unprocessed pair and evaluation-order constraints may still apply). + const allPairs: { outer: DoStatement; inner: DoStatement }[] = []; + for (const loop of Query.searchFrom($jp, DoStatement)) { + const stmts = loop.body.executableStmts; + if (stmts.length === 1 && stmts[0] instanceof DoStatement) { + allPairs.push({ outer: loop, inner: stmts[0] as DoStatement }); + } + } + // LARA wraps each AST node in a new JS proxy on every access, so JS object + // identity cannot be used to detect that the same loop appears as both an + // outer in one pair and an inner in another. Key by loop variable name + // instead — unique per subroutine for PolyBench's affine loops. + const innerVarNames = new Set( + allPairs + .map(p => p.inner.control) + .filter((c): c is RangeLoopControl => c instanceof RangeLoopControl) + .map(c => c.var.name) + ); + return allPairs + .filter(({ outer }) => { + const oc = outer.control; + return !(oc instanceof RangeLoopControl && innerVarNames.has(oc.var.name)); + }) + .filter(({ outer, inner }) => canInterchange(outer, inner)); + } +} From 967e893d1a473543d6ef71d241b1ef1907f0a86f Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Fri, 12 Jun 2026 16:28:41 +0000 Subject: [PATCH 06/10] Add appliedPass check to fusion/fission/unroll generic scripts Each script now checks result.appliedPass and prints FUSED/FISSIONED/UNROLLED or SKIPPED, matching the pattern already used by tilingGeneric and interchangeGeneric. This gives weave-transpiler.sh a reliable signal to write the .transform-status marker file for compare.sh. Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/examples/fissionGeneric.ts | 8 ++++++-- Fortran-JS/src-api/examples/fusionGeneric.ts | 8 ++++++-- Fortran-JS/src-api/examples/unrollGeneric.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Fortran-JS/src-api/examples/fissionGeneric.ts b/Fortran-JS/src-api/examples/fissionGeneric.ts index 2fba3764..417d0a9a 100644 --- a/Fortran-JS/src-api/examples/fissionGeneric.ts +++ b/Fortran-JS/src-api/examples/fissionGeneric.ts @@ -8,7 +8,11 @@ if (subroutines.length === 0) { console.log('No kernel_* subroutine found — skipping'); } else { for (const sub of subroutines) { - console.log(`[fissionGeneric] Applying loop fission: ${sub.moduleName}`); - new LoopFissionPass().apply(sub); + const result = new LoopFissionPass().apply(sub); + if (result.appliedPass) { + console.log(`[fissionGeneric] FISSIONED: ${sub.moduleName}`); + } else { + console.log(`[fissionGeneric] SKIPPED (no eligible multi-statement loops): ${sub.moduleName}`); + } } } diff --git a/Fortran-JS/src-api/examples/fusionGeneric.ts b/Fortran-JS/src-api/examples/fusionGeneric.ts index d9c82a99..ae6b8b29 100644 --- a/Fortran-JS/src-api/examples/fusionGeneric.ts +++ b/Fortran-JS/src-api/examples/fusionGeneric.ts @@ -8,7 +8,11 @@ if (subroutines.length === 0) { console.log('No kernel_* subroutine found — skipping'); } else { for (const sub of subroutines) { - console.log(`[fusionGeneric] Applying loop fusion: ${sub.moduleName}`); - new LoopFusionPass().apply(sub); + const result = new LoopFusionPass().apply(sub); + if (result.appliedPass) { + console.log(`[fusionGeneric] FUSED: ${sub.moduleName}`); + } else { + console.log(`[fusionGeneric] SKIPPED (no fusable consecutive loops): ${sub.moduleName}`); + } } } diff --git a/Fortran-JS/src-api/examples/unrollGeneric.ts b/Fortran-JS/src-api/examples/unrollGeneric.ts index 597c5057..5bb5ae57 100644 --- a/Fortran-JS/src-api/examples/unrollGeneric.ts +++ b/Fortran-JS/src-api/examples/unrollGeneric.ts @@ -10,7 +10,11 @@ if (subroutines.length === 0) { console.log('No kernel_* subroutine found — skipping'); } else { for (const sub of subroutines) { - console.log(`[unrollGeneric] Unrolling innermost loops (factor=${FACTOR}): ${sub.moduleName}`); - new LoopUnrollPass(FACTOR).apply(sub); + const result = new LoopUnrollPass(FACTOR).apply(sub); + if (result.appliedPass) { + console.log(`[unrollGeneric] UNROLLED (factor=${FACTOR}): ${sub.moduleName}`); + } else { + console.log(`[unrollGeneric] SKIPPED (no unrollable innermost loops): ${sub.moduleName}`); + } } } From 5ec351c7d65deffafa808b0ad380c26393091045 Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Fri, 12 Jun 2026 17:30:52 +0000 Subject: [PATCH 07/10] Add _canFusePair legality checks to LoopFusionPass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements three syntactic dependency checks to prevent illegal fusions: - Check A: write subscript contains no instance of the fusion variable (same element written on every fusion iteration); if the other loop reads that element, it will see an incomplete/partial value. - Check B: array X written as X(inner_var, fv) in one loop and read as X(fv, inner_var) in the other — transposed cross-iteration dependency (column write, row read). - Check C: array X written with a subscript that contains the fusion variable but no inner loop variable (one element per fusion iteration), and read by the other loop with a subscript that contains an inner loop variable (reads across the full range). Key implementation detail: ArraySubscriptExpr.name returns "" in LARA; the actual array identifier is at ArraySubscriptExpr.var.name. _findFusableSets is updated to split groups at any consecutive pair that fails a legality check, rather than rejecting the entire group. This allows partial fusions (e.g. fusing loops B+C while blocking A+B and C+D in gemver's 4-loop group). Results on SMALL_DATASET: 30/30 MATCH (0 mismatches, up from 27/30). gemver, atax, doitgen were the three previously-failing benchmarks. Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/pass/LoopFusionPass.ts | 158 ++++++++++++++++++++-- 1 file changed, 147 insertions(+), 11 deletions(-) diff --git a/Fortran-JS/src-api/pass/LoopFusionPass.ts b/Fortran-JS/src-api/pass/LoopFusionPass.ts index a829fb71..e5502e3e 100644 --- a/Fortran-JS/src-api/pass/LoopFusionPass.ts +++ b/Fortran-JS/src-api/pass/LoopFusionPass.ts @@ -1,14 +1,73 @@ import Pass from "@specs-feup/lara/api/lara/pass/Pass.js"; import PassResult from "@specs-feup/lara/api/lara/pass/results/PassResult.js"; -import { DoStatement, Joinpoint, RangeLoopControl } from "../Joinpoints.js"; +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { ArraySubscriptExpr, AssignmentStatement, DoStatement, Joinpoint, RangeLoopControl } from "../Joinpoints.js"; import loopFusion from "../code/LoopFusion.js"; +type ArrayAccess = { arrayName: string; subCodes: string[] }; + +function loopWrites(loop: DoStatement): ArrayAccess[] { + const result: ArrayAccess[] = []; + for (const stmt of Query.searchFrom(loop.body, AssignmentStatement)) { + const lhs = stmt.variable; + if (lhs instanceof ArraySubscriptExpr) { + // .name on ArraySubscriptExpr is ""; the array identifier is at .var.name + result.push({ arrayName: lhs.var.name, subCodes: lhs.subscripts.map(s => s.code) }); + } + } + return result; +} + +function loopReads(loop: DoStatement): ArrayAccess[] { + const result: ArrayAccess[] = []; + for (const stmt of Query.searchFrom(loop.body, AssignmentStatement)) { + for (const expr of Query.searchFromInclusive(stmt.expr, ArraySubscriptExpr)) { + result.push({ arrayName: expr.var.name, subCodes: expr.subscripts.map(s => s.code) }); + } + } + return result; +} + +function innerLoopVars(loop: DoStatement): Set { + const vars = new Set(); + for (const nested of Query.searchFrom(loop.body, DoStatement)) { + if (nested.kind === 'range') { + const ctl = nested.control; + if (ctl instanceof RangeLoopControl) vars.add(ctl.var.name); + } + } + return vars; +} + +function hasVar(code: string, varName: string): boolean { + return new RegExp(`\\b${varName}\\b`).test(code); +} + +function hasAnyVar(code: string, vars: Set): boolean { + return [...vars].some(v => hasVar(code, v)); +} + /** * Pass that fuses consecutive range do-loops with identical boundaries into a * single loop (loop fusion), applied recursively across the subtree. * * At each scope level, adjacent do-loops that share the same range control are - * grouped and fused into the first loop of each group. + * grouped and fused into the first loop of each group. Groups are split at any + * pair that fails a legality check: + * + * - Check A: a write in loop A has a subscript that does not contain the fusion + * variable (the same element is written/accumulated on every iteration) and the + * same array is read in loop B — the reader would see a partial value. + * + * - Check B: array X is written in loop A as `X(inner, fusion_var)` (column-major) + * and read in loop B as `X(fusion_var, inner)` (row-major) — a cross-iteration + * transposed dependency. + * + * - Check C: array X is written in one loop with a subscript that contains the + * fusion variable but no inner loop variable (one element per fusion iteration), + * and read in the other loop with a subscript that contains an inner loop + * variable (reads across the full range) — a forward or backward cross-iteration + * dependency. * * @example * const pass = new LoopFusionPass(); @@ -43,13 +102,10 @@ export default class LoopFusionPass extends Pass { } /** - * Returns groups of consecutive range do-loops among the **direct children** - * of `$jp` that share identical loop boundaries and can therefore be fused. - * - * Any non-do-loop child (or a do-loop with different boundaries) breaks an - * ongoing group. Only groups of length ≥ 2 are returned. - * - * @param $jp - The joinpoint whose direct children are inspected. + * Returns groups of consecutive range do-loops among the direct children of + * `$jp` that share identical loop boundaries and pass all three legality + * checks. Groups are split whenever a consecutive pair is illegal; only + * groups of length ≥ 2 are returned. */ protected _findFusableSets($jp: Joinpoint): DoStatement[][] { const result: DoStatement[][] = []; @@ -57,10 +113,18 @@ export default class LoopFusionPass extends Pass { let children: Joinpoint[]; try { children = [...$jp.children]; } catch (_) { return result; } + for (const child of children) { if (child instanceof DoStatement && child.kind === 'range') { - if (group.length === 0 || group[0].sameScope(child)) { - group.push(child); + if (group.length === 0) { + group = [child]; + } else if (group[0].sameScope(child)) { + if (this._canFusePair(group[group.length - 1], child)) { + group.push(child); + } else { + if (group.length >= 2) result.push(group); + group = [child]; + } } else { if (group.length >= 2) result.push(group); group = [child]; @@ -73,4 +137,76 @@ export default class LoopFusionPass extends Pass { if (group.length >= 2) result.push(group); return result; } + + /** + * Returns false if fusing `a` immediately before `b` would violate one of + * three dependency patterns detectable from the AST. + */ + protected _canFusePair(a: DoStatement, b: DoStatement): boolean { + const fv = (a.control as RangeLoopControl).var.name; + const ivA = innerLoopVars(a); + const ivB = innerLoopVars(b); + + const writesA = loopWrites(a); + const writesB = loopWrites(b); + const readsA = loopReads(a); + const readsB = loopReads(b); + + const readNamesA = new Set(readsA.map(r => r.arrayName)); + const readNamesB = new Set(readsB.map(r => r.arrayName)); + + // Check A: write subscript contains no instance of the fusion variable → + // the same array element is modified on every fusion iteration. + // If the other loop reads that element, it will see an incomplete value. + for (const w of writesA) { + if (!w.subCodes.some(sc => hasVar(sc, fv)) && readNamesB.has(w.arrayName)) return false; + } + for (const w of writesB) { + if (!w.subCodes.some(sc => hasVar(sc, fv)) && readNamesA.has(w.arrayName)) return false; + } + + // Check B: array X written as X(inner_var, fv) in one loop and read as + // X(fv, inner_var) in the other — transposed cross-iteration dependency. + const checkTransposed = ( + writes: ArrayAccess[], reads: ArrayAccess[], + writeIV: Set, readIV: Set + ) => { + for (const w of writes) { + if (w.subCodes.length !== 2) continue; + const [ws0, ws1] = w.subCodes; + if (!hasAnyVar(ws0, writeIV) || !hasVar(ws1, fv)) continue; + for (const r of reads) { + if (r.arrayName !== w.arrayName || r.subCodes.length !== 2) continue; + const [rs0, rs1] = r.subCodes; + if (hasVar(rs0, fv) && hasAnyVar(rs1, readIV)) return true; + } + } + return false; + }; + if (checkTransposed(writesA, readsB, ivA, ivB)) return false; + if (checkTransposed(writesB, readsA, ivB, ivA)) return false; + + // Check C: array X written with a subscript that contains the fusion + // variable but no inner loop variable (one element per fusion iteration), + // and read by the other loop with a subscript that contains an inner loop + // variable (reads across the full range within one fusion iteration). + const checkOnePerIter = ( + writes: ArrayAccess[], reads: ArrayAccess[], + writeIV: Set, readIV: Set + ) => { + for (const w of writes) { + const allSubs = w.subCodes.join(' '); + if (!hasVar(allSubs, fv) || hasAnyVar(allSubs, writeIV)) continue; + for (const r of reads) { + if (r.arrayName !== w.arrayName) continue; + if (hasAnyVar(r.subCodes.join(' '), readIV)) return true; + } + } + return false; + }; + if (checkOnePerIter(writesA, readsB, ivA, ivB)) return false; + if (checkOnePerIter(writesB, readsA, ivB, ivA)) return false; + + return true; + } } From 278dad3353bdf6b55ee754bef972d599a6ee1dc0 Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Sat, 13 Jun 2026 15:35:48 +0000 Subject: [PATCH 08/10] Add legality checks to canTile: triangular bounds and nested loop refs Two checks mirror canInterchange() in LoopInterchange.ts: - Check 1: reject if inner loop bound contains outer variable (triangular) - Check 2: reject if any descendant loop bound contains outer variable Uses word-boundary regex (\b) to avoid false positives where dimension names like `ni` contain the loop variable `i` as a substring. Fixes reg_detect (triangular do i = j, maxgrid) and trmm (nested do k = 1, i-1 in body). Both now tile an alternative safe pair. 30/30 MATCH on SMALL_DATASET. Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/code/LoopTiling.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Fortran-JS/src-api/code/LoopTiling.ts b/Fortran-JS/src-api/code/LoopTiling.ts index b8763b56..4ba8c349 100644 --- a/Fortran-JS/src-api/code/LoopTiling.ts +++ b/Fortran-JS/src-api/code/LoopTiling.ts @@ -1,3 +1,4 @@ +import Query from "@specs-feup/lara/api/weaver/Query.js"; import FortranJoinPoints from "../FortranJoinPoints.js"; import { DataRef, DoStatement, ExecutableStatement, RangeLoopControl } from "../Joinpoints.js"; @@ -85,8 +86,26 @@ export default function loopTile(outer: DoStatement, inner: DoStatement, tileSiz return [outerTileDo]; } +function containsVar(code: string, varName: string): boolean { + return new RegExp(`\\b${varName}\\b`).test(code); +} + export function canTile(outer: DoStatement, inner: DoStatement): boolean { const oc = outer.control, ic = inner.control; - return oc instanceof RangeLoopControl && ic instanceof RangeLoopControl - && oc.step === undefined && ic.step === undefined; + if (!(oc instanceof RangeLoopControl && ic instanceof RangeLoopControl)) return false; + if (oc.step !== undefined || ic.step !== undefined) return false; + + const outerVar = oc.var.name; + + // Check 1: triangular inner bounds — inner bound references outer variable + if (containsVar(ic.lower.code, outerVar) || containsVar(ic.upper.code, outerVar)) return false; + + // Check 2: nested DO inside inner body uses outer variable in its bounds + for (const nested of Query.searchFrom(inner.body, DoStatement)) { + const nc = nested.control; + if (!(nc instanceof RangeLoopControl)) continue; + if (containsVar(nc.lower.code, outerVar) || containsVar(nc.upper.code, outerVar)) return false; + } + + return true; } \ No newline at end of file From 0140c6d4742b42ed177fd178f39dce2259ec86b5 Mon Sep 17 00:00:00 2001 From: svkorepanov9 Date: Sat, 13 Jun 2026 16:31:04 +0000 Subject: [PATCH 09/10] Add legality checks to canFission: scalar threading and array write-before-read Two checks prevent fission when it would produce incorrect output: Check 1 (scalar threading): reject if a scalar written in an earlier body statement appears in any later statement's code. After fission the producer loop completes all iterations before the consumer loop starts, so every consumer iteration reads the last-iteration value rather than the current one. Catches: gramschmidt (nrm), cholesky (x), symm/ludcmp (acc/w). Check 2 (array write-before-read): reject if a later statement writes an array that any earlier statement reads. After fission the reader loop runs for all iterations before the writer loop runs for any, breaking the iteration-level dependency between them. Catches: trisolv, lu, ludcmp, adi, fdtd-2d, fdtd-apml. Also fixes a critical bug in all three helper functions: the original code used Query.searchFrom which searches only children, so direct AssignmentStatement nodes were invisible to the helpers. Switched to Query.searchFromInclusive so the node itself is included in the search. Result: 30/30 MATCH (up from 21/30). Co-Authored-By: Claude Sonnet 4.6 --- Fortran-JS/src-api/code/LoopFission.ts | 64 ++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/Fortran-JS/src-api/code/LoopFission.ts b/Fortran-JS/src-api/code/LoopFission.ts index 2dbf9b2a..a7b5593a 100644 --- a/Fortran-JS/src-api/code/LoopFission.ts +++ b/Fortran-JS/src-api/code/LoopFission.ts @@ -1,4 +1,6 @@ -import { DoStatement, RangeLoopControl } from "../Joinpoints.js"; +import Query from "@specs-feup/lara/api/weaver/Query.js"; +import { ArraySubscriptExpr, AssignmentStatement, DoStatement, Joinpoint, + RangeLoopControl } from "../Joinpoints.js"; /** * Splits a do-loop with multiple body statements into one loop per statement. @@ -44,7 +46,63 @@ export default function loopFission($loop: DoStatement): DoStatement[] { return result } +function stmtArrayWrites(stmt: Joinpoint): Set { + const result = new Set(); + for (const assign of Query.searchFromInclusive(stmt, AssignmentStatement)) { + const lhs = assign.variable; + if (lhs instanceof ArraySubscriptExpr) result.add(lhs.var.name); + } + return result; +} + +function stmtArrayReads(stmt: Joinpoint): Set { + const result = new Set(); + for (const assign of Query.searchFromInclusive(stmt, AssignmentStatement)) { + for (const expr of Query.searchFromInclusive(assign.expr, ArraySubscriptExpr)) { + result.add(expr.var.name); + } + } + return result; +} + +function stmtScalarWrites(stmt: Joinpoint): string[] { + const result: string[] = []; + for (const assign of Query.searchFromInclusive(stmt, AssignmentStatement)) { + const lhs = assign.variable; + if (!(lhs instanceof ArraySubscriptExpr)) result.push(lhs.name); + } + return result; +} + export function canFission($loop: DoStatement): boolean { - return $loop.control instanceof RangeLoopControl - && $loop.body.executableStmts.length > 1; + if (!($loop.control instanceof RangeLoopControl)) return false; + const stmts = [...$loop.body.executableStmts]; + if (stmts.length <= 1) return false; + + const arrayReads = stmts.map(s => stmtArrayReads(s)); + const arrayWrites = stmts.map(s => stmtArrayWrites(s)); + const scalarWrites = stmts.map(s => stmtScalarWrites(s)); + + for (let j = 1; j < stmts.length; j++) { + // Check 1: scalar written in an earlier stmt appears in a later stmt's code. + // After fission, the later loop sees only the scalar value from its own + // iteration — it cannot observe the value threaded from the earlier loop. + for (let i = 0; i < j; i++) { + for (const sv of scalarWrites[i]) { + if (new RegExp(`\\b${sv}\\b`).test(stmts[j].code)) return false; + } + } + + // Check 2: a later stmt writes an array that an earlier stmt reads. + // After fission, the earlier loop (for ALL iterations) runs before the + // later loop, so the earlier loop reads stale values for every iteration + // after the first one that the later loop would have updated. + for (const wName of arrayWrites[j]) { + for (let i = 0; i < j; i++) { + if (arrayReads[i].has(wName)) return false; + } + } + } + + return true; } From eafbb15b4cd03a5bc304ddcbdc6c29068fd86a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Bispo?= Date: Thu, 2 Jul 2026 14:23:11 +0100 Subject: [PATCH 10/10] Change Windows OS version in nightly workflow Updated Windows OS version in CI workflow to 2022. --- .github/workflows/nightly.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5a70a253..f6b0a0d9 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -136,7 +136,8 @@ jobs: matrix: #node-version: ['latest', '22.x', '20.x'] node-version: ['22.x', '20.x'] - os: [ubuntu-latest, windows-latest, macos-latest] + # node-gyp not working on the lastest windows (2025), needs to be updated + os: [ubuntu-latest, windows-2022, macos-latest] runs-on: ${{ matrix.os }}