1use crate::ast::linkage::Linkage;
2use crate::ast::types::{AlignSpec, ExtendedType};
3use crate::ast::{FloatLiteral, GlobalName, Number, NumericLiteral, Span, StringLiteral};
4use crate::lexer::{Token, TokenParser, keyword, operator};
5use crate::parse::{Parse, impl_fromstr_via_parse, maybe_newline};
6use std::fmt::{self, Display, Formatter, Write};
7
8use crate::print::{IndentedPrinter, impl_display_via_print};
9use chumsky::prelude::*;
10
11#[derive(Debug, Eq, PartialEq, Hash, Clone)]
12pub struct DataDef {
13 pub span: Span,
14 pub linkage: Linkage,
15 pub name: GlobalName,
16 pub align: Option<AlignSpec>,
17 pub fields: Vec<DataField>,
18}
19impl DataDef {
20 pub(crate) fn span(&self) -> Span {
22 self.span
23 }
24 fn print(&self, out: &mut IndentedPrinter) -> fmt::Result {
25 if self.linkage != Linkage::default() {
26 write!(out, "{} ", self.linkage)?;
27 }
28 write!(out, "data {} = ", self.name)?;
29 if let Some(ref align) = self.align {
30 write!(out, "{align} ")?;
31 }
32 out.write_char('{')?;
33 out.indented(|out| {
34 out.maybe_writeln()?;
35 out.print_separated_with(",\n", &self.fields, |field, out| write!(out, "{field}"))
36 })?;
37 out.maybe_writeln()?;
38 out.write_char('}')
39 }
40}
41impl_display_via_print!(DataDef);
42
43impl Parse for DataDef {
44 const DESC: &'static str = "data definition";
45 #[allow(clippy::unused_unit, reason = "part of select macro")]
46 fn parser<'a>() -> impl TokenParser<'a, Self> {
47 let body = DataField::parser()
48 .padded_by(maybe_newline())
49 .separated_by(operator!(,).parser())
50 .allow_trailing()
51 .collect::<Vec<_>>()
52 .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace));
53 Linkage::parser()
54 .then_ignore(keyword!(data).parser().padded_by(maybe_newline()))
55 .then(GlobalName::parser().padded_by(maybe_newline()))
56 .then_ignore(operator!(=).parser().padded_by(maybe_newline()))
57 .then(AlignSpec::parser().padded_by(maybe_newline()).or_not())
58 .then(body)
59 .map_with(|(((linkage, name), align), body), extra| DataDef {
60 span: extra.span(),
61 linkage,
62 name,
63 align,
64 fields: body,
65 })
66 .labelled(Self::DESC)
67 }
68}
69
70#[derive(Debug, Eq, PartialEq, Hash, Clone)]
72#[non_exhaustive]
73pub enum DataField {
74 Regular {
75 span: Span,
76 ty: ExtendedType,
77 items: Vec<DataItem>,
78 },
79 ZeroInitialize {
80 span: Span,
81 count: NumericLiteral<u64>,
82 },
83}
84impl Parse for DataField {
85 const DESC: &'static str = "data field";
86 fn parser<'a>() -> impl TokenParser<'a, Self> {
87 let regular_field = ExtendedType::parser()
88 .then(
89 DataItem::parser()
90 .repeated()
91 .at_least(1)
92 .collect::<Vec<_>>(),
93 )
94 .map_with(|(ty, items), extra| DataField::Regular {
95 ty,
96 span: extra.span(),
97 items,
98 })
99 .labelled("regular field");
100 let zero_init = operator!(z)
101 .parser()
102 .ignore_then(Token::number())
103 .map_with(|count, extra| DataField::ZeroInitialize {
104 span: extra.span(),
105 count,
106 })
107 .labelled("zero initialized field");
108 zero_init.or(regular_field).labelled(Self::DESC)
109 }
110}
111impl Display for DataField {
112 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113 match self {
114 DataField::Regular { span: _, ty, items } => {
115 write!(f, "{ty}")?;
116 for val in items {
117 write!(f, " {val}")?;
118 }
119 Ok(())
120 }
121 DataField::ZeroInitialize { span: _, count } => {
122 write!(f, "z {count}")
123 }
124 }
125 }
126}
127#[derive(Debug, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
132pub struct SymbolOffset(NumericLiteral<u64>);
133impl SymbolOffset {
134 pub fn unspanned<T: Number>(value: T) -> Self
135 where
136 Self: From<NumericLiteral<T>>,
137 {
138 NumericLiteral::unspanned(value).into()
139 }
140}
141impl From<NumericLiteral<u64>> for SymbolOffset {
142 fn from(value: NumericLiteral<u64>) -> Self {
143 SymbolOffset(value)
144 }
145}
146impl Parse for SymbolOffset {
147 const DESC: &'static str = "symbol offset";
148
149 fn parser<'a>() -> impl TokenParser<'a, Self> {
150 Token::number().map(SymbolOffset)
151 }
152}
153impl Display for SymbolOffset {
154 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155 Display::fmt(&self.0, f)
156 }
157}
158#[derive(Debug, Eq, PartialEq, Hash, Clone)]
159#[non_exhaustive]
160pub enum DataItem {
161 SymbolRefWithOffset {
166 span: Span,
167 name: GlobalName,
168 offset: SymbolOffset,
169 },
170 String(StringLiteral),
171 Constant(Constant),
172}
173impl Parse for DataItem {
174 const DESC: &'static str = "data item";
175
176 fn parser<'a>() -> impl TokenParser<'a, Self> {
177 let offset_symbol_ref = GlobalName::parser()
178 .then_ignore(operator!(+).parser().padded_by(maybe_newline()))
179 .then(SymbolOffset::parser())
180 .map_with(|(name, offset), extra| DataItem::SymbolRefWithOffset {
181 span: extra.span(),
182 name,
183 offset,
184 })
185 .labelled("symbol and offset");
186 choice((
187 offset_symbol_ref,
188 StringLiteral::parser().map(DataItem::String),
189 Constant::parser().map(DataItem::Constant),
190 ))
191 .labelled(Self::DESC)
192 }
193}
194impl Display for DataItem {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 match self {
197 DataItem::SymbolRefWithOffset {
198 span: _,
199 name,
200 offset,
201 } => {
202 write!(f, "{name} + {offset}")
203 }
204 DataItem::String(lit) => write!(f, "{lit}"),
205 DataItem::Constant(value) => write!(f, "{value}"),
206 }
207 }
208}
209
210#[derive(PartialEq, Eq, Clone, Debug, Hash)]
211#[non_exhaustive]
212pub enum Constant {
213 Integer(NumericLiteral<i128>),
214 Float(FloatLiteral),
215 SymbolRef(GlobalName),
216}
217impl Parse for Constant {
218 const DESC: &'static str = "constant";
219 fn parser<'a>() -> impl TokenParser<'a, Self> {
220 select! {
221 Token::GlobalName(name) => Constant::SymbolRef(name),
222 Token::Float(literal) => Constant::Float(literal),
223 Token::Number(value) => Constant::Integer(value.map_value(i128::from)),
224 Token::Integer(value) => Constant::Integer(value),
225 }
226 .labelled(Self::DESC)
227 }
228}
229impl_fromstr_via_parse!(Constant);
230impl Display for Constant {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 match self {
233 Constant::Integer(i) => write!(f, "{i}"),
234 Constant::Float(lit) => write!(f, "{lit}"),
235 Constant::SymbolRef(symbol) => write!(f, "{symbol}"),
236 }
237 }
238}
239impl From<FloatLiteral> for Constant {
240 fn from(value: FloatLiteral) -> Self {
241 Constant::Float(value)
242 }
243}
244macro_rules! impl_from_primint {
245 ($($target:ty),+ $(,)?) => {
246 $(impl From<$target> for Constant {
247 fn from(value: $target) -> Self {
248 (value as i128).into()
249 }
250 })*
251 };
252}
253impl_from_primint!(i32, i64, isize, u32, u64, usize);
254impl From<i128> for Constant {
255 fn from(value: i128) -> Self {
256 NumericLiteral::unspanned(value).into()
257 }
258}
259impl From<NumericLiteral<i128>> for Constant {
260 fn from(value: NumericLiteral<i128>) -> Self {
261 Constant::Integer(value)
262 }
263}
264
265#[cfg(test)]
266mod test {
267 use super::*;
268 use crate::parse::test_parse_print;
269 use similar_asserts::assert_eq;
270
271 fn integer(x: i128) -> Constant {
272 Constant::Integer(NumericLiteral::unspanned(x))
273 }
274
275 macro_rules! test_constants {
276 ($($text:literal => $value:expr),+ $(,)?) => {
277 test_parse_print! {
278 parse_constant, print_constant for Constant {
279 $($text => $value),*
280 }
281 }
282 test_parse_print! {
283 parse_constant_data_item, print_constant_data_item for DataItem {
284 $($text => DataItem::Constant($value)),*
285 }
286 }
287 };
288 }
289 test_constants! {
290 "128" => integer(128),
291 "-128" => integer(-128),
292 "d_8.7" => Constant::Float(FloatLiteral::double_unspanned(8.7)),
293 "s_8.7" => Constant::Float(FloatLiteral::single_unspanned(8.7)),
294 "$foo" => Constant::SymbolRef(GlobalName::unspanned("foo")),
295 }
296 test_parse_print! {
297 parse_data_item, print_data_item for DataItem {
299 "$foo + 3" => DataItem::SymbolRefWithOffset {
300 span: Span::MISSING,
301 name: GlobalName::unspanned("foo"),
302 offset: SymbolOffset::unspanned(3),
303 },
304 "$foo" => DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
306 "\"foo\"" => DataItem::String(StringLiteral::unspanned("foo")),
307 }
308 }
309 test_parse_print! {
310 parse_data_field, print_data_field for DataField {
311 "w 12 $foo \"bar\"" => DataField::Regular {
312 ty: ExtendedType::Word,
313 span: Span::MISSING,
314 items: vec![
315 DataItem::Constant(integer(12)),
316 DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
317 DataItem::String(StringLiteral::unspanned("bar")),
318 ]
319 },
320 "z 80" => DataField::ZeroInitialize {
321 span: Span::MISSING,
322 count: NumericLiteral::unspanned(80),
323 }
324 }
325 }
326 test_parse_print! {
327 parse_data_def, print_data_def for DataDef {
328 "export data $example = align 16 {
329 w 12 $foo,
330 z 80,
331 l \"bar\"
332 }" => DataDef {
333 linkage: Linkage::builder().with_export().build(),
334 span: Span::MISSING,
335 name: GlobalName::unspanned("example"),
336 align: Some(AlignSpec::unspanned(16)),
337 fields: vec![
338 DataField::Regular {
339 ty: ExtendedType::Word,
340 span: Span::MISSING,
341 items: vec![
342 DataItem::Constant(integer(12)),
343 DataItem::Constant(Constant::SymbolRef(GlobalName::unspanned("foo"))),
344 ]
345 },
346 DataField::ZeroInitialize {
347 span: Span::MISSING,
348 count: NumericLiteral::unspanned(80),
349 },
350 DataField::Regular {
351 ty: ExtendedType::Long,
352 span: Span::MISSING,
353 items: vec![DataItem::String(StringLiteral::unspanned("bar"))]
354 }
355 ]
356 }
357 }
358 }
359}