Refactor and clean up
This commit is contained in:
parent
b8957190e0
commit
e9ec838b25
@ -102,28 +102,35 @@ impl Chunk {
|
|||||||
output.push_str(name);
|
output.push_str(name);
|
||||||
output.push_str(" ==\n");
|
output.push_str(" ==\n");
|
||||||
|
|
||||||
let mut next_is_index = false;
|
let mut previous = None;
|
||||||
|
|
||||||
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
||||||
if next_is_index {
|
if let Some(Instruction::Constant) = previous {
|
||||||
let index_display = format!("{position} {offset:04} INDEX {byte}\n");
|
let display = format!("{position} {offset:04} CONSTANT_INDEX {byte}\n");
|
||||||
|
previous = None;
|
||||||
|
|
||||||
output.push_str(&index_display);
|
output.push_str(&display);
|
||||||
|
|
||||||
next_is_index = false;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(
|
||||||
|
Instruction::DefineGlobal | Instruction::GetGlobal | Instruction::SetGlobal,
|
||||||
|
) = previous
|
||||||
|
{
|
||||||
|
let display = format!("{position} {offset:04} IDENTIFIER_INDEX {byte}\n");
|
||||||
|
previous = None;
|
||||||
|
|
||||||
|
output.push_str(&display);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let instruction = Instruction::from_byte(*byte).unwrap();
|
let instruction = Instruction::from_byte(*byte).unwrap();
|
||||||
let instruction_display =
|
let display = format!("{} {}\n", position, instruction.disassemble(self, offset));
|
||||||
format!("{} {}\n", position, instruction.disassemble(self, offset));
|
previous = Some(instruction);
|
||||||
|
|
||||||
output.push_str(&instruction_display);
|
output.push_str(&display);
|
||||||
|
|
||||||
if let Instruction::Constant = instruction {
|
|
||||||
next_is_index = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
output
|
||||||
|
@ -100,7 +100,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_boolean(&mut self) -> Result<(), ParseError> {
|
fn parse_boolean(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
if let Token::Boolean(text) = self.previous_token {
|
if let Token::Boolean(text) = self.previous_token {
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
let boolean = text.parse::<bool>().unwrap();
|
||||||
let value = Value::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
@ -111,7 +111,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_float(&mut self) -> Result<(), ParseError> {
|
fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
if let Token::Float(text) = self.previous_token {
|
if let Token::Float(text) = self.previous_token {
|
||||||
let float = text.parse::<f64>().unwrap();
|
let float = text.parse::<f64>().unwrap();
|
||||||
let value = Value::float(float);
|
let value = Value::float(float);
|
||||||
@ -122,7 +122,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_integer(&mut self) -> Result<(), ParseError> {
|
fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
if let Token::Integer(text) = self.previous_token {
|
if let Token::Integer(text) = self.previous_token {
|
||||||
let integer = text.parse::<i64>().unwrap();
|
let integer = text.parse::<i64>().unwrap();
|
||||||
let value = Value::integer(integer);
|
let value = Value::integer(integer);
|
||||||
@ -133,7 +133,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_string(&mut self) -> Result<(), ParseError> {
|
fn parse_string(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
if let Token::String(text) = self.previous_token {
|
if let Token::String(text) = self.previous_token {
|
||||||
let value = Value::string(text);
|
let value = Value::string(text);
|
||||||
|
|
||||||
@ -143,12 +143,12 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_grouped(&mut self) -> Result<(), ParseError> {
|
fn parse_grouped(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.expect(TokenKind::RightParenthesis)
|
self.expect(TokenKind::RightParenthesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unary(&mut self) -> Result<(), ParseError> {
|
fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
let operator_position = self.previous_position;
|
let operator_position = self.previous_position;
|
||||||
let byte = match self.previous_token.kind() {
|
let byte = match self.previous_token.kind() {
|
||||||
TokenKind::Minus => Instruction::Negate as u8,
|
TokenKind::Minus => Instruction::Negate as u8,
|
||||||
@ -198,15 +198,25 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_variable(&mut self) -> Result<(), ParseError> {
|
fn parse_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
self.parse_named_variable_from(self.previous_token.to_owned())
|
self.parse_named_variable_from(self.previous_token.to_owned(), allow_assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_named_variable_from(&mut self, token: TokenOwned) -> Result<(), ParseError> {
|
fn parse_named_variable_from(
|
||||||
|
&mut self,
|
||||||
|
token: TokenOwned,
|
||||||
|
allow_assignment: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
let identifier_index = self.parse_identifier_from(token)?;
|
let identifier_index = self.parse_identifier_from(token)?;
|
||||||
|
|
||||||
self.emit_byte(Instruction::GetGlobal as u8, self.previous_position);
|
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||||
self.emit_byte(identifier_index, self.previous_position);
|
self.parse_expression()?;
|
||||||
|
self.emit_byte(Instruction::SetGlobal as u8, self.previous_position);
|
||||||
|
self.emit_byte(identifier_index, self.previous_position);
|
||||||
|
} else {
|
||||||
|
self.emit_byte(Instruction::GetGlobal as u8, self.previous_position);
|
||||||
|
self.emit_byte(identifier_index, self.previous_position);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -268,7 +278,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn define_variable(&mut self, identifier_index: u8, position: Span) -> Result<(), ParseError> {
|
fn define_variable(&mut self, identifier_index: u8, position: Span) -> Result<(), ParseError> {
|
||||||
self.emit_byte(Instruction::SetGlobal as u8, position);
|
self.emit_byte(Instruction::DefineGlobal as u8, position);
|
||||||
self.emit_byte(identifier_index, position);
|
self.emit_byte(identifier_index, position);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -277,19 +287,24 @@ impl<'src> Parser<'src> {
|
|||||||
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix {
|
let prefix_rule = if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix
|
||||||
|
{
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Parsing {} as prefix with precedence {precedence}",
|
"Parsing {} as prefix with precedence {precedence}",
|
||||||
self.previous_token,
|
self.previous_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
prefix(self)?;
|
prefix
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let allow_assignment = precedence <= Precedence::Assignment;
|
||||||
|
|
||||||
|
prefix_rule(self, allow_assignment)?;
|
||||||
|
|
||||||
while precedence < ParseRule::from(&self.current_token.kind()).precedence {
|
while precedence < ParseRule::from(&self.current_token.kind()).precedence {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
@ -302,6 +317,13 @@ impl<'src> Parser<'src> {
|
|||||||
self.previous_token,
|
self.previous_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||||
|
return Err(ParseError::InvalidAssignmentTarget {
|
||||||
|
found: self.previous_token.to_owned(),
|
||||||
|
position: self.previous_position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
infix(self)?;
|
infix(self)?;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -357,12 +379,13 @@ impl Display for Precedence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParserFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool) -> Result<(), ParseError>;
|
||||||
|
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ParseRule<'a> {
|
pub struct ParseRule<'a> {
|
||||||
pub prefix: Option<ParserFunction<'a>>,
|
pub prefix: Option<PrefixFunction<'a>>,
|
||||||
pub infix: Option<ParserFunction<'a>>,
|
pub infix: Option<InfixFunction<'a>>,
|
||||||
pub precedence: Precedence,
|
pub precedence: Precedence,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,6 +513,10 @@ pub enum ParseError {
|
|||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
InvalidAssignmentTarget {
|
||||||
|
found: TokenOwned,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
|
||||||
// Wrappers around foreign errors
|
// Wrappers around foreign errors
|
||||||
Chunk(ChunkError),
|
Chunk(ChunkError),
|
||||||
@ -530,7 +557,7 @@ mod tests {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::Constant as u8, Span(8, 10)),
|
(Instruction::Constant as u8, Span(8, 10)),
|
||||||
(0, Span(8, 10)),
|
(0, Span(8, 10)),
|
||||||
(Instruction::SetGlobal as u8, Span(4, 5)),
|
(Instruction::DefineGlobal as u8, Span(4, 5)),
|
||||||
(0, Span(4, 5))
|
(0, Span(4, 5))
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
|
@ -5,7 +5,7 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
|
|
||||||
vm.interpret()
|
vm.run()
|
||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(&mut self) -> Result<Option<Value>, VmError> {
|
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
||||||
while let Ok((byte, position)) = self.read().copied() {
|
while let Ok((byte, position)) = self.read().copied() {
|
||||||
let instruction = Instruction::from_byte(byte)
|
let instruction = Instruction::from_byte(byte)
|
||||||
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
||||||
@ -44,7 +44,9 @@ impl Vm {
|
|||||||
Instruction::Pop => {
|
Instruction::Pop => {
|
||||||
self.pop()?;
|
self.pop()?;
|
||||||
}
|
}
|
||||||
Instruction::SetGlobal => {
|
|
||||||
|
// Variables
|
||||||
|
Instruction::DefineGlobal => {
|
||||||
let (index, _) = self.read().copied()?;
|
let (index, _) = self.read().copied()?;
|
||||||
let identifier = self.chunk.get_identifier(index as usize)?.clone();
|
let identifier = self.chunk.get_identifier(index as usize)?.clone();
|
||||||
let value = self.pop()?;
|
let value = self.pop()?;
|
||||||
@ -61,6 +63,18 @@ impl Vm {
|
|||||||
|
|
||||||
self.push(value)?;
|
self.push(value)?;
|
||||||
}
|
}
|
||||||
|
Instruction::SetGlobal => {
|
||||||
|
let (index, _) = self.read().copied()?;
|
||||||
|
let identifier = self.chunk.get_identifier(index as usize)?.clone();
|
||||||
|
|
||||||
|
if !self.globals.contains_key(&identifier) {
|
||||||
|
return Err(VmError::UndefinedGlobal(identifier, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = self.pop()?;
|
||||||
|
|
||||||
|
self.globals.insert(identifier, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
Instruction::Negate => {
|
Instruction::Negate => {
|
||||||
@ -225,26 +239,29 @@ pub enum Instruction {
|
|||||||
Constant = 0,
|
Constant = 0,
|
||||||
Return = 1,
|
Return = 1,
|
||||||
Pop = 2,
|
Pop = 2,
|
||||||
SetGlobal = 3,
|
|
||||||
|
// Variables
|
||||||
|
DefineGlobal = 3,
|
||||||
GetGlobal = 4,
|
GetGlobal = 4,
|
||||||
|
SetGlobal = 5,
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
Negate = 5,
|
Negate = 6,
|
||||||
Not = 6,
|
Not = 7,
|
||||||
|
|
||||||
// Binary
|
// Binary
|
||||||
Add = 7,
|
Add = 8,
|
||||||
Subtract = 8,
|
Subtract = 9,
|
||||||
Multiply = 9,
|
Multiply = 10,
|
||||||
Divide = 10,
|
Divide = 11,
|
||||||
Greater = 11,
|
Greater = 12,
|
||||||
Less = 12,
|
Less = 13,
|
||||||
GreaterEqual = 13,
|
GreaterEqual = 14,
|
||||||
LessEqual = 14,
|
LessEqual = 15,
|
||||||
Equal = 15,
|
Equal = 16,
|
||||||
NotEqual = 16,
|
NotEqual = 17,
|
||||||
And = 17,
|
And = 18,
|
||||||
Or = 18,
|
Or = 19,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
@ -253,22 +270,23 @@ impl Instruction {
|
|||||||
0 => Some(Instruction::Constant),
|
0 => Some(Instruction::Constant),
|
||||||
1 => Some(Instruction::Return),
|
1 => Some(Instruction::Return),
|
||||||
2 => Some(Instruction::Pop),
|
2 => Some(Instruction::Pop),
|
||||||
3 => Some(Instruction::SetGlobal),
|
3 => Some(Instruction::DefineGlobal),
|
||||||
4 => Some(Instruction::GetGlobal),
|
4 => Some(Instruction::GetGlobal),
|
||||||
5 => Some(Instruction::Negate),
|
5 => Some(Instruction::SetGlobal),
|
||||||
6 => Some(Instruction::Not),
|
6 => Some(Instruction::Negate),
|
||||||
7 => Some(Instruction::Add),
|
7 => Some(Instruction::Not),
|
||||||
8 => Some(Instruction::Subtract),
|
8 => Some(Instruction::Add),
|
||||||
9 => Some(Instruction::Multiply),
|
9 => Some(Instruction::Subtract),
|
||||||
10 => Some(Instruction::Divide),
|
10 => Some(Instruction::Multiply),
|
||||||
11 => Some(Instruction::Greater),
|
11 => Some(Instruction::Divide),
|
||||||
12 => Some(Instruction::Less),
|
12 => Some(Instruction::Greater),
|
||||||
13 => Some(Instruction::GreaterEqual),
|
13 => Some(Instruction::Less),
|
||||||
14 => Some(Instruction::LessEqual),
|
14 => Some(Instruction::GreaterEqual),
|
||||||
15 => Some(Instruction::Equal),
|
15 => Some(Instruction::LessEqual),
|
||||||
16 => Some(Instruction::NotEqual),
|
16 => Some(Instruction::Equal),
|
||||||
17 => Some(Instruction::And),
|
17 => Some(Instruction::NotEqual),
|
||||||
18 => Some(Instruction::Or),
|
18 => Some(Instruction::And),
|
||||||
|
19 => Some(Instruction::Or),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,17 +304,32 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Instruction::Return => format!("{offset:04} RETURN"),
|
Instruction::Return => format!("{offset:04} RETURN"),
|
||||||
Instruction::Pop => format!("{offset:04} POP"),
|
Instruction::Pop => format!("{offset:04} POP"),
|
||||||
Instruction::SetGlobal => {
|
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
|
||||||
let identifier = chunk.get_identifier(*index as usize).unwrap();
|
|
||||||
|
|
||||||
format!("{offset:04} DEFINE_GLOBAL {identifier}")
|
// Variables
|
||||||
|
Instruction::DefineGlobal => {
|
||||||
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
|
let index = *index as usize;
|
||||||
|
let identifier = chunk.get_identifier(index).unwrap();
|
||||||
|
let value = chunk.get_constant(index).unwrap();
|
||||||
|
|
||||||
|
format!("{offset:04} DEFINE_GLOBAL {identifier} {value}")
|
||||||
}
|
}
|
||||||
Instruction::GetGlobal => {
|
Instruction::GetGlobal => {
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
let identifier = chunk.get_identifier(*index as usize).unwrap();
|
let index = *index as usize;
|
||||||
|
let identifier = chunk.get_identifier(index).unwrap();
|
||||||
|
let value = chunk.get_constant(index).unwrap();
|
||||||
|
|
||||||
format!("{offset:04} GET_GLOBAL {identifier}")
|
format!("{offset:04} GET_GLOBAL {identifier} {value}")
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::SetGlobal => {
|
||||||
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
|
let index = *index as usize;
|
||||||
|
let identifier = chunk.get_identifier(index).unwrap();
|
||||||
|
let value = chunk.get_constant(index).unwrap();
|
||||||
|
|
||||||
|
format!("{offset:04} SET_GLOBAL {identifier} {value}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
@ -335,7 +368,7 @@ pub mod tests {
|
|||||||
chunk.write(Instruction::Return as u8, Span(2, 3));
|
chunk.write(Instruction::Return as u8, Span(2, 3));
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
let result = vm.interpret();
|
let result = vm.run();
|
||||||
|
|
||||||
assert_eq!(result, Ok(Some(Value::integer(-42))));
|
assert_eq!(result, Ok(Some(Value::integer(-42))));
|
||||||
}
|
}
|
||||||
@ -354,7 +387,7 @@ pub mod tests {
|
|||||||
chunk.write(Instruction::Return as u8, Span(10, 11));
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
let result = vm.interpret();
|
let result = vm.run();
|
||||||
|
|
||||||
assert_eq!(result, Ok(Some(Value::integer(65))));
|
assert_eq!(result, Ok(Some(Value::integer(65))));
|
||||||
}
|
}
|
||||||
@ -373,7 +406,7 @@ pub mod tests {
|
|||||||
chunk.write(Instruction::Return as u8, Span(10, 11));
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
let result = vm.interpret();
|
let result = vm.run();
|
||||||
|
|
||||||
assert_eq!(result, Ok(Some(Value::integer(19))));
|
assert_eq!(result, Ok(Some(Value::integer(19))));
|
||||||
}
|
}
|
||||||
@ -392,7 +425,7 @@ pub mod tests {
|
|||||||
chunk.write(Instruction::Return as u8, Span(10, 11));
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
let result = vm.interpret();
|
let result = vm.run();
|
||||||
|
|
||||||
assert_eq!(result, Ok(Some(Value::integer(966))));
|
assert_eq!(result, Ok(Some(Value::integer(966))));
|
||||||
}
|
}
|
||||||
@ -412,7 +445,7 @@ pub mod tests {
|
|||||||
chunk.write(Instruction::Return as u8, Span(10, 11));
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
||||||
|
|
||||||
let mut vm = Vm::new(chunk);
|
let mut vm = Vm::new(chunk);
|
||||||
let result = vm.interpret();
|
let result = vm.run();
|
||||||
|
|
||||||
assert_eq!(result, Ok(Some(Value::integer(1))));
|
assert_eq!(result, Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user