|
|
@@ -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
|
|
|
}
|