/* Original version from plan: SPDX-FileCopyrightText: Thomas Driemeyer Adapted for use in KOrganizer: SPDX-FileCopyrightText: Preston Brown and SPDX-FileCopyrightText: Reinhold Kainhofer Portions contributed: SPDX-FileCopyrightText: Peter Littlefield Major rewrite using Bison C++ skeleton: SPDX-FileCopyrightText: 2010 John Layt SPDX-License-Identifier: LGPL-2.0-or-later */ %{ /*** C/C++ Declarations ***/ #include #include #include #include #include #include #include #include %} /*** yacc/bison Declarations ***/ /* Require bison 2.3 or later */ %require "2.3" /* add debug output code to generated parser. disable this for release * versions. */ %debug /* write out a header file containing the token defines */ %defines /* use newer C++ skeleton file */ %skeleton "lalr1.cc" /* namespace to enclose parser in */ %name-prefix="KHolidays" /* set the parser's class identifier */ %define "parser_class_name" "HolidayParserPlan" /* keep track of the current position within the input */ %locations %initial-action { // initialize the initial location object @$.begin.filename = driver.fileToParse(); @$.end.filename = @$.begin.filename; }; /* The driver is passed by reference to the parser and to the scanner. This * provides a simple but effective pure interface, not relying on global * variables. */ %parse-param { class HolidayParserDriverPlan& driver } /* verbose error messages */ %error-verbose /*** Grammar Tokens ***/ %union { int ival; char *sval; } %type offset conditionaloffset length expr pexpr number month reldate wdaycondition monthnumber %type categories %token END 0 %token NUMBER MONTH WDAY %token STRING CATEGORY CALENDAR %token INOP PLUS MINUS YEAR LEAPYEAR SHIFT IF %token LENGTH EASTER EQ NE LE GE LT GT PASCHA COUNTRY LANGUAGE NAME DESCRIPTION %destructor { free( $$ ); } STRING %left OR %left AND %right EQ NE LE GE LT GT %left '-' '+' %left '*' '/' '%' %nonassoc '!' %nonassoc UMINUS %left '?' ':' %start planfile /*** End Grammar Tokens ***/ %{ #include "holidayparserdriverplan_p.h" #include "holidayscannerplan_p.h" /* this "connects" the bison parser in the driver to the flex scanner class * object. it defines the yylex() function call to pull the next token from the * current lexer object of the driver context. */ #undef yylex #define yylex driver.m_scanner->lex %} %% /*** Grammar Rules ***/ planfile : metadata list ; metadata : countrycode languagecode name description ; countrycode : { driver.setFileCountryCode( QString() ); } | COUNTRY STRING { char *s = $2; driver.setFileCountryCode( QString::fromUtf8( s ) ); free( s ); $2 = NULL; } ; languagecode : { driver.setFileLanguageCode( QString() ); } | LANGUAGE STRING { char *s = $2; driver.setFileLanguageCode( QString::fromUtf8( s ) ); free( s ); $2 = NULL; } ; name : { driver.setFileName( QString() ); } | NAME STRING { char *s = $2; driver.setFileName( QString::fromUtf8( s ) ); free( s ); $2 = NULL; } ; description : { driver.setFileDescription( QString() ); } | DESCRIPTION STRING { char *s = $2; driver.setFileDescription( QString::fromUtf8( s ) ); free( s ); $2 = NULL; } ; list : | list eventname categories calendar eventrule ; eventname : STRING { char *s = $1; driver.setEventName( QString::fromUtf8( s ) ); free( s ); $1 = NULL; } ; categories : CATEGORY { driver.setEventCategory( $1 ); } | categories CATEGORY { driver.setEventCategory( $1 ); } ; calendar : { driver.setEventCalendarType( QLatin1String("gregorian") ); } | CALENDAR { driver.setEventCalendarType( QString::fromUtf8( $1 ) ); } ; eventrule : EASTER offset length { driver.setFromEaster( $2, $3 ); } | PASCHA offset length { driver.setFromPascha( $2, $3 ); } | date offset conditionaloffset length { driver.setFromDate( $2, $3, $4 ); } | WDAY offset length { driver.setFromWeekdayInMonth( 1, $1, 1, $2, $3 ); } | pexpr WDAY offset length { driver.setFromWeekdayInMonth( $1, $2, 1, $3, $4 ); } | pexpr WDAY INOP month offset length { driver.setFromWeekdayInMonth( $1, $2, $4, $5, $6 ); } | WDAY pexpr date offset length { driver.setFromRelativeWeekday( $2, $1, $4, $5 ); } ; offset : { $$ = 0; } | PLUS expr { $$ = $2; } | MINUS expr { $$ = -$2; } ; conditionaloffset : { $$ = 0; } | SHIFT wdaycondition IF wdaycondition { $$ = ( $2 << 8 ) | $4; } ; wdaycondition : { $$ = 0; } | WDAY { $$ = ( 1 << $1 ); } | WDAY OR wdaycondition { $$ = ( 1 << $1 ) | $3; } ; length : { $$ = 1; } | LENGTH expr { $$ = $2; } ; date : pexpr '.' month { driver.setEventDate( -99999, $3, $1 ); } | pexpr '.' month '.' { driver.setEventDate( -99999, $3, $1 ); } | pexpr '.' month '.' expr { driver.setEventDate( $5, $3, $1 ); } | month '/' pexpr { driver.setEventDate( -99999, $1, $3 ); } | month '/' pexpr '/' pexpr { driver.setEventDate( $5, $1, $3 ); } | monthnumber pexpr { driver.setEventDate( -99999, $1, $2 ); } | monthnumber pexpr pexpr { driver.setEventDate( $3, $1, $2 ); } | pexpr monthnumber { driver.setEventDate( -99999, $2, $1 ); } | pexpr monthnumber pexpr { driver.setEventDate( $3, $2, $1 ); } | pexpr '.' MONTH pexpr { driver.setEventDate( $4, $3, $1 ); } | pexpr { driver.setEventDate( $1 ); } ; reldate : STRING { char *s = $1; $$ = driver.julianDayFromEventName( s ); free( s ); $1 = NULL; } | EASTER { $$ = driver.julianDayFromEaster(); } | PASCHA { $$ = driver.julianDayFromPascha(); } | pexpr '.' month { $$ = driver.julianDayFromMonthDay( $3, $1 ); } | pexpr '.' month '.' { $$ = driver.julianDayFromMonthDay( $3, $1 ); } | month '/' pexpr { $$ = driver.julianDayFromMonthDay( $1, $3 ); } | pexpr MONTH { $$ = driver.julianDayFromMonthDay( $2, $1 ); } | monthnumber pexpr { $$ = driver.julianDayFromMonthDay( $1, $2 ); } | WDAY pexpr pexpr { $$ = driver.julianDayFromRelativeWeekday( $2, $1, $3 ); } | pexpr WDAY INOP month { $$ = driver.julianDayFromWeekdayInMonth( $1, $2, $4 ); } ; month : monthnumber | pexpr { $$ = driver.adjustedMonthNumber( $1 ); } ; monthnumber : MONTH { $$ = driver.adjustedMonthNumber( $1 ); } ; expr : pexpr { $$ = $1; } | expr OR expr { $$ = $1 || $3; } | expr AND expr { $$ = $1 && $3; } | expr EQ expr { $$ = $1 == $3; } | expr NE expr { $$ = $1 != $3; } | expr LE expr { $$ = $1 <= $3; } | expr GE expr { $$ = $1 >= $3; } | expr LT expr { $$ = $1 < $3; } | expr GT expr { $$ = $1 > $3; } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $3 ? $1 / $3 : 0; } | expr '%' expr { $$ = $3 ? $1 % $3 : 0; } | expr '?' expr ':' expr { $$ = $1 ? $3 : $5; } | '!' expr { $$ = !$2; } | '[' reldate ']' { $$ = $2; } ; pexpr : '(' expr ')' { $$ = $2; } | number { $$ = $1; } ; number : NUMBER | '-' NUMBER %prec UMINUS { $$ = -$2; } | YEAR { $$ = driver.parseYear(); } | LEAPYEAR pexpr { $$ = driver.isLeapYear( $2 ); } ; %% /*** Private Yacc callbacks and helper functions ***/ void KHolidays::HolidayParserPlan::error( const KHolidays::HolidayParserPlan::location_type &errorLocation, const std::string &errorMessage ) { driver.error( errorLocation, errorMessage.c_str() ); }