Genlang — ultra-strict symbolic/reactive dynamic language
Project description
Genlang
Genlang is a compact experimental programming language implemented as a small Python library. It is designed for teaching language implementation techniques, rapid prototyping of domain specific languages, and for fun. The implementation provides a lexer, parser, AST interpreter and a tiny runtime with dynamic variable dependencies.
This README documents language syntax, examples, and the Python API you can import into other projects.
Key ideas
- Simple, concise syntax designed to be easy to parse and reason about.
- Declarations and assignments use a
$-based variable syntax. - Dynamic expressions, declared once, automatically update when their dependencies change.
- Lightweight function definitions and calls, with expression-based return values.
- Minimal, explicit control flow: conditionals, loops, and input/print primitives.
Installation
Genlang is delivered as a single Python module. To experiment with it install via pip:
pip install genlang
Then run:
genlang run <file.gen>
That's it!
No external dependencies are required beyond the Python standard library. Notice that Genlang files has a file format of <.GEN>.
Quick start
Save your Genlang source into a string and run it from Python:
from genlang import run_genlang
source = """
:$a = 10;
:$b = $.a + 5;
! $.b;
$a = 20;
! $.b;
"""
interp = run_genlang(source)
# `run_genlang` returns the interpreter instance. You can inspect `interp.vars` after execution.
print(interp.vars)
This prints the value of b twice, demonstrating dynamic declarations. The declaration :$b = $.a + 5; makes b depend on the dynamic reference $.a so when $a changes, $b is updated automatically.
Language overview
Tokens and punctuation
- Numbers:
123or12.34 - Strings: single or double quoted with backslash escapes
- Identifiers: e.g.
foo,bar_2
Basic statements
- Declare a variable, without initializing:
:$name; - Declare and assign:
:$name = <expr>; - Assign to an already-declared variable:
$name = <expr>; - Evaluate an expression for side effects:
<expr>; - Print:
! <expr>;(prints to stdout) - Read input into a variable:
?$<name>;(reads from provided input orinput()when real_input=True)
Notes:
- Every statement must end with a semicolon
;. - Variable names used in assignment are always prefixed with
$when referenced on the left side. Plain identifiers (without$) are used for local function parameter names and ordinary identifiers in expressions.
Dynamic references
- Use
$.nameto mark a dynamic dependency to variablenameinside a declaration expression. If a declaration expression contains any dynamic references, the declared variable becomes "dynamic", and will be kept up to date whenever its dependencies change.
Example:
:$x = 2;
:$y = $.x * 3; // y is dynamic, depends on x
! $.y; // prints 6
$x = 5;
! $.y; // now prints 15, because y was dynamic
Conditionals
- If with optional else block uses
??followed by(condition)and blocks in braces{}. An optional else block follows a comma and another brace enclosed block.
?? ($a > 10) { ! 'big'; } , { ! 'small'; };
Loops
- Loop uses
*followed by(condition)and a{}-block body. The loop repeats while the condition is true.
* ($n > 0) { ! $n; $n = $n - 1; };
Functions
- Declared with
@name(params) { ... };. Parameters can be either plain identifiers or$-prefixed names when you want to accept variables by name. - A function body may contain declarations and statements. A function returns the last expression statement evaluated in its body (if any).
Example:
@add($a, $b) {
:$s = $a + $b;
s; // expression evaluates to the value returned by the function
};
! @add(3, 4);
Expressions and operators
- Arithmetic:
+ - * / % - Comparison:
== != < > <= >= - Logical:
&(and),|(or),~(not) - Unary minus supported
- String concatenation supported via
+when either operand is a string
Precedence follows a standard ordering from logical OR down to multiplication and unary operators.
Examples
Hello world
! 'Hello, world!';
Arithmetic and assignment
:$x = 10;
:$y = $.x * 2;
! $.y;
$x = 7;
! $.y;
Input and condition
?$name;
?? ($name == 'Alice') { ! 'Hi Alice'; } , { ! 'Who are you?'; };
Looping countdown
:$n = 5;
* ($n > 0) { ! $n; $n = $n - 1; };
Function example
@sum($a, $b) {
:$r = $a + $b;
r;
};
! @sum(10, 20);
Python API reference
You can import the module into Python and run Genlang programs with a few helper functions and classes.
lex(source: str) -> List[Token]
Tokenizes source into Token objects. Useful for debugging or tooling.
Parser(tokens, source)
Parses tokens into an AST you can inspect for tooling or transformations. Raises ParseError for syntax problems.
Interpreter(source, input_values=None, real_input=False)
Creates an interpreter instance. Important fields and behavior:
interp.varsstores runtime variables by nameinterp.dynamic_exprsstores any declared dynamic expressionsinterp.funcsstores declared functions- Call
interp.eval_program(ast)to run a parsed AST from the parser
Runtime exceptions raised are RuntimeErrorWithPos which include a line and column when available.
run_genlang(source: str, input_values: Optional[List[str]] = None, real_input: bool = False)
Convenience runner that lexes, parses, and executes source. Returns the interpreter instance after execution. Example:
from genlang import run_genlang
source = """
?$name;
! 'You entered: ' + $name;
"""
interp = run_genlang(source, input_values=['42'])
Error handling
- Syntax errors during parsing raise
ParseErrorwith a descriptive message and a line/column position. - Runtime problems raise
RuntimeErrorWithPos, which attempts to include a line/column for easier debugging, e.g. division by zero, undeclared variable, or cycle in dynamic dependencies.
Implementation notes
- Dynamic declarations: when a declaration expression contains
$.namereferences, the declared variable is stored as a dynamic expression. When a dependency variable changes, dependent dynamic values are recomputed and changes propagate. A cycle in dependencies will raise a runtime error. - Functions are simple. When a function executes, a snapshot of the caller environment is saved and restored after the call. Functions return the value of the last expression statement if present.
- The interpreter performs minimal type coercion. Numeric operations require numeric operands, except
+supports string concatenation when either operand is a string.
Contributing
Contributions are welcome. A few ideas:
- Add more builtin functions and types
- Extend the standard library with collections or I/O primitives
- Add static analysis tooling or an LSP server for editor support
- Implement a bytecode compiler or REPL
If you make a change, please add tests and keep the module self-contained.
License
This project is released under the MIT license. You may copy and adapt the code freely.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file genlang-0.0.2.tar.gz.
File metadata
- Download URL: genlang-0.0.2.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6d227429df489e62b02589095e120aac4e86389afb7aca4a33573091f3b3f0f
|
|
| MD5 |
760ec7de028918b714e5edccff929478
|
|
| BLAKE2b-256 |
8a07bdaada7b063101863962c166734f7945b77052dd10a2bc4987350827cf33
|
File details
Details for the file genlang-0.0.2-py3-none-any.whl.
File metadata
- Download URL: genlang-0.0.2-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36daa30ae95cfba92dfb51aa96f8873443718063fd04088b0baa65c4b0e0cf99
|
|
| MD5 |
2f0130210230e7d919ef10b04ec663eb
|
|
| BLAKE2b-256 |
772c8db0745ecb2c20d06909142830bda9505e7e1742c2cbe84c7d1e34c9e460
|