Skip to content
This repository has been archived by the owner on Jul 7, 2024. It is now read-only.

Commit

Permalink
Add multi-line detection to the REPL
Browse files Browse the repository at this point in the history
The parser will throw "resumable" errors when reaching the end of the
input string. These are caught by the REPL, allowing us to flow onto
multiple lines.
  • Loading branch information
SquidDev committed Apr 9, 2017
1 parent 24ff60e commit 53eece7
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 32 deletions.
7 changes: 2 additions & 5 deletions lib/string.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,8 @@

(defun trim (str)
"Remove whitespace from both sides of STR."
(if (or (= (sub str 1 1) " ") (= (sub str 1 1) "\t"))
(trim (sub str 2 (#s str)))
(if (or (= (sub str -1 -1) " ") (= (sub str -1 -1) "\t"))
(trim (sub str 1 (- (#s str) 1)))
str)))
(with (res (gsub (gsub str "^%s+" "") "%s+$" ""))
res))

(define quoted
"Quote the string STR so it is suitable for printing."
Expand Down
27 changes: 20 additions & 7 deletions urn/parser.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,19 @@
pos nil
pos "Invalid digit here"))

(defun lex (logger str name)
"Lex STR from a file called NAME, returning a series of tokens"
(defun eof-error! (cont logger msg node explain &lines)
"A variation of [[logger/do-node-error!]], used when we've reached the
end of the file. If CONT is true, then a \"resumable\" error will be
thrown instead."
:hidden
(if cont
(fail! { :msg msg :cont true })
(logger/do-node-error! logger msg node explain (unpack lines 1 (# lines)))))

(defun lex (logger str name cont)
"Lex STR from a file called NAME, returning a series of tokens. If CONT
is true, then \"resumable\" errors will be thrown if the end of the
stream is reached."

;; Attempt to "normalise" strings
(set! str (string/gsub str "\r\n?" "\n"))
Expand Down Expand Up @@ -196,7 +207,7 @@
[(= char "")
(let ((start (range start))
(finish (range (position))))
(logger/do-node-error! logger
(eof-error! cont logger
"Expected '\"', got eof"
finish nil
start "string started here"
Expand Down Expand Up @@ -255,7 +266,7 @@

(push-cdr! buffer (string/char val)))]
[(= char "")
(logger/do-node-error! logger
(eof-error! cont logger
"Expected escape code, got eof"
(range (position)) nil
(range (position)) "end of file here")]
Expand Down Expand Up @@ -288,8 +299,10 @@
(append! "eof")
out))

(defun parse (logger toks)
"Parse tokens TOKS, the result of [[lex]]"
(defun parse (logger toks cont)
"Parse tokens TOKS, the result of [[lex]]. If CONT is true, then
\"resumable\" errors will be thrown if the end of the stream is
reached."
(let* ((index 1)
(head '())
(stack '())
Expand Down Expand Up @@ -409,7 +422,7 @@
(.<! head :auto-close true)]
[(= tag "eof")
(when (/= 0 (# stack))
(logger/do-node-error! logger
(eof-error! cont logger
(string/format "Expected '%s', got 'eof'" (.> head :close))
tok nil
(.> head :range) "block opened here"
Expand Down
54 changes: 34 additions & 20 deletions urn/tools/repl.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,27 @@
(import urn/backend/writer writer)
(import urn/documentation docs)
(import urn/logger logger)
(import urn/logger/void void)
(import urn/logger)
(import urn/parser parser)

(define compile (.> (require "tacky.compile") :compile))

(define Scope (require "tacky.analysis.scope"))

(defun do-parse (compiler scope str)
(defun requires-input (str)
"Determine whether STR requires additional input (such as quotes or parens).
The returns false if no input is required, and nil if a syntax error occured."
(case (list (pcall (lambda()
(parser/parse
void/void
(parser/lex void/void str "<stdin>" true)
true))))
[(true _) false]
[(false (as table? ?x)) (if (.> x :cont) true false)]
[(false _) nil]))

(defun do-resolve (compiler scope str)
:hidden
(let* [(logger (.> compiler :log))
(lexed (parser/lex logger str "<stdin>"))
Expand Down Expand Up @@ -180,7 +193,7 @@

(defun exec-string (compiler scope string)
:hidden
(with (state (do-parse compiler scope string))
(with (state (do-resolve compiler scope string))
(when (> (# state) 0)
(let* [(current 0)
(exec (co/create (lambda ()
Expand Down Expand Up @@ -235,34 +248,35 @@
(defun repl (compiler)
(let* [(scope (.> compiler :rootScope))
(logger (.> compiler :log))
(buffer '())
(buffer "")
(running true)]
(while running
(io/write (colored 92 (if (empty? buffer) "> " ". ")))
(io/flush)

(with (line (io/read "*l"))
(cond
;; If we got no input, then exit the REPL
[(and (! line) (empty? buffer)) (set! running false)]
[(and line (= (string/char-at line (#s line)) "\\"))
(push-cdr! buffer (.. (string/sub line 1 (pred (#s line))) "\n"))]
[(and line (> (# buffer) 0) (> (#s line) 0))
(push-cdr! buffer (.. line "\n"))]
[true
(with (data (.. (concat buffer) (or line "")))
(set! buffer '())

(if (= (string/char-at data 1) ":")
(exec-command compiler scope (string/split (string/sub data 2) " "))
(progn
(set! scope ((.> Scope :child) scope))
(.<! scope :isRoot true)
[true
(with (data (if line (.. buffer line "\n") buffer))
(cond
[(= (string/char-at data 1) ":")
(set! buffer "")
(exec-command compiler scope (map string/trim (string/split (string/sub data 2) " ")))]
[(and line (> (#s line) 0) (requires-input data))
(set! buffer data)]
[true
(set! buffer "")
(set! scope ((.> Scope :child) scope))
(.<! scope :isRoot true)

(with (res (list (pcall exec-string compiler scope data)))
;; Clear active node/scope
(.<! compiler :active-node nil)
(.<! compiler :active-scope nil)
(unless (car res) (logger/put-error! logger (cadr res)))))))])))))
(with (res (list (pcall exec-string compiler scope data)))
;; Clear active node/scope
(.<! compiler :active-node nil)
(.<! compiler :active-scope nil)
(unless (car res) (logger/put-error! logger (cadr res))))]))])))))

(defun exec (compiler)
(let* [(data (io/read "*a"))
Expand Down

0 comments on commit 53eece7

Please sign in to comment.