-module(katt_blueprint). -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_scan,true). -define(p_seq,true). -define(p_string,true). -define(p_zero_or_more,true). %%% This file is generated by neotoma. -include("blueprint_types.hrl"). null_if_empty([]) -> null; null_if_empty(String) -> String. concatenate_lines([H|_]=Lines) when is_binary(H) -> StringList = lists:map(fun unicode:characters_to_list/1, Lines), unicode:characters_to_binary(string:join(StringList, "\n")); concatenate_lines([]) -> []. concatenate_chars(Chars) -> << <> || <> <- Chars >>. try_to_convert_to([], _Value) -> throw({error, unknown_param_type}); try_to_convert_to([boolean|Rest], Value0) -> Value = string:to_lower(Value0), case Value of "true" -> true; "false" -> false; _ -> try_to_convert_to(Rest, Value0) end; try_to_convert_to([float|Rest], Value) -> case string:to_float(Value) of {error, _} -> try_to_convert_to(Rest, Value); {FloatValue, []} -> FloatValue; _ -> try_to_convert_to(Rest, Value) end; try_to_convert_to([integer|Rest], Value) -> case string:to_integer(Value) of {error, _} -> try_to_convert_to(Rest, Value); {IntValue, []} -> IntValue; _ -> try_to_convert_to(Rest, Value) end; try_to_convert_to([null|Rest], Value0) -> Value = string:to_lower(Value0), case Value of "null" -> null; _ -> try_to_convert_to(Rest, Value) end. -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 'api'(Input,{{line,1},{column,1}}) of {AST, <<>>, _Index} -> AST; Any -> Any end, release_memo(), Result. -spec 'api'(input(), index()) -> parse_result(). 'api'(Input, Index) -> p(Input, Index, 'api', fun(I,D) -> (p_seq([p_zero_or_more(fun 'empty_line'/2), p_label('name', p_optional(fun 'api_name'/2)), p_zero_or_more(fun 'empty_line'/2), p_label('description', p_optional(fun 'api_description'/2)), p_zero_or_more(fun 'empty_line'/2), p_label('transactions', fun 'transactions'/2), p_label('footer', p_optional(fun 'api_footer'/2)), fun 'eof'/2]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Description = proplists:get_value(description, Node), Transactions = proplists:get_value(transactions, Node), Footer = proplists:get_value(footer, Node), #katt_blueprint{ name=null_if_empty(Name), description=null_if_empty(Description), transactions=Transactions, footer=null_if_empty(Footer) } end). -spec 'api_name'(input(), index()) -> parse_result(). 'api_name'(Input, Index) -> p(Input, Index, 'api_name', fun(I,D) -> (p_seq([p_not(fun 'http_method'/2), p_string(<<"---">>), p_one_or_more(fun 's'/2), p_label('name', fun 'text1'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), re:replace(Name, "\s+---$", "", [{return, binary}]) end). -spec 'api_description'(input(), index()) -> parse_result(). 'api_description'(Input, Index) -> p(Input, Index, 'api_description', fun(I,D) -> (p_seq([p_not(fun 'http_method'/2), p_string(<<"---">>), p_zero_or_more(fun 's'/2), fun 'eol'/2, p_label('lines', p_zero_or_more(fun 'api_description_line'/2)), p_string(<<"---">>), p_zero_or_more(fun 's'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> concatenate_lines(proplists:get_value(lines, Node)) end). -spec 'api_description_line'(input(), index()) -> parse_result(). 'api_description_line'(Input, Index) -> p(Input, Index, 'api_description_line', fun(I,D) -> (p_seq([p_not(p_seq([p_string(<<"---">>), p_zero_or_more(fun 's'/2), fun 'eolf'/2])), p_label('text', fun 'text0'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(text, Node) end). -spec 'api_footer'(input(), index()) -> parse_result(). 'api_footer'(Input, Index) -> p(Input, Index, 'api_footer', fun(I,D) -> (p_seq([p_one_or_more(fun 'empty_line'/2), p_label('lines', p_zero_or_more(fun 'api_footer_line'/2))]))(I,D) end, fun(Node, _Idx) -> concatenate_lines(proplists:get_value(lines, Node)) end). -spec 'api_footer_line'(input(), index()) -> parse_result(). 'api_footer_line'(Input, Index) -> p(Input, Index, 'api_footer_line', fun(I,D) -> (p_seq([p_label('text', fun 'text0'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(text, Node) end). -spec 'transactions'(input(), index()) -> parse_result(). 'transactions'(Input, Index) -> p(Input, Index, 'transactions', fun(I,D) -> (p_seq([p_label('head', p_optional(fun 'transaction'/2)), p_label('tail', p_zero_or_more(fun 'transactions_tail'/2))]))(I,D) end, fun(Node, _Idx) -> Head = proplists:get_value(head, Node), Tail = proplists:get_value(tail, Node), case Head of [] -> Tail; _ -> [Head | Tail] end end). -spec 'transactions_tail'(input(), index()) -> parse_result(). 'transactions_tail'(Input, Index) -> p(Input, Index, 'transactions_tail', fun(I,D) -> (p_seq([p_zero_or_more(fun 'empty_line'/2), p_label('transaction', fun 'transaction'/2)]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(transaction, Node) end). -spec 'transaction'(input(), index()) -> parse_result(). 'transaction'(Input, Index) -> p(Input, Index, 'transaction', fun(I,D) -> (p_seq([p_label('description', p_optional(fun 'transaction_description'/2)), p_label('params', p_optional(fun 'transaction_params'/2)), p_label('request', fun 'request'/2), p_label('response', fun 'response'/2)]))(I,D) end, fun(Node, _Idx) -> Description = proplists:get_value(description, Node), Params = proplists:get_value(params, Node), Request = proplists:get_value(request, Node), Response = proplists:get_value(response, Node), #katt_transaction{ description=null_if_empty(Description), params=Params, request=Request, response=Response } end). -spec 'transaction_description'(input(), index()) -> parse_result(). 'transaction_description'(Input, Index) -> p(Input, Index, 'transaction_description', fun(I,D) -> (p_one_or_more(fun 'transaction_description_line'/2))(I,D) end, fun(Node, _Idx) -> concatenate_lines(Node) end). -spec 'transaction_description_line'(input(), index()) -> parse_result(). 'transaction_description_line'(Input, Index) -> p(Input, Index, 'transaction_description_line', fun(I,D) -> (p_seq([p_not(fun 'http_method_or_transaction_param_keyword'/2), p_label('text', fun 'text0'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(text, Node) end). -spec 'http_method_or_transaction_param_keyword'(input(), index()) -> parse_result(). 'http_method_or_transaction_param_keyword'(Input, Index) -> p(Input, Index, 'http_method_or_transaction_param_keyword', fun(I,D) -> (p_choose([fun 'http_method'/2, fun 'transaction_param_keyword'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'transaction_params'(input(), index()) -> parse_result(). 'transaction_params'(Input, Index) -> p(Input, Index, 'transaction_params', fun(I,D) -> (p_seq([p_label('params', p_zero_or_more(fun 'transaction_param'/2)), p_zero_or_more(fun 'empty_line'/2)]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(params, Node) end). -spec 'transaction_param'(input(), index()) -> parse_result(). 'transaction_param'(Input, Index) -> p(Input, Index, 'transaction_param', fun(I,D) -> (p_choose([fun 'transaction_param_string'/2, fun 'transaction_param_nonstring'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'transaction_param_string'(input(), index()) -> parse_result(). 'transaction_param_string'(Input, Index) -> p(Input, Index, 'transaction_param_string', fun(I,D) -> (p_seq([fun 'transaction_param_keyword'/2, p_one_or_more(fun 's'/2), p_label('name', fun 'transaction_param_name'/2), p_string(<<"=">>), p_string(<<"\"">>), p_label('value', fun 'transaction_param_value_string'/2), p_string(<<"\"">>), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Value = proplists:get_value(value, Node), {Name, Value} end). -spec 'transaction_param_nonstring'(input(), index()) -> parse_result(). 'transaction_param_nonstring'(Input, Index) -> p(Input, Index, 'transaction_param_nonstring', fun(I,D) -> (p_seq([fun 'transaction_param_keyword'/2, p_one_or_more(fun 's'/2), p_label('name', fun 'transaction_param_name'/2), p_string(<<"=">>), p_label('value', fun 'transaction_param_value_nonstring'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Value0 = proplists:get_value(value, Node), Value = try_to_convert_to([boolean, null, integer, float], Value0), {Name, Value} end). -spec 'transaction_param_keyword'(input(), index()) -> parse_result(). 'transaction_param_keyword'(Input, Index) -> p(Input, Index, 'transaction_param_keyword', fun(I,D) -> (p_string(<<"PARAM">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'transaction_param_name'(input(), index()) -> parse_result(). 'transaction_param_name'(Input, Index) -> p(Input, Index, 'transaction_param_name', fun(I,D) -> (p_one_or_more(p_charclass(<<"[a-z0-9_]">>)))(I,D) end, fun(Node, _Idx) -> unicode:characters_to_list(concatenate_chars(Node)) end). -spec 'transaction_param_value_string'(input(), index()) -> parse_result(). 'transaction_param_value_string'(Input, Index) -> p(Input, Index, 'transaction_param_value_string', fun(I,D) -> (p_zero_or_more(p_charclass(<<"[^\"]">>)))(I,D) end, fun(Node, _Idx) -> unicode:characters_to_list(concatenate_chars(Node)) end). -spec 'transaction_param_value_nonstring'(input(), index()) -> parse_result(). 'transaction_param_value_nonstring'(Input, Index) -> p(Input, Index, 'transaction_param_value_nonstring', fun(I,D) -> (fun 'text0'/2)(I,D) end, fun(Node, _Idx) -> unicode:characters_to_list(Node) end). -spec 'http_method'(input(), index()) -> parse_result(). 'http_method'(Input, Index) -> p(Input, Index, 'http_method', fun(I,D) -> (p_choose([p_string(<<"GET">>), p_string(<<"POST">>), p_string(<<"PUT">>), p_string(<<"DELETE">>), p_string(<<"OPTIONS">>), p_string(<<"PATCH">>), p_string(<<"PROPPATCH">>), p_string(<<"LOCK">>), p_string(<<"UNLOCK">>), p_string(<<"COPY">>), p_string(<<"MOVE">>), p_string(<<"MKCOL">>), p_string(<<"HEAD">>)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'request'(input(), index()) -> parse_result(). 'request'(Input, Index) -> p(Input, Index, 'request', fun(I,D) -> (p_seq([p_label('signature', fun 'signature'/2), p_label('headers', fun 'request_headers'/2), p_label('body', p_optional(fun 'body'/2))]))(I,D) end, fun(Node, _Idx) -> {Method, Url} = proplists:get_value(signature, Node), Headers = proplists:get_value(headers, Node), Body = proplists:get_value(body, Node), #katt_request{ method=Method, url=Url, headers=Headers, body=null_if_empty(Body) } end). -spec 'request_headers'(input(), index()) -> parse_result(). 'request_headers'(Input, Index) -> p(Input, Index, 'request_headers', fun(I,D) -> (p_zero_or_more(fun 'request_header'/2))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'request_header'(input(), index()) -> parse_result(). 'request_header'(Input, Index) -> p(Input, Index, 'request_header', fun(I,D) -> (p_seq([fun 'in'/2, p_label('header', fun 'http_header'/2)]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(header, Node) end). -spec 'response'(input(), index()) -> parse_result(). 'response'(Input, Index) -> p(Input, Index, 'response', fun(I,D) -> (p_seq([p_label('status', fun 'response_status'/2), p_label('headers', fun 'response_headers'/2), p_label('body', p_optional(fun 'body'/2))]))(I,D) end, fun(Node, _Idx) -> Status = proplists:get_value(status, Node), Headers = proplists:get_value(headers, Node), Body = proplists:get_value(body, Node), #katt_response{ status=Status, headers=Headers, body=null_if_empty(Body) } end). -spec 'response_status'(input(), index()) -> parse_result(). 'response_status'(Input, Index) -> p(Input, Index, 'response_status', fun(I,D) -> (p_seq([fun 'out'/2, p_label('status', fun 'http_status'/2), p_zero_or_more(fun 's'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(status, Node) end). -spec 'response_headers'(input(), index()) -> parse_result(). 'response_headers'(Input, Index) -> p(Input, Index, 'response_headers', fun(I,D) -> (p_zero_or_more(fun 'response_header'/2))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'response_header'(input(), index()) -> parse_result(). 'response_header'(Input, Index) -> p(Input, Index, 'response_header', fun(I,D) -> (p_seq([fun 'out'/2, fun 'http_header'/2]))(I,D) end, fun(Node, _Idx) -> [_, Header] = Node, Header end). -spec 'http_status'(input(), index()) -> parse_result(). 'http_status'(Input, Index) -> p(Input, Index, 'http_status', fun(I,D) -> (p_seq([p_charclass(<<"[1-5]">>), p_charclass(<<"[0-9]">>), p_charclass(<<"[0-9]">>)]))(I,D) end, fun(Node, _Idx) -> {Int, _Rest} = string:to_integer(unicode:characters_to_list(concatenate_chars(Node))), Int end). -spec 'http_header'(input(), index()) -> parse_result(). 'http_header'(Input, Index) -> p(Input, Index, 'http_header', fun(I,D) -> (p_seq([p_label('name', fun 'http_header_name'/2), p_string(<<":">>), p_zero_or_more(fun 's'/2), p_label('value', fun 'http_header_value'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> Name = proplists:get_value(name, Node), Value = proplists:get_value(value, Node), {Name, Value} end). -spec 'http_header_name'(input(), index()) -> parse_result(). 'http_header_name'(Input, Index) -> p(Input, Index, 'http_header_name', fun(I,D) -> (p_one_or_more(p_charclass(<<"[\x21-\x39\x3B-\x7E]">>)))(I,D) end, fun(Node, _Idx) -> unicode:characters_to_list(concatenate_chars(Node)) end). -spec 'http_header_value'(input(), index()) -> parse_result(). 'http_header_value'(Input, Index) -> p(Input, Index, 'http_header_value', fun(I,D) -> (fun 'text0'/2)(I,D) end, fun(Node, _Idx) -> unicode:characters_to_list(Node) end). -spec 'signature'(input(), index()) -> parse_result(). 'signature'(Input, Index) -> p(Input, Index, 'signature', fun(I,D) -> (p_seq([p_label('method', fun 'http_method'/2), p_one_or_more(fun 's'/2), p_label('url', fun 'text1'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> Method = unicode:characters_to_list(proplists:get_value(method, Node)), Url = unicode:characters_to_list(proplists:get_value(url, Node)), {Method, Url} end). -spec 'body'(input(), index()) -> parse_result(). 'body'(Input, Index) -> p(Input, Index, 'body', fun(I,D) -> (p_choose([fun 'delimited_body_fixed'/2, fun 'simple_body'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'delimited_body_fixed'(input(), index()) -> parse_result(). 'delimited_body_fixed'(Input, Index) -> p(Input, Index, 'delimited_body_fixed', fun(I,D) -> (p_seq([p_string(<<"<<<">>), p_zero_or_more(fun 's'/2), fun 'eol'/2, p_label('lines', p_zero_or_more(fun 'delimited_body_fixed_line'/2)), p_string(<<">>>">>), p_zero_or_more(fun 's'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> concatenate_lines(proplists:get_value(lines, Node)) end). -spec 'delimited_body_fixed_line'(input(), index()) -> parse_result(). 'delimited_body_fixed_line'(Input, Index) -> p(Input, Index, 'delimited_body_fixed_line', fun(I,D) -> (p_seq([p_not(p_seq([p_string(<<">>>">>), p_zero_or_more(fun 's'/2), fun 'eolf'/2])), p_label('text', fun 'text0'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(text, Node) end). -spec 'simple_body'(input(), index()) -> parse_result(). 'simple_body'(Input, Index) -> p(Input, Index, 'simple_body', fun(I,D) -> (p_seq([p_not(p_string(<<"<<<">>)), p_label('lines', p_one_or_more(fun 'simple_body_line'/2))]))(I,D) end, fun(Node, _Idx) -> concatenate_lines(proplists:get_value(lines, Node)) end). -spec 'simple_body_line'(input(), index()) -> parse_result(). 'simple_body_line'(Input, Index) -> p(Input, Index, 'simple_body_line', fun(I,D) -> (p_seq([p_not(fun 'in'/2), p_not(fun 'out'/2), p_not(fun 'empty_line'/2), p_label('text', fun 'text1'/2), fun 'eolf'/2]))(I,D) end, fun(Node, _Idx) -> proplists:get_value(text, Node) end). -spec 'in'(input(), index()) -> parse_result(). 'in'(Input, Index) -> p(Input, Index, 'in', fun(I,D) -> (p_seq([p_string(<<">">>), p_one_or_more(fun 's'/2)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'out'(input(), index()) -> parse_result(). 'out'(Input, Index) -> p(Input, Index, 'out', fun(I,D) -> (p_seq([p_string(<<"<">>), p_one_or_more(fun 's'/2)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'text0'(input(), index()) -> parse_result(). 'text0'(Input, Index) -> p(Input, Index, 'text0', fun(I,D) -> (p_zero_or_more(p_charclass(<<"[^\n\r]">>)))(I,D) end, fun(Node, _Idx) -> concatenate_chars(Node) end). -spec 'text1'(input(), index()) -> parse_result(). 'text1'(Input, Index) -> p(Input, Index, 'text1', fun(I,D) -> (p_one_or_more(p_charclass(<<"[^\n\r]">>)))(I,D) end, fun(Node, _Idx) -> concatenate_chars(Node) end). -spec 'empty_line'(input(), index()) -> parse_result(). 'empty_line'(Input, Index) -> p(Input, Index, 'empty_line', fun(I,D) -> (p_seq([p_zero_or_more(fun 's'/2), fun 'eol'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'eolf'(input(), index()) -> parse_result(). 'eolf'(Input, Index) -> p(Input, Index, 'eolf', fun(I,D) -> (p_choose([fun 'eol'/2, fun 'eof'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'eol'(input(), index()) -> parse_result(). 'eol'(Input, Index) -> p(Input, Index, 'eol', fun(I,D) -> (p_choose([p_string(<<"\n">>), p_string(<<"\r\n">>), p_string(<<"\r">>)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'eof'(input(), index()) -> parse_result(). 'eof'(Input, Index) -> p(Input, Index, 'eof', fun(I,D) -> (p_not(p_anything()))(I,D) end, fun(_Node, _Idx) -> <<>> end). -spec 's'(input(), index()) -> parse_result(). 's'(Input, Index) -> p(Input, Index, 's', fun(I,D) -> (p_charclass(<<"[\t\v\f\s]">>))(I,D) end, fun(Node, _Idx) ->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} -> %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.