Sugar is a modern, statically-typed, object-oriented scripting language designed for clarity and expressiveness. It combines familiar concepts from popular languages with a unique syntax to create a powerful and developer-friendly experience.
- Static Typing: All variables and functions have explicit types, catching errors before runtime.
- Object-Oriented: Supports classes, inheritance, interfaces, and access modifiers.
- Rich Data Structures: Built-in support for arrays, maps, and tuples with a wealth of operations.
- Powerful Control Flow: Includes
if/elif/else,forloops,whileloops, and an advancedMATCHstatement. - Concurrency: Simple-to-use concurrency model with
SPAWNandJOIN. - Error Handling: Robust
TRY/CATCH/FINALLYmechanism with custom error types. - Modular: Support for importing code from other files.
- Expressive Syntax: A clean and readable syntax that aims to reduce boilerplate.
The project is organized as follows:
/
├── src/ # Interpreter source code (Python)
│ ├── sugar.py # Main entry point
│ ├── parser.py # Language parser
│ ├── interpreter.py # Language interpreter
│ ├── ast_nodes.py # Abstract Syntax Tree definitions
│ └── ...
├── examples/ # Example .sugar files
│ └── interpreting_tests/ # Suite of syntax examples
├── tests/ # Test suite for the interpreter
│ ├── test_parser.py
│ └── test_interpreter.py
├── pyproject.toml # Project dependencies
├── uv.lock # Pinned dependency versions
└── README.md # This file
This project uses uv for environment and package management.
- Install
uv: If you don't have it, follow the official installation instructions foruv. - Set up the environment: Run the following command in the project root. This will create a virtual environment and install the dependencies from
pyproject.toml.uv sync
To run a Sugar program, use the main sugar command followed by the path to the file:
sugar examples/interpreting_tests/01_var_decl.sugarFor development, you can also run the interpreter directly with uv:
uv run python src/sugar.py examples/interpreting_tests/01_var_decl.sugarVariables are declared with DEF, a name, a type, and an initial value. Re-assignment uses the := operator.
Primitive Types: #int, #float, #bool, #char, #str
DEF x #int = 10
DEF message #str = "Hello, Sugar!"
DEF is_active #bool = :T:
x := 20 // Re-assign value
Arrays are ordered collections.
DEF numbers #[#int] = [1, 2, 3, 4, 5]
numbers :ADD: (6)
DEF first #int = numbers :GET: (0)
DEF length #int = numbers :LENGTH: ()
Maps are key-value stores.
DEF scores #{#str, #int} = {"Alice" -> 100, "Bob" -> 95}
scores :SET: ("Charlie", 98)
DEF bobs_score #int = scores :GET: ("Bob")
Tuples are fixed-size, ordered collections.
DEF person #(#str, #int) = ("Alice", 30)
Boolean conditions in control flow are wrapped in $.
// If/Elif/Else
if $x > 10$ do
IO:PRINT:("x is large")
elif $x > 5$ do
IO:PRINT:("x is medium")
else do
IO:PRINT:("x is small")
end
// For Loop
DEF sum #int = 0
for DEF n #int in [1, 2, 3] do
sum := sum + n
end
// While Loop
DEF i #int = 0
while $i < 5$ do
i := i + 1
end
Functions are declared with FUNC. They require typed parameters and a return type (#void if nothing is returned).
FUNC add(a #int, b #int) #int
RETURN a + b
end
DEF result #int = add(5, 3) // result is 8
Sugar is fully object-oriented.
CLASS Adder
value #int
CONSTRUCTOR()
DEF THIS.value #int = 0
end
FUNC add(x #int) #int
THIS.value := THIS.value + x
RETURN THIS.value
end
end
DEF a #Adder = Adder()
a:add:(5)
a:add:(10) // a.value is now 15
It also supports EXTENDS for inheritance, INTERFACE and IMPLEMENTS for polymorphism, and STATIC methods.
The MATCH statement is a powerful way to control flow based on the structure of data.
DEF my_var #int = 2
DEF result #str = ""
MATCH my_var
CASE 1 do
result := "one"
CASE n if $n % 2 == 0$ do // Case with a guard
result := "an even number"
DEFAULT do
result := "something else"
end
Handle runtime errors with a TRY/CATCH/FINALLY block.
TYPE MyError EXTENDS Error
message #str
end
FUNC risky() #void
THROW MyError("fail!")
end
TRY
risky()
CATCH e #MyError do
IO:PRINT:("Caught error: " + e.message)
FINALLY do
IO:PRINT:("Execution finished.")
end
Run functions concurrently using SPAWN.
FUNC compute() #int
RETURN 42
end
DEF task #Task = SPAWN FUNC() #int RETURN compute() end
DEF result #int = task:JOIN:() // Waits for the task to finish and gets the result
Organize your code into multiple files and import them.
// In imported_module.sugar
DEF m #{#str, #int} = {"a"->5}
// In main.sugar
import imported_module.m
DEF r #int = m:GET:("a") // r is 5
Sugar has a standard library for common tasks.
IO:PRINT,INPUTMath:SQRT,POW,SIN,PI, etc.Time:NOW_STR,FORMAT_STRRandom:INT,FLOAT,SEED
The project has a suite of tests for the interpreter. To run all tests, use pytest:
uv run pytestTo run a specific test file:
uv run pytest tests/test_parser.pyContributions are welcome! Please feel free to open an issue on the project's repository to report bugs or suggest features.