package attl // Maximum amount of frames, // to prevent unlimited recursion. const FRAMES_MAX = 80 type Writer interface { Write(p []byte) (n int, err error) } type Reader interface { Read(p []byte) (n int, err error) } type Frame struct { Variables Map Effect Out Writer In Reader Rescuer Value } type Environment struct { Frames []*Frame Out Writer In Reader Rescuing bool } // Looks up the value of a variable and the frame it is in func (env Environment) LookupFrame(name string) (Value, *Frame) { for i := len(env.Frames) - 1; i >= 0; i-- { frame := env.Frames[i] val, ok := frame.Variables[name] if ok { return val, frame } } return nil, nil } func (env Environment) Lookup(name string) Value { val, _ := env.LookupFrame(name) return val } func (env *Environment) Push() *Error { env.Frames = append(env.Frames, &Frame{make(Map), nil, env.Out, env.In, nil}) if len(env.Frames) >= FRAMES_MAX && !env.Rescuing { return ErrorFromString("PROGRAM HAS DISAPPEARED INTO THE BLACK LAGOON - too much recursion or function calls") } return nil } func (env *Environment) Pop() { l := len(env.Frames) if l > 0 { env.Frames = env.Frames[0 : l-1] } } // Depth returns the amount of frames on the frame stack func (env *Environment) Depth() int { return len(env.Frames) } // Frame returns a frame pointer based on the level. // 0 is the top-level index. A negative level wil refer to the // outermost frame. // Returns nil if the level is somehow out of range or // if no frames have been pushed yet func (env *Environment) Frame(level int) *Frame { if len(env.Frames) < 1 { return nil } if level > 0 { l := len(env.Frames) index := l - level - 1 if index < 0 || index >= l { return nil } return env.Frames[index] } else { return env.Frames[0] } } // Top returns a pointer to the top or // inner most Frame of the environment's frame stack func (env *Environment) Top() *Frame { if len(env.Frames) > 0 { index := len(env.Frames) - 1 return env.Frames[index] } return nil } // Botttom returns a pointer to the bottom or// outer most Frame of the environment func (env *Environment) Bottom() *Frame { if len(env.Frames) > 0 { return env.Frames[0] } return nil } // Defines the variable in the given scope level func (env *Environment) Define(name string, val Value, level int) (Value, Effect) { frame := env.Frame(level) if frame == nil { return nil, ErrorFromString("no such frame available.") } frame.Variables[name] = val return val, nil } // Looks up the variable and sets it in the scope where it is found. // Returns an error if no such variable could be found. func (env *Environment) Set(name string, val Value) (Value, Effect) { _, frame := env.LookupFrame(name) if frame == nil { return nil, ErrorFromString("no such variable") } frame.Variables[name] = val return val, nil } func (env *Environment) Rescuer() Value { frame := env.Top() if frame == nil { return nil } return frame.Rescuer } // Prevent sets the rescue block to use for // the top frame of the environment. // It returns the previous rescuer. func (env *Environment) Prevent(block Block) (Value, Effect) { frame := env.Frame(1) if frame == nil { return env.FailString("Could not set rescuer") } old := frame.Rescuer frame.Rescuer = Rescue{block} return old, nil } // func (env *Environment) Rescue(res Value, eff Effect) (Value, Effect) { if eff == nil || eff.Flow() < FailFlow { return res, eff } // if there is no rescue installed, // just return as is. if env.Rescuer() == nil { return res, eff } // failures become normal returns // if the rescue didn't fail. val := eff.Unwrap() rres, reff := env.Rescuer().Eval(env, val, res) if reff == nil { return env.Return(rres) } else { // Here, unpack the effect and replace it with a return // to avoid recursion loops of fail in rescue return env.Return(reff.Unwrap()) } } func (env *Environment) Flow() Flow { if len(env.Frames) > 0 { index := len(env.Frames) - 1 return env.Frames[index].Flow() } return FailFlow } func (env *Environment) SetEffect(e Effect) Effect { frame := env.Frame(1) if frame != nil { frame.Effect = e } return e } func (env *Environment) Return(val Value) (Value, Effect) { effect := env.SetEffect(Return{val}) return val, effect } func (env *Environment) Fail(err *Error) (Value, Effect) { effect := env.SetEffect(err) return nil, effect } func (env *Environment) Break(val Value) (Value, Effect) { effect := env.SetEffect(Break{val}) return val, effect } func (env *Environment) FailString(msg string, args ...Value) (Value, Effect) { return env.Fail(env.ErrorFromString(msg, args...)) } func (env Environment) Interpolate(s string, args ...Value) string { runes := []rune(s) res := []rune{} name := []rune{} inName := 0 for i, a := range args { env.Define(Itoa(i+1), a, 0) } apply := func() { inName = 0 val := env.Lookup(string(name)) if val == nil { res = append(res, '!', 'n', 'i', 'l') } else { add := []rune(val.String()) res = append(res, add...) } name = []rune{} } for i := 0; i < len(runes); i++ { r := runes[i] switch r { case '$': if inName == 0 { inName = 1 } else if inName == 1 { if len(name) < 1 { // $$ escape res = append(res, '$') inName = 0 } else { // $ at end of name apply() } } case '{': if inName > 0 { inName++ } else { res = append(res, '{') } case '}': if inName > 0 { inName-- if inName == 1 { apply() } } else { res = append(res, '}') } default: if inName > 0 { if IsNumber(r) || IsLetter(r) { name = append(name, r) } else { apply() res = append(res, r) } } else { res = append(res, r) } } } if len(name) > 0 { apply() } return string(res) } func (env Environment) Printi(msg string, args ...Value) (int, error) { msg = env.Interpolate(msg, args...) return env.Write(msg) } func (env Environment) Write(msg string) (int, error) { buf := []byte(msg) writer := env.Out if len(env.Frames) > 0 { writer = env.Frames[len(env.Frames)-1].Out } if writer == nil { return -1, env.ErrorFromString("no writer set in environment.") } return writer.Write(buf) } func (env Environment) ErrorFromString(msg string, args ...Value) *Error { msg = env.Interpolate(msg, args...) return ErrorFromString(msg) } // Complete is for use with liner func (env Environment) Complete(prefix String) List { res := List{} for _, frame := range env.Frames { for name, _ := range frame.Variables { if len(name) >= len(prefix) { if String(name[0:len(prefix)]) == prefix { res = append(res, String(name)) } } } } if len(res) == 0 { res = append(res, prefix) } return res.SortStrings() } func (env *Environment) Overload(name string, target Value, types []Value) (Value, Effect) { val := env.Lookup(name) cov, ok := val.(Overload) if val == nil { cov = make(Overload) } else if !ok { return env.FailString("Not a overload: " + name) } signature := "" for _, arg := range types { signature += "_" + arg.String() } if _, ok := target.(String); ok { tarVal := env.Lookup(target.String()) cov[signature] = tarVal } else if _, ok := target.(Word); ok { tarVal := env.Lookup(target.String()) cov[signature] = tarVal } else { cov[signature] = target } env.Define(name, cov, -1) return cov, nil }