Repository: HigherOrderCO/Bend
File: src/fun/parser.rs (Rust)

Code before:
    match parser(self) {
      Ok(val) => Ok(val),
      Err(_) => self.expected(label),
    }
  }

  fn parse_top_level_name(&mut self) -> ParseResult<Name> {
    let ini_idx = *self.index();
    let nam = self.parse_bend_name()?;
    let end_idx = *self.index();
    if nam.contains("__") {
      let msg = "Top-level names are not allowed to contain \"__\".".to_string();
      self.with_ctx(Err(msg), ini_idx, end_idx)
    } else if nam.starts_with("//") {
      let msg = "Top-level names are not allowed to start with \"//\".".to_string();
      self.with_ctx(Err(msg), ini_idx, end_idx)
    } else {
      Ok(nam)
    }
  }

  fn parse_bend_name(&mut self) -> ParseResult<Name> {
    let ini_idx = *self.index();
    let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/');
    let end_idx = *self.index();
    if name.contains("__") {
      let msg = "Variable names are not allowed to contain \"__\".".to_string();
      self.with_ctx(Err(msg), ini_idx, end_idx)
    } else if name.is_empty() {
      self.expected("name")
    } else {
      Ok(Name::new(name.to_owned()))
    }
  }

  /// Consumes exactly the text without skipping.
  fn consume_exactly(&mut self, text: &str) -> ParseResult<()> {
    if self.input().get(*self.index() ..).unwrap_or_default().starts_with(text) {
      *self.index() += text.len();
      Ok(())
    } else {
      self.expected(format!("'{text}'").as_str())
    }
  }

  fn consume_new_line(&mut self) -> ParseResult<()> {
    self.skip_trivia_inline();
    self.try_consume_exactly("\r");
    self.labelled(|p| p.consume_exactly("\n"), "newline")
  }


Review comment (bug):
You're not applying the same restrictions that you wrote in the documentation (// at the start is allowed).
One of the two should be changed.

I think it's simpler if top-lvel names and variables use the same parsing function with both restrictions. It'll avoid bugs like this one for example

Code after:
    match parser(self) {
      Ok(val) => Ok(val),
      Err(_) => self.expected(label),
    }
  }

  fn parse_restricted_name(&mut self, kind: &str) -> ParseResult<Name> {
    let ini_idx = *self.index();
    let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/');
    if name.is_empty() {
      self.expected("name")?
    }
    let name = Name::new(name.to_owned());
    let end_idx = *self.index();
    if name.contains("__") {
      let msg = format!("{kind} names are not allowed to contain \"__\".");
      self.with_ctx(Err(msg), ini_idx, end_idx)
    } else if name.starts_with("//") {
      let msg = format!("{kind} names are not allowed to start with \"//\".");
      self.with_ctx(Err(msg), ini_idx, end_idx)
    } else {
      Ok(name)
    }
  }

  fn parse_top_level_name(&mut self) -> ParseResult<Name> {
    self.parse_restricted_name("Top-level")
  }

  fn parse_bend_name(&mut self) -> ParseResult<Name> {
    self.parse_restricted_name("Variable")
  }

  /// Consumes exactly the text without skipping.
  fn consume_exactly(&mut self, text: &str) -> ParseResult<()> {
    if self.input().get(*self.index() ..).unwrap_or_default().starts_with(text) {
      *self.index() += text.len();
      Ok(())
    } else {
      self.expected(format!("'{text}'").as_str())
    }
  }

  fn consume_new_line(&mut self) -> ParseResult<()> {
    self.skip_trivia_inline();
    self.try_consume_exactly("\r");
    self.labelled(|p| p.consume_exactly("\n"), "newline")
  }

  /// Skips trivia, returns the number of trivia characters skipped in the last line.
  fn advance_newlines(&mut self) -> Indent {