Add doubles.

This commit is contained in:
Jesse Brault 2026-03-10 17:32:02 -05:00
parent 7de866cf9d
commit 9df9edc508
17 changed files with 399 additions and 110 deletions

View File

@ -44,6 +44,9 @@ pub fn std_core_println(args: &[Value]) -> Result<Value, Box<dyn Error>> {
Value::Int(i) => {
println!("{}", i);
}
Value::Double(d) => {
println!("{}", d);
}
Value::String(s) => {
println!("{}", s);
}

View File

@ -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(),
)])

View File

@ -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
}
}

View File

@ -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()))
}

View File

@ -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()))
}

View File

@ -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;

View File

@ -17,6 +17,7 @@ pub enum IrExpression {
Parameter(Rc<IrParameter>),
Variable(Rc<RefCell<IrVariable>>),
Int(i32),
Double(f64),
String(Rc<str>),
}
@ -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() {
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
}

View File

@ -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;
}
}
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());
}
}

View File

@ -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(&current);
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(&current);
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()?);
}

View File

@ -33,6 +33,7 @@ pub enum TokenKind {
Let,
Equals,
IntegerLiteral,
DoubleLiteral,
LongLiteral,
String,
Extern,

View File

@ -7,6 +7,7 @@ use std::rc::Rc;
pub enum TypeInfo {
Any,
Integer,
Double,
String,
Function(Rc<RefCell<FunctionSymbol>>),
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!(),

View File

@ -89,6 +89,7 @@ impl Display for Location {
pub enum MoveOperand {
Location(Location),
Int(i32),
Double(f64),
String(Rc<str>),
}
@ -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<str>),
}
@ -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<str>),
}
@ -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<str>),
}
@ -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)
}

View File

@ -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<Rc<str>, 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),
}
}

View File

@ -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(
let left_value = add_operand_to_value(
&left_operand,
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)
}
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)
}

View File

@ -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<str>),
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<str> {
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)
}

View File

@ -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<Value> {
let mut registers: Vec<Value> = 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)]

9
examples/doubles.dm Normal file
View File

@ -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