GladLang
A dynamic, interpreted, object-oriented programming language
This is a full interpreter built from scratch in Python, complete with a lexer, parser, and runtime environment. It supports modern programming features like closures, classes, inheritance, and robust error handling.
Table of Contents
About The Language
GladLang is an interpreter for a custom scripting language. It was built as a complete system, demonstrating the core components of a programming language:
- Lexer: A tokenizer that scans source code and converts it into a stream of tokens (e.g.,
NUMBER,STRING,IDENTIFIER,KEYWORD). - Parser: A parser that takes the token stream and builds an Abstract Syntax Tree (AST), representing the code's structure.
- AST Nodes: A comprehensive set of nodes that define every syntactic structure in the language (e.g.,
BinOpNode,IfNode,FunDefNode,ClassNode). - Runtime: Defines the
ContextandSymbolTablefor managing variable scope, context (for tracebacks), and closures. - Values: Defines the language's internal data types (
Number,String,List,Function,Class,Instance). - Interpreter: The core engine that walks the AST. It uses a "Zero-Copy" architecture with Dependency Injection for high-performance execution and low memory overhead.
- Entry Point: The main file that ties everything together. It handles command-line arguments, runs files, and starts the interactive shell.
Key Features
GladLang supports a rich, modern feature set:
Data Types
Numbers, Strings, Lists, Dictionaries, Booleans, and Null.
Variables
Dynamic assignment with LET. Supports Destructuring.
String Power
Interpolation (Template Strings) and Multi-line strings.
List Manipulation
Indexing, Slicing, and Comprehensions.
Operators
Full set of arithmetic, comparison, and logical operators.
Control Flow
IF, SWITCH, WHILE, and FOR loops.
Functions
First-class citizens with support for named and anonymous functions, closures, and recursion.
Object-Oriented Programming
Classes, inheritance, polymorphism, Static Members, and Encapsulation (Public/Private/Protected).
Advanced Operators
Pre- and post-increment/decrement operators.
Advanced Math
Compound assignments (+=, *=), Power, and Modulo support.
List Manipulation
Index access, assignment, and concatenation.
Built-ins
PRINT, INPUT, STR, INT, FLOAT, and BOOL.
Error Handling
Robust, user-friendly runtime error reporting with full tracebacks.
Getting Started
1. Installation
Option A: Install via Pip (Recommended)
pip install gladlang
Option B: Install from Source
git clone --depth 1 https://github.com/gladw-in/gladlang.git cd gladlang pip install -e .
2. Usage
Interactive Shell:
gladlang
Running a Script:
gladlang "tests/test.glad"
3. Running Without Installation
If running from source without pip, use the helper script:
python run.py "tests/test.glad"
4. Building the Executable
Build a standalone executable using PyInstaller:
pip install pyinstaller pyinstaller run.py --paths src -F --name gladlang --icon=favicon.ico
This creates an executable in dist/.
Language Tour (Syntax Reference)
Here is a guide to the GladLang syntax, with examples from the tests/ directory.
1. Comments
Comments start with # and last for the entire line.
# This is a comment. LET a = 10 # This is an inline comment
2. Variables and Data Types
Variables & Constants
Use LET for mutable variables and FINAL for constants.
LET a = 10 LET b = "Hello" a = 20 # Allowed FINAL PI = 3.14 # PI = 3.15 # Error: Cannot reassign constant
Destructuring Assignment
You can unpack lists directly into variables.
LET point = [10, 20] LET [x, y] = point
Numbers
Numbers can be integers or floats. All standard arithmetic operations are supported.
LET math_result = (1 + 2) * 3 # 9 LET float_result = 10 / 4 # 2.5
Strings
Strings can be defined in three ways:
- Double Quotes: Standard strings.
- Triple Quotes: Multi-line strings that preserve formatting.
- Backticks: Template strings supporting interpolation.
# Standard LET s = "Hello\nWorld" # Multi-line LET menu = """ 1. Start 2. Settings 3. Exit """ # Interpolation (Template Strings) LET name = "User" PRINT `Welcome back, ${name}!` PRINT `5 + 10 = ${5 + 10}`
Lists & Slicing
Lists are ordered collections. You can access elements, slice them, or create new lists dynamically.
LET nums = [0, 1, 2, 3, 4, 5] # Slicing [start:end] PRINT nums[0:3] # [0, 1, 2] PRINT nums[3:] # [3, 4, 5] # List Comprehension LET squares = [n ** 2 FOR n IN nums] PRINT squares # [0, 1, 4, 9, 16, 25]
Dictionaries
Dictionaries are key-value pairs enclosed in {}. Keys must be Strings or Numbers.
LET person = { "name": "Glad", "age": 25, "is_admin": TRUE } PRINT person["name"] # Access: "Glad" LET person["age"] = 26 # Modify LET person["city"] = "NYC" # Add new key
Booleans
Booleans are TRUE and FALSE. They are the result of comparisons and logical operations.
LET t = TRUE LET f = FALSE PRINT t AND f # 0 (False) PRINT t OR f # 1 (True) PRINT NOT t # 0 (False)
Truthiness: 0, 0.0, "", NULL, and FALSE are "falsy." All other values (including non-empty strings, non-zero numbers, lists, functions, and classes) are "truthy."
Null
The NULL keyword represents a null or "nothing" value. It is falsy and prints as 0. Functions with no RETURN statement implicitly return NULL.
3. Operators
Math Operations
Standard arithmetic plus Power (**), Floor Division (//), and Modulo (%).
Standard division / always returns a Float.
PRINT 2 ** 3 # Power: 8 PRINT 10 // 3 # Floor Division: 3 PRINT 10 % 3 # Modulo: 1 PRINT 100 / 2 # 50.0 (Float)
Compound Assignments
Update variables in place using syntactic sugar.
LET score = 10 score += 5 # 15 score *= 2 # 30 score -= 5 # 25
Bitwise Operators
Perform binary manipulation on integers.
LET a = 5 # 101 LET b = 3 # 011 PRINT a & b # 1 (AND) PRINT a | b # 7 (OR) PRINT a ^ b # 6 (XOR) PRINT ~a # -6 (NOT) PRINT 1 << 2 # 4 (Left Shift)
Comparisons & Logic
Compare values, chain comparisons, and check identity.
# Chained Comparisons IF 18 <= age < 30 THEN PRINT "Young Adult" ENDIF # Identity Check ('is') LET a = [1, 2] LET b = a PRINT b is a # True # Flexible Logic IF a and b THEN PRINT "Both exist" ENDIF
Increment / Decrement
Supports C-style pre- and post-increment/decrement.
LET i = 5 PRINT i++ # 5 PRINT ++i # 7
4. Control Flow
IF / ELSE IF / ELSE
Supports complex conditional logic.
LET score = 85 IF score > 90 THEN PRINT "Excellent" ELSE IF score > 50 THEN PRINT "Pass" ELSE PRINT "Fail" ENDIF
Switch Statements
Use SWITCH for multi-way branching. It supports values, lists, and expressions.
LET status = 200 SWITCH status CASE 200: PRINT "OK" CASE 404, 500: PRINT "Error" DEFAULT: PRINT "Unknown" ENDSWITCH
WHILE Loops
Loops while a condition is TRUE.
LET i = 3 WHILE i > 0 PRINT "i = " + i LET i = i - 1 ENDWHILE # Prints: # i = 3 # i = 2 # i = 1
FOR Loops
Iterates over the elements of a list.
LET my_list = ["apple", "banana", "cherry"] FOR item IN my_list PRINT "Item: " + item ENDFOR
BREAK and CONTINUE are supported in both WHILE and FOR loops.
5. Functions
Named Functions
Defined with DEF...ENDDEF. Arguments are passed by value. RETURN sends a value back.
DEF add(a, b) RETURN a + b ENDDEF LET sum = add(10, 5) PRINT sum # 15
Anonymous Functions
Functions can be defined without a name, perfect for assigning to variables.
LET double = DEF(x) RETURN x * 2 ENDDEF PRINT double(5) # 10
Closures
Functions capture variables from their parent scope.
DEF create_greeter(greeting) DEF greeter_func(name) # 'greeting' is "closed over" from the parent RETURN greeting + ", " + name + "!" ENDDEF RETURN greeter_func ENDDEF LET say_hello = create_greeter("Hello") PRINT say_hello("Alex") # "Hello, Alex!"
Recursion
Functions can call themselves.
DEF fib(n) IF n <= 1 THEN RETURN n ENDIF RETURN fib(n - 1) + fib(n - 2) ENDDEF PRINT fib(7) # 13
6. Object-Oriented Programming (OOP)
Classes and Instantiation
Use CLASS...ENDCLASS to define classes and NEW to create instances. The constructor is init.
CLASS Counter DEF init(SELF) SELF.count = 0 # 'SELF' is the instance ENDDEF DEF increment(SELF) SELF.count = SELF.count + 1 ENDDEF DEF get_count(SELF) RETURN SELF.count ENDDEF ENDCLASS
The SELF Keyword
SELF is the mandatory first argument for all methods and is used to access instance attributes and methods.
LET c = NEW Counter() c.increment() PRINT c.get_count() # 1
Inheritance
Use the INHERITS keyword. Methods can be overridden by the child class.
CLASS Pet DEF init(SELF, name) SELF.name = name ENDDEF DEF speak(SELF) PRINT SELF.name + " makes a generic pet sound." ENDDEF ENDCLASS CLASS Dog INHERITS Pet # Override the 'speak' method DEF speak(SELF) PRINT SELF.name + " says: Woof!" ENDDEF ENDCLASS LET my_dog = NEW Dog("Buddy") my_dog.speak() # "Buddy says: Woof!"
Calling Parent Methods
To call a parent method, call the Class method directly and pass SELF.
CLASS Child INHERITS Parent DEF init(SELF, name) # Call Parent constructor manually Parent.init(SELF, name) ENDDEF ENDCLASS
Polymorphism
When a base class method calls another method on SELF, it will correctly use the child's overridden version.
CLASS Pet DEF init(SELF, name) SELF.name = name ENDDEF DEF introduce(SELF) PRINT "I am a pet and I say:" SELF.speak() ENDDEF DEF speak(SELF) PRINT "(Generic pet sound)" ENDDEF ENDCLASS CLASS Cat INHERITS Pet DEF speak(SELF) PRINT "Meow!" ENDDEF ENDCLASS LET my_cat = NEW Cat("Whiskers") my_cat.introduce() # Prints: # I am a pet and I say: # Meow!
Access Modifiers
Control visibility with PUBLIC, PRIVATE, and PROTECTED. Private members are name-mangled for safety.
CLASS Secure DEF init(SELF) PRIVATE SELF.secret = "Hidden" ENDDEF PUBLIC DEF get_secret(SELF) RETURN SELF.secret ENDDEF ENDCLASS
Static Members
GladLang supports Java-style static fields and methods using STATIC.
CLASS Config STATIC FINAL MAX_USERS = 100 STATIC PUBLIC DEF get_max() RETURN Config.MAX_USERS ENDDEF ENDCLASS PRINT Config.get_max() # 100
7. Built-in Functions
PRINT(value): Prints a value to the console.INPUT(): Reads a line of text from the user as a String.STR(value): Casts a value to a String.INT(value): Casts a String or Float to an Integer.FLOAT(value): Casts a String or Integer to a Float.BOOL(value): Casts a value to its Boolean representation (TRUEorFALSE).LEN(value): Returns the length of a String, List, Dict, or Number. Alias:LENGTH().
Error Handling
GladLang provides robust error handling using TRY, CATCH, FINALLY, and THROW.
Try / Catch Block
TRY LET x = 10 / 0 CATCH error PRINT "Caught error: " + error FINALLY PRINT "Cleanup" ENDTRY
Manual Throw
IF age < 0 THEN THROW "Age cannot be negative" ENDIF
Runtime Tracebacks
When errors are not caught, GladLang prints full tracebacks:
Example: Name Error (test_name_error.glad)
Traceback (most recent call last): File test_name_error.glad, line 6, in <program> Runtime Error: 'b' is not defined
Example: Type Error (test_type_error.glad with input "5")
Traceback (most recent call last): File test_type_error.glad, line 6, in <program> Runtime Error: Illegal operation
Example: Argument Error (test_arg_error.glad)
Traceback (most recent call last): File test_arg_error.glad, line 7, in <program> File test_arg_error.glad, line 4, in add Runtime Error: Incorrect argument count for 'add'. Expected 2, got 3
Running Tests
The tests/ directory contains a comprehensive suite of .glad files to test every feature of the language. You can run any test by executing it with the interpreter:
gladlang "test_closures.glad" gladlang "test_lists.glad" gladlang "test_polymorphism.glad"
License
You can use this under the MIT License. See LICENSE for more details.