use crate::dsl::lexer::Span;
#[derive(Debug, Clone)]
pub struct GkFile {
pub statements: Vec<Statement>,
}
#[derive(Debug, Clone)]
pub enum Statement {
InputDecl(InputDecl),
Binding(Binding),
ModuleDef(ModuleDef),
ExternPort(ExternPort),
Cursor(CursorDecl),
Pragma { name: String, span: Span },
}
#[derive(Debug, Clone)]
pub struct ExternPort {
pub name: String,
pub typ: String,
pub default: Option<Expr>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct InputDecl {
pub name: String,
pub ty: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WireModifier {
Const,
Shared,
Volatile,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct BindingModifier {
bits: u8,
}
impl BindingModifier {
pub const NONE: Self = Self { bits: 0 };
pub const CONST: Self = Self { bits: Self::bit(WireModifier::Const) };
pub const SHARED: Self = Self { bits: Self::bit(WireModifier::Shared) };
pub const VOLATILE: Self = Self { bits: Self::bit(WireModifier::Volatile) };
pub const fn has(&self, m: WireModifier) -> bool {
self.bits & Self::bit(m) != 0
}
pub const fn has_any(&self) -> bool {
self.bits != 0
}
pub fn insert(&mut self, m: WireModifier) {
self.bits |= Self::bit(m);
}
pub fn from_iter<I: IntoIterator<Item = WireModifier>>(items: I) -> Result<Self, &'static str> {
let mut out = Self::NONE;
for m in items {
out.insert(m);
}
if out.has(WireModifier::Const) && out.has(WireModifier::Volatile) {
return Err(
"modifier conflict: `const` and `volatile` are contradictory \
— `const` materializes the value once and freezes it; \
`volatile` excludes the wire from const-fold and signals \
per-cycle variability. Drop one.",
);
}
Ok(out)
}
pub fn iter(&self) -> impl Iterator<Item = WireModifier> + '_ {
const ORDER: &[WireModifier] = &[
WireModifier::Const,
WireModifier::Shared,
WireModifier::Volatile,
];
ORDER.iter().copied().filter(move |m| self.has(*m))
}
#[inline] pub const fn is_const(&self) -> bool { self.has(WireModifier::Const) }
#[inline] pub const fn is_shared(&self) -> bool { self.has(WireModifier::Shared) }
#[inline] pub const fn is_volatile(&self) -> bool { self.has(WireModifier::Volatile) }
const fn bit(m: WireModifier) -> u8 {
match m {
WireModifier::Const => 1 << 0,
WireModifier::Shared => 1 << 1,
WireModifier::Volatile => 1 << 2,
}
}
}
#[derive(Debug, Clone)]
pub struct Binding {
pub targets: Vec<String>,
pub value: Expr,
pub modifier: BindingModifier,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct CursorDecl {
pub name: String,
pub constructor: Expr,
pub over: Option<Expr>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum Expr {
Ident(String, Span),
IntLit(u64, Span),
FloatLit(f64, Span),
StringLit(String, Span),
ArrayLit(Vec<Expr>, Span),
Call(CallExpr),
BinOp(Box<Expr>, BinOpKind, Box<Expr>),
UnaryNeg(Box<Expr>, Span),
UnaryBitNot(Box<Expr>, Span),
FieldAccess {
source: String,
field: String,
span: Span,
},
}
#[derive(Debug, Clone, Copy)]
pub enum BinOpKind {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
}
#[derive(Debug, Clone)]
pub struct TypedParam {
pub name: String,
pub typ: String, }
#[derive(Debug, Clone)]
pub struct ModuleDef {
pub name: String,
pub params: Vec<TypedParam>,
pub outputs: Vec<TypedParam>,
pub body: Vec<Statement>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct CallExpr {
pub func: String,
pub args: Vec<Arg>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum Arg {
Positional(Expr),
Named(String, Expr),
}
#[cfg(test)]
mod modifier_tests {
use super::*;
#[test]
fn empty_set_has_no_modifiers() {
let m = BindingModifier::NONE;
assert!(!m.has_any());
assert!(!m.is_const() && !m.is_shared() && !m.is_volatile());
}
#[test]
fn single_modifier_consts_match_expected_flags() {
assert!(BindingModifier::CONST.is_const());
assert!(!BindingModifier::CONST.is_shared());
assert!(!BindingModifier::CONST.is_volatile());
assert!(BindingModifier::SHARED.is_shared());
assert!(!BindingModifier::SHARED.is_const());
assert!(BindingModifier::VOLATILE.is_volatile());
assert!(!BindingModifier::VOLATILE.is_const());
}
#[test]
fn from_iter_collects_combinations() {
let m = BindingModifier::from_iter(
[WireModifier::Const, WireModifier::Shared]
).expect("const+shared is valid");
assert!(m.is_const() && m.is_shared());
assert!(!m.is_volatile());
let m = BindingModifier::from_iter(
[WireModifier::Shared, WireModifier::Volatile]
).expect("shared+volatile is valid");
assert!(m.is_shared() && m.is_volatile());
}
#[test]
fn from_iter_rejects_const_plus_volatile() {
let err = BindingModifier::from_iter(
[WireModifier::Const, WireModifier::Volatile]
).expect_err("const+volatile must be rejected");
assert!(err.contains("const") && err.contains("volatile"),
"error should name both keywords: {err}");
}
#[test]
fn from_iter_rejects_const_shared_volatile() {
let err = BindingModifier::from_iter(
[WireModifier::Const, WireModifier::Shared, WireModifier::Volatile]
).expect_err("triple combo includes the contradictory pair");
assert!(err.contains("const") && err.contains("volatile"));
}
#[test]
fn iter_yields_modifiers_in_stable_order() {
let m = BindingModifier::from_iter(
[WireModifier::Volatile, WireModifier::Shared]
).unwrap();
let collected: Vec<_> = m.iter().collect();
assert_eq!(collected, vec![WireModifier::Shared, WireModifier::Volatile]);
}
#[test]
fn equality_distinguishes_combinations() {
let const_only = BindingModifier::CONST;
let const_shared = BindingModifier::from_iter(
[WireModifier::Const, WireModifier::Shared]
).unwrap();
assert_ne!(const_only, const_shared,
"const-only must not equal const+shared");
}
}