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