oden/fine/src/compiler.rs

1299 lines
41 KiB
Rust

use std::collections::HashMap;
use std::rc::Rc;
use crate::{
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
semantics::{string_constant_to_string, Declaration, Environment, Location, Semantics, Type},
tokens::TokenKind,
};
pub const EXTERN_BUILTIN_NOOP: usize = 0;
pub const EXTERN_BUILTIN_LIST_GET_ITERATOR: usize = 1;
pub const EXTERN_BUILTIN_LIST_ITERATOR_NEXT: usize = 2;
pub const EXTERN_USER_FIRST: usize = 100000;
// TODO: If I were cool this would by actual bytecode.
// But I'm not cool.
#[derive(Debug, Clone, Copy)]
pub enum Instruction {
Panic,
BoolNot,
Call(usize),
Discard,
Dup,
EqBool,
EqFloat,
EqString,
FloatAdd,
FloatDivide,
FloatMultiply,
FloatSubtract,
GreaterFloat,
GreaterString,
IsBool,
IsClass(i64),
IsFloat,
IsNothing,
IsString,
Jump(usize),
JumpFalse(usize),
JumpTrue(usize), // TODO: Only one of these, and use BoolNot?
LessFloat,
LessString,
LoadArgument(usize),
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
LoadFunction(usize),
LoadLocal(usize),
LoadModule(usize),
LoadSlot(usize),
NewObject(usize),
PushFalse,
PushFloat(f64),
PushInt(i64),
PushNothing,
PushString(usize),
PushTrue,
Return,
StoreArgument(usize),
StoreLocal(usize),
StoreModule(usize),
StoreSlot(usize),
StringAdd,
NewList(usize),
}
pub enum Export {
Function(usize),
Global(usize),
}
pub struct Module {
pub functions: Vec<Rc<Function>>, // Functions
pub globals: usize, // The number of global variables
pub exports: HashMap<String, Export>, // Exports by name
pub init: usize, // The index of the initialization function
}
impl Module {
pub fn new() -> Self {
Module {
functions: Vec::new(),
globals: 0,
exports: HashMap::new(),
init: 0,
}
}
pub fn functions(&self) -> &[Rc<Function>] {
&self.functions
}
}
// TODO: Debug information.
pub struct Function {
name: String,
instructions: Vec<Instruction>,
strings: Vec<Rc<str>>,
args: usize, // TODO: Probably type information too?
locals: usize, // TODO: Same?
}
impl Function {
pub fn new(name: &str, args: usize) -> Self {
Function {
name: name.to_string(),
instructions: Vec::new(),
strings: Vec::new(),
args,
locals: 0,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn args(&self) -> usize {
self.args
}
pub fn locals(&self) -> usize {
self.locals
}
pub fn strings(&self) -> &[Rc<str>] {
&self.strings
}
pub fn instructions(&self) -> &[Instruction] {
&self.instructions
}
}
impl std::fmt::Debug for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"fn {} ({} args, {} locals) ...",
self.name, self.args, self.locals
)
}
}
#[derive(Eq, PartialEq, Hash, Clone)]
struct FunctionKey {
tree: TreeRef,
}
struct Compiler<'a> {
source: &'a str,
semantics: &'a Semantics,
syntax: &'a SyntaxTree,
function_bindings: HashMap<FunctionKey, usize>,
pending_functions: Vec<(FunctionKey, usize, Function)>,
temp_functions: Vec<Option<Rc<Function>>>,
module: Module,
function: Function,
}
impl<'a> Compiler<'a> {
fn add_string(&mut self, result: String) -> usize {
let index = self.function.strings.len();
self.function.strings.push(result.into());
index
}
fn push(&mut self, inst: Instruction) -> usize {
let index = self.function.instructions.len();
self.function.instructions.push(inst);
index
}
fn patch(&mut self, i: usize, f: impl FnOnce(usize) -> Instruction) {
let index = self.function.instructions.len();
self.function.instructions[i] = f(index);
}
}
macro_rules! compiler_assert_eq {
($compiler:expr, $tr:expr, $ll:expr, $rr:expr $(,)?) => {{
let left = &$ll;
let right = &$rr;
if left != right {
$compiler.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 {
$compiler.semantics.dump_compiler_state(Some($tr));
assert_eq!(left, right, $($t)*);
}
}};
}
macro_rules! compiler_assert {
($compiler:expr, $tr:expr, $cond:expr $(,)?) => {{
if !$cond {
$compiler.semantics.dump_compiler_state(Some($tr));
assert!($cond);
}
}};
($compiler:expr, $tr:expr, $cond:expr, $($arg:tt)+) => {{
if !$cond {
$compiler.semantics.dump_compiler_state(Some($tr));
assert!($cond, $($arg)*);
}
}};
}
macro_rules! ice {
($compiler:expr, $tr:expr, $($t:tt)+) => {{
$compiler.semantics.dump_compiler_state(Some($tr));
panic!($($t)*)
}}
}
macro_rules! inst_panic {
($($t:tt)+) => {{
// eprintln!($($t)*);
Instruction::Panic
}};
}
// macro_rules! ice {
// ($compiler:expr, $tr:expr, $($t:tt)*) => {{}};
// }
pub fn compile(semantics: &Semantics) -> Rc<Module> {
let source = semantics.source();
let syntax_tree = semantics.tree();
let mut compiler = Compiler {
source: &source,
semantics: &semantics,
syntax: &syntax_tree,
function_bindings: HashMap::new(),
pending_functions: Vec::new(),
temp_functions: Vec::new(),
module: Module::new(),
function: Function::new("<< module >>", 0),
};
if let Some(t) = semantics.tree().root() {
compiler.temp_functions.push(None);
file(&mut compiler, t);
compiler.temp_functions[0] = Some(Rc::new(compiler.function));
compiler.module.init = 0;
}
while let Some((fk, idx, func)) = compiler.pending_functions.pop() {
if idx >= compiler.temp_functions.len() {
compiler.temp_functions.resize(idx + 1, None);
}
compiler.function = func;
compile_function(&mut compiler, fk.tree);
compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
}
let mut module = compiler.module;
for f in compiler.temp_functions {
module.functions.push(f.unwrap());
}
Rc::new(module)
}
fn file(c: &mut Compiler, t: TreeRef) {
let tree = &c.syntax[t];
compiler_assert_eq!(c, t, tree.kind, TreeKind::File, "must be compiling a file");
let children: Vec<_> = tree.child_trees().collect();
if children.len() == 0 {
c.push(Instruction::PushNothing);
} else {
for i in 0..children.len() - 1 {
compile_statement(c, children[i], false);
}
compile_statement(c, *children.last().unwrap(), true);
}
c.push(Instruction::Return);
}
type CR = Option<()>;
const OK: CR = CR::Some(());
fn compile_expression(c: &mut Compiler, t: TreeRef) {
let tree = &c.syntax[t];
let cr = match tree.kind {
TreeKind::Error => None,
TreeKind::Argument => compile_argument(c, tree),
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
TreeKind::Block => compile_block_expression(c, tree),
TreeKind::CallExpression => compile_call_expression(c, tree),
TreeKind::ConditionalExpression => compile_condition_expression(c, tree),
TreeKind::FieldValue => compile_field_value(c, t, tree),
TreeKind::GroupingExpression => compile_grouping(c, tree),
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
TreeKind::IsExpression => compile_is_expression(c, tree),
TreeKind::ListConstructor => compile_list_constructor(c, tree),
TreeKind::ListConstructorElement => compile_list_constructor_element(c, tree),
TreeKind::LiteralExpression => compile_literal(c, t, tree),
TreeKind::MemberAccess => compile_member_access(c, t, tree),
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
TreeKind::SelfReference => compile_self_reference(c),
TreeKind::UnaryExpression => compile_unary_operator(c, t, tree),
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
};
if matches!(cr, None) {
c.push(inst_panic!("panic compiling expression {:?}", tree));
}
}
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
let tok = tr.nth_token(0)?;
match c.semantics.type_of(t) {
Type::F64 => c.push(Instruction::PushFloat(
tok.as_str(c.source).parse().unwrap(),
)),
Type::Bool => c.push(if tok.kind == TokenKind::True {
Instruction::PushTrue
} else {
Instruction::PushFalse
}),
Type::String => {
let result = string_constant_to_string(tok.as_str(c.source));
let index = c.add_string(result);
c.push(Instruction::PushString(index))
}
Type::Error => c.push(inst_panic!("compiling literal {:?}", tr)),
_ => ice!(c, t, "unsupported literal type: {t:?}"),
};
OK
}
fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR {
compile_expression(c, t.nth_tree(1)?);
OK
}
fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
compile_expression(c, tr.nth_tree(1)?);
let tok = tr.nth_token(0)?;
match tok.kind {
TokenKind::Minus => {
c.push(Instruction::PushFloat(-1.0));
c.push(Instruction::FloatMultiply);
}
TokenKind::Bang => {
c.push(Instruction::BoolNot);
}
_ => ice!(c, t, "unsupported unary operator"),
}
OK
}
fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
let condition = t.nth_tree(1)?;
compile_expression(c, condition);
let jump_else_index = c.push(Instruction::JumpFalse(0));
let then_branch = t.nth_tree(2)?;
compile_expression(c, then_branch);
let jump_end_index = c.push(Instruction::Jump(0));
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
if let Some(else_branch) = t.nth_tree(4) {
compile_expression(c, else_branch);
} else {
c.push(Instruction::PushNothing);
}
c.patch(jump_end_index, |i| Instruction::Jump(i));
OK
}
fn compile_simple_binary_expression<T>(c: &mut Compiler, tr: &Tree, f: T) -> CR
where
T: FnOnce(&mut Compiler, &Type) -> Instruction,
{
compile_expression(c, tr.nth_tree(0)?);
let arg_tree = tr.nth_tree(2)?;
let arg_type = c.semantics.type_of(arg_tree);
compile_expression(c, arg_tree);
let inst = f(c, &arg_type);
c.push(inst);
OK
}
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
let op = tr.nth_token(1)?;
match op.kind {
TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::FloatAdd,
Type::String => Instruction::StringAdd,
_ => inst_panic!("panic adding {}", t),
}),
TokenKind::Minus => {
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatSubtract)
}
TokenKind::Star => {
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatMultiply)
}
TokenKind::Slash => {
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide)
}
TokenKind::Less => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString,
_ => inst_panic!("panic less {}", t),
}),
TokenKind::LessEqual => {
compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::GreaterFloat,
Type::String => Instruction::GreaterString,
_ => inst_panic!("panic less equal {}", t),
});
c.push(Instruction::BoolNot);
OK
}
TokenKind::Greater => compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::GreaterFloat,
Type::String => Instruction::GreaterString,
_ => inst_panic!("panic greater {}", t),
}),
TokenKind::GreaterEqual => {
compile_simple_binary_expression(c, tr, |_, t| match t {
Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString,
_ => inst_panic!("panic greater equal {}", t),
});
c.push(Instruction::BoolNot);
OK
}
TokenKind::And => {
// Compile the left hand side, it leaves a bool on the stack
compile_expression(c, tr.nth_tree(0)?);
// If the first part is true (hooray!) then we need to evaluate
// the right hand side, so jump around the short circuit...
let jump_true_index = c.push(Instruction::JumpTrue(0));
// ...but if the first part is false then we stop here. We need
// to leave a value on the stack (it was consumed by jump above)
// so we push an extra False here, and jump to the end.
c.push(Instruction::PushFalse);
let jump_end_index = c.push(Instruction::Jump(0));
// Here we are, we consumed the `true` off the stack now time to
// do the right hand side.
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
// The right hand side leaves true or false on the stack, it's
// the result of the expression.
compile_expression(c, tr.nth_tree(2)?);
// (here's where you go after you leave the "false" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i));
OK
}
TokenKind::Or => {
// Compile the left hand side, it leaves a bool on the stack
compile_expression(c, tr.nth_tree(0)?);
// If the first part is false (boo!) then we need to evaluate the
// right hand side, so jump around the short circuit...
let jump_false_index = c.push(Instruction::JumpFalse(0));
// ...but if the first part os true then we stop here. We need to
// leave a value on the stack (it was consumed by jump above) so
// we push an extra True here and jump to the end.
c.push(Instruction::PushTrue);
let jump_end_index = c.push(Instruction::Jump(0));
// Here we are, we consumed the `false` off the stack now time to
// do the right hand side.
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
// The right hand side leaves true or false on the stack, it's
// the result of the expression.
compile_expression(c, tr.nth_tree(2)?);
// (here's where you go after you leave "true" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i));
OK
}
TokenKind::EqualEqual => {
compile_simple_binary_expression(c, tr, |c, arg_type| {
if c.semantics.can_convert(&arg_type, &Type::Nothing) {
c.push(Instruction::Discard);
c.push(Instruction::Discard);
Instruction::PushTrue
} else {
match arg_type {
Type::F64 => Instruction::EqFloat,
Type::String => Instruction::EqString,
Type::Bool => Instruction::EqBool, // ?
_ => inst_panic!("panic comparing {}", arg_type),
}
}
})
}
TokenKind::Equal => {
compile_expression(c, tr.nth_tree(2)?);
c.push(Instruction::Dup);
let lvalue = tr.nth_tree(0)?;
let ltree = &c.syntax[lvalue];
#[allow(unused_assignments)]
let mut environment = Environment::error();
let declaration = match ltree.kind {
// TODO: Assign to list access
TreeKind::Identifier => {
let id = ltree.nth_token(0)?.as_str(&c.source);
environment = c.semantics.environment_of(lvalue);
environment.bind(id)?
}
TreeKind::MemberAccess => {
let id = ltree.nth_token(2)?.as_str(&c.source);
let t = ltree.nth_tree(0)?;
let typ = c.semantics.type_of(t);
environment = c.semantics.member_environment(t, &typ);
environment.bind(id)?
}
_ => return None,
};
let instruction = match declaration {
Declaration::Variable {
location, index, ..
} => {
let index = *index;
match location {
Location::Argument => {
compiler_assert!(c, t, index < c.function.args);
Instruction::StoreArgument(index)
}
Location::Local => {
if index >= c.function.locals {
c.function.locals = index + 1;
}
Instruction::StoreLocal(index)
}
Location::Module => {
compiler_assert!(c, t, index < c.module.globals);
Instruction::StoreModule(index)
}
Location::Slot => {
compile_expression(c, ltree.nth_tree(0)?);
Instruction::StoreSlot(index)
}
}
}
Declaration::ExternFunction { .. } => inst_panic!("store ext"),
Declaration::Function { .. } => inst_panic!("store func"),
Declaration::Class { .. } => inst_panic!("store class"),
Declaration::Import { .. } => inst_panic!("store import"),
};
c.push(instruction);
OK
}
_ => ice!(
c,
t,
"Unsupported binary expression '{}'",
op.as_str(&c.source)
),
}
}
fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> {
let ident = tree.nth_token(0)?.as_str(&c.source);
let environment = c.semantics.environment_of(t);
let declaration = environment.bind(ident)?;
compile_load_declaration(c, t, declaration)
}
fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declaration) -> CR {
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)
}
Location::Slot => {
// TODO: Assert slot is in field range?
Instruction::LoadSlot(index)
}
}
}
Declaration::Function { declaration, .. } => {
let key = FunctionKey { tree: *declaration };
let index = match c.function_bindings.get(&key) {
Some(index) => *index,
None => {
let tree = &c.syntax[*declaration];
compiler_assert_eq!(c, t, tree.kind, TreeKind::FunctionDecl);
compile_function_declaration(c, *declaration, tree, false)?;
match c.function_bindings.get(&key) {
Some(index) => *index,
None => {
ice!(
c,
t,
"did not compile the function with key {:?}!",
declaration
)
}
}
}
};
Instruction::LoadFunction(index)
}
Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()),
// Must be a static don't worry about it.
Declaration::Class { .. } => return OK,
// fix later
Declaration::Import { .. } => ice!(c, t, "import compile not supported"),
};
c.push(instruction);
OK
}
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
compile_expression(c, tree.nth_tree(0)?);
compile_pattern(c, tree.nth_tree(2)?)
}
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
let tree = &c.syntax[t];
// Let's *try* to generate good code in the presence of a wildcard pattern....
let is_wildcard = tree
.child_tree_of_kind(&c.syntax, TreeKind::WildcardPattern)
.is_some();
let type_expr = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeExpression);
let and_index = tree.children.iter().position(|c| match c {
Child::Token(t) => t.kind == TokenKind::And,
_ => false,
});
// If you have a binding, dup and store now, it is in scope.
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
if let Some(variable) = binding.nth_token(0) {
let environment = c.semantics.environment_of(t);
let declaration = environment.bind(variable.as_str(&c.source))?;
let Declaration::Variable {
location: Location::Local,
index,
..
} = declaration
else {
ice!(c, t, "is cannot make a non-local, non-variable declaration")
};
// If we aren't a wildcard or we have an attached predicate then
// we will need the value on the stack, otherwise we can discard
// it.
if and_index.is_some() || !is_wildcard {
c.push(Instruction::Dup);
}
c.push(Instruction::StoreLocal(*index));
}
}
if !is_wildcard {
let type_expr = type_expr?;
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
}
if let Some(and_index) = and_index {
let jump_end_index = if is_wildcard {
// If the pattern was a wildcard then don't bother with this jump
// nonsense; we know the pattern matched, all we need to do is
// evaluate the predicate.
None
} else {
// Otherwise test the pattern to see if it passed; if it did then
// we need to run the predicate. (This is the back half of an AND
// expression.)
let jump_true_index = c.push(Instruction::JumpTrue(0));
c.push(Instruction::PushFalse);
let jump_end_index = c.push(Instruction::Jump(0));
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
Some(jump_end_index)
};
compile_expression(c, tree.nth_tree(and_index + 1)?);
// If we wound up with a jump what needs patching, patch it.
if let Some(jump_end_index) = jump_end_index {
c.patch(jump_end_index, |i| Instruction::Jump(i));
}
} else if is_wildcard {
// If there was no predicate *and* the pattern was a wildcard then
// I'll just need to push true here.
c.push(Instruction::PushTrue);
}
OK
}
fn compile_type_expr_eq(c: &mut Compiler, t: TreeRef) {
let tree = &c.syntax[t];
let result = match tree.kind {
TreeKind::TypeIdentifier => compile_type_identifier_eq(c, t, tree),
TreeKind::AlternateType => compile_type_alternate_eq(c, tree),
_ => ice!(c, t, "tree is not a type expression"),
};
if result.is_none() {
c.push(inst_panic!("panic compiling type expression eq {:?}", tree));
}
}
fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
let identifier = tree.nth_token(0)?.as_str(&c.source);
match identifier {
"f64" => {
c.push(Instruction::IsFloat);
}
"string" => {
c.push(Instruction::IsString);
}
"bool" => {
c.push(Instruction::IsBool);
}
"nothing" => {
c.push(Instruction::IsNothing);
}
_ => {
let environment = c.semantics.environment_of(t);
match environment.bind(identifier)? {
Declaration::Class { declaration, .. } => {
// The runtime identifier of the class is the tree index of the
// class declaration sure why not.
let index = declaration.index();
c.push(Instruction::IsClass(index.try_into().unwrap()));
}
_ => return None,
}
}
};
OK
}
fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
// Compile the left hand side, it leaves a bool on the stack
compile_type_expr_eq(c, tree.nth_tree(0)?);
// If the first part is false (boo!) then we need to evaluate the
// right hand side, so jump around the short circuit...
let jump_false_index = c.push(Instruction::JumpFalse(0));
// ...but if the first part is true then we stop here. We need to
// leave a value on the stack (it was consumed by jump above) so
// we push an extra True here and jump to the end.
c.push(Instruction::PushTrue);
let jump_end_index = c.push(Instruction::Jump(0));
// Here we are, we consumed the `false` off the stack now time to
// do the right hand side.
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
// The right hand side leaves true or false on the stack, it's
// the result of the expression.
compile_type_expr_eq(c, tree.nth_tree(2)?);
// (here's where you go after you leave "true" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i));
OK
}
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)?;
let func_type = c.semantics.type_of(func);
let arg_count = match func_type {
// TODO: Consider being guided by syntax here?
Type::Method(..) => arg_count + 1,
_ => arg_count,
};
compile_expression(c, func);
c.push(Instruction::Call(arg_count));
OK
}
fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> CR {
if tree.children.len() == 2 {
c.push(Instruction::PushNothing);
return OK;
}
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(c, tree.nth_tree(i)?, false);
}
compile_statement(c, tree.nth_tree(last_index)?, true);
OK
}
fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, tree.nth_tree(0)?);
OK
}
fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
// We pass in the arguments.... by... field order?
let Type::Object(ct, _) = c.semantics.type_of(t) else {
c.push(inst_panic!("new obj not ob"));
return OK;
};
let class = c.semantics.class_of(ct);
let field_list = tree.child_tree_of_kind(&c.syntax, TreeKind::FieldList)?;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) {
let f = &c.syntax[field];
let name = f.nth_token(0)?;
field_bindings.insert(name.as_str(&c.source), field);
}
// The fields come in this order and since arguments are backwards
// (stack!) we compile them in reverse order. Missing fields panic,
// obviously.
for field in class.fields.iter().rev() {
let binding = field_bindings.get(&*field.name)?;
compile_expression(c, *binding);
}
// Fetch the correct constructor.
// TODO: Binding this type should be done by semantics, and we should borrow it.
let type_reference = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)?;
let identifier = type_reference.nth_token(0)?.as_str(&c.source);
let environment = c.semantics.environment_of(t);
match environment.bind(identifier)? {
Declaration::Class { declaration, .. } => {
let key = FunctionKey { tree: *declaration };
let index = match c.function_bindings.get(&key) {
Some(index) => *index,
None => {
let tree = &c.syntax[*declaration];
compiler_assert_eq!(c, t, tree.kind, TreeKind::ClassDecl);
compile_class_declaration(c, t, tree, false)?;
*c.function_bindings
.get(&key)
.expect("did not compile the class constructor!")
}
};
c.push(Instruction::LoadFunction(index));
}
_ => return None,
}
c.push(Instruction::Call(class.fields.len()));
OK
}
fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
if let Some(colon) = tree.nth_token(1) {
if colon.kind == TokenKind::Colon {
compile_expression(c, tree.nth_tree(2)?);
return OK;
}
}
// Form 2: { x, ... }
let environment = c.semantics.environment_of(t);
let id = tree.nth_token(0)?.as_str(&c.source);
let declaration = environment.bind(id)?;
compile_load_declaration(c, t, declaration)
}
fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
// In member access; the lhs sets up the object and in theory the rhs
// binds against it. ::shrug::
//
compile_expression(c, tree.nth_tree(0)?);
let typ = c.semantics.type_of(tree.nth_tree(0)?);
let ident = tree.nth_token(2)?.as_str(&c.source);
let environment = match &typ {
Type::Object(ct, _) => {
let class = c.semantics.class_of(*ct);
class.env.clone()
}
Type::Class(ct, _) => {
let class = c.semantics.class_of(*ct);
class.static_env.clone()
}
_ => {
c.push(inst_panic!("cannot get environment of {typ}"));
return None;
}
};
let declaration = environment.bind(ident)?;
// NOTE: If this is a method call we still don't have to do anything
// special here, since the load of the member function will *not*
// consume the self pointer from the stack.
compile_load_declaration(c, t, declaration);
OK
}
fn compile_self_reference(c: &mut Compiler) -> CR {
c.push(Instruction::LoadArgument(0));
OK
}
fn compile_list_constructor(c: &mut Compiler, tree: &Tree) -> CR {
let mut children: Vec<_> = tree
.children_of_kind(&c.syntax, TreeKind::ListConstructorElement)
.collect();
children.reverse();
let count = children.len();
for child in children {
compile_expression(c, child);
}
c.push(Instruction::NewList(count));
OK
}
fn compile_list_constructor_element(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::Error => None,
TreeKind::Import => compile_import_statement(c, gen_value),
TreeKind::Block => compile_block_statement(c, t, gen_value),
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
TreeKind::ForStatement => compile_for_statement(c, tree, gen_value),
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
TreeKind::ReturnStatement => compile_return_statement(c, tree),
TreeKind::WhileStatement => compile_while_statement(c, tree, gen_value),
_ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind),
};
if matches!(cr, None) {
c.push(inst_panic!("stat {:?}", tree));
}
}
fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
compile_expression(c, tree.nth_tree(0)?);
if !gen_value {
c.push(Instruction::Discard);
}
OK
}
fn compile_expression_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
if let Some(expr) = tree.nth_tree(0) {
compile_expression(c, expr);
if tree
.nth_token(1)
.is_some_and(|t| t.kind == TokenKind::Semicolon)
{
c.push(Instruction::Discard);
if gen_value {
c.push(Instruction::PushNothing);
}
} else if !gen_value {
c.push(Instruction::Discard);
}
} else if gen_value {
c.push(Instruction::PushNothing);
};
OK
}
fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
compile_expression(c, tree.nth_tree(3)?);
let environment = c.semantics.environment_of(t);
let declaration = environment.bind(tree.nth_token(1)?.as_str(&c.source))?;
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 index >= c.function.locals {
c.function.locals = index + 1;
}
Instruction::StoreLocal(index)
}
Location::Module => {
if index >= c.module.globals {
c.module.globals = index + 1;
}
Instruction::StoreModule(index)
}
_ => ice!(c, t, "unsuitable location for let declaration"),
};
c.push(instruction);
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
// Only compile a given function once.
//
// TODO: When it's time for generics, this should only actually compile
// if we have no unbound type variables.
let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) {
// TODO: If this is a method the name should be different.
let name = tree.nth_token(1)?.as_str(&c.source);
let param_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ParamList)?;
let param_count = param_list.children.len() - 2;
let function_index = c.temp_functions.len();
c.temp_functions.push(None);
c.pending_functions
.push((fk.clone(), function_index, Function::new(name, param_count)));
c.function_bindings.insert(fk, function_index);
c.module
.exports
.insert(name.to_string(), Export::Function(function_index));
}
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
// Only compile a given function once.
// Classes get compiled as constructor functions which get called.
let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) {
let name = tree.nth_token(1)?.as_str(&c.source);
let field_count = tree.children.len() - 2;
let function_index = c.temp_functions.len();
c.temp_functions.push(None);
c.pending_functions
.push((fk.clone(), function_index, Function::new(name, field_count)));
c.function_bindings.insert(fk, function_index);
c.module
.exports
.insert(name.to_string(), Export::Function(function_index));
}
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
let tree = &c.syntax[t];
match tree.kind {
TreeKind::FunctionDecl => {
let block = tree.child_of_kind(&c.syntax, TreeKind::Block)?;
compile_expression(c, block);
}
TreeKind::ClassDecl => {
let count = tree
.children_of_kind(&c.syntax, TreeKind::FieldDecl)
.count();
for i in 0..count {
c.push(Instruction::LoadArgument(count - 1 - i));
}
let name = tree.nth_token(1)?.as_str(&c.source);
let name_index = c.add_string(name.to_string());
c.push(Instruction::PushString(name_index));
c.push(Instruction::PushInt(t.index().try_into().unwrap()));
c.push(Instruction::NewObject(count));
}
_ => ice!(c, t, "what is this tree doing in compile_function?"),
}
c.push(Instruction::Return);
OK
}
fn compile_block_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) -> CR {
compile_expression(c, t);
if !gen_value {
c.push(Instruction::Discard);
}
OK
}
fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
let start_index = c.function.instructions.len();
compile_expression(c, tree.nth_tree(1)?);
let jump_end_index = c.push(Instruction::JumpFalse(0));
compile_block_statement(c, tree.nth_tree(2)?, false);
c.push(Instruction::Jump(start_index));
c.patch(jump_end_index, |i| Instruction::JumpFalse(i));
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_return_statement(c: &mut Compiler, tree: &Tree) -> CR {
if let Some(expr) = tree.nth_tree(1) {
compile_expression(c, expr);
} else {
c.push(Instruction::PushNothing);
}
c.push(Instruction::Return);
OK
}
fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
// Figure out the variable.
let vt = tree.nth_tree(1)?;
let var = &c.syntax[vt];
let id = var.nth_token(0)?.as_str(&c.source);
let body = tree.nth_tree(4)?;
let env = c.semantics.environment_of(body);
let Some(variable_decl) = env.bind(id) else {
ice!(c, body, "Unable to bind {id} in loop body");
};
let variable_slot = match variable_decl {
Declaration::Variable {
location, index, ..
} => {
compiler_assert_eq!(
c,
vt,
*location,
Location::Local,
"expected loop variable to be local"
);
*index
}
_ => ice!(c, vt, "loop variable was not a variable"),
};
// Figure out the generator.
let iterable = tree.nth_tree(3)?;
compile_expression(c, iterable);
// call 'get_iterator'
let iterable_typ = c.semantics.type_of(iterable);
match iterable_typ {
Type::List(_) => {
c.push(Instruction::LoadExternFunction(
EXTERN_BUILTIN_LIST_GET_ITERATOR,
));
c.push(Instruction::Call(1));
}
_ => return None, // TODO: Bind and call get_iterator() on type of iterable
}
// iterate
// (Stack is clear except for the iterator.)
let loop_top = c.push(Instruction::Dup); // Save the iterator
match iterable_typ {
Type::List(_) => {
c.push(Instruction::LoadExternFunction(
EXTERN_BUILTIN_LIST_ITERATOR_NEXT,
));
}
_ => return None, // TODO: Bind and call next() on type of iterator
}
c.push(Instruction::Call(1)); // Call 'next'
c.push(Instruction::Dup); // Save the result
c.push(Instruction::IsNothing); // Check to see if iteration is done
let jump_to_end = c.push(Instruction::JumpTrue(0));
c.push(Instruction::StoreLocal(variable_slot)); // Store the value
// (Stack is clear except for the iterator.)
compile_statement(c, body, false); // Run the body
// (Stack is clear except for the iterator.)
c.push(Instruction::Jump(loop_top));
// Clean up the loop.
c.patch(jump_to_end, |i| Instruction::JumpTrue(i));
c.push(Instruction::Discard); // Drop the unused value
c.push(Instruction::Discard); // Drop the iterator
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_import_statement(c: &mut Compiler, gen_value: bool) -> CR {
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}