bitsy_lang/
ast.rs

1use super::error::BitsyError;
2use super::loc::Loc;
3use super::loc::SourceInfo;
4
5use super::{BinOp, Length, Name, UnOp, Width, Pat};
6
7use lalrpop_util::lalrpop_mod;
8use lalrpop_util::ParseError;
9lalrpop_mod!(grammar);
10
11/// A `Package` is a compilation unit built from a single file.
12#[derive(Debug, Clone)]
13pub struct Package {
14    pub items: Vec<Item>,
15}
16
17/// A top-level declaration in a [`Package`].
18#[derive(Debug, Clone)]
19pub enum Item {
20    ModDef(ModDef),
21    ExtDef(ModDef),
22    EnumTypeDef(EnumTypeDef),
23    StructTypeDef(StructTypeDef),
24    FnDef(FnDef),
25}
26
27impl Item {
28    pub fn name(&self) -> &str {
29        match self {
30            Item::ModDef(ModDef(_loc, name, _decls)) => name,
31            Item::ExtDef(ModDef(_loc, name, _decls)) => name,
32            Item::EnumTypeDef(typedef) => typedef.name.as_str(),
33            Item::StructTypeDef(typedef) => typedef.name.as_str(),
34            Item::FnDef(fndef) => fndef.name.as_str(),
35        }
36    }
37}
38
39#[derive(Debug, Clone)]
40pub struct ModDef(pub Loc, pub Name, pub Vec<Decl>);
41
42/// A [`Decl`] is a declaration that lives inside of a `mod` or `ext` definiton.
43#[derive(Debug, Clone)]
44pub enum Decl {
45    Mod(Loc, Name, Vec<Decl>),
46    ModInst(Loc, Name, Name),
47    Incoming(Loc, Name, Type),
48    Outgoing(Loc, Name, Type),
49    Node(Loc, Name, Type),
50    Reg(Loc, Name, Type, Option<Box<Expr>>),
51    Wire(Loc, Wire),
52    When(Loc, When),
53}
54
55/// A user-defined `enum` type.
56#[derive(Debug, Clone)]
57pub struct EnumTypeDef {
58    pub name: String,
59    pub values: Vec<(String, WordLit)>,
60}
61
62/// A user-defined `struct` type.
63#[derive(Debug, Clone)]
64pub struct StructTypeDef {
65    pub name: String,
66    pub fields: Vec<(String, Type)>,
67}
68
69/// A user-defined `struct` type.
70#[derive(Debug, Clone)]
71pub struct FnDef {
72    pub name: String,
73    pub args: Vec<(String, Type)>,
74    pub ret: Type,
75    pub body: Expr,
76}
77
78/// An expression.
79#[derive(Debug, Clone)]
80pub enum Expr {
81    /// A referenec to a port, reg, or node.
82    Ref(Loc, Target),
83    /// A literal Word.
84    Word(Loc, Option<Width>, u64),
85    /// A literal enum value.
86    Enum(Loc, Type, String),
87    /// A constructor for a struct type.
88    Struct(Loc, Vec<(String, Box<Expr>)>),
89    /// A constructor for a Vec
90    Vec(Loc, Vec<Expr>),
91    /// A call-like expression, including `cat` and constructors like `@Valid`.
92    Call(Loc, String, Vec<Expr>),
93    /// Let binding. Eg, `let x = a + b in x + x`.
94    Let(Loc, String, Box<Expr>, Box<Expr>),
95    /// A unary operation. Eg, `!0b101w3`.
96    UnOp(Loc, UnOp, Box<Expr>),
97    /// A binary operation. Eg, `1w8 + 1w8`.
98    BinOp(Loc, BinOp, Box<Expr>, Box<Expr>),
99    /// An `if` expression.
100    If(Loc, Box<Expr>, Box<Expr>, Box<Expr>),
101    /// A `match` expression.
102    Match(Loc, Box<Expr>, Vec<MatchArm>),
103    /// A field index. Eg, `foo->bar`.
104    IdxField(Loc, Box<Expr>, String),
105    /// A static index. Eg, `foo[0]`.
106    Idx(Loc, Box<Expr>, u64),
107    /// A static index over a range. Eg, `foo[8..4]`.
108    IdxRange(Loc, Box<Expr>, u64, u64),
109    /// A hole. Eg, `?foo`.
110    Hole(Loc, Option<String>),
111}
112
113/// A reference to a hardware component, either in this module, or in a child module.
114#[derive(Debug, Clone)]
115pub enum Target {
116    Local(Name),
117    Nonlocal(Name, Name),
118}
119
120/// The different kinds of [`Wire`]s in Bitsy.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub enum WireType {
123    /// Direct wire. Written `:=` in the syntax. Connects one terminal to another.
124    Direct,
125    /// Latched wire. Written `<=` in the syntax. Connects one terminal to the data pin of a register.
126    Latch,
127    /// Procedural. Written `<=!` in the syntax. Connects one terminal to the data pin of a register.
128    Proc,
129}
130
131/// [`Wire`]s drive the value of port, node, or register.
132#[derive(Debug, Clone)]
133pub struct Wire(pub Loc, pub Target, pub Box<Expr>, pub WireType);
134
135/// A [`MatchArm`] is a case for a match expression.
136#[derive(Clone, Debug)]
137pub struct MatchArm(pub Pat, pub Box<Expr>);
138
139/// A [`WordLit`] is a literal for a hardware integer with an optional width ascription.
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct WordLit(pub Option<Width>, pub u64);
142
143/// A type classifier for values.
144#[derive(Clone, Debug)]
145pub enum Type {
146    /// An n-bit two's complement integer. Nominally unsigned. Written `Word<n>`.
147    Word(Width),
148    /// A n-element vector. Written `Vec<T, n>`.
149    Vec(Box<Type>, Length),
150    /// An optional value. Written `Valid<T>`.
151    Valid(Box<Type>),
152    /// An unresolved reference to a user-defined type.
153    TypeRef(String),
154}
155
156/// A [`When`] statement drives procedural logic.
157#[derive(Debug, Clone)]
158pub struct When(pub Box<Expr>, pub Vec<Wire>);
159
160pub fn parse_package_from_string(package_text: &str) -> Result<Package, Vec<BitsyError>> {
161    let source_info = SourceInfo::from_string(package_text);
162    match grammar::PackageParser::new().parse(&source_info, &package_text) {
163        Err(ParseError::UnrecognizedToken { token, expected }) => {
164            let start_idx = token.0;
165            let end_idx = token.2;
166            let loc = Loc::from(&source_info, start_idx, end_idx);
167
168            let message = format!("Parse error: Expected one of {}", expected.join(" "));
169            return Err(vec![BitsyError::ParseError(loc, message)]);
170        },
171        Err(ParseError::InvalidToken { location }) => {
172            let loc = Loc::from(&source_info, location, location + 1);
173            let message = format!("Parse error");
174            return Err(vec![BitsyError::ParseError(loc, message)]);
175        },
176        Err(ParseError::ExtraToken { token }) => {
177            let start_idx = token.0;
178            let end_idx = token.2;
179            let loc = Loc::from(&source_info, start_idx, end_idx);
180            let message = format!("Parse error: extra token: {token:?}");
181            return Err(vec![BitsyError::ParseError(loc, message)]);
182        },
183        Err(ParseError::UnrecognizedEof { location, expected }) => {
184            let loc = Loc::from(&source_info, location, location + 1);
185            let message = format!("Parse error: Unexpected end of file: Expected {expected:?}");
186            return Err(vec![BitsyError::ParseError(loc, message)]);
187        },
188        Err(ParseError::User { error }) => {
189            let message = format!("Parse error: {error:?}");
190            return Err(vec![BitsyError::ParseError(Loc::unknown(), message)]);
191        },
192        Ok(package) => Ok(package),
193    }
194}