start work on partial application, fix/abstract binding resolution

This commit is contained in:
Scott Richmond 2025-06-05 16:10:40 -04:00
parent f3bf55fe72
commit dee9bcfc33
6 changed files with 101 additions and 76 deletions

View File

@ -377,6 +377,7 @@ pub fn r#type(x: &Value) -> Value {
Value::Fn(_) => Value::Keyword("fn"), Value::Fn(_) => Value::Keyword("fn"),
Value::Box(_) => Value::Keyword("box"), Value::Box(_) => Value::Keyword("box"),
Value::BaseFn(_) => Value::Keyword("fn"), Value::BaseFn(_) => Value::Keyword("fn"),
Value::Partial(_) => Value::Keyword("fn"),
Value::Nothing => unreachable!(), Value::Nothing => unreachable!(),
} }
} }

View File

@ -13,6 +13,7 @@ use std::rc::Rc;
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
pub enum Op { pub enum Op {
Noop, Noop,
Nothing,
Nil, Nil,
True, True,
False, False,
@ -68,6 +69,7 @@ pub enum Op {
Call, Call,
Return, Return,
Partial,
Eq, Eq,
Add, Add,
@ -129,6 +131,7 @@ impl std::fmt::Display for Op {
use Op::*; use Op::*;
let rep = match self { let rep = match self {
Noop => "noop", Noop => "noop",
Nothing => "nothing",
Nil => "nil", Nil => "nil",
True => "true", True => "true",
False => "false", False => "false",
@ -198,6 +201,7 @@ impl std::fmt::Display for Op {
Call => "call", Call => "call",
Return => "return", Return => "return",
Partial => "partial",
SetUpvalue => "set_upvalue", SetUpvalue => "set_upvalue",
GetUpvalue => "get_upvalue", GetUpvalue => "get_upvalue",
@ -237,21 +241,21 @@ impl Chunk {
| Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList | ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
| ConcatList | PushList | PushDict | AppendDict | ConcatDict => { | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing => {
println!("{i:04}: {op}") println!("{i:04}: {op}")
} }
Constant | MatchConstant => { Constant | MatchConstant => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
let value = &self.constants[next as usize].show(); let value = &self.constants[next as usize].show();
println!("{i:04}: {:16} {next:04}: {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 | MatchDict | LoadDictValue | PushTuple
| PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch
| JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call | SetUpvalue | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call | SetUpvalue
| GetUpvalue => { | GetUpvalue | Partial => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:03}", op.to_string());
*i += 1; *i += 1;
} }
} }
@ -335,6 +339,10 @@ fn is_binding(expr: &Spanned<Ast>) -> bool {
} }
} }
fn has_placeholder(args: &[Spanned<Ast>]) -> bool {
args.iter().any(|arg| matches!(arg, (Ast::Placeholder, _)))
}
impl<'a> Compiler<'a> { impl<'a> Compiler<'a> {
pub fn new( pub fn new(
ast: &'static Spanned<Ast>, ast: &'static Spanned<Ast>,
@ -465,16 +473,43 @@ impl<'a> Compiler<'a> {
} }
} }
fn resolve_binding(&self, name: &'static str) -> usize { fn resolve_binding(&mut self, name: &'static str) {
if let Some(pos) = self.resolve_local(name) { match self.resolve_local(name) {
return pos; Some(position) => {
self.emit_op(Op::PushBinding);
self.emit_byte(position);
self.stack_depth += 1;
} }
match self.enclosing { None => match self.resolve_upvalue(name) {
Some(compiler) => compiler.resolve_binding(name), Some(position) => {
None => unreachable!(), println!("resolved upvalue: {name}");
self.emit_op(Op::GetUpvalue);
self.emit_byte(position);
self.stack_depth += 1;
}
None => {
println!("setting upvalue: {name}");
let upvalue = self.get_upvalue(name);
self.emit_op(Op::GetUpvalue);
self.emit_byte(upvalue.stack_pos);
self.upvalues.push(upvalue);
dbg!(&self.upvalues);
self.stack_depth += 1;
}
},
} }
} }
// fn resolve_binding(&self, name: &'static str) -> usize {
// if let Some(pos) = self.resolve_local(name) {
// return pos;
// }
// match self.enclosing {
// Some(compiler) => compiler.resolve_binding(name),
// None => unreachable!(),
// }
// }
fn pop(&mut self) { fn pop(&mut self) {
println!("Popping from: {}", self.ast); println!("Popping from: {}", self.ast);
self.emit_op(Op::Pop); self.emit_op(Op::Pop);
@ -599,30 +634,7 @@ impl<'a> Compiler<'a> {
self.emit_op(Op::Match); self.emit_op(Op::Match);
self.bind(name); self.bind(name);
} }
Word(name) | Splat(name) => match self.resolve_local(name) { Word(name) | Splat(name) => self.resolve_binding(name),
Some(position) => {
self.emit_op(Op::PushBinding);
self.emit_byte(position);
self.stack_depth += 1;
}
None => match self.resolve_upvalue(name) {
Some(position) => {
println!("resolved upvalue: {name}");
self.emit_op(Op::GetUpvalue);
self.emit_byte(position);
self.stack_depth += 1;
}
None => {
println!("setting upvalue: {name}");
let upvalue = self.get_upvalue(name);
self.emit_op(Op::GetUpvalue);
self.emit_byte(upvalue.stack_pos);
self.upvalues.push(upvalue);
dbg!(&self.upvalues);
self.stack_depth += 1;
}
},
},
PlaceholderPattern => { PlaceholderPattern => {
self.emit_op(Op::Match); self.emit_op(Op::Match);
} }
@ -797,14 +809,6 @@ impl<'a> Compiler<'a> {
} }
} }
} }
// Dict(pairs) => {
// for pair in pairs {
// self.visit(pair);
// }
// self.emit_op(Op::PushDict);
// self.emit_byte(pairs.len());
// self.stack_depth = self.stack_depth + 1 - (pairs.len() * 2);
// }
Pair(key, value) => { Pair(key, value) => {
self.emit_constant(Value::Keyword(key)); self.emit_constant(Value::Keyword(key));
self.visit(value); self.visit(value);
@ -863,7 +867,18 @@ impl<'a> Compiler<'a> {
} }
self.stack_depth = stack_depth + 1; self.stack_depth = stack_depth + 1;
} }
(Word(fn_name), Arguments(args)) => match get_builtin(fn_name, args.len()) { (Word(fn_name), Arguments(args)) => {
if has_placeholder(args) {
let arity = args.len();
for arg in args {
self.visit(arg);
}
self.resolve_binding(fn_name);
self.emit_op(Op::Partial);
self.emit_byte(arity);
self.stack_depth -= 1;
} else {
match get_builtin(fn_name, args.len()) {
Some(code) => { Some(code) => {
for arg in args { for arg in args {
self.visit(arg); self.visit(arg);
@ -876,21 +891,14 @@ impl<'a> Compiler<'a> {
for arg in args { for arg in args {
self.visit(arg); self.visit(arg);
} }
self.resolve_binding(fn_name);
self.emit_op(Op::PushBinding);
self.stack_depth += 1;
// let biter = self.bindings.iter().rev();
for binding in self.bindings.iter() {
if binding.name == *fn_name {
self.emit_byte(binding.stack_pos);
break;
}
}
self.emit_op(Op::Call); self.emit_op(Op::Call);
self.emit_byte(arity); self.emit_byte(arity);
self.stack_depth -= arity; self.stack_depth -= arity;
} }
}, }
}
}
_ => unreachable!(), _ => unreachable!(),
} }
for (term, _) in rest { for (term, _) in rest {
@ -1275,14 +1283,7 @@ impl<'a> Compiler<'a> {
self.stack_depth -= 1; self.stack_depth -= 1;
} }
StringPart::Word(word) => { StringPart::Word(word) => {
self.emit_op(Op::PushBinding); self.resolve_binding(word);
let biter = self.bindings.iter().rev();
for binding in biter {
if binding.name == word.as_str() {
self.emit_byte(binding.stack_pos);
break;
}
}
self.emit_op(Op::Stringify); self.emit_op(Op::Stringify);
self.emit_op(Op::ConcatStrings); self.emit_op(Op::ConcatStrings);
} }
@ -1301,6 +1302,9 @@ impl<'a> Compiler<'a> {
self.stack_depth -= 1; self.stack_depth -= 1;
} }
} }
Placeholder => {
self.emit_op(Op::Nothing);
}
Arguments(..) | Placeholder | InterpolatedPattern(..) | Splattern(..) => { Arguments(..) | Placeholder | InterpolatedPattern(..) | Splattern(..) => {
todo!() todo!()
} }

View File

@ -76,10 +76,7 @@ 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 = "
let x = #{:a 1, :b 2}
let y = #{x}
let z = #{...x, y}
z :y :x :a
"; ";
run(src); run(src);

View File

@ -1,3 +1,7 @@
// TODO:
// * [ ] ensure `or` and `and` never get passed by reference
// * [ ] ensure no placeholder in `or` and `and`
use crate::parser::*; use crate::parser::*;
use crate::spans::{Span, Spanned}; use crate::spans::{Span, Spanned};
use crate::value::Value; use crate::value::Value;

View File

@ -66,6 +66,13 @@ impl LFn {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub struct Partial {
args: Vec<Value>,
name: &'static str,
function: Value,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Value { pub enum Value {
Nothing, Nothing,
@ -82,6 +89,7 @@ pub enum Value {
Box(Rc<RefCell<Value>>), Box(Rc<RefCell<Value>>),
Fn(Rc<LFn>), Fn(Rc<LFn>),
BaseFn(BaseFn), BaseFn(BaseFn),
Partial(Rc<Partial>),
} }
impl PartialEq for Value { impl PartialEq for Value {
@ -100,6 +108,7 @@ impl PartialEq for Value {
(Box(x), Box(y)) => std::ptr::eq(x.as_ref().as_ptr(), y.as_ref().as_ptr()), (Box(x), Box(y)) => std::ptr::eq(x.as_ref().as_ptr(), y.as_ref().as_ptr()),
(Fn(x), Fn(y)) => x == y, (Fn(x), Fn(y)) => x == y,
(BaseFn(x), BaseFn(y)) => std::ptr::eq(x, y), (BaseFn(x), BaseFn(y)) => std::ptr::eq(x, y),
(Partial(x), Partial(y)) => x == y,
_ => false, _ => false,
} }
} }
@ -147,6 +156,7 @@ 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.name()), Fn(lfn) => write!(f, "fn {}", lfn.name()),
BaseFn(_) => write!(f, "base fn"), BaseFn(_) => write!(f, "base fn"),
Partial(partial) => write!(f, "fn {}/partial", partial.name),
} }
} }
} }
@ -187,6 +197,7 @@ impl Value {
} }
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()), Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()),
Fn(lfn) => format!("fn {}", lfn.name()), Fn(lfn) => format!("fn {}", lfn.name()),
Partial(partial) => format!("fn {}/partial", partial.name),
BaseFn(_) => "base fn".to_string(), BaseFn(_) => "base fn".to_string(),
Nothing => unreachable!(), Nothing => unreachable!(),
} }
@ -253,6 +264,7 @@ impl Value {
Box(..) => "box", Box(..) => "box",
Fn(..) => "fn", Fn(..) => "fn",
BaseFn(..) => "fn", BaseFn(..) => "fn",
Partial(..) => "fn",
} }
} }
} }

View File

@ -186,6 +186,10 @@ impl Vm {
self.push(Value::Nil); self.push(Value::Nil);
self.ip += 1; self.ip += 1;
} }
Nothing => {
self.push(Value::Nothing);
self.ip += 1;
}
True => { True => {
self.push(Value::True); self.push(Value::True);
self.ip += 1; self.ip += 1;
@ -700,6 +704,9 @@ impl Vm {
self.push(stringified); self.push(stringified);
self.ip += 1; self.ip += 1;
} }
Partial => {
todo!();
}
Call => { Call => {
let arity = self.chunk().bytecode[self.ip + 1]; let arity = self.chunk().bytecode[self.ip + 1];
self.ip += 2; self.ip += 2;