1 --*-lua-*- Set as a metalua file because it requires some metalua libs
3 --require 'verbose_require'
5 require 'metalua.compiler'
14 local acc_chunk = |kind| function (arg)
15 table.insert (chunks, { tag=kind, arg })
20 { short = 'f', long = 'file', type = 'string', action = acc_chunk 'File',
21 usage = 'load a file to compile and/or run'
23 { short = 'l', long = 'library', type = 'string', action = acc_chunk 'Library',
24 usage = 'load a libary from the standard paths'
26 { short = 'e', long = 'literal', type = 'string', action = acc_chunk 'Literal',
27 usage = 'load a literal piece of source code'
29 -- What to do with chunks
30 { short = 'o', long = 'output', type = 'string',
31 usage = 'set the target name of the next compiled file'
33 { short = 'x', long = 'run', type = 'boolean',
34 usage = 'execute the compiled file instead of saving it (unless -o is also used)'
36 { short = 'i', long = 'interactive', type = 'boolean',
37 usage = 'run an interactive loop after having run other files'
40 { short = 'v', long = 'verbose', type = 'boolean',
41 usage = 'verbose mode'
43 { short = 'a', long = 'print-ast', type = 'boolean',
44 usage = 'print the AST resulting from file compilation'
46 { short = 'b', long = 'metabugs', type = 'boolean',
47 usage = 'show syntax errors as compile-time execution errors'
49 { short = 's', long = 'sharpbang', type = 'string',
50 usage = 'set a first line to add to compiled file, typically "#!/bin/env mlr"'
52 { long = 'no-runtime', type = 'boolean',
53 usage = "prevent the automatic requirement of metalua runtime"
55 { long = '', short = 'p', type = '*',
56 action= function (newargs) runargs=table.icat(runargs, newargs) end,
57 usage = "pass all remaining arguments to the program"
61 Compile and/or execute metalua programs. Parameters passed to the
62 compiler should be prefixed with an option flag, hinting what must be
63 done with them: take tham as file names to compile, as library names
64 to load, as parameters passed to the running program... When option
65 flags lack, metalua tries to adopt a "Do What I Mean" approach:
67 - if no code (no library, no literal expression and no file) is
68 specified, the first flag-less parameter is taken as a file name to
71 - if no code and no parameter is passed, an interactive loop is
74 - if a target file is specified with --output, the program is not
75 executed by default, unless a --run flag forces it to. Conversely,
76 if no --output target is specified, the code is run unless ++run
81 INIT_COMPILATION_RING = [[require 'metalua.compiler']]
83 local function main (...)
85 local cfg = parser(...)
87 -------------------------------------------------------------------
88 -- Print messages if in verbose mode
89 -------------------------------------------------------------------
90 local function verb_print (fmt, ...)
92 return printf ("[ "..fmt.." ]", ...)
96 -------------------------------------------------------------------
97 -- If there's no chunk but there are params, interpret the first
98 -- param as a file name.
99 if #chunks==0 and cfg.params then
100 local the_file = table.remove(cfg.params, 1)
101 verb_print("Param %q considered as a source file", the_file)
102 chunks = { `File{ the_file } }
105 -------------------------------------------------------------------
106 -- If nothing to do, run REPL loop
107 if #chunks==0 and cfg.interactive==nil then
108 verb_print "Nothing to compile nor run, force interactive loop"
113 -------------------------------------------------------------------
114 -- Run if asked to, or if no --output has been given
115 -- if cfg.run==false it's been *forced* to false, don't override.
116 if cfg.run==nil and not cfg.output then
117 verb_print("No output file specified; I'll run the program")
122 -------------------------------------------------------------------
123 -- List all sources to compile if verbose
125 verb_print "Sources to compile:"
126 for s in values(chunks) do verb_print(" * %s", table.tostring(s)) end
131 -------------------------------------------------------------------
132 -- Get ASTs from sources
133 for x in values(chunks) do
136 | `Library{ l } -> ast = `Call{ `Id 'require', `String{ l } }
138 local ring = springs.new()
139 ring:dostring (INIT_COMPILATION_RING)
140 ast = ring:call('mlc.ast_of_luastring', e, 'literal')
142 local ring = springs.new()
143 ring:dostring (INIT_COMPILATION_RING)
144 ast = ring:call('mlc.ast_of_luafile', f, '@'..f) -- FIXME: handle '-'
145 ast = +{ function (...) -{ast} end(...) }
148 table.insert(code, ast)
151 -------------------------------------------------------------------
153 if cfg['print-ast'] then
154 verb_print "Resulting AST:"
155 for x in values(code) do
156 printf("--- AST From %s: ---", table.tostring(x.origin, 'nohash'))
157 if x.origin.tag=='File' then x=x[1][2][1] end
158 table.print(x, 80, 'nohash')
162 -------------------------------------------------------------------
163 -- Insert runtime loader
164 if cfg['no-runtime'] then
165 verb_print "Prevent insertion of command \"require 'metalua.runtime'\""
167 table.insert(code, 1, +{require'metalua.runtime'})
170 -- FIXME: check for failures
171 -- FIXME: handle metabugs
172 local bytecode = mlc.luacstring_of_ast (code)
175 -------------------------------------------------------------------
176 -- Insert #!... command
177 if cfg.sharpbang then
178 verb_print ("Adding sharp-bang directive %q", cfg.sharpbang)
179 if not cfg.sharpbang:strmatch'\n$' then cfg.sharpbang=cfg.sharpbang..'\n' end
180 bytecode = cfg.sharpbang..bytecode
183 -------------------------------------------------------------------
187 verb_print ("Saving to file %q", cfg.output)
188 local file, err_msg = io.open(cfg.output, 'w')
189 if not file then error("can't open output file: "..err_msg) end
192 pcall(os.execute, 'chmod a+x "'..cfg.output..'"')
195 -------------------------------------------------------------------
198 verb_print "Running:"
199 local f = mlc.function_of_luacstring (bytecode)
201 -- FIXME: check for failures
205 -------------------------------------------------------------------
207 if cfg.interactive then
209 print "*** !!! Interactive loop not implemented !!!"