我在C ++中为一个愚蠢的编程语言创建了一个解释器,整个核心结构已经完成(Tokenizer,Parser,Interpreter包括符号表,核心函数等)。
现在我在为这个解释器创建和管理函数库时遇到了问题(我稍后会解释我的意思)
所以目前我的核心函数处理程序是可怕的:
// Simplified version
myLangResult SystemFunction( name, argc, argv )
{
if ( name == "print" )
{
if( argc < 1 )
{
Error('blah');
}
cout << argv[ 0 ];
} else if ( name == "input" ) {
if( argc < 1 )
{
Error('blah');
}
string res;
getline( cin, res );
SetVariable( argv[ 0 ], res );
} else if ( name == "exit ) {
exit( 0 );
}
现在想想其他每一个,如果复杂10倍,还有25个系统功能。不可维护,感觉很可怕,太可怕了。
所以我想:如何创建某种包含所有函数的库,如果它们被导入,则自己初始化并将它们的函数添加到正在运行的解释器的符号表中。
然而,这是我真的不知道如何继续的地方。
我想要实现的是,例如:我的语言的一个(外部?)字符串库,例如:string,它是从该语言的程序中导入的,例如:
import string
myString = "abcde"
print string.at( myString, 2 ) # output: c
我的问题:
我在想什么:
在解释器的开头,由于所有库都使用它进行编译,因此每个函数都会调用类似RegisterFunction( string namespace, myLangResult (*functionPtr) );
的函数,将其自身添加到列表中。当然后从语言中调用import X
时,使用RegisterFunction构建的列表将添加到符号表中。
想到的缺点:
所有库都直接在解释器核心中,大小增加,它肯定会减慢它。
答案 0 :(得分:1)
我认为你应该研究Command
模式。然后,您可以将每个函数实现为Command
,并具有将函数名称映射到Command
个对象的映射。
这也使您能够通过让每个库都具有初始化函数来从外部库加载其他函数,该函数将其函数添加到地图中。
答案 1 :(得分:1)
如果您的解释器是作为库实现的,那么它将从其他人的C ++代码中调用。他们必须从他们自己的代码调用库中的函数来向解释器添加函数并不是不合理的。这是我自己的表达式评估员所做的。在用户代码中这样的东西:
Interpreter in; // an instance of the interpreter
in.AddFunc( lenfun, "length", 1 );
in.AddFunc( catfun, "concat", 2 );
其中调用者必须提供指向实现函数的指针,以及函数的参数名称和数量。这适用于无类型设置 - 如果你进行严格打字,当然还有很多工作要做。
答案 2 :(得分:0)
目前还不清楚你是不是想要一个功能齐全的编程语言,或者这是一个玩具,所以我不知道你想多少时间投入其中。我怀疑动态库加载会比你需要的复杂得多。您可能需要一个库位置的PATH列表,或者您需要注册名称空间 - 这会让您回到现在的位置。
更简单的方法是使用关联的处理程序保留全局符号表。
使用std :: map(或hash_map)比if / else构造快得多的函数查找。让所有函数都用符号表注册。
存储在符号表中的“处理程序”可以是简单对象(或函数指针),也可以进行自己的参数检查。
class FuncHandler {
virtual MyLangResult Run(argv, argc) = 0;
}
typedef std::map<string, FuncHandler*> FuncTableType;
// Simplified version
myLangResult SystemFunction(name, argc, argv )
{
FuncTableType::const_iterator it = function_table_.find(name);
if (it == function_table_.end()) return Error("Unknown function: " + name);
return it->second(argc, argv);
}
我写了一些代码来为这个问题中的类执行自注册,但这里不一定需要: Accessing C++ Functions From Text storage