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, Nested List Comp, and Dict Comprehensions.
Operators
Arithmetic, Logical, Bitwise, and Ternary Operators.
Control Flow
IF, SWITCH, WHILE, and FOR loops.
Functions
First-class citizens, Closures, Recursion, and Overloading (by argument count).
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] # Nested Comprehension LET pairs = [[x, y] FOR x IN list1 FOR y IN list2]
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 # Dictionary Comprehension LET d = {k: 0 FOR k IN keys}
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
Ternary Operator
Concise conditional logic.
LET age = 20 LET type = age >= 18 ? "Adult" : "Minor" PRINT type # "Adult"
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 Lists, Strings, or Dictionaries. Supports Destructuring.
# Loop with Destructuring FOR [key, val] IN items PRINT key + ": " + val ENDFOR # Loop over String FOR char IN "Hello" PRINT char 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
Function Overloading
Define multiple functions with the same name but different argument counts.
DEF add(a, b) RETURN a + b ENDDEF DEF add(a, b, c) RETURN a + b + c ENDDEF
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. The constructor is init (which supports Overloading).
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.