Compare commits
4 Commits
7431cbf380
...
b2e08fe207
Author | SHA1 | Date | |
---|---|---|---|
|
b2e08fe207 | ||
|
229470fee3 | ||
|
ecc7b26b66 | ||
|
5f154fe56f |
46
src/main.rs
46
src/main.rs
|
@ -63,8 +63,11 @@ use crate::process::*;
|
|||
#[folder = "assets/"]
|
||||
struct Asset;
|
||||
|
||||
pub fn prelude<'src>() -> Process<'src> {
|
||||
let prelude = Asset::get("prelude.ld").unwrap().data.into_owned();
|
||||
pub fn prelude<'src>() -> (
|
||||
Vec<(String, Value<'src>)>,
|
||||
std::collections::HashMap<*const Ast, FnInfo>,
|
||||
) {
|
||||
let prelude = Asset::get("test_prelude.ld").unwrap().data.into_owned();
|
||||
// we know for sure Prelude should live through the whole run of the program
|
||||
let leaked = Box::leak(Box::new(prelude));
|
||||
let prelude = std::str::from_utf8(leaked).unwrap();
|
||||
|
@ -87,19 +90,20 @@ pub fn prelude<'src>() -> Process<'src> {
|
|||
panic!();
|
||||
}
|
||||
|
||||
let (p_ast, p_span) = Box::leak(Box::new(p_ast.unwrap()));
|
||||
let prelude_parsed = Box::leak(Box::new(p_ast.unwrap()));
|
||||
let base_pkg = base();
|
||||
|
||||
let base_names = base_pkg.iter().map(|binding| binding.0.clone()).collect();
|
||||
|
||||
let mut v6or = Validator::new(p_ast, *p_span, &base_names);
|
||||
let mut v6or = Validator::new(&prelude_parsed.0, prelude_parsed.1, &base_names);
|
||||
v6or.validate();
|
||||
|
||||
// dbg!(&v6or);
|
||||
|
||||
let mut base_ctx = Process::<'src> {
|
||||
locals: base_pkg,
|
||||
ast: p_ast,
|
||||
ast: &prelude_parsed.0,
|
||||
span: prelude_parsed.1,
|
||||
prelude: vec![],
|
||||
fn_info: v6or.fn_info,
|
||||
};
|
||||
|
@ -125,12 +129,7 @@ pub fn prelude<'src>() -> Process<'src> {
|
|||
}
|
||||
};
|
||||
|
||||
Process {
|
||||
locals: vec![],
|
||||
ast: &Ast::Nil,
|
||||
prelude: p_ctx,
|
||||
fn_info: base_ctx.fn_info,
|
||||
}
|
||||
(p_ctx, base_ctx.fn_info)
|
||||
}
|
||||
|
||||
pub fn run(src: &'static str) {
|
||||
|
@ -150,21 +149,28 @@ pub fn run(src: &'static str) {
|
|||
return;
|
||||
}
|
||||
|
||||
let (ast, span) = parse_result.unwrap();
|
||||
let parsed = parse_result.unwrap();
|
||||
|
||||
let dummy_prelude = vec![];
|
||||
|
||||
let mut v6or = Validator::new(&ast, span, &dummy_prelude);
|
||||
let mut v6or = Validator::new(&parsed.0, parsed.1, &dummy_prelude);
|
||||
|
||||
v6or.validate();
|
||||
|
||||
// dbg!(&v6or);
|
||||
// dbg!(&v6or.fn_info);
|
||||
|
||||
let mut proc = prelude();
|
||||
proc.ast = *
|
||||
// dbg!(&proc.fn_info);
|
||||
proc.fn_info.extend(&mut v6or.fn_info.into_iter());
|
||||
let (prelude_ctx, mut prelude_fn_info) = prelude();
|
||||
|
||||
prelude_fn_info.extend(&mut v6or.fn_info.into_iter());
|
||||
|
||||
let mut proc = Process {
|
||||
locals: vec![],
|
||||
prelude: prelude_ctx,
|
||||
ast: &parsed.0,
|
||||
span: parsed.1,
|
||||
fn_info: prelude_fn_info,
|
||||
};
|
||||
|
||||
let result = proc.eval();
|
||||
|
||||
|
@ -176,7 +182,11 @@ pub fn run(src: &'static str) {
|
|||
|
||||
pub fn main() {
|
||||
let src = "
|
||||
|
||||
let guard = :nil
|
||||
match :foo with {
|
||||
:foo if guard -> :guarded
|
||||
_ -> :unguarded
|
||||
}
|
||||
";
|
||||
run(src);
|
||||
// struct_scalpel::print_dissection_info::<value::Value>()
|
||||
|
|
188
src/process.rs
188
src/process.rs
|
@ -3,6 +3,7 @@ use crate::parser::*;
|
|||
use crate::spans::*;
|
||||
use crate::validator::FnInfo;
|
||||
use crate::value::Value;
|
||||
use chumsky::prelude::SimpleSpan;
|
||||
use imbl::HashMap;
|
||||
use imbl::Vector;
|
||||
use std::cell::RefCell;
|
||||
|
@ -35,6 +36,7 @@ pub struct Process<'src> {
|
|||
pub locals: Vec<(String, Value<'src>)>,
|
||||
pub prelude: Vec<(String, Value<'src>)>,
|
||||
pub ast: &'src Ast,
|
||||
pub span: SimpleSpan,
|
||||
pub fn_info: std::collections::HashMap<*const Ast, FnInfo>,
|
||||
}
|
||||
|
||||
|
@ -58,12 +60,6 @@ impl<'src> Process<'src> {
|
|||
self.locals.push((word, value.clone()));
|
||||
}
|
||||
|
||||
pub fn pop_to(&mut self, n: usize) {
|
||||
while self.locals.len() > n {
|
||||
self.locals.pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_eq<T>(&self, x: T, y: T) -> Option<&Process<'src>>
|
||||
where
|
||||
T: PartialEq,
|
||||
|
@ -132,7 +128,7 @@ impl<'src> Process<'src> {
|
|||
let list = Value::List(list);
|
||||
self.match_pattern(&patt.0, &list);
|
||||
} else if self.match_pattern(&x[i].0, &y[i]).is_none() {
|
||||
self.pop_to(to);
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +145,7 @@ impl<'src> Process<'src> {
|
|||
let list = Value::List(y.skip(i));
|
||||
self.match_pattern(&patt.0, &list);
|
||||
} else if self.match_pattern(patt, y.get(i).unwrap()).is_none() {
|
||||
self.pop_to(to);
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +166,7 @@ impl<'src> Process<'src> {
|
|||
PairPattern(key, patt) => {
|
||||
if let Some(val) = y.get(key) {
|
||||
if self.match_pattern(&patt.0, val).is_none() {
|
||||
self.pop_to(to);
|
||||
self.locals.truncate(to);
|
||||
return None;
|
||||
} else {
|
||||
matched.push(key);
|
||||
|
@ -208,31 +204,23 @@ impl<'src> Process<'src> {
|
|||
clauses: &'src [Spanned<Ast>],
|
||||
) -> LResult<'src> {
|
||||
{
|
||||
let parent = self.ast;
|
||||
let root = self.ast;
|
||||
let to = self.locals.len();
|
||||
let mut clauses = clauses.iter();
|
||||
while let Some((Ast::MatchClause(patt, guard, body), _)) = clauses.next() {
|
||||
if self.match_pattern(&patt.0, value).is_some() {
|
||||
let pass_guard = match guard.as_ref() {
|
||||
None => true,
|
||||
Some((ast, _)) => {
|
||||
self.ast = ast;
|
||||
let guard_res = self.eval();
|
||||
match &guard_res {
|
||||
Err(_) => return guard_res,
|
||||
Ok(val) => val.bool(),
|
||||
}
|
||||
}
|
||||
Some(guard_expr) => self.visit(guard_expr)?.bool(),
|
||||
};
|
||||
if !pass_guard {
|
||||
self.pop_to(to);
|
||||
self.locals.truncate(to);
|
||||
continue;
|
||||
}
|
||||
self.ast = &body.0;
|
||||
let res = self.eval();
|
||||
self.pop_to(to);
|
||||
self.ast = parent;
|
||||
return res;
|
||||
let result = self.visit(body);
|
||||
self.locals.truncate(to);
|
||||
self.ast = root;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Err(LErr::new("no match".to_string()))
|
||||
|
@ -315,10 +303,17 @@ impl<'src> Process<'src> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn visit(&mut self, node: &'src Spanned<Ast>) -> LResult<'src> {
|
||||
let (expr, span) = node;
|
||||
self.ast = expr;
|
||||
self.span = *span;
|
||||
self.eval()
|
||||
}
|
||||
|
||||
pub fn eval(&mut self) -> LResult<'src> {
|
||||
use Ast::*;
|
||||
let root = self.ast;
|
||||
let result = match self.ast {
|
||||
let (root_node, root_span) = (self.ast, self.span);
|
||||
let result = match root_node {
|
||||
Nil => Ok(Value::Nil),
|
||||
Boolean(b) => Ok(Value::Boolean(*b)),
|
||||
Number(n) => Ok(Value::Number(*n)),
|
||||
|
@ -339,56 +334,42 @@ impl<'src> Process<'src> {
|
|||
Ok(Value::AllocatedString(Rc::new(interpolated)))
|
||||
}
|
||||
Block(exprs) => {
|
||||
let parent = self.ast;
|
||||
let to = self.locals.len();
|
||||
let mut result = Value::Nil;
|
||||
for (expr, _) in exprs {
|
||||
self.ast = expr;
|
||||
result = self.eval()?;
|
||||
for expr in exprs {
|
||||
result = self.visit(expr)?;
|
||||
}
|
||||
self.pop_to(to);
|
||||
self.ast = parent;
|
||||
self.locals.truncate(to);
|
||||
Ok(result)
|
||||
}
|
||||
If(cond, if_true, if_false) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &cond.0;
|
||||
let truthy = self.eval()?.bool();
|
||||
self.ast = if truthy { &if_true.0 } else { &if_false.0 };
|
||||
let result = self.eval();
|
||||
self.ast = parent;
|
||||
result
|
||||
let truthy = self.visit(cond)?;
|
||||
let to_visit = if truthy.bool() { if_true } else { if_false };
|
||||
self.visit(to_visit)
|
||||
}
|
||||
List(members) => {
|
||||
let parent = self.ast;
|
||||
let mut vect = Vector::new();
|
||||
for member in members {
|
||||
self.ast = &member.0;
|
||||
if let Ast::Splat(_) = self.ast {
|
||||
let to_splat = self.eval()?;
|
||||
match to_splat {
|
||||
let member_value = self.visit(member)?;
|
||||
match member.0 {
|
||||
Ast::Splat(_) => match member_value {
|
||||
Value::List(list) => vect.append(list),
|
||||
_ => {
|
||||
return Err(LErr::new(
|
||||
"only lists may be splatted into lists".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vect.push_back(self.eval()?)
|
||||
},
|
||||
_ => vect.push_back(member_value),
|
||||
}
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(Value::List(vect))
|
||||
}
|
||||
Tuple(members) => {
|
||||
let parent = self.ast;
|
||||
let mut vect = Vec::new();
|
||||
for member in members {
|
||||
self.ast = &member.0;
|
||||
vect.push(self.eval()?);
|
||||
vect.push(self.visit(member)?);
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(Value::Tuple(Rc::new(vect)))
|
||||
}
|
||||
Word(w) | Ast::Splat(w) => {
|
||||
|
@ -396,47 +377,35 @@ impl<'src> Process<'src> {
|
|||
Ok(val)
|
||||
}
|
||||
Let(patt, expr) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &expr.0;
|
||||
let val = self.eval()?;
|
||||
let val = self.visit(expr)?;
|
||||
let result = match self.match_pattern(&patt.0, &val) {
|
||||
Some(_) => Ok(val),
|
||||
None => Err(LErr::new("no match".to_string())),
|
||||
};
|
||||
self.ast = parent;
|
||||
result
|
||||
}
|
||||
Placeholder => Ok(Value::Placeholder),
|
||||
Arguments(a) => {
|
||||
let parent = self.ast;
|
||||
let mut args = vec![];
|
||||
for (arg, _) in a.iter() {
|
||||
self.ast = arg;
|
||||
let arg = self.eval()?;
|
||||
args.push(arg);
|
||||
for arg in a.iter() {
|
||||
args.push(self.visit(arg)?)
|
||||
}
|
||||
let result = if args.iter().any(|arg| matches!(arg, Value::Placeholder)) {
|
||||
Ok(Value::Args(Rc::new(args)))
|
||||
} else {
|
||||
Ok(Value::Tuple(Rc::new(args)))
|
||||
};
|
||||
self.ast = parent;
|
||||
result
|
||||
}
|
||||
Dict(terms) => {
|
||||
let parent = self.ast;
|
||||
let mut dict = HashMap::new();
|
||||
for term in terms {
|
||||
let (term, _) = term;
|
||||
match term {
|
||||
Ast::Pair(key, value) => {
|
||||
self.ast = &value.0;
|
||||
let value = self.eval()?;
|
||||
dict.insert(*key, value);
|
||||
(Ast::Pair(key, value), _) => {
|
||||
dict.insert(*key, self.visit(value)?);
|
||||
}
|
||||
Ast::Splat(_) => {
|
||||
self.ast = term;
|
||||
let resolved = self.eval()?;
|
||||
(Ast::Splat(_), _) => {
|
||||
let resolved = self.visit(term)?;
|
||||
let Value::Dict(to_splat) = resolved else {
|
||||
return Err(LErr::new(
|
||||
"cannot splat non-dict into dict".to_string(),
|
||||
|
@ -447,63 +416,42 @@ impl<'src> Process<'src> {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(Value::Dict(dict))
|
||||
}
|
||||
LBox(name, expr) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &expr.0;
|
||||
let val = self.eval()?;
|
||||
let val = self.visit(expr)?;
|
||||
let boxed = Value::Box(name, Rc::new(RefCell::new(val)));
|
||||
self.bind(name.to_string(), &boxed);
|
||||
self.ast = parent;
|
||||
Ok(boxed)
|
||||
}
|
||||
Synthetic(root, first, rest) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &root.0;
|
||||
let root = self.eval()?;
|
||||
self.ast = &first.0;
|
||||
let first = self.eval()?;
|
||||
let mut curr = self.apply(root, first)?;
|
||||
let root_val = self.visit(root)?;
|
||||
let first_val = self.visit(first)?;
|
||||
let mut curr = self.apply(root_val, first_val)?;
|
||||
for term in rest.iter() {
|
||||
self.ast = &term.0;
|
||||
let next = self.eval()?;
|
||||
let next = self.visit(term)?;
|
||||
curr = self.apply(curr, next)?;
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(curr)
|
||||
}
|
||||
When(clauses) => {
|
||||
let parent = self.ast;
|
||||
for clause in clauses.iter() {
|
||||
let WhenClause(cond, body) = &clause.0 else {
|
||||
unreachable!()
|
||||
};
|
||||
self.ast = &cond.0;
|
||||
if self.eval()?.bool() {
|
||||
self.ast = &body.0;
|
||||
let result = self.eval();
|
||||
self.ast = parent;
|
||||
return result;
|
||||
if self.visit(cond)?.bool() {
|
||||
return self.visit(body);
|
||||
};
|
||||
}
|
||||
Err(LErr::new("no match".to_string()))
|
||||
}
|
||||
Match(value, clauses) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &value.0;
|
||||
let value = self.eval()?;
|
||||
let result = self.match_clauses(&value, clauses);
|
||||
self.ast = parent;
|
||||
result
|
||||
Match(scrutinee, clauses) => {
|
||||
let value = self.visit(scrutinee)?;
|
||||
self.match_clauses(&value, clauses)
|
||||
}
|
||||
Fn(name, clauses, doc) => {
|
||||
let doc = doc.map(|s| s.to_string());
|
||||
let ptr: *const Ast = root;
|
||||
// dbg!(&root);
|
||||
// dbg!(&ptr);
|
||||
// dbg!(&self.fn_info);
|
||||
let ptr: *const Ast = root_node;
|
||||
let info = self.fn_info.get(&ptr).unwrap();
|
||||
let FnInfo::Defined(_, _, enclosing) = info else {
|
||||
unreachable!()
|
||||
|
@ -524,64 +472,50 @@ impl<'src> Process<'src> {
|
|||
}
|
||||
FnDeclaration(_name) => Ok(Value::Nil),
|
||||
Panic(msg) => {
|
||||
self.ast = &msg.0;
|
||||
let msg = self.eval()?;
|
||||
let msg = self.visit(msg)?;
|
||||
Err(LErr::new(format!("{msg}")))
|
||||
}
|
||||
Repeat(times, body) => {
|
||||
let parent = self.ast;
|
||||
self.ast = ×.0;
|
||||
let times_num = match self.eval() {
|
||||
let times_num = match self.visit(times) {
|
||||
Ok(Value::Number(n)) => n as usize,
|
||||
_ => return Err(LErr::new("`repeat` may only take numbers".to_string())),
|
||||
};
|
||||
self.ast = &body.0;
|
||||
for _ in 0..times_num {
|
||||
self.eval()?;
|
||||
self.visit(body)?;
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
Do(terms) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &terms[0].0;
|
||||
let mut result = self.eval()?;
|
||||
for (term, _) in terms.iter().skip(1) {
|
||||
self.ast = term;
|
||||
let next = self.eval()?;
|
||||
let mut result = self.visit(&terms[0])?;
|
||||
for term in terms.iter().skip(1) {
|
||||
let next = self.visit(term)?;
|
||||
let arg = Value::Tuple(Rc::new(vec![result]));
|
||||
result = self.apply(next, arg)?;
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(result)
|
||||
}
|
||||
Loop(init, clauses) => {
|
||||
let parent = self.ast;
|
||||
self.ast = &init.0;
|
||||
let mut args = self.eval()?;
|
||||
let mut args = self.visit(init)?;
|
||||
loop {
|
||||
let result = self.match_clauses(&args, clauses)?;
|
||||
if let Value::Recur(recur_args) = result {
|
||||
args = Value::Tuple(Rc::new(recur_args));
|
||||
} else {
|
||||
self.ast = parent;
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Recur(args) => {
|
||||
let parent = self.ast;
|
||||
let mut vect = Vec::new();
|
||||
for arg in args {
|
||||
self.ast = &arg.0;
|
||||
vect.push(self.eval()?);
|
||||
vect.push(self.visit(arg)?);
|
||||
}
|
||||
self.ast = parent;
|
||||
Ok(Value::Recur(vect))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.ast = root;
|
||||
self.ast = root_node;
|
||||
self.span = root_span;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ impl fmt::Display for Value<'_> {
|
|||
|
||||
impl Value<'_> {
|
||||
pub fn bool(&self) -> bool {
|
||||
matches!(self, Value::Nil | Value::Boolean(false))
|
||||
!matches!(self, Value::Nil | Value::Boolean(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user