Add list_append/list_set/list_concat/str_set_char/shell_exec/native_tty_write builtins
Required for Neuron CLI 2.0 el implementation: - list_append(list, item) — append item to list - list_set(list, idx, value) — replace item at index - list_concat(a, b) — concatenate two lists - list_slice(list, start, end) — extract sublist - list_reverse(list) — reverse list - list_contains(list, item) — membership test - str_set_char(s, idx, ch) — replace single character - shell_exec(cmd) — execute shell command, return stdout - native_tty_write(s) — write raw ANSI string without newline - native_tty_size() — return "cols,rows" string
This commit is contained in:
+110
-1
@@ -5738,6 +5738,109 @@ fn dispatch_builtin(
|
||||
BuiltinResult::NotBuiltin
|
||||
}
|
||||
|
||||
// ── List helpers (append / set / concat) ──────────────────────────────
|
||||
"list_append" => {
|
||||
// list_append(list, item) -> [item, ...]
|
||||
let item = stack.pop().unwrap_or(Value::Nil);
|
||||
let list = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let mut new_list = list;
|
||||
new_list.push(item);
|
||||
stack.push(Value::List(new_list)); BuiltinResult::Handled
|
||||
}
|
||||
"list_set" => {
|
||||
// list_set(list, idx, value) -> new list with item at idx replaced
|
||||
let value = stack.pop().unwrap_or(Value::Nil);
|
||||
let idx = match stack.pop().unwrap_or(Value::Int(0)) { Value::Int(i) => i, _ => 0 };
|
||||
let list = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let mut new_list = list;
|
||||
if idx >= 0 && (idx as usize) < new_list.len() {
|
||||
new_list[idx as usize] = value;
|
||||
}
|
||||
stack.push(Value::List(new_list)); BuiltinResult::Handled
|
||||
}
|
||||
"list_concat" => {
|
||||
// list_concat(a, b) -> combined list
|
||||
let b = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let a = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let mut new_list = a;
|
||||
new_list.extend(b);
|
||||
stack.push(Value::List(new_list)); BuiltinResult::Handled
|
||||
}
|
||||
"list_slice" => {
|
||||
// list_slice(list, start, end) -> sublist [start..end)
|
||||
let end_idx = match stack.pop().unwrap_or(Value::Int(0)) { Value::Int(i) => i, _ => 0 };
|
||||
let start_idx = match stack.pop().unwrap_or(Value::Int(0)) { Value::Int(i) => i, _ => 0 };
|
||||
let list = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let len = list.len() as i64;
|
||||
let s = start_idx.max(0) as usize;
|
||||
let e = end_idx.min(len) as usize;
|
||||
let sliced = if s < e && s < list.len() { list[s..e.min(list.len())].to_vec() } else { vec![] };
|
||||
stack.push(Value::List(sliced)); BuiltinResult::Handled
|
||||
}
|
||||
"list_reverse" => {
|
||||
let list = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let mut rev = list;
|
||||
rev.reverse();
|
||||
stack.push(Value::List(rev)); BuiltinResult::Handled
|
||||
}
|
||||
"list_contains" => {
|
||||
// list_contains(list, item) -> Bool — string equality check
|
||||
let item = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, other => other.to_string() };
|
||||
let list = match stack.pop().unwrap_or(Value::List(vec![])) { Value::List(l) => l, _ => vec![] };
|
||||
let found = list.iter().any(|v| {
|
||||
let vs = match v { Value::Str(s) => s.clone(), other => other.to_string() };
|
||||
vs == item
|
||||
});
|
||||
stack.push(Value::Bool(found)); BuiltinResult::Handled
|
||||
}
|
||||
|
||||
// ── String character mutation ──────────────────────────────────────────
|
||||
"str_set_char" => {
|
||||
// str_set_char(s, idx, ch) -> new string with ch at position idx
|
||||
let ch = match stack.pop().unwrap_or(Value::Str(String::new())) { Value::Str(s) => s, _ => String::new() };
|
||||
let idx = match stack.pop().unwrap_or(Value::Int(0)) { Value::Int(i) => i, _ => 0 };
|
||||
let s = match stack.pop().unwrap_or(Value::Str(String::new())) { Value::Str(s) => s, _ => String::new() };
|
||||
let ch_char = ch.chars().next().unwrap_or(' ');
|
||||
let mut chars: Vec<char> = s.chars().collect();
|
||||
if idx >= 0 && (idx as usize) < chars.len() {
|
||||
chars[idx as usize] = ch_char;
|
||||
}
|
||||
stack.push(Value::Str(chars.into_iter().collect())); BuiltinResult::Handled
|
||||
}
|
||||
|
||||
// ── Shell execution ───────────────────────────────────────────────────
|
||||
"shell_exec" => {
|
||||
// shell_exec(cmd: String) -> String — execute shell command, return stdout+stderr
|
||||
let cmd = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => String::new() };
|
||||
let output = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
.output();
|
||||
let result = match output {
|
||||
Ok(o) => {
|
||||
let stdout = String::from_utf8_lossy(&o.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&o.stderr).to_string();
|
||||
if stdout.is_empty() { stderr } else { stdout }
|
||||
}
|
||||
Err(e) => format!("error: {}", e),
|
||||
};
|
||||
stack.push(Value::Str(result)); BuiltinResult::Handled
|
||||
}
|
||||
|
||||
// ── TTY write (raw ANSI output without newline) ───────────────────────
|
||||
"native_tty_write" => {
|
||||
let s = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, other => other.to_string() };
|
||||
use std::io::Write;
|
||||
print!("{}", s);
|
||||
let _ = std::io::stdout().flush();
|
||||
stack.push(Value::Nil); BuiltinResult::Handled
|
||||
}
|
||||
"native_tty_size" => {
|
||||
// Returns "cols,rows" string
|
||||
let (cols, rows) = term_size();
|
||||
stack.push(Value::Str(format!("{},{}", cols, rows))); BuiltinResult::Handled
|
||||
}
|
||||
|
||||
// ── Stack aliases ─────────────────────────────────────────────────────
|
||||
"stack_push" => {
|
||||
let item = stack.pop().unwrap_or(Value::Nil);
|
||||
@@ -6448,10 +6551,16 @@ fn is_builtin(name: &str) -> bool {
|
||||
| "str_pad_left" | "str_pad_right" | "format_float" | "str_format"
|
||||
// List additions
|
||||
| "list_push" | "list_pop" | "list_pop_front" | "list_peek_last" | "list_range"
|
||||
| "list_new" | "list_empty"
|
||||
| "list_new" | "list_empty" | "list_append" | "list_set" | "list_concat"
|
||||
| "list_slice" | "list_reverse" | "list_contains"
|
||||
| "stack_push" | "stack_pop" | "stack_peek" | "stack_new"
|
||||
| "queue_enqueue" | "queue_dequeue" | "queue_peek" | "queue_new"
|
||||
| "list_map" | "list_filter" | "list_reduce" | "fn_ref"
|
||||
// String character mutation
|
||||
| "str_set_char"
|
||||
// Shell and TTY
|
||||
| "shell_exec" | "native_tty_write" | "native_tty_size"
|
||||
| "native_tty_read" // reserved for future raw key input
|
||||
// Time
|
||||
| "time_now_utc" | "time_to_parts" | "time_from_parts" | "time_format" | "time_parse"
|
||||
| "time_add" | "time_diff" | "time_start_of" | "time_tz_offset" | "time_to_tz"
|
||||
|
||||
Reference in New Issue
Block a user