3

I'm using YACC for the first time and getting used to using BNF grammar.

I'm currently building a list of types from a comma separated list (eg. int, float, string):

def p_type(p):
    '''type : primitive_type
            | array
            | generic_type
            | ID'''
    p[0] = p[1]


def p_type_list(p):
    '''type_list : type
                 | type COMMA type_list'''
    if not isinstance(p[0], list):
        p[0] = list()
    p[0].append(p[1])
    if len(p) == 4:
        p[0] += p[3]

The rules work, but I'm getting the sense that my p_type_list logic is a bit of a kludge and could be simplified into a one-liner.

I haven't found any PLY specific examples of this online. Any help would be greatly appreciated!

Colin Basnett
  • 4,052
  • 2
  • 30
  • 49

2 Answers2

8

There are two productions. Use two separate functions. (There is no extra cost :-) )

def p_type_list_1(p):
    '''type_list : type'''
    p[0] = [p[1]]

def p_type_list_2(p):
    '''type_list : type_list COMMA type'''
    p[0] = p[1] + [p[3]]

Note: I fixed your grammar to use left-recursion. With bottom-up parsing, left-recursion is almost always what you want, because it avoids unnecessary parser stack usage, and more importantly because it often simplifies actions. In this case, I could have written the second function as:

def p_type_list_2(p):
    '''type_list : type_list COMMA type'''
    p[0] = p[1]
    p[0] += [p[3]]

which avoids a list copy.

rici
  • 234,347
  • 28
  • 237
  • 341
2

Or "simplify" p_type_list to (you reduce by 1 line of code, not sure if that's worth it):

def p_type_list(p):
    '''type_list : type
                 | type_list COMMA type'''
    if len(p) == 2:
       p[0] = [p[1]]
    else:
       p[0] = p[1] + [p[3]]
orange
  • 7,755
  • 14
  • 75
  • 139