public static NeoValue Int(NeoValue[] args) { if (args.Length != 1) { throw new NeoError("int expects a single numeric argument"); // @TODO @Untested } return(NeoInt.ValueOf(args[0].CheckNumber().CastToInt())); }
public void VisitForKeyValueIterator(ForKeyValueIteratorNode node) { UpdateLine(node); PushScope(); asm.Declare(node.Key.Value, VariableFlags.NONE); asm.MarkLocal(node.Value.Value); asm.Declare(node.Value.Value, VariableFlags.NONE); asm.MarkLocal(node.Key.Value); var idx = "__idx_" + Guid.NewGuid().ToString("n"); asm.Declare(idx, VariableFlags.NONE); asm.MarkLocal(idx); asm.PushConstant(NeoInt.ValueOf(0)); asm.SetLocal(idx); var from = "__from_" + Guid.NewGuid().ToString("n"); asm.Declare(from, VariableFlags.NONE); asm.MarkLocal(from); node.From.Accept(this); asm.SetLocal(from); var start = asm.NewLabel(); asm.MarkLabel(start); asm.GetLocal(idx); asm.GetLocal(from); asm.Length(); asm.Eq(); var end = asm.NewLabel(); PushLoop(start, end, start); asm.Branch(end); asm.GetLocal(from); asm.GetLocal(idx); asm.ObjectIndex(); asm.SetLocal(node.Value.Value); asm.SetLocal(node.Key.Value); asm.GetLocal(idx); asm.Inc(); asm.SetLocal(idx); PreLoopCode(node.Code); node.Code.Accept(this); PostLoopCode(node.Code); asm.Jump(start); asm.MarkLabel(end); PopLoop(); PopScope(); }
public static NeoValue ToByte(NeoValue[] args) { if (args.Length != 1) { throw new NeoError("byte expects a single string parameter"); // @TODO @Untested } var str = args[0].CheckString().Value; if (str.Length != 1) { throw new NeoError($"byte expects its parameter to be of length 1, got {str.Length}"); // @TODO @Untested } return(NeoInt.ValueOf(str[0])); }
public static NeoValue ToNeo(object v) { if (v == null) { return(NeoNil.NIL); } else if (v is Int32 i) { return(NeoInt.ValueOf(i)); } else if (v is Int64 l) { return(NeoInt.ValueOf((int)l)); } else if (v is Single f) { return(NeoFloat.ValueOf(f)); } else if (v is Double d) { return(NeoFloat.ValueOf(d)); } else if (v is Boolean b) { return(NeoBool.ValueOf(b)); } else if (v is String s) { return(NeoString.ValueOf(s)); } else if (v is object[] a) { // @TODO: is this where we want to do this? or do we just want an FFITypeProxy? var r = new NeoArray(); foreach (object o in a) { r.Insert(ToNeo(o)); } return(r); } else if (v is Dictionary <object, object> m) { throw new NotImplementedException(); } else { return(new FFITypeProxy(v)); } }
public void VisitEnum(EnumNode node) { UpdateLine(node); asm.NewObject(); var elements = node.Elements.ToArray(); for (var i = 0; i < elements.Length; i++) { asm.Dup(); asm.PushConstant(NeoString.ValueOf(elements[i])); asm.PushConstant(NeoInt.ValueOf(i)); asm.Set(); asm.Dup(); asm.PushConstant(NeoInt.ValueOf(i)); asm.PushConstant(NeoString.ValueOf(elements[i])); asm.Set(); } asm.Frozen(); }
internal Procedure(BinaryReader reader) { Name = reader.ReadString(); Exported = reader.ReadBoolean(); UpValues = new string[reader.ReadInt32()]; for (var i = 0; i < UpValues.Length; i++) { UpValues[i] = reader.ReadString(); } Parameters = new Parameter[reader.ReadInt32()]; for (var i = 0; i < Parameters.Length; i++) { Parameters[i] = new Parameter(reader.ReadString(), reader.ReadBoolean()); } Varargs = reader.ReadBoolean(); Lines = new LineRange[reader.ReadInt32()]; for (var i = 0; i < Lines.Length; i++) { Lines[i] = new LineRange(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); } Line = reader.ReadInt32(); Instructions = new byte[reader.ReadInt32()]; for (var i = 0; i < Instructions.Length; i++) { Instructions[i] = reader.ReadByte(); } Constants = new NeoValue[reader.ReadInt32()]; for (var i = 0; i < Constants.Length; i++) { var type = reader.ReadByte(); switch (type) { case (byte)ConstantType.STRING: Constants[i] = NeoString.ValueOf(reader.ReadString()); break; case (byte)ConstantType.INT: Constants[i] = NeoInt.ValueOf(reader.ReadInt32()); break; case (byte)ConstantType.FLOAT: Constants[i] = NeoFloat.ValueOf(reader.ReadDouble()); break; default: throw new Exception("invalid chunk"); } } Procedures = new Procedure[reader.ReadInt32()]; for (var i = 0; i < Procedures.Length; i++) { Procedures[i] = new Procedure(reader); } }
public static NeoValue Read(NeoValue[] args) { return(NeoInt.ValueOf(Console.Read())); }
public void VisitInt(IntNode node) { UpdateLine(node); asm.PushConstant(NeoInt.ValueOf(node.Value)); }
public void VisitForRange(ForRangeNode node) { UpdateLine(node); PushScope(); asm.Declare(node.Iterator.Value, VariableFlags.NONE); node.Start.Accept(this); asm.MarkLocal(node.Iterator.Value); asm.SetLocal(node.Iterator.Value); var endValue = "__end_" + Guid.NewGuid().ToString("n"); asm.Declare(endValue, VariableFlags.FINAL); node.End.Accept(this); asm.MarkLocal(endValue); asm.SetLocal(endValue); var byValue = "__by_" + Guid.NewGuid().ToString("n"); asm.Declare(byValue, VariableFlags.FINAL); if (node.By != null) { node.By.Accept(this); } else { asm.PushConstant(NeoInt.ValueOf(1)); } asm.MarkLocal(byValue); asm.SetLocal(byValue); var cmpValue = "__cmp_" + Guid.NewGuid().ToString("n"); asm.Declare(cmpValue, VariableFlags.NONE); asm.MarkLocal(cmpValue); asm.PushConstant(NeoInt.ValueOf(1)); asm.SetLocal(cmpValue); var skip = asm.NewLabel(); asm.GetLocal(byValue); asm.PushConstant(NeoInt.ValueOf(0)); asm.Lte(); asm.Branch(skip); asm.PushConstant(NeoInt.ValueOf(-1)); asm.SetLocal(cmpValue); asm.MarkLabel(skip); var start = asm.NewLabel(); asm.MarkLabel(start); asm.GetLocal(node.Iterator.Value); asm.GetLocal(endValue); asm.Cmp(); asm.GetLocal(cmpValue); asm.Eq(); var end = asm.NewLabel(); var codeEnd = asm.NewLabel(); PushLoop(start, end, codeEnd); asm.Branch(end); PreLoopCode(node.Code); node.Code.Accept(this); asm.MarkLabel(codeEnd); PostLoopCode(node.Code); asm.GetLocal(node.Iterator.Value); asm.GetLocal(byValue); asm.Add(); asm.SetLocal(node.Iterator.Value); asm.Jump(start); asm.MarkLabel(end); PopLoop(); PopScope(); }
public NeoValue Call(NeoValue[] arguments) { var scope = new Scope(ParentScope); var level = scope.Level; var pargs = new List <NeoValue>(); foreach (var arg in arguments) { if (arg is NeoSpreadValue va) { for (var i = 0; i < va.Array.Count; i++) { pargs.Add(va.Array[i]); } } else { pargs.Add(arg); } } var varargs = new NeoArray(); var extraArgs = pargs.Count - Procedure.Parameters.Length; if (extraArgs > 0) { for (var i = 0; i < extraArgs; i++) { varargs.Insert(pargs[Procedure.Parameters.Length + i]); } } for (var i = 0; i < Procedure.Parameters.Length; i++) { var name = Procedure.Parameters[i].Name; scope.Declare(name, VariableFlags.NONE); var value = i < pargs.Count ? pargs[i] : NeoNil.NIL; if (Procedure.Parameters[i].Frozen) { value = value.Frozen(); } scope.Set(name, value); } var code = Procedure.Instructions; var ip = 4; var stack = new Stack <NeoValue>(); var rootScopeLevel = scope.Level; var openUps = new Dictionary <string, UpValue>(); var defers = new Stack <NeoProcedure>(); byte ReadByte() => code[ip++]; OpCode ReadOpCode() => (OpCode)ReadByte(); short ReadShort() => (short)(ReadByte() | (ReadByte() << 8)); int ReadInt() => ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24); NeoValue ReadConstant() => Procedure.Constants[ReadInt()]; while (ip < code.Length) { var op = ReadOpCode(); try { switch (op) { case OpCode.NOP: break; case OpCode.INC: { stack.Push(stack.Pop().Inc()); } break; case OpCode.DEC: { stack.Push(stack.Pop().Dec()); } break; case OpCode.ADD: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Add(b)); } break; case OpCode.SUB: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Sub(b)); } break; case OpCode.MUL: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Mul(b)); } break; case OpCode.DIV: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Div(b)); } break; case OpCode.POW: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Pow(b)); } break; case OpCode.MOD: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Mod(b)); } break; case OpCode.LSH: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Lsh(b)); } break; case OpCode.RSH: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Rsh(b)); } break; case OpCode.BIT_NOT: { stack.Push(stack.Pop().BitNot()); } break; case OpCode.BIT_AND: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.BitAnd(b)); } break; case OpCode.BIT_OR: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.BitOr(b)); } break; case OpCode.BIT_XOR: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.BitXor(b)); } break; case OpCode.NOT: { stack.Push(stack.Pop().Not()); } break; case OpCode.NEG: { stack.Push(stack.Pop().Neg()); } break; case OpCode.CONCAT: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Concat(b)); } break; case OpCode.LENGTH: { stack.Push(stack.Pop().Length()); } break; case OpCode.ARRAY_NEW: { stack.Push(new NeoArray()); } break; case OpCode.ARRAY_ADD: { var element = stack.Pop(); var array = stack.Pop().CheckArray(); array.Insert(element); } break; case OpCode.OBJECT_NEW: { stack.Push(new NeoObject()); } break; case OpCode.OBJECT_INDEX: { var index = stack.Pop().CheckInt(); var obj = stack.Pop().CheckObject(); stack.Push(obj.Index(index.Value)); stack.Push(obj.Get(obj.Index(index.Value))); } break; case OpCode.GET: { var key = stack.Pop(); var obj = stack.Pop(); stack.Push(obj.Get(key)); } break; case OpCode.SET: { var value = stack.Pop(); var key = stack.Pop(); var obj = stack.Pop(); obj.Set(key, value); } break; case OpCode.SLICE: { var end = stack.Pop(); var start = stack.Pop(); stack.Push(stack.Pop().Slice(start, end)); } break; case OpCode.EQ: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Eq(b)); } break; case OpCode.NE: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Ne(b)); } break; case OpCode.DEEP_EQ: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.DeepEq(b)); } break; case OpCode.DEEP_NE: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.DeepNe(b)); } break; case OpCode.LT: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Lt(b)); } break; case OpCode.GT: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Gt(b)); } break; case OpCode.LTE: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Lte(b)); } break; case OpCode.GTE: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(a.Gte(b)); } break; case OpCode.CMP: { var b = stack.Pop(); var a = stack.Pop(); stack.Push(NeoInt.ValueOf(a.Compare(b))); } break; case OpCode.JUMP: { ip = ReadInt(); } break; case OpCode.JUMP_IF: { var addr = ReadInt(); if (stack.Pop().CheckBool().Value) { ip = addr; } } break; case OpCode.CALL: { var line = Procedure.FindLine(ip); var nargs = ReadShort(); var args = new NeoValue[nargs]; for (var i = nargs - 1; i >= 0; i--) { args[i] = stack.Pop(); } var callee = stack.Pop(); if (callee.IsProcedure) { var proc = callee.CheckProcedure(); VM.PushFrame(proc.ChunkName(), proc.Name(), line); stack.Push(proc.Call(args)); VM.PopFrame(); } else { VM.PushFrame("<unknown>", "<unknown>", line); stack.Push(callee.Call(args)); VM.PopFrame(); } } break; case OpCode.RETURN: { foreach (var up in openUps.Values) { up.Close(); } NeoValue rvalue = null; if (ReadByte() == 1) { rvalue = stack.Pop(); } else { if (stack.Count > 0) { throw new Exception("VM error: stack not empty"); } rvalue = NeoNil.NIL; } while (defers.Count > 0) { defers.Pop().Call(new NeoValue[0]); } return(rvalue); } case OpCode.DEFER: { defers.Push(stack.Pop().CheckProcedure()); } break; case OpCode.VARARGS: { if (!Procedure.Varargs) { stack.Push(NeoNil.NIL); } else { stack.Push(varargs); } } break; case OpCode.PUSH_TRUE: { stack.Push(NeoBool.TRUE); } break; case OpCode.PUSH_FALSE: { stack.Push(NeoBool.FALSE); } break; case OpCode.PUSH_NIL: { stack.Push(NeoNil.NIL); } break; case OpCode.PUSH_CONSTANT: { stack.Push(ReadConstant()); } break; case OpCode.DUP: { var a = stack.Pop(); stack.Push(a); stack.Push(a); } break; case OpCode.SWAP: { var a = stack.Pop(); var b = stack.Pop(); stack.Push(a); stack.Push(b); } break; case OpCode.POP: { stack.Pop(); } break; case OpCode.CLOSURE: { var name = ReadConstant().CheckString().Value; var proc = Procedure.Procedures.First(p => p.Name == name); var upvalues = new UpValue[proc.UpValues.Length]; for (var i = 0; i < upvalues.Length; i++) { var pop = ReadOpCode(); if (pop != OpCode.GET_LOCAL && pop != OpCode.GET_UPVALUE) { throw new Exception(pop.ToString()); } var upname = ReadConstant().CheckString().Value; switch (pop) { case OpCode.GET_LOCAL: { if (!openUps.ContainsKey(upname)) { openUps[upname] = new UpValue(scope, upname); } upvalues[i] = openUps[upname]; } break; case OpCode.GET_UPVALUE: { upvalues[i] = this.upvalues.First(up => up.Name == upname); } break; } } stack.Push(new NeoBackendProcedure(VM, VM.Interpreter.Compile(scope, Chunk, proc, upvalues))); } break; case OpCode.CLOSE: { var name = ReadConstant().CheckString().Value; if (openUps.ContainsKey(name)) { openUps[name].Close(); openUps.Remove(name); } } break; case OpCode.GET_LOCAL: {; stack.Push(scope.Get(ReadConstant().CheckString().Value)); } break; case OpCode.SET_LOCAL: { var name = ReadConstant().CheckString().Value; var value = stack.Pop(); scope.Set(name, value); } break; case OpCode.GET_GLOBAL: { stack.Push(ParentScope.Get(ReadConstant().CheckString().Value)); } break; case OpCode.SET_GLOBAL: { ParentScope.Set(ReadConstant().CheckString().Value, stack.Pop()); } break; case OpCode.GET_UPVALUE: { var name = ReadConstant().CheckString().Value; var up = upvalues.First(u => u.Name == name); stack.Push(up.Get()); } break; case OpCode.SET_UPVALUE: { var name = ReadConstant().CheckString().Value; var index = 0; for (var i = 0; i < upvalues.Length; i++) { if (upvalues[i].Name == name) { index = i; break; } } upvalues[index].Set(stack.Pop()); } break; case OpCode.SPREAD: { stack.Push(new NeoSpreadValue(stack.Pop().CheckArray())); } break; case OpCode.FROZEN: { stack.Push(stack.Pop().Frozen()); } break; case OpCode.TRY: { var @catch = stack.Pop().CheckProcedure(); var @try = stack.Pop().CheckProcedure(); NeoValue r; try { r = @try.Call(new NeoValue[0]); } catch (NeoError e) { r = @catch.Call(new[] { NeoString.ValueOf(e.Message) }); } if (!r.IsNil) { foreach (var up in openUps.Values) { up.Close(); } while (defers.Count > 0) { defers.Pop().Call(new NeoValue[0]); } return(r); } } break; case OpCode.THROW: { var line = Procedure.FindLine(ip); throw new NeoError(stack.Pop().CheckString().Value, line); } case OpCode.DECLARE: { var name = ReadConstant().CheckString().Value; var flags = ReadByte(); scope.Declare(name, (VariableFlags)flags); } break; case OpCode.PUSH_SCOPE: { scope = new Scope(scope); } break; case OpCode.POP_SCOPE: { scope = scope.Parent; } break; default: { throw new Exception($"Unexpected opcode: {op}"); } } } catch (NeoError e) { if (e.Line == -1) { e.Line = Procedure.FindLine(ip); } throw e; } } throw new Exception("VM error"); }
public override NeoValue Length() => NeoInt.ValueOf(Value.Length);