/*
* 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;
}