package main import ( "bufio" "fmt" "math/rand" "os" "strings" "time" "unicode" "unicode/utf8" ) var reserved = map[string]int{ "make": 2, "thing": 1, "print": 1, "read": 0, "add": 2, "sub": 2, "mul": 2, "div": 2, "mod": 2, "erase": 1, "isname": 1, "run": 1, "eq": 2, "gt": 2, "lt": 2, "and": 2, "or": 2, "not": 1, "if": 3, "isnumber": 1, "isword": 1, "islist": 1, "isbool": 1, "isempty": 1, "return": 1, "export": 1, "readlist": 0, "word": 2, "sentence": 2, "list": 2, "join": 2, "first": 1, "last": 1, "butfirst": 1, "butlast": 1, "random": 1, "int": 1, "sqrt": 1, "save": 1, "load": 1, "erall": 0, "poall": 0} type environ map[string]value type value struct { val string list []string param []string env environ } var env []environ var stack []value func interpret(scanner *bufio.Scanner) (val value, returned bool) { stackTop, returned := len(stack), false scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { start := 0 for start < len(data) { r, width := utf8.DecodeRune(data[start:]) if r == '\n' { return start + width, data[start : start+width], nil } if !unicode.IsSpace(r) { break } start += width } for i := start; i < len(data); { r, width := utf8.DecodeRune(data[i:]) if r == '\n' { return i, data[start:i], nil } if unicode.IsSpace(r) { return i + width, data[start:i], nil } i += width } if atEOF && len(data) > start { return len(data), data[start:], nil } return start, nil, nil }) for !returned && scanner.Scan() { // for _, v := range stack { // fmt.Println(v.val) // } // fmt.Println() s := scanner.Text() if s == "\n" { continue } if s[0] == ':' { leading := len(s) - len(strings.TrimLeft(s, ":")) val := toValue(value{val: `"` + s[leading:]}) for i := 0; i < leading; i++ { val = opThing(val) } stack = append(stack, val) } else if s[0] == '[' { list, bracketCnt := []string{}, 0 for { leading := len(s) - len(strings.TrimLeft(s, "[")) trailing := len(s) - len(strings.TrimRight(s, "]")) escape := len(s) - len(strings.TrimRight(strings.TrimRight(s, "]"), "\\")) - trailing trailing -= escape & 1 s = s[leading : len(s)-trailing] for i := 0; i < leading; i++ { list = append(list, "[") } if s != "" { list = append(list, s) } for i := 0; i < trailing; i++ { list = append(list, "]") } bracketCnt += leading - trailing if bracketCnt < 0 { panic(fmt.Errorf("list: %s", errUnmatchedBracket)) } if bracketCnt == 0 { break } s = "\n" for s == "\n" && scanner.Scan() { s = scanner.Text() } if s == "\n" { panic(fmt.Errorf("list: %s", errUnmatchedBracket)) } } stack = append(stack, value{val: strings.Join(list, " ")}) } else if s[0] >= '0' && s[0] <= '9' || s[0] == '-' { toNumber(s) stack = append(stack, value{val: s}) } else { stack = append(stack, value{val: s}) } for !returned { updated := false for i := len(stack) - 1; i >= stackTop; i-- { if !isNameWithIndex(`"` + stack[i].val) { continue } paramCnt, ok := reserved[stack[i].val] if ok { if paramCnt == len(stack)-1-i { for j := i + 1; j < len(stack); j++ { stack[j] = toValue(stack[j]) } switch stack[i].val { case "make": stack[i] = opMake(stack[i+1], stack[i+2]) case "thing": stack[i] = opThing(stack[i+1]) case "print": stack[i] = opPrint(stack[i+1]) case "read": stack[i] = opRead(scanner) case "add": stack[i] = opAdd(stack[i+1], stack[i+2]) case "sub": stack[i] = opSub(stack[i+1], stack[i+2]) case "mul": stack[i] = opMul(stack[i+1], stack[i+2]) case "div": stack[i] = opDiv(stack[i+1], stack[i+2]) case "mod": stack[i] = opMod(stack[i+1], stack[i+2]) case "erase": stack[i] = opErase(stack[i+1]) case "isname": stack[i] = opIsName(stack[i+1]) case "run": stack[i], returned = opRun(stack[i+1]) case "eq": stack[i] = opEq(stack[i+1], stack[i+2]) case "gt": stack[i] = opGt(stack[i+1], stack[i+2]) case "lt": stack[i] = opLt(stack[i+1], stack[i+2]) case "and": stack[i] = opAnd(stack[i+1], stack[i+2]) case "or": stack[i] = opOr(stack[i+1], stack[i+2]) case "not": stack[i] = opNot(stack[i+1]) case "if": stack[i], returned = opIf(stack[i+1], stack[i+2], stack[i+3]) case "isnumber": stack[i] = opIsNumber(stack[i+1]) case "isword": stack[i] = opIsWord(stack[i+1]) case "islist": stack[i] = opIsList(stack[i+1]) case "isbool": stack[i] = opIsBool(stack[i+1]) case "isempty": stack[i] = opIsEmpty(stack[i+1]) case "return": stack[i], returned = stack[i+1], true case "export": stack[i] = opExport(stack[i+1]) case "readlist": stack[i] = opReadList(scanner) case "word": stack[i] = opWord(stack[i+1], stack[i+2]) case "sentence": stack[i] = opSentence(stack[i+1], stack[i+2]) case "list": stack[i] = opList(stack[i+1], stack[i+2]) case "join": stack[i] = opJoin(stack[i+1], stack[i+2]) case "first": stack[i] = opFirst(stack[i+1]) case "last": stack[i] = opLast(stack[i+1]) case "butfirst": stack[i] = opButFirst(stack[i+1]) case "butlast": stack[i] = opButLast(stack[i+1]) case "random": stack[i] = opRandom(stack[i+1]) case "int": stack[i] = opInt(stack[i+1]) case "sqrt": stack[i] = opSqrt(stack[i+1]) case "save": stack[i] = opSave(stack[i+1]) case "load": stack[i] = opLoad(stack[i+1]) case "erall": stack[i] = opErAll() case "poall": stack[i] = opPoAll() } stack = stack[:i+1] updated = true } break } val, _ := getValue(`"` + stack[i].val) if val.param != nil { if len(val.param) == len(stack)-1-i { for j := i + 1; j < len(stack); j++ { stack[j] = toValue(stack[j]) } local := environ{stack[i].val: val} for j, name := range val.param { local[name] = stack[i+1+j] } env = append(env, val.env, local) stack[i], _ = opRun(value{val: val.list[1]}) env = env[:len(env)-2] stack = stack[:i+1] updated = true } break } } if !updated { break } } } if len(stack) == stackTop { stack = append(stack, value{val: "[ ]"}) } val = toValue(stack[len(stack)-1]) stack = stack[:stackTop] return } func main() { rand.Seed(time.Now().UnixNano()) env = append(env, nil, map[string]value{"pi": {val: "3.14159"}}) if len(os.Args) > 1 { file, err := os.Open(os.Args[1]) if err != nil { panic(fmt.Errorf("mua: %s (%s)", errFileError, os.Args[1])) } os.Stdin = file } scanner := bufio.NewScanner(os.Stdin) interpret(scanner) }