Jelajahi Sumber

Update 'js/util/parseFromVisual.js'

Fixes to solve problem to read 'ivph' file with comments (contribution of Edilson Cuambe/Gleyce Santos).
Edilson: mainly new "function associateInlineComments(commands, allComments)"
leo 1 Minggu lalu
induk
melakukan
400ddf3cbf
1 mengubah file dengan 228 tambahan dan 37 penghapusan
  1. 228 37
      js/util/parseFromVisual.js

+ 228 - 37
js/util/parseFromVisual.js

@@ -163,7 +163,7 @@ function ifThenElseWalker (ifthenelse) {
     ifTrue,
     ifFalse,
     };
-}
+  }
 
 
 /**
@@ -337,14 +337,14 @@ function variableDeclarationWalker (command, global = false) {
     if (command.isVector) {
       variable.columns = lines.value;
       variable.dimension = 1;
-      const values = command.initial.value.map((exp) => variableInitialWalker(exp) );
+      const values = command.initial.value.map((exp) => variableInitialWalker(exp));
       variable.value = values;
     } else {
       const columns = expressionWalker(command.columns).pop();
       variable.dimension = 2;
       variable.rows = lines.value;
       variable.columns = columns.value;
-      const values = command.initial.value.map((rows) => rows.value.map((exp) => variableInitialWalker(exp)) );
+      const values = command.initial.value.map((rows) => rows.value.map((exp) => variableInitialWalker(exp)));
       variable.value = values;
       }
   } else {
@@ -362,10 +362,8 @@ function variableInitialWalker (expression) {
     const left = variableInitialWalker(expression.left);
     const opType = getOpType(expression.op);
     if (opType !== TYPES.ARITHMETIC) {
-      throw new Error(
-        "invalid variable initial value: " + expression.toString()
-      );
-    }
+      throw new Error("invalid variable initial value: " + expression.toString());
+      }
     return `${expression.op.value}${left}`;
   } else if (expression instanceof Expressions.BoolLiteral) {
     const value = expression.value;
@@ -396,10 +394,7 @@ function expressionWalker (expression) {
       type: TYPES.FUNCTION,
       value: expression.id,
       };
-    const paramsList = expression.actualParameters.map((e) =>
-      expressionWalker(e)
-      );
-    //const params = Array.prototype.concat.apply([], paramsList);
+    const paramsList = expression.actualParameters.map((e) => expressionWalker(e));
     funcObj.params = paramsList;
     result = [funcObj];
   } else if (expression instanceof Expressions.UnaryApp) {
@@ -445,16 +440,19 @@ function expressionWalker (expression) {
       ];
   } else {
     let value = expression.value;
-    if (expression.value.toNumber) {
-      if (
-        Types.REAL.isCompatible(expression.type) &&
-        expression.value.decimalPlaces() == 0
-      ) {
-        value = Number(expression.value.toFixed(2));
+
+    //x if (expression.value.toNumber)
+    if (value !== null && value !== undefined && typeof value.toNumber === "function") {
+      //x if (Types.REAL.isCompatible(expression.type) && expression.value.decimalPlaces() == 0)
+      if (Types.REAL.isCompatible(expression.type) && value.decimalPlaces() == 0) {
+        //x value = Number(expression.value.toFixed(2));
+        value = Number(value.toFixed(2));
       } else {
-        value = expression.value.toNumber();
+        //x value = expression.value.toNumber();
+        value = value.toNumber();
         }
       }
+
     result = [ { instance: "expression", class: "simple", type: TYPES.CONST, value: value, }, ];
     }
   if (expression.parenthesis) return ["(", ...result, ")"];
@@ -476,11 +474,12 @@ export function parseCode (text) {
   return parserCodeVisual(text);
   }
 
+
 /// @param {string} text
 /// @calledby js/util/iassignHelpers.js!setPreviousAlgorithm(code)
-export function parserCodeVisual (text) { // Gleyce
-  console.log("js/util/parseFromVisual.js!parserCodeVisual(text): starting");
-  //D console.trace();
+export function parserCodeVisual (text) { // Gleyce; Edilson
+  console.log("js/util/parseFromVisual.js!parserCodeVisual(text): starting"); //D console.trace();
+
   const parser = IVProgParser.createParser(text, false);
   const codeLinesMap = new Map();
   const tokens = Array.from(parser.lexer.reset(text));
@@ -504,21 +503,15 @@ export function parserCodeVisual (text) { // Gleyce
   parser.fill(tokenStream);
   try {
     const program = parser.parseTree();
-    const globals = program.global.map((decl) =>
-      variableDeclarationWalker(decl, true)
-      );
+    const globals = program.global.map((decl) => variableDeclarationWalker(decl, true));
     const functions = program.functions.map(functionWalker);
 
     //Gleyce start: new code to read IVPH file with comment (//)
     const allComments = [];
     for (const [line, commentTokens] of codeLinesMap.entries()) {
       for (const token of commentTokens) {
-        const commentText = token.text.replace(/^\/\//, '').trim();
-        allComments.push({
-          type: "comment",
-          comment_text: commentText,
-          line: token.line,
-          });
+        const commentText = token.text.replace(/^\/\//, "").trim();
+        allComments.push({ type: "comment", comment_text: commentText, line: token.line, });
         }
       }
     const astFunctions = program.functions;
@@ -531,13 +524,29 @@ export function parserCodeVisual (text) { // Gleyce
       } else {
         funcEndLine = Infinity;
         }
-      const funcComments = allComments.filter(comment =>
-        comment.line > funcStartLine && comment.line < funcEndLine
-        );
+
+      const funcComments = allComments.filter((comment) => comment.line > funcStartLine && comment.line < funcEndLine);
+
       if (funcComments.length > 0) {
-        const allCommands = [...visualFunc.commands, ...funcComments];
-        allCommands.sort((a, b) => a.line - b.line);
-        visualFunc.commands = allCommands;
+        let firstCommandLine = Infinity;
+        if (visualFunc.commands.length > 0) {
+          firstCommandLine = visualFunc.commands[0].line;
+          }
+
+        const commentsForVariables = funcComments.filter((c) => c.line < firstCommandLine);
+
+        if (commentsForVariables.length > 0) {
+          const allVariables = [ ...visualFunc.variables_list, ...commentsForVariables, ];
+          allVariables.sort((a, b) => a.line - b.line);
+          visualFunc.variables_list = allVariables;
+          }
+
+        if (visualFunc.commands.length > 0) {
+          const lastCommandLine = visualFunc.commands[visualFunc.commands.length - 1]?.line || funcEndLine;
+          const commentsForCommands = funcComments.filter((c) => c.line >= firstCommandLine);
+
+          visualFunc.commands = associateCommentsToCommands(visualFunc.commands, commentsForCommands, firstCommandLine - 1, lastCommandLine + 100);
+          }
         }
       });
     //Gleyce end: new code to read IVPH file with comment (//)
@@ -547,4 +556,186 @@ export function parserCodeVisual (text) { // Gleyce
     console.error(e);
     return null;
     }
-  }
+  } // export function parserCodeVisual(text)
+
+
+//comments starts (Edilson)
+
+function associateCommentsToCommands (commands, allComments, startLine, endLine) {
+  const scopeComments = allComments.filter((c) => c.line > startLine && c.line < endLine);
+
+  if (scopeComments.length === 0) return commands;
+
+  const processedCommands = commands.map((cmd) => {
+    if (cmd.type === "comment") return cmd;
+
+    const processedCmd = { ...cmd };
+
+    if (cmd.type === "iftrue") {
+      if (cmd.ifTrue && cmd.ifTrue.length > 0) {
+        const ifBlock = cmd.ifTrue;
+        const blockStart = cmd.line;
+        const blockEnd = ifBlock[ifBlock.length - 1]?.line || cmd.line;
+        processedCmd.ifTrue = associateCommentsToCommands(ifBlock, allComments, blockStart, blockEnd);
+        }
+      if (cmd.ifFalse && cmd.ifFalse.length > 0) {
+        const elseBlock = cmd.ifFalse;
+        const blockStart = cmd.ifTrue && cmd.ifTrue.length > 0 ? cmd.ifTrue[cmd.ifTrue.length - 1]?.line || cmd.line : cmd.line;
+        const blockEnd = elseBlock[elseBlock.length - 1]?.line || blockStart;
+        processedCmd.ifFalse = associateCommentsToCommands(elseBlock, allComments, blockStart, blockEnd);
+        }
+      }
+
+    if (cmd.type === "whiletrue" || cmd.type === "dowhiletrue") {
+      if (cmd.commands && cmd.commands.length > 0) {
+        const blockStart = cmd.line;
+        const blockEnd = cmd.commands[cmd.commands.length - 1]?.line || cmd.line;
+        processedCmd.commands = associateCommentsToCommands(cmd.commands, allComments, blockStart, blockEnd);
+        }
+      }
+
+    if (cmd.type === "repeatNtimes") {
+      if (cmd.commands && cmd.commands.length > 0) {
+        const blockStart = cmd.line;
+        const blockEnd = cmd.commands[cmd.commands.length - 1]?.line || cmd.line;
+        processedCmd.commands = associateCommentsToCommands(cmd.commands, allComments, blockStart, blockEnd);
+        }
+      }
+
+    if (cmd.type === "switch") {
+      if (cmd.cases) {
+        processedCmd.cases = cmd.cases.map((caseObj) => {
+          if (caseObj.commands && caseObj.commands.length > 0) {
+            const blockStart = caseObj.line || cmd.line;
+            const blockEnd = caseObj.commands[caseObj.commands.length - 1]?.line || blockStart;
+            return {
+              ...caseObj,
+              commands: associateCommentsToCommands(caseObj.commands, allComments, blockStart, blockEnd),
+              };
+            }
+          return caseObj;
+          });
+        }
+      }
+
+    return processedCmd;
+    });
+
+  const usedCommentLines = new Set();
+
+  function collectUsedComments (cmdList) {
+    cmdList.forEach((cmd) => {
+      if (cmd.type === "comment") {
+        usedCommentLines.add(cmd.line);
+        return;
+        }
+
+      if (cmd.type === "iftrue") {
+        if (cmd.ifTrue) collectUsedComments(cmd.ifTrue);
+        if (cmd.ifFalse) collectUsedComments(cmd.ifFalse);
+        }
+
+      if ((cmd.type === "whiletrue" || cmd.type === "dowhiletrue" || cmd.type === "repeatNtimes") && cmd.commands) {
+        collectUsedComments(cmd.commands);
+        }
+
+      if (cmd.type === "switch" && cmd.cases) {
+        cmd.cases.forEach((c) => {
+          if (c.commands) collectUsedComments(c.commands);
+          });
+        }
+      });
+    }
+
+  collectUsedComments(processedCommands);
+
+  const availableComments = scopeComments.filter((c) => !usedCommentLines.has(c.line));
+
+  const merged = [...processedCommands, ...availableComments].sort((a, b) => a.line - b.line);
+
+  return associateInlineComments(merged, allComments);
+  }
+
+
+function associateInlineComments (commands, allComments) {
+  if (!allComments || allComments.length === 0) return commands;
+
+  const commentsByLine = new Map();
+  allComments.forEach((comment) => {
+    if (!commentsByLine.has(comment.line)) {
+      commentsByLine.set(comment.line, []);
+      }
+    commentsByLine.get(comment.line).push(comment);
+    });
+
+  const inlineCommentLines = new Set();
+
+  function processCommands (cmdList) {
+    return cmdList.map((cmd) => {
+      if (cmd.type === "comment") return cmd;
+
+      const processedCmd = { ...cmd };
+
+      if (cmd.line && commentsByLine.has(cmd.line)) {
+        const commentsOnLine = commentsByLine.get(cmd.line);
+
+        const availableComment = commentsOnLine.find((c) => !inlineCommentLines.has(c.line));
+
+        if (availableComment) {
+          processedCmd.inlineComment = availableComment.comment_text;
+          inlineCommentLines.add(cmd.line);
+          }
+        }
+
+      if (cmd.type === "iftrue") {
+        if (cmd.ifTrue) processedCmd.ifTrue = processCommands(cmd.ifTrue);
+        if (cmd.ifFalse) processedCmd.ifFalse = processCommands(cmd.ifFalse);
+        }
+
+      if ((cmd.type === "whiletrue" || cmd.type === "dowhiletrue" || cmd.type === "repeatNtimes") && cmd.commands) {
+        processedCmd.commands = processCommands(cmd.commands);
+        }
+
+      if (cmd.type === "switch" && cmd.cases) {
+        processedCmd.cases = cmd.cases.map((caseObj) => ({
+          ...caseObj,
+          commands: caseObj.commands ? processCommands(caseObj.commands) : [],
+          }));
+        }
+
+      return processedCmd;
+      });
+    } // function processCommands(cmdList)
+
+  const processedCommands = processCommands(commands);
+
+  function filterInlineComments (cmdList) {
+    return cmdList.filter((cmd) => {
+      if (cmd.type === "comment" && inlineCommentLines.has(cmd.line)) {
+        return false;
+        }
+
+      if (cmd.type === "iftrue") {
+        if (cmd.ifTrue) cmd.ifTrue = filterInlineComments(cmd.ifTrue);
+        if (cmd.ifFalse) cmd.ifFalse = filterInlineComments(cmd.ifFalse);
+        }
+
+      if ((cmd.type === "whiletrue" || cmd.type === "dowhiletrue" || cmd.type === "repeatNtimes") && cmd.commands) {
+        cmd.commands = filterInlineComments(cmd.commands);
+        }
+
+      if (cmd.type === "switch" && cmd.cases) {
+        cmd.cases = cmd.cases.map((caseObj) => ({
+          ...caseObj,
+          commands: caseObj.commands ? filterInlineComments(caseObj.commands) : [],
+          }));
+        }
+
+      return true;
+      });
+    }
+
+  return filterInlineComments(processedCommands);
+  } // function associateInlineComments(commands, allComments)
+
+//comments end (Edilson)