|
@@ -0,0 +1,869 @@
|
|
|
+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")
|
|
|
+}
|