(230 lines)
1 #!/usr/bin/env moon2 -- This file contains the command-line Nomsu runner.3 export NOMSU_VERSION4 NOMSU_VERSION = {7, 0, 1}6 clibtype = package.cpath\match("?%.(so)") or package.cpath\match("?%.(dll)")8 if NOMSU_PREFIX9 package.path = "#{NOMSU_PREFIX}/share/nomsu/#{table.concat NOMSU_VERSION, "."}/?.lua;"..package.path10 package.cpath = "#{NOMSU_PREFIX}/lib/nomsu/#{table.concat NOMSU_VERSION, "."}/?.#{clibtype};"..package.cpath12 export COLOR_ENABLED13 COLOR_ENABLED = true14 if clibtype == "dll"15 -- Enable colors:16 ok,enable_colors = pcall(require, 'wincolors')17 COLOR_ENABLED = false18 if ok19 ok,_ = pcall(enable_colors)20 if ok21 COLOR_ENABLED = false22 -- Special hack to enable utf8 for windows console applications:23 pcall(os.execute,"chcp 65001>nul")25 {:List, :Dict} = require 'containers'26 NOMSU_VERSION = List(NOMSU_VERSION)27 Text = require 'text'28 require 'builtin_metatables'29 EXIT_FAILURE = 130 EXIT_SUCCESS = 032 usage = [=[33 Nomsu Usage: nomsu [-V version] [--help | -h] [--version] [-O optimization level] [-v] [-c] [-s] [-d debugger] [--no-core] [(file | -t tool | -e "nomsu code..." | files... -- ) [nomsu args...]]35 OPTIONS36 -t <tool> Run a tool.37 -e Execute the specified string.38 -O <level> Run the compiler with the given optimization level (>0: use precompiled .lua versions of Nomsu files, when available).39 -v Verbose: print compiled lua code.40 -c Compile the input files into a .lua files.41 -s Check the input files for syntax errors.42 -d <debugger> Attempt to use the specified debugger to wrap the main body of execution.43 -h/--help Print this message.44 --version Print the version number and exit.45 --no-core Skip loading the Nomsu core by default.46 -V specify which Nomsu version is desired.47 <file> The Nomsu file to run (can be "-" to use stdin).48 ]=]50 ok, _ = pcall ->51 export lpeg, re52 lpeg = require 'lpeg'53 re = require 're'54 if not ok55 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`")56 os.exit(EXIT_FAILURE)57 Files = require "files"58 {:NomsuCode, :LuaCode, :Source} = require "code_obj"60 sep = "\3"61 parser = re.compile([[62 args <- {| (flag %sep)*63 {:files: {|64 ( ("-m" %sep)? (file %sep)+ "--" %sep65 / file %sep66 / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :}67 {:nomsu_args: {| (nomsu_flag %sep)* {:extras: {| ({[^%sep]+} %sep)* |} :} |} :}68 |} ({.+}?)70 file <-71 ( "-e" %sep ({[^%sep]+} -> spoof)72 / "-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~}73 / !"--" {[^%sep]+})75 flag <- longflag / shortflag / "-" shortboolflag+76 longflag <-77 {:help: "--help" %true :}78 / {:version: "--version" %true :}79 / {:no_core: "--no-core" %true :}80 shortflag <-81 {:optimization: "-O" %sep? %number :}82 / {:debugger: ("-d" %sep? {[^%sep]+}) :}83 / {:requested_version: "-V" %sep? {([0-9.])+} :}84 shortboolflag <-85 {:check_syntax: "s" %true:}86 / {:compile: "c" %true:}87 / {:verbose: "v" %true :}88 / {:help: "h" %true :}90 nomsu_flag <- nomsu_longflag / "-" nomsu_shortboolflag+91 nomsu_shortboolflag <- {| {:key: [a-zA-Z] :} {:value: %true :} |}92 nomsu_longflag <- '--' {| {:key: [^%sep=]+ :} {:value: ('=' {[^%sep]+}) / %true :} |}93 ]], {94 true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep),95 spoof: Files.spoof96 })97 arg_string = table.concat(arg, sep)..sep98 args, err = parser\match(arg_string)99 if not args or err100 if err101 print("Didn't understand: #{err}")102 print usage103 os.exit(EXIT_FAILURE)104 if args.help105 print "Nomsu - A dynamically typed programming language with natural syntax and strong metaprogramming abilities."106 print "https://nomsu.org\n"107 print usage108 os.exit(EXIT_SUCCESS)109 if args.version110 print(NOMSU_VERSION\joined_with("."))111 os.exit(EXIT_SUCCESS)112 nomsu_args = Dict{}113 for argpair in *args.nomsu_args114 nomsu_args[argpair.key] = argpair.value115 nomsu_args.extras = List(args.nomsu_args.extras or {})116 optimization = tonumber(args.optimization or 1)118 nomsupath = {}119 suffixes = if optimization > 0120 {"?.lua", "?/init.lua", "?.nom", "?/init.nom"}121 else {"?.nom", "?/init.nom"}122 add_path = (p)->123 for s in *suffixes do table.insert(nomsupath, p.."/"..s)124 if NOMSU_PREFIX125 add_path "#{NOMSU_PREFIX}/share/nomsu/#{NOMSU_VERSION\joined_with(".")}/lib"126 else127 add_path "./lib"128 NOMSU_PACKAGEPATH or= "/opt/nomsu"129 add_path NOMSU_PACKAGEPATH130 add_path "."131 package.nomsupath = table.concat(nomsupath, ";")132 package.nomsuloaded = Dict{}134 nomsu_environment = require('nomsu_environment')135 nomsu_environment.NOMSU_VERSION = NOMSU_VERSION136 nomsu_environment.COMMAND_LINE_ARGS = nomsu_args137 nomsu_environment.OPTIMIZATION = optimization138 nomsu_environment.NOMSU_PACKAGEPATH = NOMSU_PACKAGEPATH139 nomsu_environment.NOMSU_PREFIX = NOMSU_PREFIX140 nomsu_environment.COLOR_ENABLED = COLOR_ENABLED142 run = ->143 unless args.no_core144 nomsu_environment\export("core")146 input_files = {}147 if args.files and #args.files > 0148 for f in *args.files149 if nomsu_name = f\match("^nomsu://(.*)%.nom")150 path, err = package.searchpath(nomsu_name, package.nomsupath, "/")151 if not path then error(err)152 table.insert input_files, path153 else154 table.insert input_files, f156 for filename in *input_files157 if args.check_syntax158 -- Check syntax159 code = Files.read(filename)160 source = Source(filename, 1, #code)161 nomsu_environment._1_parsed(NomsuCode\from(source, code))162 print("Parse succeeded: #{filename}")163 elseif args.compile164 code = Files.read(filename)165 if not code166 error("Could not find file '#{filename}'")167 -- Compile .nom files into .lua168 if filename\match("%.lua$")169 error("Cannot compile a lua file (expected a nomsu file as input)")170 output = if filename == 'stdin' then io.output()171 else io.open(filename\gsub("%.nom$", ".lua"), "w")172 source = Source(filename, 1, #code)173 code = NomsuCode\from(source, code)174 env = nomsu_environment.new_environment!175 env.MODULE_NAME = filename176 tree = env._1_parsed(code)177 if tree.shebang178 output\write tree.shebang179 tree = {tree} unless tree.type == 'FileChunks'180 for chunk_no, chunk in ipairs tree181 lua = env\compile(chunk)182 lua\declare_locals!183 lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")184 if args.verbose then print(lua\text!)185 env\run(chunk)186 output\write(lua\text!, "\n")187 print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))188 output\close!189 elseif args.verbose190 env = nomsu_environment.new_environment!191 env.MODULE_NAME = filename192 env.WAS_RUN_DIRECTLY = true193 code = Files.read(filename)194 source = Source(filename, 1, #code)195 if filename\match("%.lua$")196 code = LuaCode\from(Source(filename, 1, #code), code)197 print(code\text!)198 env\run(code)199 else200 code = NomsuCode\from(source, code)201 tree = env._1_parsed(code)202 tree = {tree} unless tree.type == 'FileChunks'203 for chunk_no, chunk in ipairs tree204 lua = env\compile(chunk)205 lua\declare_locals!206 lua\prepend((chunk_no > 1) and '\n' or '', "-- File #{filename} chunk ##{chunk_no}\n")207 print(lua\text!)208 env\run(lua)209 else210 -- Just run the file211 f = Files.read(filename)212 if not f213 if filename\match "^-"214 print "Not a valid flag: "..filename.."\n"215 print usage216 else217 print "File not found: "..filename218 os.exit EXIT_FAILURE219 if filename\match("%.lua$")220 f = LuaCode\from(Source(filename, 1, #f), f)221 env = nomsu_environment.new_environment!222 env.MODULE_NAME = filename223 env.WAS_RUN_DIRECTLY = true224 env\run(f)226 debugger = if args.debugger == "nil" then {}227 else require(args.debugger or 'error_handling')228 guard = if type(debugger) == 'function' then debugger229 else debugger.guard or debugger.call or debugger.wrap or debugger.run or ((fn)->fn())230 guard(run)