[fine] Fully untested compiler
This commit is contained in:
parent
f2b9eae339
commit
d14c9a72df
3 changed files with 286 additions and 31 deletions
|
|
@ -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!()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue