title: Python PLY issue with if-else and while statements
body: <p>if statements and while statement keep throwing syntax errors from p_error(p), and PLY tells me that there's a conflict at runtime. The issues are from the if-else and while statements because it was fine before adding them. Any help would be appreciated.</p>
  
  <p>If possible please don't change the implementation much even if it's in bad practice. I just want help understanding it I don't want a complete overhaul (that'd be plagiarism).</p>
  
  <pre><code>import ply.lex as lex
  import ply.yacc as yacc
  
  # === Lexical tokens component ===
  
  # List of possible token namesthat can be produced by the lexer
  # NAME: variable name, L/RPAREN: Left/Right Parenthesis
  tokens = (
      'NAME', 'NUMBER',
      'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO', 'EQUALS',
      'LPAREN', 'RPAREN',
      'IF', 'ELSE', 'WHILE',
      'EQUAL', 'NOTEQ', 'LARGE', 'SMALL', 'LRGEQ', 'SMLEQ',
  )
  
  # Regular expression rules for tokens format: t_&lt;TOKEN&gt;
  # Simple tokens: regex for literals +,-,*,/,%,=,(,) and variable names (alphanumeric)
  t_PLUS    = r'\+'
  t_MINUS   = r'-'
  t_TIMES   = r'\*'
  t_DIVIDE  = r'/'
  t_MODULO  = r'%'
  t_EQUALS  = r'='
  t_LPAREN  = r'\('
  t_RPAREN  = r'\)'
  t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
  t_IF      = r'if'
  t_ELSE    = r'else'
  t_WHILE   = r'while'
  t_EQUAL   = r'\=\='
  t_NOTEQ   = r'\!\='
  t_LARGE   = r'\&gt;'
  t_SMALL   = r'\&lt;'
  t_LRGEQ   = r'\&gt;\='
  t_SMLEQ   = r'\&lt;\='
  
  
  # complex tokens
  # number token
  def t_NUMBER(t):
      r'\d+'  # digit special character regex
      t.value = int(t.value)  # convert str -&gt; int
      return t
  
  
  # Ignored characters
  t_ignore = " \t"  # spaces &amp; tabs regex
  
  # newline character
  def t_newline(t):
      r'\n+'  # newline special character regex
      t.lexer.lineno += t.value.count("\n")  # increase current line number accordingly
  
  
  # error handling for invalid character
  def t_error(t):
      print("Illegal character '%s'" % t.value[0])  # print error message with causing character
      t.lexer.skip(1)  # skip invalid character
  
  
  # Build the lexer
  lex.lex()
  
  # === Yacc parsing/grammar component ===
  
  # Precedence &amp; associative rules for the arithmetic operators
  # 1. Unary, right-associative minus.
  # 2. Binary, left-associative multiplication, division, and modulus
  # 3. Binary, left-associative addition and subtraction
  # Parenthesis precedence defined through the grammar
  precedence = (
      ('left', 'PLUS', 'MINUS'),
      ('left', 'TIMES', 'DIVIDE', 'MODULO'),
      ('right', 'UMINUS'),
  )
  
  # dictionary of names (for storing variables)
  names = {}
  
  # --- Grammar:
  # &lt;statement&gt; -&gt; NAME = &lt;expression&gt; | &lt;expression&gt;
  # &lt;expression&gt; -&gt; &lt;expression&gt; + &lt;expression&gt;
  #               | &lt;expression&gt; - &lt;expression&gt;
  #               | &lt;expression&gt; * &lt;expression&gt;
  #               | &lt;expression&gt; / &lt;expression&gt;
  #               | &lt;expression&gt; % &lt;expression&gt;
  #               | - &lt;expression&gt;
  #               | ( &lt;expression&gt; )
  #               | NUMBER
  #               | NAME
  # ---
  # defined below using function definitions with format string/comment
  # followed by logic of changing state of engine
  
  
  # if statement
  def p_statement_if(p):
      '''statement : IF LPAREN comparison RPAREN statement
                      | IF LPAREN comparison RPAREN statement ELSE statement'''
      if p[3]:
          p[0] = p[5]
      else:
          if p[7] is not None:
              p[0] = p[7]
  
  
  def p_statement_while(p):
      'statement : WHILE LPAREN comparison RPAREN statement'
      while(p[3]):
          p[5];
  
  
  # assignment statement: &lt;statement&gt; -&gt; NAME = &lt;expression&gt;
  def p_statement_assign(p):
      'statement : NAME EQUALS expression'
      names[p[1]] = p[3]  # PLY engine syntax, p stores parser engine state
  
  
  # expression statement: &lt;statement&gt; -&gt; &lt;expression&gt;
  def p_statement_expr(p):
      'statement : expression'
      print(p[1])
  
  
  # comparison
  def p_comparison_binop(p):
      '''comparison : expression EQUAL expression
                            | expression NOTEQ expression
                            | expression LARGE expression
                            | expression SMALL expression
                            | expression LRGEQ expression
                            | expression SMLEQ expression'''
      if p[2] == '==':
          p[0] = p[1] == p[3]
      elif p[2] == '!=':
          p[0] = p[1] != p[3]
      elif p[2] == '&gt;':
          p[0] = p[1] &gt; p[3]
      elif p[2] == '&lt;':
          p[0] = p[1] &lt; p[3]
      elif p[2] == '&gt;=':
          p[0] = p[1] &gt;= p[3]
      elif p[2] == '&lt;=':
          p[0] = p[1] &lt;= p[3]
  
  
  # binary operator expression: &lt;expression&gt; -&gt; &lt;expression&gt; + &lt;expression&gt;
  #                                          | &lt;expression&gt; - &lt;expression&gt;
  #                                          | &lt;expression&gt; * &lt;expression&gt;
  #                                          | &lt;expression&gt; / &lt;expression&gt;
  #                                          | &lt;expression&gt; % &lt;expression&gt;
  def p_expression_binop(p):
      '''expression : expression PLUS expression
                            | expression MINUS expression
                            | expression TIMES expression
                            | expression DIVIDE expression
                            | expression MODULO expression'''
      if p[2] == '+':
          p[0] = p[1] + p[3]
      elif p[2] == '-':
          p[0] = p[1] - p[3]
      elif p[2] == '*':
          p[0] = p[1] * p[3]
      elif p[2] == '/':
          p[0] = p[1] / p[3]
      elif p[2] == '%':
          p[0] = p[1] % p[3]
  
  
  # unary minus operator expression: &lt;expression&gt; -&gt; - &lt;expression&gt;
  def p_expression_uminus(p):
      'expression : MINUS expression %prec UMINUS'
      p[0] = -p[2]
  
  
  # parenthesis group expression: &lt;expression&gt; -&gt; ( &lt;expression&gt; )
  def p_expression_group(p):
      'expression : LPAREN expression RPAREN'
      p[0] = p[2]
  
  
  # number literal expression: &lt;expression&gt; -&gt; NUMBER
  def p_expression_number(p):
      'expression : NUMBER'
      p[0] = p[1]
  
  
  # variable name literal expression: &lt;expression&gt; -&gt; NAME
  def p_expression_name(p):
      'expression : NAME'
      # attempt to lookup variable in current dictionary, throw error if not found
      try:
          p[0] = names[p[1]]
      except LookupError:
          print("Undefined name '%s'" % p[1])
          p[0] = 0
  
  
  # handle parsing errors
  def p_error(p):
      print("Syntax error at '%s'" % p.value)
  
  
  # build parser
  yacc.yacc()
  
  # start interpreter and accept input using commandline/console
  while True:
      try:
          s = input('calc &gt; ')  # get user input. use raw_input() on Python 2
      except EOFError:
          break
      yacc.parse(s)  # parse user input string
  </code></pre>