Extend CLI to cover more formatting options; Extend formatting
This commit is contained in:
parent
0c758c9768
commit
44659ec34a
@ -273,18 +273,35 @@ impl Display for Chunk {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.disassembler("Chunk").styled(true).disassemble()
|
||||
self.disassembler("Dust Program").styled(true).disassemble()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Chunk {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.disassembler("Chunk").styled(false).disassemble()
|
||||
)
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
write!(
|
||||
f,
|
||||
"\n{}\n",
|
||||
self.disassembler(&format!("Dust Program 0x{timestamp:x}"))
|
||||
.styled(false)
|
||||
.disassemble()
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.disassembler(&format!("Dust Program 0x{timestamp:x}"))
|
||||
.styled(false)
|
||||
.disassemble()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,12 +406,6 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn indent(&mut self, indent: usize) -> &mut Self {
|
||||
self.indent = indent;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disassemble(&self) -> String {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn push(
|
||||
@ -624,15 +635,13 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
|
||||
if let Some(function_disassembly) =
|
||||
value_option.as_ref().and_then(|value| match value {
|
||||
Value::Function(function) => Some(
|
||||
function
|
||||
.chunk()
|
||||
.disassembler("function")
|
||||
.styled(self.styled)
|
||||
.width(self.width)
|
||||
.indent(self.indent + 1)
|
||||
.disassemble(),
|
||||
),
|
||||
Value::Function(function) => Some({
|
||||
let mut disassembler = function.chunk().disassembler("function");
|
||||
disassembler.indent = self.indent + 1;
|
||||
|
||||
disassembler.styled(self.styled);
|
||||
disassembler.disassemble()
|
||||
}),
|
||||
Value::Primitive(_) => None,
|
||||
Value::Object(_) => None,
|
||||
})
|
||||
|
@ -1,55 +1,81 @@
|
||||
use std::mem::replace;
|
||||
|
||||
use colored::{ColoredString, Colorize, CustomColor};
|
||||
|
||||
use crate::{lex, Token};
|
||||
use crate::{DustError, LexError, Lexer, Token};
|
||||
|
||||
pub fn format(source: &str, line_numbers: bool, colored: bool) -> Result<String, DustError> {
|
||||
let lexer = Lexer::new(source);
|
||||
let formatted = Formatter::new(lexer)
|
||||
.line_numbers(line_numbers)
|
||||
.colored(colored)
|
||||
.format()
|
||||
.map_err(|error| DustError::Lex { error, source })?;
|
||||
|
||||
Ok(formatted)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Formatter<'src> {
|
||||
source: &'src str,
|
||||
lines: Vec<(String, LineKind, usize)>,
|
||||
lexer: Lexer<'src>,
|
||||
output_lines: Vec<(String, LineKind, usize)>,
|
||||
next_line: String,
|
||||
indent: usize,
|
||||
|
||||
current_token: Token<'src>,
|
||||
previous_token: Token<'src>,
|
||||
|
||||
// Options
|
||||
line_numbers: bool,
|
||||
colored: bool,
|
||||
}
|
||||
|
||||
impl<'src> Formatter<'src> {
|
||||
pub fn new(source: &'src str) -> Self {
|
||||
pub fn new(mut lexer: Lexer<'src>) -> Self {
|
||||
let (current_token, _) = lexer.next_token().unwrap();
|
||||
|
||||
Self {
|
||||
source,
|
||||
lines: Vec::new(),
|
||||
lexer,
|
||||
output_lines: Vec::new(),
|
||||
next_line: String::new(),
|
||||
indent: 0,
|
||||
current_token,
|
||||
previous_token: Token::Eof,
|
||||
line_numbers: false,
|
||||
colored: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn footer(&mut self, footer: &'src str) -> &mut Self {
|
||||
self.source = footer;
|
||||
pub fn line_numbers(mut self, line_numbers: bool) -> Self {
|
||||
self.line_numbers = line_numbers;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn format(&mut self) -> String {
|
||||
let tokens = match lex(self.source) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(error) => return format!("{}", error),
|
||||
};
|
||||
pub fn colored(mut self, colored: bool) -> Self {
|
||||
self.colored = colored;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn format(&mut self) -> Result<String, LexError> {
|
||||
let mut line_kind = LineKind::Empty;
|
||||
|
||||
for (token, _) in tokens {
|
||||
self.advance()?;
|
||||
|
||||
while self.current_token != Token::Eof {
|
||||
use Token::*;
|
||||
|
||||
match token {
|
||||
if self.current_token.is_expression() && line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
|
||||
match self.current_token {
|
||||
Boolean(boolean) => {
|
||||
self.push_colored(boolean.red());
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
Byte(byte) => {
|
||||
self.push_colored(byte.green());
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
Character(character) => {
|
||||
self.push_colored(
|
||||
@ -57,49 +83,29 @@ impl<'src> Formatter<'src> {
|
||||
.to_string()
|
||||
.custom_color(CustomColor::new(225, 150, 150)),
|
||||
);
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
Float(float) => {
|
||||
self.push_colored(float.yellow());
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
Identifier(identifier) => {
|
||||
self.push_colored(identifier.blue());
|
||||
self.next_line.push(' ');
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
Integer(integer) => {
|
||||
self.push_colored(integer.cyan());
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
String(string) => {
|
||||
self.push_colored(string.magenta());
|
||||
|
||||
if line_kind != LineKind::Assignment {
|
||||
line_kind = LineKind::Expression;
|
||||
}
|
||||
}
|
||||
LeftCurlyBrace => {
|
||||
self.next_line.push_str(token.as_str());
|
||||
self.next_line.push_str(self.current_token.as_str());
|
||||
self.commit_line(LineKind::OpenBlock);
|
||||
|
||||
self.indent += 1;
|
||||
}
|
||||
RightCurlyBrace => {
|
||||
self.commit_line(LineKind::CloseBlock);
|
||||
self.next_line.push_str(token.as_str());
|
||||
self.next_line.push_str(self.current_token.as_str());
|
||||
|
||||
self.indent -= 1;
|
||||
}
|
||||
@ -108,22 +114,21 @@ impl<'src> Formatter<'src> {
|
||||
line_kind = LineKind::Statement;
|
||||
}
|
||||
|
||||
self.next_line.push_str(token.as_str());
|
||||
self.next_line.push_str(self.current_token.as_str());
|
||||
self.commit_line(line_kind);
|
||||
}
|
||||
Let => {
|
||||
line_kind = LineKind::Assignment;
|
||||
|
||||
self.push_colored(token.as_str().bold());
|
||||
self.push_colored(self.current_token.as_str().bold());
|
||||
self.next_line.push(' ');
|
||||
}
|
||||
Break | Loop | Return | While => {
|
||||
line_kind = LineKind::Statement;
|
||||
|
||||
self.push_colored(token.as_str().bold());
|
||||
self.push_colored(self.current_token.as_str().bold());
|
||||
self.next_line.push(' ');
|
||||
}
|
||||
Eof => continue,
|
||||
token => {
|
||||
self.next_line.push_str(token.as_str());
|
||||
self.next_line.push(' ');
|
||||
@ -134,11 +139,9 @@ impl<'src> Formatter<'src> {
|
||||
let mut previous_index = 0;
|
||||
let mut current_index = 1;
|
||||
|
||||
while current_index < self.lines.len() {
|
||||
let (_, previous, _) = &self.lines[previous_index];
|
||||
let (_, current, _) = &self.lines[current_index];
|
||||
|
||||
println!("{:?} {:?}", previous, current);
|
||||
while current_index < self.output_lines.len() {
|
||||
let (_, previous, _) = &self.output_lines[previous_index];
|
||||
let (_, current, _) = &self.output_lines[current_index];
|
||||
|
||||
match (previous, current) {
|
||||
(LineKind::Empty, _)
|
||||
@ -147,7 +150,7 @@ impl<'src> Formatter<'src> {
|
||||
| (_, LineKind::CloseBlock) => {}
|
||||
(left, right) if left == right => {}
|
||||
_ => {
|
||||
self.lines
|
||||
self.output_lines
|
||||
.insert(current_index, ("".to_string(), LineKind::Empty, 0));
|
||||
}
|
||||
}
|
||||
@ -157,22 +160,42 @@ impl<'src> Formatter<'src> {
|
||||
}
|
||||
|
||||
let formatted = String::with_capacity(
|
||||
self.lines
|
||||
self.output_lines
|
||||
.iter()
|
||||
.fold(0, |total, (line, _, _)| total + line.len()),
|
||||
);
|
||||
|
||||
self.lines
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(formatted, |acc, (index, (line, _, indent))| {
|
||||
Ok(self.output_lines.iter().enumerate().fold(
|
||||
formatted,
|
||||
|acc, (index, (line, _, indent))| {
|
||||
let index = if index == 0 {
|
||||
format!("{:<3}| ", index + 1).dimmed()
|
||||
} else {
|
||||
format!("\n{:<3}| ", index + 1).dimmed()
|
||||
};
|
||||
let left_pad = " ".repeat(*indent);
|
||||
|
||||
if index == 0 {
|
||||
return format!("{:<3}| {}{}", index + 1, left_pad, line);
|
||||
}
|
||||
format!("{}\n{:<3}| {}{}", acc, index + 1, left_pad, line)
|
||||
})
|
||||
format!("{}{}{}{}", acc, index, left_pad, line)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<(), LexError> {
|
||||
if self.lexer.is_eof() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (new_token, position) = self.lexer.next_token()?;
|
||||
|
||||
log::info!(
|
||||
"Parsing {} at {}",
|
||||
new_token.to_string().bold(),
|
||||
position.to_string()
|
||||
);
|
||||
|
||||
self.previous_token = replace(&mut self.current_token, new_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_colored(&mut self, colored: ColoredString) {
|
||||
@ -180,7 +203,7 @@ impl<'src> Formatter<'src> {
|
||||
}
|
||||
|
||||
fn commit_line(&mut self, line_kind: LineKind) {
|
||||
self.lines
|
||||
self.output_lines
|
||||
.push((self.next_line.clone(), line_kind, self.indent));
|
||||
self.next_line.clear();
|
||||
}
|
||||
@ -188,11 +211,12 @@ impl<'src> Formatter<'src> {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum LineKind {
|
||||
Assignment,
|
||||
FunctionCall,
|
||||
Statement,
|
||||
Expression,
|
||||
Empty,
|
||||
Assignment,
|
||||
Expression,
|
||||
Statement,
|
||||
OpenBlock,
|
||||
CloseBlock,
|
||||
Call,
|
||||
Primary,
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{dust_error::AnnotatedError, Span, Token};
|
||||
use crate::{dust_error::AnnotatedError, DustError, Span, Token};
|
||||
|
||||
/// Lexes the input and return a vector of tokens and their positions.
|
||||
///
|
||||
@ -30,21 +30,19 @@ use crate::{dust_error::AnnotatedError, Span, Token};
|
||||
/// ]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn lex<'chars, 'src: 'chars>(
|
||||
pub fn lex<'tokens, 'src: 'tokens>(
|
||||
source: &'src str,
|
||||
) -> Result<Vec<(Token<'chars>, Span)>, LexError> {
|
||||
) -> Result<Vec<(Token<'src>, Span)>, DustError> {
|
||||
let mut lexer = Lexer::new(source);
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
loop {
|
||||
let (token, span) = lexer.next_token()?;
|
||||
let is_eof = matches!(token, Token::Eof);
|
||||
while !lexer.is_eof() {
|
||||
let (token, span) = lexer
|
||||
.next_token()
|
||||
.map_err(|error| DustError::Lex { error, source })?;
|
||||
let length = tokens.len();
|
||||
|
||||
tokens.push((token, span));
|
||||
|
||||
if is_eof {
|
||||
break;
|
||||
}
|
||||
tokens[length] = (token, span);
|
||||
}
|
||||
|
||||
Ok(tokens)
|
||||
@ -101,6 +99,10 @@ impl<'src> Lexer<'src> {
|
||||
self.source
|
||||
}
|
||||
|
||||
pub fn is_eof(&self) -> bool {
|
||||
self.position >= self.source.len()
|
||||
}
|
||||
|
||||
pub fn skip_to(&mut self, position: usize) {
|
||||
self.position = position;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use std::fmt::Display;
|
||||
|
||||
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
|
||||
pub use dust_error::{AnnotatedError, DustError};
|
||||
pub use formatter::Formatter;
|
||||
pub use formatter::{format, Formatter};
|
||||
pub use identifier::Identifier;
|
||||
pub use instruction::Instruction;
|
||||
pub use lexer::{lex, LexError, Lexer};
|
||||
|
@ -654,6 +654,10 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
|
||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||
let to_register = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
.register_index;
|
||||
|
||||
if self.allow(Token::Equal)? {
|
||||
if !allowed.assignment {
|
||||
@ -699,11 +703,12 @@ impl<'src> Parser<'src> {
|
||||
Instruction::set_local(self.current_register, local_index),
|
||||
start_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
|
||||
self.parsed_expression = false;
|
||||
} else {
|
||||
self.emit_instruction(
|
||||
Instruction::get_local(self.current_register, local_index),
|
||||
Instruction::get_local(to_register, local_index),
|
||||
self.previous_position,
|
||||
);
|
||||
|
||||
@ -792,7 +797,7 @@ impl<'src> Parser<'src> {
|
||||
self.allow(Token::Comma)?;
|
||||
}
|
||||
|
||||
let end_register = self.current_register - 1;
|
||||
let end_register = self.current_register.saturating_sub(1);
|
||||
let end = self.current_position.1;
|
||||
|
||||
self.emit_instruction(
|
||||
|
@ -4,9 +4,10 @@ use std::fmt::{self, Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Source code token.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Default, Serialize, Deserialize)]
|
||||
pub enum Token<'src> {
|
||||
// End of file
|
||||
#[default]
|
||||
Eof,
|
||||
|
||||
// Hard-coded values
|
||||
|
@ -2,7 +2,7 @@ use std::{cmp::Ordering, mem::replace};
|
||||
|
||||
use crate::{
|
||||
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction,
|
||||
Operation, Span, Value, ValueError,
|
||||
Operation, Span, Type, Value, ValueError,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
@ -111,7 +111,13 @@ impl Vm {
|
||||
let to_register = instruction.a();
|
||||
let first_register = instruction.b();
|
||||
let last_register = instruction.c();
|
||||
let item_type = self.get(first_register, position)?.r#type();
|
||||
|
||||
let is_empty = to_register == first_register && first_register == last_register;
|
||||
let item_type = if is_empty {
|
||||
Type::Any
|
||||
} else {
|
||||
self.get(first_register, position)?.r#type()
|
||||
};
|
||||
let value = Value::list(first_register, last_register, item_type);
|
||||
|
||||
self.set(to_register, value, position)?;
|
||||
@ -685,7 +691,7 @@ impl AnnotatedError for VmError {
|
||||
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
||||
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
||||
Self::RegisterIndexOutOfBounds { index, .. } => {
|
||||
Some(format!("R{index} does not exist at this time"))
|
||||
Some(format!("Register {index} does not exist"))
|
||||
}
|
||||
Self::UndefinedVariable { identifier, .. } => {
|
||||
Some(format!("{identifier} is not in scope"))
|
||||
|
@ -212,6 +212,25 @@ fn empty() {
|
||||
assert_eq!(run(source), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_list() {
|
||||
let source = "[]";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_list(0, 0, 0), Span(0, 2)),
|
||||
(Instruction::r#return(true), Span(2, 2)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let source = "1 == 2";
|
||||
|
@ -2,26 +2,40 @@ use std::{fs::read_to_string, io::Write};
|
||||
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dust_lang::{parse, run, Formatter};
|
||||
use dust_lang::{format, parse, run, Chunk, DustError, Vm};
|
||||
use log::Level;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
/// Source code send via command line
|
||||
#[arg(short, long)]
|
||||
command: Option<String>,
|
||||
|
||||
/// Whether to output formatted source code
|
||||
#[arg(short, long)]
|
||||
format: bool,
|
||||
|
||||
/// Whether to output line numbers in formatted source code
|
||||
#[arg(short = 'l', long)]
|
||||
format_line_numbers: bool,
|
||||
|
||||
/// Whether to output colors in formatted source code
|
||||
#[arg(short = 'o', long)]
|
||||
format_colored: bool,
|
||||
|
||||
/// Whether to run the source code
|
||||
#[arg(short, long)]
|
||||
no_run: bool,
|
||||
|
||||
/// Whether to output the disassembled chunk
|
||||
#[arg(short, long)]
|
||||
parse: bool,
|
||||
|
||||
/// Whether to style the disassembled chunk
|
||||
#[arg(short, long)]
|
||||
styled: bool,
|
||||
style_disassembly: bool,
|
||||
|
||||
/// Path to a source code file
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
@ -48,55 +62,83 @@ fn main() {
|
||||
.init();
|
||||
|
||||
let args = Cli::parse();
|
||||
|
||||
let source = if let Some(path) = &args.path {
|
||||
&read_to_string(path).expect("Failed to read file")
|
||||
} else if let Some(command) = &args.command {
|
||||
command
|
||||
} else {
|
||||
eprintln!("No input provided");
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
if args.parse {
|
||||
parse_source(source, args.styled);
|
||||
if !args.no_run {
|
||||
if args.format {
|
||||
format_source(source, args.format_line_numbers, args.format_colored);
|
||||
}
|
||||
|
||||
let run_result = if args.parse {
|
||||
let chunk = parse(source).unwrap();
|
||||
let disassembly = chunk
|
||||
.disassembler("Dust CLI Input")
|
||||
.source(source)
|
||||
.styled(args.style_disassembly)
|
||||
.disassemble();
|
||||
|
||||
println!("{}", disassembly);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
|
||||
vm.run()
|
||||
.map_err(|error| DustError::Runtime { error, source })
|
||||
} else {
|
||||
run(source)
|
||||
};
|
||||
|
||||
match run_result {
|
||||
Ok(Some(value)) => println!("{}", value),
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
eprintln!("{}", error.report());
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if args.format {
|
||||
format_source(source);
|
||||
format_source(source, args.format_line_numbers, args.format_colored);
|
||||
}
|
||||
|
||||
if !args.no_run {
|
||||
run_source(source);
|
||||
if args.parse {
|
||||
parse_source(source, args.style_disassembly);
|
||||
}
|
||||
}
|
||||
|
||||
fn format_source(source: &str) {
|
||||
println!("{}", Formatter::new(source).format())
|
||||
pub fn format_source(source: &str, line_numbers: bool, colored: bool) {
|
||||
log::info!("Formatting source");
|
||||
|
||||
match format(source, line_numbers, colored) {
|
||||
Ok(formatted) => println!("{}", formatted),
|
||||
Err(error) => {
|
||||
eprintln!("{}", error.report());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_source(source: &str, styled: bool) {
|
||||
match parse(source) {
|
||||
Ok(chunk) => println!(
|
||||
"{}",
|
||||
chunk
|
||||
fn parse_source(source: &str, styled: bool) -> Option<Chunk> {
|
||||
parse(source)
|
||||
.inspect(|chunk| {
|
||||
let disassembly = chunk
|
||||
.disassembler("Dust CLI Input")
|
||||
.source(source)
|
||||
.styled(styled)
|
||||
.disassemble()
|
||||
),
|
||||
Err(error) => {
|
||||
eprintln!("{}", error.report());
|
||||
}
|
||||
}
|
||||
}
|
||||
.disassemble();
|
||||
|
||||
fn run_source(source: &str) {
|
||||
match run(source) {
|
||||
Ok(Some(value)) => println!("{}", value),
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
println!("{disassembly}",);
|
||||
})
|
||||
.inspect_err(|error| {
|
||||
eprintln!("{}", error.report());
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user