///|
/// Stable integer identity of one grammar rule.
pub(all) struct RuleId(Int) derive(Debug, Eq, Hash)
///|
pub fn RuleId::value(self : RuleId) -> Int {
self.0
}
///|
pub(all) struct Alternative {
rule : RuleId
weight : Int
label : String
} derive(Debug, Eq)
///|
pub fn alternative(
rule : RuleId,
weight? : Int = 1,
label? : String = "",
) -> Alternative {
{ rule, weight, label, }
}
///|
/// Composable rule kinds. References are resolved by name when the grammar is
/// sealed, which allows explicit recursion without mutable placeholder nodes.
pub(all) enum RuleKind {
Literal(String)
Bytes(Array[Int])
Integer(Int, Int)
Dictionary(Array[String])
Sequence(Array[RuleId])
Choice(Array[Alternative])
Repeat(RuleId, Int, Int)
Optional(RuleId, Int)
Reference(String)
} derive(Debug, Eq)
///|
pub(all) struct RuleDefinition {
id : RuleId
name : String?
kind : RuleKind
} derive(Debug, Eq)
///|
pub(all) enum GrammarError {
EmptyGrammar
InvalidStartRule(Int)
InvalidRuleReference(Int, Int)
MissingNamedRule(String)
DuplicateRuleName(String)
EmptyByteSet(Int)
InvalidByteValue(Int, Int)
EmptyDictionary(Int)
EmptyChoice(Int)
NonPositiveWeight(Int, Int)
InvalidRepeatBounds(Int, Int, Int)
InvalidIntegerBounds(Int, Int, Int)
InvalidOptionalWeight(Int, Int)
} derive(Debug, Eq)
///|
pub struct Grammar {
rules : Array[RuleDefinition]
start : RuleId
names : @hashmap.HashMap[String, RuleId]
}
///|
pub fn Grammar::rule_count(self : Grammar) -> Int {
self.rules.length()
}
///|
pub fn Grammar::start(self : Grammar) -> RuleId {
self.start
}
///|
pub fn Grammar::rule(self : Grammar, id : RuleId) -> RuleDefinition? {
if id.0 < 0 || id.0 >= self.rules.length() {
None
} else {
Some(self.rules[id.0])
}
}
///|
pub fn Grammar::resolve(self : Grammar, name : String) -> RuleId? {
self.names.get(name)
}
///|
pub fn Grammar::rules(self : Grammar) -> Array[RuleDefinition] {
self.rules.copy()
}
///|
pub fn Grammar::name_of(self : Grammar, id : RuleId) -> String? {
match self.rule(id) {
Some(definition) => definition.name
None => None
}
}
///|
pub struct GrammarBuilder {
rules : Array[RuleDefinition]
mut start_rule : RuleId?
}
///|
pub fn GrammarBuilder::new() -> GrammarBuilder {
{ rules: [], start_rule: None, }
}
///|
fn GrammarBuilder::append(
self : GrammarBuilder,
kind : RuleKind,
name : String?,
) -> RuleId {
let id = RuleId(self.rules.length())
self.rules.push({ id, name, kind, })
id
}
///|
pub fn GrammarBuilder::literal(
self : GrammarBuilder,
value : String,
name? : String,
) -> RuleId {
self.append(Literal(value), name)
}
///|
pub fn GrammarBuilder::bytes(
self : GrammarBuilder,
values : Array[Int],
name? : String,
) -> RuleId {
self.append(Bytes(values.copy()), name)
}
///|
pub fn GrammarBuilder::ascii_range(
self : GrammarBuilder,
first : Char,
last : Char,
name? : String,
) -> RuleId {
let values : Array[Int] = []
let low = first.to_int()
let high = last.to_int()
if low <= high {
for value = low; value <= high; value = value + 1 {
values.push(value)
}
}
self.append(Bytes(values), name)
}
///|
pub fn GrammarBuilder::integer(
self : GrammarBuilder,
minimum : Int,
maximum : Int,
name? : String,
) -> RuleId {
self.append(Integer(minimum, maximum), name)
}
///|
pub fn GrammarBuilder::dictionary(
self : GrammarBuilder,
values : Array[String],
name? : String,
) -> RuleId {
self.append(Dictionary(values.copy()), name)
}
///|
pub fn GrammarBuilder::sequence(
self : GrammarBuilder,
children : Array[RuleId],
name? : String,
) -> RuleId {
self.append(Sequence(children.copy()), name)
}
///|
pub fn GrammarBuilder::choice(
self : GrammarBuilder,
alternatives : Array[Alternative],
name? : String,
) -> RuleId {
self.append(Choice(alternatives.copy()), name)
}
///|
pub fn GrammarBuilder::repeat(
self : GrammarBuilder,
child : RuleId,
minimum? : Int = 0,
maximum? : Int = 8,
name? : String,
) -> RuleId {
self.append(Repeat(child, minimum, maximum), name)
}
///|
pub fn GrammarBuilder::optional(
self : GrammarBuilder,
child : RuleId,
present_weight? : Int = 1,
name? : String,
) -> RuleId {
self.append(Optional(child, present_weight), name)
}
///|
pub fn GrammarBuilder::reference(
self : GrammarBuilder,
target_name : String,
name? : String,
) -> RuleId {
self.append(Reference(target_name), name)
}
///|
pub fn GrammarBuilder::set_start(
self : GrammarBuilder,
start : RuleId,
) -> GrammarBuilder {
self.start_rule = Some(start)
self
}
///|
fn validate_rule_id(
id : RuleId,
owner : Int,
count : Int,
) -> Result[Unit, GrammarError] {
if id.0 < 0 || id.0 >= count {
Err(InvalidRuleReference(owner, id.0))
} else {
Ok(())
}
}
///|
fn validate_kind(
kind : RuleKind,
owner : Int,
count : Int,
names : @hashmap.HashMap[String, RuleId],
) -> Result[Unit, GrammarError] {
match kind {
Literal(_) => Ok(())
Bytes(values) => {
if values.is_empty() {
return Err(EmptyByteSet(owner))
}
for value in values {
if value < 0 || value > 255 {
return Err(InvalidByteValue(owner, value))
}
}
Ok(())
}
Integer(minimum, maximum) =>
if maximum < minimum {
Err(InvalidIntegerBounds(owner, minimum, maximum))
} else {
Ok(())
}
Dictionary(values) =>
if values.is_empty() {
Err(EmptyDictionary(owner))
} else {
Ok(())
}
Sequence(children) => {
for child in children {
match validate_rule_id(child, owner, count) {
Err(error) => return Err(error)
Ok(_) => ()
}
}
Ok(())
}
Choice(alternatives) => {
if alternatives.is_empty() {
return Err(EmptyChoice(owner))
}
for alternative in alternatives {
match validate_rule_id(alternative.rule, owner, count) {
Err(error) => return Err(error)
Ok(_) => ()
}
if alternative.weight <= 0 {
return Err(NonPositiveWeight(owner, alternative.weight))
}
}
Ok(())
}
Repeat(child, minimum, maximum) => {
match validate_rule_id(child, owner, count) {
Err(error) => return Err(error)
Ok(_) => ()
}
if minimum < 0 || maximum < minimum {
Err(InvalidRepeatBounds(owner, minimum, maximum))
} else {
Ok(())
}
}
Optional(child, weight) => {
match validate_rule_id(child, owner, count) {
Err(error) => return Err(error)
Ok(_) => ()
}
if weight <= 0 {
Err(InvalidOptionalWeight(owner, weight))
} else {
Ok(())
}
}
Reference(name) =>
if names.contains(name) {
Ok(())
} else {
Err(MissingNamedRule(name))
}
}
}
///|
pub fn GrammarBuilder::build(
self : GrammarBuilder,
) -> Result[Grammar, GrammarError] {
if self.rules.is_empty() {
return Err(EmptyGrammar)
}
let names : @hashmap.HashMap[String, RuleId] = @hashmap.HashMap([])
for definition in self.rules {
match definition.name {
None => ()
Some(name) => {
if names.contains(name) {
return Err(DuplicateRuleName(name))
}
names.set(name, definition.id)
}
}
}
let start = match self.start_rule {
Some(value) => value
None => RuleId(self.rules.length() - 1)
}
if start.0 < 0 || start.0 >= self.rules.length() {
return Err(InvalidStartRule(start.0))
}
for definition in self.rules {
match
validate_kind(
definition.kind,
definition.id.0,
self.rules.length(),
names,
) {
Err(error) => return Err(error)
Ok(_) => ()
}
}
Ok({ rules: self.rules.copy(), start, names, })
}