diff --git a/expr/build.go b/expr/build.go index a599861..519759a 100644 --- a/expr/build.go +++ b/expr/build.go @@ -23,13 +23,7 @@ func (bctx BuildCtx) Build(stmts ...Statement) llvm.Value { func (bctx BuildCtx) BuildStmt(s Statement) Expr { m := s.Op.(Macro) - - fn := bctx.C.GetMacro(m) - if fn == nil { - panicf("unknown macro: %q", m) - } - - return fn(bctx, s.Arg) + return bctx.C.Macro(m)(bctx, s.Arg) } // may return nil if e is a Statement which has no return @@ -50,10 +44,7 @@ func (bctx BuildCtx) buildExprTill(e Expr, fn func(e Expr) bool) Expr { case Int: return llvmVal(llvm.ConstInt(llvm.Int64Type(), uint64(ea), false)) case Identifier: - if ev := bctx.C.GetIdentifier(ea); ev != nil { - return ev - } - panicf("identifier %q not found", ea) + return bctx.C.Identifier(ea) case Statement: return bctx.BuildStmt(ea) case Tuple: @@ -85,10 +76,7 @@ var globalCtx = &Ctx{ "bind": func(bctx BuildCtx, e Expr) Expr { tup := bctx.buildExprTill(e, isIdentifier).(Tuple) id := bctx.buildExprTill(tup[0], isIdentifier).(Identifier) - if bctx.C.idents[id] != nil { - panicf("identifier %q is already bound", id) - } - bctx.C.idents[id] = bctx.buildExpr(tup[1]) + *bctx.C = bctx.C.Bind(id, bctx.buildExpr(tup[1])) return nil }, }, diff --git a/expr/ctx.go b/expr/ctx.go index b54f7d7..4df708f 100644 --- a/expr/ctx.go +++ b/expr/ctx.go @@ -14,17 +14,17 @@ type Ctx struct { } // NewCtx returns a blank context instance -func NewCtx() *Ctx { - return &Ctx{ +func NewCtx() Ctx { + return Ctx{ global: globalCtx, macros: map[Macro]MacroFn{}, idents: map[Identifier]Expr{}, } } -// GetMacro returns the MacroFn associated with the given identifier, or panics +// Macro returns the MacroFn associated with the given identifier, or panics // if the macro isn't found -func (c *Ctx) GetMacro(m Macro) MacroFn { +func (c Ctx) Macro(m Macro) MacroFn { if fn := c.macros[m]; fn != nil { return fn } @@ -35,25 +35,39 @@ func (c *Ctx) GetMacro(m Macro) MacroFn { return nil } -// GetIdentifier returns the llvm.Value for the Identifier, or panics -func (c *Ctx) GetIdentifier(i Identifier) Expr { +// Identifier returns the llvm.Value for the Identifier, or panics +func (c Ctx) Identifier(i Identifier) Expr { + if e := c.idents[i]; e != nil { + return e + } // The global context doesn't have any identifiers, so don't bother checking - return c.idents[i] + panicf("identifier %q not found", i) + panic("go is dumb") } -// NewWith returns a new Ctx instance which imports the given macros from the -// parent -//func (c *Ctx) NewWith(mm ...Macro) *Ctx { -// nc := &Ctx{ -// global: c.global, -// macros: map[Macro]MacroFn{}, -// } -// for _, m := range mm { -// fn := c.macros[m] -// if fn == nil { -// panicf("no macro %q found in context", m) -// } -// nc.macros[m] = fn -// } -// return nc -//} +func (c Ctx) cp() Ctx { + cc := Ctx{ + global: c.global, + macros: make(map[Macro]MacroFn, len(c.macros)), + idents: make(map[Identifier]Expr, len(c.idents)), + } + for m, mfn := range c.macros { + cc.macros[m] = mfn + } + for i, e := range c.idents { + cc.idents[i] = e + } + return cc +} + +// Bind returns a new Ctx which is a copy of this one, but with the given +// Identifier bound to the given Expr. Will panic if the Identifier is already +// bound +func (c Ctx) Bind(i Identifier, e Expr) Ctx { + if _, ok := c.idents[i]; ok { + panicf("identifier %q is already bound", i) + } + c = c.cp() + c.idents[i] = e + return c +} diff --git a/main.go b/main.go index 13e358b..8bdfb1b 100644 --- a/main.go +++ b/main.go @@ -22,8 +22,9 @@ func main() { llvm.InitializeNativeAsmPrinter() // setup our context, builder, and module + ctx := expr.NewCtx() bctx := expr.BuildCtx{ - C: expr.NewCtx(), + C: &ctx, B: llvm.NewBuilder(), M: llvm.NewModule("my_module"), }