3

I’m attempting to create a grammar for a lighting control system and I make good progress when testing with the tree gui tool but it all seems to fall apart when I attempt to implement it into my app.

The basic structure of the language is [Source] [Mask] [Command] [Destination]. Mask is optional so a super simple sample input might look like this : Fixture 1 @ 50 which bypasses Mask. Fixture 1 is the source, @ is the command and 50 is the destination which in this case is an intensity value.

I’ve no issues with this type of input but things get complicated as I try and build out more complex source selection. Let’s say I want to select a range of fixtures and remove a few from the selection and then add more fixtures after.

Fixture 1 Thru 50 – 25 – 30 – 35 + 40 > 45 @ 50

This is a very common syntax on existing control systems but I’m stumped at how to design the grammar for this in a way that makes integration into my app not too painful.

The user could just as easily type the following:

1 Thru 50 – 25 – 30 – 35 + 40 > 45 @ 50

Because sourceType (fixture) is not provided, its inferred.

To try and deal with the above situations, I've written the following:

grammar LiteMic;

/*
 * Parser Rules
 */

start       : expression;

expression  : source command destination 
            | source mask command destination
            | command destination
            | source command; 

destination : sourceType number 
            | sourceType number sourceType number 
            | number;

command     : COMMAND;

mask        : SOURCETYPE;

operator    : ADD                                       #Add
            | SUB                                       #Subtract
            ;

plus : ADD;
minus : SUB;

source          : singleSource (plus source)*
                | rangeSource (plus source)*
                ;

singleSource    : sourceType number             #SourceWithType
                | number                        #InferedSource
                ;

rangeSource     : sourceRange (removeSource)*
                ;

sourceRange   : singleSource '>' singleSource;

removeSource     : '-' source;

sourceType      : SOURCETYPE;

number : NUMBER;


compileUnit
    :   EOF
    ;

/*
 * Lexer Rules
 */

SOURCETYPE      : 'Cue'
                | 'Playback' 
                | 'List'
                | 'Intensity'
                | 'Position'
                | 'Colour'
                | 'Beam'
                | 'Effect'
                | 'Group'
                | 'Fixture'
                ;


COMMAND     : '@' 
            | 'Record' 
            | 'Update' 
            | 'Copy' 
            | 'Move' 
            | 'Delete' 
            | 'Highlight' 
            | 'Full'
            ;

ADD         : '+'               ;       
SUB         : '-'               ;
THRU        : '>'               ;

/* A number: can be an integer value, or a decimal value */
NUMBER     : [0-9]+ ;

/* We're going to ignore all white space characters */
WS : [ \t\r\n]+ -> skip
   ;

Running the command against grun gui produces the following: enter image description here

I've had some measure of success being able to override the Listener for AddRangeSource as I can loop through and add the correct types but it all falls apart when I try and remove a range.

1 > 50 - 30 > 35 @ 50

This produces a problem as the removal of a range matches to the 'addRangeSource'.

enter image description here

I'm pretty sure I'm missing something obvious and I've been working my way through the book I bought on Amazon but it's still not cleared up in my head how to archieve what I'm after and I've been looking at this for a week.

For good measure, below is a tree for a more advanced query that seems ok apart from the selection. enter image description here

Does anyone have any pointers / suggestions on where I'm going wrong?

Cheers, Mike

Ghost4Man
  • 1,040
  • 1
  • 12
  • 19
Mike James
  • 461
  • 2
  • 4
  • 20
  • I don't see `'Thru'` in your lexer. There is a `THRU` rulke, but that matches a `>` – Bart Kiers Nov 02 '18 at 08:17
  • Thru is the physical key which is actually > in the command line parser so anytime I mention Thru, in terms of ANTLR, I'm actually meaning >. Sorry for the confusion! – Mike James Nov 05 '18 at 11:23

1 Answers1

1

You can solve the problem by reorganizing the grammar a little:

  • Merge rangeSource with sourceRange:

    rangeSource : singleSource '>' singleSource;
    

    Note: This rule also matches input like Beam 1 > Group 16, which might be unintended, in that case you could use this:

    rangeSource : sourceType? number '>' number;
    
  • Rename source to sourceList (and don't forget to change it in the expression rule):

    expression : sourceList command destination 
               | sourceList mask command destination
               | command destination
               | sourceList command; 
    
  • Add a source rule that matches either singleSource or rangeSource:

    source : singleSource | rangeSource;
    
  • Put + and - at the same level (as addSource and removeSource):

    addSource : plus source;
    removeSource : minus source;
    
  • Change sourceList to accept a list of addSource/removeSource:

    sourceList : source (addSource|removeSource)*;
    

I tried this and it doesn't have any problems with parsing even the more advanced query.

Ghost4Man
  • 1,040
  • 1
  • 12
  • 19