From 54739f87d250042ab414212db09bfe5dc4f63a05 Mon Sep 17 00:00:00 2001 From: Alex van de Sandt Date: Fri, 17 May 2024 00:34:04 -0400 Subject: [PATCH] Instrument the scanner with tracing --- .gitignore | 1 + Cargo.lock | 225 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + lox-examples/comment.lox | 1 + src/main.rs | 16 +++ src/runners/file.rs | 6 +- src/runners/repl.rs | 2 + src/scanner.rs | 32 ++++++ 8 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 lox-examples/comment.lox diff --git a/.gitignore b/.gitignore index d81f12e..79ffc76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /.idea +.env diff --git a/Cargo.lock b/Cargo.lock index 147297d..de84c9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.14" @@ -169,6 +178,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "errno" version = "0.3.9" @@ -203,6 +218,12 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.154" @@ -215,14 +236,32 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "lox" version = "0.1.0" dependencies = [ "claims", "clap", + "dotenv", "miette", "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", ] [[package]] @@ -271,6 +310,16 @@ dependencies = [ "adler", ] +[[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 = "object" version = "0.32.2" @@ -280,12 +329,30 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "proc-macro2" version = "1.0.82" @@ -304,6 +371,50 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -323,6 +434,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "smawk" version = "0.3.2" @@ -408,6 +534,77 @@ dependencies = [ "syn", ] +[[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 = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -432,6 +629,34 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 24b71c5..b881911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,11 @@ publish = false [dependencies] clap = { version = "4.5.4", features = ["derive"] } +dotenv = "0.15.0" miette = { version = "7.2.0", features = ["fancy"] } thiserror = "1.0.60" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } [dev-dependencies] claims = "0.7.1" diff --git a/lox-examples/comment.lox b/lox-examples/comment.lox new file mode 100644 index 0000000..43d15c0 --- /dev/null +++ b/lox-examples/comment.lox @@ -0,0 +1 @@ +// foobar diff --git a/src/main.rs b/src/main.rs index 458c231..4e603b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,21 @@ use std::path::PathBuf; use clap::Parser; +use miette::IntoDiagnostic; +use tracing_subscriber::EnvFilter; use lox::{run_file, run_repl}; fn main() -> miette::Result<()> { + dotenv::dotenv().into_diagnostic()?; + start_tracing(); + let args = Args::parse(); if let Some(path) = args.file { + tracing::info!(path = %path.display(), "executing file"); run_file(&path)?; } else { + tracing::info!("starting repl"); run_repl()?; } @@ -19,3 +26,12 @@ fn main() -> miette::Result<()> { struct Args { file: Option, } + +fn start_tracing() { + let filter = EnvFilter::from_env("LOX_LOG"); + tracing_subscriber::fmt() + .with_env_filter(filter) + .without_time() + .compact() + .init(); +} diff --git a/src/runners/file.rs b/src/runners/file.rs index 26b86a4..a5fea82 100644 --- a/src/runners/file.rs +++ b/src/runners/file.rs @@ -3,14 +3,16 @@ use std::path::Path; use crate::scanner::scan; use miette::{IntoDiagnostic, Report, Result, WrapErr}; +#[tracing::instrument(skip_all, fields(path = %path.display()))] pub fn run_file(path: &Path) -> Result<()> { + tracing::debug!("reading lox file"); let contents = std::fs::read_to_string(path) .into_diagnostic() .wrap_err_with(|| format!("failed to open file: {}", path.display()))?; - dbg!(&contents); + tracing::debug!("scanning"); let tokens = scan(&contents).map_err(|e| Report::new(e).with_source_code(contents))?; - dbg!(&tokens); + dbg!(&tokens.len()); Ok(()) } diff --git a/src/runners/repl.rs b/src/runners/repl.rs index 1bf22fc..acef529 100644 --- a/src/runners/repl.rs +++ b/src/runners/repl.rs @@ -4,6 +4,7 @@ use miette::{IntoDiagnostic, Report, Result, WrapErr}; use crate::scanner::scan; +#[tracing::instrument] pub fn run_repl() -> Result<()> { let mut stdout = std::io::stdout(); let mut prompt = || -> Result<()> { @@ -26,6 +27,7 @@ pub fn run_repl() -> Result<()> { .wrap_err("failed to read line from stdin")?; if line == "exit" || line == "\u{04}" { + tracing::info!("exiting repl"); return Ok(()); }; diff --git a/src/scanner.rs b/src/scanner.rs index da34418..a3adbdb 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -29,6 +29,7 @@ impl Scanner { } } + #[tracing::instrument(skip_all)] pub fn scan(mut self) -> Result, ScannerFailure> { while !self.at_end() { match self.next_token() { @@ -41,17 +42,22 @@ impl Scanner { } if self.errors.is_empty() { + tracing::info!(token_count = self.tokens.len(), "scanning complete"); Ok(self.tokens) } else { + tracing::error!("syntax errors found"); Err(ScannerFailure { errors: self.errors, }) } } + #[tracing::instrument(skip_all)] fn next_token(&mut self) -> Result> { + tracing::debug!(current_position = %self.current_position, "scanning next token"); let current_token_start = self.current_position; let Some(c) = self.pop() else { + tracing::trace!("end of input"); return Ok(None); }; @@ -73,21 +79,30 @@ impl Scanner { '>' => self.pop_if_matches_to_token('=', GreaterEq, Greater), '/' => { if self.pop_if_matches('/').is_some() { + let span = tracing::trace_span!("scan_comment", %current_token_start); + let _guard = span.enter(); + // comment, ends at the next newline self.pop_until_matches('\n'); + tracing::debug!("discarding comment"); return Ok(None); } else { Slash } } '"' => { + let span = tracing::trace_span!("scan_string_literal", %current_token_start); + let _guard = span.enter(); + let is_terminated = self.pop_until_matches('"'); if !is_terminated { + tracing::error!("unterminated string literal"); return Err(ScannerError::unterminated_string( current_token_start, self.current_position - 1, )); } + let contents = self.lexeme(current_token_start + 1, self.current_position); // discard the closing `"` self.advance(); @@ -98,7 +113,11 @@ impl Scanner { return Ok(None); } d if d.is_ascii_digit() => { + let span = tracing::trace_span!("scan_number_literal", %current_token_start); + let _guard = span.enter(); + let is_digit = |c: char| c.is_ascii_digit(); + // consume all the remaining digits self.pop_while(is_digit); if self @@ -115,8 +134,12 @@ impl Scanner { NumberLit(value) } i if i.is_ascii_alphabetic() || i == '_' => { + let span = tracing::trace_span!("scan_ident", %current_token_start); + let _guard = span.enter(); + self.pop_while(|c| c.is_ascii_alphanumeric() || c == '_'); let lexeme = self.lexeme(current_token_start, self.current_position); + match lexeme.as_str() { "and" => And, "class" => Class, @@ -138,6 +161,7 @@ impl Scanner { } } unexpected => { + tracing::error!(c = ?unexpected, "unexpected character"); return Err(ScannerError::unexpected_character( unexpected, current_token_start, @@ -145,6 +169,8 @@ impl Scanner { } }; + tracing::debug!(?token_kind, "found token"); + let span = Span::new(current_token_start, self.current_position); let token = Token { kind: token_kind, @@ -221,12 +247,15 @@ impl Scanner { /// /// Returns `true` if we reached the matching char, or `false` if we reached the end of the /// input + #[tracing::instrument(skip_all, fields(?until))] fn pop_until_matches(&mut self, until: char) -> bool { loop { let Some(next) = self.peek() else { + tracing::trace!("end of input"); return false; }; if next == until { + tracing::trace!("match found"); return true; } self.advance(); @@ -236,15 +265,18 @@ impl Scanner { /// Takes a predicate that continually receives the next character. If the predicate returns /// `true`, the character is popped. Stops when the predicate returns `false` or the end of the /// input is reached. + #[tracing::instrument(skip_all)] fn pop_while(&mut self, mut should_pop: F) where F: FnMut(char) -> bool, { loop { let Some(next) = self.peek() else { + tracing::trace!("end of input"); return; }; if !should_pop(next) { + tracing::trace!("condition met"); return; } self.advance(); -- 2.51.2