diff --git a/expr/ctx.go b/expr/ctx.go new file mode 100644 index 0000000..67592a8 --- /dev/null +++ b/expr/ctx.go @@ -0,0 +1,24 @@ +package expr + +// Ctx contains all the Macros and Identifiers available. A Ctx is based on the +// parent it was created from. If the current Ctx doesn't have a particular key +// being looked up, the parent is called instead, and so on. A consequence of +// this is that keys in the children take precedence over the parent's +type Ctx struct { + Parent *Ctx + Macros map[Macro]func(Expr) (Expr, error) +} + +// GetMacro returns the first instance of the given of the given Macro found. If +// not found nil is returned. +func (c *Ctx) GetMacro(m Macro) func(Expr) (Expr, error) { + if c.Macros != nil { + if fn, ok := c.Macros[m]; ok { + return fn + } + } + if c.Parent != nil { + return c.Parent.GetMacro(m) + } + return nil +} diff --git a/expr/expr.go b/expr/expr.go index 4054b9c..fa49081 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -19,7 +19,7 @@ import ( // information, like the token to which Actual was originally parsed from type Actual interface { // Initializes an llvm.Value and returns it. - LLVMVal(llvm.Builder) llvm.Value + LLVMVal(*Ctx, llvm.Builder) llvm.Value } // equaler is used to compare two expressions. The comparison should not take @@ -43,9 +43,9 @@ type Expr struct { // LLVMVal passes its arguments to the underlying Actual instance. It caches the // result, so if this is called multiple times the underlying one is only called // the first time. -func (e Expr) LLVMVal(builder llvm.Builder) llvm.Value { +func (e Expr) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { if e.val == nil { - v := e.Actual.LLVMVal(builder) + v := e.Actual.LLVMVal(ctx, builder) e.val = &v } return *e.val @@ -67,7 +67,7 @@ func (e Expr) equal(e2 Expr) bool { type Bool bool // LLVMVal implements the Actual interface method -func (b Bool) LLVMVal(builder llvm.Builder) llvm.Value { +func (b Bool) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { return llvm.Value{} } @@ -85,7 +85,7 @@ func (b Bool) equal(e equaler) bool { type Int int64 // LLVMVal implements the Actual interface method -func (i Int) LLVMVal(builder llvm.Builder) llvm.Value { +func (i Int) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { v := builder.CreateAlloca(llvm.Int64Type(), "") builder.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v) return v @@ -105,7 +105,7 @@ func (i Int) equal(e equaler) bool { type String string // LLVMVal implements the Actual interface method -func (s String) LLVMVal(builder llvm.Builder) llvm.Value { +func (s String) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { return llvm.Value{} } @@ -124,7 +124,7 @@ func (s String) equal(e equaler) bool { type Identifier string // LLVMVal implements the Actual interface method -func (id Identifier) LLVMVal(builder llvm.Builder) llvm.Value { +func (id Identifier) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { return llvm.Value{} } @@ -149,7 +149,7 @@ func (m Macro) String() string { } // LLVMVal implements the Actual interface method -func (m Macro) LLVMVal(builder llvm.Builder) llvm.Value { +func (m Macro) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { panic("Macros have no inherent LLVMVal") } @@ -176,7 +176,7 @@ func (tup Tuple) String() string { } // LLVMVal implements the Actual interface method -func (tup Tuple) LLVMVal(builder llvm.Builder) llvm.Value { +func (tup Tuple) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { return llvm.Value{} } @@ -208,14 +208,15 @@ func (s Statement) String() string { } // LLVMVal implements the Actual interface method -func (s Statement) LLVMVal(builder llvm.Builder) llvm.Value { +func (s Statement) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { m, ok := s.To.Actual.(Macro) if !ok { // TODO proper error panic("statement To is not a macro") } - fn, ok := macros[m] - if !ok { + + fn := ctx.GetMacro(m) + if fn == nil { // TODO proper error panic(fmt.Sprintf("unknown macro %q", m)) } @@ -224,7 +225,7 @@ func (s Statement) LLVMVal(builder llvm.Builder) llvm.Value { // TODO proper error panic(err) } - return newe.LLVMVal(builder) + return newe.LLVMVal(ctx, builder) } func (s Statement) equal(e equaler) bool { @@ -248,7 +249,7 @@ func (b Block) String() string { } // LLVMVal implements the Actual interface method -func (b Block) LLVMVal(builder llvm.Builder) llvm.Value { +func (b Block) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { return llvm.Value{} } diff --git a/expr/macros.go b/expr/macros.go index 29b83c6..6ca2563 100644 --- a/expr/macros.go +++ b/expr/macros.go @@ -8,25 +8,30 @@ import ( type addActual []Expr -func (aa addActual) LLVMVal(builder llvm.Builder) llvm.Value { - a := builder.CreateLoad(aa[0].LLVMVal(builder), "") +func (aa addActual) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value { + a := builder.CreateLoad(aa[0].LLVMVal(ctx, builder), "") for i := range aa[1:] { - b := builder.CreateLoad(aa[i+1].LLVMVal(builder), "") + b := builder.CreateLoad(aa[i+1].LLVMVal(ctx, builder), "") a = builder.CreateAdd(a, b, "") } return a } -var macros = map[Macro]func(Expr) (Expr, error){ - "add": func(e Expr) (Expr, error) { - tup, ok := e.Actual.(Tuple) - if !ok { - // TODO proper error - return Expr{}, errors.New("add only accepts a tuple") - } - return Expr{ - Actual: addActual(tup), - Token: e.Token, - }, nil +// RootCtx describes what's available to *all* contexts, and is what all +// contexts should have as the root parent in the tree +var RootCtx = &Ctx{ + Macros: map[Macro]func(Expr) (Expr, error){ + "add": func(e Expr) (Expr, error) { + tup, ok := e.Actual.(Tuple) + if !ok { + // TODO proper error + return Expr{}, errors.New("add only accepts a tuple") + } + // TODO check that it's a tuple of integers too + return Expr{ + Actual: addActual(tup), + Token: e.Token, + }, nil + }, }, } diff --git a/main.go b/main.go index e33217d..980b069 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func main() { addMacro := expr.Expr{Actual: expr.Macro("add")} stmt := expr.Expr{Actual: expr.Statement{In: tup, To: addMacro}} - result := stmt.LLVMVal(builder) + result := stmt.LLVMVal(expr.RootCtx, builder) builder.CreateRet(result) // verify it's all good