Begin adding function calls
This commit is contained in:
parent
5bbda1a24e
commit
ea0be43199
@ -227,7 +227,7 @@ impl Chunk {
|
|||||||
r#type,
|
r#type,
|
||||||
is_mutable,
|
is_mutable,
|
||||||
self.scope_depth,
|
self.scope_depth,
|
||||||
Some(register_index),
|
register_index,
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(starting_length as u8)
|
Ok(starting_length as u8)
|
||||||
@ -249,7 +249,7 @@ impl Chunk {
|
|||||||
|
|
||||||
log::debug!("Define local {}", local.identifier);
|
log::debug!("Define local {}", local.identifier);
|
||||||
|
|
||||||
local.register_index = Some(register_index);
|
local.register_index = register_index;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -309,7 +309,7 @@ pub struct Local {
|
|||||||
pub r#type: Option<Type>,
|
pub r#type: Option<Type>,
|
||||||
pub is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
pub register_index: Option<u8>,
|
pub register_index: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
@ -318,7 +318,7 @@ impl Local {
|
|||||||
r#type: Option<Type>,
|
r#type: Option<Type>,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
register_index: Option<u8>,
|
register_index: u8,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier,
|
identifier,
|
||||||
@ -499,17 +499,13 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
},
|
},
|
||||||
) in self.chunk.locals.iter().enumerate()
|
) in self.chunk.locals.iter().enumerate()
|
||||||
{
|
{
|
||||||
let register_display = register_index
|
|
||||||
.as_ref()
|
|
||||||
.map(|value| value.to_string())
|
|
||||||
.unwrap_or_else(|| "empty".to_string());
|
|
||||||
let identifier_display = identifier.as_str();
|
let identifier_display = identifier.as_str();
|
||||||
let type_display = r#type
|
let type_display = r#type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r#type| r#type.to_string())
|
.map(|r#type| r#type.to_string())
|
||||||
.unwrap_or("unknown".to_string());
|
.unwrap_or("unknown".to_string());
|
||||||
let local_display = format!(
|
let local_display = format!(
|
||||||
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_display:8}"
|
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_index:8}"
|
||||||
);
|
);
|
||||||
|
|
||||||
push(&local_display, false);
|
push(&local_display, false);
|
||||||
@ -541,7 +537,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
value_option.as_ref().and_then(|value| match value {
|
value_option.as_ref().and_then(|value| match value {
|
||||||
Value::Primitive(value_data) => value_data.as_function().map(|function| {
|
Value::Primitive(value_data) => value_data.as_function().map(|function| {
|
||||||
function
|
function
|
||||||
.body
|
.chunk
|
||||||
.disassembler("function")
|
.disassembler("function")
|
||||||
.styled(self.styled)
|
.styled(self.styled)
|
||||||
.indent(self.indent + 1)
|
.indent(self.indent + 1)
|
||||||
|
@ -208,6 +208,15 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call(function_index: u8, argument_count: u8) -> Instruction {
|
||||||
|
let mut instruction = Instruction(Operation::Call as u32);
|
||||||
|
|
||||||
|
instruction.set_a(function_index);
|
||||||
|
instruction.set_b(argument_count);
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
pub fn r#return(should_return_value: bool) -> Instruction {
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Return as u32);
|
let mut instruction = Instruction(Operation::Return as u32);
|
||||||
|
|
||||||
@ -572,6 +581,25 @@ impl Instruction {
|
|||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
Operation::Call => {
|
||||||
|
let function_index = self.a();
|
||||||
|
let argument_count = self.b();
|
||||||
|
let last_argument = function_index + argument_count;
|
||||||
|
|
||||||
|
let mut output = format!("R{function_index}(");
|
||||||
|
|
||||||
|
for register in function_index..last_argument {
|
||||||
|
if register != last_argument - 1 {
|
||||||
|
output.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_str(&format!("R{}", register));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push(')');
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
Operation::Return => None,
|
Operation::Return => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -779,6 +807,15 @@ mod tests {
|
|||||||
assert!(instruction.c_as_boolean());
|
assert!(instruction.c_as_boolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call() {
|
||||||
|
let instruction = Instruction::call(4, 1);
|
||||||
|
|
||||||
|
assert_eq!(instruction.operation(), Operation::Call);
|
||||||
|
assert_eq!(instruction.a(), 4);
|
||||||
|
assert_eq!(instruction.b(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#return() {
|
fn r#return() {
|
||||||
let instruction = Instruction::r#return(true);
|
let instruction = Instruction::r#return(true);
|
||||||
|
@ -28,7 +28,8 @@ const NEGATE: u8 = 0b0001_0010;
|
|||||||
const NOT: u8 = 0b0001_0011;
|
const NOT: u8 = 0b0001_0011;
|
||||||
|
|
||||||
const JUMP: u8 = 0b0001_0100;
|
const JUMP: u8 = 0b0001_0100;
|
||||||
const RETURN: u8 = 0b0001_0101;
|
const CALL: u8 = 0b0001_0101;
|
||||||
|
const RETURN: u8 = 0b0001_0110;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
@ -68,6 +69,7 @@ pub enum Operation {
|
|||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
Jump = JUMP as isize,
|
Jump = JUMP as isize,
|
||||||
|
Call = CALL as isize,
|
||||||
Return = RETURN as isize,
|
Return = RETURN as isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +121,7 @@ impl From<u8> for Operation {
|
|||||||
NEGATE => Operation::Negate,
|
NEGATE => Operation::Negate,
|
||||||
NOT => Operation::Not,
|
NOT => Operation::Not,
|
||||||
JUMP => Operation::Jump,
|
JUMP => Operation::Jump,
|
||||||
|
CALL => Operation::Call,
|
||||||
RETURN => Operation::Return,
|
RETURN => Operation::Return,
|
||||||
_ => {
|
_ => {
|
||||||
if cfg!(test) {
|
if cfg!(test) {
|
||||||
@ -155,6 +158,7 @@ impl From<Operation> for u8 {
|
|||||||
Operation::Negate => NEGATE,
|
Operation::Negate => NEGATE,
|
||||||
Operation::Not => NOT,
|
Operation::Not => NOT,
|
||||||
Operation::Jump => JUMP,
|
Operation::Jump => JUMP,
|
||||||
|
Operation::Call => CALL,
|
||||||
Operation::Return => RETURN,
|
Operation::Return => RETURN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +188,7 @@ impl Display for Operation {
|
|||||||
Operation::Negate => write!(f, "NEGATE"),
|
Operation::Negate => write!(f, "NEGATE"),
|
||||||
Operation::Not => write!(f, "NOT"),
|
Operation::Not => write!(f, "NOT"),
|
||||||
Operation::Jump => write!(f, "JUMP"),
|
Operation::Jump => write!(f, "JUMP"),
|
||||||
|
Operation::Call => write!(f, "CALL"),
|
||||||
Operation::Return => write!(f, "RETURN"),
|
Operation::Return => write!(f, "RETURN"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,14 +303,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> {
|
fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||||
self.allow(Token::LeftParenthesis)?;
|
self.allow(Token::LeftParenthesis)?;
|
||||||
self.parse_statement(
|
self.parse_expression()?;
|
||||||
Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: false,
|
|
||||||
implicit_return: false,
|
|
||||||
},
|
|
||||||
Context::None,
|
|
||||||
)?;
|
|
||||||
self.expect(Token::RightParenthesis)?;
|
self.expect(Token::RightParenthesis)?;
|
||||||
|
|
||||||
self.parsed_expression = true;
|
self.parsed_expression = true;
|
||||||
@ -323,14 +316,7 @@ impl<'src> Parser<'src> {
|
|||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_statement(
|
self.parse_expression()?;
|
||||||
Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: false,
|
|
||||||
implicit_return: false,
|
|
||||||
},
|
|
||||||
Context::None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (previous_instruction, previous_position) =
|
let (previous_instruction, previous_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
@ -394,11 +380,7 @@ impl<'src> Parser<'src> {
|
|||||||
let local = self.chunk.get_local(local_index, self.current_position)?;
|
let local = self.chunk.get_local(local_index, self.current_position)?;
|
||||||
is_mutable_local = local.is_mutable;
|
is_mutable_local = local.is_mutable;
|
||||||
|
|
||||||
if let Some(index) = local.register_index {
|
local.register_index
|
||||||
index
|
|
||||||
} else {
|
|
||||||
instruction.a()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
is_constant = true;
|
is_constant = true;
|
||||||
@ -705,25 +687,17 @@ impl<'src> Parser<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parse_statement(
|
self.parse_expression()?;
|
||||||
Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: true,
|
|
||||||
implicit_return: false,
|
|
||||||
},
|
|
||||||
Context::Assignment,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (mut previous_instruction, previous_position) =
|
let (mut previous_instruction, previous_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
|
||||||
if previous_instruction.operation().is_math() {
|
if previous_instruction.operation().is_math() {
|
||||||
let previous_register = self
|
let register_index = self
|
||||||
.chunk
|
.chunk
|
||||||
.get_local(local_index, start_position)?
|
.get_local(local_index, start_position)?
|
||||||
.register_index;
|
.register_index;
|
||||||
|
|
||||||
if let Some(register_index) = previous_register {
|
|
||||||
log::trace!("Condensing SET_LOCAL to binary math expression");
|
log::trace!("Condensing SET_LOCAL to binary math expression");
|
||||||
|
|
||||||
previous_instruction.set_a(register_index);
|
previous_instruction.set_a(register_index);
|
||||||
@ -731,7 +705,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_instruction(previous_instruction, previous_position);
|
self.emit_instruction(previous_instruction, previous_position);
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
@ -815,14 +788,7 @@ impl<'src> Parser<'src> {
|
|||||||
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
|
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
|
||||||
let next_register = self.current_register;
|
let next_register = self.current_register;
|
||||||
|
|
||||||
self.parse_statement(
|
self.parse_expression()?;
|
||||||
Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: false,
|
|
||||||
implicit_return: false,
|
|
||||||
},
|
|
||||||
Context::None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Operation::LoadConstant = self.chunk.get_last_operation()? {
|
if let Operation::LoadConstant = self.chunk.get_last_operation()? {
|
||||||
self.increment_register()?;
|
self.increment_register()?;
|
||||||
@ -853,21 +819,10 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_if(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_if(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
let length = self.chunk.len();
|
|
||||||
let expression_allowed = Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: false,
|
|
||||||
implicit_return: false,
|
|
||||||
};
|
|
||||||
let block_allowed = Allowed {
|
|
||||||
assignment: allowed.assignment,
|
|
||||||
explicit_return: allowed.explicit_return,
|
|
||||||
implicit_return: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_statement(expression_allowed, Context::None)?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
|
let length = self.chunk.len();
|
||||||
let is_explicit_boolean =
|
let is_explicit_boolean =
|
||||||
matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
|
matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
|
||||||
|
|
||||||
@ -878,6 +833,12 @@ impl<'src> Parser<'src> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let block_allowed = Allowed {
|
||||||
|
assignment: allowed.assignment,
|
||||||
|
explicit_return: allowed.explicit_return,
|
||||||
|
implicit_return: false,
|
||||||
|
};
|
||||||
|
|
||||||
if let Token::LeftCurlyBrace = self.current_token {
|
if let Token::LeftCurlyBrace = self.current_token {
|
||||||
self.parse_block(block_allowed)?;
|
self.parse_block(block_allowed)?;
|
||||||
}
|
}
|
||||||
@ -917,14 +878,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let jump_start = self.chunk.len();
|
let jump_start = self.chunk.len();
|
||||||
|
|
||||||
self.parse_statement(
|
self.parse_expression()?;
|
||||||
Allowed {
|
|
||||||
assignment: false,
|
|
||||||
explicit_return: false,
|
|
||||||
implicit_return: false,
|
|
||||||
},
|
|
||||||
Context::None,
|
|
||||||
)?;
|
|
||||||
self.parse_block(Allowed {
|
self.parse_block(Allowed {
|
||||||
assignment: true,
|
assignment: true,
|
||||||
explicit_return: allowed.explicit_return,
|
explicit_return: allowed.explicit_return,
|
||||||
@ -988,6 +942,17 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_expression(&mut self) -> Result<(), ParseError> {
|
||||||
|
self.parse(
|
||||||
|
Precedence::None,
|
||||||
|
Allowed {
|
||||||
|
assignment: false,
|
||||||
|
explicit_return: false,
|
||||||
|
implicit_return: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
let start = self.current_position.0;
|
let start = self.current_position.0;
|
||||||
|
|
||||||
@ -1120,7 +1085,8 @@ impl<'src> Parser<'src> {
|
|||||||
function_parser.advance()?;
|
function_parser.advance()?;
|
||||||
|
|
||||||
let end = function_parser.current_position.1;
|
let end = function_parser.current_position.1;
|
||||||
let local_index = function_parser.chunk.declare_local(
|
|
||||||
|
function_parser.chunk.declare_local(
|
||||||
parameter,
|
parameter,
|
||||||
Some(r#type),
|
Some(r#type),
|
||||||
is_mutable,
|
is_mutable,
|
||||||
@ -1128,11 +1094,6 @@ impl<'src> Parser<'src> {
|
|||||||
Span(start, end),
|
Span(start, end),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
function_parser.chunk.define_local(
|
|
||||||
local_index,
|
|
||||||
function_parser.current_register,
|
|
||||||
Span(start, end),
|
|
||||||
)?;
|
|
||||||
function_parser.increment_register()?;
|
function_parser.increment_register()?;
|
||||||
function_parser.allow(Token::Comma)?;
|
function_parser.allow(Token::Comma)?;
|
||||||
}
|
}
|
||||||
@ -1169,6 +1130,46 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_call(&mut self) -> Result<(), ParseError> {
|
||||||
|
self.advance()?;
|
||||||
|
|
||||||
|
let function_register = self.current_register;
|
||||||
|
let mut argument_count = 0;
|
||||||
|
|
||||||
|
while !self.allow(Token::RightParenthesis)? {
|
||||||
|
if argument_count > 0 {
|
||||||
|
self.expect(Token::Comma)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let register = self.current_register;
|
||||||
|
|
||||||
|
self.parse(
|
||||||
|
Precedence::None,
|
||||||
|
Allowed {
|
||||||
|
assignment: false,
|
||||||
|
explicit_return: false,
|
||||||
|
implicit_return: false,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if self.current_register == register {
|
||||||
|
return Err(ParseError::ExpectedExpression {
|
||||||
|
found: self.previous_token.to_owned(),
|
||||||
|
position: self.previous_position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
argument_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit_instruction(
|
||||||
|
Instruction::call(function_register, argument_count),
|
||||||
|
self.current_position,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(&mut self, precedence: Precedence, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse(&mut self, precedence: Precedence, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
if let Some(prefix_parser) = ParseRule::from(&self.current_token).prefix {
|
if let Some(prefix_parser) = ParseRule::from(&self.current_token).prefix {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@ -1384,8 +1385,8 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
|||||||
},
|
},
|
||||||
Token::LeftParenthesis => ParseRule {
|
Token::LeftParenthesis => ParseRule {
|
||||||
prefix: Some(Parser::parse_grouped),
|
prefix: Some(Parser::parse_grouped),
|
||||||
infix: None,
|
infix: Some(Parser::parse_call),
|
||||||
precedence: Precedence::None,
|
precedence: Precedence::Call,
|
||||||
},
|
},
|
||||||
Token::LeftSquareBrace => ParseRule {
|
Token::LeftSquareBrace => ParseRule {
|
||||||
prefix: Some(Parser::parse_list),
|
prefix: Some(Parser::parse_list),
|
||||||
|
@ -60,7 +60,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(body: Chunk) -> Self {
|
pub fn function(body: Chunk) -> Self {
|
||||||
Value::Primitive(Primitive::Function(Function { body }))
|
Value::Primitive(Primitive::Function(Function { chunk: body }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
||||||
@ -286,7 +286,7 @@ impl From<&str> for Value {
|
|||||||
|
|
||||||
impl Clone for Value {
|
impl Clone for Value {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
log::trace!("Cloning value {:?}", self);
|
log::trace!("Cloning value {self}");
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(data) => Value::Primitive(data.clone()),
|
Value::Primitive(data) => Value::Primitive(data.clone()),
|
||||||
@ -643,7 +643,7 @@ impl Ord for Primitive {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub body: Chunk,
|
pub chunk: Chunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -7,7 +7,6 @@ use crate::{
|
|||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = parse(source)?;
|
let chunk = parse(source)?;
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
|
|
||||||
vm.run()
|
vm.run()
|
||||||
@ -32,10 +31,6 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_chunk(self) -> Chunk {
|
|
||||||
self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
||||||
// DRY helper to get constant or register values for binary operations
|
// DRY helper to get constant or register values for binary operations
|
||||||
fn get_arguments(
|
fn get_arguments(
|
||||||
@ -131,14 +126,7 @@ impl Vm {
|
|||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.chunk.get_local(local_index, position)?;
|
||||||
let from_register =
|
let value = self.take(local.register_index, to_register, position)?;
|
||||||
local
|
|
||||||
.register_index
|
|
||||||
.ok_or_else(|| VmError::UndefinedVariable {
|
|
||||||
identifier: local.identifier.clone(),
|
|
||||||
position,
|
|
||||||
})?;
|
|
||||||
let value = self.take(from_register, to_register, position)?;
|
|
||||||
|
|
||||||
self.set(to_register, value, position)?;
|
self.set(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
@ -380,11 +368,38 @@ impl Vm {
|
|||||||
|
|
||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
|
Operation::Call => {
|
||||||
|
let function_index = instruction.a();
|
||||||
|
let argument_count = instruction.b();
|
||||||
|
let function = if let Value::Primitive(Primitive::Function(function)) =
|
||||||
|
self.get(function_index, position)?.clone()
|
||||||
|
{
|
||||||
|
function
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
let mut function_vm = Vm::new(function.chunk);
|
||||||
|
let first_argument_index = function_index + 1;
|
||||||
|
let last_argument_index = first_argument_index + argument_count;
|
||||||
|
|
||||||
|
for argument_index in first_argument_index..=last_argument_index {
|
||||||
|
let argument = self.empty(argument_index, position)?;
|
||||||
|
|
||||||
|
function_vm.stack.push(Register::Value(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
let return_value = function_vm.run()?;
|
||||||
|
|
||||||
|
if let Some(value) = return_value {
|
||||||
|
self.set(function_index, value, position)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let should_return_value = instruction.b_as_boolean();
|
let should_return_value = instruction.b_as_boolean();
|
||||||
|
let top_of_stack = (self.stack.len() - 1) as u8;
|
||||||
|
|
||||||
return if should_return_value {
|
return if should_return_value {
|
||||||
let value = self.empty(self.stack.len() - 1, position)?;
|
let value = self.empty(top_of_stack, position)?;
|
||||||
|
|
||||||
Ok(Some(value))
|
Ok(Some(value))
|
||||||
} else {
|
} else {
|
||||||
@ -507,7 +522,9 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
fn empty(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
if index >= self.stack.len() {
|
if index >= self.stack.len() {
|
||||||
return Err(VmError::RegisterIndexOutOfBounds { position });
|
return Err(VmError::RegisterIndexOutOfBounds { position });
|
||||||
}
|
}
|
||||||
@ -517,7 +534,7 @@ impl Vm {
|
|||||||
match register {
|
match register {
|
||||||
Register::Value(value) => Ok(value),
|
Register::Value(value) => Ok(value),
|
||||||
Register::Pointer(register_index) => {
|
Register::Pointer(register_index) => {
|
||||||
let value = self.empty(register_index as usize, position)?;
|
let value = self.empty(register_index, position)?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ fn add_assign() {
|
|||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -105,11 +105,11 @@ fn block_scope() {
|
|||||||
Value::integer(1)
|
Value::integer(1)
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
|
Local::new(Identifier::new("a"), None, false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), None, false, 1, Some(1)),
|
Local::new(Identifier::new("b"), None, false, 1, 1),
|
||||||
Local::new(Identifier::new("c"), None, false, 2, Some(2)),
|
Local::new(Identifier::new("c"), None, false, 2, 2),
|
||||||
Local::new(Identifier::new("d"), None, false, 1, Some(3)),
|
Local::new(Identifier::new("d"), None, false, 1, 3),
|
||||||
Local::new(Identifier::new("e"), None, false, 0, Some(4)),
|
Local::new(Identifier::new("e"), None, false, 0, 4),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -148,7 +148,7 @@ fn define_local() {
|
|||||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), None, false, 0, Some(0))]
|
vec![Local::new(Identifier::new("x"), None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ fn divide_assign() {
|
|||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(2)],
|
vec![Value::integer(2), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ fn equality_assignment_long() {
|
|||||||
(Instruction::r#return(true), Span(44, 44)),
|
(Instruction::r#return(true), Span(44, 44)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4), Value::integer(4)],
|
vec![Value::integer(4), Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ fn equality_assignment_short() {
|
|||||||
(Instruction::r#return(true), Span(16, 16)),
|
(Instruction::r#return(true), Span(16, 16)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4), Value::integer(4)],
|
vec![Value::integer(4), Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -664,7 +664,7 @@ fn multiply_assign() {
|
|||||||
(Instruction::r#return(true), Span(23, 23))
|
(Instruction::r#return(true), Span(23, 23))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(3)],
|
vec![Value::integer(2), Value::integer(3)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("a"), None, true, 0, 0),]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -803,7 +803,7 @@ fn set_local() {
|
|||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(41), Value::integer(42)],
|
vec![Value::integer(41), Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -852,7 +852,7 @@ fn subtract_assign() {
|
|||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(2)],
|
vec![Value::integer(42), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -879,8 +879,8 @@ fn variable_and() {
|
|||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
|
Local::new(Identifier::new("a"), None, false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), None, false, 0, Some(1)),
|
Local::new(Identifier::new("b"), None, false, 0, 1),
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
@ -909,7 +909,7 @@ fn r#while() {
|
|||||||
(Instruction::r#return(true), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, 0),]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user