parseFromVisual.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. import { IVProgParser } from "../ast/ivprogParser";
  2. import * as Expressions from "../ast/expressions";
  3. import { Types } from "../typeSystem/types";
  4. import { convertBoolToString } from "../typeSystem/parsers";
  5. import * as Commands from "../ast/commands";
  6. import { ArrayType } from "../typeSystem/array_type";
  7. import { Literal } from "../ast/expressions/literal";
  8. const TYPES = {
  9. VARIABLE: "var",
  10. CONST: "const",
  11. FUNCTION: "function",
  12. RELATIONAL: "relational",
  13. LOGIC: "logic",
  14. ARITHMETIC: "arithmetic",
  15. };
  16. function translateOp (type, op) {
  17. switch (type) {
  18. case TYPES.ARITHMETIC:
  19. return op.value;
  20. case TYPES.RELATIONAL:
  21. return op.value;
  22. case TYPES.LOGIC: {
  23. if (op.ord === 11) {
  24. return "and";
  25. } else if (op.ord === 12) {
  26. return "or";
  27. } else {
  28. return "not";
  29. }
  30. }
  31. }
  32. }
  33. function getOpType (op) {
  34. switch (op.ord) {
  35. case 0:
  36. case 1:
  37. case 2:
  38. case 3:
  39. case 4:
  40. return TYPES.ARITHMETIC;
  41. case 5:
  42. case 6:
  43. case 7:
  44. case 8:
  45. case 9:
  46. case 10:
  47. return TYPES.RELATIONAL;
  48. default:
  49. return TYPES.LOGIC;
  50. }
  51. }
  52. /**
  53. * @param {Commands.Case} switchCase
  54. * */
  55. function switchCaseWalker (switchCase) {
  56. const commands = switchCase.commands.map(commandWalker);
  57. const expression = switchCase.isDefault
  58. ? null
  59. : expressionWalker(switchCase.expression);
  60. return {
  61. type: "switchcase",
  62. line: switchCase.sourceInfo.line,
  63. expression,
  64. commands,
  65. };
  66. }
  67. /**
  68. * @param {Commands.Switch} switchCommand
  69. * */
  70. function switchWalker (switchCommand) {
  71. const expression = expressionWalker(switchCommand.expression);
  72. const cases = switchCommand.cases.map(switchCaseWalker);
  73. return {
  74. type: "switch",
  75. expression,
  76. cases,
  77. };
  78. }
  79. /**
  80. * @param {Commands.Return} returnCommand
  81. * */
  82. function returnWalker (returnCommand) {
  83. let expression = null;
  84. if (returnCommand.expression !== null) {
  85. expression = expressionWalker(returnCommand.expression);
  86. }
  87. return {
  88. type: "return",
  89. expression,
  90. };
  91. }
  92. function breakWalker (_) {
  93. return { type: "break" };
  94. }
  95. /**
  96. * @param {Commands.For} forLoop
  97. * */
  98. function forWalker (forLoop) {
  99. const var_attribution = expressionWalker(forLoop.for_id);
  100. const var_initial = expressionWalker(forLoop.for_from);
  101. const condition = expressionWalker(forLoop.for_to);
  102. const step_expression = forLoop.for_pass
  103. ? expressionWalker(forLoop.for_pass)
  104. : [];
  105. const commands = forLoop.commands.map(commandWalker);
  106. return {
  107. type: "repeatNtimes",
  108. var_attribution,
  109. var_initial,
  110. condition,
  111. step_expression,
  112. commands,
  113. };
  114. }
  115. /**
  116. * @param {Commands.While} whileLoop
  117. * */
  118. function whileWalker (whileLoop) {
  119. const expression = expressionWalker(whileLoop.expression);
  120. const commands = whileLoop.commands.map(commandWalker);
  121. let type = whileLoop.testFirst ? "whiletrue" : "dowhiletrue";
  122. return {
  123. type,
  124. expression,
  125. commands,
  126. };
  127. }
  128. /**
  129. * @param {Commands.IfThenElse} ifthenelse
  130. * */
  131. function ifThenElseWalker (ifthenelse) {
  132. //ifthenelse.
  133. const expression = expressionWalker(ifthenelse.condition);
  134. const ifTrue = ifthenelse.ifTrue.commands.map(commandWalker);
  135. let ifFalse = [];
  136. if (ifthenelse.ifFalse) {
  137. if (ifthenelse.ifFalse instanceof Commands.CommandBlock) {
  138. ifFalse = ifthenelse.ifFalse.commands.map(commandWalker);
  139. } else {
  140. ifFalse = [ifThenElseWalker(ifthenelse.ifFalse)];
  141. }
  142. }
  143. return {
  144. type: "iftrue",
  145. expression,
  146. ifTrue,
  147. ifFalse,
  148. };
  149. }
  150. /**
  151. * @param {Commands.Assign} assingment
  152. * */
  153. function assignmentWalker (assingment) {
  154. let variable = null;
  155. if (assingment instanceof Commands.ArrayIndexAssign) {
  156. const line = expressionWalker(assingment.line);
  157. let arrayClass = "vector";
  158. let column = null;
  159. if (assingment.column) {
  160. arrayClass = "matrix";
  161. column = expressionWalker(assingment.column);
  162. }
  163. variable = [
  164. {
  165. instance: "expression",
  166. type: TYPES.VARIABLE,
  167. class: arrayClass,
  168. column: column,
  169. line: line,
  170. value: assingment.id,
  171. },
  172. ];
  173. } else {
  174. variable = [
  175. { instance: "expression", type: TYPES.VARIABLE, value: assingment.id },
  176. ];
  177. }
  178. const expression = expressionWalker(assingment.expression);
  179. return {
  180. type: "attribution",
  181. variable,
  182. expression,
  183. };
  184. }
  185. /**
  186. * @param {Command} command
  187. * */
  188. function commandWalker (command) {
  189. let parsedCommand = null;
  190. if (command instanceof Commands.FunctionCall) {
  191. parsedCommand = functionCallWalker(command);
  192. } else if (command instanceof Commands.Assign) {
  193. parsedCommand = assignmentWalker(command);
  194. } else if (command instanceof Commands.IfThenElse) {
  195. parsedCommand = ifThenElseWalker(command);
  196. } else if (command instanceof Commands.While) {
  197. parsedCommand = whileWalker(command);
  198. } else if (command instanceof Commands.Break) {
  199. parsedCommand = breakWalker(command);
  200. } else if (command instanceof Commands.Return) {
  201. parsedCommand = returnWalker(command);
  202. } else if (command instanceof Commands.Switch) {
  203. parsedCommand = switchWalker(command);
  204. } else if (command instanceof Commands.For) {
  205. parsedCommand = forWalker(command);
  206. } else {
  207. throw new Error("not implemented");
  208. }
  209. parsedCommand.line = command.sourceInfo.line;
  210. return parsedCommand;
  211. }
  212. /**
  213. * @param {Commands.FunctionCall} functionCall
  214. * */
  215. function functionCallWalker (functionCall) {
  216. let name = functionCall.id;
  217. if (name.indexOf(".") !== -1) {
  218. name = name.split(".")[1];
  219. }
  220. const parameters = functionCall.actualParameters.map(expressionWalker);
  221. if (name === "$write") {
  222. const lastInput = parameters[parameters.length - 1][0];
  223. // if lastInput is an object with value === '\n', newLine is true
  224. const newLine = lastInput.value && lastInput.value.match(/^\n$/) !== null;
  225. const content = newLine
  226. ? parameters.slice(0, parameters.length - 1)
  227. : parameters;
  228. return {
  229. type: "writer",
  230. newLine,
  231. content,
  232. };
  233. }
  234. if (name === "$read") {
  235. return {
  236. type: "reader",
  237. variable: parameters[0],
  238. };
  239. }
  240. return {
  241. type: "functioncall",
  242. parameters_list: parameters,
  243. name: functionCall.id,
  244. };
  245. }
  246. /**
  247. * @param {Commands.Function} func
  248. * */
  249. function functionWalker (func) {
  250. const funcDeclaration = {
  251. name: func.name,
  252. line: func.sourceInfo.line,
  253. return_type: "",
  254. return_dimensions: 0,
  255. parameters_list: [],
  256. variables_list: [],
  257. commands: [],
  258. };
  259. if (func.returnType instanceof ArrayType) {
  260. funcDeclaration.return_type = func.returnType.innerType.value;
  261. funcDeclaration.return_dimensions = func.returnType.dimensions;
  262. } else {
  263. funcDeclaration.return_type = func.returnType.value;
  264. }
  265. funcDeclaration.parameters_list = func.formalParameters.map(
  266. functionParameterWalker
  267. );
  268. funcDeclaration.variables_list = func.variablesDeclarations.map(
  269. variableDeclarationWalker
  270. );
  271. funcDeclaration.commands = func.commands.map(commandWalker);
  272. return funcDeclaration;
  273. }
  274. /**
  275. * @param {Commands.FormalParameter} formalParameter
  276. * */
  277. function functionParameterWalker (formalParameter) {
  278. const variable = {
  279. name: formalParameter.id,
  280. line: formalParameter.sourceInfo.line,
  281. type: "",
  282. rows: 0,
  283. columns: 0,
  284. dimension: 0,
  285. value: 0,
  286. is_const: false,
  287. reference: formalParameter.byRef,
  288. };
  289. if (formalParameter.type instanceof ArrayType) {
  290. variable.type = formalParameter.type.innerType.value;
  291. variable.dimension = formalParameter.type.dimensions;
  292. } else {
  293. variable.type = formalParameter.type.value;
  294. }
  295. return variable;
  296. }
  297. /**
  298. * @param {Commands.Declaration} command
  299. * @param {boolean} global
  300. * */
  301. function variableDeclarationWalker (command, global = false) {
  302. const variable = {
  303. name: command.id,
  304. line: command.sourceInfo.line,
  305. type: "",
  306. rows: 0,
  307. columns: 0,
  308. dimension: 0,
  309. value: 0,
  310. is_const: false,
  311. };
  312. variable.is_const = global && command.isConst;
  313. if (command instanceof Commands.ArrayDeclaration) {
  314. // array
  315. const lines = expressionWalker(command.lines).pop();
  316. variable.type = command.type.innerType.value;
  317. if (command.isVector) {
  318. variable.columns = lines.value;
  319. variable.dimension = 1;
  320. const values = command.initial.value.map((exp) =>
  321. variableInitialWalker(exp)
  322. );
  323. variable.value = values;
  324. } else {
  325. const columns = expressionWalker(command.columns).pop();
  326. variable.dimension = 2;
  327. variable.rows = lines.value;
  328. variable.columns = columns.value;
  329. const values = command.initial.value.map((rows) =>
  330. rows.value.map((exp) => variableInitialWalker(exp))
  331. );
  332. variable.value = values;
  333. }
  334. } else {
  335. // atomic
  336. variable.type = command.type.value;
  337. variable.value = variableInitialWalker(command.initial);
  338. }
  339. return variable;
  340. }
  341. /**
  342. * @param {any} expression
  343. * */
  344. function variableInitialWalker (expression) {
  345. if (expression instanceof Expressions.UnaryApp) {
  346. const left = variableInitialWalker(expression.left);
  347. const opType = getOpType(expression.op);
  348. if (opType !== TYPES.ARITHMETIC) {
  349. throw new Error(
  350. "invalid variable initial value: " + expression.toString()
  351. );
  352. }
  353. return `${expression.op.value}${left}`;
  354. } else if (expression instanceof Expressions.BoolLiteral) {
  355. const value = expression.value;
  356. return convertBoolToString(value);
  357. } else if (expression instanceof Literal) {
  358. let value = expression.value;
  359. if (expression.value.toNumber) {
  360. if (
  361. Types.REAL.isCompatible(expression.type) &&
  362. expression.value.decimalPlaces() == 0
  363. ) {
  364. value = Number(expression.value.toFixed(2));
  365. } else {
  366. value = expression.value.toNumber();
  367. }
  368. }
  369. return value;
  370. }
  371. throw new Error("invalid variable initial value: " + expression.toString());
  372. }
  373. /**
  374. *
  375. * @return {[]}
  376. **/
  377. function expressionWalker (expression) {
  378. let result;
  379. if (expression instanceof Expressions.VariableLiteral) {
  380. result = [
  381. { instance: "expression", type: TYPES.VARIABLE, value: expression.id },
  382. ];
  383. } else if (expression instanceof Expressions.FunctionCall) {
  384. const funcObj = {
  385. instance: "expression",
  386. type: TYPES.FUNCTION,
  387. value: expression.id,
  388. };
  389. const paramsList = expression.actualParameters.map((e) =>
  390. expressionWalker(e)
  391. );
  392. //const params = Array.prototype.concat.apply([], paramsList);
  393. funcObj.params = paramsList;
  394. result = [funcObj];
  395. } else if (expression instanceof Expressions.UnaryApp) {
  396. const left = expressionWalker(expression.left);
  397. const opType = getOpType(expression.op);
  398. const opValue = translateOp(opType, expression.op);
  399. result = [{ instance: "operator", type: opType, value: opValue }, ...left];
  400. } else if (expression instanceof Expressions.InfixApp) {
  401. const left = expressionWalker(expression.left);
  402. const right = expressionWalker(expression.right);
  403. const opType = getOpType(expression.op);
  404. const opValue = translateOp(opType, expression.op);
  405. result = [
  406. ...left,
  407. { instance: "operator", type: opType, value: opValue },
  408. ...right,
  409. ];
  410. } else if (expression instanceof Expressions.ArrayAccess) {
  411. const line = expressionWalker(expression.line);
  412. let arrayClass = "vector";
  413. let column = null;
  414. if (expression.column) {
  415. arrayClass = "matrix";
  416. column = expressionWalker(expression.column);
  417. }
  418. result = [
  419. {
  420. instance: "expression",
  421. type: TYPES.VARIABLE,
  422. class: arrayClass,
  423. column: column,
  424. line: line,
  425. value: expression.id,
  426. },
  427. ];
  428. } else if (expression instanceof Expressions.BoolLiteral) {
  429. const value = expression.value;
  430. result = [
  431. {
  432. instance: "expression",
  433. class: "simple",
  434. type: TYPES.CONST,
  435. value: convertBoolToString(value),
  436. },
  437. ];
  438. } else {
  439. let value = expression.value;
  440. if (expression.value.toNumber) {
  441. if (
  442. Types.REAL.isCompatible(expression.type) &&
  443. expression.value.decimalPlaces() == 0
  444. ) {
  445. value = Number(expression.value.toFixed(2));
  446. } else {
  447. value = expression.value.toNumber();
  448. }
  449. }
  450. result = [
  451. {
  452. instance: "expression",
  453. class: "simple",
  454. type: TYPES.CONST,
  455. value: value,
  456. },
  457. ];
  458. }
  459. if (expression.parenthesis) return ["(", ...result, ")"];
  460. else return result;
  461. }
  462. export function parseExpression (text) {
  463. const parser = IVProgParser.createParser(text);
  464. const expressionAST = parser.parseExpressionOR();
  465. return expressionWalker(expressionAST);
  466. }
  467. /**
  468. * @param {string} text
  469. * */
  470. export function parseCode (text) {
  471. const parser = IVProgParser.createParser(text, false);
  472. const codeLinesMap = new Map();
  473. const tokens = Array.from(parser.lexer.reset(text));
  474. const tokenStream = [];
  475. for (const token of tokens) {
  476. if (token.type === parser.ruleNames.ERROR) {
  477. return null;
  478. }
  479. if (token.type === parser.ruleNames.COMMENTS) {
  480. for (let i = 0; i <= token.lineBreaks; i++) {
  481. if (codeLinesMap.has(i + token.line))
  482. codeLinesMap.get(i + token.line).push(token);
  483. else codeLinesMap.set(i + token.line, [token]);
  484. }
  485. continue;
  486. }
  487. if (token.type !== parser.ruleNames.WHITESPACE) {
  488. tokenStream.push(token);
  489. }
  490. }
  491. parser.fill(tokenStream);
  492. try {
  493. const program = parser.parseTree();
  494. const globals = program.global.map((decl) =>
  495. variableDeclarationWalker(decl, true)
  496. );
  497. const functions = program.functions.map(functionWalker);
  498. return { globals, functions };
  499. } catch (e) {
  500. console.error(e);
  501. return null;
  502. }
  503. }