///|
/// The kind of a token in the lossless syntax tree.
pub(all) enum TokenKind {
Keyword
Identifier
Arrow
Modifier
Colon
Comma
Number
Text
ConfigOpen
Config
ConfigClose
OpenBrace
CloseBrace
Whitespace
Newline
Separator
Comment
Directive
FrontMatter
Unknown
} derive(Eq, Debug)
///|
pub extend TokenKind with Eq::{not_equal, equal}
///|
pub extend TokenKind with @debug.Debug::{to_repr}
///|
/// One token. `offset` is the UTF-16 offset of its first code unit in the
/// source. The concatenated token texts of a tree are equal to the source.
pub struct Token {
kind : TokenKind
text : String
offset : Int
} derive(Eq, Debug)
///|
pub extend Token with Eq::{not_equal, equal}
///|
pub extend Token with @debug.Debug::{to_repr}
///|
/// A token.
pub fn Token::new(kind : TokenKind, text : String, offset : Int) -> Token {
{ kind, text, offset }
}
///|
/// The kind of a node. A statement is one node. A frame, such as `loop` or
/// `box`, is a `Block` node with a `BlockStart`, its statements, a `Section`
/// node for each `else`, `and` or `option`, and a `BlockEnd`. Whitespace,
/// line breaks, separators, comments, directives and front matter are tokens
/// of the parent node.
pub(all) enum NodeKind {
Document
Header
Participant
Destroy
Activate
Deactivate
Note
Link
Links
Properties
Details
Title
AccTitle
AccDescr
Autonumber
Message
Block
BlockStart
Section
BlockEnd
Error
} derive(Eq, Debug)
///|
pub extend NodeKind with Eq::{not_equal, equal}
///|
pub extend NodeKind with @debug.Debug::{to_repr}
///|
/// One element of a node: a token or a child node.
pub(all) enum Element {
Token(Token)
Node(Node)
} derive(Eq, Debug)
///|
pub extend Element with Eq::{not_equal, equal}
///|
pub extend Element with @debug.Debug::{to_repr}
///|
/// A node of the lossless syntax tree.
pub struct Node {
kind : NodeKind
children : ReadOnlyArray[Element]
} derive(Eq, Debug)
///|
pub extend Node with Eq::{not_equal, equal}
///|
pub extend Node with @debug.Debug::{to_repr}
///|
/// A node. The node keeps a copy of `children`.
pub fn Node::new(kind : NodeKind, children : ArrayView[Element]) -> Node {
{ kind, children: ReadOnlyArray::from_array(children) }
}
///|
/// All tokens below this node, in source order.
pub fn Node::tokens(self : Node) -> Array[Token] {
let out = []
fn walk(node : Node) {
for child in node.children {
match child {
Token(t) => out.push(t)
Node(n) => walk(n)
}
}
}
walk(self)
out
}
///|
/// The direct child nodes of this node.
pub fn Node::nodes(self : Node) -> Array[Node] {
let out = []
for child in self.children {
if child is Node(n) {
out.push(n)
}
}
out
}
///|
/// The source text of this node.
pub fn Node::text(self : Node) -> String {
let buf = StringBuilder()
for t in self.tokens() {
buf.write_string(t.text)
}
buf.to_string()
}
///|
/// The UTF-16 offsets of the first code unit of this node and of the code unit
/// after its last token. A node with no tokens gives `None`.
pub fn Node::range(self : Node) -> (Int, Int)? {
let tokens = self.tokens()
guard tokens.last() is Some(last) else { return None }
Some((tokens[0].offset, last.offset + last.text.length()))
}
///|
/// The position of the UTF-16 offset `offset` in `source`. Lines and columns
/// start at 1. `\r\n`, `\n` and a single `\r` each end a line.
pub fn position(source : String, offset : Int) -> @diagrams.Position {
let mut line = 1
let mut start = 0
let mut i = 0
while i < offset && i < source.length() {
let c = source.at(i).to_int()
if c == '\n'.to_int() {
line += 1
start = i + 1
} else if c == '\r'.to_int() {
if i + 1 < source.length() && source.at(i + 1).to_int() == '\n'.to_int() {
i += 1
}
line += 1
start = i + 1
}
i += 1
}
{ offset, line, column: offset - start + 1 }
}
///|
/// The span from the UTF-16 offset `start` to `end` in `source`.
pub fn span(source : String, start : Int, end : Int) -> @diagrams.Span {
{ start: position(source, start), end: position(source, end) }
}