[fine] Gobs of work
- Assertion improvements - Type check function calls - Functions in the environment - Compile function calls
This commit is contained in:
parent
9d226b205d
commit
5ebede4a21
6 changed files with 539 additions and 142 deletions
|
|
@ -2,35 +2,10 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::{
|
||||
parser::{SyntaxTree, Tree, TreeKind, TreeRef},
|
||||
semantics::{Location, Semantics, Type},
|
||||
semantics::{Declaration, Location, Semantics, Type},
|
||||
tokens::TokenKind,
|
||||
};
|
||||
|
||||
macro_rules! compiler_assert_eq {
|
||||
($compiler:expr, $tr:expr, $ll:expr, $rr:expr, $($t:tt)*) => {{
|
||||
let left = &$ll;
|
||||
let right = &$rr;
|
||||
if left != right {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
let message = format!($($t)*);
|
||||
assert_eq!(left, right, "{}", message);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! compiler_assert {
|
||||
($compiler:expr, $tr:expr, $($t:tt)*) => {{
|
||||
if !($($t)*) {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
assert!($($t)*);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// TODO: If I were cool this would by actual bytecode.
|
||||
// But I'm not cool.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -56,6 +31,10 @@ pub enum Instruction {
|
|||
PushTrue,
|
||||
StoreLocal(usize),
|
||||
StoreModule(usize),
|
||||
LoadFunction(usize),
|
||||
LoadExtern(usize),
|
||||
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
|
||||
Call(usize),
|
||||
}
|
||||
|
||||
pub enum Export {
|
||||
|
|
@ -130,7 +109,9 @@ struct Compiler<'a> {
|
|||
semantics: &'a Semantics<'a>,
|
||||
syntax: &'a SyntaxTree<'a>,
|
||||
|
||||
function_bindings: HashMap<String, usize>,
|
||||
// TODO: generic functions will actually be keyed by treeref and concrete
|
||||
// types
|
||||
function_bindings: HashMap<TreeRef, usize>,
|
||||
module: Module,
|
||||
function: Function,
|
||||
}
|
||||
|
|
@ -158,6 +139,62 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! compiler_assert_eq {
|
||||
($compiler:expr, $tr:expr, $ll:expr, $rr:expr $(,)?) => {{
|
||||
let left = &$ll;
|
||||
let right = &$rr;
|
||||
if left != right {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
assert_eq!(left, right);
|
||||
}
|
||||
}};
|
||||
|
||||
($compiler:expr, $tr:expr, $ll:expr, $rr:expr, $($t:tt)+) => {{
|
||||
let left = &$ll;
|
||||
let right = &$rr;
|
||||
if left != right {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
assert_eq!(left, right, $($t)*);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! compiler_assert {
|
||||
($compiler:expr, $tr:expr, $cond:expr $(,)?) => {{
|
||||
if !$cond {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
assert!($cond);
|
||||
}
|
||||
}};
|
||||
|
||||
($compiler:expr, $tr:expr, $cond:expr, $($arg:tt)+) => {{
|
||||
if !$cond {
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
|
||||
assert!($cond, $($arg)*);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! ice {
|
||||
($compiler: expr, $tr:expr, $($t:tt)+) => {{
|
||||
let semantics = $compiler.semantics;
|
||||
semantics.dump_compiler_state(Some($tr));
|
||||
panic!($($t)*)
|
||||
}}
|
||||
}
|
||||
|
||||
// macro_rules! ice {
|
||||
// ($compiler:expr, $tr:expr, $($t:tt)*) => {{}};
|
||||
// }
|
||||
|
||||
pub fn compile(semantics: &Semantics) -> Module {
|
||||
let mut compiler = Compiler {
|
||||
semantics,
|
||||
|
|
@ -198,15 +235,14 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
|||
TreeKind::Error => None,
|
||||
TreeKind::LiteralExpression => compile_literal(c, t, tree),
|
||||
TreeKind::GroupingExpression => compile_grouping(c, tree),
|
||||
TreeKind::UnaryExpression => compile_unary_operator(c, tree),
|
||||
TreeKind::UnaryExpression => compile_unary_operator(c, t, tree),
|
||||
TreeKind::ConditionalExpression => compile_condition_expression(c, tree),
|
||||
TreeKind::BinaryExpression => compile_binary_expression(c, tree),
|
||||
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
|
||||
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
||||
TreeKind::CallExpression => todo!(),
|
||||
TreeKind::CallExpression => compile_call_expression(c, tree),
|
||||
TreeKind::Block => compile_block_expression(c, tree),
|
||||
_ => c
|
||||
.semantics
|
||||
.internal_compiler_error(Some(t), "tree is not an expression, cannot compile"),
|
||||
TreeKind::Argument => compile_argument(c, tree),
|
||||
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
||||
};
|
||||
if matches!(cr, None) {
|
||||
c.push(Instruction::Panic);
|
||||
|
|
@ -246,7 +282,7 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
|||
c.push(Instruction::PushString(index))
|
||||
}
|
||||
Type::Error => c.push(Instruction::Panic),
|
||||
_ => panic!("unsupported literal type: {t:?}"),
|
||||
_ => ice!(c, t, "unsupported literal type: {t:?}"),
|
||||
};
|
||||
OK
|
||||
}
|
||||
|
|
@ -256,10 +292,10 @@ fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR {
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_unary_operator(c: &mut Compiler, t: &Tree) -> CR {
|
||||
compile_expression(c, t.nth_tree(1)?);
|
||||
fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||
compile_expression(c, tr.nth_tree(1)?);
|
||||
|
||||
let tok = t.nth_token(0)?;
|
||||
let tok = tr.nth_token(0)?;
|
||||
match tok.kind {
|
||||
TokenKind::Minus => {
|
||||
c.push(Instruction::PushFloat(-1.0));
|
||||
|
|
@ -268,7 +304,7 @@ fn compile_unary_operator(c: &mut Compiler, t: &Tree) -> CR {
|
|||
TokenKind::Bang => {
|
||||
c.push(Instruction::BoolNot);
|
||||
}
|
||||
_ => panic!("unsupported unary operator"),
|
||||
_ => ice!(c, t, "unsupported unary operator"),
|
||||
}
|
||||
OK
|
||||
}
|
||||
|
|
@ -294,23 +330,23 @@ fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR {
|
||||
compile_expression(c, t.nth_tree(0)?);
|
||||
match t.nth_token(1)?.kind {
|
||||
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||
compile_expression(c, tr.nth_tree(0)?);
|
||||
match tr.nth_token(1)?.kind {
|
||||
TokenKind::Plus => {
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
c.push(Instruction::FloatAdd);
|
||||
}
|
||||
TokenKind::Minus => {
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
c.push(Instruction::FloatSubtract);
|
||||
}
|
||||
TokenKind::Star => {
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
c.push(Instruction::FloatMultiply);
|
||||
}
|
||||
TokenKind::Slash => {
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
c.push(Instruction::FloatDivide);
|
||||
}
|
||||
TokenKind::And => {
|
||||
|
|
@ -321,7 +357,7 @@ fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR {
|
|||
|
||||
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
|
||||
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
}
|
||||
|
|
@ -333,11 +369,11 @@ fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR {
|
|||
|
||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||
|
||||
compile_expression(c, t.nth_tree(2)?);
|
||||
compile_expression(c, tr.nth_tree(2)?);
|
||||
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
}
|
||||
_ => panic!("Unsupported binary expression"),
|
||||
_ => ice!(c, t, "Unsupported binary expression"),
|
||||
}
|
||||
OK
|
||||
}
|
||||
|
|
@ -347,28 +383,69 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> O
|
|||
let environment = c.semantics.environment_of(t);
|
||||
let declaration = environment.bind(ident)?;
|
||||
|
||||
let instruction = match declaration.location {
|
||||
Location::Local => {
|
||||
if declaration.index >= c.function.locals {
|
||||
c.function.locals = declaration.index + 1;
|
||||
// TODO: Load function declaration. :P
|
||||
let instruction = match declaration {
|
||||
Declaration::Variable {
|
||||
location, index, ..
|
||||
} => {
|
||||
let index = *index;
|
||||
match location {
|
||||
Location::Local => {
|
||||
if index >= c.function.locals {
|
||||
c.function.locals = index + 1;
|
||||
}
|
||||
Instruction::LoadLocal(index)
|
||||
}
|
||||
Location::Argument => {
|
||||
compiler_assert!(c, t, index < c.function.args);
|
||||
Instruction::LoadArgument(index)
|
||||
}
|
||||
Location::Module => {
|
||||
compiler_assert!(c, t, index < c.module.globals);
|
||||
Instruction::LoadModule(index)
|
||||
}
|
||||
}
|
||||
Instruction::LoadLocal(declaration.index)
|
||||
}
|
||||
Location::Argument => {
|
||||
compiler_assert!(c, t, declaration.index < c.function.args);
|
||||
Instruction::LoadArgument(declaration.index)
|
||||
}
|
||||
Location::Module => {
|
||||
compiler_assert!(c, t, declaration.index < c.module.globals);
|
||||
Instruction::LoadModule(declaration.index)
|
||||
Declaration::Function { declaration, .. } => {
|
||||
let index = match c.function_bindings.get(declaration) {
|
||||
Some(index) => *index,
|
||||
None => {
|
||||
let tree = &c.syntax[*declaration];
|
||||
compiler_assert_eq!(c, t, tree.kind, TreeKind::FunctionDecl);
|
||||
compile_function_declaration(c, t, tree, false)?;
|
||||
*c.function_bindings
|
||||
.get(declaration)
|
||||
.expect("did not compile the function!")
|
||||
}
|
||||
};
|
||||
Instruction::LoadFunction(index)
|
||||
}
|
||||
Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()),
|
||||
};
|
||||
|
||||
c.push(instruction);
|
||||
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
||||
fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
let arg_list = tree.child_tree_of_kind(c.syntax, TreeKind::ArgumentList)?;
|
||||
let mut args: Vec<_> = arg_list.child_trees().collect();
|
||||
let arg_count = args.len();
|
||||
|
||||
args.reverse();
|
||||
for arg in args {
|
||||
compile_expression(c, arg);
|
||||
}
|
||||
|
||||
let func = tree.nth_tree(0)?;
|
||||
compile_expression(c, func);
|
||||
|
||||
c.push(Instruction::Call(arg_count));
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
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 };
|
||||
|
||||
|
|
@ -379,14 +456,19 @@ fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
compile_expression(c, tree.nth_tree(0)?);
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||
let tree = &c.semantics.tree()[t];
|
||||
let cr = match tree.kind {
|
||||
TreeKind::FunctionDecl => compile_function_declaration(c, tree, gen_value),
|
||||
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
|
||||
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
|
||||
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
||||
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
||||
_ => panic!("unsupported tree kind {:?}", tree.kind),
|
||||
_ => ice!(c, t, "unsupported tree kind {:?}", tree.kind),
|
||||
};
|
||||
if matches!(cr, None) {
|
||||
c.push(Instruction::Panic);
|
||||
|
|
@ -424,20 +506,28 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b
|
|||
let environment = c.semantics.environment_of(t);
|
||||
let declaration = environment.bind(tree.nth_token(1)?)?;
|
||||
|
||||
let instruction = match declaration.location {
|
||||
let Declaration::Variable {
|
||||
location, index, ..
|
||||
} = declaration
|
||||
else {
|
||||
ice!(c, t, "let cannot make a non-variable declaration")
|
||||
};
|
||||
|
||||
let index = *index;
|
||||
let instruction = match location {
|
||||
Location::Local => {
|
||||
if declaration.index >= c.function.locals {
|
||||
c.function.locals = declaration.index + 1;
|
||||
if index >= c.function.locals {
|
||||
c.function.locals = index + 1;
|
||||
}
|
||||
Instruction::StoreLocal(declaration.index)
|
||||
Instruction::StoreLocal(index)
|
||||
}
|
||||
Location::Module => {
|
||||
if declaration.index >= c.module.globals {
|
||||
c.module.globals = declaration.index + 1;
|
||||
if index >= c.module.globals {
|
||||
c.module.globals = index + 1;
|
||||
}
|
||||
Instruction::StoreModule(declaration.index)
|
||||
Instruction::StoreModule(index)
|
||||
}
|
||||
_ => panic!("unsuitable location for let declaration"),
|
||||
_ => ice!(c, t, "unsuitable location for let declaration"),
|
||||
};
|
||||
c.push(instruction);
|
||||
if gen_value {
|
||||
|
|
@ -447,30 +537,26 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_function_declaration(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||
let name = tree.nth_token(1)?;
|
||||
let block = if tree
|
||||
.nth_token(3)
|
||||
.is_some_and(|t| t.kind == TokenKind::Arrow)
|
||||
{
|
||||
tree.nth_tree(4)?
|
||||
} else {
|
||||
tree.nth_tree(3)?
|
||||
};
|
||||
fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
|
||||
// Only compile a given function once.
|
||||
// TODO: This should actually be compiled on access! How is this going to work??
|
||||
if !c.function_bindings.contains_key(&t) {
|
||||
let name = tree.nth_token(1)?;
|
||||
|
||||
let arg_list = tree.nth_tree(2)?;
|
||||
let arg_count = c.syntax[arg_list].children.len() - 2;
|
||||
let block = tree.child_of_kind(c.syntax, TreeKind::Block)?;
|
||||
let param_list = tree.child_tree_of_kind(c.syntax, TreeKind::ParamList)?;
|
||||
let param_count = param_list.children.len() - 2;
|
||||
|
||||
let mut prev = Function::new(name.as_str(), arg_count);
|
||||
std::mem::swap(&mut c.function, &mut prev);
|
||||
let mut prev = Function::new(name.as_str(), param_count);
|
||||
std::mem::swap(&mut c.function, &mut prev);
|
||||
|
||||
c.function_bindings
|
||||
.insert(c.function.name.clone(), c.module.functions.len());
|
||||
c.function_bindings.insert(t, c.module.functions.len());
|
||||
|
||||
compile_expression(c, block);
|
||||
compile_expression(c, block);
|
||||
|
||||
std::mem::swap(&mut c.function, &mut prev);
|
||||
c.module.functions.push(prev);
|
||||
std::mem::swap(&mut c.function, &mut prev);
|
||||
c.module.functions.push(prev);
|
||||
}
|
||||
|
||||
if gen_value {
|
||||
c.push(Instruction::PushNothing);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue