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::Box(_) => Value::Keyword("box"),
Value::BaseFn(_) => Value::Keyword("fn"),
Value::Partial(_) => Value::Keyword("fn"),
Value::Nothing => unreachable!(),
}
}

View File

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

View File

@ -76,10 +76,7 @@ pub fn run(src: &'static str) {
pub fn main() {
env::set_var("RUST_BACKTRACE", "1");
let src = "
let x = #{:a 1, :b 2}
let y = #{x}
let z = #{...x, y}
z :y :x :a
";
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::spans::{Span, Spanned};
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)]
pub enum Value {
Nothing,
@ -82,6 +89,7 @@ pub enum Value {
Box(Rc<RefCell<Value>>),
Fn(Rc<LFn>),
BaseFn(BaseFn),
Partial(Rc<Partial>),
}
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()),
(Fn(x), Fn(y)) => x == y,
(BaseFn(x), BaseFn(y)) => std::ptr::eq(x, y),
(Partial(x), Partial(y)) => x == y,
_ => false,
}
}
@ -147,6 +156,7 @@ impl std::fmt::Display for Value {
Box(value) => write!(f, "box {{ {} }}", value.as_ref().borrow()),
Fn(lfn) => write!(f, "fn {}", lfn.name()),
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()),
Fn(lfn) => format!("fn {}", lfn.name()),
Partial(partial) => format!("fn {}/partial", partial.name),
BaseFn(_) => "base fn".to_string(),
Nothing => unreachable!(),
}
@ -253,6 +264,7 @@ impl Value {
Box(..) => "box",
Fn(..) => "fn",
BaseFn(..) => "fn",
Partial(..) => "fn",
}
}
}

View File

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