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) => { Value::Int(i) => {
println!("{}", i); println!("{}", i);
} }
Value::Double(d) => {
println!("{}", d);
}
Value::String(s) => { Value::String(s) => {
println!("{}", s); println!("{}", s);
} }

View File

@ -73,7 +73,7 @@ impl AddExpression {
Ok(()) Ok(())
} else { } else {
Err(vec![Diagnostic::new( 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.start(),
self.source_range.end(), 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::add_expression::AddExpression;
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::double_literal::DoubleLiteral;
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;
@ -25,6 +26,7 @@ pub enum Expression {
Call(Call), Call(Call),
Identifier(Identifier), Identifier(Identifier),
Integer(IntegerLiteral), Integer(IntegerLiteral),
Double(DoubleLiteral),
String(StringLiteral), String(StringLiteral),
} }
@ -44,6 +46,7 @@ impl Expression {
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::Integer(_) => Ok(()), Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()), Expression::String(_) => Ok(()),
} }
} }
@ -60,6 +63,7 @@ impl Expression {
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::Integer(_) => Ok(()), Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()), Expression::String(_) => Ok(()),
} }
} }
@ -76,6 +80,7 @@ impl Expression {
Expression::Call(call) => call.type_check(symbol_table), Expression::Call(call) => call.type_check(symbol_table),
Expression::Identifier(identifier) => identifier.type_check(symbol_table), Expression::Identifier(identifier) => identifier.type_check(symbol_table),
Expression::Integer(_) => Ok(()), Expression::Integer(_) => Ok(()),
Expression::Double(_) => Ok(()),
Expression::String(_) => Ok(()), Expression::String(_) => Ok(()),
} }
} }
@ -88,6 +93,7 @@ impl Expression {
Expression::Call(call) => call.return_type_info(), Expression::Call(call) => call.return_type_info(),
Expression::Identifier(identifier) => identifier.type_info(), Expression::Identifier(identifier) => identifier.type_info(),
Expression::Integer(integer_literal) => integer_literal.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(), Expression::String(string_literal) => string_literal.type_info(),
} }
} }
@ -100,6 +106,7 @@ impl Expression {
Expression::Call(call) => call.source_range(), Expression::Call(call) => call.source_range(),
Expression::Identifier(identifier) => identifier.source_range(), Expression::Identifier(identifier) => identifier.source_range(),
Expression::Integer(integer_literal) => integer_literal.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(), Expression::String(string_literal) => string_literal.source_range(),
} }
} }
@ -134,6 +141,9 @@ impl Expression {
Expression::Integer(integer_literal) => { Expression::Integer(integer_literal) => {
Some(IrExpression::Int(integer_literal.value())) Some(IrExpression::Int(integer_literal.value()))
} }
Expression::Double(double_literal) => {
Some(IrExpression::Double(double_literal.value()))
}
Expression::String(string_literal) => { Expression::String(string_literal) => {
Some(IrExpression::String(string_literal.content().into())) Some(IrExpression::String(string_literal.content().into()))
} }

View File

@ -105,6 +105,9 @@ impl LetStatement {
Expression::Integer(integer_literal) => { Expression::Integer(integer_literal) => {
IrOperation::Load(IrExpression::Int(integer_literal.value())) IrOperation::Load(IrExpression::Int(integer_literal.value()))
} }
Expression::Double(double_literal) => {
IrOperation::Load(IrExpression::Double(double_literal.value()))
}
Expression::String(string_literal) => { Expression::String(string_literal) => {
IrOperation::Load(IrExpression::String(string_literal.content().into())) IrOperation::Load(IrExpression::String(string_literal.content().into()))
} }

View File

@ -1,6 +1,7 @@
pub mod add_expression; pub mod add_expression;
pub mod call; pub mod call;
pub mod compilation_unit; pub mod compilation_unit;
pub mod double_literal;
pub mod expression; pub mod expression;
pub mod expression_statement; pub mod expression_statement;
pub mod extern_function; pub mod extern_function;

View File

@ -17,6 +17,7 @@ pub enum IrExpression {
Parameter(Rc<IrParameter>), Parameter(Rc<IrParameter>),
Variable(Rc<RefCell<IrVariable>>), Variable(Rc<RefCell<IrVariable>>),
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
} }
@ -26,6 +27,7 @@ impl IrExpression {
IrExpression::Parameter(ir_parameter) => ir_parameter.type_info().clone(), IrExpression::Parameter(ir_parameter) => ir_parameter.type_info().clone(),
IrExpression::Variable(ir_variable) => ir_variable.borrow().type_info().clone(), IrExpression::Variable(ir_variable) => ir_variable.borrow().type_info().clone(),
IrExpression::Int(_) => TypeInfo::Integer, IrExpression::Int(_) => TypeInfo::Integer,
IrExpression::Double(_) => TypeInfo::Double,
IrExpression::String(_) => TypeInfo::String, IrExpression::String(_) => TypeInfo::String,
} }
} }
@ -44,6 +46,7 @@ impl IrExpression {
} }
}, },
IrExpression::Int(i) => MoveOperand::Int(*i), IrExpression::Int(i) => MoveOperand::Int(*i),
IrExpression::Double(d) => MoveOperand::Double(*d),
IrExpression::String(s) => { IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s); let constant_name = constants_table.get_or_insert(s);
MoveOperand::String(constant_name) MoveOperand::String(constant_name)
@ -65,6 +68,7 @@ impl IrExpression {
} }
}, },
IrExpression::Int(i) => PushOperand::Int(*i), IrExpression::Int(i) => PushOperand::Int(*i),
IrExpression::Double(d) => PushOperand::Double(*d),
IrExpression::String(s) => { IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s); let constant_name = constants_table.get_or_insert(s);
PushOperand::String(constant_name) PushOperand::String(constant_name)
@ -75,31 +79,34 @@ impl IrExpression {
pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand { pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand {
match self { match self {
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { 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())) AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
} }
_ => panic!( _ => panic!(
"Attempt to add non-integer/non-string (found: {})", "Attempt to add non- integer/double/string (found: {})",
ir_parameter.type_info() ir_parameter.type_info()
), ),
}, },
IrExpression::Variable(ir_variable) => match ir_variable.borrow().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 => {
IrVariableDescriptor::VirtualRegister(register_variable) => { match ir_variable.borrow().descriptor() {
AddOperand::Location(Location::Register( IrVariableDescriptor::VirtualRegister(register_variable) => {
register_variable.assigned_register(), 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!( _ => panic!(
"Attempt to add non-integer/non-string (found: {})", "Attempt to add non-integer/non-string (found: {})",
ir_variable.borrow().type_info() ir_variable.borrow().type_info()
), ),
}, },
IrExpression::Int(i) => AddOperand::Int(*i), IrExpression::Int(i) => AddOperand::Int(*i),
IrExpression::Double(d) => AddOperand::Double(*d),
IrExpression::String(s) => { IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s); let constant_name = constants_table.get_or_insert(s);
AddOperand::String(constant_name) AddOperand::String(constant_name)
@ -110,16 +117,16 @@ impl IrExpression {
pub fn subtract_operand(&self) -> SubtractOperand { pub fn subtract_operand(&self) -> SubtractOperand {
match self { match self {
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
TypeInfo::Integer => SubtractOperand::Location(Location::StackFrameOffset( TypeInfo::Integer | TypeInfo::Double => SubtractOperand::Location(
ir_parameter.stack_offset(), Location::StackFrameOffset(ir_parameter.stack_offset()),
)), ),
_ => panic!( _ => panic!(
"Attempt to subtract with non-integer type (found {})", "Attempt to subtract with non-integer type (found {})",
ir_parameter.type_info() ir_parameter.type_info()
), ),
}, },
IrExpression::Variable(ir_variable) => match ir_variable.borrow().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) => { IrVariableDescriptor::VirtualRegister(vr_variable) => {
SubtractOperand::Location(Location::Register( SubtractOperand::Location(Location::Register(
vr_variable.assigned_register(), vr_variable.assigned_register(),
@ -135,6 +142,7 @@ impl IrExpression {
), ),
}, },
IrExpression::Int(i) => SubtractOperand::Int(*i), IrExpression::Int(i) => SubtractOperand::Int(*i),
IrExpression::Double(d) => SubtractOperand::Double(*d),
IrExpression::String(_) => { IrExpression::String(_) => {
panic!("Attempt to subtract with a string"); panic!("Attempt to subtract with a string");
} }
@ -157,6 +165,7 @@ impl IrExpression {
} }
}, },
IrExpression::Int(i) => ReturnOperand::Int(*i), IrExpression::Int(i) => ReturnOperand::Int(*i),
IrExpression::Double(d) => ReturnOperand::Double(*d),
IrExpression::String(s) => { IrExpression::String(s) => {
let constant_name = constants_table.get_or_insert(s); let constant_name = constants_table.get_or_insert(s);
ReturnOperand::String(constant_name) ReturnOperand::String(constant_name)
@ -177,6 +186,9 @@ impl Display for IrExpression {
IrExpression::Int(i) => { IrExpression::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
IrExpression::Double(d) => {
write!(f, "{}", d)
}
IrExpression::String(s) => { IrExpression::String(s) => {
write!(f, "\"{}\"", s) write!(f, "\"{}\"", s)
} }
@ -202,6 +214,7 @@ impl VrUser for IrExpression {
} }
} }
IrExpression::Int(_) => HashSet::new(), IrExpression::Int(_) => HashSet::new(),
IrExpression::Double(_) => HashSet::new(),
IrExpression::String(_) => HashSet::new(), IrExpression::String(_) => HashSet::new(),
} }
} }
@ -230,6 +243,9 @@ impl VrUser for IrExpression {
IrExpression::Int(_) => { IrExpression::Int(_) => {
// no-op // no-op
} }
IrExpression::Double(_) => {
// no-op
}
IrExpression::String(_) => { IrExpression::String(_) => {
// no-op // no-op
} }
@ -257,6 +273,9 @@ impl VrUser for IrExpression {
IrExpression::Int(_) => { IrExpression::Int(_) => {
// no-op // no-op
} }
IrExpression::Double(_) => {
// no-op
}
IrExpression::String(_) => { IrExpression::String(_) => {
// no-op // no-op
} }

View File

@ -59,14 +59,37 @@ impl<'a> Lexer<'a> {
if chunk.starts_with(|c: char| c.is_ascii_digit()) { if chunk.starts_with(|c: char| c.is_ascii_digit()) {
// number literal // number literal
let mut end = self.position; let mut end = self.position;
for char in chunk.chars() { let mut whole_chars = chunk.chars();
if char.is_ascii_digit() { while let Some(c) = whole_chars.next() {
if c.is_ascii_digit() {
end += 1; end += 1;
} else { } else {
break; 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("\"") { } else if chunk.starts_with("\"") {
// string literal // string literal
let mut end = self.position; let mut end = self.position;
@ -180,4 +203,40 @@ mod tests {
let mut lexer = Lexer::new("extern"); let mut lexer = Lexer::new("extern");
assert_next(&mut lexer, TokenKind::Extern, 6); 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::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::double_literal::DoubleLiteral;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::expression_statement::ExpressionStatement; use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::extern_function::ExternFunction; use crate::ast::extern_function::ExternFunction;
@ -481,6 +482,15 @@ impl<'a> Parser<'a> {
source_range, 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 => { TokenKind::String => {
let with_quotes = self.token_text(&current); let with_quotes = self.token_text(&current);
let source_range = SourceRange::new(current.start(), current.end()); let source_range = SourceRange::new(current.start(), current.end());
@ -509,7 +519,10 @@ impl<'a> Parser<'a> {
if let Some(current) = &self.current { if let Some(current) = &self.current {
if matches!( if matches!(
current.kind(), current.kind(),
TokenKind::IntegerLiteral | TokenKind::String | TokenKind::Identifier TokenKind::IntegerLiteral
| TokenKind::DoubleLiteral
| TokenKind::String
| TokenKind::Identifier
) { ) {
arguments.append(&mut self.expression_list()?); arguments.append(&mut self.expression_list()?);
} }

View File

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

View File

@ -7,6 +7,7 @@ use std::rc::Rc;
pub enum TypeInfo { pub enum TypeInfo {
Any, Any,
Integer, Integer,
Double,
String, String,
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<RefCell<FunctionSymbol>>),
Void, Void,
@ -17,6 +18,7 @@ impl Display for TypeInfo {
match self { match self {
TypeInfo::Any => write!(f, "Any"), TypeInfo::Any => write!(f, "Any"),
TypeInfo::Integer => write!(f, "Int"), TypeInfo::Integer => write!(f, "Int"),
TypeInfo::Double => write!(f, "Double"),
TypeInfo::String => write!(f, "String"), TypeInfo::String => write!(f, "String"),
TypeInfo::Function(function_symbol) => { TypeInfo::Function(function_symbol) => {
write!(f, "fn(")?; write!(f, "fn(")?;
@ -36,6 +38,7 @@ impl TypeInfo {
match declared_name { match declared_name {
"Any" => TypeInfo::Any, "Any" => TypeInfo::Any,
"Int" => TypeInfo::Integer, "Int" => TypeInfo::Integer,
"Double" => TypeInfo::Double,
"String" => TypeInfo::String, "String" => TypeInfo::String,
"Void" => TypeInfo::Void, "Void" => TypeInfo::Void,
_ => panic!("Unknown type: {}", declared_name), _ => panic!("Unknown type: {}", declared_name),
@ -48,6 +51,9 @@ impl TypeInfo {
TypeInfo::Integer => { TypeInfo::Integer => {
matches!(other, TypeInfo::Integer) matches!(other, TypeInfo::Integer)
} }
TypeInfo::Double => {
matches!(other, TypeInfo::Double)
}
TypeInfo::String => { TypeInfo::String => {
matches!(other, TypeInfo::String) matches!(other, TypeInfo::String)
} }
@ -62,8 +68,15 @@ impl TypeInfo {
pub fn can_add(&self, rhs: &Self) -> bool { pub fn can_add(&self, rhs: &Self) -> bool {
match self { match self {
TypeInfo::Integer => matches!(rhs, TypeInfo::Integer), TypeInfo::Integer => {
TypeInfo::String => matches!(rhs, TypeInfo::String | 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, _ => false,
} }
} }
@ -72,24 +85,41 @@ impl TypeInfo {
match self { match self {
TypeInfo::Integer => match rhs { TypeInfo::Integer => match rhs {
TypeInfo::Integer => TypeInfo::Integer, TypeInfo::Integer => TypeInfo::Integer,
TypeInfo::Double => TypeInfo::Double,
TypeInfo::String => TypeInfo::String, TypeInfo::String => TypeInfo::String,
_ => panic!( _ => panic!("Unsupported add: {} + {}.", self, rhs),
"Adding things other than integers/strings to integer not yet supported." },
), TypeInfo::Double => match rhs {
TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double,
TypeInfo::String => TypeInfo::String,
_ => panic!("Unsupported add: {} + {}.", self, rhs),
}, },
TypeInfo::String => TypeInfo::String, 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 { 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 { pub fn subtract_result(&self, rhs: &Self) -> TypeInfo {
match self { match self {
TypeInfo::Integer => match rhs { TypeInfo::Integer => match rhs {
TypeInfo::Integer => TypeInfo::Integer, TypeInfo::Integer => TypeInfo::Integer,
TypeInfo::Double => TypeInfo::Double,
_ => panic!(),
},
TypeInfo::Double => match rhs {
TypeInfo::Integer | TypeInfo::Double => TypeInfo::Double,
_ => panic!(), _ => panic!(),
}, },
_ => panic!(), _ => panic!(),

View File

@ -89,6 +89,7 @@ impl Display for Location {
pub enum MoveOperand { pub enum MoveOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
} }
@ -101,6 +102,9 @@ impl Display for MoveOperand {
MoveOperand::Int(i) => { MoveOperand::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
MoveOperand::Double(d) => {
write!(f, "{}", d)
}
MoveOperand::String(s) => { MoveOperand::String(s) => {
write!(f, "{}", s) write!(f, "{}", s)
} }
@ -111,6 +115,7 @@ impl Display for MoveOperand {
pub enum PushOperand { pub enum PushOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
} }
@ -123,6 +128,9 @@ impl Display for PushOperand {
PushOperand::Int(i) => { PushOperand::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
PushOperand::Double(d) => {
write!(f, "{}", d)
}
PushOperand::String(s) => { PushOperand::String(s) => {
write!(f, "{}", s) write!(f, "{}", s)
} }
@ -133,6 +141,7 @@ impl Display for PushOperand {
pub enum AddOperand { pub enum AddOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
} }
@ -145,6 +154,9 @@ impl Display for AddOperand {
AddOperand::Int(i) => { AddOperand::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
AddOperand::Double(d) => {
write!(f, "{}", d)
}
AddOperand::String(s) => { AddOperand::String(s) => {
write!(f, "{}", s) write!(f, "{}", s)
} }
@ -155,6 +167,7 @@ impl Display for AddOperand {
pub enum SubtractOperand { pub enum SubtractOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),
Double(f64),
} }
impl Display for SubtractOperand { impl Display for SubtractOperand {
@ -166,6 +179,9 @@ impl Display for SubtractOperand {
SubtractOperand::Int(i) => { SubtractOperand::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
SubtractOperand::Double(d) => {
write!(f, "{}", d)
}
} }
} }
} }
@ -173,6 +189,7 @@ impl Display for SubtractOperand {
pub enum ReturnOperand { pub enum ReturnOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
} }
@ -185,6 +202,9 @@ impl Display for ReturnOperand {
ReturnOperand::Int(i) => { ReturnOperand::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
ReturnOperand::Double(d) => {
write!(f, "{}", d)
}
ReturnOperand::String(s) => { ReturnOperand::String(s) => {
write!(f, "{}", 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::CallFrame;
use crate::vm::constant::Constant;
use crate::vm::value::Value; 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( pub fn subtract_operand_to_value(
subtract_operand: &SubtractOperand, subtract_operand: &SubtractOperand,
@ -17,5 +40,6 @@ pub fn subtract_operand_to_value(
) )
.clone(), .clone(),
SubtractOperand::Int(i) => Value::Int(*i), 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::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::instruction_helpers::{add_operand_to_value, subtract_operand_to_value};
use crate::vm::value::Value; use crate::vm::value::Value;
use crate::vm::value_util::*; use crate::vm::value_util::*;
use std::collections::HashMap; use std::collections::HashMap;
@ -226,6 +226,7 @@ pub fn call<'a>(
) )
.clone(), .clone(),
MoveOperand::Int(i) => Value::Int(*i), MoveOperand::Int(i) => Value::Int(*i),
MoveOperand::Double(d) => Value::Double(*d),
MoveOperand::String(constant_name) => { MoveOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name) load_constant_value(context.constants(), constant_name)
} }
@ -244,6 +245,7 @@ pub fn call<'a>(
location, location,
) )
.clone(), .clone(),
PushOperand::Double(d) => Value::Double(*d),
PushOperand::Int(i) => Value::Int(*i), PushOperand::Int(i) => Value::Int(*i),
PushOperand::String(constant_name) => { PushOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name) load_constant_value(context.constants(), constant_name)
@ -316,86 +318,74 @@ pub fn call<'a>(
/* Add instructions */ /* Add instructions */
Instruction::Add(left_operand, right_operand, destination) => { Instruction::Add(left_operand, right_operand, destination) => {
let left_value = match left_operand { let left_value = add_operand_to_value(
AddOperand::Location(location) => load_value( &left_operand,
registers, registers,
call_stack.top().stack(), call_stack.top(),
call_stack.top().fp(), context.constants(),
location, );
)
.clone(), let right_value = add_operand_to_value(
AddOperand::Int(i) => Value::Int(*i), &right_operand,
AddOperand::String(constant_name) => { registers,
load_constant_value(context.constants(), constant_name) 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 { put_value(registers, call_stack.top_mut(), destination, result);
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);
}
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
Instruction::Subtract(left_operand, right_operand, destination) => { Instruction::Subtract(left_operand, right_operand, destination) => {
let left = subtract_operand_to_value(left_operand, registers, call_stack.top()) 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());
let right = subtract_operand_to_value(right_operand, registers, call_stack.top())
.unwrap_int(); let result = match left {
let result = Value::Int(left - right); 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); put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }
@ -420,6 +410,7 @@ pub fn call<'a>(
) )
.clone(), .clone(),
ReturnOperand::Int(i) => Value::Int(*i), ReturnOperand::Int(i) => Value::Int(*i),
ReturnOperand::Double(d) => Value::Double(*d),
ReturnOperand::String(constant_name) => { ReturnOperand::String(constant_name) => {
load_constant_value(context.constants(), constant_name) load_constant_value(context.constants(), constant_name)
} }

View File

@ -1,9 +1,10 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq)]
pub enum Value { pub enum Value {
Int(i32), Int(i32),
Double(f64),
String(Rc<str>), String(Rc<str>),
Null, 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> { pub fn unwrap_string(&self) -> &Rc<str> {
match self { match self {
Value::String(s) => s, Value::String(s) => s,
@ -36,6 +44,9 @@ impl Display for Value {
Value::Int(i) => { Value::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }
Value::Double(d) => {
write!(f, "{}", d)
}
Value::String(s) => { Value::String(s) => {
write!(f, "{}", s) write!(f, "{}", s)
} }

View File

@ -24,7 +24,7 @@ mod e2e_tests {
panic!("There were diagnostics."); 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 parse_result = parse_compilation_unit(input);
let mut compilation_unit = match parse_result { let mut compilation_unit = match parse_result {
Ok(compilation_unit) => compilation_unit, 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 registers: Vec<Value> = vec![Value::Null; REGISTER_COUNT];
let mut call_stack = CallStack::new(); let mut call_stack = CallStack::new();
let result = call( call(
&dvm_context, &dvm_context,
&mut registers, &mut registers,
&mut call_stack, &mut call_stack,
function_name, function_name,
&arguments, &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"), None => panic!("Call returned no value"),
Some(result_value) => { Some(result_value) => {
assert_eq!(result_value, expected_value); assert_eq!(result_value, expected_value);
@ -136,6 +148,58 @@ mod e2e_tests {
fn simple_subtract() { fn simple_subtract() {
assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1)) 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)] #[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