|
@ -0,0 +1,518 @@ |
|
|
|
|
|
-module(protoParse). |
|
|
|
|
|
|
|
|
|
|
|
-export([ |
|
|
|
|
|
parseParse/1 |
|
|
|
|
|
, parseFile/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_scan, true). |
|
|
|
|
|
-define(p_seq, true). |
|
|
|
|
|
-define(p_string, true). |
|
|
|
|
|
-define(p_zero_or_more, true). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-spec parseFile(file:name()) -> any(). |
|
|
|
|
|
parseFile(Filename) -> |
|
|
|
|
|
case file:read_file(Filename) of |
|
|
|
|
|
{ok, Bin} -> |
|
|
|
|
|
parseParse(Bin); |
|
|
|
|
|
Err -> Err |
|
|
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
-spec parseParse(binary() | list()) -> any(). |
|
|
|
|
|
parseParse(List) when is_list(List) -> |
|
|
|
|
|
parseParse(unicode:characters_to_binary(List)); |
|
|
|
|
|
parseParse(Input) when is_binary(Input) -> |
|
|
|
|
|
setup_memo(), |
|
|
|
|
|
Result = case 'all'(Input, {{line, 1}, {column, 1}}) of |
|
|
|
|
|
{AST, <<>>, _Index} -> |
|
|
|
|
|
AST; |
|
|
|
|
|
Any -> |
|
|
|
|
|
Any |
|
|
|
|
|
end, |
|
|
|
|
|
release_memo(), |
|
|
|
|
|
Result. |
|
|
|
|
|
|
|
|
|
|
|
-spec 'all'(input(), index()) -> parse_result(). |
|
|
|
|
|
'all'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'all', fun(I, D) -> |
|
|
|
|
|
(p_seq([fun 'blank0'/2, p_zero_or_more(p_seq([p_choose([fun 'type'/2, fun 'protocol'/2]), fun 'blank0'/2]))]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
[_ | [T]] = Node, |
|
|
|
|
|
DataList = [H || [H | _] <- T], |
|
|
|
|
|
|
|
|
|
|
|
DataList |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'protocol'(input(), index()) -> parse_result(). |
|
|
|
|
|
'protocol'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'protocol', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_label('name', fun 'name'/2), fun 'blanks'/2, p_label('tag', fun 'tag'/2), fun 'blank0'/2, p_string(<<"{">>), fun 'blank0'/2, p_label('sub', p_zero_or_more(p_seq([fun 'subproto'/2, fun 'blank0'/2]))), p_string(<<"}">>)]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Name = binary_to_list(iolist_to_binary(proplists:get_value(name, Node))), |
|
|
|
|
|
Tag = list_to_integer(binary_to_list(iolist_to_binary(proplists:get_value(tag, Node)))), |
|
|
|
|
|
SubList = proplists:get_value(sub, Node), |
|
|
|
|
|
SubProtoList = [Head || [Head, _] <- SubList], |
|
|
|
|
|
{protocol, Name, Tag, SubProtoList} |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'subproto'(input(), index()) -> parse_result(). |
|
|
|
|
|
'subproto'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'subproto', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_label('parta', p_choose([p_string(<<"request">>), p_string(<<"response">>)])), fun 'blanks'/2, p_label('partb', p_choose([fun 'typename'/2, fun 'struct'/2]))]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Parta = binary_to_list(iolist_to_binary(proplists:get_value(parta, Node))), |
|
|
|
|
|
Partb = proplists:get_value(partb, Node), |
|
|
|
|
|
Partb2 = |
|
|
|
|
|
case is_tuple(Partb) of |
|
|
|
|
|
false -> Partb; |
|
|
|
|
|
true -> element(1, Partb) |
|
|
|
|
|
end, |
|
|
|
|
|
{Parta, Partb2} |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'type'(input(), index()) -> parse_result(). |
|
|
|
|
|
'type'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'type', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_string(<<".">>), p_label('name', fun 'name'/2), fun 'blank0'/2, p_label('struct', fun 'struct'/2)]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Name = binary_to_list(iolist_to_binary(proplists:get_value(name, Node))), |
|
|
|
|
|
Struct = proplists:get_value(struct, Node), |
|
|
|
|
|
Tag = get(messageid), |
|
|
|
|
|
put(messageid, Tag + 1), |
|
|
|
|
|
{Name, Tag, Struct} |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'struct'(input(), index()) -> parse_result(). |
|
|
|
|
|
'struct'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'struct', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_string(<<"{">>), fun 'blank0'/2, p_zero_or_more(p_seq([p_choose([fun 'field'/2, fun 'type'/2]), fun 'blank0'/2])), p_string(<<"}">>)]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
[_, _, List, _] = Node, |
|
|
|
|
|
[H || [H | _] <- List] |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'field'(input(), index()) -> parse_result(). |
|
|
|
|
|
'field'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'field', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_label('name', fun 'name'/2), fun 'blanks'/2, p_string(<<":">>), fun 'blank0'/2, p_label('isarray', p_zero_or_more(p_string(<<"*">>))), p_label('datatype', fun 'typename'/2), p_label('key', p_zero_or_more(fun 'mainkey'/2))]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Name = binary_to_list(iolist_to_binary(proplists:get_value(name, Node))), |
|
|
|
|
|
IsArray = |
|
|
|
|
|
case proplists:get_value(isarray, Node) =:= [<<"*">>] of |
|
|
|
|
|
true -> |
|
|
|
|
|
true; |
|
|
|
|
|
false -> |
|
|
|
|
|
false |
|
|
|
|
|
end, |
|
|
|
|
|
{FieldType, DataType} = |
|
|
|
|
|
case proplists:get_value(datatype, Node) of |
|
|
|
|
|
{"boolean", _} -> |
|
|
|
|
|
case IsArray of |
|
|
|
|
|
true -> |
|
|
|
|
|
{4, 1}; |
|
|
|
|
|
_ -> |
|
|
|
|
|
{1, 1} |
|
|
|
|
|
end; |
|
|
|
|
|
{"integer", _} -> |
|
|
|
|
|
case IsArray of |
|
|
|
|
|
true -> |
|
|
|
|
|
{4, 2}; |
|
|
|
|
|
_ -> |
|
|
|
|
|
{2, 2} |
|
|
|
|
|
end; |
|
|
|
|
|
{"string", _} -> |
|
|
|
|
|
case IsArray of |
|
|
|
|
|
true -> |
|
|
|
|
|
{4, 3}; |
|
|
|
|
|
_ -> |
|
|
|
|
|
{3, 3} |
|
|
|
|
|
end; |
|
|
|
|
|
{Other, SubName} -> |
|
|
|
|
|
case IsArray of |
|
|
|
|
|
true -> |
|
|
|
|
|
{4, Other ++ SubName}; |
|
|
|
|
|
_ -> |
|
|
|
|
|
{5, Other ++ SubName} |
|
|
|
|
|
end |
|
|
|
|
|
end, |
|
|
|
|
|
{Name, FieldType, DataType} |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'eof'(input(), index()) -> parse_result(). |
|
|
|
|
|
'eof'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'eof', fun(I, D) -> (p_not(p_string(<<".">>)))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'newline'(input(), index()) -> parse_result(). |
|
|
|
|
|
'newline'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'newline', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_optional(p_charclass(<<"[\r]">>)), p_charclass(<<"[\n]">>)]))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'line_comment'(input(), index()) -> parse_result(). |
|
|
|
|
|
'line_comment'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'line_comment', |
|
|
|
|
|
fun(I, D) -> |
|
|
|
|
|
(p_seq([p_string(<<"#">>), p_label('errorcode', p_zero_or_more(p_seq([p_string(<<"$errcode">>), fun 'blanks'/2, fun 'name'/2, fun 'blanks'/2, fun 'words'/2]))), p_zero_or_more(p_seq([p_not(fun 'newline'/2), p_anything()])), p_choose([fun 'newline'/2, fun 'eof'/2])]))(I, D) |
|
|
|
|
|
end, |
|
|
|
|
|
fun(Node, _Idx) -> |
|
|
|
|
|
case proplists:get_value(errorcode, Node) of |
|
|
|
|
|
[[_ErrorCode, _Space, Tail, _Space1, Comment]] -> |
|
|
|
|
|
ErrName = binary_to_list(iolist_to_binary(Tail)), |
|
|
|
|
|
ComDesc = (iolist_to_binary(Comment)), |
|
|
|
|
|
ErrNameList = erlang:get(errorname), |
|
|
|
|
|
case ErrName =/= [] andalso lists:keyfind(ErrName, 1, ErrNameList) == false of |
|
|
|
|
|
true -> |
|
|
|
|
|
ErrCode = erlang:get(errorid), |
|
|
|
|
|
erlang:put(errorname, [{ErrName, ErrCode, ComDesc} | ErrNameList]), |
|
|
|
|
|
erlang:put(errorid, ErrCode + 1); |
|
|
|
|
|
_ -> |
|
|
|
|
|
skip |
|
|
|
|
|
end; |
|
|
|
|
|
_AAa -> |
|
|
|
|
|
skip |
|
|
|
|
|
end, |
|
|
|
|
|
Node |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'blank'(input(), index()) -> parse_result(). |
|
|
|
|
|
'blank'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'blank', fun(I, D) -> |
|
|
|
|
|
(p_choose([p_charclass(<<"[\s\t]">>), fun 'newline'/2, fun 'errorcode'/2, fun 'line_comment'/2]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'blank0'(input(), index()) -> parse_result(). |
|
|
|
|
|
'blank0'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'blank0', fun(I, D) -> (p_zero_or_more(fun 'blank'/2))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'blanks'(input(), index()) -> parse_result(). |
|
|
|
|
|
'blanks'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'blanks', fun(I, D) -> (p_one_or_more(fun 'blank'/2))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'alpha'(input(), index()) -> parse_result(). |
|
|
|
|
|
'alpha'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'alpha', fun(I, D) -> |
|
|
|
|
|
(p_choose([p_charclass(<<"[a-z]">>), p_charclass(<<"[A-Z]">>), p_string(<<"_">>)]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'str'(input(), index()) -> parse_result(). |
|
|
|
|
|
'str'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'str', |
|
|
|
|
|
fun(I, D) -> |
|
|
|
|
|
(p_seq([p_not(fun 'newline'/2), p_charclass(<<".">>)]))(I, D) |
|
|
|
|
|
end, |
|
|
|
|
|
fun(Node, _Idx) -> |
|
|
|
|
|
Node |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'alnum'(input(), index()) -> parse_result(). |
|
|
|
|
|
'alnum'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'alnum', fun(I, D) -> |
|
|
|
|
|
(p_choose([fun 'alpha'/2, p_charclass(<<"[0-9]">>)]))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'word'(input(), index()) -> parse_result(). |
|
|
|
|
|
'word'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'word', fun(I, D) -> |
|
|
|
|
|
(p_seq([fun 'alpha'/2, p_zero_or_more(fun 'alnum'/2)]))(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'words'(input(), index()) -> parse_result(). |
|
|
|
|
|
'words'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'words', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_choose([p_zero_or_more(fun 'str'/2), p_zero_or_more(fun 'word'/2)])]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'name'(input(), index()) -> parse_result(). |
|
|
|
|
|
'name'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'name', fun(I, D) -> (fun 'word'/2)(I, D) end, fun(Node, _Idx) -> Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'typename'(input(), index()) -> parse_result(). |
|
|
|
|
|
'typename'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'typename', fun(I, D) -> |
|
|
|
|
|
(p_seq([fun 'word'/2, p_zero_or_more(p_seq([p_string(<<".">>), fun 'word'/2]))]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
[Head, List] = Node, |
|
|
|
|
|
SubName = |
|
|
|
|
|
case List of |
|
|
|
|
|
[] -> ""; |
|
|
|
|
|
_ -> |
|
|
|
|
|
List2 = [[Dot, iolist_to_binary(Word)] || [Dot, Word] <- List], |
|
|
|
|
|
binary_to_list(list_to_binary(lists:append(List2))) |
|
|
|
|
|
end, |
|
|
|
|
|
{binary_to_list(iolist_to_binary(Head)), SubName} |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'tag'(input(), index()) -> parse_result(). |
|
|
|
|
|
'tag'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'tag', fun(I, D) -> (p_one_or_more(p_charclass(<<"[0-9]">>)))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
Node end). |
|
|
|
|
|
|
|
|
|
|
|
-spec 'mainkey'(input(), index()) -> parse_result(). |
|
|
|
|
|
'mainkey'(Input, Index) -> |
|
|
|
|
|
p(Input, Index, 'mainkey', fun(I, D) -> |
|
|
|
|
|
(p_seq([p_string(<<"(">>), fun 'blank0'/2, p_label('name', fun 'name'/2), fun 'blank0'/2, p_string(<<")">>)]))(I, D) end, fun(Node, _Idx) -> |
|
|
|
|
|
proplists:get_value(name, Node) |
|
|
|
|
|
end). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-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} -> % 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 |
|
|
|
|
|
<<S:Length/binary, Rest/binary>> = 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) -> |
|
|
|
|
|
<<C/utf8, Rest/binary>> = Input, |
|
|
|
|
|
{<<C/utf8>>, Rest, p_advance_index(<<C/utf8>>, 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. |