RegMs If 4 лет назад
Родитель
Сommit
32ffc8afab
12 измененных файлов с 525 добавлено и 461 удалено
  1. 31 31
      bind.go
  2. 306 0
      conv.go
  3. 2 2
      ctrl.go
  4. 25 39
      error.go
  5. 4 4
      in0
  6. 5 22
      io.go
  7. 40 32
      list.go
  8. 43 46
      math.go
  9. BIN
      mua
  10. 17 30
      mua.go
  11. 10 12
      scan.go
  12. 42 243
      type.go

+ 31 - 31
bind.go

@@ -12,27 +12,27 @@ func assign(s string, index []string, val string, envs []environ) string {
 		return val
 	}
 	list := parseList(s)
-	i := toInt(value{word: index[0]}, envs)
+	i := toInt(&_unknown{s: index[0]}, envs)
 	return spliceList(list, i, 1, assign(indexOfList(list, i), index[1:], val, envs))
 }
 
 func setValue(s string, val value, envs []environ) (value, error) {
-	if !isNameWithIndex(value{word: s}) {
-		return value{}, fmt.Errorf("set: %s (%s)", errInvalidName, s)
+	if !isNameWithIndex(&_unknown{s: s}) {
+		return nil, fmt.Errorf("set: %s (%s)", errInvalidName, s)
 	}
-	name, index := toNameWithIndex(value{word: s})
+	name, index := toNameWithIndex(&_unknown{s: s})
 	_, ok := reserved[name]
 	if ok {
-		return value{}, fmt.Errorf("set: %s (%s)", errNameReserved, name)
+		return nil, fmt.Errorf("set: %s (%s)", errNameReserved, name)
 	}
 	oldVal, ok := envs[0][name]
 	if ok {
 		delete(envs[0], name)
 		if len(index) > 0 {
-			val = value{word: assign(toString(oldVal), index, toString(val), envs)}
+			val = &_unknown{s: assign(oldVal.String(), index, val.String(), envs)}
 		}
 	} else if len(index) > 0 {
-		return value{}, fmt.Errorf("set: %s (%s)", errNameNotFound, name)
+		return nil, fmt.Errorf("set: %s (%s)", errNameNotFound, name)
 	}
 	val = toValue(val, envs)
 	envs[0][name] = val
@@ -40,43 +40,43 @@ func setValue(s string, val value, envs []environ) (value, error) {
 }
 
 func getValue(s string, envs []environ) (value, error) {
-	if !isNameWithIndex(value{word: s}) {
-		return value{}, fmt.Errorf("get: %s (%s)", errInvalidName, s)
+	if !isNameWithIndex(&_unknown{s: s}) {
+		return nil, fmt.Errorf("get: %s (%s)", errInvalidName, s)
 	}
-	name, index := toNameWithIndex(value{word: s})
+	name, index := toNameWithIndex(&_unknown{s: s})
 	for _, env := range envs {
 		val, ok := env[name]
 		if ok {
 			if len(index) > 0 {
-				val = value{word: toString(val)}
 				for _, i := range index {
-					list := parseList(val.word)
-					r := strings.Split(i, ":")
+					list := parseList(val.String())
+					r, s := strings.Split(i, ":"), ""
 					if len(r) == 1 {
-						val = value{word: indexOfList(list, toInt(value{word: r[0]}, envs))}
-						if !isList(val) {
-							val.word = `"` + val.word
+						s = indexOfList(list, toInt(&_unknown{s: r[0]}, envs))
+						if !isList(&_unknown{s: s}) {
+							s = `"` + s
 						}
 					} else {
 						num1, num2 := 0, len(list)
-						if r[0] != "" {
-							num1 = toInt(value{word: r[0]}, envs)
+						if len(r[0]) > 0 {
+							num1 = toInt(&_unknown{s: r[0]}, envs)
 						}
-						if r[1] != "" {
-							num2 = toInt(value{word: r[1]}, envs)
+						if len(r[1]) > 0 {
+							num2 = toInt(&_unknown{s: r[1]}, envs)
 						}
-						val = value{word: rangeOfList(list, num1, num2)}
+						s = rangeOfList(list, num1, num2)
 					}
+					val = &_unknown{s: s}
 				}
 			}
 			return val, nil
 		}
 	}
-	return value{}, fmt.Errorf("get: %s (%s)", errNameNotFound, name)
+	return nil, fmt.Errorf("get: %s (%s)", errNameNotFound, name)
 }
 
 func opMake(val1, val2 value, envs []environ) value {
-	val, err := setValue(val1.word, val2, envs)
+	val, err := setValue(val1.String(), val2, envs)
 	if err != nil {
 		panic(err)
 	}
@@ -84,7 +84,7 @@ func opMake(val1, val2 value, envs []environ) value {
 }
 
 func opThing(val1 value, envs []environ) value {
-	val, err := getValue(val1.word, envs)
+	val, err := getValue(val1.String(), envs)
 	if err != nil {
 		panic(err)
 	}
@@ -119,7 +119,7 @@ func opSave(val1 value, env environ) value {
 	name := toName(val1)
 	s := ""
 	for name, val := range env {
-		s += `make "` + name + " " + val.word + "\n"
+		s += `make "` + name + " " + val.String() + "\n"
 	}
 	err := ioutil.WriteFile(name, []byte(s), 0666)
 	if err != nil {
@@ -128,7 +128,7 @@ func opSave(val1 value, env environ) value {
 	return val1
 }
 
-func opLoad(val1 value, env environ) value {
+func opLoad(val1 value, env environ) *_bool {
 	name := toName(val1)
 	s, err := ioutil.ReadFile(name)
 	if err != nil {
@@ -137,20 +137,20 @@ func opLoad(val1 value, env environ) value {
 	scanner := bufio.NewScanner(strings.NewReader(string(s)))
 	scanner.Split(splitFunc)
 	interpret(&scanProvider{isList: false, scanner: scanner}, []environ{env, nil, nil})
-	return value{tp: typeBool, b: true}
+	return &_bool{b: true}
 }
 
-func opErAll(env environ) value {
+func opErAll(env environ) *_bool {
 	for name := range env {
 		delete(env, name)
 	}
-	return value{tp: typeBool, b: true}
+	return &_bool{b: true}
 }
 
-func opPoAll(env environ) value {
+func opPoAll(env environ) *_list {
 	list := []string{}
 	for name := range env {
 		list = append(list, name)
 	}
-	return value{tp: typeList, list: list}
+	return &_list{list: list}
 }

+ 306 - 0
conv.go

@@ -0,0 +1,306 @@
+package main
+
+import (
+	"fmt"
+	"math"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+const (
+	_name      = `([A-Za-z_]\w*)`
+	_nameOrInt = "(" + _name + `|\d+)`
+	_index     = "(" + _nameOrInt + "|:|" + _nameOrInt + ":|:" + _nameOrInt + "|" + _nameOrInt + ":" + _nameOrInt + ")"
+)
+
+var (
+	nameExp          = regexp.MustCompile("^" + _name + "$")
+	nameWithIndexExp = regexp.MustCompile("^" + _name + `((\[` + _index + `])*\[` + _index + `\\])?$`)
+)
+
+func isNumber(val value) bool {
+	if _, ok := val.(*_number); ok {
+		return true
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+	}
+	_, err := strconv.ParseFloat(s, 64)
+	return err == nil
+}
+
+func toNumber(val value) float64 {
+	if val, ok := val.(*_number); ok {
+		return val.Value()
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+	}
+	if num, err := strconv.ParseFloat(s, 64); err == nil {
+		return num
+	}
+	panic(fmt.Errorf("number: %s (%s)", errInvalidNumber, s))
+}
+
+func toInt(val value, envs []environ) int {
+	if isName(val) {
+		if newVal, err := getValue(val.String(), envs); err == nil {
+			val = newVal
+			if val, ok := val.(*_number); ok {
+				num := val.Value()
+				if num != math.Floor(num) {
+					panic(fmt.Errorf("int: %s (%f)", errInvalidInteger, num))
+				}
+				return int(num)
+			}
+		}
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+	}
+	if num, err := strconv.ParseInt(s, 10, 64); err == nil {
+		return int(num)
+	}
+	panic(fmt.Errorf("int: %s (%s)", errInvalidInteger, s))
+}
+
+func isBool(val value) bool {
+	if _, ok := val.(*_bool); ok {
+		return true
+	}
+	if val, ok := val.(*_number); ok && (val.Value() == 0 || val.Value() == 1) {
+		return true
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+	}
+	return s == "false" || s == "true"
+}
+
+func toBool(val value) bool {
+	if val, ok := val.(*_bool); ok {
+		return val.Value()
+	}
+	if val, ok := val.(*_number); ok && (val.Value() == 0 || val.Value() == 1) {
+		return val.Value() == 1
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+	}
+	if s == "false" || s == "true" {
+		return s == "true"
+	}
+	panic(fmt.Errorf("bool: %s (%s)", errInvalidBool, s))
+}
+
+func isWord(val value) bool {
+	if _, ok := val.(*_word); ok {
+		return true
+	}
+	if val, ok := val.(*_number); ok && len(val.word) > 0 {
+		return true
+	}
+	if val, ok := val.(*_bool); ok && len(val.word) > 0 {
+		return true
+	}
+	s := val.String()
+	return len(s) > 0 && s[0] == '"'
+}
+
+func toWord(val value) string {
+	if val, ok := val.(*_word); ok {
+		return val.Value()
+	}
+	if val, ok := val.(*_number); ok && len(val.word) > 0 {
+		return val.word
+	}
+	if val, ok := val.(*_bool); ok && len(val.word) > 0 {
+		return val.word
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '"' {
+		return parseWord(s).Value()
+	}
+	panic(fmt.Errorf("word: %s (%s)", errInvalidWord, s))
+}
+
+func parseWord(s string) *_word {
+	if len(s) > 0 && s[0] == '"' {
+		s = s[1:]
+		if len(s) > 0 {
+			if s[0] == '[' {
+				s = `\[` + s[1:]
+			}
+			if s[len(s)-1] == ']' {
+				s = s[:len(s)-1]
+				escape := len(s) - len(strings.TrimRight(s, `\`))
+				if (escape & 1) == 0 {
+					s += `\`
+				}
+				s += "]"
+			}
+			if s[len(s)-1] == '\\' {
+				escape := len(s) - len(strings.TrimRight(s, `\`))
+				if (escape & 1) == 1 {
+					panic(fmt.Errorf("word: %s (%s)", errUnmatchedEscape, s))
+				}
+			}
+		}
+	}
+	return &_word{word: s}
+}
+
+func escapeWord(s string) string {
+	for i := 0; i < len(s); i++ {
+		if s[i] == '\\' {
+			s = s[:i] + s[i+1:]
+		}
+	}
+	return s
+}
+
+func isName(val value) bool {
+	return nameExp.MatchString(val.String())
+}
+
+func toName(val value) string {
+	if nameExp.MatchString(val.String()) {
+		return val.String()
+	}
+	panic(fmt.Errorf("name: %s (%s)", errInvalidName, val.String()))
+}
+
+func isNameWithIndex(val value) bool {
+	return nameWithIndexExp.MatchString(val.String())
+}
+
+func toNameWithIndex(val value) (string, []string) {
+	if nameWithIndexExp.MatchString(val.String()) {
+		index := strings.Split(val.String(), "[")
+		name := index[0]
+		if cnt := len(index) - 1; cnt > 0 {
+			for i := 1; i < cnt; i++ {
+				index[i] = index[i][:len(index[i])-1]
+			}
+			index[cnt] = index[cnt][:len(index[cnt])-2]
+		}
+		return name, index[1:]
+	}
+	panic(fmt.Errorf("name: %s (%s)", errInvalidName, val.String()))
+}
+
+func isList(val value) bool {
+	if _, ok := val.(*_list); ok {
+		return true
+	}
+	s := val.String()
+	return len(s) > 0 && s[0] == '['
+}
+
+func toList(val value) []string {
+	if val, ok := val.(*_list); ok {
+		return val.Value()
+	}
+	s := val.String()
+	if len(s) > 0 && s[0] == '[' {
+		return parseFunc(s, []environ{nil, nil, nil}).Value()
+	}
+	panic(fmt.Errorf("list: %s (%s)", errInvalidList, s))
+}
+
+func parseFunc(s string, envs []environ) *_list {
+	list := parseList(s)
+	if len(list) != 2 || !isList(&_unknown{s: list[0]}) || !isList(&_unknown{s: list[1]}) {
+		return &_list{list: list}
+	}
+	param := parseList(list[0])
+	for _, name := range param {
+		if !isName(&_unknown{s: name}) {
+			return &_list{list: list}
+		}
+	}
+	local := environ{}
+	if envs[2] != nil {
+		for name, val := range envs[0] {
+			local[name] = val
+		}
+	}
+	return &_list{list: list, param: param, body: parseList(list[1]), env: local}
+}
+
+func isEmpty(val value) bool {
+	if val, ok := val.(*_word); ok {
+		return len(val.Value()) == 0
+	}
+	if val, ok := val.(*_list); ok {
+		return len(val.Value()) == 0
+	}
+	s := val.String()
+	return s == `"` || s == "[ ]"
+}
+
+func toValue(val value, envs []environ) value {
+	if _, ok := val.(*_unknown); !ok {
+		return val
+	}
+	s := val.String()
+	if isNumber(val) {
+		if isWord(val) {
+			return &_number{num: toNumber(val), word: s[1:]}
+		}
+		return &_number{num: toNumber(val)}
+	}
+	if isBool(val) {
+		if isWord(val) {
+			return &_bool{b: toBool(val), word: s[1:]}
+		}
+		return &_bool{b: toBool(val)}
+	}
+	if isWord(val) {
+		return parseWord(s)
+	}
+	if isList(val) {
+		return parseFunc(s, envs)
+	}
+	panic(fmt.Errorf("value: %s (%s)", errValueExpected, s))
+}
+
+func opIsNumber(val1 value) *_bool {
+	return &_bool{b: isNumber(val1)}
+}
+
+func opIsBool(val1 value) *_bool {
+	return &_bool{b: isBool(val1)}
+}
+
+func opIsWord(val1 value) *_bool {
+	return &_bool{b: isWord(val1)}
+}
+
+func opIsName(val1 value, envs []environ) *_bool {
+	if !isName(val1) {
+		return &_bool{b: false}
+	}
+	name := toName(val1)
+	for _, env := range envs {
+		_, ok := env[name]
+		if ok {
+			return &_bool{b: true}
+		}
+	}
+	return &_bool{b: false}
+}
+
+func opIsList(val1 value) *_bool {
+	return &_bool{b: isList(val1)}
+}
+
+func opIsEmpty(val1 value) *_bool {
+	return &_bool{b: isEmpty(val1)}
+}

+ 2 - 2
ctrl.go

@@ -6,9 +6,9 @@ import (
 
 func opRun(val1 value, envs []environ) (value, bool) {
 	if !isList(val1) {
-		panic(fmt.Errorf("run: %s (%s)", errListExpected, toString(val1)))
+		panic(fmt.Errorf("run: %s (%s)", errListExpected, val1.String()))
 	}
-	return interpret(&scanProvider{isList: true, list: val1.list}, envs)
+	return interpret(&scanProvider{isList: true, list: toList(val1)}, envs)
 }
 
 func opIf(val1, val2, val3 value, envs []environ) (value, bool) {

+ 25 - 39
error.go

@@ -1,41 +1,27 @@
 package main
 
-const errInvalidName = "invalid name"
-
-const errInvalidNumber = "invalid number"
-
-const errInvalidInteger = "invalid integer"
-
-const errInvalidBool = "invalid bool"
-
-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 errFunctionExpected = "function expected"
-
-const errWordOrListExpected = "word or list expected"
-
-const errEmptyWordOrList = "empty word or list"
-
-const errIndexOutOfBound = "index out of bound"
-
-const errIllegalRange = "illegal range"
-
-const errIllegalOperandType = "illegal operand type"
-
-const errFileError = "file error"
+const (
+	errInvalidNumber        = "invalid number"
+	errInvalidInteger       = "invalid integer"
+	errInvalidBool          = "invalid bool"
+	errInvalidWord          = "invalid word"
+	errInvalidName          = "invalid name"
+	errInvalidList          = "invalid list"
+	errUnmatchedEscape      = "unmatched escape"
+	errUnmatchedBracket     = "unmatched bracket"
+	errDivisionByZero       = "division by zero"
+	errNegativeSquareRoot   = "negative square root"
+	errNameReserved         = "name reserved"
+	errNameNotFound         = "name not found"
+	errUnexpectedEndOfInput = "unexpected end of input"
+	errValueExpected        = "value expected"
+	errWordExpceted         = "word expected"
+	errListExpected         = "list expected"
+	errFunctionExpected     = "function expected"
+	errWordOrListExpected   = "word or list expected"
+	errEmptyWordOrList      = "empty word or list"
+	errIndexOutOfBound      = "index out of bound"
+	errIllegalRange         = "illegal range"
+	errIllegalOperandType   = "illegal operand type"
+	errFileError            = "file error"
+)

+ 4 - 4
in0

@@ -145,16 +145,16 @@ print word "hello true
 print word "hello -134.5
 
 print sentence 483 "dba
-print sentence sentence [ [ a b ] c ] [ d [ e f ] ] [ g ]
+print sentence sentence [ [ a b ] c ] [ d [ e f ] ] "g
 
 print list 483 "dba
-print list list [ [ a b ] c ] [ d [ e f ] ] [ g ]
+print list list [ [ a b ] c ] [ d [ e f ] ] "g
 
 print join [ 483 ] "dba
-print join join [ [ a b ] c ] [ d [ e f ] ] [ g ]
+print join join [ [ a b ] c ] [ d [ e f ] ] "g
 
 print first "hello
-print first butlast list list [ [ a b ] c ] [ d [ e f ] ] [ g ]
+print first butlast list list [ [ a b ] c ] [ d [ e f ] ] "g
 
 make "pow [[a b] [
   if lt :b 1 [

+ 5 - 22
io.go

@@ -2,7 +2,6 @@ package main
 
 import (
 	"fmt"
-	"strings"
 )
 
 func opRead(scanner *scanProvider) value {
@@ -13,31 +12,15 @@ func opRead(scanner *scanProvider) value {
 	if s == "\n" {
 		panic(fmt.Errorf("read: %s", errUnexpectedEndOfInput))
 	}
-	return value{word: `"` + s}
+	return &_unknown{s: `"` + s}
 }
 
 func opPrint(val1 value) value {
-	if val1.tp == typeNumber {
-		fmt.Println(val1.num)
-	} else if val1.tp == typeBool {
-		fmt.Println(val1.b)
-	} else if val1.tp == typeWord {
-		fmt.Println(escapeWord(val1.word[1:]))
-	} else {
-		list, space := strings.Split(makeList(val1.list), " "), false
-		for i := 0; i < len(list); i++ {
-			if space && list[i] != "]" {
-				fmt.Print(" ")
-			}
-			fmt.Print(escapeWord(list[i]))
-			space = list[i] != "["
-		}
-		fmt.Println()
-	}
+	fmt.Println(escapeWord(val1.String()))
 	return val1
 }
 
-func opReadList(scanner *scanProvider) value {
+func opReadList(scanner *scanProvider) *_list {
 	list, line := []string{}, false
 	for scanner.Scan() {
 		s := scanner.Text()
@@ -46,8 +29,8 @@ func opReadList(scanner *scanProvider) value {
 		}
 		line = true
 		if s != "\n" {
-			list = append(list, parseWord(`"` + s).word[1:])
+			list = append(list, parseWord(`"`+s).Value())
 		}
 	}
-	return value{tp: typeList, list: list}
+	return &_list{list: list}
 }

+ 40 - 32
list.go

@@ -6,7 +6,7 @@ import (
 )
 
 func parseList(s string) []string {
-	if !isList(value{word: s}) {
+	if !isList(&_unknown{s: s}) {
 		panic(fmt.Errorf("list: %s (%s)", errListExpected, s))
 	}
 	list, bracketCnt, start, resList := strings.Split(s, " "), 0, 1, []string{}
@@ -58,89 +58,97 @@ func spliceList(list []string, index int, cnt int, val ...string) string {
 
 func opWord(val1, val2 value) value {
 	if !isWord(val1) || isList(val2) {
-		panic(fmt.Errorf("word: %s (%s, %s)", errWordExpceted, toString(val1), toString(val2)))
+		panic(fmt.Errorf("word: %s (%s, %s)", errWordExpceted, val1.String(), val2.String()))
 	}
-	return value{tp: typeWord, word: val1.word + toString(val2)}
+	return &_unknown{s: `"` + toWord(val1) + val2.String()}
 }
 
 func opSentence(val1, val2 value) value {
-	list1, list2 := []string{toString(val1)}, []string{toString(val2)}
+	list1, list2 := []string{val1.String()}, []string{val2.String()}
 	if isList(val1) {
-		list1 = val1.list
+		list1 = toList(val1)
 	}
 	if isList(val2) {
-		list2 = val2.list
+		list2 = toList(val2)
 	}
-	return value{word: makeList(append(list1, list2...))}
+	return &_unknown{s: makeList(append(list1, list2...))}
 }
 
 func opList(val1, val2 value) value {
-	return value{word: makeList([]string{toString(val1), toString(val2)})}
+	return &_unknown{s: makeList([]string{val1.String(), val2.String()})}
 }
 
 func opJoin(val1, val2 value) value {
 	if !isList(val1) {
-		panic(fmt.Errorf("join: %s (%s)", errListExpected, toString(val1)))
+		panic(fmt.Errorf("join: %s (%s)", errListExpected, val1.String()))
 	}
-	return value{word: makeList(append(val1.list, toString(val2)))}
+	return &_unknown{s: makeList(append(toList(val1), val2.String()))}
 }
 
 func opFirst(val1 value) value {
 	if !isWord(val1) && !isList(val1) {
-		panic(fmt.Errorf("first: %s (%s)", errWordOrListExpected, toString(val1)))
+		panic(fmt.Errorf("first: %s (%s)", errWordOrListExpected, val1.String()))
 	}
 	if isEmpty(val1) {
-		panic(fmt.Errorf("first: %s (%s)", errEmptyWordOrList, toString(val1)))
+		panic(fmt.Errorf("first: %s (%s)", errEmptyWordOrList, val1.String()))
 	}
 	if isWord(val1) {
-		return value{word: val1.word[:2]}
+		s := escapeWord(toWord(val1))
+		return &_unknown{s: `"` + s[:1]}
 	}
-	val := value{word: val1.list[0]}
-	if !isList(val) {
-		val.word = `"` + val.word
+	list := toList(val1)
+	s := list[0]
+	if !isList(&_unknown{s: s}) {
+		s = `"` + s
 	}
-	return val
+	return &_unknown{s: s}
 }
 
 func opLast(val1 value) value {
 	if !isWord(val1) && !isList(val1) {
-		panic(fmt.Errorf("last: %s (%s)", errWordOrListExpected, toString(val1)))
+		panic(fmt.Errorf("last: %s (%s)", errWordOrListExpected, val1.String()))
 	}
 	if isEmpty(val1) {
-		panic(fmt.Errorf("last: %s (%s)", errEmptyWordOrList, toString(val1)))
+		panic(fmt.Errorf("last: %s (%s)", errEmptyWordOrList, val1.String()))
 	}
 	if isWord(val1) {
-		return value{word: `"` + val1.word[len(val1.word)-1:]}
+		s := escapeWord(toWord(val1))
+		return &_unknown{s: `"` + s[len(s)-1:]}
 	}
-	val := value{word: val1.list[len(val1.list)-1]}
-	if !isList(val) {
-		val.word = `"` + val.word
+	list := toList(val1)
+	s := list[len(list)-1]
+	if !isList(&_unknown{s: s}) {
+		s = `"` + s
 	}
-	return val
+	return &_unknown{s: s}
 }
 
 func opButFirst(val1 value) value {
 	if !isWord(val1) && !isList(val1) {
-		panic(fmt.Errorf("butfirst: %s (%s)", errWordOrListExpected, toString(val1)))
+		panic(fmt.Errorf("butfirst: %s (%s)", errWordOrListExpected, val1.String()))
 	}
 	if isEmpty(val1) {
-		panic(fmt.Errorf("butfirst: %s (%s)", errEmptyWordOrList, toString(val1)))
+		panic(fmt.Errorf("butfirst: %s (%s)", errEmptyWordOrList, val1.String()))
 	}
 	if isWord(val1) {
-		return value{word: `"` + val1.word[2:]}
+		s := escapeWord(toWord(val1))
+		return &_unknown{s: `"` + s[1:]}
 	}
-	return value{word: makeList(val1.list[1:])}
+	list := toList(val1)
+	return &_unknown{s: makeList(list[1:])}
 }
 
 func opButLast(val1 value) value {
 	if !isWord(val1) && !isList(val1) {
-		panic(fmt.Errorf("butlast: %s (%s)", errWordOrListExpected, toString(val1)))
+		panic(fmt.Errorf("butlast: %s (%s)", errWordOrListExpected, (val1.String())))
 	}
 	if isEmpty(val1) {
-		panic(fmt.Errorf("butlast: %s (%s)", errEmptyWordOrList, toString(val1)))
+		panic(fmt.Errorf("butlast: %s (%s)", errEmptyWordOrList, (val1.String())))
 	}
 	if isWord(val1) {
-		return value{word: val1.word[:len(val1.word)-1]}
+		s := escapeWord(toWord(val1))
+		return &_unknown{s: `"` + s[:len(s)-1]}
 	}
-	return value{word: makeList(val1.list[:len(val1.list)-1])}
+	list := toList(val1)
+	return &_unknown{s: makeList(list[:len(list)-1])}
 }

+ 43 - 46
math.go

@@ -6,97 +6,94 @@ import (
 	"math/rand"
 )
 
-func opAdd(val1, val2 value) value {
-	return value{tp: typeNumber, num: toNumber(val1) + toNumber(val2)}
+func opAdd(val1, val2 value) *_number {
+	return &_number{num: toNumber(val1) + toNumber(val2)}
 }
 
-func opSub(val1, val2 value) value {
-	return value{tp: typeNumber, num: toNumber(val1) - toNumber(val2)}
+func opSub(val1, val2 value) *_number {
+	return &_number{num: toNumber(val1) - toNumber(val2)}
 }
 
-func opMul(val1, val2 value) value {
-	return value{tp: typeNumber, num: toNumber(val1) * toNumber(val2)}
+func opMul(val1, val2 value) *_number {
+	return &_number{num: toNumber(val1) * toNumber(val2)}
 }
 
-func opDiv(val1, val2 value) value {
-	num1, num2 := toNumber(val1), toNumber(val2)
-	if num2 == 0 {
-		panic(fmt.Errorf("div: %s (%f / %f)", errDivisionByZero, num1, num2))
+func opDiv(val1, val2 value) *_number {
+	if num1, num2 := toNumber(val1), toNumber(val2); num2 != 0 {
+		return &_number{num: num1 / num2}
 	}
-	return value{tp: typeNumber, num: num1 / num2}
+	panic(fmt.Errorf("div: %s (%s / %s)", errDivisionByZero, val1.String(), val2.String()))
 }
 
-func opMod(val1, val2 value) value {
-	num1, num2 := toNumber(val1), toNumber(val2)
-	if num2 == 0 {
-		panic(fmt.Errorf("mod: %s (%f %% %f)", errDivisionByZero, num1, num2))
+func opMod(val1, val2 value) *_number {
+	if num1, num2 := toNumber(val1), toNumber(val2); num2 != 0 {
+		return &_number{num: math.Mod(num1, num2)}
 	}
-	return value{tp: typeNumber, num: math.Mod(num1, num2)}
+	panic(fmt.Errorf("mod: %s (%s %% %s)", errDivisionByZero, val1.String(), val2.String()))
 }
 
-func opEq(val1, val2 value) value {
+func opEq(val1, val2 value) *_bool {
 	if isNumber(val1) && isNumber(val2) {
-		return value{tp: typeBool, b: toNumber(val1) == toNumber(val2)}
+		return &_bool{b: toNumber(val1) == toNumber(val2)}
 	}
 	if isBool(val1) && isBool(val2) {
-		return value{tp: typeBool, b: toBool(val1) == toBool(val2)}
+		return &_bool{b: toBool(val1) == toBool(val2)}
 	}
 	if isWord(val1) && isWord(val2) {
-		return value{tp: typeBool, b: escapeWord(val1.word) == escapeWord(val2.word)}
+		return &_bool{b: escapeWord(toWord(val1)) == escapeWord(toWord(val2))}
 	}
-	panic(fmt.Errorf("eq: %s (%s == %s)", errIllegalOperandType, toString(val1), toString(val2)))
+	panic(fmt.Errorf("eq: %s (%s == %s)", errIllegalOperandType, val1.String(), val2.String()))
 }
 
-func opGt(val1, val2 value) value {
+func opGt(val1, val2 value) *_bool {
 	if isNumber(val1) && isNumber(val2) {
-		return value{tp: typeBool, b: toNumber(val1) > toNumber(val2)}
+		return &_bool{b: toNumber(val1) > toNumber(val2)}
 	}
 	if isBool(val1) && isBool(val2) {
-		return value{tp: typeBool, b: toBool(val1) || !toBool(val2)}
+		return &_bool{b: toBool(val1) && !toBool(val2)}
 	}
 	if isWord(val1) && isWord(val2) {
-		return value{tp: typeBool, b: escapeWord(val1.word) > escapeWord(val2.word)}
+		return &_bool{b: escapeWord(toWord(val1)) > escapeWord(toWord(val2))}
 	}
-	panic(fmt.Errorf("gt: %s (%s > %s)", errIllegalOperandType, toString(val1), toString(val2)))
+	panic(fmt.Errorf("gt: %s (%s > %s)", errIllegalOperandType, val1.String(), val2.String()))
 }
 
-func opLt(val1, val2 value) value {
+func opLt(val1, val2 value) *_bool {
 	if isNumber(val1) && isNumber(val2) {
-		return value{tp: typeBool, b: toNumber(val1) < toNumber(val2)}
+		return &_bool{b: toNumber(val1) < toNumber(val2)}
 	}
 	if isBool(val1) && isBool(val2) {
-		return value{tp: typeBool, b: !toBool(val1) || toBool(val2)}
+		return &_bool{b: !toBool(val1) && toBool(val2)}
 	}
 	if isWord(val1) && isWord(val2) {
-		return value{tp: typeBool, b: escapeWord(val1.word) < escapeWord(val2.word)}
+		return &_bool{b: escapeWord(toWord(val1)) < escapeWord(toWord(val2))}
 	}
-	panic(fmt.Errorf("lt: %s (%s < %s)", errIllegalOperandType, toString(val1), toString(val2)))
+	panic(fmt.Errorf("lt: %s (%s < %s)", errIllegalOperandType, val1.String(), val2.String()))
 }
 
-func opAnd(val1, val2 value) value {
-	return value{tp: typeBool, b: toBool(val1) && toBool(val2)}
+func opAnd(val1, val2 value) *_bool {
+	return &_bool{b: toBool(val1) && toBool(val2)}
 }
 
-func opOr(val1, val2 value) value {
-	return value{tp: typeBool, b: toBool(val1) || toBool(val2)}
+func opOr(val1, val2 value) *_bool {
+	return &_bool{b: toBool(val1) || toBool(val2)}
 }
 
-func opNot(val1 value) value {
-	return value{tp: typeBool, b: !toBool(val1)}
+func opNot(val1 value) *_bool {
+	return &_bool{b: !toBool(val1)}
 }
 
-func opRandom(val1 value) value {
-	return value{tp: typeNumber, num: toNumber(val1) * rand.Float64()}
+func opRandom(val1 value) *_number {
+	return &_number{num: toNumber(val1) * rand.Float64()}
 }
 
-func opInt(val1 value) value {
-	return value{tp: typeNumber, num: math.Floor(toNumber(val1))}
+func opInt(val1 value) *_number {
+	return &_number{num: math.Floor(toNumber(val1))}
 }
 
-func opSqrt(val1 value) value {
-	num1 := toNumber(val1)
-	if num1 < 0 {
-		panic(fmt.Errorf("sqrt: %s (%f)", errNegativeSquareRoot, num1))
+func opSqrt(val1 value) *_number {
+	if num1 := toNumber(val1); num1 >= 0 {
+		return &_number{num: math.Sqrt(num1)}
 	}
-	return value{tp: typeNumber, num: math.Sqrt(num1)}
+	panic(fmt.Errorf("sqrt: %s (%s)", errNegativeSquareRoot, val1.String()))
 }


+ 17 - 30
mua.go

@@ -21,18 +21,6 @@ var reserved = map[string]int{
 	"erall": 0, "poall": 0,
 	"false": 0, "true": 0}
 
-type environ map[string]value
-type value struct {
-	tp    int
-	num   float64
-	word  string
-	list  []string
-	param []string
-	body  []string
-	env   environ
-	b     bool
-}
-
 func interpret(scanner *scanProvider, envs []environ) (val value, returned bool) {
 	stack, returned := []value{}, false
 	for !returned && scanner.Scan() {
@@ -48,7 +36,7 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 		}
 		if s[0] == ':' {
 			leading := len(s) - len(strings.TrimLeft(s, ":"))
-			val := toValue(value{word: `"` + s[leading:]}, envs)
+			val := toValue(&_unknown{s: `"` + s[leading:]}, envs)
 			for i := 0; i < leading; i++ {
 				val = toValue(opThing(val, envs), envs)
 			}
@@ -64,7 +52,7 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 				for i := 0; i < leading; i++ {
 					list = append(list, "[")
 				}
-				if s != "" {
+				if len(s) > 0 {
 					list = append(list, s)
 				}
 				for i := 0; i < trailing; i++ {
@@ -85,27 +73,26 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 					panic(fmt.Errorf("list: %s", errUnmatchedBracket))
 				}
 			}
-			stack = append(stack, value{word: strings.Join(list, " ")})
+			stack = append(stack, &_unknown{s: strings.Join(list, " ")})
 		} else {
-			stack = append(stack, value{word: s})
+			stack = append(stack, &_unknown{s: s})
 		}
-		updated := true
-		for !returned && updated {
+		for updated := true; !returned && updated; {
 			updated = false
 			for i := len(stack) - 1; i >= 0; i-- {
-				if !isName(value{word: `"` + stack[i].word}) {
+				if _, ok := stack[i].(*_unknown); !ok || !isName(stack[i]) {
 					continue
 				}
-				paramCnt, ok := reserved[stack[i].word]
-				if ok {
-					if stack[i].word == "false" || stack[i].word == "true" {
+				op := stack[i].String()
+				if paramCnt, ok := reserved[op]; ok {
+					if op == "false" || op == "true" {
 						continue
 					}
 					if paramCnt == len(stack)-1-i {
 						for j := i + 1; j < len(stack); j++ {
 							stack[j] = toValue(stack[j], envs)
 						}
-						switch stack[i].word {
+						switch op {
 						case "make":
 							stack[i] = opMake(stack[i+1], stack[i+2], envs)
 						case "thing":
@@ -196,13 +183,13 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 					}
 					break
 				}
-				val, _ := getValue(`"`+stack[i].word, envs)
-				if val.param != nil {
+				val, _ := getValue(op, envs)
+				if val, ok := val.(*_list); ok && val.param != nil {
 					if len(val.param) == len(stack)-1-i {
 						for j := i + 1; j < len(stack); j++ {
 							stack[j] = toValue(stack[j], envs)
 						}
-						local := environ{stack[i].word: val}
+						local := environ{op: val}
 						for j, name := range val.param {
 							local[name] = stack[i+1+j]
 						}
@@ -214,18 +201,18 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 						if envs[2] != nil {
 							global = envs[2]
 						}
-						stack[i], _ = opRun(value{tp: typeList, list: val.body}, []environ{local, ctx, global})
+						stack[i], _ = opRun(&_list{list: val.body}, []environ{local, ctx, global})
 						stack = stack[:i+1]
 						updated = true
 					}
 					break
 				}
-				panic(fmt.Errorf("func: %s (%s)", errFunctionExpected, stack[i].word))
+				panic(fmt.Errorf("func: %s (%s)", errFunctionExpected, op))
 			}
 		}
 	}
 	if len(stack) == 0 {
-		val = value{tp: typeList, list: []string{}}
+		val = &_list{list: []string{}}
 	} else {
 		val = toValue(stack[len(stack)-1], envs)
 	}
@@ -234,7 +221,7 @@ func interpret(scanner *scanProvider, envs []environ) (val value, returned bool)
 
 func main() {
 	rand.Seed(time.Now().UnixNano())
-	env := map[string]value{"pi": {tp: typeNumber, num: 3.14159}}
+	env := map[string]value{"pi": &_number{num: 3.141592654}}
 	if len(os.Args) > 1 {
 		file, err := os.Open(os.Args[1])
 		if err != nil {

+ 10 - 12
scan.go

@@ -13,23 +13,21 @@ type scanProvider struct {
 	scanner *bufio.Scanner
 }
 
-func (ip *scanProvider) Scan() bool {
-	if ip.isList {
-		if len(ip.list) == 0 {
-			return false
+func (sp *scanProvider) Scan() bool {
+	if sp.isList {
+		for sp.text = ""; len(sp.text) == 0 && len(sp.list) > 0; {
+			sp.text, sp.list = sp.list[0], sp.list[1:]
 		}
-		ip.text = ip.list[0]
-		ip.list = ip.list[1:]
-		return true
+		return len(sp.text) > 0
 	}
-	return ip.scanner.Scan()
+	return sp.scanner.Scan()
 }
 
-func (ip *scanProvider) Text() string {
-	if ip.isList {
-		return ip.text
+func (sp *scanProvider) Text() string {
+	if sp.isList {
+		return sp.text
 	}
-	return ip.scanner.Text()
+	return sp.scanner.Text()
 }
 
 func splitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {

+ 42 - 243
type.go

@@ -2,281 +2,80 @@ package main
 
 import (
 	"fmt"
-	"math"
-	"regexp"
 	"strconv"
-	"strings"
 )
 
-const (
-	typeUnknown = iota
-	typeNumber
-	typeBool
-	typeWord
-	typeList
-)
-
-const (
-	_name      = `([A-Za-z_]\w*)`
-	_nameOrInt = "(" + _name + `|\d+)`
-	_index     = "(" + _nameOrInt + "|:|" + _nameOrInt + ":|:" + _nameOrInt + "|" + _nameOrInt + ":" + _nameOrInt + ")"
-)
-
-var nameExp = regexp.MustCompile(`^"` + _name + "$")
-var nameWithIndexExp = regexp.MustCompile(`^"` + _name + `((\[` + _index + `])*\[` + _index + `\\])?$`)
-
-func isNumber(val value) bool {
-	if val.tp == typeNumber {
-		return true
-	}
-	if isWord(val) {
-		val.word = val.word[1:]
+type (
+	_unknown struct {
+		s string
 	}
-	_, err := strconv.ParseFloat(val.word, 64)
-	return err == nil
-}
 
-func toNumber(val value) float64 {
-	if val.tp == typeNumber {
-		return val.num
-	}
-	if isWord(val) {
-		val.word = val.word[1:]
+	_number struct {
+		num  float64
+		word string
 	}
-	num, err := strconv.ParseFloat(val.word, 64)
-	if err != nil {
-		panic(fmt.Errorf("number: %s (%s)", errInvalidNumber, toString(val)))
-	}
-	return num
-}
 
-func toInt(val value, envs []environ) int {
-	if isName(value{word: `"` + val.word}) {
-		newVal, err := getValue(`"`+val.word, envs)
-		if err == nil {
-			val = newVal
-			if val.tp == typeNumber {
-				if val.num != math.Floor(val.num) {
-					panic(fmt.Errorf("int: %s (%f)", errInvalidInteger, val.num))
-				}
-				return int(val.num)
-			}
-		}
-	}
-	if isWord(val) {
-		val.word = val.word[1:]
-	}
-	num, err := strconv.ParseInt(val.word, 10, 64)
-	if err != nil {
-		panic(fmt.Errorf("int: %s (%s)", errInvalidInteger, toString(val)))
+	_bool struct {
+		b    bool
+		word string
 	}
-	return int(num)
-}
 
-func isBool(val value) bool {
-	if val.tp == typeBool {
-		return true
-	}
-	if val.tp == typeNumber && (val.num == 0 || val.num == 1) {
-		return true
-	}
-	if isWord(val) {
-		val.word = val.word[1:]
+	_word struct {
+		word string
 	}
-	return val.word == "false" || val.word == "true"
-}
-
-func toBool(val value) bool {
-	if val.tp == typeBool {
-		return val.b
-	}
-	if val.tp == typeNumber && (val.num == 0 || val.num == 1) {
-		return val.num == 1
-	}
-	if isWord(val) {
-		val.word = val.word[1:]
-	}
-	if val.word == "false" || val.word == "true" {
-		return val.word == "true"
-	}
-	panic(fmt.Errorf("bool: %s (%s)", errInvalidBool, toString(val)))
-}
 
-func isWord(val value) bool {
-	if val.tp == typeWord {
-		return true
+	_list struct {
+		list  []string
+		param []string
+		body  []string
+		env   environ
 	}
-	return val.word != "" && val.word[0] == '"'
-}
-
-func parseWord(s string) value {
-	if s != `"` {
-		if s[1] == '[' {
-			s = `"\[` + s[2:]
-		}
-		if s[len(s)-1] == ']' {
-			s = s[:len(s)-1]
-			escape := len(s) - len(strings.TrimRight(s, `\`))
-			if (escape & 1) == 0 {
-				s += `\`
-			}
-			s += "]"
-		}
-	}
-	return value{tp: typeWord, word: s}
-}
 
-func escapeWord(s string) string {
-	for i := 0; i < len(s); i++ {
-		if s[i] == '\\' {
-			s = s[:i] + s[i+1:]
-		}
+	value interface {
+		fmt.Stringer
 	}
-	return s
-}
-
-func isName(val value) bool {
-	return nameExp.MatchString(val.word)
-}
 
-func toName(val value) string {
-	if !isName(val) {
-		panic(fmt.Errorf("name: %s (%s)", errInvalidName, toString(val)))
-	}
-	return val.word[1:]
-}
-
-func isNameWithIndex(val value) bool {
-	return nameWithIndexExp.MatchString(val.word)
-}
-
-func toNameWithIndex(val value) (string, []string) {
-	if !isNameWithIndex(val) {
-		panic(fmt.Errorf("name: %s (%s)", errInvalidName, toString(val)))
-	}
-	index := strings.Split(val.word, "[")
-	name, cnt := index[0][1:], len(index)-1
-	for i := 1; i < cnt; i++ {
-		index[i] = index[i][:len(index[i])-1]
-	}
-	index[cnt] = index[cnt][:len(index[cnt])-2]
-	return name, index[1:]
-}
-
-func isList(val value) bool {
-	if val.tp == typeList {
-		return true
-	}
-	return val.word != "" && val.word[0] == '['
-}
+	environ map[string]value
+)
 
-func parseFunc(s string, envs []environ) value {
-	list := parseList(s)
-	if len(list) != 2 || !isList(value{word: list[0]}) || !isList(value{word: list[1]}) {
-		return value{tp: typeList, list: list}
-	}
-	param := parseList(list[0])
-	for _, name := range param {
-		if !isName(value{word: `"` + name}) {
-			return value{tp: typeList, list: list}
-		}
-	}
-	local := environ{}
-	if envs[2] != nil {
-		for name, val := range envs[0] {
-			local[name] = val
-		}
-	}
-	return value{tp: typeList, list: list, param: param, body: parseList(list[1]), env: local}
+func (val *_unknown) String() string {
+	return val.s
 }
 
-func isEmpty(val value) bool {
-	if val.tp == typeWord {
-		return val.word == `"`
-	}
-	if val.tp == typeList {
-		return len(val.list) == 0
+func (val *_number) String() string {
+	if len(val.word) > 0 {
+		return val.word
 	}
-	return val.word == `"` || val.word == "[ ]"
+	return strconv.FormatFloat(val.num, 'g', -1, 64)
 }
 
-func toValue(val value, envs []environ) value {
-	if val.tp != typeUnknown {
-		return val
-	}
-	if isNumber(val) {
-		if isWord(val) {
-			return value{tp: typeNumber, word: val.word, num: toNumber(val)}
-		}
-		return value{tp: typeNumber, num: toNumber(val)}
-	}
-	if isBool(val) {
-		if isWord(val) {
-			return value{tp: typeBool, word: val.word, b: toBool(val)}
-		}
-		return value{tp: typeBool, b: toBool(val)}
-	}
-	if isWord(val) {
-		return parseWord(val.word)
-	}
-	if isList(val) {
-		return parseFunc(val.word, envs)
-	}
-	panic(fmt.Errorf("value: %s (%s)", errValueExpected, val.word))
+func (val *_number) Value() float64 {
+	return val.num
 }
 
-func toString(val value) string {
-	if val.tp == typeNumber {
-		if val.word != "" {
-			return val.word[1:]
-		}
-		return strconv.FormatFloat(val.num, 'g', -1, 64)
+func (val *_bool) String() string {
+	if len(val.word) > 0 {
+		return val.word
 	}
-	if val.tp == typeBool {
-		if val.word != "" {
-			return val.word[1:]
-		}
-		return strconv.FormatBool(val.b)
-	}
-	if val.tp == typeWord {
-		return val.word[1:]
-	}
-	if val.tp == typeList {
-		return makeList(val.list)
-	}
-	panic(fmt.Errorf("string: %s (%s)", errValueExpected, val.word))
+	return strconv.FormatBool(val.b)
 }
 
-func opIsNumber(val1 value) value {
-	return value{tp: typeBool, b: isNumber(val1)}
+func (val *_bool) Value() bool {
+	return val.b
 }
 
-func opIsBool(val1 value) value {
-	return value{tp: typeBool, b: isBool(val1)}
+func (val *_word) String() string {
+	return val.word
 }
 
-func opIsWord(val1 value) value {
-	return value{tp: typeBool, b: isWord(val1)}
-}
-
-func opIsName(val1 value, envs []environ) value {
-	if !isName(val1) {
-		return value{tp: typeBool, b: false}
-	}
-	name := toName(val1)
-	for _, env := range envs {
-		_, ok := env[name]
-		if ok {
-			return value{tp: typeBool, b: true}
-		}
-	}
-	return value{tp: typeBool, b: false}
+func (val *_word) Value() string {
+	return val.word
 }
 
-func opIsList(val1 value) value {
-	return value{tp: typeBool, b: isList(val1)}
+func (val *_list) String() string {
+	return makeList(val.list)
 }
 
-func opIsEmpty(val1 value) value {
-	return value{tp: typeBool, b: isEmpty(val1)}
+func (val *_list) Value() []string {
+	return val.list
 }