More work on nesting
This commit is contained in:
parent
d8f7651de7
commit
dd9f5416a0
|
@ -10,13 +10,6 @@ namespace Upsilon.BaseTypes
|
||||||
private readonly Dictionary<VariableSymbol, LuaType> _map;
|
private readonly Dictionary<VariableSymbol, LuaType> _map;
|
||||||
public int EvaluatorIdentifier { get; }
|
public int EvaluatorIdentifier { get; }
|
||||||
|
|
||||||
public LuaTable(int scopeIdentifier)
|
|
||||||
{
|
|
||||||
EvaluatorIdentifier = scopeIdentifier;
|
|
||||||
_map = new Dictionary<VariableSymbol, LuaType>();
|
|
||||||
_variableLookup = new Dictionary<string, VariableSymbol>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LuaTable(Dictionary<VariableSymbol, LuaType> map, int scopeIdentifier)
|
public LuaTable(Dictionary<VariableSymbol, LuaType> map, int scopeIdentifier)
|
||||||
{
|
{
|
||||||
_map = map;
|
_map = map;
|
||||||
|
|
|
@ -154,15 +154,13 @@ namespace Upsilon.Binder
|
||||||
|
|
||||||
private BoundExpression BindFunctionCallExpression(FunctionCallExpressionSyntax e)
|
private BoundExpression BindFunctionCallExpression(FunctionCallExpressionSyntax e)
|
||||||
{
|
{
|
||||||
var name = e.Identifier.Name;
|
var expression = BindExpression(e.Identifier);
|
||||||
if (!Scope.TryGetVariable(name, true, out var functionObj))
|
if (expression.Type != Type.Function && expression.Type != Type.Unknown)
|
||||||
{
|
{
|
||||||
_diagnostics.LogUnknownVariable(e.Identifier.Span, name);
|
//TODO Better error
|
||||||
return new BoundLiteralExpression(new LuaNull());
|
throw new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
var function = (FunctionVariableSymbol) functionObj;
|
|
||||||
|
|
||||||
var parameters = ImmutableArray.CreateBuilder<BoundExpression>();
|
var parameters = ImmutableArray.CreateBuilder<BoundExpression>();
|
||||||
foreach (var expressionSyntax in e.Parameters)
|
foreach (var expressionSyntax in e.Parameters)
|
||||||
{
|
{
|
||||||
|
@ -170,27 +168,32 @@ namespace Upsilon.Binder
|
||||||
parameters.Add(bound);
|
parameters.Add(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function.IsBound)
|
if (expression.Kind == BoundKind.VariableExpression)
|
||||||
{
|
{
|
||||||
Scope = new BoundScope(Scope);
|
var variableExpression =(BoundVariableExpression) expression;
|
||||||
for (var index = 0; index < function.Parameters.Length; index++)
|
var function = (FunctionVariableSymbol)variableExpression.Variable;
|
||||||
|
if (!function.IsBound)
|
||||||
{
|
{
|
||||||
var functionVariable = function.Parameters[index];
|
Scope = new BoundScope(Scope);
|
||||||
var callingVariable = parameters[index];
|
for (var index = 0; index < function.Parameters.Length; index++)
|
||||||
functionVariable.Type = callingVariable.Type;
|
{
|
||||||
Scope.SetVariable(functionVariable);
|
var functionVariable = function.Parameters[index];
|
||||||
}
|
var callingVariable = parameters[index];
|
||||||
|
functionVariable.Type = callingVariable.Type;
|
||||||
|
Scope.SetVariable(functionVariable);
|
||||||
|
}
|
||||||
|
|
||||||
var unboundFunctionStatement = _unboundFunctions[function];
|
var unboundFunctionStatement = _unboundFunctions[function];
|
||||||
unboundFunctionStatement.Block =
|
unboundFunctionStatement.Block =
|
||||||
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
|
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
|
||||||
Scope = Scope.ParentScope;
|
Scope = Scope.ParentScope;
|
||||||
function.IsBound = true;
|
function.IsBound = true;
|
||||||
_unboundFunctions.Remove(function);
|
_unboundFunctions.Remove(function);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: validate parameters
|
//TODO: validate parameters
|
||||||
return new BoundFunctionCallExpression(function, parameters.ToImmutable());
|
return new BoundFunctionCallExpression(expression, parameters.ToImmutable());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BoundExpression BindVariableExpression(VariableExpressionSyntax e)
|
private BoundExpression BindVariableExpression(VariableExpressionSyntax e)
|
||||||
|
|
|
@ -5,10 +5,10 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
public class BoundFunctionCallExpression : BoundExpression
|
public class BoundFunctionCallExpression : BoundExpression
|
||||||
{
|
{
|
||||||
public FunctionVariableSymbol Identifier { get; }
|
public BoundExpression Identifier { get; }
|
||||||
public ImmutableArray<BoundExpression> Parameters { get; }
|
public ImmutableArray<BoundExpression> Parameters { get; }
|
||||||
|
|
||||||
public BoundFunctionCallExpression(FunctionVariableSymbol identifier, ImmutableArray<BoundExpression> parameters)
|
public BoundFunctionCallExpression(BoundExpression identifier, ImmutableArray<BoundExpression> parameters)
|
||||||
{
|
{
|
||||||
Identifier = identifier;
|
Identifier = identifier;
|
||||||
Parameters = parameters;
|
Parameters = parameters;
|
||||||
|
|
|
@ -278,14 +278,12 @@ namespace Upsilon.Evaluator
|
||||||
|
|
||||||
private LuaType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
|
private LuaType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
|
||||||
{
|
{
|
||||||
var name = boundFunctionCallExpression.Identifier;
|
var variable = EvaluateExpression(boundFunctionCallExpression.Identifier);
|
||||||
if (!Scope.TryGet(name, out var functionObj))
|
if (!(variable is LuaFunction function))
|
||||||
{
|
{
|
||||||
throw new Exception("Cannot find function: " + name.Name);
|
throw new Exception("Variable is not a function.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var function = (LuaFunction) functionObj;
|
|
||||||
|
|
||||||
var innerEvaluator = new Evaluator(_diagnostics, Scope);
|
var innerEvaluator = new Evaluator(_diagnostics, Scope);
|
||||||
for (var i = 0; i < function.Parameters.Length; i++)
|
for (var i = 0; i < function.Parameters.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -318,6 +316,13 @@ namespace Upsilon.Evaluator
|
||||||
var value = EvaluateExpression(assignment.BoundExpression);
|
var value = EvaluateExpression(assignment.BoundExpression);
|
||||||
dic.Add(key, value);
|
dic.Add(key, value);
|
||||||
}
|
}
|
||||||
|
else if (boundStatement.Kind == BoundKind.BoundFunctionStatement)
|
||||||
|
{
|
||||||
|
var function = (BoundFunctionStatement) boundStatement;
|
||||||
|
var key = function.Identifier;
|
||||||
|
var func = new LuaFunction(function.Parameters, function.Block);
|
||||||
|
dic.Add(key, func);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
innerEvaluator.EvaluateStatement(boundStatement);
|
innerEvaluator.EvaluateStatement(boundStatement);
|
||||||
|
@ -333,36 +338,16 @@ namespace Upsilon.Evaluator
|
||||||
|
|
||||||
private LuaType EvaluateIndexExpression(BoundIndexExpression e)
|
private LuaType EvaluateIndexExpression(BoundIndexExpression e)
|
||||||
{
|
{
|
||||||
IIndexable index;
|
var variable = EvaluateExpression(e.Identifier);
|
||||||
if (e.Identifier.Kind == BoundKind.VariableExpression)
|
if (!(variable is IIndexable indexable))
|
||||||
{
|
{
|
||||||
var variableExpression = (BoundVariableExpression) e.Identifier;
|
throw new Exception("Variable is not indexable.");
|
||||||
var variable = variableExpression.Variable;
|
|
||||||
if (!Scope.TryGet(variable, out var val))
|
|
||||||
{
|
|
||||||
throw new Exception($"Cannot find variable: '{variable.Name}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(val is IIndexable indexable))
|
|
||||||
{
|
|
||||||
throw new Exception("Variable is not indexable.");
|
|
||||||
}
|
|
||||||
index = indexable;
|
|
||||||
}
|
|
||||||
else if (e.Identifier.Kind == BoundKind.BoundIndexExpression)
|
|
||||||
{
|
|
||||||
var indexExpression = (BoundIndexExpression)e.Identifier;
|
|
||||||
index = (IIndexable) EvaluateIndexExpression(indexExpression);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var innerEvaluator = new Evaluator(_diagnostics, Scope, index.EvaluatorIdentifier);
|
var innerEvaluator = new Evaluator(_diagnostics, Scope, indexable.EvaluatorIdentifier);
|
||||||
var indexer = innerEvaluator.EvaluateExpression(e.Index);
|
var indexer = innerEvaluator.EvaluateExpression(e.Index);
|
||||||
return index.Get(indexer.ToString(), Identifier);
|
return indexable.Get(indexer.ToString(), Identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,12 +5,12 @@ namespace Upsilon.Parser
|
||||||
{
|
{
|
||||||
public class FunctionCallExpressionSyntax : ExpressionSyntax
|
public class FunctionCallExpressionSyntax : ExpressionSyntax
|
||||||
{
|
{
|
||||||
public IdentifierToken Identifier { get; }
|
public ExpressionSyntax Identifier { get; }
|
||||||
public SyntaxToken OpenParenthesis { get; }
|
public SyntaxToken OpenParenthesis { get; }
|
||||||
public ImmutableArray<ExpressionSyntax> Parameters { get; }
|
public ImmutableArray<ExpressionSyntax> Parameters { get; }
|
||||||
public SyntaxToken CloseParenthesis { get; }
|
public SyntaxToken CloseParenthesis { get; }
|
||||||
|
|
||||||
public FunctionCallExpressionSyntax(IdentifierToken identifier, SyntaxToken openParenthesis,
|
public FunctionCallExpressionSyntax(ExpressionSyntax identifier, SyntaxToken openParenthesis,
|
||||||
ImmutableArray<ExpressionSyntax> parameters, SyntaxToken closeParenthesis)
|
ImmutableArray<ExpressionSyntax> parameters, SyntaxToken closeParenthesis)
|
||||||
{
|
{
|
||||||
Identifier = identifier;
|
Identifier = identifier;
|
||||||
|
|
|
@ -168,9 +168,12 @@ namespace Upsilon.Parser
|
||||||
private ExpressionSyntax ParseExpression()
|
private ExpressionSyntax ParseExpression()
|
||||||
{
|
{
|
||||||
var expression = ParseBinaryExpression();
|
var expression = ParseBinaryExpression();
|
||||||
while (Current.Kind == SyntaxKind.OpenBracket)
|
while (Current.Kind == SyntaxKind.OpenBracket || Current.Kind == SyntaxKind.OpenParenthesis)
|
||||||
{
|
{
|
||||||
expression = ParseIndexExpression(expression);
|
if (Current.Kind == SyntaxKind.OpenBracket)
|
||||||
|
expression = ParseIndexExpression(expression);
|
||||||
|
else if (Current.Kind == SyntaxKind.OpenParenthesis)
|
||||||
|
expression = ParseFunctionCallExpression(expression);
|
||||||
}
|
}
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
@ -236,13 +239,8 @@ namespace Upsilon.Parser
|
||||||
expression = ParseString();
|
expression = ParseString();
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
if (Next.Kind == SyntaxKind.OpenParenthesis)
|
var token = MatchToken(SyntaxKind.Identifier);
|
||||||
expression = ParseFunctionCallExpression();
|
expression = new VariableExpressionSyntax((IdentifierToken) token);
|
||||||
else
|
|
||||||
{
|
|
||||||
var token = MatchToken(SyntaxKind.Identifier);
|
|
||||||
expression = new VariableExpressionSyntax((IdentifierToken) token);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.OpenBrace:
|
case SyntaxKind.OpenBrace:
|
||||||
expression = ParseTable();
|
expression = ParseTable();
|
||||||
|
@ -260,9 +258,8 @@ namespace Upsilon.Parser
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionSyntax ParseFunctionCallExpression()
|
private ExpressionSyntax ParseFunctionCallExpression(ExpressionSyntax expression)
|
||||||
{
|
{
|
||||||
var identifier = MatchToken(SyntaxKind.Identifier);
|
|
||||||
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
||||||
var parameters = ImmutableArray.CreateBuilder<ExpressionSyntax>();
|
var parameters = ImmutableArray.CreateBuilder<ExpressionSyntax>();
|
||||||
while (Current.Kind != SyntaxKind.CloseParenthesis)
|
while (Current.Kind != SyntaxKind.CloseParenthesis)
|
||||||
|
@ -274,7 +271,7 @@ namespace Upsilon.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||||
return new FunctionCallExpressionSyntax((IdentifierToken) identifier, openParenthesis,
|
return new FunctionCallExpressionSyntax(expression, openParenthesis,
|
||||||
parameters.ToImmutable(), closeParenthesis);
|
parameters.ToImmutable(), closeParenthesis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,37 @@ return table[1][1][2]
|
||||||
{
|
{
|
||||||
const string input = @"
|
const string input = @"
|
||||||
table = {
|
table = {
|
||||||
function() test
|
function test()
|
||||||
return 100
|
return 100
|
||||||
end
|
end,
|
||||||
val = 400,
|
val = 400,
|
||||||
another = 10_000,
|
another = 10_000,
|
||||||
}
|
}
|
||||||
return table[""test""]()
|
return table[""test""]()
|
||||||
|
";
|
||||||
|
var script = new Script(input);
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
var evaluated = script.Evaluate<long>();
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
Assert.Equal(100, evaluated);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DumbassTableFunctionNesting()
|
||||||
|
{
|
||||||
|
const string input = @"
|
||||||
|
table = {
|
||||||
|
function func()
|
||||||
|
return function func()
|
||||||
|
return {
|
||||||
|
function func()
|
||||||
|
return 100
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
return table[""func""]()()[1]()
|
||||||
";
|
";
|
||||||
var script = new Script(input);
|
var script = new Script(input);
|
||||||
Assert.Empty(script.Diagnostics.Messages);
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
|
Loading…
Reference in New Issue