Parser.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace NTERA.Interpreter.Compiler
  5. {
  6. public class Parser
  7. {
  8. protected Lexer Lexer { get; }
  9. protected FunctionDefinition SelfDefinition { get; }
  10. protected ICollection<FunctionDefinition> FunctionDefinitions { get; }
  11. protected ICollection<FunctionDefinition> ProcedureDefinitions { get; }
  12. protected VariableDictionary GlobalVariables { get; }
  13. protected VariableDictionary LocalVariables { get; }
  14. protected ICollection<string> StringStatements { get; }
  15. protected CSVDefinition CsvDefinition { get; }
  16. protected IEnumerator<Token> Enumerator { get; }
  17. protected bool hasPeeked = false;
  18. protected Token peekedToken = Token.Unknown;
  19. protected Token GetNextToken(bool peek = false)
  20. {
  21. if (peek && hasPeeked)
  22. return peekedToken;
  23. if (!hasPeeked)
  24. Enumerator.MoveNext();
  25. peekedToken = Enumerator.Current;
  26. hasPeeked = peek;
  27. return Enumerator.Current;
  28. }
  29. protected Marker CurrentPosition => new Marker(Lexer.TokenMarker.Pointer + SelfDefinition.Position.Pointer,
  30. Lexer.TokenMarker.Line + SelfDefinition.Position.Line - 1,
  31. Lexer.TokenMarker.Column);
  32. public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, ICollection<string> stringStatements, CSVDefinition csvDefinition)
  33. {
  34. Lexer = new Lexer(input);
  35. Enumerator = Lexer.GetEnumerator();
  36. SelfDefinition = selfDefinition;
  37. FunctionDefinitions = functionDefinitions;
  38. ProcedureDefinitions = procedureDefinitions;
  39. GlobalVariables = globalVariables;
  40. LocalVariables = localVariables;
  41. StringStatements = stringStatements;
  42. CsvDefinition = csvDefinition;
  43. }
  44. public IEnumerable<ExecutionNode> Parse(out List<ParserError> errors)
  45. {
  46. errors = new List<ParserError>();
  47. List<ExecutionNode> nodes = new List<ExecutionNode>();
  48. using (Enumerator)
  49. {
  50. do
  51. {
  52. var node = ParseLine(out var error);
  53. if (error != null)
  54. {
  55. errors.Add(error);
  56. nodes.Add(new ExecutionNode
  57. {
  58. Type = "error",
  59. Metadata =
  60. {
  61. ["message"] = error.ErrorMessage,
  62. ["symbol"] = error.SymbolMarker.ToString()
  63. },
  64. Symbol = error.SymbolMarker
  65. });
  66. //resynchronize to a new line
  67. while (Enumerator.MoveNext()
  68. && Enumerator.Current != Token.NewLine
  69. && Enumerator.Current != Token.EOF)
  70. {
  71. }
  72. }
  73. else if (node != null)
  74. {
  75. nodes.Add(node);
  76. }
  77. hasPeeked = false;
  78. } while (Enumerator.MoveNext());
  79. }
  80. return nodes;
  81. }
  82. protected ExecutionNode ParseLine(out ParserError error)
  83. {
  84. error = null;
  85. switch (Enumerator.Current)
  86. {
  87. case Token.Identifer:
  88. if (GlobalVariables.ContainsKey(Lexer.Identifer)
  89. || LocalVariables.ContainsKey(Lexer.Identifer))
  90. {
  91. string variableName = Lexer.Identifer;
  92. bool isGlobal = GlobalVariables.ContainsKey(variableName);
  93. var node = new ExecutionNode
  94. {
  95. Type = "assignment",
  96. Symbol = CurrentPosition
  97. };
  98. var variable = GetVariable(out error);
  99. if (error != null)
  100. return null;
  101. if (GetNextToken() != Token.Equal
  102. && Enumerator.Current != Token.Increment
  103. && Enumerator.Current != Token.Decrement
  104. && !Enumerator.Current.IsArithmetic())
  105. {
  106. error = new ParserError($"Unexpected token, expecting assignment: {Enumerator.Current}", CurrentPosition);
  107. return null;
  108. }
  109. ExecutionNode value = null;
  110. if (Enumerator.Current == Token.Increment)
  111. {
  112. value = OperateNodes(variable, CreateConstant(1), Token.Plus);
  113. }
  114. else if (Enumerator.Current == Token.Decrement)
  115. {
  116. value = OperateNodes(variable, CreateConstant(1), Token.Minus);
  117. }
  118. else if (Enumerator.Current != Token.Equal)
  119. {
  120. Token arithmeticToken = Enumerator.Current;
  121. if (GetNextToken() != Token.Equal)
  122. {
  123. error = new ParserError($"Unexpected token, expecting assignment: {Enumerator.Current}", CurrentPosition);
  124. return null;
  125. }
  126. ExecutionNode newValue = Expression(out error);
  127. value = OperateNodes(variable, newValue, arithmeticToken);
  128. }
  129. else
  130. {
  131. var type = isGlobal
  132. ? GlobalVariables[variableName].Type
  133. : LocalVariables[variableName].Type;
  134. value = type == ValueType.String
  135. ? StringExpression(out error)
  136. : Expression(out error);
  137. }
  138. if (error != null)
  139. return null;
  140. node.SubNodes = new[]
  141. {
  142. variable,
  143. new ExecutionNode
  144. {
  145. Type = "value",
  146. SubNodes = new[] { value }
  147. }
  148. };
  149. return node;
  150. }
  151. else if (Lexer.Identifer == "CASE")
  152. {
  153. var node = new ExecutionNode
  154. {
  155. Type = "case",
  156. Symbol = CurrentPosition
  157. };
  158. List<ExecutionNode> subNodes = new List<ExecutionNode>();
  159. do
  160. {
  161. var value = Expression(out error);
  162. if (error != null)
  163. return null;
  164. if (Enumerator.Current == Token.Identifer)
  165. {
  166. if (Lexer.Identifer == "TO")
  167. {
  168. var value2 = Expression(out error);
  169. if (error != null)
  170. return null;
  171. subNodes.Add(new ExecutionNode
  172. {
  173. Type = "case-to",
  174. SubNodes = new[] { value, value2 }
  175. });
  176. continue;
  177. }
  178. }
  179. subNodes.Add(new ExecutionNode
  180. {
  181. Type = "case-exact",
  182. SubNodes = new[] { value }
  183. });
  184. } while (Enumerator.Current == Token.Comma);
  185. if (Enumerator.Current != Token.NewLine
  186. && Enumerator.Current != Token.EOF)
  187. {
  188. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  189. return null;
  190. }
  191. node.SubNodes = subNodes.ToArray();
  192. return node;
  193. }
  194. else if (Lexer.Identifer == "CALL"
  195. || Lexer.Identifer == "BEGIN")
  196. {
  197. Enumerator.MoveNext();
  198. if (Enumerator.Current != Token.Identifer)
  199. {
  200. error = new ParserError($"Expecting a call to a function, got token instead: {Enumerator.Current}", CurrentPosition);
  201. return null;
  202. }
  203. Marker symbolMarker = CurrentPosition;
  204. string target = Lexer.Identifer;
  205. List<ExecutionNode> parameters = new List<ExecutionNode>();
  206. if (ProcedureDefinitions.All(x => !x.Name.Equals(target, StringComparison.OrdinalIgnoreCase)))
  207. {
  208. error = new ParserError($"Could not find procedure: {Lexer.Identifer}", CurrentPosition);
  209. return null;
  210. }
  211. Enumerator.MoveNext();
  212. while (Enumerator.Current != Token.NewLine
  213. && Enumerator.Current != Token.EOF)
  214. {
  215. parameters.Add(Expression(out error));
  216. if (error != null)
  217. {
  218. error = new ParserError($"{error.ErrorMessage} (target [{target}])", error.SymbolMarker);
  219. return null;
  220. }
  221. if (Enumerator.Current != Token.Comma
  222. && Enumerator.Current != Token.RParen
  223. && Enumerator.Current != Token.NewLine
  224. && Enumerator.Current != Token.EOF)
  225. {
  226. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  227. return null;
  228. }
  229. }
  230. if (Enumerator.Current == Token.RParen)
  231. Enumerator.MoveNext();
  232. if (Enumerator.Current != Token.NewLine
  233. && Enumerator.Current != Token.EOF)
  234. {
  235. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  236. return null;
  237. }
  238. return CallMethod(target, symbolMarker, parameters.ToArray());
  239. }
  240. else if (Lexer.Identifer == "CALLFORM"
  241. || Lexer.Identifer == "TRYCALLFORM")
  242. {
  243. string statementName = Lexer.Identifer;
  244. var node = new ExecutionNode
  245. {
  246. Type = "callform",
  247. Metadata =
  248. {
  249. ["try"] = statementName.StartsWith("TRY").ToString()
  250. },
  251. Symbol = CurrentPosition
  252. };
  253. ExecutionNode nameValue = null;
  254. List<ExecutionNode> parameters = new List<ExecutionNode>();
  255. Enumerator.MoveNext();
  256. do
  257. {
  258. ExecutionNode newValue = null;
  259. if (Enumerator.Current == Token.Identifer)
  260. {
  261. newValue = CreateConstant(Lexer.Identifer);
  262. }
  263. else if (Enumerator.Current == Token.OpenBracket)
  264. {
  265. newValue = Expression(out error);
  266. if (error != null)
  267. return null;
  268. }
  269. else
  270. {
  271. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  272. return null;
  273. }
  274. nameValue = nameValue == null
  275. ? newValue
  276. : OperateNodes(nameValue, newValue, Token.Plus);
  277. Enumerator.MoveNext();
  278. } while (Enumerator.Current != Token.Comma
  279. && Enumerator.Current != Token.NewLine
  280. && Enumerator.Current != Token.EOF);
  281. while (Enumerator.Current != Token.NewLine
  282. && Enumerator.Current != Token.EOF)
  283. {
  284. parameters.Add(Expression(out error));
  285. if (error != null)
  286. {
  287. error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
  288. return null;
  289. }
  290. if (Enumerator.Current != Token.Comma
  291. && Enumerator.Current != Token.NewLine
  292. && Enumerator.Current != Token.EOF)
  293. {
  294. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  295. return null;
  296. }
  297. }
  298. node.SubNodes = new[]
  299. {
  300. new ExecutionNode
  301. {
  302. Type = "name",
  303. SubNodes = new[] { nameValue }
  304. },
  305. new ExecutionNode
  306. {
  307. Type = "parameters",
  308. SubNodes = parameters.ToArray()
  309. },
  310. };
  311. return node;
  312. }
  313. else //treat as statement
  314. {
  315. string statementName = Lexer.Identifer;
  316. var node = new ExecutionNode
  317. {
  318. Type = "statement",
  319. Metadata =
  320. {
  321. ["name"] = statementName
  322. },
  323. Symbol = CurrentPosition
  324. };
  325. List<ExecutionNode> parameters = new List<ExecutionNode>();
  326. if (StringStatements.Contains(statementName))
  327. {
  328. var value = StringExpression(out error);
  329. if (error != null)
  330. return null;
  331. if (value != null)
  332. parameters.Add(value);
  333. node.SubNodes = parameters.ToArray();
  334. return node;
  335. }
  336. if (GetNextToken(true) == Token.NewLine
  337. || GetNextToken(true) == Token.EOF)
  338. {
  339. return node;
  340. }
  341. else if (GetNextToken(true) == Token.Colon
  342. || GetNextToken(true) == Token.Equal)
  343. {
  344. error = new ParserError($"Undeclared variable: {statementName}", node.Symbol);
  345. return null;
  346. }
  347. while (Enumerator.Current != Token.NewLine
  348. && Enumerator.Current != Token.EOF)
  349. {
  350. parameters.Add(Expression(out error));
  351. if (error != null)
  352. {
  353. error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
  354. return null;
  355. }
  356. if (Enumerator.Current != Token.Comma
  357. && Enumerator.Current != Token.NewLine
  358. && Enumerator.Current != Token.EOF)
  359. {
  360. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  361. return null;
  362. }
  363. }
  364. node.SubNodes = parameters.ToArray();
  365. return node;
  366. }
  367. case Token.AtSymbol:
  368. case Token.Sharp:
  369. while (Enumerator.MoveNext()
  370. && Enumerator.Current != Token.NewLine
  371. && Enumerator.Current != Token.EOF)
  372. {
  373. }
  374. return null;
  375. case Token.NewLine:
  376. case Token.EOF:
  377. return null;
  378. default:
  379. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  380. return null;
  381. }
  382. }
  383. protected ExecutionNode GetVariable(out ParserError error)
  384. {
  385. string variableName = Lexer.Identifer;
  386. var node = new ExecutionNode
  387. {
  388. Type = "variable",
  389. Metadata =
  390. {
  391. ["name"] = variableName
  392. },
  393. Symbol = CurrentPosition
  394. };
  395. List<ExecutionNode> indices = new List<ExecutionNode>();
  396. error = null;
  397. while (GetNextToken(true) == Token.Colon)
  398. {
  399. GetNextToken();
  400. var token = GetNextToken();
  401. if (token == Token.LParen)
  402. {
  403. indices.Add(Expression(out error));
  404. if (error != null)
  405. return null;
  406. if (Enumerator.Current != Token.RParen)
  407. {
  408. error = new ParserError("Invalid expression - Expected right bracket", CurrentPosition);
  409. return null;
  410. }
  411. }
  412. else if (token == Token.Value)
  413. {
  414. indices.Add(CreateConstant(Lexer.Value));
  415. }
  416. else if (token == Token.Identifer)
  417. {
  418. if (CsvDefinition.VariableIndexDictionary.TryGetValue(variableName, out var varTable)
  419. && varTable.TryGetValue(Lexer.Identifer, out int index))
  420. {
  421. indices.Add(CreateConstant(index));
  422. continue;
  423. }
  424. if (GlobalVariables.ContainsKey(Lexer.Identifer)
  425. || LocalVariables.ContainsKey(Lexer.Identifer))
  426. {
  427. var subNode = new ExecutionNode
  428. {
  429. Type = "variable",
  430. Metadata =
  431. {
  432. ["name"] = Lexer.Identifer
  433. },
  434. Symbol = CurrentPosition
  435. };
  436. indices.Add(subNode);
  437. continue;
  438. }
  439. if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifer))
  440. {
  441. indices.Add(GetFunction(out error));
  442. if (error != null)
  443. return null;
  444. continue;
  445. }
  446. error = new ParserError($"Unknown identifier: {Lexer.Identifer}", CurrentPosition);
  447. return null;
  448. }
  449. }
  450. if (indices.Count > 0)
  451. {
  452. ExecutionNode indexNode = new ExecutionNode
  453. {
  454. Type = "index",
  455. SubNodes = indices.ToArray()
  456. };
  457. node.SubNodes = new[] { indexNode };
  458. }
  459. return node;
  460. }
  461. protected ExecutionNode GetFunction(out ParserError error)
  462. {
  463. error = null;
  464. Marker symbolMarker = CurrentPosition;
  465. List<ExecutionNode> parameters = new List<ExecutionNode>();
  466. string functionName = Lexer.Identifer;
  467. if (GetNextToken() != Token.LParen)
  468. {
  469. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  470. return null;
  471. }
  472. while (Enumerator.Current == Token.Comma
  473. || Enumerator.Current == Token.LParen)
  474. {
  475. if (GetNextToken(true) == Token.RParen)
  476. break;
  477. parameters.Add(Expression(out error));
  478. if (error != null)
  479. return null;
  480. if (Enumerator.Current != Token.Comma
  481. && Enumerator.Current != Token.RParen)
  482. {
  483. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  484. return null;
  485. }
  486. }
  487. if (Enumerator.Current != Token.RParen)
  488. {
  489. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  490. return null;
  491. }
  492. if (hasPeeked)
  493. {
  494. GetNextToken();
  495. }
  496. var functionDefinition = FunctionDefinitions.FirstOrDefault(x => x.Name == functionName
  497. && (x.Parameters.Length >= parameters.Count
  498. || x.Parameters.Any(y => y.IsArrayParameter)));
  499. if (functionDefinition == null)
  500. {
  501. error = new ParserError($"No matching method with same amount of parameters: {functionName} ({parameters.Count})", CurrentPosition);
  502. return null;
  503. }
  504. return CallMethod(functionName, symbolMarker, parameters.ToArray());
  505. }
  506. private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>
  507. {
  508. { Token.Or, 0 }, { Token.And, 0 }, { Token.Not, 0 },
  509. { Token.Equal, 1 }, { Token.NotEqual, 1 },
  510. { Token.Less, 1 }, { Token.More, 1 }, { Token.LessEqual, 1 }, { Token.MoreEqual, 1 },
  511. { Token.Plus, 2 }, { Token.Minus, 2 },
  512. { Token.Asterisk, 3 }, { Token.Slash, 3 }, { Token.Modulo, 3 },
  513. { Token.Caret, 4 }
  514. };
  515. protected ExecutionNode Expression(out ParserError error, bool useModulo = true)
  516. {
  517. error = null;
  518. var operators = new Stack<Token>();
  519. var operands = new Stack<ExecutionNode>();
  520. Token token;
  521. void ProcessOperation(out ParserError localError)
  522. {
  523. localError = null;
  524. Token op = operators.Pop();
  525. if (op.IsUnary() && operands.Count >= 1)
  526. {
  527. var operand = operands.Pop();
  528. operands.Push(new ExecutionNode
  529. {
  530. Type = "operation",
  531. Metadata =
  532. {
  533. ["type"] = GetOperationName(op),
  534. ["unary"] = "true"
  535. },
  536. SubNodes = new[]
  537. {
  538. operand
  539. }
  540. });
  541. }
  542. else if (operands.Count >= 2)
  543. {
  544. ExecutionNode right = operands.Pop();
  545. ExecutionNode left = operands.Pop();
  546. operands.Push(new ExecutionNode
  547. {
  548. Type = "operation",
  549. Metadata =
  550. {
  551. ["type"] = GetOperationName(op),
  552. ["unary"] = "false"
  553. },
  554. SubNodes = new[]
  555. {
  556. left,
  557. right
  558. }
  559. });
  560. }
  561. else
  562. localError = new ParserError("Invalid expression - not enough operands", CurrentPosition);
  563. }
  564. void AttemptUnaryConversion(out ParserError localError)
  565. {
  566. localError = null;
  567. while (operators.Count > 0
  568. && operators.Peek().IsUnary())
  569. {
  570. ProcessOperation(out localError);
  571. if (localError != null)
  572. return;
  573. }
  574. }
  575. while ((token = GetNextToken()) != Token.NewLine
  576. && token != Token.EOF
  577. && token != Token.Comma
  578. && token != Token.Colon
  579. && token != Token.Format
  580. && token != Token.CloseBracket
  581. && (useModulo || token != Token.Modulo))
  582. {
  583. if (token == Token.Value)
  584. {
  585. operands.Push(CreateConstant(Lexer.Value));
  586. AttemptUnaryConversion(out error);
  587. if (error != null)
  588. return null;
  589. }
  590. else if (token == Token.Identifer)
  591. {
  592. if (GlobalVariables.ContainsKey(Lexer.Identifer)
  593. || LocalVariables.ContainsKey(Lexer.Identifer))
  594. {
  595. operands.Push(GetVariable(out error));
  596. if (error != null)
  597. return null;
  598. }
  599. else if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifer))
  600. {
  601. operands.Push(GetFunction(out error));
  602. if (error != null)
  603. return null;
  604. }
  605. else
  606. {
  607. break;
  608. //this should be converted into a warning
  609. error = new ParserError($"Unknown identifier: {Lexer.Identifer}", CurrentPosition);
  610. return null;
  611. }
  612. }
  613. else if (token.IsArithmetic())
  614. {
  615. if (token.IsUnary())
  616. {
  617. operators.Push(token);
  618. continue;
  619. }
  620. if (!operands.Any() && !token.IsUnary())
  621. {
  622. error = new ParserError($"Invalid unary operator: {token}", CurrentPosition);
  623. return null;
  624. }
  625. while (operators.Any() && OrderOfOps[token] <= OrderOfOps[operators.Peek()])
  626. {
  627. ProcessOperation(out error);
  628. if (error != null)
  629. return null;
  630. }
  631. operators.Push(token);
  632. }
  633. else if (token == Token.LParen)
  634. {
  635. operands.Push(Expression(out var localError));
  636. if (localError != null)
  637. {
  638. error = localError;
  639. return null;
  640. }
  641. }
  642. else if (token == Token.RParen)
  643. {
  644. break;
  645. }
  646. else
  647. {
  648. error = new ParserError($"Unexpected token: {token}", CurrentPosition);
  649. return null;
  650. }
  651. }
  652. while (operators.Any())
  653. {
  654. ProcessOperation(out error);
  655. if (error != null)
  656. return null;
  657. }
  658. if (!operands.Any())
  659. {
  660. error = new ParserError("Invalid expression - Empty operand stack", CurrentPosition);
  661. return null;
  662. }
  663. return operands.Pop();
  664. }
  665. protected ExecutionNode StringExpression(out ParserError error)
  666. {
  667. error = null;
  668. ExecutionNode value = null;
  669. Lexer.Type = LexerType.String;
  670. while (Enumerator.MoveNext()
  671. && (Enumerator.Current == Token.Value
  672. || Enumerator.Current == Token.Format
  673. || Enumerator.Current == Token.OpenBracket))
  674. {
  675. if (Enumerator.Current == Token.Value)
  676. {
  677. value = value == null
  678. ? CreateConstant(Lexer.Value)
  679. : OperateNodes(value, CreateConstant(Lexer.Value), Token.Plus);
  680. }
  681. else
  682. {
  683. List<ExecutionNode> formatParams = new List<ExecutionNode>();
  684. Marker symbolMarker = CurrentPosition;
  685. bool isSpecialFormat = Enumerator.Current == Token.OpenBracket;
  686. do
  687. {
  688. Lexer.Type = LexerType.Both;
  689. var tempValue = Expression(out error, isSpecialFormat);
  690. if (error != null)
  691. return null;
  692. formatParams.Add(tempValue);
  693. } while (Enumerator.Current == Token.Comma);
  694. var formattedValue = CallMethod("_FORMAT", symbolMarker, formatParams.ToArray());
  695. value = value == null
  696. ? formattedValue
  697. : OperateNodes(value, formattedValue, Token.Plus);
  698. Lexer.Type = LexerType.String;
  699. }
  700. }
  701. Lexer.Type = LexerType.Both;
  702. return value;
  703. }
  704. private static readonly Dictionary<Token, string> OperationNames = new Dictionary<Token, string>
  705. {
  706. [Token.Plus] = "add",
  707. [Token.Asterisk] = "multiply",
  708. [Token.Minus] = "subtract",
  709. [Token.Slash] = "divide",
  710. };
  711. private static string GetOperationName(Token token)
  712. {
  713. return OperationNames.TryGetValue(token, out string result)
  714. ? result
  715. : token.ToString();
  716. }
  717. private ExecutionNode CreateConstant(Value value)
  718. {
  719. return new ExecutionNode
  720. {
  721. Type = "constant",
  722. Metadata =
  723. {
  724. ["type"] = value.Type.ToString(),
  725. ["value"] = value.ToString()
  726. },
  727. Symbol = CurrentPosition
  728. };
  729. }
  730. private static ExecutionNode OperateNodes(ExecutionNode left, ExecutionNode right, Token token)
  731. {
  732. return new ExecutionNode
  733. {
  734. Type = "operation",
  735. Metadata =
  736. {
  737. ["type"] = GetOperationName(token)
  738. },
  739. SubNodes = new[]
  740. {
  741. left,
  742. right
  743. }
  744. };
  745. }
  746. private static ExecutionNode CallMethod(string methodName, Marker symbolMarker, params ExecutionNode[] parameters)
  747. {
  748. return new ExecutionNode
  749. {
  750. Type = "call",
  751. Metadata =
  752. {
  753. ["target"] = methodName
  754. },
  755. Symbol = symbolMarker,
  756. SubNodes = new[]
  757. {
  758. new ExecutionNode
  759. {
  760. Type = "parameters",
  761. SubNodes = parameters.ToArray()
  762. }
  763. }
  764. };
  765. }
  766. }
  767. }