Universidad de Costa Rica
|
|
El programa de la Figura 1 es una calculadora que evalúa expresiones complejas, con paréntesis. Tiene la limitación de que trabaja con números de un solo dígito.
Modifique este programa para que pueda procesar expresiones con
números de varios dígitos. Limite su trabajo a los
métodos "aparea()
", "num()
" y
"Evaluar()
". Use la clase "Token.h
" de
la
Figura 2 y complete el método
"yylex()
" para obtener un programa que pueda leer de
la línea de comandos valores numéricos que contengan
separadores para miles y millones. También use funciones de
utiliería como
"atof()
" para obtener el valor numérico de una
hilera. Su programa debe procesar correctamente estos ejemplos:
C:\DIR\SubDir> CLC.exe ( 1,234,567.00 + 3 )
==> 1,234,570.00
C:\DIR\SubDir> CLC.exe ( 1.234.567,00 + 3 )
==> 1,234,570.00
C:\DIR\SubDir> CLC.exe ( 1_234_567.00 + 3 )
==> 1,234,570.00
El caracter "_
" nunca se usa como marcador de
decimles. Como no siempre es posible saber si la coma o el punto
son el marcador de decimales, su programa debe permitir indicar
cuál se usa incluyendo un punto o una coma al principio:
C:\DIR\SubDir> CLC.exe , ( 1,234 + 3 )
==> 4,234
C:\DIR\SubDir> CLC.exe . ( 1,234 + 3 )
==> 1,237
C:\DIR\SubDir> CLC.exe ( 1,234.567,00 + 3 )
==> ?? error (,.,)
C:\DIR\SubDir> CLC.exe ( 1.234,567.00 + 3 )
==> ?? error (.,.)
// Token.h (C) 2010 adolfo@di-mare.com /** \file Token.h \brief Ejemplo de una analizador léxico yylex() implementado a mano para la gramática de la calculadora. En la práctica es necesario reprogramar \c yylex() para que reconozca un conjunto de tokens y lexemas diferente. */ #ifndef Token_h #define Token_h #include <string> #include <cstdlib> // strlen(), etc. using std::string; #include <iostream> #include <cctype> #include <climits> // CHAR_MAX /// Contiene la pareja <code> <Token, Lexema> </code> que retorna \c yylex(). class Token { private: int m_codigo; ///< \c Token::Codigo. Valor numérico que representa al token. string m_lexema; ///< Lexema asociado al código. int m_fl; ///< \c first_line. Línea que contiene la primera letra del token. int m_fc; ///< \c first_column. Columna en la que aparece la primera letra del token. int m_ll; ///< \c last_line. Línea que contiene la última letra del token. int m_lc; ///< \c last_column. Columna en la que aparece la última letra del token. public: enum Codigo { NUM = 256+99+1-32, // podría ser otro, mientras no "choque" // con otros valores para tokens ERROR = 666, FIN = 999 }; Token() { *this = 0; } ///< Constructor. Token(char c) { *this = c; } ///< Constructor para una letra. Token(int c) { *this = c; } ///< Constructor sinónimo de <code> Token(char c) </code>. Token(const Token& o) { *this = o; } ///< Constructor de copia. const string& lexema() const { return m_lexema; } ///< Retorna el lexema del token. const int num() const { return m_codigo; } ///< Código numérico del token. const int token () const { return num(); } ///< Sinónimo de \c num(). const char* Nombre() const { return Nombre(m_codigo); } ///< Nombre del token. static const char* Nombre(int); void operator=( int c ); /// Asignación a partir de un entero o de una letra. Token& operator=( const Token& ); void set( int c, const Astring& lexema, int, int, int, int); void loc( int &, int &, int &, int &) const; }; // Token /// Analizador léxico simple usado para tokenizar las letras de una hilera. class Anlizador_Lexico { private: const char * m_str; ///< Hilera grandota que contiene todas las letras. int m_idx; ///< Indica la siguiente posición en \c m_str a reconocer. int m_len; ///< Longitud de \c m_str. public: /// Constructor por defecto. Anlizador_Lexico() : m_str(""),m_idx(0),m_len(0) { } ~Anlizador_Lexico() { } ///< Destructor. /// Constructor para usar \c str. Anlizador_Lexico( const char* str ) : { set(str); } /// Prepara a \* this para que use \c str. void set( const char* str ) : { m_str = str; m_idx = 0; m_len=strlen(m_str); } /// Retorna el siguiente token del analizador léxico. int yylex( Token & tk ); //<== Esto es lo que falta de implementar !!! }; /// Deja el lexema como la hilera nula a menos que \c "c" sea un caracter válido. inline void Token::operator=( int c ) { m_codigo = c; if ( (0 < c) && (c <= CHAR_MAX) ) { m_lexema = c; } else { m_lexema = ""; } m_fl = m_fc = m_ll = m_lc = 1; } inline void Token::set( int c, const Astring& lexema, int fl=1, int fc=1, int ll=1, int lc=1 ) { m_codigo = c; m_lexema = lexema; m_fl = fl; m_fc = fc; m_ll = ll; m_lc = lc; } /// Retorna la posición del token. /// - \c fl==first_line. Línea que contiene la primera letra del token. /// - \c fc==first_column. Columna en la que aparece la primera letra del token. /// - \c ll==last_line. Línea que contiene la última letra del token. /// - \c lc==last_column. Columna en la que aparece la última letra del token. inline void Token::loc( int &fl, int &fc, int &ll, int &lc) const { fl = m_fl; fc = m_fc; ll = m_ll; lc = m_lc; } /// Copia. inline Token& Token::operator=( const Token& o ) { m_codigo = o.m_codigo; m_lexema = o.m_lexema; m_fl = o._fl; m_fc = o._fc; m_ll = o._ll; m_lc = o._lc; return *this; } /// ¿ l == r ? inline operator == (const Token& l, const Token& r) { return l.token() == r.token() && l.lexema() == r.lexema(); } /// ¿ l != r ? inline operator != (const Token& l, const Token& r) { return ! (l == r); } /// Retorna el nombre del token cuyo valor numérico es \c num. const char* Token::Nombre(int num) { if (num == '+') return "+"; else if (num == '-') return "-"; else if (num == '*') return "*"; else if (num == '/') return "/"; else if (num == Token::NUM) return "NUM"; else if (num == Token::FIN) return "FIN"; else return "ERROR"; } // Token::Nombre() #endif // Token_h // EOF: Token.h |
Entregue su tarea por correo electrónico, como lo hizo anteriormente.
|
Adolfo Di Mare <adolfo@di-mare.com>.
|