1
0
This commit is contained in:
Jeff 2024-12-21 13:20:57 -05:00
parent 2df86c58bf
commit 71a68c54e4
12 changed files with 230 additions and 152 deletions

139
Cargo.lock generated
View File

@ -322,10 +322,10 @@ dependencies = [
"clap 4.5.20",
"color-print",
"dust-lang",
"env_logger",
"log",
"postcard",
"serde_json",
"tracing",
"tracing-subscriber",
]
[[package]]
@ -336,12 +336,12 @@ dependencies = [
"colored",
"criterion",
"getrandom",
"log",
"rand",
"serde",
"serde_json",
"smallvec",
"smartstring",
"tracing",
]
[[package]]
@ -350,29 +350,6 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
name = "errno"
version = "0.3.10"
@ -440,12 +417,6 @@ dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@ -532,6 +503,16 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -553,6 +534,18 @@ version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pin-project-lite"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "plotters"
version = "0.3.7"
@ -789,6 +782,15 @@ dependencies = [
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.13.2"
@ -867,6 +869,16 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
@ -877,6 +889,63 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
@ -895,6 +964,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.5"

View File

@ -22,7 +22,7 @@ clap = { version = "4.5.14", features = [
] }
color-print = "0.3.7"
dust-lang = { path = "../dust-lang" }
env_logger = "0.11.5"
log = "0.4.22"
postcard = "1.0.10"
serde_json = "1.0.133"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"

View File

@ -1,16 +1,22 @@
use std::io::{self, stdout, Read, Write};
use std::fmt::Write;
use std::io::{self, stdout, Read};
use std::time::{Duration, Instant};
use std::{fs::read_to_string, path::PathBuf};
use clap::builder::StyledStr;
use clap::Args;
use clap::error::ErrorKind;
use clap::{
builder::{styling::AnsiColor, Styles},
crate_authors, crate_description, crate_version, ColorChoice, Parser, Subcommand, ValueHint,
};
use clap::{Args, Error};
use color_print::cstr;
use dust_lang::{CompileError, Compiler, DustError, DustString, Lexer, Span, Token, Vm};
use log::{Level, LevelFilter};
use tracing::subscriber::set_global_default;
use tracing::{span, warn, Level};
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, MakeWriter};
use tracing_subscriber::FmtSubscriber;
const CLI_HELP_TEMPLATE: &str = cstr!(
r#"
@ -18,10 +24,10 @@ const CLI_HELP_TEMPLATE: &str = cstr!(
</bold>
{about}</bright-magenta>
Version: {version}
Author: {author}
License: GPL-3.0
🌐 Repository: git.jeffa.io/jeff/dust
{tab} Version: {version}
{tab} Author: {author}
{tab} License: GPL-3.0
{tab}🌐 Repository: git.jeffa.io/jeff/dust
<bright-magenta,bold>Usage
</bright-magenta,bold>
@ -43,10 +49,10 @@ const MODE_HELP_TEMPLATE: &str = cstr!(
</bold>
{about}</bright-magenta>
Version: {version}
Author: {author}
License: GPL-3.0
🌐 Repository: git.jeffa.io/jeff/dust
{tab} Version: {version}
{tab} Author: {author}
{tab} License: GPL-3.0
{tab}🌐 Repository: git.jeffa.io/jeff/dust
<bright-magenta,bold>Usage
</bright-magenta,bold>
@ -78,11 +84,25 @@ const STYLES: Styles = Styles::styled()
)]
struct Cli {
/// Overrides the DUST_LOG environment variable
#[arg(short, long, value_name = "LOG_LEVEL")]
log: Option<LevelFilter>,
#[arg(
short,
long,
value_parser = |input: &str| match input.to_uppercase().as_str() {
"TRACE" => Ok(Level::TRACE),
"DEBUG" => Ok(Level::DEBUG),
"INFO" => Ok(Level::INFO),
"WARN" => Ok(Level::WARN),
"ERROR" => Ok(Level::ERROR),
_ => Err(Error::new(ErrorKind::ValueValidation)),
}
)]
log_level: Option<Level>,
#[command(subcommand)]
mode: Mode,
mode: Option<Mode>,
#[command(flatten)]
run: Run,
}
#[derive(Args)]
@ -100,30 +120,33 @@ struct Input {
file: Option<PathBuf>,
}
/// Compile and run the program (default)
#[derive(Args)]
#[command(
short_flag = 'r',
help_template = MODE_HELP_TEMPLATE
)]
struct Run {
/// Print the time taken for compilation and execution
#[arg(long)]
time: bool,
/// Do not print the program's return value
#[arg(long)]
no_output: bool,
/// Custom program name, overrides the file name
#[arg(long)]
name: Option<DustString>,
#[command(flatten)]
input: Input,
}
#[derive(Subcommand)]
#[clap(subcommand_value_name = "MODE", flatten_help = true)]
enum Mode {
/// Compile and run the program (default)
#[command(
short_flag = 'r',
help_template = MODE_HELP_TEMPLATE
)]
Run {
/// Print the time taken for compilation and execution
#[arg(long)]
time: bool,
/// Do not print the program's return value
#[arg(long)]
no_output: bool,
/// Custom program name, overrides the file name
#[arg(long)]
name: Option<DustString>,
#[command(flatten)]
input: Input,
},
Run(Run),
/// Compile and print the bytecode disassembly
#[command(
@ -185,29 +208,18 @@ fn get_source_and_file_name(input: Input) -> (String, Option<DustString>) {
fn main() {
let start_time = Instant::now();
let mut logger = env_logger::builder();
let Cli {
log_level,
mode,
run,
} = Cli::parse();
let mode = mode.unwrap_or(Mode::Run(run));
let subscriber = FmtSubscriber::builder()
.with_max_level(log_level)
.with_thread_names(true)
.finish();
logger.format(move |buf, record| {
let elapsed = format!("T+{:.04}", start_time.elapsed().as_secs_f32());
let level_display = match record.level() {
Level::Info => cstr!("<bright-magenta,bold>INFO<bright-magenta,bold>"),
Level::Trace => cstr!("<bright-cyan,bold>TRACE<bright-cyan,bold>"),
Level::Debug => cstr!("<bright-blue,bold>DEBUG<bright-blue,bold>"),
Level::Warn => cstr!("<bright-yellow,bold>WARN<bright-yellow,bold>"),
Level::Error => cstr!("<bright-red,bold>ERROR<bright-red,bold>"),
};
let display = format!("[{elapsed}] {level_display:5} {args}", args = record.args());
writeln!(buf, "{display}")
});
let Cli { log, mode } = Cli::parse();
if let Some(level) = log {
logger.filter_level(level).init();
} else {
logger.parse_env("DUST_LOG").init();
}
set_global_default(subscriber).expect("Failed to set tracing subscriber");
if let Mode::Disassemble { style, name, input } = mode {
let (source, file_name) = get_source_and_file_name(input);
@ -235,9 +247,9 @@ fn main() {
chunk
.disassembler(&mut stdout)
.width(65)
.style(style)
.source(&source)
.width(65)
.disassemble()
.expect("Failed to write disassembly to stdout");
@ -290,13 +302,16 @@ fn main() {
return;
}
if let Mode::Run {
name,
if let Mode::Run(Run {
time,
no_output,
name,
input,
} = mode
}) = mode
{
let run_span = span!(Level::TRACE, "CLI Run Mode");
let _run_guard = run_span.enter();
let (source, file_name) = get_source_and_file_name(input);
let lexer = Lexer::new(&source);
let mut compiler = match Compiler::new(lexer) {

View File

@ -11,7 +11,6 @@ version.workspace = true
[dependencies]
annotate-snippets = "0.11.4"
colored = "2.1.0"
log = "0.4.22"
rand = "0.8.5"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
@ -22,6 +21,7 @@ smallvec = { version = "1.13.2", features = ["const_generics", "serde"] }
smartstring = { version = "1.0.1", features = [
"serde",
], default-features = false }
tracing = "0.1.41"
[dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -21,7 +21,7 @@
//! │ │
//! │ write_line("Hello world!") │
//! │ │
//! │ 3 instructions, 1 constants, 0 locals, returns str
//! │ 3 instructions, 1 constants, 0 locals, returns none
//! │ │
//! │ Instructions │
//! │ ╭─────┬────────────┬─────────────────┬────────────────────────╮ │
@ -39,10 +39,7 @@
//! │ ╰─────┴──────────────────────────┴──────────────────────────╯ │
//! ╰─────────────────────────────────────────────────────────────────╯
//! ```
use std::{
env::current_exe,
io::{self, Write},
};
use std::io::{self, Write};
use colored::{ColoredString, Colorize};
@ -397,26 +394,9 @@ impl<'a, W: Write> Disassembler<'a, W> {
pub fn disassemble(&mut self) -> Result<(), io::Error> {
self.write_page_border(TOP_BORDER)?;
let name_display = self
.chunk
.name()
.map(|identifier| identifier.to_string())
.unwrap_or_else(|| {
current_exe()
.map(|path| {
let path_string = path.to_string_lossy();
let file_name = path_string
.split('/')
.last()
.map(|slice| slice.to_string())
.unwrap_or(path_string.to_string());
file_name
})
.unwrap_or("Dust Chunk Disassembly".to_string())
});
self.write_center_border_bold(&name_display)?;
if let Some(name) = &self.chunk.name {
self.write_center_border_bold(name)?;
}
if self.show_type {
let type_display = self.chunk.r#type.to_string();

View File

@ -211,9 +211,6 @@ impl Eq for Chunk {}
impl PartialEq for Chunk {
fn eq(&self, other: &Self) -> bool {
// Do not compare stack size because the chunks created for testing will not have one due to
// not being compiled.
self.name == other.name
&& self.r#type == other.r#type
&& self.instructions == other.instructions

View File

@ -25,11 +25,11 @@ mod type_checks;
pub use error::CompileError;
use parse_rule::{ParseRule, Precedence};
use tracing::{debug, info};
use type_checks::{check_math_type, check_math_types};
use std::mem::replace;
use colored::Colorize;
use optimize::control_flow_register_consolidation;
use smallvec::{smallvec, SmallVec};
@ -58,8 +58,7 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
.compile()
.map_err(|error| DustError::compile(error, source))?;
let name = DustString::from("main");
let chunk = compiler.finish(Some(name));
let chunk = compiler.finish(None::<DustString>);
Ok(chunk)
}
@ -136,9 +135,9 @@ impl<'src> Compiler<'src> {
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> {
let (current_token, current_position) = lexer.next_token()?;
log::info!(
info!(
"Begin chunk with {} at {}",
current_token.to_string().bold(),
current_token.to_string(),
current_position.to_string()
);
@ -173,7 +172,7 @@ impl<'src> Compiler<'src> {
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(self, name: Option<impl Into<DustString>>) -> Chunk {
log::info!("End chunk");
info!("End chunk");
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
.instructions
@ -210,6 +209,10 @@ impl<'src> Compiler<'src> {
self.parse(Precedence::None)?;
if matches!(self.current_token, Token::Eof | Token::RightBrace) {
if self.get_last_operation() == Some(Operation::RETURN) {
break;
}
self.parse_implicit_return()?;
break;
@ -244,9 +247,9 @@ impl<'src> Compiler<'src> {
let (new_token, position) = self.lexer.next_token()?;
log::info!(
info!(
"Parsing {} at {}",
new_token.to_string().bold(),
new_token.to_string(),
position.to_string()
);
@ -299,7 +302,7 @@ impl<'src> Compiler<'src> {
is_mutable: bool,
scope: Scope,
) -> (u8, u8) {
log::info!("Declaring local {identifier}");
info!("Declaring local {identifier}");
let identifier = Value::Concrete(ConcreteValue::string(identifier));
let identifier_index = self.push_or_get_constant(identifier);
@ -447,9 +450,9 @@ impl<'src> Compiler<'src> {
}
fn emit_instruction(&mut self, instruction: Instruction, r#type: Type, position: Span) {
log::debug!(
debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
instruction.operation().to_string(),
position.to_string()
);
@ -965,11 +968,11 @@ impl<'src> Compiler<'src> {
continue;
}
let jump = &mut instructions[1].0;
let old_jump = &mut instructions[1].0;
let jump_index = instructions_length - group_index * 3 - 1;
let short_circuit_distance = (instructions_length - jump_index) as u8;
*jump = Instruction::jump(short_circuit_distance, true);
*old_jump = Instruction::jump(short_circuit_distance, true);
}
Ok(())
@ -1681,9 +1684,9 @@ impl<'src> Compiler<'src> {
fn parse(&mut self, precedence: Precedence) -> Result<(), CompileError> {
if let Some(prefix_parser) = ParseRule::from(&self.current_token).prefix {
log::debug!(
debug!(
"{} is prefix with precedence {precedence}",
self.current_token.to_string().bold(),
self.current_token.to_string(),
);
prefix_parser(self)?;
@ -1693,9 +1696,9 @@ impl<'src> Compiler<'src> {
while precedence <= infix_rule.precedence {
if let Some(infix_parser) = infix_rule.infix {
log::debug!(
debug!(
"{} is infix with precedence {precedence}",
self.current_token.to_string().bold(),
self.current_token.to_string(),
);
if self.current_token == Token::Equal {

View File

@ -1,5 +1,7 @@
//! Functions used by the compiler to optimize a chunk's bytecode during compilation.
use tracing::debug;
use crate::{Compiler, Instruction, Operation};
/// Optimizes a control flow pattern to use fewer registers and avoid using a `POINT` instruction.
@ -44,7 +46,7 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
return;
}
log::debug!("Consolidating registers for control flow optimization");
debug!("Consolidating registers for control flow optimization");
let first_loader_index = compiler.instructions.len() - 2;
let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();

View File

@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use smartstring::{LazyCompact, SmartString};
use tracing::trace;
use crate::{Type, Value, ValueError};
@ -281,7 +282,7 @@ impl ConcreteValue {
impl Clone for ConcreteValue {
fn clone(&self) -> Self {
log::trace!("Cloning concrete value {}", self);
trace!("Cloning concrete value {}", self);
match self {
ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean),

View File

@ -1,6 +1,7 @@
use std::mem::replace;
use smallvec::SmallVec;
use tracing::trace;
use crate::{DustString, Function, FunctionType, Local, Span, Value};
@ -79,7 +80,7 @@ impl Record {
}
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
log::trace!("Follow pointer {pointer}");
trace!("Follow pointer {pointer}");
match pointer {
Pointer::Stack(register_index) => self.open_register(register_index),
@ -88,7 +89,7 @@ impl Record {
}
pub fn get_register(&self, register_index: u8) -> &Register {
log::trace!("Get register R{register_index}");
trace!("Get register R{register_index}");
let register_index = register_index as usize;
@ -113,7 +114,7 @@ impl Record {
}
pub fn open_register(&self, register_index: u8) -> &Value {
log::trace!("Open register R{register_index}");
trace!("Open register R{register_index}");
let register_index = register_index as usize;
@ -132,7 +133,7 @@ impl Record {
}
pub fn open_register_allow_empty(&self, register_index: u8) -> Option<&Value> {
log::trace!("Open register R{register_index}");
trace!("Open register R{register_index}");
let register_index = register_index as usize;

View File

@ -1,3 +1,5 @@
use tracing::trace;
use crate::{
instruction::{
Call, CallNative, Close, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Point,
@ -119,7 +121,7 @@ pub fn load_constant(instruction_data: InstructionData, record: &mut Record) ->
} = instruction_data.into();
let register = Register::Pointer(Pointer::Constant(constant_index));
log::trace!("Load constant {constant_index} into R{destination}");
trace!("Load constant {constant_index} into R{destination}");
record.set_register(destination, register);

View File

@ -1,3 +1,5 @@
use tracing::{info, trace};
use crate::{
vm::{FunctionCall, Register},
Chunk, DustString, Value,
@ -26,7 +28,7 @@ impl Thread {
pub fn run(&mut self) -> Option<Value> {
let mut active = &mut self.records[0];
log::info!(
info!(
"Starting thread with {}",
active
.as_function()
@ -44,7 +46,7 @@ impl Thread {
}
);
log::trace!(
trace!(
"Run \"{}\" | Record = {} | IP = {}",
active
.name()
@ -78,7 +80,7 @@ impl Thread {
}
if record_index == active.index() as usize {
log::trace!("Recursion detected");
trace!("Recursion detected");
self.call_stack.last_mut_or_panic().ip = active.ip;
active.ip = 0;
@ -134,7 +136,7 @@ impl Thread {
let outer_call = self.call_stack.last_or_panic();
let record_index = outer_call.record_index as usize;
log::trace!("Return from {returning_call} to {outer_call}");
trace!("Return from {returning_call} to {outer_call}");
if should_return_value {
let return_register = active