Compiler API
The Runar compiler can be invoked programmatically in addition to the CLI. This page documents the public API for integrating the compiler into custom build pipelines, editor tooling, and automated workflows. The compiler is distributed as the runar-compiler npm package.
Importing the Compiler Module
import {
compile,
parse,
parseSolSource,
parseMoveSource,
parsePythonSource,
parseGoSource,
parseRustSource,
validate,
typecheck,
lowerToANF,
} from 'runar-compiler';
All functions are synchronous and pure — they take input, produce output, and have no side effects. They do not read from the filesystem or network. If you need to compile a file, read it first and pass the source string.
compile()
The primary entry point. Takes a source string and returns a compiled artifact.
Signature
function compile(source: string, options?: CompileOptions): CompileResult;
Parameters
source: string — The Runar contract source code. Can be TypeScript, Solidity, Move, Python, Go, or Rust syntax. The language is auto-detected from the fileName option’s file extension.
options?: CompileOptions — Optional configuration:
interface CompileOptions {
// Source file name for error messages and parser dispatch.
// The file extension determines which parser is used:
// .runar.ts → TypeScript, .runar.sol → Solidity, .runar.move → Move,
// .runar.py → Python, .runar.go → Go, .runar.rs → Rust.
// Defaults to "contract.ts".
fileName?: string;
// If true, stop after the parse pass (Pass 1).
parseOnly?: boolean;
// If true, stop after the validate pass (Pass 2).
validateOnly?: boolean;
// If true, stop after the type-check pass (Pass 3).
typecheckOnly?: boolean;
// Bake constructor argument values into the locking script,
// replacing OP_0 placeholders with actual values.
constructorArgs?: Record<string, bigint | boolean | string>;
// If true, skip the ANF constant folding pass.
// Constant folding is enabled by default.
disableConstantFolding?: boolean;
}
Return Value
interface CompileResult {
// True if there are no error-severity diagnostics.
success: boolean;
// The parsed contract AST (available after Pass 1).
contract: ContractNode | null;
// The ANF IR program (available after Pass 4, null if compilation stopped early or failed).
anf: ANFProgram | null;
// All diagnostics (errors and warnings) from all passes.
diagnostics: CompilerDiagnostic[];
// The compiled artifact (available if all 6 passes succeed).
artifact?: RunarArtifact;
// Hex-encoded Bitcoin Script (available if all 6 passes succeed).
scriptHex?: string;
// Human-readable ASM representation (available if all 6 passes succeed).
scriptAsm?: string;
}
Example
import { compile } from 'runar-compiler';
import { readFileSync } from 'node:fs';
const source = readFileSync('./contracts/P2PKH.runar.ts', 'utf8');
const result = compile(source, {
fileName: 'P2PKH.runar.ts',
});
if (result.success) {
console.log('Contract:', result.artifact.contractName);
console.log('Script hex:', result.scriptHex);
console.log('ASM:', result.scriptAsm);
} else {
for (const diag of result.diagnostics) {
console.error(`${diag.message}`);
}
}
parse()
Parses a Runar source string into a ContractNode AST. The parser is auto-dispatched based on the fileName extension (.runar.ts, .runar.sol, .runar.move, .runar.py, .runar.go, .runar.rs). Does not perform validation or type checking.
Signature
function parse(source: string, fileName?: string): ParseResult;
Parameters
source: string — The Runar contract source code in any supported language.
fileName?: string — File name used for error messages and to determine which parser to use. Defaults to "contract.ts".
Return Value
interface ParseResult {
contract: ContractNode | null; // The root AST node (null if parsing failed)
errors: CompilerDiagnostic[]; // Parse errors
}
Example
const parseResult = parse(source, 'P2PKH.runar.ts');
if (parseResult.contract) {
console.log('Contract name:', parseResult.contract.name);
console.log('Methods:', parseResult.contract.methods.map(m => m.name));
}
Language-Specific Parsers
Runar supports six frontend languages. Each has a dedicated parser that produces the same ContractNode AST. In addition to parse() (which auto-dispatches by file extension), each parser is also available as a standalone function:
parseSolSource()
function parseSolSource(source: string): ParseResult;
Parses Solidity-syntax Runar contracts. Accepts standard Solidity contract declarations with Runar-specific annotations.
const result = parseSolSource(`
pragma runar ^0.1.0;
contract P2PKH {
Ripemd160 pubKeyHash;
function unlock(Sig sig, PubKey pubKey) public {
require(hash160(pubKey) == pubKeyHash);
require(checkSig(sig, pubKey));
}
}
`);
parseMoveSource()
function parseMoveSource(source: string): ParseResult;
Parses Move-syntax Runar contracts. Accepts Move module declarations with Runar-specific types.
const result = parseMoveSource(`
module P2PKH {
struct Contract has key {
pub_key_hash: Ripemd160,
}
public fun unlock(sig: Sig, pub_key: PubKey, self: &Contract) {
assert!(hash160(pub_key) == self.pub_key_hash);
assert!(check_sig(sig, pub_key));
}
}
`);
parsePythonSource()
function parsePythonSource(source: string): ParseResult;
Parses Python-syntax Runar contracts. Accepts class definitions with type annotations.
const result = parsePythonSource(`
class P2PKH(SmartContract):
pub_key_hash: Ripemd160
def unlock(self, sig: Sig, pub_key: PubKey):
assert hash160(pub_key) == self.pub_key_hash
assert check_sig(sig, pub_key)
`);
parseGoSource()
function parseGoSource(source: string): ParseResult;
Parses Go-syntax Runar contracts. Accepts Go struct definitions with runar struct tags.
parseRustSource()
function parseRustSource(source: string): ParseResult;
Parses Rust-syntax Runar contracts. Accepts Rust struct definitions with #[runar::contract] attribute macros.
validate()
Performs semantic validation on a parsed AST. Checks for disallowed language features, missing constructors, improper super() calls, and structural correctness.
Signature
function validate(contract: ContractNode): ValidationResult;
Return Value
interface ValidationResult {
errors: CompilerDiagnostic[];
warnings: CompilerDiagnostic[];
}
Example
const parseResult = parse(source);
if (parseResult.errors.length === 0) {
const validationResult = validate(parseResult.contract);
if (validationResult.errors.length > 0) {
for (const err of validationResult.errors) {
console.error(`Validation: ${err.message}`);
}
}
}
typecheck()
Performs type checking on a validated AST. Verifies type correctness, affine type usage, and on-chain type compatibility.
Signature
function typecheck(contract: ContractNode): TypeCheckResult;
Return Value
interface TypeCheckResult {
typedContract: ContractNode;
errors: CompilerDiagnostic[];
}
Example
const typeResult = typecheck(parseResult.contract);
if (typeResult.errors.length > 0) {
for (const err of typeResult.errors) {
console.error(`Type error: ${err.message}`);
}
}
lowerToANF()
Lowers a type-checked AST to Administrative Normal Form (ANF) intermediate representation. This is the IR that feeds into the Bitcoin Script code generator.
Signature
function lowerToANF(contract: ContractNode): ANFProgram;
Return Value
interface ANFProgram {
name: string;
methods: ANFMethod[];
stateFields: ANFField[];
constructorParams: ANFField[];
}
interface ANFMethod {
name: string;
params: ANFField[];
body: ANFExpression[];
}
Example
const anf = lowerToANF(parseResult.contract);
console.log('ANF methods:', anf.methods.map(m => m.name));
console.log('ANF body ops:', anf.methods[0].body.length);
The ANF IR is useful for custom analysis tools, optimization passes, or feeding into the RunarInterpreter for reference execution.
Error Types
All error objects share a common shape:
interface CompilerDiagnostic {
message: string; // Human-readable error description
loc?: SourceLocation; // Source location (file, line, column)
severity: 'error' | 'warning';
}
interface SourceLocation {
file?: string;
line?: number;
column?: number;
}
Error Codes
| Code | Description |
|---|---|
E001 | Parse error: syntax error |
E002 | Parse error: unexpected token |
E010 | Validation: disallowed language feature |
E011 | Validation: missing constructor |
E012 | Validation: missing super() call |
E020 | Type error: type mismatch |
E021 | Type error: affine type used more than once |
E022 | Type error: unknown type |
E030 | Codegen: script too large |
E031 | Codegen: estimated stack overflow |
W001 | Warning: large script size |
W002 | Warning: unused variable |
W003 | Warning: shadowed variable |
Plugin and Extension Points
The compiler pipeline is structured as a sequence of phases: parse, validate, typecheck, lower, optimize, emit. Each phase is a standalone function, so you can insert custom processing between any two phases:
import { parse, validate, typecheck, lowerToANF } from 'runar-compiler';
// Custom pipeline with analysis between phases
const ast = parse(source).contract;
validate(ast);
typecheck(ast);
// Insert custom analysis here
const complexity = analyzeComplexity(ast);
if (complexity > threshold) {
console.warn('Contract complexity exceeds recommended threshold');
}
const anf = lowerToANF(ast);
// Feed ANF to custom code generator or analysis tool
This modular design makes it straightforward to build editor integrations (parse + typecheck for diagnostics), documentation generators (parse + extract metadata), or custom optimizers (lower + transform + emit).