【发布时间】:2014-09-23 02:54:26
【问题描述】:
我用 C++ 制作了一个简单的脚本阅读类,它允许我阅读和解析脚本。 基本上有一个 FILE 类,然后我继续用“fopen”打开它。
在函数中,我继续调用“fgetc”和“ftell”来根据需要解析脚本文件,注意这不是解释器。 每个脚本文件都应该遵循一种语法,但这就是我在这里寻求解决方案的原因。
脚本如下所示:
# Script File Comment
USERNAME = "Joe"
PASSWORD = "pw0001"
ACCESSLEVEL = 3
DATABASE = ("localhost",3306,"db","user","password")
基本上我有几个功能:
// This function searches for "variables"
nextToken();
// After I have the variable, e.g: USERNAME, PASSWORD, ACCESSLEVEL or DATABASE
// I proceed to call this function
// This function reads the char array for (,-{}()[]=) these are symbols
readSymbol();
// In a condition I check what "token/variable" I got and proceed to read
// it accordingly
// e.g; for USERNAME I do:
readString(); // reads text inside "
// e.g; for ACCESSLEVEL I do:
readNumber(); // reads digits until the next char ain't a digit
// e.g; for DATABASE I do:
readSymbol(); // (
readString(); // 127.0.0.1
readSymbol(); // ,
readNumber(); // 3306
readSymbol(); // ,
readString(); // db
readSymbol(); // ,
readString(); // user
readSymbol(); // ,
readString(); // password
readSymbol(); // )
我希望能够读取这样的变量声明:
DATABASELIST = {"data1","data2","data3"}
or
DATABASELIST = {"data1"}
我可以轻松地执行 readSymbol 和 readString 来读取变量内的 3 个不同的字符串定义,但是这个列表应该有自定义的用户数据,比如 5 个不同的字符串或 8 个不同的字符串 - 取决于。
而且我真的不知道如何使用我编写的解析器来做到这一点。 请注意,我是基于我从脚本编写者那里获取的用于这种格式的一些伪代码,我有从 IDA 中提取的伪代码,如果你想在这里看到它以便更好地理解帖子
这是我的“readSymbol”函数的示例。
READSYMBOL
int TReadScriptFile::readSymbol()
{
int currentData = 0;
int stringStart = -1;
// Check if we can't read anymore
if (end)
return 0;
while (true)
{
// Basically get chars in the script
currentData = fgetc(File);
// Check for end of file
if (currentData == -1)
{
end = true;
break;
}
if (stringStart == -1)
{
if (isdigit(currentData) || isalpha(currentData))
{
printf("TReadScriptFile::readSymbol: Symbol expected\n");
close();
return 0;
}
else if
(
currentData == '=' || currentData == ',' ||
currentData == '(' || currentData == ')' ||
currentData == '{' || currentData == '}' ||
currentData == '>' || currentData == '<' ||
currentData == ':' || currentData == '-'
)
{
#ifdef __DEBUG__
printf("Symbol: %c\n", currentData);
#endif
stringStart = ftell(File);
break;
}
}
}
return 1;
}
NEXTTOKEN
int TReadScriptFile::nextToken()
{
int currentData = 0;
int stringStart = -1;
int stringEnd = -1;
RecursionDepth = -1;
memset(String, 0, 4000);
// Check if we can't read anymore
if (end)
return 0;
while (true)
{
// ** Syntax **
if (isdigit(getNext()) || getNext() == -1)
{
printf("No more tokens left.\n");
end = true;
close();
return 0;
}
// End
// Basically get chars in the script
currentData = fgetc(File);
// Check for end of file
if (currentData == -1)
{
end = true;
break;
}
// Syntax Checking Part, this really isn't needed but w/e
if (stringStart == -1)
{
if (currentData == '=' || isdigit(currentData))
{
printf("TReadScriptFile::nextToken: Syntax Error: string expected\n");
close();
return 0;
}
}
// End Syntax Checking
// It's a comment line, we should skip
if (currentData == '#')
{
seekNewLn();
continue;
}
// There are no variables, yet
if (stringStart == -1)
{
// We found a letter, we are near a token!
if (isalpha(currentData))
{
stringStart = ftell(File);
// We might as well add the letter to the string
RecursionDepth++;
String[RecursionDepth] = currentData;
continue;
}
}
else if (stringStart != -1)
{
// Let's wait until we get an identifier or space
// We found a digit, error
if (isdigit(currentData))
{
printf("TReadScriptFile::nextToken: string expected\n");
close();
return 0;
}
// We found a space, maybe we should stop looking for tokens?
else if (isspace(currentData))
{
#ifdef __DEBUG__
printf("Token: %s\n", String);
#endif
break;
}
RecursionDepth++;
String[RecursionDepth] = currentData;
}
}
return 1;
}
我在这里找到了一个很好的例子:
【问题讨论】:
-
我认为你需要一个解析器和一个词法分析器。解析器将识别符号,词法分析器将识别语法中的词位或标记。不幸的是,这不是一门简单的学科(编译器理论是我在大学里上过的最难的一门课)。也许使用Flex and Bison 可能更容易。使用 Flex 和 Bison,您只需指定语法。
-
boost::spirit也可能是一个可行的选择。 -
这个解析器是我的代码的基础,它实际上是从 ELF 二进制文件反编译的,是由一家名为 CipSoft 的大公司编写的。他们没有使用任何外部类或程序,因为制作词法分析器和解析器需要花费大量时间和精力,而是通过在 char 数组中搜索标记、符号和标识符来使用这个简单的脚本解析器。希望你能理解我的担忧。
-
有趣的是,虽然公司中有一个人知道如何使用 Lex 和 Yacc(或 Flex 和 Bison),但他们可能编写了一个比他们手工编写的解析器更具扩展性的解析器。如果您有机会坐下来享受一些空闲时间,我建议您学习 Yacc 和 Lex。有一个用于 python 的 Yacc 和 Lex,也称为 PLY(Python Lex 和 Yacc),尽管我从未亲自使用过它
-
关于
DATABASE_LIST的问题。找到变量后,您是否考虑读取符号检查它是否为{然后在循环中执行readString()将其添加到std::vector然后检查,或}(使用readSymbol()) 。如果它是,(comma),那么您返回并阅读另一个字符串添加到vector等,直到您最终到达}。完成后,您将拥有一个代表DATABASE_LIST的字符串向量(动态数组)