Much work on adding and calling.

This commit is contained in:
Jesse Brault 2026-03-03 21:06:12 -06:00
parent 81ceeeadb8
commit e1afb6b43b
20 changed files with 502 additions and 74 deletions

View File

@ -6,8 +6,10 @@ pub enum AsmInstruction {
Move(Move),
Push(Push),
Pop(Pop),
InvokeStatic(InvokeStatic),
InvokePlatformStatic(InvokePlatformStatic),
LoadConstant(LoadConstant),
Add(Add),
}
impl AsmInstruction {
@ -16,10 +18,12 @@ impl AsmInstruction {
AsmInstruction::Move(asm_move) => asm_move.dvm(),
AsmInstruction::Push(push) => push.dvm(),
AsmInstruction::Pop(pop) => pop.dvm(),
AsmInstruction::InvokeStatic(invoke_static) => invoke_static.dvm(),
AsmInstruction::InvokePlatformStatic(invoke_platform_static) => {
invoke_platform_static.dvm()
}
AsmInstruction::LoadConstant(load_constant) => load_constant.dvm(),
AsmInstruction::Add(add) => add.dvm(),
}
}
}
@ -100,6 +104,21 @@ impl Pop {
}
}
#[derive(Debug)]
pub struct InvokeStatic {
name: String,
}
impl InvokeStatic {
pub fn new(name: &str) -> Self {
Self { name: name.into() }
}
pub fn dvm(&self) -> Instruction {
Instruction::InvokeStatic(Rc::from(self.name.clone()))
}
}
#[derive(Debug)]
pub struct InvokePlatformStatic {
name: String,
@ -137,3 +156,24 @@ impl LoadConstant {
Instruction::LoadStringConstant(Rc::from(self.name.clone()), self.destination_register)
}
}
#[derive(Debug)]
pub enum Add {
IntInt(usize, usize, usize),
StringString(usize, usize, usize),
StringInt(usize, usize, usize),
}
impl Add {
pub fn dvm(&self) -> Instruction {
match self {
Add::IntInt(lhs, rhs, destination) => Instruction::AddIntInt(*lhs, *rhs, *destination),
Add::StringString(lhs, rhs, destination) => {
Instruction::AddStringString(*lhs, *rhs, *destination)
}
Add::StringInt(lhs, rhs, destination) => {
Instruction::AddStringInt(*lhs, *rhs, *destination)
}
}
}
}

View File

@ -0,0 +1,173 @@
use crate::asm::asm_instruction::{Add, AsmInstruction, LoadConstant, Move, Operand, Pop};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::expression::Expression;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::ExpressibleSymbol;
use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo;
pub struct AdditiveExpression {
lhs: Box<Expression>,
rhs: Box<Expression>,
source_range: SourceRange,
}
impl AdditiveExpression {
pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self {
Self {
lhs: lhs.into(),
rhs: rhs.into(),
source_range,
}
}
pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![];
diagnostics.append(&mut self.lhs.gather_declared_names(symbol_table));
diagnostics.append(&mut self.rhs.gather_declared_names(symbol_table));
if diagnostics.is_empty() {
Ok(())
} else {
Err(diagnostics)
}
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics = vec![];
diagnostics.append(&mut self.lhs.check_name_usages(symbol_table));
diagnostics.append(&mut self.rhs.check_name_usages(symbol_table));
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_add(&rhs_type_info) {
Ok(())
} else {
Err(vec![Diagnostic::new(
&format!("Cannot add {} to {}", lhs_type_info, rhs_type_info),
self.source_range.start(),
self.source_range.end(),
)])
}
}
fn assemble_side(
expression: &Expression,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) -> usize {
match expression {
Expression::Call(call) => {
call.assemble(context, symbol_table, constants_table);
let register = context.new_local_register();
context.instruction(AsmInstruction::Pop(Pop::new(register)));
register
}
Expression::IntegerLiteral(integer_literal) => {
let register = context.new_local_register();
context.instruction(AsmInstruction::Move(Move::new(
Operand::IntegerLiteral(integer_literal.value()),
register,
)));
register
}
Expression::String(string_literal) => {
let register = context.new_local_register();
let constant_name = constants_table.insert_string(string_literal.content());
context.instruction(AsmInstruction::LoadConstant(LoadConstant::new(
&constant_name,
register,
)));
register
}
Expression::Identifier(identifier) => {
let register = context.new_local_register();
match identifier.expressible_symbol() {
ExpressibleSymbol::Function(_) => unreachable!(),
ExpressibleSymbol::Parameter(parameter_symbol) => {
let offset = parameter_symbol.borrow().stack_frame_offset();
context.instruction(AsmInstruction::Move(Move::new(
Operand::StackFrameOffset(offset),
register,
)));
}
ExpressibleSymbol::Variable(variable_symbol) => {
context.instruction(AsmInstruction::Move(Move::new(
Operand::Register(variable_symbol.borrow().register()),
register,
)));
}
}
register
}
Expression::Additive(additive_expression) => {
additive_expression.assemble(context, symbol_table, constants_table)
}
}
}
pub fn assemble(
&self,
context: &mut AssembleContext,
symbol_table: &SymbolTable,
constants_table: &mut ConstantsTable,
) -> usize {
let lhs_register = Self::assemble_side(&self.lhs, context, symbol_table, constants_table);
let rhs_register = Self::assemble_side(&self.rhs, context, symbol_table, constants_table);
let result_register = context.new_local_register();
match self.lhs.type_info() {
TypeInfo::Integer => match self.rhs.type_info() {
TypeInfo::Integer => {
context.instruction(AsmInstruction::Add(Add::IntInt(
lhs_register,
rhs_register,
result_register,
)));
}
_ => unreachable!(),
},
TypeInfo::String => match self.rhs.type_info() {
TypeInfo::Integer => {
context.instruction(AsmInstruction::Add(Add::StringInt(
lhs_register,
rhs_register,
result_register,
)));
}
TypeInfo::String => {
context.instruction(AsmInstruction::Add(Add::StringString(
lhs_register,
rhs_register,
result_register,
)));
}
_ => unreachable!(),
},
_ => unreachable!(),
}
result_register
}
pub fn type_info(&self) -> TypeInfo {
self.lhs.type_info().additive_result(&self.rhs.type_info())
}
pub fn source_range(&self) -> &SourceRange {
&self.source_range
}
}

View File

@ -1,5 +1,5 @@
use crate::asm::asm_instruction::{
AsmInstruction, InvokePlatformStatic, LoadConstant, Operand, Push,
AsmInstruction, InvokePlatformStatic, InvokeStatic, LoadConstant, Operand, Push,
};
use crate::ast::assemble_context::AssembleContext;
use crate::ast::expression::Expression;
@ -166,6 +166,13 @@ impl Call {
))))
}
},
Expression::Additive(additive_expression) => {
let result_register =
additive_expression.assemble(context, symbol_table, constants_table);
context.instruction(AsmInstruction::Push(Push::new(Operand::Register(
result_register,
))));
}
}
}
@ -187,7 +194,9 @@ impl Call {
InvokePlatformStatic::new(function_symbol.name(), arg_count),
));
} else {
todo!("non-platform invoke")
context.instruction(AsmInstruction::InvokeStatic(InvokeStatic::new(
function_symbol.name(),
)));
}
}

View File

@ -1,3 +1,4 @@
use crate::ast::additive_expression::AdditiveExpression;
use crate::ast::call::Call;
use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral;
@ -12,6 +13,7 @@ pub enum Expression {
IntegerLiteral(IntegerLiteral),
String(StringLiteral),
Identifier(Identifier),
Additive(AdditiveExpression),
}
impl Expression {
@ -19,6 +21,14 @@ impl Expression {
match self {
Expression::Call(call) => call.gather_declared_names(symbol_table),
Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table),
Expression::Additive(additive_expression) => {
match additive_expression.gather_declared_names(symbol_table) {
Ok(_) => {
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
}
}
@ -27,6 +37,14 @@ impl Expression {
match self {
Expression::Call(call) => call.check_name_usages(symbol_table),
Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table),
Expression::Additive(additive_expression) => {
match additive_expression.check_name_usages(symbol_table) {
Ok(_) => {
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
}
}
@ -34,6 +52,14 @@ impl Expression {
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
match self {
Expression::Call(call) => call.type_check(symbol_table),
Expression::Additive(additive_expression) => {
match additive_expression.type_check(symbol_table) {
Ok(_) => {
vec![]
}
Err(diagnostics) => diagnostics,
}
}
_ => vec![],
}
}
@ -44,6 +70,7 @@ impl Expression {
Expression::IntegerLiteral(_) => TypeInfo::Integer,
Expression::String(_) => TypeInfo::String,
Expression::Identifier(identifier) => identifier.type_info(),
Expression::Additive(additive_expression) => additive_expression.type_info(),
}
}
@ -53,6 +80,7 @@ impl Expression {
Expression::IntegerLiteral(integer_literal) => integer_literal.source_range(),
Expression::String(string_literal) => string_literal.source_range(),
Expression::Identifier(identifier) => identifier.source_range(),
Expression::Additive(additive_expression) => additive_expression.source_range(),
}
}
}

View File

@ -41,6 +41,9 @@ impl ExpressionStatement {
Expression::Call(call) => {
call.assemble(context, symbol_table, constants_table);
}
Expression::Additive(additive) => {
additive.assemble(context, symbol_table, constants_table);
}
_ => unreachable!(),
}
}

View File

@ -1,4 +1,5 @@
use crate::ast::parameter::Parameter;
use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::FunctionSymbol;
@ -8,6 +9,7 @@ pub struct ExternFunction {
declared_name: String,
declared_name_source_range: SourceRange,
parameters: Vec<Parameter>,
return_type: TypeUse,
}
impl ExternFunction {
@ -15,11 +17,13 @@ impl ExternFunction {
name: &str,
declared_name_source_range: SourceRange,
parameters: Vec<Parameter>,
return_type: TypeUse,
) -> ExternFunction {
ExternFunction {
declared_name: name.into(),
declared_name_source_range,
parameters,
return_type,
}
}
@ -30,8 +34,11 @@ impl ExternFunction {
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
let mut diagnostics = vec![];
let insert_result =
symbol_table.insert_function_symbol(FunctionSymbol::new(&self.declared_name, true));
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
&self.declared_name,
true,
self.return_type.to_type_info(),
));
let function_symbol = match insert_result {
Ok(function_symbol) => function_symbol,

View File

@ -1,16 +1,20 @@
use crate::ast::assemble_context::AssembleContext;
use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement;
use crate::ast::type_use::TypeUse;
use crate::constants_table::ConstantsTable;
use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::FunctionSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable};
use crate::type_info::TypeInfo;
use std::ops::Neg;
pub struct Function {
declared_name: String,
declared_name_source_range: SourceRange,
parameters: Vec<Parameter>,
return_type: Option<TypeUse>,
statements: Vec<Statement>,
}
@ -19,12 +23,14 @@ impl Function {
declared_name: &str,
declared_name_source_range: SourceRange,
parameters: Vec<Parameter>,
return_type: Option<TypeUse>,
statements: Vec<Statement>,
) -> Self {
Self {
declared_name: declared_name.to_string(),
declared_name_source_range,
parameters,
return_type,
statements,
}
}
@ -41,8 +47,14 @@ impl Function {
let mut diagnostics = vec![];
// insert function symbol
let insert_result =
symbol_table.insert_function_symbol(FunctionSymbol::new(self.declared_name(), false));
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
self.declared_name(),
false,
self.return_type
.as_ref()
.map(|return_type| return_type.to_type_info())
.unwrap_or(TypeInfo::Void),
));
// get function symbol if successful
let function_symbol = match insert_result {
@ -67,9 +79,14 @@ impl Function {
// handle parameters
symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name));
let mut parameter_symbols = vec![];
for parameter in &mut self.parameters {
let parameter_count = self.parameters.len();
let stack_frame_offset_base = (parameter_count as isize).neg();
for (i, parameter) in self.parameters.iter_mut().enumerate() {
match parameter.gather_declared_names(symbol_table) {
Ok(parameter_symbol) => {
parameter_symbol
.borrow_mut()
.set_stack_frame_offset(stack_frame_offset_base + (i as isize));
parameter_symbols.push(parameter_symbol);
}
Err(mut parameter_diagnostics) => {
@ -144,6 +161,7 @@ impl Function {
for statement in &self.statements {
statement.assemble(context, symbol_table, constants_table);
}
// todo: function exit, including popping passed args and pushing return value
context.complete_function();
}
}

View File

@ -130,6 +130,13 @@ impl LetStatement {
}
}
}
Expression::Additive(additive) => {
let result_register = additive.assemble(context, symbol_table, constants_table);
context.instruction(AsmInstruction::Move(Move::new(
Operand::Register(result_register),
destination_register,
)));
}
}
}
}

View File

@ -1,3 +1,4 @@
pub mod additive_expression;
pub mod assemble_context;
pub mod call;
pub mod compilation_unit;

View File

@ -1,4 +1,5 @@
use crate::source_range::SourceRange;
use crate::type_info::TypeInfo;
pub struct TypeUse {
declared_name: String,
@ -16,4 +17,8 @@ impl TypeUse {
pub fn declared_name(&self) -> &str {
&self.declared_name
}
pub fn to_type_info(&self) -> TypeInfo {
TypeInfo::from_declared_name(self.declared_name())
}
}

View File

@ -34,7 +34,9 @@ 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)
} else if chunk.starts_with("(") {
Token::new(self.position, self.position + 1, TokenKind::LeftParentheses)
} else if chunk.starts_with(")") {
Token::new(
@ -42,6 +44,8 @@ impl<'a> Lexer<'a> {
self.position + 1,
TokenKind::RightParentheses,
)
} else if chunk.starts_with("+") {
Token::new(self.position, self.position + 1, TokenKind::Plus)
} else if chunk.starts_with("=") {
Token::new(self.position, self.position + 1, TokenKind::Equals)
} else if chunk.starts_with(",") {

View File

@ -1,3 +1,4 @@
use crate::ast::additive_expression::AdditiveExpression;
use crate::ast::call::Call;
use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::expression::Expression;
@ -218,13 +219,26 @@ impl<'a> Parser<'a> {
fn function(&mut self) -> Result<Function, Vec<Diagnostic>> {
self.expect_advance(TokenKind::Fn)?;
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
self.expect_advance(TokenKind::LeftParentheses)?;
let parameters = self.parameter_list()?;
self.expect_advance(TokenKind::RightParentheses)?;
let mut statements = vec![];
let mut diagnostics = vec![];
let return_type = if self.current.is_some() && self.peek_current(TokenKind::RightArrow) {
match self.return_type() {
Ok(type_use) => Some(type_use),
Err(mut return_type_diagnostics) => {
diagnostics.append(&mut return_type_diagnostics);
None
}
}
} else {
None
};
let mut statements = vec![];
while self.current.is_some() && !self.peek_current(TokenKind::End) {
let statement_result = self.statement();
match statement_result {
@ -251,6 +265,7 @@ impl<'a> Parser<'a> {
self.token_text(&identifier_token),
SourceRange::new(identifier_token.start(), identifier_token.end()),
parameters,
return_type,
statements,
))
} else {
@ -285,11 +300,14 @@ impl<'a> Parser<'a> {
Ok(_) => {}
}
let return_type = self.return_type()?;
if diagnostics.is_empty() {
Ok(ExternFunction::new(
self.token_text(&identifier_token),
SourceRange::new(identifier_token.start(), identifier_token.end()),
maybe_parameters.unwrap(),
return_type,
))
} else {
Err(diagnostics)
@ -331,6 +349,11 @@ impl<'a> Parser<'a> {
))
}
fn return_type(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
self.expect_advance(TokenKind::RightArrow)?;
self.type_use()
}
fn type_use(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
Ok(TypeUse::new(
@ -364,96 +387,102 @@ impl<'a> Parser<'a> {
}
fn expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let current = self.get_current().clone(); // I don't love this clone
let mut diagnostics = vec![];
let mut expression = match current.kind() {
self.additive_expression()
}
fn additive_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let mut result = self.suffix_expression()?;
while self.current.is_some() {
let current = self.get_current();
match current.kind() {
TokenKind::Plus => {
self.advance(); // plus
let rhs = self.expression()?;
let source_range =
SourceRange::new(result.source_range().start(), rhs.source_range().end());
result =
Expression::Additive(AdditiveExpression::new(result, rhs, source_range));
}
_ => break,
}
}
Ok(result)
}
fn suffix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let mut result = self.expression_base()?;
while self.current.is_some() {
let current = self.get_current();
match current.kind() {
TokenKind::LeftParentheses => {
result = Expression::Call(self.call(result)?);
}
_ => break,
}
}
Ok(result)
}
fn expression_base(&mut self) -> Result<Expression, Vec<Diagnostic>> {
let current = self.get_current();
match current.kind() {
TokenKind::IntegerLiteral => {
let raw = self.token_text(&current);
let source_range = SourceRange::new(current.start(), current.end());
self.advance();
Expression::IntegerLiteral(IntegerLiteral::new(
Ok(Expression::IntegerLiteral(IntegerLiteral::new(
i32::from_str(raw).unwrap(),
source_range,
))
)))
}
TokenKind::String => {
let with_quotes = self.token_text(&current);
let source_range = SourceRange::new(current.start(), current.end());
self.advance();
Expression::String(StringLiteral::new(
Ok(Expression::String(StringLiteral::new(
&with_quotes[1..with_quotes.len() - 1],
source_range,
))
)))
}
TokenKind::Identifier => {
let declared_name = self.token_text(&current);
let source_range = SourceRange::new(current.start(), current.end());
self.advance();
Expression::Identifier(Identifier::new(declared_name, source_range))
Ok(Expression::Identifier(Identifier::new(
declared_name,
source_range,
)))
}
_ => {
diagnostics.push(Diagnostic::new(
&format!(
"Expected any of {:?} but found {:?}",
[
TokenKind::IntegerLiteral,
TokenKind::String,
TokenKind::Identifier
],
current.kind()
),
current.start(),
current.end(),
));
self.advance_until(&[
TokenKind::IntegerLiteral,
TokenKind::String,
TokenKind::Identifier,
]);
if self.current.is_some() {
let try_again_result = self.expression();
match try_again_result {
Ok(expression) => expression,
Err(mut try_again_diagnostics) => {
diagnostics.append(&mut try_again_diagnostics);
return Err(diagnostics);
}
}
} else {
return Err(diagnostics);
}
}
};
// postfixes
while let Some(current) = &self.current {
match current.kind() {
TokenKind::LeftParentheses => {
expression = Expression::Call(self.call(expression)?);
}
_ => break,
}
}
if diagnostics.is_empty() {
Ok(expression)
} else {
Err(diagnostics)
_ => unreachable!(),
}
}
fn call(&mut self, callee: Expression) -> Result<Call, Vec<Diagnostic>> {
self.expect_advance(TokenKind::LeftParentheses)?;
let mut arguments = vec![];
while self.current.is_some() && !self.peek_current(TokenKind::RightParentheses) {
arguments.push(self.expression()?);
if let Some(current) = &self.current {
if matches!(
current.kind(),
TokenKind::IntegerLiteral | TokenKind::String | TokenKind::Identifier
) {
arguments.append(&mut self.expression_list()?);
}
}
let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses)?;
let source_range =
SourceRange::new(callee.source_range().start(), right_parentheses_token.end());
Ok(Call::new(callee, arguments, source_range))
}
fn expression_list(&mut self) -> Result<Vec<Expression>, Vec<Diagnostic>> {
let mut expressions = vec![];
expressions.push(self.expression()?);
while self.current.is_some() && self.peek_current(TokenKind::Comma) {
self.advance(); // comma
expressions.push(self.expression()?);
}
Ok(expressions)
}
}
#[cfg(test)]
@ -483,7 +512,7 @@ mod smoke_tests {
#[test]
fn extern_fn_with_param() {
smoke_test("extern fn println(message: Any)");
smoke_test("extern fn println(message: Any) -> Void");
}
#[test]
@ -495,6 +524,21 @@ mod smoke_tests {
fn fn_with_params() {
smoke_test("fn foo(bar: Int, baz: Int) end");
}
#[test]
fn return_type() {
smoke_test("fn foo() -> Int end")
}
#[test]
fn extern_return_type() {
smoke_test("extern fn foo() -> Int");
}
#[test]
fn add_two_numbers() {
smoke_test("fn main() 1 + 2 end");
}
}
#[cfg(test)]
@ -503,7 +547,7 @@ mod concrete_tests {
#[test]
fn parses_extern_fn() {
let parse_result = parse_compilation_unit("extern fn println()");
let parse_result = parse_compilation_unit("extern fn println() -> Void");
let compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {

View File

@ -6,14 +6,16 @@ pub struct FunctionSymbol {
name: Rc<str>,
is_platform: bool,
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
return_type: TypeInfo,
}
impl FunctionSymbol {
pub fn new(name: &str, is_platform: bool) -> Self {
pub fn new(name: &str, is_platform: bool, return_type: TypeInfo) -> Self {
Self {
name: name.into(),
is_platform,
parameters: None,
return_type,
}
}
@ -34,7 +36,7 @@ impl FunctionSymbol {
}
pub fn return_type(&self) -> TypeInfo {
todo!()
self.return_type.clone()
}
pub fn is_platform(&self) -> bool {

View File

@ -38,4 +38,6 @@ pub enum TokenKind {
Extern,
Comma,
Colon,
RightArrow,
Plus,
}

View File

@ -9,6 +9,7 @@ pub enum TypeInfo {
Integer,
String,
Function(Rc<RefCell<FunctionSymbol>>),
Void,
}
impl Display for TypeInfo {
@ -24,6 +25,7 @@ impl Display for TypeInfo {
}
write!(f, ")")
}
TypeInfo::Void => write!(f, "Void"),
}
}
}
@ -35,6 +37,7 @@ impl TypeInfo {
"Any" => TypeInfo::Any,
"Int" => TypeInfo::Integer,
"String" => TypeInfo::String,
"Void" => TypeInfo::Void,
_ => panic!("Unknown type: {}", declared_name),
}
}
@ -51,6 +54,25 @@ impl TypeInfo {
TypeInfo::Function(_) => {
unimplemented!("Type matching on Functions not yet supported.")
}
TypeInfo::Void => {
matches!(other, TypeInfo::Void)
}
}
}
pub fn can_add(&self, rhs: &Self) -> bool {
match self {
TypeInfo::Integer => matches!(rhs, TypeInfo::Integer),
TypeInfo::String => matches!(rhs, TypeInfo::String | TypeInfo::Integer),
_ => false,
}
}
pub fn additive_result(&self, _rhs: &Self) -> TypeInfo {
match self {
TypeInfo::Integer => TypeInfo::Integer,
TypeInfo::String => TypeInfo::String,
_ => panic!("Adding things other than integers and strings not yet supported"),
}
}
}

View File

@ -14,9 +14,14 @@ pub enum Instruction {
PushInt(i32),
PushStackFrameOffset(isize),
InvokeStatic(FunctionName),
InvokePlatformStatic(FunctionName, ArgCount),
LoadStringConstant(ConstantName, Register),
Pop(Option<Register>),
AddIntInt(Register, Register, Register),
AddStringInt(Register, Register, Register),
AddStringString(Register, Register, Register),
}

View File

@ -174,6 +174,14 @@ pub fn call(
}
/* Invoke instructions */
Instruction::InvokeStatic(function_name) => {
let function = context
.functions
.get(function_name)
.expect(&format!("Function {} not found", function_name));
todo!("Call stack for invoking Deimos-native functions")
}
Instruction::InvokePlatformStatic(function_name, arg_count) => {
let stack = state.stack();
let args = &stack[stack.len() - arg_count..];
@ -210,6 +218,26 @@ pub fn call(
}
}
/* Add instructions */
Instruction::AddIntInt(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_int();
let rhs_value = state.registers()[*rhs].unwrap_int();
let result = lhs_value + rhs_value;
state.registers_mut()[*destination] = Value::Int(result);
}
Instruction::AddStringInt(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_string();
let rhs_value = state.registers()[*rhs].unwrap_int();
let formatted = format!("{}{}", lhs_value, rhs_value);
state.registers_mut()[*destination] = Value::String(Rc::from(formatted));
}
Instruction::AddStringString(lhs, rhs, destination) => {
let lhs_value = state.registers()[*lhs].unwrap_string();
let rhs_value = state.registers()[*rhs].unwrap_string();
let formatted = format!("{}{}", lhs_value, rhs_value);
state.registers_mut()[*destination] = Value::String(Rc::from(formatted));
}
/* Pop instructions */
Instruction::Pop(maybe_register) => {
let value = state.stack_mut().pop().unwrap();

View File

@ -12,3 +12,19 @@ impl Default for Value {
Value::Null
}
}
impl Value {
pub fn unwrap_int(&self) -> i32 {
match self {
Value::Int(i) => *i,
_ => panic!(),
}
}
pub fn unwrap_string(&self) -> &Rc<str> {
match self {
Value::String(s) => s,
_ => panic!(),
}
}
}

5
examples/add.dm Normal file
View File

@ -0,0 +1,5 @@
extern fn println(message: Any) -> Void
fn main()
println(6 + 7)
end

9
examples/call.dm Normal file
View File

@ -0,0 +1,9 @@
extern fn println(message: Any) -> Void
fn add(a: Int, b: Int) -> Int
a + b
end
fn main()
println(add(1, 2))
end