///|
/// Tokenizes Moon 20260920 dry-run command quoting. Never evaluates a shell.
pub fn tokenize(line : String) -> Array[String] raise ConfigError {
let words : Array[String] = []
let mut word = ""
let mut quote = '\u0000'
let mut escaped = false
let mut started = false
for ch in line {
if escaped {
word += ch.to_string()
escaped = false
} else if ch == '\\' && quote != '\'' {
escaped = true
started = true
} else if quote != '\u0000' {
if ch == quote {
quote = '\u0000'
} else {
word += ch.to_string()
}
} else if ch == '\'' || ch == '"' {
quote = ch
started = true
} else if ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' {
if started {
words.push(word)
word = ""
started = false
}
} else {
word += ch.to_string()
started = true
}
}
if escaped || quote != '\u0000' {
raise Invalid("unsupported dry-run quoting")
}
if started {
words.push(word)
}
words
}
///|
/// Selects compiler-only commands; excludes host C compilers and runtime objects.
pub fn compiler_commands(
plan : String,
moon_home : String,
) -> Array[Array[String]] raise ConfigError {
let commands : Array[Array[String]] = []
for line in plan.split("\n") {
if line.has_prefix("moonc ") {
let original = tokenize(line.to_owned())
if original.length() < 2 ||
(original[1] != "build-package" && original[1] != "link-core") {
raise Invalid("unsupported compiler step")
}
let args : Array[String] = []
let mut index = 1
while index < original.length() {
// Optional IDE metadata is absent when Moon only prints a dry-run plan.
if original[index] == "-all-pkgs" {
index += 2
} else {
args.push(
original[index].replace_all(old="$MOON_HOME", new=moon_home),
)
index += 1
}
}
commands.push(args)
}
}
if commands.is_empty() || commands.last().unwrap()[0] != "link-core" {
raise Invalid("plan has no C link-core step; unsupported Moon toolchain")
}
commands
}
///|
pub fn signature(fn_ : Function) -> String {
"pub fn " +
fn_.name +
"(" +
fn_.params.map(p => p.kind.moon_name()).join(", ") +
") -> " +
fn_.result.moon_name()
}
///|
pub fn verify_interface(
config : Config,
text : String,
) -> Unit raise ConfigError {
let declarations = text.split("\n").map(s => s.trim().to_owned()).to_array()
for fn_ in config.functions {
let expected = signature(fn_)
if !declarations.contains(expected) {
raise Invalid("signature unsupported or mismatched; expected " + expected)
}
}
}