Add doubles.
This commit is contained in:
parent
7de866cf9d
commit
9df9edc508
@ -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);
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
)])
|
||||
|
||||
31
dmc-lib/src/ast/double_literal.rs
Normal file
31
dmc-lib/src/ast/double_literal.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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()))
|
||||
}
|
||||
|
||||
@ -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()))
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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() {
|
||||
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
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()?);
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ pub enum TokenKind {
|
||||
Let,
|
||||
Equals,
|
||||
IntegerLiteral,
|
||||
DoubleLiteral,
|
||||
LongLiteral,
|
||||
String,
|
||||
Extern,
|
||||
|
||||
@ -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!(),
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
9
examples/doubles.dm
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user