|
@@ -10,15 +10,16 @@ namespace NTERA.Interpreter.Compiler
|
|
|
|
|
|
protected FunctionDefinition SelfDefinition { get; }
|
|
|
|
|
|
- protected IList<FunctionDefinition> FunctionDefinitions { get; }
|
|
|
+ protected ICollection<FunctionDefinition> FunctionDefinitions { get; }
|
|
|
+ protected ICollection<FunctionDefinition> ProcedureDefinitions { get; }
|
|
|
|
|
|
protected VariableDictionary GlobalVariables { get; }
|
|
|
|
|
|
protected VariableDictionary LocalVariables { get; }
|
|
|
|
|
|
- protected IList<string> StringStatements { get; }
|
|
|
+ protected ICollection<string> StringStatements { get; }
|
|
|
|
|
|
- protected IDictionary<string, IList<IList<string>>> CsvDefinitions { get; }
|
|
|
+ protected CSVDefinition CsvDefinition { get; }
|
|
|
|
|
|
protected IEnumerator<Token> Enumerator { get; }
|
|
|
|
|
@@ -43,17 +44,18 @@ namespace NTERA.Interpreter.Compiler
|
|
|
Lexer.TokenMarker.Line + SelfDefinition.Position.Line - 1,
|
|
|
Lexer.TokenMarker.Column);
|
|
|
|
|
|
- public Parser(string input, FunctionDefinition selfDefinition, IList<FunctionDefinition> functionDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, IList<string> stringStatements, IDictionary<string, IList<IList<string>>> csvDefinitions)
|
|
|
+ public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, ICollection<string> stringStatements, CSVDefinition csvDefinition)
|
|
|
{
|
|
|
Lexer = new Lexer(input);
|
|
|
Enumerator = Lexer.GetEnumerator();
|
|
|
|
|
|
SelfDefinition = selfDefinition;
|
|
|
FunctionDefinitions = functionDefinitions;
|
|
|
+ ProcedureDefinitions = procedureDefinitions;
|
|
|
GlobalVariables = globalVariables;
|
|
|
LocalVariables = localVariables;
|
|
|
StringStatements = stringStatements;
|
|
|
- CsvDefinitions = csvDefinitions;
|
|
|
+ CsvDefinition = csvDefinition;
|
|
|
}
|
|
|
|
|
|
public IEnumerable<ExecutionNode> Parse(out List<ParserError> errors)
|
|
@@ -191,34 +193,178 @@ namespace NTERA.Interpreter.Compiler
|
|
|
Symbol = CurrentPosition
|
|
|
};
|
|
|
|
|
|
- var value = Expression(out error);
|
|
|
- if (error != null)
|
|
|
+ List<ExecutionNode> subNodes = new List<ExecutionNode>();
|
|
|
+
|
|
|
+ do
|
|
|
+ {
|
|
|
+ var value = Expression(out error);
|
|
|
+ if (error != null)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ if (Enumerator.Current == Token.Identifer)
|
|
|
+ {
|
|
|
+ if (Lexer.Identifer == "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.Identifer == "CALL"
|
|
|
+ || Lexer.Identifer == "BEGIN")
|
|
|
+ {
|
|
|
+ Enumerator.MoveNext();
|
|
|
+
|
|
|
+ if (Enumerator.Current != Token.Identifer)
|
|
|
+ {
|
|
|
+ error = new ParserError($"Expecting a call to a function, got token instead: {Enumerator.Current}", CurrentPosition);
|
|
|
return null;
|
|
|
+ }
|
|
|
|
|
|
- if (Enumerator.Current == Token.NewLine
|
|
|
- || Enumerator.Current == Token.EOF)
|
|
|
+ Marker symbolMarker = CurrentPosition;
|
|
|
+ string target = Lexer.Identifer;
|
|
|
+ List<ExecutionNode> parameters = new List<ExecutionNode>();
|
|
|
+
|
|
|
+ if (ProcedureDefinitions.All(x => !x.Name.Equals(target, StringComparison.OrdinalIgnoreCase)))
|
|
|
{
|
|
|
- node.Metadata["casetype"] = "value";
|
|
|
- node.SubNodes = new[] { value };
|
|
|
- return node;
|
|
|
+ error = new ParserError($"Could not find procedure: {Lexer.Identifer}", CurrentPosition);
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
- if (Enumerator.Current == Token.Identifer)
|
|
|
+ Enumerator.MoveNext();
|
|
|
+
|
|
|
+ while (Enumerator.Current != Token.NewLine
|
|
|
+ && Enumerator.Current != Token.EOF)
|
|
|
{
|
|
|
- if (Lexer.Identifer == "TO")
|
|
|
+ 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.NewLine
|
|
|
+ && Enumerator.Current != Token.EOF)
|
|
|
{
|
|
|
- var value2 = Expression(out error);
|
|
|
+ error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return CallMethod(target, symbolMarker, parameters.ToArray());
|
|
|
+ }
|
|
|
+ else if (Lexer.Identifer == "CALLFORM"
|
|
|
+ || Lexer.Identifer == "TRYCALLFORM")
|
|
|
+ {
|
|
|
+ string statementName = Lexer.Identifer;
|
|
|
+
|
|
|
+ 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.Identifer);
|
|
|
+ }
|
|
|
+ else if (Enumerator.Current == Token.OpenBracket)
|
|
|
+ {
|
|
|
+ newValue = Expression(out error);
|
|
|
if (error != null)
|
|
|
return null;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ error = new ParserError($"Unexpected token: {Enumerator.Current}", CurrentPosition);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ nameValue = nameValue == null
|
|
|
+ ? newValue
|
|
|
+ : OperateNodes(nameValue, newValue, Token.Plus);
|
|
|
+
|
|
|
+ Enumerator.MoveNext();
|
|
|
|
|
|
- node.Metadata["casetype"] = "to";
|
|
|
- node.SubNodes = new[] { value, value2 };
|
|
|
- return node;
|
|
|
+ } while (Enumerator.Current != Token.Comma
|
|
|
+ && Enumerator.Current != Token.NewLine
|
|
|
+ && Enumerator.Current != Token.EOF);
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- 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 //treat as statement
|
|
|
{
|
|
@@ -347,16 +493,10 @@ namespace NTERA.Interpreter.Compiler
|
|
|
}
|
|
|
else if (token == Token.Identifer)
|
|
|
{
|
|
|
- IList<IList<string>> csvTable = CsvDefinitions
|
|
|
- .Where(x => x.Key.IndexOf(variableName, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
|
- .OrderBy(x => x.Key.Equals(variableName, StringComparison.OrdinalIgnoreCase) ? 1 : 2)
|
|
|
- .FirstOrDefault().Value;
|
|
|
-
|
|
|
- IList<string> alias = csvTable?.FirstOrDefault(x => x.Count > 1 && x[1] == Lexer.Identifer);
|
|
|
-
|
|
|
- if (alias != null)
|
|
|
+ if (CsvDefinition.VariableIndexDictionary.TryGetValue(variableName, out var varTable)
|
|
|
+ && varTable.TryGetValue(Lexer.Identifer, out int index))
|
|
|
{
|
|
|
- indices.Add(CreateConstant(int.Parse(alias[0])));
|
|
|
+ indices.Add(CreateConstant(index));
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -379,7 +519,7 @@ namespace NTERA.Interpreter.Compiler
|
|
|
|
|
|
if (FunctionDefinitions.Any(x => x.Name == Lexer.Identifer))
|
|
|
{
|
|
|
- indices.Add(Expression(out error));
|
|
|
+ indices.Add(GetFunction(out error));
|
|
|
if (error != null)
|
|
|
return null;
|
|
|
|
|
@@ -445,8 +585,14 @@ namespace NTERA.Interpreter.Compiler
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ if (hasPeeked)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ }
|
|
|
+
|
|
|
var functionDefinition = FunctionDefinitions.FirstOrDefault(x => x.Name == functionName
|
|
|
- && x.Parameters.Length >= parameters.Count);
|
|
|
+ && (x.Parameters.Length >= parameters.Count
|
|
|
+ || x.Parameters.Any(y => y.IsArrayParameter)));
|
|
|
|
|
|
if (functionDefinition == null)
|
|
|
{
|
|
@@ -568,6 +714,8 @@ namespace NTERA.Interpreter.Compiler
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ break;
|
|
|
+ //this should be converted into a warning
|
|
|
error = new ParserError($"Unknown identifier: {Lexer.Identifer}", CurrentPosition);
|
|
|
return null;
|
|
|
}
|
|
@@ -622,6 +770,12 @@ namespace NTERA.Interpreter.Compiler
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ if (!operands.Any())
|
|
|
+ {
|
|
|
+ error = new ParserError("Invalid expression - Empty operand stack", CurrentPosition);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
return operands.Pop();
|
|
|
}
|
|
|
|