package main import ( "fmt" "regexp" "strconv" "strings" ) const _name, _nameOrInt = `([A-Za-z]\w*)`, "(" + _name + `|\d+)` const _index = "(" + _nameOrInt + "|:|" + _nameOrInt + ":|:" + _nameOrInt + "|" + _nameOrInt + ":" + _nameOrInt + ")" var nameExp = regexp.MustCompile(`^"` + _name + "$") var nameWithIndexExp = regexp.MustCompile(`^"` + _name + `((\[` + _index + `])*\[` + _index + `\\])?$`) func isName(s string) bool { return nameExp.MatchString(s) } func toName(s string) string { if !isName(s) { panic(fmt.Errorf("name: %s (%s)", errInvalidName, s)) } return s[1:] } func isNameWithIndex(s string) bool { return nameWithIndexExp.MatchString(s) } func toNameWithIndex(s string) (string, []string) { if !isNameWithIndex(s) { panic(fmt.Errorf("name: %s (%s)", errInvalidName, s)) } index := strings.Split(s, "[") 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 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 toInt(s string) int { if isName(`"` + s) { val, err := getValue(`"` + s) if err == nil { s = val.val } } if s != "" && s[0] == '"' { s = s[1:] } val, err := strconv.ParseInt(s, 10, 64) if err != nil { panic(fmt.Errorf("int: %s (%s)", errInvalidInteger, s)) } return int(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 parseFunc(s string) value { if !isList(s) { return value{val: s} } list := parseList(s) if len(list) != 2 || !isList(list[0]) || !isList(list[1]) { return value{val: s, list: list} } param := parseList(list[0]) for _, name := range param { if !isName(`"` + name) { return value{val: s, list: list} } } local := make(environ) for name, val := range env[len(env)-1] { local[name] = val } return value{val: s, list: list, param: param, env: local} } func isValue(s string) bool { return isNumber(s) || isWord(s) || isList(s) || isBool(s) } func toValue(val value) value { if !isValue(val.val) { panic(fmt.Errorf("mua: %s (%s)", errValueExpected, val.val)) } if isWord(val.val) { if val.val[1] == '[' { val.val = val.val[:1] + "\\" + val.val[1:] } if val.val[len(val.val)-1] == ']' { val.val = val.val[:len(val.val)-1] escape := len(val.val) - len(strings.TrimRight(val.val, "\\")) if (escape & 1) == 0 { val.val += "\\" } val.val += "]" } return val } if isList(val.val) { if val.list == nil { return parseFunc(val.val) } return val } return val } func escapeValue(s string) string { for i := 0; i < len(s); i++ { if s[i] == '\\' { s = s[:i] + s[i+1:] } } return 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))} }