Parser.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  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;
  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. && Enumerator.Current != Token.RParen)
  215. {
  216. parameters.Add(Expression(out error));
  217. if (error != null)
  218. {
  219. error = new ParserError($"{error.ErrorMessage} (target [{target}])", error.SymbolMarker);
  220. return null;
  221. }
  222. if (Enumerator.Current != Token.Comma
  223. && Enumerator.Current != Token.RParen
  224. && Enumerator.Current != Token.NewLine
  225. && Enumerator.Current != Token.EOF)
  226. {
  227. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  228. return null;
  229. }
  230. }
  231. if (Enumerator.Current == Token.RParen)
  232. Enumerator.MoveNext();
  233. if (Enumerator.Current != Token.NewLine
  234. && Enumerator.Current != Token.EOF)
  235. {
  236. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  237. return null;
  238. }
  239. return CallMethod(target, symbolMarker, parameters.ToArray());
  240. }
  241. else if (Lexer.Identifer == "CALLFORM"
  242. || Lexer.Identifer == "TRYCALLFORM")
  243. {
  244. string statementName = Lexer.Identifer;
  245. var node = new ExecutionNode
  246. {
  247. Type = "callform",
  248. Metadata =
  249. {
  250. ["try"] = statementName.StartsWith("TRY").ToString()
  251. },
  252. Symbol = CurrentPosition
  253. };
  254. ExecutionNode nameValue = null;
  255. List<ExecutionNode> parameters = new List<ExecutionNode>();
  256. Enumerator.MoveNext();
  257. do
  258. {
  259. ExecutionNode newValue = null;
  260. if (Enumerator.Current == Token.Identifer)
  261. {
  262. newValue = CreateConstant(Lexer.Identifer);
  263. }
  264. else if (Enumerator.Current == Token.OpenBracket)
  265. {
  266. newValue = Expression(out error);
  267. if (error != null)
  268. return null;
  269. }
  270. else
  271. {
  272. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  273. return null;
  274. }
  275. nameValue = nameValue == null
  276. ? newValue
  277. : OperateNodes(nameValue, newValue, Token.Plus);
  278. Enumerator.MoveNext();
  279. } while (Enumerator.Current != Token.Comma
  280. && Enumerator.Current != Token.NewLine
  281. && Enumerator.Current != Token.EOF);
  282. while (Enumerator.Current != Token.NewLine
  283. && Enumerator.Current != Token.EOF)
  284. {
  285. parameters.Add(Expression(out error));
  286. if (error != null)
  287. {
  288. error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
  289. return null;
  290. }
  291. if (Enumerator.Current != Token.Comma
  292. && Enumerator.Current != Token.NewLine
  293. && Enumerator.Current != Token.EOF)
  294. {
  295. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  296. return null;
  297. }
  298. }
  299. node.SubNodes = new[]
  300. {
  301. new ExecutionNode
  302. {
  303. Type = "name",
  304. SubNodes = new[] { nameValue }
  305. },
  306. new ExecutionNode
  307. {
  308. Type = "parameters",
  309. SubNodes = parameters.ToArray()
  310. },
  311. };
  312. return node;
  313. }
  314. else //treat as statement
  315. {
  316. string statementName = Lexer.Identifer;
  317. var node = new ExecutionNode
  318. {
  319. Type = "statement",
  320. Metadata =
  321. {
  322. ["name"] = statementName
  323. },
  324. Symbol = CurrentPosition
  325. };
  326. List<ExecutionNode> parameters = new List<ExecutionNode>();
  327. if (StringStatements.Contains(statementName))
  328. {
  329. var value = StringExpression(out error);
  330. if (error != null)
  331. return null;
  332. if (value != null)
  333. parameters.Add(value);
  334. node.SubNodes = parameters.ToArray();
  335. return node;
  336. }
  337. if (GetNextToken(true) == Token.NewLine
  338. || GetNextToken(true) == Token.EOF)
  339. {
  340. return node;
  341. }
  342. if (GetNextToken(true) == Token.Colon
  343. || GetNextToken(true) == Token.Equal)
  344. {
  345. error = new ParserError($"Undeclared variable: {statementName}", node.Symbol);
  346. return null;
  347. }
  348. while (Enumerator.Current != Token.NewLine
  349. && Enumerator.Current != Token.EOF)
  350. {
  351. parameters.Add(Expression(out error));
  352. if (error != null)
  353. {
  354. error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
  355. return null;
  356. }
  357. if (Enumerator.Current != Token.Comma
  358. && Enumerator.Current != Token.NewLine
  359. && Enumerator.Current != Token.EOF)
  360. {
  361. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  362. return null;
  363. }
  364. }
  365. node.SubNodes = parameters.ToArray();
  366. return node;
  367. }
  368. case Token.AtSymbol:
  369. case Token.Sharp:
  370. while (Enumerator.MoveNext()
  371. && Enumerator.Current != Token.NewLine
  372. && Enumerator.Current != Token.EOF)
  373. {
  374. }
  375. return null;
  376. case Token.NewLine:
  377. case Token.EOF:
  378. return null;
  379. default:
  380. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  381. return null;
  382. }
  383. }
  384. protected ExecutionNode GetVariable(out ParserError error)
  385. {
  386. string variableName = Lexer.Identifer;
  387. var node = new ExecutionNode
  388. {
  389. Type = "variable",
  390. Metadata =
  391. {
  392. ["name"] = variableName
  393. },
  394. Symbol = CurrentPosition
  395. };
  396. List<ExecutionNode> indices = new List<ExecutionNode>();
  397. error = null;
  398. while (GetNextToken(true) == Token.Colon)
  399. {
  400. GetNextToken();
  401. var token = GetNextToken();
  402. if (token == Token.LParen)
  403. {
  404. indices.Add(Expression(out error));
  405. if (error != null)
  406. return null;
  407. if (Enumerator.Current != Token.RParen)
  408. {
  409. error = new ParserError("Invalid expression - Expected right bracket", CurrentPosition);
  410. return null;
  411. }
  412. }
  413. else if (token == Token.Value)
  414. {
  415. indices.Add(CreateConstant(Lexer.Value));
  416. }
  417. else if (token == Token.Identifer)
  418. {
  419. if (CsvDefinition.VariableIndexDictionary.TryGetValue(variableName, out var varTable)
  420. && varTable.TryGetValue(Lexer.Identifer, out int index))
  421. {
  422. indices.Add(CreateConstant(index));
  423. continue;
  424. }
  425. if (GlobalVariables.ContainsKey(Lexer.Identifer)
  426. || LocalVariables.ContainsKey(Lexer.Identifer))
  427. {
  428. var subNode = new ExecutionNode
  429. {
  430. Type = "variable",
  431. Metadata =
  432. {
  433. ["name"] = Lexer.Identifer
  434. },
  435. Symbol = CurrentPosition
  436. };
  437. indices.Add(subNode);
  438. continue;
  439. }
  440. if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifer))
  441. {
  442. indices.Add(GetFunction(out error));
  443. if (error != null)
  444. return null;
  445. continue;
  446. }
  447. error = new ParserError($"Unknown identifier: {Lexer.Identifer}", CurrentPosition);
  448. return null;
  449. }
  450. }
  451. if (indices.Count > 0)
  452. {
  453. ExecutionNode indexNode = new ExecutionNode
  454. {
  455. Type = "index",
  456. SubNodes = indices.ToArray()
  457. };
  458. node.SubNodes = new[] { indexNode };
  459. }
  460. return node;
  461. }
  462. protected ExecutionNode GetFunction(out ParserError error)
  463. {
  464. error = null;
  465. Marker symbolMarker = CurrentPosition;
  466. List<ExecutionNode> parameters = new List<ExecutionNode>();
  467. string functionName = Lexer.Identifer;
  468. if (GetNextToken() != Token.LParen)
  469. {
  470. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  471. return null;
  472. }
  473. while (Enumerator.Current == Token.Comma
  474. || Enumerator.Current == Token.LParen)
  475. {
  476. if (GetNextToken(true) == Token.RParen)
  477. break;
  478. parameters.Add(Expression(out error));
  479. if (error != null)
  480. return null;
  481. if (Enumerator.Current != Token.Comma
  482. && Enumerator.Current != Token.RParen)
  483. {
  484. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  485. return null;
  486. }
  487. }
  488. if (Enumerator.Current != Token.RParen)
  489. {
  490. error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
  491. return null;
  492. }
  493. if (hasPeeked)
  494. {
  495. GetNextToken();
  496. }
  497. var functionDefinition = FunctionDefinitions.FirstOrDefault(x => x.Name == functionName
  498. && (x.Parameters.Length >= parameters.Count
  499. || x.Parameters.Any(y => y.IsArrayParameter)));
  500. if (functionDefinition == null)
  501. {
  502. error = new ParserError($"No matching method with same amount of parameters: {functionName} ({parameters.Count})", CurrentPosition);
  503. return null;
  504. }
  505. return CallMethod(functionName, symbolMarker, parameters.ToArray());
  506. }
  507. private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>
  508. {
  509. { Token.Or, 0 }, { Token.And, 0 }, { Token.Not, 0 },
  510. { Token.Equal, 1 }, { Token.NotEqual, 1 },
  511. { Token.Less, 1 }, { Token.More, 1 }, { Token.LessEqual, 1 }, { Token.MoreEqual, 1 },
  512. { Token.Plus, 2 }, { Token.Minus, 2 },
  513. { Token.Asterisk, 3 }, { Token.Slash, 3 }, { Token.Modulo, 3 },
  514. { Token.Caret, 4 }
  515. };
  516. protected ExecutionNode Expression(out ParserError error, bool useModulo = true)
  517. {
  518. error = null;
  519. var operators = new Stack<Token>();
  520. var operands = new Stack<ExecutionNode>();
  521. Token token;
  522. void ProcessOperation(out ParserError localError)
  523. {
  524. localError = null;
  525. Token op = operators.Pop();
  526. if (op.IsUnary() && operands.Count >= 1)
  527. {
  528. var operand = operands.Pop();
  529. operands.Push(new ExecutionNode
  530. {
  531. Type = "operation",
  532. Metadata =
  533. {
  534. ["type"] = GetOperationName(op),
  535. ["unary"] = "true"
  536. },
  537. SubNodes = new[]
  538. {
  539. operand
  540. }
  541. });
  542. }
  543. else if (operands.Count >= 2)
  544. {
  545. ExecutionNode right = operands.Pop();
  546. ExecutionNode left = operands.Pop();
  547. operands.Push(new ExecutionNode
  548. {
  549. Type = "operation",
  550. Metadata =
  551. {
  552. ["type"] = GetOperationName(op),
  553. ["unary"] = "false"
  554. },
  555. SubNodes = new[]
  556. {
  557. left,
  558. right
  559. }
  560. });
  561. }
  562. else
  563. localError = new ParserError("Invalid expression - not enough operands", CurrentPosition);
  564. }
  565. void AttemptUnaryConversion(out ParserError localError)
  566. {
  567. localError = null;
  568. while (operators.Count > 0
  569. && operators.Peek().IsUnary())
  570. {
  571. ProcessOperation(out localError);
  572. if (localError != null)
  573. return;
  574. }
  575. }
  576. while ((token = GetNextToken()) != Token.NewLine
  577. && token != Token.EOF
  578. && token != Token.Comma
  579. && token != Token.Colon
  580. && token != Token.Format
  581. && token != Token.CloseBracket
  582. && token != Token.RParen
  583. && (useModulo || token != Token.Modulo))
  584. {
  585. if (token == Token.Value)
  586. {
  587. operands.Push(CreateConstant(Lexer.Value));
  588. AttemptUnaryConversion(out error);
  589. if (error != null)
  590. return null;
  591. }
  592. else if (token == Token.Identifer)
  593. {
  594. if (GlobalVariables.ContainsKey(Lexer.Identifer)
  595. || LocalVariables.ContainsKey(Lexer.Identifer))
  596. {
  597. operands.Push(GetVariable(out error));
  598. if (error != null)
  599. return null;
  600. }
  601. else if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifer))
  602. {
  603. operands.Push(GetFunction(out error));
  604. if (error != null)
  605. return null;
  606. }
  607. else
  608. {
  609. break;
  610. //this should be converted into a warning
  611. error = new ParserError($"Unknown identifier: {Lexer.Identifer}", CurrentPosition);
  612. return null;
  613. }
  614. }
  615. else if (token.IsArithmetic())
  616. {
  617. if (token.IsUnary())
  618. {
  619. operators.Push(token);
  620. continue;
  621. }
  622. if (!operands.Any() && !token.IsUnary())
  623. {
  624. error = new ParserError($"Invalid unary operator: {token}", CurrentPosition);
  625. return null;
  626. }
  627. while (operators.Any() && OrderOfOps[token] <= OrderOfOps[operators.Peek()])
  628. {
  629. ProcessOperation(out error);
  630. if (error != null)
  631. return null;
  632. }
  633. operators.Push(token);
  634. }
  635. else if (token == Token.LParen)
  636. {
  637. operands.Push(Expression(out var localError));
  638. if (localError != null)
  639. {
  640. error = localError;
  641. return null;
  642. }
  643. }
  644. else if (token == Token.RParen)
  645. {
  646. break;
  647. }
  648. else
  649. {
  650. error = new ParserError($"Unexpected token: {token}", CurrentPosition);
  651. return null;
  652. }
  653. }
  654. while (operators.Any())
  655. {
  656. ProcessOperation(out error);
  657. if (error != null)
  658. return null;
  659. }
  660. if (!operands.Any())
  661. {
  662. error = new ParserError("Invalid expression - Empty operand stack", CurrentPosition);
  663. return null;
  664. }
  665. return operands.Pop();
  666. }
  667. protected ExecutionNode StringExpression(out ParserError error)
  668. {
  669. error = null;
  670. ExecutionNode value = null;
  671. Lexer.Type = LexerType.String;
  672. while (Enumerator.MoveNext()
  673. && (Enumerator.Current == Token.Value
  674. || Enumerator.Current == Token.Format
  675. || Enumerator.Current == Token.OpenBracket))
  676. {
  677. if (Enumerator.Current == Token.Value)
  678. {
  679. value = value == null
  680. ? CreateConstant(Lexer.Value)
  681. : OperateNodes(value, CreateConstant(Lexer.Value), Token.Plus);
  682. }
  683. else
  684. {
  685. List<ExecutionNode> formatParams = new List<ExecutionNode>();
  686. Marker symbolMarker = CurrentPosition;
  687. bool isSpecialFormat = Enumerator.Current == Token.OpenBracket;
  688. do
  689. {
  690. Lexer.Type = LexerType.Both;
  691. var tempValue = Expression(out error, isSpecialFormat);
  692. if (error != null)
  693. return null;
  694. formatParams.Add(tempValue);
  695. } while (Enumerator.Current == Token.Comma);
  696. var formattedValue = CallMethod("__FORMAT", symbolMarker, formatParams.ToArray());
  697. value = value == null
  698. ? formattedValue
  699. : OperateNodes(value, formattedValue, Token.Plus);
  700. Lexer.Type = LexerType.String;
  701. }
  702. }
  703. Lexer.Type = LexerType.Both;
  704. return value;
  705. }
  706. private static readonly Dictionary<Token, string> OperationNames = new Dictionary<Token, string>
  707. {
  708. [Token.Plus] = "add",
  709. [Token.Asterisk] = "multiply",
  710. [Token.Minus] = "subtract",
  711. [Token.Slash] = "divide",
  712. };
  713. private static string GetOperationName(Token token)
  714. {
  715. return OperationNames.TryGetValue(token, out string result)
  716. ? result
  717. : token.ToString();
  718. }
  719. private ExecutionNode CreateConstant(Value value)
  720. {
  721. return new ExecutionNode
  722. {
  723. Type = "constant",
  724. Metadata =
  725. {
  726. ["type"] = value.Type.ToString(),
  727. ["value"] = value.ToString()
  728. },
  729. Symbol = CurrentPosition
  730. };
  731. }
  732. private static ExecutionNode OperateNodes(ExecutionNode left, ExecutionNode right, Token token)
  733. {
  734. return new ExecutionNode
  735. {
  736. Type = "operation",
  737. Metadata =
  738. {
  739. ["type"] = GetOperationName(token)
  740. },
  741. SubNodes = new[]
  742. {
  743. left,
  744. right
  745. }
  746. };
  747. }
  748. private static ExecutionNode CallMethod(string methodName, Marker symbolMarker, params ExecutionNode[] parameters)
  749. {
  750. return new ExecutionNode
  751. {
  752. Type = "call",
  753. Metadata =
  754. {
  755. ["target"] = methodName
  756. },
  757. Symbol = symbolMarker,
  758. SubNodes = new[]
  759. {
  760. new ExecutionNode
  761. {
  762. Type = "parameters",
  763. SubNodes = parameters.ToArray()
  764. }
  765. }
  766. };
  767. }
  768. }
  769. }