diff options
author | Juan J. Martinez <jjm@usebox.net> | 2022-07-18 07:45:58 +0100 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2022-07-18 07:45:58 +0100 |
commit | 8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642 (patch) | |
tree | c53977d1284347bb1d5963ddb4dc7723c40c6e55 /cmd | |
download | micro-lang-8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642.tar.gz micro-lang-8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642.zip |
First public release
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/micro/micro.go | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go new file mode 100644 index 0000000..3e5c9e6 --- /dev/null +++ b/cmd/micro/micro.go @@ -0,0 +1,202 @@ +package main + +import ( + "bufio" + "encoding/json" + "errors" + "flag" + "fmt" + "os" + "path" + "strings" + "time" + + "usebox.net/lang/interpreter" + "usebox.net/lang/parser" + "usebox.net/lang/tokenizer" +) + +const name = "micro" + +var Version = "<unset>" + +func perror(err error) { + fmt.Fprintln(os.Stderr, err) + + first := err + for { + perr := err + err = errors.Unwrap(err) + if err == nil { + if perr.Error() != first.Error() { + fmt.Fprintf(os.Stderr, "%s\n", perr) + } + break + } + } +} + +func fatal(errno int, err error) { + perror(err) + os.Exit(errno) +} + +func fatals(typ string, errno int, err error) { + fmt.Fprintf(os.Stderr, "%s: %s\n", name, typ) + fatal(errno, err) +} + +func repl() { + scanner := bufio.NewScanner(os.Stdin) + p := parser.NewParser(interpreter.BuiltInTypes()) + i := interpreter.NewInterpreter() + + fmt.Printf("Welcome to %s %s\nType in expressions for evaluation, or :q to exit.\n\n", strings.Title(name), Version) + + repl := 0 + for { + fmt.Printf("%s> ", name) + var line string + multiline := false + for { + if multiline { + fmt.Print(" | ") + line += " " + } + if !scanner.Scan() { + fmt.Println() + return + } + r := scanner.Text() + if r == ":q" { + return + } + line += r + + if !multiline { + if r == "" { + continue + } + if r[len(r)-1] != ';' { + multiline = true + continue + } + break + } + if r == "" { + break + } + } + + in := strings.NewReader(line) + repl++ + t := tokenizer.NewTokenizer(fmt.Sprintf("in%d", repl), in) + ts, err := t.Scan() + if err != nil { + perror(err) + continue + } + + tree, err := p.Parse(ts) + if err != nil { + perror(err) + continue + } + + for _, expr := range tree { + v, err := i.Interp(expr) + if err != nil { + perror(err) + break + } + if v != nil { + fmt.Println(v) + } + } + } +} + +func usage() { + me := path.Base(os.Args[0]) + fmt.Fprintf(os.Stderr, + "Usage: %s [options] ... [file]\nOptions:\n", me) + flag.PrintDefaults() + fmt.Fprintln(os.Stderr) +} + +func main() { + fVersion := flag.Bool("V", false, "display version and exit") + fParse := flag.Bool("parse", false, "parse reporting any errors and exit") + fDebugTokens := flag.Bool("tokens", false, "dump to stderr tokens and exit") + fDebugAst := flag.Bool("ast", false, "dump to stderr AST and exit") + fTime := flag.Bool("time", false, "measure execution time") + flag.Usage = usage + flag.Parse() + + if *fVersion { + fmt.Printf("%s %s\n", name, Version) + os.Exit(0) + } + + var input *os.File + var filename = flag.Arg(0) + + if filename == "" { + repl() + os.Exit(0) + } else { + var err error + input, err = os.Open(filename) + if err != nil { + fatals("error opening input file", 1, err) + } + } + t := tokenizer.NewTokenizer(filename, input) + ts, err := t.Scan() + if err != nil { + fatals("parsing error", 10, err) + } + + if *fDebugTokens { + out, err := json.MarshalIndent(ts, "", " ") + if err != nil { + fatals("encoding error", 11, err) + } + fmt.Fprintf(os.Stderr, "%s\n", string(out)) + os.Exit(0) + } + + p := parser.NewParser(interpreter.BuiltInTypes()) + tree, err := p.Parse(ts) + if *fParse { + if err != nil { + perror(err) + os.Exit(1) + } + os.Exit(0) + } + if err != nil { + fatal(20, err) + } + + if *fDebugAst { + out, err := json.MarshalIndent(tree, "", " ") + if err != nil { + fatals("encoding error", 21, err) + } + fmt.Fprintf(os.Stderr, "%s\n", string(out)) + os.Exit(0) + } + + start := time.Now() + i := interpreter.NewInterpreter() + for _, expr := range tree { + if _, err := i.Interp(expr); err != nil { + fatal(31, err) + } + } + + if *fTime { + fmt.Fprintf(os.Stderr, "elapsed: %f\n", time.Since(start).Seconds()) + } +} |