1use {
4 proc_macro2::TokenStream,
5 quote::{quote_spanned, ToTokens},
6 syn::{Error, Lifetime, Type},
7 thunderdome::Index,
8};
9
10use crate::{
11 cfg::{Cfg, Ir},
12 target::Target,
13 CompileError, Spanned,
14};
15
16#[derive(Debug, Clone)]
19pub struct Invocation {
20 pub syntax: Spanned<Syntax>,
22}
23
24impl Invocation {
25 pub fn compile(&self) -> Result<Spanned<Target>, Error> {
27 compile(&self.syntax)
28 }
29}
30
31#[derive(Debug, Clone)]
37pub enum Syntax {
38 Recv(Type),
40 Send(Type),
42 Call(Box<Spanned<Syntax>>),
44 Choose(Vec<Spanned<Syntax>>),
46 Offer(Vec<Spanned<Syntax>>),
48 Split {
50 tx_only: Box<Spanned<Syntax>>,
52 rx_only: Box<Spanned<Syntax>>,
54 },
55 Loop(Option<String>, Box<Spanned<Syntax>>),
57 Break(Option<String>),
59 Continue(Option<String>),
61 Block(Vec<Spanned<Syntax>>),
63 Type(Type),
65}
66
67impl Syntax {
68 pub fn recv(ty: &str) -> Self {
74 Syntax::Recv(syn::parse_str(ty).unwrap())
75 }
76
77 pub fn send(ty: &str) -> Self {
83 Syntax::Send(syn::parse_str(ty).unwrap())
84 }
85
86 pub fn call(callee: impl Into<Spanned<Syntax>>) -> Self {
88 Syntax::Call(Box::new(callee.into()))
89 }
90
91 pub fn loop_(label: Option<String>, body: impl Into<Spanned<Syntax>>) -> Self {
93 Syntax::Loop(label, Box::new(body.into()))
94 }
95
96 pub fn type_(ty: &str) -> Self {
102 Syntax::Type(syn::parse_str(ty).unwrap())
103 }
104}
105
106pub fn compile(syntax: &Spanned<Syntax>) -> Result<Spanned<Target>, Error> {
109 let mut cfg = Cfg::new();
110 let head = to_cfg(syntax, &mut cfg, &mut Vec::new()).0;
111 cfg.resolve_scopes(head);
112 cfg.report_dead_code(head);
113 cfg.generate_target(head)
114}
115
116fn to_cfg<'a>(
122 syntax: &'a Spanned<Syntax>,
123 cfg: &mut Cfg,
124 env: &mut Vec<(&'a Option<String>, Index)>,
125) -> (Option<Index>, Option<Index>) {
126 let mut convert_jump_to_cfg = |label: &Option<String>,
132 outside_loop_error: CompileError,
133 constructor: fn(Index) -> Ir|
134 -> (Option<Index>, Option<Index>) {
135 let node = match env.last() {
136 None => cfg.create_error(outside_loop_error, syntax.span),
138 Some((_, index)) => match label {
140 None => cfg.spanned(constructor(*index), syntax.span),
142 Some(label) => {
144 let found_index = env.iter().rev().find(|&l| l.0.as_ref() == Some(label));
145 match found_index {
146 None => cfg.create_error(
148 CompileError::UndeclaredLabel(label.clone()),
149 syntax.span,
150 ),
151 Some((_, index)) => cfg.spanned(constructor(*index), syntax.span),
153 }
154 }
155 },
156 };
157
158 (Some(node), Some(node))
159 };
160
161 use Syntax::*;
162 let ir = match &syntax.inner {
163 Recv(ty) => Ir::Recv(ty.clone()),
164 Send(ty) => Ir::Send(ty.clone()),
165 Type(ty) => Ir::Type(ty.clone()),
166 Call(callee) => {
167 let callee_node = to_cfg(callee, cfg, env).0;
168 Ir::Call(callee_node)
169 }
170 Split { tx_only, rx_only } => {
171 let tx_only = to_cfg(tx_only, cfg, env).0;
172 let rx_only = to_cfg(rx_only, cfg, env).0;
173 Ir::Split { tx_only, rx_only }
174 }
175 Choose(choices) => {
176 let choice_nodes = choices
177 .iter()
178 .map(|choice| to_cfg(choice, cfg, env).0)
179 .collect();
180 Ir::Choose(choice_nodes)
181 }
182 Offer(choices) => {
183 let choice_nodes = choices
184 .iter()
185 .map(|choice| to_cfg(choice, cfg, env).0)
186 .collect();
187 Ir::Offer(choice_nodes)
188 }
189 Continue(label) => {
190 return convert_jump_to_cfg(label, CompileError::ContinueOutsideLoop, Ir::Continue)
191 }
192 Break(label) => {
193 return convert_jump_to_cfg(label, CompileError::BreakOutsideLoop, Ir::Break)
194 }
195 Loop(maybe_label, body) => {
196 let ir_node = cfg.spanned(Ir::Loop(None), syntax.span);
202
203 env.push((maybe_label, ir_node)); let head = to_cfg(body, cfg, env).0;
206 let _ = env.pop(); cfg[ir_node].expr = Ir::Loop(head);
210
211 if maybe_label.is_some() && env.iter().any(|scope| scope.0 == maybe_label) {
214 cfg.insert_error_at(
215 ir_node,
216 CompileError::ShadowedLabel(maybe_label.clone().unwrap()),
217 );
218 }
219
220 return (Some(ir_node), Some(ir_node));
223 }
224 Block(statements) => {
225 let mut next_head = None;
228 let mut next_tail = None;
229 for stmt in statements.iter().rev() {
230 let (head, tail) = to_cfg(stmt, cfg, env);
231 next_tail = next_tail.or(tail);
232
233 if let Some(tail) = tail {
234 cfg[tail].next = next_head
235 }
236
237 next_head = head;
238 }
239
240 return (next_head, next_tail);
243 }
244 };
245
246 let node = cfg.spanned(ir, syntax.span);
247 (Some(node), Some(node))
248}
249
250impl Spanned<Syntax> {
251 pub fn to_token_stream_with<F: ?Sized>(&self, add_optional: &mut F) -> TokenStream
254 where
255 F: FnMut() -> bool,
256 {
257 let mut acc = TokenStream::new();
258 self.to_tokens_with(add_optional, &mut acc);
259 acc
260 }
261
262 pub fn to_tokens_with<F: ?Sized>(&self, mut add_optional: &mut F, tokens: &mut TokenStream)
268 where
269 F: FnMut() -> bool,
270 {
271 use Syntax::*;
272
273 let sp = self.span;
274
275 let choice_arms_to_tokens =
276 |add_optional: &mut dyn FnMut() -> bool, arms: &[Spanned<Syntax>]| -> TokenStream {
277 let mut acc = TokenStream::new();
278 for (i, choice) in arms.iter().enumerate() {
279 quote_spanned!(sp=> #i => ).to_tokens(&mut acc);
280 choice.to_tokens_with(add_optional, &mut acc);
281 if i < arms.len() - 1 || add_optional() {
282 quote_spanned!(sp=> ,).to_tokens(&mut acc);
283 }
284 }
285 acc
286 };
287
288 match &self.inner {
289 Recv(t) => quote_spanned! {sp=> recv #t },
290 Send(t) => quote_spanned! {sp=> send #t },
291 Call(callee) => {
292 let mut acc = TokenStream::new();
293 quote_spanned!(sp=> call ).to_tokens(&mut acc);
294
295 if matches!(callee.inner, Block(_) | Type(_)) {
296 callee.to_tokens_with(add_optional, &mut acc);
297 } else {
298 let callee_tokens = callee.to_token_stream_with(add_optional);
299 quote_spanned!(sp=> { #callee_tokens }).to_tokens(&mut acc);
300 }
301
302 acc
303 }
304 Split { tx_only, rx_only } => {
305 let tx = tx_only.to_token_stream_with(add_optional);
306 let rx = rx_only.to_token_stream_with(add_optional);
307 quote_spanned! {sp=> split { -> #tx, <- #rx, } }
308 }
309 Choose(choices) => {
310 let arms = choice_arms_to_tokens(&mut add_optional, choices);
311 quote_spanned! {sp=> choose { #arms } }
312 }
313 Offer(choices) => {
314 let arms = choice_arms_to_tokens(&mut add_optional, choices);
315 quote_spanned! {sp=> offer { #arms } }
316 }
317 Loop(None, body) => {
318 let body_tokens = body.to_token_stream_with(add_optional);
319 quote_spanned! {sp=> loop #body_tokens }
320 }
321 Loop(Some(label), body) => {
322 let lt = Lifetime::new(&format!("'{}", label), sp);
323 let body_tokens = body.to_token_stream_with(add_optional);
324 quote_spanned! {sp=> #lt: loop #body_tokens }
325 }
326 Break(None) => quote_spanned! {sp=> break },
327 Break(Some(label)) => {
328 let lt = Lifetime::new(&format!("'{}", label), sp);
329 quote_spanned! {sp=> break #lt }
330 }
331 Continue(None) => quote_spanned! {sp=> continue },
332 Continue(Some(label)) => {
333 let lt = Lifetime::new(&format!("'{}", label), sp);
334 quote_spanned! {sp=> continue #lt }
335 }
336 Block(stmts) => {
337 let mut acc = TokenStream::new();
338
339 if let Some((last, up_to_penultimate)) = stmts.split_last() {
342 for stmt in up_to_penultimate {
343 stmt.to_tokens_with(add_optional, &mut acc);
344
345 let is_call_of_block = matches!(
348 &stmt.inner,
349 Call(callee) if matches!(&callee.inner, Block(_)),
350 );
351
352 let ends_with_block = matches!(
356 &stmt.inner,
357 Block(_) | Split { .. } | Offer(_) | Choose(_) | Loop(_, _),
358 );
359
360 if !(is_call_of_block || ends_with_block) || add_optional() {
361 quote_spanned!(sp=> ;).to_tokens(&mut acc);
362 }
363 }
364
365 last.to_tokens_with(add_optional, &mut acc);
366
367 if add_optional() {
368 quote_spanned!(sp=> ;).to_tokens(&mut acc);
369 }
370 }
371
372 quote_spanned!(sp=> { #acc })
373 }
374 Type(ty) => quote_spanned! {sp=> #ty },
375 }
376 .to_tokens(tokens);
377 }
378}
379
380impl ToTokens for Spanned<Syntax> {
381 fn to_tokens(&self, tokens: &mut TokenStream) {
382 self.to_tokens_with(&mut || false, tokens);
383 }
384}
385
386#[cfg(feature = "quickcheck")]
387mod tests {
388 use super::*;
389
390 use {
391 quickcheck::{Arbitrary, Gen},
392 syn::parse_quote,
393 };
394
395 #[derive(Debug, Clone)]
396 struct Label(String);
397
398 impl Arbitrary for Label {
399 fn arbitrary(g: &mut Gen) -> Self {
400 let s = *g
401 .choose(&[
402 "foo", "bar", "baz", "qux", "quux", "corge", "grault", "garply", "waldo",
403 "fred", "plugh", "xyzzy", "thud", "wibble", "wobble", "wubble", "flob",
404 ])
405 .unwrap();
406 Label(s.to_owned())
407 }
408 }
409
410 impl Arbitrary for Spanned<Syntax> {
417 fn arbitrary(g: &mut Gen) -> Self {
418 use Syntax::*;
419 let g = &mut Gen::new(g.size().max(2) - 1);
423 let syntax = match *g
424 .choose(&[
425 "recv", "send", "call", "choose", "offer", "split", "loop", "break",
426 "continue", "block", "type",
427 ])
428 .unwrap()
429 {
430 "recv" => Recv(parse_quote!(())),
431 "send" => Send(parse_quote!(())),
432 "call" => match *g.choose(&["block", "type"]).unwrap() {
437 "block" => Call(Box::new(Spanned::from(Block(Arbitrary::arbitrary(g))))),
438 "type" => Type(parse_quote!(())),
439 other => unreachable!("{}", other),
440 },
441 "choose" => Choose(Arbitrary::arbitrary(g)),
442 "offer" => Offer(Arbitrary::arbitrary(g)),
443 "split" => Split {
444 tx_only: Arbitrary::arbitrary(g),
445 rx_only: Arbitrary::arbitrary(g),
446 },
447 "loop" => Loop(
448 <Option<Label>>::arbitrary(g).map(|l| l.0),
449 Box::new(Spanned::from(Block(Arbitrary::arbitrary(g)))),
450 ),
451 "break" => Break(<Option<Label>>::arbitrary(g).map(|l| l.0)),
452 "continue" => Continue(<Option<Label>>::arbitrary(g).map(|l| l.0)),
453 "block" => Block(Arbitrary::arbitrary(g)),
454 "type" => Type(parse_quote!(())),
455 other => unreachable!("{}", other),
456 };
457
458 Spanned::from(syntax)
459 }
460
461 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
464 use Syntax::*;
465 let span = self.span;
466 let v = match &self.inner {
467 Recv(_) | Send(_) | Type(_) | Break(None) | Continue(None) => vec![],
468 Call(callee) => callee.shrink().map(Call).collect(),
469 Split { tx_only, rx_only } => tx_only
470 .shrink()
471 .flat_map(|tx_shrunk| {
472 rx_only.shrink().map(move |rx_shrunk| Split {
473 tx_only: tx_shrunk.clone(),
474 rx_only: rx_shrunk,
475 })
476 })
477 .collect(),
478 Choose(choices) => choices.shrink().map(Choose).collect(),
479 Offer(choices) => choices.shrink().map(Offer).collect(),
480 Loop(label, body) => body
481 .shrink()
482 .map(|body_shrunk| Loop(label.clone(), body_shrunk))
483 .chain(label.as_ref().map(|_| Loop(None, body.clone())))
484 .collect(),
485 Block(stmts) => stmts.shrink().map(Block).collect(),
486 Break(Some(_)) => vec![Break(None)],
487 Continue(Some(_)) => vec![Continue(None)],
488 };
489
490 Box::new(v.into_iter().map(move |inner| Spanned { inner, span }))
491 }
492 }
493}