//This file is mainly to implement parsing DTD (Document Type Decl).
///|
fn pnmtokens() -> Parser[Char, String] {
@combinator.pchar_such_that(is_name_char)
.and_then(
@combinator.pchar_such_that(is_name_char)
.repeat()
.map(arr => arr.fold(init="", (str, c) => str + c.to_string())),
)
.map(fn(tuple) {
let (c, str) = tuple
c.to_string() + str
})
}
///|
fn ppubidLiteral() -> Parser[Char, String] {
fn is_pubid_char(c : Char) -> Bool {
match c {
'\u0020'
| '\u000D'
| 'a'..='z'
| 'A'..='Z'
| '0'..='9'
| '-'
| '\''
| '('
| ')'
| '+'
| ','
| '.'
| '/'
| ':'
| '='
| '?'
| ';'
| '!'
| '*'
| '#'
| '@'
| '$'
| '_'
| '%' => true
_ => false
}
}
let single_quote = @combinator.pchar('\'')
.and_then(
@combinator.pchar_such_that(c => is_pubid_char(c) && c != '\'')
.repeat()
.map(arr => arr.fold(init="", (str, c) => str + c.to_string())),
)
.and_then(@combinator.pchar('\''))
.omit_second()
.map(fn(tuple) {
let (quote, str) = tuple
quote.to_string() + str + quote.to_string()
})
let double_quote = @combinator.pchar('"')
.and_then(
@combinator.pchar_such_that(c => is_pubid_char(c))
.repeat()
.map(arr => arr.fold(init="", (str, c) => str + c.to_string())),
)
.and_then(@combinator.pchar('"'))
.omit_second()
.map(fn(tuple) {
let (quote, str) = tuple
quote.to_string() + str + quote.to_string()
})
single_quote.or_else(double_quote)
}
///|
fn psystemLiteral() -> Parser[Char, String] {
fn make_parser(quote : Char) {
@combinator.pchar(quote)
.and_then(@combinator.pchar_such_that(fn(c) { c != quote }).repeat())
.and_then(@combinator.pchar(quote))
.omit_second()
.map(fn(tuple) {
let (quote, arr) = tuple
arr.fold(init=quote.to_string(), (str, c) => str + c.to_string()) +
quote.to_string()
})
}
make_parser('\'').or_else(make_parser('"'))
}
///|
fn pPEReference() -> Parser[Char, String] {
@combinator.pchar('%')
.and_then(pname())
.and_then(@combinator.pchar(';'))
.map(fn(tuple) {
let ((_and, name), _semicolon) = tuple
"&" + name + ";"
})
}
///|
fn pentityValue() -> Parser[Char, String] {
fn make_parser(quote : Char) {
@combinator.pchar(quote)
.and_then(
@combinator.pchar_such_that(fn(c) { c != quote && c != '%' && c != '&' })
.map(fn(c) { c.to_string() })
.or_else(pPEReference())
.or_else(preference())
.repeat(),
)
.and_then(@combinator.pchar(quote))
.map(fn(tuple) {
let ((_, arr), _) = tuple
arr.fold(init=quote.to_string(), (str, c) => str + c) + quote.to_string()
})
}
let parser = make_parser('\'').or_else(make_parser('"'))
parser
}
///|
fn pexternalID() -> Parser[Char, ExternalID] {
let system = @combinator.pstring("SYSTEM")
.and_then(pwhite_space())
.and_then(psystemLiteral())
.omit_first()
.map(fn(str) { System(str) })
let public = @combinator.pstring("PUBLIC")
.and_then(pwhite_space())
.and_then(ppubidLiteral())
.omit_first()
.and_then(pwhite_space())
.omit_second()
.and_then(psystemLiteral())
.map(fn(tuple) {
let (pubid, system) = tuple
Public(pubid, system)
})
public.or_else(system)
}
///|
fn ppublicID() -> Parser[Char, String] {
@combinator.pstring("PUBLIC")
.and_then(pwhite_space())
.and_then(ppubidLiteral())
.omit_first()
}
///|
fn pentityDecl() -> Parser[Char, EntityDecl] {
let pNDataDecl = pwhite_space()
.and_then(@combinator.pstring("NDATA"))
.and_then(pwhite_space())
.and_then(pname())
.omit_first()
let pGEDecl = @combinator.pstring(""))
.omit_second()
.map(fn(tuple) {
let (name, value) = tuple
GEDecl(name, value)
})
let pPEDecl = @combinator.pstring(""))
.omit_second()
.map(fn(tuple) {
let (name, value) = tuple
PEDecl(name, value)
})
pGEDecl.or_else(pPEDecl)
}
///|
test "EnitytDecl" {
//
//
//
//
let str =
#|
let seq = Seq::from_string(str)
let parser = pentityDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
let str =
#|
let seq = Seq::from_string(str)
let parser = pentityDecl()
let _result = parser.run(seq).unwrap().0
let str =
#|
let seq = Seq::from_string(str)
let parser = pentityDecl()
let _result = parser.run(seq).unwrap().0
let str =
#|
let seq = Seq::from_string(str)
let parser = pentityDecl()
let _result = parser.run(seq).unwrap().0
}
///|
fn pelementDecl() -> Parser[Char, ElementDecl] {
let cp_ref : Ref[Parser[Char, ContentParticle]] = Ref::new(
@combinator.pfail(),
)
let choice = @combinator.pchar('(')
.and_then(pwhite_space().optional())
.and_then(Parser::from_ref(cp_ref))
.omit_first()
.and_then(
pwhite_space()
.optional()
.and_then(@combinator.pchar('|'))
.and_then(pwhite_space().optional())
.and_then(Parser::from_ref(cp_ref))
.omit_first()
.repeat(),
)
.and_then(pwhite_space().optional())
.omit_second()
.and_then(@combinator.pchar(')'))
.omit_second()
.map(fn(tuple) {
let (cp, arr) = tuple
arr.insert(0, cp)
arr
})
let seq = @combinator.pchar('(')
.and_then(pwhite_space().optional())
.and_then(Parser::from_ref(cp_ref))
.omit_first()
.and_then(
pwhite_space()
.optional()
.and_then(@combinator.pchar(','))
.and_then(pwhite_space().optional())
.and_then(Parser::from_ref(cp_ref))
.omit_first()
.repeat(),
)
.and_then(pwhite_space().optional())
.omit_second()
.and_then(@combinator.pchar(')'))
.omit_second()
.map(fn(tuple) {
let (cp, arr) = tuple
arr.insert(0, cp)
arr
})
let cp = pname()
.map(fn(s) { Name(s) })
.or_else(choice.map(fn(arr) { Choice(arr) }))
.or_else(seq.map(fn(arr) { Seq(arr) }))
.and_then(
@combinator.pchar_such_that(ch => ch == '*' || ch == '+' || ch == '?').optional(),
)
.map(fn(tuple) {
let (cp, ch) = tuple
let pair = match ch {
Some('*') => (cp, Some(ZeroOrMore))
Some('+') => (cp, Some(OneOrMore))
Some('?') => (cp, Some(Optional))
None => (cp, None)
_ => abort("unreachable")
}
ContentParticle(pair.0, pair.1)
})
cp_ref.val = cp
let children : Parser[Char, ChildrenContentSpec] = choice
.map(fn(arr) { Choice(arr) })
.or_else(seq.map(fn(arr) { Seq(arr) }))
.and_then(
@combinator.pchar_such_that(ch => ch == '*' || ch == '+' || ch == '?').optional(),
)
.map(fn(tuple) {
let (cp, ch) = tuple
let pair = match ch {
Some('*') => (cp, Some(ZeroOrMore))
Some('+') => (cp, Some(OneOrMore))
Some('?') => (cp, Some(Optional))
None => (cp, None)
_ => abort("unreachable")
}
ContentParticle(pair.0, pair.1)
})
let mixed = @combinator.pstring("(")
.and_then(pwhite_space().optional())
.and_then(@combinator.pstring("#PCDATA"))
.and_then(pwhite_space().optional())
.and_then(@combinator.pstring(")"))
.map(fn(_tuple) { Mixed([]) })
.or_else(
@combinator.pstring("(")
.and_then(pwhite_space().optional())
.and_then(@combinator.pstring("#PCDATA"))
.and_then(
pwhite_space()
.optional()
.and_then(@combinator.pchar('|'))
.and_then(pwhite_space().optional())
.and_then(pname())
.omit_first()
.repeat(),
)
.omit_first()
.and_then(pwhite_space().optional())
.omit_second()
.and_then(@combinator.pstring(")*"))
.omit_second()
.map(fn(arr) { Mixed(arr) }),
)
let contentspec = @combinator.pstring("EMPTY")
.map(fn(_s) { EMPTY })
.or_else(@combinator.pstring("ANY").map(fn(_s) { ANY }))
.or_else(mixed)
.or_else(children.map(fn(child) { Children(child) }))
let parser = @combinator.pstring(""))
.omit_second()
.map(fn(tuple) {
let (name, content_spec) = tuple
// println(tuple)
{ name, content_spec }
})
return parser
}
///|
test "ElementDecl" {
//
//
// don't support
//
//
//
//
//
//
//
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
inspect(result.name, content="br")
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
// let str = #|
// let seq = Seq::from_string(str)
// let result = parser.run(seq).unwrap().0
// println(result)
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result.name, content="spec")
inspect(result, content=str)
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result.name, content="div1")
inspect(result, content=str)
// let str = #|
// let seq = Seq::from_string(str)
// let parser = pelementDecl()
// let result = parser.run(seq).unwrap().0
// println(result)
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result.name, content="p")
inspect(result, content=str)
// let str = #|
// let seq = Seq::from_string(str)
// let parser = pelementDecl()
// let result = parser.run(seq).unwrap().0
// println(result)
let str =
#|
let seq = Seq::from_string(str)
let parser = pelementDecl()
let result = parser.run(seq).unwrap().0
inspect(result.name, content="b")
inspect(result, content=str)
// let str = #|
// let seq = Seq::from_string(str)
// let parser = pelementDecl()
// let result = parser.run(seq).unwrap().0
// println(result)
}
///|
fn pattListDecl() -> Parser[Char, AttlistDecl] {
fn pattType() -> Parser[Char, AttType] {
let notationType = @combinator.pstring("NOTATION")
.and_then(pwhite_space())
.and_then(@combinator.pchar('('))
.and_then(pwhite_space().optional())
.and_then(pname())
.omit_first()
.and_then(
pwhite_space()
.optional()
.and_then(@combinator.pchar('|'))
.and_then(pwhite_space().optional())
.and_then(pname())
.omit_first()
.repeat(),
)
.and_then(pwhite_space().optional())
.omit_second()
.and_then(@combinator.pchar(')'))
.omit_second()
.map(fn(tuple) {
let (str, arr) = tuple
arr.insert(0, str)
NotationType(arr)
})
let enumeration = @combinator.pchar('(')
.and_then(pwhite_space().optional())
.and_then(pnmtokens())
.omit_first()
.and_then(
pwhite_space()
.optional()
.and_then(@combinator.pchar('|'))
.and_then(pwhite_space().optional())
.and_then(pnmtokens())
.omit_first()
.repeat(),
)
.and_then(pwhite_space().optional())
.omit_second()
.and_then(@combinator.pchar(')'))
.omit_second()
.map(fn(tuple) {
let (str, arr) = tuple
arr.insert(0, str)
Enumeration(arr)
})
let stringType = @combinator.pstring("CDATA").map(fn(str) {
StringType(str)
})
let tokenizedType = @combinator.pstring("IDREFS")
.or_else(@combinator.pstring("IDREF"))
.or_else(@combinator.pstring("ID"))
.or_else(@combinator.pstring("ENTITY"))
.or_else(@combinator.pstring("ENTITIES"))
.or_else(@combinator.pstring("NMTOKENS"))
.or_else(@combinator.pstring("NMTOKEN"))
.map(fn(str) { TokenizedType(str) })
let attType = stringType
.or_else(tokenizedType)
.or_else(notationType)
.or_else(enumeration)
attType
}
fn pdefaultDecl() -> Parser[Char, DefaultDecl] {
let required = @combinator.pstring("#REQUIRED").map(fn(_str) { Required })
let implied = @combinator.pstring("#IMPLIED").map(fn(_str) { Implied })
let fixed = @combinator.pstring("#FIXED")
.and_then(pwhite_space())
.and_then(pattValue())
.map(fn(tuple) {
let (_, str) = tuple
Fixed(str)
})
let value = pattValue().map(fn(str) { Value(str) })
required.or_else(implied).or_else(fixed).or_else(value)
}
let pattDef = pwhite_space()
.and_then(pname())
.omit_first()
.and_then(pwhite_space())
.omit_second()
.and_then(pattType())
.and_then(pwhite_space())
.omit_second()
.and_then(pdefaultDecl())
.map(fn(tuple) {
let ((name, att_type), default_decl) = tuple
{ name, att_type, default_decl }
})
let parser = @combinator.pstring(""))
.omit_second()
.map(fn(tuple) {
let (name, att_defs) = tuple
{ name, att_defs }
})
parser
}
///|
test "attListDecl" {
//
//
//
let parser = pattListDecl()
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap()
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap().0
inspect(result.name, content="termdef")
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap().0
inspect(result.name, content="list")
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap().0
inspect(result.name, content="form")
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap().0
inspect(result.name, content="poem")
let str =
#|
let seq = Seq::from_string(str)
let result = parser.run(seq).unwrap().0
inspect(result.name, content="pre")
}
///|
fn pnotationDecl() -> Parser[Char, NotationDecl] {
@combinator.pstring(""))
.omit_second()
.map(fn(tuple) {
let (name, id) = tuple
{ name, id }
})
}
///|
test "NotationDecl" {
//
//
let str =
#|
let seq = Seq::from_string(str)
let parser = pnotationDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
let str =
#|
let seq = Seq::from_string(str)
let parser = pnotationDecl()
let result = parser.run(seq).unwrap().0
inspect(result, content=str)
}
///|
fn pmarkupDecl() -> Parser[Char, MarkUpDecl] {
pattListDecl()
.map(fn(attlist) { AttListDecl(attlist) })
.or_else(pelementDecl().map(fn(element) { ElementDecl(element) }))
.or_else(pentityDecl().map(fn(entity) { EntityDecl(entity) }))
.or_else(pnotationDecl().map(fn(notation) { NotationDecl(notation) }))
.or_else(ppi().map(fn(pi) { MarkUpDecl::PI(pi) }))
.or_else(pcomment().map(fn(comment) { MarkUpDecl::Comment(comment) }))
}
///|
fn pinternalSubset() -> Parser[Char, Array[DTDStatement]] {
let declSep = pPEReference().or_else(pwhite_space()).map(fn(s) { Sep(s) })
let parser = pmarkupDecl()
.map(fn(decl) { Decl(decl) })
.or_else(declSep)
.repeat()
parser
}
///|
fn pmisc() -> Parser[Char, Misc] {
pcomment()
.map(fn(comment) { Misc::Comment(comment) })
.or_else(ppi().map(fn(pi) { Misc::PI(pi) }))
.or_else(pwhite_space().map(fn(s) { Misc::WhiteSpace(s) }))
}