123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NTERA.Engine.Compiler
- {
- public class Parser
- {
- protected Lexer Lexer { get; }
- protected FunctionDefinition SelfDefinition { get; }
- protected ICollection<FunctionDefinition> FunctionDefinitions { get; }
- protected ICollection<FunctionDefinition> ProcedureDefinitions { get; }
- protected ICollection<FunctionVariable> ConstantDefinitions { get; }
- protected ICollection<FunctionVariable> GlobalVariables { get; }
- protected ICollection<FunctionVariable> LocalVariables { get; }
- protected ICollection<Keyword> ExplicitKeywords { get; }
- protected CSVDefinition CsvDefinition { get; }
- protected List<ParserError> Errors { get; } = new List<ParserError>();
- protected List<ParserError> Warnings { get; } = new List<ParserError>();
- protected IEnumerator<Token> Enumerator { get; }
- protected bool hasPeeked = false;
- protected Token peekedToken = Token.Unknown;
-
- protected Token GetNextToken(bool peek = false)
- {
- if (peek && hasPeeked)
- return peekedToken;
- if (!hasPeeked)
- Enumerator.MoveNext();
- peekedToken = Enumerator.Current;
- hasPeeked = peek;
- return Enumerator.Current;
- }
- protected Marker CurrentPosition => new Marker(Lexer.TokenMarker.Pointer + SelfDefinition.Position.Pointer,
- Lexer.TokenMarker.Line + SelfDefinition.Position.Line - 1,
- Lexer.TokenMarker.Column);
- public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, ICollection<FunctionVariable> globalVariables, ICollection<FunctionVariable> localVariables, ICollection<Keyword> explicitKeywords, CSVDefinition csvDefinition, ICollection<FunctionVariable> constantDefinitions)
- {
- Lexer = new Lexer(input);
- Enumerator = Lexer.GetEnumerator();
- SelfDefinition = selfDefinition;
- FunctionDefinitions = functionDefinitions;
- ProcedureDefinitions = procedureDefinitions;
- ConstantDefinitions = constantDefinitions;
- GlobalVariables = globalVariables;
- LocalVariables = localVariables;
- ExplicitKeywords = explicitKeywords;
- CsvDefinition = csvDefinition;
- }
- public IEnumerable<ExecutionNode> Parse(out List<ParserError> errors, out List<ParserError> warnings)
- {
- List<ExecutionNode> nodes = new List<ExecutionNode>();
- using (Enumerator)
- {
- do
- {
- var node = ParseLine(out var error);
- if (error != null)
- {
- Errors.Add(error);
- nodes.Add(new ExecutionNode
- {
- Type = "error",
- Metadata =
- {
- ["message"] = error.ErrorMessage,
- ["symbol"] = error.SymbolMarker.ToString()
- },
- Symbol = error.SymbolMarker
- });
- //resynchronize to a new line
- while (Enumerator.MoveNext()
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- }
- }
- else if (node != null)
- {
- nodes.Add(node);
- }
- hasPeeked = false;
- } while (Enumerator.MoveNext());
- }
- errors = Errors;
- warnings = Warnings;
- if (errors.Count == 0)
- PostProcess(nodes);
- return nodes;
- }
- #region Processor
- protected ExecutionNode ParseLine(out ParserError error)
- {
- error = null;
- switch (Enumerator.Current)
- {
- case Token.Identifer:
- if (IsVariable(Lexer.Identifier))
- {
- string variableName = Lexer.Identifier;
- ValueType type = 0;
-
- if (GlobalVariables.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
- type = GlobalVariables.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
- else if (LocalVariables.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
- type = LocalVariables.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
- else if (ConstantDefinitions.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
- type = ConstantDefinitions.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
- var node = new ExecutionNode
- {
- Type = "assignment",
- Symbol = CurrentPosition
- };
- var variable = GetVariable(out error);
- if (error != null)
- return null;
- if (GetNextToken() != Token.Equal
- && Enumerator.Current != Token.Increment
- && Enumerator.Current != Token.Decrement
- && Enumerator.Current != Token.Append
- && !Enumerator.Current.IsArithmetic())
- {
- error = new ParserError($"Unexpected token, expecting assignment: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- ExecutionNode value;
- if (Enumerator.Current == Token.Increment)
- {
- value = OperateNodes(variable, CreateConstant(1, CurrentPosition), Token.Plus);
- }
- else if (Enumerator.Current == Token.Decrement)
- {
- value = OperateNodes(variable, CreateConstant(1, CurrentPosition), Token.Minus);
- }
- else if (Enumerator.Current == Token.Append)
- {
- value = OperateNodes(variable, Expression(out error), Token.Plus);
- if (error != null)
- return null;
- }
- else if (Enumerator.Current != Token.Equal)
- {
- Token arithmeticToken = Enumerator.Current;
- if (GetNextToken() != Token.Equal)
- {
- error = new ParserError($"Unexpected token, expecting assignment: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- ExecutionNode newValue = Expression(out error);
- value = OperateNodes(variable, newValue, arithmeticToken);
- }
- else
- {
- value = type == ValueType.String
- ? ParseString(out error, true, true)
- : Expression(out error);
- }
- if (error != null)
- return null;
- node.SubNodes = new[]
- {
- variable,
- new ExecutionNode
- {
- Type = "value",
- SubNodes = new[] { value }
- }
- };
- return node;
- }
- else if (Lexer.Identifier.Equals("CASE", StringComparison.OrdinalIgnoreCase))
- {
- var node = new ExecutionNode
- {
- Type = "case",
- Symbol = CurrentPosition
- };
- List<ExecutionNode> subNodes = new List<ExecutionNode>();
- do
- {
- if (GetNextToken(true) == Token.NewLine
- || GetNextToken(true) == Token.EOF)
- break;
- var value = Expression(out error);
- if (error != null)
- return null;
- if (Enumerator.Current == Token.To)
- {
- var value2 = Expression(out error);
- if (error != null)
- return null;
- subNodes.Add(new ExecutionNode
- {
- Type = "case-to",
- SubNodes = new[] { value, value2 }
- });
- continue;
- }
- subNodes.Add(new ExecutionNode
- {
- Type = "case-exact",
- SubNodes = new[] { value }
- });
- } while (Enumerator.Current == Token.Comma);
- if (Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- node.SubNodes = subNodes.ToArray();
- return node;
- }
- else if (Lexer.Identifier.Equals("CALL", StringComparison.OrdinalIgnoreCase)
- || Lexer.Identifier.Equals("TRYCALL", StringComparison.OrdinalIgnoreCase))
- {
- Enumerator.MoveNext();
- if (Enumerator.Current != Token.Identifer)
- {
- error = new ParserError($"Expecting a call to a function, got token instead: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- Marker symbolMarker = CurrentPosition;
- string target = Lexer.Identifier;
- List<ExecutionNode> parameters = new List<ExecutionNode>();
- if (ProcedureDefinitions.All(x => !x.Name.Equals(target, StringComparison.OrdinalIgnoreCase)))
- {
- error = new ParserError($"Could not find procedure: {Lexer.Identifier}", CurrentPosition);
- return null;
- }
- Enumerator.MoveNext();
- while (Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF
- && Enumerator.Current != Token.RParen)
- {
- parameters.Add(Expression(out error));
- if (error != null)
- {
- error = new ParserError($"{error.ErrorMessage} (target [{target}])", error.SymbolMarker);
- return null;
- }
- if (Enumerator.Current != Token.Comma
- && Enumerator.Current != Token.RParen
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- }
- if (Enumerator.Current == Token.RParen)
- Enumerator.MoveNext();
- if (Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- return CallMethod(target, symbolMarker, parameters.ToArray());
- }
- else if (Lexer.Identifier.Equals("CALLFORM", StringComparison.OrdinalIgnoreCase)
- || Lexer.Identifier.Equals("TRYCALLFORM", StringComparison.OrdinalIgnoreCase)
- || Lexer.Identifier.Equals("TRYCCALLFORM", StringComparison.OrdinalIgnoreCase)
- || Lexer.Identifier.Equals("TRYJUMPFORM", StringComparison.OrdinalIgnoreCase))
- {
- string statementName = Lexer.Identifier;
- var node = new ExecutionNode
- {
- Type = "callform",
- Metadata =
- {
- ["try"] = statementName.StartsWith("TRY").ToString()
- },
- Symbol = CurrentPosition
- };
- ExecutionNode nameValue = null;
- List<ExecutionNode> parameters = new List<ExecutionNode>();
- Enumerator.MoveNext();
- do
- {
- ExecutionNode newValue = null;
- if (Enumerator.Current == Token.Identifer)
- {
- newValue = CreateConstant(Lexer.Identifier, CurrentPosition);
- }
- else if (Enumerator.Current == Token.OpenBracket)
- {
- newValue = Expression(out error);
- if (error != null)
- return null;
- }
- else if (Enumerator.Current == Token.LParen)
- {
- break;
- }
- else
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- nameValue = nameValue == null
- ? newValue
- : OperateNodes(nameValue, newValue, Token.Plus);
- Enumerator.MoveNext();
- } while (Enumerator.Current != Token.Comma
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF);
- while (Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF
- && Enumerator.Current != Token.RParen)
- {
- parameters.Add(Expression(out error));
- if (error != null)
- {
- error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
- return null;
- }
- if (Enumerator.Current != Token.Comma
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF
- && Enumerator.Current != Token.RParen)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- }
- node.SubNodes = new[]
- {
- new ExecutionNode
- {
- Type = "name",
- SubNodes = new[] { nameValue }
- },
- new ExecutionNode
- {
- Type = "parameters",
- SubNodes = parameters.ToArray()
- },
- };
- return node;
- }
- else if (Lexer.Identifier.Equals("BEGIN", StringComparison.OrdinalIgnoreCase))
- {
- var node = new ExecutionNode
- {
- Type = "statement",
- Metadata =
- {
- ["name"] = "BEGIN"
- },
- Symbol = CurrentPosition
- };
- Enumerator.MoveNext();
- if (Enumerator.Current != Token.Identifer)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- node.SubNodes = new[] { CreateConstant(Lexer.Identifier, CurrentPosition) };
- return node;
- }
- else //treat as statement
- {
- string statementName = Lexer.Identifier;
- var node = new ExecutionNode
- {
- Type = "statement",
- Metadata =
- {
- ["name"] = statementName
- },
- Symbol = CurrentPosition
- };
- List<ExecutionNode> parameters = new List<ExecutionNode>();
- Keyword keyword = ExplicitKeywords.FirstOrDefault(x => x.Name == statementName);
- if (keyword?.ImplicitString == true)
- {
- var value = ParseString(out error, true, keyword.ImplicitFormatted);
- if (error != null)
- return null;
- if (value != null)
- parameters.Add(value);
- node.SubNodes = parameters.ToArray();
- return node;
- }
- if (GetNextToken(true) == Token.NewLine
- || GetNextToken(true) == Token.EOF)
- {
- return node;
- }
- if (GetNextToken(true) == Token.Colon
- || GetNextToken(true) == Token.Equal)
- {
- error = new ParserError($"Undeclared variable: {statementName}", node.Symbol);
- return null;
- }
- while (Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- parameters.Add(Expression(out error));
- if (error != null)
- {
- error = new ParserError($"{error.ErrorMessage} (statement [{statementName}])", error.SymbolMarker);
- return null;
- }
- if (Enumerator.Current != Token.Comma
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- }
- node.SubNodes = parameters.ToArray();
- return node;
- }
- case Token.AtSymbol:
- case Token.Sharp:
- while (Enumerator.MoveNext()
- && Enumerator.Current != Token.NewLine
- && Enumerator.Current != Token.EOF)
- {
- }
- return null;
- case Token.NewLine:
- case Token.EOF:
- return null;
- default:
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- }
- protected bool IsVariable(string identifier)
- {
- return GlobalVariables.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase))
- || LocalVariables.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase))
- || ConstantDefinitions.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase));
- }
- protected ExecutionNode GetVariable(out ParserError error)
- {
- string variableName = Lexer.Identifier;
- Marker symbol = CurrentPosition;
- List<ExecutionNode> indices = new List<ExecutionNode>();
- error = null;
- while (GetNextToken(true) == Token.Colon)
- {
- GetNextToken();
- var token = GetNextToken();
- if (token == Token.LParen)
- {
- indices.Add(Expression(out error));
- if (error != null)
- return null;
- if (Enumerator.Current != Token.RParen)
- {
- error = new ParserError("Invalid expression - Expected right bracket", CurrentPosition);
- return null;
- }
- }
- else if (token == Token.Value)
- {
- indices.Add(CreateConstant(Lexer.Value, CurrentPosition));
- }
- else if (token == Token.Identifer)
- {
- if (CsvDefinition.VariableIndexDictionary.TryGetValue(variableName, out var varTable)
- && varTable.TryGetValue(Lexer.Identifier, out int index))
- {
- indices.Add(CreateConstant(index, CurrentPosition));
- continue;
- }
- if (IsVariable(Lexer.Identifier))
- {
- var subNode = new ExecutionNode
- {
- Type = "variable",
- Metadata =
- {
- ["name"] = Lexer.Identifier
- },
- Symbol = CurrentPosition
- };
- indices.Add(subNode);
- continue;
- }
- if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifier))
- {
- indices.Add(GetFunction(out error));
- if (error != null)
- return null;
- continue;
- }
- error = new ParserError($"Unknown identifier: {Lexer.Identifier}", CurrentPosition);
- return null;
- }
- }
- return GetVariable(variableName, symbol, indices.ToArray());
- }
- protected ExecutionNode GetFunction(out ParserError error)
- {
- error = null;
- Marker symbolMarker = CurrentPosition;
- List<ExecutionNode> parameters = new List<ExecutionNode>();
- string functionName = Lexer.Identifier;
- if (GetNextToken() != Token.LParen)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- while (Enumerator.Current == Token.Comma
- || Enumerator.Current == Token.LParen)
- {
- if (GetNextToken(true) == Token.RParen)
- break;
- if (GetNextToken(true) == Token.Comma)
- {
- var defaultValue = new ExecutionNode
- {
- Type = "defaultvalue",
- Symbol = CurrentPosition
- };
- parameters.Add(defaultValue);
- GetNextToken();
- continue;
- }
- parameters.Add(Expression(out error));
- if (error != null)
- return null;
- if (Enumerator.Current != Token.Comma
- && Enumerator.Current != Token.RParen)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- }
- if (Enumerator.Current != Token.RParen)
- {
- error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
- return null;
- }
- if (hasPeeked)
- {
- GetNextToken();
- }
- var functionDefinition = FunctionDefinitions.FirstOrDefault(x => x.Name == functionName
- && (x.Parameters.Length >= parameters.Count
- || x.Parameters.Any(y => y.IsArrayParameter)));
- if (functionDefinition == null)
- {
- error = new ParserError($"No matching method with same amount of parameters: {functionName} ({parameters.Count})", CurrentPosition);
- return null;
- }
- return CallMethod(functionName, symbolMarker, parameters.ToArray());
- }
- private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>
- {
- { Token.Or, 0 }, { Token.And, 0 }, { Token.Not, 0 },
- { Token.Equal, 1 }, { Token.NotEqual, 1 },
- { Token.Less, 1 }, { Token.More, 1 }, { Token.LessEqual, 1 }, { Token.MoreEqual, 1 },
- { Token.Plus, 2 }, { Token.Minus, 2 },
- { Token.Asterisk, 3 }, { Token.Slash, 3 }, { Token.Modulo, 3 },
- { Token.Caret, 4 }, { Token.ShiftLeft, 4 }, { Token.ShiftRight, 4 }
- };
- protected ExecutionNode Expression(out ParserError error, bool useModulo = true, bool ternaryString = false)
- {
- error = null;
- var operators = new Stack<Token>();
- var operands = new Stack<ExecutionNode>();
- Token token;
- void ProcessOperation(out ParserError localError)
- {
- localError = null;
- Token op = operators.Pop();
- if (op.IsUnary() && operands.Count == 1)
- {
- var operand = operands.Pop();
- operands.Push(new ExecutionNode
- {
- Type = "operation",
- Metadata =
- {
- ["type"] = GetOperationName(op),
- ["unary"] = "true"
- },
- SubNodes = new[]
- {
- operand
- }
- });
- }
- else if (operands.Count >= 2)
- {
- ExecutionNode right = operands.Pop();
- ExecutionNode left = operands.Pop();
- operands.Push(new ExecutionNode
- {
- Type = "operation",
- Metadata =
- {
- ["type"] = GetOperationName(op),
- ["unary"] = "false"
- },
- SubNodes = new[]
- {
- left,
- right
- }
- });
- }
- else
- localError = new ParserError("Invalid expression - not enough operands", CurrentPosition);
- }
- void AttemptUnaryConversion(out ParserError localError)
- {
- localError = null;
- while (operators.Count > 0
- && operators.Peek().IsUnary())
- {
- ProcessOperation(out localError);
- if (localError != null)
- return;
- }
- }
- while ((token = GetNextToken()) != Token.NewLine
- && token != Token.EOF
- && token != Token.Comma
- && token != Token.Colon
- && token != Token.To
- && token != Token.CloseBracket
- && token != Token.RParen
- && token != Token.QuestionMark
- && token != Token.Sharp
- && (!ternaryString || token != Token.TernaryEscape)
- && (useModulo || token != Token.Modulo))
- {
- if (token == Token.Value)
- {
- operands.Push(CreateConstant(Lexer.Value, CurrentPosition));
- AttemptUnaryConversion(out error);
- if (error != null)
- return null;
- }
- else if (token == Token.QuotationMark || token == Token.AtSymbol)
- {
- operands.Push(ParseString(out error, false, false));
- if (error != null)
- return null;
- }
- else if (token == Token.Identifer)
- {
- if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifier))
- {
- operands.Push(GetFunction(out error));
- if (error != null)
- return null;
- }
- else if (IsVariable(Lexer.Identifier))
- {
- operands.Push(GetVariable(out error));
- if (error != null)
- return null;
- }
- else
- {
- Warnings.Add(new ParserError($"Unknown identifier: {Lexer.Identifier}", CurrentPosition));
- break;
- }
- }
- else if (token == Token.TernaryEscape)
- {
- operands.Push(Expression(out error, useModulo, true));
- if (error != null)
- return null;
- }
- else if (token.IsArithmetic())
- {
- if (!operands.Any() && token.IsUnary())
- {
- operators.Push(token);
- continue;
- }
- if (!operands.Any() && !token.IsUnary())
- {
- error = new ParserError($"Invalid unary operator: {token}", CurrentPosition);
- return null;
- }
- while (operators.Any() && OrderOfOps[token] <= OrderOfOps[operators.Peek()])
- {
- ProcessOperation(out error);
- if (error != null)
- return null;
- }
- operators.Push(token);
- }
- else if (token == Token.LParen)
- {
- operands.Push(Expression(out var localError));
- if (localError != null)
- {
- error = localError;
- return null;
- }
- }
- else if (token == Token.RParen)
- {
- break;
- }
- else
- {
- error = new ParserError($"Unexpected token: {token}", CurrentPosition);
- return null;
- }
- }
- while (operators.Any())
- {
- ProcessOperation(out error);
- if (error != null)
- return null;
- }
- if (!operands.Any())
- {
- error = new ParserError("Invalid expression - Empty operand stack", CurrentPosition);
- return null;
- }
- var result = operands.Pop();
- if (token != Token.QuestionMark)
- return result;
- var resultTrue = ternaryString ? ParseString(out error, useModulo, true, true) : Expression(out error, useModulo, false);
- if (error != null)
- return null;
- var resultFalse = ternaryString ? ParseString(out error, useModulo, true, true) : Expression(out error, useModulo, false);
- if (error != null)
- return null;
- return CallMethod("__INLINEIF", CurrentPosition, result, resultTrue, resultFalse);
- }
- protected ExecutionNode ParseString(out ParserError error, bool implicitString, bool canFormat = false, bool nestedTernary = false)
- {
- error = null;
- ExecutionNode value = null;
- if (Lexer.IsPeeking)
- Lexer.GetNextChar();
- if (nestedTernary && (Lexer.CurrentChar == '?' || Lexer.CurrentChar == '#'))
- Lexer.GetNextChar();
- if (!implicitString)
- {
- if (Lexer.CurrentChar == '@')
- {
- canFormat = true;
- Lexer.GetNextChar();
- }
- if (Lexer.CurrentChar == '"')
- {
- Lexer.GetNextChar();
- }
- }
- else
- {
- if (char.IsWhiteSpace(Lexer.CurrentChar))
- Lexer.GetNextChar();
- }
- StringBuilder currentBlock = new StringBuilder();
- void commitBlock()
- {
- if (currentBlock.Length == 0)
- return;
- ExecutionNode stringBlock = CreateConstant(currentBlock.ToString(), CurrentPosition);
- value = value == null
- ? stringBlock
- : OperateNodes(value, stringBlock, Token.Plus);
- currentBlock.Clear();
- }
- while ((Lexer.CurrentChar != '"' || implicitString)
- && Lexer.CurrentChar != '\n'
- && Lexer.CurrentChar != '\0')
- {
- if (Lexer.CurrentChar == '\r')
- {
- Lexer.GetNextChar();
- continue;
- }
- if (nestedTernary && Lexer.CurrentChar == '#')
- break;
- if (canFormat && Lexer.CurrentChar == '\\')
- {
- Lexer.GetNextChar();
- if (Lexer.CurrentChar == '@')
- {
- if (nestedTernary)
- break;
- var expressionValue = Expression(out error, true, true);
- if (error != null)
- return null;
- commitBlock();
- value = value == null
- ? expressionValue
- : OperateNodes(value, expressionValue, Token.Plus);
- }
- else if (Lexer.CurrentChar == 'n')
- {
- currentBlock.Append('\n');
- Lexer.GetNextChar();
- continue;
- }
- currentBlock.Append(Lexer.CurrentChar);
- Lexer.GetNextChar();
- continue;
- }
- if (canFormat && (Lexer.CurrentChar == '{' || Lexer.CurrentChar == '%'))
- {
- bool useModulo = Lexer.CurrentChar != '%';
- List<ExecutionNode> formatParams = new List<ExecutionNode>();
- Marker symbolMarker = CurrentPosition;
- do
- {
- var expressionValue = Expression(out error, useModulo, nestedTernary);
- if (error != null)
- return null;
- formatParams.Add(expressionValue);
- } while (Enumerator.Current == Token.Comma);
- var formattedValue = CallMethod("__FORMAT", symbolMarker, formatParams.ToArray());
- commitBlock();
- value = value == null
- ? formattedValue
- : OperateNodes(value, formattedValue, Token.Plus);
- Lexer.GetNextChar();
- continue;
- }
- currentBlock.Append(Lexer.CurrentChar);
- Lexer.GetNextChar();
- }
- if (!nestedTernary && !implicitString && (Lexer.CurrentChar == '\0' || Lexer.CurrentChar == '\n'))
- {
- error = new ParserError("Was expecting string to be closed", CurrentPosition);
- return null;
- }
- commitBlock();
- value = value ?? CreateConstant("", CurrentPosition);
- return value;
- }
- private static readonly Dictionary<Token, string> OperationNames = new Dictionary<Token, string>
- {
- [Token.Plus] = "add",
- [Token.Asterisk] = "multiply",
- [Token.Minus] = "subtract",
- [Token.Slash] = "divide",
- };
- public static string GetOperationName(Token token)
- {
- return OperationNames.TryGetValue(token, out string result)
- ? result
- : token.ToString();
- }
- public static ExecutionNode CreateConstant(Value value, Marker symbolMarker)
- {
- return new ExecutionNode
- {
- Type = "constant",
- Metadata =
- {
- ["type"] = value.Type.ToString(),
- ["value"] = value.ToString()
- },
- Symbol = symbolMarker
- };
- }
- public static ExecutionNode OperateNodes(ExecutionNode left, ExecutionNode right, Token token)
- {
- return new ExecutionNode
- {
- Type = "operation",
- Metadata =
- {
- ["type"] = GetOperationName(token)
- },
- SubNodes = new[]
- {
- left,
- right
- }
- };
- }
- public static ExecutionNode CallMethod(string methodName, Marker symbolMarker, params ExecutionNode[] parameters)
- {
- return new ExecutionNode
- {
- Type = "call",
- Metadata =
- {
- ["target"] = methodName
- },
- Symbol = symbolMarker,
- SubNodes = new[]
- {
- new ExecutionNode
- {
- Type = "parameters",
- SubNodes = parameters.ToArray()
- }
- }
- };
- }
- public static ExecutionNode GetVariable(string variableName, Marker marker, params ExecutionNode[] indexNodes)
- {
- var node = new ExecutionNode
- {
- Type = "variable",
- Metadata =
- {
- ["name"] = variableName
- },
- SubNodes = indexNodes,
- Symbol = marker
- };
- if (indexNodes.Length > 0)
- node.SubNodes = new[]
- {
- new ExecutionNode
- {
- Type = "index",
- SubNodes = indexNodes
- }
- };
- return node;
- }
- #endregion
- #region Post-processor
- protected void PostProcess(List<ExecutionNode> nodes)
- {
- Branchify(nodes);
- }
- protected void Branchify(List<ExecutionNode> nodes)
- {
- Stack<ExecutionNode> forNodeStack = new Stack<ExecutionNode>();
- Stack<ExecutionNode> doNodeStack = new Stack<ExecutionNode>();
- foreach (var node in nodes)
- {
- if (node.Type == "statement")
- {
- if (node["name"].Equals("FOR", StringComparison.OrdinalIgnoreCase))
- forNodeStack.Push(node);
- else if (node["name"].Equals("DO", StringComparison.OrdinalIgnoreCase))
- doNodeStack.Push(node);
- }
- }
- foreach (var forNode in forNodeStack)
- {
- int index = nodes.IndexOf(forNode);
- int endIndex = 0;
- for (int i = index; i < nodes.Count; i++)
- {
- var node = nodes[i];
- if (node.Type == "statement" && node["name"].Equals("NEXT", StringComparison.OrdinalIgnoreCase))
- {
- endIndex = i;
- break;
- }
- }
- if (endIndex == 0)
- throw new ParserException("Could not find matching NEXT for FOR statement");
- List<ExecutionNode> subNodes = new List<ExecutionNode>();
- forNode.Type = "for-context";
- subNodes.Add(forNode);
- subNodes.AddRange(nodes.Skip(index + 1).Take(endIndex - index - 1));
- nodes.RemoveRange(index, (endIndex - index) + 1);
- ExecutionNode newNode = new ExecutionNode
- {
- Type = "for",
- SubNodes = subNodes.ToArray()
- };
- nodes.Insert(index, newNode);
- }
- foreach (var doNode in doNodeStack)
- {
- int index = nodes.IndexOf(doNode);
- int endIndex = 0;
- for (int i = index; i < nodes.Count; i++)
- {
- var node = nodes[i];
- if (node.Type == "statement" && node["name"].Equals("LOOP", StringComparison.OrdinalIgnoreCase))
- {
- endIndex = i;
- break;
- }
- }
- if (endIndex == 0)
- throw new ParserException("Could not find matching LOOP for DO statement");
- List<ExecutionNode> subNodes = new List<ExecutionNode>();
- var loopNode = nodes[endIndex];
- loopNode.Type = "loop-context";
- subNodes.Add(loopNode);
- subNodes.AddRange(nodes.Skip(index + 1).Take(endIndex - index - 1));
- nodes.RemoveRange(index, (endIndex - index) + 1);
- ExecutionNode newNode = new ExecutionNode
- {
- Type = "do",
- SubNodes = subNodes.ToArray()
- };
- nodes.Insert(index, newNode);
- }
- }
- #endregion
- }
- }
|