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.

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 Context and SymbolTable for 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:

  1. Double Quotes: Standard strings.
  2. Triple Quotes: Multi-line strings that preserve formatting.
  3. 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 (TRUE or FALSE).
  • 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.