// Last edited on 2000-09-01 09:36:47 by stolfi // Analisador léxico para arquivos de dados da planta import java.io.FileReader; import java.io.StreamTokenizer; import java.io.IOException; public final class LeitorDeArquivo { public int numObjs; // Número de objetos no arquivo. public String nome; // Nome externo do arquivo. public FileReader fr; // O arquivo em si. public StreamTokenizer toks; // Um tokenizador que trabalha sobe o arquivo. // Caracteres e tipos de tokens especiais public static final int TT_EOL = StreamTokenizer.TT_EOL; public static final int TT_EOF = StreamTokenizer.TT_EOF; public static final int TT_NUMBER = StreamTokenizer.TT_NUMBER; public static final int TT_SEP = (int)':'; // Separador de campos public static final int TT_PLUS = (int)'+'; public static final int TT_MINUS = (int)'-'; public LeitorDeArquivo(String nome) throws IOException // Abre o arquivo com o nome dado, inicializa o tokenizer, // e lê o número de objetos da primeira linha. { this.nome = nome; System.out.println("Abrindo arquivo " + nome + "..."); try { this.fr = new FileReader(nome); } catch( IOException e ) { throw new IOException(e + " - na abertura arquivo " + this.nome); } this.toks = makeTokenizer(this.fr); try { toks.nextToken(); // Carrega primeiro token do arquivo getNumObjs(); System.out.println("(" + numObjs + " objetos)"); } catch(IOException e) { erroDeFormato(e + " - no numero de objetos"); } } public void erroDeFormato(String message) throws IOException // Imprime a mensagem e levanta uma exceção. { throw new IOException ( "arquivo " + nome + ", linha " + toks.lineno() + ": " + message ); } public String proxString() throws IOException // Consome o próximo token como um string. { int t = this.toks.ttype; if ((t == TT_EOL) || (t == TT_EOF) || (t == TT_SEP)) { erroDeFormato("token nao encontrado"); } String val = this.toks.sval; this.toks.nextToken(); return val; } public double proxNumero () throws IOException // Consome o próximo token e exige que ele seja um número, // possivelmente com sinal "+" ou "-". (Note que "+" e "-" // são ordinarios). { double val; if (this.toks.ttype == TT_MINUS) { val = -1.0; this.toks.nextToken(); } else if (this.toks.ttype == TT_PLUS) { val = +1.0; this.toks.nextToken(); } else { val = +1.0; } if (this.toks.ttype != TT_NUMBER) { erroDeFormato("valor numérico errado"); } { val *= this.toks.nval; } this.toks.nextToken(); return val; } public int proxId() throws IOException // Analiza o próximo token como um inteiro não negativo { if (this.toks.ttype != TT_NUMBER) { erroDeFormato("esperava um valor inteiro"); } double fval = this.proxNumero(); int ival = (int) fval; if (fval < 0) { erroDeFormato("valor inteiro negativo"); } else if (fval != (double)ival) { erroDeFormato("valor inteiro fracionario"); } return ival; } public int proxInt() throws IOException // Analiza o próximo token como um inteiro { double fval = this.proxNumero(); int ival = (int) fval; if (fval != (double)ival) { erroDeFormato("valor inteiro fracionario"); } return ival; } public int proxIdOrNull (int idNula) throws IOException // Analiza o próximo token como um índice de objeto (inteiro >= 0), // ou possivelmente idNula. (Note que "+" e "-" são ordinarios). { double fd = this.proxNumero(); int id = (int) fd; if ((fd < 0) && (fd != idNula)) { erroDeFormato("identificador invalido"); } else if (fd != (double)id) { erroDeFormato("identificador fracionario"); } return id; } public void exigeEOL() throws IOException // Exige que o próximo token seja EOL (ou EOF). // Se for, consome-o. Senão, detona. // { int t = this.toks.ttype; if ((t != TT_EOF) && (t != TT_EOL)) { erroDeFormato("EOF/EOL esperado - lixo no fim da linha"); } else { this.toks.nextToken(); } } public void pulaComentarios() throws IOException // Se o próximo token for EOL, vai pulando // EOLs até encontrar algo diferente (inclusive EOF). // Note que comentários aparecem como EOLs. // { while (this.toks.ttype == TT_EOL) { this.toks.nextToken(); } } public void bombaSeEOF() throws IOException // Detona se o token corrente é EOF. { if (this.toks.ttype == TT_EOF) { erroDeFormato("fim de arquivo inesperado"); } } public void pulaSeparador() throws IOException // Exige que o token corrente seja um separador, // e avança para o token seguinte. { if (this.toks.ttype != TT_SEP) { erroDeFormato("falta separador"); } this.toks.nextToken(); } //////////////////////////////////////////////////////////////////////////////// private StreamTokenizer makeTokenizer(FileReader reader) { StreamTokenizer toks = new StreamTokenizer(reader); toks.slashSlashComments(true); toks.eolIsSignificant(true); toks.parseNumbers(); toks.ordinaryChar(TT_SEP); toks.ordinaryChar(TT_PLUS); toks.ordinaryChar(TT_MINUS); toks.quoteChar((int)'\''); // Aspas simples delimitam strings. return toks; } private void getNumObjs () throws IOException { pulaComentarios(); this.numObjs = (int) proxNumero(); if ((this.numObjs < 0) || (this.numObjs >= 1000000)) { erroDeFormato("numero de objetos invalido"); } exigeEOL(); pulaComentarios(); } }