/* S-expression grammar for Minor's interpreter's reader. */ /* %{ */ #include "minor/minor.h" #include "minor/cc.h" /* The type of Bison semantic values. */ #define YYSTYPE mn_ref * /* The context for datum parsing. The parsing and lexing functions each take a pointer to one of these objects, cast to 'void *'. */ struct context { /* The call to use for allocation while parsing. */ mn_call *call; /* A function to call to read the next character from the input stream. Returns -1 on end-of-input. */ int get (void *closure); /* A function to call to push back one character onto the input stream. C must be the last character returned by 'get'. */ int unget (int c, void *closure); /* The CLOSURE value to pass to 'get' and 'unget'. */ void *closure; }; /* Our parser takes an mn_call, cast to a void *, as a parameter. */ #define YYPARSE_PARAM untyped_context #define YYLEX_PARAM #define ctx ((struct context *) untyped_context) /* Our lexer is static to this file. */ static int yylex (YYSTYPE *lvalp); %} %pure_parser %token CHARACTER %token FALSE %token IDENTIFIER %token NEWLINE_CHARACTER %token NUMBER %token SPACE_CHARACTER %token START_VECTOR %token STRING %token TRUE %token UNQUOTE_SPLICING %% datum: simple_datum | compound_datum; simple_datum: boolean | NUMBER | character | STRING | symbol; boolean: TRUE | FALSE; character: SPACE_CHARACTER | NEWLINE_CHARACTER | CHARACTER ; symbol: IDENTIFIER; compound_datum: list | vector; list: '(' list_data ')' { $$ = $2 }; /* This rule is deliberately right-recursive, even though such rules use unbounded stack space in a Bison-generated parser. Since lisp lists are right-recursive, we can't start constructing the list until we parse its end anyway, so any left-to-right parser is going to use some kind of temporary space. The usual strategy of building the list backwards, and then reversing it, simply uses the pairs themselves as the temporary space. But mutation isn't free in a generational collector; in our implementation, every mutation allocates a pair. So since a lot of fancy shuffling really isn't going to win much anyway, we just write the rule in the most straightforward fashion, and let Bison cope. */ list_data: datum list_data { $$ = mn_to_cons (c, $1, $2); } | datum '.' datum { $$ = mn_to_cons (c, $1, $3); } | { $$ = mn_null (c); } ; vector: START_VECTOR vector_data ')' { $$ = mn_list_to_vector ($2); mn_free_local_ref ($2); } ; vector_data: datum vector_data { $$ = mn_to_cons (c, $1, $2); } | { $$ = mn_null (c); } ; %% static int yylex (YYSTYPE *lvalp) { } void mn__interp_datum_init (mn_call *c) { quote = mn_to_global_ref (mn_symbol_from_str (c, "quote")); quasiquote = mn_to_global_ref (mn_symbol_from_str (c, "quasiquote")); unquote = mn_to_global_ref (mn_symbol_from_str (c, "unquote")); unquote_splicing = mn_to_global_ref (mn_symbol_from_str (c, "unquote-splicing")); }