1#![feature(proc_macro_span, proc_macro_diagnostic, proc_macro_def_site)]
2
3#![doc = include_str!("../README.md")]
4
5extern crate proc_macro;
6extern crate itertools;
7
8use itertools::Itertools;
9
10mod syntax;
11
12use proc_macro::{
13 TokenStream,
14 TokenTree,
15 Span,
16 Group,
17 Delimiter,
18 Literal,
19 Spacing,
20};
21
22use self::syntax::{
23 Expr,
24 Pat,
25 Stmt,
26 from_source,
27 new_ident,
28 new_spanned_ident,
29 new_block,
30 surround,
31};
32
33use std::iter::once;
34use std::iter::FromIterator;
35use std::collections::VecDeque;
36
37type Result<T> = ::std::result::Result<T, ()>;
38
39#[proc_macro]
73pub fn command(input: TokenStream) -> TokenStream {
74 match try_expand_command(input) {
75 Ok(stream) => stream,
76 Err(()) => "::std::process::Command::new(\"dummy\")".parse().unwrap(),
77 }
78}
79
80fn try_expand_command(input: TokenStream) -> Result<TokenStream> {
81 let trees = Parser::new(input).parse()?;
82 Ok(generate(trees)?.into_stream())
83}
84
85#[derive(Debug)]
88enum Condition {
89 Bool(Expr),
90 IfLet(TokenTree, Pat, TokenTree, Expr),
91}
92
93#[derive(Debug)]
94struct Spanned<T> {
95 elem: T,
96 span: Span,
97}
98
99#[allow(non_snake_case)]
100fn Spanned<T>(elem: T, span: Span) -> Spanned<T> {
101 Spanned { elem, span }
102}
103
104#[derive(Debug)]
105struct Block(Spanned<Vec<Tree>>);
106
107#[derive(Debug)]
108enum Splice {
109 Word(String), Literal(Literal), ToStr(Expr), AsOsStr(Expr), }
114
115#[derive(Debug)]
116enum Arg {
117 Single(Spanned<Splice>),
118 Touching(Vec<Spanned<Splice>>),
119}
120
121#[derive(Debug)]
122struct For {
123 for_span: Span,
124 pat: Pat,
125 in_tt: TokenTree,
126 expr: Expr,
127 block: Block,
128}
129
130#[derive(Debug)]
131struct If {
132 if_span: Span,
133 cond: Condition,
134 then_block: Block,
135 else_block: Block,
136}
137
138type Arm = (Pat, (TokenTree, TokenTree), Block);
139
140#[derive(Debug)]
141struct Match {
142 match_span: Span,
143 expr: Expr,
144 block_span: Span,
145 arms: Vec<Arm>,
146}
147
148#[derive(Debug)]
149enum Tree {
150 Arg(Arg),
151 Args(Spanned<Expr>), Cmd(Expr), If(If),
154 Match(Match),
155 For(For),
156}
157
158impl Arg {
159 fn span(&self) -> Span {
160 match self {
161 Arg::Single(splice) => splice.span,
162 Arg::Touching(splices) => {
163 splices.first().unwrap().span.join(
164 splices.last().unwrap().span
165 ).unwrap()
166 }
167 }
168 }
169
170 fn into_vec(self) -> Vec<Spanned<Splice>> {
171 match self {
172 Arg::Single(splice) => vec![splice],
173 Arg::Touching(splices) => splices,
174 }
175 }
176}
177
178impl Tree {
179 fn span(&self) -> Span {
180 match self {
181 Tree::Arg(arg) => arg.span(),
182 Tree::Args(args) => args.span,
183 Tree::Cmd(expr) => expr.span(),
184 Tree::If(if_) => if_.span(),
185 Tree::Match(match_) => match_.span(),
186 Tree::For(for_) => for_.span(),
187 }
188 }
189}
190
191impl From<Arg> for Tree {
192 fn from(arg: Arg) -> Tree {
193 Tree::Arg(arg)
194 }
195}
196
197impl From<Spanned<Splice>> for Arg {
198 fn from(splice: Spanned<Splice>) -> Arg {
199 Arg::Single(splice)
200 }
201}
202
203impl From<Spanned<Splice>> for Tree {
204 fn from(splice: Spanned<Splice>) -> Tree {
205 Arg::from(splice).into()
206 }
207}
208
209impl Block {
210 fn new(trees: Vec<Tree>, span: Span) -> Block { Block(Spanned(trees, span)) }
211}
212
213impl If {
214 fn span(&self) -> Span {
215 let last_span = if self.else_block.0.elem.is_empty() {
216 self.then_block.0.span
217 } else {
218 self.else_block.0.span
219 };
220 self.if_span.join(last_span).unwrap_or(self.if_span)
221 }
222}
223
224impl For {
225 fn span(&self) -> Span {
226 self.for_span.join(self.block.0.span).unwrap_or(self.for_span)
227 }
228}
229
230impl Match {
231 fn span(&self) -> Span {
232 self.match_span.join(self.block_span).unwrap_or(self.match_span)
233 }
234}
235
236fn generate(mut trees: Vec<Tree>) -> Result<Expr> {
239 if trees.is_empty() {
240 Span::call_site().error("This macro needs at least the command name").emit();
241 return Err(());
242 }
243
244 let cmd_tree = trees.remove(0);
245 let cmd_expr: Expr = match cmd_tree {
246 Tree::Arg(arg) => {
247 let span = arg.span();
248 let str_expr = generate_os_str(arg)?;
249 Expr::call(from_source("::std::process::Command::new", span), str_expr, span)
250 }
251 Tree::Cmd(cmd) => cmd,
252 other => {
253 other.span().error("Command name should be `cmd` `(cmd_name_expr)` or `{Command_expr}`").emit();
254 return Err(())
255 }
256 };
257
258 let cmd_var: TokenTree = new_ident("cmd");
259
260 let init_stmt = Stmt::new_let(&cmd_var, cmd_expr);
261 let mut stmts: Vec<Stmt> = vec![init_stmt];
262 stmts.extend(generate_stmts(&cmd_var, trees)?);
263
264 let block = Expr::block(stmts, Expr::from_tt(cmd_var), Span::call_site());
265
266 Ok(block)
267}
268
269fn generate_stmts(cmd_var: &TokenTree, trees: Vec<Tree>) -> Result<Vec<Stmt>> {
270 trees
271 .into_iter()
272 .map(|tree| {
273 match tree {
274 Tree::Arg(arg) => generate_arg(cmd_var, arg),
275 Tree::Args(args) => generate_args(cmd_var, args),
276 Tree::For(pieces) => generate_for(cmd_var, pieces),
277 Tree::If(pieces) => generate_if(cmd_var, pieces),
278 Tree::Match(pieces) => generate_match(cmd_var, pieces),
279 Tree::Cmd(expr) => {
280 expr.span()
281 .error("Command block could be used only at the beginning")
282 .emit();
283 return Err(())
284 }
285 }
286 })
287 .collect()
288}
289
290fn generate_block(cmd_var: &TokenTree, block: Block) -> Result<TokenTree> {
291 let stmts = generate_stmts(cmd_var, block.0.elem)?;
292 Ok(new_block(stmts, block.0.span))
293}
294
295fn generate_arg(cmd: &TokenTree, arg: Arg) -> Result<Stmt> {
296 let span = arg.span();
297 let os_str = generate_os_str(arg)?;
298 let call_expr = Expr::call_method_on(cmd, "arg", os_str, span);
299 Ok(call_expr.into_stmt())
300}
301
302fn generate_args(cmd: &TokenTree, Spanned { elem: expr, span }: Spanned<Expr>) -> Result<Stmt> {
303 let call_expr = Expr::call_method_on(cmd, "args", expr, span);
304 Ok(call_expr.into_stmt())
305}
306
307fn generate_for(cmd_var: &TokenTree, For { for_span, pat, in_tt, expr, block }: For) -> Result<Stmt> {
308 let stream = once(new_spanned_ident("for", for_span))
309 .chain(pat.0)
310 .chain(once(in_tt))
311 .chain(expr.into_stream())
312 .chain(once(generate_block(cmd_var, block)?))
313 .collect();
314 Ok(Stmt::from_stream(stream))
315}
316
317fn generate_if(cmd_var: &TokenTree, If { if_span, cond, then_block, else_block }: If) -> Result<Stmt> {
318 let cond_stream = match cond {
319 Condition::Bool(expr) => expr.into_stream(),
320 Condition::IfLet(let_tt, pat, equals_tt, expr) => {
321 once(let_tt)
322 .chain(pat.0)
323 .chain(once(equals_tt))
324 .chain(expr.into_stream())
325 .collect()
326 }
327 };
328 let stream = once(new_spanned_ident("if", if_span))
329 .chain(cond_stream)
330 .chain(once(generate_block(cmd_var, then_block)?))
331 .chain(once(new_spanned_ident("else", Span::call_site())))
332 .chain(once(generate_block(cmd_var, else_block)?))
333 .collect();
334 Ok(Stmt::from_stream(stream))
335}
336
337fn generate_match(cmd_var: &TokenTree, Match { match_span, expr, block_span, arms }: Match) -> Result<Stmt> {
338 let mut arm_stream = Vec::new();
339 for arm in arms {
340 arm_stream.extend(generate_arm(cmd_var, arm)?);
341 }
342 let arm_stream = arm_stream.into_iter().collect();
343
344 let block = surround(arm_stream, Delimiter::Brace, block_span);
345
346 let stream = once(new_spanned_ident("match", match_span))
347 .chain(expr.into_stream())
348 .chain(once(block))
349 .collect();
350
351 Ok(Stmt::from_stream(stream))
352}
353
354fn generate_arm(cmd_var: &TokenTree, (pat, arrows, block): Arm) -> Result<impl Iterator<Item=TokenTree>> {
355 Ok(
356 pat.0.into_iter()
357 .chain(once(arrows.0))
358 .chain(once(arrows.1))
359 .chain(once(generate_block(cmd_var, block)?))
360 )
361}
362
363fn generate_splice(Spanned { elem: splice, span }: Spanned<Splice>) -> Result<Expr> {
364 let expr = match splice {
365 Splice::Word(word) => Expr::string_literal(&word),
366 Splice::Literal(lit) => generate_literal(lit)?,
367 Splice::AsOsStr(expr) => Expr::reference(expr, span),
368 Splice::ToStr(expr) => Expr::call(
369 from_source("ToString::to_string", span),
370 Expr::reference(expr, span),
371 span
372 ),
373 };
374 Ok(expr)
375}
376
377fn generate_literal(literal: Literal) -> Result<Expr> {
378 let repr = literal.to_string();
379 if repr.starts_with("'") {
380 literal.span().error("Use string literals instead").emit();
381 Ok(Expr::string_literal("<error>"))
382 } else if repr.contains("\"") {
383 Ok(Expr::from_tt(literal.into()))
384 } else if repr.contains("'") {
385 literal.span().error("Unsupported literal").emit();
386 Ok(Expr::string_literal("<error>"))
387 } else {
388 Ok(Expr::string_literal(&literal.to_string()))
389 }
390}
391
392fn generate_os_str(arg: Arg) -> Result<Expr> {
393 let full_span = arg.span();
394 match arg {
395 Arg::Single(splice) => generate_splice(splice),
396 Arg::Touching(splices) => {
397 let os_string = Expr::from_source("::std::ffi::OsString::new()", full_span);
398 let buf_var = new_ident("buf");
399 let init_stmt = Stmt::new_let(&buf_var, os_string);
400 let mut stmts = vec![init_stmt];
401
402 for splice in splices {
403 let span = splice.span;
404 stmts.push(Expr::call_method_on(
405 &buf_var,
406 "push",
407 Expr::reference(generate_splice(splice)?, span),
408 span,
409 ).into_stmt())
410 }
411
412 Ok(Expr::block(stmts, Expr::from_tt(buf_var), full_span))
413 }
414 }
415}
416
417#[derive(Debug)]
420struct Parser {
421 last_span: Option<Span>,
422 stream: VecDeque<TokenTree>,
423}
424
425impl Parser {
426 pub fn new(stream: TokenStream) -> Self {
427 Parser {
428 stream: VecDeque::from_iter(stream),
429 last_span: None
430 }
431 }
432
433 pub fn parse(&mut self) -> Result<Vec<Tree>> {
434 let trees = self.parse_trees()?;
435 Ok(join_touching(trees))
436 }
437}
438
439fn join_touching(trees: Vec<Tree>) -> Vec<Tree> {
440 trees.into_iter()
441 .coalesce(|left, right| {
442 match (left, right) {
443 (Tree::Arg(left), Tree::Arg(right)) => {
444 if are_separated_spans(left.span(), right.span()) {
445 Err((left.into(), right.into()))
446 } else {
447 let mut splices = left.into_vec();
448 let right = match right {
449 Arg::Single(s) => s,
450 _ => unreachable!(),
451 };
452 splices.push(right);
453 Ok(Arg::Touching(splices).into())
454 }
455 }
456 (left, right) => Err((left, right))
457 }
458 })
459 .collect()
460}
461
462impl Parser {
463 fn peek(&self) -> Option<&TokenTree> {
464 self.stream.front()
465 }
466
467 fn peek_two(&self) -> Option<(&TokenTree, &TokenTree)> {
468 match (self.stream.get(0), self.stream.get(1)) {
469 (Some(first), Some(second)) => Some((first, second)),
470 _ => None
471 }
472 }
473
474 fn next(&mut self) -> Option<TokenTree> {
475 self.stream.pop_front().map(|tt| {
476 self.last_span = Some(tt.span());
477 tt
478 })
479 }
480
481 fn parse_block(&mut self) -> Result<Block> {
482 let last_span = self.last_span;
483 let orig_block = match self.next() {
484 Some(TokenTree::Group(group)) => group,
485 _ => {
486 last_span.unwrap_or(Span::call_site())
487 .error("Expected a {} block after this token")
488 .emit();
489 return Err(())
490 }
491 };
492 let trees = Parser::new(orig_block.stream()).parse()?;
493 Ok(Block::new(trees, orig_block.span()))
494 }
495
496 fn parse_trees(&mut self) -> Result<Vec<Tree>> {
497 let mut trees = Vec::new();
498 while !self.stream.is_empty() {
499 trees.push(self.parse_tree()?);
500 }
501 Ok(trees)
502 }
503
504 fn parse_tree(&mut self) -> Result<Tree> {
505 let previous_span = self.last_span;
506 let tt = self.next().unwrap();
507 let span = tt.span();
508 match tt {
509 TokenTree::Group(group) => Ok(Parser::parse_splice(group)?.into()),
510
511 TokenTree::Ident(ident) => {
512 let next_span = self.peek().map(|next| next.span());
513
514 let word = ident.to_string();
515 let warn_keyword = || {
516 if !is_separated_span(previous_span, span, next_span) {
517 span.warning(format!("Keyword `{}` not separated by whitespace", word))
518 .note("Keywords should be separated by whitespace to avoid confusion")
519 .help("To interpret as a string, surround it with quotes")
520 .emit();
521 }
522 };
523
524 match word.as_str() {
525 "let" => {
526 warn_keyword();
527 span.error("Let statements are not supported")
528 .note("You can emulate them with `match`")
529 .emit();
530 Err(())
531 }
532 "if" => {
533 warn_keyword();
534 self.parse_if(span)
535 }
536 "for" => {
537 warn_keyword();
538 self.parse_for(span)
539 }
540 "match" => {
541 warn_keyword();
542 self.parse_match(span)
543 }
544 word => {
545 Ok(Spanned(Splice::Word(word.into()), span).into())
546 }
547 }
548 }
549
550 TokenTree::Literal(lit) => Ok(Spanned(Splice::Literal(lit), span).into()),
551
552 TokenTree::Punct(punct) => {
553 match punct.as_char() {
554 '$' => {
555 punct.span()
556 .error("Dollar sign interpollation is not supported")
557 .help("To insert a variable, use `(var)` or `((var))`")
558 .emit();
559 }
560 '>' | '<' => {
561 punct.span().error("File redirection is not supported").emit();
562 }
563 '|' => {
564 punct.span().error("Pipe redirection is not supported").emit();
565 }
566 '&' => {
567 punct.span().error("The `&` and `&&` operators are not supported").emit();
568 }
569 ';' => {
570 punct.span()
571 .error("Unexpected semicolon")
572 .help("To interpret literally, surround in quotes")
573 .note("Semicolon is not needed in this macro")
574 .emit();
575 }
576 ch => {
577 return Ok(Spanned(Splice::Word(ch.to_string()), span).into());
578 }
579 }
580 Err(())
581 }
582 }
583 }
584
585 fn parse_splice(mut group: Group) -> Result<Tree> {
586 let span = group.span();
587 let stream = group.stream();
588
589 let tree = match group.delimiter() {
590 Delimiter::Brace if is_really_empty(&group) => {
591 return Ok(Spanned(Splice::Word("{}".into()), span).into())
592 }
593 Delimiter::Brace => Tree::Cmd(Expr::from_stream(stream)),
594 Delimiter::Parenthesis => {
595 match try_into_singleton(&stream) {
596 Some(TokenTree::Group(ref inner))
597 if inner.delimiter() == Delimiter::Parenthesis
598 => {
599 group = inner.clone();
601 Spanned(Splice::ToStr(Expr::from_stream(group.stream())), span).into()
602 }
603 _ => Spanned(Splice::AsOsStr(Expr::from_stream(stream)), span).into(),
604 }
605 }
606 Delimiter::Bracket => Tree::Args(Spanned(Expr::from_stream(stream), span)),
607 Delimiter::None => {
608 span.error("You've probably tried to use a nested macro.\
609 This is not supported").emit();
610 return Err(())
611 }
612 };
613
614 if group.stream().is_empty() {
615 group.span().error("Rust expression expected inside this block").emit();
616 return Err(())
617 }
618
619 Ok(tree)
620 }
621
622 fn parse_if(&mut self, if_span: Span) -> Result<Tree> {
623 let cond = if self.is_ident_next("let") {
624 let let_tt = self.next().unwrap();
625 let pat = Pat(self.parse_until(
626 |parser| match parser.peek() {
627 Some(TokenTree::Punct(punct))
628 if punct.as_char() == '=' && punct.spacing() == Spacing::Alone => true,
629 _ => false,
630 },
631 "`=`",
632 "a pattern",
633 )?);
634 let equals_tt = self.next().unwrap();
635 let expr = self.parse_until_block()?;
636 Condition::IfLet(let_tt, pat, equals_tt, expr)
637 } else {
638 Condition::Bool(self.parse_until_block()?)
639 };
640 let then_block = self.parse_block()?;
641
642 let else_block = if self.is_ident_next("else") {
643 let _ = self.next().unwrap();
644 if self.is_block_next() {
645 self.parse_block()?
646 } else if self.is_ident_next("if") {
647 let if_tt = self.next().unwrap();
648 let inner_if = self.parse_if(if_tt.span())?;
649 let inner_span = inner_if.span();
650 Block::new(vec![inner_if], inner_span).into()
651 } else {
652 self.last_span.unwrap()
653 .error("Expected `if` or {} block after this `else`")
654 .emit();
655 return Err(());
656 }
657 } else {
658 Block::new(Vec::new(), Span::def_site())
659 };
660
661 Ok(Tree::If(If { if_span, cond, then_block, else_block }))
662 }
663
664 fn parse_for(&mut self, for_span: Span) -> Result<Tree> {
665 let pat = Pat(self.parse_until_ident("in", "a pattern")?);
666 let in_tt = self.next().unwrap();
667 let expr = self.parse_until_block()?;
668 let block = self.parse_block()?;
669 Ok(Tree::For(For { for_span, pat, in_tt, expr, block }))
670 }
671
672 fn parse_match(&mut self, match_span: Span) -> Result<Tree> {
673 use self::Spacing::{Alone, Joint};
674
675 let expr = self.parse_until_block()?;
676
677 let block = match self.next() {
678 Some(TokenTree::Group(group)) => group,
679 _ => unreachable!(),
680 };
681 let block_span = block.span();
682
683 let _dont_use_self = self;
685 let mut parser = Parser::new(block.stream());
686 let mut arms = Vec::new();
687
688 while !parser.stream.is_empty() {
689 let pat = Pat(parser.parse_until(
690 |parser| match parser.peek_two() {
691 Some((TokenTree::Punct(left), TokenTree::Punct(right)))
692 if left.as_char() == '=' && right.as_char() == '>'
693 && left.spacing() == Joint && right.spacing() == Alone => true,
694 _ => false,
695 },
696 "`=>`",
697 "a pattern",
698 )?);
699 let arrow = (
700 parser.next().unwrap(),
701 parser.next().unwrap(),
702 );
703 let block = parser.parse_block()?;
704 arms.push((pat, arrow, block))
705 }
706
707 drop(_dont_use_self);
708
709 Ok(Tree::Match(Match { match_span, expr, block_span, arms }))
710 }
711
712 fn parse_until_block(&mut self) -> Result<Expr> {
713 if self.is_block_next() {
714 return Ok( Expr::from_tt(self.next().unwrap()) )
715 }
716 let stream = self.parse_until(Parser::is_block_next, "{} block", "an expression")?;
717 Ok(Expr::from_stream(stream))
718 }
719
720 fn is_block_next(&self) -> bool {
721 match self.peek() {
722 Some(&TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => true,
723 _ => false,
724 }
725 }
726
727 fn is_ident_next(&self, expected: &str) -> bool {
728 match self.peek() {
729 Some(TokenTree::Ident(actual)) if actual.to_string() == expected => true,
730 _ => false,
731 }
732 }
733
734 fn parse_until_ident(&mut self, until_ident: &str, what: &str) -> Result<TokenStream> {
735 self.parse_until(
736 |parser| parser.is_ident_next(until_ident),
737 &format!("`{}`", until_ident),
738 what
739 )
740 }
741
742 fn parse_until<F>(&mut self, until: F, until_str: &str, what: &str) -> Result<TokenStream>
743 where F: Fn(&Parser) -> bool
744 {
745 let mut tts = Vec::new();
746
747 while !until(self) {
748 match self.next() {
749 Some(tt) => tts.push(tt),
750 None => {
751 self.last_span.unwrap()
752 .error(format!("Found end of macro when looking for {}", until_str))
753 .emit();
754 return Err(())
755 }
756 }
757 }
758
759 if tts.is_empty() {
760 self.peek().unwrap().span()
761 .error(format!("Expected {} before {}", what, until_str))
762 .emit();
763 return Err(())
764 }
765
766 Ok(tts.into_iter().collect())
767 }
768}
769
770fn are_separated_spans(left: Span, right: Span) -> bool {
771 left.end().line != right.start().line ||
772 left.end().column < right.start().column
773}
774
775fn is_separated_span(left: Option<Span>, this: Span, right: Option<Span>) -> bool {
776 left.map_or(true, |left| are_separated_spans(left, this)) &&
777 right.map_or(true, |right| are_separated_spans(this, right))
778}
779
780fn is_really_empty(group: &Group) -> bool {
781 let span = group.span();
782 let start = span.start();
783 let end = span.end();
784 group.stream().is_empty() && start.line == end.line && start.column + 2 == end.column
785}
786
787fn try_into_singleton(stream: &TokenStream) -> Option<TokenTree> {
788 let mut stream = stream.clone().into_iter();
789 let tt = stream.next()?;
790 match stream.next() {
791 None => Some(tt),
792 _ => None
793 }
794}