~subsetpark/ec

129c670da21623a7e377236363129d669915cd40 — Zach Smith 3 months ago 713e51f rlrepl v0.4.0
Readline support
5 files changed, 156 insertions(+), 99 deletions(-)

M main.janet
M project.janet
M src/eval.janet
M src/operations.janet
A src/repl.janet
M main.janet => main.janet +3 -93
@@ 1,97 1,7 @@
(import /src/calc)
(import /src/print)
(import /src/parser)
(import /src/env)
(import /src/eval)
(import /src/repl)

(use argparse)

(defn handle-signal
  [input]
  (let [str (string input)]
    (when (= str "") (os/exit 0))
    str))

(defn- display
  [data]
  (print (print/p data))
  "")

(defn- display-all
  [stack]
  (let [inner (map print/p stack)]
    (print (string/join inner " ")))
  "")

(defn- display-help
  []
  (each [k v] (pairs env/dictionary)
    (printf "%s: %s" k (string (v :type))))
  "")

(defn- describe-all
  [s q]
  (defn- describe
    [elem]
    (let [evaled (eval/eval s elem)]
      (printf "--\n\n%s: %s\n\n%s%s"
              (print/p evaled)
              (string (evaled :type))
              (evaled :doc)
              (if (evaled :composes)
                (string/format "\n\n(%s)" (print/p (evaled :composes)))
                ""))))
  (each item (q :data)
    (describe item))
  "")

(defn- handle-special
  [s special]
  (case (freeze (string/trim special))
    "." (display (calc/pop s))
    "p" (display (calc/peek s))
    "s" (display-all (s :data))
    "?" (describe-all s (calc/pop s))
    "??" (display-help)))

(defn handle-commands
  [s input]
  (each token input
    (match token
      @[:special patt] (handle-special s patt)
      _ (eval/eval-and-push s token))))

(defn- prompt
  [s]
  (->> s
       (calc/peek)
       (print/p)
       (print/truncate)
       (string/format "<%s> $ ")))

(defn repl
  []
  (def s (env/new-env))
  (while true
    (def bak (array/slice (s :data)))
    (try (->> (getline (prompt s) @"" env/dictionary)
              (handle-signal)
              (parser/parse)
              (handle-commands s))

      ([err fib]
        (eprint err)
        (if (os/getenv "EC_TRACEBACK")
          (propagate err fib)
          (put s :data bak))))))

(defn handle-line
  [line]
  (let [s (env/new-env)]
    (->> line
         (parser/parse)
         (handle-commands s))
    (display-all (s :data))))

(def params
  [```


@@ 173,5 83,5 @@
  (let [res (argparse ;params)]
    (when res
      (match (res :default)
        nil (repl)
        inputs (handle-line (string/join inputs " "))))))
        nil (repl/repl)
        inputs (repl/handle-line (string/join inputs " "))))))

M project.janet => project.janet +12 -1
@@ 3,10 3,21 @@
  :description "a very good calculator"
  :dependencies ["https://git.sr.ht/~subsetpark/fugue"
                 "https://github.com/janet-lang/argparse.git"
                 "https://github.com/pyrmont/testament"])
                 "https://github.com/pyrmont/testament"
                 "https://git.sr.ht/~subsetpark/janet-rl"])

(def *static-build* (= (or (os/getenv "EC_STATIC_BUILD") "0") "1"))

(defn pkg-config [what]
  (def f (file/popen (string "pkg-config " what)))
  (def v (->>
           (file/read f :all)
           (string/trim)
           (string/split " ")))
  (unless (zero? (file/close f))
    (error "pkg-config failed!"))
  v)

(declare-executable
  :name "ec"
  :entry "main.janet"

M src/eval.janet => src/eval.janet +3 -3
@@ 43,9 43,9 @@
  [stack maybe-quote]
  (match maybe-quote
    (q (calc/Quotation*? q)) (each elem (calc/data q)
                          (->> elem
                               (eval stack)
                               (calc/push stack)))
                               (->> elem
                                    (eval stack)
                                    (calc/push stack)))
    not-a-quote (calc/push stack not-a-quote)))

(extend-multi calc/push

M src/operations.janet => src/operations.janet +2 -2
@@ 29,13 29,13 @@
  ~(defop ,name ,arity
     (fn [x y] (if (,cmp x y) 1 0))
     (string/format
      `
       `
      x y -- bool
      Comparison predicate.
      Push 1 if x %s y;
             else 0.
      `
      ,(string cmp))))
       ,(string cmp))))

(defcmp lt 2 <)
(defcmp gt 2 >)

A src/repl.janet => src/repl.janet +136 -0
@@ 0,0 1,136 @@
(import rl)
(import /src/env)
(import /src/print)
(import /src/eval)
(import /src/calc)
(import /src/parser)

(defn- env-keys
  [env]
  (let [these-keys (keys env)
        proto (table/getproto env)]
    (match proto
      nil these-keys
      proto (array/concat these-keys (env-keys proto)))))

(defn- getline
  [{:env env} prompt buf]

  (defn get-completions
    [line start end]
    (try
      (let [to-expand (string (string/slice line start end))]
        (->>
         (env-keys env)
         (filter (fn [s] (string/has-prefix? to-expand s)))))
      
      ([err f]
       (eprint "error in tab completion function:")
       (debug/stacktrace f err)
       @[])))
  
  (when-let [ln (rl/readline prompt get-completions)]
    (buffer/push-string buf ln "\n")
    buf))

(defn- prompt
  [s]
  (->> s
       (calc/peek)
       (print/p)
       (print/truncate)
       (string/format "<%s> $ ")))

(defn- handle-signal
  [input]
  (let [str (string input)]
    (when (= str "") (os/exit 0))
    str))

(defn- display
  [data]
  (print (print/p data)))

(defn- display-all
  [stack]
  (let [inner (map print/p stack)]
    (print (string/join inner " "))))

(defn- display-help
  [{:env env}]
  (each k (env-keys env)
    (when-let [v (env k)]
      (printf "%s: %s" k (string (v :type))))))

(defn- format
  [obj]
  (match obj
    @[:special char] (string char " (Special)")
    _ (string/format
       ```
       --
       %s : %s
       
       %s%s
       ```
       (print/p obj)
       (string (obj :type))
       (obj :doc)
       (match (obj :composes)
         nil ""
         words (string "\n\n(" (print/p words) ")")))))

(defn- describe-all
  [s q]
  (defn- describe
    [elem]
    (let [evaled (eval/eval s elem)]
      (print (format evaled))))
  (each item (q :data)
    (describe item)))

(defn- handle-special
  [s special]
  (case (freeze (string/trim special))
    "." (display (calc/pop s))
    "p" (display (calc/peek s))
    "s" (display-all (s :data))
    "?" (describe-all s (calc/pop s))
    "??" (display-help s)))

(defn- handle-commands
  [s input]
  (each token input
    (match token
      @[:special patt] (handle-special s patt)
      _ (eval/eval-and-push s token))))

(defn repl
  ```
  Interactive read-eval-print-loop.
  ```
  []
  (def s (env/new-env))
  (while true
    (def bak (array/slice (s :data)))
    (try (->> (getline s (prompt s) @"")
              (handle-signal)
              (parser/parse)
              (handle-commands s))

         ([err fib]
          (eprint err)
          (if (os/getenv "EC_TRACEBACK")
            (propagate err fib)
            (put s :data bak))))))

(defn handle-line
  ```
  Handle a single input line from the command line.
  ```
  [line]
  (let [s (env/new-env)]
    (->> line
         (parser/parse)
         (handle-commands s))
    (display-all (s :data))))