| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- 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:]
- }
- _, 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:]
- }
- 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)))
- }
- 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:]
- }
- 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
- }
- 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:]
- }
- }
- 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] == '['
- }
- func parseFunc(s string, env 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{}
- for name, val := range env {
- local[name] = val
- }
- return value{tp: typeList, list: list, param: param, body: parseList(list[1]), env: local}
- }
- func isEmpty(val value) bool {
- if val.tp == typeWord {
- return val.word == `"`
- }
- if val.tp == typeList {
- return len(val.list) == 0
- }
- return val.word == `"` || val.word == "[ ]"
- }
- func toValue(val value, env 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, env)
- }
- panic(fmt.Errorf("value: %s (%s)", errValueExpected, val.word))
- }
- func toString(val value) string {
- if val.tp == typeNumber {
- if val.word != "" {
- return val.word[1:]
- }
- return strconv.FormatFloat(val.num, 'g', -1, 64)
- }
- 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))
- }
- func opIsNumber(val1 value) value {
- return value{tp: typeBool, b: isNumber(val1)}
- }
- func opIsBool(val1 value) value {
- return value{tp: typeBool, b: isBool(val1)}
- }
- 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 opIsList(val1 value) value {
- return value{tp: typeBool, b: isList(val1)}
- }
- func opIsEmpty(val1 value) value {
- return value{tp: typeBool, b: isEmpty(val1)}
- }
|