forLoopStmt method

Stmt forLoopStmt()

Implementation

Stmt forLoopStmt() {
  final token = consume(TokenType.kFor, 'Expected "for" keyword.');

  // Look-ahead to determine for-loop type.
  final next = peek(offset: 1);
  if (next.type == TokenType.kComma) {
    // This is an iterator for-loop.
    final key = consume(TokenType.kRaw, 'Expected name for key term.');
    consume(TokenType.kComma, 'Expected "," between key and value terms.');
    final value = consume(TokenType.kRaw, 'Expected name for value term.');
    consume(TokenType.kIn, 'Expected "in" before for-in loop iterator.');
    final iterExpr = math();
    consume(TokenType.kDo, 'Expected "do" before for-in loop body.');
    final body = bodyStmt(terminal: TokenType.kEnd);
    consume(
      TokenType.kEnd,
      'Expected "end" keyword to terminate for-in loop.',
    );
    return ForIterLoopStmt(
      token,
      key: key,
      value: value,
      iterExpr: iterExpr,
      body: body,
    );
  } else if (next.type == TokenType.kAssign) {
    // This is a ranged for-loop.
    final control = assignExpr();
    consume(TokenType.kComma, 'Expected "," before for-loop end term.');
    final endExpr = math();

    final MathExpr stepExpr;
    final pk = peek();
    if (pk.type == TokenType.kDo) {
      // This is a ranged for-loop with implied step of 1.
      stepExpr = NumberLiteral(
        Token(TokenType.kNumber, "1", pk.pos),
        value: 1.0,
      );
    } else {
      consume(TokenType.kComma, 'Expected "," before for-loop step term.');
      stepExpr = math();
    }

    consume(TokenType.kDo, 'Expected "do" before for-loop body.');

    final body = bodyStmt(terminal: TokenType.kEnd);
    consume(TokenType.kEnd, 'Expected for-loop to terminate with "end".');

    return ForLoopStmt(
      token,
      control: control,
      endExpr: endExpr,
      stepExpr: stepExpr,
      body: body,
    );
  } else {
    // There is a problem.
    throw '${next.pos} Unexpected $next in for-loop statement.';
  }
}