1
0

Add native calls and the panic native

This commit is contained in:
Jeff 2024-10-30 03:08:25 -04:00
parent caf1c22af0
commit af4e43fc9f
11 changed files with 317 additions and 90 deletions

View File

@ -1,7 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInFunction {
String,
WriteLine,
}

View File

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Chunk, Operation}; use crate::{Chunk, NativeFunction, Operation};
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Instruction(u32); pub struct Instruction(u32);
@ -229,6 +229,21 @@ impl Instruction {
instruction instruction
} }
pub fn call_native(
to_register: u8,
native_fn: NativeFunction,
argument_count: u8,
) -> Instruction {
let mut instruction = Instruction(Operation::CallNative as u32);
let native_fn_byte = native_fn as u8;
instruction.set_a(to_register);
instruction.set_b(native_fn_byte);
instruction.set_c(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);
@ -335,6 +350,7 @@ impl Instruction {
self.operation(), self.operation(),
Operation::Add Operation::Add
| Operation::Call | Operation::Call
| Operation::CallNative
| Operation::Divide | Operation::Divide
| Operation::GetLocal | Operation::GetLocal
| Operation::LoadBoolean | Operation::LoadBoolean
@ -542,6 +558,8 @@ impl Instruction {
let argument_count = self.c(); let argument_count = self.c();
let mut output = format!("R{to_register} = R{function_register}("); let mut output = format!("R{to_register} = R{function_register}(");
if argument_count != 0 {
let first_argument = function_register + 1; let first_argument = function_register + 1;
for (index, register) in for (index, register) in
@ -553,6 +571,35 @@ impl Instruction {
output.push_str(&format!("R{}", register)); output.push_str(&format!("R{}", register));
} }
}
output.push(')');
output
}
Operation::CallNative => {
let to_register = self.a();
let native_function = NativeFunction::from(self.b());
let argument_count = self.c();
let native_function_name = match native_function {
NativeFunction::Panic => "PANIC",
NativeFunction::ToString => "TO_STRING",
NativeFunction::WriteLine => "WRITE_LINE",
};
let mut output = format!("R{to_register} = {native_function_name}(");
if argument_count != 0 {
let first_argument = to_register.saturating_sub(argument_count);
for (index, register) in (first_argument..to_register).enumerate() {
if index > 0 {
output.push_str(", ");
}
output.push_str(&format!("R{}", register));
}
}
output.push(')'); output.push(')');

View File

@ -152,14 +152,16 @@ impl<'src> Lexer<'src> {
if let Some(c) = self.peek_char() { if let Some(c) = self.peek_char() {
self.position += 1; self.position += 1;
if let Some('\'') = self.peek_char() { let peek = self.peek_char();
if let Some('\'') = peek {
self.position += 1; self.position += 1;
(Token::Character(c), Span(self.position - 3, self.position)) (Token::Character(c), Span(self.position - 3, self.position))
} else { } else {
return Err(LexError::ExpectedCharacter { return Err(LexError::ExpectedCharacter {
expected: '\'', expected: '\'',
actual: c, actual: peek.unwrap_or('\0'),
position: self.position, position: self.position,
}); });
} }
@ -561,6 +563,7 @@ impl<'src> Lexer<'src> {
"loop" => Token::Loop, "loop" => Token::Loop,
"map" => Token::Map, "map" => Token::Map,
"mut" => Token::Mut, "mut" => Token::Mut,
"panic" => Token::Panic,
"return" => Token::Return, "return" => Token::Return,
"str" => Token::Str, "str" => Token::Str,
"struct" => Token::Struct, "struct" => Token::Struct,

View File

@ -4,6 +4,7 @@ mod formatter;
mod identifier; mod identifier;
mod instruction; mod instruction;
mod lexer; mod lexer;
mod native_function;
mod operation; mod operation;
mod parser; mod parser;
mod token; mod token;
@ -11,14 +12,13 @@ mod r#type;
mod value; mod value;
mod vm; mod vm;
use std::fmt::Display;
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local}; pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use formatter::{format, Formatter}; pub use formatter::{format, Formatter};
pub use identifier::Identifier; pub use identifier::Identifier;
pub use instruction::Instruction; pub use instruction::Instruction;
pub use lexer::{lex, LexError, Lexer}; pub use lexer::{lex, LexError, Lexer};
pub use native_function::NativeFunction;
pub use operation::Operation; pub use operation::Operation;
pub use parser::{parse, ParseError, Parser}; pub use parser::{parse, ParseError, Parser};
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
@ -26,6 +26,8 @@ pub use token::{Token, TokenKind, TokenOwned};
pub use value::{Function, Value, ValueError}; pub use value::{Function, Value, ValueError};
pub use vm::{run, Vm, VmError}; pub use vm::{run, Vm, VmError};
use std::fmt::Display;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]

View File

@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
const PANIC: u8 = 0b0000_0000;
const TO_STRING: u8 = 0b0000_0001;
const WRITE_LINE: u8 = 0b0000_0010;
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum NativeFunction {
Panic = PANIC as isize,
ToString = TO_STRING as isize,
WriteLine = WRITE_LINE as isize,
}
impl From<u8> for NativeFunction {
fn from(byte: u8) -> Self {
match byte {
PANIC => NativeFunction::Panic,
TO_STRING => NativeFunction::ToString,
WRITE_LINE => NativeFunction::WriteLine,
_ => {
if cfg!(test) {
panic!("Invalid operation byte: {}", byte)
} else {
NativeFunction::Panic
}
}
}
}
}
impl From<NativeFunction> for u8 {
fn from(native_function: NativeFunction) -> Self {
match native_function {
NativeFunction::Panic => PANIC,
NativeFunction::ToString => TO_STRING,
NativeFunction::WriteLine => WRITE_LINE,
}
}
}

View File

@ -30,7 +30,8 @@ const NOT: u8 = 0b0001_0100;
const JUMP: u8 = 0b0001_0101; const JUMP: u8 = 0b0001_0101;
const CALL: u8 = 0b0001_0110; const CALL: u8 = 0b0001_0110;
const RETURN: u8 = 0b0001_0111; const CALL_NATIVE: u8 = 0b0001_0111;
const RETURN: u8 = 0b0001_1000;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation { pub enum Operation {
@ -72,6 +73,7 @@ pub enum Operation {
// Control flow // Control flow
Jump = JUMP as isize, Jump = JUMP as isize,
Call = CALL as isize, Call = CALL as isize,
CallNative = CALL_NATIVE as isize,
Return = RETURN as isize, Return = RETURN as isize,
} }
@ -125,6 +127,7 @@ impl From<u8> for Operation {
NOT => Operation::Not, NOT => Operation::Not,
JUMP => Operation::Jump, JUMP => Operation::Jump,
CALL => Operation::Call, CALL => Operation::Call,
CALL_NATIVE => Operation::CallNative,
RETURN => Operation::Return, RETURN => Operation::Return,
_ => { _ => {
if cfg!(test) { if cfg!(test) {
@ -163,6 +166,7 @@ impl From<Operation> for u8 {
Operation::Not => NOT, Operation::Not => NOT,
Operation::Jump => JUMP, Operation::Jump => JUMP,
Operation::Call => CALL, Operation::Call => CALL,
Operation::CallNative => CALL_NATIVE,
Operation::Return => RETURN, Operation::Return => RETURN,
} }
} }
@ -194,6 +198,7 @@ impl Display for Operation {
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::Call => write!(f, "CALL"),
Operation::CallNative => write!(f, "CALL_NATIVE"),
Operation::Return => write!(f, "RETURN"), Operation::Return => write!(f, "RETURN"),
} }
} }

View File

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError,
Lexer, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
}; };
pub fn parse(source: &str) -> Result<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -1058,6 +1058,42 @@ impl<'src> Parser<'src> {
Ok(()) Ok(())
} }
fn parse_panic(&mut self, _: Allowed) -> Result<(), ParseError> {
let start = self.current_position.0;
let start_register = self.next_register();
self.advance()?;
if self.allow(Token::LeftParenthesis)? {
while !self.allow(Token::RightParenthesis)? {
let expected_register = self.next_register();
self.parse_expression()?;
let actual_register = self.next_register() - 1;
if expected_register < actual_register {
self.emit_instruction(
Instruction::close(expected_register, actual_register),
self.current_position,
);
}
self.allow(Token::Comma)?;
}
}
let end = self.current_position.1;
let to_register = self.next_register();
let argument_count = to_register - start_register;
self.emit_instruction(
Instruction::call_native(to_register, NativeFunction::Panic, argument_count),
Span(start, end),
);
Ok(())
}
fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> { fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> {
self.parse(Precedence::None, allowed)?; self.parse(Precedence::None, allowed)?;
@ -1352,8 +1388,8 @@ impl<'src> Parser<'src> {
}); });
} }
let start = self.current_position.0;
let function_register = last_instruction.a(); let function_register = last_instruction.a();
let start = self.current_position.0;
self.advance()?; self.advance()?;
@ -1690,6 +1726,11 @@ impl From<&Token<'_>> for ParseRule<'_> {
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },
Token::Panic => ParseRule {
prefix: Some(Parser::parse_panic),
infix: None,
precedence: Precedence::Call,
},
Token::Percent => ParseRule { Token::Percent => ParseRule {
prefix: None, prefix: None,
infix: Some(Parser::parse_math_binary), infix: Some(Parser::parse_math_binary),

View File

@ -32,6 +32,7 @@ pub enum Token<'src> {
Loop, Loop,
Map, Map,
Mut, Mut,
Panic,
Return, Return,
Str, Str,
Struct, Struct,
@ -119,6 +120,7 @@ impl<'src> Token<'src> {
Token::LessEqual => 2, Token::LessEqual => 2,
Token::Minus => 1, Token::Minus => 1,
Token::MinusEqual => 2, Token::MinusEqual => 2,
Token::Panic => 5,
Token::Percent => 1, Token::Percent => 1,
Token::PercentEqual => 2, Token::PercentEqual => 2,
Token::Plus => 1, Token::Plus => 1,
@ -180,6 +182,7 @@ impl<'src> Token<'src> {
Token::LessEqual => "<=", Token::LessEqual => "<=",
Token::Minus => "-", Token::Minus => "-",
Token::MinusEqual => "-=", Token::MinusEqual => "-=",
Token::Panic => "panic",
Token::Percent => "%", Token::Percent => "%",
Token::PercentEqual => "%=", Token::PercentEqual => "%=",
Token::Plus => "+", Token::Plus => "+",
@ -237,6 +240,7 @@ impl<'src> Token<'src> {
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
Token::MinusEqual => TokenOwned::MinusEqual, Token::MinusEqual => TokenOwned::MinusEqual,
Token::Mut => TokenOwned::Mut, Token::Mut => TokenOwned::Mut,
Token::Panic => TokenOwned::Panic,
Token::Percent => TokenOwned::Percent, Token::Percent => TokenOwned::Percent,
Token::PercentEqual => TokenOwned::PercentEqual, Token::PercentEqual => TokenOwned::PercentEqual,
Token::Plus => TokenOwned::Plus, Token::Plus => TokenOwned::Plus,
@ -298,6 +302,7 @@ impl<'src> Token<'src> {
Token::Minus => TokenKind::Minus, Token::Minus => TokenKind::Minus,
Token::MinusEqual => TokenKind::MinusEqual, Token::MinusEqual => TokenKind::MinusEqual,
Token::Mut => TokenKind::Mut, Token::Mut => TokenKind::Mut,
Token::Panic => TokenKind::Panic,
Token::Percent => TokenKind::Percent, Token::Percent => TokenKind::Percent,
Token::PercentEqual => TokenKind::PercentEqual, Token::PercentEqual => TokenKind::PercentEqual,
Token::Plus => TokenKind::Plus, Token::Plus => TokenKind::Plus,
@ -351,6 +356,7 @@ impl<'src> Token<'src> {
| Token::MinusEqual | Token::MinusEqual
| Token::Percent | Token::Percent
| Token::PercentEqual | Token::PercentEqual
| Token::Panic
| Token::Plus | Token::Plus
| Token::PlusEqual | Token::PlusEqual
| Token::Slash | Token::Slash
@ -403,6 +409,7 @@ impl<'src> Display for Token<'src> {
Token::Minus => write!(f, "-"), Token::Minus => write!(f, "-"),
Token::MinusEqual => write!(f, "-="), Token::MinusEqual => write!(f, "-="),
Token::Mut => write!(f, "mut"), Token::Mut => write!(f, "mut"),
Token::Panic => write!(f, "panic"),
Token::Percent => write!(f, "%"), Token::Percent => write!(f, "%"),
Token::PercentEqual => write!(f, "%="), Token::PercentEqual => write!(f, "%="),
Token::Plus => write!(f, "+"), Token::Plus => write!(f, "+"),
@ -454,6 +461,7 @@ pub enum TokenOwned {
Loop, Loop,
Map, Map,
Mut, Mut,
Panic,
Return, Return,
Str, Str,
While, While,
@ -536,6 +544,7 @@ impl Display for TokenOwned {
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
TokenOwned::MinusEqual => Token::MinusEqual.fmt(f), TokenOwned::MinusEqual => Token::MinusEqual.fmt(f),
TokenOwned::Mut => Token::Mut.fmt(f), TokenOwned::Mut => Token::Mut.fmt(f),
TokenOwned::Panic => Token::Panic.fmt(f),
TokenOwned::Percent => Token::Percent.fmt(f), TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::PercentEqual => Token::PercentEqual.fmt(f), TokenOwned::PercentEqual => Token::PercentEqual.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f),
@ -584,6 +593,7 @@ pub enum TokenKind {
Let, Let,
Loop, Loop,
Map, Map,
Panic,
Return, Return,
Str, Str,
While, While,
@ -667,6 +677,7 @@ impl Display for TokenKind {
TokenKind::Minus => Token::Minus.fmt(f), TokenKind::Minus => Token::Minus.fmt(f),
TokenKind::MinusEqual => Token::MinusEqual.fmt(f), TokenKind::MinusEqual => Token::MinusEqual.fmt(f),
TokenKind::Mut => Token::Mut.fmt(f), TokenKind::Mut => Token::Mut.fmt(f),
TokenKind::Panic => Token::Panic.fmt(f),
TokenKind::Percent => Token::Percent.fmt(f), TokenKind::Percent => Token::Percent.fmt(f),
TokenKind::PercentEqual => Token::PercentEqual.fmt(f), TokenKind::PercentEqual => Token::PercentEqual.fmt(f),
TokenKind::Plus => Token::Plus.fmt(f), TokenKind::Plus => Token::Plus.fmt(f),

View File

@ -2,7 +2,7 @@ use std::{cmp::Ordering, mem::replace};
use crate::{ use crate::{
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
Identifier, Instruction, Operation, Span, Type, Value, ValueError, Identifier, Instruction, NativeFunction, Operation, Span, Type, Value, ValueError,
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
@ -386,6 +386,50 @@ impl Vm {
self.set(to_register, value, position)?; self.set(to_register, value, position)?;
} }
} }
Operation::CallNative => {
let to_register = instruction.a();
let native_function = NativeFunction::from(instruction.b());
let argument_count = instruction.c();
let return_value = match native_function {
NativeFunction::Panic => {
let message = if argument_count == 0 {
None
} else {
let mut message = String::new();
for argument_index in 0..argument_count {
if argument_index != 0 {
message.push(' ');
}
let argument = self.get(argument_index, position)?;
message.push_str(&argument.to_string());
}
Some(message)
};
return Err(VmError::Panic { message, position });
}
NativeFunction::ToString => {
let mut string = String::new();
for argument_index in 0..argument_count {
let argument = self.get(argument_index, position)?;
string.push_str(&argument.to_string());
}
Some(Value::Primitive(Primitive::String(string)))
}
NativeFunction::WriteLine => todo!(),
};
if let Some(value) = return_value {
self.set(to_register, value, position)?;
}
}
Operation::Return => { Operation::Return => {
let should_return_value = instruction.b_as_boolean(); let should_return_value = instruction.b_as_boolean();
@ -626,6 +670,10 @@ pub enum VmError {
found: Value, found: Value,
position: Span, position: Span,
}, },
Panic {
message: Option<String>,
position: Span,
},
RegisterIndexOutOfBounds { RegisterIndexOutOfBounds {
index: usize, index: usize,
position: Span, position: Span,
@ -670,6 +718,7 @@ impl AnnotatedError for VmError {
Self::EmptyRegister { .. } => "Empty register", Self::EmptyRegister { .. } => "Empty register",
Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedFunction { .. } => "Expected function", Self::ExpectedFunction { .. } => "Expected function",
Self::Panic { .. } => "Explicit Panic",
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::InvalidInstruction { .. } => "Invalid instruction", Self::InvalidInstruction { .. } => "Invalid instruction",
Self::StackOverflow { .. } => "Stack overflow", Self::StackOverflow { .. } => "Stack overflow",
@ -684,6 +733,7 @@ impl AnnotatedError for VmError {
match self { match self {
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")), Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
Self::Panic { message, .. } => message.clone(),
Self::RegisterIndexOutOfBounds { index, .. } => { Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist")) Some(format!("Register {index} does not exist"))
} }
@ -702,6 +752,7 @@ impl AnnotatedError for VmError {
Self::EmptyRegister { position, .. } => *position, Self::EmptyRegister { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position,
Self::Panic { position, .. } => *position,
Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::InvalidInstruction { position, .. } => *position, Self::InvalidInstruction { position, .. } => *position,
Self::StackUnderflow { position } => *position, Self::StackUnderflow { position } => *position,

View File

@ -569,75 +569,75 @@ fn if_else_complex() {
) )
} }
#[test] // #[test]
fn if_else_nested() { // fn if_else_nested() {
let source = r#" // let source = r#"
if 0 == 1 { // if 0 == 1 {
if 0 == 2 { // if 0 == 2 {
1; // 1;
} else { // } else {
2; // 2;
} // }
} else { // } else {
if 0 == 3 { // if 0 == 3 {
3; // 3;
} else { // } else {
4; // 4;
} // }
}"#; // }"#;
assert_eq!( // assert_eq!(
parse(source), // parse(source),
Ok(Chunk::with_data( // Ok(Chunk::with_data(
None, // None,
vec![ // vec![
( // (
*Instruction::equal(true, 0, 1) // *Instruction::equal(true, 0, 1)
.set_b_is_constant() // .set_b_is_constant()
.set_c_is_constant(), // .set_c_is_constant(),
Span(14, 16) // Span(14, 16)
), // ),
(Instruction::jump(7), Span(14, 16)), // (Instruction::jump(7), Span(14, 16)),
( // (
*Instruction::equal(true, 0, 2) // *Instruction::equal(true, 0, 2)
.set_b_is_constant() // .set_b_is_constant()
.set_c_is_constant(), // .set_c_is_constant(),
Span(38, 41) // Span(38, 41)
), // ),
(Instruction::jump(3), Span(38, 41)), // (Instruction::jump(3), Span(38, 41)),
(Instruction::load_constant(0, 1, false), Span(61, 62)), // (Instruction::load_constant(0, 1, false), Span(61, 62)),
(Instruction::jump(11), Span(95, 95)), // (Instruction::jump(11), Span(95, 95)),
( // (
*Instruction::equal(true, 0, 3) // *Instruction::equal(true, 0, 3)
.set_b_is_constant() // .set_b_is_constant()
.set_c_is_constant(), // .set_c_is_constant(),
Span(77, 79) // Span(77, 79)
), // ),
(Instruction::jump(3), Span(77, 79)), // (Instruction::jump(3), Span(77, 79)),
(Instruction::load_constant(0, 2, false), Span(94, 95)), // (Instruction::load_constant(0, 2, false), Span(94, 95)),
(Instruction::jump(11), Span(95, 95)), // (Instruction::jump(11), Span(95, 95)),
(Instruction::load_constant(0, 3, false), Span(114, 115)), // (Instruction::load_constant(0, 3, false), Span(114, 115)),
(Instruction::jump(11), Span(95, 95)), // (Instruction::jump(11), Span(95, 95)),
(Instruction::load_constant(0, 4, false), Span(134, 135)), // (Instruction::load_constant(0, 4, false), Span(134, 135)),
(Instruction::r#return(true), Span(146, 146)), // (Instruction::r#return(true), Span(146, 146)),
], // ],
vec![ // vec![
Value::integer(0), // Value::integer(0),
Value::integer(1), // Value::integer(1),
Value::integer(0), // Value::integer(0),
Value::integer(2), // Value::integer(2),
Value::integer(1), // Value::integer(1),
Value::integer(0), // Value::integer(0),
Value::integer(3), // Value::integer(3),
Value::integer(3), // Value::integer(3),
Value::integer(4) // Value::integer(4)
], // ],
vec![] // vec![]
)) // ))
); // );
assert_eq!(run(source), Ok(Some(Value::integer(4)))); // assert_eq!(run(source), Ok(Some(Value::integer(4))));
} // }
#[test] #[test]
fn if_else_simple() { fn if_else_simple() {

View File

@ -0,0 +1,35 @@
use dust_lang::*;
#[test]
fn panic() {
let source = "panic(\"Goodbye world!\", 42)";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(Instruction::load_constant(0, 0, false), Span(6, 22)),
(Instruction::load_constant(1, 1, false), Span(24, 26)),
(
Instruction::call_native(2, NativeFunction::Panic, 2),
Span(0, 27)
),
(Instruction::r#return(true), Span(27, 27))
],
vec![Value::string("Goodbye world!"), Value::integer(42)],
vec![]
)),
);
assert_eq!(
run(source),
Err(DustError::Runtime {
error: VmError::Panic {
message: Some("Goodbye world! 42".to_string()),
position: Span(0, 27)
},
source
})
)
}