Exemple #1
0
        public void Run(string file, string[] args)
        {
            try {
                LoadEnvironment();

                var path = Path.GetFullPath(file);
                sourceRoot = Directory.GetParent(path).FullName;


                if (Verbose)
                {
                    Console.WriteLine($"Source Root: {sourceRoot}");
                    Console.WriteLine("Extra Seach Paths:");
                    foreach (var p in extraSourceSearchPaths)
                    {
                        Console.WriteLine($"    {p}");
                    }
                }


                if (Verbose)
                {
                    Console.WriteLine($"Main File: {path}");
                }
                var chunk = LoadFile(path);

                var mainv = chunk.Scope.Get("main");
                if (!mainv.IsProcedure)
                {
                    Console.WriteLine("No 'main' procedure found");
                    return;
                }
                var main = mainv.CheckProcedure();

                ((NeoBackendProcedure)main).JIT();

                var argsa = new NeoArray();
                foreach (var arg in args)
                {
                    argsa.Insert(NeoString.ValueOf(arg));
                }

                PushFrame(chunk.Chunk.Name, main.Name(), -1);
                main.Call(new[] { argsa });
                PopFrame();
            } catch (NeoError e) {
                PrintStackTrace(e);
                Environment.Exit(1);
            } catch (StackOverflowException e) {
                PrintStackTrace(e);
                Environment.Exit(1);
            }
        }
Exemple #2
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));
     }
 }
Exemple #3
0
        public static NeoValue List(NeoValue[] args)
        {
            if (args.Length != 1)
            {
                throw new NeoError("list expects a single string argument");                              //@TODO @Untested
            }
            var path   = args[0].CheckString().Value;
            var result = new NeoArray();

            foreach (var file in Directory.GetFiles(Path.GetFullPath(path)))
            {
                result.Insert(NeoString.ValueOf(file));
            }
            return(result);
        }
Exemple #4
0
        public NeoValue Call(NeoValue[] arguments)
        {
            var scope = new Scope(parentScope);

            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]);
                }
            }

            var upvalues = new Dictionary <string, UpValue>();

            foreach (var upvalue in this.upvalues)
            {
                upvalues[upvalue.Name] = upvalue;
            }

            foreach (var parameter in procedure.Parameters)
            {
                scope.Declare(parameter.Name, VariableFlags.NONE);
            }

            return(@delegate(scope, pargs.ToArray(), varargs, upvalues, vm, parentScope, chunk, procedure));
        }
Exemple #5
0
        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");
        }