///|
pub fn vald(tree : Tree) -> Vctx {
{ tree, digs: [] }
}
///|
pub fn Vctx::chec(self : Vctx, ok : Bool, msg : String) -> Vctx {
if ok {
self
} else {
self.fail(msg)
}
}
///|
pub fn Vctx::warn(self : Vctx, msg : String) -> Vctx {
let digs = self.digs.copy()
digs.push(warn(msg))
{ tree: self.tree, digs }
}
///|
pub fn Vctx::fail(self : Vctx, msg : String) -> Vctx {
let digs = self.digs.copy()
digs.push(fail(msg))
{ tree: self.tree, digs }
}
///|
pub fn chec(v : Vctx, ok : Bool, msg : String) -> Vctx {
v.chec(ok, msg)
}
///|
pub fn vspc(value : Dslx) -> Array[Diag] {
let spec = value.spec
let digs : Array[Diag] = []
if spec.name.length() == 0 {
digs.push(fail("empty spec name"))
} else if !vidx(spec.name) {
digs.push(fail("invalid generated name").hint("spec:" + spec.name))
}
if spec.nods.length() == 0 {
digs.push(warn("spec has no node"))
}
if spec.ruls.length() == 0 {
digs.push(warn("spec has no rule"))
}
let nodn : Array[String] = []
for node in spec.nods {
if node.name.length() == 0 {
digs.push(fail("empty node name"))
} else if !vidx(node.name) {
digs.push(fail("invalid generated name").hint("node:" + node.name))
}
if hasx(nodn, node.name) {
digs.push(fail("duplicate node name").hint(node.name))
}
nodn.push(node.name)
if node.casx.length() == 0 {
digs.push(warn("node has no case").hint(node.name))
}
let casn : Array[String] = []
for item in node.casx {
if item.name.length() == 0 {
digs.push(fail("empty case name").hint(node.name))
} else if !vidx(item.name) {
digs.push(fail("invalid generated name").hint("case:" + item.name))
}
if hasx(casn, item.name) {
digs.push(fail("duplicate case name").hint(node.name + "." + item.name))
}
casn.push(item.name)
if item.flds.length() == 0 {
digs.push(warn("case has no field").hint(item.name))
}
let fldn : Array[String] = []
for fld in item.flds {
if fld.name.length() == 0 {
digs.push(fail("empty field name").hint(item.name))
} else if !vidx(fld.name) {
digs.push(fail("invalid generated name").hint("field:" + fld.name))
}
if hasx(fldn, fld.name) {
digs.push(
fail("duplicate field name").hint(item.name + "." + fld.name),
)
}
fldn.push(fld.name)
}
}
}
let ruln : Array[String] = []
for rule in spec.ruls {
if rule.name.length() == 0 {
digs.push(fail("empty rule name"))
} else if !vidx(rule.name) {
digs.push(fail("invalid generated name").hint("rule:" + rule.name))
}
if hasx(ruln, rule.name) {
digs.push(fail("duplicate rule name").hint(rule.name))
}
ruln.push(rule.name)
if rule.expr is Atom {
digs.push(warn("rule has no body").hint(rule.name))
}
vexx(rule.expr, spec.ruls, digs)
cyrx(rule.expr, spec.ruls, [rule.name], digs)
}
digs
}
///|
fn vexx(expr : Expr, rules : Array[Rule], digs : Array[Diag]) -> Unit {
match expr {
Seqx(items) => {
if items.length() == 0 {
digs.push(fail("empty seqx").hint("add at least one expression"))
}
for item in items {
vexx(item, rules, digs)
}
}
Altx(items) => {
if items.length() == 0 {
digs.push(fail("empty altx").hint("add at least one alternative"))
}
for item in items {
vexx(item, rules, digs)
}
}
Many(item) => vexx(item, rules, digs)
Optx(item) => vexx(item, rules, digs)
Rgxx(patt) =>
if !sprg(patt) {
digs.push(fail("unsupported rgxx").hint(patt))
}
Refx(name) =>
if name.length() == 0 {
digs.push(fail("empty refx name"))
} else if frul(rules, name) is None {
digs.push(fail("undefined refx").hint(name))
}
Litx(_) | Tokn(_) | Atom => ()
}
}
///|
fn sprg(patt : String) -> Bool {
patt == "[0-9]+" || patt == "[a-zA-Z_][a-zA-Z0-9_]*"
}
///|
fn vidx(name : String) -> Bool {
if name.length() == 0 || !nsta(name[0].to_int()) {
return false
}
if keyw(name) {
return false
}
for idx in 1.. Bool {
match name {
"as"
| "async"
| "break"
| "const"
| "continue"
| "else"
| "enum"
| "false"
| "fn"
| "for"
| "if"
| "impl"
| "import"
| "in"
| "let"
| "loop"
| "match"
| "mut"
| "priv"
| "pub"
| "return"
| "struct"
| "trait"
| "true"
| "type"
| "while" => true
_ => false
}
}
///|
fn cyrx(
expr : Expr,
rules : Array[Rule],
stkx : Array[String],
digs : Array[Diag],
) -> Unit {
match expr {
Seqx(items) | Altx(items) =>
for item in items {
cyrx(item, rules, stkx, digs)
}
Many(item) | Optx(item) => cyrx(item, rules, stkx, digs)
Refx(name) =>
if hasx(stkx, name) {
digs.push(fail("refx cycle").hint(ptxt(stkx, name)))
} else {
match frul(rules, name) {
Some(item) => {
let next = stkx.copy()
next.push(name)
cyrx(item, rules, next, digs)
}
None => ()
}
}
Litx(_) | Tokn(_) | Rgxx(_) | Atom => ()
}
}
///|
fn hasx(items : Array[String], name : String) -> Bool {
for item in items {
if item == name {
return true
}
}
false
}
///|
fn ptxt(stkx : Array[String], name : String) -> String {
let mut out = ""
for item in stkx {
if out.length() > 0 {
out = out + " -> "
}
out = out + item
}
if out.length() > 0 {
out = out + " -> "
}
out + name
}
///|
pub fn diag(kind : String, text : String) -> Diag {
{ kind, text, span: None, hint: None }
}
///|
pub fn fail(text : String) -> Diag {
diag("fail", text)
}
///|
pub fn warn(text : String) -> Diag {
diag("warn", text)
}
///|
pub fn Diag::span(self : Diag, span : Span) -> Diag {
{ kind: self.kind, text: self.text, span: Some(span), hint: self.hint }
}
///|
pub fn Diag::hint(self : Diag, hint : String) -> Diag {
{ kind: self.kind, text: self.text, span: self.span, hint: Some(hint) }
}
///|
pub fn hint(text : String) -> String {
text
}