From 9df9edc508217eff6503e11fad5ca4707b0d3ffa Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Tue, 10 Mar 2026 17:32:02 -0500 Subject: [PATCH] Add doubles. --- dm-std-lib/src/lib.rs | 3 + dmc-lib/src/ast/add_expression.rs | 2 +- dmc-lib/src/ast/double_literal.rs | 31 ++++++ dmc-lib/src/ast/expression.rs | 10 ++ dmc-lib/src/ast/let_statement.rs | 3 + dmc-lib/src/ast/mod.rs | 1 + dmc-lib/src/ir/ir_expression.rs | 49 ++++++--- dmc-lib/src/lexer.rs | 65 +++++++++++- dmc-lib/src/parser.rs | 15 ++- dmc-lib/src/token.rs | 1 + dmc-lib/src/type_info.rs | 44 ++++++-- dvm-lib/src/instruction.rs | 20 ++++ dvm-lib/src/vm/instruction_helpers.rs | 28 ++++- dvm-lib/src/vm/mod.rs | 143 ++++++++++++-------------- dvm-lib/src/vm/value.rs | 13 ++- e2e-tests/src/lib.rs | 72 ++++++++++++- examples/doubles.dm | 9 ++ 17 files changed, 399 insertions(+), 110 deletions(-) create mode 100644 dmc-lib/src/ast/double_literal.rs create mode 100644 examples/doubles.dm diff --git a/dm-std-lib/src/lib.rs b/dm-std-lib/src/lib.rs index f104817..3126eaa 100644 --- a/dm-std-lib/src/lib.rs +++ b/dm-std-lib/src/lib.rs @@ -44,6 +44,9 @@ pub fn std_core_println(args: &[Value]) -> Result> { Value::Int(i) => { println!("{}", i); } + Value::Double(d) => { + println!("{}", d); + } Value::String(s) => { println!("{}", s); } diff --git a/dmc-lib/src/ast/add_expression.rs b/dmc-lib/src/ast/add_expression.rs index 98d5ee2..4897c6c 100644 --- a/dmc-lib/src/ast/add_expression.rs +++ b/dmc-lib/src/ast/add_expression.rs @@ -73,7 +73,7 @@ impl AddExpression { Ok(()) } else { Err(vec![Diagnostic::new( - &format!("Cannot add {} to {}", lhs_type_info, rhs_type_info), + &format!("Cannot add {} to {}", rhs_type_info, lhs_type_info), self.source_range.start(), self.source_range.end(), )]) diff --git a/dmc-lib/src/ast/double_literal.rs b/dmc-lib/src/ast/double_literal.rs new file mode 100644 index 0000000..b743fb3 --- /dev/null +++ b/dmc-lib/src/ast/double_literal.rs @@ -0,0 +1,31 @@ +use crate::source_range::SourceRange; +use crate::type_info::TypeInfo; + +pub struct DoubleLiteral { + value: f64, + source_range: SourceRange, + type_info: &'static TypeInfo, +} + +impl DoubleLiteral { + pub fn new(value: f64, source_range: SourceRange) -> Self { + const TYPE_INFO: TypeInfo = TypeInfo::Double; + Self { + value, + source_range, + type_info: &TYPE_INFO, + } + } + + pub fn value(&self) -> f64 { + self.value + } + + pub fn type_info(&self) -> &TypeInfo { + &self.type_info + } + + pub fn source_range(&self) -> &SourceRange { + &self.source_range + } +} diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index ac40cf1..05c8784 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -1,5 +1,6 @@ use crate::ast::add_expression::AddExpression; use crate::ast::call::Call; +use crate::ast::double_literal::DoubleLiteral; use crate::ast::identifier::Identifier; use crate::ast::integer_literal::IntegerLiteral; use crate::ast::ir_builder::IrBuilder; @@ -25,6 +26,7 @@ pub enum Expression { Call(Call), Identifier(Identifier), Integer(IntegerLiteral), + Double(DoubleLiteral), String(StringLiteral), } @@ -44,6 +46,7 @@ impl Expression { Expression::Call(call) => call.gather_declared_names(symbol_table), Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), Expression::Integer(_) => Ok(()), + Expression::Double(_) => Ok(()), Expression::String(_) => Ok(()), } } @@ -60,6 +63,7 @@ impl Expression { Expression::Call(call) => call.check_name_usages(symbol_table), Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table), Expression::Integer(_) => Ok(()), + Expression::Double(_) => Ok(()), Expression::String(_) => Ok(()), } } @@ -76,6 +80,7 @@ impl Expression { Expression::Call(call) => call.type_check(symbol_table), Expression::Identifier(identifier) => identifier.type_check(symbol_table), Expression::Integer(_) => Ok(()), + Expression::Double(_) => Ok(()), Expression::String(_) => Ok(()), } } @@ -88,6 +93,7 @@ impl Expression { Expression::Call(call) => call.return_type_info(), Expression::Identifier(identifier) => identifier.type_info(), Expression::Integer(integer_literal) => integer_literal.type_info(), + Expression::Double(double_literal) => double_literal.type_info(), Expression::String(string_literal) => string_literal.type_info(), } } @@ -100,6 +106,7 @@ impl Expression { Expression::Call(call) => call.source_range(), Expression::Identifier(identifier) => identifier.source_range(), Expression::Integer(integer_literal) => integer_literal.source_range(), + Expression::Double(double_literal) => double_literal.source_range(), Expression::String(string_literal) => string_literal.source_range(), } } @@ -134,6 +141,9 @@ impl Expression { Expression::Integer(integer_literal) => { Some(IrExpression::Int(integer_literal.value())) } + Expression::Double(double_literal) => { + Some(IrExpression::Double(double_literal.value())) + } Expression::String(string_literal) => { Some(IrExpression::String(string_literal.content().into())) } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index fc88df5..90952dc 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -105,6 +105,9 @@ impl LetStatement { Expression::Integer(integer_literal) => { IrOperation::Load(IrExpression::Int(integer_literal.value())) } + Expression::Double(double_literal) => { + IrOperation::Load(IrExpression::Double(double_literal.value())) + } Expression::String(string_literal) => { IrOperation::Load(IrExpression::String(string_literal.content().into())) } diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index fb681e5..a918efb 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -1,6 +1,7 @@ pub mod add_expression; pub mod call; pub mod compilation_unit; +pub mod double_literal; pub mod expression; pub mod expression_statement; pub mod extern_function; diff --git a/dmc-lib/src/ir/ir_expression.rs b/dmc-lib/src/ir/ir_expression.rs index 816c7fc..99b3638 100644 --- a/dmc-lib/src/ir/ir_expression.rs +++ b/dmc-lib/src/ir/ir_expression.rs @@ -17,6 +17,7 @@ pub enum IrExpression { Parameter(Rc), Variable(Rc>), Int(i32), + Double(f64), String(Rc), } @@ -26,6 +27,7 @@ impl IrExpression { IrExpression::Parameter(ir_parameter) => ir_parameter.type_info().clone(), IrExpression::Variable(ir_variable) => ir_variable.borrow().type_info().clone(), IrExpression::Int(_) => TypeInfo::Integer, + IrExpression::Double(_) => TypeInfo::Double, IrExpression::String(_) => TypeInfo::String, } } @@ -44,6 +46,7 @@ impl IrExpression { } }, IrExpression::Int(i) => MoveOperand::Int(*i), + IrExpression::Double(d) => MoveOperand::Double(*d), IrExpression::String(s) => { let constant_name = constants_table.get_or_insert(s); MoveOperand::String(constant_name) @@ -65,6 +68,7 @@ impl IrExpression { } }, IrExpression::Int(i) => PushOperand::Int(*i), + IrExpression::Double(d) => PushOperand::Double(*d), IrExpression::String(s) => { let constant_name = constants_table.get_or_insert(s); PushOperand::String(constant_name) @@ -75,31 +79,34 @@ impl IrExpression { pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand { match self { IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { - TypeInfo::Integer | TypeInfo::String => { + TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => { AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } _ => panic!( - "Attempt to add non-integer/non-string (found: {})", + "Attempt to add non- integer/double/string (found: {})", ir_parameter.type_info() ), }, IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() { - TypeInfo::Integer | TypeInfo::String => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(register_variable) => { - AddOperand::Location(Location::Register( - register_variable.assigned_register(), - )) + TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => { + match ir_variable.borrow().descriptor() { + IrVariableDescriptor::VirtualRegister(register_variable) => { + AddOperand::Location(Location::Register( + register_variable.assigned_register(), + )) + } + IrVariableDescriptor::Stack(stack_variable) => AddOperand::Location( + Location::StackFrameOffset(stack_variable.offset()), + ), } - IrVariableDescriptor::Stack(stack_variable) => { - AddOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } _ => panic!( "Attempt to add non-integer/non-string (found: {})", ir_variable.borrow().type_info() ), }, IrExpression::Int(i) => AddOperand::Int(*i), + IrExpression::Double(d) => AddOperand::Double(*d), IrExpression::String(s) => { let constant_name = constants_table.get_or_insert(s); AddOperand::String(constant_name) @@ -110,16 +117,16 @@ 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(), - )), + TypeInfo::Integer | TypeInfo::Double => 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() { + TypeInfo::Integer | TypeInfo::Double => match ir_variable.borrow().descriptor() { IrVariableDescriptor::VirtualRegister(vr_variable) => { SubtractOperand::Location(Location::Register( vr_variable.assigned_register(), @@ -135,6 +142,7 @@ impl IrExpression { ), }, IrExpression::Int(i) => SubtractOperand::Int(*i), + IrExpression::Double(d) => SubtractOperand::Double(*d), IrExpression::String(_) => { panic!("Attempt to subtract with a string"); } @@ -157,6 +165,7 @@ impl IrExpression { } }, IrExpression::Int(i) => ReturnOperand::Int(*i), + IrExpression::Double(d) => ReturnOperand::Double(*d), IrExpression::String(s) => { let constant_name = constants_table.get_or_insert(s); ReturnOperand::String(constant_name) @@ -177,6 +186,9 @@ impl Display for IrExpression { IrExpression::Int(i) => { write!(f, "{}", i) } + IrExpression::Double(d) => { + write!(f, "{}", d) + } IrExpression::String(s) => { write!(f, "\"{}\"", s) } @@ -202,6 +214,7 @@ impl VrUser for IrExpression { } } IrExpression::Int(_) => HashSet::new(), + IrExpression::Double(_) => HashSet::new(), IrExpression::String(_) => HashSet::new(), } } @@ -230,6 +243,9 @@ impl VrUser for IrExpression { IrExpression::Int(_) => { // no-op } + IrExpression::Double(_) => { + // no-op + } IrExpression::String(_) => { // no-op } @@ -257,6 +273,9 @@ impl VrUser for IrExpression { IrExpression::Int(_) => { // no-op } + IrExpression::Double(_) => { + // no-op + } IrExpression::String(_) => { // no-op } diff --git a/dmc-lib/src/lexer.rs b/dmc-lib/src/lexer.rs index 53df915..6a475d3 100644 --- a/dmc-lib/src/lexer.rs +++ b/dmc-lib/src/lexer.rs @@ -59,14 +59,37 @@ impl<'a> Lexer<'a> { if chunk.starts_with(|c: char| c.is_ascii_digit()) { // number literal let mut end = self.position; - for char in chunk.chars() { - if char.is_ascii_digit() { + let mut whole_chars = chunk.chars(); + while let Some(c) = whole_chars.next() { + if c.is_ascii_digit() { end += 1; } else { break; } } - Token::new(self.position, end, TokenKind::IntegerLiteral) + + let mut fraction_chars = chunk.chars().skip(end - self.position); + if fraction_chars.next().map(|c| c == '.').unwrap_or(false) { + let mut found_fraction = false; + while let Some(c) = fraction_chars.next() { + if c.is_ascii_digit() { + end += 1; + if !found_fraction { + end += 1; // to account for decimal point + found_fraction = true; + } + } else { + break; + } + } + if found_fraction { + Token::new(self.position, end, TokenKind::DoubleLiteral) + } else { + Token::new(self.position, end, TokenKind::IntegerLiteral) + } + } else { + Token::new(self.position, end, TokenKind::IntegerLiteral) + } } else if chunk.starts_with("\"") { // string literal let mut end = self.position; @@ -180,4 +203,40 @@ mod tests { let mut lexer = Lexer::new("extern"); assert_next(&mut lexer, TokenKind::Extern, 6); } + + #[test] + fn double_literal() { + let mut lexer = Lexer::new("123.45"); + assert_next(&mut lexer, TokenKind::DoubleLiteral, 6); + } + + #[test] + fn one_digit_whole_part_double() { + let mut lexer = Lexer::new("1.0"); + assert_next(&mut lexer, TokenKind::DoubleLiteral, 3); + } + + #[test] + fn two_digits_fraction_double() { + let mut lexer = Lexer::new("1.23"); + assert_next(&mut lexer, TokenKind::DoubleLiteral, 4); + } + + #[test] + fn int_literal() { + let mut lexer = Lexer::new("123"); + assert_next(&mut lexer, TokenKind::IntegerLiteral, 3); + } + + #[test] + fn doubles() { + let mut lexer = Lexer::new("println(1.23 + 2.34)"); + assert_next(&mut lexer, TokenKind::Identifier, 7); + assert_next(&mut lexer, TokenKind::LeftParentheses, 1); + assert_next(&mut lexer, TokenKind::DoubleLiteral, 4); + assert_next(&mut lexer, TokenKind::Plus, 1); + assert_next(&mut lexer, TokenKind::DoubleLiteral, 4); + assert_next(&mut lexer, TokenKind::RightParentheses, 1); + assert!(lexer.next().is_none()); + } } diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 78af7ae..96efc41 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -1,6 +1,7 @@ use crate::ast::add_expression::AddExpression; use crate::ast::call::Call; use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::double_literal::DoubleLiteral; use crate::ast::expression::Expression; use crate::ast::expression_statement::ExpressionStatement; use crate::ast::extern_function::ExternFunction; @@ -481,6 +482,15 @@ impl<'a> Parser<'a> { source_range, ))) } + TokenKind::DoubleLiteral => { + let raw = self.token_text(¤t); + let source_range = SourceRange::new(current.start(), current.end()); + self.advance(); + Ok(Expression::Double(DoubleLiteral::new( + f64::from_str(raw).unwrap(), + source_range, + ))) + } TokenKind::String => { let with_quotes = self.token_text(¤t); let source_range = SourceRange::new(current.start(), current.end()); @@ -509,7 +519,10 @@ impl<'a> Parser<'a> { if let Some(current) = &self.current { if matches!( current.kind(), - TokenKind::IntegerLiteral | TokenKind::String | TokenKind::Identifier + TokenKind::IntegerLiteral + | TokenKind::DoubleLiteral + | TokenKind::String + | TokenKind::Identifier ) { arguments.append(&mut self.expression_list()?); } diff --git a/dmc-lib/src/token.rs b/dmc-lib/src/token.rs index b96f3f7..7e79457 100644 --- a/dmc-lib/src/token.rs +++ b/dmc-lib/src/token.rs @@ -33,6 +33,7 @@ pub enum TokenKind { Let, Equals, IntegerLiteral, + DoubleLiteral, LongLiteral, String, Extern, diff --git a/dmc-lib/src/type_info.rs b/dmc-lib/src/type_info.rs index 28d75cf..b060126 100644 --- a/dmc-lib/src/type_info.rs +++ b/dmc-lib/src/type_info.rs @@ -7,6 +7,7 @@ use std::rc::Rc; pub enum TypeInfo { Any, Integer, + Double, String, Function(Rc>), Void, @@ -17,6 +18,7 @@ impl Display for TypeInfo { match self { TypeInfo::Any => write!(f, "Any"), TypeInfo::Integer => write!(f, "Int"), + TypeInfo::Double => write!(f, "Double"), TypeInfo::String => write!(f, "String"), TypeInfo::Function(function_symbol) => { write!(f, "fn(")?; @@ -36,6 +38,7 @@ impl TypeInfo { match declared_name { "Any" => TypeInfo::Any, "Int" => TypeInfo::Integer, + "Double" => TypeInfo::Double, "String" => TypeInfo::String, "Void" => TypeInfo::Void, _ => panic!("Unknown type: {}", declared_name), @@ -48,6 +51,9 @@ impl TypeInfo { TypeInfo::Integer => { matches!(other, TypeInfo::Integer) } + TypeInfo::Double => { + matches!(other, TypeInfo::Double) + } TypeInfo::String => { matches!(other, TypeInfo::String) } @@ -62,8 +68,15 @@ impl TypeInfo { pub fn can_add(&self, rhs: &Self) -> bool { match self { - TypeInfo::Integer => matches!(rhs, TypeInfo::Integer), - TypeInfo::String => matches!(rhs, TypeInfo::String | TypeInfo::Integer), + TypeInfo::Integer => { + matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String) + } + TypeInfo::Double => { + matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String) + } + TypeInfo::String => { + matches!(rhs, TypeInfo::Integer | TypeInfo::Double | TypeInfo::String) + } _ => false, } } @@ -72,24 +85,41 @@ impl TypeInfo { match self { TypeInfo::Integer => match rhs { TypeInfo::Integer => TypeInfo::Integer, + TypeInfo::Double => TypeInfo::Double, TypeInfo::String => TypeInfo::String, - _ => panic!( - "Adding things other than integers/strings to integer not yet supported." - ), + _ => panic!("Unsupported add: {} + {}.", self, rhs), + }, + TypeInfo::Double => match rhs { + TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double, + TypeInfo::String => TypeInfo::String, + _ => panic!("Unsupported add: {} + {}.", self, rhs), }, TypeInfo::String => TypeInfo::String, - _ => panic!("Adding things other than integers and strings not yet supported"), + _ => panic!("Unsupported add: {} + {}.", self, rhs), } } pub fn can_subtract(&self, rhs: &Self) -> bool { - matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer) + match self { + TypeInfo::Integer => { + matches!(rhs, TypeInfo::Integer | TypeInfo::Double) + } + TypeInfo::Double => { + matches!(rhs, TypeInfo::Integer | TypeInfo::Double) + } + _ => false, + } } pub fn subtract_result(&self, rhs: &Self) -> TypeInfo { match self { TypeInfo::Integer => match rhs { TypeInfo::Integer => TypeInfo::Integer, + TypeInfo::Double => TypeInfo::Double, + _ => panic!(), + }, + TypeInfo::Double => match rhs { + TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double, _ => panic!(), }, _ => panic!(), diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index 47fa44d..5f2e0a3 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -89,6 +89,7 @@ impl Display for Location { pub enum MoveOperand { Location(Location), Int(i32), + Double(f64), String(Rc), } @@ -101,6 +102,9 @@ impl Display for MoveOperand { MoveOperand::Int(i) => { write!(f, "{}", i) } + MoveOperand::Double(d) => { + write!(f, "{}", d) + } MoveOperand::String(s) => { write!(f, "{}", s) } @@ -111,6 +115,7 @@ impl Display for MoveOperand { pub enum PushOperand { Location(Location), Int(i32), + Double(f64), String(Rc), } @@ -123,6 +128,9 @@ impl Display for PushOperand { PushOperand::Int(i) => { write!(f, "{}", i) } + PushOperand::Double(d) => { + write!(f, "{}", d) + } PushOperand::String(s) => { write!(f, "{}", s) } @@ -133,6 +141,7 @@ impl Display for PushOperand { pub enum AddOperand { Location(Location), Int(i32), + Double(f64), String(Rc), } @@ -145,6 +154,9 @@ impl Display for AddOperand { AddOperand::Int(i) => { write!(f, "{}", i) } + AddOperand::Double(d) => { + write!(f, "{}", d) + } AddOperand::String(s) => { write!(f, "{}", s) } @@ -155,6 +167,7 @@ impl Display for AddOperand { pub enum SubtractOperand { Location(Location), Int(i32), + Double(f64), } impl Display for SubtractOperand { @@ -166,6 +179,9 @@ impl Display for SubtractOperand { SubtractOperand::Int(i) => { write!(f, "{}", i) } + SubtractOperand::Double(d) => { + write!(f, "{}", d) + } } } } @@ -173,6 +189,7 @@ impl Display for SubtractOperand { pub enum ReturnOperand { Location(Location), Int(i32), + Double(f64), String(Rc), } @@ -185,6 +202,9 @@ impl Display for ReturnOperand { ReturnOperand::Int(i) => { write!(f, "{}", i) } + ReturnOperand::Double(d) => { + write!(f, "{}", d) + } ReturnOperand::String(s) => { write!(f, "{}", s) } diff --git a/dvm-lib/src/vm/instruction_helpers.rs b/dvm-lib/src/vm/instruction_helpers.rs index ce1acab..515390d 100644 --- a/dvm-lib/src/vm/instruction_helpers.rs +++ b/dvm-lib/src/vm/instruction_helpers.rs @@ -1,7 +1,30 @@ -use crate::instruction::SubtractOperand; +use crate::instruction::{AddOperand, SubtractOperand}; use crate::vm::CallFrame; +use crate::vm::constant::Constant; use crate::vm::value::Value; -use crate::vm::value_util::load_value; +use crate::vm::value_util::{load_constant_value, load_value}; +use std::collections::HashMap; +use std::rc::Rc; + +pub fn add_operand_to_value( + add_operand: &AddOperand, + registers: &[Value], + current_frame: &CallFrame, + constants: &HashMap, Constant>, +) -> Value { + match add_operand { + AddOperand::Location(location) => load_value( + registers, + current_frame.stack(), + current_frame.fp(), + location, + ) + .clone(), + AddOperand::Int(i) => Value::Int(*i), + AddOperand::Double(d) => Value::Double(*d), + AddOperand::String(constant_name) => load_constant_value(constants, constant_name), + } +} pub fn subtract_operand_to_value( subtract_operand: &SubtractOperand, @@ -17,5 +40,6 @@ pub fn subtract_operand_to_value( ) .clone(), SubtractOperand::Int(i) => Value::Int(*i), + SubtractOperand::Double(d) => Value::Double(*d), } } diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index a353bca..72768d3 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -1,8 +1,8 @@ -use crate::instruction::{AddOperand, Instruction, MoveOperand, PushOperand, ReturnOperand}; +use crate::instruction::{Instruction, MoveOperand, PushOperand, ReturnOperand}; use crate::platform_function::PlatformFunction; use crate::vm::constant::Constant; use crate::vm::function::Function; -use crate::vm::instruction_helpers::subtract_operand_to_value; +use crate::vm::instruction_helpers::{add_operand_to_value, subtract_operand_to_value}; use crate::vm::value::Value; use crate::vm::value_util::*; use std::collections::HashMap; @@ -226,6 +226,7 @@ pub fn call<'a>( ) .clone(), MoveOperand::Int(i) => Value::Int(*i), + MoveOperand::Double(d) => Value::Double(*d), MoveOperand::String(constant_name) => { load_constant_value(context.constants(), constant_name) } @@ -244,6 +245,7 @@ pub fn call<'a>( location, ) .clone(), + PushOperand::Double(d) => Value::Double(*d), PushOperand::Int(i) => Value::Int(*i), PushOperand::String(constant_name) => { load_constant_value(context.constants(), constant_name) @@ -316,86 +318,74 @@ pub fn call<'a>( /* Add instructions */ Instruction::Add(left_operand, right_operand, destination) => { - let left_value = match left_operand { - AddOperand::Location(location) => load_value( - registers, - call_stack.top().stack(), - call_stack.top().fp(), - location, - ) - .clone(), - AddOperand::Int(i) => Value::Int(*i), - AddOperand::String(constant_name) => { - load_constant_value(context.constants(), constant_name) - } + let left_value = add_operand_to_value( + &left_operand, + registers, + call_stack.top(), + context.constants(), + ); + + let right_value = add_operand_to_value( + &right_operand, + registers, + call_stack.top(), + context.constants(), + ); + + let result = match left_value { + Value::Int(li) => match right_value { + Value::Int(ri) => Value::Int(li + ri), + Value::Double(rd) => Value::Double(li as f64 + rd), + Value::String(rs) => Value::String(format!("{}{}", li, rs).into()), + _ => panic!( + "Attempt to add non-addable {:?} type to {:?}", + right_value, left_value + ), + }, + Value::Double(ld) => match right_value { + Value::Int(ri) => Value::Double(ld + ri as f64), + Value::Double(rd) => Value::Double(ld + rd), + Value::String(rs) => Value::String(format!("{}{}", ld, rs).into()), + _ => panic!( + "Attempt to add non-addable {:?} type to {:?}", + right_value, left_value + ), + }, + Value::String(ref ls) => match right_value { + Value::Int(ri) => Value::String(format!("{}{}", ls, ri).into()), + Value::Double(rd) => Value::String(format!("{}{}", ls, rd).into()), + Value::String(rs) => Value::String(format!("{}{}", ls, rs).into()), + _ => panic!( + "Attempt to add non-addable {:?} type to {:?}", + right_value, left_value + ), + }, + _ => panic!("Attempt to add with non-addable type: {:?}", left_value), }; - let right_value = match right_operand { - AddOperand::Location(location) => load_value( - registers, - call_stack.top().stack(), - call_stack.top().fp(), - location, - ) - .clone(), - AddOperand::Int(i) => Value::Int(*i), - AddOperand::String(constant_name) => { - load_constant_value(context.constants(), constant_name) - } - }; - - if let Value::Int(left) = left_value { - if let Value::Int(right) = right_value { - let result = left + right; - put_value( - registers, - call_stack.top_mut(), - destination, - Value::Int(result), - ); - } else if let Value::String(s) = right_value { - let result = format!("{}{}", left, s); - put_value( - registers, - call_stack.top_mut(), - destination, - Value::String(result.into()), - ); - } else { - panic!("Attempt to add on non-addable type: {:?}", right_value); - } - } else if let Value::String(left) = left_value { - if let Value::Int(right) = right_value { - let result = format!("{}{}", left, right); - put_value( - registers, - call_stack.top_mut(), - destination, - Value::String(result.into()), - ); - } else if let Value::String(right) = right_value { - let result = format!("{}{}", left, right); - put_value( - registers, - call_stack.top_mut(), - destination, - Value::String(result.into()), - ); - } else { - panic!("Attempt to add on non-addable type: {:?}", right_value); - } - } else { - panic!("Attempt to add on non-addable type: {:?}", left_value); - } + put_value(registers, call_stack.top_mut(), destination, result); 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); + let left = subtract_operand_to_value(left_operand, registers, call_stack.top()); + let right = subtract_operand_to_value(right_operand, registers, call_stack.top()); + + let result = match left { + Value::Int(li) => match right { + Value::Int(ri) => Value::Int(li - ri), + Value::Double(rd) => Value::Double(li as f64 - rd), + _ => panic!("Attempt to subtract {:?} from {:?}", right, left), + }, + Value::Double(ld) => match right { + Value::Int(ri) => Value::Double(ld - ri as f64), + Value::Double(rd) => Value::Double(ld - rd), + _ => panic!("Attempt to subtract {:?} from {:?}", right, left), + }, + _ => panic!("Attempt to subtract {:?} from {:?}", right, left), + }; + put_value(registers, call_stack.top_mut(), destination, result); call_stack.top_mut().increment_ip(); } @@ -420,6 +410,7 @@ pub fn call<'a>( ) .clone(), ReturnOperand::Int(i) => Value::Int(*i), + ReturnOperand::Double(d) => Value::Double(*d), ReturnOperand::String(constant_name) => { load_constant_value(context.constants(), constant_name) } diff --git a/dvm-lib/src/vm/value.rs b/dvm-lib/src/vm/value.rs index 60f0281..be92a69 100644 --- a/dvm-lib/src/vm/value.rs +++ b/dvm-lib/src/vm/value.rs @@ -1,9 +1,10 @@ use std::fmt::{Display, Formatter}; use std::rc::Rc; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq)] pub enum Value { Int(i32), + Double(f64), String(Rc), Null, } @@ -22,6 +23,13 @@ impl Value { } } + pub fn unwrap_double(&self) -> f64 { + match self { + Value::Double(d) => *d, + _ => panic!("Attempt to unwrap Double; found {:?}", self), + } + } + pub fn unwrap_string(&self) -> &Rc { match self { Value::String(s) => s, @@ -36,6 +44,9 @@ impl Display for Value { Value::Int(i) => { write!(f, "{}", i) } + Value::Double(d) => { + write!(f, "{}", d) + } Value::String(s) => { write!(f, "{}", s) } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 1c29b8b..3f8e45b 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -24,7 +24,7 @@ mod e2e_tests { panic!("There were diagnostics."); } - fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { + fn prepare_context(input: &str) -> DvmContext { let parse_result = parse_compilation_unit(input); let mut compilation_unit = match parse_result { Ok(compilation_unit) => compilation_unit, @@ -82,17 +82,29 @@ mod e2e_tests { ); } + dvm_context + } + + fn get_result( + dvm_context: &DvmContext, + function_name: &str, + arguments: &[Value], + ) -> Option { let mut registers: Vec = vec![Value::Null; REGISTER_COUNT]; let mut call_stack = CallStack::new(); - let result = call( + call( &dvm_context, &mut registers, &mut call_stack, function_name, &arguments, - ); - match result { + ) + } + + fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { + let context = prepare_context(input); + match get_result(&context, function_name, arguments) { None => panic!("Call returned no value"), Some(result_value) => { assert_eq!(result_value, expected_value); @@ -136,6 +148,58 @@ mod e2e_tests { fn simple_subtract() { assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1)) } + + #[test] + fn add_two_doubles() { + assert_result( + "fn add(a: Double, b: Double) -> Double a + b end", + "add", + &vec![Value::Double(1.23), Value::Double(1.23)], + Value::Double(1.23 + 1.23), + ); + } + + #[test] + fn add_two_double_variables() { + assert_result( + " + fn add() -> Double + let a = 1.0 + let b = 2.0 + a + b + end + ", + "add", + &vec![Value::Double(1.0), Value::Double(2.0)], + Value::Double(3.0), + ); + } + + #[test] + fn subtract_two_doubles() { + assert_result( + "fn subtract(a: Double, b: Double) -> Double a - b end", + "subtract", + &vec![Value::Double(3.0), Value::Double(2.0)], + Value::Double(1.0), + ); + } + + #[test] + fn subtract_two_double_variables() { + assert_result( + " + fn subtract() -> Double + let a = 3.0 + let b = 2.0 + a - b + end + ", + "subtract", + &vec![], + Value::Double(1.0), + ) + } } #[cfg(test)] diff --git a/examples/doubles.dm b/examples/doubles.dm new file mode 100644 index 0000000..6c8a0fc --- /dev/null +++ b/examples/doubles.dm @@ -0,0 +1,9 @@ +extern fn println(message: Any) -> Void + +fn main() + println(1.23 + 2.34) + println(1 + 2.0) + println(2.0 + 1) + println("hello " + 2.34) + println(2.34 + " hello") +end \ No newline at end of file