keywords and interned strings use &'static str instead of indexes into vecs

This commit is contained in:
Scott Richmond 2025-06-03 16:23:37 -04:00
parent 681176282c
commit 86992078e9
4 changed files with 96 additions and 117 deletions

View File

@ -219,7 +219,7 @@ impl Chunk {
} }
Constant | MatchConstant => { Constant | MatchConstant => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
let value = &self.constants[next as usize].show(self); let value = &self.constants[next as usize].show();
println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
*i += 1; *i += 1;
} }
@ -242,9 +242,9 @@ impl Chunk {
} }
} }
pub fn kw_from(&self, kw: &str) -> Option<Value> { // pub fn kw_from(&self, kw: &str) -> Option<Value> {
self.kw_index_from(kw).map(Value::Keyword) // self.kw_index_from(kw).map(Value::Keyword)
} // }
pub fn kw_index_from(&self, kw: &str) -> Option<usize> { pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
self.keywords.iter().position(|s| *s == kw) self.keywords.iter().position(|s| *s == kw)
@ -334,13 +334,13 @@ impl Compiler {
} }
} }
pub fn kw_from(&self, kw: &str) -> Option<Value> { // pub fn kw_from(&self, kw: &str) -> Option<Value> {
self.kw_index_from(kw).map(Value::Keyword) // self.kw_index_from(kw).map(Value::Keyword)
} // }
pub fn kw_index_from(&self, kw: &str) -> Option<usize> { // pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
self.chunk.keywords.iter().position(|s| *s == kw) // self.chunk.keywords.iter().position(|s| *s == kw)
} // }
pub fn visit(&mut self, node: &'static Spanned<Ast>) { pub fn visit(&mut self, node: &'static Spanned<Ast>) {
let root_node = self.ast; let root_node = self.ast;
@ -353,31 +353,36 @@ impl Compiler {
self.span = root_span; self.span = root_span;
} }
fn emit_keyword(&mut self, s: &'static str) { // fn emit_keyword(&mut self, s: &'static str) {
let existing_kw = self.chunk.keywords.iter().position(|kw| *kw == s); // // let existing_kw = self.chunk.keywords.iter().position(|kw| *kw == s);
let kw_index = match existing_kw { // // let kw_index = match existing_kw {
Some(index) => index, // // Some(index) => index,
None => self.chunk.keywords.len(), // // None => self.chunk.keywords.len(),
}; // // };
if kw_index == self.chunk.keywords.len() { // // if kw_index == self.chunk.keywords.len() {
self.chunk.keywords.push(s); // // self.chunk.keywords.push(s);
} // // }
self.emit_constant(Value::Keyword(kw_index)); // self.emit_constant(Value::Keyword(s));
} // }
fn emit_constant(&mut self, val: Value) { fn emit_constant(&mut self, val: Value) {
let constant_index = self.chunk.constants.len(); let const_idx = if let Some(idx) = self.chunk.constants.iter().position(|v| *v == val) {
if constant_index > u8::MAX as usize { idx
} else {
self.chunk.constants.push(val);
self.chunk.constants.len() - 1
};
if const_idx > u8::MAX as usize {
panic!( panic!(
"internal Ludus compiler error: too many constants in chunk:{}:: {}", "internal Ludus compiler error: too many constants in chunk:{}:: {}",
self.span, self.ast self.span, self.ast
) )
} }
self.chunk.constants.push(val);
self.emit_op(Op::Constant); self.emit_op(Op::Constant);
// self.chunk.bytecode.push(Op::Constant as u8); // self.chunk.bytecode.push(Op::Constant as u8);
// self.spans.push(self.span); // self.spans.push(self.span);
self.emit_byte(constant_index); self.emit_byte(const_idx);
// self.chunk.bytecode.push(constant_index as u8); // self.chunk.bytecode.push(constant_index as u8);
// self.spans.push(self.span); // self.spans.push(self.span);
self.stack_depth += 1; self.stack_depth += 1;
@ -386,7 +391,10 @@ impl Compiler {
fn match_constant(&mut self, val: Value) { fn match_constant(&mut self, val: Value) {
let constant_index = match self.chunk.constants.iter().position(|v| *v == val) { let constant_index = match self.chunk.constants.iter().position(|v| *v == val) {
Some(idx) => idx, Some(idx) => idx,
None => self.chunk.constants.len(), None => {
self.chunk.constants.push(val);
self.chunk.constants.len() - 1
}
}; };
if constant_index > u8::MAX as usize { if constant_index > u8::MAX as usize {
panic!( panic!(
@ -394,15 +402,8 @@ impl Compiler {
self.span, self.ast self.span, self.ast
) )
} }
if constant_index == self.chunk.constants.len() {
self.chunk.constants.push(val);
}
self.emit_op(Op::MatchConstant); self.emit_op(Op::MatchConstant);
self.emit_byte(constant_index); self.emit_byte(constant_index);
// self.chunk.bytecode.push(Op::MatchConstant as u8);
// self.spans.push(self.span);
// self.chunk.bytecode.push(constant_index as u8);
// self.spans.push(self.span);
} }
fn emit_op(&mut self, op: Op) { fn emit_op(&mut self, op: Op) {
@ -476,15 +477,15 @@ impl Compiler {
self.stack_depth += 1; self.stack_depth += 1;
} }
String(s) => { String(s) => {
let existing_str = self.chunk.strings.iter().position(|e| e == s); // let existing_str = self.chunk.strings.iter().position(|e| e == s);
let str_index = match existing_str { // let str_index = match existing_str {
Some(idx) => idx, // Some(idx) => idx,
None => self.chunk.strings.len(), // None => self.chunk.strings.len(),
}; // };
self.chunk.strings.push(s); // self.chunk.strings.push(s);
self.emit_constant(Value::Interned(str_index)); self.emit_constant(Value::Interned(s));
} }
Keyword(s) => self.emit_keyword(s), Keyword(s) => self.emit_constant(Value::Keyword(s)),
Block(lines) => { Block(lines) => {
self.scope_depth += 1; self.scope_depth += 1;
let stack_depth = self.stack_depth; let stack_depth = self.stack_depth;
@ -589,24 +590,24 @@ impl Compiler {
if kw_index == self.chunk.keywords.len() { if kw_index == self.chunk.keywords.len() {
self.chunk.keywords.push(s); self.chunk.keywords.push(s);
} }
self.match_constant(Value::Keyword(kw_index)); self.match_constant(Value::Keyword(s));
} }
AsPattern(word, typ) => { AsPattern(word, typ) => {
self.emit_constant(self.chunk.kw_from(typ).unwrap()); self.emit_constant(Value::Keyword(typ));
self.emit_op(Op::MatchType); self.emit_op(Op::MatchType);
self.stack_depth -= 1; self.stack_depth -= 1;
self.bind(word); self.bind(word);
} }
StringPattern(s) => { StringPattern(s) => {
let existing_str = self.chunk.strings.iter().position(|e| e == s); // let existing_str = self.chunk.strings.iter().position(|e| e == s);
let str_index = match existing_str { // let str_index = match existing_str {
Some(idx) => idx, // Some(idx) => idx,
None => self.chunk.strings.len(), // None => self.chunk.strings.len(),
}; // };
if str_index == self.chunk.strings.len() { // if str_index == self.chunk.strings.len() {
self.chunk.strings.push(s) // self.chunk.strings.push(s)
} // }
self.match_constant(Value::Interned(str_index)); self.match_constant(Value::Interned(s));
} }
TuplePattern(members) => { TuplePattern(members) => {
self.emit_op(Op::MatchTuple); self.emit_op(Op::MatchTuple);
@ -696,7 +697,7 @@ impl Compiler {
}; };
// algo: // algo:
// push the keyword onto the stack // push the keyword onto the stack
self.emit_keyword(key); self.emit_constant(Value::Keyword(key));
// pull the value out of the dict // pull the value out of the dict
self.emit_op(Op::LoadDictValue); self.emit_op(Op::LoadDictValue);
self.emit_byte(dict_stack_pos); self.emit_byte(dict_stack_pos);
@ -759,15 +760,15 @@ impl Compiler {
); );
} }
Pair(key, value) => { Pair(key, value) => {
let existing_kw = self.chunk.keywords.iter().position(|kw| kw == key); // let existing_kw = self.chunk.keywords.iter().position(|kw| kw == key);
let kw_index = match existing_kw { // let kw_index = match existing_kw {
Some(index) => index, // Some(index) => index,
None => self.chunk.keywords.len(), // None => self.chunk.keywords.len(),
}; // };
if kw_index == self.chunk.keywords.len() { // if kw_index == self.chunk.keywords.len() {
self.chunk.keywords.push(key); // self.chunk.keywords.push(key);
} // }
self.emit_constant(Value::Keyword(kw_index)); self.emit_constant(Value::Keyword(key));
self.visit(value); self.visit(value);
} }
Synthetic(first, second, rest) => { Synthetic(first, second, rest) => {

View File

@ -61,14 +61,12 @@ pub fn run(src: &'static str) {
println!("=== vm run: test ==="); println!("=== vm run: test ===");
} }
// TODO: investigate lifeteims and remove this clone
let show_chunk = compiler.chunk.clone();
let vm_chunk = compiler.chunk; let vm_chunk = compiler.chunk;
let mut vm = Vm::new(vm_chunk); let mut vm = Vm::new(vm_chunk);
let result = vm.run(); let result = vm.run();
let output = match result { let output = match result {
Ok(val) => val.show(&show_chunk), Ok(val) => val.show(),
Err(panic) => format!("Ludus panicked! {panic}"), Err(panic) => format!("Ludus panicked! {panic}"),
}; };
vm.print_stack(); vm.print_stack();
@ -78,9 +76,9 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
env::set_var("RUST_BACKTRACE", "1"); env::set_var("RUST_BACKTRACE", "1");
let src = " let src = "
fn foo (_) -> [1, 2, 3] fn foo (_) -> :foo
foo (1) foo (nil)
"; ";
run(src); run(src);
} }

View File

@ -29,14 +29,13 @@ pub enum Value {
Nil, Nil,
True, True,
False, False,
Keyword(usize), Keyword(&'static str),
Interned(usize), Interned(&'static str),
FnDecl(usize),
String(Rc<String>), String(Rc<String>),
Number(f64), Number(f64),
Tuple(Rc<Vec<Value>>), Tuple(Rc<Vec<Value>>),
List(Box<Vector<Value>>), List(Box<Vector<Value>>),
Dict(Box<HashMap<usize, Value>>), Dict(Box<HashMap<&'static str, Value>>),
Box(Rc<RefCell<Value>>), Box(Rc<RefCell<Value>>),
Fn(Rc<OnceCell<LFn>>), Fn(Rc<OnceCell<LFn>>),
} }
@ -49,8 +48,8 @@ impl std::fmt::Display for Value {
Nil => write!(f, "nil"), Nil => write!(f, "nil"),
True => write!(f, "true"), True => write!(f, "true"),
False => write!(f, "false"), False => write!(f, "false"),
Keyword(idx) => write!(f, ":{idx}"), Keyword(str) => write!(f, ":{str}"),
Interned(idx) => write!(f, "\"@{idx}\""), Interned(str) => write!(f, "\"{str}\""),
String(str) => write!(f, "\"{str}\""), String(str) => write!(f, "\"{str}\""),
Number(n) => write!(f, "{n}"), Number(n) => write!(f, "{n}"),
Tuple(members) => write!( Tuple(members) => write!(
@ -82,76 +81,63 @@ impl std::fmt::Display for Value {
), ),
Box(value) => write!(f, "box {}", value.as_ref().borrow()), Box(value) => write!(f, "box {}", value.as_ref().borrow()),
Fn(lfn) => write!(f, "fn {}", lfn.get().unwrap().name), Fn(lfn) => write!(f, "fn {}", lfn.get().unwrap().name),
_ => todo!(),
} }
} }
} }
impl Value { impl Value {
pub fn show(&self, ctx: &Chunk) -> String { pub fn show(&self) -> String {
use Value::*; use Value::*;
match &self { match &self {
Nil => "nil".to_string(), Nil => "nil".to_string(),
True => "true".to_string(), True => "true".to_string(),
False => "false".to_string(), False => "false".to_string(),
Number(n) => format!("{n}"), Number(n) => format!("{n}"),
Interned(i) => { Interned(str) => format!("\"{str}\""),
let str_str = ctx.strings[*i];
format!("\"{str_str}\"")
}
String(str) => { String(str) => {
let str_str = str.to_string(); let str_str = str.to_string();
format!("\"{str_str}\"") format!("\"{str_str}\"")
} }
Keyword(i) => { Keyword(str) => format!(":{str}"),
let kw_str = ctx.keywords[*i];
format!(":{kw_str}")
}
Tuple(t) => { Tuple(t) => {
let members = t.iter().map(|e| e.show(ctx)).collect::<Vec<_>>().join(", "); let members = t.iter().map(|e| e.show()).collect::<Vec<_>>().join(", ");
format!("({members})") format!("({members})")
} }
List(l) => { List(l) => {
let members = l.iter().map(|e| e.show(ctx)).collect::<Vec<_>>().join(", "); let members = l.iter().map(|e| e.show()).collect::<Vec<_>>().join(", ");
format!("[{members}]") format!("[{members}]")
} }
Dict(d) => { Dict(d) => {
let members = d let members = d
.iter() .iter()
.map(|(k, v)| { .map(|(k, v)| {
let key_show = Value::Keyword(*k).show(ctx); let key_show = Value::Keyword(k).show();
let value_show = v.show(ctx); let value_show = v.show();
format!("{key_show} {value_show}") format!("{key_show} {value_show}")
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!("#{{{members}}}") format!("#{{{members}}}")
} }
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)), Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()),
Fn(lfn) => format!("fn {}", lfn.get().unwrap().name), Fn(lfn) => format!("fn {}", lfn.get().unwrap().name),
_ => todo!(), _ => todo!(),
} }
} }
pub fn stringify(&self, ctx: &Chunk) -> String { pub fn stringify(&self) -> String {
use Value::*; use Value::*;
match &self { match &self {
Nil => "nil".to_string(), Nil => "nil".to_string(),
True => "true".to_string(), True => "true".to_string(),
False => "false".to_string(), False => "false".to_string(),
Number(n) => format!("{n}"), Number(n) => format!("{n}"),
Interned(i) => { Interned(str) => str.to_string(),
let str_str = ctx.strings[*i]; Keyword(str) => str.to_string(),
str_str.to_string()
}
Keyword(i) => {
let kw_str = ctx.keywords[*i];
format!(":{kw_str}")
}
Tuple(t) => { Tuple(t) => {
let members = t let members = t
.iter() .iter()
.map(|e| e.stringify(ctx)) .map(|e| e.stringify())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
members.to_string() members.to_string()
@ -159,7 +145,7 @@ impl Value {
List(l) => { List(l) => {
let members = l let members = l
.iter() .iter()
.map(|e| e.stringify(ctx)) .map(|e| e.stringify())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
members.to_string() members.to_string()
@ -168,8 +154,8 @@ impl Value {
let members = d let members = d
.iter() .iter()
.map(|(k, v)| { .map(|(k, v)| {
let key_show = Value::Keyword(*k).show(ctx); let key_show = Value::Keyword(k).stringify();
let value_show = v.show(ctx); let value_show = v.stringify();
format!("{key_show} {value_show}") format!("{key_show} {value_show}")
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -177,7 +163,7 @@ impl Value {
members.to_string() members.to_string()
} }
String(s) => s.as_ref().clone(), String(s) => s.as_ref().clone(),
Box(x) => x.as_ref().borrow().stringify(ctx), Box(x) => x.as_ref().borrow().stringify(),
Fn(lfn) => lfn.get().unwrap().name.to_string(), Fn(lfn) => lfn.get().unwrap().name.to_string(),
_ => todo!(), _ => todo!(),
} }
@ -192,7 +178,6 @@ impl Value {
False => "bool", False => "bool",
Keyword(..) => "keyword", Keyword(..) => "keyword",
Interned(..) => "string", Interned(..) => "string",
FnDecl(..) => "fn",
String(..) => "string", String(..) => "string",
Number(..) => "number", Number(..) => "number",
Tuple(..) => "tuple", Tuple(..) => "tuple",

View File

@ -300,9 +300,11 @@ impl Vm {
} }
MatchType => { MatchType => {
let as_type = self.pop(); let as_type = self.pop();
let Value::Keyword(as_type) = as_type else {
unreachable!()
};
let idx = self.stack.len() - self.match_depth as usize - 1; let idx = self.stack.len() - self.match_depth as usize - 1;
let val_type = self.stack[idx].type_of(); let val_type = self.stack[idx].type_of();
let val_type = self.chunk().kw_from(val_type).unwrap();
self.matches = val_type == as_type; self.matches = val_type == as_type;
self.ip += 1; self.ip += 1;
} }
@ -473,7 +475,7 @@ impl Vm {
} }
TypeOf => { TypeOf => {
let val = self.pop(); let val = self.pop();
let type_of = self.chunk().kw_from(val.type_of()).unwrap(); let type_of = Value::Keyword(val.type_of());
self.push(type_of); self.push(type_of);
self.ip += 1; self.ip += 1;
} }
@ -626,7 +628,7 @@ impl Vm {
self.ip += 1; self.ip += 1;
} }
Panic => { Panic => {
let msg = self.pop().show(self.chunk()); let msg = self.pop().show();
return self.panic_with(msg); return self.panic_with(msg);
} }
EmptyString => { EmptyString => {
@ -650,7 +652,7 @@ impl Vm {
} }
Stringify => { Stringify => {
let to_stringify = self.pop(); let to_stringify = self.pop();
let the_string = to_stringify.stringify(self.chunk()); let the_string = to_stringify.stringify();
let stringified = Value::String(Rc::new(the_string)); let stringified = Value::String(Rc::new(the_string));
self.push(stringified); self.push(stringified);
self.ip += 1; self.ip += 1;
@ -661,8 +663,7 @@ impl Vm {
let val = self.pop(); let val = self.pop();
let Value::Fn(_) = val else { let Value::Fn(_) = val else {
return self return self.panic_with(format!("{} is not a function", val.show()));
.panic_with(format!("{} is not a function", val.show(self.chunk())));
}; };
let mut frame = CallFrame { let mut frame = CallFrame {
@ -679,18 +680,12 @@ impl Vm {
self.ip = 0; self.ip = 0;
if crate::DEBUG_RUN { if crate::DEBUG_RUN {
println!( println!("== calling into {} ==", self.frame.function.show());
"== calling into {} ==",
self.frame.function.show(self.chunk())
);
} }
} }
Return => { Return => {
if crate::DEBUG_RUN { if crate::DEBUG_RUN {
println!( println!("== returning from {} ==", self.frame.function.show())
"== returning from {} ==",
self.frame.function.show(self.chunk())
)
} }
self.frame = self.call_stack.pop().unwrap(); self.frame = self.call_stack.pop().unwrap();
self.ip = self.frame.ip; self.ip = self.frame.ip;