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.
Metadata
Release files for genlang 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| genlang-0.0.2.tar.gz | 12.6 kB | Without provenance | Details for genlang-0.0.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| genlang-0.0.2-py3-none-any.whl | Python 3 | none | any | Details for genlang-0.0.2-py3-none-any.whl |
Total release size: 22.8 kB
Release files / genlang-0.0.2.tar.gz
| Download URL | genlang-0.0.2.tar.gz |
|---|---|
| Size | 12.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d6d227429df489e62b02589095e120aac4e86389afb7aca4a33573091f3b3f0f
|
|
BLAKE2b-256 checksum How to use checksums |
8a07bdaada7b063101863962c166734f7945b77052dd10a2bc4987350827cf33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.4
|
Release files / genlang-0.0.2-py3-none-any.whl
| Download URL | genlang-0.0.2-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36daa30ae95cfba92dfb51aa96f8873443718063fd04088b0baa65c4b0e0cf99
|
|
BLAKE2b-256 checksum How to use checksums |
772c8db0745ecb2c20d06909142830bda9505e7e1742c2cbe84c7d1e34c9e460
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.4
|
Security for genlang 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.