package attl func p(env *Environment, args ...Value) (Value, Effect) { for _, arg := range args { print(arg, " ") } print("\n") return nil, nil } func print_(env *Environment, args ...Value) (Value, Effect) { var msg string erra := Args(args, &msg) if erra != nil { return env.FailString("printf: ${1}", erra) } extra := []Value{} if len(args) > 1 { extra = args[1:len(args)] } n, err := env.Printi(msg, extra...) if err == nil { return Int(n), nil } return Int(n), ErrorFromError(err) } func write(env *Environment, args ...Value) (Value, Effect) { var msg string erra := Args(args, &msg) if erra != nil { return env.FailString("write: ${1}", erra) } n, err := env.Write(msg) if err == nil { return Int(n), nil } return Int(n), ErrorFromError(err) } func iadd(env *Environment, args ...Value) (Value, Effect) { var i, j int err := Args(args, &i, &j) if err != nil { return env.Fail(err) } return Int(i + j), nil } func isub(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Int(v1 - v2), nil } func imul(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Int(v1 * v2), nil } func idiv(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } if v2 == 0 { return nil, ErrorFromString("division by 0") } return Int(v1 / v2), nil } func igt(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 > v2), nil } func ilt(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 < v2), nil } func ige(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 >= v2), nil } func ile(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 <= v2), nil } func ieq(env *Environment, args ...Value) (Value, Effect) { var v1, v2 int err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 == v2), nil } func seq(env *Environment, args ...Value) (Value, Effect) { var v1, v2 string err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(v1 == v2), nil } func teq(env *Environment, args ...Value) (Value, Effect) { var t1, t2 Type err := Args(args, &t1, &t2) if err != nil { return env.Fail(err) } return Bool(t1 == t2), nil } func updateIntByName(update func(in Int) Int, env *Environment, args ...Value) (Int, Effect) { var name Word err := Args(args, &name) if err != nil { return Int(0), err } val := env.Lookup(name.String()) vi, ok := val.(Int) if !ok { return Int(0), ErrorFromString("Not an integer.") } newi := update(vi) env.Set(name.String(), newi) return newi, nil } func inc(env *Environment, args ...Value) (Value, Effect) { return updateIntByName(func(in Int) Int { return in + 1 }, env, args...) } func dec(env *Environment, args ...Value) (Value, Effect) { return updateIntByName(func(in Int) Int { return in - 1 }, env, args...) } func str(env *Environment, args ...Value) (Value, Effect) { var v1 Value err := Args(args, &v1) if err != nil { return env.Fail(err) } return String(v1.String()), nil } func int_(env *Environment, args ...Value) (Value, Effect) { var v1 Value err := Args(args, &v1) if err != nil { return env.Fail(err) } rs := []rune(v1.String() + " ") index := 0 return ParseInteger(rs, &index) } func boolBinop(op func(b1, b2 bool) bool, env *Environment, args ...Value) (Value, Effect) { var v1, v2 bool err := Args(args, &v1, &v2) if err != nil { return env.Fail(err) } return Bool(op(v1, v2)), nil } func isnil(env *Environment, args ...Value) (Value, Effect) { if len(args) < 1 { return env.FailString("isnil requires 1 argument") } return Bool(args[0] == nil), nil } func band(env *Environment, args ...Value) (Value, Effect) { return boolBinop(func(b1, b2 bool) bool { return b1 && b2 }, env, args...) } func bor(env *Environment, args ...Value) (Value, Effect) { return boolBinop(func(b1, b2 bool) bool { return b1 || b2 }, env, args...) } func bxor(env *Environment, args ...Value) (Value, Effect) { return boolBinop(func(b1, b2 bool) bool { return b1 != b2 }, env, args...) } func bnot(env *Environment, args ...Value) (Value, Effect) { var v1 bool err := Args(args, &v1) if err != nil { return env.Fail(err) } return Bool(!v1), nil } func val(env *Environment, args ...Value) (Value, Effect) { if len(args) < 1 { return env.FailString("val requres at least one argument.") } return List(args), nil } func ret(env *Environment, args ...Value) (Value, Effect) { if len(args) < 1 { return env.Return(nil) } else if len(args) == 1 { return env.Return(args[0]) } else { return env.Return(List(args)) } } func fail(env *Environment, args ...Value) (Value, Effect) { if len(args) < 1 { return env.Fail(ErrorFromString("fail")) } else { return env.FailString(args[0].String(), args[1:len(args)]...) } } func break_(env *Environment, args ...Value) (Value, Effect) { if len(args) < 1 { return env.Break(nil) } else if len(args) == 1 { return env.Break(args[0]) } else { return env.Break(List(args)) } } func nop(env *Environment, args ...Value) (Value, Effect) { return nil, nil } func typeof_(env *Environment, args ...Value) (Value, Effect) { var val Value err := Args(args, &val) if err != nil { return nil, err } return TypeOf(val), nil } func type_(env *Environment, args ...Value) (Value, Effect) { var val Value err := Args(args, &val) if err != nil { return nil, err } name := val.String() return Type(name), nil } func to(env *Environment, args ...Value) (Value, Effect) { var name string if len(args) < 2 { return env.FailString("to needs at least 2 arguments") } err := Convert(args[0], &name) if err != nil { return env.Fail(err) } block, ok := (args[len(args)-1]).(Block) if !ok { return env.FailString("to: last argument must be a block") } last := args[len(args)-1] block, isBlock := last.(Block) if !isBlock { return env.FailString("Not a block") } params := args[1 : len(args)-1] defined := Defined{name, params, block} env.Define(name, defined, 1) return defined, nil } func do(env *Environment, args ...Value) (Value, Effect) { var name string var doArgs List err := Args(args, &name, &doArgs) if err != nil { return env.Fail(err) } fun := env.Lookup(name) if fun == nil { return env.FailString("Cannot evaluate unknown order: " + name) } eva, ok := fun.(Evaler) if !ok { return env.FailString("Cannot evaluate: " + name) } return eva.Eval(env, doArgs...) } func if_(env *Environment, args ...Value) (Value, Effect) { var cond, ok, haveElse bool var ifBlock, elseBlock Block if len(args) < 2 { return env.FailString("if needs at least 2 arguments") } if len(args) > 4 { return env.FailString("if needs at most 4 arguments") } err := Convert(args[0], &cond) if err != nil { return env.Fail(err) } ifBlock, ok = (args[1]).(Block) if !ok { return env.FailString("if: second argument must be a block") } elseIndex := 2 if 2 < len(args) { // look for an else keyword but don't mind if it really is else _, ok = (args[2]).(Word) if ok { // block after else keyword elseIndex = 3 } } if elseIndex < len(args) { // There should be an else block... elseBlock, ok = (args[elseIndex]).(Block) if !ok { return env.FailString("if: missing else block") } haveElse = true } if cond { return ifBlock.Eval(env, args...) } else { if haveElse { return elseBlock.Eval(env, args...) } else { return nil, nil } } } func switch_(env *Environment, args ...Value) (Value, Effect) { var defaultBlock Block var haveDefault bool = false if len(args) < 3 { return env.FailString("switch needs at least 3 arguments") } compareTo := args[0] for i := 2; i < len(args); i += 2 { case_ := args[i-1] block, blockOk := args[i].(Block) if !blockOk { return env.FailString("switch: argument ${1} is not a block", Int(i)) } if kw, kwOk := case_.(Word); kwOk && kw.String() == "default" { if haveDefault { return env.FailString("switch: duplicate default block ${1}", Int(i)) } haveDefault = true defaultBlock = block } else { if compareTo.String() == case_.String() { return block.Eval(env, args...) } } } if haveDefault { return defaultBlock.Eval(env, args...) } return nil, nil } func while(env *Environment, args ...Value) (Value, Effect) { var blockRes Value var blockEff Effect if len(args) != 2 { return env.FailString("while needs exactly 3 arguments") } cond, condOk := args[0].(Block) block, blockOk := args[1].(Block) if !condOk { return env.FailString("while condition must be a block") } if !blockOk { return env.FailString("while body must be a block") } for res, eff := cond.Eval(env, args...); ValToBool(res); res, eff = cond.Eval(env, args...) { if eff != nil && eff.Flow() > NormalFlow { return res, eff } blockRes, blockEff = block.Eval(env, args...) if blockEff != nil && blockEff.Flow() > NormalFlow { return blockRes, blockEff } } return blockRes, blockEff } func rescue(env *Environment, args ...Value) (Value, Effect) { var block Block err := Args(args, &block) if err != nil { return env.Fail(err) } return env.Prevent(block) } func set(env *Environment, args ...Value) (Value, Effect) { if len(args) < 2 { return env.FailString("set needs at 2 arguments") } if args[0] == nil { return env.FailString("set $1 is nil") } return env.Set(args[0].String(), args[1]) } func let(env *Environment, args ...Value) (Value, Effect) { if len(args) < 2 { return env.FailString("def needs at 2 arguments") } if args[0] == nil { return env.FailString("def $1 is nil") } return env.Define(args[0].String(), args[1], 1) } func get(env *Environment, val ...Value) (Value, Effect) { if len(val) < 1 { return env.FailString("get needs at least 1 argument") } target := val[0].String() return env.Lookup(target), nil } func list(env *Environment, args ...Value) (Value, Effect) { return List(args), nil } func sadd(env *Environment, args ...Value) (Value, Effect) { var value Value var str String err := Args(args, &str, &value) if err != nil { return env.Fail(err) } str = str + String(value.String()) return str, nil } func sget(env *Environment, args ...Value) (Value, Effect) { var index int var str String err := Args(args, &str, &index) if err != nil { return env.Fail(err) } runes := []rune(str) if (index < 0) || (index >= len(runes)) { return env.FailString("index out of range") } return Int(runes[index]), nil } func runes(env *Environment, args ...Value) (Value, Effect) { var str String err := Args(args, &str) if err != nil { return env.Fail(err) } res := List{} runes := []rune(str) for i := 0; i < len(runes); i++ { res = append(res, Int(runes[i])) } return res, nil } func wire(env *Environment, args ...Value) (Value, Effect) { var str String for i := 0; i < len(args); i++ { var ch Int err := Convert(args[i], &ch) if err != nil { return str, err } str = str + String([]rune{rune(ch)}) } return str, nil } func slen(env *Environment, args ...Value) (Value, Effect) { var str String err := Args(args, &str) if err != nil { return env.Fail(err) } runes := []rune(str) return Int(len(runes)), nil } func ladd(env *Environment, args ...Value) (Value, Effect) { var value Value var list List err := Args(args, &list, &value) if err != nil { return env.Fail(err) } list = append(list, value) return list, nil } func lget(env *Environment, args ...Value) (Value, Effect) { var index int var list List err := Args(args, &list, &index) if err != nil { return env.Fail(err) } if (index < 0) || (index >= len(list)) { return env.FailString("index out of range") } return list[index], nil } func lset(env *Environment, args ...Value) (Value, Effect) { var index int var list List var val Value err := Args(args, &list, &index, &val) if err != nil { return env.Fail(err) } if (index < 0) || (index >= len(list)) { return env.FailString("index out of range") } list[index] = val return list[index], nil } func llen(env *Environment, args ...Value) (Value, Effect) { var list List err := Args(args, &list) if err != nil { return env.Fail(err) } return Int(len(list)), nil } func lsort(env *Environment, args ...Value) (Value, Effect) { var list List err := Args(args, &list) if err != nil { return env.Fail(err) } return list.SortStrings(), nil } func leach(env *Environment, args ...Value) (Value, Effect) { var list List var key Word var name Word var block Block err := Args(args, &list, &key, &name, &block) if err != nil { return env.Fail(err) } for i, v := range list { env.Define(key.String(), Int(i), 0) env.Define(name.String(), v, 0) bval, berr := block.Eval(env, args...) if berr != nil { return bval, berr } } return list, nil } func lslice(env *Environment, args ...Value) (Value, Effect) { var list List var from Int var to Int err := Args(args, &list, &from, &to) if err != nil { return env.Fail(err) } length := Int(len(list)) if length == 0 { return list, nil } if from < 0 { from = length - from } if to < 0 { from = length - from } if from >= length { from = length - 1 } if to >= length { to = length - 1 } if from > to { from, to = to, from } return list[from:to], nil } func map_(env *Environment, args ...Value) (Value, Effect) { res := make(Map) for i := 1; i < len(args); i += 2 { key := args[i-1] val := args[i] res[key.String()] = val } return res, nil } func mget(env *Environment, args ...Value) (Value, Effect) { var index string var hmap Map err := Args(args, &hmap, &index) if err != nil { return env.Fail(err) } return hmap[index], nil } func mset(env *Environment, args ...Value) (Value, Effect) { var index string var hmap Map var val Value err := Args(args, &hmap, &index, &val) if err != nil { return env.Fail(err) } hmap[index] = val return hmap[index], nil } func mkeys(env *Environment, args ...Value) (Value, Effect) { var hmap Map err := Args(args, &hmap) if err != nil { return env.Fail(err) } res := List{} for k, _ := range hmap { res = append(res, String(k)) } return res, nil } func meach(env *Environment, args ...Value) (Value, Effect) { var map_ Map var key Word var name Word var block Block err := Args(args, &map_, &key, &name, &block) if err != nil { return env.Fail(err) } miter := Map{} for k, v := range map_ { miter[k] = v } for k, v := range miter { env.Define(key.String(), String(k), 0) env.Define(name.String(), v, 0) bval, beff := block.Eval(env, args...) if beff != nil { return bval, beff } } return map_, nil } func expand(env *Environment, args ...Value) (Value, Effect) { var msg string err := Args(args, &msg) if err != nil { return env.Fail(err) } res := env.Interpolate(msg) return String(res), nil } func help(env *Environment, args ...Value) (Value, Effect) { var name string err := Args(args, &name) if err != nil { return env.Fail(err) } helpMap := env.Lookup("HELP") if helpMap == nil { env.Printi("help: $1:No help available 1.\n", String(name)) return nil, err } if name == "all" { keys := helpMap.(Map).SortedKeys() for _, k := range keys { v := helpMap.(Map)[k.String()] env.Printi("$1: $2\n", k, v) } return nil, nil } msg, ok := helpMap.(Map)[name] if ok { env.Printi("help: $1:\n$2\n", String(name), msg) } else { env.Printi("help: $1:No help available 2.\n", String(name)) } return msg, nil } func explain(env *Environment, args ...Value) (Value, Effect) { var name string var help String err := Args(args, &name, &help) if err != nil { return env.Fail(err) } helpMap := env.Lookup("HELP") if helpMap == nil { helpMap = make(Map) } helpMap.(Map)[name] = help env.Define("HELP", helpMap, -1) return help, nil } func overload(env *Environment, args ...Value) (Value, Effect) { var name string var target Value err := Args(args, &name, &target) if err != nil { return env.Fail(err) } if len(args) < 3 { return env.FailString("overload needs at least 3 arguments") } return env.Overload(name, target, args[2:len(args)]) } func (env *Environment) Register(name string, f func(e *Environment, args ...Value) (Value, Effect), help string) { env.Define(name, Proc(f), -1) explain(env, String(name), String(help)) } func (env *Environment) RegisterBuiltins() { env.Define("true", Bool(true), -1) env.Define("false", Bool(false), -1) env.Register("sadd", sadd, "returns a string with $2 appended to string $1") env.Register("sget", sget, "gets a rune from a string by index") env.Register("slen", slen, "returns the length of a string") env.Register("iadd", iadd, "adds two integers together") env.Register("band", band, `returns true if $1 and $2 arguments are true`) env.Register("bor", bor, `returns true if $1 or $2 arguments are true`) env.Register("bxor", bxor, `returns true if $1 and $2 are different booleans`) env.Register("bnot", bnot, `returns true if $1 is false and false otherwise`) env.Register("ladd", ladd, "returns a list with $2 appended to List $1") env.Register("list", list, "creates a new array list") env.Register("lget", lget, "gets a value from a list by index") env.Register("lset", lset, "sets a value to a list by index and value") env.Register("llen", llen, "returns the length of a list") env.Register("lsort", lsort, "returns the List $1 sorted by string value") env.Register("leach", leach, "calls the block $4 for each entry in the list") env.Register("lslice", lslice, "slices the list $1 from $2 to $3") env.Register("iadd", iadd, "adds and Ints to and Int") env.Register("isub", isub, "subtracts an Int from an Int") env.Register("imul", imul, "multiplies an Ints by an Int") env.Register("idiv", idiv, "divides an Int by an Int") env.Register("ilt", ilt, "checks if $1 < $2, where $1 and $2 must be Int") env.Register("ile", ile, "checks if $1 <= $2, where $1 and $2 must be Int") env.Register("igt", igt, "checks if $1 > $2, where $1 and $2 must be Int") env.Register("ige", ige, "checks if $1 >= $2, where $1 and $2 must be Int") env.Register("ieq", ieq, "checks if $1 == $2, where $1 and $2 must be Int") env.Register("seq", seq, "checks if [str $1] == [str $2]") env.Register("str", str, "converts $1 to String") env.Register("wire", wire, "converts unicode character indexes or runes to String") env.Register("runes", runes, "converts String to alist of character indexes or runes") env.Register("int", int_, "converts $1 to Int") env.Register("inc", inc, "increments the named integer $1") env.Register("dec", dec, "decrements the named integer $1") env.Register("map", map_, "creates a new hash map") env.Register("mget", mget, "gets a value from a map by key") env.Register("mset", mset, "sets a value to a map by key and value") env.Register("mkeys", mkeys, "returns all keys of a map as an unsorted list") env.Register("meach", meach, "calls the block $4 for each entry in the map") env.Register("p", p, "print debug output") env.Register("print", print_, "print to the environnment's current writer with interpolation") env.Register("write", write, "write to the environnment's current writer") env.Register("to", to, "define a procedure") env.Register("do", do, "execute a command $1 with arguments in $2 as array") env.Register("ret", ret, "return from a procedure") env.Register("return", ret, "return from a procedure") env.Register("break", break_, "return from a block") env.Register("val", val, "gets the value of a value") env.Register("let", let, "creates a new vavariable with given value") env.Register("set", set, "sets an existing variable") env.Register("get", get, "get the contents of a variable") env.Register("help", help, "get help for a procedure") env.Register("explain", explain, "set the help for a procedure") env.Register("expand", expand, "interpolate strings from environment") env.Register("fail", fail, "fail execution of a procedure") env.Register("rescue", rescue, "call $1 as the error handler on failure") env.Register("if", if_, "if runs $1 if $0 is true, otherwise runs $2") env.Register("isnil", isnil, "returns true if $1 is nil, false if not") env.Register("switch", switch_, "selects one of many cases") env.Register("type", type_, "returns $1 converted to a type") env.Register("teq", teq, "checks if $1 and $2 are exactly the same type") env.Register("typeof", typeof_, "returns the type of $1 or Unknown if not known") env.Register("nop", nop, "does nothing and returns nil") env.Register("overload", overload, "creates a command overload named $1 targeting $2 for the types following $2") } // This function registers builtins that make Attl turing complete // Not to be used in situations where this is undesirable. func (env *Environment) RegisterTuringCompleteBuiltins() { env.Register("while", while, "executes $2 while $1 returns true") }