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, 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:

  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]

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 (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.