mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Critical fixes
This commit is contained in:
+148
@@ -684,6 +684,36 @@ fn (mut v Vm) native(id int, _argc int) ! {
|
||||
native_spawn_join {
|
||||
v.push(v.native_spawn_join()!)!
|
||||
}
|
||||
native_read_line {
|
||||
line := os.input_opt('') or { '' } // EOF → empty string
|
||||
v.push(v.alloc_str(line))!
|
||||
}
|
||||
native_input {
|
||||
prompt := v.pop_str()!
|
||||
eprint(prompt) // prompt on stderr so stdout stays clean for piping
|
||||
line := os.input_opt('') or { '' }
|
||||
v.push(v.alloc_str(line))!
|
||||
}
|
||||
native_flag_val {
|
||||
name := v.pop_str()!
|
||||
v.ensure_flags()
|
||||
v.push(v.alloc_str(v.flag_lookup(name)))!
|
||||
}
|
||||
native_flag_has {
|
||||
name := v.pop_str()!
|
||||
v.ensure_flags()
|
||||
v.push(v.enc_int(if v.flag_present_key(name) { 1 } else { 0 }))!
|
||||
}
|
||||
native_flag_positional {
|
||||
v.ensure_flags()
|
||||
mut arr := []i64{}
|
||||
for s in v.flags.positionals {
|
||||
v.strings << s
|
||||
arr << v.mkstr(v.strings.len - 1)
|
||||
}
|
||||
v.arrays << arr
|
||||
v.push(v.mkarr(v.arrays.len - 1))!
|
||||
}
|
||||
native_cwd {
|
||||
v.push(v.alloc_str(os.getwd()))!
|
||||
}
|
||||
@@ -1083,6 +1113,124 @@ fn (mut v Vm) pop_str() !string {
|
||||
return v.strings[v.hand(x)]
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getopt-style flag parsing over the program's args.
|
||||
//
|
||||
// Flags are parsed once (lazily) into v.flags, and every flag / positional
|
||||
// query reads from that shared result, so a token consumed as a flag value is
|
||||
// never also reported as a positional. Supported forms: --name value,
|
||||
// --name=value, boolean --name, single-letter short aliases (-n) derived from
|
||||
// the long name's first character, and `--` to stop flag parsing. Negative
|
||||
// numbers like -5 are treated as values, not flags. The first occurrence of a
|
||||
// flag wins.
|
||||
fn (mut v Vm) ensure_flags() {
|
||||
if v.flags.parsed {
|
||||
return
|
||||
}
|
||||
v.flags = FlagArgs{ parsed: true }
|
||||
mut consumed_value := false // the current flag took a separate value token
|
||||
mut i := 0
|
||||
for i < v.prog_args.len {
|
||||
a := v.prog_args[i]
|
||||
if a == '--' {
|
||||
// everything after -- is positional
|
||||
v.flags.positionals << v.prog_args[i + 1..]
|
||||
break
|
||||
}
|
||||
if !v.flag_token(a) {
|
||||
// positional, unless it was already consumed as a flag's value below
|
||||
v.flags.positionals << a
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// a flag token. Only long flags (--name) consume a separate value token;
|
||||
// short flags (-v) are treated as boolean to avoid swallowing a following
|
||||
// positional (use -n=val or --name val for short aliases needing a value).
|
||||
name, val, has_inline := v.flag_key_val(a)
|
||||
long_flag := a.starts_with('--')
|
||||
if name !in v.flags.vals {
|
||||
if has_inline {
|
||||
v.flags.vals[name] = val
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if long_flag && i + 1 < v.prog_args.len && !v.flag_token(v.prog_args[i + 1]) {
|
||||
v.flags.vals[name] = v.prog_args[i + 1]
|
||||
i += 2
|
||||
} else {
|
||||
v.flags.vals[name] = 'true'
|
||||
i++
|
||||
}
|
||||
continue
|
||||
}
|
||||
// duplicate flag: keep the first value, but still consume its value
|
||||
if long_flag && !has_inline && i + 1 < v.prog_args.len && !v.flag_token(v.prog_args[i + 1]) {
|
||||
i += 2
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flag_token reports whether a is parsed as a flag rather than a value.
|
||||
// flag_lookup returns the value of flag `name`, accepting its single-letter
|
||||
// short alias as well. Returns '' when the flag was not passed.
|
||||
fn (mut v Vm) flag_lookup(name string) string {
|
||||
if name in v.flags.vals {
|
||||
return v.flags.vals[name]
|
||||
}
|
||||
if name.len > 1 {
|
||||
short := name[..1]
|
||||
if short in v.flags.vals {
|
||||
return v.flags.vals[short]
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// flag_present_key reports whether flag `name` (or its short alias) was passed
|
||||
// at all, regardless of its value.
|
||||
fn (mut v Vm) flag_present_key(name string) bool {
|
||||
if name in v.flags.vals {
|
||||
return true
|
||||
}
|
||||
if name.len > 1 {
|
||||
return name[..1] in v.flags.vals
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (mut v Vm) flag_token(a string) bool {
|
||||
if a == '-' {
|
||||
return false
|
||||
}
|
||||
if !a.starts_with('-') {
|
||||
return false
|
||||
}
|
||||
// a negative number (-5, -3.14) is a value, not a flag
|
||||
if a.len > 1 && a[1] >= `0` && a[1] <= `9` {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// flag_key_val splits a flag token into its canonical (dash-stripped) name and
|
||||
// any inline value. Returns (name, value, has_inline_value).
|
||||
fn (mut v Vm) flag_key_val(a string) (string, string, bool) {
|
||||
raw := a.trim_left('-') // strip leading dashes
|
||||
mut eq := -1
|
||||
for i in 0..raw.len {
|
||||
if raw[i] == `=` {
|
||||
eq = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if eq >= 0 {
|
||||
return raw[..eq], raw[eq + 1..], true
|
||||
}
|
||||
return raw, '', false
|
||||
}
|
||||
|
||||
// type_name returns the type label of a tagged value.
|
||||
fn (mut v Vm) type_name(x i64) string {
|
||||
if v.is_str(x) {
|
||||
|
||||
@@ -181,3 +181,12 @@ const native_sb_len = 192
|
||||
// concurrency: spawn/join threads
|
||||
const native_spawn = 193
|
||||
const native_spawn_join = 194
|
||||
|
||||
// interactive input: read a line from stdin
|
||||
const native_read_line = 195
|
||||
const native_input = 196
|
||||
|
||||
// getopt-style flag parsing over args()
|
||||
const native_flag_val = 197
|
||||
const native_flag_has = 198
|
||||
const native_flag_positional = 199
|
||||
|
||||
@@ -4,6 +4,18 @@ module vm
|
||||
import math
|
||||
|
||||
fn (mut v Vm) add(a i64, b i64) !i64 {
|
||||
// String + any: coerce the other operand to its string form so that
|
||||
// `"caught: " + e`, `"n=" + count`, and record/array debugging "just
|
||||
// work" the way users expect when building messages.
|
||||
if v.is_str(a) && !v.is_str(b) {
|
||||
return v.alloc_str(v.strings[v.hand(a)] + v.val_str(b, 0))
|
||||
}
|
||||
if v.is_str(b) && !v.is_str(a) {
|
||||
return v.alloc_str(v.val_str(a, 0) + v.strings[v.hand(b)])
|
||||
}
|
||||
if v.is_str(a) && v.is_str(b) {
|
||||
return v.alloc_str(v.strings[v.hand(a)] + v.strings[v.hand(b)])
|
||||
}
|
||||
if v.is_none(a) || v.is_none(b) {
|
||||
return error('cannot use none with +')
|
||||
}
|
||||
@@ -13,15 +25,6 @@ fn (mut v Vm) add(a i64, b i64) !i64 {
|
||||
if v.is_struct(a) || v.is_struct(b) {
|
||||
return error('cannot add structs with +')
|
||||
}
|
||||
if v.is_str(a) && v.is_str(b) {
|
||||
return v.alloc_str(v.strings[v.hand(a)] + v.strings[v.hand(b)])
|
||||
}
|
||||
if v.is_str(a) {
|
||||
return v.alloc_str(v.strings[v.hand(a)] + v.num_str(b))
|
||||
}
|
||||
if v.is_str(b) {
|
||||
return v.alloc_str(v.num_str(a) + v.strings[v.hand(b)])
|
||||
}
|
||||
if v.is_float(a) || v.is_float(b) {
|
||||
return v.push_float(v.to_f64(a) + v.to_f64(b))
|
||||
}
|
||||
|
||||
+14
@@ -99,6 +99,20 @@ mut:
|
||||
fn_of_ip []int // code offset -> function index (for profiling)
|
||||
max_ops i64 // instruction budget; 0 = unlimited (fuzzing safety)
|
||||
ops i64 // instructions executed so far
|
||||
flags FlagArgs // cached getopt parse of prog_args (lazy)
|
||||
}
|
||||
|
||||
// FlagArgs is the parsed view of the command-line arguments, computed lazily
|
||||
// and shared by flag_val / flag_has / flag_positional so that a value consumed
|
||||
// as a flag value is never also returned as a positional.
|
||||
struct FlagArgs {
|
||||
// parsed indicates the parse is only valid when built against the same
|
||||
// argv length as prog_args currently has (args never change at runtime).
|
||||
mut:
|
||||
parsed bool
|
||||
vals map[string]string // canonical flag name -> value ('true' for boolean w/o inline)
|
||||
ordered []string // flag names in first-appearance order
|
||||
positionals []string // non-flag arguments not consumed as flag values
|
||||
}
|
||||
|
||||
fn bool_i64(b bool) i64 {
|
||||
|
||||
Reference in New Issue
Block a user