package zygo import ( "fmt" ) var NoExpressionsFound = fmt.Errorf("No expressions found") type Generator struct { env *Zlisp funcname string Tail bool scopes int instructions []Instruction } type Loop struct { stmtname *SexpSymbol label *SexpSymbol loopStart int loopLen int breakOffset int // i.e. relative to loopStart continueOffset int // i.e. relative to loopStart } func (loop *Loop) IsStackElem() {} func NewGenerator(env *Zlisp) *Generator { gen := new(Generator) gen.env = env gen.instructions = make([]Instruction, 0) // tail marks whether or not we are in the tail position gen.Tail = false // scopes is the number of extra (non-function) scopes we've created gen.scopes = 0 return gen } func (gen *Generator) AddInstructions(instr []Instruction) { gen.instructions = append(gen.instructions, instr...) } func (gen *Generator) AddInstruction(instr Instruction) { gen.instructions = append(gen.instructions, instr) } func (gen *Generator) GenerateBegin(expressions []Sexp) error { size := len(expressions) oldtail := gen.Tail gen.Tail = false if size == 0 { return nil //return NoExpressionsFound } startInstructionCount := 0 for _, expr := range expressions[:size-1] { startInstructionCount = len(gen.instructions) err := gen.Generate(expr) if err != nil { return err } // insert pops after all but the last instruction // that way the stack remains clean. // Without the (conditional) pop here, we really mess up // the closure.zy tests. if len(gen.instructions) > startInstructionCount { gen.AddInstruction(PopInstr(0)) } } gen.Tail = oldtail return gen.Generate(expressions[size-1]) } func buildSexpFun( env *Zlisp, name string, funcargs *SexpArray, funcbody []Sexp, orig Sexp) (*SexpFunction, error) { defer func() { VPrintf("exiting buildSexpFun()\n") }() gen := NewGenerator(env) gen.Tail = true if len(name) == 0 { gen.funcname = env.GenSymbol("__anon").name } else { gen.funcname = name } afsHelper := &AddFuncScopeHelper{} gen.AddInstruction(AddFuncScopeInstr{Name: "runtime " + gen.funcname, Helper: afsHelper}) argsyms := make([]*SexpSymbol, len(funcargs.Val)) for i, expr := range funcargs.Val { switch t := expr.(type) { case *SexpSymbol: argsyms[i] = t default: return MissingFunction, fmt.Errorf("function argument must be symbol") } } varargs := false nargs := len(funcargs.Val) if len(argsyms) >= 2 && argsyms[len(argsyms)-2].name == "&" { argsyms[len(argsyms)-2] = argsyms[len(argsyms)-1] argsyms = argsyms[0 : len(argsyms)-1] varargs = true nargs = len(argsyms) - 1 } VPrintf("\n in buildSexpFun(): DumpFunction just before %v args go onto stack\n", len(argsyms)) if Working { DumpFunction(ZlispFunction(gen.instructions), -1) } for i := len(argsyms) - 1; i >= 0; i-- { gen.AddInstruction(PopStackPutEnvInstr{argsyms[i]}) } err := gen.GenerateBegin(funcbody) if err != nil { return MissingFunction, err } gen.AddInstruction(RemoveScopeInstr{}) gen.AddInstruction(ReturnInstr{nil}) newfunc := ZlispFunction(gen.instructions) sfun := gen.env.MakeFunction(gen.funcname, nargs, varargs, newfunc, orig) // tell the function scope where their function is, to // provide access to the captured-closure scopes at runtime. afsHelper.MyFunction = sfun return sfun, nil } func (gen *Generator) GenerateFn(args []Sexp, orig Sexp) error { if len(args) < 2 { return fmt.Errorf("malformed function definition") } var funcargs *SexpArray switch expr := args[0].(type) { case *SexpArray: funcargs = expr default: return fmt.Errorf("function arguments must be in vector") } VPrintf("GenerateFn() about to call buildSexpFun\n") funcbody := args[1:] sfun, err := buildSexpFun(gen.env, "", funcargs, funcbody, orig) if err != nil { return err } VPrintf("in GenerateFn(): gen of sfun:\n") if Working { DumpFunction(sfun.fun, -1) } gen.AddInstruction(CreateClosureInstr{sfun}) return nil } func (gen *Generator) GenerateDef(args []Sexp, opname string) error { if len(args) != 2 { return fmt.Errorf("Wrong number of arguments to %s", opname) } Q("GenerateDef call with args[0]=%v", args[0].SexpString(nil)) dup := true var instr Instruction switch args[0].(type) { case *SexpPair: dup = false Q("def sees assign to pair, using AssignInstr{}") instr = AssignInstr{} err := gen.Generate(args[0]) if err != nil { return err } default: lhs, err := gen.GetLHS(args[0], "def") if err != nil { return err } switch opname { case "def": instr = PopStackPutEnvInstr{lhs} case "set": Q("GenerateDef is doing set with UpdateInstr: lhs = '%s'", lhs.SexpString(nil)) instr = UpdateInstr{lhs} default: panic(fmt.Errorf("unknown opname '%s'", opname)) } } gen.Tail = false err := gen.Generate(args[1]) if err != nil { return err } // duplicate the value so def leaves its value // on the stack and becomes an expression rather // than a statement. if dup { gen.AddInstruction(DupInstr(0)) } gen.AddInstruction(instr) return nil } func (gen *Generator) GenerateDefn(args []Sexp, orig Sexp) error { if len(args) < 3 { return WrongNargs } var funcargs *SexpArray switch expr := args[1].(type) { case *SexpArray: funcargs = expr default: return fmt.Errorf("function arguments must be in vector") } var sym *SexpSymbol switch expr := args[0].(type) { case *SexpSymbol: sym = expr default: return fmt.Errorf("Definition name must be symbol") } builtin, typ := gen.env.IsBuiltinSym(sym) if builtin { return fmt.Errorf("already have %s '%s', refusing to overwrite with defn", typ, sym.name) } if gen.env.HasMacro(sym) { return fmt.Errorf("Already have macro named '%s': refusing"+ " to define function of same name.", sym.name) } VPrintf("GenerateDefn() about to call buildSexpFun\n") sfun, err := buildSexpFun(gen.env, sym.name, funcargs, args[2:], orig) if err != nil { return err } VPrintf("in GenerateDefn(): gen of sfun:\n") if Working { DumpFunction(sfun.fun, -1) } gen.AddInstruction(CreateClosureInstr{sfun}) gen.AddInstruction(PopStackPutEnvInstr{sym}) gen.AddInstruction(PushInstr{SexpNull}) return nil } func (gen *Generator) GenerateDefmac(args []Sexp, orig Sexp) error { if len(args) < 3 { return fmt.Errorf("Wrong number of arguments to defmac") } var funcargs *SexpArray switch expr := args[1].(type) { case *SexpArray: funcargs = expr default: return fmt.Errorf("defmac arguments must be in vector") } var sym *SexpSymbol switch expr := args[0].(type) { case *SexpSymbol: sym = expr default: return fmt.Errorf("defmac name must be symbol") } _, isBuiltin := gen.env.builtins[sym.number] if isBuiltin { return fmt.Errorf("'%s' is already a built-in function, cannot define macro with same name.", sym.name) } xpr, err, _ := gen.env.LexicalLookupSymbol(sym, nil) if err == nil { return fmt.Errorf("'%s' is already bound to '%s', refusing to define conflicting macro", sym.name, xpr.SexpString(nil)) } sfun, err := buildSexpFun(gen.env, sym.name, funcargs, args[2:], orig) if err != nil { return err } gen.env.macros[sym.number] = sfun gen.AddInstruction(PushInstr{SexpNull}) return nil } func (gen *Generator) GenerateMacexpand(args []Sexp) error { if len(args) != 1 { return WrongNargs } var list *SexpPair var islist bool var ismacrocall bool switch t := args[0].(type) { case *SexpPair: if IsList(t.Tail) { list = t islist = true } default: islist = false } var macro *SexpFunction if islist { switch t := list.Head.(type) { case *SexpSymbol: macro, ismacrocall = gen.env.macros[t.number] default: ismacrocall = false } } if !ismacrocall { gen.AddInstruction(PushInstr{args[0]}) return nil } macargs, err := ListToArray(list.Tail) if err != nil { return err } // don't mess up the previous environment // just to run a macroexpand. newenv := gen.env.Duplicate() expr, err := newenv.Apply(macro, macargs) if err != nil { return err } quotedExpansion := Cons(gen.env.MakeSymbol("quote"), expr) gen.AddInstruction(PushInstr{quotedExpansion}) return nil } func (gen *Generator) GenerateShortCircuit(or bool, args []Sexp) error { size := len(args) subgen := NewGenerator(gen.env) subgen.scopes = gen.scopes subgen.Tail = gen.Tail subgen.funcname = gen.funcname subgen.Generate(args[size-1]) instructions := subgen.instructions for i := size - 2; i >= 0; i-- { subgen = NewGenerator(gen.env) subgen.Generate(args[i]) subgen.AddInstruction(DupInstr(0)) subgen.AddInstruction(BranchInstr{or, len(instructions) + 2}) subgen.AddInstruction(PopInstr(0)) instructions = append(subgen.instructions, instructions...) } gen.AddInstructions(instructions) return nil } func (gen *Generator) GenerateCond(args []Sexp) error { if len(args)%2 == 0 { return fmt.Errorf("missing default case") } subgen := NewGenerator(gen.env) subgen.Tail = gen.Tail subgen.scopes = gen.scopes subgen.funcname = gen.funcname err := subgen.Generate(args[len(args)-1]) if err != nil { return err } instructions := subgen.instructions // we generate the cond bottom up, so i counts down. for i := len(args)/2 - 1; i >= 0; i-- { subgen.Reset() err := subgen.Generate(args[2*i]) if err != nil { return err } pred_code := subgen.instructions subgen.Reset() subgen.Tail = gen.Tail subgen.scopes = gen.scopes subgen.funcname = gen.funcname err = subgen.Generate(args[2*i+1]) if err != nil { return err } body_code := subgen.instructions subgen.Reset() subgen.AddInstructions(pred_code) subgen.AddInstruction(BranchInstr{false, len(body_code) + 2}) subgen.AddInstructions(body_code) subgen.AddInstruction(JumpInstr{addpc: len(instructions) + 1}) subgen.AddInstructions(instructions) instructions = subgen.instructions } gen.AddInstructions(instructions) return nil } func (gen *Generator) GenerateQuote(args []Sexp) error { for _, expr := range args { gen.AddInstruction(PushInstr{expr}) } return nil } func (gen *Generator) GenerateLet(name string, args []Sexp) error { if len(args) < 2 { return fmt.Errorf("malformed let statement") } lstatements := make([]*SexpSymbol, 0) rstatements := make([]Sexp, 0) var bindings []Sexp switch expr := args[0].(type) { case *SexpArray: bindings = expr.Val default: return fmt.Errorf("let bindings must be in array") } if len(bindings)%2 != 0 { return fmt.Errorf("uneven let binding list") } for i := 0; i < len(bindings)/2; i++ { switch t := bindings[2*i].(type) { case *SexpSymbol: lstatements = append(lstatements, t) default: return fmt.Errorf("cannot bind to non-symbol") } rstatements = append(rstatements, bindings[2*i+1]) } gen.AddInstruction(AddScopeInstr{Name: "runtime " + name}) gen.scopes++ if name == "letseq" { for i, rs := range rstatements { err := gen.Generate(rs) if err != nil { return err } gen.AddInstruction(PopStackPutEnvInstr{lstatements[i]}) } } else if name == "let" { for _, rs := range rstatements { err := gen.Generate(rs) if err != nil { return err } } for i := len(lstatements) - 1; i >= 0; i-- { gen.AddInstruction(PopStackPutEnvInstr{lstatements[i]}) } } err := gen.GenerateBegin(args[1:]) if err != nil { return err } gen.AddInstruction(RemoveScopeInstr{}) gen.scopes-- return nil } func (gen *Generator) GenerateAssert(args []Sexp) error { if len(args) != 1 { return WrongNargs } err := gen.Generate(args[0]) if err != nil { return err } reterrmsg := fmt.Sprintf("Assertion failed: %s\n", args[0].SexpString(nil)) gen.AddInstruction(BranchInstr{true, 2}) gen.AddInstruction(ReturnInstr{fmt.Errorf(reterrmsg)}) gen.AddInstruction(PushInstr{SexpNull}) return nil } func (gen *Generator) GenerateInclude(args []Sexp) error { if len(args) < 1 { return WrongNargs } var err error var exps []Sexp var sourceItem func(item Sexp) error sourceItem = func(item Sexp) error { switch t := item.(type) { case *SexpArray: for _, v := range t.Val { if err := sourceItem(v); err != nil { return err } } case *SexpPair: expr := item for expr != SexpNull { list := expr.(*SexpPair) if err := sourceItem(list.Head); err != nil { return err } expr = list.Tail } case *SexpStr: exps, err = gen.env.ParseFile(t.S) if err != nil { return err } err = gen.GenerateBegin(exps) if err != nil { return err } default: return fmt.Errorf("include: Expected `string`, `list`, `array` given type %T val %v", item, item) } return nil } for _, v := range args { err = sourceItem(v) if err != nil { return err } } return nil } func (gen *Generator) GenerateCallBySymbol(sym *SexpSymbol, args []Sexp, orig Sexp) error { switch sym.name { case "and": return gen.GenerateShortCircuit(false, args) case "or": return gen.GenerateShortCircuit(true, args) case "cond": return gen.GenerateCond(args) case "quote": return gen.GenerateQuote(args) case "def": return gen.GenerateDef(args, "def") case "mdef": return gen.GenerateMultiDef(args) case "fn": return gen.GenerateFn(args, orig) case "defn": return gen.GenerateDefn(args, orig) case "begin": return gen.GenerateBegin(args) case "let": return gen.GenerateLet("let", args) case "letseq": return gen.GenerateLet("letseq", args) case "assert": return gen.GenerateAssert(args) case "defmac": return gen.GenerateDefmac(args, orig) case "macexpand": return gen.GenerateMacexpand(args) case "syntaxQuote": return gen.GenerateSyntaxQuote(args) case "include": return gen.GenerateInclude(args) case "for": return gen.GenerateForLoop(args) case "set": return gen.GenerateDef(args, "set") case "break": return gen.GenerateBreak(args) case "continue": return gen.GenerateContinue(args) case "newScope": return gen.GenerateNewScope(args) case "package": return gen.GeneratePackage(args) case "return": return gen.GenerateReturn(args) case "_ls": return gen.GenerateDebug("showScopes") } // this is where macros are run macro, found := gen.env.macros[sym.number] if found { // calling Apply on the current environment will screw up // the stack, creating a duplicate environment is safer env := gen.env.Duplicate() expr, err := env.Apply(macro, args) if err != nil { return err } return gen.Generate(expr) } oldtail := gen.Tail gen.Tail = false err := gen.GenerateAll(args) if err != nil { return err } if oldtail && sym.name == gen.funcname { // to do a tail call // pop off all the extra scopes // then jump to beginning of function for i := 0; i < gen.scopes; i++ { gen.AddInstruction(RemoveScopeInstr{}) } gen.AddInstruction(GotoInstr{1}) // goto 1 instead of 0 to avoid adding a new scope } else { gen.AddInstruction(CallInstr{sym, len(args)}) } gen.Tail = oldtail return nil } func (gen *Generator) GenerateBuilder(fun Sexp, args []Sexp) error { //Q("GenerateBuilder is pushing unevaluated arguments onto the stack") n := len(args) for i := 0; i < n; i++ { gen.AddInstruction(PushInstr{args[i]}) } gen.Generate(fun) gen.AddInstruction(DispatchInstr{len(args)}) return nil } func (gen *Generator) GenerateDispatch(fun Sexp, args []Sexp) error { gen.GenerateAll(args) gen.Generate(fun) gen.AddInstruction(DispatchInstr{len(args)}) return nil } func (gen *Generator) GenerateAssignment(expr *SexpPair, assignPos int) error { Q("in GenerateAssignment, expr='%v', assignPos = %v", expr.SexpString(nil), assignPos) if assignPos == 0 { return gen.GenerateCall(expr) } arr, err := ListToArray(expr) panicOn(err) // internal error, should never happen since we prevalidate that we have a list. if len(arr) <= 1 || assignPos == len(arr)-1 { return fmt.Errorf("bad assignment syntax: no right-hand-side") } lhs := arr[:assignPos] rhs := arr[assignPos+1:] if len(lhs) != len(rhs) { return fmt.Errorf("assignment imbalance: left-hand-side had %v, while right-hand-side had %v; in expression '%s'", len(lhs), len(rhs), expr.SexpString(nil)) } // TODO: once functions have typed number of return values, check that we have balance // of return value flow, rather than exact lhs to rhs count equality. for i := range rhs { err = gen.GenerateDef([]Sexp{lhs[i], rhs[i]}, "def") if err != nil { return err } } return nil } func (gen *Generator) GenerateCall(expr *SexpPair) error { arr, _ := ListToArray(expr.Tail) switch head := expr.Head.(type) { case *SexpSymbol: // detect builtin builder calls x, err, _ := gen.env.LexicalLookupSymbol(head, nil) if err == nil { fun, isFun := x.(*SexpFunction) if isFun && fun.isBuilder { return gen.GenerateBuilder(fun, arr) } } // flow control and macros go here return gen.GenerateCallBySymbol(head, arr, expr) } // regular SexpFunction calls go here return gen.GenerateDispatch(expr.Head, arr) } func (gen *Generator) GenerateArray(arr *SexpArray) error { err := gen.GenerateAll(arr.Val) if err != nil { return err } gen.AddInstruction(CallInstr{gen.env.MakeSymbol("array"), len(arr.Val)}) return nil } func (gen *Generator) Generate(expr Sexp) error { if _, isComment := expr.(*SexpComment); isComment { return nil } switch e := expr.(type) { case *SexpSymbol: gen.AddInstruction(EnvToStackInstr{e}) return nil case *SexpPair: if IsList(e) { isAssign, pos := IsAssignmentList(e, 0) legalLeftHandSide := true if isAssign && pos > 0 { lhs, err := gen.GetLHS(e.Head, "assign") if err != nil || (lhs != nil && lhs.isDot) { legalLeftHandSide = false } } if isAssign && pos > 0 && legalLeftHandSide { err := gen.GenerateAssignment(e, pos) if err != nil { return fmt.Errorf("Error generating %s:\n%v", expr.SexpString(nil), err) } return nil } err := gen.GenerateCall(e) if err != nil { return fmt.Errorf("Error generating %s:\n%v", expr.SexpString(nil), err) } return nil } else { gen.AddInstruction(PushInstr{expr}) } case *SexpArray: return gen.GenerateArray(e) default: gen.AddInstruction(PushInstr{expr}) return nil } return nil } func (gen *Generator) GenerateAll(expressions []Sexp) error { for _, expr := range expressions { err := gen.Generate(expr) if err != nil { return err } } return nil } func (gen *Generator) Reset() { gen.instructions = make([]Instruction, 0) gen.Tail = false gen.scopes = 0 } var ErrBadLoopSyntax = fmt.Errorf("for loop: first argument must be a label or a vector of [init predicate advance]") // for loops: Just like in C. // // (for {optional-label} [init predicate advance] (expr)*) // // Each of init, predicate, and advance are expressions // that are evaluated during the running of the for loop. // The init expression is evaluated once at the top. // The predicate is tested. If it is true then // the body expressions are run. Then the advance // expression is evaluated, and we return to the // predicate test. func (gen *Generator) GenerateForLoop(args []Sexp) error { narg := len(args) if narg < 2 { return fmt.Errorf("malformed for loop") } startgen := 1 var controlargs *SexpArray var labelsym *SexpSymbol var err error foundSym := false switch expr := args[0].(type) { case *SexpSymbol: labelsym = expr foundSym = true startgen = 2 case *SexpPair: labelsym, err = getQuotedSymbol(expr) if err != nil { return ErrBadLoopSyntax } foundSym = true startgen = 2 case *SexpArray: controlargs = expr default: return ErrBadLoopSyntax } if foundSym { switch expr := args[1].(type) { case *SexpArray: controlargs = expr default: return fmt.Errorf("for loop: 2nd argument after the label must be a vector of [init predicate advance]") } } if len(controlargs.Val) != 3 { return fmt.Errorf("for loop: control vector argument wrong size; must be a vector of three [init test advance]") } var loop *Loop if foundSym { loop = &Loop{ stmtname: gen.env.GenSymbol("__loop_" + labelsym.name + "_"), //label: &labelsym, label: labelsym, } } else { loop = &Loop{ stmtname: gen.env.GenSymbol("__loop"), } } gen.env.loopstack.Push(loop) defer gen.env.loopstack.Pop() startPos := len(gen.instructions) gen.AddInstruction(LoopStartInstr{loop: loop}) // A new scope makes it harder to update variables // just outside the loop; one must use (set). // But this is preferred to having nested, sourced // loops use repeat the use variable i in an index and then // end up clobering the parents loop index // inadvertently. gen.AddInstruction(AddScopeInstr{Name: "runtime " + loop.stmtname.name}) gen.AddInstruction(PushStackmarkInstr{sym: loop.stmtname}) // generate the body of the loop subgenBody := NewGenerator(gen.env) subgenBody.Tail = gen.Tail subgenBody.scopes = gen.scopes subgenBody.funcname = gen.funcname err = subgenBody.GenerateBegin(args[startgen:]) if err != nil { return err } // insert pop so the stack remains clean subgenBody.AddInstruction(PopUntilStackmarkInstr{sym: loop.stmtname}) len_body_code := len(subgenBody.instructions) // generate the init code subgenInit := NewGenerator(gen.env) subgenInit.Tail = gen.Tail subgenInit.scopes = gen.scopes subgenInit.funcname = gen.funcname err = subgenInit.Generate(controlargs.Val[0]) if err != nil { return err } // insert pop so the stack remains clean subgenInit.AddInstruction(PopUntilStackmarkInstr{sym: loop.stmtname}) init_code := subgenInit.instructions // generate the test subgenT := NewGenerator(gen.env) subgenT.Tail = gen.Tail subgenT.scopes = gen.scopes subgenT.funcname = gen.funcname err = subgenT.Generate(controlargs.Val[1]) if err != nil { return err } // need to leave value on stack to branch on // so do not popuntil stackmark here! test_code := subgenT.instructions // generate the increment code subgenIncr := NewGenerator(gen.env) subgenIncr.Tail = gen.Tail subgenIncr.scopes = gen.scopes subgenIncr.funcname = gen.funcname err = subgenIncr.Generate(controlargs.Val[2]) if err != nil { return err } subgenIncr.AddInstruction(PopUntilStackmarkInstr{sym: loop.stmtname}) incr_code := subgenIncr.instructions exit_loop := len_body_code + 3 jump_to_test := len(incr_code) + 2 gen.AddInstruction(LabelInstr{label: "start of init for " + loop.stmtname.name}) gen.AddInstructions(init_code) gen.AddInstruction(JumpInstr{addpc: jump_to_test, where: "to-test"}) // top of loop starts with test_code: (continue) target. continuePos := len(gen.instructions) gen.AddInstruction(LabelInstr{label: "start of increment for " + loop.stmtname.name}) gen.AddInstructions(incr_code) gen.AddInstruction(LabelInstr{label: "start of test for " + loop.stmtname.name}) gen.AddInstructions(test_code) gen.AddInstruction(BranchInstr{false, exit_loop}) bodyPos := len(gen.instructions) // Set the break/continue offsets. // // The final coordinates are relative to gen, but the // break statement will only know its own position with // respect to subgenBody, so we use loopStart to tell it // the additional (negative) distance to startPos. gen.AddInstruction(LabelInstr{label: "start of body for " + loop.stmtname.name}) gen.AddInstructions(subgenBody.instructions) gen.AddInstruction(JumpInstr{addpc: continuePos - len(gen.instructions), where: "to-continue-position-aka-increment"}) gen.AddInstruction(LabelInstr{label: "end of body for " + loop.stmtname.name}) // bottom is (break) target bottomPos := len(gen.instructions) // cleanup gen.AddInstruction(ClearStackmarkInstr{sym: loop.stmtname}) gen.AddInstruction(RemoveScopeInstr{}) gen.AddInstruction(PushInstr{SexpNull}) // for is a statement; leave null on the stack. loop.loopStart = startPos - bodyPos // offset; should be negative. //loop.loopLen = len(gen.instructions) - startPos loop.breakOffset = bottomPos - startPos // previously loop.loopLen loop.continueOffset = continuePos - startPos VPrintf("\n debug at end of for loop generation, loop = %#v at address %p\n", loop, loop) return nil } func (gen *Generator) GetLHS(arg Sexp, opname string) (*SexpSymbol, error) { Q("GetLHS (opname=%s) called with arg '%s'", opname, arg.SexpString(nil)) var lhs *SexpSymbol switch expr := arg.(type) { case *SexpSymbol: lhs = expr case *SexpPair: // gracefully handle the quoted symbols we get from macros unquotedSymbol, isQuo := isQuotedSymbol(expr) switch { case isQuo: // auto-unquoting first argument to def lhs = unquotedSymbol.(*SexpSymbol) default: return nil, fmt.Errorf("%s: left-hand-side must be a symbol,"+ " but we have %T/val=%v", opname, expr, expr.SexpString(nil)) } default: return nil, fmt.Errorf("%s: left-hand-side must be a symbol; we have %T", opname, arg) } builtin, typ := gen.env.IsBuiltinSym(lhs) if builtin { return nil, fmt.Errorf("already have %s '%s', refusing to overwrite with %s", typ, lhs.name, opname) } if gen.env.HasMacro(lhs) { return nil, fmt.Errorf("Already have macro named '%s': refusing "+ "to %s variable of same name.", lhs.name, opname) } if lhs.isDot { Q("in GetLHS(), lhs.isDot == true, return lhs='%v'", lhs.SexpString(nil)) //return nil, fmt.Errorf("illegal to %s dot-symbol, attempted on '%s'", opname, lhs.name) } return lhs, nil } // (mdef a b c (list 1 2 3)) will bind a:1 b:2 c:3 func (gen *Generator) GenerateMultiDef(args []Sexp) error { if len(args) < 2 { return fmt.Errorf("Wrong number of arguments to def") } nsym := len(args) - 1 lastpos := len(args) - 1 syms := make([]*SexpSymbol, nsym) for i := 0; i < nsym; i++ { switch sym := args[i].(type) { case *SexpSymbol: syms[i] = sym if gen.env.HasMacro(sym) { return fmt.Errorf("Already have macro named '%s': refusing "+ "to define variable of same name.", sym.name) } case *SexpPair: // gracefully handle the quoted symbols we get from the range macro unquotedSymbol, isQuo := isQuotedSymbol(sym) if isQuo { syms[i] = unquotedSymbol.(*SexpSymbol) } default: return fmt.Errorf("All mdef targets must be symbols, but %d-th was not, instead of type %T: '%s'", i+1, sym, sym.SexpString(nil)) } } gen.Tail = false err := gen.Generate(args[lastpos]) if err != nil { return err } // duplicate the value so def leaves its value // on the stack and becomes an expression rather // than a statement. gen.AddInstruction(DupInstr(0)) gen.AddInstruction(BindlistInstr{syms: syms}) return nil } func isQuotedSymbol(list *SexpPair) (unquotedSymbol Sexp, isQuo bool) { head := list.Head switch h := head.(type) { case *SexpSymbol: if h.name != "quote" { return SexpNull, false } } // good, keep going to tail t := list.Tail switch tt := t.(type) { case *SexpPair: // good, keep going to head hh := tt.Head switch hhh := hh.(type) { case *SexpSymbol: // grab the symbol return hhh, true } } return SexpNull, false } // side-effect (or main effect) has to be pushing an expression on the top of // the datastack that represents the expanded and substituted expression func (gen *Generator) GenerateSyntaxQuote(args []Sexp) error { //Q("GenerateSyntaxQuote() called with args[0]='%#v'", args[0]) if len(args) != 1 { return fmt.Errorf("syntaxQuote takes exactly one argument") } arg := args[0] // need to handle arrays, since they can have unquotes // in them too. switch aaa := arg.(type) { case *SexpArray: gen.generateSyntaxQuoteArray(aaa) return nil case *SexpPair: if !IsList(arg) { break } gen.generateSyntaxQuoteList(arg) return nil case *SexpHash: gen.generateSyntaxQuoteHash(arg) return nil } gen.AddInstruction(PushInstr{arg}) return nil } func (gen *Generator) generateSyntaxQuoteList(arg Sexp) error { //Q("GenerateSyntaxQuoteList() called with arg='%#v'", arg) switch a := arg.(type) { case *SexpPair: //good, required here default: return fmt.Errorf("arg to generateSyntaxQuoteList() must be list; got %T", a) } // things that need unquoting end up as // (unquote mysym) // i.e. a pair // list of length 2 exactly, with first atom // being "unquote" and second being the symbol // to substitute. quotebody, _ := ListToArray(arg) //Q("quotebody = '%#v'", quotebody) if len(quotebody) == 2 { var issymbol bool var sym *SexpSymbol switch t := quotebody[0].(type) { case *SexpSymbol: sym = t issymbol = true default: issymbol = false } if issymbol { if sym.name == "unquote" { VPrintf("detected unquote with quotebody[1]='%#v' arg='%#v'\n", quotebody[1], arg) gen.Generate(quotebody[1]) return nil } else if sym.name == "unquote-splicing" { gen.Generate(quotebody[1]) gen.AddInstruction(ExplodeInstr(0)) return nil } } } gen.AddInstruction(PushInstr{SexpMarker}) for _, expr := range quotebody { gen.GenerateSyntaxQuote([]Sexp{expr}) } gen.AddInstruction(SquashInstr(0)) return nil } func (gen *Generator) generateSyntaxQuoteArray(arg Sexp) error { VPrintf("\n GenerateSyntaxQuoteArray() called with arg='%#v'\n", arg) var arr *SexpArray switch a := arg.(type) { case *SexpArray: //good, required here arr = a default: return fmt.Errorf("arg to generateSyntaxQuoteArray() must be an array; got %T", a) } gen.AddInstruction(PushInstr{SexpMarker}) for _, expr := range arr.Val { gen.AddInstruction(PushInstr{SexpMarker}) gen.GenerateSyntaxQuote([]Sexp{expr}) gen.AddInstruction(SquashInstr(0)) gen.AddInstruction(ExplodeInstr(0)) } gen.AddInstruction(VectorizeInstr(0)) return nil } func (gen *Generator) generateSyntaxQuoteHash(arg Sexp) error { VPrintf("\n GenerateSyntaxQuoteHash() called with arg='%#v'\n", arg) var hash *SexpHash switch a := arg.(type) { case *SexpHash: //good, required here hash = a default: return fmt.Errorf("arg to generateSyntaxQuoteHash() must be a hash; got %T", a) } n := HashCountKeys(hash) gen.AddInstruction(PushInstr{SexpMarker}) for i := 0; i < n; i++ { // must reverse order here to preserve order on rebuild key := hash.KeyOrder[(n-i)-1] val, err := hash.HashGet(nil, key) if err != nil { return err } // value first, since value comes second on rebuild gen.AddInstruction(PushInstr{SexpMarker}) gen.GenerateSyntaxQuote([]Sexp{val}) gen.AddInstruction(SquashInstr(0)) gen.AddInstruction(ExplodeInstr(0)) gen.AddInstruction(PushInstr{SexpMarker}) gen.GenerateSyntaxQuote([]Sexp{key}) gen.AddInstruction(SquashInstr(0)) gen.AddInstruction(ExplodeInstr(0)) } gen.AddInstruction(HashizeInstr{ HashLen: n, TypeName: hash.TypeName, }) return nil } func (gen *Generator) GenerateContinue(args []Sexp) error { if len(args) > 1 { return fmt.Errorf("too many arguments to continue; (continue) or (continue label:) is the form.") } var labelsym *SexpSymbol var err error foundSym := false if len(args) == 1 { switch expr := args[0].(type) { case *SexpSymbol: labelsym = expr foundSym = true case *SexpPair: labelsym, err = getQuotedSymbol(expr) if err != nil { return ErrBadContinueLabel } foundSym = true default: return ErrBadContinueLabel } } if gen.env.loopstack.IsEmpty() { return fmt.Errorf("(continue) found but not inside a loop.") } var loop *Loop isLoop := false n := gen.env.loopstack.Size() matchedTheLabel := false scanUpTheLoops: for i := 0; i < n; i++ { lse, err := gen.env.loopstack.Get(i) if err != nil || lse == nil { return fmt.Errorf("(continue) found but not inside a loop.") } loop, isLoop = lse.(*Loop) if !isLoop { panic(fmt.Errorf("unexpected type found on loopstack: type=%T value='%#v'", lse, lse)) } if !foundSym { break scanUpTheLoops } if loop.label != nil { if loop.label.number == labelsym.number { matchedTheLabel = true Q("\n labeled countinue found matching loop label '%s'\n", labelsym.name) break scanUpTheLoops } } } if foundSym && !matchedTheLabel { return fmt.Errorf("(continue %s:) problem: could not find matching for-loop with label %s:", labelsym.name, labelsym.name) } myPos := len(gen.instructions) VPrintf("\n debug GenerateContinue() : myPos =%d loop=%#v\n", myPos, loop) gen.AddInstruction(&ContinueInstr{loop: loop}) return nil } var ErrBadBreakLabel = fmt.Errorf("bad break label") var ErrBadContinueLabel = fmt.Errorf("bad continue label") func (gen *Generator) GenerateBreak(args []Sexp) error { if len(args) > 1 { return fmt.Errorf("too many arguments to break; (break) or (break label:) is the form.") } var labelsym *SexpSymbol var err error foundSym := false if len(args) == 1 { switch expr := args[0].(type) { case *SexpSymbol: labelsym = expr foundSym = true case *SexpPair: labelsym, err = getQuotedSymbol(expr) if err != nil { return ErrBadBreakLabel } foundSym = true default: return ErrBadBreakLabel } } if gen.env.loopstack.IsEmpty() { return fmt.Errorf("(break) found but not inside a loop.") } var loop *Loop isLoop := false n := gen.env.loopstack.Size() matchedTheLabel := false scanUpTheLoops: for i := 0; i < n; i++ { lse, err := gen.env.loopstack.Get(i) if err != nil || lse == nil { return fmt.Errorf("(break) found but not inside a loop.") } loop, isLoop = lse.(*Loop) if !isLoop { panic(fmt.Errorf("unexpected type found on loopstack: type=%T value='%#v'", lse, lse)) } if !foundSym { break scanUpTheLoops } if loop.label != nil { if loop.label.number == labelsym.number { matchedTheLabel = true Q("\n labeled break found matching loop label '%s'\n", labelsym.name) break scanUpTheLoops } } } if foundSym && !matchedTheLabel { return fmt.Errorf("(break %s:) problem: could not find matching for-loop with label %s:", labelsym.name, labelsym.name) } VPrintf("\n debug GenerateBreak() : loop=%#v\n", loop) gen.AddInstruction(&BreakInstr{loop: loop}) return nil } // like begin, but puts its contents in a new scope func (gen *Generator) GenerateNewScope(expressions []Sexp) error { size := len(expressions) oldtail := gen.Tail gen.Tail = false if size == 0 { return nil //return NoExpressionsFound } gen.AddInstruction(AddScopeInstr{Name: "newScope"}) for _, expr := range expressions[:size-1] { err := gen.Generate(expr) if err != nil { return err } // insert pops after all but the last instruction // that way the stack remains clean... Q: how does this extra popping not mess up the expressions? gen.AddInstruction(PopInstr(0)) } gen.Tail = oldtail err := gen.Generate(expressions[size-1]) if err != nil { return err } gen.AddInstruction(RemoveScopeInstr{}) return nil } func (gen *Generator) GeneratePackage(expressions []Sexp) error { size := len(expressions) if size < 1 { return WrongNargs } var pkgName string expr := expressions[0] switch v := expr.(type) { case *SexpSymbol: pkgName = v.name case *SexpStr: pkgName = v.S default: return fmt.Errorf("package name must be symbol or string") } // use GenSymbol so we are sure to have a unique point // to aim at when cleaning up. symPkgName := gen.env.GenSymbol(pkgName) oldtail := gen.Tail gen.Tail = false gen.AddInstruction(AddScopeInstr{Name: pkgName}) gen.AddInstruction(PushStackmarkInstr{sym: symPkgName}) if size > 1 { for _, expr := range expressions[1 : size-1] { err := gen.Generate(expr) if err != nil { return err } } } gen.Tail = oldtail err := gen.Generate(expressions[size-1]) if err != nil { return err } gen.AddInstruction(PopUntilStackmarkInstr{sym: symPkgName}) gen.AddInstruction(PopInstr(0)) // remove the stackmark itself now gen.AddInstruction(PopScopeTransferToDataStackInstr{PackageName: pkgName}) return nil } func (gen *Generator) GenerateDebug(diag string) error { gen.AddInstruction(DebugInstr{diagnostic: diag}) gen.AddInstruction(PushInstr{SexpNull}) return nil } var ErrBadQuotedSym = fmt.Errorf("not a quoted symbol") // insist that expr is of the form '(quote mysymbol)', // and return mysymbol, nil if it is. func getQuotedSymbol(expr *SexpPair) (*SexpSymbol, error) { n, err := ListLen(expr) if err != nil { return &SexpSymbol{}, ErrBadQuotedSym } if n != 2 { return &SexpSymbol{}, ErrBadQuotedSym } qu, isSym := expr.Head.(*SexpSymbol) if !isSym { return &SexpSymbol{}, ErrBadQuotedSym } if qu.name != "quote" { return &SexpSymbol{}, ErrBadQuotedSym } eth := expr.Tail.(*SexpPair).Head labelsym, isSym := eth.(*SexpSymbol) if !isSym { return &SexpSymbol{}, ErrBadQuotedSym } return labelsym, nil } func (gen *Generator) GenerateReturn(xs []Sexp) error { n := len(xs) if n == 0 { return nil } if n > 1 { gen.AddInstruction(PushInstr{SexpMarker}) } for i := range xs { Q("return calling Generate on xs[i=%v]=%v", i, xs[i].SexpString(nil)) err := gen.Generate(xs[i]) if err != nil { return err } } if n > 1 { gen.AddInstruction(VectorizeInstr(0)) } return nil }