Website Summary Files Commits Branches @commit @diff @stats
path: root / homework / 81568 / c2html / lexcer.cpp
diff options
context:
space:
mode:
Commit06811744a002f4ab332d94d510475231afcd45a1 (patch)
Author
 Mincho Paskalev <minchopaskal@gmail.com>
2018-11-03 13:36:57 +0200
Committer
 Martin Dobrev <martindobrev0@gmail.com>
2018-11-03 13:36:57 +0200
Files91c41776fbdf899dcb61a8d5df3ac1316c24f3c8 /homework/81568/c2html/lexcer.cpp
IPL-06811744a002f4ab332d94d510475231afcd45a1.tar
IPL-06811744a002f4ab332d94d510475231afcd45a1.tar.gz
IPL-06811744a002f4ab332d94d510475231afcd45a1.zip
Parent26e5f891d537b5d4fc2890c9cdbeb73d5748d568 (diff)
First HW (#9)
* Add 1st hw * --amend * moving readme for 1st hw to designated folder * renamed TokenType enum to TokenClass * fixed character constant lexing. added sampe output file * add new line at end-of-file. added gitignore * a bit of refactoring
1 files changed, 413 insertions, 0 deletions
-rw-r--r--homework/81568/c2html/lexcer.cpp413
diff --git a/homework/81568/c2html/lexcer.cpp b/homework/81568/c2html/lexcer.cpp
new file mode 100644
index 0000000..6ad3dbd
--- /dev/null
+++ b/homework/81568/c2html/lexcer.cpp
@@ -0,0 +1,413 @@
+#include "lexcer.h"
+#include "lexconst.h"
+
+#include <cctype>
+#include <cstdlib>
+
+// using LexCer::TokenClass;
+// using LexCer::Token;
+// using LexCer::keywords;
+// using LexCer::preprocessor;
+// using LexCer::tokenToClass;
+// using LexCer::spanBegin;
+// using LexCer::spanEnd;
+
+//const char* Tokenizer::spanBegin = "<span class=\"";
+//const char* Tokenizer::spanEnd = "</span>";
+
+namespace LexCer {
+
+Tokenizer::Tokenizer(FILE *in, FILE *out)
+ : in(in), out(out) {
+ token.type = TokenClass::Invalid;
+ typenames = {"void", "char", "short", "int", "long", "float", "double", "signed", "unsigned"};
+}
+
+void Tokenizer::makeToken(TokenClass type, unsigned line, std::string lexeme, double number) {
+ token.type = type;
+ token.line = line;
+ token.lexeme = lexeme;
+ token.number = number;
+}
+
+bool Tokenizer::readNextDirective(std::string *lexeme) {
+ char c;
+ while (isalpha((c = fgetc(in))))
+ lexeme->push_back(c);
+ // we may need that last bit
+ ungetc((unsigned) c, in);
+ auto directive = lexeme->substr(1, lexeme->size() - 1);
+ if (!preprocessor.count(directive))
+ return false;
+ if (directive.compare("define") == 0) {
+ //readDefine();
+ }
+ return true;
+}
+
+bool Tokenizer::readNextString(std::string *lexeme, char oldc) {
+ char c, prevc = oldc;
+ while (((c = fgetc(in)) != '"' || prevc == '\\') && c != '\n' && c != EOF) {
+ lexeme->push_back(c);
+ prevc = c;
+ }
+
+ if (c == '\n' || c == EOF) {
+ ungetc((unsigned) c, in);
+ return false;
+ }
+ lexeme->push_back(c);
+ return true;
+}
+
+bool Tokenizer::readNextIdentifier(std::string *lexeme) {
+ char c;
+ bool valid = true;
+ while (isalnum(c = fgetc(in)) || c == '_') {
+ lexeme->push_back(c);
+ }
+ ungetc((unsigned) c, in);
+ return valid;
+}
+
+bool Tokenizer::readNextNumber(char beg, double *number, std::string *numberRepr) {
+ bool valid = true;
+ num_t base = DEC;
+ char c;
+ if (beg == '0') {
+ c = fgetc(in);
+ numberRepr->push_back(c);
+ if (c == 'b')
+ base = BIN;
+ else if (c == 'x' || c == 'X')
+ base = HEX;
+ else if (isdigit(c))
+ base = OCT;
+ else
+ ungetc((unsigned) c, in);
+ }
+
+ while (isValidDigit(c = fgetc(in), base)) {
+ numberRepr->push_back(c);
+ }
+
+ if (c == '.') {
+ numberRepr->push_back(c);
+ while (isValidDigit(c = fgetc(in), base))
+ numberRepr->push_back(c);
+ }
+ if (tolower(c) == 'e') {
+ numberRepr->push_back(c);
+ if (base != DEC)
+ return false;
+ if (!isdigit(c = fgetc(in)) && c != '-' && c != '+') {
+ ungetc((unsigned) c, in);
+ return false;
+ }
+ numberRepr->push_back(c);
+ while (isValidDigit(c = fgetc(in), base))
+ numberRepr->push_back(c);
+ }
+
+ char lower = tolower(c);
+ if (lower == 'u') {
+ numberRepr->push_back(c);
+ lower = tolower(c = fgetc(in));
+ }
+ if (lower == 'f' || lower == 'l')
+ numberRepr->push_back(c);
+ else
+ ungetc((unsigned) c, in);
+
+ *number = this->atod(*numberRepr); // not working ...
+ return true;
+}
+
+bool Tokenizer::isValidDigit(char c, num_t numType) const {
+ bool valid;
+ switch (numType) {
+ case DEC:
+ return isdigit(c);
+ case BIN:
+ return (c == '0' || c == '1');
+ case OCT:
+ return (c >= '0' || c <= '7');
+ case HEX:
+ return (isdigit(c) || tolower(c) >= 'a' || tolower(c) <= 'f');
+ default:
+ return false;
+ }
+}
+
+bool Tokenizer::readLineComment(std::string *lexeme) {
+ char c;
+ while ((c = fgetc(in)) != '\n' && c != EOF)
+ lexeme->push_back(c);
+ ungetc((unsigned) c, in);
+ return true; // for consistency
+}
+
+bool Tokenizer::readMultilineComment(std::string *lexeme) {
+ char c, prevc = (char) -1;
+ while (((c = fgetc(in)) != '/' && prevc != '*') && c != EOF)
+ lexeme->push_back(c);
+
+ if (c == EOF) {
+ return false;
+ }
+ lexeme->push_back(c); // closing slash
+ return true;
+}
+
+bool Tokenizer::readInclude(std::string *lexeme) {
+ char c;
+ bool valid = true;
+ while ((c = fgetc(in)) != '>') {
+ lexeme->push_back(c);
+ if (!isalpha(c) && c != '_' && c != '.')
+ valid = false;
+ }
+ lexeme->push_back(c);
+ return valid;
+}
+
+bool Tokenizer::readUntilBlank(std::string *lexeme) {
+ char c;
+ while (!isblank(c = fgetc(in)) && c != EOF) ;
+ ungetc((unsigned) c, in);
+ return true;
+}
+
+bool Tokenizer::writeToOutput() {
+ if (token.type == TokenClass::Eof) {
+ printf("End of file!\n");
+ fputc('\n', out);
+ return false;
+ }
+
+ if (token.type == TokenClass::Skip) {
+ if (fputs(token.lexeme.c_str(), out) == EOF) {
+ printf("Error while writing to output");
+ return false;
+ }
+ return true;
+ }
+
+ printf("Token of type %s at line %d: ", tokenToClass.at(token.type).c_str(), token.line);
+ if (token.type == TokenClass::Number)
+ printf("%f\n", token.number);
+ else
+ printf("%s\n", token.lexeme.c_str());
+
+ auto lexeme = escapeHTML(token.lexeme);
+
+ std::string output;
+ output.append(spanBegin).append(tokenToClass.at(token.type)).append("\">").append(lexeme).append(spanEnd);
+
+ if (fputs(output.c_str(), out) == EOF) {
+ printf("Error while writing to output");
+ return false;
+ }
+ return true;
+}
+
+std::string Tokenizer::escapeHTML(const std::string &lexeme) {
+ std::string result;
+ for (auto c : lexeme) {
+ switch(c) {
+ case '<':
+ result.append("&lt");
+ break;
+ case '>':
+ result.append("&gt");
+ break;
+ case '&':
+ result.append("&amp");
+ break;
+ default:
+ result.push_back(c);
+ break;
+ }
+ }
+ return result;
+}
+
+TokenClass Tokenizer::nextToken(std::string *lexeme, double *number) {
+ if (token.type == TokenClass::Eof) {
+ return TokenClass::Invalid;
+ }
+
+ char c = fgetc(in);
+ if (c == EOF) {
+ return TokenClass::Eof;
+ }
+ lexeme->push_back(c);
+ // operator || symbol || string || preprocessor directive
+ // probably should check for errors like '5 * % 4;', but...
+ switch(c) {
+ case '\n':
+ ++line;
+ case '\t':
+ case ' ':
+ case ';':
+ case ',':
+ case '{':
+ case '}':
+ return TokenClass::Skip;
+ case '(':
+ return TokenClass::LeftParen;
+ case ')':
+ return TokenClass::RightParen;
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ return TokenClass::Operator;
+ case '*':
+ c = fgetc(in);
+ if (c == '/') {
+ return TokenClass::Invalid;
+ }
+ ungetc((unsigned) c, in);
+ c = '*';
+ case '^':
+ case '%':
+ case '=':
+ case '!':
+ case '~':
+ c = fgetc(in);
+ if (c != '=') ungetc((unsigned) c, in);
+ else lexeme->push_back(c);
+ return TokenClass::Operator;
+ case '<':
+ case '>':
+ c = fgetc(in);
+ if (c == lexeme->at(lexeme->size() - 1)) {
+ lexeme->push_back(c);
+ c = fgetc(in);
+ if (c == '=')
+ lexeme->push_back(c);
+ else
+ ungetc((unsigned) c, in);
+ } else if (lexeme->at(lexeme->size() - 1) == '<' && isalpha(c)) {
+ lexeme->push_back(c);
+ if (!readInclude(lexeme))
+ return TokenClass::Invalid;
+ return TokenClass::String;
+ } else
+ ungetc((unsigned) c, in);
+ return TokenClass::Operator;
+ case '&':
+ case '|':
+ case '+':
+ c = fgetc(in);
+ if (c == lexeme->at(lexeme->size() - 1) || c == '=')
+ lexeme->push_back(c);
+ else
+ ungetc((unsigned) c, in);
+ return TokenClass::Operator;
+ case '-':
+ c = fgetc(in);
+ if (c == '-' || c == '=' || c == '>')
+ lexeme->push_back(c);
+ else
+ ungetc((unsigned) c, in);
+ return TokenClass::Operator;
+ case '#':
+ if (readNextDirective(lexeme))
+ return TokenClass::PreprocessorDirective;
+ return TokenClass::Invalid;
+ case '\'':
+ lexeme->push_back(c = fgetc(in));
+ if (c == '\\') {
+ lexeme->push_back(c = fgetc(in));
+ }
+ lexeme->push_back(c = fgetc(in)); // the closing mark
+ if (c != '\'')
+ return TokenClass::Invalid;
+ return TokenClass::String;
+ case '"':
+ if (readNextString(lexeme, c))
+ return TokenClass::String;
+ return TokenClass::Invalid;
+ case '/':
+ c = fgetc(in);
+ lexeme->push_back(c);
+ switch (c) {
+ case '=':
+ return TokenClass::Operator;
+ case '/':
+ if (!readLineComment(lexeme))
+ return TokenClass::Invalid;
+ return TokenClass::Comment;
+ case '*':
+ if (!readMultilineComment(lexeme))
+ return TokenClass::Invalid;
+ return TokenClass::Comment;
+ default:
+ if (!isblank(c) || !isdigit(c)) {
+ ungetc((unsigned) c, in);
+ lexeme->pop_back();
+ return TokenClass::Invalid;
+ }
+ return TokenClass::Operator;
+ }
+ case '\\':
+ c = fgetc(in);
+ ungetc((unsigned) c, in);
+ if (c != '\n')
+ return TokenClass::Invalid; // stray '\'
+ return TokenClass::Operator;
+ }
+
+ // identifier || keyword || typename
+ if (isalpha(c) || c == '_') {
+ if (readNextIdentifier(lexeme)) {
+ if (keywords.count(*lexeme) != 0)
+ return TokenClass::Keyword;
+ else if (typenames.count(*lexeme) != 0)
+ return TokenClass::Typename;
+ return TokenClass::Identifier;
+ } else {
+ return TokenClass::Invalid;
+ }
+ }
+
+ if (!isdigit(c)) {
+ return TokenClass::Invalid;
+ }
+
+ // number
+ if (readNextNumber(c, number, lexeme))
+ return TokenClass::Number;
+ return TokenClass::Invalid;
+}
+
+
+std::string getOutputFileName(const std::string &name) {
+ auto pos = name.find_last_of('.');
+ if (pos == std::string::npos) {
+ return name + ".html";
+ }
+
+ return name.substr(0, pos) + ".html";
+}
+
+void lexCit(FILE *in, FILE *out) {
+ Tokenizer hl {in, out};
+
+ std::string lexeme;
+ double number;
+ TokenClass type;
+ do {
+ type = hl.nextToken(&lexeme, &number);
+
+ hl.makeToken(type, hl.lineNumber(), lexeme, number);
+ if (!hl.writeToOutput())
+ break;
+ lexeme = "";
+ number = 0.0;
+ } while(hl.currentToken().type != TokenClass::Eof || hl.currentToken().type != TokenClass::Invalid);
+}
+
+} /* Namespace Lexcer */