aboutsummaryrefslogtreecommitdiff
path: root/cmd/micro/micro.go
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2022-07-18 07:45:58 +0100
committerJuan J. Martinez <jjm@usebox.net>2022-07-18 07:45:58 +0100
commit8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642 (patch)
treec53977d1284347bb1d5963ddb4dc7723c40c6e55 /cmd/micro/micro.go
downloadmicro-lang-8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642.tar.gz
micro-lang-8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642.zip
First public release
Diffstat (limited to 'cmd/micro/micro.go')
-rw-r--r--cmd/micro/micro.go202
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())
+ }
+}