summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-10-03 14:14:31 -0400
committerMica White <botahamec@outlook.com>2026-10-03 14:14:31 -0400
commit1b7ddd5894c2636618e630f44b1bb7d598d29b48 (patch)
tree23634f9a1e95a719628b5978c834d1c7de69e615
parent67083043f5da93c447468578c49ffec1a679f1cc (diff)
Planning for 0.2
-rw-r--r--src/analyze.rs16
-rw-r--r--src/compile.rs17
-rw-r--r--src/diagnostics.rs48
-rw-r--r--src/diagnostics/forbidden_top_level_tag.rs6
-rw-r--r--src/diagnostics/non_self_closing_void_tag.rs3
-rw-r--r--src/diagnostics/non_void_self_closing_tag.rs3
-rw-r--r--src/lib.rs6
7 files changed, 72 insertions, 27 deletions
diff --git a/src/analyze.rs b/src/analyze.rs
index b8a7012..6526c2b 100644
--- a/src/analyze.rs
+++ b/src/analyze.rs
@@ -58,6 +58,10 @@ pub struct HeadSymbol<'a> {
#[derive(Debug)]
pub enum ConstantType<'a> {
String,
+ Fragment {
+ direct_children_tags: Option<&'a [&'a str]>,
+ transitive_children_tags: Option<&'a [&'a str]>,
+ },
Block {
direct_children_tags: Option<&'a [&'a str]>,
transitive_children_tags: Option<&'a [&'a str]>,
@@ -65,14 +69,14 @@ pub enum ConstantType<'a> {
}
#[derive(Debug)]
-pub enum ConstantValue<'a> {
+pub enum ConstantLiteral<'a> {
String(&'a str),
}
#[derive(Debug)]
pub enum ConstantExpression<'a> {
Name(&'a str),
- Value(ConstantValue<'a>),
+ Value(ConstantLiteral<'a>),
Fragment(&'a [ConstantSymbolId]),
Block {
tag_name_lowercase: &'a str,
@@ -104,7 +108,7 @@ fn element_attributes<'a>(
id,
name: None,
ty: Some(ConstantType::String),
- value: Some(ConstantExpression::Value(ConstantValue::String(value))),
+ value: Some(ConstantExpression::Value(ConstantLiteral::String(value))),
definition_site: None,
},
);
@@ -146,7 +150,7 @@ fn node_to_constant<'a>(
id,
name: None,
ty: Some(ConstantType::String),
- value: Some(ConstantExpression::Value(ConstantValue::String(value))),
+ value: Some(ConstantExpression::Value(ConstantLiteral::String(value))),
definition_site: None,
},
);
@@ -164,7 +168,7 @@ fn node_to_constant<'a>(
ConstantSymbol {
id,
name: None,
- // TODO fragment type
+ // TODO acquire the type
ty: None,
value: Some(fragment),
definition_site: None,
@@ -259,7 +263,7 @@ fn add_constant<'a>(symbol_table: &mut ScopedSymbolTable<'a>, element: &'a Eleme
id,
name,
ty: Some(ConstantType::String),
- value: value.map(|value| ConstantExpression::Value(ConstantValue::String(value))),
+ value: value.map(|value| ConstantExpression::Value(ConstantLiteral::String(value))),
definition_site: Some(*element),
},
);
diff --git a/src/compile.rs b/src/compile.rs
index 9e19b18..a0877cd 100644
--- a/src/compile.rs
+++ b/src/compile.rs
@@ -1,6 +1,9 @@
use html_escape::{encode_double_quoted_attribute, encode_single_quoted_attribute};
-use crate::analyze::{ConstantExpression, ConstantSymbolId, ConstantValue, ScopedSymbolTable};
+use crate::{
+ VOID_TAGS,
+ analyze::{ConstantExpression, ConstantLiteral, ConstantSymbolId, ScopedSymbolTable},
+};
fn symbol_table_from_path<'a>(
symbol_table: &'a ScopedSymbolTable<'a>,
@@ -62,7 +65,7 @@ fn compile_constant<'a>(
};
match expression {
ConstantExpression::Value(value) => match value {
- ConstantValue::String(value) => buffer.push_str(value),
+ ConstantLiteral::String(value) => buffer.push_str(value),
},
ConstantExpression::Name(name) => {
let mut name = *name;
@@ -97,10 +100,12 @@ fn compile_constant<'a>(
compile_attribute_value(buffer, symbol_table, name, *value);
}
buffer.push('>');
- compile_constant(buffer, symbol_table, *child);
- buffer.push_str("</");
- buffer.push_str(tag_name_lowercase);
- buffer.push('>');
+ if !VOID_TAGS.contains(tag_name_lowercase) {
+ compile_constant(buffer, symbol_table, *child);
+ buffer.push_str("</");
+ buffer.push_str(tag_name_lowercase);
+ buffer.push('>');
+ }
}
}
}
diff --git a/src/diagnostics.rs b/src/diagnostics.rs
index d2db96b..295de94 100644
--- a/src/diagnostics.rs
+++ b/src/diagnostics.rs
@@ -6,6 +6,8 @@ use crate::{
tokenize::{AttributeToken, Token, TokenType},
};
+mod deprecated_attributes;
+mod deprecated_tag;
mod duplicate_attribute_names;
mod empty_tag_name;
mod end_tag_with_attributes;
@@ -25,12 +27,6 @@ mod unclosed_start_tag;
mod unterminated_comment;
mod unterminated_tag;
-const VOID_TAGS: &[&str] = &[
- "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source",
- "track", "wbr", "import",
-];
-const ALLOWED_TOP_LEVEL_TAGS: &[&str] = &["import", "const", "block", "head", "body"];
-
pub struct DiagnosticsContext {
source_code: &'static str,
}
@@ -194,6 +190,8 @@ pub fn run_token_diagnostics(ctx: &DiagnosticsContext, tokens: &[Token<'_>]) ->
ctx, tokens,
)
.is_none();
+ // TODO allow the AST diagnostics to still run if this fails
+ has_failed |= run_token_diagnostic::<deprecated_tag::DeprectatedTag>(ctx, tokens).is_none();
// TODO invalid character reference
(!has_failed).then_some(())
}
@@ -221,8 +219,29 @@ pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) ->
// TODO invalid <import />
// TODO invalid <const />
// TODO invalid <block />
- // TODO deprecated tag
+ // TODO invalid <kuht-http />
// TODO deprecated tag attribute
+ // TODO warning-only obsolete features
+ // TODO invalid name on anchor
+ // TODO invalid language on script
+ // TODO invalid type on style
+ // TODO base without href or target
+ // TODO multiple base elements
+ // TODO base target with a newline, tab, or <
+ // TODO invalid cite on <blockquote> or <q>
+ // TODO non html1 tag or attribute
+ // TODO empty, missing, or invalid image src
+ // TODO invalid form enctype
+ // TODO invalid form action
+ // TODO invalid form method
+ // TODO input without name
+ // TODO invalid input attributes for type
+ // TODO invalid select size
+ // TODO invalid select mutliple attribute
+ // TODO select without name
+ // TODO invalid option selected attribute
+ // TODO invalid cols and rows attributes for textarea
+ // TODO textarea without name
//
// TODO forbidden tag in noscript
// TODO head tag outside head
@@ -244,6 +263,20 @@ pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) ->
// TODO select inside select
// TODO input, keygen, textarea inside select
// TODO caption, table, tbody, tfoot, thread, tr, td, th inside select
+ // TODO more than one body
+ // TODO invalid head child
+ // TODO missing title
+ // TODO title outside head
+ // TODO warning for title longer than 64 characters
+ // TODO warning for more than one <h1>
+ // TODO invalid heading hierarchy
+ // TODO invalid <p>, <pre>, <address>, <cite> or <h_> content
+ // TODO wrap on <pre>
+ // TODO address inside address
+ // TODO heading inside addres
+ // TODO <header> or <footer> inside <address>
+ // TODO section content (<article>, <aside>, <section>, <nav>) inside address
+ // TODO image map outside <a>
//
// TODO anchor with invalid href
// TODO dt or dd outside dl
@@ -251,7 +284,6 @@ pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) ->
// TODO invalid dl, ul, menu
// TODO invalid attribute values (see microsyntaxes)
// 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/forbidden_top_level_tag.rs b/src/diagnostics/forbidden_top_level_tag.rs
index 8b5301e..c834e8a 100644
--- a/src/diagnostics/forbidden_top_level_tag.rs
+++ b/src/diagnostics/forbidden_top_level_tag.rs
@@ -1,11 +1,7 @@
use miette::Diagnostic;
use thiserror::Error;
-use crate::{
- diagnostics::{ALLOWED_TOP_LEVEL_TAGS, AstDiagnostic},
- parse::Node,
- tokenize::Span,
-};
+use crate::{ALLOWED_TOP_LEVEL_TAGS, diagnostics::AstDiagnostic, parse::Node, tokenize::Span};
#[derive(Error, Debug, Diagnostic)]
#[error("Forbidden top-level tag is present")]
diff --git a/src/diagnostics/non_self_closing_void_tag.rs b/src/diagnostics/non_self_closing_void_tag.rs
index bfff266..1c987c3 100644
--- a/src/diagnostics/non_self_closing_void_tag.rs
+++ b/src/diagnostics/non_self_closing_void_tag.rs
@@ -2,7 +2,8 @@ use miette::Diagnostic;
use thiserror::Error;
use crate::{
- diagnostics::{TokenDiagnostic, VOID_TAGS},
+ VOID_TAGS,
+ diagnostics::TokenDiagnostic,
tokenize::{Span, Token, TokenType},
};
diff --git a/src/diagnostics/non_void_self_closing_tag.rs b/src/diagnostics/non_void_self_closing_tag.rs
index 8a64be1..2667fa1 100644
--- a/src/diagnostics/non_void_self_closing_tag.rs
+++ b/src/diagnostics/non_void_self_closing_tag.rs
@@ -2,7 +2,8 @@ use miette::Diagnostic;
use thiserror::Error;
use crate::{
- diagnostics::{TokenDiagnostic, VOID_TAGS},
+ VOID_TAGS,
+ diagnostics::TokenDiagnostic,
tokenize::{Span, Token, TokenType},
};
diff --git a/src/lib.rs b/src/lib.rs
index 17d2c6d..8691254 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,3 +3,9 @@ pub mod compile;
pub mod diagnostics;
pub mod parse;
pub mod tokenize;
+
+pub const VOID_TAGS: &[&str] = &[
+ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source",
+ "track", "wbr", "import",
+];
+pub const ALLOWED_TOP_LEVEL_TAGS: &[&str] = &["import", "const", "block", "head", "body"];