parseFromVisual.js 13 KB

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