/// <summary> /// Map parameter names to positional names. This must match the expectations of the interpreter /// </summary> private static void ParameterPositions(Scope parameterNames, IEnumerable <string> paramNames, NanCodeWriter wr) { parameterNames.PushScope(); var i = 0; foreach (var paramName in paramNames) { if (parameterNames.InScope(NanTags.GetCrushedName(paramName))) { throw new Exception("Duplicate parameter '" + paramName + "'.\r\n" + "All parameter names must be unique in a single function definition."); } var originalReference = NanTags.GetCrushedName(paramName); var parameterReference = Scope.NameFor(i); var parameterByteCode = NanTags.EncodeVariableRef(parameterReference); parameterNames.SetValue(originalReference, parameterByteCode); wr.AddSymbol(originalReference, paramName); wr.AddSymbol(parameterReference, "param[" + i + "]"); i++; } }
static Scope() { posParamHash = new uint[128]; // this limits the number of parameters, so is quite high for (int i = 0; i < 128; i++) { posParamHash[i] = NanTags.GetCrushedName("__p" + i); } }
public static HashLookup <string> DebugSymbolsForBuiltIns() { var tmp = new HashLookup <string>(64); Action <string> add = name => { tmp.Add(NanTags.GetCrushedName(name), name); }; add("="); add("equals"); add(">"); add("<"); add("<>"); add("not-equal"); add("assert"); add("random"); add("eval"); add("call"); add("not"); add("or"); add("and"); add("readkey"); add("readline"); add("print"); add("substring"); add("length"); add("replace"); add("concat"); add("+"); add("-"); add("*"); add("/"); add("%"); add("()"); return(tmp); }
/// <summary> /// Symbol mapping for built-in functions /// </summary> public static HashLookup <FunctionDefinition> BuiltInFunctionSymbols() { var tmp = new HashLookup <FunctionDefinition>(64); Action <string, FuncDef> add = (name, type) => { tmp.Add(NanTags.GetCrushedName(name), new FunctionDefinition { Kind = type }); }; add("=", FuncDef.Equal); add("equals", FuncDef.Equal); add(">", FuncDef.GreaterThan); add("<", FuncDef.LessThan); add("<>", FuncDef.NotEqual); add("not-equal", FuncDef.NotEqual); add("assert", FuncDef.Assert); add("random", FuncDef.Random); add("eval", FuncDef.Eval); add("call", FuncDef.Call); add("not", FuncDef.LogicNot); add("or", FuncDef.LogicOr); add("and", FuncDef.LogicAnd); add("readkey", FuncDef.ReadKey); add("readline", FuncDef.ReadLine); add("print", FuncDef.Print); add("substring", FuncDef.Substring); add("length", FuncDef.Length); add("replace", FuncDef.Replace); add("concat", FuncDef.Concat); add("+", FuncDef.MathAdd); add("-", FuncDef.MathSub); add("*", FuncDef.MathProd); add("/", FuncDef.MathDiv); add("%", FuncDef.MathMod); add("()", FuncDef.UnitEmpty); // empty value marker return(tmp); }
/// <summary> /// Output atom values /// </summary> private static void EmitLeafNode(Node root, bool debug, Scope parameterNames, Context compileContext, NanCodeWriter wr) { double leafValue = 0; bool substitute = false; var valueName = root.Text; var nameHash = NanTags.GetCrushedName(valueName); if (parameterNames.CanResolve(nameHash)) { substitute = true; leafValue = parameterNames.Resolve(nameHash); } // An unwrapped variable name? if (IsUnwrappedIdentifier(valueName, root, compileContext)) { if (debug) { wr.Comment("// treating '" + valueName + "' as an implicit get()"); } if (substitute) { wr.Memory('g', leafValue); } else { wr.Memory('g', valueName, 0); } return; } if (debug) { wr.Comment("// Value : \"" + root + "\"\r\n"); if (substitute) { wr.Comment("// Parameter reference redefined as '" + valueName + "'\r\n"); } } switch (root.NodeType) { case NodeType.Numeric: wr.LiteralNumber(double.Parse(valueName.Replace("_", ""))); break; case NodeType.StringLiteral: wr.LiteralString(valueName); break; case NodeType.Atom: if (valueName == "true") { wr.LiteralInt32(-1); } else if (valueName == "false") { wr.LiteralInt32(0); } else if (substitute) { wr.RawToken(leafValue); } else { wr.VariableReference(valueName); } break; default: throw new Exception("Unexpected compiler state"); } }
private double EvaluateBuiltInFunction(ref int position, FuncDef kind, int nbParams, double[] param, Stack <int> returnStack, Stack <double> valueStack) { switch (kind) { // each element equal to the first case FuncDef.Equal: if (nbParams < 2) { throw new Exception("equals ( = ) must have at least two things to compare"); } return(NanTags.EncodeBool(ListEquals(param))); // Each element smaller than the last case FuncDef.GreaterThan: if (nbParams < 2) { throw new Exception("greater than ( > ) must have at least two things to compare"); } return(NanTags.EncodeBool(FoldGreaterThan(param))); // Each element larger than the last case FuncDef.LessThan: if (nbParams < 2) { throw new Exception("less than ( < ) must have at least two things to compare"); } return(NanTags.EncodeBool(FoldLessThan(param))); // Each element DIFFERENT TO THE FIRST (does not check set uniqueness!) case FuncDef.NotEqual: if (nbParams < 2) { throw new Exception("not-equal ( <> ) must have at least two things to compare"); } return(NanTags.EncodeBool(!ListEquals(param))); case FuncDef.Assert: if (nbParams < 1) { return(NanTags.VoidReturn()); // assert nothing passes } var condition = param.ElementAt(0); if (_memory.CastBoolean(condition) == false) { var msg = ConcatList(param, 1); throw new Exception("Assertion failed: " + msg); } return(NanTags.VoidReturn()); case FuncDef.Random: if (nbParams < 1) { return(rnd.NextDouble()); // 0 params - any size } if (nbParams < 2) { return(rnd.Next(_memory.CastInt(param.ElementAt(0)))); // 1 param - max size } return(rnd.Next(_memory.CastInt(param.ElementAt(0)), _memory.CastInt(param.ElementAt(1)))); // 2 params - range case FuncDef.Eval: var reader = new SourceCodeTokeniser(); var statements = _memory.CastString(param.ElementAt(0)); var programTmp = reader.Read(statements, false); var bin = ToNanCodeCompiler.CompileRoot(programTmp, false); var interpreter = new ByteCodeInterpreter(); interpreter.Init(new RuntimeMemoryModel(bin, _memory.Variables), _input, _output, DebugSymbols); return(interpreter.Execute(false, _runningVerbose, false).Result); case FuncDef.Call: NanTags.DecodePointer(param.ElementAt(0), out var target, out var type); if (type != DataType.PtrString && type != DataType.PtrStaticString) { throw new Exception("Tried to call a function by name, but passed a '" + type + "' at " + position); } // this should be a string, but we need a function name hash -- so calculate it: var strName = _memory.DereferenceString(target); var functionNameHash = NanTags.GetCrushedName(strName); nbParams--; var newParam = param.Skip(1).ToArray(); return(EvaluateFunctionCall(ref position, functionNameHash, nbParams, newParam, returnStack, valueStack)); case FuncDef.LogicNot: if (nbParams != 1) { throw new Exception("'not' should be called with one argument"); } var bval = _memory.CastBoolean(param.ElementAt(0)); return(NanTags.EncodeBool(!bval)); case FuncDef.LogicOr: { bool more = nbParams > 0; int i = 0; while (more) { var bresult = _memory.CastBoolean(param.ElementAt(i)); if (bresult) { return(NanTags.EncodeBool(true)); } i++; more = i < nbParams; } return(NanTags.EncodeBool(false)); } case FuncDef.LogicAnd: { bool more = nbParams > 0; int i = 0; while (more) { var bresult = _memory.CastBoolean(param.ElementAt(i)); if (!bresult) { return(NanTags.EncodeBool(false)); } i++; more = i < nbParams; } return(NanTags.EncodeBool(true)); } case FuncDef.ReadKey: return(_memory.StoreStringAndGetReference(((char)_input.Read()).ToString())); case FuncDef.ReadLine: return(_memory.StoreStringAndGetReference(_input.ReadLine())); case FuncDef.Print: { string lastStr = null; foreach (var v in param) { lastStr = _memory.CastString(v); _output.Write(lastStr); } if (lastStr != "") { _output.WriteLine(); } } return(NanTags.VoidReturn()); case FuncDef.Substring: if (nbParams == 2) { var newString = _memory.CastString(param.ElementAt(0)).Substring(_memory.CastInt(param.ElementAt(1))); return(_memory.StoreStringAndGetReference(newString)); } else if (nbParams == 3) { int start = _memory.CastInt(param.ElementAt(1)); int length = _memory.CastInt(param.ElementAt(2)); string s = _memory.CastString(param.ElementAt(0)).Substring(start, length); return(_memory.StoreStringAndGetReference(s)); } else { throw new Exception("'Substring' should be called with 2 or 3 parameters"); } case FuncDef.Length: return(_memory.CastString(param.ElementAt(0)).Length); case FuncDef.Replace: if (nbParams != 3) { throw new Exception("'Replace' should be called with 3 parameters"); } string exp = _memory.CastString(param.ElementAt(0)); string oldValue = _memory.CastString(param.ElementAt(1)); string newValue = _memory.CastString(param.ElementAt(2)); exp = exp.Replace(oldValue, newValue); return(_memory.StoreStringAndGetReference(exp)); case FuncDef.Concat: var builder = new StringBuilder(); foreach (var v in param) { builder.Append(_memory.CastString(v)); } return(_memory.StoreStringAndGetReference(builder.ToString())); case FuncDef.UnitEmpty: { // valueless marker (like an empty object) return(NanTags.EncodeNonValue(NonValueType.Unit)); } case FuncDef.MathAdd: if (nbParams == 1) { return(param[0]); } return(param.ChainSum()); case FuncDef.MathSub: if (nbParams == 1) { return(-param[0]); } return(param.ChainDifference()); case FuncDef.MathProd: if (nbParams == 1) { throw new Exception("Uniary '*' is not supported"); } return(param.ChainProduct()); case FuncDef.MathDiv: if (nbParams == 1) { throw new Exception("Uniary '/' is not supported"); } return(param.ChainDivide()); case FuncDef.MathMod: if (nbParams == 1) { return(param[0] % 2); } return(param.ChainRemainder()); default: throw new Exception("Unrecognised built-in! Type = " + ((int)kind)); } }