-module(xmlrat_generic_parse). -export([parse/1,file/1]). -define(p_anything,true). -define(p_charclass,true). -define(p_choose,true). -define(p_label,true). -define(p_not,true). -define(p_one_or_more,true). -define(p_optional,true). -define(p_regexp,true). -define(p_scan,true). -define(p_seq,true). -define(p_string,true). -define(p_zero_or_more,true). -include_lib("xmlrat/include/records.hrl"). -spec file(file:name()) -> any(). file(Filename) -> case file:read_file(Filename) of {ok,Bin} -> parse(Bin); Err -> Err end. -spec parse(binary() | list()) -> any(). parse(List) when is_list(List) -> parse(unicode:characters_to_binary(List)); parse(Input) when is_binary(Input) -> _ = setup_memo(), Result = case 'document'(Input,{{line,1},{column,1}}) of {AST, <<>>, _Index} -> AST; Any -> Any end, release_memo(), Result. -spec 'document'(input(), index()) -> parse_result(). 'document'(Input, Index) -> p(Input, Index, 'document', fun(I,D) -> (p_seq([fun 'prolog'/2, fun 'element'/2, p_zero_or_more(fun 'misc'/2)]))(I,D) end, fun(Node, _Idx) ->lists:flatten(Node) end). -spec 'prolog'(input(), index()) -> parse_result(). 'prolog'(Input, Index) -> p(Input, Index, 'prolog', fun(I,D) -> (p_seq([p_optional(fun 'xmldecl'/2), p_zero_or_more(fun 'misc'/2), p_optional(p_seq([fun 'doctypedecl'/2, p_zero_or_more(fun 'misc'/2)]))]))(I,D) end, fun(Node, _Idx) ->lists:flatten(Node) end). -spec 'xmldecl'(input(), index()) -> parse_result(). 'xmldecl'(Input, Index) -> p(Input, Index, 'xmldecl', fun(I,D) -> (p_seq([p_string(<<">), fun 'xmlver'/2, p_optional(fun 'encoding'/2), p_optional(fun 'sddecl'/2), p_optional(fun 's'/2), p_string(<<"?>">>)]))(I,D) end, fun(Node, _Idx) -> Opts = maps:from_list([X || X = {K, _V} <- Node, is_atom(K)]), #{version := Ver} = Opts, #xml{version = Ver, encoding = maps:get(encoding, Opts, undefined), standalone = maps:get(standalone, Opts, undefined)} end). -spec 'xmlver'(input(), index()) -> parse_result(). 'xmlver'(Input, Index) -> p(Input, Index, 'xmlver', fun(I,D) -> (p_seq([fun 's'/2, p_string(<<"version">>), fun 'eq'/2, p_charclass(<<"[\'\"]">>), p_label('v', fun 'vernum'/2), p_charclass(<<"[\'\"]">>)]))(I,D) end, fun(Node, _Idx) -> V = proplists:get_value(v, Node), {version, V} end). -spec 'vernum'(input(), index()) -> parse_result(). 'vernum'(Input, Index) -> p(Input, Index, 'vernum', fun(I,D) -> (p_seq([p_string(<<"1.">>), p_one_or_more(p_charclass(<<"[0-9]">>))]))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'sddecl'(input(), index()) -> parse_result(). 'sddecl'(Input, Index) -> p(Input, Index, 'sddecl', fun(I,D) -> (p_seq([fun 's'/2, p_string(<<"standalone">>), fun 'eq'/2, p_charclass(<<"[\'\"]">>), p_label('v', p_choose([p_string(<<"yes">>), p_string(<<"no">>)])), p_charclass(<<"[\'\"]">>)]))(I,D) end, fun(Node, _Idx) -> V = proplists:get_value(v, Node), {standalone, binary_to_atom(iolist_to_binary(V))} end). -spec 'encoding'(input(), index()) -> parse_result(). 'encoding'(Input, Index) -> p(Input, Index, 'encoding', fun(I,D) -> (p_seq([fun 's'/2, p_string(<<"encoding">>), fun 'eq'/2, p_charclass(<<"[\'\"]">>), p_label('v', fun 'encname'/2), p_charclass(<<"[\"\']">>)]))(I,D) end, fun(Node, _Idx) -> V = proplists:get_value(v, Node), {encoding, V} end). -spec 'encname'(input(), index()) -> parse_result(). 'encname'(Input, Index) -> p(Input, Index, 'encname', fun(I,D) -> (p_regexp(<<"[A-Za-z][A-Za-z0-9._-]*">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'doctypedecl'(input(), index()) -> parse_result(). 'doctypedecl'(Input, Index) -> p(Input, Index, 'doctypedecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_label('n', fun 'name'/2), p_label('extid', p_optional(p_seq([fun 's'/2, fun 'externalid'/2]))), p_optional(fun 's'/2), p_label('subset', p_optional(p_seq([p_string(<<"[">>), fun 'intsubset'/2, p_string(<<"]">>), p_optional(fun 's'/2)]))), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Opts = maps:from_list([X || X = {K, _V} <- Node, is_atom(K)]), #{n := Name} = Opts, Info0 = #{}, Info1 = case Opts of #{extid := [_, Id]} -> Info0#{external_id => Id}; _ -> Info0 end, Info2 = case Opts of #{subset := [_, IntSubset, _, _S]} -> Info1#{subset => lists:flatten(IntSubset)}; _ -> Info1 end, #xml_doctype{name = Name, info = Info2} end). -spec 'intsubset'(input(), index()) -> parse_result(). 'intsubset'(Input, Index) -> p(Input, Index, 'intsubset', fun(I,D) -> (p_zero_or_more(p_choose([fun 'markupdecl'/2, fun 'peref'/2, fun 's'/2])))(I,D) end, fun(Node, Idx) ->transform('intsubset', Node, Idx) end). -spec 'markupdecl'(input(), index()) -> parse_result(). 'markupdecl'(Input, Index) -> p(Input, Index, 'markupdecl', fun(I,D) -> (p_choose([fun 'elementdecl'/2, fun 'attlistdecl'/2, fun 'entitydecl'/2, fun 'notationdecl'/2, fun 'pi'/2, fun 'comment'/2]))(I,D) end, fun(Node, Idx) ->transform('markupdecl', Node, Idx) end). -spec 'attlistdecl'(input(), index()) -> parse_result(). 'attlistdecl'(Input, Index) -> p(Input, Index, 'attlistdecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_label('n', fun 'name'/2), p_label('def', p_zero_or_more(fun 'attdef'/2)), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Defs = proplists:get_value(def, Node), #xmld_attlist{tag = Name, attributes = Defs} end). -spec 'attdef'(input(), index()) -> parse_result(). 'attdef'(Input, Index) -> p(Input, Index, 'attdef', fun(I,D) -> (p_seq([fun 's'/2, p_label('n', fun 'name'/2), fun 's'/2, p_label('t', fun 'atttype'/2), fun 's'/2, p_label('d', fun 'defaultdecl'/2)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Type = proplists:get_value(t, Node), Default = proplists:get_value(d, Node), #xmld_attr{name = Name, type = Type, default = Default} end). -spec 'atttype'(input(), index()) -> parse_result(). 'atttype'(Input, Index) -> p(Input, Index, 'atttype', fun(I,D) -> (p_choose([fun 'stringtype'/2, fun 'tokentype'/2, fun 'enumtype'/2]))(I,D) end, fun(Node, Idx) ->transform('atttype', Node, Idx) end). -spec 'stringtype'(input(), index()) -> parse_result(). 'stringtype'(Input, Index) -> p(Input, Index, 'stringtype', fun(I,D) -> (p_string(<<"CDATA">>))(I,D) end, fun(_Node, _Idx) ->cdata end). -spec 'tokentype'(input(), index()) -> parse_result(). 'tokentype'(Input, Index) -> p(Input, Index, 'tokentype', fun(I,D) -> (p_choose([fun 'idtype'/2, fun 'idrefstype'/2, fun 'idreftype'/2, fun 'entitytype'/2, fun 'entitiestype'/2, fun 'nmtokenstype'/2, fun 'nmtokentype'/2]))(I,D) end, fun(Node, Idx) ->transform('tokentype', Node, Idx) end). -spec 'idtype'(input(), index()) -> parse_result(). 'idtype'(Input, Index) -> p(Input, Index, 'idtype', fun(I,D) -> (p_string(<<"ID">>))(I,D) end, fun(_Node, _Idx) ->{one, id} end). -spec 'idreftype'(input(), index()) -> parse_result(). 'idreftype'(Input, Index) -> p(Input, Index, 'idreftype', fun(I,D) -> (p_string(<<"IDREF">>))(I,D) end, fun(_Node, _Idx) ->{one, idref} end). -spec 'idrefstype'(input(), index()) -> parse_result(). 'idrefstype'(Input, Index) -> p(Input, Index, 'idrefstype', fun(I,D) -> (p_string(<<"IDREFS">>))(I,D) end, fun(_Node, _Idx) ->{many, idref} end). -spec 'entitytype'(input(), index()) -> parse_result(). 'entitytype'(Input, Index) -> p(Input, Index, 'entitytype', fun(I,D) -> (p_string(<<"ENTITY">>))(I,D) end, fun(_Node, _Idx) ->{one, entity} end). -spec 'entitiestype'(input(), index()) -> parse_result(). 'entitiestype'(Input, Index) -> p(Input, Index, 'entitiestype', fun(I,D) -> (p_string(<<"ENTITIES">>))(I,D) end, fun(_Node, _Idx) ->{many, entity} end). -spec 'nmtokentype'(input(), index()) -> parse_result(). 'nmtokentype'(Input, Index) -> p(Input, Index, 'nmtokentype', fun(I,D) -> (p_string(<<"NMTOKEN">>))(I,D) end, fun(_Node, _Idx) ->{one, nmtoken} end). -spec 'nmtokenstype'(input(), index()) -> parse_result(). 'nmtokenstype'(Input, Index) -> p(Input, Index, 'nmtokenstype', fun(I,D) -> (p_string(<<"NMTOKENS">>))(I,D) end, fun(_Node, _Idx) ->{many, nmtoken} end). -spec 'enumtype'(input(), index()) -> parse_result(). 'enumtype'(Input, Index) -> p(Input, Index, 'enumtype', fun(I,D) -> (p_choose([fun 'notationtype'/2, fun 'enumeration'/2]))(I,D) end, fun(Node, Idx) ->transform('enumtype', Node, Idx) end). -spec 'notationtype'(input(), index()) -> parse_result(). 'notationtype'(Input, Index) -> p(Input, Index, 'notationtype', fun(I,D) -> (p_seq([p_string(<<"NOTATION">>), fun 's'/2, p_string(<<"(">>), p_optional(fun 's'/2), p_label('head', fun 'name'/2), p_label('tail', p_zero_or_more(p_seq([p_optional(fun 's'/2), p_string(<<"|">>), p_optional(fun 's'/2), fun 'name'/2]))), p_optional(fun 's'/2), p_string(<<")">>)]))(I,D) end, fun(Node, _Idx) -> Head = proplists:get_value(head, Node), Tails = proplists:get_value(tail, Node), Tail = [X || [_, _, _, X] <- Tails], {enum, [Head | Tail]} end). -spec 'enumeration'(input(), index()) -> parse_result(). 'enumeration'(Input, Index) -> p(Input, Index, 'enumeration', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 's'/2), p_label('head', fun 'nmtoken'/2), p_label('tail', p_zero_or_more(p_seq([p_optional(fun 's'/2), p_string(<<"|">>), p_optional(fun 's'/2), fun 'nmtoken'/2]))), p_optional(fun 's'/2), p_string(<<")">>)]))(I,D) end, fun(Node, _Idx) -> Head = proplists:get_value(head, Node), Tails = proplists:get_value(tail, Node), Tail = [X || [_, _, _, X] <- Tails], {enum, [Head | Tail]} end). -spec 'defaultdecl'(input(), index()) -> parse_result(). 'defaultdecl'(Input, Index) -> p(Input, Index, 'defaultdecl', fun(I,D) -> (p_choose([fun 'defrequired'/2, fun 'defimplied'/2, fun 'deffixed'/2]))(I,D) end, fun(Node, Idx) ->transform('defaultdecl', Node, Idx) end). -spec 'defrequired'(input(), index()) -> parse_result(). 'defrequired'(Input, Index) -> p(Input, Index, 'defrequired', fun(I,D) -> (p_string(<<"#REQUIRED">>))(I,D) end, fun(_Node, _Idx) ->required end). -spec 'defimplied'(input(), index()) -> parse_result(). 'defimplied'(Input, Index) -> p(Input, Index, 'defimplied', fun(I,D) -> (p_string(<<"#IMPLIED">>))(I,D) end, fun(_Node, _Idx) ->implied end). -spec 'deffixed'(input(), index()) -> parse_result(). 'deffixed'(Input, Index) -> p(Input, Index, 'deffixed', fun(I,D) -> (p_seq([p_optional(p_seq([p_string(<<"#FIXED">>), fun 's'/2])), p_label('v', fun 'attvalue'/2)]))(I,D) end, fun(Node, _Idx) -> V = proplists:get_value(v, Node), {fixed, V} end). -spec 'entitydecl'(input(), index()) -> parse_result(). 'entitydecl'(Input, Index) -> p(Input, Index, 'entitydecl', fun(I,D) -> (p_choose([fun 'gedecl'/2, fun 'pedecl'/2]))(I,D) end, fun(Node, Idx) ->transform('entitydecl', Node, Idx) end). -spec 'gedecl'(input(), index()) -> parse_result(). 'gedecl'(Input, Index) -> p(Input, Index, 'gedecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_label('n', fun 'name'/2), fun 's'/2, p_label('d', fun 'entitydef'/2), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Def = proplists:get_value(d, Node), #xmld_entity{name = Name, content = Def} end). -spec 'pedecl'(input(), index()) -> parse_result(). 'pedecl'(Input, Index) -> p(Input, Index, 'pedecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_string(<<"%">>), fun 's'/2, p_label('n', fun 'name'/2), fun 's'/2, p_label('d', fun 'pedef'/2), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Def = proplists:get_value(d, Node), #xmld_parameter{name = Name, content = Def} end). -spec 'pedef'(input(), index()) -> parse_result(). 'pedef'(Input, Index) -> p(Input, Index, 'pedef', fun(I,D) -> (p_choose([fun 'entityvalue'/2, fun 'externalid'/2]))(I,D) end, fun(Node, Idx) ->transform('pedef', Node, Idx) end). -spec 'entitydef'(input(), index()) -> parse_result(). 'entitydef'(Input, Index) -> p(Input, Index, 'entitydef', fun(I,D) -> (p_choose([fun 'entityvalue'/2, fun 'extentitydef'/2]))(I,D) end, fun(Node, Idx) ->transform('entitydef', Node, Idx) end). -spec 'extentitydef'(input(), index()) -> parse_result(). 'extentitydef'(Input, Index) -> p(Input, Index, 'extentitydef', fun(I,D) -> (p_seq([fun 'externalid'/2, p_optional(fun 'ndatadecl'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [ExtID, NDataDecl] -> {ExtID, NDataDecl}; [ExtID] -> ExtID end end). -spec 'ndatadecl'(input(), index()) -> parse_result(). 'ndatadecl'(Input, Index) -> p(Input, Index, 'ndatadecl', fun(I,D) -> (p_seq([fun 's'/2, p_string(<<"NDATA">>), fun 's'/2, p_label('n', fun 'name'/2)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), {ndata, Name} end). -spec 'entityvalue'(input(), index()) -> parse_result(). 'entityvalue'(Input, Index) -> p(Input, Index, 'entityvalue', fun(I,D) -> (p_choose([fun 'entvaldq'/2, fun 'entvalsq'/2]))(I,D) end, fun(Node, Idx) ->transform('entityvalue', Node, Idx) end). -spec 'entvaldq'(input(), index()) -> parse_result(). 'entvaldq'(Input, Index) -> p(Input, Index, 'entvaldq', fun(I,D) -> (p_seq([p_charclass(<<"[\"]">>), p_zero_or_more(p_choose([fun 'notdq'/2, fun 'reference'/2, fun 'peref'/2])), p_charclass(<<"[\"]">>)]))(I,D) end, fun(Node, _Idx) ->[_,V,_] = Node, V end). -spec 'entvalsq'(input(), index()) -> parse_result(). 'entvalsq'(Input, Index) -> p(Input, Index, 'entvalsq', fun(I,D) -> (p_seq([p_charclass(<<"[\']">>), p_zero_or_more(p_choose([fun 'notsq'/2, fun 'reference'/2, fun 'peref'/2])), p_charclass(<<"[\']">>)]))(I,D) end, fun(Node, _Idx) ->[_,V,_] = Node, V end). -spec 'notationdecl'(input(), index()) -> parse_result(). 'notationdecl'(Input, Index) -> p(Input, Index, 'notationdecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_label('n', fun 'name'/2), fun 's'/2, p_label('id', p_choose([fun 'externalid'/2, fun 'publicid'/2])), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Id = proplists:get_value(id, Node), #xmld_notation{name = Name, id = Id} end). -spec 'publicid'(input(), index()) -> parse_result(). 'publicid'(Input, Index) -> p(Input, Index, 'publicid', fun(I,D) -> (p_seq([p_string(<<"PUBLIC">>), fun 's'/2, fun 'pubidlit'/2]))(I,D) end, fun(Node, _Idx) ->[_,_,Lit] = Node, {public, Lit} end). -spec 'elementdecl'(input(), index()) -> parse_result(). 'elementdecl'(Input, Index) -> p(Input, Index, 'elementdecl', fun(I,D) -> (p_seq([p_string(<<">), fun 's'/2, p_label('n', fun 'name'/2), fun 's'/2, p_label('c', fun 'contentspec'/2), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(n, Node), Content = proplists:get_value(c, Node), #xmld_element{tag = Name, content = Content} end). -spec 'contentspec'(input(), index()) -> parse_result(). 'contentspec'(Input, Index) -> p(Input, Index, 'contentspec', fun(I,D) -> (p_choose([fun 'emptycs'/2, fun 'anycs'/2, fun 'mixed'/2, fun 'children'/2]))(I,D) end, fun(Node, Idx) ->transform('contentspec', Node, Idx) end). -spec 'emptycs'(input(), index()) -> parse_result(). 'emptycs'(Input, Index) -> p(Input, Index, 'emptycs', fun(I,D) -> (p_string(<<"EMPTY">>))(I,D) end, fun(_Node, _Idx) ->empty end). -spec 'anycs'(input(), index()) -> parse_result(). 'anycs'(Input, Index) -> p(Input, Index, 'anycs', fun(I,D) -> (p_string(<<"ANY">>))(I,D) end, fun(_Node, _Idx) ->any end). -spec 'mixed'(input(), index()) -> parse_result(). 'mixed'(Input, Index) -> p(Input, Index, 'mixed', fun(I,D) -> (p_choose([fun 'mixedwn'/2, fun 'mixeds'/2]))(I,D) end, fun(Node, _Idx) ->{mixed, Node} end). -spec 'mixedwn'(input(), index()) -> parse_result(). 'mixedwn'(Input, Index) -> p(Input, Index, 'mixedwn', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 's'/2), p_string(<<"#PCDATA">>), p_label('ns', p_zero_or_more(p_seq([p_optional(fun 's'/2), p_string(<<"|">>), p_optional(fun 's'/2), p_label('n', fun 'name'/2)]))), p_optional(fun 's'/2), p_string(<<")*">>)]))(I,D) end, fun(Node, _Idx) -> Ns = proplists:get_value(ns, Node), [proplists:get_value(n, N) || N <- Ns] end). -spec 'mixeds'(input(), index()) -> parse_result(). 'mixeds'(Input, Index) -> p(Input, Index, 'mixeds', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 's'/2), p_string(<<"#PCDATA">>), p_optional(fun 's'/2), p_string(<<")">>)]))(I,D) end, fun(_Node, _Idx) ->[] end). -spec 'children'(input(), index()) -> parse_result(). 'children'(Input, Index) -> p(Input, Index, 'children', fun(I,D) -> (p_seq([p_label('inner', p_choose([fun 'choice'/2, fun 'seq'/2])), p_label('post', p_optional(p_charclass(<<"[?*+]">>)))]))(I,D) end, fun(Node, _Idx) -> Inner = proplists:get_value(inner, Node), Post = iolist_to_binary(proplists:get_value(post, Node)), Verb = case Post of <<$?>> -> zero_or_one; <<$*>> -> zero_or_more; <<$+>> -> one_or_more; <<>> -> one end, {Verb, Inner} end). -spec 'cp'(input(), index()) -> parse_result(). 'cp'(Input, Index) -> p(Input, Index, 'cp', fun(I,D) -> (p_seq([p_label('inner', p_choose([fun 'name'/2, fun 'choice'/2, fun 'seq'/2])), p_label('post', p_optional(p_charclass(<<"[?*+]">>)))]))(I,D) end, fun(Node, _Idx) -> Inner = proplists:get_value(inner, Node), Post = iolist_to_binary(proplists:get_value(post, Node)), Verb = case Post of <<$?>> -> zero_or_one; <<$*>> -> zero_or_more; <<$+>> -> one_or_more; <<>> -> one end, {Verb, Inner} end). -spec 'choice'(input(), index()) -> parse_result(). 'choice'(Input, Index) -> p(Input, Index, 'choice', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 's'/2), p_label('head', fun 'cp'/2), p_label('tail', p_one_or_more(p_seq([p_optional(fun 's'/2), p_string(<<"|">>), p_optional(fun 's'/2), fun 'cp'/2]))), p_optional(fun 's'/2), p_string(<<")">>)]))(I,D) end, fun(Node, _Idx) -> Head = proplists:get_value(head, Node), Tails = proplists:get_value(tail, Node), Tail = [X || [_, _, _, X] <- Tails], {choice, [Head | Tail]} end). -spec 'seq'(input(), index()) -> parse_result(). 'seq'(Input, Index) -> p(Input, Index, 'seq', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 's'/2), p_label('head', fun 'cp'/2), p_label('tail', p_zero_or_more(p_seq([p_optional(fun 's'/2), p_string(<<",">>), p_optional(fun 's'/2), fun 'cp'/2]))), p_optional(fun 's'/2), p_string(<<")">>)]))(I,D) end, fun(Node, _Idx) -> Head = proplists:get_value(head, Node), Tails = proplists:get_value(tail, Node), Tail = [X || [_, _, _, X] <- Tails], {seq, [Head | Tail]} end). -spec 'externalid'(input(), index()) -> parse_result(). 'externalid'(Input, Index) -> p(Input, Index, 'externalid', fun(I,D) -> (p_choose([fun 'systemextid'/2, fun 'pubextid'/2]))(I,D) end, fun(Node, Idx) ->transform('externalid', Node, Idx) end). -spec 'systemextid'(input(), index()) -> parse_result(). 'systemextid'(Input, Index) -> p(Input, Index, 'systemextid', fun(I,D) -> (p_seq([p_string(<<"SYSTEM">>), fun 's'/2, fun 'systemlit'/2]))(I,D) end, fun(Node, _Idx) ->[_,_,A] = Node, {system, A} end). -spec 'pubextid'(input(), index()) -> parse_result(). 'pubextid'(Input, Index) -> p(Input, Index, 'pubextid', fun(I,D) -> (p_seq([p_string(<<"PUBLIC">>), fun 's'/2, fun 'pubidlit'/2, fun 's'/2, fun 'systemlit'/2]))(I,D) end, fun(Node, _Idx) ->[_, _, Pub, Sys] = Node, {public, Pub, Sys} end). -spec 'element'(input(), index()) -> parse_result(). 'element'(Input, Index) -> p(Input, Index, 'element', fun(I,D) -> (p_choose([fun 'emptyelemtag'/2, fun 'elemtag'/2]))(I,D) end, fun(Node, Idx) ->transform('element', Node, Idx) end). -spec 'elemtag'(input(), index()) -> parse_result(). 'elemtag'(Input, Index) -> p(Input, Index, 'elemtag', fun(I,D) -> (p_seq([fun 'stag'/2, fun 'content'/2, fun 'etag'/2]))(I,D) end, fun(Node, _Idx) -> [STag, Content, ETag] = Node, #xml_element{tag = Name} = STag, case ETag of {end_element, Name} -> ok; {end_element, OtherName} -> error({tag_close_mismatch, [{expected, Name}, {closed, OtherName}]}) end, STag#xml_element{content = Content} end). -spec 'emptyelemtag'(input(), index()) -> parse_result(). 'emptyelemtag'(Input, Index) -> p(Input, Index, 'emptyelemtag', fun(I,D) -> (p_seq([p_string(<<"<">>), p_label('name', fun 'name'/2), p_label('attrs', p_zero_or_more(p_seq([fun 's'/2, fun 'attribute'/2]))), p_label('tailws', p_optional(fun 's'/2)), p_string(<<"\/>">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Attrs = lists:flatten([proplists:get_value(attrs, Node), proplists:get_value(tailws, Node)]), #xml_element{tag = Name, attributes = Attrs} end). -spec 'stag'(input(), index()) -> parse_result(). 'stag'(Input, Index) -> p(Input, Index, 'stag', fun(I,D) -> (p_seq([p_string(<<"<">>), p_label('name', fun 'name'/2), p_label('attrs', p_zero_or_more(p_seq([fun 's'/2, fun 'attribute'/2]))), p_label('tailws', p_optional(fun 's'/2)), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Attrs = lists:flatten([proplists:get_value(attrs, Node), proplists:get_value(tailws, Node)]), #xml_element{tag = Name, attributes = Attrs} end). -spec 'attribute'(input(), index()) -> parse_result(). 'attribute'(Input, Index) -> p(Input, Index, 'attribute', fun(I,D) -> (p_seq([p_label('name', fun 'name'/2), fun 'eq'/2, p_label('val', fun 'attvalue'/2)]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Value = proplists:get_value(val, Node), case Name of {<<"xmlns">>, Ns} -> #xml_namespace{name = Ns, uri = Value}; <<"xmlns">> -> #xml_namespace{name = default, uri = Value}; _ -> #xml_attribute{name = Name, value = Value} end end). -spec 'attvalue'(input(), index()) -> parse_result(). 'attvalue'(Input, Index) -> p(Input, Index, 'attvalue', fun(I,D) -> (p_choose([fun 'attvaluedq'/2, fun 'attvaluesq'/2]))(I,D) end, fun(Node, Idx) ->transform('attvalue', Node, Idx) end). -spec 'attvaluedq'(input(), index()) -> parse_result(). 'attvaluedq'(Input, Index) -> p(Input, Index, 'attvaluedq', fun(I,D) -> (p_seq([p_charclass(<<"[\"]">>), p_zero_or_more(p_choose([fun 'notdq'/2, fun 'reference'/2])), p_charclass(<<"[\"]">>)]))(I,D) end, fun(Node, _Idx) ->[_,V,_] = Node, V end). -spec 'attvaluesq'(input(), index()) -> parse_result(). 'attvaluesq'(Input, Index) -> p(Input, Index, 'attvaluesq', fun(I,D) -> (p_seq([p_charclass(<<"[\']">>), p_zero_or_more(p_choose([fun 'notsq'/2, fun 'reference'/2])), p_charclass(<<"[\']">>)]))(I,D) end, fun(Node, _Idx) ->[_,V,_] = Node, V end). -spec 'content'(input(), index()) -> parse_result(). 'content'(Input, Index) -> p(Input, Index, 'content', fun(I,D) -> (p_seq([p_optional(fun 'chardata'/2), p_zero_or_more(p_seq([p_choose([fun 'element'/2, fun 'reference'/2, fun 'cdata'/2, fun 'pi'/2, fun 'comment'/2]), p_optional(fun 'chardata'/2)]))]))(I,D) end, fun(Node, _Idx) -> lists:flatten(Node) end). -spec 'etag'(input(), index()) -> parse_result(). 'etag'(Input, Index) -> p(Input, Index, 'etag', fun(I,D) -> (p_seq([p_string(<<"<\/">>), p_label('n', fun 'name'/2), p_optional(fun 's'/2), p_string(<<">">>)]))(I,D) end, fun(Node, _Idx) ->{end_element, proplists:get_value(n, Node)} end). -spec 'reference'(input(), index()) -> parse_result(). 'reference'(Input, Index) -> p(Input, Index, 'reference', fun(I,D) -> (p_choose([fun 'deccharref'/2, fun 'hexcharref'/2, fun 'entityref'/2]))(I,D) end, fun(Node, Idx) ->transform('reference', Node, Idx) end). -spec 'deccharref'(input(), index()) -> parse_result(). 'deccharref'(Input, Index) -> p(Input, Index, 'deccharref', fun(I,D) -> (p_seq([p_string(<<"&#">>), p_label('n', p_one_or_more(p_charclass(<<"[0-9]">>))), p_string(<<";">>)]))(I,D) end, fun(Node, _Idx) -> NB = proplists:get_value(n, Node), N = binary_to_integer(iolist_to_binary(NB), 10), unicode:characters_to_binary([N], utf8) end). -spec 'hexcharref'(input(), index()) -> parse_result(). 'hexcharref'(Input, Index) -> p(Input, Index, 'hexcharref', fun(I,D) -> (p_seq([p_string(<<"&#x">>), p_label('n', p_one_or_more(p_charclass(<<"[0-9a-fA-F]">>))), p_string(<<";">>)]))(I,D) end, fun(Node, _Idx) -> NB = proplists:get_value(n, Node), N = binary_to_integer(iolist_to_binary(NB), 16), unicode:characters_to_binary([N], utf8) end). -spec 'entityref'(input(), index()) -> parse_result(). 'entityref'(Input, Index) -> p(Input, Index, 'entityref', fun(I,D) -> (p_seq([p_string(<<"&">>), fun 'name'/2, p_string(<<";">>)]))(I,D) end, fun(Node, _Idx) ->[_,Name,_] = Node, {entity, Name} end). -spec 'peref'(input(), index()) -> parse_result(). 'peref'(Input, Index) -> p(Input, Index, 'peref', fun(I,D) -> (p_seq([p_string(<<"%">>), fun 'name'/2, p_string(<<";">>)]))(I,D) end, fun(Node, _Idx) ->[_,Name,_] = Node, {parameter, Name} end). -spec 'misc'(input(), index()) -> parse_result(). 'misc'(Input, Index) -> p(Input, Index, 'misc', fun(I,D) -> (p_choose([fun 'comment'/2, fun 'pi'/2, fun 's'/2]))(I,D) end, fun(Node, Idx) ->transform('misc', Node, Idx) end). -spec 'comment'(input(), index()) -> parse_result(). 'comment'(Input, Index) -> p(Input, Index, 'comment', fun(I,D) -> (p_seq([p_string(<<"">>)), p_anything()])), p_string(<<"-->">>)]))(I,D) end, fun(Node, _Idx) -> [_Front, Body, _Back] = Node, #xml_comment{text = iolist_to_binary(Body)} end). -spec 'cdata'(input(), index()) -> parse_result(). 'cdata'(Input, Index) -> p(Input, Index, 'cdata', fun(I,D) -> (p_seq([p_string(<<">), p_zero_or_more(p_seq([p_not(p_string(<<"]]>">>)), p_anything()])), p_string(<<"]]>">>)]))(I,D) end, fun(Node, _Idx) -> [_Front, Body, _Back] = Node, iolist_to_binary(Body) end). -spec 'pi'(input(), index()) -> parse_result(). 'pi'(Input, Index) -> p(Input, Index, 'pi', fun(I,D) -> (p_choose([fun 'tagpi'/2, fun 'genericpi'/2]))(I,D) end, fun(Node, Idx) ->transform('pi', Node, Idx) end). -spec 'tagpi'(input(), index()) -> parse_result(). 'tagpi'(Input, Index) -> p(Input, Index, 'tagpi', fun(I,D) -> (p_seq([p_string(<<">), p_label('t', fun 'name'/2), p_label('attrs', p_one_or_more(p_seq([fun 's'/2, fun 'attribute'/2]))), p_label('trail', p_optional(fun 's'/2)), p_string(<<"?>">>)]))(I,D) end, fun(Node, _Idx) -> Target = proplists:get_value(t, Node), Attrs = lists:flatten([proplists:get_value(attrs, Node), proplists:get_value(trail, Node)]), #xml_pi{target = Target, options = Attrs} end). -spec 'genericpi'(input(), index()) -> parse_result(). 'genericpi'(Input, Index) -> p(Input, Index, 'genericpi', fun(I,D) -> (p_seq([p_string(<<">), fun 'name'/2, p_optional(p_seq([fun 's'/2, p_zero_or_more(p_seq([p_not(p_string(<<"?>">>)), p_anything()]))])), p_string(<<"?>">>)]))(I,D) end, fun(Node, _Idx) -> [_, Target, OptPart, _] = Node, Options = case OptPart of [_S, Rest] -> iolist_to_binary(Rest); _ -> undefined end, #xml_pi{target = Target, options = Options} end). -spec 'systemlit'(input(), index()) -> parse_result(). 'systemlit'(Input, Index) -> p(Input, Index, 'systemlit', fun(I,D) -> (p_choose([fun 'systemlitdq'/2, fun 'systemlitsq'/2]))(I,D) end, fun(Node, Idx) ->transform('systemlit', Node, Idx) end). -spec 'systemlitdq'(input(), index()) -> parse_result(). 'systemlitdq'(Input, Index) -> p(Input, Index, 'systemlitdq', fun(I,D) -> (p_seq([p_charclass(<<"[\"]">>), p_zero_or_more(p_seq([p_not(p_charclass(<<"[\"]">>)), p_anything()])), p_charclass(<<"[\"]">>)]))(I,D) end, fun(Node, _Idx) ->[_,A,_] = Node, iolist_to_binary(A) end). -spec 'systemlitsq'(input(), index()) -> parse_result(). 'systemlitsq'(Input, Index) -> p(Input, Index, 'systemlitsq', fun(I,D) -> (p_seq([p_charclass(<<"[\']">>), p_zero_or_more(p_seq([p_not(p_charclass(<<"[\']">>)), p_anything()])), p_charclass(<<"[\']">>)]))(I,D) end, fun(Node, _Idx) ->[_,A,_] = Node, iolist_to_binary(A) end). -spec 'pubidlit'(input(), index()) -> parse_result(). 'pubidlit'(Input, Index) -> p(Input, Index, 'pubidlit', fun(I,D) -> (p_choose([fun 'pubidlitdq'/2, fun 'pubidlitsq'/2]))(I,D) end, fun(Node, Idx) ->transform('pubidlit', Node, Idx) end). -spec 'pubidlitdq'(input(), index()) -> parse_result(). 'pubidlitdq'(Input, Index) -> p(Input, Index, 'pubidlitdq', fun(I,D) -> (p_seq([p_charclass(<<"[\"]">>), p_zero_or_more(p_charclass(<<"[a-zA-Z0-9\'()+,.\/:=?;!*#@$_%\x20\x0d\x0a-]">>)), p_charclass(<<"[\"]">>)]))(I,D) end, fun(Node, _Idx) ->[_,A,_] = Node, iolist_to_binary(A) end). -spec 'pubidlitsq'(input(), index()) -> parse_result(). 'pubidlitsq'(Input, Index) -> p(Input, Index, 'pubidlitsq', fun(I,D) -> (p_seq([p_charclass(<<"[\']">>), p_zero_or_more(p_charclass(<<"[a-zA-Z0-9()+,.\/:=?;!*#@$_%\x20\x0d\x0a-]">>)), p_charclass(<<"[\']">>)]))(I,D) end, fun(Node, _Idx) ->[_,A,_] = Node, iolist_to_binary(A) end). -spec 'ncname'(input(), index()) -> parse_result(). 'ncname'(Input, Index) -> p(Input, Index, 'ncname', fun(I,D) -> (p_regexp(<<"[A-Z_a-z][A-Z_a-z\\-.0-9]*">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'name'(input(), index()) -> parse_result(). 'name'(Input, Index) -> p(Input, Index, 'name', fun(I,D) -> (p_seq([fun 'ncname'/2, p_optional(p_seq([p_string(<<":">>), fun 'ncname'/2]))]))(I,D) end, fun(Node, _Idx) -> case Node of [NS, [_, N]] when is_binary(N) -> {NS, N}; [N, _] -> N end end). -spec 'nmtoken'(input(), index()) -> parse_result(). 'nmtoken'(Input, Index) -> p(Input, Index, 'nmtoken', fun(I,D) -> (p_regexp(<<"[A-Z_a-z\\-.0-9]+">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'chardata'(input(), index()) -> parse_result(). 'chardata'(Input, Index) -> p(Input, Index, 'chardata', fun(I,D) -> (p_regexp(<<"[^<&]*">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 's'(input(), index()) -> parse_result(). 's'(Input, Index) -> p(Input, Index, 's', fun(I,D) -> (p_regexp(<<"[\\x20\\x09\\x0d\\x0A]+">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'eq'(input(), index()) -> parse_result(). 'eq'(Input, Index) -> p(Input, Index, 'eq', fun(I,D) -> (p_seq([p_optional(fun 's'/2), p_string(<<"=">>), p_optional(fun 's'/2)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'notdq'(input(), index()) -> parse_result(). 'notdq'(Input, Index) -> p(Input, Index, 'notdq', fun(I,D) -> (p_regexp(<<"[^<%&\"]+">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'notsq'(input(), index()) -> parse_result(). 'notsq'(Input, Index) -> p(Input, Index, 'notsq', fun(I,D) -> (p_regexp(<<"[^<%&']+">>))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). transform(_,Node,_Index) -> Node. -file("peg_includes.hrl", 1). -type index() :: {{line, pos_integer()}, {column, pos_integer()}}. -type input() :: binary(). -type parse_failure() :: {fail, term()}. -type parse_success() :: {term(), input(), index()}. -type parse_result() :: parse_failure() | parse_success(). -type parse_fun() :: fun((input(), index()) -> parse_result()). -type xform_fun() :: fun((input(), index()) -> term()). -spec p(input(), index(), atom(), parse_fun(), xform_fun()) -> parse_result(). p(Inp, StartIndex, Name, ParseFun, TransformFun) -> case get_memo(StartIndex, Name) of % See if the current reduction is memoized {ok, Memo} -> %Memo; % If it is, return the stored result Memo; _ -> % If not, attempt to parse Result = case ParseFun(Inp, StartIndex) of {fail,_} = Failure -> % If it fails, memoize the failure Failure; {Match, InpRem, NewIndex} -> % If it passes, transform and memoize the result. Transformed = TransformFun(Match, StartIndex), {Transformed, InpRem, NewIndex} end, memoize(StartIndex, Name, Result), Result end. -spec setup_memo() -> ets:tid(). setup_memo() -> put({parse_memo_table, ?MODULE}, ets:new(?MODULE, [set])). -spec release_memo() -> true. release_memo() -> ets:delete(memo_table_name()). -spec memoize(index(), atom(), parse_result()) -> true. memoize(Index, Name, Result) -> Memo = case ets:lookup(memo_table_name(), Index) of [] -> []; [{Index, Plist}] -> Plist end, ets:insert(memo_table_name(), {Index, [{Name, Result}|Memo]}). -spec get_memo(index(), atom()) -> {ok, term()} | {error, not_found}. get_memo(Index, Name) -> case ets:lookup(memo_table_name(), Index) of [] -> {error, not_found}; [{Index, Plist}] -> case proplists:lookup(Name, Plist) of {Name, Result} -> {ok, Result}; _ -> {error, not_found} end end. -spec memo_table_name() -> ets:tid(). memo_table_name() -> get({parse_memo_table, ?MODULE}). -ifdef(p_eof). -spec p_eof() -> parse_fun(). p_eof() -> fun(<<>>, Index) -> {eof, [], Index}; (_, Index) -> {fail, {expected, eof, Index}} end. -endif. -ifdef(p_optional). -spec p_optional(parse_fun()) -> parse_fun(). p_optional(P) -> fun(Input, Index) -> case P(Input, Index) of {fail,_} -> {[], Input, Index}; {_, _, _} = Success -> Success end end. -endif. -ifdef(p_not). -spec p_not(parse_fun()) -> parse_fun(). p_not(P) -> fun(Input, Index)-> case P(Input,Index) of {fail,_} -> {[], Input, Index}; {Result, _, _} -> {fail, {expected, {no_match, Result},Index}} end end. -endif. -ifdef(p_assert). -spec p_assert(parse_fun()) -> parse_fun(). p_assert(P) -> fun(Input,Index) -> case P(Input,Index) of {fail,_} = Failure-> Failure; _ -> {[], Input, Index} end end. -endif. -ifdef(p_seq). -spec p_seq([parse_fun()]) -> parse_fun(). p_seq(P) -> fun(Input, Index) -> p_all(P, Input, Index, []) end. -spec p_all([parse_fun()], input(), index(), [term()]) -> parse_result(). p_all([], Inp, Index, Accum ) -> {lists:reverse( Accum ), Inp, Index}; p_all([P|Parsers], Inp, Index, Accum) -> case P(Inp, Index) of {fail, _} = Failure -> Failure; {Result, InpRem, NewIndex} -> p_all(Parsers, InpRem, NewIndex, [Result|Accum]) end. -endif. -ifdef(p_choose). -spec p_choose([parse_fun()]) -> parse_fun(). p_choose(Parsers) -> fun(Input, Index) -> p_attempt(Parsers, Input, Index, none) end. -spec p_attempt([parse_fun()], input(), index(), none | parse_failure()) -> parse_result(). p_attempt([], _Input, _Index, Failure) -> Failure; p_attempt([P|Parsers], Input, Index, FirstFailure)-> case P(Input, Index) of {fail, _} = Failure -> case FirstFailure of none -> p_attempt(Parsers, Input, Index, Failure); _ -> p_attempt(Parsers, Input, Index, FirstFailure) end; Result -> Result end. -endif. -ifdef(p_zero_or_more). -spec p_zero_or_more(parse_fun()) -> parse_fun(). p_zero_or_more(P) -> fun(Input, Index) -> p_scan(P, Input, Index, []) end. -endif. -ifdef(p_one_or_more). -spec p_one_or_more(parse_fun()) -> parse_fun(). p_one_or_more(P) -> fun(Input, Index)-> Result = p_scan(P, Input, Index, []), case Result of {[_|_], _, _} -> Result; _ -> {fail, {expected, Failure, _}} = P(Input,Index), {fail, {expected, {at_least_one, Failure}, Index}} end end. -endif. -ifdef(p_label). -spec p_label(atom(), parse_fun()) -> parse_fun(). p_label(Tag, P) -> fun(Input, Index) -> case P(Input, Index) of {fail,_} = Failure -> Failure; {Result, InpRem, NewIndex} -> {{Tag, Result}, InpRem, NewIndex} end end. -endif. -ifdef(p_scan). -spec p_scan(parse_fun(), input(), index(), [term()]) -> {[term()], input(), index()}. p_scan(_, <<>>, Index, Accum) -> {lists:reverse(Accum), <<>>, Index}; p_scan(P, Inp, Index, Accum) -> case P(Inp, Index) of {fail,_} -> {lists:reverse(Accum), Inp, Index}; {Result, InpRem, NewIndex} -> p_scan(P, InpRem, NewIndex, [Result | Accum]) end. -endif. -ifdef(p_string). -spec p_string(binary()) -> parse_fun(). p_string(S) -> Length = erlang:byte_size(S), fun(Input, Index) -> try <> = Input, {S, Rest, p_advance_index(S, Index)} catch error:{badmatch,_} -> {fail, {expected, {string, S}, Index}} end end. -endif. -ifdef(p_anything). -spec p_anything() -> parse_fun(). p_anything() -> fun(<<>>, Index) -> {fail, {expected, any_character, Index}}; (Input, Index) when is_binary(Input) -> <> = Input, {<>, Rest, p_advance_index(<>, Index)} end. -endif. -ifdef(p_charclass). -spec p_charclass(string() | binary()) -> parse_fun(). p_charclass(Class) -> {ok, RE} = re:compile(Class, [unicode, dotall]), fun(Inp, Index) -> case re:run(Inp, RE, [anchored]) of {match, [{0, Length}|_]} -> {Head, Tail} = erlang:split_binary(Inp, Length), {Head, Tail, p_advance_index(Head, Index)}; _ -> {fail, {expected, {character_class, binary_to_list(Class)}, Index}} end end. -endif. -ifdef(p_regexp). -spec p_regexp(binary()) -> parse_fun(). p_regexp(Regexp) -> {ok, RE} = re:compile(Regexp, [unicode, dotall, anchored]), fun(Inp, Index) -> case re:run(Inp, RE) of {match, [{0, Length}|_]} -> {Head, Tail} = erlang:split_binary(Inp, Length), {Head, Tail, p_advance_index(Head, Index)}; _ -> {fail, {expected, {regexp, binary_to_list(Regexp)}, Index}} end end. -endif. -ifdef(line). -spec line(index() | term()) -> pos_integer() | undefined. line({{line,L},_}) -> L; line(_) -> undefined. -endif. -ifdef(column). -spec column(index() | term()) -> pos_integer() | undefined. column({_,{column,C}}) -> C; column(_) -> undefined. -endif. -spec p_advance_index(input() | unicode:charlist() | pos_integer(), index()) -> index(). p_advance_index(MatchedInput, Index) when is_list(MatchedInput) orelse is_binary(MatchedInput)-> % strings lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput)); p_advance_index(MatchedInput, Index) when is_integer(MatchedInput) -> % single characters {{line, Line}, {column, Col}} = Index, case MatchedInput of $\n -> {{line, Line+1}, {column, 1}}; _ -> {{line, Line}, {column, Col+1}} end.