📄 yecc.erl
字号:
atom_option(verbose) -> {verbose, true};atom_option(Key) -> Key.is_filename(T) -> try filename:flatten(T) of Filename -> Filename catch error: _ -> no end. shorten_filename(Name0) -> {ok,Cwd} = file:get_cwd(), case lists:prefix(Cwd, Name0) of false -> Name0; true -> case lists:nthtail(length(Cwd), Name0) of "/"++N -> N; N -> N end end.start(Infilex, Options) -> Infile = assure_extension(Infilex, ".yrl"), {value, {_, Outfilex0}} = keysearch(parserfile, 1, Options), {value, {_, Includefilex}} = keysearch(includefile, 1, Options), Outfilex = case Outfilex0 of [] -> filename:rootname(Infilex, ".yrl"); _ -> Outfilex0 end, Includefile = case Includefilex of [] -> []; _ -> assure_extension(Includefilex,".hrl") end, Outfile = assure_extension(Outfilex, ".erl"), Module = list_to_atom(filename:basename(Outfile, ".erl")), #yecc{infile = Infile, outfile = Outfile, includefile = Includefile, module = Module, options = Options, verbose = member(verbose, Options), file_attrs = member(file_attributes, Options)}.assure_extension(File, Ext) -> concat([strip_extension(File, Ext), Ext]).%% Assumes File is a filename.strip_extension(File, Ext) -> case filename:extension(File) of Ext -> filename:rootname(File); _Other -> File end.infile(Parent, Infilex, Options) -> St0 = start(Infilex, Options), St = case file:open(St0#yecc.infile, [read, read_ahead]) of {ok, Inport} -> try outfile(St0#yecc{inport = Inport}) after ok = file:close(Inport) end; {error, Reason} -> add_error(St0#yecc.infile, none, {file_error, Reason}, St0) end, case St#yecc.errors of [] -> ok; _ -> _ = file:delete(St#yecc.outfile) end, Parent ! {self(), yecc_ret(St)}.outfile(St0) -> case file:open(St0#yecc.outfile, [write, delayed_write]) of {ok, Outport} -> try generate(St0#yecc{outport = Outport}) catch throw: St1 -> St1; exit: Reason -> add_error({internal_error, Reason}, St0) after ok = file:close(Outport) end; {error, Reason} -> add_error(St0#yecc.outfile, none, {file_error, Reason}, St0) end.os_process_size() -> case os:type() of {unix, sunos} -> Size = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"), list_to_integer(lib:nonl(Size)); _ -> 0 end. timeit(Name, Fun, St0) -> Time = runtime, %% Time = wall_clock, {Before, _} = statistics(Time), St = Fun(St0), {After, _} = statistics(Time), Mem0 = erts_debug:flat_size(St)*erlang:system_info(wordsize), Mem = lists:flatten(io_lib:format("~.1f kB", [Mem0/1024])), Sz = lists:flatten(io_lib:format("~.1f MB", [os_process_size()/1024])), io:fwrite(" ~-30w: ~10.2f s ~12s ~10s\n", [Name, (After-Before)/1000, Mem, Sz]), St.-define(PASS(P), {P, fun P/1}).generate(St0) -> Passes = [?PASS(parse_grammar), ?PASS(check_grammar), ?PASS(states_and_goto_table), ?PASS(parse_actions), ?PASS(action_conflicts), ?PASS(write_file)], F = case member(time, St0#yecc.options) of true -> io:fwrite("Generating parser from grammar in ~s\n", [format_filename(St0#yecc.infile)]), fun timeit/3; false -> fun(_Name, Fn, St) -> Fn(St) end end, Fun = fun({Name, Fun}, St) -> St2 = F(Name, Fun, St), if St2#yecc.errors =:= [] -> St2; true -> throw(St2) end end, foldl(Fun, St0, Passes).parse_grammar(St) -> parse_grammar(St#yecc.inport, 1, St).parse_grammar(Inport, Line, St) -> {NextLine, Grammar} = read_grammar(Inport, Line), parse_grammar(Grammar, Inport, NextLine, St).parse_grammar(eof, _Inport, _NextLine, St) -> St;parse_grammar({#symbol{name = 'Erlang'}, [#symbol{name = code}]}, _Inport, NextLine, St) -> St#yecc{erlang_code = NextLine};parse_grammar(Grammar, Inport, NextLine, St0) -> St = parse_grammar(Grammar, St0), parse_grammar(Inport, NextLine, St).parse_grammar({error,ErrorLine,Error}, St) -> add_error(ErrorLine, Error, St);parse_grammar({rule, Rule, Tokens}, St0) -> NmbrOfDaughters = case Rule of [_, #symbol{name = '$empty'}] -> 0; _ -> length(Rule) - 1 end, St1 = check_action(Tokens, St0), {Tokens1, St2} = subst_pseudo_vars(Tokens, NmbrOfDaughters, St1), RuleDef = #rule{symbols = Rule, tokens = Tokens1}, St2#yecc{rules_list = [RuleDef | St2#yecc.rules_list]};parse_grammar({prec, Prec}, St) -> St#yecc{prec = Prec ++ St#yecc.prec};parse_grammar({#symbol{line = Line, name = Name}, Symbols}, St) -> CF = fun(I) -> case element(I, St) of [] -> setelement(I, St, Symbols); _ -> add_error(Line, {duplicate_declaration, Name}, St) end end, OneSymbol = length(Symbols) =:= 1, case Name of 'Nonterminals' -> CF(#yecc.nonterminals); 'Terminals' -> CF(#yecc.terminals); 'Rootsymbol' when OneSymbol -> CF(#yecc.rootsymbol); 'Endsymbol' when OneSymbol -> CF(#yecc.endsymbol); 'Expect' when OneSymbol -> CF(#yecc.expect_shift_reduce); 'States' when OneSymbol -> CF(#yecc.expect_n_states); % undocumented _ -> add_warning(Line, bad_declaration, St) end.read_grammar(Inport, Line) -> case yeccscan:scan(Inport, '', Line) of {eof, NextLine} -> {NextLine, eof}; {error, {ErrorLine, Mod, What}, NextLine} -> {NextLine, {error, ErrorLine, {error, Mod, What}}}; {ok, Input, NextLine} -> {NextLine, case yeccparser:parse(Input) of {error, {ErrorLine, Mod, Message}} -> {error, ErrorLine, {error, Mod, Message}}; {ok, {rule, Rule, {erlang_code, Tokens}}} -> {rule, Rule, Tokens}; {ok, {#symbol{name=P}, [#symbol{name=I} | OpL]}=Ss} -> A = precedence(P), if A =/= unknown, is_integer(I), OpL =/= [] -> Ps = [{Op, I , A} || Op <- OpL], {prec, Ps}; true -> Ss end end} end.precedence('Left') -> left;precedence('Right') -> right;precedence('Unary') -> unary;precedence('Nonassoc') -> nonassoc;precedence(_) -> unknown.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%check_grammar(St0) -> Empty = #symbol{line = none, name = '$empty'}, AllSymbols = St0#yecc.nonterminals ++ St0#yecc.terminals ++ [Empty], St1 = St0#yecc{all_symbols = AllSymbols}, Cs = [fun check_nonterminals/1, fun check_terminals/1, fun check_rootsymbol/1, fun check_endsymbol/1, fun check_expect/1, fun check_states/1, fun check_precedences/1, fun check_rules/1], foldl(fun(F, St) -> F(St) end, St1, Cs).check_nonterminals(St) -> case St#yecc.nonterminals of [] -> add_error(nonterminals_missing, St); Nonterminals -> {Unique, Dups} = duplicates(names(Nonterminals)), St1 = add_warnings(Dups, duplicate_nonterminal, St), St2 = check_reserved(Unique, St1), St2#yecc{nonterminals = [?ACCEPT | Unique]} end.check_terminals(St0) -> case St0#yecc.terminals of [] -> add_error(terminals_missing, St0); Terminals -> {Unique, Dups} = duplicates(names(Terminals)), St1 = add_warnings(Dups, duplicate_terminal, St0), Common = intersect(St1#yecc.nonterminals, Unique), St2 = add_errors(Common, symbol_terminal_and_nonterminal, St1), St3 = check_reserved(Unique, St2), St3#yecc{terminals = ['$empty' | Unique]} end.check_reserved(Names, St) -> add_errors(intersect(Names, ['$empty', '$end', '$undefined']), reserved, St).check_rootsymbol(St) -> case St#yecc.rootsymbol of [] -> add_error(rootsymbol_missing, St); [#symbol{line = Line, name = SymName}] -> case kind_of_symbol(St, SymName) of nonterminal -> St#yecc{rootsymbol = SymName}; _ -> add_error(Line, {bad_rootsymbol, SymName}, St) end end.check_endsymbol(St) -> case St#yecc.endsymbol of [] -> St#yecc{endsymbol = '$end'}; [#symbol{line = Line, name = SymName}] -> case kind_of_symbol(St, SymName) of nonterminal -> add_error(Line, {endsymbol_is_nonterminal, SymName}, St); terminal -> add_error(Line, {endsymbol_is_terminal, SymName}, St); _ -> St#yecc{endsymbol = SymName} end end.check_expect(St0) -> case St0#yecc.expect_shift_reduce of [] -> St0#yecc{expect_shift_reduce = 0}; [#symbol{name = Expect}] when is_integer(Expect) -> St0#yecc{expect_shift_reduce = Expect}; [#symbol{line = Line, name = Name}] -> St1 = add_error(Line, {bad_expect, Name}, St0), St1#yecc{expect_shift_reduce = 0} end.check_states(St) -> case St#yecc.expect_n_states of [] -> St; [#symbol{name = NStates}] when is_integer(NStates) -> St#yecc{expect_n_states = NStates}; [#symbol{line = Line, name = Name}] -> add_error(Line, {bad_states, Name}, St) end.check_precedences(St0) -> {St1, _} = foldr(fun({#symbol{line = Line, name = Op},_I,_A}, {St,Ps}) -> case member(Op, Ps) of true -> {add_error(Line, {duplicate_precedence,Op}, St), Ps}; false -> {St, [Op | Ps]} end end, {St0,[]}, St0#yecc.prec), foldl(fun({#symbol{line = Line, name = Op},I,A}, St) -> case kind_of_symbol(St, Op) of endsymbol -> add_error(Line,{precedence_op_is_endsymbol,Op}, St); unknown -> add_error(Line, {precedence_op_is_unknown, Op}, St); _ -> St#yecc{prec = [{Op,I,A} | St#yecc.prec]} end end, St1#yecc{prec = []}, St1#yecc.prec).check_rule(Rule0, {St0,Rules}) -> Symbols = Rule0#rule.symbols, #symbol{line = HeadLine, name = Head} = hd(Symbols), case member(Head, St0#yecc.nonterminals) of false -> {add_error(HeadLine, {undefined_nonterminal, Head}, St0), Rules}; true -> St = check_rhs(tl(Symbols), St0), Rule = Rule0#rule{line = HeadLine, symbols = names(Symbols)}, {St, [Rule | Rules]} end.check_rules(St0) -> {St,Rules0} = foldl(fun check_rule/2, {St0,[]}, St0#yecc.rules_list), case St#yecc.rules_list of [] -> add_error(no_grammar_rules, St); _ -> Rule = #rule{line = none, symbols = [?ACCEPT, St#yecc.rootsymbol], tokens = []}, Rules1 = [Rule | Rules0], Rules = map(fun({R,I}) -> R#rule{n = I} end, count(0, Rules1)),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -