[fine] Fully untested compiler

This commit is contained in:
John Doty 2024-01-08 22:30:34 -08:00
parent f2b9eae339
commit d14c9a72df
3 changed files with 286 additions and 31 deletions

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
parser::{Tree, TreeKind, TreeRef}, parser::{Tree, TreeKind, TreeRef},
semantics::{Semantics, Type}, semantics::{Location, Semantics, Type},
tokens::TokenKind, tokens::TokenKind,
}; };
@ -8,10 +8,25 @@ use crate::{
// But I'm not cool. // But I'm not cool.
pub enum Instruction { pub enum Instruction {
Panic, Panic,
BoolNot,
Discard,
FloatAdd,
FloatDivide,
FloatMultiply,
FloatSubtract,
Jump(usize),
JumpFalse(usize),
JumpTrue(usize),
LoadArgument(usize),
LoadLocal(usize),
LoadModule(usize),
PushFalse,
PushFloat(f64), PushFloat(f64),
PushNothing,
PushString(usize), PushString(usize),
PushTrue, PushTrue,
PushFalse, StoreLocal(usize),
} }
pub struct Function { pub struct Function {
@ -19,29 +34,32 @@ pub struct Function {
strings: Vec<String>, strings: Vec<String>,
} }
type CR = Option<()>;
const OK: CR = CR::Some(());
pub fn compile_expression(code: &mut Function, semantics: &Semantics, t: TreeRef) { pub fn compile_expression(code: &mut Function, semantics: &Semantics, t: TreeRef) {
let tree = &semantics.tree()[t]; let tree = &semantics.tree()[t];
match tree.kind { let cr = match tree.kind {
TreeKind::Error => code.instructions.push(Instruction::Panic), TreeKind::Error => None,
TreeKind::LiteralExpression => compile_literal(code, semantics, t, tree), TreeKind::LiteralExpression => compile_literal(code, semantics, t, tree),
TreeKind::GroupingExpression => compile_grouping(code, semantics, tree), TreeKind::GroupingExpression => compile_grouping(code, semantics, tree),
TreeKind::UnaryExpression => todo!(), TreeKind::UnaryExpression => compile_unary_operator(code, semantics, tree),
TreeKind::ConditionalExpression => todo!(), TreeKind::ConditionalExpression => compile_condition_expression(code, semantics, tree),
TreeKind::BinaryExpression => todo!(), TreeKind::BinaryExpression => compile_binary_expression(code, semantics, tree),
TreeKind::Identifier => todo!(), TreeKind::Identifier => compile_identifier_expression(code, semantics, t, tree),
TreeKind::CallExpression => todo!(), TreeKind::CallExpression => todo!(),
TreeKind::Block => todo!(), TreeKind::Block => compile_block_expression(code, semantics, tree),
_ => { _ => {
semantics.internal_compiler_error(Some(t), "tree is not an expression, cannot compile") semantics.internal_compiler_error(Some(t), "tree is not an expression, cannot compile")
} }
};
if matches!(cr, None) {
code.instructions.push(Instruction::Panic);
} }
} }
fn compile_literal(code: &mut Function, semantics: &Semantics, t: TreeRef, tr: &Tree) { fn compile_literal(code: &mut Function, semantics: &Semantics, t: TreeRef, tr: &Tree) -> CR {
let Some(tok) = tr.nth_token(0) else { let tok = tr.nth_token(0)?;
code.instructions.push(Instruction::Panic);
return;
};
match semantics.type_of(t) { match semantics.type_of(t) {
Type::F64 => code Type::F64 => code
.instructions .instructions
@ -79,15 +97,226 @@ fn compile_literal(code: &mut Function, semantics: &Semantics, t: TreeRef, tr: &
} }
Type::Error => code.instructions.push(Instruction::Panic), Type::Error => code.instructions.push(Instruction::Panic),
_ => panic!("unsupported literal type: {t:?}"), _ => panic!("unsupported literal type: {t:?}"),
} };
OK
} }
fn compile_grouping(code: &mut Function, semantics: &Semantics, t: &Tree) { fn compile_grouping(code: &mut Function, semantics: &Semantics, t: &Tree) -> CR {
if let Some(t) = t.nth_tree(1) { compile_expression(code, semantics, t.nth_tree(1)?);
compile_expression(code, semantics, t) OK
}
fn compile_unary_operator(code: &mut Function, semantics: &Semantics, t: &Tree) -> CR {
compile_expression(code, semantics, t.nth_tree(1)?);
let tok = t.nth_token(0)?;
match tok.kind {
TokenKind::Minus => {
code.instructions.push(Instruction::PushFloat(-1.0));
code.instructions.push(Instruction::FloatMultiply);
}
TokenKind::Bang => {
code.instructions.push(Instruction::BoolNot);
}
_ => panic!("unsupported unary operator"),
}
OK
}
fn compile_condition_expression(code: &mut Function, semantics: &Semantics, t: &Tree) -> CR {
let condition = t.nth_tree(1)?;
compile_expression(code, semantics, condition);
let jump_else_index = code.instructions.len();
code.instructions.push(Instruction::JumpFalse(0));
let then_branch = t.nth_tree(2)?;
compile_expression(code, semantics, then_branch);
if let Some(else_branch) = t.nth_tree(4) {
let jump_end_index = code.instructions.len();
code.instructions.push(Instruction::Jump(0));
let else_index = code.instructions.len();
code.instructions[jump_else_index] = Instruction::JumpFalse(else_index);
compile_expression(code, semantics, else_branch);
let end_index = code.instructions.len();
code.instructions[jump_end_index] = Instruction::Jump(end_index);
} else { } else {
let else_index = code.instructions.len();
code.instructions[jump_else_index] = Instruction::JumpFalse(else_index);
}
OK
}
fn compile_binary_expression(code: &mut Function, semantics: &Semantics, t: &Tree) -> CR {
compile_expression(code, semantics, t.nth_tree(0)?);
match t.nth_token(1)?.kind {
TokenKind::Plus => {
compile_expression(code, semantics, t.nth_tree(2)?);
code.instructions.push(Instruction::FloatAdd);
}
TokenKind::Minus => {
compile_expression(code, semantics, t.nth_tree(2)?);
code.instructions.push(Instruction::FloatSubtract);
}
TokenKind::Star => {
compile_expression(code, semantics, t.nth_tree(2)?);
code.instructions.push(Instruction::FloatMultiply);
}
TokenKind::Slash => {
compile_expression(code, semantics, t.nth_tree(2)?);
code.instructions.push(Instruction::FloatDivide);
}
TokenKind::And => {
let jump_false_index = code.instructions.len();
code.instructions.push(Instruction::JumpFalse(0));
code.instructions.push(Instruction::PushTrue);
let jump_end_index = code.instructions.len();
code.instructions.push(Instruction::Jump(0));
let false_index = code.instructions.len();
code.instructions[jump_false_index] = Instruction::JumpFalse(false_index);
compile_expression(code, semantics, t.nth_tree(2)?);
let end_index = code.instructions.len();
code.instructions[jump_end_index] = Instruction::Jump(end_index);
}
TokenKind::Or => {
let jump_true_index = code.instructions.len();
code.instructions.push(Instruction::JumpTrue(0));
code.instructions.push(Instruction::PushTrue);
let jump_end_index = code.instructions.len();
code.instructions.push(Instruction::Jump(0));
let true_index = code.instructions.len();
code.instructions[jump_true_index] = Instruction::JumpTrue(true_index);
compile_expression(code, semantics, t.nth_tree(2)?);
let end_index = code.instructions.len();
code.instructions[jump_end_index] = Instruction::Jump(end_index);
}
_ => panic!("Unsupported binary expression"),
}
OK
}
fn compile_identifier_expression(
code: &mut Function,
semantics: &Semantics,
t: TreeRef,
tree: &Tree,
) -> Option<()> {
let ident = tree.nth_token(0)?;
let environment = semantics.environment_of(t);
let declaration = environment.bind(ident)?;
let instruction = match declaration.location {
Location::Local => Instruction::LoadLocal(declaration.index),
Location::Argument => Instruction::LoadArgument(declaration.index),
Location::Module => Instruction::LoadModule(declaration.index),
};
code.instructions.push(instruction);
OK
}
fn compile_block_expression(code: &mut Function, semantics: &Semantics, tree: &Tree) -> Option<()> {
let last_is_brace = tree.nth_token(tree.children.len() - 1).is_some();
let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 };
for i in 1..last_index {
compile_statement(code, semantics, tree.nth_tree(i)?, false);
}
compile_statement(code, semantics, tree.nth_tree(last_index)?, true);
OK
}
pub fn compile_statement(code: &mut Function, semantics: &Semantics, t: TreeRef, gen_value: bool) {
let tree = &semantics.tree()[t];
let cr = match tree.kind {
TreeKind::FunctionDecl => compile_function_declaration(code, semantics, tree, gen_value),
TreeKind::LetStatement => compile_let_statement(code, semantics, t, tree, gen_value),
TreeKind::ExpressionStatement => {
compile_expression_statement(code, semantics, tree, gen_value)
}
TreeKind::IfStatement => compile_if_statement(code, semantics, tree, gen_value),
_ => panic!("unsupported tree kind {:?}", tree.kind),
};
if matches!(cr, None) {
code.instructions.push(Instruction::Panic); code.instructions.push(Instruction::Panic);
} }
} }
pub fn compile_statement(code: &mut Function, semantics: &Semantics, t: TreeRef) {} fn compile_if_statement(
code: &mut Function,
semantics: &Semantics,
tree: &Tree,
gen_value: bool,
) -> CR {
compile_expression(code, semantics, tree.nth_tree(0)?);
if !gen_value {
code.instructions.push(Instruction::Discard);
}
OK
}
fn compile_expression_statement(
code: &mut Function,
semantics: &Semantics,
tree: &Tree,
gen_value: bool,
) -> CR {
compile_expression(code, semantics, tree.nth_tree(0)?);
if tree
.nth_token(1)
.is_some_and(|t| t.kind == TokenKind::Semicolon)
{
code.instructions.push(Instruction::Discard);
if gen_value {
code.instructions.push(Instruction::PushNothing);
}
} else if !gen_value {
code.instructions.push(Instruction::Discard);
}
OK
}
fn compile_let_statement(
code: &mut Function,
semantics: &Semantics,
t: TreeRef,
tree: &Tree,
gen_value: bool,
) -> CR {
compile_expression(code, semantics, tree.nth_tree(3)?);
let environment = semantics.environment_of(t);
let declaration = environment.bind(tree.nth_token(1)?)?;
// NOTE: Because this is a let statement I assume it's local!
assert!(matches!(declaration.location, Location::Local));
code.instructions
.push(Instruction::StoreLocal(declaration.index));
if gen_value {
code.instructions.push(Instruction::PushNothing);
}
OK
}
fn compile_function_declaration(
_code: &mut Function,
_semantics: &Semantics,
_tree: &Tree,
_gen_value: bool,
) -> CR {
todo!()
}

View file

@ -121,23 +121,48 @@ impl fmt::Display for Type {
} }
} }
#[derive(Clone, Copy)]
pub enum Location {
Argument,
Local,
Module,
}
pub struct Declaration { pub struct Declaration {
pub declaration_type: Type, pub declaration_type: Type,
pub location: Location,
pub index: usize,
} }
pub struct Environment { pub struct Environment {
pub parent: Option<EnvironmentRef>, pub parent: Option<EnvironmentRef>,
pub location: Location,
pub base_index: usize,
pub declarations: HashMap<Box<str>, Declaration>, pub declarations: HashMap<Box<str>, Declaration>,
} }
impl Environment { impl Environment {
pub fn new(parent: Option<EnvironmentRef>) -> Self { pub fn new(parent: Option<EnvironmentRef>, location: Location, base_index: usize) -> Self {
Environment { Environment {
parent, parent,
location,
base_index,
declarations: HashMap::new(), declarations: HashMap::new(),
} }
} }
pub fn insert(&mut self, token: &Token, t: Type) {
let index = self.base_index + self.declarations.len();
self.declarations.insert(
token.as_str().into(),
Declaration {
declaration_type: t,
location: self.location,
index,
},
);
}
pub fn bind(&self, token: &Token) -> Option<&Declaration> { pub fn bind(&self, token: &Token) -> Option<&Declaration> {
if let Some(decl) = self.declarations.get(token.as_str()) { if let Some(decl) = self.declarations.get(token.as_str()) {
return Some(decl); return Some(decl);
@ -260,7 +285,7 @@ impl<'a> Semantics<'a> {
errors: RefCell::new(vec![]), errors: RefCell::new(vec![]),
types: RefCell::new(vec![Incremental::None; tree.len()]), types: RefCell::new(vec![Incremental::None; tree.len()]),
environments: RefCell::new(vec![Incremental::None; tree.len()]), environments: RefCell::new(vec![Incremental::None; tree.len()]),
empty_environment: EnvironmentRef::new(Environment::new(None)), empty_environment: EnvironmentRef::new(Environment::new(None, Location::Module, 0)),
}; };
// NOTE: We ensure all the known errors are reported before we move // NOTE: We ensure all the known errors are reported before we move
@ -397,10 +422,13 @@ impl<'a> Semantics<'a> {
None => Type::Error, None => Type::Error,
}; };
let mut environment = Environment::new(Some(parent)); let base_index = match parent.location {
environment Location::Local => parent.base_index + parent.declarations.len(),
.declarations _ => 0,
.insert(name.as_str().into(), Declaration { declaration_type }); };
let mut environment = Environment::new(Some(parent), Location::Local, base_index);
environment.insert(name, declaration_type);
EnvironmentRef::new(environment) EnvironmentRef::new(environment)
} }
@ -414,7 +442,7 @@ impl<'a> Semantics<'a> {
return parent; // SE return parent; // SE
} }
let mut environment = Environment::new(Some(parent)); let mut environment = Environment::new(Some(parent), Location::Argument, 0);
for child in param_list.children.iter() { for child in param_list.children.iter() {
let Child::Tree(ct) = child else { let Child::Tree(ct) = child else {
continue; continue;
@ -434,9 +462,7 @@ impl<'a> Semantics<'a> {
Type::Error Type::Error
}; };
environment environment.insert(param_name, declaration_type);
.declarations
.insert(param_name.as_str().into(), Declaration { declaration_type });
} }
EnvironmentRef::new(environment) EnvironmentRef::new(environment)

View file

@ -136,7 +136,7 @@ fn assert_type_at(
}; };
let tree_type = semantics.type_of(tree_ref); let tree_type = semantics.type_of(tree_ref);
let actual = format!("{}", tree_type.unwrap_or(Type::Error)); let actual = format!("{}", tree_type);
semantic_assert_eq!( semantic_assert_eq!(
&semantics, &semantics,
Some(tree_ref), Some(tree_ref),
@ -167,7 +167,7 @@ fn assert_type_error_at(
semantic_assert!( semantic_assert!(
&semantics, &semantics,
Some(tree_ref), Some(tree_ref),
matches!(tree_type, Some(Type::Error)), matches!(tree_type, Type::Error),
"The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error", "The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error",
tree[tree_ref].kind tree[tree_ref].kind
); );