📄 gram.y
字号:
%{/* * $Id: //pentools/main/datemath/gram.y#3 $ * * written by: Stephen J. Friedl * Software Consultant * Tustin, California USA * steve@unixwiz.net / www.unixwiz.net * * This is the grammar for the datemath program. It uses a long * yylval to contain the actual dates (either yymm or Julian), * and the return values from the scanner are the tokens shown * below. */#include <stdio.h>#include <stdlib.h>#include "defs.h"#define pr (void)printfstatic int formatted_output = FALSE;static void bogus ( char const * );static void print_jdate ( long );static void print_num ( long );static void print_yymm ( int );static long yymm_diff ( long, long );static long day_jdate ( long );static long fday_jdate ( long );static long fday_yymm ( int );static long lday_jdate ( long );static long lday_yymm ( int );static long month_jdate ( long );static long month_yymm ( int );static long ndays_jdate ( long );static long ndays_yymm ( int );static long year_jdate ( long );static long year_yymm ( int );static short mdy[3]; /* for use by any function that needs it */static void cannot_add(void);%}%token PLUS MINUS TIMES DIV MOD%token MMDDYY YYMM INTEGER%token LPAREN RPAREN %token FDAY LDAY KYYMM DOY%token NDAYS YEAR MONTH DAY WEEKS%left TIMES DIV MOD%left PLUS MINUS%%expr : mmddyy { print_jdate($1); } | yymm { print_yymm($1); } | num { print_num($1); } | bogus /* error checking */ ;/*---------------------------------------------------------------------- * this is an attempt to catch errors where the user does things that * don't make any sense (i.e., semantic errors). This is not intended * to be an exhaustive list, but since they might be common we try to * have reasonable error reporting. */bogus : FDAY LPAREN num RPAREN { bogus("fday"); } | LDAY LPAREN num RPAREN { bogus("lday"); } | NDAYS LPAREN num RPAREN { bogus("ndays"); } | MONTH LPAREN num RPAREN { bogus("month"); } | DAY LPAREN num RPAREN { bogus("day"); } | YEAR LPAREN num RPAREN { bogus("year"); } | mmddyy PLUS mmddyy { cannot_add(); } | mmddyy PLUS yymm { cannot_add(); } | yymm PLUS mmddyy { cannot_add(); } | yymm PLUS yymm { cannot_add(); } | yymm MINUS mmddyy { die("can't subtract mmddyy dates from yymm dates"); } | mmddyy MINUS yymm { die("can't subtract yymm dates from mmddyy dates"); } ;/*---------------------------------------------------------------------- * This is the symbol for anything that can evaluate to a Julian date * (internally, a long). */mmddyy : MMDDYY { $$ = $1 ; } | num PLUS mmddyy { $$ = $1 + $3 ; } | mmddyy PLUS num { $$ = $1 + $3 ; } | mmddyy MINUS num { $$ = $1 - $3 ; } | FDAY LPAREN mmddyy RPAREN { $$ = fday_jdate($3); } | LDAY LPAREN mmddyy RPAREN { $$ = lday_jdate($3); } | FDAY LPAREN yymm RPAREN { $$ = fday_yymm($3); } | LDAY LPAREN yymm RPAREN { $$ = lday_yymm($3); } | LPAREN mmddyy RPAREN { $$ = $2; } ;/*---------------------------------------------------------------------- * These are all expressions that can yield a yymm date. */yymm : YYMM { $$ = $1 ; } | yymm PLUS num { $$ = yymm_add($1, $3); } | num PLUS yymm { $$ = yymm_add($3, $1); } | yymm MINUS num { $$ = yymm_add($1, -$3); } | KYYMM LPAREN mmddyy RPAREN { $$ = jultoyymm($3); } | KYYMM LPAREN yymm RPAREN { $$ = $3; } | LPAREN yymm RPAREN { $$ = $2 ; } ;/*---------------------------------------------------------------------- * These are all the expressions that can yield a numeric value. * We allow some limited normal math (+-* /) here because it was * easy, but expr(1) is probably best used for these tasks. */num : INTEGER { $$ = $1 ; } | mmddyy MINUS mmddyy { $$ = $1 - $3; } | yymm MINUS yymm { $$ = yymm_diff($1, $3); } | INTEGER WEEKS { $$ = $2 * 7; } | NDAYS LPAREN yymm RPAREN { $$ = ndays_yymm($3); } | NDAYS LPAREN mmddyy RPAREN { $$ = ndays_jdate($3); } | MONTH LPAREN mmddyy RPAREN { $$ = month_jdate($3); } | YEAR LPAREN mmddyy RPAREN { $$ = year_jdate ($3); } | DAY LPAREN mmddyy RPAREN { $$ = day_jdate ($3); } | MONTH LPAREN yymm RPAREN { $$ = month_yymm ($3); } | YEAR LPAREN yymm RPAREN { $$ = year_yymm ($3); } | DAY LPAREN yymm RPAREN { die("can't take \"day\" of yymm date"); } | num MINUS num { $$ = $1 - $3 ; } | num PLUS num { $$ = $1 + $3 ; } | num TIMES num { $$ = $1 * $3 ; } | num DIV num { $$ = ($3 == 0) ? 0 : ($1 / $3); } | num MOD num { $$ = ($3 == 0) ? 0 : ($1 % $3); } | LPAREN num RPAREN { $$ = $2 ; } ;%%/* * yymm_diff() * * Given two yymm dates, return their difference in months. * Do this by converting both to the number of months since * 1/1/1900 and subtracting. */static longyymm_diff(long yymm1, long yymm2){long y1, y2; y1 = ((yymm1 / 100) * 12) + (yymm1 % 100); y2 = ((yymm2 / 100) * 12) + (yymm2 % 100); return(y1 - y2);}/* * print_jdate() * * Given a Julian date, print it in the format requested by the * user. We can print either two-digit or four-digit dates, and * we handle this by simply lopping off the century part for two- * digit dates. Printing the year with %02d works correctly for * years 0..9, but it correctly expands to four digits when needed. */static void print_jdate(long jdate){ if (rjulmdy(jdate, mdy) < 0) die("cannot convert date to ASCII (this should not happen)"); if ( year_digits == 2 ) mdy[YY] %= 100; if ( collate_order ) { printf("%02d/%02d/%02d\n", mdy[YY], mdy[MM], mdy[DD]); } else { printf("%02d/%02d/%02d\n", mdy[MM], mdy[DD], mdy[YY]); }}/* * print_num() * * Given an number, print it in the normal method (just digits). */static void print_num(long num){ pr("%ld\n", num);}/* * print_yymm() * * Given a yymm date, print it. This routine has support for * output as either mm/yy or yymm formats, but there is currently * no way to set the formatted_output flag. */static void print_yymm(int yymm){ if (formatted_output) pr("%02d/%02d\n", yymm % 100, yymm / 100); else pr("%04d\n", yymm);}/* * fday_jdate() * * Given a Julian date, return the Julian date of the * first day of the month. */static long fday_jdate(long jdate){ return(yymmtojul(jultoyymm(jdate), 0));}/* * fday_yymm() * * Given a yymm date, return the Julian date of the * first day of the month. */static long fday_yymm(int yymm){ return(yymmtojul(yymm, 0));}/* * lday_jdate() * * Given a Julian date, return the Julian date of the * last day of the month. */static long lday_jdate(long jdate){ return(yymmtojul(jultoyymm(jdate), 1));}/* * lday_yymm() * * Given a yymm date, return the Julian date of the * last day of the month. */static long lday_yymm(int yymm){ return(yymmtojul(yymm, 1));}/* * ndays_yymm() * * Given a YYMM date, return the number of days in this month. */static long ndays_yymm(int yymm){ return daysinyymm(yymm);}/* * ndays_jdate() * * Given a Julian date, return the number of days in this month. */static long ndays_jdate(long jdate){ return(daysinyymm(jultoyymm(jdate)));}static long month_yymm(int yymm){ return(yymm % 100);}static long year_yymm(int yymm){ return((yymm / 100) + 1900);}static long year_jdate(long jdate){ rjulmdy(jdate, mdy); return mdy[YY];}static long month_jdate(long jdate){ rjulmdy(jdate, mdy); return(mdy[MM]);}static long day_jdate(long jdate){ rjulmdy(jdate, mdy); return(mdy[DD]);}/* * bogus() * * Given a string that is the name of a date function, print * an error message saying that we can't apply this function * to an integer. Then die. */static void bogus(const char *str){ die("can't take \"%s\" of a numeric expression!", str);}static void cannot_add(void){ die("Adding dates doesn't make any sense!");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -