aboutsummaryrefslogtreecommitdiff
path: root/new-luxc/source/luxc/repl.lux
blob: 9df2d181d7910701b2bc25dab34dff1d35cbf6c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
(.module:
  lux
  (lux (control [monad #+ do]
                ["ex" exception #+ exception:])
       (data [maybe]
             ["e" error #+ Error]
             [text "text/" Equivalence<Text>]
             text/format
             (coll (dictionary ["dict" unordered])))
       [macro]
       (lang [syntax #+ Aliases]
             (type [check])
             [".L" init]
             [".L" module]
             [".L" scope]
             [".L" extension]
             (extension [".E" analysis]))
       (concurrency [promise]
                    [task #+ Task])
       [io]
       (world [file #+ File]
              [console #+ Console]))
  (luxc [lang]
        (lang [".L" host]
              [".L" translation]
              [".L" eval]
              (translation (jvm [".T" runtime]))
              (extension [".E" synthesis]
                         [".E" translation]
                         [".E" statement]))))

(do-template [<name>]
  [(exception: #export (<name> {message Text})
     message)]

  [repl-initialization-failed]
  [repl-error]
  )

(def: repl-module "<REPL>")

(def: no-aliases Aliases (dict.new text.Hash<Text>))

(def: (initialize source-dirs target-dir console)
  (-> (List File) File Console (Task Lux))
  (do promise.Monad<Promise>
    [output (promise.future
             (do io.Monad<IO>
               [host hostL.init-host]
               (case (macro.run' (initL.compiler host)
                                 (moduleL.with-module +0 repl-module
                                   runtimeT.translate))
                 (#e.Success [compiler _])
                 (|> compiler
                     (set@ [#.info #.mode] #.REPL)
                     (set@ #.extensions
                           (:coerce Nothing
                                    {#extensionL.analysis analysisE.defaults
                                     #extensionL.synthesis synthesisE.defaults
                                     #extensionL.translation translationE.defaults
                                     #extensionL.statement statementE.defaults}))
                     (translationL.translate-module source-dirs target-dir translationL.prelude))

                 (#e.Error error)
                 (wrap (#e.Error error)))))]
    (case output
      (#e.Success compiler)
      (do task.Monad<Task>
        [_ (console.write (format "\nWelcome to the REPL!\n"
                                  "Type \"exit\" to leave.\n\n")
                          console)]
        (wrap compiler))
      
      (#e.Error message)
      (task.throw repl-initialization-failed message))))

(def: (add-line line [where offset input])
  (-> Text Source Source)
  [where offset (format input "\n" line)])

(def: (repl-translate source-dirs target-dir code)
  (-> (List File) File Code (Meta [Type Any]))
  (function (_ compiler)
    (case ((translationL.translate (translationL.translate-module source-dirs target-dir)
                                   no-aliases
                                   code)
           compiler)
      (#e.Success [compiler' aliases'])
      (#e.Success [compiler' [Nothing []]])

      (#e.Error error)
      (if (ex.match? translationL.Unrecognized-Statement error)
        ((do macro.Monad<Meta>
           [[var-id varT] (lang.with-type-env check.var)
            exprV (scopeL.with-scope repl-module
                    (evalL.eval varT code))
            ?exprT (lang.with-type-env (check.read var-id))]
           (wrap [(maybe.assume ?exprT) exprV]))
         compiler)
        (#e.Error error)))))

(def: fresh-source Source [[repl-module +1 +0] +0 ""])

(def: #export (run source-dirs target-dir)
  (-> (List File) File (Task Any))
  (do task.Monad<Task>
    [console (promise.future console.open)
     compiler (initialize source-dirs target-dir console)]
    (loop [compiler compiler
           source fresh-source
           multi-line? false]
      (do @
        [_ (if multi-line?
             (console.write "  " console)
             (console.write "> " console))
         line (console.read-line console)]
        (if (text/= "exit" line)
          (console.write "Till next time..." console)
          (case (do e.Monad<Error>
                  [[source' exprC] (syntax.read repl-module no-aliases (add-line line source))]
                  (macro.run' compiler
                              (lang.with-current-module repl-module
                                (do macro.Monad<Meta>
                                  [[exprT exprV] (repl-translate source-dirs target-dir exprC)]
                                  (wrap [source' exprT exprV])))))
            (#e.Success [compiler' [source' exprT exprV]])
            (do @
              [_ (console.write (represent compiler' exprT exprV) console)]
              (recur compiler' source' false))

            (#e.Error error)
            (if (ex.match? syntax.end-of-file error)
              (recur compiler source true)
              (exec (log! (ex.construct repl-error error))
                (recur compiler fresh-source false))))))
      )))