Upsilon/Upsilon/Binder/BoundBinaryOperator.cs

160 lines
7.0 KiB
C#

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,
GreaterEquals,
Greater,
LessEquals,
Less,
And,
Or,
Exponent,
Remainder
}
private Type LeftType { get; }
private Type RightType { get; }
public Type OutType { get; }
public OperatorKind Kind { get; }
private BoundBinaryOperator(OperatorKind kind, Type outType)
{
Kind = kind;
LeftType = outType;
RightType = outType;
OutType = outType;
}
private BoundBinaryOperator(OperatorKind kind, Type leftType, Type rightType, Type outType)
{
Kind = kind;
LeftType = leftType;
RightType = rightType;
OutType = outType;
}
private static readonly BoundBinaryOperator[] Operators = {
// 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),
new BoundBinaryOperator(OperatorKind.Exponent, Type.Number),
new BoundBinaryOperator(OperatorKind.Remainder, Type.Number),
// Number equality
new BoundBinaryOperator(OperatorKind.Equality, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Number, Type.Number, Type.Boolean),
// Number comparison
new BoundBinaryOperator(OperatorKind.Less, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.LessEquals, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Greater, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.GreaterEquals, Type.Number, Type.Number, Type.Boolean),
// Boolean equality
new BoundBinaryOperator(OperatorKind.Equality, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Boolean),
// Boolean chaining
new BoundBinaryOperator(OperatorKind.And, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Or, Type.Boolean),
// String operators
new BoundBinaryOperator(OperatorKind.Addition, Type.String, Type.String, Type.String),
new BoundBinaryOperator(OperatorKind.Addition, Type.String, Type.Number, Type.String),
new BoundBinaryOperator(OperatorKind.Addition, Type.String, Type.Boolean, Type.String),
// String equality
new BoundBinaryOperator(OperatorKind.Equality, Type.String, Type.String, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.String, Type.String, Type.Boolean),
// general nil checks
new BoundBinaryOperator(OperatorKind.Equality, Type.String, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.String, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Equality, Type.UserData, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.UserData, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Equality, Type.Table, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Table, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Equality, Type.Boolean, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Boolean, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Equality, Type.Unknown, Type.Nil, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Unknown, Type.Nil, Type.Boolean),
// Userdata equality
new BoundBinaryOperator(OperatorKind.Equality, Type.UserData, Type.UserData, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.UserData, Type.UserData, Type.Boolean),
};
public static BoundBinaryOperator Bind(SyntaxKind operatorToken, Type left, Type right)
{
if (left == Type.UserData) left = Type.Unknown;
if (right == Type.UserData) right = Type.Unknown;
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.PercentSign:
kind = OperatorKind.Remainder;
break;
case SyntaxKind.RoofSign:
kind = OperatorKind.Exponent;
break;
case SyntaxKind.EqualsEquals:
kind = OperatorKind.Equality;
break;
case SyntaxKind.TildeEquals:
kind = OperatorKind.Inequality;
break;
case SyntaxKind.Less:
kind = OperatorKind.Less;
break;
case SyntaxKind.LessEquals:
kind = OperatorKind.LessEquals;
break;
case SyntaxKind.Greater:
kind = OperatorKind.Greater;
break;
case SyntaxKind.GreaterEquals:
kind = OperatorKind.GreaterEquals;
break;
case SyntaxKind.AndKeyword:
kind = OperatorKind.And;
break;
case SyntaxKind.OrKeyword:
kind = OperatorKind.Or;
break;
default:
throw new Exception("Unknown binary operator token: " + operatorToken);
}
if (left == Type.Unknown && right == Type.Unknown)
return Operators.FirstOrDefault(op => op.Kind == kind);
if (left == Type.Unknown)
return Operators.FirstOrDefault(op => op.Kind == kind && op.RightType == right);
if (right == Type.Unknown)
return Operators.FirstOrDefault(op => op.Kind == kind && op.LeftType == left);
return Operators.FirstOrDefault(op => op.Kind == kind && op.LeftType == left && op.RightType == right);
}
}
}