/* * This is the grammar part of a parser for .bst files. * * The code here is tightly integrated with the support in runtime.scm. * * I would like to be able to preserve comments, but it's unexpectedly * difficult to do in a comprehensive list of cases -- see corresponding * remarks in `parse-bst.lex`. * * The complicating case is finding comments between the arguments to * the `ENTRY` or `FUNCTION` commands, or inside the argument to `EXECUTE`. * * There are a couple of ways of preserving comments in these more * general cases. One way is to have the lexer accumulate comments * separately but not report them in a terminal, and then to add the * accumulated list of comments at opportune points within the grammar * below. That's neat, but requires changes at an unexpectedly large * set of points in the grammar below, and does end up moving comments * around. * * To include them in the grammar, we can possibly add rules to * `braced.list.of.tokens`, to add two with `comment.list` in front of * the `{...}`, where `comment.list` is a list of `COMMENT`. Then * remove `COMMENT` from `token` and add `comment.list` to * `list.of.tokens`. Or something like that. I seemed to be getting * close to a solution with that, but via alternatives which generated * various reduce/reduce conflicts. * * This file is part of Beastie * SPDX-FileCopyrightText: 2023 Norman Gray * SPDX-License-Identifier: BSD-2-Clause */ %{ #include "beastie.h" #include "util.h" #include "core.h" #include "parse-bst.h" static void bsterror(YYLTYPE* locp, bst_extra_t, s7_pointer*, yyscan_t, const char* msg); /* #define L_SCHEME_LINENO s7_make_integer(S7, bstget_lineno(scanner)) */ //#define X(field) (extra_info->field) %} %locations %define parse.error verbose %define api.pure %lex-param {yyscan_t scanner} %parse-param {bst_extra_t extra_info} %parse-param {s7_pointer* parse_result} %parse-param {yyscan_t scanner} // semantic value: integer %token NUMBER // semantic value: symbol %token TOKEN // semantic value: ustring %token STRING /* tokens BO and BC are '{' and '}', but with a semantic value which is their line number */ %token BO %token BC // the following have no semantic value %token QUOTE %token ASSIGNMENT /* All of the following have a semantic value which is the line number */ %token CMD_ENTRY %token CMD_EXECUTE %token CMD_FUNCTION %token CMD_INTEGERS %token CMD_ITERATE %token CMD_MACRO %token CMD_READ %token CMD_REVERSE %token CMD_SORT %token CMD_STRINGS %% input: list.of.commands { *parse_result = s7_reverse(S7, $1); } list.of.commands: command { $$ = GCP(s7_cons(S7, $1, s7_nil(S7))); } | list.of.commands command { $$ = GCP(s7_cons(S7, $2, $1)); } command: CMD_ENTRY braced.list.of.tokens braced.list.of.tokens braced.list.of.tokens { // braced.list.of.tokens is '(block content line-number: N) $$ = scheme_eval("bst:make-entry", s7_cadr($2), s7_cadr($3), s7_cadr($4), NULL); } | CMD_EXECUTE BO TOKEN BC { $$ = scheme_eval("bst:make-execute", $3, $1, NULL); } | CMD_FUNCTION BO TOKEN BC function.body { $$ = scheme_eval("bst:make-function", $3, $5, $1, NULL); } | CMD_INTEGERS braced.list.of.tokens { // braced.list.of.tokens is '(block content line-number: N) $$ = scheme_eval("bst:make-integers", s7_cadr($2), NULL); } | CMD_ITERATE BO TOKEN BC { $$ = scheme_eval("bst:make-iterate", $3, $1, NULL); } | CMD_MACRO BO TOKEN BC BO STRING BC { $$ = scheme_eval("bst:make-macro", $3, $6, NULL); } | CMD_READ { $$ = scheme_eval("bst:make-read", $1, NULL); } | CMD_REVERSE BO TOKEN BC { $$ = scheme_eval("bst:make-reverse", $3, $1, NULL); } | CMD_SORT { $$ = scheme_eval("bst:make-sort", $1, NULL); } | CMD_STRINGS braced.list.of.tokens { $$ = scheme_eval("bst:make-strings", s7_cadr($2), NULL); } function.body: braced.list.of.tokens /* Match {...}, for either ENTRY or FUNCTION. */ braced.list.of.tokens: BO BC { $$ = scheme_eval("bst:make-block", s7_nil(S7), $1, NULL); } | BO list.of.tokens BC { $$ = scheme_eval("bst:make-block", s7_reverse(S7, $2), $1, NULL); } list.of.tokens: token { $$ = GCP(s7_cons(S7, GCP($1), s7_nil(S7))); } | braced.list.of.tokens { $$ = GCP(s7_cons(S7, $1, s7_nil(S7))); } | list.of.tokens token { $$ = GCP(s7_cons(S7, GCP($2), $1)); } | list.of.tokens braced.list.of.tokens { $$ = GCP(s7_cons(S7, $2, $1)); } token: TOKEN { $$ = GCP($1); } | NUMBER { $$ = GCP($1); } | STRING { $$ = GCP($1); } | ASSIGNMENT { $$ = GCP(s7_make_symbol(S7, ":=")); } | QUOTE TOKEN { $$ = scheme_eval("bst:make-quote", $2, NULL); } | '>' { $$ = GCP(s7_make_symbol(S7, ">")); } | '<' { $$ = GCP(s7_make_symbol(S7, "<")); } | '=' { $$ = GCP(s7_make_symbol(S7, "=")); } | '+' { $$ = GCP(s7_make_symbol(S7, "+")); } | '-' { $$ = GCP(s7_make_symbol(S7, "-")); } | '*' { $$ = GCP(s7_make_symbol(S7, "*")); } %% static void bsterror(YYLTYPE* locp, bst_extra_t extra, s7_pointer* parse_result, yyscan_t scanner, const char* const msg) { fprintf(stderr, "parse-bst error at %s:%d: %s\n", (extra->path == NULL ? "" : extra->path), bstget_lineno(scanner), msg); }