Compare commits

..

3 Commits

Author SHA1 Message Date
Scott Richmond
096d8d00bc add untracked from opening bytecode branch 2024-12-15 23:50:12 -05:00
Scott Richmond
9c3205d4c1 DRY out validator, simplify code 2024-12-15 23:49:43 -05:00
Scott Richmond
6c78cffe56 finish list of valid types 2024-12-15 23:49:27 -05:00
4 changed files with 211 additions and 160 deletions

View File

@ -59,7 +59,7 @@ impl<'a> Chunk<'a> {
spans: vec![], spans: vec![],
strings: vec![], strings: vec![],
keywords: vec![ keywords: vec![
"nil", "bool", "number", "keyword", "string", "tuple", "list", "", "nil", "bool", "number", "keyword", "string", "tuple", "list", "dict", "box", "fn",
], ],
nodes: vec![], nodes: vec![],
ast: &ast.0, ast: &ast.0,

68
src/main.rs Normal file
View File

@ -0,0 +1,68 @@
use chumsky::{input::Stream, prelude::*};
mod spans;
mod lexer;
use crate::lexer::lexer;
mod parser;
use crate::parser::parser;
mod validator;
mod compiler;
use crate::compiler::Chunk;
mod value;
mod vm;
use vm::Vm;
pub fn run(src: &'static str) {
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if !lex_errs.is_empty() {
println!("{:?}", lex_errs);
return;
}
let tokens = tokens.unwrap();
let (parse_result, parse_errors) = parser()
.parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
.into_output_errors();
if !parse_errors.is_empty() {
println!("{:?}", parse_errors);
return;
}
let parsed = parse_result.unwrap();
let mut chunk = Chunk::new(&parsed, "test", src);
chunk.compile();
chunk.disassemble();
let mut vm = Vm::new(&chunk);
let result = vm.interpret();
let output = match result {
Ok(val) => val.show(&chunk),
Err(panic) => format!("{:?}", panic),
};
println!("{output}");
}
pub fn main() {
let src = "
if false
then {
:foo
:bar
:baz
}
else {
1
2
3
}
";
run(src);
}

View File

@ -1,5 +1,5 @@
use crate::parser::*; use crate::parser::*;
use crate::spans::Span; use crate::spans::{Span, Spanned};
use crate::value::Value; use crate::value::Value;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -143,6 +143,13 @@ impl<'a> Validator<'a> {
} }
} }
fn visit(&mut self, node: &'a Spanned<Ast>) {
let (expr, span) = node;
self.ast = expr;
self.span = *span;
self.validate();
}
pub fn validate(&mut self) { pub fn validate(&mut self) {
use Ast::*; use Ast::*;
let root = self.ast; let root = self.ast;
@ -179,18 +186,13 @@ impl<'a> Validator<'a> {
} }
let to = self.locals.len(); let to = self.locals.len();
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
for (expr, span) in block.iter().take(block.len() - 1) { for line in block.iter().take(block.len() - 1) {
self.status.tail_position = false; self.status.tail_position = false;
self.ast = expr; self.visit(line);
self.span = *span;
self.validate();
} }
let (expr, span) = block.last().unwrap();
self.ast = expr;
self.span = *span;
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
self.validate(); self.visit(block.last().unwrap());
let block_bindings = self.locals.split_off(to); let block_bindings = self.locals.split_off(to);
@ -207,22 +209,12 @@ impl<'a> Validator<'a> {
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = cond.as_ref(); self.visit(cond.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
// pass through tailpos only to then/else // pass through tailpos only to then/else
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
let (expr, span) = then.as_ref(); self.visit(then.as_ref());
self.ast = expr; self.visit(r#else.as_ref());
self.span = *span;
self.validate();
let (expr, span) = r#else.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
Tuple(members) => { Tuple(members) => {
if members.is_empty() { if members.is_empty() {
@ -230,10 +222,8 @@ impl<'a> Validator<'a> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in members { for member in members {
self.ast = expr; self.visit(member);
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
@ -244,10 +234,8 @@ impl<'a> Validator<'a> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in args { for arg in args {
self.ast = expr; self.visit(arg);
self.span = *span;
self.validate();
} }
self.status.has_placeholder = false; self.status.has_placeholder = false;
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
@ -267,30 +255,21 @@ impl<'a> Validator<'a> {
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in list { for member in list {
self.ast = expr; self.visit(member);
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
Pair(_, value) => { Pair(_, value) => self.visit(value.as_ref()),
let (expr, span) = value.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
}
Dict(dict) => { Dict(dict) => {
if dict.is_empty() { if dict.is_empty() {
return; return;
} }
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
for (expr, span) in dict { for pair in dict {
self.ast = expr; self.visit(pair)
self.span = *span;
self.validate();
} }
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
@ -299,31 +278,16 @@ impl<'a> Validator<'a> {
// check arity against fn info if first term is word and second term is args // check arity against fn info if first term is word and second term is args
Synthetic(first, second, rest) => { Synthetic(first, second, rest) => {
match (&first.0, &second.0) { match (&first.0, &second.0) {
(Ast::Word(_), Ast::Keyword(_)) => { (Ast::Word(_), Ast::Keyword(_)) => self.visit(first.as_ref()),
let (expr, span) = first.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
}
(Ast::Keyword(_), Ast::Arguments(args)) => { (Ast::Keyword(_), Ast::Arguments(args)) => {
if args.len() != 1 { if args.len() != 1 {
self.err("called keywords may only take one argument".to_string()) self.err("called keywords may only take one argument".to_string())
} }
let (expr, span) = second.as_ref(); self.visit(second.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
(Ast::Word(name), Ast::Arguments(args)) => { (Ast::Word(name), Ast::Arguments(args)) => {
let (expr, span) = first.as_ref(); self.visit(first.as_ref());
self.ast = expr; self.visit(second.as_ref());
self.span = *span;
self.validate();
let (expr, span) = second.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
//TODO: check arities of prelude fns, too //TODO: check arities of prelude fns, too
let fn_binding = self.bound(name); let fn_binding = self.bound(name);
@ -337,32 +301,20 @@ impl<'a> Validator<'a> {
_ => unreachable!(), _ => unreachable!(),
} }
for term in rest { for term in rest {
let (expr, span) = term; self.visit(term);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
WhenClause(cond, body) => { WhenClause(cond, body) => {
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = cond.as_ref(); self.visit(cond.as_ref());
self.ast = expr; //pass through tail position for when bodies
self.span = *span;
self.validate();
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
let (expr, span) = body.as_ref(); self.visit(body.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
When(clauses) => { When(clauses) => {
for clause in clauses { for clause in clauses {
let (expr, span) = clause; self.visit(clause);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
@ -374,54 +326,30 @@ impl<'a> Validator<'a> {
} else { } else {
self.bind(name.to_string()); self.bind(name.to_string());
} }
let (expr, span) = boxed.as_ref(); self.visit(boxed.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
} }
Let(lhs, rhs) => { Let(lhs, rhs) => {
let (expr, span) = rhs.as_ref(); self.visit(rhs.as_ref());
self.ast = expr; self.visit(lhs.as_ref());
self.span = *span;
self.validate();
let (expr, span) = lhs.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
MatchClause(pattern, guard, body) => { MatchClause(pattern, guard, body) => {
let to = self.locals.len(); let to = self.locals.len();
let (patt, span) = pattern.as_ref(); self.visit(pattern.as_ref());
self.ast = patt;
self.span = *span;
self.validate();
if let Some((expr, span)) = guard.as_ref() { if let Some(guard) = guard.as_ref() {
self.ast = expr; self.visit(guard);
self.span = *span;
self.validate();
} }
let (expr, span) = body.as_ref(); self.visit(body.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
self.locals.truncate(to); self.locals.truncate(to);
} }
Match(scrutinee, clauses) => { Match(scrutinee, clauses) => {
let (expr, span) = scrutinee.as_ref(); self.visit(scrutinee.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
for clause in clauses { for clause in clauses {
let (expr, span) = clause; self.visit(clause);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
FnDeclaration(name) => { FnDeclaration(name) => {
@ -488,10 +416,7 @@ impl<'a> Validator<'a> {
Panic(msg) => { Panic(msg) => {
let tailpos = self.status.tail_position; let tailpos = self.status.tail_position;
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = msg.as_ref(); self.visit(msg.as_ref());
self.ast = expr;
self.span = *span;
self.validate();
self.status.tail_position = tailpos; self.status.tail_position = tailpos;
} }
// TODO: fix the tail call here? // TODO: fix the tail call here?
@ -500,39 +425,23 @@ impl<'a> Validator<'a> {
return self.err("do expressions must have at least two terms".to_string()); return self.err("do expressions must have at least two terms".to_string());
} }
for term in terms.iter().take(terms.len() - 1) { for term in terms.iter().take(terms.len() - 1) {
let (expr, span) = term; self.visit(term);
self.ast = expr;
self.span = *span;
self.validate();
} }
let last = terms.last().unwrap();
let (expr, span) = terms.last().unwrap(); self.visit(last);
self.ast = expr; if matches!(last.0, Ast::Recur(_)) {
self.span = *span;
if matches!(expr, Ast::Recur(_)) {
self.err("`recur` may not be used in `do` forms".to_string()); self.err("`recur` may not be used in `do` forms".to_string());
} }
self.validate();
} }
Repeat(times, body) => { Repeat(times, body) => {
self.status.tail_position = false; self.status.tail_position = false;
let (expr, span) = times.as_ref(); self.visit(times.as_ref());
self.ast = expr; self.visit(body.as_ref());
self.span = *span;
self.validate();
let (expr, span) = body.as_ref();
self.ast = expr;
self.span = *span;
self.validate();
} }
Loop(with, body) => { Loop(with, body) => {
let (expr, span) = with.as_ref(); self.visit(with.as_ref());
self.span = *span;
self.ast = expr;
self.validate();
let Ast::Tuple(input) = expr else { let Ast::Tuple(input) = &with.0 else {
unreachable!() unreachable!()
}; };
@ -588,10 +497,7 @@ impl<'a> Validator<'a> {
self.status.tail_position = false; self.status.tail_position = false;
for arg in args { for arg in args {
let (expr, span) = arg; self.visit(arg);
self.ast = expr;
self.span = *span;
self.validate();
} }
} }
WordPattern(name) => match self.bound(name) { WordPattern(name) => match self.bound(name) {
@ -654,25 +560,15 @@ impl<'a> Validator<'a> {
return; return;
} }
for term in terms.iter().take(terms.len() - 1) { for term in terms.iter().take(terms.len() - 1) {
let (patt, span) = term; self.visit(term);
self.ast = patt;
self.span = *span;
self.validate();
} }
self.status.last_term = true; self.status.last_term = true;
let (patt, span) = terms.last().unwrap(); let last = terms.last().unwrap();
self.ast = patt; self.visit(last);
self.span = *span;
self.validate();
self.status.last_term = false; self.status.last_term = false;
} }
PairPattern(_, patt) => { PairPattern(_, patt) => self.visit(patt.as_ref()),
let (patt, span) = patt.as_ref();
self.ast = patt;
self.span = *span;
self.validate();
}
// terminals can never be invalid // terminals can never be invalid
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (), Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (),
// terminal patterns can never be invalid // terminal patterns can never be invalid

87
src/vm.rs Normal file
View File

@ -0,0 +1,87 @@
use crate::compiler::{Chunk, Op};
use crate::parser::Ast;
use crate::spans::Spanned;
use crate::value::Value;
use chumsky::prelude::SimpleSpan;
use num_traits::FromPrimitive;
#[derive(Debug, Clone, PartialEq)]
pub struct Panic {
pub input: &'static str,
pub src: &'static str,
pub msg: String,
pub span: SimpleSpan,
pub trace: Vec<Trace>,
pub extra: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Trace {
pub callee: Spanned<Ast>,
pub caller: Spanned<Ast>,
pub function: Value,
pub arguments: Value,
pub input: &'static str,
pub src: &'static str,
}
pub struct Vm<'a> {
pub stack: Vec<Value>,
pub chunk: &'a Chunk<'a>,
pub ip: usize,
pub bindings: Vec<(u8, usize)>,
}
impl<'a> Vm<'a> {
pub fn new(chunk: &'a Chunk) -> Vm<'a> {
Vm {
chunk,
stack: vec![],
ip: 0,
bindings: vec![],
}
}
pub fn push(&mut self, value: Value) {
self.stack.push(value);
}
pub fn pop(&mut self) -> Value {
self.stack.pop().unwrap()
}
pub fn interpret(&mut self) -> Result<Value, Panic> {
let byte = self.chunk.bytecode[self.ip];
let op = Op::from_u8(byte).unwrap();
use Op::*;
match op {
Return => Ok(self.stack.pop().unwrap()),
Constant => {
let const_idx = self.chunk.bytecode[self.ip + 1];
let value = self.chunk.constants[const_idx as usize].clone();
self.push(value);
self.ip += 2;
self.interpret()
}
Jump => {
let jump_len = self.chunk.bytecode[self.ip + 1];
self.ip += jump_len as usize;
self.interpret()
}
JumpIfFalse => {
let jump_len = self.chunk.bytecode[self.ip + 1];
let cond = self.stack.pop().unwrap();
match cond {
Value::Nil | Value::False => {
self.ip += jump_len as usize + 2;
self.interpret()
}
_ => {
self.ip += 2;
self.interpret()
}
}
}
}
}
}