Login
7 branches 0 tags
Benjamin Vincent Schulenburg Added support for executable modules a8bf349 3 years ago 876 Commits
nujel / binlib / repl.nuj
;;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;;; This project uses the MIT license, a copy should be included under /LICENSE
;;;
;;; The REPL implementation, also parses command line arguments and determines
;;; which files to load and whether to start a interactive session.

[def *1 #nil]
[def *2 #nil]
[def *3 #nil]

[def repl/executable-name "nujel"]
[def repl/args #nil]
[def repl/parse-args/eval-next-module #f]
[def repl/parse-args/eval-next #f]
[def repl/parse-args/run-repl #t]
[def repl/options @[]]
[def repl/option-map @[]]
[def repl/line-history #nil]

[defn repl/exception-handler [error]
      [print/error error]]

[defn repl/welcome []
      [require :ansi]
      [println [cat [ansi/rainbow "Nujel"] " is ready for business!"]]]

[defn repl/prompt []
      "> "]

[defn repl/wasm [line]
      "Evaluate LINE in the wasm context"
      [string/write [try repl/exception-handler
                         [eval-in root-closure [cons do [read line]]]]]]

[defn repl/cmd/raw [ctx line]
      [try [fn [err]
               [if [= [car err] :unmatched-opening-bracket]
                   [repl/cmd/raw ctx [cat line [readline "... "]]]
                   [throw err]]]
           [def expr [read line]]
           [when [equal? '[] expr]
                 [print "\r"]
                 [return]]
           [try repl/exception-handler
                [def result [eval-in ctx [cons do expr]]]
                [repl/push-result result]
                [println [if [nil? result] "" [string/display result]]]]]]

[defn repl/push-result [result]
      [set! *3 *2]
      [set! *2 *1]
      [set! *1 result]
      [return result]]

[defn repl/cmd [ctx]
      [def buf ""]
      [def line ""]
      [while [not= [trim line] "[/cmd]"]
             [set! buf [cat buf line]]
             [set! line [readline]]]
      [def expr [cons do [read buf]]]
      [def result [eval-in ctx expr]]
      [repl/push-result result]
      [println [if [nil? result] "" [string/display result]]]]

[defn repl/readline [ctx]
      [def line [readline [repl/prompt]]]
      [set! repl/line-history [cons line repl/line-history]]
      [when [nil? line]
            [println "Adios, cowboy..."]
            [exit 0]]
      [if [= [trim line] "[cmd]"]
          [repl/cmd ctx]
          [repl/cmd/raw ctx line]]]

[defn repl []
      [repl/welcome]
      [while #t
             [try repl/exception-handler
                  [repl/readline root-closure]]]]

[tree/set! repl/option-map 'm
           [fn [option]
               [set! repl/parse-args/eval-next-module #t]]]
[tree/set! repl/option-map 'h
           [fn [option]
               [import [main] :help]
               [main #nil]
               [exit 0]]]
[tree/set! repl/option-map 'no-color
           [fn [option]
               [require :ansi]
               [set! ansi/disable! #t]]]
[tree/set! repl/option-map 'color
           [fn [option]
               [require :ansi]
               [set! ansi/disable! #f]]]
[tree/set! repl/option-map 'x
           [fn [option]
               [set! repl/parse-args/eval-next #t]
             [set! repl/parse-args/run-repl #f]]]
[tree/set! repl/option-map :default
           [fn [option]
               [tree/set! repl/options option #t]]]

[defn repl/parse-option [option]
      [[or [ref repl/option-map option]
           [ref repl/option-map :default]] option]]

[defn repl/parse-options [options]
      [if [= [buffer/ref options 0] #\-]
          [repl/parse-option [string->keyword [cut options 1]]]
          [for-each [map [split options ""] string->symbol] repl/parse-option]]]

[defn repl/parse-arg [arg args]
      [cond [repl/parse-args/eval-next [try print/error
                                            [eval-in root-closure [cons do [read arg]]]
                                            [set! repl/parse-args/eval-next #f]]]
            [repl/parse-args/eval-next-module [try [fn [e]
                                                       [print/error e]
                                                     [exit 1]]
                                                   [when [= [buffer/ref arg 0] #\:]
                                                     [set! arg [string/cut 1]]]
                                                   [import [main] [string->keyword arg]]
                                                   [main [cdr args]]
                                                   [exit 0]]]
            [[= [buffer/ref arg 0] #\-] [repl/parse-options [string/cut arg 1]]]
            [#t [try [fn [e]
                         [print/error e]
                         [exit 1]]
                     [file/eval-module arg [cdr args]]
                     [exit 0]]
                [set! repl/parse-args/run-repl #f]]]]

[defn repl/parse-args [args]
      [if args
          [do [repl/parse-arg [car args] args]
              [repl/parse-args [cdr args]]]
          repl/parse-args/run-repl]]

[defn repl/init/wasm [args]
      [repl/welcome]]

[defn repl/init/bin [args]
      [try repl/exception-handler
           [set! repl/executable-name [car args]]
           [when [repl/parse-args [cdr args]] [repl]]]]

[defn repl/init args
      [set! repl/args args]
      [if [= System/Architecture 'wasm]
          [repl/init/wasm args]
          [repl/init/bin args]]]