private Expr factor() throws IOException {
        Expr e;
        switch (token) {
        case '(':
            token = tokenizer.nextToken();
            e = expr();
            if (token != ')')
                throw new XLException("expecting \")\", found: " + token);
            token = tokenizer.nextToken();
            return e;
        case StreamTokenizer.TT_NUMBER:
            double x = tokenizer.nval;
            token = tokenizer.nextToken();
            return new Num(x);
        case StreamTokenizer.TT_WORD:
            String address = tokenizer.sval.toUpperCase();
            if (!Pattern.matches("[A-Z][0-9]+", address))
                throw new XLException("illegal address: " + address);
            token = tokenizer.nextToken();
            return new Variable(address);
        case StreamTokenizer.TT_EOF:
            throw new XLException("unexpected end of text");
        default:
            throw new XLException("unexpected " + (char) token);
        }
    }