| applyTo | **/*.bpl, **/*.x, **/*.sh, **/*.ts, **/*.md |
|---|
You are working with BPL, a statically-typed, compiled programming language that transpiles to LLVM IR. Follow these instructions strictly when writing code, creating examples, running tests, or modifying the compiler.
- Declarations:
frame(function),struct,spec(interface/trait),enum,type(alias),local,global,const. - Control Flow:
if,else,loop,break,continue,switch,case,default,return,defer. - Pattern Matching:
match. - Error Handling:
try,catch,throw. - Modules:
import,export,from,as. - Types & Values:
true,false,nullptr,void,int,float,bool,char,string,ushort,uint,ulong. - Operators:
cast,sizeof,is,as,offsetof,typeof.sizeof<T>()orsizeof(T)orsizeof(expr)offsetof(Type, member)typeof<T>()ortypeof(expr)
- FFI & Low Level:
extern,asm.
int(i32),uint(u32)u8,u16,u32,u64(unsigned integers)i8,i16,i32,i64(signed integers)float(f64),f32,f64bool(true,false)char(u8)void(empty type)nullptr(null pointer)
- Pointers:
*T(e.g.,*int,**char) - Arrays:
T[](dynamic/slice) orT[N](fixed size, e.g.,int[5]) - Tuples:
(T1, T2)(e.g.,(int, bool)) - Function Types:
Func<Ret>(Arg1, Arg2) - Lambda Types: Lambda(Arg1, Arg2)
- Generics:
Struct<T>,Func<T> - String:
string(immutable string slice)
- Local:
local name: Type = value; - Global:
global NAME: Type = value; - Constants:
local const name: Type = value; - Destructuring:
local (x, y) = tuple;
frame add(a: int, b: int) ret int {
return a + b;
}
# Generic function
frame identity<T>(val: T) ret T {
return val;
}
**Note**: Functions (`Func<T>`) are raw function pointers (compatible with C ABI). Lambdas (`Lambda<T>`) are fat pointers (struct `{ func*, ctx* }`). You can cast a `Func` to a `Lambda` (wrapping it), but casting a `Lambda` to a `Func` is forbidden.struct Point {
x: int,
y: int,
# Method
frame distance(this: *Point) ret float {
return sqrt(cast<float>(this.x * this.x + this.y * this.y));
}
}
**Layout Note**: Structs defined without methods (and without invalidating inheritance) are "POD" (Plain Old Data) and match C memory layout exactly. Structs with methods (or inheriting from classes with methods) implicitly inherit from `Type` and carry a hidden compilation-generated vtable pointer as their first field.
# Inheritance
struct Dog : Animal { ... }
### Bound Methods
Methods can be used as values, creating a `Lambda` that captures the object instance (`this`).
```bpl
struct Counter {
count: int,
frame increment(this: *Counter) { this.count = this.count + 1; }
}
frame main() {
local c = Counter { count: 0 };
# 'inc' is a Lambda<void> that captures 'c'
local inc = c.increment;
inc();
}enum Option<T> {
Some(T),
None,
}
# Pattern Matching
match (opt) {
Option.Some(val) => printf("%d", val),
Option.None => printf("None"),
}BPL supports comprehensive pattern matching with the match expression:
# Primitive patterns (int, float, bool, string, char)
match (x) {
0 => printf("Zero\n"),
42 => printf("The answer!\n"),
n if n < 0 => printf("Negative: %d\n", n),
_ => printf("Other\n"),
}
# Tuple patterns
match (point) {
(0, 0) => printf("Origin\n"),
(0, y) => printf("Y-axis at %d\n", y),
(x, 0) => printf("X-axis at %d\n", x),
(x, y) if x == y => printf("Diagonal\n"),
(x, y) => printf("Point (%d, %d)\n", x, y),
}
# Enum patterns with destructuring
enum Result<T, E> { Ok(T), Err(E) }
match (result) {
Result.Ok(val) => printf("Success: %d\n", val),
Result.Err(err) => printf("Error: %d\n", err),
}Pattern Types:
- Literals:
0,3.14,true,"hello",'A' - Identifiers:
x,n(binds matched value) - Tuples:
(a, b),(0, y),(x, y, z) - Wildcards:
_(matches anything) - Guards:
pattern if condition
- If/Else:
if (cond) { ... } else { ... } - Loop:
loop (cond) { ... }(while loop)loop { ... }(infinite)loop (init; cond; step) { ... }(C-style for loop)
- Switch:
switch (val) { case 1: ... break; default: ... break; }(Strict termination required) - Match:
match (val) { Pattern => ... } - Defer:
defer { ... }(executes on scope exit or throw)
try {
throw 1;
} catch (e: int) {
printf("Error: %d", e);
} catch {
printf("Unknown error");
}import printf from "libc";
import [Point] from "./geometry.bpl";
import exec from "std/process.bpl";
export frame myFunc() { ... }# Intel Syntax
asm("intel") {
mov eax, (a) # Input (default "r")
add eax, (b: "r") # Input with explicit constraint
mov (=result), eax # Output (default "=r")
mov (=out: "=a"), ebx # Output with explicit constraint
[ "ebx" ] # Clobbers
}
# AT&T Syntax
asm("att") {
movl (a), %eax
movl %eax, (=result)
}
# Raw LLVM IR
asm("llvm") {
"%ptr = getelementptr i32, i32* (a), i32 0"
}- Bun: The project uses
bunfor runtime, testing, and building. - LLVM:
llcandclangmust be available for compilation.
- Build Compiler:
bun run build-> Createsbplbinary. - Run Compiler:
bun index.ts <args> - Type Check:
bun run check-> Runstscto verify TypeScript code. MUST RUN before finishing a task. - Run Tests:
bun test-> Runs all tests. - Format Code:
bun run format-> Formats TS files. - Format Examples:
bun run format-examples-> Formats BPL files inexamples/.
- Implement Feature: Modify
compiler/source code. - Type Check: Run
bun run checkto ensure no TypeScript errors. - Unit Test: Create/Update
tests/*.test.tsand runbun test tests/MyTest.test.ts. - Integration Test: Create an example in
examples/and runbun test tests/Integration.test.ts. - Verify All: Run
bun testto ensure no regressions. - Documentation: Update
docs/andAGENTS.MDif the feature changes syntax or behavior.
-
Located in
tests/. -
Use
bun:test. -
Test individual compiler components (Lexer, Parser, TypeChecker, CodeGen).
-
Example:
import { describe, expect, it } from "bun:test"; import { lexWithGrammar } from "../compiler/frontend/GrammarLexer"; describe("Lexer", () => { it("should lex integers", () => { const tokens = lexWithGrammar("123"); expect(tokens[0].type).toBe("Number"); }); });
- Runs full compilation and execution of BPL programs.
- Uses
examples/directory. - Configuration: Each example folder needs
test_config.json.{ "expectedOutput": ["Hello World"], "args": [], "env": {}, "input": "" }
- Generates random token streams to test compiler stability.
- Run with
bun test tests/fuzz.test.ts.
- Strict Typing: No
anyunless absolutely necessary. - Immutability: Prefer
readonlyarrays and properties. - Error Handling: Use
CompilerErrorclass with source location. - Formatting: Prettier default settings.
- Structs: PascalCase (
MyStruct). - Functions/Variables: camelCase (
myFunction,myVar). - Constants: SCREAMING_SNAKE_CASE (
MAX_VALUE). - Files: snake_case (
my_module.bpl).
Before marking a task as complete:
- Code Implemented: Feature works as requested.
- Type Check Passed:
bun run checkreturns no errors. - Tests Added: Unit tests for edge cases, Integration test for end-to-end.
- Formatting: Code is formatted (
bun run format). - Tests Passed:
bun testpasses (green). - Docs Updated:
docs/reflects changes. - AGENTS.MD Updated: If syntax/rules changed, update this file.
- Bugs Logged: If a bug is found, it MUST be logged in
BUGS.md. Do not hide or ignore it. Create a workaround if necessary, but document the issue.
- Parser Issues: If
grammar/bpl.peggyis modified, the parser is auto-generated at runtime (or cached). Ensure grammar logic is correct. - LLVM IR: Use
--emit llvmto inspect generated IR.bun index.ts examples/my_test/main.bpl --emit llvm cat examples/my_test/main.ll
- Segfaults: Check
malloc/freeusage in BPL code. Usevalgrindon the generated binary if needed. - Operator Precedence: Check
Formatter.tsandParser.ts(or grammar) to ensure consistency.
If you discover a bug during development:
- Do not ignore it.
- Log it in
BUGS.mdimmediately. - If you cannot fix it right away, create a workaround if necessary, but you must document the bug.
- Never hide a bug or leave it undocumented.
compiler/: Source code for the compiler.frontend/: Lexer, Parser (Peggy).middleend/: TypeChecker, PackageManager.backend/: CodeGenerator (LLVM), Formatter.common/: AST, Errors, Utils.
grammar/: Peggy grammar file (bpl.peggy).examples/: Integration test cases.tests/: Unit and integration test runners.docs/: Documentation files.lib/: Standard library (std).
Remember: You are an expert. Write clean, maintainable, and well-tested code.