///|
fn bytes_hex_digit(n : Int32) -> Char {
if n < 10 {
('0'.to_int() + n).to_char().unwrap()
} else {
('a'.to_int() + (n - 10)).to_char().unwrap()
}
}
///|
fn bytes_hex_byte(value : Int32) -> String {
let buf = StringBuilder()
let hi = (value >> 4) & 0xF
let lo = value & 0xF
buf.write_char(bytes_hex_digit(hi))
buf.write_char(bytes_hex_digit(lo))
buf.to_string()
}
///|
fn bytes_repr(values : Array[Int32]) -> String {
let buf = StringBuilder()
buf.write_string("b'")
for byte in values {
let v = byte & 0xFF
if v == 0x5c {
buf.write_string("\\\\")
} else if v == 0x27 {
buf.write_string("\\'")
} else if v == 0x09 {
buf.write_string("\\t")
} else if v == 0x0a {
buf.write_string("\\n")
} else if v == 0x0d {
buf.write_string("\\r")
} else if v >= 0x20 && v <= 0x7e {
buf.write_char(v.to_char().unwrap())
} else {
buf.write_string("\\x")
buf.write_string(bytes_hex_byte(v))
}
}
buf.write_char('\'')
buf.to_string()
}
///|
fn complex_to_string(real : Double, imag : Double) -> String {
if real == 0.0 {
if imag == 0.0 {
"0j"
} else {
imag.to_string() + "j"
}
} else {
let sign = if imag >= 0.0 { "+" } else { "-" }
let imag_abs = if imag < 0.0 { -imag } else { imag }
"(" + real.to_string() + sign + imag_abs.to_string() + "j)"
}
}
///|
fn value_to_string(value : @mpython.Value) -> String {
match value {
@mpython.Value::None => "None"
@mpython.Value::Bool(v) => if v { "True" } else { "False" }
@mpython.Value::Int(v) => v.to_string()
@mpython.Value::Float(v) => v.to_string()
@mpython.Value::Complex(real, imag) => complex_to_string(real, imag)
@mpython.Value::Str(v) => v
@mpython.Value::Bytes(values) => bytes_repr(values)
@mpython.Value::ByteArray(values) => "bytearray(" + bytes_repr(values) + ")"
@mpython.Value::MemoryView(values) =>
"memoryview(" + bytes_repr(values) + ")"
@mpython.Value::List(values) => {
let buf = StringBuilder()
buf.write_char('[')
for i = 0; i < values.length(); i = i + 1 {
if i > 0 {
buf.write_string(", ")
}
buf.write_string(value_to_string(values[i]))
}
buf.write_char(']')
buf.to_string()
}
@mpython.Value::Tuple(values) => {
let buf = StringBuilder()
buf.write_char('(')
for i = 0; i < values.length(); i = i + 1 {
if i > 0 {
buf.write_string(", ")
}
buf.write_string(value_to_string(values[i]))
}
if values.length() == 1 {
buf.write_string(",")
}
buf.write_char(')')
buf.to_string()
}
@mpython.Value::Dict(_) => "{}"
@mpython.Value::Set(values) => {
let buf = StringBuilder()
buf.write_char('{')
for i = 0; i < values.length(); i = i + 1 {
if i > 0 {
buf.write_string(", ")
}
buf.write_string(value_to_string(values[i]))
}
buf.write_char('}')
buf.to_string()
}
@mpython.Value::Function(func) => ""
@mpython.Value::Class(klass) => ""
@mpython.Value::Instance(inst) => "<" + inst.class.name + " object>"
@mpython.Value::BoundMethod(bound_method) =>
""
}
}
///|
fn print_run(run : @mpython.RunResult) -> Unit {
if run.stdout.length() > 0 {
@fs.write_string_to_file("/dev/stdout", run.stdout) catch {
_ => ()
}
}
if run.stderr.length() > 0 {
@fs.write_string_to_file("/dev/stderr", run.stderr) catch {
_ => ()
}
}
match run.value {
@mpython.Value::None => ()
_ => println(value_to_string(run.value))
}
}
///|
fn print_usage() -> Unit {
println(
"usage: moon run cmd/main -- [--parse-only] [--stdlib PATH] (-m MODULE | program.py) [args...]",
)
}
///|
fn module_rel_path(name : String) -> String {
let parts : Array[String] = []
for part in name.split(".") {
let p = part.to_owned()
if p.length() > 0 {
parts.push(p)
}
}
if parts.length() == 0 {
name
} else {
let mut joined = parts[0]
for i = 1; i < parts.length(); i = i + 1 {
joined = joined + "/" + parts[i]
}
joined
}
}
///|
fn module_parent_name(name : String) -> String {
let parts : Array[String] = []
for part in name.split(".") {
let p = part.to_owned()
if p.length() > 0 {
parts.push(p)
}
}
if parts.length() <= 1 {
"".to_string()
} else {
let mut joined = parts[0]
for i = 1; i + 1 < parts.length(); i = i + 1 {
joined = joined + "." + parts[i]
}
joined
}
}
///|
fn resolve_module_entry(
module_name : String,
roots : Array[String],
) -> (String, String)? {
// Prefer package execution (pkg/__main__.py) over module (pkg.py) when present.
let rel = module_rel_path(module_name)
let package_main = rel + "/__main__.py"
for root in roots {
let candidate = if root.length() == 0 {
package_main
} else {
@path.Path(root).join(@path.Path(package_main)).to_string()
}
if @fs.path_exists(candidate) {
return Some((candidate, module_name))
}
}
let module_file = rel + ".py"
for root in roots {
let candidate = if root.length() == 0 {
module_file
} else {
@path.Path(root).join(@path.Path(module_file)).to_string()
}
if @fs.path_exists(candidate) {
return Some((candidate, module_parent_name(module_name)))
}
}
None
}
///|
fn main {
let args = @env.args()
let mut script_path : String? = None
let mut module_name : String? = None
let mut module_run_name : String? = None
let mut parse_only = false
let stdlib_paths : Array[String] = []
let program_args : Array[String] = []
let mut i = 1
while i < args.length() {
let arg = args[i]
if module_name is None && script_path is None {
if arg == "-h" || arg == "--help" {
print_usage()
@sys.exit(0)
}
if arg == "--parse-only" {
parse_only = true
i += 1
continue
}
if arg == "--stdlib" || arg == "--stdlib-path" {
if i + 1 >= args.length() {
println("missing value for " + arg)
print_usage()
@sys.exit(1)
}
stdlib_paths.push(args[i + 1])
i += 2
continue
}
if arg == "-m" {
if i + 1 >= args.length() {
println("missing module name for -m")
print_usage()
@sys.exit(1)
}
module_name = Some(args[i + 1])
module_run_name = Some(args[i + 1])
i += 2
continue
}
script_path = Some(arg)
i += 1
continue
}
program_args.push(arg)
i += 1
}
// Build search roots for -m resolution: cwd first, then explicit stdlib roots.
let search_roots : Array[String] = [""]
for root in stdlib_paths {
search_roots.push(root)
}
let (entry_path, package_name, argv) = match module_name {
Some(modname) => {
let resolved = resolve_module_entry(modname, search_roots)
if resolved is None {
println("cannot find module: " + modname)
@sys.exit(1)
}
let resolved = resolved.unwrap()
let argv : Array[String] = [resolved.0]
for item in program_args {
argv.push(item)
}
(resolved.0, resolved.1, argv)
}
None => {
let script_path = match script_path {
Some(path) => path
None => {
print_usage()
@sys.exit(1)
""
}
}
let argv : Array[String] = [script_path]
for item in program_args {
argv.push(item)
}
(script_path, "".to_string(), argv)
}
}
let source = @fs.read_file_to_string(entry_path) catch {
_ => {
println("cannot read file: " + entry_path)
@sys.exit(1)
""
}
}
if parse_only {
match @mpython.parse(source) {
Ok(_) => @sys.exit(0)
Err(err) => {
println(@mpython.format_parse_error(err))
@sys.exit(1)
}
}
}
let config = @mpython.Config::for_cli(stdlib_paths, Some(entry_path), argv)
let interpreter = @mpython.Interpreter::with_config(config)
interpreter.set_global_str("__file__", entry_path)
interpreter.set_global_str("__package__", package_name)
match module_run_name {
Some(modname) => {
let is_pkg_main = entry_path.has_suffix("/__main__.py") ||
entry_path.has_suffix("\\__main__.py")
interpreter.set_main_module_spec(
modname, entry_path, package_name, is_pkg_main,
)
}
None => ()
}
match interpreter.exec_source(source) {
Ok(run) => print_run(run)
Err(err) => {
println(
@mpython.format_runtime_error_with_traceback(
err,
config.traceback_limit,
),
)
@sys.exit(1)
}
}
}