The repository formerly known as dotfiles
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

251 行
5.6KB

  1. /* Copyright (C) 2019 C. McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. %{
  17. #include <ctype.h>
  18. #include <err.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <strings.h>
  22. #include <sysexits.h>
  23. #include <time.h>
  24. static void yyerror(const char *str);
  25. static int yylex(void);
  26. #define YYSTYPE struct tm
  27. static const char *Days[7] = {
  28. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
  29. };
  30. static const char *Months[12] = {
  31. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  32. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  33. };
  34. static const struct tm Week = { .tm_mday = 7 };
  35. static struct tm normalize(struct tm date) {
  36. time_t time = timegm(&date);
  37. struct tm *norm = gmtime(&time);
  38. if (!norm) err(EX_OSERR, "gmtime");
  39. return *norm;
  40. }
  41. static struct tm today(void) {
  42. time_t now = time(NULL);
  43. struct tm *local = localtime(&now);
  44. if (!local) err(EX_OSERR, "localtime");
  45. struct tm date = {
  46. .tm_year = local->tm_year,
  47. .tm_mon = local->tm_mon,
  48. .tm_mday = local->tm_mday,
  49. };
  50. return normalize(date);
  51. }
  52. static struct tm monthDay(int month, int day) {
  53. struct tm date = today();
  54. date.tm_mon = month;
  55. date.tm_mday = day;
  56. return normalize(date);
  57. }
  58. static struct tm monthDayYear(int month, int day, int year) {
  59. struct tm date = today();
  60. date.tm_mon = month;
  61. date.tm_mday = day;
  62. date.tm_year = year - 1900;
  63. return normalize(date);
  64. }
  65. static struct tm weekDay(int day) {
  66. struct tm date = today();
  67. date.tm_mday += day - date.tm_wday;
  68. return normalize(date);
  69. }
  70. static struct tm scalarAdd(struct tm a, struct tm b) {
  71. a.tm_mday += b.tm_mday;
  72. a.tm_mon += b.tm_mon;
  73. a.tm_year += b.tm_year;
  74. return a;
  75. }
  76. static struct tm scalarSub(struct tm a, struct tm b) {
  77. a.tm_mday -= b.tm_mday;
  78. a.tm_mon -= b.tm_mon;
  79. a.tm_year -= b.tm_year;
  80. return a;
  81. }
  82. static struct tm dateAdd(struct tm date, struct tm scalar) {
  83. return normalize(scalarAdd(date, scalar));
  84. }
  85. static struct tm dateSub(struct tm date, struct tm scalar) {
  86. return normalize(scalarSub(date, scalar));
  87. }
  88. static struct tm dateDiff(struct tm a, struct tm b) {
  89. struct tm diff = {
  90. .tm_year = a.tm_year - b.tm_year,
  91. .tm_mon = a.tm_mon - b.tm_mon,
  92. .tm_mday = a.tm_mday - b.tm_mday,
  93. };
  94. if (a.tm_mon < b.tm_mon) {
  95. diff.tm_year--;
  96. diff.tm_mon += 12;
  97. }
  98. if (a.tm_mday < b.tm_mday) {
  99. diff.tm_mon--;
  100. diff.tm_mday = 0;
  101. while (dateAdd(b, diff).tm_mday != a.tm_mday) diff.tm_mday++;
  102. }
  103. time_t atime = timegm(&a), btime = timegm(&b);
  104. diff.tm_yday = (atime - btime) / 24 / 60 / 60;
  105. return diff;
  106. }
  107. static void printDate(struct tm date) {
  108. printf(
  109. "%s %s %d %d\n",
  110. Days[date.tm_wday], Months[date.tm_mon],
  111. date.tm_mday, 1900 + date.tm_year
  112. );
  113. }
  114. static void printScalar(struct tm scalar) {
  115. if (scalar.tm_year) printf("%dy ", scalar.tm_year);
  116. if (scalar.tm_mon) printf("%dm ", scalar.tm_mon);
  117. if (scalar.tm_mday % 7) {
  118. printf("%dd ", scalar.tm_mday);
  119. } else if (scalar.tm_mday) {
  120. printf("%dw ", scalar.tm_mday / 7);
  121. }
  122. if (scalar.tm_yday && scalar.tm_mon) printf("(%dd) ", scalar.tm_yday);
  123. printf("\n");
  124. }
  125. %}
  126. %token Number Month Day
  127. %left '+' '-'
  128. %right '<' '>'
  129. %%
  130. expr:
  131. date { printDate($1); }
  132. | scalar { printScalar($1); }
  133. ;
  134. date:
  135. dateLit
  136. | '(' date ')' { $$ = $2; }
  137. | '<' date { $$ = dateSub($2, Week); }
  138. | '>' date { $$ = dateAdd($2, Week); }
  139. | date '+' scalar { $$ = dateAdd($1, $3); }
  140. | date '-' scalar { $$ = dateSub($1, $3); }
  141. ;
  142. scalar:
  143. scalarLit
  144. | '(' scalar ')' { $$ = $2; }
  145. | scalar '+' scalar { $$ = scalarAdd($1, $3); }
  146. | scalar '-' scalar { $$ = scalarSub($1, $3); }
  147. | date '-' date { $$ = dateDiff($1, $3); }
  148. ;
  149. dateLit:
  150. { $$ = today(); }
  151. | '.' { $$ = today(); }
  152. | Month Number { $$ = monthDay($1.tm_mon, $2.tm_sec); }
  153. | Month Number Number { $$ = monthDayYear($1.tm_mon, $2.tm_sec, $3.tm_sec); }
  154. | Day { $$ = weekDay($1.tm_wday); }
  155. ;
  156. scalarLit:
  157. Number 'd' { $$ = (struct tm) { .tm_mday = $1.tm_sec }; }
  158. | Number 'w' { $$ = (struct tm) { .tm_mday = 7 * $1.tm_sec }; }
  159. | Number 'm' { $$ = (struct tm) { .tm_mon = $1.tm_sec }; }
  160. | Number 'y' { $$ = (struct tm) { .tm_year = $1.tm_sec }; }
  161. ;
  162. %%
  163. static void yyerror(const char *str) {
  164. warnx("%s", str);
  165. }
  166. static const char *input;
  167. static int yylex(void) {
  168. while (isspace(*input)) input++;
  169. if (!*input) return EOF;
  170. if (isdigit(*input)) {
  171. char *rest;
  172. yylval.tm_sec = strtol(input, &rest, 10);
  173. input = rest;
  174. return Number;
  175. }
  176. for (int i = 0; i < 7; ++i) {
  177. if (strncasecmp(input, Days[i], 3)) continue;
  178. while (isalpha(*input)) input++;
  179. yylval.tm_wday = i;
  180. return Day;
  181. }
  182. for (int i = 0; i < 12; ++i) {
  183. if (strncasecmp(input, Months[i], 3)) continue;
  184. while (isalpha(*input)) input++;
  185. yylval.tm_mon = i;
  186. return Month;
  187. }
  188. return *input++;
  189. }
  190. int main(int argc, char *argv[]) {
  191. if (argc > 1) {
  192. input = argv[1];
  193. return yyparse();
  194. }
  195. struct tm date = today();
  196. printDate(date);
  197. printf("\n");
  198. char *line = NULL;
  199. size_t cap = 0;
  200. while (0 < getline(&line, &cap, stdin)) {
  201. if (line[0] == '\n') continue;
  202. if (today().tm_mday != date.tm_mday) {
  203. warnx("the date has changed");
  204. date = today();
  205. }
  206. input = line;
  207. yyparse();
  208. printf("\n");
  209. }
  210. }