-1

I'm working on a simple Pascal-like interpreter using PLY, but I'm encountering a Syntax error at ',' during parsing. The issue arises when trying to parse a source file that includes commas. Below is the code for the PLY code, as well as a sample of the source code I'm using that's causing the error.

Pascal.py

import ply.lex as lex
import ply.yacc as yacc

# Lexical Analysis
tokens = (
    'PROGRAM', 'VAR', 'BEGIN', 'END', 'INTEGER', 'REAL', 'ASSIGN',
    'SEMICOLON', 'COLON', 'COMMA', 'PLUS', 'DIVIDE', 'LPAREN', 'RPAREN',
    'WRITELN', 'STRING', 'ID', 'NUMBER', 'DOT'
)

# Reserved words
reserved = {
    'PROGRAM': 'PROGRAM',
    'VAR': 'VAR',
    'BEGIN': 'BEGIN',
    'END': 'END',
    'integer': 'INTEGER',
    'real': 'REAL',
    'WRITELN': 'WRITELN'
}

# Token definitions
t_ASSIGN = r':='
t_SEMICOLON = r';'
t_COLON = r':'
t_COMMA = r','
t_PLUS = r'\+'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_DOT = r'\.'
t_ignore = ' \t'  # Ignore spaces and tabs

def t_STRING(t):
    r"'[^']*'"
    t.value = t.value[1:-1]  # Remove surrounding quotes
    return t

def t_ID(t):
    r'[a-zA-Z_][a-zA-Z_0-9]*'
    t.type = reserved.get(t.value, 'ID')  # Check if it's a reserved word
    return t

def t_NUMBER(t):
    r'\d+(\.\d+)?'
    t.value = float(t.value) if '.' in t.value else int(t.value)
    return t

def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)

def t_error(t):
    print(f"Illegal character '{t.value[0]}'")
    t.lexer.skip(1)

lexer = lex.lex()

# Syntax Analysis
precedence = (
    ('left', 'PLUS', 'DIVIDE'),
)

def p_program(p):
    'program : PROGRAM ID SEMICOLON declarations BEGIN statements END DOT'
    interpret(p[5])  # Pass the parsed statements to the interpreter

def p_declarations(p):
    '''declarations : VAR var_declaration
                    | empty'''

def p_var_declaration(p):
    '''var_declaration : ID COLON type SEMICOLON var_declaration
                       | ID COLON type SEMICOLON'''

def p_type(p):
    '''type : INTEGER
            | REAL'''

def p_statements(p):
    '''statements : statements statement SEMICOLON
                  | statement SEMICOLON'''
    if len(p) == 4:
        p[0] = p[1] + [p[2]]
    else:
        p[0] = [p[1]]

def p_statement(p):
    '''statement : ID ASSIGN expression
                 | WRITELN LPAREN writeln_args RPAREN'''
    if p[1] == 'WRITELN':
        p[0] = ('WRITELN', p[3])
    else:
        p[0] = ('ASSIGN', p[1], p[3])

def p_writeln_args(p):
    '''writeln_args : writeln_args COMMA writeln_arg
                    | writeln_arg'''
    if len(p) == 4:
        p[0] = p[1] + [p[3]]
    else:
        p[0] = [p[1]]

def p_writeln_arg(p):
    '''writeln_arg : STRING
                   | expression'''
    p[0] = p[1]

def p_expression(p):
    '''expression : expression PLUS term
                  | expression DIVIDE term
                  | term'''
    if len(p) == 4:
        p[0] = (p[2], p[1], p[3])
    else:
        p[0] = p[1]

def p_term(p):
    '''term : ID
            | NUMBER'''
    p[0] = p[1]

def p_empty(p):
    'empty :'
    p[0] = []

def p_error(p):
    print(f"Syntax error at '{p.value}'" if p else "Syntax error at EOF")

parser = yacc.yacc()

# Interpreter
variables = {}

def evaluate(tree):
    if isinstance(tree, (int, float)):
        return tree
    if isinstance(tree, str):
        return variables.get(tree, 0)
    if tree[0] == '+':
        return evaluate(tree[1]) + evaluate(tree[2])
    if tree[0] == '/':
        return evaluate(tree[1]) / evaluate(tree[2])
    return 0

def interpret(statements):
    for stmt in statements:
        if stmt[0] == 'ASSIGN':
            variables[stmt[1]] = evaluate(stmt[2])
        elif stmt[0] == 'WRITELN':
            print(" ".join(str(evaluate(arg)) if not isinstance(arg, str) else arg for arg in stmt[1]))

# Run the Program
if __name__ == '__main__':
    import sys
    if len(sys.argv) < 2:
        print("Usage: python Pascal.py <file>")
        sys.exit(1)
    with open(sys.argv[1], 'r') as file:
        source = file.read()
    lexer.input(source)
    parser.parse(source)

Sample Source Code (SumAndAverage.pas)

PROGRAM SUMANDAVERAGE;
VAR num1,num2,num3: integer;
    sum:integer;
    avg:real;
BEGIN
    num1:=10;
    num2:=20;
    num3:=30;
    sum:=num1+num2+num3;
    avg:=sum/3;
    WRITELN('Num1 is ',num1);
    WRITELN('Num2 is ',num2);
    WRITELN('Num3 is ',num3);
    WRITELN('Sum 3 numbers is ',sum);
    WRITELN('Average is ',avg)
END.

Input and the expected Output

python Pascal.py SumAndAverage.pas
Num1 is 10
Num2 is 20
Num3 is 30
Sum 3 numbers is 60
Average is 2.0000000000000000E+001

I've reviewed my token definitions and grammar, but I can't figure out why the parser is having trouble with the comma in the WRITELN statement. Could someone help me figure out what's going wrong?

3
  • 1
    How do you know the problem is with the WRITELN statement? It seems that you have omitted part of the error message. Please post the full error message. Commented Jan 2 at 20:04
  • 2
    I suspect the problem is the VAR statement. Your grammar doesn't allow a comma-separated list of IDs. Commented Jan 2 at 20:11
  • p.__dict__ --> {'value': ',', 'lineno': 2, 'lexpos': 31, 'type': 'COMMA', 'lexer': <ply.lex.Lexer object at 0x102a5c2d0>} There error is not in the WRITELN statement. Commented Jan 2 at 20:11

1 Answer 1

0

As mentioned in the comments: your p_var_declaration only accepts one ID:

def p_var_declaration(p):
    '''var_declaration : ID COLON type SEMICOLON var_declaration
                       | ID COLON type SEMICOLON'''

change that into:

def p_var_declaration(p):
    '''var_declaration : vars COLON type SEMICOLON var_declaration
                       | vars COLON type SEMICOLON'''

def p_vars(p):
    '''vars : ID
            | vars COMMA ID'''

Also, your last WRITELN('Average is ',avg) is missing a semi-colon at the end.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.