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); 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 Macro(Cons args, Env env, Interpreter interpreter, Loc loc) : base(args, env, interpreter, 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 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 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; }
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 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 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 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 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); }
//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; }