CO523 · Programming Languages · University of Peradeniya

C-Lite
Interpreter

A Python-based tree-walking interpreter for a simplified subset of C — demonstrating lexical analysis, recursive descent parsing, and semantic evaluation.

▶ Try Live Demo Explore Architecture GitHub Repo Project Report
Source Code
Lexer
Tokens
Parser
AST
Interpreter
Output

What C-Lite Supports

A carefully chosen subset of C that covers the essential constructs needed to demonstrate a complete interpreter pipeline.

🔢

Two Data Types

int (32-bit integer) and float (double-precision). Type coercion follows C semantics — floats assigned to int are truncated.

📐

Arithmetic & Precedence

Full + - * / support with correct operator precedence encoded structurally in the grammar. Parentheses override at any depth.

⚖️

Comparisons

Comparison operators > < == return integer 0 or 1 — exactly as C does — usable directly in conditional expressions.

🔀

If / Else Branching

Full if (expr) { } else { } with arbitrary nesting. Each block creates its own lexical scope with a parent-pointer chain.

🖨️

printf Output

Built-in printf(a, b, ...) accepts multiple comma-separated expressions and prints space-separated values to the output.

💬

Comments

Both single-line // ... and multi-line /* ... */ comments are silently consumed during lexical analysis.

🔍

Block Scoping

Variables declared inside if/else blocks are destroyed on exit. Inner scopes can read and modify outer variables.

🚨

Precise Error Reporting

Three distinct error types: LexerError, ParseError, and RuntimeError_ — each with line numbers and descriptive messages.

Three-Phase Architecture

Four independent Python modules, each with a single responsibility, communicating through clean interfaces.

01

Lexical Analyzer

src/lexer.py

Hand-written character-by-character scanner. Classifies tokens into keywords, identifiers, literals, operators, and symbols in a single linear pass with one character of lookahead.

02

AST Nodes

src/ast_nodes.py

Pure data containers defined as Python dataclasses. Immutable node types for every expression and statement kind in the language.

03

Recursive Descent Parser

src/parser.py

Each EBNF non-terminal maps directly to a method. LL(1) lookahead selects productions. Operator precedence encoded structurally — no precedence table needed.

04

Tree-Walking Interpreter

src/interpreter.py

Walks the AST recursively. Scoped symbol table implemented as a parent-pointer chain. Type coercion and C-compatible integer division at assignment time.

EBNF Grammar

The complete LL(1) grammar. Operator precedence is encoded through rule stratification — no disambiguation logic needed.

Statements

program     = { statement } EOF

statement   = var_decl
            | assignment
            | if_stmt
            | printf_stmt

var_decl    = type IDENT ';'
type        = 'int' | 'float'

assignment  = IDENT '=' expr ';'

if_stmt     = 'if' '(' expr ')'
              block [ 'else' block ]
block       = '{' { statement } '}'

printf_stmt = 'printf' '(' arg_list ')' ';'
arg_list    = expr { ',' expr }

Expressions — Precedence Chain

expr           = comparison
comparison     = addition
                 { ( '==' | '<' | '>' ) addition }
addition       = multiplication
                 { ( '+' | '-' ) multiplication }
multiplication = unary
                 { ( '*' | '/' ) unary }
unary          = '-' unary | primary
primary        = INT_LIT | FLOAT_LIT
               | IDENT | '(' expr ')'

Operator Precedence Table

Level Operators Assoc. Priority
1 == < > Left Lowest
2 + - Left
3 * / Left
4 unary - Right Highest

Lexical Tokens

INT_LIT   = digit { digit }
FLOAT_LIT = digit { digit } '.'
            digit { digit }
IDENT     = letter
            { letter | digit | '_' }

// Keywords
'int'  'float'  'if'
'else'  'printf'

// Comments (silently consumed)
// single-line
/* multi-line */

Type System Notes

// float → int : truncated (C-style)
int x;  x = 3.9;  // x = 3

// int → float : widened
float f; f = 2;    // f = 2.0

// int/int → floor division
7 / 2  // → 3

// float/float → true division
7.0 / 2.0  // → 3.5

Try the Interpreter

Your C-Lite code runs entirely in the browser using Pyodide — the real Python interpreter compiled to WebAssembly. No server involved.

clite-playground.c
Editor C-Lite
Output
// Click Run or press Ctrl+Enter to execute

15 Built-in Tests · 92 pytest Tests

All tests pass. The built-in suite covers every language feature; the pytest suite adds unit-level coverage per module.

T01

Integer declaration & assignment

int type, declaration, assignment

✓ PASS
T02

Float declaration & assignment

float type, float literals

✓ PASS
T03

Arithmetic operations

Operator precedence (* before +)

✓ PASS
T04

Integer division

int / int = floor division

✓ PASS
T05

Float division

float / float = true division

✓ PASS
T06

if — true branch

if with true condition

✓ PASS
T07

if-else — false branch

if-else with false condition

✓ PASS
T08

Equality comparison

== operator

✓ PASS
T09

Nested if-else

Multi-level conditional branching

✓ PASS
T10

Multiple printf args

printf with comma-separated args

✓ PASS
T11

Parenthesised expression

Grouping with parentheses

✓ PASS
T12

Unary minus

Negative literals via unary -

✓ PASS
T13

Undeclared variable

Semantic error detection

✓ PASS
T14

Use before assignment

Semantic error detection

✓ PASS
T15

Float + int type widening

Mixed-type arithmetic expression

✓ PASS