Repository: casey/just
File: src/parser.rs (Rust)

Code before:
              || self.next_are(&[Identifier, Identifier, Comment, Eof])
              || self.next_are(&[Identifier, Identifier, Comment, Eol])
              || self.next_are(&[Identifier, Identifier, Eof])
              || self.next_are(&[Identifier, Identifier, Eol]) =>
          {
            items.push(Item::Set(self.parse_set()?));
          }
          _ => {
            if self.next_are(&[Identifier, ColonEquals]) {
              items.push(Item::Assignment(self.parse_assignment(false)?));
            } else {
              let doc = pop_doc_comment(&mut items, eol_since_last_comment);
              items.push(Item::Recipe(self.parse_recipe(doc, false, false)?));
            }
          }
        }
      } else if self.accepted(At)? {
        let doc = pop_doc_comment(&mut items, eol_since_last_comment);
        items.push(Item::Recipe(self.parse_recipe(doc, true, false)?));
      } else if self.accepted(BracketL)? {
        self.parse_attribute()?;
        self.expect_eol()?;
        let quiet = if self.accepted(At)? { true } else { false };
        let doc = pop_doc_comment(&mut items, eol_since_last_comment);
        items.push(Item::Recipe(self.parse_recipe(doc, quiet, true)?));
      } else {
        return Err(self.unexpected_token()?);
      }
    }

    if self.next == self.tokens.len() {
      Ok(Ast {
        warnings: Vec::new(),
        items,
      })
    } else {
      Err(self.internal_error(format!(
        "Parse completed with {} unparsed tokens",
        self.tokens.len() - self.next,
      ))?)
    }
  }

  /// Parse an alias, e.g `alias name := target`
  fn parse_alias(&mut self) -> CompileResult<'src, Alias<'src, Name<'src>>> {
    self.presume_keyword(Keyword::Alias)?;
    let name = self.parse_name()?;
    self.presume_any(&[Equals, ColonEquals])?;
    let target = self.parse_name()?;
    self.expect_eol()?;
    Ok(Alias { name, target })

Review comment (suggestion):
I think it's a little confusing that `parse_attribute` consumes the `BracketR` but not the `BracketL`. How about something like this:

(Also, `if CONDITION { true } else { false }` is equivalent to `CONDITION`.)

```suggestion
      } else if self.accepted(BracketL)? {
        let attribute_name = self.parse_attribute_name()?
        self.expect(BracketR)?;
        self.expect_eol()?;
        let quiet = self.accepted(At)?;
        let doc = pop_doc_comment(&mut items, eol_since_last_comment);
        items.push(Item::Recipe(self.parse_recipe(doc, quiet, true)?));
```

Code after:
              || self.next_are(&[Identifier, Identifier, Comment, Eof])
              || self.next_are(&[Identifier, Identifier, Comment, Eol])
              || self.next_are(&[Identifier, Identifier, Eof])
              || self.next_are(&[Identifier, Identifier, Eol]) =>
          {
            items.push(Item::Set(self.parse_set()?));
          }
          _ => {
            if self.next_are(&[Identifier, ColonEquals]) {
              items.push(Item::Assignment(self.parse_assignment(false)?));
            } else {
              let doc = pop_doc_comment(&mut items, eol_since_last_comment);
              items.push(Item::Recipe(self.parse_recipe(doc, false, false)?));
            }
          }
        }
      } else if self.accepted(At)? {
        let doc = pop_doc_comment(&mut items, eol_since_last_comment);
        items.push(Item::Recipe(self.parse_recipe(doc, true, false)?));
      } else if self.accepted(BracketL)? {
        let Attribute::NoExitMessage = self.parse_attribute_name()?;
        self.expect(BracketR)?;
        self.expect_eol()?;
        let quiet = self.accepted(At)?;
        let doc = pop_doc_comment(&mut items, eol_since_last_comment);
        items.push(Item::Recipe(self.parse_recipe(doc, quiet, true)?));
      } else {
        return Err(self.unexpected_token()?);
      }
    }

    if self.next == self.tokens.len() {
      Ok(Ast {
        warnings: Vec::new(),
        items,
      })
    } else {
      Err(self.internal_error(format!(
        "Parse completed with {} unparsed tokens",
        self.tokens.len() - self.next,
      ))?)
    }
  }

  /// Parse an alias, e.g `alias name := target`
  fn parse_alias(&mut self) -> CompileResult<'src, Alias<'src, Name<'src>>> {
    self.presume_keyword(Keyword::Alias)?;
    let name = self.parse_name()?;
    self.presume_any(&[Equals, ColonEquals])?;
    let target = self.parse_name()?;
    self.expect_eol()?;