aboutsummaryrefslogtreecommitdiff
path: root/nomsu.moon
blob: fc2fe5ae0b84020bbfe33a7e56ed977025bca4db (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
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
#!/usr/bin/env moon
-- This file contains the command-line Nomsu runner.

if NOMSU_VERSION and NOMSU_PREFIX
    package.path = "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/?.lua;"..package.path
    package.cpath = "#{NOMSU_PREFIX}/lib/nomsu/#{NOMSU_VERSION}/?.so;"..package.cpath

EXIT_SUCCESS, EXIT_FAILURE = 0, 1

if _VERSION == "Lua 5.1" and not jit
    -- Cannot run on Lua5.1 because it doesn't have gotos.
    print("Sorry, Nomsu does not run on Lua 5.1. Please use LuaJIT 2+ or Lua 5.2+")
    os.exit(EXIT_FAILURE)

usage = [=[
Nomsu Compiler

Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [--help | -h] [--version] [-O optimization level] [-v] [-c] [-s] [-d debugger] [--no-core] [(file | -t tool | -e "nomsu code..." | files... -- ) [nomsu args...]]

OPTIONS
    -t <tool> Run a tool.
    -e Execute the specified string.
    -O <level> Run the compiler with the given optimization level (>0: use precompiled .lua versions of Nomsu files, when available).
    -v Verbose: print compiled lua code.
    -c Compile the input files into a .lua files.
    -s Check the input files for syntax errors.
    -d <debugger> Attempt to use the specified debugger to wrap the main body of execution.
    -h/--help Print this message.
    --version Print the version number and exit.
    --no-core Skip loading the Nomsu core by default.
    -V specify which Nomsu version is desired.
    <file> The Nomsu file to run (can be "-" to use stdin).
]=]

ok, _ = pcall ->
    export lpeg, re
    lpeg = require 'lpeg'
    re = require 're'
if not ok
    print("Error: unable to find the 'lpeg' Lua module. Please install LPEG either from http://www.inf.puc-rio.br/~roberto/lpeg/re.html or, if you use luarocks: `luarocks install lpeg`")
    os.exit(EXIT_FAILURE)
Files = require "files"
Errhand = require "error_handling"
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
{:List, :Dict, :Text} = require 'containers'

sep = "\3"
parser = re.compile([[
    args <- {| (flag %sep)*
         {:files: {|
          ( ("-m" %sep)? (file %sep)+ "--" %sep
           / file %sep
           / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :}
      {:nomsu_args: {| (nomsu_flag %sep)* {:extras: {| ({[^%sep]+} %sep)* |} :} |} :}
      |} ({.+}?)

    file <-
        (  "-e" %sep ({[^%sep]+} -> spoof)
         / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~}
         / !"--" {[^%sep]+})

    flag <- longflag / shortflag / "-" shortboolflag+
    longflag <-
        {:help: "--help" %true :}
      / {:version: "--version" %true :}
      / {:no_core: "--no-core" %true :}
    shortflag <-
        {:optimization: "-O" %sep? %number :}
      / {:debugger: ("-d" %sep? {[^%sep]+}) :}
      / {:requested_version: "-V" %sep? {([0-9.])+} :}
    shortboolflag <-
        {:check_syntax: "s" %true:}
      / {:compile: "c" %true:}
      / {:verbose: "v" %true :}
      / {:help: "h" %true :}

    nomsu_flag <- nomsu_longflag / "-" nomsu_shortboolflag+
    nomsu_shortboolflag <- {| {:key: [a-zA-Z] :} {:value: %true :} |}
    nomsu_longflag <- '--' {| {:key: [^%sep=]+ :} {:value: ('=' {[^%sep]+}) / %true :} |}
]], {
    true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep),
    spoof: Files.spoof
})
arg_string = table.concat(arg, sep)..sep
args, err = parser\match(arg_string)
if not args or err or args.help
    if err
        print("Didn't understand: \x1b[31;1m#{err}\x1b[0m")
    print usage
    os.exit(EXIT_FAILURE)
nomsu_args = Dict{}
for argpair in *args.nomsu_args
    nomsu_args[argpair.key] = argpair.value
nomsu_args.extras = List(args.nomsu_args.extras or {})
optimization = tonumber(args.optimization or 1)

nomsupath = {}
suffixes = if optimization > 0
    {"?.lua", "?/init.lua", "?.nom", "?/init.nom"}
else {"?.nom", "?/init.nom"}
add_path = (p)->
    for s in *suffixes do table.insert(nomsupath, p.."/"..s)
if NOMSU_VERSION and NOMSU_PREFIX
    add_path "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION}/lib"
else
    add_path "./lib"
NOMSU_PACKAGEPATH or= "/opt/nomsu"
add_path NOMSU_PACKAGEPATH
add_path "."
package.nomsupath = table.concat(nomsupath, ";")

nomsu_environment = require('nomsu_environment')
nomsu_environment.COMMAND_LINE_ARGS = nomsu_args
nomsu_environment.OPTIMIZATION = optimization
nomsu_environment.NOMSU_PACKAGEPATH = NOMSU_PACKAGEPATH
nomsu_environment.NOMSU_PREFIX = NOMSU_PREFIX

run = ->
    unless args.no_core
        nomsu_environment\export("core")

    if args.version
        nomsu_environment\run("say (Nomsu version)")
        os.exit(EXIT_SUCCESS)

    input_files = {}
    if args.files
        for f in *args.files
            if nomsu_name = f\match("^nomsu://(.*)%.nom")
                path, err = package.searchpath(nomsu_name, package.nomsupath, "/")
                if not path then error(err)
                table.insert input_files, path
            else
                table.insert input_files, f

    for filename in *input_files
        if args.check_syntax
            -- Check syntax
            code = Files.read(filename)
            source = Source(filename, 1, #code)
            nomsu_environment._1_parsed(NomsuCode\from(source, code))
            print("Parse succeeded: #{filename}")
        elseif args.compile
            -- Compile .nom files into .lua
            output = if filename == 'stdin' then io.output()
            else io.open(filename\gsub("%.nom$", ".lua"), "w")
            code = Files.read(filename)
            source = Source(filename, 1, #code)
            code = NomsuCode\from(source, code)
            env = nomsu_environment.new_environment!
            env.MODULE_NAME = filename
            tree = env._1_parsed(code)
            tree = {tree} unless tree.type == 'FileChunks'
            for chunk_no, chunk in ipairs tree
                lua = env\compile(chunk)
                lua\declare_locals!
                lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")
                if args.verbose then print(lua\text!)
                env\run(chunk)
                output\write(lua\text!, "\n")
            print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))
            output\close!
        elseif args.verbose
            env = nomsu_environment.new_environment!
            env.MODULE_NAME = filename
            env.WAS_RUN_DIRECTLY = true
            code = Files.read(filename)
            source = Source(filename, 1, #code)
            if filename\match("%.lua$")
                code = LuaCode\from(Source(filename, 1, #code), code)
                print(code\text!)
                env\run(code)
            else
                code = NomsuCode\from(source, code)
                tree = env._1_parsed(code)
                tree = {tree} unless tree.type == 'FileChunks'
                for chunk_no, chunk in ipairs tree
                    lua = env\compile(chunk)
                    lua\declare_locals!
                    lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")
                    print(lua\text!)
                    env\run(lua)
        else
            -- Just run the file
            f = Files.read(filename)
            if filename\match("%.lua$")
                f = LuaCode\from(Source(filename, 1, #f), f)
            env = nomsu_environment.new_environment!
            env.MODULE_NAME = filename
            env.WAS_RUN_DIRECTLY = true
            env\run(f)
            --nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)

debugger = if args.debugger == "nil" then {}
else require(args.debugger or 'error_handling')
guard = if type(debugger) == 'function' then debugger
else debugger.guard or debugger.call or debugger.wrap or debugger.run or ((fn)->fn())
guard(run)