/// <summary>Load a module file</summary> private GraceObject loadNativeModule(string path) { Interpreter.Debug("========== LOAD " + path + " =========="); var dll = Assembly.LoadFile(path); foreach (var t in dll.GetExportedTypes()) { foreach (var a in t.GetCustomAttributes(false)) { if (a is ModuleEntryPoint) { var m = t.GetMethod("Instantiate"); return((GraceObject)m.Invoke(null, new Object[1] { this })); } } } ErrorReporting.RaiseError(this, "R2005", new Dictionary <string, string> { { "path", path } }, "LookupError: Could not find module ${path}"); return(null); }
/// <inheritdoc/> public override GraceObject Respond( EvaluationContext ctx, GraceObject self, MethodRequest req ) { checkAccessibility(ctx, req); var arg = req[1].Arguments[0]; if (Type != null) { if (Matching.TryMatch(ctx, Type, arg, out GraceObject result)) { arg = result; } else { ErrorReporting.RaiseError(ctx, "R2025", new Dictionary <string, string> { { "field", req[0].Name }, { "required", GraceString.AsNativeString(ctx, Type) } }, "TypeError: ${field} can only hold ${required}."); } } var tmp = cell.Value; cell.Value = arg; return(tmp); }
private static GraceObject mAt(EvaluationContext ctx, MethodRequest req, StringCodepoints self) { MethodHelper.CheckArity(ctx, req, 1); var oth = req[0].Arguments[0].FindNativeParent <GraceNumber>(); if (oth == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", req.Name }, { "index", "1" }, { "part", req.Name }, { "required", "Number" }, }, "ArgumentTypeError: Index must be a number"); } int idx = oth.GetInt() - 1; if (idx >= self.codepoints.Length || idx < 0) { ErrorReporting.RaiseError(ctx, "R2013", new Dictionary <string, string> { { "index", "" + (idx + 1) }, { "valid", self.codepoints.Length > 0 ? "1 .. " + self.codepoints.Length : "none (empty)" } }, "IndexError: Index out of range"); } if (self.codepoints[idx] == null) { self.codepoints[idx] = CodepointObject.Create(self.utf32[idx]); } return(self.codepoints[idx]); }
/// <summary>Native method for Grace ||</summary> /// <param name="ctx">Current interpreter</param> /// <param name="other">Argument to the method</param> public GraceObject OrOr(EvaluationContext ctx, GraceObject other) { GraceBoolean oth = other as GraceBoolean; if (oth != null) { return(GraceBoolean.Create(this.Boolean || oth.Boolean)); } GraceObjectProxy op = other as GraceObjectProxy; if (op != null) { return(GraceBoolean.Create(this.Boolean || (dynamic)op.Object)); } ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string>() { { "method", "||" }, { "index", "1" }, { "part", "||" }, { "required", "Boolean" } }, "ArgumentTypeError: || requires a Boolean argument" ); return(GraceBoolean.False); }
private static GraceObject mAt( EvaluationContext ctx, GraceString self, GraceObject other ) { var oth = other.FindNativeParent <GraceNumber>(); if (oth == null) { return(GraceString.Create("bad index")); } int idx = oth.GetInt() - 1; if (idx >= self.graphemeIndices.Length || idx < 0) { ErrorReporting.RaiseError(ctx, "R2013", new Dictionary <string, string> { { "index", "" + (idx + 1) }, { "valid", self.graphemeIndices.Length > 0 ? "1 .. " + self.graphemeIndices.Length : "none (empty)" } }, "Index must be a number"); } int start = self.graphemeIndices[idx]; return(GraceString.Create( StringInfo.GetNextTextElement(self.Value, start))); }
/// <summary>Confirm that this method can be accessed through /// the given request in this context</summary> /// <remarks>If this method is confidential and the request is /// not an interior one with privileged access, this method /// will raise a Grace exception reporting an accessibility /// violation.</remarks> /// <param name="ctx">Current interpreter</param> /// <param name="req">Request to check</param> protected virtual void checkAccessibility(EvaluationContext ctx, MethodRequest req) { if (Conflict) { ErrorReporting.RaiseError(ctx, "R2022", new Dictionary <string, string>() { { "method", req.Name } }, "InheritanceError: Method ${method} is a conflict." ); } if (Abstract) { ErrorReporting.RaiseError(ctx, "R2021", new Dictionary <string, string>() { { "method", req.Name } }, "InheritanceError: Method ${method} is abstract." ); } if (Confidential && !req.IsInterior) { ErrorReporting.RaiseError(ctx, "R2003", new Dictionary <string, string>() { { "method", req.Name } }, "AccessibilityError: Method ${method} is confidential" ); } }
private static GraceObject substringFromTo( EvaluationContext ctx, MethodRequest req, GraceString self ) { MethodHelper.CheckArity(ctx, req, 1, 1); // Index of first grapheme to include. var start = req[0].Arguments[0]; // Index of last grapheme to include. var end = req[1].Arguments[0]; var st = start.FindNativeParent <GraceNumber>(); if (st == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", req.Name }, { "index", "1" }, { "part", "substringFrom" } }, "Start must be a number"); } var en = end.FindNativeParent <GraceNumber>(); if (en == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", req.Name }, { "index", "1" }, { "part", "to" } }, "End must be a number"); } // Because, e.g., substringFrom(1) to(1) should return the // first grapheme, the start value must be adjusted for // base-one indexing, but the end value must not be. int stInd = st.GetInt() - 1; int enInd = en.GetInt(); if (stInd < 0) { stInd = 0; } if (enInd < 0) { enInd = 0; } if (enInd >= self.graphemeIndices.Length) { enInd = self.graphemeIndices.Length; } int endIndex = enInd < self.graphemeIndices.Length ? self.graphemeIndices[enInd] : self.Value.Length; stInd = self.graphemeIndices[stInd]; return(GraceString.Create(self.Value.Substring(stInd, endIndex - stInd))); }
/// <summary> /// Raise an error if the user is trying to inherit from /// this method. /// </summary> /// <param name="ctx">Current interpreter</param> /// <param name="req">Method request to check</param> public static void CheckNoInherits(EvaluationContext ctx, MethodRequest req) { if (req.IsInherits) { ErrorReporting.RaiseError(ctx, "R2017", new Dictionary <string, string> { { "method", req.Name } }, "InheritanceError: Invalid inheritance" ); } }
/// <inheritdoc /> public int NestRequest(string module, int line, string name) { if (callStackMethod.Count > 511) { ErrorReporting.RaiseError(this, "R2024", new Dictionary <string, string>(), "RecursionError: maximum call stack size exceeded." ); } callStackMethod.Push(name); callStackModule.Push(module); callStackLine.Push(line); return(callStackMethod.Count - 1); }
/// <summary> /// Load a resource file /// </summary> /// <param name="filePath">Filesystem path to resource</param> /// <param name="importPath">Import path used to reach resource</param> private GraceObject loadResource(string filePath, string importPath) { var ext = Path.GetExtension(importPath); if (ext == ".txt") { return(GraceString.Create(File.OpenText(filePath).ReadToEnd())); } ErrorReporting.RaiseError(this, "R2010", new Dictionary <string, string> { { "path", importPath }, { "extension", ext } }, "LookupError: No resource handler for ${importPath}"); return(null); }
private static void checkIndex( EvaluationContext ctx, int i, GraceObject[] data ) { if (i < 0 || i >= data.Length) { ErrorReporting.RaiseError(ctx, "R2013", new Dictionary <string, string> { { "index", "" + i }, { "valid", "0.." + (data.Length - 1) } }, "IndexError: Invalid index " + i ); } }
/// <summary> /// Attempt to match a pattern, raising R2025 TypeError on failure. /// </summary> /// <param name="ctx">Current interpreter</param> /// <param name="pattern">Pattern to match against</param> /// <param name="target">Object to examine</param> /// <param name="name">Name to report in error (e.g. field or parameter name)</param> /// <returns></returns> public static GraceObject TypeMatch(EvaluationContext ctx, GraceObject pattern, GraceObject target, string name) { if (Matching.TryMatch(ctx, pattern, target, out var result)) { return(result); } else { ErrorReporting.RaiseError(ctx, "R2025", new Dictionary <string, string> { { "field", name }, { "required", GraceString.AsNativeString(ctx, pattern) } }, "TypeError: argument type mismatch"); return(null); } }
/// <summary>Native method for Grace /</summary> /// <param name="ctx">Current interpreter</param> /// <param name="self">Receiver of the method</param> /// <param name="other">Argument to the method</param> private static GraceObject mDivide( EvaluationContext ctx, GraceNumber self, GraceObject other ) { var oth = other.FindNativeParent <GraceNumber>(); if (oth.Value == Rational.Zero) { ErrorReporting.RaiseError(ctx, "R2012", new Dictionary <string, string> { { "dividend", self.Value.ToString() }, }, "ZeroDivisionError: Division by zero."); } return(GraceNumber.Create(self.Value / oth.Value)); }
private GraceObject mDotDot(EvaluationContext ctx, GraceObject step) { var n = step.FindNativeParent <GraceNumber>(); if (n == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", ".." }, { "index", "1" }, { "part", ".." }, { "required", "Number" } }, "ArgumentTypeError: .. requires a Number argument" ); } return(new GraceRange(_low, _high, _step * n.Value)); }
private static GraceObject mConcat(EvaluationContext ctx, MethodRequest req, StringCodepoints self) { MethodHelper.CheckArity(ctx, req, 1); var oth = req[0].Arguments[0].FindNativeParent <StringCodepoints>(); if (oth == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", req.Name }, { "index", "1" }, { "part", req.Name }, { "required", "codepoints" }, }, "ArgumentTypeError: Needed codepoints object"); } return(new StringCodepoints(self.utf32.Concat(oth.utf32))); }
/// <summary>Return a value from this method</summary> /// <param name="ctx">Current interpreter</param> /// <param name="val">Return value</param> /// <param name="ret">Return statement</param> /// <exception cref="ReturnException">Always thrown to indicate a /// return from a Grace method</exception> public void Return(EvaluationContext ctx, GraceObject val, Node ret) { if (completed) { // Put the return statement into the call stack // so that its location is shown to the user, even // though it isn't actually a method request. ctx.NestRequest(ret.Location.Module, ret.Location.line, "return"); ErrorReporting.RaiseError(ctx, "R2009", new Dictionary <string, string> { { "method", Name } }, "IllegalReturnError: Method «" + Name + "» already returned"); } completed = true; throw new ReturnException(this, val); }
private GraceObject mAt(EvaluationContext ctx, GraceObject index) { var num = index.FindNativeParent <GraceNumber>(); var i = (int)num.Double; checkIndex(ctx, i, data); var ret = data[i]; if (ret == null) { ErrorReporting.RaiseError(ctx, "R2008", new Dictionary <string, string> { { "name", "index " + i }, { "receiver", ToString() } }, "UninitialisedReadError: Cannot read from index " + i ); } return(ret); }
/// <summary> /// Request a method of this object in a given /// context with a particular receiver identity /// </summary> /// <param name="ctx">Current interpreter</param> /// <param name="req">Request</param> /// <param name="receiver">Receiver identity</param> /// <returns>Return value of the resolved method</returns> public virtual GraceObject Request(EvaluationContext ctx, MethodRequest req, GraceObject receiver) { var m = FindMethod(req.Name); if (m == null) { ErrorReporting.RaiseError(ctx, "R2000", new Dictionary <string, string> { { "method", req.Name }, { "receiver", ToString() } }, "LookupError: Method «" + req.Name + "» not found."); } var ret = m.Respond(ctx, receiver, req); return(ret); }
/// <summary>Native method for Grace ifTrue ifFalse</summary> /// <param name="ctx">Current interpreter</param> /// <param name="req">Method request that gave rise to this method /// execution</param> public GraceObject IfTrueIfFalse(EvaluationContext ctx, MethodRequest req) { MethodHelper.CheckArity(ctx, req, 1, 1); var trueBlock = req[0].Arguments[0]; var falseBlock = req[1].Arguments[0]; if (!(trueBlock is GraceBlock)) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string>() { { "method", req.Name }, { "index", "1" }, { "part", "ifTrue" }, { "required", "Block" } }, "ArgumentTypeError: ifTrue ifFalse requires two block arguments" ); } if (!(falseBlock is GraceBlock)) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string>() { { "method", req.Name }, { "index", "1" }, { "part", "ifFalse" }, { "required", "Block" } }, "ArgumentTypeError: ifTrue ifFalse requires two block arguments" ); } var apply = MethodRequest.Nullary("apply"); if (Boolean) { return(trueBlock.Request(ctx, apply)); } return(falseBlock.Request(ctx, apply)); }
private static GraceObject mAt(EvaluationContext ctx, MethodRequest req, UTF32CodepointsView self) { MethodHelper.CheckArity(ctx, req, 1); var arg = req[0].Arguments[0]; var index = arg.FindNativeParent <GraceNumber>(); var idx = index.GetInt() - 1; if (idx < 0 || idx >= self.utf32.Count) { ErrorReporting.RaiseError(ctx, "R2013", new Dictionary <string, string> { { "index", "" + (idx + 1) }, { "valid", self.utf32.Count > 0 ? "1 .. " + self.utf32.Count : "none (empty)" } }, "IndexError: Index out of range"); } return(GraceNumber.Create(self.utf32[idx])); }
private static GraceObject mAt(EvaluationContext ctx, MethodRequest req, ByteString self) { MethodHelper.CheckArity(ctx, req, 1); var arg = req[0].Arguments[0]; var index = arg.FindNativeParent <GraceNumber>(); var idx = index.GetInt() - 1; if (idx < 0 || idx >= self.data.Length) { ErrorReporting.RaiseError(ctx, "R2013", new Dictionary <string, string> { { "index", "" + (idx + 1) }, { "valid", self.data.Length > 0 ? "1 .. " + self.data.Length : "none (empty)" } }, "IndexError: Index out of range"); } return(getByteObject(self.data[idx])); }
/// <summary>Native method for Grace ..</summary> /// <param name="ctx">Current interpreter</param> /// <param name="self">Receiver of the method</param> /// <param name="other">Argument to the method</param> private static GraceObject mDotDot( EvaluationContext ctx, GraceNumber self, GraceObject other ) { var n = other.FindNativeParent <GraceNumber>(); if (n == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", ".." }, { "index", "1" }, { "part", ".." }, { "required", "Number" } }, "ArgumentTypeError: .. requires a Number argument" ); } return(new GraceRange(self.Value, n.Value, 1)); }
/// <inheritdoc/> public override GraceObject Respond( EvaluationContext ctx, GraceObject self, MethodRequest req ) { checkAccessibility(ctx, req); MethodHelper.CheckNoInherits(ctx, req); MethodNode.CheckArgCount(ctx, req.Name, req.Name, 0, false, req[0].Arguments.Count); if (cell.Value == GraceObject.Uninitialised) { ErrorReporting.RaiseError(ctx, "R2008", new Dictionary <string, string> { { "name", req.Name }, { "receiver", self.ToString() } }, "UninitialisedReadError: Cannot read from " + req.Name ); } return(cell.Value); }
private static GraceObject mConcat(EvaluationContext ctx, MethodRequest req, ByteString self) { MethodHelper.CheckArity(ctx, req, 1); var oth = req[0].Arguments[0].FindNativeParent <ByteString>(); if (oth == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string> { { "method", req.Name }, { "index", "1" }, { "part", req.Name }, { "required", "byte string" }, }, "ArgumentTypeError: Needed byte string"); } var d2 = new byte[self.data.Length + oth.data.Length]; self.data.CopyTo(d2, 0); oth.data.CopyTo(d2, self.data.Length); return(new ByteString(d2)); }
/// <inheritdoc/> /// <remarks>This method uses the indexer on the LocalScope /// object the method was requested on.</remarks> public override GraceObject Respond(EvaluationContext ctx, GraceObject self, MethodRequest req) { checkAccessibility(ctx, req); MethodHelper.CheckNoInherits(ctx, req); MethodNode.CheckArgCount(ctx, req.Name, req.Name, 0, false, req[0].Arguments.Count); LocalScope s = self as LocalScope; string name = req.Name; if (s[name] == GraceObject.Uninitialised || s[name] == null) { ErrorReporting.RaiseError(ctx, "R2008", new Dictionary <string, string> { { "name", name }, { "receiver", ToString() } }, "UninitialisedReadError: Cannot read from «" + name + "»" ); } return(s[name]); }
/// <summary>Native method for Grace orElse</summary> /// <param name="ctx">Current interpreter</param> /// <param name="other">Block to apply if false</param> public GraceObject OrElse(EvaluationContext ctx, GraceObject other) { var oth = other as GraceBlock; if (oth == null) { ErrorReporting.RaiseError(ctx, "R2001", new Dictionary <string, string>() { { "method", "orElse" }, { "index", "1" }, { "part", "ifFalse" }, { "required", "Block" } }, "ArgumentTypeError: orElse requires a block argument" ); } if (!Boolean) { var req = MethodRequest.Nullary("apply"); return(other.Request(ctx, req)); } return(True); }
/// <inheritdoc /> /// <remarks>The import path will be resolved according to /// the directories given by /// <c cref="GetModulePaths">GetModulePaths</c>. If this import /// path has been loaded previously, the existing module object /// is returned.</remarks> public GraceObject LoadModule(string path) { if (modules.ContainsKey(path)) { return(modules[path]); } importStack.Push(path); if (importedPaths.Contains(path)) { var chain = String.Join(" -> ", importStack.Reverse()); ErrorReporting.RaiseError(this, "R2011", new Dictionary <string, string> { { "path", path }, { "chain", chain } }, "CyclicImportError: Module ${path} imports itself."); } importedPaths.Add(path); var name = Path.GetFileName(path); var isResource = name.Contains('.'); var bases = GetModulePaths(); try { foreach (var p in bases) { string filePath; GraceObject mod; if (isResource) { filePath = Path.Combine(p, path); mod = tryLoadResource(filePath, path); if (mod != null) { modules[path] = mod; return(mod); } continue; } filePath = Path.Combine(p, path + ".grace"); mod = tryLoadModuleFile(filePath, path); if (mod != null) { modules[path] = mod; return(mod); } filePath = Path.Combine(p, path + ".dll"); mod = tryLoadNativeModule(filePath); if (mod != null) { modules[path] = mod; return(mod); } } if (FailedImportHook != null) { // Optionally, the host program can try to satisfy a module // and indicate that we should retry the import. if (FailedImportHook(path, this)) { importedPaths.Remove(path); importStack.Pop(); return(LoadModule(path)); } } } finally { importStack.Pop(); } ErrorReporting.RaiseError(this, "R2005", new Dictionary <string, string> { { "path", path } }, "LookupError: Could not find module ${path}"); return(null); }
/// <inheritsdoc/> /// <remarks>Uses reflection to access the method, or the /// dynamic type to access operators.</remarks> public override GraceObject Request(EvaluationContext ctx, MethodRequest req) { string name = req.Name; switch (name) { case "isNull": if (obj == null) { return(GraceBoolean.True); } return(GraceBoolean.False); case "+(_)": return(GraceObjectProxy.Create((dynamic)obj + (dynamic)viewAsNative(req[0].Arguments[0]))); case "-(_)": return(GraceObjectProxy.Create((dynamic)obj - (dynamic)viewAsNative(req[0].Arguments[0]))); case "*(_)": return(GraceObjectProxy.Create((dynamic)obj * (dynamic)viewAsNative(req[0].Arguments[0]))); case "/(_)": return(GraceObjectProxy.Create((dynamic)obj / (dynamic)viewAsNative(req[0].Arguments[0]))); case "<(_)": return(GraceObjectProxy.Create((dynamic)obj < (dynamic)viewAsNative(req[0].Arguments[0]))); case "<=(_)": return(GraceObjectProxy.Create((dynamic)obj <= (dynamic)viewAsNative(req[0].Arguments[0]))); case ">(_)": return(GraceObjectProxy.Create((dynamic)obj > (dynamic)viewAsNative(req[0].Arguments[0]))); case ">=(_)": return(GraceObjectProxy.Create((dynamic)obj >= (dynamic)viewAsNative(req[0].Arguments[0]))); case "==(_)": return(GraceObjectProxy.Create((dynamic)obj == (dynamic)viewAsNative(req[0].Arguments[0]))); case "!=(_)": return(GraceObjectProxy.Create((dynamic)obj != (dynamic)viewAsNative(req[0].Arguments[0]))); case "%(_)": return(GraceObjectProxy.Create((dynamic)obj % (dynamic)viewAsNative(req[0].Arguments[0]))); case "^(_)": return(GraceObjectProxy.Create(Math.Pow((dynamic)obj, (dynamic)viewAsNative(req[0].Arguments[0])))); case "asString": if (obj == null) { return(GraceString.Create("(null)")); } return(GraceString.Create(obj.ToString())); case "prefix!": return(GraceObjectProxy.Create(!(dynamic)obj)); case "at(_)": if (Interpreter.JSIL) { // Calling get_Item directly is iffy, but // works on JSIL where accessing Item fails, // and [] uses native (the wrong) []. return(GraceObjectProxy.Create( ((dynamic)obj) .get_Item( (dynamic)viewAsNative(req[0].Arguments[0])) )); } return(GraceObjectProxy.Create( ((dynamic)obj)[ (dynamic)viewAsNative(req[0].Arguments[0])])); } object[] args = new object[req[0].Arguments.Count]; for (int i = 0; i < req[0].Arguments.Count; i++) { args[i] = viewAsNative(req[0].Arguments[i]); } Type[] types = new Type[args.Length]; for (int i = 0; i < types.Length; i++) { types[i] = args[i].GetType(); } MethodInfo meth = type.GetMethod(name, types); if (meth == null) { ErrorReporting.RaiseError(ctx, "R2000", new Dictionary <string, string>() { { "method", req.Name }, { "receiver", "Native Proxy" } }, "LookupError: Native proxy failed to find method «${method}»" ); } return(GraceObjectProxy.Create(meth.Invoke(obj, args))); }
/// <summary>Native version of the built-in Grace /// "try-*catch-?finally" method /// </summary> public static GraceObject BaseTryCatchFinally(EvaluationContext ctx, MethodRequest req) { Interpreter.ScopeMemo memo = ctx.Memorise(); GraceObject tryBlock; GraceObject finallyBlock; var catchBlocks = new List <GraceObject>(); tryBlock = req[0].Arguments[0]; finallyBlock = req[0].Arguments[1]; for (int i = 2; i < req[0].Arguments.Count; i++) { catchBlocks.Add(req[0].Arguments[i]); } var ret = GraceObject.Done; var caught = true; try { ret = tryBlock.Request(ctx, MethodRequest.Nullary("apply")); } catch (GraceExceptionPacketException e) { ctx.RestoreExactly(memo); GraceObject gep = e.ExceptionPacket; MethodRequest matchReq = MethodRequest.Single("match", gep); caught = false; foreach (var cb in catchBlocks) { var mr = cb.Request(ctx, matchReq); if (Matching.Succeeded(ctx, mr)) { caught = true; ret = mr.Request(ctx, MethodRequest.Nullary("result")); break; } } if (!caught) { throw; } } finally { ctx.RestoreExactly(memo); if (finallyBlock != null) { if (caught) { finallyBlock.Request(ctx, MethodRequest.Nullary("apply")); } else { try { finallyBlock.Request(ctx, MethodRequest.Nullary("apply")); } catch (ReturnException) { ErrorReporting.RaiseError(ctx, "R2018", new Dictionary <string, string>(), "IllegalReturnError: From completed finally"); } } } } return(ret); }