ginger/expr/expr.go
2016-08-05 11:44:12 -06:00

419 lines
10 KiB
Go

package expr
import (
"fmt"
"strings"
"llvm.org/llvm/bindings/go/llvm"
"github.com/mediocregopher/ginger/lexer"
)
// TODO empty blocks?
// TODO empty parenthesis
// TODO need to figure out how to test LLVMVal stuff
// TODO once we're a bit more confident, make ActualFunc
// TODO LLVMVal -> LLVMBuild?
type LLVMCtx struct {
B llvm.Builder
M llvm.Module
}
// Actual represents the actual expression in question, and has certain
// properties. It is wrapped by Expr which also holds onto contextual
// information, like the token to which Actual was originally parsed from
type Actual interface {
// Returns the llvm.Type which the expression accepts as an input, if any
LLVMInType(ctx *Ctx) llvm.Type
// Returns the llvm.Type which the expressions outputs
LLVMOutType(ctx *Ctx) llvm.Type
// Initializes an llvm.Value and returns it.
LLVMVal(*Ctx, LLVMCtx) llvm.Value
}
// equaler is used to compare two expressions. The comparison should not take
// into account Token values, only the actual value being represented
type equaler interface {
equal(equaler) bool
}
// Expr contains the actual expression as well as some contextual information
// wrapping it. Most interactions will be with this and not with the Actual
// directly.
type Expr struct {
Actual Actual
// Token is a nice-to-have, nothing will break if it's not there
Token lexer.Token
val *llvm.Value
}
// LLVMInType passes through to the method on the underlying Actual
func (e Expr) LLVMInType(ctx *Ctx) llvm.Type {
return e.Actual.LLVMInType(ctx)
}
// LLVMOutType passes through to the method on the underlying Actual
func (e Expr) LLVMOutType(ctx *Ctx) llvm.Type {
return e.Actual.LLVMOutType(ctx)
}
// 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(ctx *Ctx, lctx LLVMCtx) llvm.Value {
if e.val == nil {
v := e.Actual.LLVMVal(ctx, lctx)
e.val = &v
}
return *e.val
}
// will panic if either Expr's Actual doesn't implement equaler
func (e Expr) equal(e2 Expr) bool {
eq1, ok1 := e.Actual.(equaler)
eq2, ok2 := e2.Actual.(equaler)
if !ok1 || !ok2 {
panic(fmt.Sprintf("can't compare %T and %T", e.Actual, e2.Actual))
}
return eq1.equal(eq2)
}
////////////////////////////////////////////////////////////////////////////////
// Void represents no data (size = 0)
type Void struct{}
// LLVMInType implements the Actual interface method
func (v Void) LLVMInType(ctx *Ctx) llvm.Type {
panic("Void has no InType")
}
// LLVMOutType implements the Actual interface method
func (v Void) LLVMOutType(ctx *Ctx) llvm.Type {
return llvm.VoidType()
}
// LLVMVal implements the Actual interface method
func (v Void) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
// Kind of weird that this only works for return type, but I guess makes
// sense
return lctx.B.CreateRetVoid()
}
////////////////////////////////////////////////////////////////////////////////
// Bool represents a true or false value
type Bool bool
// LLVMInType implements the Actual interface method
func (b Bool) LLVMInType(ctx *Ctx) llvm.Type {
panic("Bool has no InType")
}
// LLVMOutType implements the Actual interface method
func (b Bool) LLVMOutType(ctx *Ctx) llvm.Type {
return llvm.IntType(1)
}
// LLVMVal implements the Actual interface method
func (b Bool) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
return llvm.Value{}
}
func (b Bool) equal(e equaler) bool {
bb, ok := e.(Bool)
if !ok {
return false
}
return bb == b
}
////////////////////////////////////////////////////////////////////////////////
// Int represents an integer value
type Int int64
// LLVMInType implements the Actual interface method
func (i Int) LLVMInType(ctx *Ctx) llvm.Type {
panic("Int has no InType")
}
// LLVMOutType implements the Actual interface method
func (i Int) LLVMOutType(ctx *Ctx) llvm.Type {
return llvm.Int64Type()
}
// LLVMVal implements the Actual interface method
func (i Int) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
v := lctx.B.CreateAlloca(llvm.Int64Type(), "")
lctx.B.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v)
return v
}
func (i Int) equal(e equaler) bool {
ii, ok := e.(Int)
if !ok {
return false
}
return ii == i
}
////////////////////////////////////////////////////////////////////////////////
// String represents a string value
type String string
// LLVMInType implements the Actual interface method
func (s String) LLVMInType(ctx *Ctx) llvm.Type {
panic("String has no InType")
}
// LLVMOutType implements the Actual interface method
func (s String) LLVMOutType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMVal implements the Actual interface method
func (s String) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
panic("TODO")
}
func (s String) equal(e equaler) bool {
ss, ok := e.(String)
if !ok {
return false
}
return ss == s
}
////////////////////////////////////////////////////////////////////////////////
// Identifier represents a binding to some other value which has been given a
// name
type Identifier string
// LLVMInType implements the Actual interface method
func (id Identifier) LLVMInType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMOutType implements the Actual interface method
func (id Identifier) LLVMOutType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMVal implements the Actual interface method
func (id Identifier) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
panic("TODO")
}
func (id Identifier) equal(e equaler) bool {
idid, ok := e.(Identifier)
if !ok {
return false
}
return idid == id
}
////////////////////////////////////////////////////////////////////////////////
// Macro is an identifier for a macro which can be used to transform
// expressions. The tokens for macros start with a '%', but the Macro identifier
// itself has that stripped off
type Macro string
// String returns the Macro with a '%' prepended to it
func (m Macro) String() string {
return "%" + string(m)
}
// LLVMInType implements the Actual interface method
func (m Macro) LLVMInType(ctx *Ctx) llvm.Type {
panic("Macro has no InType")
}
// LLVMOutType implements the Actual interface method
func (m Macro) LLVMOutType(ctx *Ctx) llvm.Type {
panic("Macro has no OutType")
}
// LLVMVal implements the Actual interface method
func (m Macro) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
panic("Macro has no Val")
}
func (m Macro) equal(e equaler) bool {
mm, ok := e.(Macro)
if !ok {
return false
}
return m == mm
}
////////////////////////////////////////////////////////////////////////////////
// Tuple represents a fixed set of expressions which are interacted with as if
// they were a single value
type Tuple []Expr
func (tup Tuple) String() string {
strs := make([]string, len(tup))
for i := range tup {
strs[i] = fmt.Sprint(tup[i].Actual)
}
return "(" + strings.Join(strs, ", ") + ")"
}
// LLVMInType implements the Actual interface method
func (tup Tuple) LLVMInType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMOutType implements the Actual interface method
func (tup Tuple) LLVMOutType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMVal implements the Actual interface method
func (tup Tuple) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
panic("TODO")
}
func (tup Tuple) equal(e equaler) bool {
tuptup, ok := e.(Tuple)
if !ok || len(tuptup) != len(tup) {
return false
}
for i := range tup {
if !tup[i].equal(tuptup[i]) {
return false
}
}
return true
}
////////////////////////////////////////////////////////////////////////////////
// Statement represents an actual action which will be taken. The input value is
// used as the input to the pipe, and the output of the pipe is the output of
// the statement
type Statement struct {
In Expr
To Expr
}
func (s Statement) String() string {
return fmt.Sprintf("(%v > %s)", s.In.Actual, s.To.Actual)
}
func (s Statement) maybeMacro(ctx *Ctx) (Expr, bool) {
m, ok := s.To.Actual.(Macro)
if !ok {
return Expr{}, false
}
fn := ctx.GetMacro(m)
if fn == nil {
return Expr{}, false
}
newe, err := fn(s.In)
if err != nil {
// TODO proper error
panic(err)
}
return newe, true
}
// LLVMInType implements the Actual interface method
func (s Statement) LLVMInType(ctx *Ctx) llvm.Type {
if newe, ok := s.maybeMacro(ctx); ok {
return newe.LLVMInType(ctx)
}
// TODO futures
panic("unknown Statement.To")
}
// LLVMOutType implements the Actual interface method
func (s Statement) LLVMOutType(ctx *Ctx) llvm.Type {
if newe, ok := s.maybeMacro(ctx); ok {
return newe.LLVMOutType(ctx)
}
panic("unknown Statement.To")
}
// LLVMVal implements the Actual interface method
func (s Statement) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
if newe, ok := s.maybeMacro(ctx); ok {
return newe.LLVMVal(ctx, lctx)
}
panic("unknown Statement.To")
}
func (s Statement) equal(e equaler) bool {
ss, ok := e.(Statement)
return ok && s.In.equal(ss.In) && s.To.equal(ss.To)
}
////////////////////////////////////////////////////////////////////////////////
// Block represents a set of statements which share a scope, i.e. If one
// statement binds a variable the rest of the statements in the block can use
// that variable, including sub-blocks within this one.
type Block []Expr
func (b Block) String() string {
strs := make([]string, len(b))
for i := range b {
strs[i] = b[i].Actual.(Statement).String()
}
return fmt.Sprintf("{ %s }", strings.Join(strs, " "))
}
// LLVMInType implements the Actual interface method
func (b Block) LLVMInType(ctx *Ctx) llvm.Type {
panic("TODO")
}
// LLVMOutType implements the Actual interface method
func (b Block) LLVMOutType(ctx *Ctx) llvm.Type {
return b[len(b)-1].LLVMOutType(ctx)
}
// LLVMVal implements the Actual interface method
func (b Block) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
name := randStr() // TODO make this based on token
// TODO make these based on actual statements
out := llvm.Int64Type()
in := []llvm.Type{}
fn := llvm.AddFunction(lctx.M, name, llvm.FunctionType(out, in, false))
block := llvm.AddBasicBlock(fn, "entry")
lctx.B.SetInsertPoint(block, block.FirstInstruction())
var v llvm.Value
for _, se := range b {
v = se.Actual.LLVMVal(ctx, lctx)
}
// last v is used as return
// TODO empty return
lctx.B.CreateRet(v)
return fn
}
func (b Block) equal(e equaler) bool {
bb, ok := e.(Block)
if !ok {
return false
}
for i := range b {
if !b[i].equal(bb[i]) {
return false
}
}
return true
}