work on prelude; update tailcall to deal properly with base fns
This commit is contained in:
parent
c73c7e0d6a
commit
1e3fcde57a
|
@ -24,6 +24,7 @@ pub enum Op {
|
||||||
Pop,
|
Pop,
|
||||||
PopN,
|
PopN,
|
||||||
PushBinding,
|
PushBinding,
|
||||||
|
PushGlobal,
|
||||||
Store,
|
Store,
|
||||||
StoreAt,
|
StoreAt,
|
||||||
Stash,
|
Stash,
|
||||||
|
@ -151,6 +152,7 @@ impl std::fmt::Display for Op {
|
||||||
Pop => "pop",
|
Pop => "pop",
|
||||||
PopN => "pop_n",
|
PopN => "pop_n",
|
||||||
PushBinding => "push_binding",
|
PushBinding => "push_binding",
|
||||||
|
PushGlobal => "push_global",
|
||||||
Store => "store",
|
Store => "store",
|
||||||
StoreAt => "store_at",
|
StoreAt => "store_at",
|
||||||
Stash => "stash",
|
Stash => "stash",
|
||||||
|
@ -253,6 +255,7 @@ pub struct Chunk {
|
||||||
pub bytecode: Vec<u8>,
|
pub bytecode: Vec<u8>,
|
||||||
pub keywords: Vec<&'static str>,
|
pub keywords: Vec<&'static str>,
|
||||||
pub string_patterns: Vec<StrPattern>,
|
pub string_patterns: Vec<StrPattern>,
|
||||||
|
pub env: imbl::HashMap<&'static str, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
|
@ -265,7 +268,7 @@ 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 | Nothing => {
|
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -383,12 +386,14 @@ impl<'a> Compiler<'a> {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
src: &'static str,
|
src: &'static str,
|
||||||
enclosing: Option<&'a Compiler>,
|
enclosing: Option<&'a Compiler>,
|
||||||
|
env: imbl::HashMap<&'static str, Value>,
|
||||||
) -> Compiler<'a> {
|
) -> Compiler<'a> {
|
||||||
let chunk = Chunk {
|
let chunk = Chunk {
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
bytecode: vec![],
|
bytecode: vec![],
|
||||||
keywords: vec![],
|
keywords: vec![],
|
||||||
string_patterns: vec![],
|
string_patterns: vec![],
|
||||||
|
env,
|
||||||
};
|
};
|
||||||
Compiler {
|
Compiler {
|
||||||
chunk,
|
chunk,
|
||||||
|
@ -530,6 +535,31 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_binding(&mut self, name: &'static str) {
|
fn resolve_binding(&mut self, name: &'static str) {
|
||||||
|
if let Some(pos) = self.resolve_local(name) {
|
||||||
|
self.emit_op(Op::PushBinding);
|
||||||
|
self.emit_byte(pos);
|
||||||
|
self.stack_depth += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(pos) = self.resolve_upvalue(name) {
|
||||||
|
self.emit_op(Op::GetUpvalue);
|
||||||
|
self.emit_byte(pos);
|
||||||
|
self.stack_depth += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.chunk.env.contains_key(name) {
|
||||||
|
self.emit_constant(Value::Keyword(name));
|
||||||
|
self.emit_op(Op::PushGlobal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let upvalue = self.get_upvalue(name);
|
||||||
|
self.emit_op(Op::GetUpvalue);
|
||||||
|
self.emit_byte(self.upvalues.len());
|
||||||
|
self.upvalues.push(upvalue);
|
||||||
|
self.stack_depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_binding_old(&mut self, name: &'static str) {
|
||||||
match self.resolve_local(name) {
|
match self.resolve_local(name) {
|
||||||
Some(position) => {
|
Some(position) => {
|
||||||
self.emit_op(Op::PushBinding);
|
self.emit_op(Op::PushBinding);
|
||||||
|
@ -1250,8 +1280,13 @@ impl<'a> Compiler<'a> {
|
||||||
let compiler = match compilers.get_mut(&arity) {
|
let compiler = match compilers.get_mut(&arity) {
|
||||||
Some(compiler) => compiler,
|
Some(compiler) => compiler,
|
||||||
None => {
|
None => {
|
||||||
let mut compiler =
|
let mut compiler = Compiler::new(
|
||||||
Compiler::new(clause, self.name, self.src, Some(self));
|
clause,
|
||||||
|
self.name,
|
||||||
|
self.src,
|
||||||
|
Some(self),
|
||||||
|
self.chunk.env.clone(),
|
||||||
|
);
|
||||||
compiler.emit_op(Op::ResetMatch);
|
compiler.emit_op(Op::ResetMatch);
|
||||||
compilers.insert(arity, compiler);
|
compilers.insert(arity, compiler);
|
||||||
compilers.get_mut(&arity).unwrap()
|
compilers.get_mut(&arity).unwrap()
|
||||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use chumsky::{input::Stream, prelude::*};
|
use chumsky::{input::Stream, prelude::*};
|
||||||
|
use imbl::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
const DEBUG_COMPILE: bool = true;
|
const DEBUG_COMPILE: bool = true;
|
||||||
|
@ -21,10 +22,43 @@ mod compiler;
|
||||||
use crate::compiler::Compiler;
|
use crate::compiler::Compiler;
|
||||||
|
|
||||||
mod value;
|
mod value;
|
||||||
|
use value::Value;
|
||||||
|
|
||||||
mod vm;
|
mod vm;
|
||||||
use vm::Vm;
|
use vm::Vm;
|
||||||
|
|
||||||
|
const PRELUDE: &str = "
|
||||||
|
fn print! (...args) -> base :print! (args)
|
||||||
|
fn inc (x) -> base :inc (x)
|
||||||
|
fn dec (x) -> base :dec (x)
|
||||||
|
fn eq? (x, y) -> base :eq? (x, y)
|
||||||
|
|
||||||
|
#{print!, inc, dec, eq?}
|
||||||
|
";
|
||||||
|
|
||||||
|
pub fn prelude() -> HashMap<&'static str, Value> {
|
||||||
|
let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap();
|
||||||
|
let parsed = parser()
|
||||||
|
.parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s)))
|
||||||
|
.into_output_errors()
|
||||||
|
.0
|
||||||
|
.unwrap();
|
||||||
|
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
|
||||||
|
let mut compiler = Compiler::new(parsed, "prelude", PRELUDE, None, HashMap::new());
|
||||||
|
let base = base::make_base();
|
||||||
|
compiler.emit_constant(base);
|
||||||
|
compiler.bind("base");
|
||||||
|
compiler.compile();
|
||||||
|
|
||||||
|
let chunk = compiler.chunk;
|
||||||
|
let mut vm = Vm::new(chunk);
|
||||||
|
let prelude = vm.run().clone().unwrap();
|
||||||
|
match prelude {
|
||||||
|
Value::Dict(hashmap) => *hashmap,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(src: &'static str) {
|
pub fn run(src: &'static str) {
|
||||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||||
if !lex_errs.is_empty() {
|
if !lex_errs.is_empty() {
|
||||||
|
@ -47,11 +81,9 @@ pub fn run(src: &'static str) {
|
||||||
// in any event, the AST should live forever
|
// in any event, the AST should live forever
|
||||||
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
||||||
|
|
||||||
let base = base::make_base();
|
let prelude = prelude();
|
||||||
|
|
||||||
let mut compiler = Compiler::new(parsed, "test", src, None);
|
let mut compiler = Compiler::new(parsed, "test", src, None, prelude);
|
||||||
compiler.emit_constant(base);
|
|
||||||
compiler.bind("base");
|
|
||||||
|
|
||||||
compiler.compile();
|
compiler.compile();
|
||||||
if DEBUG_COMPILE {
|
if DEBUG_COMPILE {
|
||||||
|
@ -80,8 +112,11 @@ 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 print! (...args) -> base :print! (args)
|
[
|
||||||
print! (:foo, :bar, :baz)
|
eq? (1, 2)
|
||||||
|
eq? (:foo, :foo)
|
||||||
|
inc (3)
|
||||||
|
]
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
}
|
}
|
||||||
|
|
29
src/vm.rs
29
src/vm.rs
|
@ -301,6 +301,15 @@ impl Vm {
|
||||||
self.push(binding_value);
|
self.push(binding_value);
|
||||||
self.ip += 2;
|
self.ip += 2;
|
||||||
}
|
}
|
||||||
|
PushGlobal => {
|
||||||
|
let key = self.pop();
|
||||||
|
let Value::Keyword(name) = key else {
|
||||||
|
unreachable!("internal Ludus error: expected key for global resolution")
|
||||||
|
};
|
||||||
|
let value = self.chunk().env.get(name).unwrap();
|
||||||
|
self.push(value.clone());
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
Store => {
|
Store => {
|
||||||
self.return_register[0] = self.pop();
|
self.return_register[0] = self.pop();
|
||||||
self.push(Value::Nothing);
|
self.push(Value::Nothing);
|
||||||
|
@ -919,10 +928,26 @@ impl Vm {
|
||||||
let value = match (arity, base_fn) {
|
let value = match (arity, base_fn) {
|
||||||
(0, BaseFn::Nullary(f)) => f(),
|
(0, BaseFn::Nullary(f)) => f(),
|
||||||
(1, BaseFn::Unary(f)) => f(&self.pop()),
|
(1, BaseFn::Unary(f)) => f(&self.pop()),
|
||||||
(2, BaseFn::Binary(f)) => f(&self.pop(), &self.pop()),
|
(2, BaseFn::Binary(f)) => {
|
||||||
(3, BaseFn::Ternary(f)) => f(&self.pop(), &self.pop(), &self.pop()),
|
let y = &self.pop();
|
||||||
|
let x = &self.pop();
|
||||||
|
f(x, y)
|
||||||
|
}
|
||||||
|
(3, BaseFn::Ternary(f)) => {
|
||||||
|
let z = &self.pop();
|
||||||
|
let y = &self.pop();
|
||||||
|
let x = &self.pop();
|
||||||
|
f(x, y, z)
|
||||||
|
}
|
||||||
_ => return self.panic("internal ludus error"),
|
_ => return self.panic("internal ludus error"),
|
||||||
};
|
};
|
||||||
|
// algo:
|
||||||
|
// clear the stack
|
||||||
|
self.stack.truncate(self.frame.stack_base);
|
||||||
|
// then pop back out to the enclosing stack frame
|
||||||
|
self.frame = self.call_stack.pop().unwrap();
|
||||||
|
self.ip = self.frame.ip;
|
||||||
|
// finally, throw the value on the stack
|
||||||
self.push(value);
|
self.push(value);
|
||||||
}
|
}
|
||||||
Value::Partial(partial) => {
|
Value::Partial(partial) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user