diff options
| author | Mica White <botahamec@outlook.com> | 2026-07-12 19:49:14 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-07-12 19:49:14 -0400 |
| commit | 8399983fd45bc0f11eac9def280c0833ffb93c04 (patch) | |
| tree | f84173ca30a3c62db7abb92a0d869ff5d5199c51 /src/compile.rs | |
Diffstat (limited to 'src/compile.rs')
| -rw-r--r-- | src/compile.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/compile.rs b/src/compile.rs new file mode 100644 index 0000000..ccbf6f0 --- /dev/null +++ b/src/compile.rs @@ -0,0 +1,140 @@ +use html_escape::{encode_double_quoted_attribute, encode_single_quoted_attribute}; + +use crate::analyze::{ConstantExpression, ConstantSymbolId, ConstantValue, ScopedSymbolTable}; + +fn symbol_table_from_path<'a>( + symbol_table: &'a ScopedSymbolTable<'a>, + path: &'a str, +) -> Option<&'a ScopedSymbolTable<'a>> { + let mut symbol_table = symbol_table; + for component in path.split("::") { + if let Some(module) = symbol_table + .modules + .values() + .find(|module| module.name == Some(component)) + .and_then(|module| module.symbol_table) + { + symbol_table = module; + } else { + return None; + } + } + + Some(symbol_table) +} + +fn compile_attribute_value<'a>( + buffer: &mut String, + symbol_table: &'a ScopedSymbolTable<'a>, + name: &'a str, + value: Option<ConstantSymbolId>, +) { + buffer.push(' '); + buffer.push_str(name); + let Some(value) = value else { + return; + }; + + buffer.push('='); + + let mut new_buffer = String::new(); + compile_constant(&mut new_buffer, symbol_table, value); + + let use_double_quotes = !new_buffer.contains('"') || new_buffer.contains('\''); + let delimiter = if !use_double_quotes { '\'' } else { '"' }; + let value = if use_double_quotes { + encode_double_quoted_attribute(&new_buffer) + } else { + encode_single_quoted_attribute(&new_buffer) + }; + + buffer.push(delimiter); + buffer.push_str(&value); + buffer.push(delimiter); +} + +fn compile_constant<'a>( + buffer: &mut String, + symbol_table: &'a ScopedSymbolTable<'a>, + id: ConstantSymbolId, +) { + let Some(constant) = symbol_table.constants.get(&id) else { + return; + }; + let Some(expression) = &constant.value else { + return; + }; + match expression { + ConstantExpression::Value(value) => match value { + ConstantValue::String(value) => buffer.push_str(value), + }, + ConstantExpression::Name(name) => { + let mut name = *name; + let mut symbol_table = symbol_table; + if let Some((path, item)) = name.rsplit_once("::") + && let Some(module) = symbol_table_from_path(symbol_table, path) + { + name = item; + symbol_table = module; + } + if let Some(id) = symbol_table + .constants + .values() + .find_map(|value| (value.name == Some(name)).then_some(value.id)) + { + compile_constant(buffer, symbol_table, id); + } + } + ConstantExpression::Fragment(constant_symbol_ids) => { + for constant in *constant_symbol_ids { + compile_constant(buffer, symbol_table, *constant); + } + } + ConstantExpression::Block { + tag_name_lowercase, + attributes, + child, + } => { + buffer.push('<'); + buffer.push_str(tag_name_lowercase); + for (name, value) in attributes { + 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('>'); + } + } +} + +fn compile_head<'a>(buffer: &mut String, symbol_tables: &[ScopedSymbolTable<'a>]) { + for symbol_table in symbol_tables { + for head in symbol_table.heads.values().filter_map(|head| head.contents) { + compile_constant(buffer, symbol_table, head); + } + } +} + +fn compile_body<'a>(buffer: &mut String, symbol_tables: &[ScopedSymbolTable<'a>]) { + for symbol_table in symbol_tables { + for body in symbol_table + .bodies + .values() + .filter_map(|body| body.contents) + { + compile_constant(buffer, symbol_table, body); + } + } +} + +pub fn compile<'a>(symbol_tables: &[ScopedSymbolTable<'a>]) -> Box<str> { + let mut buffer = String::from("<!DOCTYPE html><html lang=\"en\"><head>"); + compile_head(&mut buffer, symbol_tables); + buffer.push_str("</head><body>"); + compile_body(&mut buffer, symbol_tables); + buffer.push_str("</body></html>"); + + buffer.into_boxed_str() +} |
