Compound assignment: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= Shift: <<, >> Each compound assignment token parses at the same precedence as `=` (right-associative, lowest) and produces ExprKind::CompoundAssign. Shifts parse between additive and multiplicative precedence. GRAMMAR.ebnf and SYNTAX.md updated accordingly.
291 lines
6.9 KiB
Rust
291 lines
6.9 KiB
Rust
use crate::token::Span;
|
|
|
|
// ── Operators ──────────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum UnaryOp {
|
|
Neg, // `-`
|
|
Not, // `!`
|
|
BitNot, // `~`
|
|
Deref, // `*`
|
|
AddrOf, // `&`
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum CompoundAssignOp {
|
|
Add, // `+=`
|
|
Sub, // `-=`
|
|
Mul, // `*=`
|
|
Div, // `/=`
|
|
Rem, // `%=`
|
|
BitAnd, // `&=`
|
|
BitOr, // `|=`
|
|
BitXor, // `^=`
|
|
Shl, // `<<=`
|
|
Shr, // `>>=`
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum BinaryOp {
|
|
// Logical
|
|
Or, // `or`
|
|
And, // `and`
|
|
// Bitwise
|
|
BitOr, // `|`
|
|
BitXor, // `^`
|
|
BitAnd, // `&`
|
|
// Comparison
|
|
Eq, // `==`
|
|
Ne, // `!=`
|
|
Lt, // `<`
|
|
Gt, // `>`
|
|
Le, // `<=`
|
|
Ge, // `>=`
|
|
// Arithmetic
|
|
Add, // `+`
|
|
Sub, // `-`
|
|
Mul, // `*`
|
|
Div, // `/`
|
|
Rem, // `%`
|
|
// Shift
|
|
Shl, // `<<`
|
|
Shr, // `>>`
|
|
// Assignment (lowest precedence, right-associative)
|
|
Assign, // `=`
|
|
}
|
|
|
|
// ── Types ──────────────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum Type {
|
|
// Unsigned integers
|
|
U8,
|
|
U16,
|
|
U32,
|
|
U64,
|
|
// Signed integers
|
|
I8,
|
|
I16,
|
|
I32,
|
|
I64,
|
|
// Floating-point
|
|
F32,
|
|
F64,
|
|
// Other primitives
|
|
Bool,
|
|
Char,
|
|
// User-defined named type (e.g. a struct)
|
|
Named(String, Span),
|
|
// Typed pointer: `*type`
|
|
Pointer(Box<Type>),
|
|
// Opaque (untyped) pointer: `*opaque`
|
|
OpaquePointer,
|
|
// Fixed-size array: `[type; INT_LIT]`
|
|
Array { elem: Box<Type>, size: String },
|
|
// Error placeholder for recovery
|
|
Error,
|
|
}
|
|
|
|
// ── Struct literal field ───────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct StructField {
|
|
pub name: String,
|
|
pub name_span: Span,
|
|
pub value: Expr,
|
|
}
|
|
|
|
// ── Expression ────────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Expr {
|
|
pub kind: ExprKind,
|
|
pub span: Span,
|
|
}
|
|
|
|
impl Expr {
|
|
pub fn new(kind: ExprKind, span: Span) -> Self {
|
|
Self { kind, span }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ExprKind {
|
|
// Literals
|
|
IntLit(String),
|
|
FloatLit(String),
|
|
StringLit(String),
|
|
CharLit(String),
|
|
Bool(bool),
|
|
|
|
// Identifier
|
|
Ident(String),
|
|
|
|
// Struct literal: `Foo { x: 1, y: 2 }`
|
|
StructLit {
|
|
name: String,
|
|
name_span: Span,
|
|
fields: Vec<StructField>,
|
|
},
|
|
|
|
// Operators
|
|
Unary {
|
|
op: UnaryOp,
|
|
op_span: Span,
|
|
expr: Box<Expr>,
|
|
},
|
|
Binary {
|
|
op: BinaryOp,
|
|
op_span: Span,
|
|
lhs: Box<Expr>,
|
|
rhs: Box<Expr>,
|
|
},
|
|
// Compound assignment: `lhs op= rhs` (expands to `lhs = lhs op rhs`)
|
|
CompoundAssign {
|
|
op: CompoundAssignOp,
|
|
op_span: Span,
|
|
lhs: Box<Expr>,
|
|
rhs: Box<Expr>,
|
|
},
|
|
|
|
// Postfix
|
|
Field {
|
|
expr: Box<Expr>,
|
|
field: String,
|
|
field_span: Span,
|
|
},
|
|
Index {
|
|
expr: Box<Expr>,
|
|
index: Box<Expr>,
|
|
},
|
|
Call {
|
|
callee: Box<Expr>,
|
|
args: Vec<Expr>,
|
|
},
|
|
|
|
// Parenthesised expression
|
|
Group(Box<Expr>),
|
|
|
|
// Placeholder for parse errors — allows parsing to continue
|
|
Error,
|
|
}
|
|
|
|
// ── Block ──────────────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Block {
|
|
pub stmts: Vec<Stmt>,
|
|
pub span: Span,
|
|
}
|
|
|
|
// ── Else branch ───────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ElseBranch {
|
|
If(Box<Stmt>), // `else if …`
|
|
Block(Block), // `else { … }`
|
|
}
|
|
|
|
// ── Statement ─────────────────────────────────────────────────────────────────
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Stmt {
|
|
pub kind: StmtKind,
|
|
pub span: Span,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum StmtKind {
|
|
/// `let [mut] name [: type] [= expr] ;`
|
|
Let {
|
|
mutable: bool,
|
|
name: String,
|
|
name_span: Span,
|
|
ty: Option<Type>,
|
|
init: Option<Expr>,
|
|
},
|
|
/// `return [expr] ;`
|
|
Return(Option<Expr>),
|
|
/// `if expr_ns block [else else_branch]`
|
|
If {
|
|
cond: Expr,
|
|
then_block: Block,
|
|
else_branch: Option<ElseBranch>,
|
|
},
|
|
/// `while expr_ns block`
|
|
While { cond: Expr, body: Block },
|
|
/// `loop block`
|
|
Loop { body: Block },
|
|
/// `break ;`
|
|
Break,
|
|
/// `continue ;`
|
|
Continue,
|
|
/// `{ stmts }`
|
|
Block(Block),
|
|
/// `expr ;`
|
|
Expr(Expr),
|
|
/// Error placeholder — emitted during recovery so the parent can continue.
|
|
Error,
|
|
}
|
|
|
|
// ── Top-level definitions ──────────────────────────────────────────────────────
|
|
|
|
/// A function parameter: `[mut] name : type`.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Param {
|
|
pub mutable: bool,
|
|
pub name: String,
|
|
pub name_span: Span,
|
|
pub ty: Type,
|
|
}
|
|
|
|
/// A struct definition field: `name : type`.
|
|
///
|
|
/// Named `FieldDef` to distinguish from `StructField`, which is a
|
|
/// field in a struct *literal expression*.
|
|
#[derive(Debug, Clone)]
|
|
pub struct FieldDef {
|
|
pub name: String,
|
|
pub name_span: Span,
|
|
pub ty: Type,
|
|
}
|
|
|
|
/// `fn name ( params ) [ -> type ] block`
|
|
#[derive(Debug, Clone)]
|
|
pub struct FuncDef {
|
|
pub name: String,
|
|
pub name_span: Span,
|
|
pub params: Vec<Param>,
|
|
pub ret_ty: Option<Type>,
|
|
pub body: Block,
|
|
}
|
|
|
|
/// `struct name { fields }`
|
|
#[derive(Debug, Clone)]
|
|
pub struct StructDef {
|
|
pub name: String,
|
|
pub name_span: Span,
|
|
pub fields: Vec<FieldDef>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TopLevelDef {
|
|
pub kind: TopLevelDefKind,
|
|
pub span: Span,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum TopLevelDefKind {
|
|
Func(FuncDef),
|
|
Struct(StructDef),
|
|
/// Error placeholder for recovery.
|
|
Error,
|
|
}
|
|
|
|
/// The root of the AST — a sequence of top-level definitions.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Program {
|
|
pub defs: Vec<TopLevelDef>,
|
|
pub span: Span,
|
|
}
|