package main import ( "bufio" "fmt" "math/rand" "os" "strings" "time" ) 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 { tp int num float64 word string list []string split []string param []string body []string env environ b bool } func interpret(scanner *scanProvider, envs []environ) (val value, returned bool) { stack, returned := []value{}, false for !returned && scanner.Scan() { // for _, v := range stack { // fmt.Println(toString(v)) // } // fmt.Println() // fmt.Println(envs[0]) // fmt.Println() s := scanner.Text() if s == "\n" { continue } if s[0] == ':' { leading := len(s) - len(strings.TrimLeft(s, ":")) val := toValue(value{word: `"` + s[leading:]}, envs[0]) for i := 0; i < leading; i++ { val = toValue(opThing(val, envs), envs[0]) } 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{word: strings.Join(list, " ")}) } else { stack = append(stack, value{word: s}) } for !returned { updated := false for i := len(stack) - 1; i >= 0; i-- { if !isName(value{word: `"` + stack[i].word}) { continue } paramCnt, ok := reserved[stack[i].word] if ok { if paramCnt > len(stack)-1-i { break } for j := i + 1; j < len(stack); j++ { stack[j] = toValue(stack[j], envs[0]) } switch stack[i].word { case "make": stack[i] = opMake(stack[i+1], stack[i+2], envs) case "thing": stack[i] = opThing(stack[i+1], envs) 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], envs) case "isname": stack[i] = opIsName(stack[i+1]) case "run": stack[i], returned = opRun(stack[i+1], envs) 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], envs) 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], envs) 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], envs[0]) case "load": stack[i] = opLoad(stack[i+1], envs[0]) case "erall": stack[i] = opErAll(envs[0]) case "poall": stack[i] = opPoAll(envs[0]) } stack = stack[:i+1] updated = true } val, _ := getValue(`"`+stack[i].word, envs) if val.param != nil { if len(val.param) > len(stack)-1-i { break } for j := i + 1; j < len(stack); j++ { stack[j] = toValue(stack[j], envs[0]) } local := environ{stack[i].word: val} for j, name := range val.param { local[name] = stack[i+1+j] } stack[i], _ = opRun(value{tp: typeList, split: val.body}, []environ{local, val.env, envs[2]}) stack = stack[:i+1] updated = true } } if !updated { break } } } if len(stack) == 0 { val = value{tp: typeList, list: []string{}, split: []string{"[", "]"}} return } val = toValue(stack[len(stack)-1], envs[0]) return } func main() { rand.Seed(time.Now().UnixNano()) env := map[string]value{"pi": {tp: typeNumber, num: 3.14159}} if len(os.Args) > 1 { file, err := os.Open(os.Args[1]) if err != nil { panic(fmt.Errorf("script: %s (%s)", errFileError, os.Args[1])) } os.Stdin = file } scanner := bufio.NewScanner(os.Stdin) scanner.Split(splitFunc) interpret(&scanProvider{isList: false, scanner: scanner}, []environ{env, nil, env}) }