Skip to content

Semantic actions on layout terminals are delayed #52

@krame505

Description

@krame505

For context, this problem showed up in ableC, where we are trying to use the C preprocessor tags to control the disambiguation of identifiers, based on whether we are currently parsing a system header file. These preprocessor tags are recognized as a layout terminal, with a semantic action that sets a parser attribute which is later referenced in a disambiguation function.

The following is a simple example in Silver:

grammar layoutactions;

parser attribute isMarked :: Boolean action { isMarked = false; };

ignore terminal Marker_t '#'
  action {
    print "Shifted Marker";
    isMarked = true;
  };

terminal Id1_t /[a-zA-Z]+/ action { print "Shifted Id1"; };
terminal Id2_t /[a-zA-Z]+/ action { print "Shifted Id2"; };

disambiguate Id1_t, Id2_t {
  print "Disambiguating Id";
  pluck if isMarked then Id1_t else Id2_t;
}

nonterminal Foo;
concrete productions top::Foo
| Id1_t {} action { print "Reduced 1"; }
| Id2_t {} action { print "Reduced 2"; }

parser parse :: Foo {  layoutactions; }
function main 
IOVal<Integer> ::= args::[String] ioin::IO
{
  local result::ParseResult<Foo> = parse(head(args), "test");
  return
    if null(args) then ioval(print("no argument\n", ioin), 1)
    else if !result.parseSuccess then ioval(print(result.parseErrors ++ "\n", ioin), 0)
    else ioval(ioin, 0);
}

When run on the input #a, this prints

Disambiguating Id
Shifted Marker
Shifted Id2
Reduced 2

The disambiguation function is being called before the semantic action for '#' has been run, thus giving the wrong result.

As a (very hacky) workaround, one can introduce a second, ambiguous layout terminal and use a disambiguation function to update the parser attribute in the disambiguation function:

ignore terminal Marker2_t '#';
disambiguate Marker_t, Marker2_t {
  print "Disambiguating Marker";
  isMarked = true;
  pluck Marker_t;
}

This gives the desired output of

Disambiguating Marker
Disambiguating Id
Shifted Marker
Shifted Id1
Reduced 1

This workaround does work in the context of ableC but is something that we would greatly wish to avoid.

Since we evidently know which layout terminal is going to be shifted as soon as the disambiguation function is run, why aren't the semantic actions executed immediately, before attempting to disambiguate the next terminal? I vaguely remember there being a reason for this, but I don't remember what it was.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions