Files
flux/fluxc/src/ast.rs
Jooris Hadeler a82b7e4633 Feat: add compound assignment and shift operators
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.
2026-03-10 18:29:52 +01:00

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,
}