A small Pascal-like interpreter built as a learning project.
Current pipeline:
source text -> Lexer -> Parser -> AST -> SemanticAnalyser -> Interpreter
- Lexer for Pascal-like tokens (
PROGRAM,VAR,BEGIN/END, arithmetic ops, assignment, literals, identifiers) - Recursive-descent parser that builds an AST
- AST node model in
nodes.py - Visitor-based execution (
NodeVisitor+Interpreter) - Semantic pass (
SemanticAnalyser) with symbol table population, duplicate declaration checks, undeclared variable checks, assignment compatibility checks, and numeric operator checks - Script runner supports startup file arguments and interactive
:run <path>execution
src/main.py: entry point and script command loopsrc/Lexer.py: lexical analysissrc/Parser.py: AST construction from tokenssrc/nodes.py: AST node classessrc/interpreter.py: base visitor and runtime interpretersrc/SemanticAnalyser.py: semantic checks + symbol table populationsrc/tokens.py: token constants, token class, symbol classes/table, and custom exceptionsgrammar.txt: grammar notesinstructions/instructions.txt: sample input programinstructions/a.pas: additional sample script
program : PROGRAM variable SEMI block DOT
block : declarations compound_statement
declarations : VAR (variable_declaration SEMI)+
| empty
variable_declaration : ID (COMMA ID)* COLON type_spec
type_spec : INTEGER | REAL
compound_statement : BEGIN statement_list END
statement_list : statement
| statement SEMI statement_list
statement : compound_statement
| assignment_statement
| empty
assignment_statement : variable ASSIGN expr
empty :
expr : term ((PLUS | MINUS) term)*
term : factor ((MUL | INTEGER_DIV | FLOAT_DIV) factor)*
factor : PLUS factor
| MINUS factor
| INTEGER_CONST
| REAL_CONST
| LPAREN expr RPAREN
| variable
variable : ID
Note: standalone expression statements like 1+1 are not valid in this grammar.
- Use Python 3.
- From the project folder:
python src/main.pyBehavior:
- Pass a script path as an argument to run immediately:
python src/main.py instructions/instructions.txt- Or start interactive mode and run scripts by path:
script> :run instructions/instructions.txt
- You can also type a path directly at the prompt:
script> instructions/a.pas
- Type
:qto quit
- Variables must be declared before use
- Duplicate variable declarations are rejected
DIVrequiresINTEGERoperands/(FLOAT_DIV) yieldsREAL+,-,*yieldINTEGERonly when both operands areINTEGER, otherwiseREAL- Assignments allow exact type match and widening
INTEGER -> REAL
The project currently uses:
LexerErrorParserErrorInterpreterError(also used by semantic analysis at the moment)
- No procedures/functions yet
- No nested scopes yet
- No booleans/relational operators yet
- Semantic errors are not split into a dedicated
SemanticErrortype yet