733 lines
32 KiB
C#
733 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using Upsilon.BaseTypes;
|
|
using Upsilon.BaseTypes.Number;
|
|
using Upsilon.Parser;
|
|
using Type = Upsilon.BaseTypes.Type;
|
|
|
|
namespace Upsilon.Binder
|
|
{
|
|
internal class Binder : IDisposable
|
|
{
|
|
private Diagnostics _diagnostics;
|
|
public BoundScope Scope { get; private set; }
|
|
|
|
private Dictionary<string, UnboundFunctionExpression> _unboundFunctions =
|
|
new Dictionary<string, UnboundFunctionExpression>();
|
|
|
|
public Binder(Diagnostics diagnostics, Dictionary<string, VariableSymbol> variables)
|
|
{
|
|
_diagnostics = diagnostics;
|
|
Scope = new BoundScope(variables, null);
|
|
}
|
|
|
|
private Binder(){}
|
|
|
|
internal static Binder CreateWithSetScope(Diagnostics diagnostics, BoundScope scope)
|
|
{
|
|
return new Binder {_diagnostics = diagnostics, Scope = scope};
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Scope?.Dispose();
|
|
Scope = null;
|
|
_unboundFunctions.Clear();
|
|
}
|
|
|
|
public BoundScript BindScript(BlockStatementSyntax e)
|
|
{
|
|
var bound = BindStatement(e);
|
|
foreach (var unboundFunctionStatement in _unboundFunctions)
|
|
{
|
|
Scope = new BoundScope(Scope);
|
|
foreach (var valueParameter in unboundFunctionStatement.Value.Parameters)
|
|
{
|
|
Scope.AssignToNearest(valueParameter.VariableSymbol);
|
|
_diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span);
|
|
}
|
|
unboundFunctionStatement.Value.Block =
|
|
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.Value.UnboundBlock);
|
|
var resultType = Scope.ReturnType;
|
|
Scope = Scope.ParentScope;
|
|
var variable =
|
|
(FunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[
|
|
unboundFunctionStatement.Key];
|
|
variable.IsBound = true;
|
|
variable.ResultType = resultType;
|
|
}
|
|
_unboundFunctions = new Dictionary<string, UnboundFunctionExpression>();
|
|
return new BoundScript((BoundBlockStatement) bound, e.Span, Scope);
|
|
}
|
|
|
|
private BoundStatement BindStatement(StatementSyntax s)
|
|
{
|
|
switch (s.Kind)
|
|
{
|
|
case SyntaxKind.ExpressionStatement:
|
|
return BindExpressionStatement((ExpressionStatementSyntax) s);
|
|
case SyntaxKind.AssignmentStatement:
|
|
return BindAssignmentStatement((AssignmentStatementSyntax) s);
|
|
case SyntaxKind.BlockStatement:
|
|
return BindBlockStatement((BlockStatementSyntax) s);
|
|
case SyntaxKind.IfStatement:
|
|
return BindIfStatement((IfStatementSyntax) s);
|
|
case SyntaxKind.ReturnStatement:
|
|
return BindReturnStatement((ReturnStatementSyntax) s);
|
|
case SyntaxKind.FunctionAssignmentStatement:
|
|
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
|
|
case SyntaxKind.TableAssignmentStatement:
|
|
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
|
case SyntaxKind.MultiAssignmentStatement:
|
|
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
|
|
case SyntaxKind.NumericForStatement:
|
|
return BindNumericForStatement((NumericForStatementSyntax) s);
|
|
case SyntaxKind.GenericForStatement:
|
|
return BindGenericForStatement((GenericForStatementSyntax) s);
|
|
|
|
case SyntaxKind.BreakStatement:
|
|
return new BoundBreakStatement(s.Span);
|
|
}
|
|
|
|
throw new NotImplementedException(s.Kind.ToString());
|
|
}
|
|
|
|
private BoundExpression BindExpression(ExpressionSyntax e)
|
|
{
|
|
switch (e.Kind)
|
|
{
|
|
case SyntaxKind.UnaryExpression:
|
|
return BindUnaryExpression((UnaryExpressionSyntax) e);
|
|
case SyntaxKind.BinaryExpression:
|
|
return BindBinaryExpression((BinaryExpressionSyntax) e);
|
|
case SyntaxKind.LiteralExpression:
|
|
return BindLiteralExpression((LiteralExpressionSyntax) e);
|
|
case SyntaxKind.ParenthesizedExpression:
|
|
return BindParenthesizedExpression((ParenthesizedExpressionSyntax) e);
|
|
case SyntaxKind.VariableExpression:
|
|
return BindVariableExpression((VariableExpressionSyntax) e);
|
|
case SyntaxKind.FunctionCallExpression:
|
|
return BindFunctionCallExpression((FunctionCallExpressionSyntax) e);
|
|
case SyntaxKind.TableExpression:
|
|
return BindTableExpression((TableExpressionSyntax) e);
|
|
case SyntaxKind.IndexExpression:
|
|
return BindIndexExpression((IndexExpressionSyntax) e);
|
|
case SyntaxKind.FullStopIndexExpression:
|
|
return BindFullStopIndexExpression((FullStopIndexExpressionSyntax) e);
|
|
case SyntaxKind.FunctionExpression:
|
|
return BindFunctionExpression((FunctionExpressionSyntax) e);
|
|
case SyntaxKind.BadExpression:
|
|
break;
|
|
case SyntaxKind.ScriptUnit:
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
throw new NotImplementedException(e.Kind.ToString());
|
|
}
|
|
|
|
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax e)
|
|
{
|
|
var inExp = BindExpression(e.Expression);
|
|
var op = BoundUnaryOperator.Bind(e.Operator.Kind, inExp.Type);
|
|
if (op == null)
|
|
{
|
|
_diagnostics.LogUnknownUnaryOperator(e.Span, e.Operator.Kind, inExp.Type);
|
|
return inExp;
|
|
}
|
|
|
|
return new BoundUnaryExpression(op, inExp, op.OutType, e.Span);
|
|
}
|
|
|
|
private BoundExpression BindBinaryExpression(BinaryExpressionSyntax e)
|
|
{
|
|
var left = BindExpression(e.Left);
|
|
var right = BindExpression(e.Right);
|
|
var op = BoundBinaryOperator.Bind(e.Operator.Kind, left.Type, right.Type);
|
|
if (op == null)
|
|
{
|
|
_diagnostics.LogUnknownBinaryOperator(e.Span, e.Operator.Kind, left.Type, right.Type);
|
|
return left;
|
|
}
|
|
return new BoundBinaryExpression(op, left, right, op.OutType, e.Span);
|
|
}
|
|
|
|
private BoundExpression BindLiteralExpression(LiteralExpressionSyntax e)
|
|
{
|
|
var value = e.Value;
|
|
ScriptType outValue = null;
|
|
switch (value)
|
|
{
|
|
case double d:
|
|
outValue = new ScriptNumberDouble(d);
|
|
break;
|
|
case long l:
|
|
outValue = new ScriptNumberLong(l);
|
|
break;
|
|
case bool b:
|
|
outValue = new ScriptBoolean(b);
|
|
break;
|
|
case string s:
|
|
outValue = new ScriptString(s);
|
|
break;
|
|
case null:
|
|
outValue = new ScriptNull();
|
|
break;
|
|
|
|
default:
|
|
_diagnostics.LogUnknownType(e.Span);
|
|
break;
|
|
}
|
|
return new BoundLiteralExpression(outValue, e.Span);
|
|
}
|
|
|
|
private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntax e)
|
|
{
|
|
return BindExpression(e.Expression);
|
|
}
|
|
|
|
private BoundExpression BindFunctionCallExpression(FunctionCallExpressionSyntax e)
|
|
{
|
|
var expression = BindExpression(e.Identifier);
|
|
if (expression.Type != Type.Function && expression.Type != Type.Unknown)
|
|
{
|
|
//TODO Better error
|
|
_diagnostics.LogError($"Unknown function called.", e.Span);
|
|
return new BoundLiteralExpression(new ScriptNull(), e.Span);
|
|
}
|
|
|
|
var parameters = ImmutableArray.CreateBuilder<BoundExpression>();
|
|
foreach (var expressionSyntax in e.Parameters)
|
|
{
|
|
var bound = BindExpression(expressionSyntax);
|
|
parameters.Add(bound);
|
|
}
|
|
|
|
var returnType = Type.Unknown;
|
|
if (ResolveVariable(expression) is FunctionVariableSymbol function)
|
|
{
|
|
if (!function.IsBound)
|
|
{
|
|
Scope = new BoundScope(Scope);
|
|
for (var index = 0; index < function.Parameters.Length; index++)
|
|
{
|
|
var functionVariable = function.Parameters[index];
|
|
var callingVariable = parameters[index];
|
|
functionVariable.Type = callingVariable.Type;
|
|
Scope.DefineLocalVariable(functionVariable);
|
|
}
|
|
|
|
var unboundFunctionStatement = _unboundFunctions[function.Name];
|
|
unboundFunctionStatement.Block =
|
|
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
|
|
returnType = Scope.ReturnType;
|
|
Scope = Scope.ParentScope;
|
|
function.IsBound = true;
|
|
function.ResultType = returnType;
|
|
_unboundFunctions.Remove(function.Name);
|
|
}
|
|
|
|
returnType = function.ResultType;
|
|
|
|
}
|
|
|
|
//TODO: validate parameters
|
|
return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span, returnType);
|
|
}
|
|
|
|
private VariableSymbol ResolveVariable(BoundExpression expression)
|
|
{
|
|
if (expression.Kind == BoundKind.VariableExpression)
|
|
{
|
|
var variableExpression = (BoundVariableExpression) expression;
|
|
return variableExpression.Variable.VariableSymbol;
|
|
}
|
|
if (expression.Kind == BoundKind.BoundFullstopIndexExpression)
|
|
{
|
|
var fullStopIndexExpression = (BoundFullStopIndexExpression) expression;
|
|
var indexerExpression = fullStopIndexExpression.Expression;
|
|
var indexerVariable = (TableVariableSymbol)ResolveVariable(indexerExpression);
|
|
return indexerVariable.Variables[fullStopIndexExpression.Index];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private BoundExpression BindVariableExpression(VariableExpressionSyntax e)
|
|
{
|
|
var name = e.Identifier.Name;
|
|
if (!Scope.TryGetVariable(name, true, out var variable))
|
|
{
|
|
_diagnostics.LogUnknownVariable(e.Identifier.Span, name);
|
|
return new BoundLiteralExpression(new ScriptNull(), e.Span);
|
|
}
|
|
var boundVariable = new BoundVariableSymbol(variable, e.Identifier.Span);
|
|
return new BoundVariableExpression(boundVariable, e.Span);
|
|
}
|
|
|
|
private BoundStatement BindExpressionStatement(ExpressionStatementSyntax s)
|
|
{
|
|
var exp = BindExpression(s.Expression);
|
|
return new BoundExpressionStatement(exp, s.Span);
|
|
}
|
|
|
|
private VariableSymbol TryBindVariable(string name, bool isLocal, BoundExpression assignment, string[] commentData)
|
|
{
|
|
if (name == "_")
|
|
return null;
|
|
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
|
{
|
|
if (assignment.Type == Type.Table)
|
|
{
|
|
if (assignment.Kind == BoundKind.BoundTableExpression)
|
|
{
|
|
variable = new TableVariableSymbol(name, isLocal, ((BoundTableExpression)assignment).Expressions);
|
|
}
|
|
else if (assignment.Kind == BoundKind.VariableExpression)
|
|
{
|
|
variable = new TableVariableSymbol(name, isLocal,
|
|
((TableVariableSymbol) ((BoundVariableExpression) assignment).Variable.VariableSymbol)
|
|
.Variables);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
variable = new VariableSymbol(name, assignment.Type, isLocal);
|
|
}
|
|
|
|
variable.CommentValue = commentData;
|
|
|
|
if (isLocal)
|
|
Scope.DefineLocalVariable(variable);
|
|
else
|
|
Scope.AssignToNearest(variable);
|
|
}
|
|
else
|
|
{
|
|
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
|
if (assignment.Type != variable.Type)
|
|
{
|
|
if (variable.Type == Type.Nil || assignment.Type == Type.Nil)
|
|
{
|
|
variable.Type = assignment.Type;
|
|
}
|
|
else if (variable.Type == Type.Unknown)
|
|
{
|
|
variable.Type = assignment.Type;
|
|
}
|
|
else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v)
|
|
{
|
|
v.Variable.VariableSymbol.Type = variable.Type;
|
|
}
|
|
else if (assignment.Type == Type.Unknown)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
_diagnostics.LogCannotConvert(assignment.Type, variable.Type, assignment.Span);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
return variable;
|
|
}
|
|
|
|
private BoundStatement BindAssignmentStatement(AssignmentStatementSyntax e)
|
|
{
|
|
if (e.Identifier.Kind == SyntaxKind.VariableExpression)
|
|
{
|
|
var variableExpression = (VariableExpressionSyntax) e.Identifier;
|
|
var name = variableExpression.Identifier.Name;
|
|
var boundExpression = BindExpression(e.Expression);
|
|
|
|
var isLocal = e.LocalToken != null;
|
|
var boundVariable = TryBindVariable(name, isLocal, boundExpression, e.CommentData);
|
|
if (boundVariable != null)
|
|
{
|
|
if (boundVariable.Type == Type.Unknown)
|
|
{
|
|
_diagnostics.LogUnknownVariableType(boundVariable.Name, variableExpression.Span);
|
|
}
|
|
var variable = new BoundVariableSymbol(boundVariable, variableExpression.Span);
|
|
return new BoundVariableAssignment(variable, boundExpression, isLocal, e.Span);
|
|
}
|
|
}
|
|
|
|
return new BoundExpressionStatement(new BoundLiteralExpression(new ScriptNull(), e.Span), e.Span);
|
|
}
|
|
|
|
private BoundStatement BindMultiAssignmentStatement(MultiAssignmentStatementSyntax s)
|
|
{
|
|
var ls = new List<VariableSymbol>();
|
|
var assignment = BindExpression(s.Expression);
|
|
var isLocal = s.LocalKeyword != null;
|
|
|
|
foreach (var identifierToken in s.Identifiers)
|
|
{
|
|
var boundVariable = TryBindVariable(identifierToken.Name, isLocal, assignment, null);
|
|
if (boundVariable.Type == Type.Unknown)
|
|
{
|
|
_diagnostics.LogUnknownVariableType(boundVariable.Name, identifierToken.Span);
|
|
}
|
|
ls.Add(boundVariable);
|
|
}
|
|
return new BoundMultiAssignmentStatement(ls.ToImmutableArray(), assignment, s.Span);
|
|
}
|
|
|
|
private BoundStatement BindBlockStatement(BlockStatementSyntax e)
|
|
{
|
|
var arr = ImmutableArray.CreateBuilder<BoundStatement>();
|
|
foreach (var statementSyntax in e.Statements)
|
|
{
|
|
var bound = BindStatement(statementSyntax);
|
|
arr.Add(bound);
|
|
}
|
|
|
|
return new BoundBlockStatement(arr.ToImmutable(), e.Span);
|
|
}
|
|
|
|
private BoundStatement BindIfStatement(IfStatementSyntax e)
|
|
{
|
|
Scope = new BoundScope(Scope);
|
|
var condition = BindExpressionStatement(e.Condition);
|
|
var block = BindBlockStatement(e.Block);
|
|
Scope = Scope.ParentScope;
|
|
if (e.NextElseIfStatement != null)
|
|
{
|
|
var nextElseIf = BindIfStatement(e.NextElseIfStatement);
|
|
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block,
|
|
(BoundIfStatement) nextElseIf, e.Span);
|
|
}
|
|
if (e.ElseStatement == null)
|
|
{
|
|
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block, e.Span);
|
|
}
|
|
else
|
|
{
|
|
Scope = new BoundScope(Scope);
|
|
var elseBlock = BindBlockStatement(e.ElseStatement.Block);
|
|
var elseStatement = new BoundElseStatement((BoundBlockStatement) elseBlock, e.Span);
|
|
Scope = Scope.ParentScope;
|
|
|
|
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block,
|
|
elseStatement, e.Span);
|
|
}
|
|
}
|
|
|
|
private BoundExpression BindFunctionExpression(FunctionExpressionSyntax e, string variableSymbol = null)
|
|
{
|
|
var innerScope = new BoundScope(Scope);
|
|
var parameters = ImmutableArray.CreateBuilder<BoundVariableSymbol>();
|
|
foreach (var identifierToken in e.Parameters)
|
|
{
|
|
var vari = new VariableSymbol(identifierToken.Name, Type.Unknown, true);
|
|
parameters.Add(new BoundVariableSymbol(vari, identifierToken.Span));
|
|
innerScope.DefineLocalVariable(vari);
|
|
}
|
|
|
|
if (parameters.Count == 0)
|
|
{
|
|
Scope = innerScope;
|
|
var block = BindBlockStatement(e.Block);
|
|
var returnType = Scope.ReturnType;
|
|
Scope = Scope.ParentScope;
|
|
var func = new BoundFunctionExpression(parameters.ToImmutable(), (BoundBlockStatement) block, e.Span,
|
|
innerScope, returnType);
|
|
return func;
|
|
}
|
|
else
|
|
{
|
|
var unbound = new UnboundFunctionExpression(parameters.ToImmutable(), e.Block, e.Span, innerScope);
|
|
if (variableSymbol == null)
|
|
{
|
|
_unboundFunctions.Add( Guid.NewGuid().ToString(), unbound);
|
|
}
|
|
else
|
|
{
|
|
_unboundFunctions.Add(variableSymbol, unbound);
|
|
}
|
|
return unbound;
|
|
}
|
|
}
|
|
|
|
private BoundStatement BindFunctionAssignmentStatement(FunctionAssignmentStatementSyntax e)
|
|
{
|
|
var name = e.Identifier.Name;
|
|
var isLocal = e.LocalToken != null;
|
|
var innerScope = new BoundScope(Scope);
|
|
|
|
var func = (BoundFunctionExpression)BindFunctionExpression(e.FunctionExpression, name);
|
|
var parameters = ImmutableArray.CreateBuilder<VariableSymbol>();
|
|
|
|
foreach (var parameter in func.Parameters)
|
|
{
|
|
var vari = new VariableSymbol(parameter.VariableSymbol.Name, Type.Unknown, true);
|
|
parameters.Add(vari);
|
|
innerScope.DefineLocalVariable(vari);
|
|
}
|
|
var commentData = new List<string>();
|
|
if (e.CommentData != null)
|
|
{
|
|
commentData.AddRange(e.CommentData);
|
|
}
|
|
|
|
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
|
{
|
|
var functionVariable = new FunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType)
|
|
{
|
|
CommentValue = commentData.ToArray()
|
|
};
|
|
variable = functionVariable;
|
|
functionVariable.IsBound = !(func is UnboundFunctionExpression);
|
|
functionVariable.ResultType = func.ReturnType;
|
|
if (isLocal)
|
|
Scope.DefineLocalVariable(variable);
|
|
else
|
|
Scope.AssignToNearest(variable);
|
|
}
|
|
else
|
|
{
|
|
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
|
if (variable.Type != Type.Function)
|
|
{
|
|
if (variable.Type == Type.Nil )
|
|
{
|
|
variable.Type = Type.Function;
|
|
}
|
|
else
|
|
{
|
|
_diagnostics.LogCannotConvert(Type.Function, variable.Type, e.Span);
|
|
return new BoundExpressionStatement(new BoundLiteralExpression(new ScriptNull(), e.Span), e.Span);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return new BoundFunctionAssignmentStatement(variable, func, e.Span);
|
|
}
|
|
|
|
private BoundStatement BindReturnStatement(ReturnStatementSyntax e)
|
|
{
|
|
if (e.Expression == null)
|
|
return new BoundReturnStatement(null, e.Span);
|
|
|
|
var expression = BindExpression(e.Expression);
|
|
if (expression.Type != Type.Unknown && expression.Type != Type.Unknown &&
|
|
Scope.ReturnType != Type.Unknown && Scope.ReturnType != Type.Nil)
|
|
{
|
|
if (expression.Type != Scope.ReturnType)
|
|
{
|
|
_diagnostics.LogError($"Can't return type '{expression.Type}' from this scope, earlier in the" +
|
|
$" scope a return type of '{Scope.ReturnType}' is defined.", e.Span);
|
|
}
|
|
}
|
|
if (expression.Type != Type.Unknown && expression.Type != Type.Nil)
|
|
Scope.ReturnType = expression.Type;
|
|
return new BoundReturnStatement(expression, e.Span);
|
|
}
|
|
|
|
private BoundExpression BindTableExpression(TableExpressionSyntax e)
|
|
{
|
|
var statements = ImmutableArray.CreateBuilder<BoundStatement>();
|
|
var s = Scope;
|
|
Scope = BoundScope.WithReadOnlyScope(s);
|
|
var innerVariables = new Dictionary<string, VariableSymbol>();
|
|
var current = 0;
|
|
|
|
foreach (var expressionSyntax in e.Expressions)
|
|
{
|
|
var bound = BindStatement((StatementSyntax) expressionSyntax);
|
|
if (bound.Kind == BoundKind.BoundExpressionStatement)
|
|
{
|
|
var boundExpression = (BoundExpressionStatement)bound;
|
|
current++;
|
|
innerVariables.Add(current.ToString(),
|
|
new VariableSymbol(current.ToString(), boundExpression.Expression.Type, false));
|
|
}
|
|
statements.Add(bound);
|
|
}
|
|
|
|
innerVariables = innerVariables.Union(Scope.Variables).ToDictionary(k => k.Key, v => v.Value);
|
|
var innerSCope = Scope;
|
|
Scope = s;
|
|
return new BoundTableExpression(innerVariables, statements.ToImmutable(), e.Span, innerSCope);
|
|
}
|
|
|
|
private BoundExpression BindIndexExpression(IndexExpressionSyntax e, bool isAssignment = false)
|
|
{
|
|
var expression = BindExpression(e.Expression);
|
|
|
|
var index = BindExpression(e.Index);
|
|
if (index.Type != Type.Number && index.Type != Type.String && index.Type != Type.Unknown)
|
|
{
|
|
_diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span);
|
|
return new BoundLiteralExpression(new ScriptNull(), e.Span);
|
|
}
|
|
switch (expression.Type)
|
|
{
|
|
case Type.Table:
|
|
if (isAssignment)
|
|
{
|
|
return new BoundIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
}
|
|
if (expression.Kind == BoundKind.BoundTableExpression && index.Kind == BoundKind.BoundLiteralExpression)
|
|
{
|
|
var table = (BoundTableExpression)expression;
|
|
var realIndex = (BoundLiteralExpression) index;
|
|
var variableDic = table.Expressions;
|
|
if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable))
|
|
{
|
|
return new BoundIndexExpression(expression, index, variable.Type, e.Span);
|
|
}
|
|
|
|
_diagnostics.LogError($"No variable '{realIndex.Value}' found in table.",
|
|
e.Span);
|
|
}
|
|
else if (expression.Kind == BoundKind.VariableExpression &&
|
|
index.Kind == BoundKind.BoundLiteralExpression)
|
|
{
|
|
var table = (BoundVariableExpression)expression;
|
|
var realTable = table.Variable;
|
|
var realIndex = (BoundLiteralExpression) index;
|
|
var variableDic = ((TableVariableSymbol) realTable.VariableSymbol).Variables;
|
|
if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable))
|
|
{
|
|
return new BoundIndexExpression(expression, index, variable.Type, e.Span);
|
|
}
|
|
|
|
_diagnostics.LogError($"No variable '{realIndex.Value}' found in table '{realTable.VariableSymbol.Name}'.",
|
|
e.Span);
|
|
}
|
|
return new BoundIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
case Type.UserData:
|
|
case Type.Unknown:
|
|
return new BoundIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
case Type.String when index.Type == Type.Number:
|
|
return new BoundIndexExpression(expression, index, Type.String, e.Span);
|
|
default:
|
|
_diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span);
|
|
return new BoundLiteralExpression(new ScriptNull(), e.Span);
|
|
}
|
|
}
|
|
|
|
private BoundExpression BindFullStopIndexExpression(FullStopIndexExpressionSyntax e, bool isAssignment = false)
|
|
{
|
|
var expression = BindExpression(e.Expression);
|
|
var index = e.Index.Name;
|
|
switch (expression.Type)
|
|
{
|
|
case Type.Table:
|
|
if (isAssignment)
|
|
{
|
|
return new BoundFullStopIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
}
|
|
if (expression.Kind == BoundKind.BoundTableExpression)
|
|
{
|
|
var table = (BoundTableExpression)expression;
|
|
if (table.Expressions.TryGetValue(index, out var variable))
|
|
{
|
|
return new BoundFullStopIndexExpression(expression, index, variable.Type, e.Span);
|
|
}
|
|
_diagnostics.LogError($"No variable '{index}' found in table.", e.Span);
|
|
}
|
|
if (expression.Kind == BoundKind.VariableExpression)
|
|
{
|
|
var table = (BoundVariableExpression)expression;
|
|
var realTable = table.Variable;
|
|
var variableDic = ((TableVariableSymbol) realTable.VariableSymbol).Variables;
|
|
if (variableDic.TryGetValue(index, out var variable))
|
|
{
|
|
return new BoundFullStopIndexExpression(expression, index, variable.Type, e.Span);
|
|
}
|
|
|
|
_diagnostics.LogError($"No variable '{index}' found in table '{realTable.VariableSymbol.Name}'.",
|
|
e.Span);
|
|
}
|
|
return new BoundFullStopIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
case Type.UserData:
|
|
case Type.Unknown:
|
|
return new BoundFullStopIndexExpression(expression, index, Type.Unknown, e.Span);
|
|
case Type.String:
|
|
return new BoundFullStopIndexExpression(expression, index, Type.String, e.Span);
|
|
default:
|
|
_diagnostics.LogInvalidIndexExpression(expression.Type, Type.String, e.Span);
|
|
return new BoundLiteralExpression(new ScriptNull(), e.Span);
|
|
}
|
|
}
|
|
|
|
private BoundStatement BindTableAssignmentStatement(TableAssigmentStatementSyntax e)
|
|
{
|
|
BoundExpression indexableExpression;
|
|
var value = BindExpression(e.Expression);
|
|
|
|
if (e.TableExpression.Kind == SyntaxKind.IndexExpression)
|
|
{
|
|
var indexable =
|
|
(BoundIndexExpression) BindIndexExpression((IndexExpressionSyntax) e.TableExpression, true);
|
|
|
|
if (indexable.Identifier.Kind == BoundKind.VariableExpression &&
|
|
indexable.Index.Kind == BoundKind.BoundLiteralExpression)
|
|
{
|
|
var variable = (BoundVariableExpression)indexable.Identifier;
|
|
var index = (BoundLiteralExpression)indexable.Index;
|
|
((TableVariableSymbol)variable.Variable.VariableSymbol).Variables.Add(index.Value.ToString(),
|
|
new VariableSymbol(index.Value.ToString(), value.Type, false));
|
|
}
|
|
indexableExpression = indexable;
|
|
}
|
|
else
|
|
{
|
|
var indexable =
|
|
(BoundFullStopIndexExpression) BindFullStopIndexExpression(
|
|
(FullStopIndexExpressionSyntax) e.TableExpression, true);
|
|
|
|
if (indexable.Expression.Kind == BoundKind.VariableExpression)
|
|
{
|
|
var variable = (BoundVariableExpression)indexable.Expression;
|
|
if (variable.Type == Type.Table)
|
|
{
|
|
((TableVariableSymbol)variable.Variable.VariableSymbol).Variables.Add(indexable.Index,
|
|
new VariableSymbol(indexable.Index, value.Type, false));
|
|
}
|
|
}
|
|
indexableExpression = indexable;
|
|
}
|
|
|
|
return new BoundTableAssigmentStatement(indexableExpression, value, e.Span);
|
|
}
|
|
|
|
private BoundStatement BindNumericForStatement(NumericForStatementSyntax e)
|
|
{
|
|
var variableName = e.Identifier.Name;
|
|
Scope = new BoundScope(Scope);
|
|
var variable = new VariableSymbol(variableName, Type.Number, true);
|
|
Scope.DefineLocalVariable(variable);
|
|
var boundStart = BindExpression(e.StartExpression);
|
|
var boundStop = BindExpression(e.StopExpression);
|
|
BoundExpression boundStep = null;
|
|
if (e.StepExpression != null)
|
|
boundStep = BindExpression(e.StepExpression);
|
|
var block = BindBlockStatement((BlockStatementSyntax) e.Block);
|
|
Scope = Scope.ParentScope;
|
|
return new BoundNumericForStatement(variable, boundStart, boundStop, boundStep, (BoundBlockStatement) block,
|
|
e.Span);
|
|
}
|
|
|
|
private BoundStatement BindGenericForStatement(GenericForStatementSyntax e)
|
|
{
|
|
Scope = new BoundScope(Scope);
|
|
var array = ImmutableArray.CreateBuilder<VariableSymbol>();
|
|
foreach (var variableIdentifier in e.Variables)
|
|
{
|
|
var variable = new VariableSymbol(variableIdentifier.Name, Type.Unknown, true);
|
|
Scope.DefineLocalVariable(variable);
|
|
array.Add(variable);
|
|
}
|
|
var boundEnumerableExpression = BindExpression(e.EnumerableExpression);
|
|
var block = BindBlockStatement(e.Block);
|
|
|
|
return new BoundGenericForStatement(array.ToImmutable(), boundEnumerableExpression, block, e.Span);
|
|
}
|
|
}
|
|
} |