make some progress: atoms and ifs

This commit is contained in:
Scott Richmond 2024-12-15 23:28:57 -05:00
parent eff2ed90d5
commit 35fc591c76
3 changed files with 155 additions and 29 deletions

View File

@ -6,14 +6,37 @@ use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive;
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
enum Op {
pub enum Op {
Return,
Constant,
Jump,
JumpIfFalse,
}
impl std::fmt::Display for Op {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Op::*;
match self {
Return => write!(f, "return"),
Constant => write!(f, "constant"),
Jump => write!(f, "jump"),
JumpIfFalse => write!(f, "jump_if_false"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
struct Chunk<'a> {
pub constants: Vec<Value<'a>>,
pub struct Local {
name: &'static str,
depth: u8,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Chunk<'a> {
pub locals: Vec<Local>,
scope_depth: usize,
local_count: usize,
pub constants: Vec<Value>,
pub bytecode: Vec<u8>,
pub spans: Vec<SimpleSpan>,
pub strings: Vec<&'static str>,
@ -26,7 +49,34 @@ struct Chunk<'a> {
}
impl<'a> Chunk<'a> {
fn visit(&mut self, node: &'a Spanned<Ast>) {
pub fn new(ast: &'a Spanned<Ast>, name: &'static str, src: &'static str) -> Chunk<'a> {
Chunk {
locals: vec![],
scope_depth: 0,
local_count: 0,
constants: vec![],
bytecode: vec![],
spans: vec![],
strings: vec![],
keywords: vec![
"nil", "bool", "number", "keyword", "string", "tuple", "list", "",
],
nodes: vec![],
ast: &ast.0,
span: ast.1,
src,
name,
}
}
pub fn kw_from(&self, kw: &str) -> Option<Value> {
self.keywords
.iter()
.position(|s| *s == kw)
.map(Value::Keyword)
}
pub fn visit(&mut self, node: &'a Spanned<Ast>) {
let root_node = self.ast;
let root_span = self.span;
let (ast, span) = node;
@ -37,7 +87,7 @@ impl<'a> Chunk<'a> {
self.span = root_span;
}
fn emit_constant(&mut self, val: Value<'a>) {
fn emit_constant(&mut self, val: Value) {
let constant_index = self.constants.len();
if constant_index > u8::MAX as usize {
panic!(
@ -57,39 +107,78 @@ impl<'a> Chunk<'a> {
self.spans.push(self.span);
}
fn compile(&mut self) {
pub fn compile(&mut self) {
use Ast::*;
match self.ast {
Ast::Nil => self.emit_constant(Value::Nil),
Ast::Number(n) => self.emit_constant(Value::Number(*n)),
Ast::Boolean(b) => self.emit_constant(Value::Boolean(*b)),
Ast::String(s) => {
Nil => self.emit_constant(Value::Nil),
Number(n) => self.emit_constant(Value::Number(*n)),
Boolean(b) => self.emit_constant(if *b { Value::True } else { Value::False }),
String(s) => {
let str_index = self.strings.len();
self.strings.push(s);
self.emit_constant(Value::InternedString(s));
self.emit_constant(Value::Interned(str_index));
}
Ast::Block(lines) => {
Keyword(s) => {
let existing_kw = self.keywords.iter().position(|kw| kw == s);
let kw_index = match existing_kw {
Some(index) => index,
None => self.keywords.len(),
};
self.keywords.push(s);
self.emit_constant(Value::Keyword(kw_index));
}
Block(lines) => {
self.scope_depth += 1;
for expr in lines {
self.visit(expr);
}
self.emit_op(Op::Return);
self.scope_depth -= 1;
}
If(cond, then, r#else) => {
self.visit(cond);
let jif_idx = self.bytecode.len();
self.emit_op(Op::JumpIfFalse);
self.bytecode.push(0xff);
self.visit(then);
let jump_idx = self.bytecode.len();
self.emit_op(Op::Jump);
self.bytecode.push(0xff);
self.visit(r#else);
let end_idx = self.bytecode.len();
let jif_offset = jump_idx - jif_idx;
let jump_offset = end_idx - jump_idx;
self.bytecode[jif_idx + 1] = jif_offset as u8;
self.bytecode[jump_idx + 1] = jump_offset as u8;
}
// Let(patt, expr) => {
// self.visit(expr);
// self.visit(patt);
// }
// WordPattern(name) => {}
// PlaceholderPattern => {}
_ => todo!(),
}
}
fn disassemble(&self) {
pub fn disassemble(&self) {
println!("=== chunk: {} ===", self.name);
println!("IDX | CODE | INFO");
let mut codes = self.bytecode.iter().enumerate();
while let Some((i, byte)) = codes.next() {
let op = Op::from_u8(*byte);
let op = Op::from_u8(*byte).unwrap();
use Op::*;
match op {
Some(Op::Return) => println!("{i:04}: {byte:16}"),
Some(Op::Constant) => {
Return => println!("{i:04}: {op}"),
Constant => {
let (_, next) = codes.next().unwrap();
let value = &self.constants[*next as usize];
println!("i:04: {byte:16} {next:16} {value}")
let value = &self.constants[*next as usize].show(self);
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
}
Jump | JumpIfFalse => {
let (_, next) = codes.next().unwrap();
println!("{i:04}: {:16} {next:04}", op.to_string())
}
_ => unreachable!(),
}
}
}

View File

@ -53,9 +53,9 @@ fn match_arities(arities: &HashSet<Arity>, num_args: u8) -> bool {
}
#[derive(Debug, PartialEq)]
pub struct Validator<'a, 'src> {
pub struct Validator<'a> {
pub locals: Vec<(String, Span, FnInfo)>,
pub prelude: &'a Vec<(String, Value<'src>)>,
pub prelude: &'a Vec<(&'static str, Value)>,
pub input: &'static str,
pub src: &'static str,
pub ast: &'a Ast,
@ -65,14 +65,14 @@ pub struct Validator<'a, 'src> {
status: VStatus,
}
impl<'a, 'src: 'a> Validator<'a, 'src> {
impl<'a> Validator<'a> {
pub fn new(
ast: &'a Ast,
span: Span,
input: &'static str,
src: &'static str,
prelude: &'a Vec<(String, Value<'src>)>,
) -> Validator<'a, 'src> {
prelude: &'a Vec<(&'static str, Value)>,
) -> Validator<'a> {
Validator {
input,
src,
@ -109,7 +109,7 @@ impl<'a, 'src: 'a> Validator<'a, 'src> {
fn resolved(&self, name: &str) -> bool {
self.locals.iter().any(|(bound, ..)| name == bound.as_str())
|| self.prelude.iter().any(|(bound, _)| name == bound.as_str())
|| self.prelude.iter().any(|(bound, _)| name == *bound)
}
fn bound(&self, name: &str) -> Option<&(String, Span, FnInfo)> {

View File

@ -25,23 +25,60 @@ pub struct LFn {
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Nil,
Boolean(bool),
True,
False,
Keyword(usize), // use an idx, rather than a raw index
Interned(usize),
FnDecl(usize),
String(Rc<String>),
Number(f64),
Tuple(Rc<Vec<Value>>),
TupleStart { len: u8, size: u16 },
TupleEnd { len: u8, size: u16 },
List(Box<Vector<Value>>),
Dict(Box<HashMap<&'static str, Value>>),
Dict(Box<HashMap<usize, Value>>),
Box(Rc<LBox>),
Fn(Rc<RefCell<LFn>>),
}
impl Value {
fn show(&self, ctx: &Chunk) -> String {
pub fn show(&self, ctx: &Chunk) -> String {
use Value::*;
match &self {
Nil => format!("nil"),
Nil => "nil".to_string(),
True => "true".to_string(),
False => "false".to_string(),
Number(n) => format!("{n}"),
Interned(i) => {
let str_str = ctx.strings[*i];
format!("\"{str_str}\"")
}
Keyword(i) => {
let kw_str = ctx.keywords[*i];
format!(":{kw_str}")
}
Tuple(t) => {
let members = t.iter().map(|e| e.show(ctx)).collect::<Vec<_>>().join(", ");
format!("({members})")
}
List(l) => {
let members = l.iter().map(|e| e.show(ctx)).collect::<Vec<_>>().join(", ");
format!("[{members}]")
}
Dict(d) => {
let members = d
.iter()
.map(|(k, v)| {
let key_show = Value::Keyword(*k).show(ctx);
let value_show = v.show(ctx);
format!("{key_show} {value_show}")
})
.collect::<Vec<_>>()
.join(", ");
format!("#{{{members}}}")
}
String(s) => s.as_ref().clone(),
_ => todo!(),
}
}
}