-module(solid_parser). -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). -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 'text'(Input,{{line,1},{column,1}}) of {AST, <<>>, _Index} -> AST; Any -> Any end, release_memo(), Result. -spec 'text'(input(), index()) -> parse_result(). 'text'(Input, Index) -> p(Input, Index, 'text', fun(I,D) -> (p_seq([p_label('string', fun 'blob'/2), p_optional(p_choose([p_seq([p_label('object', fun 'object'/2), p_label('text', fun 'text'/2)]), p_seq([p_label('tag', fun 'tag'/2), p_label('text', fun 'text'/2)]), p_seq([p_label('open_object', fun 'open_object'/2), p_label('text', fun 'text'/2)])]))]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'blob'(input(), index()) -> parse_result(). 'blob'(Input, Index) -> p(Input, Index, 'blob', fun(I,D) -> (p_zero_or_more(p_seq([p_not(fun 'open_object'/2), p_not(fun 'open_tag'/2), p_anything()])))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'object'(input(), index()) -> parse_result(). 'object'(Input, Index) -> p(Input, Index, 'object', fun(I,D) -> (p_seq([p_label('open_object', fun 'open_object'/2), fun 'space'/2, p_optional(p_choose([p_seq([p_label('argument', fun 'argument'/2), p_label('filters', fun 'filters'/2)]), p_label('argument', fun 'argument'/2)])), fun 'space'/2, p_label('close_object', fun 'close_object'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, Block, _, _] -> lists:flatten([Block]) end end). -spec 'tag'(input(), index()) -> parse_result(). 'tag'(Input, Index) -> p(Input, Index, 'tag', fun(I,D) -> (p_choose([fun 'cond_if_tag'/2, fun 'cond_unless_tag'/2, fun 'cond_case_tag'/2, fun 'assign_tag'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'assign_tag'(input(), index()) -> parse_result(). 'assign_tag'(Input, Index) -> p(Input, Index, 'assign_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'assign'/2, fun 'space'/2, fun 'field'/2, fun 'space'/2, p_string(<<"=">>), fun 'space'/2, fun 'argument'/2, fun 'space'/2, fun 'close_tag'/2]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _, _, Field, _, _, _, Argument, _, _] -> {assign_exp, Field, Argument} end end). -spec 'cond_case_tag'(input(), index()) -> parse_result(). 'cond_case_tag'(Input, Index) -> p(Input, Index, 'cond_case_tag', fun(I,D) -> (p_seq([fun 'case_tag'/2, p_one_or_more(fun 'when_tag'/2), p_optional(fun 'else_tag'/2), fun 'open_tag'/2, fun 'space'/2, fun 'endcase'/2, fun 'space'/2, fun 'close_tag'/2]))(I,D) end, fun(Node, _Idx) -> case Node of [Case, Whens, Else, _, _, _, _, _] -> WhenMap = lists:foldl(fun({ Value, Text }, Map) -> maps:put(Value, Text, Map) end, #{}, Whens), [Case, {whens, WhenMap}, Else] end end). -spec 'case_tag'(input(), index()) -> parse_result(). 'case_tag'(Input, Index) -> p(Input, Index, 'case_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'case'/2, fun 'space'/2, fun 'argument'/2, fun 'space'/2, fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _Case, _, Arg, _, _, _Text] -> {case_exp, [Arg]} end end). -spec 'when_tag'(input(), index()) -> parse_result(). 'when_tag'(Input, Index) -> p(Input, Index, 'when_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'when'/2, fun 'value'/2, fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _When, Value, _, Text] -> { Value, [Text] } end end). -spec 'cond_if_tag'(input(), index()) -> parse_result(). 'cond_if_tag'(Input, Index) -> p(Input, Index, 'cond_if_tag', fun(I,D) -> (p_seq([fun 'if_tag'/2, p_zero_or_more(fun 'elsif_tag'/2), p_optional(fun 'else_tag'/2), fun 'open_tag'/2, fun 'space'/2, fun 'endif'/2, fun 'space'/2, fun 'close_tag'/2]))(I,D) end, fun(Node, _Idx) -> case Node of [IfExp, [], [], _, _, _EndIf, _, _] -> [IfExp]; [IfExp, [], ElseExp, _, _, _EndIf, _, _] -> [IfExp, ElseExp]; [IfExp, Elsifs, [], _, _, _EndIf, _, _] -> [IfExp, {elsif_exps, Elsifs}]; [IfExp, Elsifs, ElseExp, _, _, _EndIf, _, _] -> [IfExp, Elsifs, ElseExp] end end). -spec 'if_tag'(input(), index()) -> parse_result(). 'if_tag'(Input, Index) -> p(Input, Index, 'if_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'if'/2, p_label('expression', fun 'boolean_expression'/2), fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _If, Exp, _, Text] -> {if_exp, [Exp, Text]} end end). -spec 'elsif_tag'(input(), index()) -> parse_result(). 'elsif_tag'(Input, Index) -> p(Input, Index, 'elsif_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'elsif'/2, p_label('expression', fun 'boolean_expression'/2), fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _Elsif, Exp, _, Text] -> {elsif_exp, [Exp, Text]} end end). -spec 'else_tag'(input(), index()) -> parse_result(). 'else_tag'(Input, Index) -> p(Input, Index, 'else_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'else'/2, fun 'space'/2, fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _Else, _, _, ElseText] -> {else_exp, [ElseText]} end end). -spec 'unless_tag'(input(), index()) -> parse_result(). 'unless_tag'(Input, Index) -> p(Input, Index, 'unless_tag', fun(I,D) -> (p_seq([fun 'open_tag'/2, fun 'space'/2, fun 'unless'/2, p_label('expression', fun 'boolean_expression'/2), fun 'close_tag'/2, p_label('text', fun 'text'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, _, _Unless, Exp, _, Text] -> {unless_exp, [Exp, Text]} end end). -spec 'cond_unless_tag'(input(), index()) -> parse_result(). 'cond_unless_tag'(Input, Index) -> p(Input, Index, 'cond_unless_tag', fun(I,D) -> (p_seq([fun 'unless_tag'/2, p_zero_or_more(fun 'elsif_tag'/2), p_optional(fun 'else_tag'/2), fun 'open_tag'/2, fun 'space'/2, fun 'endunless'/2, fun 'space'/2, fun 'close_tag'/2]))(I,D) end, fun(Node, _Idx) -> case Node of [UnlessExp, [], _, _, _EndUnless, _, _] -> [UnlessExp]; [UnlessExp, [], ElseExp, _, _, _EndUnless, _, _] -> [UnlessExp, ElseExp]; [UnlessExp, Elsifs, [], _, _, _EndUnless, _, _] -> [UnlessExp, {elsif_exps, Elsifs}]; [UnlessExp, Elsifs, ElseExp, _, _, _EndUnless, _, _] -> [UnlessExp, Elsifs, ElseExp] end end). -spec 'expression'(input(), index()) -> parse_result(). 'expression'(Input, Index) -> p(Input, Index, 'expression', fun(I,D) -> (p_seq([fun 'space'/2, p_choose([p_seq([fun 'argument'/2, fun 'space'/2, fun 'operator'/2, fun 'space'/2, fun 'argument'/2]), fun 'boolean'/2]), fun 'space'/2]))(I,D) end, fun(Node, _Idx) -> case Node of [_, [Arg1, _, Op, _, Arg2], _] -> {Arg1, Op, Arg2}; [_, Bool, _] -> Bool end end). -spec 'boolean_expression'(input(), index()) -> parse_result(). 'boolean_expression'(Input, Index) -> p(Input, Index, 'boolean_expression', fun(I,D) -> (p_seq([fun 'expression'/2, p_zero_or_more(p_seq([p_choose([fun 'and'/2, fun 'or'/2]), fun 'expression'/2]))]))(I,D) end, fun(Node, _Idx) -> case Node of [Exp, Exps] -> [Exp | Exps] end end). -spec 'filters'(input(), index()) -> parse_result(). 'filters'(Input, Index) -> p(Input, Index, 'filters', fun(I,D) -> (p_one_or_more(p_seq([fun 'space'/2, p_string(<<"|">>), fun 'space'/2, fun 'filter'/2, p_optional(p_seq([p_string(<<":">>), fun 'space'/2, fun 'arguments'/2]))])))(I,D) end, fun(Node, _Idx) -> [case N of [_, _, _, Filter, [_, _, Args]] -> {Filter, Args}; [_, _, _, Filter, _] -> {Filter, []} end || N <- Node] end). -spec 'arguments'(input(), index()) -> parse_result(). 'arguments'(Input, Index) -> p(Input, Index, 'arguments', fun(I,D) -> (p_seq([fun 'argument'/2, p_zero_or_more(p_seq([fun 'space'/2, p_string(<<",">>), fun 'space'/2, fun 'argument'/2]))]))(I,D) end, fun(Node, _Idx) -> case Node of [Arg1, Args] -> [Arg1 | [Arg || [_, _, _, Arg] <- Args]] end end). -spec 'argument'(input(), index()) -> parse_result(). 'argument'(Input, Index) -> p(Input, Index, 'argument', fun(I,D) -> (p_choose([p_label('value', fun 'value'/2), fun 'field'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'field'(input(), index()) -> parse_result(). 'field'(Input, Index) -> p(Input, Index, 'field', fun(I,D) -> (p_seq([p_zero_or_more(p_charclass(<<"[0-9a-zA-Z\.]">>)), p_zero_or_more(fun 'access'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [FieldName | [[]]] -> {field, iolist_to_binary(FieldName)}; [FieldName | [Accesses]] -> {field, iolist_to_binary(FieldName), Accesses} end end). -spec 'access'(input(), index()) -> parse_result(). 'access'(Input, Index) -> p(Input, Index, 'access', fun(I,D) -> (p_seq([p_string(<<"[">>), fun 'int'/2, p_string(<<"]">>)]))(I,D) end, fun(Node, _Idx) -> case Node of [_, Integer, _] -> {access, list_to_integer(binary_to_list(iolist_to_binary(Integer)))} end end). -spec 'value'(input(), index()) -> parse_result(). 'value'(Input, Index) -> p(Input, Index, 'value', fun(I,D) -> (p_seq([fun 'space'/2, p_choose([fun 'string'/2, fun 'number'/2, fun 'true'/2, fun 'false'/2, fun 'null'/2]), fun 'space'/2]))(I,D) end, fun(Node, _Idx) ->lists:nth(2, Node) end). -spec 'string'(input(), index()) -> parse_result(). 'string'(Input, Index) -> p(Input, Index, 'string', fun(I,D) -> (p_choose([fun 'single_quoted_string'/2, fun 'double_quoted_string'/2]))(I,D) end, fun(Node, _Idx) -> iolist_to_binary(proplists:get_value(string, Node)) end). -spec 'operator'(input(), index()) -> parse_result(). 'operator'(Input, Index) -> p(Input, Index, 'operator', fun(I,D) -> (p_choose([p_string(<<"==">>), p_string(<<"!=">>), p_string(<<">=">>), p_string(<<"<=">>), p_string(<<">">>), p_string(<<"<">>), p_string(<<"contains">>)]))(I,D) end, fun(Node, _Idx) -> binary_to_existing_atom(Node, utf8) end). -spec 'open_object'(input(), index()) -> parse_result(). 'open_object'(Input, Index) -> p(Input, Index, 'open_object', fun(I,D) -> (p_string(<<"{{">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'close_object'(input(), index()) -> parse_result(). 'close_object'(Input, Index) -> p(Input, Index, 'close_object', fun(I,D) -> (p_string(<<"}}">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'open_tag'(input(), index()) -> parse_result(). 'open_tag'(Input, Index) -> p(Input, Index, 'open_tag', fun(I,D) -> (p_string(<<"{%">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'close_tag'(input(), index()) -> parse_result(). 'close_tag'(Input, Index) -> p(Input, Index, 'close_tag', fun(I,D) -> (p_string(<<"%}">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'if'(input(), index()) -> parse_result(). 'if'(Input, Index) -> p(Input, Index, 'if', fun(I,D) -> (p_string(<<"if">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'else'(input(), index()) -> parse_result(). 'else'(Input, Index) -> p(Input, Index, 'else', fun(I,D) -> (p_string(<<"else">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'elsif'(input(), index()) -> parse_result(). 'elsif'(Input, Index) -> p(Input, Index, 'elsif', fun(I,D) -> (p_string(<<"elsif">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'endif'(input(), index()) -> parse_result(). 'endif'(Input, Index) -> p(Input, Index, 'endif', fun(I,D) -> (p_string(<<"endif">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'unless'(input(), index()) -> parse_result(). 'unless'(Input, Index) -> p(Input, Index, 'unless', fun(I,D) -> (p_string(<<"unless">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'endunless'(input(), index()) -> parse_result(). 'endunless'(Input, Index) -> p(Input, Index, 'endunless', fun(I,D) -> (p_string(<<"endunless">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'case'(input(), index()) -> parse_result(). 'case'(Input, Index) -> p(Input, Index, 'case', fun(I,D) -> (p_string(<<"case">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'when'(input(), index()) -> parse_result(). 'when'(Input, Index) -> p(Input, Index, 'when', fun(I,D) -> (p_string(<<"when">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'endcase'(input(), index()) -> parse_result(). 'endcase'(Input, Index) -> p(Input, Index, 'endcase', fun(I,D) -> (p_string(<<"endcase">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'assign'(input(), index()) -> parse_result(). 'assign'(Input, Index) -> p(Input, Index, 'assign', fun(I,D) -> (p_string(<<"assign">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'filter'(input(), index()) -> parse_result(). 'filter'(Input, Index) -> p(Input, Index, 'filter', fun(I,D) -> (p_zero_or_more(p_charclass(<<"[a-zA-Z]">>)))(I,D) end, fun(Node, _Idx) ->iolist_to_binary(Node) end). -spec 'space'(input(), index()) -> parse_result(). 'space'(Input, Index) -> p(Input, Index, 'space', fun(I,D) -> (p_zero_or_more(p_charclass(<<"[\s\t\n\s\r]">>)))(I,D) end, fun(Node, _Idx) ->{space, iolist_to_binary(Node)} end). -spec 'double_quoted_string'(input(), index()) -> parse_result(). 'double_quoted_string'(Input, Index) -> p(Input, Index, 'double_quoted_string', fun(I,D) -> (p_seq([p_string(<<"\"">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"\"">>)), p_choose([p_string(<<"\\\\">>), p_string(<<"\\\"">>), p_anything()])]))), p_string(<<"\"">>)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'single_quoted_string'(input(), index()) -> parse_result(). 'single_quoted_string'(Input, Index) -> p(Input, Index, 'single_quoted_string', fun(I,D) -> (p_seq([p_string(<<"\'">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"\'">>)), p_choose([p_string(<<"\\\\">>), p_string(<<"\\\'">>), p_anything()])]))), p_string(<<"\'">>)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'number'(input(), index()) -> parse_result(). 'number'(Input, Index) -> p(Input, Index, 'number', fun(I,D) -> (p_seq([fun 'int'/2, p_optional(fun 'frac'/2), p_optional(fun 'exp'/2)]))(I,D) end, fun(Node, _Idx) -> case Node of [Int, [], []] -> list_to_integer(binary_to_list(iolist_to_binary(Int))); [Int, Frac, []] -> list_to_float(binary_to_list(iolist_to_binary([Int, Frac]))); [Int, [], Exp] -> list_to_float(binary_to_list(iolist_to_binary([Int, ".0", Exp]))); _ -> list_to_float(binary_to_list(iolist_to_binary(Node))) end end). -spec 'int'(input(), index()) -> parse_result(). 'int'(Input, Index) -> p(Input, Index, 'int', fun(I,D) -> (p_seq([p_optional(p_string(<<"-">>)), p_choose([p_seq([fun 'non_zero_digit'/2, p_one_or_more(fun 'digit'/2)]), fun 'digit'/2])]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'frac'(input(), index()) -> parse_result(). 'frac'(Input, Index) -> p(Input, Index, 'frac', fun(I,D) -> (p_seq([p_string(<<".">>), p_one_or_more(fun 'digit'/2)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'exp'(input(), index()) -> parse_result(). 'exp'(Input, Index) -> p(Input, Index, 'exp', fun(I,D) -> (p_seq([fun 'e'/2, p_one_or_more(fun 'digit'/2)]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'e'(input(), index()) -> parse_result(). 'e'(Input, Index) -> p(Input, Index, 'e', fun(I,D) -> (p_seq([p_charclass(<<"[eE]">>), p_optional(p_choose([p_string(<<"+">>), p_string(<<"-">>)]))]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'non_zero_digit'(input(), index()) -> parse_result(). 'non_zero_digit'(Input, Index) -> p(Input, Index, 'non_zero_digit', fun(I,D) -> (p_charclass(<<"[1-9]">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'digit'(input(), index()) -> parse_result(). 'digit'(Input, Index) -> p(Input, Index, 'digit', fun(I,D) -> (p_charclass(<<"[0-9]">>))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'boolean'(input(), index()) -> parse_result(). 'boolean'(Input, Index) -> p(Input, Index, 'boolean', fun(I,D) -> (p_choose([fun 'true'/2, fun 'false'/2]))(I,D) end, fun(Node, _Idx) ->Node end). -spec 'true'(input(), index()) -> parse_result(). 'true'(Input, Index) -> p(Input, Index, 'true', fun(I,D) -> (p_string(<<"true">>))(I,D) end, fun(_Node, _Idx) ->true end). -spec 'false'(input(), index()) -> parse_result(). 'false'(Input, Index) -> p(Input, Index, 'false', fun(I,D) -> (p_string(<<"false">>))(I,D) end, fun(_Node, _Idx) ->false end). -spec 'null'(input(), index()) -> parse_result(). 'null'(Input, Index) -> p(Input, Index, 'null', fun(I,D) -> (p_string(<<"nil">>))(I,D) end, fun(_Node, _Idx) ->nil end). -spec 'and'(input(), index()) -> parse_result(). 'and'(Input, Index) -> p(Input, Index, 'and', fun(I,D) -> (p_string(<<"and">>))(I,D) end, fun(_Node, _Idx) ->bool_and end). -spec 'or'(input(), index()) -> parse_result(). 'or'(Input, Index) -> p(Input, Index, 'or', fun(I,D) -> (p_string(<<"or">>))(I,D) end, fun(_Node, _Idx) ->bool_or 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.