Lots of work on type binding

This commit is contained in:
Deukhoofd 2018-11-11 18:12:42 +01:00
parent 699377cdfc
commit 3561979ded
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
23 changed files with 530 additions and 142 deletions

View File

@ -0,0 +1,78 @@
namespace Upsilon.BaseTypes.Number
{
public abstract class Number
{
protected abstract bool IsFloat { get; }
#region Binary Operators
public static Number operator + (Number a, Number b)
{
if (!a.IsFloat && !b.IsFloat)
return new NumberLong(((NumberLong) a).Value + ((NumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new NumberDouble(((NumberDouble) a).Value + ((NumberDouble) b).Value);
if (a.IsFloat)
return new NumberDouble(((NumberDouble) a).Value + ((NumberLong) b).Value);
return new NumberDouble(((NumberLong) a).Value + ((NumberDouble) b).Value);
}
public static Number operator - (Number a, Number b)
{
if (!a.IsFloat && !b.IsFloat)
return new NumberLong(((NumberLong) a).Value - ((NumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new NumberDouble(((NumberDouble) a).Value - ((NumberDouble) b).Value);
if (a.IsFloat)
return new NumberDouble(((NumberDouble) a).Value - ((NumberLong) b).Value);
return new NumberDouble(((NumberLong) a).Value - ((NumberDouble) b).Value);
}
public static Number operator * (Number a, Number b)
{
if (!a.IsFloat && !b.IsFloat)
return new NumberLong(((NumberLong) a).Value * ((NumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new NumberDouble(((NumberDouble) a).Value * ((NumberDouble) b).Value);
if (a.IsFloat)
return new NumberDouble(((NumberDouble) a).Value * ((NumberLong) b).Value);
return new NumberDouble(((NumberLong) a).Value * ((NumberDouble) b).Value);
}
public static Number operator / (Number a, Number b)
{
if (!a.IsFloat && !b.IsFloat)
return new NumberLong(((NumberLong) a).Value / ((NumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new NumberDouble(((NumberDouble) a).Value / ((NumberDouble) b).Value);
if (a.IsFloat)
return new NumberDouble(((NumberDouble) a).Value / ((NumberLong) b).Value);
return new NumberDouble(((NumberLong) a).Value / ((NumberDouble) b).Value);
}
#endregion
#region Equality
private bool Equals(Number other)
{
if (!IsFloat && !other.IsFloat)
return ((NumberLong) this).Value.Equals(((NumberLong) other).Value);
if (IsFloat && other.IsFloat)
return ((NumberDouble) this).Value.Equals(((NumberDouble) other).Value);
return false;
}
#pragma warning disable 659
public override bool Equals(object obj)
#pragma warning restore 659
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Number) obj);
}
#endregion
}
}

View File

@ -0,0 +1,25 @@
using System.Globalization;
namespace Upsilon.BaseTypes.Number
{
public class NumberDouble : Number
{
public double Value { get; }
protected override bool IsFloat { get; } = true;
public NumberDouble(double value)
{
Value = value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -0,0 +1,25 @@
using System.Globalization;
namespace Upsilon.BaseTypes.Number
{
public class NumberLong : Number
{
public long Value { get; }
protected override bool IsFloat { get; } = true;
public NumberLong(long val)
{
Value = val;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
}

14
Upsilon/BaseTypes/Type.cs Normal file
View File

@ -0,0 +1,14 @@
namespace Upsilon.BaseTypes
{
public enum Type
{
Nil,
Boolean,
Number,
String,
Function,
UserData,
Thread,
Table
}
}

87
Upsilon/Binder/Binder.cs Normal file
View File

@ -0,0 +1,87 @@
using System;
using System.Runtime.InteropServices;
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(ScriptSyntax e)
{
var bound = BindExpression(e.Statement);
return new BoundScript(bound);
}
public BoundExpression BindExpression(ExpressionSyntax e)
{
switch (e.Kind)
{
case SyntaxKind.UnaryExpression:
break;
case SyntaxKind.BinaryExpression:
return BindBinaryExpression((BinaryExpressionSyntax) e);
case SyntaxKind.LiteralExpression:
return BindLiteralExpression((LiteralExpressionSyntax) e);
case SyntaxKind.ParenthesizedExpression:
break;
case SyntaxKind.AssignmentExpression:
break;
case SyntaxKind.VariableExpression:
break;
case SyntaxKind.BadExpression:
break;
case SyntaxKind.ScriptUnit:
break;
default:
throw new ArgumentOutOfRangeException();
}
throw new NotImplementedException(e.Kind.ToString());
}
private BoundExpression BindBinaryExpression(BinaryExpressionSyntax e)
{
var left = BindExpression(e.Left);
var right = BindExpression(e.Right);
var op = BoundBinaryOperator.BindBinaryOperator(e.Operator.Kind, left.Type, right.Type);
if (op == null)
{
_diagnostics.LogUnknownOperator(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 bool b:
type = Type.Boolean;
outValue = value;
break;
default:
_diagnostics.LogUnknownType(e.Span);
break;
}
return new BoundLiteralExpression(outValue, type);
}
}
}

View File

@ -0,0 +1,23 @@
using Upsilon.BaseTypes;
using Upsilon.Parser;
namespace Upsilon.Binder
{
public class BoundBinaryExpression : BoundExpression
{
public BoundBinaryExpression(BoundBinaryOperator op, BoundExpression leftExpression, BoundExpression rightExpression, Type type)
{
Operator = op;
LeftExpression = leftExpression;
RightExpression = rightExpression;
Type = type;
}
public override BoundKind Kind => BoundKind.BoundBinaryExpression;
public override Type Type { get; }
public BoundBinaryOperator Operator { get; }
public BoundExpression LeftExpression { get; }
public BoundExpression RightExpression { get; }
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.Linq;
using Upsilon.Parser;
using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Binder
{
public class BoundBinaryOperator
{
public enum OperatorKind
{
Addition, Subtraction, Multiplication, Division, Equality, Inequality
}
public Type LeftType { get; }
public Type RightType { get; }
public Type OutType { get; }
public OperatorKind Kind { get; }
public BoundBinaryOperator(OperatorKind kind, Type outType)
{
Kind = kind;
LeftType = outType;
RightType = outType;
OutType = outType;
}
public BoundBinaryOperator(OperatorKind kind, Type leftType, Type rightType, Type outType)
{
Kind = kind;
LeftType = leftType;
RightType = rightType;
OutType = outType;
}
public static BoundBinaryOperator[] Operators = new BoundBinaryOperator[]
{
// Math operators
new BoundBinaryOperator(OperatorKind.Addition, Type.Number),
new BoundBinaryOperator(OperatorKind.Subtraction, Type.Number),
new BoundBinaryOperator(OperatorKind.Multiplication, Type.Number),
new BoundBinaryOperator(OperatorKind.Division, Type.Number),
// Number equality
new BoundBinaryOperator(OperatorKind.Equality, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Number, Type.Number, Type.Boolean),
// Boolean equality
new BoundBinaryOperator(OperatorKind.Equality, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Boolean),
};
public static BoundBinaryOperator BindBinaryOperator(SyntaxKind operatorToken, Type left, Type right)
{
OperatorKind kind;
switch (operatorToken)
{
case SyntaxKind.Plus:
kind = OperatorKind.Addition;
break;
case SyntaxKind.Minus:
kind = OperatorKind.Subtraction;
break;
case SyntaxKind.Star:
kind = OperatorKind.Multiplication;
break;
case SyntaxKind.Slash:
kind = OperatorKind.Division;
break;
case SyntaxKind.EqualsEquals:
kind = OperatorKind.Equality;
break;
case SyntaxKind.TildeEquals:
kind = OperatorKind.Inequality;
break;
default:
throw new Exception("Unknown binary operator token: " + operatorToken);
}
return Operators.FirstOrDefault(op => op.Kind == kind && op.LeftType == left && op.RightType == right);
}
}
}

View File

@ -0,0 +1,9 @@
using Upsilon.BaseTypes;
namespace Upsilon.Binder
{
public abstract class BoundExpression : BoundNode
{
public abstract Type Type { get; }
}
}

View File

@ -0,0 +1,10 @@
namespace Upsilon.Binder
{
public enum BoundKind
{
BoundScript,
BoundLiteralExpression,
BoundBinaryExpression,
}
}

View File

@ -0,0 +1,17 @@
using Upsilon.BaseTypes;
namespace Upsilon.Binder
{
public class BoundLiteralExpression : BoundExpression
{
public BoundLiteralExpression(object value, Type type)
{
Value = value;
Type = type;
}
public override BoundKind Kind => BoundKind.BoundLiteralExpression;
public override Type Type { get; }
public object Value { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace Upsilon.Binder
{
public abstract class BoundNode
{
public abstract BoundKind Kind { get; }
}
}

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
namespace Upsilon.Binder
{
public class BoundScope
{
private readonly BoundScope _parentScope;
private readonly Dictionary<string, VariableSymbol> _variables;
public BoundScope(BoundScope parentScope)
{
_parentScope = parentScope;
_variables = new Dictionary<string, VariableSymbol>();
}
public void SetVariable(VariableSymbol var)
{
if (_variables.ContainsKey(var.Name))
_variables[var.Name] = var;
else
_variables.Add(var.Name, var);
}
public void SetGlobalVariable(VariableSymbol var)
{
if (_parentScope == null)
{
SetVariable(var);
}
else
{
_parentScope.SetGlobalVariable(var);
}
}
public bool TryGetVariable(string key, out VariableSymbol result)
{
if (_variables.TryGetValue(key, out result))
{
return true;
}
if (_parentScope != null)
{
return _parentScope.TryGetVariable(key, out result);
}
return false;
}
}
}

View File

@ -0,0 +1,14 @@
namespace Upsilon.Binder
{
public class BoundScript : BoundNode
{
public BoundScript(BoundExpression statement)
{
Statement = statement;
}
public override BoundKind Kind => BoundKind.BoundScript;
public BoundExpression Statement { get; }
}
}

View File

@ -0,0 +1,16 @@
using Upsilon.BaseTypes;
namespace Upsilon.Binder
{
public class VariableSymbol
{
public VariableSymbol(string name, Type type)
{
Type = type;
Name = name;
}
public Type Type { get; }
public string Name { get; }
}
}

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.Parser;
using Upsilon.Text;
namespace Upsilon
@ -37,6 +39,16 @@ namespace Upsilon
{
LogError($"Null Reference Encountered", span);
}
public void LogUnknownType(TextSpan eSpan)
{
LogError($"Unknown Type found", eSpan);
}
public void LogUnknownOperator(TextSpan eSpan, SyntaxKind text, Type leftType, Type rightType)
{
LogError($"No binary operator {text} found for types '{leftType}' and '{rightType}'", eSpan);
}
}
public class DiagnosticsMessage
@ -71,7 +83,7 @@ namespace Upsilon
public string AfterError(int i = 5)
{
return Diagnostics.ScriptString.GetSpan(Span.Start + 1, i);
return Diagnostics.ScriptString.GetSpan(Span.Start + Span.Length, i);
}
}

View File

@ -1,4 +1,6 @@
using System;
using Upsilon.BaseTypes.Number;
using Upsilon.Binder;
using Upsilon.Parser;
namespace Upsilon.Evaluator
@ -7,46 +9,39 @@ namespace Upsilon.Evaluator
{
private readonly Diagnostics _diagnostics;
public Script Script { get; }
public VariableScope Scope { get; }
public Evaluator(Script script, VariableScope scope, Diagnostics diagnostics)
public Evaluator(Script script, Diagnostics diagnostics)
{
_diagnostics = diagnostics;
Script = script;
Scope = scope;
}
/*
public object Evaluate(ScriptSyntax e)
{
return EvaluateExpression(e.Statement);
}*/
public object Evaluate(BoundScript e)
{
return EvaluateExpression(e.Statement);
}
public object Evaluate(ExpressionSyntax e)
{
return EvaluateExpression(e);
}
private object EvaluateExpression(ExpressionSyntax e)
private object EvaluateExpression(BoundExpression e)
{
switch (e.Kind)
{
case SyntaxKind.UnaryExpression:
return EvaluateUnaryExpression((UnaryExpressionSyntax) e);
case SyntaxKind.BinaryExpression:
return EvaluateBinaryExpression((BinaryExpressionSyntax) e);
case SyntaxKind.LiteralExpression:
return ((LiteralExpressionSyntax) e).Value;
case SyntaxKind.ParenthesizedExpression:
return Evaluate(((ParenthesizedExpressionSyntax)e).Expression);
case SyntaxKind.AssignmentExpression:
return EvaluateAssignmentExpression((AssignmentExpressionSyntax)e);
case SyntaxKind.VariableExpression:
return EvaluateVariableExpression((VariableExpressionSyntax) e);
case BoundKind.BoundLiteralExpression:
return ((BoundLiteralExpression) e).Value;
case BoundKind.BoundBinaryExpression:
return EvaluateBinaryExpression((BoundBinaryExpression) e);
default:
throw new Exception("Invalid expression: " + e.Kind);
throw new ArgumentOutOfRangeException();
}
throw new NotImplementedException();
}
/*
private object EvaluateUnaryExpression(UnaryExpressionSyntax e)
{
var operand = EvaluateExpression(e.Expression);
@ -61,66 +56,42 @@ namespace Upsilon.Evaluator
default:
throw new Exception("Invalid Unary Operator: " + e.Operator.Kind);
}
}
}*/
private object EvaluateBinaryExpression(BinaryExpressionSyntax e)
private object EvaluateBinaryExpression(BoundBinaryExpression e)
{
var left = EvaluateExpression(e.Left);
var right = EvaluateExpression(e.Right);
try
var left = EvaluateExpression(e.LeftExpression);
var right = EvaluateExpression(e.RightExpression);
switch (e.Operator.Kind)
{
switch (e.Operator.Kind)
{
case SyntaxKind.Plus:
return (double) left + (double) right;
case SyntaxKind.Minus:
return (double) left - (double) right;
case SyntaxKind.Star:
return (double) left * (double) right;
case SyntaxKind.Slash:
return (double) left / (double) right;
case SyntaxKind.AndKeyword:
return (bool) left && (bool) right;
case SyntaxKind.OrKeyword:
return (bool) left || (bool) right;
case SyntaxKind.EqualsEquals:
return Equals(left, right);
case SyntaxKind.TildeEquals:
return !Equals(left, right);
default:
throw new Exception("Invalid Binary Operator: " + e.Operator.Kind);
}
}
catch (NullReferenceException)
{
_diagnostics.LogNullReferenceError(e.Span);
return null;
case BoundBinaryOperator.OperatorKind.Addition:
return ((Number)left) + ((Number)right);
case BoundBinaryOperator.OperatorKind.Subtraction:
return ((Number)left) - ((Number)right);
case BoundBinaryOperator.OperatorKind.Multiplication:
return ((Number)left) * ((Number)right);
case BoundBinaryOperator.OperatorKind.Division:
return ((Number)left) / ((Number)right);
case BoundBinaryOperator.OperatorKind.Equality:
return Equals(left, right);
case BoundBinaryOperator.OperatorKind.Inequality:
return !Equals(left, right);
default:
throw new Exception("Invalid Binary Operator: " + e.Operator);
}
}
/*
private object EvaluateAssignmentExpression(AssignmentExpressionSyntax e)
{
var variableName = e.Identifier.Name;
var val = EvaluateExpression(e.Expression);
if (e.LocalToken == null)
{
Scope.SetGlobalVariable(variableName, val);
}
else
{
Scope.SetVariable(variableName, val);
}
return val;
}
}*/
private object EvaluateVariableExpression(VariableExpressionSyntax e)
{
var varName = e.Identifier.Name;
if (Scope.TryGetVariable(varName, out var value))
{
return value;
}
_diagnostics.LogUnknownVariable(e.Span, varName);
return null;
}

View File

@ -1,41 +1,37 @@
using System.Collections.Generic;
using Upsilon.Binder;
using Upsilon.Parser;
using Upsilon.Text;
namespace Upsilon.Evaluator
{
public class Script : VariableScope
public class Script
{
private SourceText ScriptString { get; }
private Evaluator Evaluator { get; }
public readonly ScriptSyntax Parsed;
private readonly ScriptSyntax _parsed;
public Diagnostics Diagnostics { get; }
private Binder.Binder Binder { get; }
public Script(string scriptString)
{
ScriptString = new SourceText(scriptString);
Diagnostics = new Diagnostics(ScriptString);
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
Evaluator = new Evaluator(this, this, Diagnostics);
}
public Script(string scriptString, Dictionary<string, object> variables = null)
:base(variables: variables)
{
ScriptString = new SourceText(scriptString);
Diagnostics = new Diagnostics(ScriptString);
Evaluator = new Evaluator(this, this, Diagnostics);
Parsed = Parser.Parser.Parse(scriptString, Diagnostics);
_parsed = Parser.Parser.Parse(scriptString, Diagnostics);
Binder = new Binder.Binder(new BoundScope(null), Diagnostics);
Evaluator = new Evaluator(this, Diagnostics);
}
public object Evaluate()
{
return Evaluator.Evaluate(Parsed);
var bound = Binder.BindScript(_parsed);
return Evaluator.Evaluate(bound);
}
public T Evaluate<T>()
{
return (T)Evaluator.Evaluate(Parsed);
var bound = Binder.BindScript(_parsed);
return (T)Evaluator.Evaluate(bound);
}
}
}

View File

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
namespace Upsilon.Evaluator
{
public class VariableScope
{
private VariableScope _parentScope;
public readonly Dictionary<string, object> Variables = new Dictionary<string, object>();
public VariableScope(VariableScope parentScope = null, Dictionary<string, object> variables = null)
{
_parentScope = parentScope;
if (variables != null)
Variables = variables;
}
public void SetVariable(string key, object value)
{
if (Variables.ContainsKey(key))
Variables[key] = value;
else
Variables.Add(key, value);
}
public void SetGlobalVariable(string key, object value)
{
if (_parentScope == null)
{
SetVariable(key, value);
}
else
{
_parentScope.SetGlobalVariable(key, value);
}
}
public bool TryGetVariable(string key, out object result)
{
if (Variables.TryGetValue(key, out result))
{
return true;
}
if (_parentScope != null)
{
return _parentScope.TryGetVariable(key, out result);
}
return false;
}
}
}

View File

@ -7,7 +7,7 @@ namespace Upsilon.Parser
public override SyntaxKind Kind => SyntaxKind.BadExpression;
public override IEnumerable<SyntaxNode> ChildNodes()
{
throw new System.NotImplementedException();
yield break;
}
}
}

View File

@ -1,14 +1,16 @@
using System.Collections.Generic;
using Upsilon.Text;
namespace Upsilon.Parser
{
public class BinaryExpressionSyntax : ExpressionSyntax
public sealed class BinaryExpressionSyntax : ExpressionSyntax
{
public BinaryExpressionSyntax(ExpressionSyntax left, SyntaxToken @operator, ExpressionSyntax right)
{
Left = left;
Operator = @operator;
Right = right;
Span = new TextSpan(left.Span.Start, right.Span.End);
}
public override SyntaxKind Kind => SyntaxKind.BinaryExpression;

View File

@ -2,12 +2,13 @@ using System.Collections.Generic;
namespace Upsilon.Parser
{
public class LiteralExpressionSyntax : ExpressionSyntax
public sealed class LiteralExpressionSyntax : ExpressionSyntax
{
public LiteralExpressionSyntax(SyntaxToken literal, object value)
{
Literal = literal;
Value = value;
Span = literal.Span;
}
public override SyntaxKind Kind => SyntaxKind.LiteralExpression;

View File

@ -10,6 +10,6 @@ namespace Upsilon.Text
public int Start { get; }
public int Length { get; }
public int End => Start + End;
public int End => Start + Length;
}
}

View File

@ -20,7 +20,7 @@ namespace Yc
return;
}
var parsed = new Script(input, variables);
var parsed = new Script(input);
if (parsed.Diagnostics.Messages.Count > 0)
{
Console.ForegroundColor = ConsoleColor.Red;
@ -47,7 +47,7 @@ namespace Yc
else
{
Console.WriteLine(evaluate);
variables = parsed.Variables;
//variables = parsed.Variables;
}
}
}