///|
priv enum RuleChain {
BlockContinue
BlockStart
Inline
Delimiter
Core
} derive(Eq)
///|
/// A positive count of inline source characters consumed by a matched rule.
pub struct ConsumedChars {
priv value : Int
}
///|
/// Creates a positive consumed-character count.
pub fn ConsumedChars::ConsumedChars(value : Int) -> ConsumedChars {
if value <= 0 {
abort("ConsumedChars must be positive")
}
{ value, }
}
///|
/// Returns the consumed-character count.
pub fn ConsumedChars::to_int(self : ConsumedChars) -> Int {
self.value
}
///|
/// A positive count of block source lines consumed by a matched rule.
pub struct ConsumedLines {
priv value : Int
}
///|
/// Creates a positive consumed-line count.
pub fn ConsumedLines::ConsumedLines(value : Int) -> ConsumedLines {
if value <= 0 {
abort("ConsumedLines must be positive")
}
{ value, }
}
///|
/// Returns the consumed-line count.
pub fn ConsumedLines::to_int(self : ConsumedLines) -> Int {
self.value
}
///|
pub(all) enum InlineRuleResult {
NoMatch
Match(ConsumedChars)
}
///|
pub(all) enum BlockRuleResult {
NoMatch
Match(ConsumedLines, EventFragment)
}
///|
/// A block rule consumes source lines and returns events for accepted matches.
pub type BlockRuleFn = (BlockContext) -> BlockRuleResult
///|
/// An inline rule consumes characters from inline source and emits events for accepted matches.
pub type InlineRuleFn = (InlineContext, Sink) -> InlineRuleResult
///|
/// A core rule wraps a downstream sink with stream transformation behavior.
pub type CoreRuleFn = (Sink) -> Sink
///|
/// An atomic parser entry installed into the rule registry.
priv struct RuleEntry {
name : RuleName
chain : RuleChain
before : Array[RuleName]
after : Array[RuleName]
priority : Int
order : Int
triggers : String
block : BlockRuleFn?
inline : InlineRuleFn?
core : CoreRuleFn?
}
///|
priv enum InlineParseMode {
CustomInlinePipeline
}
///|
priv enum RuleOp {
RegisterRule(RuleEntry)
DisableRule(RuleName)
EnableRule(RuleName)
ReplaceRule(RuleEntry)
}
///|
/// A composable Markdown syntax rule.
///
/// A rule can be a single block, inline, delimiter, or core parser entry, or a
/// larger rule made by composing smaller rules. Rules preserve operation order
/// when composed, so a rule can model one extension or a whole rule.
pub struct Rule {
priv name : String
priv ops : Array[RuleOp]
}
///|
/// Creates an empty composable rule.
pub fn Rule::Rule(name : String) -> Rule {
{ name, ops: [] }
}
///|
/// Returns the rule name.
pub fn Rule::name(self : Rule) -> String {
self.name
}
///|
/// Creates a block-start rule.
pub fn Rule::block(
name : RuleName,
priority? : Int = 0,
before? : Array[RuleName] = [],
after? : Array[RuleName] = [],
triggers? : String = "",
f : BlockRuleFn,
) -> Rule {
Rule::from_entry({
name,
chain: BlockStart,
before: copy_rule_names(before),
after: copy_rule_names(after),
priority,
order: 0,
triggers,
block: Some(f),
inline: None,
core: None,
})
}
///|
/// Creates a block-continuation rule.
///
/// Continuation rules run before block-start rules at each source line.
pub fn Rule::block_continue(
name : RuleName,
priority? : Int = 0,
before? : Array[RuleName] = [],
after? : Array[RuleName] = [],
triggers? : String = "",
f : BlockRuleFn,
) -> Rule {
Rule::from_entry({
name,
chain: BlockContinue,
before: copy_rule_names(before),
after: copy_rule_names(after),
priority,
order: 0,
triggers,
block: Some(f),
inline: None,
core: None,
})
}
///|
/// Creates an inline rule.
pub fn Rule::inline(
name : RuleName,
priority? : Int = 0,
before? : Array[RuleName] = [],
after? : Array[RuleName] = [],
triggers? : String = "",
f : InlineRuleFn,
) -> Rule {
Rule::from_entry({
name,
chain: Inline,
before: copy_rule_names(before),
after: copy_rule_names(after),
priority,
order: 0,
triggers,
block: None,
inline: Some(f),
core: None,
})
}
///|
/// Creates a delimiter rule.
///
/// Delimiter rules run in the inline phase before ordinary inline rules.
pub fn Rule::delimiter(
name : RuleName,
priority? : Int = 0,
before? : Array[RuleName] = [],
after? : Array[RuleName] = [],
triggers? : String = "",
f : InlineRuleFn,
) -> Rule {
Rule::from_entry({
name,
chain: Delimiter,
before: copy_rule_names(before),
after: copy_rule_names(after),
priority,
order: 0,
triggers,
block: None,
inline: Some(f),
core: None,
})
}
///|
/// Creates a core event rule.
pub fn Rule::core(
name : RuleName,
priority? : Int = 0,
before? : Array[RuleName] = [],
after? : Array[RuleName] = [],
f : CoreRuleFn,
) -> Rule {
Rule::from_entry({
name,
chain: Core,
before: copy_rule_names(before),
after: copy_rule_names(after),
priority,
order: 0,
triggers: "",
block: None,
inline: None,
core: Some(f),
})
}
///|
fn Rule::from_entry(entry : RuleEntry) -> Rule {
{ name: entry.name.to_string(), ops: [RegisterRule(entry)] }
}
///|
/// Appends all operations from `rule` to this rule.
pub fn Rule::extend(self : Rule, rule : Rule) -> Rule {
let ops = copy_rule_ops(self.ops)
for op in rule.ops {
ops.push(copy_rule_op(op))
}
{ name: self.name, ops }
}
///|
/// Disables a named parser entry when this rule is installed.
pub fn Rule::disable(self : Rule, name : RuleName) -> Rule {
let ops = copy_rule_ops(self.ops)
ops.push(DisableRule(name))
{ name: self.name, ops }
}
///|
/// Re-enables a named parser entry when this rule is installed.
pub fn Rule::enable(self : Rule, name : RuleName) -> Rule {
let ops = copy_rule_ops(self.ops)
ops.push(EnableRule(name))
{ name: self.name, ops }
}
///|
/// Replaces parser entries with the same names as entries in `rule`.
///
/// Replacements are remembered by the registry, so they apply to both current
/// and later matching entries from composed rules. If no base entry is ever
/// registered, the replacement rule still participates as an ordinary rule.
pub fn Rule::replace(self : Rule, rule : Rule) -> Rule {
let ops = copy_rule_ops(self.ops)
for op in rule.ops {
match op {
RegisterRule(entry) => ops.push(ReplaceRule(copy_rule_entry(entry)))
ReplaceRule(entry) => ops.push(ReplaceRule(copy_rule_entry(entry)))
DisableRule(name) => ops.push(DisableRule(name))
EnableRule(name) => ops.push(EnableRule(name))
}
}
{ name: self.name, ops }
}
///|