Compare commits

..

3 Commits

Author SHA1 Message Date
Jesse Brault
ebca87ddb9 A lot of work just to do subtraction. 2026-03-09 19:22:35 -05:00
Jesse Brault
e35bacb583 Bunch of AST refactoring to make api easier. 2026-03-09 16:35:32 -05:00
Jesse Brault
9d09f7481b Add unary minus and subtraction to lexer/parser/ast node kinds. 2026-03-09 12:33:14 -05:00
35 changed files with 1126 additions and 342 deletions

View File

@ -47,14 +47,26 @@ fn main() {
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table); match compilation_unit.gather_declared_names(&mut symbol_table) {
check_and_report_diagnostics(&files, script_file_id, &gather_names_diagnostics); Ok(_) => {}
Err(diagnostics) => {
report_and_exit(&diagnostics, script_file_id, &files);
}
}
let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table); match compilation_unit.check_name_usages(&symbol_table) {
check_and_report_diagnostics(&files, script_file_id, &name_usages_diagnostics); Ok(_) => {}
Err(diagnostics) => {
report_and_exit(&diagnostics, script_file_id, &files);
}
}
let type_check_diagnostics = compilation_unit.type_check(&symbol_table); match compilation_unit.type_check(&symbol_table) {
check_and_report_diagnostics(&files, script_file_id, &type_check_diagnostics); Ok(_) => {}
Err(diagnostics) => {
report_and_exit(&diagnostics, script_file_id, &files);
}
}
let mut ir_functions = compilation_unit.to_ir(&symbol_table); let mut ir_functions = compilation_unit.to_ir(&symbol_table);
@ -118,6 +130,15 @@ fn check_and_report_diagnostics(
diagnostics: &[Diagnostic], diagnostics: &[Diagnostic],
) { ) {
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
report_and_exit(diagnostics, script_file_id, files);
}
}
fn report_and_exit(
diagnostics: &[Diagnostic],
script_file_id: usize,
files: &SimpleFiles<&str, &str>,
) -> ! {
let writer = StandardStream::stderr(ColorChoice::Always); let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default(); let config = term::Config::default();
for diagnostic in diagnostics { for diagnostic in diagnostics {
@ -139,5 +160,4 @@ fn check_and_report_diagnostics(
); );
} }
std::process::exit(1); std::process::exit(1);
}
} }

View File

@ -6,28 +6,41 @@ use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
pub struct AdditiveExpression { pub struct AddExpression {
lhs: Box<Expression>, lhs: Box<Expression>,
rhs: Box<Expression>, rhs: Box<Expression>,
source_range: SourceRange, source_range: SourceRange,
type_info: Option<TypeInfo>,
} }
impl AdditiveExpression { impl AddExpression {
pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self { pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self {
Self { Self {
lhs: lhs.into(), lhs: lhs.into(),
rhs: rhs.into(), rhs: rhs.into(),
source_range, source_range,
type_info: None,
} }
} }
pub fn lhs(&self) -> &Expression {
&self.lhs
}
pub fn rhs(&self) -> &Expression {
&self.rhs
}
pub fn gather_declared_names( pub fn gather_declared_names(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let diagnostics: Vec<Diagnostic> = [self.lhs.as_mut(), self.rhs.as_mut()]
diagnostics.append(&mut self.lhs.gather_declared_names(symbol_table)); .iter_mut()
diagnostics.append(&mut self.rhs.gather_declared_names(symbol_table)); .map(|expression| expression.gather_declared_names(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() { if diagnostics.is_empty() {
Ok(()) Ok(())
} else { } else {
@ -36,9 +49,12 @@ impl AdditiveExpression {
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let diagnostics: Vec<Diagnostic> = [self.lhs.as_mut(), self.rhs.as_mut()]
diagnostics.append(&mut self.lhs.check_name_usages(symbol_table)); .iter_mut()
diagnostics.append(&mut self.rhs.check_name_usages(symbol_table)); .map(|expression| expression.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() { if diagnostics.is_empty() {
Ok(()) Ok(())
} else { } else {
@ -47,12 +63,13 @@ impl AdditiveExpression {
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.lhs.type_check(symbol_table); self.lhs.type_check(symbol_table)?;
self.rhs.type_check(symbol_table); self.rhs.type_check(symbol_table)?;
let lhs_type_info = self.lhs.type_info(); let lhs_type_info = self.lhs.type_info();
let rhs_type_info = self.rhs.type_info(); let rhs_type_info = self.rhs.type_info();
if lhs_type_info.can_add(&rhs_type_info) { if lhs_type_info.can_add(rhs_type_info) {
self.type_info = Some(lhs_type_info.add_result(rhs_type_info));
Ok(()) Ok(())
} else { } else {
Err(vec![Diagnostic::new( Err(vec![Diagnostic::new(
@ -75,8 +92,8 @@ impl AdditiveExpression {
IrAdd::new(lhs_ir_expression, rhs_ir_expression) IrAdd::new(lhs_ir_expression, rhs_ir_expression)
} }
pub fn type_info(&self) -> TypeInfo { pub fn type_info(&self) -> &TypeInfo {
self.lhs.type_info().additive_result(&self.rhs.type_info()) self.type_info.as_ref().unwrap()
} }
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {

View File

@ -14,6 +14,7 @@ pub struct Call {
callee: Box<Expression>, callee: Box<Expression>,
arguments: Vec<Expression>, arguments: Vec<Expression>,
source_range: SourceRange, source_range: SourceRange,
return_type_info: Option<TypeInfo>,
} }
impl Call { impl Call {
@ -22,6 +23,7 @@ impl Call {
callee: callee.into(), callee: callee.into(),
arguments, arguments,
source_range, source_range,
return_type_info: None,
} }
} }
@ -33,44 +35,74 @@ impl Call {
self.arguments.iter().collect() self.arguments.iter().collect()
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
let mut diagnostics = vec![]; &mut self,
diagnostics.append(&mut self.callee.gather_declared_names(symbol_table)); symbol_table: &mut SymbolTable,
for argument in &mut self.arguments { ) -> Result<(), Vec<Diagnostic>> {
diagnostics.append(&mut argument.gather_declared_names(symbol_table)); let mut to_gather = vec![];
to_gather.push(self.callee.as_mut());
to_gather.extend(&mut self.arguments);
let diagnostics: Vec<Diagnostic> = to_gather
.iter_mut()
.map(|expression| expression.gather_declared_names(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
diagnostics
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut to_check = vec![];
diagnostics.append(&mut self.callee.check_name_usages(symbol_table)); to_check.push(self.callee.as_mut());
for argument in &mut self.arguments { to_check.extend(&mut self.arguments);
diagnostics.append(&mut argument.check_name_usages(symbol_table)); let diagnostics: Vec<Diagnostic> = to_check
.iter_mut()
.map(|expression| expression.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
diagnostics
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; self.callee.as_mut().type_check(symbol_table)?;
diagnostics.append(&mut self.callee.type_check(symbol_table));
for argument in &mut self.arguments { let mut diagnostics: Vec<Diagnostic> = self
diagnostics.append(&mut argument.type_check(symbol_table)); .arguments
} .iter_mut()
.map(|argument| argument.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
// check that callee is callable // check that callee is callable
let function_symbol = match self.callee.type_info() { let function_symbol = match self.callee.type_info() {
TypeInfo::Function(function_symbol) => function_symbol, TypeInfo::Function(function_symbol) => function_symbol,
_ => { _ => {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
"Receiver is not callable", &format!(
"Receiver of type {} is not callable.",
self.callee.type_info()
),
self.callee.source_range().start(), self.callee.source_range().start(),
self.callee.source_range().end(), self.callee.source_range().end(),
)); ));
return diagnostics; return Err(diagnostics);
} }
}; };
// set return type
self.return_type_info = Some(function_symbol.borrow().return_type_info().clone());
// check arguments length // check arguments length
let function_symbol_ref = function_symbol.borrow(); let function_symbol_ref = function_symbol.borrow();
let parameters = function_symbol_ref.parameters(); let parameters = function_symbol_ref.parameters();
@ -87,23 +119,20 @@ impl Call {
} }
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
return diagnostics; return Err(diagnostics);
} }
// check argument types // check argument types
for i in 0..parameters.len() { for i in 0..parameters.len() {
let parameter = &parameters[i]; let parameter = &parameters[i].borrow();
let argument = &self.arguments[i]; let argument = &self.arguments[i];
if !parameter let parameter_type_info = parameter.type_info();
.borrow() let argument_type_info = argument.type_info();
.type_info() if !parameter_type_info.is_assignable_from(argument_type_info) {
.is_assignable_from(&argument.type_info())
{
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
&format!( &format!(
"Mismatched types; expected {} but found {}", "Mismatched types: expected {} but found {}",
parameter.borrow().type_info(), parameter_type_info, argument_type_info
argument.type_info()
), ),
argument.source_range().start(), argument.source_range().start(),
argument.source_range().end(), argument.source_range().end(),
@ -111,14 +140,15 @@ impl Call {
} }
} }
diagnostics if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn type_info(&self) -> TypeInfo { pub fn return_type_info(&self) -> &TypeInfo {
match self.callee.type_info() { self.return_type_info.as_ref().unwrap()
TypeInfo::Function(function_symbol) => function_symbol.borrow().return_type(),
_ => panic!(),
}
} }
fn get_callee_symbol(&self) -> Rc<RefCell<FunctionSymbol>> { fn get_callee_symbol(&self) -> Rc<RefCell<FunctionSymbol>> {

View File

@ -16,30 +16,57 @@ impl CompilationUnit {
&self.declarations &self.declarations
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
let mut diagnostics = vec![]; &mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
symbol_table.push_scope("compilation_unit_scope"); symbol_table.push_scope("compilation_unit_scope");
for declaration in &mut self.declarations {
diagnostics.append(&mut declaration.gather_declared_names(symbol_table)); let diagnostics: Vec<Diagnostic> = self
} .declarations
.iter_mut()
.map(|declaration| declaration.gather_declared_names(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
symbol_table.pop_scope(); symbol_table.pop_scope();
diagnostics
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let diagnostics: Vec<Diagnostic> = self
for declaration in &mut self.declarations { .declarations
diagnostics.append(&mut declaration.check_name_usages(symbol_table)); .iter_mut()
.map(|declaration| declaration.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
diagnostics
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let diagnostics: Vec<Diagnostic> = self
for declaration in &mut self.declarations { .declarations
diagnostics.append(&mut declaration.type_check(symbol_table)); .iter_mut()
.map(|declaration| declaration.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
} }
diagnostics
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> { pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> {

View File

@ -1,9 +1,11 @@
use crate::ast::additive_expression::AdditiveExpression; use crate::ast::add_expression::AddExpression;
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::ir_builder::IrBuilder; use crate::ast::ir_builder::IrBuilder;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::string_literal::StringLiteral; use crate::ast::string_literal::StringLiteral;
use crate::ast::subtract_expression::SubtractExpression;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign; use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
@ -17,78 +19,88 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub enum Expression { pub enum Expression {
Add(AddExpression),
Subtract(SubtractExpression),
Negative(NegativeExpression),
Call(Call), Call(Call),
IntegerLiteral(IntegerLiteral),
String(StringLiteral),
Identifier(Identifier), Identifier(Identifier),
Additive(AdditiveExpression), Integer(IntegerLiteral),
String(StringLiteral),
} }
impl Expression { impl Expression {
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Expression::Add(add_expression) => add_expression.gather_declared_names(symbol_table),
Expression::Subtract(subtract_expression) => {
subtract_expression.gather_declared_names(symbol_table)
}
Expression::Negative(negative_expression) => {
negative_expression.gather_declared_names(symbol_table)
}
Expression::Call(call) => call.gather_declared_names(symbol_table), Expression::Call(call) => call.gather_declared_names(symbol_table),
Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table),
Expression::Additive(additive_expression) => { Expression::Integer(_) => Ok(()),
match additive_expression.gather_declared_names(symbol_table) { Expression::String(_) => Ok(()),
Ok(_) => {
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Expression::Add(add_expression) => add_expression.check_name_usages(symbol_table),
Expression::Subtract(subtract_expression) => {
subtract_expression.check_name_usages(symbol_table)
}
Expression::Negative(negative_expression) => {
negative_expression.check_name_usages(symbol_table)
}
Expression::Call(call) => call.check_name_usages(symbol_table), Expression::Call(call) => call.check_name_usages(symbol_table),
Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table), Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table),
Expression::Additive(additive_expression) => { Expression::Integer(_) => Ok(()),
match additive_expression.check_name_usages(symbol_table) { Expression::String(_) => Ok(()),
Ok(_) => {
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Expression::Add(add_expression) => add_expression.type_check(symbol_table),
Expression::Subtract(subtract_expression) => {
subtract_expression.type_check(symbol_table)
}
Expression::Negative(negative_expression) => {
negative_expression.type_check(symbol_table)
}
Expression::Call(call) => call.type_check(symbol_table), Expression::Call(call) => call.type_check(symbol_table),
Expression::Additive(additive_expression) => { Expression::Identifier(identifier) => identifier.type_check(symbol_table),
match additive_expression.type_check(symbol_table) { Expression::Integer(_) => Ok(()),
Ok(_) => { Expression::String(_) => Ok(()),
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
} }
} }
pub fn type_info(&self) -> TypeInfo { pub fn type_info(&self) -> &TypeInfo {
match self { match self {
Expression::Call(call) => call.type_info(), Expression::Add(add_expression) => add_expression.type_info(),
Expression::IntegerLiteral(_) => TypeInfo::Integer, Expression::Subtract(subtract_expression) => subtract_expression.type_info(),
Expression::String(_) => TypeInfo::String, Expression::Negative(negative_expression) => negative_expression.type_info(),
Expression::Call(call) => call.return_type_info(),
Expression::Identifier(identifier) => identifier.type_info(), Expression::Identifier(identifier) => identifier.type_info(),
Expression::Additive(additive_expression) => additive_expression.type_info(), Expression::Integer(integer_literal) => integer_literal.type_info(),
Expression::String(string_literal) => string_literal.type_info(),
} }
} }
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
match self { match self {
Expression::Add(additive_expression) => additive_expression.source_range(),
Expression::Subtract(subtract_expression) => subtract_expression.source_range(),
Expression::Negative(negative_expression) => negative_expression.source_range(),
Expression::Call(call) => call.source_range(), Expression::Call(call) => call.source_range(),
Expression::IntegerLiteral(integer_literal) => integer_literal.source_range(),
Expression::String(string_literal) => string_literal.source_range(),
Expression::Identifier(identifier) => identifier.source_range(), Expression::Identifier(identifier) => identifier.source_range(),
Expression::Additive(additive_expression) => additive_expression.source_range(), Expression::Integer(integer_literal) => integer_literal.source_range(),
Expression::String(string_literal) => string_literal.source_range(),
} }
} }
@ -100,7 +112,7 @@ impl Expression {
match self { match self {
Expression::Call(call) => { Expression::Call(call) => {
let ir_call = call.to_ir(builder, symbol_table); let ir_call = call.to_ir(builder, symbol_table);
if matches!(call.type_info(), TypeInfo::Void) { if matches!(call.return_type_info(), TypeInfo::Void) {
builder builder
.current_block_mut() .current_block_mut()
.add_statement(IrStatement::Call(ir_call)); .add_statement(IrStatement::Call(ir_call));
@ -109,7 +121,7 @@ impl Expression {
let t_var = IrVariable::new_vr( let t_var = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
builder.current_block().id(), builder.current_block().id(),
call.type_info(), call.return_type_info(),
); );
let as_rc = Rc::new(RefCell::new(t_var)); let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call)); let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call));
@ -119,7 +131,7 @@ impl Expression {
Some(IrExpression::Variable(as_rc)) Some(IrExpression::Variable(as_rc))
} }
} }
Expression::IntegerLiteral(integer_literal) => { Expression::Integer(integer_literal) => {
Some(IrExpression::Int(integer_literal.value())) Some(IrExpression::Int(integer_literal.value()))
} }
Expression::String(string_literal) => { Expression::String(string_literal) => {
@ -129,7 +141,7 @@ impl Expression {
let expressible_symbol = identifier.expressible_symbol(); let expressible_symbol = identifier.expressible_symbol();
Some(expressible_symbol.ir_expression()) Some(expressible_symbol.ir_expression())
} }
Expression::Additive(additive_expression) => { Expression::Add(additive_expression) => {
let ir_add = additive_expression.to_ir(builder, symbol_table); let ir_add = additive_expression.to_ir(builder, symbol_table);
let t_var = IrVariable::new_vr( let t_var = IrVariable::new_vr(
builder.new_t_var().into(), builder.new_t_var().into(),
@ -143,6 +155,10 @@ impl Expression {
.add_statement(IrStatement::Assign(assign)); .add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc)) Some(IrExpression::Variable(as_rc))
} }
Expression::Subtract(subtract_expression) => {
Some(subtract_expression.to_ir_expression(builder, symbol_table))
}
Expression::Negative(_) => todo!(),
} }
} }
} }

View File

@ -20,15 +20,18 @@ impl ExpressionStatement {
&self.expression &self.expression
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
self.expression.gather_declared_names(symbol_table) self.expression.gather_declared_names(symbol_table)
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.expression.check_name_usages(symbol_table) self.expression.check_name_usages(symbol_table)
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.expression.type_check(symbol_table) self.expression.type_check(symbol_table)
} }

View File

@ -31,7 +31,10 @@ impl ExternFunction {
&self.declared_name &self.declared_name
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
@ -53,7 +56,7 @@ impl ExternFunction {
self.declared_name_source_range.start(), self.declared_name_source_range.start(),
self.declared_name_source_range.end(), self.declared_name_source_range.end(),
)); ));
diagnostics Err(diagnostics)
} }
}; };
} }
@ -79,24 +82,40 @@ impl ExternFunction {
symbol_table.pop_scope(); symbol_table.pop_scope();
diagnostics if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.parameters let diagnostics: Vec<Diagnostic> = self
.parameters
.iter_mut() .iter_mut()
.map(|parameter| parameter.check_name_usages(symbol_table)) .map(|parameter| parameter.check_name_usages(symbol_table))
.filter_map(Result::err) .filter_map(Result::err)
.flatten() .flatten()
.collect() .collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.parameters let diagnostics: Vec<Diagnostic> = self
.parameters
.iter_mut() .iter_mut()
.map(|parameter| parameter.type_check(symbol_table)) .map(|parameter| parameter.type_check(symbol_table))
.filter_map(Result::err) .filter_map(Result::err)
.flatten() .flatten()
.collect() .collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
} }

View File

@ -48,7 +48,10 @@ impl Function {
self.statements.iter().collect() self.statements.iter().collect()
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
// insert function symbol // insert function symbol
@ -75,7 +78,7 @@ impl Function {
self.declared_name_source_range.start(), self.declared_name_source_range.start(),
self.declared_name_source_range.end(), self.declared_name_source_range.end(),
)); ));
diagnostics Err(diagnostics)
} }
}; };
} }
@ -103,14 +106,24 @@ impl Function {
symbol_table.push_scope(&format!("body_scope({})", self.declared_name)); symbol_table.push_scope(&format!("body_scope({})", self.declared_name));
for statement in &mut self.statements { for statement in &mut self.statements {
diagnostics.append(&mut statement.gather_declared_names(symbol_table)); match statement.gather_declared_names(symbol_table) {
Ok(_) => {}
Err(mut statement_diagnostics) => {
diagnostics.append(&mut statement_diagnostics);
}
}
} }
symbol_table.pop_scope(); // body symbol_table.pop_scope(); // body
symbol_table.pop_scope(); // params symbol_table.pop_scope(); // params
diagnostics
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
// parameters // parameters
@ -126,12 +139,22 @@ impl Function {
// statements // statements
for statement in &mut self.statements { for statement in &mut self.statements {
diagnostics.append(&mut statement.check_name_usages(symbol_table)); match statement.check_name_usages(symbol_table) {
Ok(_) => {}
Err(mut statement_diagnostics) => {
diagnostics.append(&mut statement_diagnostics);
}
} }
diagnostics
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
// parameters // parameters
@ -140,17 +163,27 @@ impl Function {
.parameters .parameters
.iter_mut() .iter_mut()
.map(|parameter| parameter.type_check(symbol_table)) .map(|parameter| parameter.type_check(symbol_table))
.filter_map(|result| result.err()) .filter_map(Result::err)
.flatten() .flatten()
.collect(), .collect(),
); );
// statements // statements
for statement in &mut self.statements { diagnostics.append(
diagnostics.append(&mut statement.type_check(symbol_table)); &mut self
} .statements
.iter_mut()
.map(|statement| statement.type_check(symbol_table))
.filter_map(Result::err)
.flatten()
.collect(),
);
diagnostics if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> IrFunction { pub fn to_ir(&self, symbol_table: &SymbolTable) -> IrFunction {
@ -172,12 +205,8 @@ impl Function {
let entry_block_id = builder.new_block(); let entry_block_id = builder.new_block();
let return_type_info = self let function_symbol = self.function_symbol.as_ref().unwrap().borrow();
.function_symbol let return_type_info = function_symbol.return_type_info();
.as_ref()
.unwrap()
.borrow()
.return_type();
let should_return_value = !matches!(return_type_info, TypeInfo::Void); let should_return_value = !matches!(return_type_info, TypeInfo::Void);

View File

@ -8,6 +8,7 @@ pub struct Identifier {
name: String, name: String,
scope_id: Option<usize>, scope_id: Option<usize>,
expressible_symbol: Option<ExpressibleSymbol>, expressible_symbol: Option<ExpressibleSymbol>,
type_info: Option<TypeInfo>,
source_range: SourceRange, source_range: SourceRange,
} }
@ -17,6 +18,7 @@ impl Identifier {
name: name.into(), name: name.into(),
scope_id: None, scope_id: None,
expressible_symbol: None, expressible_symbol: None,
type_info: None,
source_range, source_range,
} }
} }
@ -25,41 +27,38 @@ impl Identifier {
&self.name &self.name
} }
pub fn gather_declared_names(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
self.scope_id = Some(symbol_table.current_scope_id()); self.scope_id = Some(symbol_table.current_scope_id());
vec![] Ok(())
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let maybe_expressible_symbol = let maybe_expressible_symbol =
symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name);
match maybe_expressible_symbol { match maybe_expressible_symbol {
None => { None => Err(vec![Diagnostic::new(
vec![Diagnostic::new(
&format!("Unable to resolve symbol {}", self.name), &format!("Unable to resolve symbol {}", self.name),
self.source_range.start(), self.source_range.start(),
self.source_range.end(), self.source_range.end(),
)] )]),
}
Some(expressible_symbol) => { Some(expressible_symbol) => {
self.expressible_symbol = Some(expressible_symbol); self.expressible_symbol = Some(expressible_symbol);
vec![] Ok(())
} }
} }
} }
pub fn type_info(&self) -> TypeInfo { pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self.expressible_symbol.as_ref().unwrap() { let type_info = self.expressible_symbol.as_ref().unwrap().type_info();
ExpressibleSymbol::Function(function_symbol) => { self.type_info = Some(type_info);
TypeInfo::Function(function_symbol.clone()) Ok(())
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
parameter_symbol.borrow().type_info().clone()
}
ExpressibleSymbol::Variable(variable_symbol) => {
variable_symbol.borrow().type_info().clone()
}
} }
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
} }
pub fn expressible_symbol(&self) -> &ExpressibleSymbol { pub fn expressible_symbol(&self) -> &ExpressibleSymbol {

View File

@ -1,15 +1,19 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::type_info::TypeInfo;
pub struct IntegerLiteral { pub struct IntegerLiteral {
value: i32, value: i32,
source_range: SourceRange, source_range: SourceRange,
type_info: &'static TypeInfo,
} }
impl IntegerLiteral { impl IntegerLiteral {
pub fn new(value: i32, source_range: SourceRange) -> Self { pub fn new(value: i32, source_range: SourceRange) -> Self {
const TYPE_INFO: TypeInfo = TypeInfo::Integer;
Self { Self {
value, value,
source_range, source_range,
type_info: &TYPE_INFO,
} }
} }
@ -17,6 +21,10 @@ impl IntegerLiteral {
self.value self.value
} }
pub fn type_info(&self) -> &TypeInfo {
&self.type_info
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -45,9 +45,19 @@ impl LetStatement {
&mut self.initializer &mut self.initializer
} }
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
self.initializer_mut().gather_declared_names(symbol_table);
match self.initializer_mut().gather_declared_names(symbol_table) {
Ok(_) => {}
Err(mut initializer_diagnostics) => {
diagnostics.append(&mut initializer_diagnostics);
}
}
let insert_result = let insert_result =
symbol_table.insert_variable_symbol(VariableSymbol::new(self.declared_name())); symbol_table.insert_variable_symbol(VariableSymbol::new(self.declared_name()));
if let Err(symbol_insert_error) = insert_result { if let Err(symbol_insert_error) = insert_result {
@ -64,32 +74,35 @@ impl LetStatement {
} }
} }
} }
self.scope_id = Some(symbol_table.current_scope_id()); self.scope_id = Some(symbol_table.current_scope_id());
diagnostics
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.initializer.check_name_usages(symbol_table) self.initializer.check_name_usages(symbol_table)
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let initializer_diagnostics = self.initializer.type_check(symbol_table); self.initializer.type_check(symbol_table)?;
if !initializer_diagnostics.is_empty() {
return initializer_diagnostics;
}
let initializer_type_info = self.initializer.type_info(); let initializer_type_info = self.initializer.type_info();
let variable_symbol = let variable_symbol =
symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name);
variable_symbol variable_symbol
.borrow_mut() .borrow_mut()
.set_type_info(initializer_type_info); .set_type_info(initializer_type_info.clone());
vec![] Ok(())
} }
pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) {
let init_operation = match self.initializer() { let init_operation = match self.initializer() {
Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)), Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)),
Expression::IntegerLiteral(integer_literal) => { Expression::Integer(integer_literal) => {
IrOperation::Load(IrExpression::Int(integer_literal.value())) IrOperation::Load(IrExpression::Int(integer_literal.value()))
} }
Expression::String(string_literal) => { Expression::String(string_literal) => {
@ -98,9 +111,13 @@ impl LetStatement {
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
IrOperation::Load(identifier.expressible_symbol().ir_expression()) IrOperation::Load(identifier.expressible_symbol().ir_expression())
} }
Expression::Additive(additive_expression) => { Expression::Add(additive_expression) => {
IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) IrOperation::Add(additive_expression.to_ir(builder, symbol_table))
} }
Expression::Subtract(subtract_expression) => {
IrOperation::Subtract(subtract_expression.to_ir_subtract(builder, symbol_table))
}
Expression::Negative(_) => todo!(),
}; };
let destination_symbol = let destination_symbol =

View File

@ -1,4 +1,4 @@
pub mod additive_expression; pub mod add_expression;
pub mod call; pub mod call;
pub mod compilation_unit; pub mod compilation_unit;
pub mod expression; pub mod expression;
@ -11,7 +11,9 @@ pub mod integer_literal;
pub mod ir_builder; pub mod ir_builder;
pub mod let_statement; pub mod let_statement;
pub mod module_level_declaration; pub mod module_level_declaration;
pub mod negative_expression;
pub mod parameter; pub mod parameter;
pub mod statement; pub mod statement;
pub mod string_literal; pub mod string_literal;
pub mod subtract_expression;
pub mod type_use; pub mod type_use;

View File

@ -9,7 +9,10 @@ pub enum ModuleLevelDeclaration {
} }
impl ModuleLevelDeclaration { impl ModuleLevelDeclaration {
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
match self { match self {
ModuleLevelDeclaration::Function(function) => { ModuleLevelDeclaration::Function(function) => {
function.gather_declared_names(symbol_table) function.gather_declared_names(symbol_table)
@ -20,7 +23,7 @@ impl ModuleLevelDeclaration {
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
ModuleLevelDeclaration::Function(function) => function.check_name_usages(symbol_table), ModuleLevelDeclaration::Function(function) => function.check_name_usages(symbol_table),
ModuleLevelDeclaration::ExternFunction(extern_function) => { ModuleLevelDeclaration::ExternFunction(extern_function) => {
@ -29,7 +32,7 @@ impl ModuleLevelDeclaration {
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
ModuleLevelDeclaration::Function(function) => function.type_check(symbol_table), ModuleLevelDeclaration::Function(function) => function.type_check(symbol_table),
ModuleLevelDeclaration::ExternFunction(extern_function) => { ModuleLevelDeclaration::ExternFunction(extern_function) => {

View File

@ -0,0 +1,64 @@
use crate::ast::expression::Expression;
use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
pub struct NegativeExpression {
operand: Box<Expression>,
source_range: SourceRange,
type_info: Option<TypeInfo>,
}
impl NegativeExpression {
pub fn new(operand: Expression, source_range: SourceRange) -> Self {
Self {
operand: operand.into(),
source_range,
type_info: None,
}
}
pub fn source_range(&self) -> &SourceRange {
&self.source_range
}
pub fn operand(&self) -> &Expression {
&self.operand
}
pub fn operand_mut(&mut self) -> &mut Expression {
&mut self.operand
}
pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
self.operand.gather_declared_names(symbol_table)
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.operand.check_name_usages(symbol_table)
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.operand.type_check(symbol_table)?;
let type_info = self.operand.type_info();
if type_info.can_negate() {
self.type_info = Some(type_info.negate_result());
Ok(())
} else {
Err(vec![Diagnostic::new(
&format!("Cannot negate {}", type_info),
self.source_range.start(),
self.source_range.end(),
)])
}
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
}

View File

@ -10,7 +10,10 @@ pub enum Statement {
} }
impl Statement { impl Statement {
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table), Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table),
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
@ -19,7 +22,7 @@ impl Statement {
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table), Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table),
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {
@ -28,7 +31,7 @@ impl Statement {
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
match self { match self {
Statement::Let(let_statement) => let_statement.type_check(symbol_table), Statement::Let(let_statement) => let_statement.type_check(symbol_table),
Statement::Expression(expression_statement) => { Statement::Expression(expression_statement) => {

View File

@ -1,15 +1,19 @@
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::type_info::TypeInfo;
pub struct StringLiteral { pub struct StringLiteral {
content: String, content: String,
source_range: SourceRange, source_range: SourceRange,
type_info: &'static TypeInfo,
} }
impl StringLiteral { impl StringLiteral {
pub fn new(content: &str, source_range: SourceRange) -> Self { pub fn new(content: &str, source_range: SourceRange) -> Self {
const TYPE_INFO: TypeInfo = TypeInfo::Integer;
Self { Self {
content: content.into(), content: content.into(),
source_range, source_range,
type_info: &TYPE_INFO,
} }
} }
@ -17,6 +21,10 @@ impl StringLiteral {
&self.content &self.content
} }
pub fn type_info(&self) -> &TypeInfo {
&self.type_info
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -0,0 +1,135 @@
use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder;
use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_subtract::IrSubtract;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub struct SubtractExpression {
lhs: Box<Expression>,
rhs: Box<Expression>,
source_range: SourceRange,
type_info: Option<TypeInfo>,
}
impl SubtractExpression {
pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self {
Self {
lhs: lhs.into(),
rhs: rhs.into(),
source_range,
type_info: None,
}
}
pub fn lhs(&self) -> &Expression {
&self.lhs
}
pub fn rhs(&self) -> &Expression {
&self.rhs
}
pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let diagnostics = [&mut self.lhs, &mut self.rhs]
.iter_mut()
.map(|expression| expression.gather_declared_names(symbol_table))
.filter_map(|result| result.err())
.flatten()
.collect::<Vec<Diagnostic>>();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let diagnostics: Vec<Diagnostic> = [&mut self.lhs, &mut self.rhs]
.iter_mut()
.map(|expression| expression.check_name_usages(symbol_table))
.filter_map(Result::err)
.flatten()
.collect();
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
self.lhs.type_check(symbol_table)?;
self.rhs.type_check(symbol_table)?;
let lhs_type_info = self.lhs.type_info();
let rhs_type_info = self.rhs.type_info();
if lhs_type_info.can_subtract(rhs_type_info) {
self.type_info = Some(lhs_type_info.add_result(rhs_type_info));
Ok(())
} else {
Err(vec![Diagnostic::new(
&format!(
"Incompatible types: cannot subtract {} from {}",
rhs_type_info, lhs_type_info
), // n.b. order
self.lhs.source_range().start(),
self.lhs.source_range().end(),
)])
}
}
pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap()
}
pub fn source_range(&self) -> &SourceRange {
&self.source_range
}
pub fn to_ir_subtract(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
) -> IrSubtract {
let lhs = self
.lhs
.to_ir(builder, symbol_table)
.expect("Attempt to subtract non-expression");
let rhs = self
.rhs
.to_ir(builder, symbol_table)
.expect("Attempt to subtract non-expression");
IrSubtract::new(lhs, rhs)
}
pub fn to_ir_expression(
&self,
builder: &mut IrBuilder,
symbol_table: &SymbolTable,
) -> IrExpression {
let ir_subtract = self.to_ir_subtract(builder, symbol_table);
let t_var = IrVariable::new_vr(
builder.new_t_var().into(),
builder.current_block().id(),
self.type_info(),
);
let as_rc = Rc::new(RefCell::new(t_var));
let assign = IrAssign::new(as_rc.clone(), IrOperation::Subtract(ir_subtract));
builder
.current_block_mut()
.add_statement(IrStatement::Assign(assign));
IrExpression::Variable(as_rc)
}
}

View File

@ -97,6 +97,10 @@ impl Assemble for IrAssign {
let (left, right) = ir_add.operand_pair(constants_table); let (left, right) = ir_add.operand_pair(constants_table);
builder.push(Instruction::Add(left, right, destination)); builder.push(Instruction::Add(left, right, destination));
} }
IrOperation::Subtract(ir_subtract) => {
let (left, right) = ir_subtract.operand_pair();
builder.push(Instruction::Subtract(left, right, destination));
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
ir_call.assemble(builder, constants_table); ir_call.assemble(builder, constants_table);
builder.push(Instruction::Pop(Some(destination))); builder.push(Instruction::Pop(Some(destination)));

View File

@ -5,7 +5,9 @@ use crate::ir::ir_variable::{
}; };
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand}; use dvm_lib::instruction::{
AddOperand, Location, MoveOperand, PushOperand, ReturnOperand, SubtractOperand,
};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -105,6 +107,40 @@ impl IrExpression {
} }
} }
pub fn subtract_operand(&self) -> SubtractOperand {
match self {
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
TypeInfo::Integer => SubtractOperand::Location(Location::StackFrameOffset(
ir_parameter.stack_offset(),
)),
_ => panic!(
"Attempt to subtract with non-integer type (found {})",
ir_parameter.type_info()
),
},
IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() {
TypeInfo::Integer => match ir_variable.borrow().descriptor() {
IrVariableDescriptor::VirtualRegister(vr_variable) => {
SubtractOperand::Location(Location::Register(
vr_variable.assigned_register(),
))
}
IrVariableDescriptor::Stack(stack_variable) => SubtractOperand::Location(
Location::StackFrameOffset(stack_variable.offset()),
),
},
_ => panic!(
"Attempt to subtract with non-integer type (found {})",
ir_variable.borrow().type_info()
),
},
IrExpression::Int(i) => SubtractOperand::Int(*i),
IrExpression::String(_) => {
panic!("Attempt to subtract with a string");
}
}
}
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand { pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
match self { match self {
IrExpression::Parameter(ir_parameter) => { IrExpression::Parameter(ir_parameter) => {

View File

@ -23,13 +23,13 @@ impl IrFunction {
pub fn new( pub fn new(
function_symbol: Rc<RefCell<FunctionSymbol>>, function_symbol: Rc<RefCell<FunctionSymbol>>,
parameters: &[Rc<IrParameter>], parameters: &[Rc<IrParameter>],
return_type_info: TypeInfo, return_type_info: &TypeInfo,
entry: Rc<RefCell<IrBlock>>, entry: Rc<RefCell<IrBlock>>,
) -> Self { ) -> Self {
Self { Self {
function_symbol, function_symbol,
parameters: parameters.to_vec(), parameters: parameters.to_vec(),
return_type_info, return_type_info: return_type_info.clone(),
entry, entry,
} }
} }

View File

@ -1,6 +1,7 @@
use crate::ir::ir_add::IrAdd; use crate::ir::ir_add::IrAdd;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_subtract::IrSubtract;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -9,6 +10,7 @@ use std::fmt::{Display, Formatter};
pub enum IrOperation { pub enum IrOperation {
Load(IrExpression), Load(IrExpression),
Add(IrAdd), Add(IrAdd),
Subtract(IrSubtract),
Call(IrCall), Call(IrCall),
} }
@ -21,6 +23,9 @@ impl Display for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
write!(f, "{}", ir_add) write!(f, "{}", ir_add)
} }
IrOperation::Subtract(ir_subtract) => {
write!(f, "{}", ir_subtract)
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
write!(f, "{}", ir_call) write!(f, "{}", ir_call)
} }
@ -33,6 +38,7 @@ impl VrUser for IrOperation {
match self { match self {
IrOperation::Load(ir_expression) => ir_expression.vr_definitions(), IrOperation::Load(ir_expression) => ir_expression.vr_definitions(),
IrOperation::Add(ir_add) => ir_add.vr_definitions(), IrOperation::Add(ir_add) => ir_add.vr_definitions(),
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_definitions(),
IrOperation::Call(ir_call) => ir_call.vr_definitions(), IrOperation::Call(ir_call) => ir_call.vr_definitions(),
} }
} }
@ -41,6 +47,7 @@ impl VrUser for IrOperation {
match self { match self {
IrOperation::Load(ir_expression) => ir_expression.vr_uses(), IrOperation::Load(ir_expression) => ir_expression.vr_uses(),
IrOperation::Add(ir_add) => ir_add.vr_uses(), IrOperation::Add(ir_add) => ir_add.vr_uses(),
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_uses(),
IrOperation::Call(ir_call) => ir_call.vr_uses(), IrOperation::Call(ir_call) => ir_call.vr_uses(),
} }
} }
@ -53,6 +60,9 @@ impl VrUser for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
ir_add.propagate_spills(spills); ir_add.propagate_spills(spills);
} }
IrOperation::Subtract(ir_subtract) => {
ir_subtract.propagate_spills(spills);
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
ir_call.propagate_spills(spills); ir_call.propagate_spills(spills);
} }
@ -70,6 +80,9 @@ impl VrUser for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
ir_add.propagate_register_assignments(assignments); ir_add.propagate_register_assignments(assignments);
} }
IrOperation::Subtract(ir_subtract) => {
ir_subtract.propagate_register_assignments(assignments);
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
ir_call.propagate_register_assignments(assignments); ir_call.propagate_register_assignments(assignments);
} }

View File

@ -0,0 +1,63 @@
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser};
use dvm_lib::instruction::SubtractOperand;
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
pub struct IrSubtract {
left: Box<IrExpression>,
right: Box<IrExpression>,
}
impl IrSubtract {
pub fn new(left: IrExpression, right: IrExpression) -> Self {
Self {
left: left.into(),
right: right.into(),
}
}
pub fn operand_pair(&self) -> (SubtractOperand, SubtractOperand) {
(self.left.subtract_operand(), self.right.subtract_operand())
}
}
impl VrUser for IrSubtract {
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
[&self.left, &self.right]
.iter()
.flat_map(|expression| expression.vr_definitions())
.collect()
}
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
[&self.left, &self.right]
.iter()
.flat_map(|expression| expression.vr_uses())
.collect()
}
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
self.left.propagate_spills(spills);
self.right.propagate_spills(spills);
}
fn propagate_register_assignments(
&mut self,
assignments: &HashMap<IrVrVariableDescriptor, usize>,
) {
self.left.propagate_register_assignments(assignments);
self.right.propagate_register_assignments(assignments);
}
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
}
impl Display for IrSubtract {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}", self.left, self.right)
}
}

View File

@ -9,19 +9,19 @@ pub struct IrVariable {
} }
impl IrVariable { impl IrVariable {
pub fn new_vr(name: Rc<str>, block_id: usize, type_info: TypeInfo) -> Self { pub fn new_vr(name: Rc<str>, block_id: usize, type_info: &TypeInfo) -> Self {
Self { Self {
descriptor: IrVariableDescriptor::VirtualRegister(IrVrVariableDescriptor::new( descriptor: IrVariableDescriptor::VirtualRegister(IrVrVariableDescriptor::new(
name, block_id, name, block_id,
)), )),
type_info, type_info: type_info.clone(),
} }
} }
pub fn new_stack(name: Rc<str>, block_id: usize, type_info: TypeInfo) -> Self { pub fn new_stack(name: Rc<str>, block_id: usize, type_info: TypeInfo) -> Self {
Self { Self {
descriptor: IrVariableDescriptor::Stack(IrStackVariableDescriptor::new(name, block_id)), descriptor: IrVariableDescriptor::Stack(IrStackVariableDescriptor::new(name, block_id)),
type_info, type_info: type_info.clone(),
} }
} }

View File

@ -9,5 +9,6 @@ pub mod ir_operation;
pub mod ir_parameter; pub mod ir_parameter;
pub mod ir_return; pub mod ir_return;
pub mod ir_statement; pub mod ir_statement;
pub mod ir_subtract;
pub mod ir_variable; pub mod ir_variable;
mod register_allocation; mod register_allocation;

View File

@ -36,6 +36,8 @@ impl<'a> Lexer<'a> {
let token = if chunk.starts_with("->") { let token = if chunk.starts_with("->") {
Token::new(self.position, self.position + 2, TokenKind::RightArrow) Token::new(self.position, self.position + 2, TokenKind::RightArrow)
} else if chunk.starts_with("-") {
Token::new(self.position, self.position + 1, TokenKind::Minus)
} else if chunk.starts_with("(") { } else if chunk.starts_with("(") {
Token::new(self.position, self.position + 1, TokenKind::LeftParentheses) Token::new(self.position, self.position + 1, TokenKind::LeftParentheses)
} else if chunk.starts_with(")") { } else if chunk.starts_with(")") {

View File

@ -1,4 +1,4 @@
use crate::ast::additive_expression::AdditiveExpression; use crate::ast::add_expression::AddExpression;
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::compilation_unit::CompilationUnit; use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
@ -9,9 +9,11 @@ use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::ast::module_level_declaration::ModuleLevelDeclaration; use crate::ast::module_level_declaration::ModuleLevelDeclaration;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::parameter::Parameter; use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::ast::string_literal::StringLiteral; use crate::ast::string_literal::StringLiteral;
use crate::ast::subtract_expression::SubtractExpression;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::lexer::Lexer; use crate::lexer::Lexer;
@ -24,6 +26,12 @@ pub fn parse_compilation_unit(input: &str) -> Result<CompilationUnit, Vec<Diagno
parser.compilation_unit() parser.compilation_unit()
} }
pub fn parse_expression(input: &str) -> Result<Expression, Vec<Diagnostic>> {
let mut parser = Parser::new(input);
parser.advance(); // get started
parser.expression()
}
struct Parser<'a> { struct Parser<'a> {
input: &'a str, input: &'a str,
lexer: Lexer<'a>, lexer: Lexer<'a>,
@ -391,17 +399,24 @@ impl<'a> Parser<'a> {
} }
fn additive_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> { fn additive_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let mut result = self.suffix_expression()?; let mut result = self.prefix_expression()?;
while self.current.is_some() { while self.current.is_some() {
let current = self.get_current(); let current = self.get_current();
match current.kind() { match current.kind() {
TokenKind::Plus => { TokenKind::Plus => {
self.advance(); // plus self.advance(); // plus
let rhs = self.suffix_expression()?; let rhs = self.prefix_expression()?;
let source_range =
SourceRange::new(result.source_range().start(), rhs.source_range().end());
result = Expression::Add(AddExpression::new(result, rhs, source_range));
}
TokenKind::Minus => {
self.advance(); // minus
let rhs = self.prefix_expression()?;
let source_range = let source_range =
SourceRange::new(result.source_range().start(), rhs.source_range().end()); SourceRange::new(result.source_range().start(), rhs.source_range().end());
result = result =
Expression::Additive(AdditiveExpression::new(result, rhs, source_range)); Expression::Subtract(SubtractExpression::new(result, rhs, source_range));
} }
_ => break, _ => break,
} }
@ -409,6 +424,37 @@ impl<'a> Parser<'a> {
Ok(result) Ok(result)
} }
fn prefix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
// first, collect all consecutive operators
let mut operator_tokens = vec![];
while self.current.is_some() {
let current = self.get_current();
match current.kind() {
TokenKind::Minus => {
operator_tokens.push(current.clone()); // unfortunately necessary
self.advance();
}
_ => break,
}
}
// now go in reverse and build up expressions
// the parser is currently just after the prefix operators, so we need a suffix expression
// as a base
let mut result = self.suffix_expression()?;
while let Some(operator_token) = operator_tokens.pop() {
let source_range =
SourceRange::new(operator_token.start(), result.source_range().end());
match operator_token.kind() {
TokenKind::Minus => {
result = Expression::Negative(NegativeExpression::new(result, source_range));
}
_ => unreachable!(),
}
}
Ok(result)
}
fn suffix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> { fn suffix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let mut result = self.expression_base()?; let mut result = self.expression_base()?;
while self.current.is_some() { while self.current.is_some() {
@ -430,7 +476,7 @@ impl<'a> Parser<'a> {
let raw = self.token_text(&current); let raw = self.token_text(&current);
let source_range = SourceRange::new(current.start(), current.end()); let source_range = SourceRange::new(current.start(), current.end());
self.advance(); self.advance();
Ok(Expression::IntegerLiteral(IntegerLiteral::new( Ok(Expression::Integer(IntegerLiteral::new(
i32::from_str(raw).unwrap(), i32::from_str(raw).unwrap(),
source_range, source_range,
))) )))
@ -453,7 +499,7 @@ impl<'a> Parser<'a> {
source_range, source_range,
))) )))
} }
_ => unreachable!(), _ => unreachable!("Unreachable token type found: {:?}", current.kind()),
} }
} }
@ -539,24 +585,90 @@ mod smoke_tests {
fn add_two_numbers() { fn add_two_numbers() {
smoke_test("fn main() 1 + 2 end"); smoke_test("fn main() 1 + 2 end");
} }
#[test]
fn negative_return() {
smoke_test("fn main() -> Int -1 end");
}
#[test]
fn negative_left_add() {
smoke_test("fn main() -> Int -1 + 1 end");
}
#[test]
fn negative_right_add() {
smoke_test("fn main() -> Int 1 + -1 end");
}
#[test]
fn two_negatives() {
smoke_test("fn main() -> Int -1 + -1 end");
}
#[test]
fn minus_positive_number() {
smoke_test("fn main() -> Int 1 - 1 end");
}
#[test]
fn minus_negative_number() {
smoke_test("fn main() -> Int 1 - -1 end");
}
} }
#[cfg(test)] #[cfg(test)]
mod concrete_tests { mod concrete_tests {
use super::*; use super::*;
#[test] fn report_diagnostics(diagnostics: &[Diagnostic]) -> ! {
fn parses_extern_fn() {
let parse_result = parse_compilation_unit("extern fn println() -> Void");
let compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
for diagnostic in diagnostics { for diagnostic in diagnostics {
eprintln!("{:?}", diagnostic); eprintln!("{:?}", diagnostic);
} }
panic!(); panic!();
} }
};
fn assert_compilation_unit(input: &str) -> CompilationUnit {
let parse_result = parse_compilation_unit(input);
match parse_result {
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
}
}
fn assert_expression(input: &str) -> Expression {
let parse_result = parse_expression(input);
match parse_result {
Ok(expression) => expression,
Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
}
}
fn assert_function_in<'a>(
compilation_unit: &'a CompilationUnit,
function_name: &str,
) -> &'a Function {
let declarations = compilation_unit.declarations();
for declaration in declarations {
match declaration {
ModuleLevelDeclaration::Function(function) => {
if function.declared_name() == function_name {
return function;
}
}
_ => {}
}
}
panic!("Function {} not found", function_name)
}
#[test]
fn parses_extern_fn() {
let compilation_unit = assert_compilation_unit("extern fn println() -> Void");
let declarations = compilation_unit.declarations(); let declarations = compilation_unit.declarations();
assert_eq!(declarations.len(), 1); assert_eq!(declarations.len(), 1);
let extern_function = match &declarations[0] { let extern_function = match &declarations[0] {
@ -568,23 +680,8 @@ mod concrete_tests {
#[test] #[test]
fn hello_world() { fn hello_world() {
let parse_result = parse_compilation_unit("fn main() println(\"Hello, World!\") end"); let compilation_unit = assert_compilation_unit("fn main() println(\"Hello, World!\") end");
let compilation_unit = match parse_result { let function = assert_function_in(&compilation_unit, "main");
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
for diagnostic in &diagnostics {
eprintln!("{:?}", diagnostic)
}
panic!()
}
};
let declarations = compilation_unit.declarations();
assert_eq!(declarations.len(), 1);
let function = match &declarations[0] {
ModuleLevelDeclaration::Function(function) => function,
_ => panic!(),
};
assert_eq!(function.declared_name(), "main");
let statements = function.statements(); let statements = function.statements();
assert_eq!(statements.len(), 1); assert_eq!(statements.len(), 1);
if let Statement::Expression(expression_statement) = statements[0] { if let Statement::Expression(expression_statement) = statements[0] {
@ -612,6 +709,69 @@ mod concrete_tests {
panic!("Expected expression"); panic!("Expected expression");
} }
} }
#[test]
fn negative_expression() {
let expression = assert_expression("-1");
match expression {
Expression::Negative(negative_expression) => match negative_expression.operand() {
Expression::Integer(integer_literal) => {
assert_eq!(integer_literal.value(), 1);
}
_ => panic!("Expected integer literal"),
},
_ => panic!("Expected negative expression"),
}
}
#[test]
fn add_negative() {
let expression = assert_expression("1 + -1");
match expression {
Expression::Add(add_expression) => {
match add_expression.lhs() {
Expression::Integer(integer_literal) => {
assert_eq!(integer_literal.value(), 1);
}
_ => panic!("Expected integer literal"),
}
match add_expression.rhs() {
Expression::Negative(negative_expression) => {
match negative_expression.operand() {
Expression::Integer(integer_literal) => {
assert_eq!(integer_literal.value(), 1);
}
_ => panic!("Expected integer literal"),
}
}
_ => panic!("Expected negative expression"),
}
}
_ => panic!("Expected additive expression"),
}
}
#[test]
fn simple_subtract() {
let expression = assert_expression("1 - 1");
match expression {
Expression::Subtract(subtract_expression) => {
match subtract_expression.lhs() {
Expression::Integer(integer_literal) => {
assert_eq!(integer_literal.value(), 1);
}
_ => panic!("Expected integer literal"),
}
match subtract_expression.rhs() {
Expression::Integer(integer_literal) => {
assert_eq!(integer_literal.value(), 1);
}
_ => panic!("Expected integer literal"),
}
}
_ => panic!("Expected subtract expression"),
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -38,8 +38,8 @@ impl FunctionSymbol {
self.parameters.as_ref().unwrap() self.parameters.as_ref().unwrap()
} }
pub fn return_type(&self) -> TypeInfo { pub fn return_type_info(&self) -> &TypeInfo {
self.return_type.clone() &self.return_type
} }
pub fn is_platform(&self) -> bool { pub fn is_platform(&self) -> bool {
@ -134,6 +134,20 @@ pub enum ExpressibleSymbol {
} }
impl ExpressibleSymbol { impl ExpressibleSymbol {
pub fn type_info(&self) -> TypeInfo {
match self {
ExpressibleSymbol::Function(function_symbol) => {
TypeInfo::Function(function_symbol.clone()) // n.b. not the return type!
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
parameter_symbol.borrow().type_info().clone()
}
ExpressibleSymbol::Variable(variable_symbol) => {
variable_symbol.borrow().type_info().clone()
}
}
}
pub fn ir_expression(&self) -> IrExpression { pub fn ir_expression(&self) -> IrExpression {
match self { match self {
ExpressibleSymbol::Function(_) => { ExpressibleSymbol::Function(_) => {

View File

@ -40,4 +40,5 @@ pub enum TokenKind {
Colon, Colon,
RightArrow, RightArrow,
Plus, Plus,
Minus,
} }

View File

@ -68,11 +68,36 @@ impl TypeInfo {
} }
} }
pub fn additive_result(&self, _rhs: &Self) -> TypeInfo { pub fn add_result(&self, _rhs: &Self) -> TypeInfo {
match self { match self {
TypeInfo::Integer => TypeInfo::Integer, TypeInfo::Integer => TypeInfo::Integer,
TypeInfo::String => TypeInfo::String, TypeInfo::String => TypeInfo::String,
_ => panic!("Adding things other than integers and strings not yet supported"), _ => panic!("Adding things other than integers and strings not yet supported"),
} }
} }
pub fn can_subtract(&self, rhs: &Self) -> bool {
matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer)
}
pub fn subtract_result(&self, rhs: &Self) -> TypeInfo {
match self {
TypeInfo::Integer => match rhs {
TypeInfo::Integer => TypeInfo::Integer,
_ => panic!(),
},
_ => panic!(),
}
}
pub fn can_negate(&self) -> bool {
matches!(self, TypeInfo::Integer)
}
pub fn negate_result(&self) -> TypeInfo {
match self {
TypeInfo::Integer => TypeInfo::Integer,
_ => panic!(),
}
}
} }

View File

@ -16,6 +16,7 @@ pub enum Instruction {
InvokePlatformStatic(FunctionName, ArgCount), InvokePlatformStatic(FunctionName, ArgCount),
Add(AddOperand, AddOperand, Location), Add(AddOperand, AddOperand, Location),
Subtract(SubtractOperand, SubtractOperand, Location),
Pop(Option<Location>), Pop(Option<Location>),
@ -50,6 +51,9 @@ impl Display for Instruction {
Instruction::Add(left, right, destination) => { Instruction::Add(left, right, destination) => {
write!(f, "add {}, {}, {}", left, right, destination) write!(f, "add {}, {}, {}", left, right, destination)
} }
Instruction::Subtract(left, right, destination) => {
write!(f, "sub {}, {}, {}", left, right, destination)
}
Instruction::SetReturnValue(source) => { Instruction::SetReturnValue(source) => {
write!(f, "srv {}", source) write!(f, "srv {}", source)
} }
@ -148,6 +152,24 @@ impl Display for AddOperand {
} }
} }
pub enum SubtractOperand {
Location(Location),
Int(i32),
}
impl Display for SubtractOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SubtractOperand::Location(location) => {
write!(f, "{}", location)
}
SubtractOperand::Int(i) => {
write!(f, "{}", i)
}
}
}
}
pub enum ReturnOperand { pub enum ReturnOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),

View File

@ -0,0 +1,21 @@
use crate::instruction::SubtractOperand;
use crate::vm::CallFrame;
use crate::vm::value::Value;
use crate::vm::value_util::load_value;
pub fn subtract_operand_to_value(
subtract_operand: &SubtractOperand,
registers: &[Value],
current_frame: &CallFrame,
) -> Value {
match subtract_operand {
SubtractOperand::Location(location) => load_value(
registers,
current_frame.stack(),
current_frame.fp(),
location,
)
.clone(),
SubtractOperand::Int(i) => Value::Int(*i),
}
}

View File

@ -1,17 +1,19 @@
use crate::instruction::{ use crate::instruction::{AddOperand, Instruction, MoveOperand, PushOperand, ReturnOperand};
AddOperand, ConstantName, Instruction, Location, MoveOperand, PushOperand, ReturnOperand,
};
use crate::platform_function::PlatformFunction; use crate::platform_function::PlatformFunction;
use crate::vm::constant::Constant; use crate::vm::constant::Constant;
use crate::vm::function::Function; use crate::vm::function::Function;
use crate::vm::instruction_helpers::subtract_operand_to_value;
use crate::vm::value::Value; use crate::vm::value::Value;
use crate::vm::value_util::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub mod constant; pub mod constant;
pub mod function; pub mod function;
mod instruction_helpers;
pub mod object; pub mod object;
pub mod value; pub mod value;
mod value_util;
pub struct DvmContext { pub struct DvmContext {
functions: HashMap<Rc<str>, Function>, functions: HashMap<Rc<str>, Function>,
@ -165,55 +167,6 @@ impl<'a> CallStack<'a> {
} }
} }
fn load_value<'a>(
registers: &'a [Value],
stack: &'a [Value],
fp: usize,
location: &Location,
) -> &'a Value {
match location {
Location::Register(register) => &registers[*register],
Location::StackFrameOffset(offset) => {
let value_index = fp.checked_add_signed(*offset).expect(&format!(
"Overflow when adding offset {} to fp {}",
*offset, fp
));
&stack[value_index]
}
}
}
fn load_constant_value(
constants: &HashMap<Rc<str>, Constant>,
constant_name: &ConstantName,
) -> Value {
let constant = &constants[constant_name];
match constant {
Constant::String(string_constant) => Value::String(string_constant.content_owned()),
}
}
fn put_value(
registers: &mut Vec<Value>,
stack: &mut Vec<Value>,
fp: usize,
destination: &Location,
value: Value,
) {
match destination {
Location::Register(register) => {
registers[*register] = value;
}
Location::StackFrameOffset(offset) => {
let target_index = fp.checked_add_signed(*offset).expect(&format!(
"Failed to calculate target index from fp + offset ({} + {})",
fp, offset
));
stack[target_index] = value;
}
}
}
pub fn call<'a>( pub fn call<'a>(
context: &'a DvmContext, context: &'a DvmContext,
registers: &mut Vec<Value>, registers: &mut Vec<Value>,
@ -277,14 +230,7 @@ pub fn call<'a>(
load_constant_value(context.constants(), constant_name) load_constant_value(context.constants(), constant_name)
} }
}; };
let fp = call_stack.top().fp(); put_value(registers, call_stack.top_mut(), destination, value);
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
value,
);
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
@ -401,21 +347,17 @@ pub fn call<'a>(
if let Value::Int(left) = left_value { if let Value::Int(left) = left_value {
if let Value::Int(right) = right_value { if let Value::Int(right) = right_value {
let result = left + right; let result = left + right;
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::Int(result), Value::Int(result),
); );
} else if let Value::String(s) = right_value { } else if let Value::String(s) = right_value {
let result = format!("{}{}", left, s); let result = format!("{}{}", left, s);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
@ -425,21 +367,17 @@ pub fn call<'a>(
} else if let Value::String(left) = left_value { } else if let Value::String(left) = left_value {
if let Value::Int(right) = right_value { if let Value::Int(right) = right_value {
let result = format!("{}{}", left, right); let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
} else if let Value::String(right) = right_value { } else if let Value::String(right) = right_value {
let result = format!("{}{}", left, right); let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
@ -452,19 +390,21 @@ pub fn call<'a>(
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
Instruction::Subtract(left_operand, right_operand, destination) => {
let left = subtract_operand_to_value(left_operand, registers, call_stack.top())
.unwrap_int();
let right = subtract_operand_to_value(right_operand, registers, call_stack.top())
.unwrap_int();
let result = Value::Int(left - right);
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
/* Pop instructions */ /* Pop instructions */
Instruction::Pop(maybe_location) => { Instruction::Pop(maybe_location) => {
let value = call_stack.top_mut().stack_mut().pop().unwrap(); let value = call_stack.top_mut().stack_mut().pop().unwrap();
if let Some(location) = maybe_location { if let Some(location) = maybe_location {
let fp = call_stack.top().fp(); put_value(registers, call_stack.top_mut(), location, value);
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
location,
value,
);
} }
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }

View File

@ -0,0 +1,55 @@
use crate::instruction::{ConstantName, Location};
use crate::vm::CallFrame;
use crate::vm::constant::Constant;
use crate::vm::value::Value;
use std::collections::HashMap;
use std::rc::Rc;
pub fn load_value<'a>(
registers: &'a [Value],
stack: &'a [Value],
fp: usize,
location: &Location,
) -> &'a Value {
match location {
Location::Register(register) => &registers[*register],
Location::StackFrameOffset(offset) => {
let value_index = fp.checked_add_signed(*offset).expect(&format!(
"Overflow when adding offset {} to fp {}",
*offset, fp
));
&stack[value_index]
}
}
}
pub fn load_constant_value(
constants: &HashMap<Rc<str>, Constant>,
constant_name: &ConstantName,
) -> Value {
let constant = &constants[constant_name];
match constant {
Constant::String(string_constant) => Value::String(string_constant.content_owned()),
}
}
pub fn put_value(
registers: &mut Vec<Value>,
current_frame: &mut CallFrame,
destination: &Location,
value: Value,
) {
match destination {
Location::Register(register) => {
registers[*register] = value;
}
Location::StackFrameOffset(offset) => {
let fp = current_frame.fp();
let target_index = fp.checked_add_signed(*offset).expect(&format!(
"Failed to calculate target index from fp + offset ({} + {})",
fp, offset
));
current_frame.stack_mut()[target_index] = value;
}
}
}

View File

@ -35,19 +35,25 @@ mod e2e_tests {
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table); match compilation_unit.gather_declared_names(&mut symbol_table) {
if !gather_names_diagnostics.is_empty() { Ok(_) => {}
report_diagnostics(&gather_names_diagnostics); Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
} }
let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table); match compilation_unit.check_name_usages(&symbol_table) {
if !name_usages_diagnostics.is_empty() { Ok(_) => {}
report_diagnostics(&name_usages_diagnostics); Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
} }
let type_check_diagnostics = compilation_unit.type_check(&symbol_table); match compilation_unit.type_check(&symbol_table) {
if !type_check_diagnostics.is_empty() { Ok(_) => {}
report_diagnostics(&type_check_diagnostics); Err(diagnostics) => {
report_diagnostics(&diagnostics);
}
} }
let mut ir_functions = compilation_unit.to_ir(&symbol_table); let mut ir_functions = compilation_unit.to_ir(&symbol_table);
@ -125,4 +131,9 @@ mod e2e_tests {
Value::String(Rc::from("Hello. 1 plus 2 is 3")), Value::String(Rc::from("Hello. 1 plus 2 is 3")),
); );
} }
#[test]
fn simple_subtract() {
assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1))
}
} }

16
examples/subtract.dm Normal file
View File

@ -0,0 +1,16 @@
extern fn println(message: Any) -> Void
fn add(a: Int, b: Int) -> Int
a + b
end
fn subtract(a: Int, b: Int) -> Int
a - b
end
fn main()
let a = 42
let b = 42
println(add(a, b))
println(subtract(a, b))
end