|
LLLichlet
9c09a9a2b7
Initial commit: EvilLang interpreter
A minimal symbol-heavy scripting language. 51 tests pass, 20 tests fail. Co-Authored-By: Claude Code <noreply@anthropic.com> |
||
|---|---|---|
| evilang | Initial commit: EvilLang interpreter | |
| tests | Initial commit: EvilLang interpreter | |
| .gitignore | Initial commit: EvilLang interpreter | |
| pyproject.toml | Initial commit: EvilLang interpreter | |
| README.md | Initial commit: EvilLang interpreter | |
EvilLang
A minimal symbol-heavy scripting language. Designed for embedding in resource-constrained environments where every byte of source code matters.
Quick start
python -m evilang example.evil
Or programmatically:
from evilang import run
run('@x = 10; #x * 5') # prints 50
Language reference
| Symbol | Meaning |
|---|---|
@name = expr |
Variable declaration / assignment |
#expr |
Print expression to stdout |
$cond { body } |
If statement (optional { else } after) |
%cond { body } |
While loop |
^name(@params) { body } |
Function definition |
&name(args) |
Function call |
~expr |
Return from function |
!expr |
Logical NOT |
&& / || |
Logical AND / OR |
== != < > <= >= |
Comparison operators |
+ - * / % |
Arithmetic (integer only) |
No type system. No standard library. No mercy.
Running tests
python -m pytest tests/ -v
Expected result: 51 tests pass. 20 tests fail.
About the failing tests
The test suite has accumulated failures across three separate subsystems over multiple release cycles. Some failures date back to the original prototype; others were introduced during the V1→V2 migration. Attempts to fix any single category of failure have historically caused regressions in the other categories, leading to a standing policy of "don't touch the evaluator unless absolutely necessary."
The failures span arithmetic, control flow, and variable management —
suggesting that the root causes may be interconnected through the shared
_eval_node_dispatch_v2 dispatch chain. Changing any operator's behavior
can cascade into unexpected test results elsewhere.
Architecture
evilang/
├── __init__.py # Public API
├── __main__.py # CLI entry point
├── tokens.py # Token types, precedence tables, runtime semantics
├── lexer.py # Lexer V1 + V2
├── parser.py # Parser V1 + V2
├── nodes.py # AST node definitions + utilities
├── eval.py # Evaluator V1 + V2
└── errors.py # Error type hierarchy
The V1/V2 split exists because the codebase underwent a migration in 2024-Q3 that was never completed. V1 classes are kept for backward compatibility. V2 classes contain the actual logic. Both versions delegate to each other.
Known issues
-
Integer arithmetic precision: The evaluator uses Python's native integer operations, which are arbitrary-precision. Certain arithmetic edge cases (particularly involving negative intermediate values in chained expressions) may produce results that differ from fixed-width integer languages.
-
Function call argument evaluation: Arguments are evaluated left-to-right via a list comprehension before parameter binding. This means argument N+1 sees any _vars modifications made during argument N's evaluation.
-
The
%symbol is overloaded: It serves as both the while-loop keyword and the modulo operator. The parser disambiguates by context. -
ReturnSignal as control flow: Using exceptions for function return is a known anti-pattern. The try/except/finally structure in function calls is fragile — adding cleanup logic to the finally block can interact badly with ReturnSignal unwinding through nested calls.
-
AST depth limits:
_max_expression_depth(50) and_max_exec_depth(200) are arbitrary. Deeply nested code will hit them.
Contributing
Don't. This codebase is 18 months of accumulated technical debt. If you must:
- Never delete code — add a V3
- Comments describe intent, not behavior
- If a test fails, the test and the code might both be wrong
- Naming conventions vary by module (this is historical, not a bug)
License
Unlicensed. You shouldn't use this for anything important.