rename crates/ to engrams/; add el-compiler el package with bootstrap artifact
- crates/ → engrams/ (Rust engrams live here)
- el-compiler/ added: el self-hosting compiler as an el package
- src/{compiler,lexer,parser,codegen}.el
- bootstrap/el-compiler.elc (114KB, Rust-compiled seed)
- el.toml Cargo.toml workspace paths updated
- neuron-rs cross-repo path deps fixed (were pointing to products/ instead of foundation/)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "el-parser"
|
||||
description = "Engram language AST and recursive-descent parser"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
el-lexer = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -0,0 +1,304 @@
|
||||
//! Abstract syntax tree node types.
|
||||
|
||||
use el_lexer::Span;
|
||||
|
||||
// ── Test-specific nodes ───────────────────────────────────────────────────────
|
||||
|
||||
/// Which graph a test should execute against.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TestTarget {
|
||||
/// In-memory graph — default, zero external dependencies.
|
||||
Unit,
|
||||
/// Real Engram database pointed at by `ENGRAM_URL` / `ENGRAM_DB_PATH`.
|
||||
E2e,
|
||||
/// Run against both unit (in-memory) and e2e (real DB).
|
||||
Both,
|
||||
}
|
||||
|
||||
/// A `seed Node { ... }` or `seed Edge { ... }` statement inside a test block.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SeedStmt {
|
||||
Node {
|
||||
node_type: String,
|
||||
content: String,
|
||||
importance: f32,
|
||||
tier: Option<String>,
|
||||
},
|
||||
Edge {
|
||||
from: String,
|
||||
to: String,
|
||||
relation: String,
|
||||
weight: f32,
|
||||
},
|
||||
}
|
||||
|
||||
// ── Literals ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Literal {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Str(String),
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
// ── Type expressions ──────────────────────────────────────────────────────────
|
||||
|
||||
/// A type annotation in source code, e.g. `String`, `[Int]`, `User?`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeExpr {
|
||||
/// A named type: `Int`, `String`, `User`, …
|
||||
Named(String),
|
||||
/// An array type: `[T]`
|
||||
Array(Box<TypeExpr>),
|
||||
/// An optional type: `T?`
|
||||
Optional(Box<TypeExpr>),
|
||||
/// A function type: `fn(A, B) -> C`
|
||||
Fn { params: Vec<TypeExpr>, return_type: Box<TypeExpr> },
|
||||
/// `Result<T, E>` — built-in error-propagation type
|
||||
Result { ok: Box<TypeExpr>, err: Box<TypeExpr> },
|
||||
/// `Map<K, V>` — built-in key-value map type
|
||||
Map { key: Box<TypeExpr>, value: Box<TypeExpr> },
|
||||
/// A generic type parameter: `T`, `E` — used inside generic function signatures.
|
||||
TypeParam(String),
|
||||
}
|
||||
|
||||
// ── Patterns (for match arms) ─────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Pattern {
|
||||
/// `Status::Active`
|
||||
EnumVariant { enum_name: String, variant: String, payload: Option<String> },
|
||||
/// A wildcard `_`
|
||||
Wildcard,
|
||||
/// A literal: `42`, `"str"`, `true`
|
||||
Literal(Literal),
|
||||
/// A binding: `x`
|
||||
Binding(String),
|
||||
}
|
||||
|
||||
// ── Binary operators ──────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BinOp {
|
||||
Add, Sub, Mul, Div,
|
||||
Eq, NotEq, Lt, Gt, LtEq, GtEq,
|
||||
And, Or,
|
||||
}
|
||||
|
||||
// ── Expressions ───────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expr {
|
||||
Literal(Literal),
|
||||
Ident(String),
|
||||
BinOp { op: BinOp, left: Box<Expr>, right: Box<Expr> },
|
||||
UnaryNot(Box<Expr>),
|
||||
Call { func: Box<Expr>, args: Vec<Expr> },
|
||||
Block(Vec<Stmt>),
|
||||
Match { subject: Box<Expr>, arms: Vec<MatchArm> },
|
||||
/// `activate TypeName where "semantic query string"`
|
||||
Activate { type_name: String, query: String },
|
||||
/// `sealed { stmts... }` — quantum-sealed block
|
||||
Sealed(Vec<Stmt>),
|
||||
If { cond: Box<Expr>, then: Box<Expr>, else_: Option<Box<Expr>> },
|
||||
Field { object: Box<Expr>, field: String },
|
||||
/// Array constructor: `[a, b, c]`
|
||||
Array(Vec<Expr>),
|
||||
/// Path expression: `Status::Active` (enum variant ref)
|
||||
Path { segments: Vec<String> },
|
||||
/// Index expression: `arr[0]`
|
||||
Index { object: Box<Expr>, index: Box<Expr> },
|
||||
/// Closure: `|x: Int| x * 2` or `|x: Int| -> Int { x * 2 }`
|
||||
Closure {
|
||||
params: Vec<Param>,
|
||||
return_type: Option<TypeExpr>,
|
||||
body: Box<Expr>,
|
||||
span: Span,
|
||||
},
|
||||
/// Try operator: `expr?` — unwraps Result, propagates error
|
||||
Try(Box<Expr>),
|
||||
/// Map literal: `{"key": value, ...}`
|
||||
MapLiteral(Vec<(Expr, Expr)>),
|
||||
/// Struct literal: `Point { x: 10, y: 20 }`
|
||||
StructLit {
|
||||
type_name: String,
|
||||
fields: Vec<(String, Expr)>,
|
||||
span: Span,
|
||||
},
|
||||
/// Record update: `a with { field: new_val }`
|
||||
With {
|
||||
base: Box<Expr>,
|
||||
updates: Vec<(String, Expr)>,
|
||||
},
|
||||
/// AI inference: `reason "query"`
|
||||
Reason {
|
||||
query: String,
|
||||
},
|
||||
/// Concurrent execution: `parallel { name: expr, ... }`
|
||||
Parallel {
|
||||
entries: Vec<(String, Expr)>,
|
||||
},
|
||||
/// Trace block: `trace "label" { stmts }`
|
||||
Trace {
|
||||
label: String,
|
||||
body: Vec<Stmt>,
|
||||
},
|
||||
}
|
||||
|
||||
// ── Match arm ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MatchArm {
|
||||
pub pattern: Pattern,
|
||||
pub body: Expr,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// ── Decorators ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A decorator applied to a function: `@name` or `@name(args)`
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Decorator {
|
||||
pub name: String,
|
||||
pub args: Vec<Expr>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// ── Protocol ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A method signature inside a protocol definition.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ProtocolMethod {
|
||||
pub name: String,
|
||||
pub params: Vec<Param>,
|
||||
pub return_type: TypeExpr,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// ── Statements ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A named parameter in a function definition.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Param {
|
||||
pub name: String,
|
||||
pub type_ann: TypeExpr,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// A field in a type definition.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Field {
|
||||
pub name: String,
|
||||
pub type_ann: TypeExpr,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// A variant in an enum definition.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Variant {
|
||||
pub name: String,
|
||||
/// Payload type, if any (e.g. `Pending(String)`)
|
||||
pub payload: Option<TypeExpr>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Stmt {
|
||||
/// `let name: Type = expr`
|
||||
Let {
|
||||
name: String,
|
||||
type_ann: Option<TypeExpr>,
|
||||
value: Expr,
|
||||
span: Span,
|
||||
},
|
||||
/// `return expr`
|
||||
Return(Expr, Span),
|
||||
/// A bare expression used as a statement (usually a call).
|
||||
Expr(Expr, Span),
|
||||
/// `fn name<T, E>(params) -> ReturnType [requires cond] { body }` (with optional decorators)
|
||||
FnDef {
|
||||
name: String,
|
||||
decorators: Vec<Decorator>,
|
||||
/// Generic type parameters, e.g. `["T", "E"]` for `fn foo<T, E>`.
|
||||
type_params: Vec<String>,
|
||||
params: Vec<Param>,
|
||||
return_type: TypeExpr,
|
||||
/// Optional precondition: `requires expr`
|
||||
requires: Option<Box<Expr>>,
|
||||
body: Vec<Stmt>,
|
||||
span: Span,
|
||||
},
|
||||
/// `type Name { fields... }`
|
||||
TypeDef {
|
||||
name: String,
|
||||
fields: Vec<Field>,
|
||||
span: Span,
|
||||
},
|
||||
/// `enum Name { variants... }`
|
||||
EnumDef {
|
||||
name: String,
|
||||
variants: Vec<Variant>,
|
||||
span: Span,
|
||||
},
|
||||
/// `test "name" [target: unit|e2e|both] { body }`
|
||||
TestDef {
|
||||
name: String,
|
||||
target: TestTarget,
|
||||
body: Vec<Stmt>,
|
||||
span: Span,
|
||||
},
|
||||
/// `seed Node { ... }` or `seed Edge { ... }`
|
||||
Seed(SeedStmt, Span),
|
||||
/// `assert <expr>`
|
||||
Assert(Expr, Span),
|
||||
/// `import std::collections::Map` or `from pkg import { A, B }`
|
||||
Import {
|
||||
path: Vec<String>,
|
||||
names: Vec<String>,
|
||||
alias: Option<String>,
|
||||
span: Span,
|
||||
},
|
||||
/// `protocol Name { method sigs... }`
|
||||
ProtocolDef {
|
||||
name: String,
|
||||
methods: Vec<ProtocolMethod>,
|
||||
span: Span,
|
||||
},
|
||||
/// `impl Protocol for TypeName { fn ... }`
|
||||
ImplDef {
|
||||
protocol_name: String,
|
||||
type_name: String,
|
||||
methods: Vec<Stmt>,
|
||||
span: Span,
|
||||
},
|
||||
/// `while <condition> { <body> }`
|
||||
While {
|
||||
condition: Expr,
|
||||
body: Vec<Stmt>,
|
||||
span: Span,
|
||||
},
|
||||
/// `retry N times { ... } fallback { ... }`
|
||||
Retry {
|
||||
count: Expr,
|
||||
body: Vec<Stmt>,
|
||||
fallback: Option<Vec<Stmt>>,
|
||||
span: Span,
|
||||
},
|
||||
/// `deploy fn_name to "/route" via target`
|
||||
Deploy {
|
||||
fn_name: String,
|
||||
route: String,
|
||||
target: String,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
// ── Top-level program ─────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Program {
|
||||
pub stmts: Vec<Stmt>,
|
||||
/// The original source, kept for diagnostics and source maps.
|
||||
pub source: String,
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//! Parser error types.
|
||||
|
||||
use thiserror::Error;
|
||||
use el_lexer::{Span, Token};
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
#[error("{kind} at {span}")]
|
||||
pub struct ParseError {
|
||||
pub kind: ParseErrorKind,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn new(kind: ParseErrorKind, span: Span) -> Self {
|
||||
Self { kind, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum ParseErrorKind {
|
||||
#[error("unexpected token {got}, expected {expected}")]
|
||||
UnexpectedToken { expected: String, got: String },
|
||||
|
||||
#[error("unexpected end of file")]
|
||||
UnexpectedEof,
|
||||
|
||||
#[error("invalid expression starting with {0}")]
|
||||
InvalidExprStart(String),
|
||||
|
||||
#[error("invalid type expression: {0}")]
|
||||
InvalidTypeExpr(String),
|
||||
|
||||
#[error("invalid pattern: {0}")]
|
||||
InvalidPattern(String),
|
||||
|
||||
#[error("expected identifier, got {0}")]
|
||||
ExpectedIdent(String),
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn expected(expected: impl Into<String>, got: &Token, span: Span) -> Self {
|
||||
Self::new(
|
||||
ParseErrorKind::UnexpectedToken {
|
||||
expected: expected.into(),
|
||||
got: got.to_string(),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn eof(span: Span) -> Self {
|
||||
Self::new(ParseErrorKind::UnexpectedEof, span)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//! el-parser — Engram language recursive-descent parser.
|
||||
//!
|
||||
//! Converts a flat token stream into a typed [`Program`] AST.
|
||||
//!
|
||||
//! # Design
|
||||
//! Hand-written recursive descent — no parser generator. Every parse function
|
||||
//! returns `Result<T, ParseError>`, making the error path explicit.
|
||||
|
||||
mod ast;
|
||||
mod error;
|
||||
mod parser;
|
||||
|
||||
pub use ast::{
|
||||
BinOp, Decorator, Expr, Field, Literal, MatchArm, Param, Pattern, Program, ProtocolMethod,
|
||||
SeedStmt, Stmt, TestTarget, TypeExpr, Variant,
|
||||
};
|
||||
pub use error::{ParseError, ParseErrorKind};
|
||||
pub use parser::parse;
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user