diff --git a/lang/Compiler.go b/lang/Compiler.go index 18bdaf1..8f6a289 100644 --- a/lang/Compiler.go +++ b/lang/Compiler.go @@ -3,6 +3,7 @@ package lang import ( "io" "math/rand" + "golang.org/x/net/context" ) const ( @@ -50,10 +51,18 @@ var loadNs *Symbol = InternSymbolByNsname("load-ns") var inlineKey *Symbol = InternSymbolByNsname("inline") // TODO: more declarations here... - +var KEYWORDS *Var = CreateVarFromNothing().SetDynamic() var COMPILE_PATH *Var = InternVar(FindOrCreateNamespace(InternSymbolByNsname("clojure.core")), InternSymbolByNsname("*compile-path*"), nil).SetDynamic() +type ObjExpr struct { + name string + internalName string + thisName string + keywords IPersistentMap + constants *PersistentVector +} + /* Compiler struct and methods */ @@ -62,8 +71,10 @@ type compiler struct{} var Compiler = &compiler{} +// TODO func (_ *compiler) CurrentNS() *Namespace { - return CURRENT_NS.Deref().(*Namespace) + panic(NotYetImplementedException) + // return CURRENT_NS.Deref().(*Namespace) } func (_ *compiler) NamespaceFor(inns *Namespace, sym *Symbol) *Namespace { @@ -90,9 +101,11 @@ func (_ *compiler) Macroexpand(form interface{}) interface{} { func (_ *compiler) Compile(rdr *io.Reader, sourcePath string, sourceName string) interface{} { // TODO: Do we need this? I don't know. // #VESTIGIAL + /* if COMPILE_PATH.Deref() == nil { panic("*compile-path* not set") } + */ var EOF int = rand.Int() // TODO: Sentinel value var ret interface{} @@ -105,31 +118,33 @@ func (_ *compiler) Compile(rdr *io.Reader, sourcePath string, sourceName string) } // In JVM Clojure, gen is a GeneratorAdapter. We don't have an analog for that here. -func (_ *compiler) Compile1(gen interface{}, objx ObjExpr, form interface{}) { +func (_ *compiler) Compile1(ctx context.Context, gen interface{}, objx ObjExpr, form interface{}) { // TODO: some initial set-up. - // try, catch (might want better error handling here) + ctx = + // try block begins here form = Compiler.Macroexpand(form) switch f := form.(type) { case ISeq: if Util.Equals(RT.First(form), DO) { for s := RT.Next(form); s != nil; s = RT.Next(s) { - Compiler.Compile1(gen, objx, RT.First(s)) + Compiler.Compile1(ctx, gen, objx, RT.First(s)) } } default: expr := Compiler.Analyze(EVAL, form) - objx.keywords = KEYWORDS.Deref() - objx.vars = VAR.Deref() - objx.constants = CONSTANTS.Deref() + objx.keywords = KEYWORDS.Deref().(IPersistentMap) + objx.vars = VAR.Deref().(IPersistentMap) + objx.constants = CONSTANTS.Deref().(*PersistentVector) expr.Emit(EXPRESSION, objx, gen) expr.Eval() } - // TODO: Var.Pop thread bindings } func (_ *compiler) Eval(form interface{}, freshLoader bool) interface{} { createdLoader := false // do we need this? +} - +func (_ *compiler) Analyze(a interface{}, b interface{}) interface{} { + panic(NotYetImplementedException) } \ No newline at end of file diff --git a/lang/Var.go b/lang/Var.go index 0f99a89..7baa8bd 100644 --- a/lang/Var.go +++ b/lang/Var.go @@ -2,14 +2,11 @@ package lang import ( "bytes" - "sync" - + "golang.org/x/net/context" ) /* Assorted class variables - - */ // TODO: I found this really useful: http://www.golangbootcamp.com/book/concurrency @@ -26,18 +23,30 @@ var rev int = 0 // maybe atomic integer since this will be shared. /* Var + Extends: ARef + Implements: IFn, IRef, Settable + + Notes: in JVM Clojure, the implementation of Vars is heavily dependent on the JVM's support + for thread-local storage. In the almost total absence of a simple analog here in Go, I've + chosen to instead use the x/net/context package instead as a way of passing explicit + bindings. */ type Var struct { ARef + // AReference _meta IPersistentMap + + // ARef + validator IFn + watches *PersistentHashMap // default EMPTY + rev int privateMeta IPersistentMap root interface{} - dyanmic bool - threadBound bool // atomicboolean TODO + dynamic bool sym *Symbol ns *Namespace } @@ -63,15 +72,15 @@ func (v *Var) String() string { func (v *Var) SetDynamic(args ...bool) *Var { if len(args) == 0 { - v.dyanmic = true + v.dynamic = true } else { - v.dyanmic = args[0] + v.dynamic = args[0] } return v } func (v *Var) IsDynamic() bool { - return v.IsDynamic() + return v.dynamic } func InternVar(ns *Namespace, sym *Symbol, root interface{}) *Var { @@ -112,7 +121,6 @@ func CreateVarFromNothing() *Var { return &Var{ ns: nil, sym: nil, - threadBound: false, // TODOAtomicBoolean root: VarUnbound{ v: v, }, @@ -127,23 +135,53 @@ func CreateVarFromRoot(root interface{}) *Var { } } -func (v *Var) isBound() bool { - // TODO - panic(NotYetImplementedException) +// unexported type and key for var context utility functions +type key int +var varKey key = 0 + +// Set the new Var root for the provided context +func setVarBindingForContext(ctx context.Context, val interface{}) context.Context { + return context.WithValue(ctx, varKey, val) } -// TODO -func (v *Var) Get() interface{} { - panic(NotYetImplementedException) +// Retrieve the new Var root from the provided context +func getVarBindingFromContext(ctx context.Context) (*Frame, bool) { + r, ok := ctx.Value(varKey).(*Frame) + return r, ok } -// TODO: this is a naive and stupid implementation -func (v *Var) Deref() interface{} { +func (v *Var) isBound(ctx context.Context) bool { + r, ok := getVarBindingFromContext(ctx) + if ok == false { + panic("Stored var value wasn't an instance of *lang.Frame") + } + return v.HasRoot() || r != nil && r.bindings.ContainsKey(v) +} + +func (v *Var) Get(ctx context.Context) interface{} { + r, ok := getVarBindingFromContext(ctx) + if ok == false { + panic("Stored var value wasn't an instance of *lang.Frame") + } + if r == nil { + return v.root + } + return v.Deref(ctx) +} + +func (v *Var) Deref(ctx context.Context) interface{} { + r, ok := getVarBindingFromContext(ctx) + if (r != nil) { + return r.bindings.EntryAt(v) + } return v.root } // TODO func (v *Var) SetValidator(vf IFn) { + if v.HasRoot() { + v.validate() + } panic(NotYetImplementedException) } @@ -213,35 +251,26 @@ func (v *Var) BindRoot(root interface{}) { v.root = root } -func (v *Var) Fn() IFn { - return v.Deref().(IFn) +func (v *Var) Fn(ctx context.Context) IFn { + return v.Deref(ctx).(IFn) } -func (v *Var) Call() interface{} { - return v.Invoke() +func (v *Var) Call(ctx context.Context) interface{} { + return v.Invoke(ctx) } -func (v *Var) Run() { - v.Invoke() +func (v *Var) Run(ctx context.Context) { + v.Invoke(ctx) } -func (v *Var) Invoke(args ...interface{}) interface{} { - return v.Fn().Invoke(args...) +func (v *Var) Invoke(ctx context.Context, args ...interface{}) interface{} { + return v.Fn(ctx).Invoke(args...) } func (v *Var) ApplyTo(arglist ISeq) interface{} { return AFn_ApplyToHelper(v, arglist) } -/* - VarTBox [TBox] -*/ - -type VarTBox struct { - val interface{} - lock *sync.Mutex // instead of keeping track of thread -} - /* VarUnbound [Unbound] */ @@ -284,36 +313,9 @@ func (f *Frame) Clone() interface{} { } } -/* - VarFrameDvals - - This class is just a ThreadLocal in JVM Clojure with an initialValue() func. -*/ - -type VarFrameDvals struct { - Frame - - lock *sync.Mutex -} - -func (d *VarFrameDvals) InitialValue() *Frame { - return TOP_FRAME -} - -// TODO -func getThreadBindingFrame() interface{} { - return nil -} - -// TODO -func cloneThreadBindingFrame() interface{} { - return nil -} - -// TODO -func resetThreadBindingFrame() interface{} { - return nil -} +// In JVM Clojure, dvals is a ThreadLocal. Here we pass it around in a context. +// First we initialize dvals to TOP_FRAME +var dvals *Frame = TOP_FRAME // TODO...still most of this file. @@ -326,4 +328,4 @@ var assoc = &assocAnon{} func (a *assocAnon) Invoke(args ...interface{}) interface{} { m, k, v := args[0], args[1], args[2] return RT.Assoc(m, k, v) -} +} \ No newline at end of file