///|
enum PrecType {
NonAssoc
Left
Right
Dynamic
} derive(Eq, Hash)
///|
impl Show for PrecType with to_string(self) {
match self {
PrecType::NonAssoc => "PREC"
PrecType::Left => "PREC_LEFT"
PrecType::Right => "PREC_RIGHT"
PrecType::Dynamic => "PREC_DYNAMIC"
}
}
///|
impl Show for PrecType with output(self, logger) {
logger.write_string(self.to_string())
}
///|
impl ToJson for PrecType with to_json(self) {
self.to_string().to_json()
}
///|
enum PrecValue {
Number(Int)
String(String)
} derive(Show, Hash, Eq)
///|
impl ToJson for PrecValue with to_json(self : PrecValue) -> Json {
match self {
PrecValue::Number(value) => value.to_json()
PrecValue::String(value) => value.to_json()
}
}
///|
impl @json.FromJson for PrecValue with from_json(
json : Json,
json_path : @json.JsonPath,
) -> PrecValue raise @json.JsonDecodeError {
match json {
Number(value, ..) => PrecValue::Number(value.to_int())
String(value) => PrecValue::String(value)
_ => {
let message = "PrecValue::from_json: expected number or string, got \{json}"
raise @json.JsonDecodeError((json_path, message))
}
}
}
///|
pub enum Rule {
Alias(named~ : Bool, String, Rule)
Blank
Symbol(String)
String(String)
Repeat(Rule)
Repeat1(Rule)
Choice(Array[Rule])
Seq(Array[Rule])
Field(name~ : String, Rule)
Token(immediate~ : Bool, Rule)
Prec(prec~ : PrecType, value~ : PrecValue, Rule)
Pattern(String)
} derive(Show, Hash, Eq)
///|
fn[E : Error] Rule::visit_rule(
self : Rule,
visitor : (Rule) -> Unit raise E,
) -> Unit raise E {
visitor(self)
match self {
Alias(_, rule, ..) => rule.visit_rule(visitor)
Prec(rule, ..) => rule.visit_rule(visitor)
Token(rule, ..) => rule.visit_rule(visitor)
Field(rule, ..) => rule.visit_rule(visitor)
Seq(rules) | Choice(rules) =>
for rule in rules {
rule.visit_rule(visitor)
}
Repeat1(rule) | Repeat(rule) => rule.visit_rule(visitor)
String(_) => ()
Symbol(_) => ()
Blank => ()
Pattern(_) => ()
}
}
///|
impl @json.FromJson for Rule with from_json(
json : Json,
json_path : @json.JsonPath,
) -> Rule raise @json.JsonDecodeError {
match json {
{
"type": "ALIAS",
"named": True
| False as named,
"value": String(value),
"content": rule,
..
} =>
Rule::Alias(
named=@json.from_json(named),
value,
Rule::from_json(rule, json_path.add_key("content")),
)
{ "type": "BLANK", .. } => Rule::Blank
{ "type": "SYMBOL", "name": String(name), .. } => Rule::Symbol(name)
{ "type": "STRING", "value": String(value), .. } => Rule::String(value)
{ "type": "CHOICE", "members": Array(members_json), .. } => {
let members_json_path = json_path.add_key("members")
let rules = []
for i, member_json in members_json {
rules.push(Rule::from_json(member_json, members_json_path.add_index(i)))
}
Rule::Choice(rules)
}
{ "type": "SEQ", "members": Array(members_json), .. } => {
let members_json_path = json_path.add_key("members")
let rules = []
for i, member_json in members_json {
rules.push(Rule::from_json(member_json, members_json_path.add_index(i)))
}
Rule::Seq(rules)
}
{ "type": "REPEAT", "content": rule, .. } =>
Rule::Repeat(Rule::from_json(rule, json_path.add_key("content")))
{ "type": "REPEAT1", "content": rule, .. } =>
Rule::Repeat1(Rule::from_json(rule, json_path.add_key("content")))
{ "type": "TOKEN", "content": rule, .. } =>
Rule::Token(
immediate=false,
Rule::from_json(rule, json_path.add_key("content")),
)
{ "type": "IMMEDIATE_TOKEN", "content": rule, .. } =>
Rule::Token(
immediate=true,
Rule::from_json(rule, json_path.add_key("content")),
)
{ "type": "FIELD", "name": String(name), "content": rule, .. } =>
Rule::Field(name~, Rule::from_json(rule, json_path.add_key("content")))
{
"type": String([.. "PREC", .. suffix]),
"value": value,
"content": rule,
..
} => {
let prec = match suffix {
[.. ""] => PrecType::NonAssoc
[.. "_LEFT"] => PrecType::Left
[.. "_RIGHT"] => PrecType::Right
[.. "_DYNAMIC"] => PrecType::Dynamic
suffix => {
let message = "Rule::from_json: invalid precedence type \"PREC\{suffix}\""
raise @json.JsonDecodeError((json_path, message))
}
}
Rule::Prec(
prec~,
value=value |> @json.from_json(path=json_path.add_key("value")),
Rule::from_json(rule, json_path.add_key("content")),
)
}
{ "type": "PATTERN", "value": String(value), .. } => Rule::Pattern(value)
{ "type": String(type_), .. } => {
let message = "Rule::from_json: unknown rule type \"\{type_}\""
raise @json.JsonDecodeError((json_path, message))
}
{ "type": type_, .. } => {
let message = "Rule::from_json: expected object with \"type\" field, got \{type_}"
raise @json.JsonDecodeError((json_path, message))
}
json => {
let message = "Rule::from_json: expected object with \"type\" field, got \{json}"
raise @json.JsonDecodeError((json_path, message))
}
}
}
///|
impl ToJson for Rule with to_json(self : Rule) -> Json {
match self {
Alias(named~, value, rule) =>
{
"type": "ALIAS",
"value": value.to_json(),
"named": named.to_json(),
"content": rule.to_json(),
}
Prec(prec~, value~, rule) =>
{
"type": prec.to_json(),
"value": value.to_json(),
"content": rule.to_json(),
}
Token(immediate~, rule) => {
let token_type = if immediate { "IMMEDIATE_TOKEN" } else { "TOKEN" }
{ "type": token_type.to_json(), "content": rule.to_json() }
}
Field(name~, rule) =>
{ "type": "FIELD", "name": Json::string(name), "content": rule.to_json() }
Seq(rules) => { "type": "SEQ", "members": rules.to_json() }
Choice(rules) => { "type": "CHOICE", "members": rules.to_json() }
Repeat1(rule) => { "type": "REPEAT1", "content": rule.to_json() }
Repeat(rule) => { "type": "REPEAT", "content": rule.to_json() }
String(value) => { "type": "STRING", "value": value.to_json() }
Symbol(name) => { "type": "SYMBOL", "name": name.to_json() }
Blank => { "type": "BLANK" }
Pattern(value) => { "type": "PATTERN", "value": value.to_json() }
}
}
///|
struct RuleName {
name : String
mut rule : Rule?
} derive(Show)
///|
impl @json.FromJson for RuleName with from_json(
json : Json,
json_path : @json.JsonPath,
) -> RuleName raise @json.JsonDecodeError {
guard json is String(name) else {
let message = "RuleName::from_json: expected string, got \{json}"
raise @json.JsonDecodeError((json_path, message))
}
{ name, rule: None }
}
///|
impl ToJson for RuleName with to_json(self : RuleName) -> Json {
self.name.to_json()
}
///|
pub struct Grammar {
schema : String
name : String
rules : Map[String, Rule]
extras : Array[Rule]
supertypes : Array[RuleName]
reserved : Map[String, Rule]
precedences : Array[Array[Rule]]
externals : Array[Rule]
inline : Array[RuleName]
conflicts : Array[Array[RuleName]]
word : RuleName?
} derive (
@json.FromJson(fields(schema(rename="$schema"), reserved(default={}))),
Show,
ToJson(fields(schema(rename="$schema"))),
)
///|
suberror GrammarParseError {
JsonDecodeError(@json.JsonDecodeError)
RuleNotFound(String)
} derive(Show)
///|
pub fn Grammar::from_json(json : Json) -> Grammar raise GrammarParseError {
let grammar : Grammar = @json.from_json(json) catch {
error => raise JsonDecodeError(error)
}
for _, rule in grammar.rules {
let visitor = fn(rule : Rule) -> Unit raise GrammarParseError {
match rule {
Symbol(name) =>
if grammar.rules.get(name) is None {
raise RuleNotFound(name)
}
_ => return
}
}
rule.visit_rule(visitor)
}
for supertype in grammar.supertypes {
guard grammar.rules.get(supertype.name) is Some(rule) else {
raise RuleNotFound(supertype.name)
}
supertype.rule = Some(rule)
}
for inline in grammar.inline {
guard grammar.rules.get(inline.name) is Some(rule) else {
raise RuleNotFound(inline.name)
}
inline.rule = Some(rule)
}
for conflict in grammar.conflicts {
for rule_name in conflict {
guard grammar.rules.get(rule_name.name) is Some(rule) else {
raise RuleNotFound(rule_name.name)
}
rule_name.rule = Some(rule)
}
}
if grammar.word is Some(word) {
guard grammar.rules.get(word.name) is Some(rule) else {
raise RuleNotFound(word.name)
}
word.rule = Some(rule)
}
grammar
}