///|
priv struct CliOptions {
output_file : String
input_file : String
code_unit : @type.CodeUnit
default_encoding : @type.Encoding?
}
///|
fn parse_code_unit(code_unit_str : String) -> @type.CodeUnit {
match code_unit_str {
"1" => CodeUnit1
"2" => CodeUnit2
"4" => CodeUnit4
_ => abort("Invalid code unit: \{code_unit_str}")
}
}
///|
fn parse_default_encoding(encoding : String) -> @type.Encoding {
match encoding {
"ascii" => Ascii
"latin1" => Latin1
"utf8" => Utf8
"utf16" => Utf16
_ => abort("Invalid encoding: \{encoding}")
}
}
///|
fn get_value(matches : @argparse.Matches, name : String) -> String? {
match matches.values.get(name) {
Some(values) => values.last()
None => None
}
}
///|
fn moonlex_command() -> @argparse.Command {
Command(
"moonlex",
version="moonlex 0.1.0",
disable_version_flag=true,
flags=[
FlagArg(
"version",
short='v',
long="version",
action=Version,
about="Show version",
),
],
options=[
OptionArg(
"output-file",
short='o',
long="output-file",
about="Output file",
),
OptionArg(
"code-unit",
long="code-unit",
about="Set code unit (1, 2, or 4)",
),
OptionArg(
"default-encoding",
long="default-encoding",
about="Set default encoding (ascii, latin1, utf8, or utf16). Only valid when code unit is 1",
),
],
positionals=[PositionArg("input-file", allow_hyphen_values=true)],
)
}
///|
fn parse_cli_args(argv : ArrayView[String]) -> CliOptions raise {
let matches = moonlex_command().parse(argv~, env={})
let code_unit = match get_value(matches, "code-unit") {
Some(code_unit_str) => parse_code_unit(code_unit_str)
None => CodeUnit4
}
let default_encoding = get_value(matches, "default-encoding").map(
parse_default_encoding,
)
{
output_file: get_value(matches, "output-file").unwrap_or(""),
input_file: get_value(matches, "input-file").unwrap_or(""),
code_unit,
default_encoding,
}
}
///|
fn cli_user_args(args : Array[String]) -> ArrayView[String] {
if args.length() > 1 &&
(args[1].has_suffix(".js") || args[1].has_suffix(".cjs")) {
args[2:]
} else if args.length() > 0 {
args[1:]
} else {
[]
}
}
///|
#cfg(target="wasm")
fn proc_exit_ffi(rval : Int) -> Unit = "wasi_snapshot_preview1" "proc_exit"
///|
#cfg(target="native")
extern "c" fn proc_exit_ffi(rval : Int) -> Unit = "exit"
///|
async fn write_stderr_line(msg : String) -> Unit {
@stdio.stderr.write(msg + "\n") catch {
_ => ()
}
}
///|
async fn[T] exit_with_error(msg : String) -> T {
write_stderr_line(msg)
proc_exit_ffi(1)
panic()
}
///|
async fn main {
let options = parse_cli_args(cli_user_args(@env.args())) catch {
err => exit_with_error(err.to_string())
}
guard options.input_file != "" else {
exit_with_error("Input file is required")
}
let output = {
let lexer_spec_src = @fs.read_file(options.input_file).text() catch {
err => exit_with_error("Failed to read input file: \{err}")
}
let lex_ = @parser.parse_lex_from_string(lexer_spec_src) catch {
UnexpectedToken(token, loc, expected) => {
let loc_str = loc_to_string(options.input_file, lexer_spec_src, loc)
let expected_str = array_to_or_list(expected.map(Show::to_string)[:])
exit_with_error(
"SyntaxError: Unexpected token \{token.kind()}, expected \{expected_str}.\n at \{loc_str}",
)
}
}
@codegen.codegen_lex(
lex_,
code_unit=options.code_unit,
default_encoding=options.default_encoding,
)
}
if options.output_file == "" {
@stdio.stdout.write(output + "\n") catch {
err => exit_with_error("Failed to write stdout: \{err}")
}
} else {
@fs.write_file(options.output_file, output, create_mode=CreateOrTruncate) catch {
err => exit_with_error("Failed to write output file: \{err}")
}
}
}