internal Closure(Cons args,Env env,Interpreter interpreter,Loc loc) { this.interpreter = interpreter; this.loc = loc; ArgSpecs specs = analyzeArgSpec((Cons)args.first,env,loc); //create an env expanded by params in which to analyze body Env env2 = new Env(specs.parameters, null, env, interpreter); this.argSpecs = specs; this.body = interpreter.analyze(new Cons(interpreter.BLOCK,args.rest), env2,loc); this.env = env; }
internal BacktraceFrame(Loc loc,Object f,Object args) { if(loc != null) { this.file = loc.file; this.line = loc.line; } else { this.file = "no file"; this.line = 0; } this.f = f; this.args = args; }
internal ApplyExpression(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; fexpr = interpreter.analyze(args.first,env,loc); if(fexpr is IVar) { fsym = ((IVar)fexpr).getSymbol(); } Int32 len = Cons.Length(args.rest); argexprs = new IExpression[len]; args = args.rest; for(Int32 i=0;i<argexprs.Length;i++,args = args.rest) { argexprs[i] = interpreter.analyze(args.first,env,loc); } }
internal IExpression analyze(Object expr, Env env, Loc enclosingLoc) { Symbol symbol = expr as Symbol; if(symbol != null) { if(symbol.isDynamic) return new DynamicVar(symbol); Object var = env.lookup(symbol); if(var is LocalVariable) return new LocalVar((LocalVariable)var); else return new GlobalVar((Symbol)var); } if(!(expr is Cons)) //must be literal return new QuoteExpr(expr); Cons exprs = (Cons)expr; Loc loc = (Loc)reader.locTable[expr]; if(loc != null) reader.locTable.Remove(expr); else loc = enclosingLoc; Object f = exprs.first; Cons args = exprs.rest; //see if it's a macro Symbol s = f as Symbol; if(s != null && s.isDefined() && s.getGlobalValue() is Macro) { Macro m = (Macro)s.getGlobalValue(); Object[] argarray = Cons.ToVector(args); Object mexprs = null; try { mexprs = m.Invoke(argarray); } catch(Exception ex) { BacktraceFrame frame = new BacktraceFrame(loc,s,args); throw BacktraceException.push(ex,frame,this); } try { return analyze(mexprs,env,loc); } catch(Exception ex) { BacktraceFrame frame = new BacktraceFrame(loc,"when expanding ",exprs); throw BacktraceException.push(ex,frame,this); } } Int32 numargs = Cons.Length(args); if(f == DLET) return new DynamicLet(args,env,this,loc); else if(f == FN) return new Closure(args,env,this,loc); else if(f == MACRO) return new Macro(args,env,this,loc); else if(f == WHILE) return new WhileExpression(args,env,this,loc); else if(f == BLOCK) { if(numargs == 0) return new QuoteExpr(null); //remove block from block of one else if(numargs == 1) return analyze(args.first,env,loc); else return new BlockExpression(args,env,this,loc); } else if(f == OR) { if(numargs == 0) return new QuoteExpr(null); else return new OrExpression(args,env,this,loc); } else if(f == IF) return new IfExpression(args,env,this,loc); else if(f == QUOTE) return new QuoteExpr(args.first); else if(f == SET) return new SetExpression(args,env,this,loc); else //presume funcall return new ApplyExpression(exprs,env,this,loc); }
internal Object Load(LocTextReader t) { Object expr = null; do { Int32 line = t.line; expr = Read(t); if(!Eof(expr)) { try { eval(expr,globalenv); } catch(Exception ex) { Loc loc = new Loc(t.file, line); Console.Error.WriteLine(ex); Console.Error.WriteLine("loc=" + loc); Console.ReadLine(); throw BacktraceException.push(ex, new BacktraceFrame(loc, "when evaluating ", expr), this); } } }while(!Eof(expr)); return true; }
internal Object ReadVector(params Object[] args) { LocTextReader t = (LocTextReader)args[0]; Int32 line = t.line; Cons largs = readDelimitedList(t,']'); Object ret = new Cons(interpreter.VECTOR,largs); //record the location locTable[ret] = new Loc(t.file,line); return ret; }
internal Object ReadUnquote(params Object[] args) { LocTextReader t = (LocTextReader)args[0]; Int32 line = t.line; Int32 ch = t.Peek(); Object ret = null; if(ch == '@') { t.Read(); ret = Cons.MakeList(interpreter.UNQUOTE_SPLICING,doRead(t,false)); } else ret = Cons.MakeList(interpreter.UNQUOTE,doRead(t,false)); //record the location locTable[ret] = new Loc(t.file,line); return ret; }
internal Object ReadString(params Object[] args) { StringBuilder b = new StringBuilder(); LocTextReader t = (LocTextReader)args[0]; Int32 line = t.line; //eat the double-quote //t.Read(); //Int32 ch = t.Peek(); Int32 ch = t.Read(); while(ch != '"') { if(ch == -1) { throw new Exception("Read error - eof found before matching: \"" + "\n File: " + t.file + ", line: " + t.line); } //eat it //t.Read(); if(ch == '\\') //escape { ch = t.Read(); if(ch == -1) { throw new Exception("Read error - eof found before matching: \"" + "\n File: " + t.file + ", line: " + t.line); } switch(ch) { case 't': ch = '\t'; break; case 'r': ch = '\r'; break; case 'n': ch = '\n'; break; case '\\': break; case '"': break; default: throw new Exception("Unsupported escape character: \\" + (Char)ch + "\n File: " + t.file + ", line: " + t.line); } } b.Append((Char)ch); //ch = t.Peek(); ch = t.Read(); } //eat the trailing quote //t.Read(); Object ret = b.ToString(); //record the location locTable[ret] = new Loc(t.file,line); return ret; }
internal Object ReadQuote(params Object[] args) { LocTextReader t = (LocTextReader)args[0]; Int32 line = t.line; Object ret = Cons.MakeList(interpreter.QUOTE,doRead(t,false)); //record the location locTable[ret] = new Loc(t.file,line); return ret; }
internal Macro(Cons args,Env env,Interpreter interpreter,Loc loc) :base(args,env,interpreter,loc) { }
internal SetExpression(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; Int32 len = Cons.Length(args); if(len != 2) throw new Exception("Wrong number of args for set"); var = (IVar)interpreter.analyze(args.first,env,loc); val = interpreter.analyze(Cons.Second(args),env,loc); }
internal IfExpression(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; Int32 len = Cons.Length(args); if(len < 2 || len > 3) throw new Exception("Wrong number of args for if"); test = interpreter.analyze(args.first,env,loc); brtrue = interpreter.analyze(Cons.Second(args),env,loc); if(len == 3) brfalse = interpreter.analyze(Cons.Third(args),env,loc); else brfalse = new QuoteExpr(null); }
internal OrExpression(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; exprs = new IExpression[Cons.Length(args)]; for(Int32 i=0;i<exprs.Length;i++,args = args.rest) { exprs[i] = interpreter.analyze(args.first,env,loc); } }
internal WhileExpression(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; this.test = interpreter.analyze(args.first,env,loc); this.body = interpreter.analyze(new Cons(interpreter.BLOCK,args.rest),env,loc); }
internal DynamicLet(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; Cons bindlist = (Cons)args.first; Int32 blen = Cons.Length(bindlist); if((blen%2) != 0) //odd { throw new Exception("Odd number of args in dynamic-let binding list"); } binds = new BindPair[blen/2]; for(int i = 0;i<binds.Length;i++) { binds[i].dvar = (DynamicVar)interpreter.analyze(bindlist.first, env,loc); bindlist = bindlist.rest; binds[i].expr = interpreter.analyze(bindlist.first, env,loc); bindlist = bindlist.rest; } this.body = interpreter.analyze(new Cons(interpreter.BLOCK,args.rest), env,loc); }
internal DynamicLet(Cons args,Env env,Interpreter interpreter,Loc loc) { this.loc = loc; this.interpreter = interpreter; Cons bindlist = (Cons)args.first; Int32 blen = Cons.Length(bindlist); if((blen%2) != 0) //odd { throw new Exception("Odd number of args in dynamic-let binding list"); } binds = new BindPair[blen/2]; for(int i = 0;i<binds.Length;i++) { // MEH: Better error instead of InvalidCastException //binds[i].dvar = (DynamicVar)interpreter.analyze(bindlist.first, env,loc); if((binds[i].dvar = (interpreter.analyze(bindlist.first, env,loc) as DynamicVar)) == null) throw new Exception("Dynamic vars must have prefix *"); bindlist = bindlist.rest; binds[i].expr = interpreter.analyze(bindlist.first, env,loc); bindlist = bindlist.rest; } this.body = interpreter.analyze(new Cons(interpreter.BLOCK,args.rest), env,loc); }
//parse out params from spec (which may contain &optional, &key, &rest, initforms etc internal ArgSpecs analyzeArgSpec(Cons arglist, Env env,Loc loc) { //count the params int nParams = 0; Cons a = arglist; while(a!=null) { Object p = a.first; if(p!=interpreter.AMPOPT && p!=interpreter.AMPKEY && p!=interpreter.AMPREST) ++nParams; a = a.rest; } ArgSpecs ret = new ArgSpecs(env); ret.parameters = new Parameter[nParams]; Parameter.Spec state = Parameter.Spec.REQ; int param = 0; a = arglist; while(a!=null) { Object p = a.first; switch(state) { case Parameter.Spec.REQ: if(p==interpreter.AMPOPT) state = Parameter.Spec.OPT; else if(p==interpreter.AMPKEY) state = Parameter.Spec.KEY; else if(p==interpreter.AMPREST) state = Parameter.Spec.REST; else { if(p is Symbol) { ret.parameters[param++] = new Parameter((Symbol)p,Parameter.Spec.REQ,null); ++ret.numReq; } else if(p is Cons) { ret.parameters[param] = new Parameter((Symbol)((Cons)p).first,Parameter.Spec.REQ,null); ret.parameters[param].typeSpec = interpreter.eval(Cons.Second((Cons)p),env); ++param; ++ret.numReq; } } break; case Parameter.Spec.OPT: if(p==interpreter.AMPOPT) throw new Exception("&optional can appear only once in arg list"); else if(p==interpreter.AMPKEY) state = Parameter.Spec.KEY; else if(p==interpreter.AMPREST) state = Parameter.Spec.REST; else { if(p is Symbol) { ret.parameters[param++] = new Parameter((Symbol)p,Parameter.Spec.OPT,null); ++ret.numOpt; } else if(p is Cons) { ret.parameters[param++] = new Parameter((Symbol)((Cons)p).first,Parameter.Spec.OPT, interpreter.analyze(Cons.Second((Cons)p),env,loc)); ++ret.numOpt; } else throw new Exception("&optional parameters must be symbols or (symbol init-form)"); } break; case Parameter.Spec.KEY: if(p==interpreter.AMPOPT) throw new Exception("&optional must appear before &key in arg list"); else if(p==interpreter.AMPKEY) throw new Exception("&key can appear only once in arg list"); else if(p==interpreter.AMPREST) state = Parameter.Spec.REST; else { if(p is Symbol) { ret.parameters[param] = new Parameter((Symbol)p,Parameter.Spec.KEY,null); ret.parameters[param].key = interpreter.intern(":" + ((Symbol)p).name); ++param; ++ret.numKey; } else if(p is Cons) { ret.parameters[param] = new Parameter((Symbol)((Cons)p).first,Parameter.Spec.KEY, interpreter.analyze(Cons.Second((Cons)p),env,loc)); ret.parameters[param].key = interpreter.intern(":" + ((Symbol)((Cons)p).first).name); ++param; ++ret.numKey; } else throw new Exception("&key parameters must be symbols or (symbol init-form)"); } break; case Parameter.Spec.REST: if(p==interpreter.AMPOPT) throw new Exception("&optional must appear before &rest in arg list"); else if(p==interpreter.AMPKEY) throw new Exception("&key must appear before &rest in arg list"); else if(p==interpreter.AMPREST) throw new Exception("&rest can appear only once in arg list"); else { if(!(p is Symbol)) throw new Exception("&rest parameter must be a symbol"); else { if(ret.numRest > 0) //already got a rest param throw new Exception("Only one &rest arg can be specified"); ret.parameters[param++] = new Parameter((Symbol)p,Parameter.Spec.REST,null); ++ret.numRest; } } break; } a = a.rest; } return ret; }
internal Object ReadList(params Object[] args) { LocTextReader t = (LocTextReader)args[0]; Int32 line = t.line; Object ret = readDelimitedList(t,')'); //record the location if(ret != null) locTable[ret] = new Loc(t.file,line); return ret; }