declFuncExpr method

FuncExpr declFuncExpr()

Implementation

FuncExpr declFuncExpr() {
  consume(TokenType.kFunc, 'Expected "function" before declaration.');
  final List<RawExpr> idParts = [];
  final Token start = peek();
  Token token = start;

  bool desugarSelf = false;

  // Assume true. The next step will read a name, if any,
  // and change this to false if no name was read.
  if (token.type == TokenType.kRaw) {
    addIDPart() {
      idParts.add(
        RawExpr(
          consume(
            TokenType.kRaw,
            'Expected valid identifier in function name.',
          ),
        ),
      );
    }

    do {
      addIDPart();
      switch (peek().type) {
        case TokenType.kDot:
          advance();
        case TokenType.kColon:
          advance();
          // Break early. The grammar allows only the last
          // ID part to contain a colon.
          desugarSelf = true;
          addIDPart();
          break;
        default:
      }
      token = peek();
    } while (token.type != TokenType.kLParen);

    consume(
      TokenType.kLParen,
      'Missing left parentheses after function name.',
    );
  } else {
    consume(
      TokenType.kLParen,
      'Expected an unnamed function to have an argument list wrapped in parentheses.',
    );
  }

  final List<DeclArg> args = [
    if (desugarSelf)
      DeclArg(Token.synthesized('self', type: TokenType.kSelf)),
  ];

  while (peek().type != TokenType.kRParen) {
    final arg = advance();

    if (!vars.contains(arg.type)) {
      throw '${arg.pos} Expected argument name in declaration.';
    }

    args.add(DeclArg(arg));
    if (peek().type == TokenType.kComma) {
      advance();
    }
  }

  if (args.isNotEmpty) {
    final int count = args
        .where((e) => e.id.type == TokenType.kSpread)
        .length;
    if (count > 1 || (count == 1 && args.last.id.type != TokenType.kSpread)) {
      throw 'Varargs can only appear once at the end of an argument list.';
    }
  }

  consume(
    TokenType.kRParen,
    'Expected closing parentheses after argument list.',
  );

  final List<Stmt> body = bodyStmt(terminal: TokenType.kEnd);

  consume(TokenType.kEnd, 'Expected "end" after function body.');

  // No name expression was found. This must be an anonymous function.
  if (idParts.isEmpty) {
    return FuncExpr.anonymous(start, body: body, args: args);
  }

  return FuncExpr.named(start, body: body, args: args, idParts: idParts);
}