///|
/// A 1-based source position plus the underlying string offset.
///
/// `offset` is the raw string offset used by scanners, while `line` and
/// `column` are intended for human-readable diagnostics.
pub(all) struct SourcePos {
offset : Int
line : Int
column : Int
} derive(Eq, ToJson, Debug)
///|
/// Creates a source position for diagnostics and source spans.
pub fn pos(offset~ : Int, line~ : Int, column~ : Int) -> SourcePos {
{ offset, line, column }
}
///|
/// A half-open source span in one file.
///
/// The `start` position points at the first character in the range and `end`
/// points just after the range. This keeps parser and codegen diagnostics easy
/// to map back to the original template or MoonBit source.
pub(all) struct SourceSpan {
file : String
start : SourcePos
end : SourcePos
} derive(Eq, ToJson, Debug)
///|
/// Creates a half-open source span for a single file.
pub fn span(file~ : String, start~ : SourcePos, end~ : SourcePos) -> SourceSpan {
{ file, start, end }
}
///|
/// One parsed template item.
///
/// Nodes preserve source spans so downstream tools can report precise errors.
/// Escaped expressions correspond to `<%= ... %>`, raw expressions correspond
/// to `<%- ... %>`, statements correspond to `<% ... %>`, includes correspond
/// to `<%~ "path" %>`, and comments correspond to `<%# ... %>`.
pub(all) enum TemplateNode {
Text(text~ : String, span~ : SourceSpan)
EscapedExpr(expr~ : String, span~ : SourceSpan)
RawExpr(expr~ : String, span~ : SourceSpan)
Statement(code~ : String, span~ : SourceSpan)
Include(path~ : String, span~ : SourceSpan)
Comment(text~ : String, span~ : SourceSpan)
} derive(Eq, ToJson, Debug)
///|
/// Creates a plain text node.
///
/// Text nodes are emitted directly as string writes by the generator.
pub fn text_node(text : String, span : SourceSpan) -> TemplateNode {
Text(text~, span~)
}
///|
/// Creates an escaped expression output node.
///
/// Generated renderers evaluate the expression, format it with `Show`, and
/// HTML-escape the formatted value before writing it.
pub fn escaped_expr_node(expr : String, span : SourceSpan) -> TemplateNode {
EscapedExpr(expr~, span~)
}
///|
/// Creates a raw expression output node.
///
/// Generated renderers evaluate the expression and write the formatted value
/// without HTML escaping, so callers should use it only for trusted content.
pub fn raw_expr_node(expr : String, span : SourceSpan) -> TemplateNode {
RawExpr(expr~, span~)
}
///|
/// Creates a MoonBit statement node.
///
/// Statement nodes are copied into the generated renderer after template field
/// references and short filters have been qualified by codegen.
pub fn statement_node(code : String, span : SourceSpan) -> TemplateNode {
Statement(code~, span~)
}
///|
/// Creates a static include node.
///
/// File-based codegen expands include nodes before emitting MoonBit source.
pub fn include_node(path : String, span : SourceSpan) -> TemplateNode {
Include(path~, span~)
}
///|
/// Creates a template comment node.
///
/// Comments are preserved in the AST for tooling, but they do not emit output.
pub fn comment_node(text : String, span : SourceSpan) -> TemplateNode {
Comment(text~, span~)
}
///|
/// A parsed template document.
///
/// `path` identifies the source template for diagnostics, and `nodes` stores
/// the parsed items in render order.
pub(all) struct Template {
path : String
nodes : Array[TemplateNode]
} derive(Eq, ToJson, Debug)
///|
/// Creates a parsed template document from a path and node sequence.
pub fn template(path~ : String, nodes~ : Array[TemplateNode]) -> Template {
{ path, nodes }
}
///|
/// A user struct bound to a template path by `#tpl.path`.
///
/// Codegen uses the binding to connect a MoonBit struct with a template file.
/// `field_names` records the struct fields that may be referenced without
/// `self.` inside templates; generated code prefixes them automatically.
pub(all) struct TemplateBinding {
source_path : String
template_path : String
struct_name : String
field_names : Array[String]
span : SourceSpan
} derive(Eq, ToJson, Debug)
///|
/// Creates a template binding discovered in MoonBit source.
///
/// This constructor is useful for tests and custom scanners that want to feed
/// bindings into the code generator without parsing files again.
pub fn template_binding(
source_path~ : String,
template_path~ : String,
struct_name~ : String,
field_names? : Array[String] = [],
span~ : SourceSpan,
) -> TemplateBinding {
{ source_path, template_path, struct_name, field_names, span }
}