Skip to content

Commit

Permalink
Imply precedence & associativity to LALR generator
Browse files Browse the repository at this point in the history
This feature was ignored in the past to make the parser generator as universal as possible. In this case, I think its better to support this feature, althought it wouldn't be applicable in other parsing algorithms.

A pbnf example which already works is:

	%skip	/[\s]+/ ;
	Int		: /[0-9]+/ = int;

	<<		'+' '-';
	<<		'*' '/';

	factor	: Int
			| '(' expr ')'
			;

	expr$	: expr '*' expr = mul
			| expr '/' expr = div
			| expr '+' expr = add
			| expr '-' expr = sub
			| factor
			;
  • Loading branch information
phorward committed Apr 13, 2018
1 parent 5201f91 commit aaca6ba
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 124 deletions.
31 changes: 25 additions & 6 deletions src/parse/gram.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Don't call it if you're unsure ;)... */
pboolean pp_gram_prepare( ppgram* g )
{
plistel* e;
plistel* er;
plistel* f;
ppprod* prod;
ppprod* cprod;
ppsym* sym;
Expand Down Expand Up @@ -111,9 +111,9 @@ pboolean pp_gram_prepare( ppgram* g )
{
plist_push( done, prod );

plist_for( prod->rhs, er )
plist_for( prod->rhs, f )
{
sym = (ppsym*)plist_access( er );
sym = (ppsym*)plist_access( f );

nullable = FALSE;

Expand Down Expand Up @@ -149,7 +149,7 @@ pboolean pp_gram_prepare( ppgram* g )
}

/* Flag nullable */
if( !er )
if( !f )
{
cprod->flags |= PPFLAG_NULLABLE;
cprod->lhs->flags |= PPFLAG_NULLABLE;
Expand Down Expand Up @@ -183,9 +183,9 @@ pboolean pp_gram_prepare( ppgram* g )

for( i = 0; ( prod = pp_sym_getprod( sym, i ) ); i++ )
{
plist_for( prod->rhs, er )
plist_for( prod->rhs, f )
{
sym = (ppsym*)plist_access( er );
sym = (ppsym*)plist_access( f );
sym->flags |= PPFLAG_LEXEM;

if( !PPSYM_IS_TERMINAL( sym ) )
Expand All @@ -202,6 +202,25 @@ pboolean pp_gram_prepare( ppgram* g )
plist_free( call );
plist_free( done );

/* Inherit precedences */
plist_for( g->prods, e )
{
prod = (ppprod*)plist_access( e );

if( prod->prec < prod->lhs->prec )
prod->prec = prod->lhs->prec;

for( f = plist_last( prod->rhs ); f; f = plist_prev( f ) )
{
sym = (ppsym*)plist_access( f );
if( sym->prec > prod->prec )
{
prod->prec = sym->prec;
break;
}
}
}

/* Set finalized */
g->flags |= PPFLAG_FINALIZED;
g->strval = pfree( g->strval );
Expand Down
119 changes: 105 additions & 14 deletions src/parse/lr.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ static pplrstate* lr_get_undone( plist* states )
return (pplrstate*)NULL;
}

static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
static plist* pp_lr_closure( ppgram* gram, pboolean optimize, pboolean resolve )
{
plist* states;
pplrstate* st;
Expand All @@ -385,9 +385,10 @@ static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
int* prodcnt;
pboolean printed;

PROC( "pp_parser_lr_closure" );
PROC( "pp_lr_closure" );
PARMS( "gram", "%p", gram );
PARMS( "optimize", "%s", BOOLEAN_STR( optimize ) );
PARMS( "resolve", "%s", BOOLEAN_STR( resolve ) );

if( !( gram ) )
{
Expand All @@ -413,10 +414,8 @@ static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
{
st->done = TRUE;

#if DEBUGLEVEL > 1
fprintf( stderr, "---\nClosing state %d\n",
plist_offset( plist_get_by_ptr( states, st ) ) );
#endif
LOG( "--- Closing state %d",
plist_offset( plist_get_by_ptr( states, st ) ) );

MSG( "Closing state" );
VARS( "State", "%d", plist_offset(
Expand Down Expand Up @@ -675,6 +674,8 @@ static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
printed = FALSE;
st = (pplrstate*)plist_access( e );

LOG( "State %d", plist_offset( plist_get_by_ptr( states, st ) ) );

/* Reductions */
for( part = st->kernel; part;
part = ( part == st->kernel ? st->epsilon : (plist*)NULL ) )
Expand All @@ -696,18 +697,100 @@ static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
}
}

/* Detect and report conflicts */
plist_for( st->actions, f )
MSG( "Detect and report, or resolve conflicts" );

for( f = plist_first( st->actions ); f; )
{
col = (pplrcolumn*)plist_access( f );
sym = (ppsym*)NULL;

for( g = plist_next( f ); g; g = plist_next( g ) )
{
ccol = (pplrcolumn*)plist_access( g );

if( ccol->symbol == col->symbol )
if( ccol->symbol == col->symbol
&& ccol->reduce ) /* Assertion: Shift-shift entries
are never possible! */
{
LOG( "State %d encounters %s/reduce-conflict on %s",
plist_offset( e ),
col->reduce ? "reduce" : "shift",
col->symbol->name );

if( resolve )
{
/* Try to resolve reduce-reduce conflict */
if( col->reduce )
{
/* Resolve by lower production! */
if( col->reduce->idx > ccol->reduce->idx )
{
MSG( "Conflict resolved in favor of "
"lower production" );
col->reduce = ccol->reduce;

LOG( "Conflict resolved by lower production %d",
ccol->reduce );
}

plist_remove( st->actions, g );

/* Clear non-associative entry! */
if( col->symbol->assoc == PPASSOC_NOT )
{
LOG( "Symbol '%s' is non-associative, "
"removing entry",
pp_sym_to_str( col->symbol ) );

plist_remove( st->actions, f );
f = g = (plistel*)NULL;
}

break; /* Restart search! */
}
/* Try to resolve shift-reduce conflict */
else
{
/* In case there are precedences,
resolving is possible */
if( col->symbol->prec && ccol->reduce->prec )
{
/* Resolve by symbol/production precedence,
or by associativity */
if( col->symbol->prec < ccol->reduce->prec
|| ( col->symbol->prec == ccol->reduce->prec
&& col->symbol->assoc == PPASSOC_LEFT )
)
{
MSG( "Conflict resolved "
"in favor of reduce" );
col->reduce = ccol->reduce;
col->shift = (pplrstate*)NULL;
}
/* Clear non-associative entry! */
else if( col->symbol->assoc == PPASSOC_NOT )
{
LOG( "Symbol '%s' is non-associative, "
"removing entry",
pp_sym_to_str( col->symbol ) );

plist_remove( st->actions, f );
f = g = (plistel*)NULL;
break;
}
else
{
MSG( "Conflict resolved in favor"
"of shift" );
}

plist_remove( st->actions, g );
break;
}
}
}

/* If no resolution is possible or wanted,
report conflict */
if( !printed )
{
#if DEBUGLEVEL > 0
Expand All @@ -717,18 +800,26 @@ static plist* pp_lr_closure( ppgram* gram, pboolean optimize )
printed = TRUE;
}

/* TODO Conflict resolution */
fprintf( stderr,
"State %d encouters %s/reduce-conflict on %s\n",
"State %d encounters %s/reduce-conflict on %s\n",
plist_offset( e ),
col->reduce ? "reduce" : "shift",
col->symbol->name );
}
}

if( g )
continue;

if( f )
f = plist_next( f );
else
f = plist_first( st->actions );
}

/* Detect default prods */
#if 0
MSG( "Detect default productions" );

memset( prodcnt, 0, plist_count( gram->prods ) * sizeof( int ) );
cnt = 0;

Expand Down Expand Up @@ -803,7 +894,7 @@ pboolean pp_lr_build( unsigned int* cnt, unsigned int*** dfa, ppgram* grm )
}

/* Compute LALR(1) states */
states = pp_lr_closure( grm, FALSE ); /* fixme: optimiziation */
states = pp_lr_closure( grm, TRUE, TRUE );

#if DEBUGLEVEL > 0
fprintf( stderr, "\n\n--- FINAL GLR STATES ---\n\n" );
Expand Down
45 changes: 30 additions & 15 deletions src/parse/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ typedef struct _ppgram ppgram;
typedef struct _ppast ppast;

/* Flags for grammars and their objects */
#define PPFLAG_NONE 0
#define PPFLAG_CALLED 1
#define PPFLAG_DEFINED 2
#define PPFLAG_NULLABLE 4
#define PPFLAG_LEFTREC 8
#define PPFLAG_LEXEM 16
#define PPFLAG_WHITESPACE 32
#define PPFLAG_PREVENTLREC 64
#define PPFLAG_NAMELESS 128
#define PPFLAG_GENERATED 256
#define PPFLAG_FREENAME 512
#define PPFLAG_FREEEMIT 1024
#define PPFLAG_SPECIAL 2048
#define PPFLAG_FINALIZED 4096
#define PPFLAG_FROZEN 8192
#define PPFLAG_NONE 0x00
#define PPFLAG_CALLED 0x01
#define PPFLAG_DEFINED 0x02
#define PPFLAG_NULLABLE 0x04
#define PPFLAG_LEFTREC 0x08
#define PPFLAG_LEXEM 0x10
#define PPFLAG_WHITESPACE 0x20
#define PPFLAG_PREVENTLREC 0x40
#define PPFLAG_NAMELESS 0x80
#define PPFLAG_GENERATED 0x100
#define PPFLAG_FREENAME 0x200
#define PPFLAG_FREEEMIT 0x400
#define PPFLAG_SPECIAL 0x800
#define PPFLAG_FINALIZED 0x1000
#define PPFLAG_FROZEN 0x2000

#define PPMOD_OPTIONAL '?'
#define PPMOD_POSITIVE '+'
Expand All @@ -37,6 +37,15 @@ typedef struct _ppast ppast;
#define PPLR_SHIFT 1
#define PPLR_REDUCE 2

/* Associativity */
typedef enum
{
PPASSOC_NONE,
PPASSOC_NOT,
PPASSOC_LEFT,
PPASSOC_RIGHT
} ppassoc;

/* Production */
struct _ppprod
{
Expand All @@ -47,6 +56,9 @@ struct _ppprod
plist* rhs; /* Left-hand side items */
unsigned int flags; /* Configuration flags */

ppassoc assoc; /* LR associativity */
unsigned int prec; /* LR precedence level */

char* emit; /* AST emitting node */

char* strval; /* String represenation */
Expand All @@ -65,6 +77,9 @@ struct _ppsym

unsigned int flags; /* Configuration flags */

ppassoc assoc; /* LR associativity */
unsigned int prec; /* LR precedence level */

plist* first; /* Set of FIRST() symbols */

char* emit; /* AST emitting node */
Expand Down
Loading

0 comments on commit aaca6ba

Please sign in to comment.