From 85b4ae8d361ef1bba619610e10c4f73bc6f7c927 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 08:17:31 +0100 Subject: [PATCH 01/36] Add VisualizationTool specialization --- .../visualization/ClavaAstConverter.ts | 34 +++++++++++++++++++ .../visualization/VisualizationTool.ts | 18 ++++++++++ .../clava/weaver/ClavaApiJsResource.java | 2 ++ .../clava/visualization/ClavaAstConverter.js | 20 +++++++++++ .../clava/visualization/VisualizationTool.js | 14 ++++++++ 5 files changed, 88 insertions(+) create mode 100644 Clava-JS/src-api/visualization/ClavaAstConverter.ts create mode 100644 Clava-JS/src-api/visualization/VisualizationTool.ts create mode 100644 ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js create mode 100644 ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts new file mode 100644 index 000000000..a3a44551b --- /dev/null +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -0,0 +1,34 @@ +import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; +import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; +import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; +import { Joinpoint } from "../Joinpoints.js"; + +export default class ClavaAstConverter implements GenericAstConverter { + private sortByLocation(jps: Joinpoint[]): Joinpoint[] { + return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); + } + + private toToolJoinPoint(jp: LaraJoinPoint): ToolJoinPoint { + const clavaJp = jp as Joinpoint; + const sortedChildren = this.sortByLocation(clavaJp.children.slice()); + + return new ToolJoinPoint( + clavaJp.astId, + clavaJp.joinPointType, + clavaJp.code, + sortedChildren.map(child => this.toToolJoinPoint(child)), + ); + } + + private refineJoinPoint(jp: ToolJoinPoint) { + + } + + public getToolAst(root: LaraJoinPoint): ToolJoinPoint { + return this.toToolJoinPoint(root); + } + + public getPrettyHtmlCode(): string { + return ''; + } +} \ No newline at end of file diff --git a/Clava-JS/src-api/visualization/VisualizationTool.ts b/Clava-JS/src-api/visualization/VisualizationTool.ts new file mode 100644 index 000000000..983b90c84 --- /dev/null +++ b/Clava-JS/src-api/visualization/VisualizationTool.ts @@ -0,0 +1,18 @@ +import GenericAstConverter from 'lara-js/api/visualization/GenericAstConverter.js'; +import GenericVisualizationTool from 'lara-js/api/visualization/GenericVisualizationTool.js' +import ClavaAstConverter from './ClavaAstConverter.js'; + +export class VisualizationTool extends GenericVisualizationTool { + private joinPointConverter = new ClavaAstConverter(); + private static instance: VisualizationTool = new VisualizationTool(); + + public static getInstance(): VisualizationTool { + return this.instance; + } + + protected override getAstConverter(): GenericAstConverter { + return this.joinPointConverter; + } +} + +export default VisualizationTool.getInstance(); \ No newline at end of file diff --git a/ClavaLaraApi/src-java/pt/up/fe/specs/clava/weaver/ClavaApiJsResource.java b/ClavaLaraApi/src-java/pt/up/fe/specs/clava/weaver/ClavaApiJsResource.java index e4e115caf..ec4f4a5d9 100644 --- a/ClavaLaraApi/src-java/pt/up/fe/specs/clava/weaver/ClavaApiJsResource.java +++ b/ClavaLaraApi/src-java/pt/up/fe/specs/clava/weaver/ClavaApiJsResource.java @@ -154,6 +154,8 @@ public enum ClavaApiJsResource implements LaraResourceProvider { TIMER_JS("lara/code/Timer.js"), ENERGYMETRIC_JS("lara/metrics/EnergyMetric.js"), EXECUTIONTIMEMETRIC_JS("lara/metrics/ExecutionTimeMetric.js"), + CLAVAASTCONVERTER_JS("visualization/ClavaAstConverter.js"), + VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"), WEAVERLAUNCHER_JS("weaver/WeaverLauncher.js"); private final String resource; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js new file mode 100644 index 000000000..8aefe6715 --- /dev/null +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -0,0 +1,20 @@ +import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; +export default class ClavaAstConverter { + sortByLocation(jps) { + return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); + } + toToolJoinPoint(jp) { + const clavaJp = jp; + const sortedChildren = this.sortByLocation(clavaJp.children.slice()); + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.code, sortedChildren.map(child => this.toToolJoinPoint(child))); + } + refineJoinPoint(jp) { + } + getToolAst(root) { + return this.toToolJoinPoint(root); + } + getPrettyHtmlCode() { + return ''; + } +} +//# sourceMappingURL=ClavaAstConverter.js.map \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js b/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js new file mode 100644 index 000000000..24ec8e872 --- /dev/null +++ b/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js @@ -0,0 +1,14 @@ +import GenericVisualizationTool from 'lara-js/api/visualization/GenericVisualizationTool.js'; +import ClavaAstConverter from './ClavaAstConverter.js'; +export class VisualizationTool extends GenericVisualizationTool { + joinPointConverter = new ClavaAstConverter(); + static instance = new VisualizationTool(); + static getInstance() { + return this.instance; + } + getAstConverter() { + return this.joinPointConverter; + } +} +export default VisualizationTool.getInstance(); +//# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From 267c9f22a1d4e00dcd4bc3b1ea721cee19e9fc77 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 09:23:53 +0100 Subject: [PATCH 02/36] Move AST refining to Clava --- .../visualization/ClavaAstConverter.ts | 32 +++++++++++++++++-- .../clava/visualization/ClavaAstConverter.js | 25 +++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index a3a44551b..de879561b 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -20,12 +20,38 @@ export default class ClavaAstConverter implements GenericAstConverter { ); } - private refineJoinPoint(jp: ToolJoinPoint) { - + private addIdentation(code: string, indentation: number): string { + return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); + } + + private refineAstCode(root: ToolJoinPoint, indentation: number = 0): ToolJoinPoint { + root.code = this.addIdentation(root.code.trim(), indentation); + + const children = root.children; + if (root.type == 'loop') { + children + .filter(child => child.type === 'exprStmt') + .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + } + + if (root.type == 'declStmt') { + root.children + .slice(1) + .forEach(child => { + child.code = child.code.match(/(?:\S+\s+)(\S.*)/)![1]; + }); // Remove type from variable declarations + } + + + for (const child of root.children) { + this.refineAstCode(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); + } + + return root; } public getToolAst(root: LaraJoinPoint): ToolJoinPoint { - return this.toToolJoinPoint(root); + return this.refineAstCode(this.toToolJoinPoint(root)); } public getPrettyHtmlCode(): string { diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 8aefe6715..a469c1caf 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -8,10 +8,31 @@ export default class ClavaAstConverter { const sortedChildren = this.sortByLocation(clavaJp.children.slice()); return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.code, sortedChildren.map(child => this.toToolJoinPoint(child))); } - refineJoinPoint(jp) { + addIdentation(code, indentation) { + return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); + } + refineAstCode(root, indentation = 0) { + root.code = this.addIdentation(root.code.trim(), indentation); + const children = root.children; + if (root.type == 'loop') { + children + .filter(child => child.type === 'exprStmt') + .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + } + if (root.type == 'declStmt') { + root.children + .slice(1) + .forEach(child => { + child.code = child.code.match(/(?:\S+\s+)(\S.*)/)[1]; + }); // Remove type from variable declarations + } + for (const child of root.children) { + this.refineAstCode(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); + } + return root; } getToolAst(root) { - return this.toToolJoinPoint(root); + return this.refineAstCode(this.toToolJoinPoint(root)); } getPrettyHtmlCode() { return ''; From 5690dc1d2197b2e9d1e94a4872981c4711d6d3e0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 16:18:43 +0100 Subject: [PATCH 03/36] Perform node to code linking on Clava --- .../visualization/ClavaAstConverter.ts | 104 ++++++++++++++---- .../clava/visualization/ClavaAstConverter.js | 87 +++++++++++---- 2 files changed, 148 insertions(+), 43 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index de879561b..ec24d1e06 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -2,59 +2,117 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { Joinpoint } from "../Joinpoints.js"; +import Clava from "../clava/Clava.js"; -export default class ClavaAstConverter implements GenericAstConverter { - private sortByLocation(jps: Joinpoint[]): Joinpoint[] { - return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); - } +type CodeMap = { [nodeId: string]: string }; - private toToolJoinPoint(jp: LaraJoinPoint): ToolJoinPoint { - const clavaJp = jp as Joinpoint; - const sortedChildren = this.sortByLocation(clavaJp.children.slice()); +export default class ClavaAstConverter implements GenericAstConverter { + public getToolAst(root: LaraJoinPoint): ToolJoinPoint { + const clavaJp = root as Joinpoint; return new ToolJoinPoint( clavaJp.astId, clavaJp.joinPointType, clavaJp.code, - sortedChildren.map(child => this.toToolJoinPoint(child)), + clavaJp.children.map(child => this.getToolAst(child)), ); } + private toCodeMap(jp: Joinpoint, codeMap: CodeMap): CodeMap { + codeMap[jp.astId] = jp.code.trim(); + jp.children.forEach(child => this.toCodeMap(child, codeMap)); + return codeMap; + } + private addIdentation(code: string, indentation: number): string { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } - private refineAstCode(root: ToolJoinPoint, indentation: number = 0): ToolJoinPoint { - root.code = this.addIdentation(root.code.trim(), indentation); + private refineCodeMap(root: Joinpoint, codeMap: CodeMap, indentation: number = 0): CodeMap { + codeMap[root.astId] = this.addIdentation(codeMap[root.astId], indentation); - const children = root.children; - if (root.type == 'loop') { - children - .filter(child => child.type === 'exprStmt') - .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + if (root.joinPointType == 'loop') { + root.children + .filter(child => child.joinPointType === 'exprStmt') + .forEach(child => codeMap[child.astId] = codeMap[child.astId].slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.type == 'declStmt') { + if (root.joinPointType == 'declStmt') { root.children .slice(1) .forEach(child => { - child.code = child.code.match(/(?:\S+\s+)(\S.*)/)![1]; + codeMap[child.astId] = codeMap[child.astId].match(/(?:\S+\s+)(\S.*)/)![1]; }); // Remove type from variable declarations } - for (const child of root.children) { - this.refineAstCode(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); + const newIndentation = ['body', 'class'].includes(root.joinPointType) ? indentation + 1 : indentation; + this.refineCodeMap(child, codeMap, newIndentation); } - return root; + return codeMap; } - public getToolAst(root: LaraJoinPoint): ToolJoinPoint { - return this.refineAstCode(this.toToolJoinPoint(root)); + private escapeHtml(text: string): string { + const specialCharMap: { [char: string]: string } = { + '&': '&', + '<': '<', + '>': '>', + }; + + return text.replace(/[&<>]/g, (match) => specialCharMap[match]); + } + + private getSpanTags(attrs: string[]): string[] { + return [``, '']; + } + + private getNodeCodeTags(nodeId: string): string[] { + return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); + } + + private sortByLocation(jps: Joinpoint[]): Joinpoint[] { + return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); + } + + private linkCodeToAstNodes(root: Joinpoint, codeMap: CodeMap, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { + const nodeCode = codeMap[root.astId]; + const nodeCodeHtml = this.escapeHtml(nodeCode); + const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); + const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; + if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { + console.warn(`Code of node "${root.joinPointType}" not found in code container: "${nodeCodeHtml}"`); + return [outerCodeStart, outerCodeStart, ""]; + } + if (root.joinPointType === 'varref' && nodeCode === 'i') { + console.warn(outerCode.slice(outerCodeStart, outerCodeEnd), innerCodeStart) + } + + const [openingTag, closingTag] = this.getNodeCodeTags(root.astId); + + let newCode = openingTag; + let newCodeIndex = innerCodeStart; + + const sortedChildren = this.sortByLocation(root.children.slice()); + for (const child of sortedChildren) { + const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, codeMap, outerCode, newCodeIndex, innerCodeEnd); + newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; + newCodeIndex = childCodeEnd; + } + newCode += outerCode.slice(newCodeIndex, innerCodeEnd) + closingTag; + + return [innerCodeStart, innerCodeEnd, newCode]; } public getPrettyHtmlCode(): string { - return ''; + const root = Clava.getProgram(); + const codeMap: CodeMap = {}; + this.toCodeMap(root, codeMap); + this.refineCodeMap(root, codeMap); + + let code = codeMap[root.astId]; + code = this.escapeHtml(code); + code = this.linkCodeToAstNodes(root, codeMap, code, 0, code.length)[2]; + return code; } } \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index a469c1caf..31ae42be3 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,41 +1,88 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; +import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { - sortByLocation(jps) { - return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); + getToolAst(root) { + const clavaJp = root; + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.code, clavaJp.children.map(child => this.getToolAst(child))); } - toToolJoinPoint(jp) { - const clavaJp = jp; - const sortedChildren = this.sortByLocation(clavaJp.children.slice()); - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.code, sortedChildren.map(child => this.toToolJoinPoint(child))); + toCodeMap(jp, codeMap) { + codeMap[jp.astId] = jp.code.trim(); + jp.children.forEach(child => this.toCodeMap(child, codeMap)); + return codeMap; } addIdentation(code, indentation) { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } - refineAstCode(root, indentation = 0) { - root.code = this.addIdentation(root.code.trim(), indentation); - const children = root.children; - if (root.type == 'loop') { - children - .filter(child => child.type === 'exprStmt') - .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + refineCodeMap(root, codeMap, indentation = 0) { + codeMap[root.astId] = this.addIdentation(codeMap[root.astId], indentation); + if (root.joinPointType == 'loop') { + root.children + .filter(child => child.joinPointType === 'exprStmt') + .forEach(child => codeMap[child.astId] = codeMap[child.astId].slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.type == 'declStmt') { + if (root.joinPointType == 'declStmt') { root.children .slice(1) .forEach(child => { - child.code = child.code.match(/(?:\S+\s+)(\S.*)/)[1]; + codeMap[child.astId] = codeMap[child.astId].match(/(?:\S+\s+)(\S.*)/)[1]; }); // Remove type from variable declarations } for (const child of root.children) { - this.refineAstCode(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); + const newIndentation = ['body', 'class'].includes(root.joinPointType) ? indentation + 1 : indentation; + this.refineCodeMap(child, codeMap, newIndentation); } - return root; + return codeMap; } - getToolAst(root) { - return this.refineAstCode(this.toToolJoinPoint(root)); + escapeHtml(text) { + const specialCharMap = { + '&': '&', + '<': '<', + '>': '>', + }; + return text.replace(/[&<>]/g, (match) => specialCharMap[match]); + } + getSpanTags(attrs) { + return [``, '']; + } + getNodeCodeTags(nodeId) { + return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); + } + sortByLocation(jps) { + return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); + } + linkCodeToAstNodes(root, codeMap, outerCode, outerCodeStart, outerCodeEnd) { + const nodeCode = codeMap[root.astId]; + const nodeCodeHtml = this.escapeHtml(nodeCode); + const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); + const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; + if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { + console.warn(`Code of node "${root.joinPointType}" not found in code container: "${nodeCodeHtml}"`); + return [outerCodeStart, outerCodeStart, ""]; + } + if (root.joinPointType === 'varref' && nodeCode === 'i') { + console.warn(outerCode.slice(outerCodeStart, outerCodeEnd), innerCodeStart); + } + const [openingTag, closingTag] = this.getNodeCodeTags(root.astId); + let newCode = openingTag; + let newCodeIndex = innerCodeStart; + const sortedChildren = this.sortByLocation(root.children.slice()); + for (const child of sortedChildren) { + const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, codeMap, outerCode, newCodeIndex, innerCodeEnd); + newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; + newCodeIndex = childCodeEnd; + } + newCode += outerCode.slice(newCodeIndex, innerCodeEnd) + closingTag; + return [innerCodeStart, innerCodeEnd, newCode]; } getPrettyHtmlCode() { - return ''; + const root = Clava.getProgram(); + const codeMap = {}; + this.toCodeMap(root, codeMap); + this.refineCodeMap(root, codeMap); + let code = codeMap[root.astId]; + code = this.escapeHtml(code); + code = this.linkCodeToAstNodes(root, codeMap, code, 0, code.length)[2]; + return code; } } //# sourceMappingURL=ClavaAstConverter.js.map \ No newline at end of file From 30dee00288ccd437fbedd6ff079e2f1cf6adebbf Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 16:28:06 +0100 Subject: [PATCH 04/36] Remove code from ToolJoinPoint --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 1 - ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index ec24d1e06..f9b8f7949 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -13,7 +13,6 @@ export default class ClavaAstConverter implements GenericAstConverter { return new ToolJoinPoint( clavaJp.astId, clavaJp.joinPointType, - clavaJp.code, clavaJp.children.map(child => this.getToolAst(child)), ); } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 31ae42be3..79b0b970a 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -3,7 +3,7 @@ import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { getToolAst(root) { const clavaJp = root; - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.code, clavaJp.children.map(child => this.getToolAst(child))); + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.children.map(child => this.getToolAst(child))); } toCodeMap(jp, codeMap) { codeMap[jp.astId] = jp.code.trim(); From fbb1078fe321c0f4db98e556bea48bf6b70379e2 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 17:40:52 +0100 Subject: [PATCH 05/36] Fix code linking of typedefs --- .../visualization/ClavaAstConverter.ts | 97 ++++++++++++------- .../clava/visualization/ClavaAstConverter.js | 86 +++++++++------- 2 files changed, 111 insertions(+), 72 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index f9b8f7949..eec07567b 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -4,7 +4,11 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js" import { Joinpoint } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; -type CodeMap = { [nodeId: string]: string }; +type CodeNode = { + jp: Joinpoint; + code: string; + children: CodeNode[]; +}; export default class ClavaAstConverter implements GenericAstConverter { public getToolAst(root: LaraJoinPoint): ToolJoinPoint { @@ -17,39 +21,67 @@ export default class ClavaAstConverter implements GenericAstConverter { ); } - private toCodeMap(jp: Joinpoint, codeMap: CodeMap): CodeMap { - codeMap[jp.astId] = jp.code.trim(); - jp.children.forEach(child => this.toCodeMap(child, codeMap)); - return codeMap; + private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { + return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + } + + private toCodeNode(jp: Joinpoint): CodeNode { + return { + jp: jp, + code: jp.code.trim(), + children: jp.children.map(child => this.toCodeNode(child)), + }; } private addIdentation(code: string, indentation: number): string { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } - private refineCodeMap(root: Joinpoint, codeMap: CodeMap, indentation: number = 0): CodeMap { - codeMap[root.astId] = this.addIdentation(codeMap[root.astId], indentation); + private refineCode(node: CodeNode, indentation: number = 0): CodeNode { + node.code = this.addIdentation(node.code, indentation); + this.sortByLocation(node.children); - if (root.joinPointType == 'loop') { - root.children - .filter(child => child.joinPointType === 'exprStmt') - .forEach(child => codeMap[child.astId] = codeMap[child.astId].slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + if (node.jp.joinPointType == 'loop') { + node.children + .filter(child => child.jp.joinPointType === 'exprStmt') + .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.joinPointType == 'declStmt') { - root.children + if (node.jp.joinPointType == 'declStmt') { + node.children .slice(1) .forEach(child => { - codeMap[child.astId] = codeMap[child.astId].match(/(?:\S+\s+)(\S.*)/)![1]; + child.code = child.code.match(/^(?:\S+\s+)(\S.*)$/)![1]; }); // Remove type from variable declarations } - for (const child of root.children) { - const newIndentation = ['body', 'class'].includes(root.joinPointType) ? indentation + 1 : indentation; - this.refineCodeMap(child, codeMap, newIndentation); + for (const child of node.children) { + const newIndentation = ['body', 'class'].includes(node.jp.joinPointType) ? indentation + 1 : indentation; + this.refineCode(child, newIndentation); } - return codeMap; + + if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { + const tagDeclVars = node.children[0]; + const typedef = tagDeclVars.children[0]; + if (typedef.jp.joinPointType === 'typedefDecl') { + const [, code1, code2] = typedef.code.match(/^(.*\S)\s+(\S+)$/)!; + tagDeclVars.code = typedef.code = code1; + + const newChild = { + jp: tagDeclVars.jp, + code: code2, + children: [{ + jp: typedef.jp, + code: code2, + children: [], + }], + }; + node.children.push(newChild); + } // Assign typedef code to TagDeclVars and split into two children + } + + return node; } private escapeHtml(text: string): string { @@ -70,31 +102,23 @@ export default class ClavaAstConverter implements GenericAstConverter { return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); } - private sortByLocation(jps: Joinpoint[]): Joinpoint[] { - return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); - } - - private linkCodeToAstNodes(root: Joinpoint, codeMap: CodeMap, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { - const nodeCode = codeMap[root.astId]; + private linkCodeToAstNodes(root: CodeNode, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { + const nodeCode = root.code; const nodeCodeHtml = this.escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { - console.warn(`Code of node "${root.joinPointType}" not found in code container: "${nodeCodeHtml}"`); + console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); return [outerCodeStart, outerCodeStart, ""]; } - if (root.joinPointType === 'varref' && nodeCode === 'i') { - console.warn(outerCode.slice(outerCodeStart, outerCodeEnd), innerCodeStart) - } - const [openingTag, closingTag] = this.getNodeCodeTags(root.astId); + const [openingTag, closingTag] = this.getNodeCodeTags(root.jp.astId); let newCode = openingTag; let newCodeIndex = innerCodeStart; - const sortedChildren = this.sortByLocation(root.children.slice()); - for (const child of sortedChildren) { - const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, codeMap, outerCode, newCodeIndex, innerCodeEnd); + for (const child of root.children) { + const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; newCodeIndex = childCodeEnd; } @@ -105,13 +129,12 @@ export default class ClavaAstConverter implements GenericAstConverter { public getPrettyHtmlCode(): string { const root = Clava.getProgram(); - const codeMap: CodeMap = {}; - this.toCodeMap(root, codeMap); - this.refineCodeMap(root, codeMap); + const rootCodeNode = this.toCodeNode(root); + this.refineCode(rootCodeNode); - let code = codeMap[root.astId]; + let code = rootCodeNode.code; code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(root, codeMap, code, 0, code.length)[2]; + code = this.linkCodeToAstNodes(rootCodeNode, code, 0, code.length)[2]; return code; } } \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 79b0b970a..4d1718e7c 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -5,33 +5,57 @@ export default class ClavaAstConverter { const clavaJp = root; return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.children.map(child => this.getToolAst(child))); } - toCodeMap(jp, codeMap) { - codeMap[jp.astId] = jp.code.trim(); - jp.children.forEach(child => this.toCodeMap(child, codeMap)); - return codeMap; + sortByLocation(codeNodes) { + return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + } + toCodeNode(jp) { + return { + jp: jp, + code: jp.code.trim(), + children: jp.children.map(child => this.toCodeNode(child)), + }; } addIdentation(code, indentation) { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } - refineCodeMap(root, codeMap, indentation = 0) { - codeMap[root.astId] = this.addIdentation(codeMap[root.astId], indentation); - if (root.joinPointType == 'loop') { - root.children - .filter(child => child.joinPointType === 'exprStmt') - .forEach(child => codeMap[child.astId] = codeMap[child.astId].slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + refineCode(node, indentation = 0) { + node.code = this.addIdentation(node.code, indentation); + this.sortByLocation(node.children); + if (node.jp.joinPointType == 'loop') { + node.children + .filter(child => child.jp.joinPointType === 'exprStmt') + .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.joinPointType == 'declStmt') { - root.children + if (node.jp.joinPointType == 'declStmt') { + node.children .slice(1) .forEach(child => { - codeMap[child.astId] = codeMap[child.astId].match(/(?:\S+\s+)(\S.*)/)[1]; + child.code = child.code.match(/^(?:\S+\s+)(\S.*)$/)[1]; }); // Remove type from variable declarations } - for (const child of root.children) { - const newIndentation = ['body', 'class'].includes(root.joinPointType) ? indentation + 1 : indentation; - this.refineCodeMap(child, codeMap, newIndentation); + for (const child of node.children) { + const newIndentation = ['body', 'class'].includes(node.jp.joinPointType) ? indentation + 1 : indentation; + this.refineCode(child, newIndentation); + } + if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { + const tagDeclVars = node.children[0]; + const typedef = tagDeclVars.children[0]; + if (typedef.jp.joinPointType === 'typedefDecl') { + const [, code1, code2] = typedef.code.match(/^(.*\S)\s+(\S+)$/); + tagDeclVars.code = typedef.code = code1; + const newChild = { + jp: tagDeclVars.jp, + code: code2, + children: [{ + jp: typedef.jp, + code: code2, + children: [], + }], + }; + node.children.push(newChild); + } // Assign typedef code to TagDeclVars and split into two children } - return codeMap; + return node; } escapeHtml(text) { const specialCharMap = { @@ -47,27 +71,20 @@ export default class ClavaAstConverter { getNodeCodeTags(nodeId) { return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); } - sortByLocation(jps) { - return jps.sort((jp1, jp2) => jp1.location.localeCompare(jp2.location, 'en', { numeric: true })); - } - linkCodeToAstNodes(root, codeMap, outerCode, outerCodeStart, outerCodeEnd) { - const nodeCode = codeMap[root.astId]; + linkCodeToAstNodes(root, outerCode, outerCodeStart, outerCodeEnd) { + const nodeCode = root.code; const nodeCodeHtml = this.escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { - console.warn(`Code of node "${root.joinPointType}" not found in code container: "${nodeCodeHtml}"`); + console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); return [outerCodeStart, outerCodeStart, ""]; } - if (root.joinPointType === 'varref' && nodeCode === 'i') { - console.warn(outerCode.slice(outerCodeStart, outerCodeEnd), innerCodeStart); - } - const [openingTag, closingTag] = this.getNodeCodeTags(root.astId); + const [openingTag, closingTag] = this.getNodeCodeTags(root.jp.astId); let newCode = openingTag; let newCodeIndex = innerCodeStart; - const sortedChildren = this.sortByLocation(root.children.slice()); - for (const child of sortedChildren) { - const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, codeMap, outerCode, newCodeIndex, innerCodeEnd); + for (const child of root.children) { + const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; newCodeIndex = childCodeEnd; } @@ -76,12 +93,11 @@ export default class ClavaAstConverter { } getPrettyHtmlCode() { const root = Clava.getProgram(); - const codeMap = {}; - this.toCodeMap(root, codeMap); - this.refineCodeMap(root, codeMap); - let code = codeMap[root.astId]; + const rootCodeNode = this.toCodeNode(root); + this.refineCode(rootCodeNode); + let code = rootCodeNode.code; code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(root, codeMap, code, 0, code.length)[2]; + code = this.linkCodeToAstNodes(rootCodeNode, code, 0, code.length)[2]; return code; } } From 20f1d3112657a759f3a9b8d8c381abdb8c4a2d9f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 20:54:39 +0100 Subject: [PATCH 06/36] Fix inline comment on naked body --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 9 ++++++++- .../src-lara/clava/visualization/ClavaAstConverter.js | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index eec07567b..31b257bd4 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { Joinpoint } from "../Joinpoints.js"; +import { Body, Joinpoint } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; type CodeNode = { @@ -60,6 +60,13 @@ export default class ClavaAstConverter implements GenericAstConverter { this.refineCode(child, newIndentation); } + if (node.jp.joinPointType == 'body' && (node.jp as Body).naked) { + const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); + if (match) { + const [, statement, comment] = match; + node.code = statement + ' ' + comment; + } + } // Fix space between statement and inline comment in naked body if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 4d1718e7c..d2424af6b 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -37,6 +37,13 @@ export default class ClavaAstConverter { const newIndentation = ['body', 'class'].includes(node.jp.joinPointType) ? indentation + 1 : indentation; this.refineCode(child, newIndentation); } + if (node.jp.joinPointType == 'body' && node.jp.naked) { + const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); + if (match) { + const [, statement, comment] = match; + node.code = statement + ' ' + comment; + } + } // Fix space between statement and inline comment in naked body if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; From d31ac8f32ee1c459711247567ca1616d990a3717 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 11:31:26 +0100 Subject: [PATCH 07/36] Add info to ToolJoinPoint --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 9 +++++---- .../src-lara/clava/visualization/ClavaAstConverter.js | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 31b257bd4..3cfb26f3d 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -17,14 +17,11 @@ export default class ClavaAstConverter implements GenericAstConverter { return new ToolJoinPoint( clavaJp.astId, clavaJp.joinPointType, + {}, clavaJp.children.map(child => this.getToolAst(child)), ); } - private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { - return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); - } - private toCodeNode(jp: Joinpoint): CodeNode { return { jp: jp, @@ -37,6 +34,10 @@ export default class ClavaAstConverter implements GenericAstConverter { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } + private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { + return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + } + private refineCode(node: CodeNode, indentation: number = 0): CodeNode { node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index d2424af6b..bdf5546f6 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -3,10 +3,7 @@ import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { getToolAst(root) { const clavaJp = root; - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.children.map(child => this.getToolAst(child))); - } - sortByLocation(codeNodes) { - return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, {}, clavaJp.children.map(child => this.getToolAst(child))); } toCodeNode(jp) { return { @@ -18,6 +15,9 @@ export default class ClavaAstConverter { addIdentation(code, indentation) { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } + sortByLocation(codeNodes) { + return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + } refineCode(node, indentation = 0) { node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); From 49f2e311c6fb0e21cc7855ef03c7b3df21e9db80 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 12:25:46 +0100 Subject: [PATCH 08/36] Create method to extract Joinpoint info --- .../visualization/ClavaAstConverter.ts | 22 ++++++++++++++++--- .../clava/visualization/ClavaAstConverter.js | 15 ++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 3cfb26f3d..cc9b8f4aa 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; -import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { Body, Joinpoint } from "../Joinpoints.js"; +import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; +import { Body, IntLiteral, Joinpoint } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; type CodeNode = { @@ -11,13 +11,29 @@ type CodeNode = { }; export default class ClavaAstConverter implements GenericAstConverter { + private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { + const info: JoinPointInfo = { + 'astId': jp.astId, + 'astName': jp.astName, + }; + + switch (jp.joinPointType) { + case 'intLiteral': + const intLiteral = jp as IntLiteral; + info['value'] = intLiteral.value.toString(); + break; + } + + return info; + } + public getToolAst(root: LaraJoinPoint): ToolJoinPoint { const clavaJp = root as Joinpoint; return new ToolJoinPoint( clavaJp.astId, clavaJp.joinPointType, - {}, + this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child)), ); } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index bdf5546f6..e3f876c0b 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,9 +1,22 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { + getJoinPointInfo(jp) { + const info = { + 'astId': jp.astId, + 'astName': jp.astName, + }; + switch (jp.joinPointType) { + case 'intLiteral': + const intLiteral = jp; + info['value'] = intLiteral.value.toString(); + break; + } + return info; + } getToolAst(root) { const clavaJp = root; - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, {}, clavaJp.children.map(child => this.getToolAst(child))); + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child))); } toCodeNode(jp) { return { From ee0e98c72b2263fb89d3fba50c412132d5cdc4fb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 21:51:23 +0100 Subject: [PATCH 09/36] Perform location sorting based on start line and column --- .../visualization/ClavaAstConverter.ts | 23 +++++++++++-------- .../clava/visualization/ClavaAstConverter.js | 22 ++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index cc9b8f4aa..271386031 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { Body, IntLiteral, Joinpoint } from "../Joinpoints.js"; +import { Body, IntLiteral, Joinpoint, TypedefDecl } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; type CodeNode = { @@ -13,15 +13,14 @@ type CodeNode = { export default class ClavaAstConverter implements GenericAstConverter { private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { const info: JoinPointInfo = { - 'astId': jp.astId, - 'astName': jp.astName, + 'AST ID': jp.astId, + 'AST Name': jp.astName, }; - switch (jp.joinPointType) { - case 'intLiteral': - const intLiteral = jp as IntLiteral; - info['value'] = intLiteral.value.toString(); - break; + if (jp instanceof IntLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); } return info; @@ -51,7 +50,11 @@ export default class ClavaAstConverter implements GenericAstConverter { } private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { - return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + return codeNodes.sort((node1, node2) => { + if (node1.jp.line === node2.jp.line) + return (node1.jp.column ?? -1) - (node2.jp.column ?? -1); + return (node1.jp.line ?? -1) - (node2.jp.line ?? -1); + }); } private refineCode(node: CodeNode, indentation: number = 0): CodeNode { @@ -88,7 +91,7 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; - if (typedef.jp.joinPointType === 'typedefDecl') { + if (typedef.jp instanceof TypedefDecl) { const [, code1, code2] = typedef.code.match(/^(.*\S)\s+(\S+)$/)!; tagDeclVars.code = typedef.code = code1; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index e3f876c0b..9f2b29a30 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,16 +1,16 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; +import { IntLiteral, TypedefDecl } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { - 'astId': jp.astId, - 'astName': jp.astName, + 'AST ID': jp.astId, + 'AST Name': jp.astName, }; - switch (jp.joinPointType) { - case 'intLiteral': - const intLiteral = jp; - info['value'] = intLiteral.value.toString(); - break; + if (jp instanceof IntLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); } return info; } @@ -29,7 +29,11 @@ export default class ClavaAstConverter { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); } sortByLocation(codeNodes) { - return codeNodes.sort((node1, node2) => node1.jp.location.localeCompare(node2.jp.location, 'en', { numeric: true })); + return codeNodes.sort((node1, node2) => { + if (node1.jp.line === node2.jp.line) + return (node1.jp.column ?? -1) - (node2.jp.column ?? -1); + return (node1.jp.line ?? -1) - (node2.jp.line ?? -1); + }); } refineCode(node, indentation = 0) { node.code = this.addIdentation(node.code, indentation); @@ -60,7 +64,7 @@ export default class ClavaAstConverter { if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; - if (typedef.jp.joinPointType === 'typedefDecl') { + if (typedef.jp instanceof TypedefDecl) { const [, code1, code2] = typedef.code.match(/^(.*\S)\s+(\S+)$/); tagDeclVars.code = typedef.code = code1; const newChild = { From 93e8af64faf4ff0b43a97c4908e5965e2955eda1 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sun, 21 Jul 2024 17:11:11 +0100 Subject: [PATCH 10/36] Implement node info retrieval based on the joinpoint type --- .../visualization/ClavaAstConverter.ts | 115 +++++++++++++++++- .../clava/visualization/ClavaAstConverter.js | 98 ++++++++++++++- 2 files changed, 209 insertions(+), 4 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 271386031..e2ddcda69 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { Body, IntLiteral, Joinpoint, TypedefDecl } from "../Joinpoints.js"; +import { AdjustedType, Body, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; type CodeNode = { @@ -14,15 +14,126 @@ export default class ClavaAstConverter implements GenericAstConverter { private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { const info: JoinPointInfo = { 'AST ID': jp.astId, - 'AST Name': jp.astName, + 'AST name': jp.astName, }; + if (jp instanceof FileJp) { + Object.assign(info, { + 'File name': jp.name, + 'Source folder': jp.sourceFoldername, + 'Relative path': jp.relativeFilepath, + 'Is C++': jp.isCxx, + 'Is header file': jp.isHeader, + 'Has main': jp.hasMain + }); + } + + if (jp instanceof Include) { + Object.assign(info, { + 'Include name': jp.name, + }); + } + + if (jp instanceof NamedDecl) { + Object.assign(info, { + 'Name': jp.name, + }); + } + + if (jp instanceof Pragma) { + Object.assign(info, { + 'Pragma name': jp.name, + }); + } + + if (jp instanceof Program) { + Object.assign(info, { + 'Program name': jp.name, + 'Standard': jp.standard, + }); + } + + if (jp instanceof Tag) { + Object.assign(info, { + 'Pragma ID': jp.id, + }); + } + + if (jp instanceof Type) { + Object.assign(info, { + 'Constant?': jp.constant, + 'Is builtin': jp.isBuiltin, + 'Has sugar': jp.hasSugar, + }); + } + + if (jp instanceof Varref) { + Object.assign(info, { + 'Name': jp.name, + }); + } + + if (jp instanceof WrapperStmt) { + Object.assign(info, { + 'Wrapper type': jp.kind, + }); + } + + if (jp instanceof AdjustedType) { + Object.assign(info, { + 'Original type': jp.originalType, + }); + } + + if (jp instanceof BoolLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); + } + + if (jp instanceof Call) { + Object.assign(info, { + 'Function name': jp.name, + }); + } + + if (jp instanceof Class) { + Object.assign(info, { + 'Is interface': jp.isInterface, + }); + } + + if (jp instanceof FloatLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); + } + if (jp instanceof IntLiteral) { Object.assign(info, { 'Value': jp.value.toString(), }); } + if (jp instanceof Loop) { + Object.assign(info, { + 'Loop ID': jp.id, + 'Loop kind': jp.kind, + }); + } + + if (jp instanceof Marker) { + Object.assign(info, { + 'Marker ID': jp.id, + }); + } + + if (jp instanceof Omp) { + Object.assign(info, { + 'Omp kind': jp.kind, + }); + } + return info; } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 9f2b29a30..79dc4389b 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,17 +1,111 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { IntLiteral, TypedefDecl } from "../Joinpoints.js"; +import { AdjustedType, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { 'AST ID': jp.astId, - 'AST Name': jp.astName, + 'AST name': jp.astName, }; + if (jp instanceof FileJp) { + Object.assign(info, { + 'File name': jp.name, + 'Source folder': jp.sourceFoldername, + 'Relative path': jp.relativeFilepath, + 'Is C++': jp.isCxx, + 'Is header file': jp.isHeader, + 'Has main': jp.hasMain + }); + } + if (jp instanceof Include) { + Object.assign(info, { + 'Include name': jp.name, + }); + } + if (jp instanceof NamedDecl) { + Object.assign(info, { + 'Name': jp.name, + }); + } + if (jp instanceof Pragma) { + Object.assign(info, { + 'Pragma name': jp.name, + }); + } + if (jp instanceof Program) { + Object.assign(info, { + 'Program name': jp.name, + 'Standard': jp.standard, + }); + } + if (jp instanceof Tag) { + Object.assign(info, { + 'Pragma ID': jp.id, + }); + } + if (jp instanceof Type) { + Object.assign(info, { + 'Constant?': jp.constant, + 'Is builtin': jp.isBuiltin, + 'Has sugar': jp.hasSugar, + }); + } + if (jp instanceof Varref) { + Object.assign(info, { + 'Name': jp.name, + }); + } + if (jp instanceof WrapperStmt) { + Object.assign(info, { + 'Wrapper type': jp.kind, + }); + } + if (jp instanceof AdjustedType) { + Object.assign(info, { + 'Original type': jp.originalType, + }); + } + if (jp instanceof BoolLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); + } + if (jp instanceof Call) { + Object.assign(info, { + 'Function name': jp.name, + }); + } + if (jp instanceof Class) { + Object.assign(info, { + 'Is interface': jp.isInterface, + }); + } + if (jp instanceof FloatLiteral) { + Object.assign(info, { + 'Value': jp.value.toString(), + }); + } if (jp instanceof IntLiteral) { Object.assign(info, { 'Value': jp.value.toString(), }); } + if (jp instanceof Loop) { + Object.assign(info, { + 'Loop ID': jp.id, + 'Loop kind': jp.kind, + }); + } + if (jp instanceof Marker) { + Object.assign(info, { + 'Marker ID': jp.id, + }); + } + if (jp instanceof Omp) { + Object.assign(info, { + 'Omp kind': jp.kind, + }); + } return info; } getToolAst(root) { From 1d33fad66912904a2eb05eaada8e8a2a5d589612 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 10:40:20 +0100 Subject: [PATCH 11/36] Allow updating with random root --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 6 ++---- .../src-lara/clava/visualization/ClavaAstConverter.js | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index e2ddcda69..206deeb46 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -2,7 +2,6 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AdjustedType, Body, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; -import Clava from "../clava/Clava.js"; type CodeNode = { jp: Joinpoint; @@ -265,9 +264,8 @@ export default class ClavaAstConverter implements GenericAstConverter { return [innerCodeStart, innerCodeEnd, newCode]; } - public getPrettyHtmlCode(): string { - const root = Clava.getProgram(); - const rootCodeNode = this.toCodeNode(root); + public getPrettyHtmlCode(root: LaraJoinPoint): string { + const rootCodeNode = this.toCodeNode(root as Joinpoint); this.refineCode(rootCodeNode); let code = rootCodeNode.code; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 79dc4389b..2953db33e 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,6 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AdjustedType, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; -import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -209,8 +208,7 @@ export default class ClavaAstConverter { newCode += outerCode.slice(newCodeIndex, innerCodeEnd) + closingTag; return [innerCodeStart, innerCodeEnd, newCode]; } - getPrettyHtmlCode() { - const root = Clava.getProgram(); + getPrettyHtmlCode(root) { const rootCodeNode = this.toCodeNode(root); this.refineCode(rootCodeNode); let code = rootCodeNode.code; From 7f61b5ee659002e966099052be6527580967bfef Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 11:22:08 +0100 Subject: [PATCH 12/36] Refactor code refining and start division of code into files --- .../visualization/ClavaAstConverter.ts | 26 +++++++++++-------- .../clava/visualization/ClavaAstConverter.js | 21 ++++++++------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 206deeb46..ff9ec7a5f 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; -import GenericAstConverter from "lara-js/api/visualization/GenericAstConverter.js"; +import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, Body, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AdjustedType, Body, BoolLiteral, Call, Class, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; type CodeNode = { jp: Joinpoint; @@ -171,13 +171,13 @@ export default class ClavaAstConverter implements GenericAstConverter { node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); - if (node.jp.joinPointType == 'loop') { + if (node.jp instanceof Loop) { node.children - .filter(child => child.jp.joinPointType === 'exprStmt') + .filter(child => child.jp instanceof ExprStmt) .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (node.jp.joinPointType == 'declStmt') { + if (node.jp instanceof DeclStmt) { node.children .slice(1) .forEach(child => { @@ -186,11 +186,11 @@ export default class ClavaAstConverter implements GenericAstConverter { } for (const child of node.children) { - const newIndentation = ['body', 'class'].includes(node.jp.joinPointType) ? indentation + 1 : indentation; + const newIndentation = (node.jp instanceof Scope || node.jp instanceof Class) ? indentation + 1 : indentation; this.refineCode(child, newIndentation); } - if (node.jp.joinPointType == 'body' && (node.jp as Body).naked) { + if (node.jp instanceof Body && (node.jp as Body).naked) { const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; @@ -218,6 +218,10 @@ export default class ClavaAstConverter implements GenericAstConverter { } // Assign typedef code to TagDeclVars and split into two children } + if (node.jp instanceof Program) { + node.children = node.children.map(file => ({ jp: node.jp, code: file.code, children: [file] })); + } // Divide program code into its files + return node; } @@ -264,13 +268,13 @@ export default class ClavaAstConverter implements GenericAstConverter { return [innerCodeStart, innerCodeEnd, newCode]; } - public getPrettyHtmlCode(root: LaraJoinPoint): string { + public getPrettyHtmlCode(root: LaraJoinPoint): FilesCode { const rootCodeNode = this.toCodeNode(root as Joinpoint); this.refineCode(rootCodeNode); - let code = rootCodeNode.code; + let code = rootCodeNode.children[0].code; code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(rootCodeNode, code, 0, code.length)[2]; - return code; + code = this.linkCodeToAstNodes(rootCodeNode.children[0], code, 0, code.length)[2]; + return { "": code }; } } \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 2953db33e..e52dfbb19 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, BoolLiteral, Call, Class, FileJp, FloatLiteral, Include, IntLiteral, Loop, Marker, NamedDecl, Omp, Pragma, Program, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AdjustedType, Body, BoolLiteral, Call, Class, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -131,12 +131,12 @@ export default class ClavaAstConverter { refineCode(node, indentation = 0) { node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); - if (node.jp.joinPointType == 'loop') { + if (node.jp instanceof Loop) { node.children - .filter(child => child.jp.joinPointType === 'exprStmt') + .filter(child => child.jp instanceof ExprStmt) .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (node.jp.joinPointType == 'declStmt') { + if (node.jp instanceof DeclStmt) { node.children .slice(1) .forEach(child => { @@ -144,10 +144,10 @@ export default class ClavaAstConverter { }); // Remove type from variable declarations } for (const child of node.children) { - const newIndentation = ['body', 'class'].includes(node.jp.joinPointType) ? indentation + 1 : indentation; + const newIndentation = (node.jp instanceof Scope || node.jp instanceof Class) ? indentation + 1 : indentation; this.refineCode(child, newIndentation); } - if (node.jp.joinPointType == 'body' && node.jp.naked) { + if (node.jp instanceof Body && node.jp.naked) { const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; @@ -172,6 +172,9 @@ export default class ClavaAstConverter { node.children.push(newChild); } // Assign typedef code to TagDeclVars and split into two children } + if (node.jp instanceof Program) { + node.children = node.children.map(file => ({ jp: node.jp, code: file.code, children: [file] })); + } // Divide program code into its files return node; } escapeHtml(text) { @@ -211,10 +214,10 @@ export default class ClavaAstConverter { getPrettyHtmlCode(root) { const rootCodeNode = this.toCodeNode(root); this.refineCode(rootCodeNode); - let code = rootCodeNode.code; + let code = rootCodeNode.children[0].code; code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(rootCodeNode, code, 0, code.length)[2]; - return code; + code = this.linkCodeToAstNodes(rootCodeNode.children[0], code, 0, code.length)[2]; + return { "": code }; } } //# sourceMappingURL=ClavaAstConverter.js.map \ No newline at end of file From 93a97206b1386965656e9959a2665471738f11fe Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 13:57:50 +0100 Subject: [PATCH 13/36] Finish division of code into files --- .../visualization/ClavaAstConverter.ts | 22 +++++++++++++++---- .../clava/visualization/ClavaAstConverter.js | 21 ++++++++++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index ff9ec7a5f..d2700f774 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -272,9 +272,23 @@ export default class ClavaAstConverter implements GenericAstConverter { const rootCodeNode = this.toCodeNode(root as Joinpoint); this.refineCode(rootCodeNode); - let code = rootCodeNode.children[0].code; - code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(rootCodeNode.children[0], code, 0, code.length)[2]; - return { "": code }; + if (root instanceof Program) { + return Object.fromEntries(rootCodeNode.children.map(child => { + const file = child.children[0]; + const filename = (file.jp as FileJp).name; + + const fileCode = child.code; // same as file.code + const fileHtmlCode = this.escapeHtml(fileCode); + const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; + return [filename, fileLinkedHtmlCode]; + })); + } else { + const filename = (root as Joinpoint).filename; + const code = rootCodeNode.code; + const htmlCode = this.escapeHtml(code); + const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; + + return { [filename]: linkedHtmlCode }; + } } } \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index e52dfbb19..2c39f07fb 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -214,10 +214,23 @@ export default class ClavaAstConverter { getPrettyHtmlCode(root) { const rootCodeNode = this.toCodeNode(root); this.refineCode(rootCodeNode); - let code = rootCodeNode.children[0].code; - code = this.escapeHtml(code); - code = this.linkCodeToAstNodes(rootCodeNode.children[0], code, 0, code.length)[2]; - return { "": code }; + if (root instanceof Program) { + return Object.fromEntries(rootCodeNode.children.map(child => { + const file = child.children[0]; + const filename = file.jp.name; + const fileCode = child.code; // same as file.code + const fileHtmlCode = this.escapeHtml(fileCode); + const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; + return [filename, fileLinkedHtmlCode]; + })); + } + else { + const filename = root.filename; + const code = rootCodeNode.code; + const htmlCode = this.escapeHtml(code); + const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; + return { [filename]: linkedHtmlCode }; + } } } //# sourceMappingURL=ClavaAstConverter.js.map \ No newline at end of file From d842019330490659286469367c215835008b1894 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 07:51:01 +0100 Subject: [PATCH 14/36] Add filename to ToolJoinPoint --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 5 +++-- .../src-lara/clava/visualization/ClavaAstConverter.js | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index d2700f774..e81f71202 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -142,6 +142,7 @@ export default class ClavaAstConverter implements GenericAstConverter { return new ToolJoinPoint( clavaJp.astId, clavaJp.joinPointType, + clavaJp.filename, this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child)), ); @@ -235,12 +236,12 @@ export default class ClavaAstConverter implements GenericAstConverter { return text.replace(/[&<>]/g, (match) => specialCharMap[match]); } - private getSpanTags(attrs: string[]): string[] { + private getSpanTags(...attrs: string[]): string[] { return [``, '']; } private getNodeCodeTags(nodeId: string): string[] { - return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); + return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } private linkCodeToAstNodes(root: CodeNode, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 2c39f07fb..8e0ba075e 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -109,7 +109,7 @@ export default class ClavaAstConverter { } getToolAst(root) { const clavaJp = root; - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child))); + return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.filename, this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child))); } toCodeNode(jp) { return { @@ -185,11 +185,11 @@ export default class ClavaAstConverter { }; return text.replace(/[&<>]/g, (match) => specialCharMap[match]); } - getSpanTags(attrs) { + getSpanTags(...attrs) { return [``, '']; } getNodeCodeTags(nodeId) { - return this.getSpanTags(['class="node-code"', `data-node-id="${nodeId}"`]); + return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } linkCodeToAstNodes(root, outerCode, outerCodeStart, outerCodeEnd) { const nodeCode = root.code; From ab49f54946ed9209eddc0c3cd598a9015c9f7cd5 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 08:11:14 +0100 Subject: [PATCH 15/36] Fix repeated node ids --- .../visualization/ClavaAstConverter.ts | 42 ++++++++++++------- .../clava/visualization/ClavaAstConverter.js | 26 ++++++++---- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index e81f71202..566146b4a 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -5,6 +5,7 @@ import { AdjustedType, Body, BoolLiteral, Call, Class, DeclStmt, ExprStmt, FileJ type CodeNode = { jp: Joinpoint; + id: string; code: string; children: CodeNode[]; }; @@ -137,23 +138,32 @@ export default class ClavaAstConverter implements GenericAstConverter { } public getToolAst(root: LaraJoinPoint): ToolJoinPoint { - const clavaJp = root as Joinpoint; - - return new ToolJoinPoint( - clavaJp.astId, - clavaJp.joinPointType, - clavaJp.filename, - this.getJoinPointInfo(clavaJp), - clavaJp.children.map(child => this.getToolAst(child)), - ); + let nextId = 0; + const toToolJoinPoint = (jp: Joinpoint): ToolJoinPoint => { + return new ToolJoinPoint( + (nextId++).toString(), + jp.joinPointType, + jp.filename, + this.getJoinPointInfo(jp), + jp.children.map(child => toToolJoinPoint(child)), + ); + }; + + return toToolJoinPoint(root as Joinpoint); } private toCodeNode(jp: Joinpoint): CodeNode { - return { - jp: jp, - code: jp.code.trim(), - children: jp.children.map(child => this.toCodeNode(child)), + let nextId = 0; + const toCodeNode = (jp: Joinpoint): CodeNode => { + return { + jp: jp, + id: (nextId++).toString(), + code: jp.code.trim(), + children: jp.children.map(child => toCodeNode(child)), + }; }; + + return toCodeNode(jp); } private addIdentation(code: string, indentation: number): string { @@ -208,9 +218,11 @@ export default class ClavaAstConverter implements GenericAstConverter { const newChild = { jp: tagDeclVars.jp, + id: tagDeclVars.id, code: code2, children: [{ jp: typedef.jp, + id: typedef.id, code: code2, children: [], }], @@ -220,7 +232,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Program) { - node.children = node.children.map(file => ({ jp: node.jp, code: file.code, children: [file] })); + node.children = node.children.map(file => ({ jp: node.jp, id: node.id, code: file.code, children: [file] })); } // Divide program code into its files return node; @@ -254,7 +266,7 @@ export default class ClavaAstConverter implements GenericAstConverter { return [outerCodeStart, outerCodeStart, ""]; } - const [openingTag, closingTag] = this.getNodeCodeTags(root.jp.astId); + const [openingTag, closingTag] = this.getNodeCodeTags(root.id); let newCode = openingTag; let newCodeIndex = innerCodeStart; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 8e0ba075e..7e67b50a7 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -108,15 +108,23 @@ export default class ClavaAstConverter { return info; } getToolAst(root) { - const clavaJp = root; - return new ToolJoinPoint(clavaJp.astId, clavaJp.joinPointType, clavaJp.filename, this.getJoinPointInfo(clavaJp), clavaJp.children.map(child => this.getToolAst(child))); + let nextId = 0; + const toToolJoinPoint = (jp) => { + return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); + }; + return toToolJoinPoint(root); } toCodeNode(jp) { - return { - jp: jp, - code: jp.code.trim(), - children: jp.children.map(child => this.toCodeNode(child)), + let nextId = 0; + const toCodeNode = (jp) => { + return { + jp: jp, + id: (nextId++).toString(), + code: jp.code.trim(), + children: jp.children.map(child => toCodeNode(child)), + }; }; + return toCodeNode(jp); } addIdentation(code, indentation) { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); @@ -162,9 +170,11 @@ export default class ClavaAstConverter { tagDeclVars.code = typedef.code = code1; const newChild = { jp: tagDeclVars.jp, + id: tagDeclVars.id, code: code2, children: [{ jp: typedef.jp, + id: typedef.id, code: code2, children: [], }], @@ -173,7 +183,7 @@ export default class ClavaAstConverter { } // Assign typedef code to TagDeclVars and split into two children } if (node.jp instanceof Program) { - node.children = node.children.map(file => ({ jp: node.jp, code: file.code, children: [file] })); + node.children = node.children.map(file => ({ jp: node.jp, id: node.id, code: file.code, children: [file] })); } // Divide program code into its files return node; } @@ -200,7 +210,7 @@ export default class ClavaAstConverter { console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); return [outerCodeStart, outerCodeStart, ""]; } - const [openingTag, closingTag] = this.getNodeCodeTags(root.jp.astId); + const [openingTag, closingTag] = this.getNodeCodeTags(root.id); let newCode = openingTag; let newCodeIndex = innerCodeStart; for (const child of root.children) { From 2baaffeb31a367ec988f3cc1dfb13d20056d3898 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 10:12:22 +0100 Subject: [PATCH 16/36] Start syntax highlighting --- .../visualization/ClavaAstConverter.ts | 41 +++++++++++++++++-- .../clava/visualization/ClavaAstConverter.js | 38 +++++++++++++++-- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 566146b4a..625606de6 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, Body, BoolLiteral, Call, Class, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AdjustedType, Body, BoolLiteral, Call, Class, Comment, Decl, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; type CodeNode = { jp: Joinpoint; @@ -256,6 +256,34 @@ export default class ClavaAstConverter implements GenericAstConverter { return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } + private syntaxHighlight(code: string, node: CodeNode): string { + if (node.jp.astName === "StringLiteral") { + const [openingTag, closingTag] = this.getSpanTags('class="string"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof Literal) { + const [openingTag, closingTag] = this.getSpanTags('class="literal"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof Comment) { + const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof NamedDecl) { + if (node.jp instanceof Vardecl && node.jp.leftJp instanceof Vardecl) + return code; // Ignore variable declarations without type + + const [openingTag, closingTag] = this.getSpanTags('class="type"'); + return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + } + if (node.jp instanceof Decl) { + const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); + return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + } + + return code; + } + private linkCodeToAstNodes(root: CodeNode, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { const nodeCode = root.code; const nodeCodeHtml = this.escapeHtml(nodeCode); @@ -268,17 +296,22 @@ export default class ClavaAstConverter implements GenericAstConverter { const [openingTag, closingTag] = this.getNodeCodeTags(root.id); - let newCode = openingTag; + let newCode = ''; let newCodeIndex = innerCodeStart; + if (root.jp instanceof Vardecl) { + newCodeIndex = outerCode.slice(innerCodeStart, innerCodeEnd).search(/[=;]/) + innerCodeStart + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore variable type and name in declaration for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; newCodeIndex = childCodeEnd; } - newCode += outerCode.slice(newCodeIndex, innerCodeEnd) + closingTag; + newCode += outerCode.slice(newCodeIndex, innerCodeEnd); + newCode = this.syntaxHighlight(newCode, root); - return [innerCodeStart, innerCodeEnd, newCode]; + return [innerCodeStart, innerCodeEnd, openingTag + newCode + closingTag]; } public getPrettyHtmlCode(root: LaraJoinPoint): FilesCode { diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 7e67b50a7..81aa72d9e 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, Body, BoolLiteral, Call, Class, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AdjustedType, Body, BoolLiteral, Call, Class, Comment, Decl, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -201,6 +201,31 @@ export default class ClavaAstConverter { getNodeCodeTags(nodeId) { return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } + syntaxHighlight(code, node) { + if (node.jp.astName === "StringLiteral") { + const [openingTag, closingTag] = this.getSpanTags('class="string"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof Literal) { + const [openingTag, closingTag] = this.getSpanTags('class="literal"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof Comment) { + const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + return openingTag + code + closingTag; + } + if (node.jp instanceof NamedDecl) { + if (node.jp instanceof Vardecl && node.jp.leftJp instanceof Vardecl) + return code; // Ignore variable declarations without type + const [openingTag, closingTag] = this.getSpanTags('class="type"'); + return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + } + if (node.jp instanceof Decl) { + const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); + return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + } + return code; + } linkCodeToAstNodes(root, outerCode, outerCodeStart, outerCodeEnd) { const nodeCode = root.code; const nodeCodeHtml = this.escapeHtml(nodeCode); @@ -211,15 +236,20 @@ export default class ClavaAstConverter { return [outerCodeStart, outerCodeStart, ""]; } const [openingTag, closingTag] = this.getNodeCodeTags(root.id); - let newCode = openingTag; + let newCode = ''; let newCodeIndex = innerCodeStart; + if (root.jp instanceof Vardecl) { + newCodeIndex = outerCode.slice(innerCodeStart, innerCodeEnd).search(/[=;]/) + innerCodeStart + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore variable type and name in declaration for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; newCodeIndex = childCodeEnd; } - newCode += outerCode.slice(newCodeIndex, innerCodeEnd) + closingTag; - return [innerCodeStart, innerCodeEnd, newCode]; + newCode += outerCode.slice(newCodeIndex, innerCodeEnd); + newCode = this.syntaxHighlight(newCode, root); + return [innerCodeStart, innerCodeEnd, openingTag + newCode + closingTag]; } getPrettyHtmlCode(root) { const rootCodeNode = this.toCodeNode(root); From a09351033de3ffdf44efde29a0b915359a41024e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 16:38:04 +0100 Subject: [PATCH 17/36] Implement syntax highlighting --- .../visualization/ClavaAstConverter.ts | 56 +++++++++++++++---- .../clava/visualization/ClavaAstConverter.js | 48 +++++++++++++--- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 625606de6..7487ee189 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, Body, BoolLiteral, Call, Class, Comment, Decl, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, Expression, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; type CodeNode = { jp: Joinpoint; @@ -269,16 +269,48 @@ export default class ClavaAstConverter implements GenericAstConverter { const [openingTag, closingTag] = this.getSpanTags('class="comment"'); return openingTag + code + closingTag; } - if (node.jp instanceof NamedDecl) { - if (node.jp instanceof Vardecl && node.jp.leftJp instanceof Vardecl) - return code; // Ignore variable declarations without type - - const [openingTag, closingTag] = this.getSpanTags('class="type"'); - return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); - } - if (node.jp instanceof Decl) { + if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); - return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + + if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case + || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier) { + + return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + } + + if (node.jp instanceof If) { + const elsePos = code.indexOf('else'); + if (elsePos !== -1) { + return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); + } else { + return openingTag + 'if' + closingTag + code.slice(2); + } + } + + if (node.jp instanceof Loop) { + if (node.jp.kind == "dowhile") { + const loopBodyEndPos = code.indexOf(node.children[0].code) + node.children[0].code.length; + const whilePos = code.indexOf('while', loopBodyEndPos); + return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); + } else { + return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + } + } + + if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { + return openingTag + 'typedef' + closingTag + code.slice(7); + } + + if (node.jp instanceof RecordJp) { + return code.replace(/^(.*?)(class(?!=)|struct)(.*)$/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + } + + if (node.jp instanceof Include || node.jp instanceof Pragma) { + return code.replace(/^(#\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); + } + + } return code; @@ -302,6 +334,10 @@ export default class ClavaAstConverter implements GenericAstConverter { newCodeIndex = outerCode.slice(innerCodeStart, innerCodeEnd).search(/[=;]/) + innerCodeStart + 1; newCode += outerCode.slice(innerCodeStart, newCodeIndex); } // Ignore variable type and name in declaration + if (root.jp instanceof FunctionJp) { + newCodeIndex = outerCode.indexOf('(', innerCodeStart) + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore function return type and name in declaration for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 81aa72d9e..4954285e3 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AdjustedType, Body, BoolLiteral, Call, Class, Comment, Decl, DeclStmt, ExprStmt, FileJp, FloatLiteral, Include, IntLiteral, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, Scope, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, DeclStmt, EnumDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -214,15 +214,41 @@ export default class ClavaAstConverter { const [openingTag, closingTag] = this.getSpanTags('class="comment"'); return openingTag + code + closingTag; } - if (node.jp instanceof NamedDecl) { - if (node.jp instanceof Vardecl && node.jp.leftJp instanceof Vardecl) - return code; // Ignore variable declarations without type - const [openingTag, closingTag] = this.getSpanTags('class="type"'); - return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); - } - if (node.jp instanceof Decl) { + if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); - return code.replace(/^(\S*)(\s.*)$/s, `${openingTag}$1${closingTag}$2`); + if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case + || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier) { + return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + } + if (node.jp instanceof If) { + const elsePos = code.indexOf('else'); + if (elsePos !== -1) { + return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); + } + else { + return openingTag + 'if' + closingTag + code.slice(2); + } + } + if (node.jp instanceof Loop) { + if (node.jp.kind == "dowhile") { + const loopBodyEndPos = code.indexOf(node.children[0].code) + node.children[0].code.length; + const whilePos = code.indexOf('while', loopBodyEndPos); + return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); + } + else { + return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + } + } + if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { + return openingTag + 'typedef' + closingTag + code.slice(7); + } + if (node.jp instanceof RecordJp) { + return code.replace(/^(.*?)(class(?!=)|struct)(.*)$/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + } + if (node.jp instanceof Include || node.jp instanceof Pragma) { + return code.replace(/^(#\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); + } } return code; } @@ -242,6 +268,10 @@ export default class ClavaAstConverter { newCodeIndex = outerCode.slice(innerCodeStart, innerCodeEnd).search(/[=;]/) + innerCodeStart + 1; newCode += outerCode.slice(innerCodeStart, newCodeIndex); } // Ignore variable type and name in declaration + if (root.jp instanceof FunctionJp) { + newCodeIndex = outerCode.indexOf('(', innerCodeStart) + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore function return type and name in declaration for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; From a18ef5c0310231249c54be91000718c3c09c98af Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 00:52:34 +0100 Subject: [PATCH 18/36] Fix some errors and make code in code node nullable --- .../visualization/ClavaAstConverter.ts | 54 ++++++++++--------- .../clava/visualization/ClavaAstConverter.js | 29 ++++++---- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 7487ee189..79972d6e2 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -6,7 +6,7 @@ import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Cl type CodeNode = { jp: Joinpoint; id: string; - code: string; + code: string | undefined; children: CodeNode[]; }; @@ -117,17 +117,10 @@ export default class ClavaAstConverter implements GenericAstConverter { if (jp instanceof Loop) { Object.assign(info, { - 'Loop ID': jp.id, 'Loop kind': jp.kind, }); } - if (jp instanceof Marker) { - Object.assign(info, { - 'Marker ID': jp.id, - }); - } - if (jp instanceof Omp) { Object.assign(info, { 'Omp kind': jp.kind, @@ -155,10 +148,18 @@ export default class ClavaAstConverter implements GenericAstConverter { private toCodeNode(jp: Joinpoint): CodeNode { let nextId = 0; const toCodeNode = (jp: Joinpoint): CodeNode => { + let code; + try { + code = jp.code.trim(); + } catch (e) { + console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); + code = undefined; + } + return { jp: jp, id: (nextId++).toString(), - code: jp.code.trim(), + code: code, children: jp.children.map(child => toCodeNode(child)), }; }; @@ -179,20 +180,21 @@ export default class ClavaAstConverter implements GenericAstConverter { } private refineCode(node: CodeNode, indentation: number = 0): CodeNode { - node.code = this.addIdentation(node.code, indentation); + if (node.code) + node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); if (node.jp instanceof Loop) { node.children .filter(child => child.jp instanceof ExprStmt) - .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + .forEach(child => child.code = child.code!.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } if (node.jp instanceof DeclStmt) { node.children .slice(1) .forEach(child => { - child.code = child.code.match(/^(?:\S+\s+)(\S.*)$/)![1]; + child.code = child.code!.match(/^(?:\S+\s+)(\S.*)$/)![1]; }); // Remove type from variable declarations } @@ -202,7 +204,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Body && (node.jp as Body).naked) { - const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); + const match = node.code!.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; node.code = statement + ' ' + comment; @@ -213,7 +215,7 @@ export default class ClavaAstConverter implements GenericAstConverter { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; if (typedef.jp instanceof TypedefDecl) { - const [, code1, code2] = typedef.code.match(/^(.*\S)\s+(\S+)$/)!; + const [, code1, code2] = typedef.code!.match(/^(.*\S)\s+(\S+)$/)!; tagDeclVars.code = typedef.code = code1; const newChild = { @@ -256,7 +258,10 @@ export default class ClavaAstConverter implements GenericAstConverter { return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } - private syntaxHighlight(code: string, node: CodeNode): string { + private syntaxHighlight(code: string | undefined, node: CodeNode): string | undefined { + if (code === undefined) + return undefined; + if (node.jp.astName === "StringLiteral") { const [openingTag, closingTag] = this.getSpanTags('class="string"'); return openingTag + code + closingTag; @@ -284,13 +289,13 @@ export default class ClavaAstConverter implements GenericAstConverter { if (elsePos !== -1) { return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } else { - return openingTag + 'if' + closingTag + code.slice(2); + return openingTag + 'if' + closingTag + code.slice(2); } } if (node.jp instanceof Loop) { if (node.jp.kind == "dowhile") { - const loopBodyEndPos = code.indexOf(node.children[0].code) + node.children[0].code.length; + const loopBodyEndPos = code.indexOf(node.children[0].code!) + node.children[0].code!.length; const whilePos = code.indexOf('while', loopBodyEndPos); return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { @@ -298,7 +303,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } } - if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { + if (node.jp instanceof TypedefDecl && node.code!.startsWith('typedef')) { return openingTag + 'typedef' + closingTag + code.slice(7); } @@ -309,15 +314,16 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Include || node.jp instanceof Pragma) { return code.replace(/^(#\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); } - - } return code; } - private linkCodeToAstNodes(root: CodeNode, outerCode: string, outerCodeStart: number, outerCodeEnd: number): any[] { + private linkCodeToAstNodes(root: CodeNode, outerCode: string | undefined, outerCodeStart: number, outerCodeEnd: number): any[] { const nodeCode = root.code; + if (!nodeCode || !outerCode) + return [outerCodeStart, outerCodeStart, ""]; + const nodeCodeHtml = this.escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; @@ -345,7 +351,7 @@ export default class ClavaAstConverter implements GenericAstConverter { newCodeIndex = childCodeEnd; } newCode += outerCode.slice(newCodeIndex, innerCodeEnd); - newCode = this.syntaxHighlight(newCode, root); + newCode = this.syntaxHighlight(newCode, root)!; return [innerCodeStart, innerCodeEnd, openingTag + newCode + closingTag]; } @@ -359,7 +365,7 @@ export default class ClavaAstConverter implements GenericAstConverter { const file = child.children[0]; const filename = (file.jp as FileJp).name; - const fileCode = child.code; // same as file.code + const fileCode = child.code!; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; return [filename, fileLinkedHtmlCode]; @@ -367,7 +373,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } else { const filename = (root as Joinpoint).filename; const code = rootCodeNode.code; - const htmlCode = this.escapeHtml(code); + const htmlCode = code ? this.escapeHtml(code) : ""; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; return { [filename]: linkedHtmlCode }; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 4954285e3..9d090f6d1 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, DeclStmt, EnumDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, DeclStmt, EnumDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -91,15 +91,9 @@ export default class ClavaAstConverter { } if (jp instanceof Loop) { Object.assign(info, { - 'Loop ID': jp.id, 'Loop kind': jp.kind, }); } - if (jp instanceof Marker) { - Object.assign(info, { - 'Marker ID': jp.id, - }); - } if (jp instanceof Omp) { Object.assign(info, { 'Omp kind': jp.kind, @@ -117,10 +111,18 @@ export default class ClavaAstConverter { toCodeNode(jp) { let nextId = 0; const toCodeNode = (jp) => { + let code; + try { + code = jp.code.trim(); + } + catch (e) { + console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); + code = undefined; + } return { jp: jp, id: (nextId++).toString(), - code: jp.code.trim(), + code: code, children: jp.children.map(child => toCodeNode(child)), }; }; @@ -137,7 +139,8 @@ export default class ClavaAstConverter { }); } refineCode(node, indentation = 0) { - node.code = this.addIdentation(node.code, indentation); + if (node.code) + node.code = this.addIdentation(node.code, indentation); this.sortByLocation(node.children); if (node.jp instanceof Loop) { node.children @@ -202,6 +205,8 @@ export default class ClavaAstConverter { return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); } syntaxHighlight(code, node) { + if (code === undefined) + return undefined; if (node.jp.astName === "StringLiteral") { const [openingTag, closingTag] = this.getSpanTags('class="string"'); return openingTag + code + closingTag; @@ -254,6 +259,8 @@ export default class ClavaAstConverter { } linkCodeToAstNodes(root, outerCode, outerCodeStart, outerCodeEnd) { const nodeCode = root.code; + if (!nodeCode || !outerCode) + return [outerCodeStart, outerCodeStart, ""]; const nodeCodeHtml = this.escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; @@ -288,7 +295,7 @@ export default class ClavaAstConverter { return Object.fromEntries(rootCodeNode.children.map(child => { const file = child.children[0]; const filename = file.jp.name; - const fileCode = child.code; // same as file.code + const fileCode = child.code; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; return [filename, fileLinkedHtmlCode]; @@ -297,7 +304,7 @@ export default class ClavaAstConverter { else { const filename = root.filename; const code = rootCodeNode.code; - const htmlCode = this.escapeHtml(code); + const htmlCode = code ? this.escapeHtml(code) : ""; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; return { [filename]: linkedHtmlCode }; } From 4ce89a993d9d5c368ce9806cc40f87a3af149254 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 01:29:23 +0100 Subject: [PATCH 19/36] Add code to ToolJoinPoint --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 9 +++++++++ .../src-lara/clava/visualization/ClavaAstConverter.js | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 79972d6e2..0643bd372 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -133,9 +133,18 @@ export default class ClavaAstConverter implements GenericAstConverter { public getToolAst(root: LaraJoinPoint): ToolJoinPoint { let nextId = 0; const toToolJoinPoint = (jp: Joinpoint): ToolJoinPoint => { + let code; + try { + code = jp.code.trim(); + } catch (e) { + console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); + code = undefined; + } + return new ToolJoinPoint( (nextId++).toString(), jp.joinPointType, + code ?? '', jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child)), diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 9d090f6d1..c08407ad9 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -104,7 +104,15 @@ export default class ClavaAstConverter { getToolAst(root) { let nextId = 0; const toToolJoinPoint = (jp) => { - return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); + let code; + try { + code = jp.code.trim(); + } + catch (e) { + console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); + code = undefined; + } + return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, code ?? '', jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); }; return toToolJoinPoint(root); } From 66d2c7dabf0b59aae58c77b39cc871538c8383b9 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 01:57:58 +0100 Subject: [PATCH 20/36] Fix else matching in syntax highlighting --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 2 +- ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 0643bd372..da668d79e 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -294,7 +294,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof If) { - const elsePos = code.indexOf('else'); + const elsePos = code.search(/(?))\belse\b/); if (elsePos !== -1) { return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } else { diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index c08407ad9..fbf08ae3d 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -235,7 +235,7 @@ export default class ClavaAstConverter { return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word } if (node.jp instanceof If) { - const elsePos = code.indexOf('else'); + const elsePos = code.search(/(?))\belse\b/); if (elsePos !== -1) { return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } From 85315ff4b1873ef925610d25c2932dceaf60aa96 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 02:20:27 +0100 Subject: [PATCH 21/36] Fix comment and do-while loop syntax highlighting --- .../src-api/visualization/ClavaAstConverter.ts | 15 +++++++++------ .../clava/visualization/ClavaAstConverter.js | 12 ++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index da668d79e..b502f2992 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -279,10 +279,14 @@ export default class ClavaAstConverter implements GenericAstConverter { const [openingTag, closingTag] = this.getSpanTags('class="literal"'); return openingTag + code + closingTag; } - if (node.jp instanceof Comment) { - const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + + const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + if (node.jp instanceof Comment) return openingTag + code + closingTag; - } + + code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); + code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); + if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); @@ -294,7 +298,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof If) { - const elsePos = code.search(/(?))\belse\b/); + const elsePos = code.search(/(?)\belse\b/); if (elsePos !== -1) { return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } else { @@ -304,8 +308,7 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Loop) { if (node.jp.kind == "dowhile") { - const loopBodyEndPos = code.indexOf(node.children[0].code!) + node.children[0].code!.length; - const whilePos = code.indexOf('while', loopBodyEndPos); + const whilePos = code.search(/(?)\while\b/); return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index fbf08ae3d..65d9fbf49 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -223,10 +223,11 @@ export default class ClavaAstConverter { const [openingTag, closingTag] = this.getSpanTags('class="literal"'); return openingTag + code + closingTag; } - if (node.jp instanceof Comment) { - const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + if (node.jp instanceof Comment) return openingTag + code + closingTag; - } + code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); + code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case @@ -235,7 +236,7 @@ export default class ClavaAstConverter { return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word } if (node.jp instanceof If) { - const elsePos = code.search(/(?))\belse\b/); + const elsePos = code.search(/(?)\belse\b/); if (elsePos !== -1) { return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } @@ -245,8 +246,7 @@ export default class ClavaAstConverter { } if (node.jp instanceof Loop) { if (node.jp.kind == "dowhile") { - const loopBodyEndPos = code.indexOf(node.children[0].code) + node.children[0].code.length; - const whilePos = code.indexOf('while', loopBodyEndPos); + const whilePos = code.search(/(?)\while\b/); return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { From 31aa5d461908e6e368e9e5330f7ddbcde4ebfbfe Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 02:55:24 +0100 Subject: [PATCH 22/36] Implement syntax highlighting of types and fix some bugs --- .../visualization/ClavaAstConverter.ts | 19 ++++++++++++++----- .../clava/visualization/ClavaAstConverter.js | 19 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index b502f2992..5d9c66d85 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -286,15 +286,24 @@ export default class ClavaAstConverter implements GenericAstConverter { code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); + + if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { + const [openingTag, closingTag] = this.getSpanTags('class="type"'); + + const regex = new RegExp(`\\s*[&*]*${node.jp.name}\\b`); + const namePos = code.search(regex); + return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); + } if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt - || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier) { + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier + || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { - return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { @@ -311,11 +320,11 @@ export default class ClavaAstConverter implements GenericAstConverter { const whilePos = code.search(/(?)\while\b/); return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { - return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word } } - if (node.jp instanceof TypedefDecl && node.code!.startsWith('typedef')) { + if (node.jp instanceof TypedefDecl && node.code!.startsWith('typedef')) { // The code of a TypedefDecl can be divided, and the second part does not have the keyword return openingTag + 'typedef' + closingTag + code.slice(7); } @@ -324,7 +333,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); + return code.replace(/^(#\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); } } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 65d9fbf49..ccbbe58c5 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, DeclStmt, EnumDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; export default class ClavaAstConverter { getJoinPointInfo(jp) { const info = { @@ -228,12 +228,19 @@ export default class ClavaAstConverter { return openingTag + code + closingTag; code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); + if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { + const [openingTag, closingTag] = this.getSpanTags('class="type"'); + const regex = new RegExp(`\\s*[&*]*${node.jp.name}\\b`); + const namePos = code.search(regex); + return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); + } if (node.jp instanceof Statement || node.jp instanceof Decl) { const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt - || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier) { - return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier + || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { + return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { const elsePos = code.search(/(?)\belse\b/); @@ -250,17 +257,17 @@ export default class ClavaAstConverter { return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { - return code.replace(/^(\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); // Highlight first word + return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word } } - if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { + if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { // The code of a TypedefDecl can be divided, and the second part does not have the keyword return openingTag + 'typedef' + closingTag + code.slice(7); } if (node.jp instanceof RecordJp) { return code.replace(/^(.*?)(class(?!=)|struct)(.*)$/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)(\W.*)$/s, `${openingTag}$1${closingTag}$2`); + return code.replace(/^(#\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); } } return code; From f03c8ab15c81b17e12f6b5da7af977e075a43a1d Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 12:41:54 +0100 Subject: [PATCH 23/36] Fix syntax highlighting of else and do-while with comments --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 13 +++++++------ .../clava/visualization/ClavaAstConverter.js | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 5d9c66d85..afee1306c 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -303,12 +303,13 @@ export default class ClavaAstConverter implements GenericAstConverter { || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { - return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/s, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { - const elsePos = code.search(/(?)\belse\b/); - if (elsePos !== -1) { + const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); + if (elseMatch) { + const elsePos = elseMatch[1].length; return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } else { return openingTag + 'if' + closingTag + code.slice(2); @@ -317,7 +318,7 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Loop) { if (node.jp.kind == "dowhile") { - const whilePos = code.search(/(?)\while\b/); + const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)![1].length; return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word @@ -329,11 +330,11 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof RecordJp) { - return code.replace(/^(.*?)(class(?!=)|struct)(.*)$/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + return code.replace(/(class(?!=)|struct)/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/s, `${openingTag}$1${closingTag}`); } } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index ccbbe58c5..b53f57088 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -240,11 +240,12 @@ export default class ClavaAstConverter { || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { - return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/s, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { - const elsePos = code.search(/(?)\belse\b/); - if (elsePos !== -1) { + const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); + if (elseMatch) { + const elsePos = elseMatch[1].length; return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); } else { @@ -253,7 +254,7 @@ export default class ClavaAstConverter { } if (node.jp instanceof Loop) { if (node.jp.kind == "dowhile") { - const whilePos = code.search(/(?)\while\b/); + const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)[1].length; return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { @@ -264,10 +265,10 @@ export default class ClavaAstConverter { return openingTag + 'typedef' + closingTag + code.slice(7); } if (node.jp instanceof RecordJp) { - return code.replace(/^(.*?)(class(?!=)|struct)(.*)$/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + return code.replace(/(class(?!=)|struct)/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/s, `${openingTag}$1${closingTag}`); } } return code; From 101a689922d8fa1b588fdda4f2d66c5d4fa1eb09 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 12:48:09 +0100 Subject: [PATCH 24/36] Fix class and struct syntax highlighting --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 8 ++++---- .../src-lara/clava/visualization/ClavaAstConverter.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index afee1306c..0a21bfa5a 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -303,7 +303,7 @@ export default class ClavaAstConverter implements GenericAstConverter { || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { - return code.replace(/^(\w+)\b/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { @@ -321,7 +321,7 @@ export default class ClavaAstConverter implements GenericAstConverter { const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)![1].length; return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { - return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } } @@ -330,11 +330,11 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof RecordJp) { - return code.replace(/(class(?!=)|struct)/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + return code.replace(/(class(?!=)|struct)/, `${openingTag}$1${closingTag}`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)\b/s, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); } } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index b53f57088..4022a37b0 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -240,7 +240,7 @@ export default class ClavaAstConverter { || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { - return code.replace(/^(\w+)\b/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); @@ -258,17 +258,17 @@ export default class ClavaAstConverter { return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { - return code.replace(/^(\w+)(?=\W)/s, `${openingTag}$1${closingTag}`); // Highlight first word + return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } } if (node.jp instanceof TypedefDecl && node.code.startsWith('typedef')) { // The code of a TypedefDecl can be divided, and the second part does not have the keyword return openingTag + 'typedef' + closingTag + code.slice(7); } if (node.jp instanceof RecordJp) { - return code.replace(/(class(?!=)|struct)/s, `$1${openingTag}$2${closingTag}$3`); // Highlight 'class' or 'struct' in declaration + return code.replace(/(class(?!=)|struct)/, `${openingTag}$1${closingTag}`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)\b/s, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); } } return code; From d8919bec05afebc9b5cb6883fe25cccd52efdf35 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 14:48:54 +0100 Subject: [PATCH 25/36] Fix type syntax highlighting --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 2 +- ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 0a21bfa5a..aabbea2e2 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -290,7 +290,7 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = this.getSpanTags('class="type"'); - const regex = new RegExp(`\\s*[&*]*${node.jp.name}\\b`); + const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 4022a37b0..a4b88fcfa 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -230,7 +230,7 @@ export default class ClavaAstConverter { code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = this.getSpanTags('class="type"'); - const regex = new RegExp(`\\s*[&*]*${node.jp.name}\\b`); + const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } From 079f61f8d16f03127b5759fc31003378bc2e6b46 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 15:00:07 +0100 Subject: [PATCH 26/36] Make ToolJoinPoint code nullable --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index aabbea2e2..d4cf753ba 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -144,7 +144,7 @@ export default class ClavaAstConverter implements GenericAstConverter { return new ToolJoinPoint( (nextId++).toString(), jp.joinPointType, - code ?? '', + code, jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child)), From 5bf40814119cc71b4a42a921d50f0f618a329c23 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 15:05:32 +0100 Subject: [PATCH 27/36] Switch from filename to filepath --- .../src-api/visualization/ClavaAstConverter.ts | 14 +++++++------- .../clava/visualization/ClavaAstConverter.js | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index d4cf753ba..546ec4c2e 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, Expression, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; type CodeNode = { jp: Joinpoint; @@ -145,7 +145,7 @@ export default class ClavaAstConverter implements GenericAstConverter { (nextId++).toString(), jp.joinPointType, code, - jp.filename, + jp.filepath, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child)), ); @@ -385,20 +385,20 @@ export default class ClavaAstConverter implements GenericAstConverter { if (root instanceof Program) { return Object.fromEntries(rootCodeNode.children.map(child => { const file = child.children[0]; - const filename = (file.jp as FileJp).name; + const filepath = (file.jp as FileJp).path; const fileCode = child.code!; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; - return [filename, fileLinkedHtmlCode]; + return [filepath, fileLinkedHtmlCode]; })); } else { - const filename = (root as Joinpoint).filename; + const filepath = (root as Joinpoint).filepath; const code = rootCodeNode.code; - const htmlCode = code ? this.escapeHtml(code) : ""; + const htmlCode = code ? this.escapeHtml(code) : ''; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; - return { [filename]: linkedHtmlCode }; + return { [filepath]: linkedHtmlCode }; } } } \ No newline at end of file diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index a4b88fcfa..5924fd75b 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -112,7 +112,7 @@ export default class ClavaAstConverter { console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); code = undefined; } - return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, code ?? '', jp.filename, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); + return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, code, jp.filepath, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); }; return toToolJoinPoint(root); } @@ -310,19 +310,19 @@ export default class ClavaAstConverter { if (root instanceof Program) { return Object.fromEntries(rootCodeNode.children.map(child => { const file = child.children[0]; - const filename = file.jp.name; + const filepath = file.jp.path; const fileCode = child.code; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; - return [filename, fileLinkedHtmlCode]; + return [filepath, fileLinkedHtmlCode]; })); } else { - const filename = root.filename; + const filepath = root.filepath; const code = rootCodeNode.code; - const htmlCode = code ? this.escapeHtml(code) : ""; + const htmlCode = code ? this.escapeHtml(code) : ''; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; - return { [filename]: linkedHtmlCode }; + return { [filepath]: linkedHtmlCode }; } } } From 841bb25815ea2f9978ef3b0bf4a447ebe45eb551 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 16:09:59 +0100 Subject: [PATCH 28/36] Fix filepath with single file --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 3 ++- ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 546ec4c2e..c982ab9fa 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -385,7 +385,8 @@ export default class ClavaAstConverter implements GenericAstConverter { if (root instanceof Program) { return Object.fromEntries(rootCodeNode.children.map(child => { const file = child.children[0]; - const filepath = (file.jp as FileJp).path; + const fileJp = file.jp as FileJp; + const filepath = fileJp.filepath; const fileCode = child.code!; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 5924fd75b..af86ce4f1 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -310,7 +310,8 @@ export default class ClavaAstConverter { if (root instanceof Program) { return Object.fromEntries(rootCodeNode.children.map(child => { const file = child.children[0]; - const filepath = file.jp.path; + const fileJp = file.jp; + const filepath = fileJp.filepath; const fileCode = child.code; // same as file.code! const fileHtmlCode = this.escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; From 5416271cc6c05e37d5062e6c01defeac2c658b0f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 17:37:41 +0100 Subject: [PATCH 29/36] Pass AST rebuilding to Clava --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 5 +++++ .../src-lara/clava/visualization/ClavaAstConverter.js | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index c982ab9fa..367f66a49 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -2,6 +2,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import Clava from "../clava/Clava.js"; type CodeNode = { jp: Joinpoint; @@ -11,6 +12,10 @@ type CodeNode = { }; export default class ClavaAstConverter implements GenericAstConverter { + public updateAst(): void { + Clava.rebuild(); + } + private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { const info: JoinPointInfo = { 'AST ID': jp.astId, diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index af86ce4f1..1c3cef766 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,6 +1,10 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import Clava from "../clava/Clava.js"; export default class ClavaAstConverter { + updateAst() { + Clava.rebuild(); + } getJoinPointInfo(jp) { const info = { 'AST ID': jp.astId, From 2516e905cfeea1414274390643cf15dd78c74eb4 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 23:56:08 +0100 Subject: [PATCH 30/36] Pass generic operations of ClavaAstConverter to LARA --- .../visualization/ClavaAstConverter.ts | 80 +++++++------------ .../clava/visualization/ClavaAstConverter.js | 73 +++++++---------- 2 files changed, 55 insertions(+), 98 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 367f66a49..21e3dbb70 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -3,6 +3,7 @@ import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/Generi import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; +import { addIdentation, escapeHtml, getNodeCodeTags, getSpanTags } from "lara-js/api/visualization/AstConverterUtils.js" type CodeNode = { jp: Joinpoint; @@ -16,6 +17,19 @@ export default class ClavaAstConverter implements GenericAstConverter { Clava.rebuild(); } + private getCode(node: Joinpoint): string | undefined { + // When hasCode implementation is ready, replace this method with the following line: + // return node.hasCode ? node.code.trim() : undefined + let code; + try { + code = node.code.trim(); + } catch (e) { + console.error(`Could not get code of node '${node.joinPointType}': ${e}`); + code = undefined; + } + return code; + } + private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { const info: JoinPointInfo = { 'AST ID': jp.astId, @@ -138,18 +152,10 @@ export default class ClavaAstConverter implements GenericAstConverter { public getToolAst(root: LaraJoinPoint): ToolJoinPoint { let nextId = 0; const toToolJoinPoint = (jp: Joinpoint): ToolJoinPoint => { - let code; - try { - code = jp.code.trim(); - } catch (e) { - console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); - code = undefined; - } - return new ToolJoinPoint( (nextId++).toString(), jp.joinPointType, - code, + this.getCode(jp), jp.filepath, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child)), @@ -162,18 +168,10 @@ export default class ClavaAstConverter implements GenericAstConverter { private toCodeNode(jp: Joinpoint): CodeNode { let nextId = 0; const toCodeNode = (jp: Joinpoint): CodeNode => { - let code; - try { - code = jp.code.trim(); - } catch (e) { - console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); - code = undefined; - } - return { jp: jp, id: (nextId++).toString(), - code: code, + code: this.getCode(jp), children: jp.children.map(child => toCodeNode(child)), }; }; @@ -181,10 +179,6 @@ export default class ClavaAstConverter implements GenericAstConverter { return toCodeNode(jp); } - private addIdentation(code: string, indentation: number): string { - return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); - } - private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { return codeNodes.sort((node1, node2) => { if (node1.jp.line === node2.jp.line) @@ -195,7 +189,7 @@ export default class ClavaAstConverter implements GenericAstConverter { private refineCode(node: CodeNode, indentation: number = 0): CodeNode { if (node.code) - node.code = this.addIdentation(node.code, indentation); + node.code = addIdentation(node.code, indentation); this.sortByLocation(node.children); if (node.jp instanceof Loop) { @@ -254,46 +248,27 @@ export default class ClavaAstConverter implements GenericAstConverter { return node; } - private escapeHtml(text: string): string { - const specialCharMap: { [char: string]: string } = { - '&': '&', - '<': '<', - '>': '>', - }; - - return text.replace(/[&<>]/g, (match) => specialCharMap[match]); - } - - private getSpanTags(...attrs: string[]): string[] { - return [``, '']; - } - - private getNodeCodeTags(nodeId: string): string[] { - return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); - } - private syntaxHighlight(code: string | undefined, node: CodeNode): string | undefined { if (code === undefined) return undefined; if (node.jp.astName === "StringLiteral") { - const [openingTag, closingTag] = this.getSpanTags('class="string"'); + const [openingTag, closingTag] = getSpanTags('class="string"'); return openingTag + code + closingTag; } if (node.jp instanceof Literal) { - const [openingTag, closingTag] = this.getSpanTags('class="literal"'); + const [openingTag, closingTag] = getSpanTags('class="literal"'); return openingTag + code + closingTag; } - const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + const [openingTag, closingTag] = getSpanTags('class="comment"'); if (node.jp instanceof Comment) return openingTag + code + closingTag; - code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { - const [openingTag, closingTag] = this.getSpanTags('class="type"'); + const [openingTag, closingTag] = getSpanTags('class="type"'); const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); @@ -301,7 +276,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Statement || node.jp instanceof Decl) { - const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); + const [openingTag, closingTag] = getSpanTags('class="keyword"'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt @@ -351,7 +326,7 @@ export default class ClavaAstConverter implements GenericAstConverter { if (!nodeCode || !outerCode) return [outerCodeStart, outerCodeStart, ""]; - const nodeCodeHtml = this.escapeHtml(nodeCode); + const nodeCodeHtml = escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { @@ -359,10 +334,11 @@ export default class ClavaAstConverter implements GenericAstConverter { return [outerCodeStart, outerCodeStart, ""]; } - const [openingTag, closingTag] = this.getNodeCodeTags(root.id); + const [openingTag, closingTag] = getNodeCodeTags(root.id); let newCode = ''; let newCodeIndex = innerCodeStart; + if (root.jp instanceof Vardecl) { newCodeIndex = outerCode.slice(innerCodeStart, innerCodeEnd).search(/[=;]/) + innerCodeStart + 1; newCode += outerCode.slice(innerCodeStart, newCodeIndex); @@ -394,14 +370,14 @@ export default class ClavaAstConverter implements GenericAstConverter { const filepath = fileJp.filepath; const fileCode = child.code!; // same as file.code! - const fileHtmlCode = this.escapeHtml(fileCode); + const fileHtmlCode = escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; return [filepath, fileLinkedHtmlCode]; - })); + })); // Associate code with each file } else { const filepath = (root as Joinpoint).filepath; const code = rootCodeNode.code; - const htmlCode = code ? this.escapeHtml(code) : ''; + const htmlCode = code ? escapeHtml(code) : ''; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; return { [filepath]: linkedHtmlCode }; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 1c3cef766..baa5f0175 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,10 +1,24 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; +import { addIdentation, escapeHtml, getNodeCodeTags, getSpanTags } from "lara-js/api/visualization/AstConverterUtils.js"; export default class ClavaAstConverter { updateAst() { Clava.rebuild(); } + getCode(node) { + // When hasCode implementation is ready, replace this method with the following line: + // return node.hasCode ? node.code.trim() : undefined + let code; + try { + code = node.code.trim(); + } + catch (e) { + console.error(`Could not get code of node '${node.joinPointType}': ${e}`); + code = undefined; + } + return code; + } getJoinPointInfo(jp) { const info = { 'AST ID': jp.astId, @@ -108,41 +122,22 @@ export default class ClavaAstConverter { getToolAst(root) { let nextId = 0; const toToolJoinPoint = (jp) => { - let code; - try { - code = jp.code.trim(); - } - catch (e) { - console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); - code = undefined; - } - return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, code, jp.filepath, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); + return new ToolJoinPoint((nextId++).toString(), jp.joinPointType, this.getCode(jp), jp.filepath, this.getJoinPointInfo(jp), jp.children.map(child => toToolJoinPoint(child))); }; return toToolJoinPoint(root); } toCodeNode(jp) { let nextId = 0; const toCodeNode = (jp) => { - let code; - try { - code = jp.code.trim(); - } - catch (e) { - console.error(`Could not get code of node '${jp.joinPointType}': ${e}`); - code = undefined; - } return { jp: jp, id: (nextId++).toString(), - code: code, + code: this.getCode(jp), children: jp.children.map(child => toCodeNode(child)), }; }; return toCodeNode(jp); } - addIdentation(code, indentation) { - return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); - } sortByLocation(codeNodes) { return codeNodes.sort((node1, node2) => { if (node1.jp.line === node2.jp.line) @@ -152,7 +147,7 @@ export default class ClavaAstConverter { } refineCode(node, indentation = 0) { if (node.code) - node.code = this.addIdentation(node.code, indentation); + node.code = addIdentation(node.code, indentation); this.sortByLocation(node.children); if (node.jp instanceof Loop) { node.children @@ -202,44 +197,30 @@ export default class ClavaAstConverter { } // Divide program code into its files return node; } - escapeHtml(text) { - const specialCharMap = { - '&': '&', - '<': '<', - '>': '>', - }; - return text.replace(/[&<>]/g, (match) => specialCharMap[match]); - } - getSpanTags(...attrs) { - return [``, '']; - } - getNodeCodeTags(nodeId) { - return this.getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); - } syntaxHighlight(code, node) { if (code === undefined) return undefined; if (node.jp.astName === "StringLiteral") { - const [openingTag, closingTag] = this.getSpanTags('class="string"'); + const [openingTag, closingTag] = getSpanTags('class="string"'); return openingTag + code + closingTag; } if (node.jp instanceof Literal) { - const [openingTag, closingTag] = this.getSpanTags('class="literal"'); + const [openingTag, closingTag] = getSpanTags('class="literal"'); return openingTag + code + closingTag; } - const [openingTag, closingTag] = this.getSpanTags('class="comment"'); + const [openingTag, closingTag] = getSpanTags('class="comment"'); if (node.jp instanceof Comment) return openingTag + code + closingTag; code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { - const [openingTag, closingTag] = this.getSpanTags('class="type"'); + const [openingTag, closingTag] = getSpanTags('class="type"'); const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } if (node.jp instanceof Statement || node.jp instanceof Decl) { - const [openingTag, closingTag] = this.getSpanTags('class="keyword"'); + const [openingTag, closingTag] = getSpanTags('class="keyword"'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier @@ -281,14 +262,14 @@ export default class ClavaAstConverter { const nodeCode = root.code; if (!nodeCode || !outerCode) return [outerCodeStart, outerCodeStart, ""]; - const nodeCodeHtml = this.escapeHtml(nodeCode); + const nodeCodeHtml = escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); return [outerCodeStart, outerCodeStart, ""]; } - const [openingTag, closingTag] = this.getNodeCodeTags(root.id); + const [openingTag, closingTag] = getNodeCodeTags(root.id); let newCode = ''; let newCodeIndex = innerCodeStart; if (root.jp instanceof Vardecl) { @@ -317,15 +298,15 @@ export default class ClavaAstConverter { const fileJp = file.jp; const filepath = fileJp.filepath; const fileCode = child.code; // same as file.code! - const fileHtmlCode = this.escapeHtml(fileCode); + const fileHtmlCode = escapeHtml(fileCode); const fileLinkedHtmlCode = this.linkCodeToAstNodes(child, fileHtmlCode, 0, fileHtmlCode.length)[2]; return [filepath, fileLinkedHtmlCode]; - })); + })); // Associate code with each file } else { const filepath = root.filepath; const code = rootCodeNode.code; - const htmlCode = code ? this.escapeHtml(code) : ''; + const htmlCode = code ? escapeHtml(code) : ''; const linkedHtmlCode = this.linkCodeToAstNodes(rootCodeNode, htmlCode, 0, htmlCode.length)[2]; return { [filepath]: linkedHtmlCode }; } From 2d7b415f9c296e037ca4ed04c0af9aebc7b036eb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 09:49:30 +0100 Subject: [PATCH 31/36] Create getSyntaxHighlightTags --- .../src-api/visualization/ClavaAstConverter.ts | 16 ++++++++-------- .../clava/visualization/ClavaAstConverter.js | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 21e3dbb70..f87e69f55 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -3,7 +3,7 @@ import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/Generi import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; -import { addIdentation, escapeHtml, getNodeCodeTags, getSpanTags } from "lara-js/api/visualization/AstConverterUtils.js" +import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js" type CodeNode = { jp: Joinpoint; @@ -18,7 +18,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } private getCode(node: Joinpoint): string | undefined { - // When hasCode implementation is ready, replace this method with the following line: + // TODO: When hasCode implementation is ready, replace this body with the following line: // return node.hasCode ? node.code.trim() : undefined let code; try { @@ -253,22 +253,22 @@ export default class ClavaAstConverter implements GenericAstConverter { return undefined; if (node.jp.astName === "StringLiteral") { - const [openingTag, closingTag] = getSpanTags('class="string"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('string'); return openingTag + code + closingTag; } if (node.jp instanceof Literal) { - const [openingTag, closingTag] = getSpanTags('class="literal"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('literal'); return openingTag + code + closingTag; } - const [openingTag, closingTag] = getSpanTags('class="comment"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('comment'); if (node.jp instanceof Comment) return openingTag + code + closingTag; code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { - const [openingTag, closingTag] = getSpanTags('class="type"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('type'); const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); @@ -276,7 +276,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Statement || node.jp instanceof Decl) { - const [openingTag, closingTag] = getSpanTags('class="keyword"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('keyword'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt @@ -297,7 +297,7 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Loop) { - if (node.jp.kind == "dowhile") { + if (node.jp.kind == 'dowhile') { const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)![1].length; return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index baa5f0175..060092c57 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,13 +1,13 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; -import { addIdentation, escapeHtml, getNodeCodeTags, getSpanTags } from "lara-js/api/visualization/AstConverterUtils.js"; +import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js"; export default class ClavaAstConverter { updateAst() { Clava.rebuild(); } getCode(node) { - // When hasCode implementation is ready, replace this method with the following line: + // TODO: When hasCode implementation is ready, replace this body with the following line: // return node.hasCode ? node.code.trim() : undefined let code; try { @@ -201,26 +201,26 @@ export default class ClavaAstConverter { if (code === undefined) return undefined; if (node.jp.astName === "StringLiteral") { - const [openingTag, closingTag] = getSpanTags('class="string"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('string'); return openingTag + code + closingTag; } if (node.jp instanceof Literal) { - const [openingTag, closingTag] = getSpanTags('class="literal"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('literal'); return openingTag + code + closingTag; } - const [openingTag, closingTag] = getSpanTags('class="comment"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('comment'); if (node.jp instanceof Comment) return openingTag + code + closingTag; code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { - const [openingTag, closingTag] = getSpanTags('class="type"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('type'); const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } if (node.jp instanceof Statement || node.jp instanceof Decl) { - const [openingTag, closingTag] = getSpanTags('class="keyword"'); + const [openingTag, closingTag] = getSyntaxHighlightTags('keyword'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier @@ -238,7 +238,7 @@ export default class ClavaAstConverter { } } if (node.jp instanceof Loop) { - if (node.jp.kind == "dowhile") { + if (node.jp.kind == 'dowhile') { const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)[1].length; return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } From ea8cfc0de7f114e0022fa7be42c272d6a4c56ba5 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 27 Jul 2024 01:45:38 +0100 Subject: [PATCH 32/36] Write documentation for ClavaAstConverter.ts --- .../visualization/ClavaAstConverter.ts | 106 +++++++++++++++--- .../clava/visualization/ClavaAstConverter.js | 98 +++++++++++++--- 2 files changed, 175 insertions(+), 29 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index f87e69f55..90a665f20 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -5,6 +5,9 @@ import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Cl import Clava from "../clava/Clava.js"; import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js" +/** + * @brief Intermediate AST representation, used to store the (processed) code of each node. + */ type CodeNode = { jp: Joinpoint; id: string; @@ -12,11 +15,21 @@ type CodeNode = { children: CodeNode[]; }; +/** + * @brief Clava specialization of GenericAstConverter. + */ export default class ClavaAstConverter implements GenericAstConverter { public updateAst(): void { Clava.rebuild(); } + /** + * @brief Returns the code of the given node. + * @details If the node has no code, returns undefined, instead of raising an error. + * + * @param node Node + * @returns The code of the node, or undefined if it has no code + */ private getCode(node: Joinpoint): string | undefined { // TODO: When hasCode implementation is ready, replace this body with the following line: // return node.hasCode ? node.code.trim() : undefined @@ -30,6 +43,12 @@ export default class ClavaAstConverter implements GenericAstConverter { return code; } + /** + * @brief Returns the information of the given join point, according to its type. + * + * @param jp Join point + * @returns The information of the join point + */ private getJoinPointInfo(jp: Joinpoint): JoinPointInfo { const info: JoinPointInfo = { 'AST ID': jp.astId, @@ -83,6 +102,7 @@ export default class ClavaAstConverter implements GenericAstConverter { 'Constant?': jp.constant, 'Is builtin': jp.isBuiltin, 'Has sugar': jp.hasSugar, + 'Type kind': jp.kind }); } @@ -165,6 +185,14 @@ export default class ClavaAstConverter implements GenericAstConverter { return toToolJoinPoint(root as Joinpoint); } + /** + * @brief Converts the given join point to a CodeNode. + * @details This function assigns ids to the nodes in pre-order, and it MUST + * be the same as the order used for getToolAst. + * + * @param jp Join point + * @returns The CodeNode representation of the join point + */ private toCodeNode(jp: Joinpoint): CodeNode { let nextId = 0; const toCodeNode = (jp: Joinpoint): CodeNode => { @@ -179,6 +207,15 @@ export default class ClavaAstConverter implements GenericAstConverter { return toCodeNode(jp); } + /** + * @brief Sorts the given code nodes by location. + * @details This function uses the join points' line and column, instead of + * location attribute. If a node does not have a location (line and code), + * it is placed before all the others. + * + * @param codeNodes Array of code nodes + * @returns Reference to the original (sorted) array + */ private sortByLocation(codeNodes: CodeNode[]): CodeNode[] { return codeNodes.sort((node1, node2) => { if (node1.jp.line === node2.jp.line) @@ -187,6 +224,17 @@ export default class ClavaAstConverter implements GenericAstConverter { }); } + /** + * @brief Applies refinements and corrections to the node code and order of + * the children. + * @details This is necessary because some nodes do not have its designated + * code equal to the matching code in the container. For example, the code + * inside code blocks do not have its indentation. + * + * @param node The node code + * @param indentation The current indentation to use + * @returns Reference to the original (refined) node + */ private refineCode(node: CodeNode, indentation: number = 0): CodeNode { if (node.code) node.code = addIdentation(node.code, indentation); @@ -195,15 +243,15 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Loop) { node.children .filter(child => child.jp instanceof ExprStmt) - .forEach(child => child.code = child.code!.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses + .forEach(child => child.code = child.code!.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } if (node.jp instanceof DeclStmt) { node.children .slice(1) .forEach(child => { - child.code = child.code!.match(/^(?:\S+\s+)(\S.*)$/)![1]; - }); // Remove type from variable declarations + child.code = child.code!.match(/^(?:\S+\s+)(\S.*)$/)![1]; // Remove type from variable declarations + }); } for (const child of node.children) { @@ -215,9 +263,9 @@ export default class ClavaAstConverter implements GenericAstConverter { const match = node.code!.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; - node.code = statement + ' ' + comment; + node.code = statement + ' ' + comment; // Fix space between statement and inline comment in naked body } - } // Fix space between statement and inline comment in naked body + } if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; @@ -248,6 +296,16 @@ export default class ClavaAstConverter implements GenericAstConverter { return node; } + /** + * @brief Performs C/C++ syntax hioverlighting on the given code. + * @details This is done by inserting the appropriate span tags around the + * code to be highlighted, using the HTML code as a string + * + * @param code The code to be highlighted, with the code of its children + * already linked and syntax highlighted + * @param node The node that the code belongs to + * @returns The syntax highlighted code + */ private syntaxHighlight(code: string | undefined, node: CodeNode): string | undefined { if (code === undefined) return undefined; @@ -264,13 +322,15 @@ export default class ClavaAstConverter implements GenericAstConverter { const [openingTag, closingTag] = getSyntaxHighlightTags('comment'); if (node.jp instanceof Comment) return openingTag + code + closingTag; - code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); - code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); + + code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); // Highlight unhighlighted single-line comments + code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); // Highlight unhighlighted multi-line comments + // We know that a piece of code is not highlighted if it is not preceded by a '>' (from the opening span tag) if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); - const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); + const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); // Match the declaration name with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } @@ -287,7 +347,9 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof If) { - const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); + const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); // Match first unhighlighted 'else' that is not inside a comment (single or multi-line) + // Note that the function is meant to be called recursively, so the 'else' of child ifs are already highlighted + if (elseMatch) { const elsePos = elseMatch[1].length; return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); @@ -298,7 +360,9 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Loop) { if (node.jp.kind == 'dowhile') { - const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)![1].length; + const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)![1].length; // Match first unhighlighted 'while' that is not inside a comment (single or multi-line) + // Same logic as the 'else' keyword + return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word @@ -314,24 +378,36 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight the directive } } return code; } + /** + * @brief Links the nodes of the given AST to their respective portion of code. + * + * @param root The root node of the AST + * @param outerCode The outer code, which should contain the code of all the + * nodes + * @param outerCodeStart The start index of the outer code section to be used + * @param outerCodeEnd The end index of the outer code section to be used + * @returns Array with three elements: the start index of the node code in the + * outer code, the end index of the node code in the outer code, and the + * linked code. + */ private linkCodeToAstNodes(root: CodeNode, outerCode: string | undefined, outerCodeStart: number, outerCodeEnd: number): any[] { const nodeCode = root.code; if (!nodeCode || !outerCode) - return [outerCodeStart, outerCodeStart, ""]; + return [outerCodeStart, outerCodeStart, ""]; // Return empty string if the node has no code const nodeCodeHtml = escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); - return [outerCodeStart, outerCodeStart, ""]; + return [outerCodeStart, outerCodeStart, ""]; // Return empty string if the node code is not found } const [openingTag, closingTag] = getNodeCodeTags(root.id); @@ -350,10 +426,10 @@ export default class ClavaAstConverter implements GenericAstConverter { for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); - newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; + newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; // Add portion behind children that is not matched and the linked child code newCodeIndex = childCodeEnd; } - newCode += outerCode.slice(newCodeIndex, innerCodeEnd); + newCode += outerCode.slice(newCodeIndex, innerCodeEnd); // Add the remaining unmatched code newCode = this.syntaxHighlight(newCode, root)!; return [innerCodeStart, innerCodeEnd, openingTag + newCode + closingTag]; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 060092c57..a5ec829aa 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -2,10 +2,20 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js" import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js"; +/** + * @brief Clava specialization of GenericAstConverter. + */ export default class ClavaAstConverter { updateAst() { Clava.rebuild(); } + /** + * @brief Returns the code of the given node. + * @details If the node has no code, returns undefined, instead of raising an error. + * + * @param node Node + * @returns The code of the node, or undefined if it has no code + */ getCode(node) { // TODO: When hasCode implementation is ready, replace this body with the following line: // return node.hasCode ? node.code.trim() : undefined @@ -19,6 +29,12 @@ export default class ClavaAstConverter { } return code; } + /** + * @brief Returns the information of the given join point, according to its type. + * + * @param jp Join point + * @returns The information of the join point + */ getJoinPointInfo(jp) { const info = { 'AST ID': jp.astId, @@ -65,6 +81,7 @@ export default class ClavaAstConverter { 'Constant?': jp.constant, 'Is builtin': jp.isBuiltin, 'Has sugar': jp.hasSugar, + 'Type kind': jp.kind }); } if (jp instanceof Varref) { @@ -126,6 +143,14 @@ export default class ClavaAstConverter { }; return toToolJoinPoint(root); } + /** + * @brief Converts the given join point to a CodeNode. + * @details This function assigns ids to the nodes in pre-order, and it MUST + * be the same as the order used for getToolAst. + * + * @param jp Join point + * @returns The CodeNode representation of the join point + */ toCodeNode(jp) { let nextId = 0; const toCodeNode = (jp) => { @@ -138,6 +163,15 @@ export default class ClavaAstConverter { }; return toCodeNode(jp); } + /** + * @brief Sorts the given code nodes by location. + * @details This function uses the join points' line and column, instead of + * location attribute. If a node does not have a location (line and code), + * it is placed before all the others. + * + * @param codeNodes Array of code nodes + * @returns Reference to the original (sorted) array + */ sortByLocation(codeNodes) { return codeNodes.sort((node1, node2) => { if (node1.jp.line === node2.jp.line) @@ -145,6 +179,17 @@ export default class ClavaAstConverter { return (node1.jp.line ?? -1) - (node2.jp.line ?? -1); }); } + /** + * @brief Applies refinements and corrections to the node code and order of + * the children. + * @details This is necessary because some nodes do not have its designated + * code equal to the matching code in the container. For example, the code + * inside code blocks do not have its indentation. + * + * @param node The node code + * @param indentation The current indentation to use + * @returns Reference to the original (refined) node + */ refineCode(node, indentation = 0) { if (node.code) node.code = addIdentation(node.code, indentation); @@ -158,8 +203,8 @@ export default class ClavaAstConverter { node.children .slice(1) .forEach(child => { - child.code = child.code.match(/^(?:\S+\s+)(\S.*)$/)[1]; - }); // Remove type from variable declarations + child.code = child.code.match(/^(?:\S+\s+)(\S.*)$/)[1]; // Remove type from variable declarations + }); } for (const child of node.children) { const newIndentation = (node.jp instanceof Scope || node.jp instanceof Class) ? indentation + 1 : indentation; @@ -169,9 +214,9 @@ export default class ClavaAstConverter { const match = node.code.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; - node.code = statement + ' ' + comment; + node.code = statement + ' ' + comment; // Fix space between statement and inline comment in naked body } - } // Fix space between statement and inline comment in naked body + } if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; @@ -197,6 +242,16 @@ export default class ClavaAstConverter { } // Divide program code into its files return node; } + /** + * @brief Performs C/C++ syntax hioverlighting on the given code. + * @details This is done by inserting the appropriate span tags around the + * code to be highlighted, using the HTML code as a string + * + * @param code The code to be highlighted, with the code of its children + * already linked and syntax highlighted + * @param node The node that the code belongs to + * @returns The syntax highlighted code + */ syntaxHighlight(code, node) { if (code === undefined) return undefined; @@ -211,11 +266,12 @@ export default class ClavaAstConverter { const [openingTag, closingTag] = getSyntaxHighlightTags('comment'); if (node.jp instanceof Comment) return openingTag + code + closingTag; - code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); - code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); + code = code.replaceAll(/(?)(\/\/.*)/g, `${openingTag}$1${closingTag}`); // Highlight unhighlighted single-line comments + code = code.replaceAll(/(?)(\/\*.*?\*\/)/g, `${openingTag}$1${closingTag}`); // Highlight unhighlighted multi-line comments + // We know that a piece of code is not highlighted if it is not preceded by a '>' (from the opening span tag) if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); - const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); + const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); // Match the declaration name with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } @@ -228,7 +284,8 @@ export default class ClavaAstConverter { return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { - const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); + const elseMatch = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\belse\b/); // Match first unhighlighted 'else' that is not inside a comment (single or multi-line) + // Note that the function is meant to be called recursively, so the 'else' of child ifs are already highlighted if (elseMatch) { const elsePos = elseMatch[1].length; return openingTag + 'if' + closingTag + code.slice(2, elsePos) + openingTag + 'else' + closingTag + code.slice(elsePos + 4); @@ -239,7 +296,8 @@ export default class ClavaAstConverter { } if (node.jp instanceof Loop) { if (node.jp.kind == 'dowhile') { - const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)[1].length; + const whilePos = code.match(/^(([^/]|\/[^/*]|\/\/.*|\/\*([^*]|\*[^/])*\*\/)*?)(?)\bwhile\b/)[1].length; // Match first unhighlighted 'while' that is not inside a comment (single or multi-line) + // Same logic as the 'else' keyword return openingTag + 'do' + closingTag + code.slice(2, whilePos) + openingTag + 'while' + closingTag + code.slice(whilePos + 5); } else { @@ -253,21 +311,33 @@ export default class ClavaAstConverter { return code.replace(/(class(?!=)|struct)/, `${openingTag}$1${closingTag}`); // Highlight 'class' or 'struct' in declaration } if (node.jp instanceof Include || node.jp instanceof Pragma) { - return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); + return code.replace(/^(#\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight the directive } } return code; } + /** + * @brief Links the nodes of the given AST to their respective portion of code. + * + * @param root The root node of the AST + * @param outerCode The outer code, which should contain the code of all the + * nodes + * @param outerCodeStart The start index of the outer code section to be used + * @param outerCodeEnd The end index of the outer code section to be used + * @returns Array with three elements: the start index of the node code in the + * outer code, the end index of the node code in the outer code, and the + * linked code. + */ linkCodeToAstNodes(root, outerCode, outerCodeStart, outerCodeEnd) { const nodeCode = root.code; if (!nodeCode || !outerCode) - return [outerCodeStart, outerCodeStart, ""]; + return [outerCodeStart, outerCodeStart, ""]; // Return empty string if the node has no code const nodeCodeHtml = escapeHtml(nodeCode); const innerCodeStart = outerCode.indexOf(nodeCodeHtml, outerCodeStart); const innerCodeEnd = innerCodeStart + nodeCodeHtml.length; if (innerCodeStart === -1 || innerCodeEnd > outerCodeEnd) { console.warn(`Code of node "${root.jp.joinPointType}" not found in code container: "${nodeCodeHtml}"`); - return [outerCodeStart, outerCodeStart, ""]; + return [outerCodeStart, outerCodeStart, ""]; // Return empty string if the node code is not found } const [openingTag, closingTag] = getNodeCodeTags(root.id); let newCode = ''; @@ -282,10 +352,10 @@ export default class ClavaAstConverter { } // Ignore function return type and name in declaration for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); - newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; + newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; // Add portion behind children that is not matched and the linked child code newCodeIndex = childCodeEnd; } - newCode += outerCode.slice(newCodeIndex, innerCodeEnd); + newCode += outerCode.slice(newCodeIndex, innerCodeEnd); // Add the remaining unmatched code newCode = this.syntaxHighlight(newCode, root); return [innerCodeStart, innerCodeEnd, openingTag + newCode + closingTag]; } From 4c60c282dc292d6ef4a9810be77b114c5d9c8803 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 29 Jul 2024 00:24:12 +0100 Subject: [PATCH 33/36] Apply some fixes to syntax highlighting --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 11 ++++++----- .../src-lara/clava/visualization/ClavaAstConverter.js | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 90a665f20..8a1da5c74 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -1,7 +1,7 @@ import { LaraJoinPoint } from "lara-js/api/LaraJoinPoint.js"; import GenericAstConverter, { FilesCode } from "lara-js/api/visualization/GenericAstConverter.js"; import ToolJoinPoint, { JoinPointInfo } from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, DeleteExpr, EnumDecl, EnumeratorDecl, Expression, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Joinpoint, Literal, Loop, Marker, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js" @@ -330,18 +330,19 @@ export default class ClavaAstConverter implements GenericAstConverter { if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); - const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); // Match the declaration name with the '&' and '*' prefixes + const escapedName = node.jp.name?.replace(/\[[^\]]*]$/, "") // Remove array suffix from variable name + const regex = new RegExp(`\\s*[&*]*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } - if (node.jp instanceof Statement || node.jp instanceof Decl) { + if (node.jp instanceof Statement || node.jp instanceof Decl || node.jp instanceof Expression) { const [openingTag, closingTag] = getSyntaxHighlightTags('keyword'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt - || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier - || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp instanceof DeleteExpr + || ["FunctionTemplateDecl", "TemplateTypeParmDecl", "NamespaceDecl"].includes(node.jp.astName)) { return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index a5ec829aa..8dded2180 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -1,5 +1,5 @@ import ToolJoinPoint from "lara-js/api/visualization/public/js/ToolJoinPoint.js"; -import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, EnumDecl, EnumeratorDecl, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; +import { AccessSpecifier, AdjustedType, Body, BoolLiteral, Break, Call, Case, Class, Comment, Continue, Decl, Declarator, DeclStmt, DeleteExpr, EnumDecl, EnumeratorDecl, Expression, ExprStmt, FileJp, FloatLiteral, FunctionJp, GotoStmt, If, Include, IntLiteral, Literal, Loop, NamedDecl, Omp, Pragma, Program, RecordJp, ReturnStmt, Scope, Statement, Switch, Tag, Type, TypedefDecl, Vardecl, Varref, WrapperStmt } from "../Joinpoints.js"; import Clava from "../clava/Clava.js"; import { addIdentation, escapeHtml, getNodeCodeTags, getSyntaxHighlightTags } from "lara-js/api/visualization/AstConverterUtils.js"; /** @@ -271,16 +271,17 @@ export default class ClavaAstConverter { // We know that a piece of code is not highlighted if it is not preceded by a '>' (from the opening span tag) if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); - const regex = new RegExp(`\\s*[&*]*\\b${node.jp.name}\\b`); // Match the declaration name with the '&' and '*' prefixes + const escapedName = node.jp.name?.replace(/\[[^\]]*]$/, ""); // Remove array suffix from variable name + const regex = new RegExp(`\\s*[&*]*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } - if (node.jp instanceof Statement || node.jp instanceof Decl) { + if (node.jp instanceof Statement || node.jp instanceof Decl || node.jp instanceof Expression) { const [openingTag, closingTag] = getSyntaxHighlightTags('keyword'); if (node.jp instanceof Switch || node.jp instanceof Break || node.jp instanceof Case || node.jp instanceof Continue || node.jp instanceof GotoStmt || node.jp instanceof ReturnStmt - || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier - || node.jp.astName == "FunctionTemplateDecl" || node.jp.astName == "TemplateTypeParmDecl") { + || node.jp instanceof EnumDecl || node.jp instanceof AccessSpecifier || node.jp instanceof DeleteExpr + || ["FunctionTemplateDecl", "TemplateTypeParmDecl", "NamespaceDecl"].includes(node.jp.astName)) { return code.replace(/^(\w+)\b/, `${openingTag}$1${closingTag}`); // Highlight first word } if (node.jp instanceof If) { From 6e7a4a798bc4515c9ceb929c75d7336057c28997 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 29 Jul 2024 00:48:55 +0100 Subject: [PATCH 34/36] Fix lambda body --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 9 ++++++++- .../src-lara/clava/visualization/ClavaAstConverter.js | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 8a1da5c74..9ed059785 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -259,13 +259,20 @@ export default class ClavaAstConverter implements GenericAstConverter { this.refineCode(child, newIndentation); } - if (node.jp instanceof Body && (node.jp as Body).naked) { + if (node.jp instanceof Body && node.jp.naked) { const match = node.code!.match(/^([^\/]*\S)\s*(\/\/.*)$/); if (match) { const [, statement, comment] = match; node.code = statement + ' ' + comment; // Fix space between statement and inline comment in naked body } } + + if (node.jp.astName === 'LambdaExpr' && node.children.length >= 1) { + const body = node.children[0]; + if (body.jp instanceof Body && !node.code?.includes(body.code!)) { + body.code = body.code!.replaceAll(/\n */g, ' '); // Remove newlines and indentation from lambda body + } + } if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 8dded2180..0ffec6873 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -217,6 +217,12 @@ export default class ClavaAstConverter { node.code = statement + ' ' + comment; // Fix space between statement and inline comment in naked body } } + if (node.jp.astName === 'LambdaExpr' && node.children.length >= 1) { + const body = node.children[0]; + if (body.jp instanceof Body && !node.code?.includes(body.code)) { + body.code = body.code.replaceAll(/\n */g, ' '); + } + } if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { const tagDeclVars = node.children[0]; const typedef = tagDeclVars.children[0]; From 776bede1553eabe6b020823409cb382159731433 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 29 Jul 2024 01:17:49 +0100 Subject: [PATCH 35/36] Some minor fixes --- Clava-JS/src-api/visualization/ClavaAstConverter.ts | 10 +++++++--- .../clava/visualization/ClavaAstConverter.js | 12 ++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Clava-JS/src-api/visualization/ClavaAstConverter.ts b/Clava-JS/src-api/visualization/ClavaAstConverter.ts index 9ed059785..3012fb1bc 100644 --- a/Clava-JS/src-api/visualization/ClavaAstConverter.ts +++ b/Clava-JS/src-api/visualization/ClavaAstConverter.ts @@ -268,8 +268,8 @@ export default class ClavaAstConverter implements GenericAstConverter { } if (node.jp.astName === 'LambdaExpr' && node.children.length >= 1) { - const body = node.children[0]; - if (body.jp instanceof Body && !node.code?.includes(body.code!)) { + const body = node.children.find(child => child.jp instanceof Body); + if (body && body.jp instanceof Body && !node.code?.includes(body.code!)) { body.code = body.code!.replaceAll(/\n */g, ' '); // Remove newlines and indentation from lambda body } } @@ -338,7 +338,7 @@ export default class ClavaAstConverter implements GenericAstConverter { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); const escapedName = node.jp.name?.replace(/\[[^\]]*]$/, "") // Remove array suffix from variable name - const regex = new RegExp(`\\s*[&*]*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes + const regex = new RegExp(`\\s*(&|\\*)*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } @@ -431,6 +431,10 @@ export default class ClavaAstConverter implements GenericAstConverter { newCodeIndex = outerCode.indexOf('(', innerCodeStart) + 1; newCode += outerCode.slice(innerCodeStart, newCodeIndex); } // Ignore function return type and name in declaration + if (root.jp instanceof ReturnStmt) { + newCodeIndex = outerCode.indexOf(' ', innerCodeStart) + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore keyword in return statement for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); diff --git a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js index 0ffec6873..a69b1c697 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js +++ b/ClavaLaraApi/src-lara/clava/visualization/ClavaAstConverter.js @@ -218,9 +218,9 @@ export default class ClavaAstConverter { } } if (node.jp.astName === 'LambdaExpr' && node.children.length >= 1) { - const body = node.children[0]; - if (body.jp instanceof Body && !node.code?.includes(body.code)) { - body.code = body.code.replaceAll(/\n */g, ' '); + const body = node.children.find(child => child.jp instanceof Body); + if (body && body.jp instanceof Body && !node.code?.includes(body.code)) { + body.code = body.code.replaceAll(/\n */g, ' '); // Remove newlines and indentation from lambda body } } if (node.children.length >= 1 && node.children[0].jp.astName === 'TagDeclVars') { @@ -278,7 +278,7 @@ export default class ClavaAstConverter { if (node.jp instanceof Declarator || node.jp instanceof EnumeratorDecl) { const [openingTag, closingTag] = getSyntaxHighlightTags('type'); const escapedName = node.jp.name?.replace(/\[[^\]]*]$/, ""); // Remove array suffix from variable name - const regex = new RegExp(`\\s*[&*]*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes + const regex = new RegExp(`\\s*(&|\\*)*\\s*` + (escapedName ? `\\b${escapedName}\\b` : "$")); // Match the declaration name (if one exists) with the '&' and '*' prefixes const namePos = code.search(regex); return namePos <= 0 ? code : openingTag + code.slice(0, namePos) + closingTag + code.slice(namePos); } @@ -357,6 +357,10 @@ export default class ClavaAstConverter { newCodeIndex = outerCode.indexOf('(', innerCodeStart) + 1; newCode += outerCode.slice(innerCodeStart, newCodeIndex); } // Ignore function return type and name in declaration + if (root.jp instanceof ReturnStmt) { + newCodeIndex = outerCode.indexOf(' ', innerCodeStart) + 1; + newCode += outerCode.slice(innerCodeStart, newCodeIndex); + } // Ignore keyword in return statement for (const child of root.children) { const [childCodeStart, childCodeEnd, childCode] = this.linkCodeToAstNodes(child, outerCode, newCodeIndex, innerCodeEnd); newCode += outerCode.slice(newCodeIndex, childCodeStart) + childCode; // Add portion behind children that is not matched and the linked child code From c14bfeb2ec9f400240437f9064dfee8db57dcba4 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 14 Aug 2024 17:31:32 +0100 Subject: [PATCH 36/36] Add README --- Clava-JS/src-api/visualization/README.md | 28 +++++++++++++++++++ .../visualization/VisualizationTool.ts | 4 +-- .../clava/visualization/VisualizationTool.js | 4 +-- 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 Clava-JS/src-api/visualization/README.md diff --git a/Clava-JS/src-api/visualization/README.md b/Clava-JS/src-api/visualization/README.md new file mode 100644 index 000000000..218f2a604 --- /dev/null +++ b/Clava-JS/src-api/visualization/README.md @@ -0,0 +1,28 @@ +# LARA Visualization Tool + +Clava integration of LARA's web tool for visualization and analysis of the AST and its source code. + +For more details, see the [LARA Framework repository](https://github.com/specs-feup/lara-framework). + +## Usage + +To launch or update the visualization tool, execute the following statements: + +```js +import VisualizationTool from "clava-js/api/visualization/VisualizationTool.js"; + +await VisualizationTool.visualize(); +``` + +Once ready, Clava will provide the URL that should be opened in the browser to access the web interface. The function can also change the AST root and URL domain and port. + +Other properties will allow the user to know other important information from the server: + +```js +VisualizationTool.isLaunched; // true if the server is running +VisualizationTool.url; // URL where the server is running +VisualizationTool.port; // port to which the server is listening +VisualizationTool.hostname; // hostname to which the server is listening +``` + +For more details, refer to the `GenericVisualizationTool` documentation, from [LARA](https://github.com/specs-feup/lara-framework). \ No newline at end of file diff --git a/Clava-JS/src-api/visualization/VisualizationTool.ts b/Clava-JS/src-api/visualization/VisualizationTool.ts index 983b90c84..d09258efd 100644 --- a/Clava-JS/src-api/visualization/VisualizationTool.ts +++ b/Clava-JS/src-api/visualization/VisualizationTool.ts @@ -3,7 +3,7 @@ import GenericVisualizationTool from 'lara-js/api/visualization/GenericVisualiza import ClavaAstConverter from './ClavaAstConverter.js'; export class VisualizationTool extends GenericVisualizationTool { - private joinPointConverter = new ClavaAstConverter(); + private astConverter = new ClavaAstConverter(); private static instance: VisualizationTool = new VisualizationTool(); public static getInstance(): VisualizationTool { @@ -11,7 +11,7 @@ export class VisualizationTool extends GenericVisualizationTool { } protected override getAstConverter(): GenericAstConverter { - return this.joinPointConverter; + return this.astConverter; } } diff --git a/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js b/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js index 24ec8e872..9e6e3e6a5 100644 --- a/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js +++ b/ClavaLaraApi/src-lara/clava/visualization/VisualizationTool.js @@ -1,13 +1,13 @@ import GenericVisualizationTool from 'lara-js/api/visualization/GenericVisualizationTool.js'; import ClavaAstConverter from './ClavaAstConverter.js'; export class VisualizationTool extends GenericVisualizationTool { - joinPointConverter = new ClavaAstConverter(); + astConverter = new ClavaAstConverter(); static instance = new VisualizationTool(); static getInstance() { return this.instance; } getAstConverter() { - return this.joinPointConverter; + return this.astConverter; } } export default VisualizationTool.getInstance();