Česky
Kamil Dudka

Flex/Bison based compiler and interpreter written in C++ (using Boost)

File detail

Name:Downloadtest-scanner.cc [Download]
Location: vyp08 > vyp08-1.0pre1 > src
Size:10.9 KB
Last modification:2009-07-04 19:51

Source code

/*
 * Copyright (C) 2008 Kamil Dudka <xdudka00@stud.fit.vutbr.cz>
 *
 * This file is part of vyp08 (compiler and interpreter of VYP08 language).
 *
 * vyp08 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * vyp08 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with vyp08.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include "config.h"
#include "scanner.h"
#include "vypIO.h"
 
#ifndef BUILDING_DOX
#   include <algorithm>
#   include <sstream>
#   include <string>
#   include <vector>
#endif
 
using namespace StreamDecorator;
using std::string;
 
typedef STD_VECTOR(Token) TTokenList;
 
/// scanner test
struct Test {
    string      input;      /// given input
    bool        hasError;   /// false if input is expected to be valid
    TTokenList  tokenList;  /// expected output (ignored if hasError==true)
};
 
static int errorCode = 0;
 
/// compare tokens by value
bool operator==(const Token &a, const Token &b) {
    if (a.type != b.type)
        return false;
    if (a.lineno != b.lineno)
        return false;
    switch (a.type) {
        case ETOKEN_NUMBER_INT:
            return a.numberInt == b.numberInt;
        case ETOKEN_NUMBER_DOUBLE:
            return b.numberDouble == b.numberDouble;
        case ETOKEN_ID:
        case ETOKEN_STRING:
            return a.text == b.text;
        default:
            return true;
    }
}
/// compare tokens by value
bool operator!=(const Token &a, const Token &b) {
    return !operator==(a,b);
}
 
/**
 * run test
 * Print progress to std::cout and errors to std::cerr.
 * @note Global variable errorCode is set to 1 on test fail.
 */
void run(const Test &test) {
    // test name
    static int nTest = 0;
    std::ostringstream tmp;
    tmp << "test" << ++nTest;
    string name(tmp.str());
 
    // print header
    std::cout << ">>> " << name << " - input follows:" << std::endl;
    std::cout << test.input << std::endl;
    std::istringstream stream(test.input);
 
    // crate scanner
    IScanner *scanner = ScannerFactory::createScanner(stream, name);
    Token token;
    const TTokenList &tl = test.tokenList;
    unsigned i;
    for (i = 0; scanner->readNext(token); i++) {
#if 0
        std::cout << token << std::endl;
#endif
        if (test.hasError)
            // input is expected not to be valid
            continue;
 
        const Token &tokenExpected = tl[i];
        if (i >= tl.size()) {
            std::cerr << "!!! trailing token" << std::endl
                << "---      got: " << token << std::endl << std::endl;
            ::errorCode = -1;
            break;
        }
        if (token != tokenExpected) {
            std::cerr << "!!! token mismatch" << std::endl
                << "--- expected: " << tl[i] << std::endl
                << "---      got: " << token << std::endl << std::endl;
            ::errorCode = -1;
        }
    }
    if (!test.hasError && i < tl.size()) {
        std::cerr << "!!! unexpected end of input" << std::endl
            << "--- expected: " << tl[i] << std::endl;
        ::errorCode = -1;
    }
    if (scanner->hasError() != test.hasError) {
        std::cerr << "!!! unexpected error status" << std::endl
            << "--- expected: " << test.hasError << std::endl
            << "---      got: " << scanner->hasError() << std::endl;
        ::errorCode = -1;
    }
    delete scanner;
    std::cout << std::endl;
}
 
int main(int argc, char *argv[]) {
#if CONSOLE_COLOR_OUTPUT
    Color::enable(true);
#endif
 
    if (argc == 2 && string(argv[1]) == string("-")) {
        // manual scanner diagnostic
 
        // create scanner
        IScanner *scanner = ScannerFactory::createScanner(std::cin, "-");
 
        // read from stdin and stream to stdout
        Token t;
        while (scanner->readNext(t))
            std::cout << t << std::endl;
 
        // evalueate return value
        bool bErr = scanner->hasError();
        delete scanner;
        return bErr ? -1:0;
    }
 
    // initialize test list
    typedef STD_VECTOR(Test) TTestList;
    TTestList testList;
    Test t;
    TTokenList &tl = t.tokenList;
 
    // test #1 - comment / id
    t.input = "\n\tid1/* simple comment */id2// inline comment /* garbage /*\nid3////////";
    t.hasError = false;
    tl.clear();
    { Token t(ETOKEN_ID, 2, 0, 0.0, "id1"); tl.push_back(t); }
    { Token t(ETOKEN_ID, 2, 0, 0.0, "id2"); tl.push_back(t); }
    { Token t(ETOKEN_ID, 3, 0, 0.0, "id3"); tl.push_back(t); }
    testList.push_back(t);
 
    // test #2 - keyword / id
    t.input = "int a;doubleb;double d;string\ts;;";
    t.hasError = false;
    tl.clear();
    { Token t(ETOKEN_KW_INT,        1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "a"         ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "doubleb"   ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_DOUBLE,     1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "d"         ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_STRING,     1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "s"         ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  1, 0, 0.0, ""          ); tl.push_back(t); }
    testList.push_back(t);
 
    // test #3 - string ok
    t.input =
        "STRING /* \"asdffsi\" */\"\\\\n - \\n, /* pseudo-comment */, //,\\n"
        "  \\\\\\\\ - \\\\, \\\\\\\" - \\\", garbge\"id+0.01///\n\"\""
        "\"a\\\\b -> \\\"c\\\"\"";
    t.hasError = false;
    tl.clear();
    { Token t(ETOKEN_ID,            1, 0, 0.0, "STRING"    ); tl.push_back(t); }
    { Token t(ETOKEN_STRING,        1, 0, 0.0, "\\n - \n, /* pseudo-comment */, //,\n  \\\\ - \\, \\\" - \", garbge"); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "id"        ); tl.push_back(t); }
    { Token t(ETOKEN_OP_PLUS,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_NUMBER_DOUBLE, 1, 0, .01, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_STRING,        2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_STRING,        2, 0, 0.0, "a\\b -> \"c\""); tl.push_back(t); }
    testList.push_back(t);
 
    // tests #4, #5, #6, #7 - string error
    t.hasError = true;
    tl.clear();
    t.input = "\"str";                  testList.push_back(t);
    t.input = "\"str\\a\"";             testList.push_back(t);
    t.input = "\"str\\\\\"\"";          testList.push_back(t);
    t.input = "str\"";                  testList.push_back(t);
 
    // test #8 - numbers ok
    t.input = "double d:=0.0000000000000001e15,-15";
    t.hasError = false;
    tl.clear();
    { Token t(ETOKEN_KW_DOUBLE,     1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "d"         ); tl.push_back(t); }
    { Token t(ETOKEN_OP_ASSIGN,     1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_NUMBER_DOUBLE, 1, 0, 0.0000000000000001e15, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_COMMA,      1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_MINUS,      1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_NUMBER_INT,    1, 15, 0.0, ""         ); tl.push_back(t); }
    testList.push_back(t);
 
    // tests #9, #10, #11 - number error
    t.hasError = true;
    tl.clear();
    t.input = "1_";                     testList.push_back(t);
    t.input = "9999999999999999999";    testList.push_back(t);
    t.input = "10.0e1000";              testList.push_back(t);
 
    // test #12 - kw/op
    t.input = "{and}(div)double*/else+-eq if<int>ne\n"
        "q\tneq<=not>=or:=string,var\n\n\n;void/**/while \n"
        "////////////////////////////////////////////";
    t.hasError = false;
    tl.clear();
    { Token t(ETOKEN_OP_LCBR,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_AND,        1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_RCBR,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_LPAR,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_DIV,        1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_RPAR,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_DOUBLE,     1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_STAR,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SLASH,      1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_ELSE,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_PLUS,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_MINUS,      1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_EQ,         1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_IF,         1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_LESS,       1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_INT,        1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_GREATER,    1, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            1, 0, 0.0, "ne"        ); tl.push_back(t); }
    { Token t(ETOKEN_ID,            2, 0, 0.0, "q"         ); tl.push_back(t); }
    { Token t(ETOKEN_KW_NEQ,        2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_LESS_EQ,    2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_NOT,        2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_GREATER_EQ, 2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_OR,         2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_ASSIGN,     2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_STRING,     2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_COMMA,      2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_VAR,        2, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_OP_SEMICOLON,  5, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_VOID,       5, 0, 0.0, ""          ); tl.push_back(t); }
    { Token t(ETOKEN_KW_WHILE,      5, 0, 0.0, ""          ); tl.push_back(t); }
    testList.push_back(t);
 
    std::for_each(testList.begin(), testList.end(), run);
    return ::errorCode;
}