diff --git a/GraceLanguage/Execution/AST.cs b/GraceLanguage/Execution/AST.cs old mode 100644 new mode 100755 index 372771f..adbdc7a --- a/GraceLanguage/Execution/AST.cs +++ b/GraceLanguage/Execution/AST.cs @@ -122,10 +122,10 @@ private static GraceObject mReportWith(EvaluationContext ctx, var message = GraceString.AsNativeString(ctx, req[0].Arguments[1]); var dict = new Dictionary(); var pairs = req[1].Arguments[0]; - Iterables.ForEach(ctx, pairs, + GraceSequence.ForEach(ctx, pairs, GraceBlock.Create((GraceObject o) => { string k = null, v = null; - Iterables.ForEach(ctx, o, + GraceSequence.ForEach(ctx, o, GraceBlock.Create((GraceObject kv) => { if (k == null) k = GraceString.AsNativeString(ctx, kv); @@ -865,7 +865,7 @@ protected override void addMethods() private static GraceObject mParts(RequestNode self) { - return GraceVariadicList.Of(self.parts); + return GraceSequence.Of(self.parts); } /// @@ -1174,12 +1174,12 @@ private static GraceObject mName(RequestPartNode self) private static GraceObject mArguments(RequestPartNode self) { - return GraceVariadicList.Of(self.Arguments); + return GraceSequence.Of(self.Arguments); } private static GraceObject mTypeArguments(RequestPartNode self) { - return GraceVariadicList.Of(self.GenericArguments); + return GraceSequence.Of(self.GenericArguments); } private static GraceObject mAccept(EvaluationContext ctx, @@ -1495,7 +1495,7 @@ protected override void addMethods() private static GraceObject mBody(ObjectConstructorNode self) { - return GraceVariadicList.Of(self.Body); + return GraceSequence.Of(self.Body); } /// @@ -1640,7 +1640,7 @@ public virtual GraceObject Respond(EvaluationContext ctx, if (idNode.Variadic) { hadVariadic = true; - var gvl = new GraceVariadicList(); + var gvl = new GraceSequence(); for (var i = sigPart.Parameters.Count - 1; i < pp.req.Arguments.Count; i++) @@ -1672,7 +1672,7 @@ public virtual GraceObject Respond(EvaluationContext ctx, string name = idNode.Name; if (idNode.Variadic) { - var gvl = new GraceVariadicList(); + var gvl = new GraceSequence(); myScope.AddLocalDef(name, gvl); } } @@ -1816,7 +1816,7 @@ private static GraceObject mSignature(MethodNode self) private static GraceObject mBody(MethodNode self) { - return GraceVariadicList.Of(self.body); + return GraceSequence.Of(self.body); } private static GraceObject mAnnotations(MethodNode self) @@ -1928,12 +1928,12 @@ protected override void addMethods() private static GraceObject mParameters(BlockNode self) { - return GraceVariadicList.Of(self.Parameters); + return GraceSequence.Of(self.Parameters); } private static GraceObject mBody(BlockNode self) { - return GraceVariadicList.Of(self.Body); + return GraceSequence.Of(self.Body); } /// @@ -2651,7 +2651,7 @@ protected override void addMethods() private static GraceObject mSignatures(InterfaceNode self) { - return GraceVariadicList.Of(self.body); + return GraceSequence.Of(self.body); } /// @@ -2928,7 +2928,7 @@ private static GraceObject mExcludes(InheritsNode self) foreach (var e in self.Excludes) myexcludes.Add(GraceString.Create(e)); - return GraceVariadicList.Of(myexcludes); + return GraceSequence.Of(myexcludes); } private static GraceObject mAliases(InheritsNode self) @@ -2942,10 +2942,10 @@ private static GraceObject mAliases(InheritsNode self) IList myannotations = new List(); foreach (var a in kv.Value.Annotations) myannotations.Add(a); - oneAlias.Add(GraceVariadicList.Of(myannotations)); - myaliases.Add(GraceVariadicList.Of(oneAlias)); + oneAlias.Add(GraceSequence.Of(myannotations)); + myaliases.Add(GraceSequence.Of(oneAlias)); } - return GraceVariadicList.Of(myaliases); + return GraceSequence.Of(myaliases); } /// @@ -3181,7 +3181,7 @@ protected override void addMethods() private static GraceObject mParts(SignatureNode self) { - return GraceVariadicList.Of(self.Parts); + return GraceSequence.Of(self.Parts); } private static GraceObject mReturnType(SignatureNode self) @@ -3309,12 +3309,12 @@ private static GraceObject mName(OrdinarySignaturePartNode self) private static GraceObject mTypeParameters( OrdinarySignaturePartNode self) { - return GraceVariadicList.Of(self.GenericParameters); + return GraceSequence.Of(self.GenericParameters); } private static GraceObject mParameters(OrdinarySignaturePartNode self) { - return GraceVariadicList.Of(self.Parameters); + return GraceSequence.Of(self.Parameters); } /// diff --git a/GraceLanguage/Execution/ExecutionTree.cs b/GraceLanguage/Execution/ExecutionTree.cs index a2339aa..7f7779d 100644 --- a/GraceLanguage/Execution/ExecutionTree.cs +++ b/GraceLanguage/Execution/ExecutionTree.cs @@ -392,7 +392,7 @@ public Node Visit(BlockParseNode d) { parameters.Add(p.Visit(this)); } - else if (p is ParenthesisedParseNode) + else { var tok = p.Token; var it = new IdentifierToken(tok.module, tok.line, @@ -400,10 +400,6 @@ public Node Visit(BlockParseNode d) id = new IdentifierParseNode(it); parameters.Add(new ParameterNode(tok, id, p.Visit(this))); } - else - { - throw new Exception("unimplemented - unusual parameters"); - } } var ret = new BlockNode(d.Token, d, parameters, diff --git a/GraceLanguage/GraceLanguage.csproj b/GraceLanguage/GraceLanguage.csproj old mode 100755 new mode 100644 index c35bd8c..ad5ca22 --- a/GraceLanguage/GraceLanguage.csproj +++ b/GraceLanguage/GraceLanguage.csproj @@ -1,4 +1,4 @@ - + @@ -54,9 +54,9 @@ + - @@ -80,6 +80,7 @@ + @@ -115,6 +116,7 @@ + @@ -146,4 +148,4 @@ --> - \ No newline at end of file + diff --git a/GraceLanguage/Parsing/Lexer.cs b/GraceLanguage/Parsing/Lexer.cs index 6c5fe63..5582da3 100644 --- a/GraceLanguage/Parsing/Lexer.cs +++ b/GraceLanguage/Parsing/Lexer.cs @@ -614,11 +614,13 @@ private Token lexStringRemainder() { char c = code[index]; if (c == 'n') - b.Append('\u2028'); + b.Append('\n'); else if (c == 't') b.Append('\t'); else if (c == 'l') b.Append('\u2028'); + else if (c == 'e') + b.Append('\x1b'); else if (c == '{') b.Append('{'); else if (c == '}') diff --git a/GraceLanguage/Parsing/Parser.cs b/GraceLanguage/Parsing/Parser.cs index c18e35d..b2ff22f 100644 --- a/GraceLanguage/Parsing/Parser.cs +++ b/GraceLanguage/Parsing/Parser.cs @@ -399,20 +399,8 @@ StatementLevel level "May not have ${token} inside ${context}."); return null; } - takeSemicolon(); - if (!(lexer.current is NewLineToken - || lexer.current is CommentToken - || lexer.current is EndToken - || lexer.current is RBraceToken)) - { - if (start.line == lexer.current.line - || lexer.current.line == lexer.previous.line) - reportError("P1004", lexer.current, - "Unexpected token after statement."); - else - reportError("P1030", lexer.current, - "Unexpected continuation token after statement."); - } + if (lexer.current is SemicolonToken) + lexer.NextToken(); // use semicolon to end statement while (lexer.current is NewLineToken) lexer.NextToken(); attachComments(ret, comments); @@ -871,10 +859,6 @@ private List parseTypeBody() attachComments(sig, comments); restoreComments(origComments); consumeBlankLines(); - if (sig.Token.line == lexer.current.line - && lexer.current.line != start.line) - reportError("P1004", lexer.current, - "Unexpected token after statement."); lastSig = sig; } lexer.NextToken(); @@ -1220,6 +1204,12 @@ private ParseNode parseExpressionNoBind() private ParseNode parseExpression() { var start = lexer.current; + if (start is NewLineToken) + { + nextToken(); + return parseExpression(); + } + ParseNode lhs = parseExpressionNoBind(); if (lexer.current is BindToken) { @@ -1300,6 +1290,7 @@ private ParseNode parseTerm() ret = new IdentifierParseNode((SelfKeywordToken)lexer.current); nextToken(); } + if (ret == null) { reportError("P1018", lexer.current, "Expected term."); @@ -1443,23 +1434,6 @@ private ParseNode parseOperatorStream(ParseNode lhs) bool allArith = true; while (tok != null) { - if ((!tok.SpaceBefore || !tok.SpaceAfter)) - { - if (tok.Name.StartsWith(":=")) - reportError("P1038", - new Dictionary - { - { "rest", tok.Name.Substring(2) } - }, - ":= needs space before prefix operator" - ); - reportError("P1020", - new Dictionary() - { - { "operator", tok.Name } - }, - "Infix operators must be surrounded by spaces."); - } nextToken(); if (lexer.current is CommentToken) { @@ -1559,17 +1533,6 @@ private void parseBraceDelimitedBlock(List body, Token lastToken = lexerCurrent(); while (awaiting(start)) { - if (lexer.current.column != indentColumn) - { - reportError("P1016", new Dictionary() - { - { "required indentation", "" + (indentColumn - 1) }, - { "given indentation", "" + (lexer.current.column - 1) } - }, - "Indentation mismatch; is " - + (lexer.current.column - 1) + ", should be " - + (indentColumn - 1) + "."); - } body.Add(parseStatement(level)); if (lexer.current == lastToken) { @@ -1601,11 +1564,6 @@ private void takeSemicolon() if (lexer.current is SemicolonToken) { lexer.NextToken(); - if (!(lexer.current is NewLineToken - || lexer.current is CommentToken - || lexer.current is EndToken - || lexer.current is RBraceToken)) - reportError("P1003", "Other code cannot follow a semicolon on the same line."); } } @@ -1704,17 +1662,6 @@ private ParseNode parseBlock() indentColumn = firstBodyToken.column; while (awaiting(start)) { - if (lexer.current.column != indentColumn) - { - reportError("P1016", new Dictionary - { - { "required indentation", "" + (indentColumn - 1) }, - { "given indentation", "" + (lexer.current.column - 1) } - }, - "Indentation mismatch; is " - + (lexer.current.column - 1) + ", should be " - + (indentColumn - 1) + "."); - } ret.Body.Add(parseStatement(StatementLevel.MethodLevel)); if (lexer.current == lastToken) { diff --git a/GraceLanguage/Runtime/Callback.cs b/GraceLanguage/Runtime/Callback.cs new file mode 100644 index 0000000..ee37f59 --- /dev/null +++ b/GraceLanguage/Runtime/Callback.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Grace.Execution; + +namespace Grace.Runtime +{ + // A new version of "Method.cs" that's much easier to use! + public static class Callback + { + /// A callback for a method on a receiver with type T0, and taking any number of arguments + public static Method Nary(Func m) where T0 : GraceObject + => new DelegateMethodInheritable((ctx, req, self) => m(ctx, (T0)self, req)); + + /// A callback for a method on a receiver with type T0, and taking no arguments + public static Method Nullary(Func m) where T0 : GraceObject + => CustomArity((ctx, self, req) => m(ctx, self)); + + /// A callback for a method on a receiver with type T0, and taking one argument of type T1 + public static Method Unary(Func m) where T0 : GraceObject where T1 : GraceObject + => CustomArity((ctx, self, req) => m(ctx, self, GetArg(ctx, req, 0, 0)), 1); + + /// A callback for a method on a receiver with type T0, and taking two arguments of type T1 and T2 in one part + public static Method Binary(Func m) where T0 : GraceObject where T1 : GraceObject where T2 : GraceObject + => CustomArity((ctx, self, req) => m(ctx, self, GetArg(ctx, req, 0, 0), GetArg(ctx, req, 0, 1)), 2); + + /// A callback for a method on a receiver with type T0, and taking one argument of type T1 in the first part, and one of type T2 in the second part + public static Method UnaryUnary(Func m) where T0 : GraceObject where T1 : GraceObject where T2 : GraceObject + => CustomArity((ctx, self, req) => m(ctx, self, GetArg(ctx, req, 0, 0), GetArg(ctx, req, 1, 0)), 1, 1); + + // Utility method to check the arity before calling "m" + private static Method CustomArity(Func m, params int[] arities) where T0 : GraceObject + => Nary((ctx, self, req) => { + MethodHelper.CheckArity(ctx, req, arities); + return m(ctx, self, req);}); + + // A utility method to convert types accordingly + private static T GetArg(EvaluationContext ctx, MethodRequest req, int part, int index) where T : GraceObject { + var arg = req[part].Arguments[index].FindNativeParent(); + if (arg == null) ErrorReporting.RaiseError(ctx, "R2001", + new Dictionary { + { "method", req.Name }, + { "index", index.ToString() }, + { "part", req[part].Name } + }, "must be a " + typeof(T).Name.Replace("Grace", "")); + return arg; } + } +} diff --git a/GraceLanguage/Runtime/DictionaryDataObject.cs b/GraceLanguage/Runtime/DictionaryDataObject.cs index 7d61574..3bfac80 100644 --- a/GraceLanguage/Runtime/DictionaryDataObject.cs +++ b/GraceLanguage/Runtime/DictionaryDataObject.cs @@ -5,7 +5,7 @@ namespace Grace.Runtime { - class DictionaryDataObject : GraceObject, + public class DictionaryDataObject : GraceObject, IEnumerable> { private Dictionary data; diff --git a/GraceLanguage/Runtime/GraceBlock.cs b/GraceLanguage/Runtime/GraceBlock.cs old mode 100644 new mode 100755 index 7a10aac..1b55a8a --- a/GraceLanguage/Runtime/GraceBlock.cs +++ b/GraceLanguage/Runtime/GraceBlock.cs @@ -38,6 +38,7 @@ private GraceBlock(EvaluationContext ctx, List parameters, if (parameters.Count == 1) { AddMethod("match(_)", null); + AddMethod("matches(_)", null); AddMethod("|(_)", Matching.OrMethod); AddMethod("&(_)", Matching.AndMethod); var par = parameters[0]; @@ -77,6 +78,7 @@ protected override Method getLazyMethod(string name) return new DelegateMethodReq(Apply); switch(name) { case "match(_)": return new DelegateMethodReq(Match); + case "matches(_)": return new DelegateMethodReq(Matches); case "spawn": return new DelegateMethod0Ctx(mSpawn); case "asString": return new DelegateMethod0Ctx(mAsString); } @@ -92,9 +94,8 @@ public void ForcePattern(GraceObject pattern) if (Pattern != null) return; explicitPattern = true; - AddMethod("match(_)", - new DelegateMethodReq( - new NativeMethodReq(this.Match))); + AddMethod("match(_)", new DelegateMethodReq(new NativeMethodReq(this.Match))); + AddMethod("matches(_)", new DelegateMethodReq(new NativeMethodReq(this.Matches))); AddMethod("|(_)", Matching.OrMethod); AddMethod("&(_)", Matching.AndMethod); Pattern = pattern; @@ -174,7 +175,7 @@ public GraceObject Apply(EvaluationContext ctx, MethodRequest req) { // Populate variadic parameter with all remaining // arguments. - var gvl = new GraceVariadicList(); + var gvl = new GraceSequence(); for (var i = parameters.Count - 1; i < req[0].Arguments.Count; i++) @@ -194,7 +195,7 @@ public GraceObject Apply(EvaluationContext ctx, MethodRequest req) var idNode = param as ParameterNode; if (idNode != null && idNode.Variadic) { - var gvl = new GraceVariadicList(); + var gvl = new GraceSequence(); myScope.AddLocalDef(idNode.Name, gvl); } } @@ -208,6 +209,12 @@ public GraceObject Apply(EvaluationContext ctx, MethodRequest req) return ret; } + /// Native method representing the matches method + /// Current interpreter + /// Request that obtained this method + public GraceObject Matches(EvaluationContext ctx, MethodRequest req) + => Match(ctx, req).Request(ctx, MethodRequest.Nullary("succeeded")); + /// Native method representing the match method /// Current interpreter /// Request that obtained this method diff --git a/GraceLanguage/Runtime/GraceBoolean.cs b/GraceLanguage/Runtime/GraceBoolean.cs old mode 100644 new mode 100755 index 11191ae..609492c --- a/GraceLanguage/Runtime/GraceBoolean.cs +++ b/GraceLanguage/Runtime/GraceBoolean.cs @@ -50,8 +50,10 @@ private GraceBoolean(bool val) AddMethod("orElse(_)", new DelegateMethod1Ctx( new NativeMethod1Ctx(this.OrElse))); - AddMethod("match(_)", new DelegateMethod1Ctx( - new NativeMethod1Ctx(this.Match))); + AddMethod("match(_)", Callback.Unary((ctx, self, target) => + self.DoesMatch(ctx, target) ? Matching.SuccessfulMatch(ctx, target) : Matching.FailedMatch(ctx, target))); + AddMethod("matches(_)", Callback.Unary((ctx, self, target) => + GraceBoolean.Create(self.DoesMatch(ctx, target)))); AddMethod("asString", new DelegateMethod0(new NativeMethod0(this.AsString))); } @@ -69,15 +71,15 @@ private GraceObject mHash() return GraceNumber.Create(Boolean.GetHashCode()); } - /// Native method for Grace match + /// Native method used for both Grace match and matches /// Current interpreter /// Target of the match - public GraceObject Match(EvaluationContext ctx, GraceObject target) + public bool DoesMatch(EvaluationContext ctx, GraceObject target) { var b = target as GraceBoolean; if (b != null && b.Boolean == Boolean) - return Matching.SuccessfulMatch(ctx, target); - return Matching.FailedMatch(ctx, target); + return true; + return false; } /// Native method for Grace asString @@ -93,8 +95,10 @@ public GraceObject AsString() /// Argument to the method public GraceObject AndAnd(EvaluationContext ctx, GraceObject other) { + if (other is GraceBlock) { return AndAlso(ctx, other); } GraceBoolean oth = other as GraceBoolean; - if (oth != null) + + if (oth != null) return GraceBoolean.Create(this.Boolean && oth.Boolean); GraceObjectProxy op = other as GraceObjectProxy; if (op != null) @@ -116,7 +120,8 @@ public GraceObject AndAnd(EvaluationContext ctx, GraceObject other) /// Argument to the method public GraceObject OrOr(EvaluationContext ctx, GraceObject other) { - GraceBoolean oth = other as GraceBoolean; + if (other is GraceBlock) { return OrElse(ctx, other); } + GraceBoolean oth = other as GraceBoolean; if (oth != null) return GraceBoolean.Create(this.Boolean || oth.Boolean); GraceObjectProxy op = other as GraceObjectProxy; diff --git a/GraceLanguage/Runtime/GraceNumber.cs b/GraceLanguage/Runtime/GraceNumber.cs index 311b11d..d7cd89d 100644 --- a/GraceLanguage/Runtime/GraceNumber.cs +++ b/GraceLanguage/Runtime/GraceNumber.cs @@ -74,7 +74,9 @@ private static Dictionary createSharedMethods() new DelegateMethodTyped0(mAsString) }, { "prefix-", new DelegateMethodTyped0(mNegate) }, { "..(_)", new DelegateMethodTyped1Ctx(mDotDot) }, - { "match(_)", new DelegateMethodTyped1Ctx(mMatch) }, + { "match(_)", Callback.Unary((ctx, self, target) => + mEqualsEquals(self, target) == GraceBoolean.True ? Matching.SuccessfulMatch(ctx, target) : Matching.FailedMatch(ctx, target)) }, + { "matches(_)", Callback.Unary((ctx, self, target) => mEqualsEquals(self, target))}, { "hash", new DelegateMethodTyped0(mHash) }, // These methods are extensions, and should not be used. { "numerator", @@ -83,6 +85,7 @@ private static Dictionary createSharedMethods() new DelegateMethodTyped0(mDenominator) }, { "integral", new DelegateMethodTyped0(mIntegral) }, + {"truncated", Callback.Nullary((ctx, self) => Create(self.GetInt())) }, }; return sharedMethods; } @@ -294,23 +297,6 @@ private static GraceObject mHash(GraceNumber self) return GraceNumber.Create(self.Value.GetHashCode()); } - /// Native method for Grace match - /// Current interpreter - /// Receiver of the method - /// Target of the match - private static GraceObject mMatch( - EvaluationContext ctx, - GraceNumber self, - GraceObject target - ) - { - if (mEqualsEquals(self, target) == GraceBoolean.True) - { - return Matching.SuccessfulMatch(ctx, target); - } - return Matching.FailedMatch(ctx, target); - } - /// Native method for Grace numerator private static GraceObject mNumerator(GraceNumber self) { diff --git a/GraceLanguage/Runtime/GraceObject.cs b/GraceLanguage/Runtime/GraceObject.cs index e54851a..bb38251 100644 --- a/GraceLanguage/Runtime/GraceObject.cs +++ b/GraceLanguage/Runtime/GraceObject.cs @@ -163,6 +163,7 @@ private void initialise(bool defaults) AddMethod("asString", null); AddMethod("==(_)", null); AddMethod("!=(_)", null); + AddMethod("hash", null); } } @@ -218,6 +219,15 @@ private static GraceObject mNotEquals(EvaluationContext ctx, return GraceBoolean.Create(!object.ReferenceEquals(self, other)); } + /// Native method supporting Grace hash + /// Current interpreter + /// Receiver + private static GraceObject mHash(EvaluationContext ctx, + GraceObject self) + { + return GraceNumber.Create(RuntimeHelpers.GetHashCode(self)); + } + /// Remove a method from this object /// Method name to remove public virtual void RemoveMethod(string m) @@ -289,6 +299,9 @@ protected virtual Method getLazyMethod(string name) case "!=(_)": m = new DelegateMethodReceiver1Ctx(mNotEquals); return m; + case "hash": + m = new DelegateMethodReceiver0Ctx(mHash); + return m; } return null; } @@ -383,6 +396,23 @@ public bool HasFlag(Flags f) /// The singleton uninherited parent object public static readonly GraceObject UninheritedParent = new GraceObject("ParentNotInheritedYet", true); + + public int GetHashCode(EvaluationContext ctx) + // TODO: Proper error if result is not a Number! + => this.Request(ctx, MethodRequest.Nullary("hash")).FindNativeParent().GetInt(); + + public bool Equals(EvaluationContext ctx, GraceObject other) + // TODO: Proper error if result is not a boolean! + => this.Request(ctx, MethodRequest.Single("==", other)).FindNativeParent().Boolean; + + public class EqualityComparer : IEqualityComparer + { + EvaluationContext ctx; + public EqualityComparer(EvaluationContext ctx) { this.ctx = ctx; } + public bool Equals(GraceObject self, GraceObject other) => self.Equals(ctx, other); + public int GetHashCode(GraceObject self) => self.GetHashCode(ctx); + } + } } diff --git a/GraceLanguage/Runtime/GraceSequence.cs b/GraceLanguage/Runtime/GraceSequence.cs new file mode 100755 index 0000000..81118d6 --- /dev/null +++ b/GraceLanguage/Runtime/GraceSequence.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Grace.Execution; + +namespace Grace.Runtime +{ + public class GraceSequence : GraceObject + { + /// + /// Execute a block of native code for each element + /// of an iterable. + /// + /// Interpreter to use + /// Iterable to loop over + /// + /// Block of code to execute. + /// + public static void ForEach( + EvaluationContext ctx, + GraceObject iterable, + GraceObject block + ) + { + var req = MethodRequest.Single("do", block); + iterable.Request(ctx, req); + } + + private List elements = new List(); + + /// Empty list + public GraceSequence() + { + AddMethod("==(_)", Callback.Unary(mEquals)); + AddMethod("indexOf(_)", Callback.Unary(mIndexOf)); + AddMethod("do(_)", Callback.Unary(mDo)); + AddMethod("do(_) separatedBy(_)", Callback.UnaryUnary(mDoSeparatedBy)); + AddMethod("map(_)", Callback.Unary(mMap)); + AddMethod("++(_)", Callback.Unary(mConcat)); + AddMethod("with(_) do(_)", Callback.UnaryUnary(mWithDo)); + AddMethod("size", Callback.Nullary((ctx, self) => GraceNumber.Create(self.elements.Count))); + AddMethod("sizeIfUnknown(_)", Callback.Unary((ctx, self, arg) => GraceNumber.Create(self.elements.Count))); + AddMethod("first", Callback.Nullary((ctx, self) => self.elements.First())); + AddMethod("last", Callback.Nullary((ctx, self) => self.elements.Last())); + AddMethod("indices", Callback.Nullary((ctx, self) => Of(Enumerable.Range(1, self.elements.Count).Select((x) => GraceNumber.Create(x))))); + AddMethod("hash", Callback.Nullary((ctx, self) => + // This is the same algorithm as minigrace's standard grace + GraceNumber.Create(self.elements.Aggregate(0x5E0EACE, (result, next) => (result * 2) ^ next.GetHashCode(ctx))))); + AddMethod("at(_)", Callback.Unary(mAt)); + AddMethod("contains(_)", Callback.Unary((ctx, self, val) => GraceBoolean.Create(self.elements.Contains(val, new EqualityComparer(ctx))))); + TagName = "Sequence"; + } + // Mostly Copied from GraceString + private static GraceObject mAt(EvaluationContext ctx, GraceSequence self, GraceNumber index) + { + int idx = index.GetInt() - 1; + if (idx >= self.elements.Count || idx < 0) + ErrorReporting.RaiseError(ctx, "R2013", + new Dictionary { + { "index", "" + (idx + 1) }, + { "valid", self.elements.Count > 0 ? + "1 .. " + self.elements.Count + : "none (empty)" } + }, "Index must be a number"); + return self.elements[idx]; + } + + /// + /// List of particular items. + /// + /// Enumerable of items to use + public static GraceSequence Of(IEnumerable items) + { + var ret = new GraceSequence(); + foreach (var it in items) + ret.Add(it); + return ret; + } + + /// Add an object to the list + /// Object to add + public void Add(GraceObject obj) + { + elements.Add(obj); + } + + /// Native method for Grace ++ + /// Current interpreter + /// Second iterable to concatenate + public static GraceObject mConcat(EvaluationContext ctx, GraceSequence self, GraceObject other) + { + var res = new GraceSequence(); + res.elements = new List(self.elements); + // TODO: Something more efficient? + ForEach(ctx, other, GraceBlock.Create((elem) => res.elements.Add(elem))); + return res; + } + + /// Native method for Grace do + /// Current interpreter + /// Block to apply for each element + public static GraceObject mDo(EvaluationContext ctx, GraceSequence self, GraceObject block) + { + var req = MethodRequest.Single("apply", null); + foreach (var o in self.elements) + { + req[0].Arguments[0] = o; + block.Request(ctx, req); + } + return GraceObject.Done; + } + + /// Native method for Grace do separatedBy + /// Current interpreter + /// Block to apply for each element + /// Block to apply between each element + public static GraceObject mDoSeparatedBy(EvaluationContext ctx, GraceSequence self, GraceObject block, GraceObject sep_block) + { + var req = MethodRequest.Single("apply", null); + var sep_req = MethodRequest.Nullary("apply"); + bool first = true; + foreach (var o in self.elements) + { + if (first) { first = false; } + else { sep_block.Request(ctx, sep_req); } + req[0].Arguments[0] = o; + block.Request(ctx, req); + } + return GraceObject.Done; + } + + /// Native method for Grace map (I'm too lazy to make this lazy like minigrace's) + /// Current interpreter + /// Block to apply for each element + public static GraceObject mMap(EvaluationContext ctx, GraceSequence self, GraceObject block) + { + var req = MethodRequest.Single("apply", null); + var res = new GraceSequence(); + res.elements.Capacity = self.elements.Count; // For efficiency + foreach (var o in self.elements) + { + req[0].Arguments[0] = o; + res.elements.Add(block.Request(ctx, req)); + } + return res; + } + + + /// Native method for Grace equals + /// Current interpreter + /// Second iterable to compare + public static GraceObject mEquals(EvaluationContext ctx, GraceSequence self, GraceObject other) + { + if (!other.RespondsTo(MethodRequest.Single("do", null))) + return GraceBoolean.False; + var others = new List(); // Compute each element of other + ForEach(ctx, other, GraceBlock.Create((elem) => others.Add(elem))); + if (self.elements.Count != others.Count) + return GraceBoolean.False; + + // Is element-wise equality correct? Or should a sequence only equal another sequence (and not say a mutable list) + for (int i = 0; i < self.elements.Count; i++) + if (!self.elements[i].Equals(ctx, others[i])) + return GraceBoolean.False; + return GraceBoolean.True; + } + + + /// Native method for Grace indexOf + /// Current interpreter + /// Element to find + public static GraceObject mIndexOf(EvaluationContext ctx, GraceSequence self, GraceObject other) + { + // Is element-wise equality correct? Or should a sequence only equal another sequence (and not say a mutable list) + for (int i = 0; i < self.elements.Count; i++) + if (self.elements[i].Equals(ctx, other)) + return GraceNumber.Create(i + 1); + return GraceNumber.Create(0); // Not found + } + + private static GraceObject mWithDo(EvaluationContext ctx, GraceSequence self, GraceObject with, GraceObject block) + { + var withBlock = new WithBlock(self.elements, block); + var innerReq = MethodRequest.Single("do", withBlock); + with.Request(ctx, innerReq); + return GraceObject.Done; + } + + private class WithBlock : GraceObject + { + private List _elements; + private GraceObject _block; + private int index; + + public WithBlock(List elements, + GraceObject block) + { + _elements = elements; + _block = block; + AddMethod("apply(_)", + new DelegateMethod1Ctx( + new NativeMethod1Ctx(this.apply))); + } + + private GraceObject apply(EvaluationContext ctx, + GraceObject arg) + { + if (index >= _elements.Count) + return GraceObject.Done; + var el = _elements[index++]; + var req = new MethodRequest(); + var rpn = new RequestPart("apply", + new List(), + new List() { + el, arg + } + ); + req.AddPart(rpn); + _block.Request(ctx, req); + return GraceObject.Done; + } + } + } +} diff --git a/GraceLanguage/Runtime/GraceString.cs b/GraceLanguage/Runtime/GraceString.cs old mode 100644 new mode 100755 index 4f8c3e8..a31f26d --- a/GraceLanguage/Runtime/GraceString.cs +++ b/GraceLanguage/Runtime/GraceString.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Globalization; using System.Text; using System.Collections.Generic; @@ -105,8 +106,8 @@ private static Dictionary createSharedMethods() { { "==(_)", new DelegateMethodTyped1(mEqualsEquals) }, { "!=(_)", new DelegateMethodTyped1(mNotEquals) }, - { "++(_)", - new DelegateMethodTyped1Ctx(mConcatenate) }, + { "++(_)", new DelegateMethodTyped1Ctx(mConcatenate) }, + { "*(_)", Callback.Unary((ctx, self, arg) => Create(String.Concat(Enumerable.Repeat(self.Value, arg.GetInt())))) }, { ">(_)", new DelegateMethodTyped1(mGreaterThan) }, { ">=(_)", new DelegateMethodTyped1(mGreaterThanEqual) }, @@ -121,7 +122,14 @@ private static Dictionary createSharedMethods() new DelegateMethodTyped(substringFromTo) }, { "codepoints", new DelegateMethodTyped0(mCodepoints) }, - { "match(_)", new DelegateMethodTyped1Ctx(mMatch) }, + { "replace(_) with(_)", + Callback.UnaryUnary((ctx, self, arg1, arg2) => Create(self.Value.Replace(arg1.Value, arg2.Value)))}, + { "split(_)", + Callback.Unary((ctx, self, arg) => // TODO: Is the behaviour of .Net's split method the same as minigraces? + GraceSequence.Of(self.Value.Split(new[]{arg.Value}, StringSplitOptions.None).Select(Create))) }, + { "match(_)", Callback.Unary((ctx, self, target) => + mEqualsEquals(self, target) == GraceBoolean.True ? Matching.SuccessfulMatch(ctx, target) : Matching.FailedMatch(ctx, target)) }, + { "matches(_)", Callback.Unary((ctx, self, target) => mEqualsEquals(self, target))}, { "hash", new DelegateMethodTyped0(mHash) }, { "|(_)", Matching.OrMethod }, { "&(_)", Matching.AndMethod }, @@ -140,7 +148,7 @@ public static void ExtendWith(IDictionary meths) if (sharedMethods == null) createSharedMethods(); foreach (var m in meths) - sharedMethods[m.Key] = m.Value; + sharedMethods[m.Key] = m.Value; } private static GraceObject mConcatenate(EvaluationContext ctx, @@ -368,16 +376,6 @@ private static GraceObject mSize(GraceString self) return GraceNumber.Create(self.graphemeIndices.Length); } - private static GraceObject mMatch( - EvaluationContext ctx, - GraceString self, - GraceObject target) - { - return (mEqualsEquals(self, target) == GraceBoolean.True) - ? Matching.SuccessfulMatch(ctx, target) - : Matching.FailedMatch(ctx, target); - } - private static GraceObject mAsString(GraceString self) { return self; diff --git a/GraceLanguage/Runtime/GraceType.cs b/GraceLanguage/Runtime/GraceType.cs old mode 100644 new mode 100755 index 23afbfb..7a8de9f --- a/GraceLanguage/Runtime/GraceType.cs +++ b/GraceLanguage/Runtime/GraceType.cs @@ -15,8 +15,10 @@ public class GraceType : GraceObject public GraceType(string name) { this.name = name; - AddMethod("match(_)", new DelegateMethod1Ctx( - new NativeMethod1Ctx(this.Match))); + AddMethod("match(_)", Callback.Unary((ctx, self, target) => + self.DoesMatch(ctx, target) ? Matching.SuccessfulMatch(ctx, target) : Matching.FailedMatch(ctx, target))); + AddMethod("matches(_)", Callback.Unary((ctx, self, target) => + GraceBoolean.Create(self.DoesMatch(ctx, target)))); AddMethod("|(_)", Matching.OrMethod); AddMethod("&(_)", Matching.AndMethod); } @@ -28,11 +30,10 @@ public void Add(SignatureNode n) methods.Add(n); } - /// Native method implementing the .match Grace method - /// for types + /// method used for both the .matches and .match grace methods /// At present, this matching uses only the method /// names in both the object and the type. - public GraceObject Match(EvaluationContext ctx, GraceObject target) + public bool DoesMatch(EvaluationContext ctx, GraceObject target) { if (requests == null) { @@ -52,9 +53,16 @@ public GraceObject Match(EvaluationContext ctx, GraceObject target) foreach (var req in requests) { if (!target.RespondsTo(req)) - return Matching.FailedMatch(ctx, target); + return false; } - return Matching.SuccessfulMatch(ctx, target); + return true; + } + + /// Native method implementing the .match Grace method + /// for types + public GraceObject Matches(EvaluationContext ctx, GraceObject target) + { + return DoesMatch(ctx, target) ? Matching.SuccessfulMatch(ctx, target) : Matching.FailedMatch(ctx, target); } /// diff --git a/GraceLanguage/Runtime/Iterables.cs b/GraceLanguage/Runtime/Iterables.cs deleted file mode 100644 index e1cdb75..0000000 --- a/GraceLanguage/Runtime/Iterables.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Grace.Execution; - -namespace Grace.Runtime -{ - /// Encapsulates behaviour relating to iterables - public static class Iterables - { - /// - /// Execute a block of native code for each element - /// of an iterable. - /// - /// Interpreter to use - /// Iterable to loop over - /// - /// Block of code to execute. - /// - public static void ForEach( - EvaluationContext ctx, - GraceObject iterable, - GraceObject block - ) - { - var req = MethodRequest.Single("do", block); - iterable.Request(ctx, req); - } - - /// Create an iterable from the concatenation of - /// two existing iterables - /// First iterable - /// Second iterable - public static GraceObject Concatenate(GraceObject left, - GraceObject right) - { - return new Concatenated(left, right); - } - - /// Iterable that iterates through one iterable - /// followed by another - public class Concatenated : GraceObject - { - private GraceObject left, right; - - /// First iterable - /// Second iterable - public Concatenated(GraceObject l, GraceObject r) - { - left = l; - right = r; - AddMethod("do(_)", - new DelegateMethod1Ctx( - new NativeMethod1Ctx(this.Do))); - AddMethod("++(_)", Iterables.ConcatMethod); - TagName = "ConcatenatedIterables"; - } - - /// Native method for Grace ++ - /// Current interpreter - /// Second iterable to concatenate - public GraceObject Concat(EvaluationContext ctx, GraceObject other) - { - return Iterables.Concatenate(this, other); - } - - /// Native method for Grace do - /// Current interpreter - /// Block to apply for each element - public GraceObject Do(EvaluationContext ctx, GraceObject block) - { - var req = MethodRequest.Single("do", block); - left.Request(ctx, req); - right.Request(ctx, req); - return GraceObject.Done; - } - } - - /// Reusable native method for concatenation - /// Current interpreter - /// Object on which the method was - /// requested - /// Argument to the method - public static GraceObject MConcat(EvaluationContext ctx, - GraceObject receiver, - GraceObject other) - { - return Iterables.Concatenate(receiver, other); - } - - /// Reusable method for ++ - public static readonly Method ConcatMethod = new DelegateMethodReceiver1Ctx( - new NativeMethodReceiver1Ctx(Iterables.MConcat) - ); - } - - /// The iterable created by *variadic parameters - public class GraceVariadicList : GraceObject - { - private List elements = new List(); - - /// Empty list - public GraceVariadicList() - { - AddMethod("do(_)", - new DelegateMethod1Ctx( - new NativeMethod1Ctx(this.Do))); - AddMethod("++(_)", Iterables.ConcatMethod); - AddMethod("with(_) do(_)", - new DelegateMethodReq( - new NativeMethodReq(this.WithDo))); - TagName = "Lineup"; - } - - /// - /// List of particular items. - /// - /// Enumerable of items to use - public static GraceVariadicList Of(IEnumerable items) - { - var ret = new GraceVariadicList(); - foreach (var it in items) - ret.Add(it); - return ret; - } - - /// Add an object to the list - /// Object to add - public void Add(GraceObject obj) - { - elements.Add(obj); - } - - /// Native method for Grace ++ - /// Current interpreter - /// Second iterable to concatenate - public GraceObject Concat(EvaluationContext ctx, GraceObject other) - { - return Iterables.Concatenate(this, other); - } - - /// Native method for Grace do - /// Current interpreter - /// Block to apply for each element - public GraceObject Do(EvaluationContext ctx, GraceObject block) - { - var req = MethodRequest.Single("apply", null); - foreach (var o in elements) - { - req[0].Arguments[0] = o; - block.Request(ctx, req); - } - return GraceObject.Done; - } - - private GraceObject WithDo(EvaluationContext ctx, MethodRequest req) - { - var with = req[0].Arguments[0]; - var block = req[1].Arguments[0]; - var withBlock = new WithBlock(elements, block); - var innerReq = MethodRequest.Single("do", withBlock); - with.Request(ctx, innerReq); - return GraceObject.Done; - } - - private class WithBlock : GraceObject - { - private List _elements; - private GraceObject _block; - private int index; - - public WithBlock(List elements, - GraceObject block) { - _elements = elements; - _block = block; - AddMethod("apply(_)", - new DelegateMethod1Ctx( - new NativeMethod1Ctx(this.apply))); - } - - private GraceObject apply(EvaluationContext ctx, - GraceObject arg) - { - if (index >= _elements.Count) - return GraceObject.Done; - var el = _elements[index++]; - var req = new MethodRequest(); - var rpn = new RequestPart("apply", - new List(), - new List() { - el, arg - } - ); - req.AddPart(rpn); - _block.Request(ctx, req); - return GraceObject.Done; - } - } - } - -} diff --git a/GraceLanguage/Runtime/Matching.cs b/GraceLanguage/Runtime/Matching.cs old mode 100644 new mode 100755 index 0a98afe..0f28e99 --- a/GraceLanguage/Runtime/Matching.cs +++ b/GraceLanguage/Runtime/Matching.cs @@ -103,7 +103,7 @@ public static GraceObject AndCombinator(EvaluationContext ctx, GraceObject self, GraceObject other) { var patReq = new MethodRequest(); - patReq.AddPart(new RequestPart("_OrPattern", + patReq.AddPart(new RequestPart("_AndPattern", RequestPart.EmptyList, new List { self, other })); GraceObject patRec = ctx.FindReceiver(patReq); diff --git a/GraceLanguage/prelude.grace b/GraceLanguage/prelude.grace index a189a2d..e806a19 100644 --- a/GraceLanguage/prelude.grace +++ b/GraceLanguage/prelude.grace @@ -203,6 +203,7 @@ method _OrPattern(l, r) { } _FailedMatch(o) } + method matches(o) { l.matches(o) || {r.matches(o)} } method |(o) { _OrPattern(self, o) } @@ -222,6 +223,7 @@ method _AndPattern(l, r) { } return mr } + method matches(o) { l.matches(o) && {r.matches(o)} } method |(o) { _OrPattern(self, o) }