Compare commits

...

4 Commits

Author SHA1 Message Date
Scott Richmond
a4d68fa02e first draft of list splatterns 2025-06-19 12:18:09 -04:00
Scott Richmond
2b95094eca first stab at splatted lists 2025-06-19 12:06:47 -04:00
Scott Richmond
4871dbd048 add match splatted list opcode 2025-06-19 11:54:26 -04:00
Scott Richmond
647f3d4463 update punch list 2025-06-19 11:48:50 -04:00
4 changed files with 63 additions and 20 deletions

View File

@ -240,11 +240,16 @@ println!("{a} // {b_high}/{b_low} // {c}");
``` ```
To reiterate the punch list that *I would have needed for Computer Class 1*: To reiterate the punch list that *I would have needed for Computer Class 1*:
* [ ] jump instructions need 16 bits of operand * [x] jump instructions need 16 bits of operand
- Whew, that took longer than I expected
* [ ] splatterns * [ ] splatterns
- [ ] validator should ensure splatterns are the longest patterns in a form - [ ] validator should ensure splatterns are the longest patterns in a form
* [ ] add guards to loop forms * [ ] improve validator
* [ ] check loop forms against function calls: do they still work the way we want them to? - [ ] Tuples may not be longer than n members
- [ ] Loops may not have splatterns
- [ ] Identify others
* [x] add guards to loop forms
* [x] check loop forms against function calls: do they still work the way we want them to?
* [ ] tail call elimination * [ ] tail call elimination
* [ ] stack traces in panics * [ ] stack traces in panics
* [ ] actually good error messages * [ ] actually good error messages

View File

@ -42,7 +42,9 @@ pub enum Op {
PushTuple, PushTuple,
LoadTuple, LoadTuple,
MatchList, MatchList,
MatchSplattedList,
LoadList, LoadList,
LoadSplattedList,
PushList, PushList,
AppendList, AppendList,
ConcatList, ConcatList,
@ -162,7 +164,9 @@ impl std::fmt::Display for Op {
PushTuple => "push_tuple", PushTuple => "push_tuple",
LoadTuple => "load_tuple", LoadTuple => "load_tuple",
MatchList => "match_list", MatchList => "match_list",
MatchSplattedList => "match_splatted_list",
LoadList => "load_list", LoadList => "load_list",
LoadSplattedList => "load_splatted_list",
PushList => "push_list", PushList => "push_list",
AppendList => "append_list", AppendList => "append_list",
ConcatList => "concat_list", ConcatList => "concat_list",
@ -260,9 +264,9 @@ impl Chunk {
println!("{i:04}: {:16} {next:03}: {value}", op.to_string()); println!("{i:04}: {:16} {next:03}: {value}", op.to_string());
*i += 1; *i += 1;
} }
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple PushBinding | MatchTuple | MatchList | MatchSplattedList | LoadSplattedList
| PushBox | MatchDepth | PopN | StoreAt | Call | SetUpvalue | GetUpvalue | Partial | MatchDict | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreAt
| MatchString | PushStringMatches => { | Call | SetUpvalue | GetUpvalue | Partial | MatchString | PushStringMatches => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
println!("{i:04}: {:16} {next:03}", op.to_string()); println!("{i:04}: {:16} {next:03}", op.to_string());
*i += 1; *i += 1;
@ -778,7 +782,14 @@ impl<'a> Compiler<'a> {
self.match_depth = match_depth + members.len(); self.match_depth = match_depth + members.len();
} }
ListPattern(members) => { ListPattern(members) => {
self.emit_op(Op::MatchList); let mut is_splatted = false;
if let Some((Splattern(_), _)) = members.last() {
is_splatted = true;
self.emit_op(Op::MatchSplattedList)
} else {
self.emit_op(Op::MatchList);
}
// TODO: lists must be able to be longer than 256 elements; fix this
self.emit_byte(members.len()); self.emit_byte(members.len());
let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch); let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch);
@ -786,7 +797,12 @@ impl<'a> Compiler<'a> {
let match_depth = self.match_depth; let match_depth = self.match_depth;
self.match_depth = members.len(); self.match_depth = members.len();
self.emit_op(Op::LoadList); if is_splatted {
self.emit_op(Op::LoadSplattedList);
self.emit_byte(members.len());
} else {
self.emit_op(Op::LoadList);
}
self.stack_depth += members.len(); self.stack_depth += members.len();
for member in members { for member in members {
@ -841,8 +857,8 @@ impl<'a> Compiler<'a> {
self.patch_jump(before_load_dict_idx, self.len() - before_load_dict_idx - 3); self.patch_jump(before_load_dict_idx, self.len() - before_load_dict_idx - 3);
self.patch_jump(jump_idx, self.len() - jump_idx - 3); self.patch_jump(jump_idx, self.len() - jump_idx - 3);
} }
Splattern(..) => { Splattern(patt) => {
todo!() self.visit(patt);
} }
InterpolatedPattern(parts, _) => { InterpolatedPattern(parts, _) => {
println!("An interpolated pattern of {} parts", parts.len()); println!("An interpolated pattern of {} parts", parts.len());

View File

@ -75,10 +75,8 @@ 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 = "
loop (1) with { let [x, ...y] = [1, 2, 3, 4, 5]
(0) -> :done y
(1) -> recur (0) ";
}
";
run(src); run(src);
} }

View File

@ -458,27 +458,51 @@ impl Vm {
} }
MatchList => { MatchList => {
let idx = self.stack.len() - self.match_depth as usize - 1; let idx = self.stack.len() - self.match_depth as usize - 1;
let tuple_len = self.chunk().bytecode[self.ip + 1]; let list_len = self.chunk().bytecode[self.ip + 1];
let scrutinee = self.stack[idx].clone(); let scrutinee = self.stack[idx].clone();
match scrutinee { match scrutinee {
Value::List(members) => self.matches = members.len() == tuple_len as usize, Value::List(members) => self.matches = members.len() == list_len as usize,
_ => self.matches = false, _ => self.matches = false,
}; };
self.ip += 2; self.ip += 2;
} }
MatchSplattedList => {
let idx = self.stack.len() - self.match_depth as usize - 1;
let patt_len = self.chunk().bytecode[self.ip + 1];
let scrutinee = self.stack[idx].clone();
match scrutinee {
Value::List(members) => self.matches = members.len() >= patt_len as usize,
_ => self.matches = false,
}
self.ip += 2;
}
LoadList => { LoadList => {
let idx = self.stack.len() - self.match_depth as usize - 1; let idx = self.stack.len() - self.match_depth as usize - 1;
let tuple = self.stack[idx].clone(); let list = self.stack[idx].clone();
match tuple { match list {
Value::List(members) => { Value::List(members) => {
for member in members.iter() { for member in members.iter() {
self.push(member.clone()); self.push(member.clone());
} }
} }
_ => return self.panic("internal error: expected tuple"), _ => return self.panic("internal error: expected list"),
}; };
self.ip += 1; self.ip += 1;
} }
LoadSplattedList => {
let loaded_len = self.chunk().bytecode[self.ip + 1] as usize;
let idx = self.stack.len() - self.match_depth as usize - 1;
let list = self.stack[idx].clone();
let Value::List(members) = list else {
return self.panic("internal error: expected list");
};
for i in 0..loaded_len - 1 {
self.push(members[i].clone());
}
let splatted = Value::List(Box::new(members.skip(loaded_len - 1)));
self.push(splatted);
self.ip += 2;
}
// PushDict => { // PushDict => {
// let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2; // let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2;
// let dict_members = self.stack.split_off(self.stack.len() - dict_len); // let dict_members = self.stack.split_off(self.stack.len() - dict_len);