vs = sym_find(g, tk.txt);
if (vs->num == TK_newline)
p.line_like = 1;
- else if (vs->type != Virtual) {
- err = "symbol after $$ must be virtual";
+ else if (vs->num == TK_out)
+ p.line_like = 2;
+ else if (vs->precedence == 0) {
+ err = "symbol after $$ must have precedence";
goto abort;
} else {
p.precedence = vs->precedence;
struct symbol *s;
struct symset LA = INIT_SYMSET;
unsigned short sn = 0;
+ struct symset LAnl = INIT_SYMSET;
+ unsigned short snnl = 0;
if (is->min_prefix == 0 ||
(bs > 0 && bs < is->min_prefix))
continue;
if (symset_find(&done, s->num) < 0) {
symset_add(&done, s->num, 0);
- if (s->line_like)
- is->starts_line = 1;
}
if (s->type != Nonterminal)
continue;
+ if (s->line_like)
+ is->starts_line = 1;
again = 1;
if (type >= LALR) {
// Need the LA set.
int to_end;
add_first(pr, bs+1, &LA, g, &to_end);
if (to_end) {
- if (pr->line_like)
- symset_add(&LA, TK_newline, 0);
- else {
- struct symset ss = set_find(g, is->items.data[i]);
- symset_union(&LA, &ss);
- }
+ struct symset ss = set_find(g, is->items.data[i]);
+ symset_union(&LA, &ss);
}
sn = save_set(g, LA);
LA = set_find(g, sn);
+ if (symset_find(&LA, TK_newline))
+ symset_add(&LAnl, TK_newline, 0);
+ snnl = save_set(g, LAnl);
+ LAnl = set_find(g, snnl);
}
/* Add productions for this symbol */
int itm = item_num(p2, 0);
int pos = symset_find(&is->items, itm);
if (pos < 0) {
- symset_add(&is->items, itm, sn);
+ if (g->productions[p2]->line_like)
+ symset_add(&is->items, itm, snnl);
+ else
+ symset_add(&is->items, itm, sn);
/* Will have re-ordered, so start
* from beginning again */
i = -1;
} else if (type >= LALR) {
struct symset ss = set_find(g, is->items.data[pos]);
struct symset tmp = INIT_SYMSET;
+ struct symset *la = &LA;
+ if (g->productions[p2]->line_like)
+ la = &LAnl;
symset_union(&tmp, &ss);
- if (symset_union(&tmp, &LA)) {
+ if (symset_union(&tmp, la)) {
is->items.data[pos] = save_set(g, tmp);
i = -1;
- }else
+ } else
symset_free(tmp);
}
}
printf(" [%d%s]", s->precedence,
assoc_names[s->assoc]);
}
- if (pr->line_like)
+ if (pr->line_like == 1)
printf(" $$NEWLINE");
+ else if (pr->line_like)
+ printf(" $$OUT");
printf("\n");
}
which maps terminals to items that could be reduced when the terminal
is in look-ahead. We report when we get conflicts between the two.
-As a special case, if we find a SHIFT/REDUCE conflict, where a
-terminal that could be shifted is in the lookahead set of some
-reducable item, then set check if the reducable item also have
-`TK_newline` in its lookahead set. If it does, then a newline will
-force the reduction, but anything else can reasonably be shifted, so
-that isn't really a conflict. Such apparent conflicts do not get
-counted, and are reported as non-critical. This will not affect a
-"traditional" grammar that does not include newlines as token.
+As a special case, if we find a SHIFT/REDUCE conflict, on the NEWLINE
+terminal, we ignore it. NEWLINES are handled specially with its own
+rules for when to shift and when to reduce. Conflicts are expected,
+but handled internally.
static int conflicts_slr(struct grammar *g, enum grammar_type type)
{
for (k = 0; k < la.cnt; k++) {
int pos = symset_find(&shifts, la.syms[k]);
if (pos >= 0 && la.syms[k] != TK_newline) {
- if (symset_find(&la, TK_newline) < 0) {
- printf(" State %d has SHIFT/REDUCE conflict on ", i);
- cnt++;
- } else
- printf(" State %d has non-critical SHIFT/REDUCE conflict on ", i);
- prtxt(g->symtab[la.syms[k]]->name);
+ printf(" State %d has SHIFT/REDUCE conflict on ", i);
+ cnt++;
+ prtxt(g->symtab[la.syms[k]]->name);
printf(":\n");
report_item(g, shifts.data[pos]);
report_item(g, itm);
force_reduce:
if (states[tos->state].reduce_prod >= 0 &&
states[tos->state].newline_only &&
- tk->num != TK_newline && tk->num != TK_eof && tk->num != TK_out) {
- /* Anything other than newline in an error as this
- * production must end at EOL
+ !(tk->num == TK_newline ||
+ tk->num == TK_eof ||
+ tk->num == TK_out ||
+ (tos->indents == 0 && tos->since_newline == 0))) {
+ /* Anything other than newline or out or eof
+ * in an error unless we are already at start
+ * of line, as this production must end at EOL.
*/
} else if (states[tos->state].reduce_prod >= 0) {
void **body;
fputs(reserved_words[tk->num], trace);
else
text_dump(trace, tk->txt, 20);
- fputs("]", trace);
+ fprintf(trace, ":%d:%d]", tk->line, tk->col);
}
void parser_trace_action(FILE *trace, char *action)
# calc: header
- #include "number.h"
+ #include "parse_number.h"
// what do we use for a demo-grammar? A calculator of course.
struct number {
mpq_t val;
#include <string.h>
#include "mdcode.h"
#include "scanner.h"
- #include "number.h"
#include "parser.h"
#include "calc.h"