فهرست منبع

ADD. 添加代码及测试数据

RegMs If 4 سال پیش
والد
کامیت
54168283ef
10فایلهای تغییر یافته به همراه984 افزوده شده و 0 حذف شده
  1. 143 0
      bind.go
  2. 29 0
      ctrl.go
  3. 35 0
      error.go
  4. 3 0
      go.mod
  5. 103 0
      in
  6. 58 0
      io.go
  7. 140 0
      list.go
  8. 115 0
      math.go
  9. 261 0
      mua.go
  10. 97 0
      type.go

+ 143 - 0
bind.go

@@ -0,0 +1,143 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"strings"
+)
+
+func parseFunc(val string) value {
+	if !isList(val) {
+		return value{body: errNop}
+	}
+	list := strings.Split(val, " ")
+	if list[1] != "[" {
+		return value{body: errNop}
+	}
+	i := 2
+	for ; list[i] != "]"; i++ {
+		if !isName(`"` + list[i]) {
+			return value{body: errNop}
+		}
+	}
+	if i == len(list)-2 {
+		return value{body: errNop}
+	}
+	j, bracketCnt := i+1, 0
+	for ; j < len(list)-1; j++ {
+		if list[j] == "[" {
+			bracketCnt++
+		}
+		if list[j] == "]" {
+			bracketCnt--
+		}
+		if bracketCnt == 0 && j < len(list)-2 {
+			return value{body: errNop}
+		}
+	}
+	local := make(environ)
+	for name, val := range env[len(env)-1] {
+		local[name] = val
+	}
+	return value{
+		val:   val,
+		param: list[2:i],
+		body:  strings.Join(list[i+1:len(list)-1], " "),
+		env:   local}
+}
+
+func getFunc(name string) value {
+	if !isName(`"` + name) {
+		return value{body: errNop}
+	}
+	envs := []int{len(env) - 1, len(env) - 2, 1}
+	for _, id := range envs {
+		val, ok := env[id][name]
+		if ok {
+			return val
+		}
+	}
+	return value{body: errNop}
+}
+
+func opMake(val1, val2 value) value {
+	name := toName(val1.val)
+	_, ok := reserved[name]
+	if ok {
+		panic(fmt.Errorf("make: %s (%s)", errNameReserved, name))
+	}
+	if isList(val2.val) && val2.body == "" {
+		val2 = parseFunc(val2.val)
+	}
+	env[len(env)-1][name] = val2
+	return val2
+}
+
+func opThing(val1 value) value {
+	name := toName(val1.val)
+	envs := []int{len(env) - 1, len(env) - 2, 1}
+	for _, id := range envs {
+		val, ok := env[id][name]
+		if ok {
+			return val
+		}
+	}
+	panic(fmt.Errorf("thing: %s (%s)", errNameNotFound, name))
+}
+
+func opErase(val1 value) value {
+	name := toName(val1.val)
+	val, ok := env[len(env)-1][name]
+	if ok {
+		delete(env[len(env)-1], name)
+		return val
+	}
+	panic(fmt.Errorf("erase: %s (%s)", errNameNotFound, name))
+}
+
+func opExport(val1 value) value {
+	name := toName(val1.val)
+	val, ok := env[len(env)-1][name]
+	if ok {
+		env[1][name] = val
+		return val
+	}
+	panic(fmt.Errorf("export: %s (%s)", errNameNotFound, name))
+}
+
+func opSave(val1 value) value {
+	name := toName(val1.val)
+	s := ""
+	for name, val := range env[len(env)-1] {
+		s += `make "` + name + " " + val.val + "\n"
+	}
+	err := os.WriteFile(name, []byte(s), 0666)
+	if err != nil {
+		panic(fmt.Errorf("save: %s (%s)", errFileError, name))
+	}
+	return val1
+}
+
+func opLoad(val1 value) value {
+	name := toName(val1.val)
+	s, err := os.ReadFile(name)
+	if err != nil {
+		panic(fmt.Errorf("load: %s (%s)", errFileError, name))
+	}
+	interpret(bufio.NewScanner(strings.NewReader(string(s))))
+	return value{val: "true"}
+}
+
+func opErAll() value {
+	env[len(env)-1] = make(environ)
+	return value{val: "true"}
+}
+
+func opPoAll() value {
+	list := []string{}
+	for name := range env[len(env)-1] {
+		list = append(list, name)
+	}
+	return value{val: "[ " + strings.Join(list, " ") + " ]"}
+}

+ 29 - 0
ctrl.go

@@ -0,0 +1,29 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"strings"
+)
+
+func opRun(val1 value) value {
+	if !isList(val1.val) {
+		panic(fmt.Errorf("run: %s (%s)", errListExpected, val1.val))
+	}
+	return interpret(bufio.NewScanner(strings.NewReader(val1.val[1 : len(val1.val)-1])))
+}
+
+func opIf(val1, val2, val3 value) value {
+	b1 := toBool(val1.val)
+	if b1 {
+		return opRun(val2)
+	}
+	return opRun(val3)
+}
+
+func opReturn(val1 value) value {
+	if isList(val1.val) && val1.body == "" {
+		return parseFunc(val1.val)
+	}
+	return val1
+}

+ 35 - 0
error.go

@@ -0,0 +1,35 @@
+package main
+
+const errInvalidNumber = "invalid number"
+
+const errInvalidBool = "invalid bool"
+
+const errInvalidName = "invalid name"
+
+const errUnmatchedBracket = "unmatched bracket"
+
+const errDivisionByZero = "division by zero"
+
+const errNegativeSquareRoot = "negative square root"
+
+const errNameReserved = "name reserved"
+
+const errNameNotFound = "name not found"
+
+const errUnexpectedEndOfInput = "unexpected end of input"
+
+const errValueExpected = "value expected"
+
+const errWordExpceted = "word expected"
+
+const errListExpected = "list expected"
+
+const errWordOrListExpected = "word or list expected"
+
+const errEmptyWordOrList = "empty word or list"
+
+const errIllegalOperandType = "illegal operand type"
+
+const errFileError = "file error"
+
+const errNop = "nop"

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module mua
+
+go 1.17

+ 103 - 0
in

@@ -0,0 +1,103 @@
+print load "test
+print fib_out 3
+
+print poall
+print erall
+print poall
+
+print readlist
+a b c 1 2 3
+
+make "fib_out [
+    [x]
+    [
+        print :fib_out
+        make "fib [
+            [x]
+            [if lt :x 2 [1] [add fib sub :x 1 fib sub :x 2]]
+        ]
+        fib :x
+    ]
+]
+print fib_out 3
+
+make "f [[x] [
+  make "g [[y] [return add :x :y]]
+  return g 42
+]]
+print f 233
+
+make "f1 [[x] [
+  make "g1 [[y] [return add :x :y]]
+  return :g1
+]]
+make "c1 f1 42
+make "c2 f1 24
+print c1 1
+print c2 2
+
+make "curry_two [[f x] [
+  return [[y] [return f :x :y]]
+]]
+make "f2 [[x y] [
+  return add :x :y
+]]
+make "f3 curry_two :f2 42
+print f3 233
+
+make "fun [
+    [x]
+    [
+        make "b [
+            [y]
+            [return make "x add :x :y]
+        ]
+        return :b
+    ]
+]
+make "adder fun 5
+print adder 1
+print adder 2
+print adder 3
+
+print "abcd12
+make "a 16
+print :a
+make "b "a
+print thing :b
+make "c mul add :a 13 :a
+print if false [] [print sub :c "6]
+print div 12 5
+print mod 12 5
+make "d read
+1234dd
+make "e print :d
+print :e
+
+make "prt [
+  [a]
+  [
+      make "b [
+          []
+          [print :a]
+      ]
+      return :b
+  ]
+]
+make "c prt "hello
+c
+
+make "x 2
+print :x
+make "test [
+    []
+    [
+        make "x 1
+        export "x
+        print :x
+    ]
+]
+test
+print :x
+
+print save "test

+ 58 - 0
io.go

@@ -0,0 +1,58 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"strings"
+)
+
+func opRead(scanner *bufio.Scanner) value {
+	s := "\n"
+	for s == "\n" && scanner.Scan() {
+		s = scanner.Text()
+	}
+	if s == "\n" {
+		panic(fmt.Errorf("read: %s", errUnexpectedEndOfInput))
+	}
+	return value{val: `"` + s}
+}
+
+func opPrint(val1 value) value {
+	s := val1.val
+	if s != "" {
+		if s[0] == '"' {
+			fmt.Println(s[1:])
+		} else if s[0] == '[' {
+			list, space := strings.Split(s, " "), false
+			for i := 0; i < len(list); i++ {
+				if space && list[i] != "]" {
+					fmt.Print(" ")
+				}
+				fmt.Print(list[i])
+				space = list[i] != "["
+			}
+			fmt.Println()
+		} else {
+			fmt.Println(s)
+		}
+	}
+	return val1
+}
+
+func opReadList(scanner *bufio.Scanner) value {
+	list, line := []string{}, false
+	for scanner.Scan() {
+		s := scanner.Text()
+		if s == "\n" && line {
+			break
+		}
+		line = true
+		if s[0] == '[' || s[len(s)-1] == ']' {
+			panic(fmt.Errorf("readlist: %s, (%s)", errWordExpceted, s))
+		}
+		if s != "\n" {
+			list = append(list, s)
+		}
+	}
+	return value{val: "[ " + strings.Join(list, " ") + " ]"}
+}

+ 140 - 0
list.go

@@ -0,0 +1,140 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+func opWord(val1, val2 value) value {
+	if !isWord(val1.val) {
+		panic(fmt.Errorf("word: %s (%s)", errWordExpceted, val1.val))
+	}
+	if isWord(val2.val) {
+		return value{val: val1.val + val2.val[1:]}
+	}
+	if isNumber(val2.val) || isBool(val2.val) {
+		return value{val: val1.val + val2.val}
+	}
+	panic(fmt.Errorf("word: %s (%s)", errWordExpceted, val2.val))
+}
+
+func opSentence(val1, val2 value) value {
+	return value{val: "[ " +
+		strings.TrimSuffix(strings.TrimPrefix(val1.val, "[ "), " ]") + " " +
+		strings.TrimSuffix(strings.TrimPrefix(val2.val, "[ "), " ]") + " ]"}
+}
+
+func opList(val1, val2 value) value {
+	return value{val: "[ " + val1.val + " " + val2.val + " ]"}
+}
+
+func opJoin(val1, val2 value) value {
+	if !isList(val1.val) {
+		panic(fmt.Errorf("join: %s (%s)", errListExpected, val1.val))
+	}
+	return value{val: strings.TrimSuffix(val1.val, " ]") + " " + val2.val + " ]"}
+}
+
+func opFirst(val1 value) value {
+	if !isWord(val1.val) && !isList(val1.val) {
+		panic(fmt.Errorf("first: %s (%s)", errWordOrListExpected, val1.val))
+	}
+	if isEmpty(val1.val) {
+		panic(fmt.Errorf("first: %s (%s)", errEmptyWordOrList, val1.val))
+	}
+	if isWord(val1.val) {
+		return value{val: val1.val[1:2]}
+	}
+	list1 := strings.Split(val1.val, " ")
+	if list1[1] != "[" {
+		return value{val: list1[1]}
+	}
+	i, bracketCnt := 2, 1
+	for ; bracketCnt > 0; i++ {
+		if list1[i] == "[" {
+			bracketCnt++
+		}
+		if list1[i] == "]" {
+			bracketCnt--
+		}
+	}
+	return value{val: strings.Join(list1[1:i], " ")}
+}
+
+func opLast(val1 value) value {
+	if !isWord(val1.val) && !isList(val1.val) {
+		panic(fmt.Errorf("last: %s (%s)", errWordOrListExpected, val1.val))
+	}
+	if isEmpty(val1.val) {
+		panic(fmt.Errorf("last: %s (%s)", errEmptyWordOrList, val1.val))
+	}
+	if isWord(val1.val) {
+		return value{val: val1.val[len(val1.val)-1:]}
+	}
+	list1 := strings.Split(val1.val, " ")
+	if list1[len(list1)-2] != "]" {
+		return value{val: list1[len(list1)-2]}
+	}
+	i, bracketCnt := len(list1)-3, 1
+	for ; bracketCnt > 0; i-- {
+		if list1[i] == "]" {
+			bracketCnt++
+		}
+		if list1[i] == "[" {
+			bracketCnt--
+		}
+	}
+	return value{val: strings.Join(list1[i+1:len(list1)-1], " ")}
+}
+
+func opButFirst(val1 value) value {
+	if !isWord(val1.val) && !isList(val1.val) {
+		panic(fmt.Errorf("butfirst: %s (%s)", errWordOrListExpected, val1.val))
+	}
+	if isEmpty(val1.val) {
+		panic(fmt.Errorf("butfirst: %s (%s)", errEmptyWordOrList, val1.val))
+	}
+	if isWord(val1.val) {
+		return value{val: `"` + val1.val[2:]}
+	}
+	list1 := strings.Split(val1.val, " ")
+	if list1[1] != "[" {
+		return value{val: "[ " + strings.Join(list1[2:], " ")}
+	}
+	i, bracketCnt := 2, 1
+	for ; bracketCnt > 0; i++ {
+		if list1[i] == "[" {
+			bracketCnt++
+		}
+		if list1[i] == "]" {
+			bracketCnt--
+		}
+	}
+	return value{val: "[ " + strings.Join(list1[i:], " ")}
+}
+
+func opButLast(val1 value) value {
+	if !isWord(val1.val) && !isList(val1.val) {
+		panic(fmt.Errorf("butlast: %s (%s)", errWordOrListExpected, val1.val))
+	}
+	if isEmpty(val1.val) {
+		panic(fmt.Errorf("butlast: %s (%s)", errEmptyWordOrList, val1.val))
+	}
+	if isWord(val1.val) {
+		return value{val: val1.val[:len(val1.val)-1]}
+	}
+	list1 := strings.Split(val1.val, " ")
+	if list1[len(list1)-2] != "]" {
+		return value{val: strings.Join(list1[:len(list1)-2], " ") + " ]"}
+	}
+	i, bracketCnt := len(list1)-3, 1
+	for ; bracketCnt > 0; i-- {
+		if list1[i] == "]" {
+			bracketCnt++
+		}
+		if list1[i] == "[" {
+			bracketCnt--
+		}
+	}
+	return value{val: strings.Join(list1[:i+1], " ") + " ]"}
+}

+ 115 - 0
math.go

@@ -0,0 +1,115 @@
+package main
+
+import (
+	"fmt"
+	"math"
+	"math/rand"
+	"strconv"
+)
+
+func opAdd(val1, val2 value) value {
+	num1, num2 := toNumber(val1.val), toNumber(val2.val)
+	return value{val: strconv.FormatFloat(num1+num2, 'g', -1, 64)}
+}
+
+func opSub(val1, val2 value) value {
+	num1, num2 := toNumber(val1.val), toNumber(val2.val)
+	return value{val: strconv.FormatFloat(num1-num2, 'g', -1, 64)}
+}
+
+func opMul(val1, val2 value) value {
+	num1, num2 := toNumber(val1.val), toNumber(val2.val)
+	return value{val: strconv.FormatFloat(num1*num2, 'g', -1, 64)}
+}
+
+func opDiv(val1, val2 value) value {
+	num1, num2 := toNumber(val1.val), toNumber(val2.val)
+	if num2 == 0 {
+		panic(fmt.Errorf("div: %s (%s / %s)", errDivisionByZero, val1.val, val2.val))
+	}
+	return value{val: strconv.FormatFloat(num1/num2, 'g', -1, 64)}
+}
+
+func opMod(val1, val2 value) value {
+	num1, num2 := toNumber(val1.val), toNumber(val2.val)
+	if num2 == 0 {
+		panic(fmt.Errorf("mod: %s (%s %% %s)", errDivisionByZero, val1.val, val2.val))
+	}
+	return value{val: strconv.FormatFloat(math.Mod(num1, num2), 'g', -1, 64)}
+}
+
+func opEq(val1, val2 value) value {
+	if isNumber(val1.val) && isNumber(val2.val) {
+		num1, num2 := toNumber(val1.val), toNumber(val2.val)
+		return value{val: strconv.FormatBool(num1 == num2)}
+	}
+	if isBool(val1.val) && isBool(val2.val) {
+		b1, b2 := toBool(val1.val), toBool(val2.val)
+		return value{val: strconv.FormatBool(b1 == b2)}
+	}
+	if isWord(val1.val) && isWord(val2.val) {
+		return value{val: strconv.FormatBool(val1.val == val2.val)}
+	}
+	panic(fmt.Errorf("eq: %s (%s, %s)", errIllegalOperandType, val1.val, val2.val))
+}
+
+func opGt(val1, val2 value) value {
+	if isNumber(val1.val) && isNumber(val2.val) {
+		num1, num2 := toNumber(val1.val), toNumber(val2.val)
+		return value{val: strconv.FormatBool(num1 > num2)}
+	}
+	if isBool(val1.val) && isBool(val2.val) {
+		b1, b2 := toBool(val1.val), toBool(val2.val)
+		return value{val: strconv.FormatBool(b1 || !b2)}
+	}
+	if isWord(val1.val) && isWord(val2.val) {
+		return value{val: strconv.FormatBool(val1.val > val2.val)}
+	}
+	panic(fmt.Errorf("gt: %s (%s, %s)", errIllegalOperandType, val1.val, val2.val))
+}
+
+func opLt(val1, val2 value) value {
+	if isNumber(val1.val) && isNumber(val2.val) {
+		num1, num2 := toNumber(val1.val), toNumber(val2.val)
+		return value{val: strconv.FormatBool(num1 < num2)}
+	}
+	if isBool(val1.val) && isBool(val2.val) {
+		b1, b2 := toBool(val1.val), toBool(val2.val)
+		return value{val: strconv.FormatBool(!b1 || b2)}
+	}
+	if isWord(val1.val) && isWord(val2.val) {
+		return value{val: strconv.FormatBool(val1.val < val2.val)}
+	}
+	panic(fmt.Errorf("lt: %s (%s, %s)", errIllegalOperandType, val1.val, val2.val))
+}
+
+func opAnd(val1, val2 value) value {
+	b1, b2 := toBool(val1.val), toBool(val2.val)
+	return value{val: strconv.FormatBool(b1 && b2)}
+}
+
+func opOr(val1, val2 value) value {
+	b1, b2 := toBool(val1.val), toBool(val2.val)
+	return value{val: strconv.FormatBool(b1 || b2)}
+}
+
+func opNot(val1 value) value {
+	b1 := toBool(val1.val)
+	return value{val: strconv.FormatBool(!b1)}
+}
+
+func opRandom(val1 value) value {
+	return value{val: strconv.FormatFloat(toNumber(val1.val)*rand.Float64(), 'g', -1, 64)}
+}
+
+func opInt(val1 value) value {
+	return value{val: strconv.FormatFloat(math.Floor(toNumber(val1.val)), 'g', -1, 64)}
+}
+
+func opSqrt(val1 value) value {
+	num1 := toNumber(val1.val)
+	if num1 < 0 {
+		panic(fmt.Errorf("sqrt: %s (%s)", errNegativeSquareRoot, val1.val))
+	}
+	return value{val: strconv.FormatFloat(math.Sqrt(num1), 'g', -1, 64)}
+}

+ 261 - 0
mua.go

@@ -0,0 +1,261 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"math/rand"
+	"os"
+	"strings"
+	"time"
+	"unicode"
+	"unicode/utf8"
+)
+
+var reserved = map[string]int{
+	"make": 2, "thing": 1, "print": 1, "read": 0, "add": 2,
+	"sub": 2, "mul": 2, "div": 2, "mod": 2, "erase": 1,
+	"isname": 1, "run": 1, "eq": 2, "gt": 2, "lt": 2,
+	"and": 2, "or": 2, "not": 1, "if": 3, "isnumber": 1,
+	"isword": 1, "islist": 1, "isbool": 1, "isempty": 1, "return": 1,
+	"export": 1, "readlist": 0, "word": 2, "sentence": 2, "list": 2,
+	"join": 2, "first": 1, "last": 1, "butfirst": 1, "butlast": 1,
+	"random": 1, "int": 1, "sqrt": 1, "save": 1, "load": 1,
+	"erall": 0, "poall": 0}
+
+type environ map[string]value
+type value struct {
+	val   string
+	param []string
+	body  string
+	env   environ
+}
+
+var env []environ
+var stack []value
+
+func interpret(scanner *bufio.Scanner) value {
+	stackTop, returned := len(stack), false
+	scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+		start := 0
+		for start < len(data) {
+			r, width := utf8.DecodeRune(data[start:])
+			if r == '\n' {
+				return start + width, data[start : start+width], nil
+			}
+			if !unicode.IsSpace(r) {
+				break
+			}
+			start += width
+		}
+		for i := start; i < len(data); {
+			r, width := utf8.DecodeRune(data[i:])
+			if r == '\n' {
+				return i, data[start:i], nil
+			}
+			if unicode.IsSpace(r) {
+				return i + width, data[start:i], nil
+			}
+			i += width
+		}
+		if atEOF && len(data) > start {
+			return len(data), data[start:], nil
+		}
+		return start, nil, nil
+	})
+	for !returned && scanner.Scan() {
+		// for _, v := range stack {
+		// 	fmt.Println(v.val)
+		// }
+		// fmt.Println()
+		s := scanner.Text()
+		if s == "\n" {
+			continue
+		}
+		if s[0] == ':' {
+			leading := len(s) - len(strings.TrimLeft(s, ":"))
+			val := value{val: `"` + strings.TrimLeft(s, ":")}
+			for i := 0; i < leading; i++ {
+				val = opThing(val)
+			}
+			stack = append(stack, val)
+		} else if s[0] == '[' {
+			list, bracketCnt := []string{}, 0
+			for {
+				leading := len(s) - len(strings.TrimLeft(s, "["))
+				trailing := len(s) - len(strings.TrimRight(s, "]"))
+				s = strings.TrimRight(strings.TrimLeft(s, "["), "]")
+				for i := 0; i < leading; i++ {
+					list = append(list, "[")
+				}
+				if s != "" {
+					list = append(list, s)
+				}
+				for i := 0; i < trailing; i++ {
+					list = append(list, "]")
+				}
+				bracketCnt += leading - trailing
+				if bracketCnt < 0 {
+					panic(fmt.Errorf("list: %s", errUnmatchedBracket))
+				}
+				if bracketCnt == 0 {
+					break
+				}
+				s = "\n"
+				for s == "\n" && scanner.Scan() {
+					s = scanner.Text()
+				}
+				if s == "\n" {
+					panic(fmt.Errorf("list: %s", errUnmatchedBracket))
+				}
+			}
+			stack = append(stack, value{val: strings.Join(list, " ")})
+		} else if '0' <= s[0] && s[0] <= '9' || s[0] == '-' {
+			toNumber(s)
+			stack = append(stack, value{val: s})
+		} else {
+			stack = append(stack, value{val: s})
+		}
+		for !returned {
+			updated := false
+			for i := len(stack) - 1; i >= stackTop; i-- {
+				paramCnt, ok := reserved[stack[i].val]
+				if ok {
+					if paramCnt == len(stack)-1-i {
+						for j := i + 1; j < len(stack); j++ {
+							if !isValue(stack[j].val) {
+								panic(fmt.Errorf("mua: %s (%s)", errValueExpected, stack[j].val))
+							}
+						}
+						switch stack[i].val {
+						case "make":
+							stack[i] = opMake(stack[i+1], stack[i+2])
+						case "thing":
+							stack[i] = opThing(stack[i+1])
+						case "print":
+							stack[i] = opPrint(stack[i+1])
+						case "read":
+							stack[i] = opRead(scanner)
+						case "add":
+							stack[i] = opAdd(stack[i+1], stack[i+2])
+						case "sub":
+							stack[i] = opSub(stack[i+1], stack[i+2])
+						case "mul":
+							stack[i] = opMul(stack[i+1], stack[i+2])
+						case "div":
+							stack[i] = opDiv(stack[i+1], stack[i+2])
+						case "mod":
+							stack[i] = opMod(stack[i+1], stack[i+2])
+						case "erase":
+							stack[i] = opErase(stack[i+1])
+						case "isname":
+							stack[i] = opIsName(stack[i+1])
+						case "run":
+							stack[i] = opRun(stack[i+1])
+						case "eq":
+							stack[i] = opEq(stack[i+1], stack[i+2])
+						case "gt":
+							stack[i] = opGt(stack[i+1], stack[i+2])
+						case "lt":
+							stack[i] = opLt(stack[i+1], stack[i+2])
+						case "and":
+							stack[i] = opAnd(stack[i+1], stack[i+2])
+						case "or":
+							stack[i] = opOr(stack[i+1], stack[i+2])
+						case "not":
+							stack[i] = opNot(stack[i+1])
+						case "if":
+							stack[i] = opIf(stack[i+1], stack[i+2], stack[i+3])
+						case "isnumber":
+							stack[i] = opIsNumber(stack[i+1])
+						case "isword":
+							stack[i] = opIsWord(stack[i+1])
+						case "islist":
+							stack[i] = opIsList(stack[i+1])
+						case "isbool":
+							stack[i] = opIsBool(stack[i+1])
+						case "isempty":
+							stack[i] = opIsEmpty(stack[i+1])
+						case "return":
+							stack[i] = opReturn(stack[i+1])
+							returned = true
+						case "export":
+							stack[i] = opExport(stack[i+1])
+						case "readlist":
+							stack[i] = opReadList(scanner)
+						case "word":
+							stack[i] = opWord(stack[i+1], stack[i+2])
+						case "sentence":
+							stack[i] = opSentence(stack[i+1], stack[i+2])
+						case "list":
+							stack[i] = opList(stack[i+1], stack[i+2])
+						case "join":
+							stack[i] = opJoin(stack[i+1], stack[i+2])
+						case "first":
+							stack[i] = opFirst(stack[i+1])
+						case "last":
+							stack[i] = opLast(stack[i+1])
+						case "butfirst":
+							stack[i] = opButFirst(stack[i+1])
+						case "butlast":
+							stack[i] = opButLast(stack[i+1])
+						case "random":
+							stack[i] = opRandom(stack[i+1])
+						case "int":
+							stack[i] = opInt(stack[i+1])
+						case "sqrt":
+							stack[i] = opSqrt(stack[i+1])
+						case "save":
+							stack[i] = opSave(stack[i+1])
+						case "load":
+							stack[i] = opLoad(stack[i+1])
+						case "erall":
+							stack[i] = opErAll()
+						case "poall":
+							stack[i] = opPoAll()
+						}
+						stack = stack[:i+1]
+						updated = true
+					}
+					break
+				}
+				val := getFunc(stack[i].val)
+				if val.body != errNop {
+					if len(val.param) == len(stack)-1-i {
+						for j := i + 1; j < len(stack); j++ {
+							if !isValue(stack[j].val) {
+								panic(fmt.Errorf("mua: %s (%s)", errValueExpected, stack[j].val))
+							}
+						}
+						local := environ{stack[i].val: val}
+						for j, name := range val.param {
+							local[name] = stack[i+1+j]
+						}
+						env = append(env, val.env, local)
+						stack[i] = opRun(value{val: val.body})
+						env = env[:len(env)-2]
+						stack = stack[:i+1]
+						updated = true
+					}
+					break
+				}
+			}
+			if !updated {
+				break
+			}
+		}
+	}
+	if len(stack) == stackTop {
+		stack = append(stack, value{val: "[ ]"})
+	}
+	val := stack[len(stack)-1]
+	stack = stack[:stackTop]
+	return val
+}
+
+func main() {
+	rand.Seed(time.Now().UnixNano())
+	env = append(env, nil, map[string]value{"pi": {val: "3.14159"}})
+
+	scanner := bufio.NewScanner(os.Stdin)
+	interpret(scanner)
+}

+ 97 - 0
type.go

@@ -0,0 +1,97 @@
+package main
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+)
+
+func isName(s string) bool {
+	reg := regexp.MustCompile(`^"[A-Za-z]\w*$`)
+	return reg.MatchString(s)
+}
+
+func toName(s string) string {
+	if !isName(s) {
+		panic(fmt.Errorf("name: %s (%s)", errInvalidName, s))
+	}
+	return s[1:]
+}
+
+func isNumber(s string) bool {
+	if s != "" && s[0] == '"' {
+		s = s[1:]
+	}
+	_, err := strconv.ParseFloat(s, 64)
+	return err == nil
+}
+
+func toNumber(s string) float64 {
+	if s != "" && s[0] == '"' {
+		s = s[1:]
+	}
+	val, err := strconv.ParseFloat(s, 64)
+	if err != nil {
+		panic(fmt.Errorf("number: %s (%s)", errInvalidNumber, s))
+	}
+	return val
+}
+
+func isWord(s string) bool {
+	return s != "" && s[0] == '"'
+}
+
+func isList(s string) bool {
+	return s != "" && s[0] == '['
+}
+
+func isBool(s string) bool {
+	if s != "" && s[0] == '"' {
+		s = s[1:]
+	}
+	_, err := strconv.ParseBool(s)
+	return err == nil
+}
+
+func toBool(s string) bool {
+	if s != "" && s[0] == '"' {
+		s = s[1:]
+	}
+	val, err := strconv.ParseBool(s)
+	if err != nil {
+		panic(fmt.Errorf("bool: %s (%s)", errInvalidBool, s))
+	}
+	return val
+}
+
+func isEmpty(s string) bool {
+	return s == `"` || s == "[ ]"
+}
+
+func isValue(s string) bool {
+	return isNumber(s) || isWord(s) || isList(s) || isBool(s)
+}
+
+func opIsName(val1 value) value {
+	return value{val: strconv.FormatBool(isName(val1.val))}
+}
+
+func opIsNumber(val1 value) value {
+	return value{val: strconv.FormatBool(isNumber(val1.val))}
+}
+
+func opIsWord(val1 value) value {
+	return value{val: strconv.FormatBool(isWord(val1.val))}
+}
+
+func opIsList(val1 value) value {
+	return value{val: strconv.FormatBool(isList(val1.val))}
+}
+
+func opIsBool(val1 value) value {
+	return value{val: strconv.FormatBool(isBool(val1.val))}
+}
+
+func opIsEmpty(val1 value) value {
+	return value{val: strconv.FormatBool(isEmpty(val1.val))}
+}