Fix bugs and improve disassembler
This commit is contained in:
parent
82a2b8f6b7
commit
a0439675b7
@ -62,8 +62,10 @@ impl Chunk {
|
|||||||
self.instructions.push((instruction, position));
|
self.instructions.push((instruction, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_instruction(&mut self) -> Option<(Instruction, Span)> {
|
pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
|
||||||
self.instructions.pop()
|
self.instructions
|
||||||
|
.pop()
|
||||||
|
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> {
|
pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> {
|
||||||
@ -207,10 +209,7 @@ impl Display for Chunk {
|
|||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
self.disassembler("Chunk Display")
|
self.disassembler("Chunk Display").styled().disassemble()
|
||||||
.styled()
|
|
||||||
.width(80)
|
|
||||||
.disassemble()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,42 +259,65 @@ pub struct ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ChunkDisassembler<'a> {
|
impl<'a> ChunkDisassembler<'a> {
|
||||||
|
const INSTRUCTION_HEADER: [&'static str; 5] = [
|
||||||
|
"",
|
||||||
|
"Instructions",
|
||||||
|
"------------",
|
||||||
|
"OFFSET OPERATION INFO POSITION",
|
||||||
|
"------- -------------- -------------------- --------",
|
||||||
|
];
|
||||||
|
|
||||||
|
const CONSTANT_HEADER: [&'static str; 5] = [
|
||||||
|
"",
|
||||||
|
"Constants",
|
||||||
|
"---------",
|
||||||
|
"INDEX KIND VALUE",
|
||||||
|
"----- ----- -----",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LOCAL_HEADER: [&'static str; 5] = [
|
||||||
|
"",
|
||||||
|
"Locals",
|
||||||
|
"------",
|
||||||
|
"INDEX IDENTIFIER DEPTH KIND VALUE",
|
||||||
|
"----- ---------- ----- ----- -----",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// The default width of the disassembly output. To correctly align the output, this should be
|
||||||
|
/// set to the width of the longest line that the disassembler is guaranteed to produce.
|
||||||
|
const DEFAULT_WIDTH: usize = Self::INSTRUCTION_HEADER[3].len() + 1;
|
||||||
|
|
||||||
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
chunk,
|
chunk,
|
||||||
width: 0,
|
width: Self::DEFAULT_WIDTH,
|
||||||
styled: false,
|
styled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) -> String {
|
pub fn disassemble(&self) -> String {
|
||||||
let mut disassembled = String::new();
|
let chunk_header = self.chunk_header();
|
||||||
let mut push_centered_line = |section: &str| {
|
let mut disassembled = String::with_capacity(self.predict_capacity());
|
||||||
let width = self.width;
|
|
||||||
let is_header = section.contains('\n');
|
|
||||||
|
|
||||||
if is_header && self.styled {
|
println!("capactity: {}", disassembled.capacity());
|
||||||
disassembled.push('\n');
|
|
||||||
|
|
||||||
for line in section.lines() {
|
let center = |line: &str| format!("{line:^width$}\n", width = self.width);
|
||||||
if line.is_empty() {
|
let style = |line: String| {
|
||||||
continue;
|
if self.styled {
|
||||||
}
|
line.bold().to_string()
|
||||||
|
|
||||||
let centered = format!("{:^width$}\n", line.bold());
|
|
||||||
|
|
||||||
disassembled.push_str(¢ered);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let centered = format!("{section:^width$}\n");
|
line
|
||||||
|
|
||||||
disassembled.push_str(¢ered);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
push_centered_line(&self.name_header());
|
for line in chunk_header.iter() {
|
||||||
push_centered_line(self.instructions_header());
|
disassembled.push_str(&style(center(line)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for line in Self::INSTRUCTION_HEADER {
|
||||||
|
disassembled.push_str(&style(center(line)));
|
||||||
|
}
|
||||||
|
|
||||||
for (offset, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
for (offset, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
@ -307,17 +329,18 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
format!("{offset:<7} {operation:14} {:20} {position:8}", " ")
|
format!("{offset:<7} {operation:14} {:20} {position:8}", " ")
|
||||||
};
|
};
|
||||||
|
|
||||||
push_centered_line(&instruction_display);
|
disassembled.push_str(¢er(&instruction_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
push_centered_line(Self::constant_header());
|
for line in Self::CONSTANT_HEADER {
|
||||||
|
disassembled.push_str(&style(center(line)));
|
||||||
|
}
|
||||||
|
|
||||||
for (index, value_option) in self.chunk.constants.iter().enumerate() {
|
for (index, value_option) in self.chunk.constants.iter().enumerate() {
|
||||||
let value_kind_display = match value_option {
|
let value_kind_display = if let Some(value) = value_option {
|
||||||
Some(Value::Raw(_)) => "RAW",
|
value.kind().to_string()
|
||||||
Some(Value::Reference(_)) => "REF",
|
} else {
|
||||||
Some(Value::Mutable(_)) => "MUT",
|
"empty".to_string()
|
||||||
None => "EMPTY",
|
|
||||||
};
|
};
|
||||||
let value_display = value_option
|
let value_display = value_option
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -325,27 +348,28 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
.unwrap_or_else(|| "EMPTY".to_string());
|
.unwrap_or_else(|| "EMPTY".to_string());
|
||||||
let constant_display = format!("{index:<5} {value_kind_display:<5} {value_display:<5}");
|
let constant_display = format!("{index:<5} {value_kind_display:<5} {value_display:<5}");
|
||||||
|
|
||||||
push_centered_line(&constant_display);
|
disassembled.push_str(¢er(&constant_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
push_centered_line(Self::local_header());
|
for line in Self::LOCAL_HEADER {
|
||||||
|
disassembled.push_str(&style(center(line)));
|
||||||
|
}
|
||||||
|
|
||||||
for (
|
for (
|
||||||
index,
|
index,
|
||||||
Local {
|
Local {
|
||||||
identifier,
|
identifier,
|
||||||
depth,
|
depth,
|
||||||
value,
|
value: value_option,
|
||||||
},
|
},
|
||||||
) in self.chunk.locals.iter().enumerate()
|
) in self.chunk.locals.iter().enumerate()
|
||||||
{
|
{
|
||||||
let value_kind_display = match value {
|
let value_kind_display = if let Some(value) = value_option {
|
||||||
Some(Value::Raw(_)) => "RAW",
|
value.kind().to_string()
|
||||||
Some(Value::Reference(_)) => "REF",
|
} else {
|
||||||
Some(Value::Mutable(_)) => "MUT",
|
"empty".to_string()
|
||||||
None => "EMPTY",
|
|
||||||
};
|
};
|
||||||
let value_display = value
|
let value_display = value_option
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "EMPTY".to_string());
|
.unwrap_or_else(|| "EMPTY".to_string());
|
||||||
@ -353,9 +377,11 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
let local_display =
|
let local_display =
|
||||||
format!("{index:<5} {identifier_display:<10} {depth:<5} {value_kind_display:<4} {value_display:<5}");
|
format!("{index:<5} {identifier_display:<10} {depth:<5} {value_kind_display:<4} {value_display:<5}");
|
||||||
|
|
||||||
push_centered_line(&local_display);
|
disassembled.push_str(¢er(&local_display));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("length: {}", disassembled.len());
|
||||||
|
|
||||||
disassembled
|
disassembled
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,31 +397,38 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_header(&self) -> String {
|
fn chunk_header(&self) -> [String; 3] {
|
||||||
let name_length = self.name.len();
|
[
|
||||||
|
self.name.to_string(),
|
||||||
format!("\n{}\n{}\n", self.name, "=".repeat(name_length))
|
"=".repeat(self.name.len()),
|
||||||
|
format!(
|
||||||
|
"{} instructions, {} constants, {} locals",
|
||||||
|
self.chunk.instructions.len(),
|
||||||
|
self.chunk.constants.len(),
|
||||||
|
self.chunk.locals.len()
|
||||||
|
),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instructions_header(&self) -> &'static str {
|
/// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
|
||||||
"\nInstructions\n\
|
/// buffer to avoid reallocations.
|
||||||
------------\n\
|
///
|
||||||
OFFSET OPERATION INFO POSITION\n\
|
/// The capacity is calculated as follows:
|
||||||
------- -------------- -------------------- --------\n"
|
/// - Get the number of static lines, i.e. lines that are always present in the disassembly
|
||||||
}
|
/// - Get the number of dynamic lines, i.e. lines that are generated from the chunk
|
||||||
|
/// - Add 1 to the width to account for the newline character
|
||||||
|
/// - Multiply the total number of lines by the width of the disassembly output
|
||||||
|
fn predict_capacity(&self) -> usize {
|
||||||
|
let chunk_header_line_count = 3; // self.chunk_header().len() is hard-coded to 3
|
||||||
|
let static_line_count = chunk_header_line_count
|
||||||
|
+ Self::INSTRUCTION_HEADER.len()
|
||||||
|
+ Self::CONSTANT_HEADER.len()
|
||||||
|
+ Self::LOCAL_HEADER.len();
|
||||||
|
let dynamic_line_count =
|
||||||
|
self.chunk.instructions.len() + self.chunk.constants.len() + self.chunk.locals.len();
|
||||||
|
let total_line_count = static_line_count + dynamic_line_count;
|
||||||
|
|
||||||
fn constant_header() -> &'static str {
|
total_line_count * (self.width + 1)
|
||||||
"\nConstants\n\
|
|
||||||
---------\n\
|
|
||||||
INDEX KIND VALUE\n\
|
|
||||||
----- ----- -----\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn local_header() -> &'static str {
|
|
||||||
"\nLocals\n\
|
|
||||||
------\n\
|
|
||||||
INDEX IDENTIFIER DEPTH KIND VALUE\n\
|
|
||||||
----- ---------- ----- ----- -----\n"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,6 +449,9 @@ pub enum ChunkError {
|
|||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
InstructionUnderflow {
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
LocalIndexOutOfBounds {
|
LocalIndexOutOfBounds {
|
||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -440,6 +476,7 @@ impl AnnotatedError for ChunkError {
|
|||||||
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
|
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
|
||||||
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
||||||
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
|
ChunkError::InstructionUnderflow { .. } => "Instruction underflow",
|
||||||
ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds",
|
ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds",
|
||||||
ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
|
ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
|
||||||
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
||||||
@ -455,6 +492,7 @@ impl AnnotatedError for ChunkError {
|
|||||||
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Constant index: {}", index))
|
Some(format!("Constant index: {}", index))
|
||||||
}
|
}
|
||||||
|
ChunkError::InstructionUnderflow { .. } => None,
|
||||||
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Identifier index: {}", index))
|
Some(format!("Identifier index: {}", index))
|
||||||
}
|
}
|
||||||
|
@ -255,47 +255,39 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.parse(rule.precedence.increment())?;
|
self.parse(rule.precedence.increment())?;
|
||||||
|
|
||||||
let previous_instruction = self.chunk.pop_instruction();
|
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
||||||
let right_register = match previous_instruction {
|
let right_register = match previous_instruction {
|
||||||
Some((
|
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::LoadConstant,
|
operation: Operation::LoadConstant,
|
||||||
arguments,
|
arguments,
|
||||||
..
|
..
|
||||||
},
|
} => {
|
||||||
_,
|
|
||||||
)) => {
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
|
|
||||||
arguments[0]
|
arguments[0]
|
||||||
}
|
}
|
||||||
Some((instruction, position)) => {
|
_ => {
|
||||||
self.chunk.push_instruction(instruction, position);
|
self.chunk.push_instruction(previous_instruction, position);
|
||||||
|
|
||||||
self.current_register - 1
|
self.current_register - 1
|
||||||
}
|
}
|
||||||
_ => self.current_register - 1,
|
|
||||||
};
|
};
|
||||||
let previous_instruction = self.chunk.pop_instruction();
|
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
||||||
let left_register = match previous_instruction {
|
let left_register = match previous_instruction {
|
||||||
Some((
|
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::LoadConstant,
|
operation: Operation::LoadConstant,
|
||||||
arguments,
|
arguments,
|
||||||
..
|
..
|
||||||
},
|
} => {
|
||||||
_,
|
|
||||||
)) => {
|
|
||||||
self.decrement_register()?;
|
self.decrement_register()?;
|
||||||
|
|
||||||
arguments[0]
|
arguments[0]
|
||||||
}
|
}
|
||||||
Some((instruction, position)) => {
|
_ => {
|
||||||
self.chunk.push_instruction(instruction, position);
|
self.chunk.push_instruction(previous_instruction, position);
|
||||||
|
|
||||||
self.current_register - 2
|
self.current_register - 2
|
||||||
}
|
}
|
||||||
_ => self.current_register - 2,
|
|
||||||
};
|
};
|
||||||
let instruction = match operator {
|
let instruction = match operator {
|
||||||
TokenKind::Plus => {
|
TokenKind::Plus => {
|
||||||
@ -449,17 +441,17 @@ impl<'src> Parser<'src> {
|
|||||||
self.expect(TokenKind::Equal)?;
|
self.expect(TokenKind::Equal)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let local_index = self.chunk.declare_local(identifier, position)?;
|
let local_index = self
|
||||||
let previous_instruction = self.chunk.pop_instruction();
|
.chunk
|
||||||
|
.declare_local(identifier, self.current_position)?;
|
||||||
|
let (previous_instruction, previous_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
|
||||||
if let Some((
|
if let Instruction {
|
||||||
Instruction {
|
|
||||||
operation: Operation::GetLocal,
|
operation: Operation::GetLocal,
|
||||||
destination,
|
destination,
|
||||||
arguments,
|
arguments,
|
||||||
},
|
} = previous_instruction
|
||||||
_,
|
|
||||||
)) = previous_instruction
|
|
||||||
{
|
{
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction {
|
Instruction {
|
||||||
@ -469,13 +461,9 @@ impl<'src> Parser<'src> {
|
|||||||
},
|
},
|
||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
} else if let Some((instruction, position)) = previous_instruction {
|
|
||||||
self.chunk.push_instruction(instruction, position);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedExpression {
|
self.chunk
|
||||||
found: self.previous_token.to_owned(),
|
.push_instruction(previous_instruction, previous_position);
|
||||||
position: self.previous_position,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
|
@ -1,4 +1,36 @@
|
|||||||
//! Dust value representation
|
//! Dust value representation
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Each type of value has a corresponding method for instantiation:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use dust_lang::Value;
|
||||||
|
//! let boolean = Value::boolean(true);
|
||||||
|
//! let float = Value::float(3.14);
|
||||||
|
//! let integer = Value::integer(42);
|
||||||
|
//! let string = Value::string("Hello, world!");
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Values can be combined into more complex values:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use dust_lang::Value;
|
||||||
|
//! let list = Value::list(vec![
|
||||||
|
//! Value::integer(1),
|
||||||
|
//! Value::integer(2),
|
||||||
|
//! Value::integer(3),
|
||||||
|
//! ]);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Values have a type, which can be retrieved using the `r#type` method:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use dust_lang::*;
|
||||||
|
//! let value = Value::integer(42);
|
||||||
|
//!
|
||||||
|
//! assert_eq!(value.r#type(), Type::Integer);
|
||||||
|
//! ```
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -17,35 +49,7 @@ use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type}
|
|||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
/// Each type of value has a corresponding constructor, here are some simple examples:
|
/// See the [module-level documentation][self] for more.
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use dust_lang::Value;
|
|
||||||
/// let boolean = Value::boolean(true);
|
|
||||||
/// let float = Value::float(3.14);
|
|
||||||
/// let integer = Value::integer(42);
|
|
||||||
/// let string = Value::string("Hello, world!");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Values can be combined into more complex values:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use dust_lang::Value;
|
|
||||||
/// let list = Value::list(vec![
|
|
||||||
/// Value::integer(1),
|
|
||||||
/// Value::integer(2),
|
|
||||||
/// Value::integer(3),
|
|
||||||
/// ]);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Values have a type, which can be retrieved using the `type` method:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use dust_lang::*;
|
|
||||||
/// let value = Value::integer(42);
|
|
||||||
///
|
|
||||||
/// assert_eq!(value.r#type(), Type::Integer);
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Raw(ValueData),
|
Raw(ValueData),
|
||||||
@ -114,14 +118,6 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_mutable(self) -> Self {
|
|
||||||
match self {
|
|
||||||
Value::Raw(data) => Value::Mutable(Arc::new(RwLock::new(data))),
|
|
||||||
Value::Reference(data) => Value::Mutable(Arc::new(RwLock::new(data.as_ref().clone()))),
|
|
||||||
Value::Mutable(_) => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_raw(self) -> Self {
|
pub fn into_raw(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Value::Raw(_) => self,
|
Value::Raw(_) => self,
|
||||||
@ -142,6 +138,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_mutable(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Value::Raw(data) => Value::Mutable(Arc::new(RwLock::new(data))),
|
||||||
|
Value::Reference(data) => Value::Mutable(Arc::new(RwLock::new(data.as_ref().clone()))),
|
||||||
|
Value::Mutable(_) => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clone_data(&self) -> ValueData {
|
pub fn clone_data(&self) -> ValueData {
|
||||||
match self {
|
match self {
|
||||||
Value::Raw(data) => data.clone(),
|
Value::Raw(data) => data.clone(),
|
||||||
@ -734,6 +738,14 @@ impl Value {
|
|||||||
.ok_or_else(|| ValueError::CannotOr(self.clone(), other.clone()))
|
.ok_or_else(|| ValueError::CannotOr(self.clone(), other.clone()))
|
||||||
.map(Value::Raw)
|
.map(Value::Raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> ValueKind {
|
||||||
|
match self {
|
||||||
|
Value::Raw(_) => ValueKind::Raw,
|
||||||
|
Value::Reference(_) => ValueKind::Reference,
|
||||||
|
Value::Mutable(_) => ValueKind::Mutable,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
@ -928,6 +940,23 @@ impl<'de> Deserialize<'de> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ValueKind {
|
||||||
|
Raw,
|
||||||
|
Reference,
|
||||||
|
Mutable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ValueKind {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ValueKind::Raw => write!(f, "raw"),
|
||||||
|
ValueKind::Reference => write!(f, "reference"),
|
||||||
|
ValueKind::Mutable => write!(f, "mutable"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ValueData {
|
pub enum ValueData {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
@ -60,14 +60,7 @@ fn main() {
|
|||||||
|
|
||||||
fn parse_and_display_errors(source: &str) {
|
fn parse_and_display_errors(source: &str) {
|
||||||
match parse(source) {
|
match parse(source) {
|
||||||
Ok(chunk) => println!(
|
Ok(chunk) => println!("{}", chunk.disassembler("Dust CLI Input").disassemble()),
|
||||||
"{}",
|
|
||||||
chunk
|
|
||||||
.disassembler("Dust CLI Input")
|
|
||||||
.styled()
|
|
||||||
.width(80)
|
|
||||||
.disassemble()
|
|
||||||
),
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("{}", error.report());
|
eprintln!("{}", error.report());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user