API Reference
Sakko API Reference
All types below are re-exported from the sakko crate root.
Parsing
parse_sakko(input: &str) -> Result<RootNode>
Parse a .sako source string into a document AST.
use sakko::parse_sakko;
let ast = parse_sakko(r#"<counter { text: "Hello" }>"#)?;
// ast: RootNode { name: "counter", children: [...], ... }
The returned RootNode contains:
name: Cow<str>- component tag namemodifiers: Vec<Modifier>- flag/pair modifiers on the rootdeclarations: Vec<AtcodeDeclaration>- reactive block declarations (@state,@derived,@effect,@if,@each,@class,@on,@bind)children: Vec<AstNode>- child elements, inlines, and lists
tokenize(input: &str) -> Result<Vec<Token>>
Tokenize a source string. Useful for lexing-level tooling; the parser uses
this internally.
use sakko::tokenize;
let tokens = tokenize("button(accent): Save")?;
// Token { kind: TokenKind::Ident, value: "button", ... }
Token kinds: Lt, Gt, Lbrace, Rbrace, Lparen, Rparen,
Lbracket, Rbracket, Colon, Semi, Comma, Ident, String,
BacktickString, At, Equals, InterpStart, InterpEnd, Expr,
Dot, Plus, Minus, Star, Pipe, Ampersand, Bang, Question,
Percent.
Typechecking
check_source(src: &str) -> Result<Report>
Parse and typecheck a .sako source string in one call. Returns a Report
containing diagnostics and raw-JS escape records.
use sakko::check_source;
let report = check_source(r#"<counter { @state { count = 0 } }>"#)?;
assert!(report.diagnostics.is_empty());
check_ast(ast: &AstNode) -> Report
Typecheck an already-parsed AST node. parse_sakko returns a RootNode, so
wrap it as AstNode::Root before passing it here:
use sakko::{AstNode, check_ast, parse_sakko};
let root = parse_sakko(r#"<counter { @state { count = 0 } }>"#)?;
let report = check_ast(&AstNode::Root(root));
assert!(report.diagnostics.is_empty());
Report types
Report
pub struct Report {
pub diagnostics: Vec<Diagnostic>,
pub js_escapes: Vec<JsEscape>,
}
Diagnostic
pub struct Diagnostic {
pub severity: Severity, // currently always Error
pub code: Code, // stable code: SKT001..SKT015
pub span: Span,
pub snippet: String,
pub caret: String,
pub message: String,
pub suggestion: Option<String>,
}
impl Diagnostic {
pub fn render(&self) -> String { /* formatted with source + caret */ }
}
Diagnostic codes:
| Code | Name | Description |
|---|---|---|
| SKT001 | UnknownIdent |
Unknown identifier |
| SKT002 | UnknownProp |
Unknown property |
| SKT003 | NotCallable |
Value is not callable |
| SKT004 | AssignMismatch |
Type mismatch in assignment |
| SKT005 | BadOperand |
Invalid operand for operator |
| SKT006 | BadUnaryOperand |
Invalid operand for unary operator |
| SKT007 | DuplicateDecl |
Duplicate state declaration |
| SKT008 | BadBindTarget |
@bind must target a state variable |
| SKT009 | BadEachSource |
Malformed @each source |
| SKT010 | RenderedFunction |
Function used where a value is rendered |
| SKT011 | SnippetParse |
Saho parse error inside a snippet |
| SKT012 | ConstReassign |
Cannot reassign @derived or constant |
| SKT013 | UnknownUse |
Use of a value the checker cannot resolve |
| SKT014 | ImpossibleCast |
as cast between disjoint concrete types |
| SKT015 | BadClassType |
@class expression must yield a string or array of strings |
JsEscape
One recorded js { ... } occurrence. Bodies are never typechecked.
pub struct JsEscape {
pub kind_label: String,
pub location: Option<(u32, u32)>,
pub span: Span,
pub body: String,
}
AST node types
RootNode / ElementNode / InlineNode / ListNode
pub struct RootNode<'a> {
pub name: Cow<'a, str>,
pub modifiers: Vec<Modifier<'a>>,
pub declarations: Vec<AtcodeDeclaration<'a>>,
pub children: Vec<AstNode<'a>>,
}
pub struct ElementNode<'a> {
pub name: Cow<'a, str>,
pub modifiers: Vec<Modifier<'a>>,
pub children: Vec<AstNode<'a>>,
}
pub struct InlineNode<'a> {
pub name: Cow<'a, str>,
pub modifiers: Vec<Modifier<'a>>,
pub value: InlineValue<'a>,
}
pub struct ListNode<'a> {
pub items: Vec<AstNode<'a>>,
}
pub enum AstNode<'a> {
Root(RootNode<'a>),
Element(ElementNode<'a>),
Inline(InlineNode<'a>),
List(ListNode<'a>),
}
Reactive snippets
Every reactive payload is parsed once at document-parse time into a typed
Saho node; the typechecker walks the pre-built tree (no re-parse).
pub struct ExprSnippet<'a> {
pub raw: Cow<'a, str>,
pub parsed: Option<Box<saho::Node>>,
pub errors: Vec<SnippetDiag>,
}
pub struct BlockSnippet<'a> {
pub raw: Cow<'a, str>,
pub parsed: Option<Vec<saho::Stmt>>,
pub errors: Vec<SnippetDiag>,
}
Declaration types
pub enum AtcodeDeclaration<'a> {
State { declarations: Vec<StateVar<'a>>, line: u32, col: u32 },
Derived { declarations: Vec<DerivedVar<'a>>, line: u32, col: u32 },
Effect { body: BlockSnippet<'a>, line: u32, col: u32 },
}
pub struct StateVar<'a> {
pub name: Cow<'a, str>,
pub value: ExprSnippet<'a>,
}
pub struct DerivedVar<'a> {
pub name: Cow<'a, str>,
pub expr: ExprSnippet<'a>,
}
pub struct EachSpec<'a> {
pub item: Cow<'a, str>,
pub source: ExprSnippet<'a>,
}
Re-exports
From the crate root:
// Parsing
pub use syntax::ast::{AstNode, RootNode, ElementNode, InlineNode, ListNode, Modifier, ...};
pub use syntax::lexer::tokenize;
pub use syntax::parser::parse_sakko;
pub use syntax::token::{Token, TokenKind};
// Typechecking
pub use typecheck::{check_source, check_ast, Diagnostic, JsEscape, Report};
// Sahō (expression language)
pub mod saho;
// Shared types
pub use span::{Span, LineIndex};
pub use error::{Result, SakkoError};