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

View File

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

View File

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

View File

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