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 { s := val.String() return nameExp.MatchString(s) } func toName(val value) string { s := val.String() if nameExp.MatchString(s) { return s } panic(fmt.Errorf("name: %s (%s)", errInvalidName, s)) } func isNameWithIndex(val value) bool { s := val.String() return nameWithIndexExp.MatchString(s) } func toNameWithIndex(val value) (string, []string) { s := val.String() if nameWithIndexExp.MatchString(s) { index := strings.Split(s, "[") 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, s)) } 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[1] != 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) { s = s[1:] } return &_number{num: toNumber(val), word: s} } if isBool(val) { if isWord(val) { s = s[1:] } return &_bool{b: toBool(val), word: s} } 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)} }