Pour les développeurs du compilateur UniStack.
- Parser API
- Transpiler API
- AST Types
- Runtime API
- IR (Intermediate Representation)
- Extending the Compiler
Source: src/parser/uniParser.ts
Analyse un fichier source UniStack et retourne un Abstract Syntax Tree (AST).
export function parseUniFile(source: string, fileName: string): UniFile| Param | Type | Description |
|---|---|---|
source |
string |
Le contenu complet du fichier .uni |
fileName |
string |
Chemin/nom du fichier (pour messages d'erreur) |
interface UniFile {
name: string; // Nom de l'app (depuis header)
version: string; // Version (X.Y format)
config: ConfigSection | null;
sections: Section[]; // Array de config/html/css/py/js/routes
}| Erreur | Cause |
|---|---|
"empty file" |
Fichier vide |
"invalid header" |
Header mal formé (attendu: unistack app "..." version X.Y) |
"invalid config entry" |
Entrée config with format invalide |
"invalid route line" |
Route with format invalide |
import { parseUniFile } from './src/parser/uniParser.js';
const source = `
unistack app "MyApp" version 1.0 {
config: port=3000;
html-ui: <h1>Hello</h1>;
}
`;
const ast = parseUniFile(source, 'myapp.uni');
console.log(ast.name); // "MyApp"
console.log(ast.version); // "1.0"
console.log(ast.sections.length); // 2Analyse la première ligne du fichier.
// Input: 'unistack app "TodoApp" version 1.0'
// Output: { name: "TodoApp", version: "1.0" }Regroupe les lignes par sections (config, html-ui, css, py-logic, js-events, routes).
Parse une section config.
// Input: 'port=3000, db="sqlite:app.db", debug=true'
// Output: ConfigSection avec entries [] remplieParse une section html-ui avec support des interpolations {py:func()}, {js:var}, {sql(...)}.
Parse une section css (traité comme texte brut).
Parse une section py-logic (traité comme texte brut, pour future intégration Python).
Parse une section js-events (traité comme texte brut, injected au client).
Parse une section routes avec support HTTP verbes et chemins.
Parse une référence inter-langage comme py:getUsers() ou sql("SELECT ...").
// Examples
parseLangRef("py:getUsers()");
// → { lang: "py", name: "getUsers", args: [] }
parseLangRef("sql(\"SELECT * FROM users\")");
// → { lang: "sql", query: "SELECT * FROM users" }
parseLangRef("js:myVar");
// → { lang: "js", name: "myVar", args: [] }Source: src/transpiler/index.ts
Orchestrates the full compilation: Parse → IR → Code generation → File output.
export interface BuildOptions {
entryPath: string; // Absolute path to .uni file
generatedDir: string; // Output directory for generated files
}
export async function buildUniStack(options: BuildOptions): Promise<void>- Parse:
parseUniFile()→ AST - IR Build:
buildIR()→ Intermediate representation - Code Gen:
generateServerTs(),generateClientTs(),generateIndexHtml() - File Emit: Écrire dans
generatedDir/app.server.tsapp.client.tsindex.html
import { buildUniStack } from './src/transpiler/index.js';
await buildUniStack({
entryPath: '/absolute/path/to/app.uni',
generatedDir: '/absolute/path/to/generated'
});
// Generated files créés in:
// - generated/app.server.ts
// - generated/app.client.ts
// - generated/index.htmlTransforme l'AST en Intermediate Representation pour code generation.
interface CompilationIR {
frontend: FrontendIR;
backend: BackendIR;
assets: AssetsIR;
}Extrait HTML, CSS et crée des placeholders pour les interpolations dynamiques.
interface FrontendIR {
html: string; // Markup HTML brut
css: string; // Déclarations CSS
placeholders: [{ // Mappings pour {py:...} et {js:...}
id: string;
ref: LangRef;
}];
}Extrait les routes et leurs handlers.
interface BackendIR {
routes: [{
method: HttpMethod; // GET, POST, etc
path: string; // /api/users
handler: LangRef; // py:getUsers() ou sql("SELECT...")
}];
}Collecte le code JavaScript client.
interface AssetsIR {
clientEntry: string; // Code JS à exécuter côté client
}Produit app.server.ts avec les routes Express.
Template generé:
import express from 'express';
import type { UniRuntime } from '../runtime/server.js';
export function createServer(runtime: UniRuntime) {
const app = express();
app.get('/api/users', async (req, res) => {
const data = await runtime.callPy('getUsers');
res.json(data);
});
// ... autres routes
return app;
}Produit app.client.ts avec le code JavaScript utilisateur.
Produit index.html avec HTML + CSS injecté, ready pour bundling.
Écrit les 3 fichiers générés en concurrence (Promise.all).
Source: src/lang/ast.ts
export type Literal =
| { kind: 'string'; value: string }
| { kind: 'number'; value: number }
| { kind: 'boolean'; value: boolean };interface ConfigEntry {
key: string;
value: Literal;
}
interface ConfigSection {
kind: 'config';
entries: ConfigEntry[];
}type HtmlNode = HtmlTextNode | HtmlExprNode;
interface HtmlTextNode {
kind: 'htmlText';
text: string;
}
interface HtmlExprNode {
kind: 'htmlExpr';
target: LangRef; // py:func() ou js:var ou sql(...)
}
interface HtmlSection {
kind: 'html';
blocks: HtmlBlock[];
}
interface HtmlBlock {
nodes: HtmlNode[];
}interface CssSection {
kind: 'css';
chunks: string[];
}interface PyChunk {
kind: 'pyChunk';
code: string;
}
interface PySection {
kind: 'py';
chunks: PyChunk[];
}interface JsChunk {
kind: 'jsChunk';
code: string;
}
interface JsSection {
kind: 'js';
chunks: JsChunk[];
}export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
interface RouteSection {
kind: 'routes';
routes: RouteDef[];
}
interface RouteDef {
method: HttpMethod;
path: string;
body: RouteStmt[];
}
type RouteStmt = RouteReturnStmt | RouteStatusStmt;
interface RouteReturnStmt {
kind: 'return';
expr: LangRef;
}
interface RouteStatusStmt {
kind: 'status';
code: number; // HTTP status code
}export type LangKind = 'py' | 'js' | 'sql';
interface LangRefPyJs extends LangRefBase {
lang: 'py' | 'js';
name: string;
args: Expr[];
}
interface LangRefSql extends LangRefBase {
lang: 'sql';
query: string;
}
export type LangRef = LangRefPyJs | LangRefSql;
type Expr = Literal | { kind: 'identifier'; name: string };export type Section =
| ConfigSection
| HtmlSection
| CssSection
| PySection
| JsSection
| RouteSection;
interface UniFile {
name: string;
version: string;
config: ConfigSection | null;
sections: Section[];
}interface UniRuntime {
callPy(name: string, ...args: unknown[]): Promise<unknown>;
sql(query: string, params?: unknown[]): Promise<unknown[]>;
}Constructor
const runtime = new BasicRuntime();Methods:
Enregistre une fonction Python (pour MVP avant vraie intégration Python).
runtime.registerPy('getUsers', async () => {
return [{ id: 1, name: 'Alice' }];
});
runtime.registerPy('getUserById', (uid: number) => {
return { id: uid, name: 'User ' + uid };
});Appelle une fonction Python enregistrée.
const result = await runtime.callPy('getUserById', 42);
// → { id: 42, name: 'User 42' }Throws: Error si fonction non trouvée.
Exécute une requête SQL (stub pour MVP).
// Stub: logs la requête, retourne []
const results = await runtime.sql('SELECT * FROM users WHERE id = ?', [1]);
// → []Démarre le serveur Express.
startServer(
(runtime) => {
const app = express();
app.get('/', (req, res) => res.json({ hello: 'world' }));
return app;
},
new BasicRuntime(),
{ port: 3000 }
);
// → Server listening on http://localhost:3000Wrapper autour de fetch() pour JSON.
interface FetchJsonOptions {
method?: string; // GET, POST, etc (default: GET)
body?: unknown; // JSON body
}Example:
const users = await fetchJson<User[]>('/api/users');
const newUser = await fetchJson('/api/users', {
method: 'POST',
body: { name: 'Bob', email: 'bob@example.com' }
});Throws: Error si HTTP status non 2xx.
Exécute un callback quand le DOM est prêt (amélioration sur DOMContentLoaded).
attachDomReady(() => {
console.log('DOM is ready!');
});interface CompilationIR {
frontend: FrontendIR;
backend: BackendIR;
assets: AssetsIR;
}interface FrontendPlaceholder {
id: string; // Unique placeholder ID
ref: LangRef; // Dynamique reference
}
interface FrontendIR {
html: string; // HTML markup brut
css: string; // CSS declarations concatenés
placeholders: FrontendPlaceholder[];
}interface BackendRouteIR {
method: HttpMethod;
path: string;
handler: LangRef; // Function à appeler
}
interface BackendIR {
routes: BackendRouteIR[];
}interface AssetsIR {
clientEntry: string; // Code JS client
}Étapes:
-
Ajouter au type AST (
src/lang/ast.ts)interface MySection { kind: 'my-section'; content: string; } type Section = ... | MySection;
-
Ajouter au parser (
src/parser/uniParser.ts)type SectionKind = 'config' | 'html-ui' | ... | 'my-section'; // Dans parseUniFile() case 'my-section': { const section = parseMySection(raw.lines); sections.push(section); break; } function parseMySection(lines: string[]): MySection { return { kind: 'my-section', content: lines.join('\n') }; }
-
Ajouter au IR builder (
src/transpiler/index.ts)function buildIR(ast: UniFile): CompilationIR { // Extraire votre section const mySections = ast.sections.filter(s => s.kind === 'my-section'); // Traiter comme vous le voulez }
-
Test: Parser + build + vérify output
L'ANTLR grammaire existe déjà dans src/lang/UniStack.g4.
Pour l'utiliser:
-
Install ANTLR4:
npm install antlr4 antlr4ng
-
Générer parser TypeScript:
antlr4 -Dlanguage=TypeScript -visitor src/lang/UniStack.g4
-
Adapter
uniParser.tspour utiliser le parser ANTLR généré -
Remplacer le parser hand-written
import { parseUniFile } from './src/parser/uniParser.js';
describe('UniStack Parser', () => {
it('should parse a valid .uni file', () => {
const source = `
unistack app "Test" version 1.0 {
config: port=3000;
html-ui: <h1>Hello</h1>;
}
`;
const ast = parseUniFile(source, 'test.uni');
expect(ast.name).toBe('Test');
expect(ast.sections).toHaveLength(2);
});
});import { buildUniStack } from './src/transpiler/index.js';
import { promises as fs } from 'node:fs';
describe('UniStack Transpiler', () => {
it('should generate server.ts', async () => {
await buildUniStack({
entryPath: './test.uni',
generatedDir: './test-output'
});
const serverTs = await fs.readFile('./test-output/app.server.ts', 'utf8');
expect(serverTs).toContain('express');
});
});- [OK] Hand-written parser
- [OK] Basic transpiler
- [OK] Express server
- [OK] HTML/CSS/JS/Python/SQL synthesis
- ANTLR parser integration
- Python runtime (Pyodide or Python-wasm)
- SQLite integration
- Module system (imports)
- Type validation
- C++/Rust → WASM
- GraphQL support
- Hot-reload
- IDE plugins
Last updated: 26 février 2026 Compilateur version: 0.1.0-MVP