package DkZero_Exec

  1. Overview
  2. Docs

Source file ShellLua.ml

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
let quick_error msg =
  prerr_endline "FATAL: The `lua` subcommand failed.";
  prerr_endline (MlFront_Thunk.ThunkStrings.left_pad msg);
  exit 1

type lua_options = {
  analysis : bool;
  interactive : bool;
  valuescan : bool;
  lua_script : string option;
  lua_args : ShellCoreLua.lua_arg2 list;
}

let lua_options ~usage_msg =
  let analysis = ref false in
  let valuescan = ref false in
  let interactive = ref None in
  let lua_script = ref None in
  let lua_args = ref [ ShellCoreLua.Interpreter2 "lua" ] in
  let anon_fun s =
    match !lua_script with
    | None ->
        lua_script := Some s;
        lua_args := LuaScript2 s :: !lua_args
    | Some _ -> lua_args := Arg2 s :: !lua_args
  in
  ( [
      ( "-help",
        Arg.Unit
          (fun () ->
            print_endline usage_msg;
            exit 0),
        "" );
      ( "--help",
        Arg.Unit
          (fun () ->
            print_endline usage_msg;
            exit 0),
        "" );
      ("--analysis", Arg.Set analysis, "");
      ("--valuescan", Arg.Set valuescan, "");
      ("-i", Arg.Unit (fun () -> interactive := Some ()), "");
      ("-e", Arg.String (fun s -> lua_args := DoString2 s :: !lua_args), "");
      ( "-l",
        Arg.String
          (fun s ->
            lua_args := Require2 (ShellCoreLua.parse_modname s) :: !lua_args),
        "" );
    ],
    anon_fun,
    fun () ->
      let interactive =
        (* default is interactive if no `script` is given _and_ stdin is a terminal *)
        match (!interactive, !lua_script) with
        | Some (), _ -> true
        | _, Some _ -> false
        | None, None ->
            if Unix.isatty Unix.stdin then true
            else
              let console = MlFront_Console.Console.create_no_terminal () in
              MlFront_Console.Console.supports_virtual_terminal console
      in
      {
        analysis = !analysis;
        valuescan = !valuescan;
        interactive;
        lua_script = !lua_script;
        lua_args = List.rev !lua_args;
      } )

let rec pp_lua_stmt :
    Format.formatter -> MlFront_Thunk.ThunkLuaScript.Ast.stmt -> unit =
 fun ppf -> function
  | Stmt' (loc, stmt) ->
      Format.fprintf ppf "@[<hov 2>Stmt'(@,byte %d: %a)@]" loc pp_lua_stmt stmt
  | Assign (lvals, exps) ->
      Format.fprintf ppf "@[<hov 2>Assign(@[%a@]@ =@ @[%a@])@]"
        (Format.pp_print_list pp_lua_lval)
        lvals
        (Format.pp_print_list pp_lua_exp)
        exps
  | WhileDo (_exp, _block) -> Format.fprintf ppf "WhileDo"
  | RepeatUntil (_block, _exp) -> Format.fprintf ppf "RepeatUntil"
  | If (_exp, _block, _list, _option) -> Format.fprintf ppf "If"
  | Return _exp_list -> Format.fprintf ppf "Return"
  | Callstmt _call -> Format.fprintf ppf "Callstmt"
  | Local (_name_list, _exp_list) -> Format.fprintf ppf "Local"

and pp_lua_lval :
    Format.formatter -> MlFront_Thunk.ThunkLuaScript.Ast.lval -> unit =
 fun ppf -> function
  | Lvar name -> Format.fprintf ppf "Lvar(%s)" name
  | Lindex (_exp1, _exp2) -> Format.fprintf ppf "Lindex"

and pp_lua_exp :
    Format.formatter -> MlFront_Thunk.ThunkLuaScript.Ast.exp -> unit =
 fun ppf -> function
  | Var name -> Format.fprintf ppf "Var(%s)" name
  | Lit _value -> Format.fprintf ppf "Lit"
  | Binop (_exp1, _op, _exp2) -> Format.fprintf ppf "Binop"
  | Unop (_op, _exp) -> Format.fprintf ppf "Unop"
  | Index (_exp1, _exp2) -> Format.fprintf ppf "Index"
  | Table (exp_list, name_exp_list) ->
      Format.fprintf ppf "@[<hov 2>Table(%a;@ %a)@]"
        (Format.pp_print_list ~pp_sep:Format.pp_print_space pp_lua_exp)
        exp_list
        (Format.pp_print_list ~pp_sep:Format.pp_print_space
           (fun ppf (name, _exp) -> Format.fprintf ppf "%s = ..." name))
        name_exp_list
  | Call _call -> Format.fprintf ppf "Call"

let pp_lua_chunk :
    Format.formatter -> MlFront_Thunk.ThunkLuaScript.Ast.chunk -> unit =
 fun ppf -> function
  | Debug _ -> Format.fprintf ppf "[lua-lex] Debug()"
  | Statement stmt ->
      Format.fprintf ppf "@[<hov 2>[lua-lex] Statement(@,%a)@]" pp_lua_stmt stmt
  | Fundef (loc, _, _, _) -> Format.fprintf ppf "[lua-lex] Fundef(byte %d)" loc
  | Methdef (loc, _, _, _, _) ->
      Format.fprintf ppf "[lua-lex] Methdef(byte %d)" loc

let lua_lex map buf = MlFront_Lua.Luascanner.token buf map

(* Nanopass 3 *)
let nanopass3 args =
  List.filter_map
    (function
      | ShellCoreLua.FileWithUiRuleEmbedded2 _ | ShellCoreLua.UiRule2 _ ->
          (* no UI rules in `lua` subcommand *)
          None
      | ShellCoreLua.Interpreter2 _ | ShellCoreLua.Arg2 _ ->
          (* no Arg and Interpreter in nanopass 3 *)
          None
      | ShellCoreLua.DoString2 x -> Some (ShellCoreLua.DoString3 x)
      | ShellCoreLua.Require2 x -> Some (ShellCoreLua.Require3 x)
      | ShellCoreLua.LuaScript2 x -> Some (ShellCoreLua.LuaScript3 x))
    args

let process_lua_command ~usage_msg ~mini_usage_msg ~baseconfig ~verbosity
    ~progress ~install ~random_seed ~wait_trace_store ~nobuiltininc ~nosysinc
    ~noworkspaceinc ~sysincludedirs ~userincludedirs ~cells ~local_packages
    ~build_number ~long_ids ~import ~invalidations debugmodes module_or args =
  (* Parse *)
  let current = ref 1 in
  let speclist, anon_fun, mk_options = lua_options ~usage_msg in
  let cmdline = "dk0" :: "lua" :: args in
  (try
     Arg.parse_argv ~current (Array.of_list cmdline) speclist anon_fun
       mini_usage_msg
   with
  | Arg.Bad msg ->
      prerr_endline msg;
      exit 1
  | Arg.Help msg ->
      prerr_endline msg;
      exit 0);

  (* Consider options *)
  let options = mk_options () in

  (* Setup VALUESCAN runner *)
  let module ForAnalysis = struct
    let in_what = "in analysis"
    let command_name = "lua"
  end in
  let module ValueScanRunner =
    ShellCoreLua.MakeRunner
      (ForAnalysis)
      (MlFront_Thunk.ThunkLuaScript.IAnalyze)
  in
  ();

  (* Parse + analyze chunks if VALUESCAN *)
  let prefetch2_lua_scripts =
    if options.valuescan then
      let g_analyze = ValueScanRunner.mk () in
      let analysis =
        List.fold_left
          (fun acc -> function
            | ShellCoreLua.DoString2 s ->
                ValueScanRunner.dostring_analysis ~sourcename:"<cli>" g_analyze
                  s
                :: acc
            | Arg2 _ -> acc
            | Require2 { global_opt = _; modname; version_opt = None } ->
                Printf.eprintf
                  "[valuescan] Missing .at(version) for require(%s)\n" modname;
                acc
            | Require2 { global_opt = _; modname; version_opt = Some version }
              -> begin
                match
                  MlFront_Thunk.ThunkCommand.InternalUse.parse_moduleversion
                    (modname ^ "@" ^ version)
                with
                | Error msg ->
                    Printf.eprintf
                      "[valuescan] The version `%s` for require(%s) is not a \
                       valid semver. %s\n"
                      version modname msg;
                    acc
                | Ok modver ->
                    [
                      MlFront_Thunk.ThunkLuaTypAnalysis.StandardModuleRequireAt
                        { modver };
                    ]
                    :: acc
              end
            | LuaScript2 script ->
                ValueScanRunner.dofile_analysis g_analyze script :: acc
            | UiRule2 _ | FileWithUiRuleEmbedded2 _ ->
                (* no analysis on UI rules; `lua` subcommand does not use them anyways *)
                acc
            | Interpreter2 _ -> acc)
          [] options.lua_args
      in
      let flat_analysis = List.flatten (List.rev analysis) in
      List.filter_map
        (function
          | MlFront_Thunk.ThunkLuaTypAnalysis.StandardModuleRequireAt { modver }
            ->
              if verbosity > 0 then
                Printf.eprintf "[valuescan] Prefetching Lua module %s@%s\n"
                  (MlFront_Core.StandardModuleId.show_dot modver.id)
                  (MlFront_Thunk.ThunkSemver64.to_string modver.version);
              Some (modver.id, modver.version)
          | MlFront_Thunk.ThunkLuaTypAnalysis.StandardModuleExport _
          | MlFront_Thunk.ThunkLuaTypAnalysis.FreeRuleExport _
          | MlFront_Thunk.ThunkLuaTypAnalysis.UiRuleExport _ ->
              None)
        flat_analysis
    else []
  in

  (* Start Lua state *)
  let g, dostring, doscript, run_interactive =
    if options.analysis then
      (* Analysis: Same as VALUESCAN runner, with fresh state *)
      let module Runner =
        ShellCoreLua.MakeRunner
          (ForAnalysis)
          (MlFront_Thunk.ThunkLuaScript.IAnalyze)
      in
      let g = Runner.mk () in
      let doscript lua_file =
        let analysis = Runner.dofile_analysis g lua_file in
        List.iter
          (fun dr ->
            Printf.printf "(analysis) %s\n"
              (MlFront_Thunk.ThunkLuaTypAnalysis.show dr))
          analysis
      in
      (g, Runner.dostring, doscript, Runner.run_interactive)
    else begin
      (* Build ... *)
      let source_file, (_source_sha256, _vsl_source_sz) =
        ShellCoreLua.mk_source cmdline
      in
      try
        (* Start config *)
        let preconfig, ctx =
          ShellCoreLua.start_config ~baseconfig ~install ~random_seed ~verbosity
            ~progress ~nobuiltininc ~nosysinc ~noworkspaceinc ~sysincludedirs
            ~userincludedirs ~cells ~local_packages ~build_number ~long_ids
            ~import debugmodes module_or
        in

        (* Calculate the origin of the valuescan *)
        let lua_valuescan_origin =
          if options.valuescan then `UserRanLuaWithValueScan
          else `UserRanLuaWithoutValueScan
        in

        (* Start build *)
        let g, dostring, doscript, run_interactive, _tasks, _state, _newgen =
          (* Create a transaction dedicated to this build *)
          let parent_txn = None in
          ShellCoreLua.start_build ~preconfig ctx ~command_name:"lua"
            ~lua_valuescan_origin ~prefetch2_lua_scripts ~wait_trace_store
            ~invalidations parent_txn
        in
        (g, dostring, doscript, run_interactive)
      with
      | DkZero_Base.Exceptions.EngineShutdown
          { trace; exitcode_posix; exitcode_windows }
      ->
        ShellBacktrace.process_exception ~trace ~exitcode_posix
          ~exitcode_windows ~cant_do:"run Lua command" ~source_file
          ~autofix:false module_or
    end
  in

  (* Set the 'arg' global table. *)
  let argtable_statement =
    ShellCoreLua.create_argtable_statement options.lua_args
  in
  let (_ : MlFront_Thunk.ThunkLuaScript.Value.value list) =
    dostring ~exit_on_error:true ~sourcename:"<argtable>" g argtable_statement
  in

  (* Evaluate scripts and libraries. *)
  let lua_args3 = nanopass3 options.lua_args in
  let (_ : Wiring.BuildContext.State.t option) =
    try
      ShellCoreLua.evaluate_args ~command_name:"lua" ~dostring ~doscript g
        lua_args3
    with
    | ShellCoreLua.LuaEngineShutdown
        { trace; exitcode_posix; exitcode_windows; lua_source_file; cant_do }
    ->
      ShellBacktrace.process_exception ~trace ~exitcode_posix ~exitcode_windows
        ~cant_do ~source_file:lua_source_file ~autofix:false module_or
  in

  (* Use stdin to make a REPL *)
  if options.interactive then (
    Printf.printf "dk/Lua %s REPL%s.\nUse Ctrl-C %s to exit the REPL.\n%!"
      MlFront_Core.MlFrontConstants.mlfront_version
      (if options.analysis then " in analysis mode" else "")
      (if Sys.win32 then "(or Ctrl-Z + RETURN on a blank line on Windows)"
       else "(or Ctrl-D on Unix)");
    run_interactive ~prompt:">" ~sourcename:"<repl>" stdin g)