BioLang

A small interpreted language where the keywords are biology. An organism holds organs, the nucleus is where it starts, and a fossil can never be changed.

v1.0.0 C# / .NET 10 117 tests self-hosting: lexer + parser MIT tree-walking interpreter

The whole language, in one program

organism FibonacciSystem {

    organ computeGen(dna generations) -> colony {
        cell colony sequence = [0, 1]
        replicate (sequence.length < generations) {
            dna nextGen = sequence[-1] + sequence[-2]
            sequence.inject(nextGen)
        }
        return sequence
    }

    nucleus() {
        fossil dna target = 10
        fossil enzyme isReady = active

        mutate (isReady) {
            cell colony result = computeGen(target)
            secrete("Generation Complete:")
            secrete(result)
        } adapt {
            secrete("System is dormant.")
        }
    }
}
$ biolang fibonacci.bio
Generation Complete:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Keywords

organismthe module. one per file.
nucleusentry point. runs first.
organa function.
cella variable you can change.
fossila constant. enforced, not suggested.
mutateif.
adaptelse. adapt mutate (...) chains.
replicatewhile.
traversecounted for. (init; cond; step)
severbreak out of a loop.
skipcontinue to the next iteration.
graftpull in another .bio file.
secreteprint.
absorbread a line of input.
activetrue.
dormantfalse.
rnastring.
dnanumber.
enzymeboolean.
membranea struct: fields + methods
traita promise about behaviour
witnessesa membrane keeping it
sporean anonymous function
colonyarray. 17 methods

What it does, and what it does not

Working, and under test

  • Lexer, parser, AST, tree-walking interpreter
  • Functions with typed parameters and returns
  • mutate/adapt, replicate, traverse, sever, skip
  • All four types, checked at every boundary
  • fossil refuses reassign and mutation
  • Negative indexing: -1 is the last element
  • Modules via graft, with cycle detection
  • Self-hosting: the lexer and parser are written in BioLang, byte-identical to the C# reference
  • Structs (membrane) with methods and field mutation
  • Traits: one organ accepts any membrane that witnesses it
  • Closures: organs are values, spore is a lambda
  • Input via absorb; 17 array methods
  • Errors carry line and column

Not there yet

  • It is an interpreter, not a compiler — no bytecode, no JIT, no optimiser
  • No generics — traits cover the polymorphism that was missing
  • Traits cannot require other traits
  • No generics, no user-defined operators, no namespaces
  • One nucleus per organism; grafted files may not declare their own
  • Loops are capped at 1,000,000 iterations — raise with --max-loop N

The project states its scope in the source, at the point each limit applies, rather than in a marketing line.

Self-hosting

The lexer is also written in BioLang. It reads a .bio source and emits the same token stream the C# lexer does — checked by byte-equality, not by hand-written expectations.

PASS  fibonacci.bio          100 tokens identical
PASS  lexer.bio             1943 tokens identical

PASS  fibonacci.bio         2994 bytes identical
PASS  lexlib.bio           53224 bytes identical
PASS  parser.bio          286200 bytes identical

The second line is the interesting one: the self-hosted lexer lexing its own 266-line source, through the same escape handling, comments and token types. Three bugs came out of that comparison — including an escape that turned into the letter r, which silently made every identifier starting with r disappear.

The parser parses its own 1,100-line source and produces a tree identical to the C# one, all 286 KB of it. Byte-equality is the only test that catches a column drifting by one or a precedence level collapsing - and four of those happened while this was being written.

Stage two of four. The lexer and the parser are done. The evaluator and the bootstrap are not.

Design notes

The published specification had three defects. Each is fixed in the interpreter and documented where it lives:

// 1. nucleus() was unreachable
   MainDecl was defined but never referenced from Program,
   so the grammar could not parse its own example program.

// 2. no Expression production existed
   The grammar used Expression in seven places and defined it
   in none. Precedence is now: comparison > term > factor > unary.

// 3. the grammar rejected the spec's own example
   VarDeclaration requires 'cell' or 'fossil', but the example
   writes `dna nextGen = ...` bare. A bare typed declaration is
   now legal and means the same as 'cell'.

Semantic decisions the spec left open — how dna prints, what a condition coerces to, whether fossil blocks mutation as well as reassignment — are written down in the interpreter header with the reasoning.

Build and run

# needs the .NET SDK
git clone https://github.com/athallajovian9-cyber/BioLang
cd BioLang/src
dotnet build

# run a program
./bin/Debug/net10.0/biolang ../tests/fibonacci.bio

# debug modes
biolang --tokens file.bio   # dump the token stream
biolang --ast    file.bio   # parse, report the tree, do not run

# the test suite
cd .. && bash run_tests.sh   # 117 checks