/* Copyright (C) 2017 xaizek <xaizek@posteo.net> * * This file is part of zograscope. * * zograscope is free software: you can redistribute it and/or modify * it under the terms of version 3 of the GNU Affero General Public License as * published by the Free Software Foundation. * * zograscope is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with zograscope. If not, see <http://www.gnu.org/licenses/>. */ %{ #include "make/make-parser.gen.hpp" #include <cstdio> #include <iostream> #include <string> #include "make/MakeLexerData.hpp" #include "make/MakeSType.hpp" #include "make/make-lexer.gen.hpp" #include "TreeBuilder.hpp" struct MakeParseData { const std::string &contents; std::string fileName; bool hitError; }; using namespace makestypes; %} %code requires { #include "TreeBuilder.hpp" #define MAKE_LTYPE Location namespace cpp17 { namespace pmr { class monolithic; } } struct MakeParseData; TreeBuilder make_parse(const std::string &contents, const std::string &fileName, int tabWidth, bool debug, cpp17::pmr::monolithic &mr); void make_error(MAKE_LTYPE *loc, void *scanner, TreeBuilder *tb, MakeParseData *pd, const char s[]); } %define api.prefix {make_} %define parse.error verbose %glr-parser %define api.pure %param {void *scanner} %parse-param {TreeBuilder *tb} {MakeParseData *pd} %locations %union { Text text; PNode *node; } %token END 0 "end of file" %token <text> ',' ':' '(' ')' %token <text> COMMENT ASSIGN_OP CHARS WS NL VAR SLIT %token <text> CALL_PREFIX "$(" %token <text> CALL_SUFFIX ")" %token <text> OVERRIDE "override" %token <text> EXPORT "export" %token <text> UNEXPORT "unexport" %token <text> IFDEF "ifdef" %token <text> IFNDEF "ifndef" %token <text> IFEQ "ifeq" %token <text> IFNEQ "ifneq" %token <text> ELSE "else" %token <text> ENDIF "endif" %token <text> DEFINE "define" %token <text> ENDEF "endef" %token <text> UNDEFINE "undefine" %token <text> INCLUDE "include" %token <text> LEADING_TAB %token <text> NTOKENS %printer { if ($$.len == 1U && pd->contents[$$.from] == '\n') { std::fprintf(yyoutput, "<newline>"); } else { std::fprintf(yyoutput, "%.*s", (int)$$.len, &pd->contents[$$.from]); } } <text> %type <node> statement statements statements_opt specifiers export %type <node> assignment assignment_prefix %type <node> conditional conditional_in_recipe condition define include %type <node> expressions expressions_opt %type <node> expression expression_text expression_function %type <node> pattern_text pattern_function %type <node> arguments argument %type <node> function definition %type <node> rule target pattern targets prerequisites %type <node> recipes mid_recipes recipes_opt recipe last_recipe %type <node> function_name function_name_text function_name_function %type <node> function_name_piece %type <node> if_def_kw if_eq_kw identifier text char br colon %type <node> keywords comment_opt /* Contents of parenthesised expressions. */ %type <node> exprs_nested expr_nested expr_text_nested expr_func_nested %type <node> text_nested char_nested /* Expressions in RHS of assignments. */ %type <node> exprs_in_assign expr_in_assign %type <node> expr_text_in_assign expr_func_in_assign %type <node> text_in_assign char_in_assign /* Expressions in definitions. */ %type <node> exprs_in_def first_expr_in_def char_in_def /* Expressions in recipes. */ %type <node> exprs_in_recipe expr_in_recipe %type <node> expr_text_in_recipe expr_func_in_recipe %type <node> text_in_recipe char_in_recipe %precedence NL %precedence COMMENT "ifdef" "ifndef" "ifeq" "ifneq" %start makefile %% makefile : statements END { tb->setRoot(tb->addNode($1, @1, +MakeSType::Makefile)); } | END { tb->setRoot(tb->addNode({ tb->addNode({ }, +MakeSType::Statements) }, +MakeSType::Makefile)); } ; statements : br { $$ = tb->addNode({ }, +MakeSType::Statements); } | statement { $$ = tb->addNode({ $1 }, +MakeSType::Statements); } | statements br %dprec 1 { $$ = $1; } | statements statement %dprec 2 { $$ = tb->append($1, tb->addNode($2, @2)); } ; conditional : if_eq_kw condition statements_opt "endif" comment_opt br { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_eq_kw condition statements_opt "else" statements_opt "endif" comment_opt br { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5), tb->addNode($6, @6, +MakeSType::Separator), tb->addNode($7, @7) }, +MakeSType::IfStmt); } | if_eq_kw condition statements_opt "else" conditional { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_def_kw identifier statements_opt "endif" comment_opt br { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_def_kw identifier statements_opt "else" statements_opt "endif" comment_opt br { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5), tb->addNode($6, @6, +MakeSType::Separator), tb->addNode($7, @7) }, +MakeSType::IfStmt); } | if_def_kw identifier statements_opt "else" conditional { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } ; conditional_in_recipe : if_eq_kw condition recipes_opt "endif" comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_eq_kw condition recipes_opt "else" recipes_opt "endif" comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5), tb->addNode($6, @6, +MakeSType::Separator), tb->addNode($7, @7) }, +MakeSType::IfStmt); } | if_eq_kw condition recipes_opt "else" conditional_in_recipe { $$ = tb->addNode({ tb->addNode($1, @1), $2, tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_def_kw identifier recipes_opt "endif" comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } | if_def_kw identifier recipes_opt "else" recipes_opt "endif" comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5), tb->addNode($6, @6, +MakeSType::Separator), tb->addNode($7, @7) }, +MakeSType::IfStmt); } | if_def_kw identifier recipes_opt "else" conditional_in_recipe { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5) }, +MakeSType::IfStmt); } ; condition : '(' expressions_opt ',' expressions_opt ')' { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3, +MakeSType::Separator), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::IfCond); } | SLIT SLIT { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::IfCond); } ; define : "define" pattern definition "endef" br { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator) }, +MakeSType::MultilineAssignment); } | specifiers "define" pattern definition "endef" br { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::MultilineAssignment); } | "define" pattern ASSIGN_OP definition "endef" br { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3, +MakeSType::Separator), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::MultilineAssignment); } | specifiers "define" pattern ASSIGN_OP definition "endef" br { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5), tb->addNode($6, @6, +MakeSType::Separator) }, +MakeSType::MultilineAssignment); } ; definition : comment_opt br { $$ = $1; } | comment_opt br exprs_in_def br { $$ = tb->addNode({ $1, $3 }, +MakeSType::TemporaryContainer); } ; include : "include" expressions br { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2) }, +MakeSType::Include); } ; statements_opt : comment_opt br { $$ = $1; } | comment_opt br statements { $$ = tb->addNode({ $1, $3 }, +MakeSType::TemporaryContainer); } ; if_def_kw : "ifdef" { $$ = tb->addNode($1, @1, +MakeSType::Punctuation); } | "ifndef" { $$ = tb->addNode($1, @1, +MakeSType::Punctuation); } ; if_eq_kw : "ifeq" { $$ = tb->addNode($1, @1, +MakeSType::Punctuation); } | "ifneq" { $$ = tb->addNode($1, @1, +MakeSType::Punctuation); } ; statement : COMMENT { $$ = tb->addNode($1, @1, +MakeSType::Comment); } | assignment br { $$ = tb->addNode($1, @1, +MakeSType::AssignmentExpr); } | function br | rule | conditional | define | include | export br ; export : "export" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator) }, +MakeSType::Directive); } | "unexport" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator) }, +MakeSType::Directive); } | assignment_prefix { $$ = tb->addNode($1, @1, +MakeSType::AssignmentExpr); } | assignment_prefix WS targets { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($3, @3) }, +MakeSType::Directive); } ; assignment : pattern ASSIGN_OP comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3) }, +MakeSType::TemporaryContainer); } | pattern ASSIGN_OP exprs_in_assign comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3), tb->addNode($4, @4) }, +MakeSType::TemporaryContainer); } | assignment_prefix ASSIGN_OP comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3) }, +MakeSType::TemporaryContainer); } | assignment_prefix ASSIGN_OP exprs_in_assign comment_opt { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3), tb->addNode($4, @4) }, +MakeSType::TemporaryContainer); } ; assignment_prefix : specifiers pattern { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::TemporaryContainer); } ; specifiers : "override" { $$ = tb->addNode($1, @1); } | "export" { $$ = tb->addNode($1, @1); } | "unexport" { $$ = tb->addNode($1, @1); } | "override" "export" { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::TemporaryContainer); } | "export" "override" { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::TemporaryContainer); } | "undefine" { $$ = tb->addNode($1, @1); } | "override" "undefine" { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::TemporaryContainer); } | "undefine" "override" { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2) }, +MakeSType::TemporaryContainer); } ; expressions_opt : %empty { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | expressions ; expressions : expression { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expressions WS expression { $$ = tb->append($1, tb->addNode($3, @3)); } ; exprs_nested : expr_nested { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | exprs_nested WS expr_nested { $$ = tb->append($1, tb->addNode($3, @3)); } ; exprs_in_assign : expr_in_assign { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | exprs_in_assign WS expr_in_assign { $$ = tb->append($1, tb->addNode($3, @3)); } ; exprs_in_def : first_expr_in_def | br { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | br first_expr_in_def { $$ = $2; } | exprs_in_def br { $$ = $1; } | exprs_in_def WS expr_in_recipe { $$ = tb->append($1, tb->addNode($3, @3)); } | exprs_in_def br first_expr_in_def { $$ = tb->append($1, tb->addNode($3, @3)); } ; first_expr_in_def : char_in_def expr_in_recipe { $$ = tb->addNode({ $1, $2 }, +MakeSType::TemporaryContainer); } | function expr_in_recipe { $$ = tb->addNode({ $1, $2 }, +MakeSType::TemporaryContainer); } | char_in_def { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } ; exprs_in_recipe : expr_in_recipe { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | exprs_in_recipe WS expr_in_recipe { $$ = tb->append($1, tb->addNode($3, @3)); } ; expression : expression_text | expression_function ; expr_nested : expr_text_nested | expr_func_nested ; expr_in_assign : expr_text_in_assign | expr_func_in_assign ; expr_in_recipe : expr_text_in_recipe | expr_func_in_recipe ; expression_text : text { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expression_function text { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_text_nested : text_nested { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expr_func_nested text_nested { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_text_in_assign : text_in_assign { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expr_func_in_assign text_in_assign { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_text_in_recipe : text_in_recipe { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expr_func_in_recipe text_in_recipe { $$ = tb->append($1, tb->addNode($2, @2)); } ; expression_function : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | '(' exprs_nested ')' { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3) }, +MakeSType::TemporaryContainer); } | expression_text function { $$ = tb->append($1, tb->addNode($2, @2)); } | expression_function function { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_func_nested : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | '(' exprs_nested ')' { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3) }, +MakeSType::TemporaryContainer); } | expr_func_nested function { $$ = tb->append($1, tb->addNode($2, @2)); } | expr_text_nested function { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_func_in_assign : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expr_func_in_assign function { $$ = tb->append($1, tb->addNode($2, @2)); } | expr_text_in_assign function { $$ = tb->append($1, tb->addNode($2, @2)); } ; expr_func_in_recipe : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | expr_func_in_recipe function { $$ = tb->append($1, tb->addNode($2, @2)); } | expr_text_in_recipe function { $$ = tb->append($1, tb->addNode($2, @2)); } ; function : VAR { $$ = tb->addNode($1, @1); } | "$(" function_name ")" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3, +MakeSType::Separator) }, +MakeSType::CallExpr); } | "$(" function_name WS arguments ")" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::CallExpr); } | "$(" function_name ',' arguments ")" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4, +MakeSType::Separator), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::CallExpr); } | "$(" function_name ':' expressions ")" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3, +MakeSType::Separator), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::CallExpr); } | "$(" function_name ASSIGN_OP expressions ")" { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2), tb->addNode($3, @3, +MakeSType::Separator), tb->addNode($4, @4), tb->addNode($5, @5, +MakeSType::Separator) }, +MakeSType::CallExpr); } ; function_name : function_name_text | function_name_function ; function_name_text : function_name_piece { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | function_name_function function_name_piece { $$ = tb->append($1, tb->addNode($2, @2)); } ; function_name_piece : CHARS { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | function_name_piece CHARS { $$ = tb->append($1, tb->addNode($2, @2)); } ; function_name_function : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | function_name_text function { $$ = tb->append($1, tb->addNode($2, @2)); } ; arguments : %empty { $$ = tb->addNode({ tb->addNode({ }, +MakeSType::Argument) }, +MakeSType::ArgumentList); } | argument { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Argument) }, +MakeSType::ArgumentList); } | arguments ',' { $$ = tb->append($1, tb->addNode($2, @2, +MakeSType::Separator)); } | arguments ',' argument { $$ = tb->append($1, { tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3, +MakeSType::Argument) }); } ; argument : expressions ; rule : targets colon prerequisites NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3) }, +MakeSType::Rule); } | targets colon prerequisites recipes NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4) }, +MakeSType::Rule); } | targets colon assignment NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3) }, +MakeSType::Rule); } | targets colon targets colon prerequisites NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4) }, +MakeSType::Rule); } | targets colon targets colon prerequisites recipes NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4), tb->addNode($5, @5), tb->addNode($6, @6) }, +MakeSType::Rule); } | targets colon targets colon assignment NL { $$ = tb->addNode({ tb->addNode($1, @1), tb->addNode($2, @2), tb->addNode($3, @3), tb->addNode($4, @4), tb->addNode($5, @5) }, +MakeSType::Rule); } ; target : pattern ; pattern : pattern_text | pattern_function ; pattern_text : identifier { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | pattern_function identifier { $$ = tb->append($1, tb->addNode($2, @2)); } ; pattern_function : function { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | pattern_text function { $$ = tb->append($1, tb->addNode($2, @2)); } | pattern_function function { $$ = tb->append($1, tb->addNode($2, @2)); } ; prerequisites : %empty { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | targets ; targets : target { $$ = tb->addNode($1, @1, +MakeSType::TemporaryContainer); } | targets WS target { $$ = tb->append($1, tb->addNode($3, @3)); } ; recipes : mid_recipes last_recipe { $$ = tb->append($1, $2); } ; mid_recipes : %empty { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | mid_recipes recipe { $$ = tb->append($1, $2); } ; recipes_opt : comment_opt NL { $$ = $1; } | comment_opt recipes NL { $$ = tb->addNode({ $1, $2 }, +MakeSType::TemporaryContainer); } ; recipe : last_recipe | br { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | NL COMMENT { $$ = tb->addNode($2, @2, +MakeSType::Comment); } ; last_recipe : LEADING_TAB exprs_in_recipe { $$ = tb->addNode($2, @2, +MakeSType::Recipe); } | NL conditional_in_recipe { $$ = $2; } ; identifier : CHARS { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | ',' { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | '(' { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | ')' { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | identifier CHARS { $$ = tb->append($1, tb->addNode($2, @2)); } | identifier keywords { $$ = tb->append($1, tb->addNode($2, @2)); } | identifier ',' { $$ = tb->append($1, tb->addNode($2, @2)); } | identifier '(' { $$ = tb->append($1, tb->addNode($2, @2)); } | identifier ')' { $$ = tb->append($1, tb->addNode($2, @2)); } ; text : char { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | text char { $$ = tb->append($1, tb->addNode($2, @2)); } ; text_nested : char_nested { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | text_nested char_nested { $$ = tb->append($1, tb->addNode($2, @2)); } ; text_in_assign : char_in_assign { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | text_in_assign char_in_assign { $$ = tb->append($1, tb->addNode($2, @2)); } ; text_in_recipe : char_in_recipe { $$ = tb->addNode({ tb->addNode($1, @1) }, +MakeSType::TemporaryContainer); } | text_in_recipe char_in_recipe { $$ = tb->append($1, tb->addNode($2, @2)); } ; char : CHARS { $$ = tb->addNode($1, @1); } | SLIT { $$ = tb->addNode($1, @1); } | ASSIGN_OP { $$ = tb->addNode($1, @1, 0); } | ':' { $$ = tb->addNode($1, @1, 0); } ; char_nested : char | ',' { $$ = tb->addNode($1, @1, 0); } ; char_in_assign : char_nested | '(' { $$ = tb->addNode($1, @1, 0); } | ')' { $$ = tb->addNode($1, @1, 0); } | keywords ; char_in_def : char | '(' { $$ = tb->addNode($1, @1, 0); } | ')' { $$ = tb->addNode($1, @1, 0); } | ',' { $$ = tb->addNode($1, @1, 0); } | COMMENT { $$ = tb->addNode($1, @1, 0); } | "include" { $$ = tb->addNode($1, @1, 0); } | "override" { $$ = tb->addNode($1, @1, 0); } | "export" { $$ = tb->addNode($1, @1, 0); } | "unexport" { $$ = tb->addNode($1, @1, 0); } | "ifdef" { $$ = tb->addNode($1, @1, 0); } | "ifndef" { $$ = tb->addNode($1, @1, 0); } | "ifeq" { $$ = tb->addNode($1, @1, 0); } | "ifneq" { $$ = tb->addNode($1, @1, 0); } | "else" { $$ = tb->addNode($1, @1, 0); } | "endif" { $$ = tb->addNode($1, @1, 0); } | "define" { $$ = tb->addNode($1, @1, 0); } | "undefine" { $$ = tb->addNode($1, @1, 0); } ; char_in_recipe : char_in_assign | COMMENT { $$ = tb->addNode($1, @1, 0); } ; keywords : "include" { $$ = tb->addNode($1, @1, 0); } | "override" { $$ = tb->addNode($1, @1, 0); } | "export" { $$ = tb->addNode($1, @1, 0); } | "unexport" { $$ = tb->addNode($1, @1, 0); } | "ifdef" { $$ = tb->addNode($1, @1, 0); } | "ifndef" { $$ = tb->addNode($1, @1, 0); } | "ifeq" { $$ = tb->addNode($1, @1, 0); } | "ifneq" { $$ = tb->addNode($1, @1, 0); } | "else" { $$ = tb->addNode($1, @1, 0); } | "endif" { $$ = tb->addNode($1, @1, 0); } | "define" { $$ = tb->addNode($1, @1, 0); } | "endef" { $$ = tb->addNode($1, @1, 0); } | "undefine" { $$ = tb->addNode($1, @1, 0); } ; br : NL { $$ = tb->addNode($1, @1, 0); } | LEADING_TAB { $$ = tb->addNode($1, @1, 0); } ; colon : ':' { $$ = tb->addNode($1, @1, +MakeSType::Separator); } | WS ':' { $$ = tb->addNode($2, @2, +MakeSType::Separator); } | ':' ':' { $$ = tb->addNode({ tb->addNode($1, @1, +MakeSType::Separator), tb->addNode($2, @2, +MakeSType::Separator) }); } | WS ':' ':' { $$ = tb->addNode({ tb->addNode($2, @2, +MakeSType::Separator), tb->addNode($3, @3, +MakeSType::Separator) }); } ; comment_opt : %empty { $$ = tb->addNode({ }, +MakeSType::TemporaryContainer); } | COMMENT { $$ = tb->addNode($1, @1); } ; %% TreeBuilder make_parse(const std::string &contents, const std::string &fileName, int tabWidth, bool debug, cpp17::pmr::monolithic &mr) { TreeBuilder tb(mr); MakeParseData pd { contents, fileName, false }; MakeLexerData ld(contents, tabWidth, tb, pd); yyscan_t scanner; make_lex_init_extra(&ld, &scanner); #if YYDEBUG make_debug = debug; make_set_debug(debug, scanner); #endif const bool failed = (yyparse(scanner, &tb, &pd) != 0); tb.finish(failed || pd.hitError); make_lex_destroy(scanner); return tb; } void make_error(MAKE_LTYPE *loc, void */*scanner*/, TreeBuilder */*tb*/, MakeParseData *pd, const char s[]) { std::cerr << pd->fileName << ':' << loc->first_line << ':' << loc->first_column << ": parse error: " << s << std::endl; pd->hitError = true; }