diff options
| -rw-r--r-- | Cargo.lock | 231 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/analyze.rs | 17 | ||||
| -rw-r--r-- | src/compile.rs | 10 | ||||
| -rw-r--r-- | src/diagnostics.rs | 104 | ||||
| -rw-r--r-- | src/diagnostics/non_alphabetic_tag_name.rs | 38 | ||||
| -rw-r--r-- | src/main.rs | 54 | ||||
| -rw-r--r-- | src/tokenize.rs | 2 |
8 files changed, 358 insertions, 100 deletions
@@ -3,6 +3,51 @@ version = 4 [[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] name = "bpaf" version = "0.9.26" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -19,7 +64,7 @@ checksum = "2f7e98cee839b19076cb3ce1afdb62bb182e04ff5f71f70188827002fae91094" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -35,12 +80,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] name = "html-escape" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c1ff2d1cbf39efe5af0900ced8a069b5e61557a17544eb0c4a50239937389e" [[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] name = "kuht" version = "0.1.0" dependencies = [ @@ -53,14 +120,40 @@ dependencies = [ ] [[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] name = "miette" version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ + "backtrace", + "backtrace-ext", "cfg-if", "miette-derive", - "unicode-width", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "unicode-width 0.1.14", ] [[package]] @@ -71,10 +164,34 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -93,12 +210,52 @@ dependencies = [ ] [[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustix" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] name = "snob" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39dc8789b83604bf5864a6f6d669263e1d4895a5d847047288f308997292443f" [[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + +[[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -110,23 +267,54 @@ dependencies = [ ] [[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix", + "windows-sys", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "unicode-linebreak", + "unicode-width 0.2.2", +] + +[[package]] name = "thiserror" -version = "1.0.69" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -136,7 +324,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] @@ -7,6 +7,6 @@ edition = "2024" bumpalo = "3" bpaf = { version = "0.9", features = ["derive"] } html-escape = "0.2" -miette = "7" +miette = { version = "7", features = ["fancy"] } snob = "0.1" thiserror = "2" diff --git a/src/analyze.rs b/src/analyze.rs index 7debb0d..b8a7012 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - sync::atomic::{AtomicUsize, Ordering::Relaxed}, -}; +use std::{collections::HashMap, sync::atomic::AtomicUsize}; use bumpalo::Bump; @@ -12,10 +9,12 @@ use crate::{ static NEXT_ID: AtomicUsize = AtomicUsize::new(0); +#[derive(Debug, Default)] pub struct SymbolTableSet<'a> { symbol_tables: HashMap<SymbolTableId, ScopedSymbolTable<'a>>, } +#[derive(Debug, Default)] pub struct ScopedSymbolTable<'a> { pub modules: HashMap<ModuleSymbolId, ModuleSymbol<'a>>, pub constants: HashMap<ConstantSymbolId, ConstantSymbol<'a>>, @@ -32,6 +31,7 @@ pub struct ConstantSymbolId(pub usize); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HeadSymbolId(pub usize); +#[derive(Debug)] pub struct ModuleSymbol<'a> { pub id: ModuleSymbolId, pub name: Option<&'a str>, @@ -39,6 +39,7 @@ pub struct ModuleSymbol<'a> { pub definition_site: Option<Element<'a>>, } +#[derive(Debug)] pub struct ConstantSymbol<'a> { pub id: ConstantSymbolId, pub name: Option<&'a str>, @@ -47,12 +48,14 @@ pub struct ConstantSymbol<'a> { pub definition_site: Option<Element<'a>>, } +#[derive(Debug)] pub struct HeadSymbol<'a> { pub id: HeadSymbolId, pub contents: Option<ConstantSymbolId>, pub definition_site: Option<Element<'a>>, } +#[derive(Debug)] pub enum ConstantType<'a> { String, Block { @@ -61,10 +64,12 @@ pub enum ConstantType<'a> { }, } +#[derive(Debug)] pub enum ConstantValue<'a> { String(&'a str), } +#[derive(Debug)] pub enum ConstantExpression<'a> { Name(&'a str), Value(ConstantValue<'a>), @@ -77,7 +82,7 @@ pub enum ConstantExpression<'a> { } fn next_id() -> usize { - NEXT_ID.fetch_add(1, Relaxed) + NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) } fn element_attributes<'a>( @@ -273,7 +278,7 @@ fn add_block<'a>( ConstantSymbol { id, name, - // fill this in + // TODO fill this in ty: None, value: Some(fragment), definition_site: Some(*element), diff --git a/src/compile.rs b/src/compile.rs index ccbf6f0..9e19b18 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -8,16 +8,12 @@ fn symbol_table_from_path<'a>( ) -> Option<&'a ScopedSymbolTable<'a>> { let mut symbol_table = symbol_table; for component in path.split("::") { - if let Some(module) = symbol_table + let module = symbol_table .modules .values() .find(|module| module.name == Some(component)) - .and_then(|module| module.symbol_table) - { - symbol_table = module; - } else { - return None; - } + .and_then(|module| module.symbol_table)?; + symbol_table = module; } Some(symbol_table) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 514818a..d2db96b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -13,7 +13,7 @@ mod forbidden_top_level_tag; mod html_tag; mod image_tag; mod non_alphabetic_attribute_name; -mod non_alphabetic_tag_name; +mod non_alphanumeric_tag_name; mod non_self_closing_void_tag; mod non_void_self_closing_tag; mod self_closing_end_tag; @@ -27,7 +27,7 @@ mod unterminated_tag; const VOID_TAGS: &[&str] = &[ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", - "track", "wbr", + "track", "wbr", "import", ]; const ALLOWED_TOP_LEVEL_TAGS: &[&str] = &["import", "const", "block", "head", "body"]; @@ -35,6 +35,12 @@ pub struct DiagnosticsContext { source_code: &'static str, } +impl DiagnosticsContext { + pub fn new(source_code: &'static str) -> Self { + Self { source_code } + } +} + trait TokenDiagnostic: Sized + Send + Sync + 'static { fn handle_token(_: &Token<'_>) -> Option<Self> { None @@ -66,13 +72,16 @@ trait AstDiagnostic: Sized + Send + Sync + 'static { fn maybe_print_diagnostic<D: Diagnostic + Send + Sync + 'static>( ctx: &DiagnosticsContext, diagnostic: Option<D>, -) { +) -> Option<()> { if let Some(diagnostic) = diagnostic { eprintln!( - "{}", + "{:?}", Error::new(diagnostic).with_source_code(ctx.source_code) ); + return None; } + + Some(()) } #[derive(Debug, Error, Diagnostic)] @@ -119,9 +128,10 @@ pub fn run_character_diagnostics(source: &'static [u8]) -> Option<&'static str> fn run_token_diagnostic<D: Diagnostic + TokenDiagnostic>( ctx: &DiagnosticsContext, tokens: &[Token<'_>], -) { +) -> Option<()> { + let mut has_failed = false; for token in tokens { - maybe_print_diagnostic(ctx, D::handle_token(token)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_token(token)).is_none(); let TokenType::Tag { attribute_tokens, .. @@ -131,53 +141,82 @@ fn run_token_diagnostic<D: Diagnostic + TokenDiagnostic>( }; for token in attribute_tokens { - maybe_print_diagnostic(ctx, D::handle_attribute_token(token)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_attribute_token(token)).is_none(); } } + + (!has_failed).then_some(()) } fn run_ast_diagnostic<D: AstDiagnostic + Diagnostic>( ctx: &DiagnosticsContext, document: &Document<'_>, -) { - maybe_print_diagnostic(ctx, D::handle_document(document)); +) -> Option<()> { + let mut has_failed = maybe_print_diagnostic(ctx, D::handle_document(document)).is_none(); for node in document.children { - maybe_print_diagnostic(ctx, D::handle_node(node)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_node(node)).is_none(); let Node::Element(element) = node else { continue; }; - maybe_print_diagnostic(ctx, D::handle_element(element)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_element(element)).is_none(); for attribute in element.attributes { - maybe_print_diagnostic(ctx, D::handle_attribute(attribute)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_attribute(attribute)).is_none(); } } + + (!has_failed).then_some(()) } -pub fn run_token_diagnostics(ctx: &DiagnosticsContext, tokens: &[Token<'_>]) { - run_token_diagnostic::<non_void_self_closing_tag::NonVoidSelfClosingTag>(ctx, tokens); - run_token_diagnostic::<non_self_closing_void_tag::NonSelfClosingVoidTag>(ctx, tokens); - run_token_diagnostic::<end_tag_with_attributes::EndTagWithAttributes>(ctx, tokens); - run_token_diagnostic::<self_closing_end_tag::SelfClosingEndTag>(ctx, tokens); - run_token_diagnostic::<non_alphabetic_tag_name::NonAlphabeticTagName>(ctx, tokens); - run_token_diagnostic::<empty_tag_name::EmptyTagName>(ctx, tokens); - run_token_diagnostic::<unterminated_tag::UnterminatedTag>(ctx, tokens); - run_token_diagnostic::<unterminated_comment::UnterminatedComment>(ctx, tokens); - run_token_diagnostic::<non_alphabetic_attribute_name::NonAlphabeticAttributeName>(ctx, tokens); +pub fn run_token_diagnostics(ctx: &DiagnosticsContext, tokens: &[Token<'_>]) -> Option<()> { + let mut has_failed = false; + has_failed |= + run_token_diagnostic::<non_void_self_closing_tag::NonVoidSelfClosingTag>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<non_self_closing_void_tag::NonSelfClosingVoidTag>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<end_tag_with_attributes::EndTagWithAttributes>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<self_closing_end_tag::SelfClosingEndTag>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<non_alphanumeric_tag_name::NonAlphaNumericTagName>(ctx, tokens) + .is_none(); + has_failed |= run_token_diagnostic::<empty_tag_name::EmptyTagName>(ctx, tokens).is_none(); + has_failed |= run_token_diagnostic::<unterminated_tag::UnterminatedTag>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<unterminated_comment::UnterminatedComment>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<non_alphabetic_attribute_name::NonAlphabeticAttributeName>( + ctx, tokens, + ) + .is_none(); // TODO invalid character reference + (!has_failed).then_some(()) } -pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) { - run_ast_diagnostic::<stray_equal_sign::StrayEqualSign>(ctx, document); - run_ast_diagnostic::<too_many_equal_signs::TooManyEqualSigns>(ctx, document); - run_ast_diagnostic::<duplicate_attribute_names::DuplicateAttributeNames>(ctx, document); - run_ast_diagnostic::<top_level_character_data::TopLevelCharacterData>(ctx, document); - run_ast_diagnostic::<forbidden_top_level_tag::ForbiddenTopLevelTag>(ctx, document); - run_ast_diagnostic::<stray_end_tag::StrayEndTag>(ctx, document); - run_ast_diagnostic::<unclosed_start_tag::UnclosedStartTag>(ctx, document); - run_ast_diagnostic::<html_tag::HtmlTag>(ctx, document); - run_ast_diagnostic::<image_tag::ImageTag>(ctx, document); +pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) -> Option<()> { + let mut has_failed = false; + has_failed |= run_ast_diagnostic::<stray_equal_sign::StrayEqualSign>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<too_many_equal_signs::TooManyEqualSigns>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<duplicate_attribute_names::DuplicateAttributeNames>(ctx, document) + .is_none(); + has_failed |= + run_ast_diagnostic::<top_level_character_data::TopLevelCharacterData>(ctx, document) + .is_none(); + has_failed |= + run_ast_diagnostic::<forbidden_top_level_tag::ForbiddenTopLevelTag>(ctx, document) + .is_none(); + has_failed |= run_ast_diagnostic::<stray_end_tag::StrayEndTag>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<unclosed_start_tag::UnclosedStartTag>(ctx, document).is_none(); + has_failed |= run_ast_diagnostic::<html_tag::HtmlTag>(ctx, document).is_none(); + has_failed |= run_ast_diagnostic::<image_tag::ImageTag>(ctx, document).is_none(); // TODO invalid <kuht-var /> // TODO invalid <import /> // TODO invalid <const /> @@ -214,4 +253,5 @@ pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) { // TODO aria tags don't match tag semantics // TODO empty or missing image src // TODO missing alt text for image (unless inside figure with caption, or has title, or meta name = "generator") + (!has_failed).then_some(()) } diff --git a/src/diagnostics/non_alphabetic_tag_name.rs b/src/diagnostics/non_alphabetic_tag_name.rs deleted file mode 100644 index b36cb75..0000000 --- a/src/diagnostics/non_alphabetic_tag_name.rs +++ /dev/null @@ -1,38 +0,0 @@ -use miette::Diagnostic; -use thiserror::Error; - -use crate::{ - diagnostics::TokenDiagnostic, - tokenize::{Span, Token, TokenType}, -}; - -#[derive(Error, Debug, Diagnostic)] -#[error("Tag names must be alphabetic")] -#[diagnostic(code(NonAlphabeticTagName), severity(Error))] -pub struct NonAlphabeticTagName { - #[label(primary, "tag names must only contain uppercase and lowercase letters")] - tag: Span, -} - -impl TokenDiagnostic for NonAlphabeticTagName { - fn handle_token(token: &Token<'_>) -> Option<Self> { - let TokenType::Tag { - is_end, - lowercase_tag_name, - .. - } = token.ty - else { - return None; - }; - - if is_end - || lowercase_tag_name - .chars() - .all(|char| char.is_ascii_alphabetic()) - { - return None; - } - - Some(Self { tag: token.span }) - } -} diff --git a/src/main.rs b/src/main.rs index 1ac5a54..dcfd81b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,13 @@ use std::collections::HashMap; use bpaf::{Bpaf, Parser}; use bumpalo::Bump; -use kuht::{analyze::symbol_table, compile::compile, parse::parse_document, tokenize::tokenize}; +use kuht::{ + analyze::symbol_table, + compile::compile, + diagnostics::{self, DiagnosticsContext}, + parse::parse_document, + tokenize::tokenize, +}; #[derive(Debug, Clone, Bpaf)] struct Options { @@ -21,39 +27,73 @@ struct Options { fn main() { let options = options().run(); + let mut has_failed = false; + let sources = options .files .into_iter() .map(|filename| { - let source = match std::fs::read_to_string(&filename) { + let source = match std::fs::read(&filename) { Ok(source) => source, Err(error) => { - eprintln!("{error}"); + eprintln!("Error: {error}"); std::process::exit(1); } }; (filename, source) }) + .filter_map(|(filename, source)| { + let source: &'static [u8] = Vec::leak(source); + let Some(source) = diagnostics::run_character_diagnostics(source) else { + has_failed = true; + return None; + }; + Some((filename, source)) + }) .collect::<HashMap<_, _>>(); let arena = Bump::new(); let tokens = sources .into_iter() - .map(|(filename, source)| (filename, tokenize(&source, &arena))) + .map(|(filename, source)| { + ( + filename, + (DiagnosticsContext::new(source), tokenize(source, &arena)), + ) + }) + .filter(|(_, (ctx, tokens))| { + let failed = diagnostics::run_token_diagnostics(ctx, tokens).is_none(); + has_failed |= failed; + failed + }) .collect::<HashMap<_, _>>(); let asts = tokens .into_iter() - .map(|(filename, tokens)| (filename, parse_document(tokens, &arena))) + .map(|(filename, (ctx, tokens))| (filename, (ctx, parse_document(tokens, &arena)))) + .filter(|(_, (ctx, document))| { + let failed = diagnostics::run_ast_diagnostics(ctx, document).is_none(); + has_failed |= failed; + failed + }) .collect::<HashMap<_, _>>(); + if has_failed { + std::process::exit(1); + } + let symbol_tables = asts .into_iter() - .map(|(filename, document)| (filename, symbol_table(&arena, document))) + .map(|(filename, (ctx, document))| (filename, (ctx, symbol_table(&arena, document)))) .collect::<HashMap<_, _>>(); if !options.dry { - let output = compile(&symbol_tables.into_values().collect::<Box<_>>()); + let output = compile( + &symbol_tables + .into_values() + .map(|(_, document)| document) + .collect::<Box<_>>(), + ); if let Err(e) = std::fs::write(options.out, output.as_bytes()) { eprintln!("{e}"); std::process::exit(1); diff --git a/src/tokenize.rs b/src/tokenize.rs index 43ad305..a8e83c8 100644 --- a/src/tokenize.rs +++ b/src/tokenize.rs @@ -52,7 +52,7 @@ pub enum AttributeTokenType<'a> { impl From<Span> for miette::SourceSpan { fn from(value: Span) -> Self { - miette::SourceSpan::from((value.start, value.start - value.end)) + miette::SourceSpan::from((value.start, value.end - value.start)) } } |
