From cd04486d1687d7d1aaae863d65da2274045deb12 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 24 Nov 2018 15:11:33 +0100 Subject: [PATCH] Adds Unary length operator --- Upsilon/BaseTypes/ScriptString.cs | 7 ++++- Upsilon/BaseTypes/ScriptTable.cs | 8 +++++- .../ScriptTypeInterfaces/ILengthType.cs | 9 +++++++ .../BaseTypes/UserData/DictionaryUserData.cs | 10 ++++++- Upsilon/BaseTypes/UserData/ListUserData.cs | 7 ++++- Upsilon/Binder/BoundUnaryOperator.cs | 18 ++++++++----- Upsilon/Evaluator/Evaluator.cs | 7 ++++- Upsilon/Parser/Lexer.cs | 2 ++ Upsilon/Parser/SyntaxKind.cs | 1 + Upsilon/Parser/SyntaxKindPrecedence.cs | 2 ++ .../GeneralTests/LengthOperatorTests.cs | 27 +++++++++++++++++++ .../BasicFunctionsTests.cs | 2 +- 12 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 Upsilon/BaseTypes/ScriptTypeInterfaces/ILengthType.cs create mode 100644 UpsilonTests/GeneralTests/LengthOperatorTests.cs diff --git a/Upsilon/BaseTypes/ScriptString.cs b/Upsilon/BaseTypes/ScriptString.cs index bbd99b0..8d0e9df 100644 --- a/Upsilon/BaseTypes/ScriptString.cs +++ b/Upsilon/BaseTypes/ScriptString.cs @@ -6,7 +6,7 @@ using Upsilon.Text; namespace Upsilon.BaseTypes { - internal class ScriptString : ScriptType, IIndexable + internal class ScriptString : ScriptType, IIndexable, ILengthType { public ScriptString(string value) { @@ -85,6 +85,11 @@ namespace Upsilon.BaseTypes return Value; } + public ScriptNumberLong Length() + { + return new ScriptNumberLong(Value.Length); + } + public ScriptType Get(Diagnostics diagnostics, TextSpan span, ScriptType index, EvaluationScope scope) { int i; diff --git a/Upsilon/BaseTypes/ScriptTable.cs b/Upsilon/BaseTypes/ScriptTable.cs index abbbcea..2983493 100644 --- a/Upsilon/BaseTypes/ScriptTable.cs +++ b/Upsilon/BaseTypes/ScriptTable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder; using Upsilon.Evaluator; @@ -8,7 +9,7 @@ using Upsilon.Text; namespace Upsilon.BaseTypes { - internal class ScriptTable : ScriptType, IIndexable, IScopeOwner, IIterable + internal class ScriptTable : ScriptType, IIndexable, IScopeOwner, IIterable, ILengthType { public EvaluationScope EvaluationScope { get; } @@ -66,5 +67,10 @@ namespace Upsilon.BaseTypes yield return variable.Key.ToScriptType(); } } + + public ScriptNumberLong Length() + { + return new ScriptNumberLong(EvaluationScope.Variables.Count); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/ScriptTypeInterfaces/ILengthType.cs b/Upsilon/BaseTypes/ScriptTypeInterfaces/ILengthType.cs new file mode 100644 index 0000000..bad0411 --- /dev/null +++ b/Upsilon/BaseTypes/ScriptTypeInterfaces/ILengthType.cs @@ -0,0 +1,9 @@ +using Upsilon.BaseTypes.Number; + +namespace Upsilon.BaseTypes.ScriptTypeInterfaces +{ + internal interface ILengthType + { + ScriptNumberLong Length(); + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/DictionaryUserData.cs b/Upsilon/BaseTypes/UserData/DictionaryUserData.cs index 1587c07..03900c7 100644 --- a/Upsilon/BaseTypes/UserData/DictionaryUserData.cs +++ b/Upsilon/BaseTypes/UserData/DictionaryUserData.cs @@ -1,10 +1,13 @@ using System.Collections; +using System.Collections.Generic; +using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Evaluator; using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { - internal class DictionaryUserData : ScriptType, IUserData + internal class DictionaryUserData : ScriptType, IUserData, ILengthType { public IDictionary Dictionary { get; } @@ -47,5 +50,10 @@ namespace Upsilon.BaseTypes.UserData { return Dictionary.GetType(); } + + public ScriptNumberLong Length() + { + return new ScriptNumberLong(Dictionary.Count); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/ListUserData.cs b/Upsilon/BaseTypes/UserData/ListUserData.cs index 5a6c1bf..5018e36 100644 --- a/Upsilon/BaseTypes/UserData/ListUserData.cs +++ b/Upsilon/BaseTypes/UserData/ListUserData.cs @@ -8,7 +8,7 @@ using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { - internal class ListUserData : ScriptType, IUserData, IIterable + internal class ListUserData : ScriptType, IUserData, IIterable, ILengthType { private IList List { get; } @@ -66,5 +66,10 @@ namespace Upsilon.BaseTypes.UserData yield return new ScriptNumberLong(i); } } + + public ScriptNumberLong Length() + { + return new ScriptNumberLong(List.Count); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundUnaryOperator.cs b/Upsilon/Binder/BoundUnaryOperator.cs index 55cb7d2..be64ceb 100644 --- a/Upsilon/Binder/BoundUnaryOperator.cs +++ b/Upsilon/Binder/BoundUnaryOperator.cs @@ -11,10 +11,11 @@ namespace Upsilon.Binder { Identity, Negation, - LogicalNegation + LogicalNegation, + Length } - public Type InType { get; } + private Type InType { get; } public Type OutType { get; } public OperatorKind Kind { get; } @@ -25,11 +26,13 @@ namespace Upsilon.Binder OutType = outType; } - private static BoundUnaryOperator[] _operators= new BoundUnaryOperator[] - { + private static readonly BoundUnaryOperator[] Operators= { new BoundUnaryOperator(OperatorKind.Identity, Type.Number, Type.Number), new BoundUnaryOperator(OperatorKind.Negation, Type.Number, Type.Number), new BoundUnaryOperator(OperatorKind.LogicalNegation, Type.Boolean, Type.Boolean), + new BoundUnaryOperator(OperatorKind.Length, Type.String, Type.Number), + new BoundUnaryOperator(OperatorKind.Length, Type.Table, Type.Number), + new BoundUnaryOperator(OperatorKind.Length, Type.UserData, Type.Number), }; public static BoundUnaryOperator Bind(SyntaxKind operatorToken, Type inType) @@ -46,13 +49,16 @@ namespace Upsilon.Binder case SyntaxKind.NotKeyword: kind = OperatorKind.LogicalNegation; break; + case SyntaxKind.PoundSign: + kind = OperatorKind.Length; + break; default: throw new Exception("Unknown unary operator token: " + operatorToken); } if (inType == Type.Unknown) - return _operators.FirstOrDefault(op => op.Kind == kind); - return _operators.FirstOrDefault(op => op.Kind == kind && op.InType == inType); + return Operators.FirstOrDefault(op => op.Kind == kind); + return Operators.FirstOrDefault(op => op.Kind == kind && op.InType == inType); } } } \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 6b0006e..6a56f4f 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -211,7 +211,12 @@ namespace Upsilon.Evaluator return type; } goto default; - + case BoundUnaryOperator.OperatorKind.Length: + if (operand is ILengthType length) + { + return length.Length(); + } + goto default; default: throw new Exception("Invalid Unary Operator: " + e.Operator.Kind); } diff --git a/Upsilon/Parser/Lexer.cs b/Upsilon/Parser/Lexer.cs index c2edbd6..1d2f8ef 100644 --- a/Upsilon/Parser/Lexer.cs +++ b/Upsilon/Parser/Lexer.cs @@ -78,6 +78,8 @@ namespace Upsilon.Parser return new SyntaxToken(SyntaxKind.FullStop, _position, ".", null); case ',': return new SyntaxToken(SyntaxKind.Comma, _position, ",", null); + case '#': + return new SyntaxToken(SyntaxKind.PoundSign, _position, "#", null); case '"': return LexString(); case '=': diff --git a/Upsilon/Parser/SyntaxKind.cs b/Upsilon/Parser/SyntaxKind.cs index ef3b43b..0241344 100644 --- a/Upsilon/Parser/SyntaxKind.cs +++ b/Upsilon/Parser/SyntaxKind.cs @@ -27,6 +27,7 @@ namespace Upsilon.Parser CloseBrace, OpenBracket, CloseBracket, + PoundSign, // key words TrueKeyword, diff --git a/Upsilon/Parser/SyntaxKindPrecedence.cs b/Upsilon/Parser/SyntaxKindPrecedence.cs index 9074166..534bae9 100644 --- a/Upsilon/Parser/SyntaxKindPrecedence.cs +++ b/Upsilon/Parser/SyntaxKindPrecedence.cs @@ -11,6 +11,7 @@ namespace Upsilon.Parser PlusMinus, StarSlash, Unary, + Exponentiation } public static Precedence UnaryOperatorPrecedence(this SyntaxKind kind) @@ -20,6 +21,7 @@ namespace Upsilon.Parser case SyntaxKind.Plus: case SyntaxKind.Minus: case SyntaxKind.NotKeyword: + case SyntaxKind.PoundSign: return Precedence.Unary; default: return Precedence.None; diff --git a/UpsilonTests/GeneralTests/LengthOperatorTests.cs b/UpsilonTests/GeneralTests/LengthOperatorTests.cs new file mode 100644 index 0000000..040e3e4 --- /dev/null +++ b/UpsilonTests/GeneralTests/LengthOperatorTests.cs @@ -0,0 +1,27 @@ +using Upsilon.Binder; +using Upsilon.Evaluator; +using Upsilon.StandardLibraries; +using Xunit; + +namespace UpsilonTests.GeneralTests +{ + public class LengthOperatorTests : TestClass + { + public LengthOperatorTests(StaticScriptFixture fix) : base(fix) + { + } + + [Theory] + [InlineData(@"#""test""", 4)] + [InlineData(@"#{100,50,60,7863,1564,12354,10354}", 7)] + public void Test(string input, long expectedOutput) + { + var script = new Script(input, BoundScope, StaticScope); + Assert.Empty(script.Diagnostics.Messages); + var actual = script.Evaluate(); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(expectedOutput, actual); + } + + } +} \ No newline at end of file diff --git a/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs index 59a915e..c3cebd9 100644 --- a/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs +++ b/UpsilonTests/StandardLibraryTests/BasicFunctionsTests.cs @@ -26,7 +26,7 @@ namespace UpsilonTests.StandardLibraryTests } [Fact] - public void IPairsTest() + public void UpTillNilPairsTest() { const string input = @" arr = {100, 56, 28, nil, 100}