A lot of work just to do subtraction.
This commit is contained in:
parent
e35bacb583
commit
ebca87ddb9
@ -155,7 +155,9 @@ impl Expression {
|
||||
.add_statement(IrStatement::Assign(assign));
|
||||
Some(IrExpression::Variable(as_rc))
|
||||
}
|
||||
Expression::Subtract(subtract_expression) => todo!(),
|
||||
Expression::Subtract(subtract_expression) => {
|
||||
Some(subtract_expression.to_ir_expression(builder, symbol_table))
|
||||
}
|
||||
Expression::Negative(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +114,9 @@ impl LetStatement {
|
||||
Expression::Add(additive_expression) => {
|
||||
IrOperation::Add(additive_expression.to_ir(builder, symbol_table))
|
||||
}
|
||||
Expression::Subtract(subtract_expression) => todo!(),
|
||||
Expression::Subtract(subtract_expression) => {
|
||||
IrOperation::Subtract(subtract_expression.to_ir_subtract(builder, symbol_table))
|
||||
}
|
||||
Expression::Negative(_) => todo!(),
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
use crate::ast::expression::Expression;
|
||||
use crate::ast::ir_builder::IrBuilder;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::ir::ir_assign::IrAssign;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_operation::IrOperation;
|
||||
use crate::ir::ir_statement::IrStatement;
|
||||
use crate::ir::ir_subtract::IrSubtract;
|
||||
use crate::ir::ir_variable::IrVariable;
|
||||
use crate::source_range::SourceRange;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::type_info::TypeInfo;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct SubtractExpression {
|
||||
lhs: Box<Expression>,
|
||||
@ -88,4 +97,39 @@ impl SubtractExpression {
|
||||
pub fn source_range(&self) -> &SourceRange {
|
||||
&self.source_range
|
||||
}
|
||||
|
||||
pub fn to_ir_subtract(
|
||||
&self,
|
||||
builder: &mut IrBuilder,
|
||||
symbol_table: &SymbolTable,
|
||||
) -> IrSubtract {
|
||||
let lhs = self
|
||||
.lhs
|
||||
.to_ir(builder, symbol_table)
|
||||
.expect("Attempt to subtract non-expression");
|
||||
let rhs = self
|
||||
.rhs
|
||||
.to_ir(builder, symbol_table)
|
||||
.expect("Attempt to subtract non-expression");
|
||||
IrSubtract::new(lhs, rhs)
|
||||
}
|
||||
|
||||
pub fn to_ir_expression(
|
||||
&self,
|
||||
builder: &mut IrBuilder,
|
||||
symbol_table: &SymbolTable,
|
||||
) -> IrExpression {
|
||||
let ir_subtract = self.to_ir_subtract(builder, symbol_table);
|
||||
let t_var = IrVariable::new_vr(
|
||||
builder.new_t_var().into(),
|
||||
builder.current_block().id(),
|
||||
self.type_info(),
|
||||
);
|
||||
let as_rc = Rc::new(RefCell::new(t_var));
|
||||
let assign = IrAssign::new(as_rc.clone(), IrOperation::Subtract(ir_subtract));
|
||||
builder
|
||||
.current_block_mut()
|
||||
.add_statement(IrStatement::Assign(assign));
|
||||
IrExpression::Variable(as_rc)
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +97,10 @@ impl Assemble for IrAssign {
|
||||
let (left, right) = ir_add.operand_pair(constants_table);
|
||||
builder.push(Instruction::Add(left, right, destination));
|
||||
}
|
||||
IrOperation::Subtract(ir_subtract) => {
|
||||
let (left, right) = ir_subtract.operand_pair();
|
||||
builder.push(Instruction::Subtract(left, right, destination));
|
||||
}
|
||||
IrOperation::Call(ir_call) => {
|
||||
ir_call.assemble(builder, constants_table);
|
||||
builder.push(Instruction::Pop(Some(destination)));
|
||||
|
||||
@ -5,7 +5,9 @@ use crate::ir::ir_variable::{
|
||||
};
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use crate::type_info::TypeInfo;
|
||||
use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand};
|
||||
use dvm_lib::instruction::{
|
||||
AddOperand, Location, MoveOperand, PushOperand, ReturnOperand, SubtractOperand,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
@ -105,6 +107,40 @@ 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(),
|
||||
)),
|
||||
_ => 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() {
|
||||
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
||||
SubtractOperand::Location(Location::Register(
|
||||
vr_variable.assigned_register(),
|
||||
))
|
||||
}
|
||||
IrVariableDescriptor::Stack(stack_variable) => SubtractOperand::Location(
|
||||
Location::StackFrameOffset(stack_variable.offset()),
|
||||
),
|
||||
},
|
||||
_ => panic!(
|
||||
"Attempt to subtract with non-integer type (found {})",
|
||||
ir_variable.borrow().type_info()
|
||||
),
|
||||
},
|
||||
IrExpression::Int(i) => SubtractOperand::Int(*i),
|
||||
IrExpression::String(_) => {
|
||||
panic!("Attempt to subtract with a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
|
||||
match self {
|
||||
IrExpression::Parameter(ir_parameter) => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::ir::ir_add::IrAdd;
|
||||
use crate::ir::ir_call::IrCall;
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_subtract::IrSubtract;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@ -9,6 +10,7 @@ use std::fmt::{Display, Formatter};
|
||||
pub enum IrOperation {
|
||||
Load(IrExpression),
|
||||
Add(IrAdd),
|
||||
Subtract(IrSubtract),
|
||||
Call(IrCall),
|
||||
}
|
||||
|
||||
@ -21,6 +23,9 @@ impl Display for IrOperation {
|
||||
IrOperation::Add(ir_add) => {
|
||||
write!(f, "{}", ir_add)
|
||||
}
|
||||
IrOperation::Subtract(ir_subtract) => {
|
||||
write!(f, "{}", ir_subtract)
|
||||
}
|
||||
IrOperation::Call(ir_call) => {
|
||||
write!(f, "{}", ir_call)
|
||||
}
|
||||
@ -33,6 +38,7 @@ impl VrUser for IrOperation {
|
||||
match self {
|
||||
IrOperation::Load(ir_expression) => ir_expression.vr_definitions(),
|
||||
IrOperation::Add(ir_add) => ir_add.vr_definitions(),
|
||||
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_definitions(),
|
||||
IrOperation::Call(ir_call) => ir_call.vr_definitions(),
|
||||
}
|
||||
}
|
||||
@ -41,6 +47,7 @@ impl VrUser for IrOperation {
|
||||
match self {
|
||||
IrOperation::Load(ir_expression) => ir_expression.vr_uses(),
|
||||
IrOperation::Add(ir_add) => ir_add.vr_uses(),
|
||||
IrOperation::Subtract(ir_subtract) => ir_subtract.vr_uses(),
|
||||
IrOperation::Call(ir_call) => ir_call.vr_uses(),
|
||||
}
|
||||
}
|
||||
@ -53,6 +60,9 @@ impl VrUser for IrOperation {
|
||||
IrOperation::Add(ir_add) => {
|
||||
ir_add.propagate_spills(spills);
|
||||
}
|
||||
IrOperation::Subtract(ir_subtract) => {
|
||||
ir_subtract.propagate_spills(spills);
|
||||
}
|
||||
IrOperation::Call(ir_call) => {
|
||||
ir_call.propagate_spills(spills);
|
||||
}
|
||||
@ -70,6 +80,9 @@ impl VrUser for IrOperation {
|
||||
IrOperation::Add(ir_add) => {
|
||||
ir_add.propagate_register_assignments(assignments);
|
||||
}
|
||||
IrOperation::Subtract(ir_subtract) => {
|
||||
ir_subtract.propagate_register_assignments(assignments);
|
||||
}
|
||||
IrOperation::Call(ir_call) => {
|
||||
ir_call.propagate_register_assignments(assignments);
|
||||
}
|
||||
|
||||
63
dmc-lib/src/ir/ir_subtract.rs
Normal file
63
dmc-lib/src/ir/ir_subtract.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::ir::ir_expression::IrExpression;
|
||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
||||
use crate::ir::register_allocation::{OffsetCounter, VrUser};
|
||||
use dvm_lib::instruction::SubtractOperand;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub struct IrSubtract {
|
||||
left: Box<IrExpression>,
|
||||
right: Box<IrExpression>,
|
||||
}
|
||||
|
||||
impl IrSubtract {
|
||||
pub fn new(left: IrExpression, right: IrExpression) -> Self {
|
||||
Self {
|
||||
left: left.into(),
|
||||
right: right.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operand_pair(&self) -> (SubtractOperand, SubtractOperand) {
|
||||
(self.left.subtract_operand(), self.right.subtract_operand())
|
||||
}
|
||||
}
|
||||
|
||||
impl VrUser for IrSubtract {
|
||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
[&self.left, &self.right]
|
||||
.iter()
|
||||
.flat_map(|expression| expression.vr_definitions())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
[&self.left, &self.right]
|
||||
.iter()
|
||||
.flat_map(|expression| expression.vr_uses())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||
self.left.propagate_spills(spills);
|
||||
self.right.propagate_spills(spills);
|
||||
}
|
||||
|
||||
fn propagate_register_assignments(
|
||||
&mut self,
|
||||
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||
) {
|
||||
self.left.propagate_register_assignments(assignments);
|
||||
self.right.propagate_register_assignments(assignments);
|
||||
}
|
||||
|
||||
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IrSubtract {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} - {}", self.left, self.right)
|
||||
}
|
||||
}
|
||||
@ -9,5 +9,6 @@ pub mod ir_operation;
|
||||
pub mod ir_parameter;
|
||||
pub mod ir_return;
|
||||
pub mod ir_statement;
|
||||
pub mod ir_subtract;
|
||||
pub mod ir_variable;
|
||||
mod register_allocation;
|
||||
|
||||
@ -16,6 +16,7 @@ pub enum Instruction {
|
||||
InvokePlatformStatic(FunctionName, ArgCount),
|
||||
|
||||
Add(AddOperand, AddOperand, Location),
|
||||
Subtract(SubtractOperand, SubtractOperand, Location),
|
||||
|
||||
Pop(Option<Location>),
|
||||
|
||||
@ -50,6 +51,9 @@ impl Display for Instruction {
|
||||
Instruction::Add(left, right, destination) => {
|
||||
write!(f, "add {}, {}, {}", left, right, destination)
|
||||
}
|
||||
Instruction::Subtract(left, right, destination) => {
|
||||
write!(f, "sub {}, {}, {}", left, right, destination)
|
||||
}
|
||||
Instruction::SetReturnValue(source) => {
|
||||
write!(f, "srv {}", source)
|
||||
}
|
||||
@ -148,6 +152,24 @@ impl Display for AddOperand {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SubtractOperand {
|
||||
Location(Location),
|
||||
Int(i32),
|
||||
}
|
||||
|
||||
impl Display for SubtractOperand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SubtractOperand::Location(location) => {
|
||||
write!(f, "{}", location)
|
||||
}
|
||||
SubtractOperand::Int(i) => {
|
||||
write!(f, "{}", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ReturnOperand {
|
||||
Location(Location),
|
||||
Int(i32),
|
||||
|
||||
21
dvm-lib/src/vm/instruction_helpers.rs
Normal file
21
dvm-lib/src/vm/instruction_helpers.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::instruction::SubtractOperand;
|
||||
use crate::vm::CallFrame;
|
||||
use crate::vm::value::Value;
|
||||
use crate::vm::value_util::load_value;
|
||||
|
||||
pub fn subtract_operand_to_value(
|
||||
subtract_operand: &SubtractOperand,
|
||||
registers: &[Value],
|
||||
current_frame: &CallFrame,
|
||||
) -> Value {
|
||||
match subtract_operand {
|
||||
SubtractOperand::Location(location) => load_value(
|
||||
registers,
|
||||
current_frame.stack(),
|
||||
current_frame.fp(),
|
||||
location,
|
||||
)
|
||||
.clone(),
|
||||
SubtractOperand::Int(i) => Value::Int(*i),
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,19 @@
|
||||
use crate::instruction::{
|
||||
AddOperand, ConstantName, Instruction, Location, MoveOperand, PushOperand, ReturnOperand,
|
||||
};
|
||||
use crate::instruction::{AddOperand, 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::value::Value;
|
||||
use crate::vm::value_util::*;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod constant;
|
||||
pub mod function;
|
||||
mod instruction_helpers;
|
||||
pub mod object;
|
||||
pub mod value;
|
||||
mod value_util;
|
||||
|
||||
pub struct DvmContext {
|
||||
functions: HashMap<Rc<str>, Function>,
|
||||
@ -165,55 +167,6 @@ impl<'a> CallStack<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_value<'a>(
|
||||
registers: &'a [Value],
|
||||
stack: &'a [Value],
|
||||
fp: usize,
|
||||
location: &Location,
|
||||
) -> &'a Value {
|
||||
match location {
|
||||
Location::Register(register) => ®isters[*register],
|
||||
Location::StackFrameOffset(offset) => {
|
||||
let value_index = fp.checked_add_signed(*offset).expect(&format!(
|
||||
"Overflow when adding offset {} to fp {}",
|
||||
*offset, fp
|
||||
));
|
||||
&stack[value_index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_constant_value(
|
||||
constants: &HashMap<Rc<str>, Constant>,
|
||||
constant_name: &ConstantName,
|
||||
) -> Value {
|
||||
let constant = &constants[constant_name];
|
||||
match constant {
|
||||
Constant::String(string_constant) => Value::String(string_constant.content_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
fn put_value(
|
||||
registers: &mut Vec<Value>,
|
||||
stack: &mut Vec<Value>,
|
||||
fp: usize,
|
||||
destination: &Location,
|
||||
value: Value,
|
||||
) {
|
||||
match destination {
|
||||
Location::Register(register) => {
|
||||
registers[*register] = value;
|
||||
}
|
||||
Location::StackFrameOffset(offset) => {
|
||||
let target_index = fp.checked_add_signed(*offset).expect(&format!(
|
||||
"Failed to calculate target index from fp + offset ({} + {})",
|
||||
fp, offset
|
||||
));
|
||||
stack[target_index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call<'a>(
|
||||
context: &'a DvmContext,
|
||||
registers: &mut Vec<Value>,
|
||||
@ -277,14 +230,7 @@ pub fn call<'a>(
|
||||
load_constant_value(context.constants(), constant_name)
|
||||
}
|
||||
};
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
destination,
|
||||
value,
|
||||
);
|
||||
put_value(registers, call_stack.top_mut(), destination, value);
|
||||
call_stack.top_mut().increment_ip();
|
||||
}
|
||||
|
||||
@ -401,21 +347,17 @@ pub fn call<'a>(
|
||||
if let Value::Int(left) = left_value {
|
||||
if let Value::Int(right) = right_value {
|
||||
let result = left + right;
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
call_stack.top_mut(),
|
||||
destination,
|
||||
Value::Int(result),
|
||||
);
|
||||
} else if let Value::String(s) = right_value {
|
||||
let result = format!("{}{}", left, s);
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
call_stack.top_mut(),
|
||||
destination,
|
||||
Value::String(result.into()),
|
||||
);
|
||||
@ -425,21 +367,17 @@ pub fn call<'a>(
|
||||
} else if let Value::String(left) = left_value {
|
||||
if let Value::Int(right) = right_value {
|
||||
let result = format!("{}{}", left, right);
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
call_stack.top_mut(),
|
||||
destination,
|
||||
Value::String(result.into()),
|
||||
);
|
||||
} else if let Value::String(right) = right_value {
|
||||
let result = format!("{}{}", left, right);
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
call_stack.top_mut(),
|
||||
destination,
|
||||
Value::String(result.into()),
|
||||
);
|
||||
@ -452,19 +390,21 @@ pub fn call<'a>(
|
||||
|
||||
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);
|
||||
put_value(registers, call_stack.top_mut(), destination, result);
|
||||
call_stack.top_mut().increment_ip();
|
||||
}
|
||||
|
||||
/* Pop instructions */
|
||||
Instruction::Pop(maybe_location) => {
|
||||
let value = call_stack.top_mut().stack_mut().pop().unwrap();
|
||||
if let Some(location) = maybe_location {
|
||||
let fp = call_stack.top().fp();
|
||||
put_value(
|
||||
registers,
|
||||
call_stack.top_mut().stack_mut(),
|
||||
fp,
|
||||
location,
|
||||
value,
|
||||
);
|
||||
put_value(registers, call_stack.top_mut(), location, value);
|
||||
}
|
||||
call_stack.top_mut().increment_ip();
|
||||
}
|
||||
|
||||
55
dvm-lib/src/vm/value_util.rs
Normal file
55
dvm-lib/src/vm/value_util.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::instruction::{ConstantName, Location};
|
||||
use crate::vm::CallFrame;
|
||||
use crate::vm::constant::Constant;
|
||||
use crate::vm::value::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn load_value<'a>(
|
||||
registers: &'a [Value],
|
||||
stack: &'a [Value],
|
||||
fp: usize,
|
||||
location: &Location,
|
||||
) -> &'a Value {
|
||||
match location {
|
||||
Location::Register(register) => ®isters[*register],
|
||||
Location::StackFrameOffset(offset) => {
|
||||
let value_index = fp.checked_add_signed(*offset).expect(&format!(
|
||||
"Overflow when adding offset {} to fp {}",
|
||||
*offset, fp
|
||||
));
|
||||
&stack[value_index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_constant_value(
|
||||
constants: &HashMap<Rc<str>, Constant>,
|
||||
constant_name: &ConstantName,
|
||||
) -> Value {
|
||||
let constant = &constants[constant_name];
|
||||
match constant {
|
||||
Constant::String(string_constant) => Value::String(string_constant.content_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_value(
|
||||
registers: &mut Vec<Value>,
|
||||
current_frame: &mut CallFrame,
|
||||
destination: &Location,
|
||||
value: Value,
|
||||
) {
|
||||
match destination {
|
||||
Location::Register(register) => {
|
||||
registers[*register] = value;
|
||||
}
|
||||
Location::StackFrameOffset(offset) => {
|
||||
let fp = current_frame.fp();
|
||||
let target_index = fp.checked_add_signed(*offset).expect(&format!(
|
||||
"Failed to calculate target index from fp + offset ({} + {})",
|
||||
fp, offset
|
||||
));
|
||||
current_frame.stack_mut()[target_index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,4 +131,9 @@ mod e2e_tests {
|
||||
Value::String(Rc::from("Hello. 1 plus 2 is 3")),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_subtract() {
|
||||
assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1))
|
||||
}
|
||||
}
|
||||
|
||||
16
examples/subtract.dm
Normal file
16
examples/subtract.dm
Normal file
@ -0,0 +1,16 @@
|
||||
extern fn println(message: Any) -> Void
|
||||
|
||||
fn add(a: Int, b: Int) -> Int
|
||||
a + b
|
||||
end
|
||||
|
||||
fn subtract(a: Int, b: Int) -> Int
|
||||
a - b
|
||||
end
|
||||
|
||||
fn main()
|
||||
let a = 42
|
||||
let b = 42
|
||||
println(add(a, b))
|
||||
println(subtract(a, b))
|
||||
end
|
||||
Loading…
Reference in New Issue
Block a user