///|
pub const DEFAULT_CONFIG_PATH : String = "starshine.config.json"
///|
pub enum CliInputFormat {
Wasm
Wat
Wast
} derive(Eq, Debug)
///|
pub fn CliInputFormat::wasm() -> CliInputFormat {
Wasm
}
///|
pub fn CliInputFormat::wat() -> CliInputFormat {
Wat
}
///|
pub fn CliInputFormat::wast() -> CliInputFormat {
Wast
}
///|
pub enum CliOutputTarget {
StdoutOutput
FileOutput(String)
DirOutput(String)
} derive(Eq, Debug)
///|
pub fn CliOutputTarget::stdout() -> CliOutputTarget {
StdoutOutput
}
///|
pub fn CliOutputTarget::file(path : String) -> CliOutputTarget {
FileOutput(path)
}
///|
pub fn CliOutputTarget::dir(path : String) -> CliOutputTarget {
DirOutput(path)
}
///|
pub enum CliOptimizationFlag {
OptimizePreset
OptimizeSizePreset
ShrinkPreset
OLevel(Int32, Bool)
} derive(Eq, Debug)
///|
pub fn CliOptimizationFlag::optimize() -> CliOptimizationFlag {
OptimizePreset
}
///|
pub fn CliOptimizationFlag::osize() -> CliOptimizationFlag {
OptimizeSizePreset
}
///|
pub fn CliOptimizationFlag::shrink() -> CliOptimizationFlag {
ShrinkPreset
}
///|
pub fn CliOptimizationFlag::olevel(
level : Int32,
size_bias : Bool,
) -> CliOptimizationFlag {
OLevel(level, size_bias)
}
///|
pub enum TrapMode {
Allow
Never
} derive(Eq, Debug)
///|
pub fn TrapMode::allow() -> TrapMode {
Allow
}
///|
pub fn TrapMode::never() -> TrapMode {
Never
}
///|
pub enum CliTracingLevel {
Pass
Phase
Helper
} derive(Eq, Debug)
///|
pub fn CliTracingLevel::pass() -> CliTracingLevel {
Pass
}
///|
pub fn CliTracingLevel::phase() -> CliTracingLevel {
Phase
}
///|
pub fn CliTracingLevel::helper() -> CliTracingLevel {
Helper
}
///|
pub enum CliParseError {
MissingFlagValue(String)
InvalidInputFormat(String)
StdinNeedsFormat
UnknownShortFlag(Char)
InvalidLongFlag(String)
UnexpectedFlagValue(String)
InvalidDumpPath(String)
InvalidFunctionIndexList(String)
InvalidOptimizationFlag(String)
InvalidTrapMode(String)
InvalidTracingLevel(String)
} derive(Eq, Debug)
///|
pub impl Show for CliParseError with fn output(self, logger) {
match self {
InvalidTrapMode(arg) => {
logger.write_string("InvalidTrapMode(")
logger.write_string(arg)
logger.write_char(')')
}
InvalidOptimizationFlag(arg) => {
logger.write_string("InvalidOptimizationFlag(")
logger.write_string(arg)
logger.write_char(')')
}
InvalidFunctionIndexList(arg) => {
logger.write_string("InvalidFunctionIndexList(")
logger.write_string(arg)
logger.write_char(')')
}
InvalidDumpPath(arg) => {
logger.write_string("InvalidDumpPath(")
logger.write_string(arg)
logger.write_char(')')
}
UnexpectedFlagValue(arg) => {
logger.write_string("UnexpectedFlagValue(")
logger.write_string(arg)
logger.write_char(')')
}
InvalidLongFlag(arg) => {
logger.write_string("InvalidLongFlag(")
logger.write_string(arg)
logger.write_char(')')
}
UnknownShortFlag(arg) => {
logger.write_string("UnknownShortFlag(")
logger.write_char(arg)
logger.write_char(')')
}
StdinNeedsFormat => logger.write_string("StdinNeedsFormat")
InvalidInputFormat(arg) => {
logger.write_string("InvalidInputFormat(")
logger.write_string(arg)
logger.write_char(')')
}
MissingFlagValue(arg) => {
logger.write_string("MissingFlagValue(")
logger.write_string(arg)
logger.write_char(')')
}
InvalidTracingLevel(arg) => {
logger.write_string("InvalidTracingLevel(")
logger.write_string(arg)
logger.write_char(')')
}
}
}
///|
pub fn CliParseError::missing_flag_value(flag : String) -> CliParseError {
MissingFlagValue(flag)
}
///|
pub fn CliParseError::invalid_input_format(format : String) -> CliParseError {
InvalidInputFormat(format)
}
///|
pub fn CliParseError::stdin_needs_format() -> CliParseError {
StdinNeedsFormat
}
///|
pub fn CliParseError::unknown_short_flag(flag : Char) -> CliParseError {
UnknownShortFlag(flag)
}
///|
pub fn CliParseError::invalid_long_flag(flag : String) -> CliParseError {
InvalidLongFlag(flag)
}
///|
pub fn CliParseError::unexpected_flag_value(flag : String) -> CliParseError {
UnexpectedFlagValue(flag)
}
///|
pub fn CliParseError::invalid_dump_path(path : String) -> CliParseError {
InvalidDumpPath(path)
}
///|
pub fn CliParseError::invalid_function_index_list(
raw : String,
) -> CliParseError {
InvalidFunctionIndexList(raw)
}
///|
pub fn CliParseError::invalid_optimization_flag(flag : String) -> CliParseError {
InvalidOptimizationFlag(flag)
}
///|
pub fn CliParseError::invalid_trap_mode(mode : String) -> CliParseError {
InvalidTrapMode(mode)
}
///|
pub fn CliParseError::invalid_tracing_level(level : String) -> CliParseError {
InvalidTracingLevel(level)
}
///|
pub struct CliParseResult {
mut config_path : String?
input_globs : Array[String]
mut glob_enabled : Bool
mut help_requested : Bool
mut version_requested : Bool
mut debug_serial_passes : Bool
mut read_stdin : Bool
mut input_format : CliInputFormat?
output_targets : Array[CliOutputTarget]
pass_flags : Array[String]
optimize_flags : Array[CliOptimizationFlag]
mut trap_mode : TrapMode?
mut ignore_implicit_traps : Bool
mut monomorphize_min_benefit : Int32?
mut inlining_always_inline_max_size : Int32?
mut inlining_one_caller_inline_max_size : Int32?
mut inlining_flexible_inline_max_size : Int32?
mut inlining_max_combined_binary_size : Int32?
mut inlining_allow_functions_with_loops : Bool?
mut inlining_partial_inlining_ifs : Int32?
mut low_memory_unused : Bool?
mut low_memory_bound : UInt64?
mut closed_world : Bool?
mut compiler_facts_policy : String?
mut tracing : CliTracingLevel?
} derive(Eq, Debug)
///|
pub fn CliParseResult::new(
config_path~ : String? = None,
input_globs~ : Array[String] = [],
glob_enabled~ : Bool = false,
help_requested~ : Bool = false,
version_requested~ : Bool = false,
debug_serial_passes~ : Bool = false,
read_stdin~ : Bool = false,
input_format~ : CliInputFormat? = None,
output_targets~ : Array[CliOutputTarget] = [],
pass_flags~ : Array[String] = [],
optimize_flags~ : Array[CliOptimizationFlag] = [],
trap_mode~ : TrapMode? = None,
ignore_implicit_traps~ : Bool = false,
monomorphize_min_benefit~ : Int32? = None,
inlining_always_inline_max_size~ : Int32? = None,
inlining_one_caller_inline_max_size~ : Int32? = None,
inlining_flexible_inline_max_size~ : Int32? = None,
inlining_max_combined_binary_size~ : Int32? = None,
inlining_allow_functions_with_loops~ : Bool? = None,
inlining_partial_inlining_ifs~ : Int32? = None,
low_memory_unused~ : Bool? = None,
low_memory_bound~ : UInt64? = None,
closed_world~ : Bool? = None,
compiler_facts_policy~ : String? = None,
tracing~ : CliTracingLevel? = None,
) -> CliParseResult {
{
config_path,
input_globs,
glob_enabled,
help_requested,
version_requested,
debug_serial_passes,
read_stdin,
input_format,
output_targets,
pass_flags,
optimize_flags,
trap_mode,
ignore_implicit_traps,
monomorphize_min_benefit,
inlining_always_inline_max_size,
inlining_one_caller_inline_max_size,
inlining_flexible_inline_max_size,
inlining_max_combined_binary_size,
inlining_allow_functions_with_loops,
inlining_partial_inlining_ifs,
low_memory_unused,
low_memory_bound,
closed_world,
compiler_facts_policy,
tracing,
}
}
///|
fn is_ascii_space(c : UInt16) -> Bool {
c == ' '.to_int().to_uint16() ||
c == '\t'.to_int().to_uint16() ||
c == '\n'.to_int().to_uint16() ||
c == '\r'.to_int().to_uint16()
}
///|
fn ascii_lower_u16(c : UInt16) -> UInt16 {
if c >= 'A'.to_int().to_uint16() && c <= 'Z'.to_int().to_uint16() {
(c.to_int() + 32).to_uint16()
} else {
c
}
}
///|
fn trimmed_bounds(input : String) -> (Int32, Int32) {
let mut start = 0
let mut end = input.length()
while start < end && is_ascii_space(input[start]) {
start += 1
}
while end > start && is_ascii_space(input[end - 1]) {
end -= 1
}
(start, end)
}
///|
fn trimmed_ascii_eq_ignore_case(input : String, expected : String) -> Bool {
let (start, end) = trimmed_bounds(input)
if end - start != expected.length() {
return false
}
let mut i = 0
while i < expected.length() {
if ascii_lower_u16(input[start + i]) != expected[i] {
return false
}
i += 1
}
true
}
///|
fn trimmed_string(input : String) -> String {
let (start, end) = trimmed_bounds(input)
if start == 0 && end == input.length() {
input
} else {
input.sub(start~, end~).to_owned()
}
}
///|
fn parse_input_format(raw : String) -> CliInputFormat? {
if trimmed_ascii_eq_ignore_case(raw, "wasm") {
Some(CliInputFormat::wasm())
} else if trimmed_ascii_eq_ignore_case(raw, "wat") {
Some(CliInputFormat::wat())
} else if trimmed_ascii_eq_ignore_case(raw, "wast") {
Some(CliInputFormat::wast())
} else {
None
}
}
///|
fn parse_trap_mode(raw : String) -> TrapMode? {
if trimmed_ascii_eq_ignore_case(raw, "allow") ||
trimmed_ascii_eq_ignore_case(raw, "traps-may-happen") {
Some(TrapMode::allow())
} else if trimmed_ascii_eq_ignore_case(raw, "never") ||
trimmed_ascii_eq_ignore_case(raw, "traps-never-happen") {
Some(TrapMode::never())
} else {
None
}
}
///|
fn parse_tracing_level(raw : String) -> CliTracingLevel? {
if trimmed_ascii_eq_ignore_case(raw, "pass") {
Some(CliTracingLevel::pass())
} else if trimmed_ascii_eq_ignore_case(raw, "phase") {
Some(CliTracingLevel::phase())
} else if trimmed_ascii_eq_ignore_case(raw, "helper") {
Some(CliTracingLevel::helper())
} else {
None
}
}
///|
fn append_input_glob(out : Array[String], raw : String) -> Unit {
let trimmed = trimmed_string(raw)
if trimmed.length() == 0 {
return
}
out.push(normalize_cli_path(trimmed))
}
///|
fn looks_like_option_token(token : String) -> Bool {
token.length() > 1 && token[0] == '-'
}
///|
fn path_has_ascii_suffix_ignore_case(path : String, suffix : String) -> Bool {
if path.length() < suffix.length() {
return false
}
let start = path.length() - suffix.length()
let mut i = 0
while i < suffix.length() {
if ascii_lower_u16(path[start + i]) != suffix[i] {
return false
}
i += 1
}
true
}
///|
fn parse_dump_pass_value(raw : String) -> Result[String, CliParseError] {
let value = trimmed_string(raw)
if value.length() == 0 {
return Err(CliParseError::missing_flag_value("--dump"))
}
if path_has_ascii_suffix_ignore_case(value, ".wasm") ||
path_has_ascii_suffix_ignore_case(value, ".wat") {
return Ok("dump=\{value}")
}
Err(CliParseError::invalid_dump_path(value))
}
///|
fn parse_print_pass_value(
flag_name : String,
raw : String,
) -> Result[String, CliParseError] {
let value = trimmed_string(raw)
if value.length() == 0 {
return Err(CliParseError::missing_flag_value(long_flag_name(flag_name)))
}
Ok("\{flag_name}=\{value}")
}
///|
fn parse_extract_functions_value(raw : String) -> Result[String, CliParseError] {
let normalized_parts : Array[String] = []
for part in raw.split(",") {
match parse_non_negative_decimal(part.to_owned()) {
Some(idx) => normalized_parts.push("\{idx}")
None => return Err(CliParseError::invalid_function_index_list(raw))
}
}
if normalized_parts.is_empty() {
return Err(CliParseError::invalid_function_index_list(raw))
}
let out = StringBuilder::new()
let mut i = 0
while i < normalized_parts.length() {
if i > 0 {
out.write_char(',')
}
out.write_string(normalized_parts[i])
i += 1
}
Ok("extract-functions=\{out.to_string()}")
}
///|
fn push_unique_output_target(
targets : Array[CliOutputTarget],
target : CliOutputTarget,
) -> Unit {
for existing in targets {
if existing == target {
return
}
}
targets.push(target)
}
///|
fn long_flag_name_char_is_valid(c : UInt16) -> Bool {
(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-'
}
///|
fn split_long_flag_token(token : String) -> (String, String?, Bool) {
let mut i = 2
let mut valid_kebab_name = true
let mut prev_dash = false
while i < token.length() {
let c = token[i]
if c == '=' {
if i == 2 || prev_dash {
valid_kebab_name = false
}
let name = token.sub(start=2, end=i).to_owned()
let value = token.sub(start=i + 1).to_owned()
return (name, Some(value), valid_kebab_name)
}
if !long_flag_name_char_is_valid(c) ||
(i == 2 && c == '-') ||
(prev_dash && c == '-') {
valid_kebab_name = false
}
prev_dash = c == '-'
i += 1
}
if i == 2 || prev_dash {
valid_kebab_name = false
}
(token.sub(start=2).to_owned(), None, valid_kebab_name)
}
///|
fn long_flag_name(name : String) -> String {
"--" + name
}
///|
fn parse_opt_level_flag(
flag : String,
) -> Result[CliOptimizationFlag, CliParseError] {
if flag.length() < 2 || flag[0] != '-' || flag[1] != 'O' {
return Err(CliParseError::invalid_optimization_flag(flag))
}
let mut i = 2
let mut saw_digit = false
let mut level = 2
while i < flag.length() {
let c = flag[i]
if c >= '0' && c <= '9' {
if !saw_digit {
saw_digit = true
level = 0
}
level = level * 10 + (c.to_int() - '0'.to_int())
i += 1
continue
}
break
}
let mut size_bias = false
if i < flag.length() {
if flag[i] == 's' && !saw_digit {
i += 1
if i != flag.length() {
return Err(CliParseError::invalid_optimization_flag(flag))
}
return Ok(CliOptimizationFlag::osize())
} else if flag[i] == 'z' {
size_bias = true
i += 1
} else {
return Err(CliParseError::invalid_optimization_flag(flag))
}
}
if i != flag.length() {
return Err(CliParseError::invalid_optimization_flag(flag))
}
Ok(CliOptimizationFlag::olevel(level, size_bias))
}
///|
fn parse_non_negative_decimal(raw : String) -> Int32? {
let (start, end) = trimmed_bounds(raw)
if start == end {
return None
}
let mut value = 0
let mut i = start
while i < end {
let c = raw[i]
if c < '0' || c > '9' {
return None
}
value = value * 10 + (c.to_int() - '0'.to_int())
i += 1
}
Some(value)
}
///|
fn parse_short_option_cluster(
args : Array[String],
index : Int32,
parsed : CliParseResult,
) -> Result[Int32, CliParseError] {
let token = args[index]
let mut i = 1
while i < token.length() {
let flag = token[i]
match flag {
'i' => {
if token.sub(start=i).to_owned() == "iit" {
parsed.ignore_implicit_traps = true
return Ok(index + 1)
}
return Err(CliParseError::unknown_short_flag(flag.unsafe_to_char()))
}
'h' => {
parsed.help_requested = true
i += 1
}
'V' => {
parsed.version_requested = true
i += 1
}
'g' => {
parsed.glob_enabled = true
i += 1
}
's' => {
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::stdout(),
)
i += 1
}
'c' => {
let attached = token.sub(start=i + 1).to_owned()
if attached.length() > 0 {
parsed.config_path = Some(attached)
return Ok(index + 1)
}
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
parsed.config_path = Some(args[index + 1])
return Ok(index + 2)
}
parsed.config_path = Some(DEFAULT_CONFIG_PATH)
return Ok(index + 1)
}
'o' => {
let attached = token.sub(start=i + 1).to_owned()
if attached.length() > 0 {
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::file(attached),
)
return Ok(index + 1)
}
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::file(args[index + 1]),
)
return Ok(index + 2)
}
return Err(CliParseError::missing_flag_value("-o"))
}
'd' => {
let attached = token.sub(start=i + 1).to_owned()
if attached.length() > 0 {
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::dir(attached),
)
return Ok(index + 1)
}
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::dir(args[index + 1]),
)
return Ok(index + 2)
}
return Err(CliParseError::missing_flag_value("-d"))
}
_ => return Err(CliParseError::unknown_short_flag(flag.unsafe_to_char()))
}
}
Ok(index + 1)
}
///|
pub fn parse_starshine_input_env(raw : String?) -> Array[String] {
let out = Array::new()
match raw {
Some(text) =>
for part in text.split(",") {
append_input_glob(out, part.to_owned())
}
None => ()
}
out
}
///|
pub fn infer_input_format(path : String) -> CliInputFormat? {
let normalized = normalize_cli_path(path)
let mut last_dot = -1
let mut last_sep = -1
let mut i = 0
while i < normalized.length() {
if normalized[i] == '.' {
last_dot = i
} else if normalized[i] == '/' {
last_sep = i
}
i += 1
}
if last_dot < 0 || last_dot <= last_sep || last_dot + 1 >= normalized.length() {
return None
}
let ext = normalized.sub(start=last_dot + 1).to_owned()
parse_input_format(ext)
}
///|
pub fn cli_config_schema_json() -> String {
let schema =
#|{
#| "schemaVersion": 1,
#| "inputs": {
#| "globs": ["**/*.wasm"],
#| "stdin": false,
#| "format": "wasm"
#| },
#| "outputs": {
#| "stdout": false,
#| "out": null,
#| "outDir": null
#| },
#| "passes": ["validate"],
#| "tracing": "phase",
#| "optimize": {
#| "preset": "optimize",
#| "olevel": "O2",
#| "trapMode": "allow"
#| },
#| "options": {
#| "trapsNeverHappen": false,
#| "ignoreImplicitTraps": false,
#| "monomorphizeMinBenefit": 5,
#| "lowMemoryUnused": false,
#| "lowMemoryBound": 1024,
#| "closedWorld": false
#| }
#|}
schema
}
///|
fn inlining_flag_value(
args : Array[String],
index : Int32,
inline_value : String?,
flag : String,
allow_minus_one~ : Bool = false,
) -> Result[(String, Bool), CliParseError] {
match inline_value {
Some(v) if v.length() > 0 => Ok((v, false))
Some(_) => Err(CliParseError::missing_flag_value(flag))
None =>
if index + 1 < args.length() &&
(
!looks_like_option_token(args[index + 1]) ||
(allow_minus_one && args[index + 1] == "-1")
) {
Ok((args[index + 1], true))
} else {
Err(CliParseError::missing_flag_value(flag))
}
}
}
///|
fn parse_inlining_non_negative_or_minus_one(
value : String,
allow_minus_one~ : Bool = false,
) -> Int32? {
if allow_minus_one && trimmed_string(value) == "-1" {
Some(-1)
} else {
parse_non_negative_decimal(value)
}
}
///|
fn expand_inlining_short_alias(arg : String) -> String {
let aliases : Array[(String, String)] = [
("-aimfs", "--always-inline-max-function-size"),
("-ocimfs", "--one-caller-inline-max-function-size"),
("-fimfs", "--flexible-inline-max-function-size"),
("-imcbs", "--inline-max-combined-binary-size"),
("-ifwl", "--inline-functions-with-loops"),
("-pii", "--partial-inlining-ifs"),
]
for pair in aliases {
let (short, long) = pair
if arg == short {
return long
}
let prefix = "\{short}="
if arg.has_prefix(prefix) {
return "\{long}=\{arg.sub(start=prefix.length()).to_owned()}"
}
}
arg
}
///|
pub fn parse_cli_args(
args : Array[String],
starshine_input~ : String? = None,
) -> Result[CliParseResult, CliParseError] {
let parsed = CliParseResult::new()
let mut index = 0
let mut positional_only = false
while index < args.length() {
let arg = expand_inlining_short_alias(args[index])
if positional_only {
append_input_glob(parsed.input_globs, arg)
index += 1
continue
}
if arg == "--" {
positional_only = true
index += 1
continue
}
if arg.length() > 1 && arg[0] == '-' {
if arg.length() >= 2 && arg[1] == 'O' {
match parse_opt_level_flag(arg) {
Ok(flag) => parsed.optimize_flags.push(flag)
Err(e) => return Err(e)
}
index += 1
continue
}
if arg.length() > 2 && arg[1] == '-' {
let (name, inline_value, valid_kebab_name) = split_long_flag_token(arg)
match name {
"help" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--help"))
}
parsed.help_requested = true
index += 1
}
"version" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--version"))
}
parsed.version_requested = true
index += 1
}
"glob" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--glob"))
}
parsed.glob_enabled = true
index += 1
}
"stdin" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--stdin"))
}
parsed.read_stdin = true
index += 1
}
"format" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(CliParseError::missing_flag_value("--format"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(CliParseError::missing_flag_value("--format"))
}
}
match parse_input_format(value) {
Some(format) => parsed.input_format = Some(format)
None => return Err(CliParseError::invalid_input_format(value))
}
index += if consumed_next { 2 } else { 1 }
}
"config" => {
match inline_value {
Some(v) if v.length() > 0 => parsed.config_path = Some(v)
Some(_) => parsed.config_path = Some(DEFAULT_CONFIG_PATH)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
parsed.config_path = Some(args[index + 1])
index += 1
} else {
parsed.config_path = Some(DEFAULT_CONFIG_PATH)
}
}
index += 1
}
"stdout" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--stdout"))
}
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::stdout(),
)
index += 1
}
"out" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) => return Err(CliParseError::missing_flag_value("--out"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(CliParseError::missing_flag_value("--out"))
}
}
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::file(value),
)
index += if consumed_next { 2 } else { 1 }
}
"out-dir" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(CliParseError::missing_flag_value("--out-dir"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(CliParseError::missing_flag_value("--out-dir"))
}
}
push_unique_output_target(
parsed.output_targets,
CliOutputTarget::dir(value),
)
index += if consumed_next { 2 } else { 1 }
}
"dump" => {
let raw_value = match inline_value {
Some(v) if v.length() > 0 => v
Some(_) => return Err(CliParseError::missing_flag_value("--dump"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
args[index + 1]
} else {
return Err(CliParseError::missing_flag_value("--dump"))
}
}
let dump_flag = match parse_dump_pass_value(raw_value) {
Ok(flag) => flag
Err(err) => return Err(err)
}
parsed.pass_flags.push(dump_flag)
index += if inline_value is None { 2 } else { 1 }
}
"extract-functions" => {
let raw_value = match inline_value {
Some(v) if v.length() > 0 => v
Some(_) =>
return Err(
CliParseError::missing_flag_value("--extract-functions"),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
args[index + 1]
} else {
return Err(
CliParseError::missing_flag_value("--extract-functions"),
)
}
}
let extract_flag = match parse_extract_functions_value(raw_value) {
Ok(flag) => flag
Err(err) => return Err(err)
}
parsed.pass_flags.push(extract_flag)
index += if inline_value is None { 2 } else { 1 }
}
"optimize" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--optimize"))
}
parsed.optimize_flags.push(CliOptimizationFlag::optimize())
parsed.pass_flags.push("optimize")
index += 1
}
"shrink" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--shrink"))
}
parsed.optimize_flags.push(CliOptimizationFlag::shrink())
parsed.pass_flags.push("shrink")
index += 1
}
"optimize-level" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value("--optimize-level"),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value("--optimize-level"),
)
}
}
match parse_non_negative_decimal(value) {
Some(level) =>
parsed.optimize_flags.push(
CliOptimizationFlag::olevel(level, false),
)
None =>
return Err(
CliParseError::invalid_optimization_flag("--optimize-level"),
)
}
index += if consumed_next { 2 } else { 1 }
}
"shrink-level" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(CliParseError::missing_flag_value("--shrink-level"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value("--shrink-level"),
)
}
}
match parse_non_negative_decimal(value) {
Some(level) =>
parsed.optimize_flags.push(
CliOptimizationFlag::olevel(level, true),
)
None =>
return Err(
CliParseError::invalid_optimization_flag("--shrink-level"),
)
}
index += if consumed_next { 2 } else { 1 }
}
"always-inline-max-function-size" => {
let (value, consumed_next) = match
inlining_flag_value(
args, index, inline_value, "--always-inline-max-function-size",
) {
Ok(value) => value
Err(err) => return Err(err)
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.inlining_always_inline_max_size = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--always-inline-max-function-size",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"one-caller-inline-max-function-size" => {
let (value, consumed_next) = match
inlining_flag_value(
args,
index,
inline_value,
"--one-caller-inline-max-function-size",
allow_minus_one=true,
) {
Ok(value) => value
Err(err) => return Err(err)
}
match
parse_inlining_non_negative_or_minus_one(
value,
allow_minus_one=true,
) {
Some(parsed_value) =>
parsed.inlining_one_caller_inline_max_size = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--one-caller-inline-max-function-size",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"flexible-inline-max-function-size" => {
let (value, consumed_next) = match
inlining_flag_value(
args, index, inline_value, "--flexible-inline-max-function-size",
) {
Ok(value) => value
Err(err) => return Err(err)
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.inlining_flexible_inline_max_size = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--flexible-inline-max-function-size",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"inline-max-combined-binary-size" => {
let (value, consumed_next) = match
inlining_flag_value(
args, index, inline_value, "--inline-max-combined-binary-size",
) {
Ok(value) => value
Err(err) => return Err(err)
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.inlining_max_combined_binary_size = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--inline-max-combined-binary-size",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"inline-functions-with-loops" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value(
"--inline-functions-with-loops",
),
)
}
parsed.inlining_allow_functions_with_loops = Some(true)
index += 1
}
"partial-inlining-ifs" => {
let (value, consumed_next) = match
inlining_flag_value(
args, index, inline_value, "--partial-inlining-ifs",
) {
Ok(value) => value
Err(err) => return Err(err)
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.inlining_partial_inlining_ifs = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--partial-inlining-ifs",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"monomorphize-min-benefit" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value(
"--monomorphize-min-benefit",
),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value(
"--monomorphize-min-benefit",
),
)
}
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.monomorphize_min_benefit = Some(parsed_value)
None =>
return Err(
CliParseError::invalid_optimization_flag(
"--monomorphize-min-benefit",
),
)
}
index += if consumed_next { 2 } else { 1 }
}
"low-memory-unused" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--low-memory-unused"),
)
}
parsed.low_memory_unused = Some(true)
index += 1
}
"no-low-memory-unused" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--no-low-memory-unused"),
)
}
parsed.low_memory_unused = Some(false)
index += 1
}
"low-memory-bound" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value("--low-memory-bound"),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value("--low-memory-bound"),
)
}
}
match parse_non_negative_decimal(value) {
Some(parsed_value) =>
parsed.low_memory_bound = Some(parsed_value.to_uint64())
None =>
return Err(
CliParseError::invalid_optimization_flag("--low-memory-bound"),
)
}
index += if consumed_next { 2 } else { 1 }
}
"compiler-facts" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value("--compiler-facts"),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value("--compiler-facts"),
)
}
}
if value != "ignore" && value != "hints" && value != "trust" {
return Err(
CliParseError::invalid_optimization_flag("--compiler-facts"),
)
}
parsed.compiler_facts_policy = Some(value)
index += if consumed_next { 2 } else { 1 }
}
"closed-world" => {
if inline_value is Some(_) {
return Err(CliParseError::unexpected_flag_value("--closed-world"))
}
parsed.closed_world = Some(true)
index += 1
}
"no-closed-world" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--no-closed-world"),
)
}
parsed.closed_world = Some(false)
index += 1
}
"trap-mode" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(CliParseError::missing_flag_value("--trap-mode"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(CliParseError::missing_flag_value("--trap-mode"))
}
}
match parse_trap_mode(value) {
Some(mode) => parsed.trap_mode = Some(mode)
None => return Err(CliParseError::invalid_trap_mode(value))
}
index += if consumed_next { 2 } else { 1 }
}
"traps-never-happen" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--traps-never-happen"),
)
}
parsed.trap_mode = Some(TrapMode::never())
index += 1
}
"traps-may-happen" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--traps-may-happen"),
)
}
parsed.trap_mode = Some(TrapMode::allow())
index += 1
}
"ignore-implicit-traps" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--ignore-implicit-traps"),
)
}
parsed.ignore_implicit_traps = true
index += 1
}
"tracing" => {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(CliParseError::missing_flag_value("--tracing"))
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(CliParseError::missing_flag_value("--tracing"))
}
}
match parse_tracing_level(value) {
Some(level) => parsed.tracing = Some(level)
None => return Err(CliParseError::invalid_tracing_level(value))
}
index += if consumed_next { 2 } else { 1 }
}
"debug-serial-passes" => {
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value("--debug-serial-passes"),
)
}
parsed.debug_serial_passes = true
index += 1
}
_ => {
if !valid_kebab_name {
return Err(CliParseError::invalid_long_flag(name))
}
if name == "no-inline" ||
name == "no-full-inline" ||
name == "no-partial-inline" {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value(long_flag_name(name)),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value(long_flag_name(name)),
)
}
}
parsed.pass_flags.push("\{name}=\{value}")
index += if consumed_next { 2 } else { 1 }
continue
}
if name.has_prefix("print-") {
let (value, consumed_next) = match inline_value {
Some(v) if v.length() > 0 => (v, false)
Some(_) =>
return Err(
CliParseError::missing_flag_value(long_flag_name(name)),
)
None =>
if index + 1 < args.length() &&
!looks_like_option_token(args[index + 1]) {
(args[index + 1], true)
} else {
return Err(
CliParseError::missing_flag_value(long_flag_name(name)),
)
}
}
let print_flag = match parse_print_pass_value(name, value) {
Ok(flag) => flag
Err(err) => return Err(err)
}
parsed.pass_flags.push(print_flag)
index += if consumed_next { 2 } else { 1 }
continue
}
if inline_value is Some(_) {
return Err(
CliParseError::unexpected_flag_value(long_flag_name(name)),
)
}
parsed.pass_flags.push(name)
index += 1
}
}
continue
}
match parse_short_option_cluster(args, index, parsed) {
Ok(next_index) => index = next_index
Err(e) => return Err(e)
}
continue
}
append_input_glob(parsed.input_globs, arg)
index += 1
}
for env_glob in parse_starshine_input_env(starshine_input) {
parsed.input_globs.push(env_glob)
}
if !parsed.help_requested &&
!parsed.version_requested &&
parsed.read_stdin &&
parsed.input_format is None {
return Err(CliParseError::stdin_needs_format())
}
Ok(parsed)
}
///|
pub fn resolve_pass_flags(parsed : CliParseResult) -> Array[String] {
parsed.pass_flags
}
///|
pub fn resolve_traps_never_happen(
parsed : CliParseResult,
default~ : Bool = false,
) -> Bool {
match parsed.trap_mode {
Some(Never) => true
Some(Allow) => false
None => default
}
}
///|
pub fn resolve_ignore_implicit_traps(parsed : CliParseResult) -> Bool {
parsed.ignore_implicit_traps
}
///|
pub fn resolve_closed_world(
parsed : CliParseResult,
default~ : Bool = false,
) -> Bool {
match parsed.closed_world {
Some(value) => value
None => default
}
}
///|
pub impl Show for CliInputFormat with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub impl Show for CliOutputTarget with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub impl Show for CliOptimizationFlag with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub impl Show for TrapMode with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub impl Show for CliTracingLevel with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub impl Show for CliParseResult with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}