Implement number comparison operators

This commit is contained in:
Deukhoofd 2018-12-03 16:05:14 +01:00
parent a9f4ef1b65
commit 6ba3860e84
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
7 changed files with 146 additions and 2 deletions

View File

@ -60,6 +60,7 @@ namespace Upsilon.BaseTypes.Number
return new ScriptNumberLong(-((ScriptNumberLong)n).Value); return new ScriptNumberLong(-((ScriptNumberLong)n).Value);
} }
#region Equality #region Equality
private bool Equals(ScriptNumber other) private bool Equals(ScriptNumber other)
{ {
@ -82,6 +83,40 @@ namespace Upsilon.BaseTypes.Number
#endregion #endregion
public static ScriptBoolean operator < (ScriptNumber a, ScriptNumber b)
{
if (!a.IsFloat && !b.IsFloat)
return new ScriptBoolean(((ScriptNumberLong) a).Value < ((ScriptNumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new ScriptBoolean(((ScriptNumberDouble) a).Value < ((ScriptNumberDouble) b).Value);
if (a.IsFloat)
return new ScriptBoolean(((ScriptNumberDouble) a).Value < ((ScriptNumberLong) b).Value);
return new ScriptBoolean(((ScriptNumberLong) a).Value < ((ScriptNumberDouble) b).Value);
}
public static ScriptBoolean operator > (ScriptNumber a, ScriptNumber b)
{
if (!a.IsFloat && !b.IsFloat)
return new ScriptBoolean(((ScriptNumberLong) a).Value > ((ScriptNumberLong) b).Value);
if (a.IsFloat && b.IsFloat)
return new ScriptBoolean(((ScriptNumberDouble) a).Value > ((ScriptNumberDouble) b).Value);
if (a.IsFloat)
return new ScriptBoolean(((ScriptNumberDouble) a).Value > ((ScriptNumberLong) b).Value);
return new ScriptBoolean(((ScriptNumberLong) a).Value > ((ScriptNumberDouble) b).Value);
}
public static ScriptBoolean operator <= (ScriptNumber a, ScriptNumber b)
{
return (a < b) || a.Equals(b);
}
public static ScriptBoolean operator >= (ScriptNumber a, ScriptNumber b)
{
return !(a < b) || a.Equals(b);
}
public static explicit operator double(ScriptNumber n) public static explicit operator double(ScriptNumber n)
{ {
if (n.IsFloat) if (n.IsFloat)

View File

@ -9,7 +9,11 @@ namespace Upsilon.Binder
{ {
public enum OperatorKind public enum OperatorKind
{ {
Addition, Subtraction, Multiplication, Division, Equality, Inequality Addition, Subtraction, Multiplication, Division, Equality, Inequality,
GreaterEquals,
Greater,
LessEquals,
Less
} }
private Type LeftType { get; } private Type LeftType { get; }
@ -45,6 +49,12 @@ namespace Upsilon.Binder
new BoundBinaryOperator(OperatorKind.Equality, Type.Number, Type.Number, Type.Boolean), new BoundBinaryOperator(OperatorKind.Equality, Type.Number, Type.Number, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, 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 // Boolean equality
new BoundBinaryOperator(OperatorKind.Equality, Type.Boolean), new BoundBinaryOperator(OperatorKind.Equality, Type.Boolean),
new BoundBinaryOperator(OperatorKind.Inequality, Type.Boolean), new BoundBinaryOperator(OperatorKind.Inequality, Type.Boolean),
@ -82,6 +92,18 @@ namespace Upsilon.Binder
case SyntaxKind.TildeEquals: case SyntaxKind.TildeEquals:
kind = OperatorKind.Inequality; kind = OperatorKind.Inequality;
break; 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;
default: default:
throw new Exception("Unknown binary operator token: " + operatorToken); throw new Exception("Unknown binary operator token: " + operatorToken);
} }

View File

@ -7,6 +7,8 @@ using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.BaseTypes.UserData; using Upsilon.BaseTypes.UserData;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols; using Upsilon.Binder.VariableSymbols;
using Upsilon.Exceptions;
using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Evaluator namespace Upsilon.Evaluator
@ -328,11 +330,47 @@ namespace Upsilon.Evaluator
return new ScriptBoolean(Equals(left, right)); return new ScriptBoolean(Equals(left, right));
case BoundBinaryOperator.OperatorKind.Inequality: case BoundBinaryOperator.OperatorKind.Inequality:
return new ScriptBoolean(!Equals(left, right)); return new ScriptBoolean(!Equals(left, right));
case BoundBinaryOperator.OperatorKind.Less:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber)left) < ((ScriptNumber)right);
}
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
return new ScriptNull();
case BoundBinaryOperator.OperatorKind.LessEquals:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber)left) <= ((ScriptNumber)right);
}
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
return new ScriptNull();
case BoundBinaryOperator.OperatorKind.Greater:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber)left) > ((ScriptNumber)right);
}
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
return new ScriptNull();
case BoundBinaryOperator.OperatorKind.GreaterEquals:
if (left.Type == Type.Number && right.Type == Type.Number)
{
return ((ScriptNumber)left) >= ((ScriptNumber)right);
}
ThrowException($"Can't find operator for types '{left.Type}' and '{right.Type}'", e.Span);
return new ScriptNull();
default: default:
throw new Exception("Invalid Binary Operator: " + e.Operator); throw new Exception("Invalid Binary Operator: " + e.Operator);
} }
} }
private void ThrowException(string message, TextSpan location)
{
var (i, pos) = _script.ScriptString.GetLinePosition(location.Start);
var line = _script.ScriptString.GetLine(i);
throw new ScriptRuntimeException(message, i, pos, line);
}
private void EvaluateAssignmentStatement(BoundVariableAssignment e) private void EvaluateAssignmentStatement(BoundVariableAssignment e)
{ {
var val = EvaluateExpression(e.BoundExpression); var val = EvaluateExpression(e.BoundExpression);

View File

@ -0,0 +1,27 @@
using System;
namespace Upsilon.Exceptions
{
public class ScriptRuntimeException : Exception
{
public string ErrorMessage { get; }
public int Line { get; }
public int Character { get; }
public string ErrorLine { get; }
public ScriptRuntimeException(string errorMessage, int line, int character, string errorLine)
{
ErrorMessage = errorMessage;
Line = line;
Character = character;
ErrorLine = errorLine;
}
public override string ToString()
{
return $"{ErrorMessage} at ({Line}, {Character})\n{ErrorLine}";
}
public override string Message => ToString();
}
}

View File

@ -118,6 +118,20 @@ namespace Upsilon.Parser
return new SyntaxToken(SyntaxKind.TildeEquals, _position - 1, "~=", null); return new SyntaxToken(SyntaxKind.TildeEquals, _position - 1, "~=", null);
} }
return new SyntaxToken(SyntaxKind.Tilde, _position, "~", null); return new SyntaxToken(SyntaxKind.Tilde, _position, "~", null);
case '<':
if (Next == '=')
{
_position++;
return new SyntaxToken(SyntaxKind.LessEquals, _position - 1, "<=", null);
}
return new SyntaxToken(SyntaxKind.Less, _position, "<", null);
case '>':
if (Next == '=')
{
_position++;
return new SyntaxToken(SyntaxKind.GreaterEquals, _position - 1, ">=", null);
}
return new SyntaxToken(SyntaxKind.Greater, _position, ">", null);
default: default:
if (char.IsLetter(Current) || Current == '_') if (char.IsLetter(Current) || Current == '_')
return LexIdentifierOrKeyword(); return LexIdentifierOrKeyword();

View File

@ -29,6 +29,10 @@ namespace Upsilon.Parser
OpenBracket, OpenBracket,
CloseBracket, CloseBracket,
PoundSign, PoundSign,
Less,
LessEquals,
Greater,
GreaterEquals,
// key words // key words
TrueKeyword, TrueKeyword,

View File

@ -34,8 +34,12 @@ namespace Upsilon.Parser
{ {
// equality operators // equality operators
case SyntaxKind.EqualsEquals: case SyntaxKind.EqualsEquals:
return Precedence.Equality;
case SyntaxKind.TildeEquals: case SyntaxKind.TildeEquals:
case SyntaxKind.Greater:
case SyntaxKind.GreaterEquals:
case SyntaxKind.Less:
case SyntaxKind.LessEquals:
return Precedence.Equality; return Precedence.Equality;
// logical operators // logical operators