An XML parsing library written in MoonBit that converts XML strings into structured data, supporting elements, attributes, comments, CDATA, and processing instructions.
Dependencies
let str =
#|<?xml version="1.0"?>
#|<root>
#| Text before
#| <child empty="true"/>
#| Text between
#| <?php echo "processing instruction" ?>
#| <![CDATA[Preserving <tags> in CDATA]]>
#| <!-- A comment here -->
#| <child>
#| Nested text
#| <grandchild />
#| More nested text
#| </child>
#| Text after
#|</root>
let xml = @parser.xml_from_string(str)
let xml = @parser.xml_from_iter(str.iter())
let (xml, ctx) = @parser.xml_from_string_with_ctx(str)
let xml = xml.unwrap()
println(xml)impl Show for AttlistDeclpub(all) enum ChildrenContentSpecOp {
Optional
ZeroOrMore
OneOrMore
}impl Show for ChildrenContentSpecOppub(all) enum DefaultDecl {
Required
Implied
Fixed(String)
Value(String)
}impl Show for DefaultDeclpub(all) struct DocTypeDecl {
name : String
externalID : ExternalID?
intSubset : Array[DTDStatement]?
}impl Show for EntityDeclpub(all) enum EntityDef {
EntityValue(String)
GExternalID(ExternalID, String?)
PExternalID(ExternalID)
}pub(all) enum ExternalID {
System(String)
Public(String, String)
}impl Show for ExternalIDpub(all) struct Location {
index : Int
line : Int
column : Int
length : Int
}pub(all) enum MarkUpDecl {
ElementDecl(ElementDecl)
AttListDecl(AttlistDecl)
EntityDecl(EntityDecl)
NotationDecl(NotationDecl)
PI(String)
Comment(String)
}impl Show for MarkUpDeclpub(all) enum SingleContentParticle {
Name(String)
Seq(Array[ContentParticle])
Choice(Array[ContentParticle])
}impl Show for SingleContentParticlepub(all) enum XMLChildren {
Element(XMLElement)
Reference(String)
CDATA(String)
PI(String)
Comment(String)
Text(String)
WhiteSpace(String)
}pub(all) struct XMLDocument {
version : String
encoding : String
standalone : Bool
dtd : DocTypeDecl?
root : XMLElement
}pub(all) struct XMLElement {
name : String
empty_element : Bool
attributes : Map[String, String]
children : Array[XMLChildren]
}pub(all) enum XMLErrorKind {
SyntaxError
ValidationError
EncodingError
MalformedReference
MismatchedTags(String, String)
InternalParserError
}impl Show for XMLErrorKindpub(all) struct XMLParser[Token, Value]((Seq[Token], XMLParserContext) -> ((Value, Seq[Token])?, XMLParseError?, XMLParserContext))fn[Token, Value] XMLParser::new(f : (Seq[Token], XMLParserContext) -> ((Value, Seq[Token])?, XMLParseError?, XMLParserContext)) -> XMLParser[Token, Value]fn[Token, Value] XMLParser::run(self : XMLParser[Token, Value], seq : Seq[Token], ctx : XMLParserContext) -> ((Value, Seq[Token])?, XMLParseError?, XMLParserContext)pub struct XMLParserContext {
input_array : Array[Char]
index : Int
line_number : Int
column_number : Int
errors : Array[XMLParseError]
warnings : Array[String]
}impl Show for XMLParserContexttest {
let result = pEntityRef().run(@combinator.Seq::from_string("<"))
debug_inspect(
result,
content=(
#|Some(("<"))
),
)
}test {
let result = pattribute().run(@combinator.Seq::from_string("name=\"value\""))
debug_inspect(
result,
content=(
#|Some((("name", "value")))
),
)
}test {
let result = pattributes().run(
@combinator.Seq::from_string(" name=\"value\" type=\"text\""),
)
let attrs = result.unwrap().0
debug_inspect(attrs.get("name"), content="Some(\"value\")")
debug_inspect(attrs.get("type"), content="Some(\"text\")")
}test {
let input = "<![CDATA[Some <raw> XML & content]]>"
let result = pcdata().run(@combinator.Seq::from_string(input))
debug_inspect(
result,
content=(
#|Some(("Some <raw> XML & content"))
),
)
let input = "<![CDATA[]]>"
let result = pcdata().run(@combinator.Seq::from_string(input))
debug_inspect(
result,
content=(
#|Some((""))
),
)
}test {
let decimal = pcharRef().run(@combinator.Seq::from_string("{"))
let hex = pcharRef().run(@combinator.Seq::from_string("¥"))
debug_inspect(
decimal,
content=(
#|Some(("#123;"))
),
)
debug_inspect(
hex,
content=(
#|Some(("#A5;"))
),
)
}test {
let comment = "<!-- This is a comment -->"
let result = pcomment().run(@combinator.Seq::from_string(comment))
debug_inspect(
result,
content=(
#|Some((" This is a comment "))
),
)
}test {
let result = pdtd()
.run(@combinator.Seq::from_string("<!DOCTYPE html>"))
.unwrap().0
debug_inspect(result, content="<!DOCTYPE html>")
}test {
// Parse a simple XML element with attributes and text content
let input = "<user id=\"1\" name=\"John\">Hello, World!</user>"
let result = pelement().run(@combinator.Seq::from_string(input))
let element = result.unwrap().0
debug_inspect(
element.name,
content=(
#|"user"
),
)
debug_inspect(element.attributes.get("id"), content="Some(\"1\")")
debug_inspect(element.attributes.get("name"), content="Some(\"John\")")
}test {
// Valid XML names
let result1 = pname().run(@combinator.Seq::from_string("element1"))
debug_inspect(
result1,
content=(
#|Some(("element1"))
),
)
// Invalid XML names (starting with digit)
let result2 = pname().run(@combinator.Seq::from_string("1element"))
debug_inspect(result2, content="None")
}test {
let result = ppi().run(@combinator.Seq::from_string("<?php echo 'Hello' ?>"))
debug_inspect(
result,
content=(
#|Some(("php echo 'Hello' "))
),
)
let result = ppi().run(@combinator.Seq::from_string("<??>"))
debug_inspect(
result,
content=(
#|Some((""))
),
)
let result = ppi().run(@combinator.Seq::from_string("<?incomplete"))
debug_inspect(result, content="None")
}test {
// Entity reference
let entity = preference().run(@combinator.Seq::from_string("&"))
debug_inspect(
entity,
content=(
#|Some(("&"))
),
)
// Character reference (decimal)
let decimal = preference().run(@combinator.Seq::from_string(" "))
debug_inspect(
decimal,
content=(
#|Some(("#32;"))
),
)
// Character reference (hexadecimal)
let hex = preference().run(@combinator.Seq::from_string(" "))
debug_inspect(
hex,
content=(
#|Some(("#20;"))
),
)
}test {
let result = ptext().run(@combinator.Seq::from_string("Hello World"))
debug_inspect(
result,
content=(
#|Some(("Hello World"))
),
)
let result = ptext().run(@combinator.Seq::from_string("Text with & and <"))
debug_inspect(
result,
content=(
#|Some(("Text with ", & and <))
),
) // stops parsing at '&'
let result = ptext().run(@combinator.Seq::from_string(""))
debug_inspect(
result,
content=(
#|Some((""))
),
)
}test {
let result = pwhite_space().run(@combinator.Seq::from_string(" \t\n\rtext"))
debug_inspect(
result,
content=(
#|Some((" \t\n\r", text))
),
)
}test {
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><child>Text</child></root>"
let result = pxml().run(@combinator.Seq::from_string(xml))
let doc = result.unwrap().0
debug_inspect(
doc.version,
content=(
#|"1.0"
),
)
debug_inspect(
doc.root.name,
content=(
#|"root"
),
)
}test {
let xml = "<?xml version=\"1.0\"?><root><child>Content</child></root>"
let doc = xml_from_string(xml).unwrap()
debug_inspect(
doc.root.name,
content=(
#|"root"
),
)
}An XML parsing library written in MoonBit that converts XML strings into structured data, supporting elements, attributes, comments, CDATA, and processing instructions.
Dependencies