1
0
Fork
You've already forked Evil
0
So how will you fix it?
  • Python 100%
Find a file
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>
2026年06月22日 04:24:32 +08:00
evilang Initial commit: EvilLang interpreter 2026年06月22日 04:24:32 +08:00
tests Initial commit: EvilLang interpreter 2026年06月22日 04:24:32 +08:00
.gitignore Initial commit: EvilLang interpreter 2026年06月22日 04:24:32 +08:00
pyproject.toml Initial commit: EvilLang interpreter 2026年06月22日 04:24:32 +08:00
README.md Initial commit: EvilLang interpreter 2026年06月22日 04:24:32 +08:00

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:

  1. Never delete code — add a V3
  2. Comments describe intent, not behavior
  3. If a test fails, the test and the code might both be wrong
  4. Naming conventions vary by module (this is historical, not a bug)

License

Unlicensed. You shouldn't use this for anything important.