177 lines
6.3 KiB
C#
177 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Immutable;
|
|
using Upsilon.BaseTypes.Number;
|
|
using Upsilon.Parser;
|
|
using Type = Upsilon.BaseTypes.Type;
|
|
|
|
namespace Upsilon.Binder
|
|
{
|
|
public class Binder
|
|
{
|
|
private readonly Diagnostics _diagnostics;
|
|
private BoundScope _scope;
|
|
|
|
public Binder(BoundScope parentScope, Diagnostics diagnostics)
|
|
{
|
|
_diagnostics = diagnostics;
|
|
_scope = new BoundScope(parentScope);
|
|
}
|
|
|
|
public BoundScript BindScript(BlockStatementSyntax e)
|
|
{
|
|
var bound = BindStatement(e);
|
|
return new BoundScript(bound);
|
|
}
|
|
|
|
public BoundStatement BindStatement(StatementSyntax s)
|
|
{
|
|
switch (s.Kind)
|
|
{
|
|
case SyntaxKind.ExpressionStatement:
|
|
return BindExpressionStatement((ExpressionStatementSyntax) s);
|
|
case SyntaxKind.AssignmentStatement:
|
|
return BindAssignmentStatement((AssignmentExpressionSyntax) s);
|
|
case SyntaxKind.BlockStatement:
|
|
return BindBlockStatement((BlockStatementSyntax) s);
|
|
}
|
|
|
|
throw new NotImplementedException(s.Kind.ToString());
|
|
}
|
|
|
|
public 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.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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private BoundExpression BindLiteralExpression(LiteralExpressionSyntax e)
|
|
{
|
|
var value = e.Value;
|
|
var type = Type.Nil;
|
|
object outValue = null;
|
|
switch (value)
|
|
{
|
|
case double d:
|
|
type = Type.Number;
|
|
outValue = new NumberDouble(d);
|
|
break;
|
|
case long l:
|
|
type = Type.Number;
|
|
outValue = new NumberLong(l);
|
|
break;
|
|
case bool b:
|
|
type = Type.Boolean;
|
|
outValue = value;
|
|
break;
|
|
default:
|
|
_diagnostics.LogUnknownType(e.Span);
|
|
break;
|
|
}
|
|
return new BoundLiteralExpression(outValue, type);
|
|
}
|
|
|
|
private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntax e)
|
|
{
|
|
return BindExpression(e.Expression);
|
|
}
|
|
|
|
private BoundExpression BindVariableExpression(VariableExpressionSyntax e)
|
|
{
|
|
var name = e.Identifier.Name;
|
|
if (!_scope.TryGetVariable(name, out var variable))
|
|
{
|
|
_diagnostics.LogUnknownVariable(e.Identifier.Span, name);
|
|
return new BoundLiteralExpression(null, Type.Nil);
|
|
}
|
|
|
|
return new BoundVariableExpression(variable);
|
|
}
|
|
|
|
private BoundStatement BindExpressionStatement(ExpressionStatementSyntax s)
|
|
{
|
|
var exp = BindExpression(s.Expression);
|
|
return new BoundExpressionStatement(exp);
|
|
}
|
|
|
|
private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e)
|
|
{
|
|
var name = e.Identifier.Name;
|
|
var boundExpression = BindExpression(e.Expression);
|
|
|
|
if (!_scope.TryGetVariable(name, out var variable))
|
|
{
|
|
variable = new VariableSymbol(name, boundExpression.Type);
|
|
if (e.LocalToken != null)
|
|
_scope.SetVariable(variable);
|
|
else
|
|
_scope.SetGlobalVariable(variable);
|
|
}
|
|
else
|
|
{
|
|
if (boundExpression.Type != variable.Type)
|
|
{
|
|
_diagnostics.LogCannotConvert(boundExpression.Type, variable.Type, e.Span);
|
|
return new BoundExpressionStatement(boundExpression);
|
|
}
|
|
}
|
|
|
|
return new BoundVariableAssignment(variable, boundExpression);
|
|
}
|
|
|
|
|
|
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());
|
|
}
|
|
}
|
|
} |