C++ 对于口译员来说,良好的优化
我的爱好是使用C++11为自己的编程语言构建一个解释器。到目前为止,我的解释器可以理解变量、表达式和打印语句C++ 对于口译员来说,良好的优化,c++,c++11,optimization,vector,interpreter,C++,C++11,Optimization,Vector,Interpreter,我的爱好是使用C++11为自己的编程语言构建一个解释器。到目前为止,我的解释器可以理解变量、表达式和打印语句 我的解释器只是一个直接的转换程序,它将源文件转换成令牌列表,而解析器只是在C++向量中找到令牌,令牌被存储在./p> 变量查找 查找变量的方法是,获取变量名的长度,然后循环变量向量,只获取变量名长度的第一个字符。当我找到变量名时,我抓取变量的其余部分,变量名和值存储如下: variable_name:variable_value [0] print [1] string:"Hello
我的解释器只是一个直接的转换程序,它将源文件转换成令牌列表,而解析器只是在C++向量中找到令牌,令牌被存储在./p> 变量查找
查找变量的方法是,获取变量名的长度,然后循环变量向量,只获取变量名长度的第一个字符。当我找到变量名时,我抓取变量的其余部分,变量名和值存储如下:variable_name:variable_value
[0] print
[1] string:"Hello World"
[2] sc
如果变量值是字符串,则变量值周围有引号
表达式计算
在计算表达式时,我首先一次遍历表达式1个字符,并确定该字符是数字、运算符还是括号。然后,如果有一个超过1个字符的数字,我将这些数字组合在一起,如果是完整的数字字符
当我这样做的时候,我把所有的东西都加到一个向量上。我计算预结束负数的数量是否为偶数,以便知道是否用加号或减号替换它们
例如:
然后我循环遍历向量并计算左括号的数量,当我看到左括号时,我将迭代器的值存储在一个变量中。然后我从迭代器开始,获取括号内的内容
然后我在括号内循环,首先进行除法,然后再次循环并进行乘法,然后进行减法,最后进行加法。然后,当计算完成后,我移除表达式该部分周围的环绕括号,并将值存储在向量中左括号的位置
例如:
我写了一个手写的lexer和parser。lexer只做了您所期望的,它以字符串的形式循环源代码,每次1个字符,并将这些字符组合在一起以识别每个标记
解析器只是将标记向量中的项与字符串进行比较,而不是正则表达式
解析器示例
令牌向量如下所示:
variable_name:variable_value
[0] print
[1] string:"Hello World"
[2] sc
解析器使用字符串,print string sc
来识别标记顺序背后的含义
我在做事的方式上有什么好的优化吗?我从来没有学过计算机科学或任何编译器课程,所以我希望能得到一些改进我的解释器的建议。如果您需要,我可以向您展示代码,尽管它相当长。您可以获得的最大加速可能是(假设使用动态类型语言):
struct Value {
int type;
enum {
NUMBER = 1,
STRING = 2
};
double doubleValue; // Value in case it's a number
std::string stringValue; // Value in case it's a string
Value(double x) : type(NUMBER), doubleValue(x) { }
Value(const std::string& x) : type(STRING), stringValue(x) { }
};
然后AST节点可以是常量、变量查找、一元运算或二进制运算:
struct ASTNode {
virtual Value compute() = 0;
virtual ~ASTNode() {}
};
struct ConstantNode : ASTNode {
Value x;
ConstantNode(Value x) : x(x) {}
virtual Value compute() { return x; }
};
struct VariableNode : ASTNode {
std::string name;
VariableNode(const std::string& name) : name(name) {}
virtual value compute() { return lookup(name); }
};
struct AdditionNode : ASTNode {
ASTNode *a, *b;
AdditionNode(ASTNode *a, ASTNode *b) : a(a), b(b) {}
virtual Value compute() {
Value av = a->compute();
Value bv = b->compute();
if (av.type == Value::NUMBER && bv.type == Value::NUMBER) {
return av.doubleValue + bv.doubleValue;
} else if (av.type == Value::STRING && bv.type == Value::STRING) {
return av.stringValue + bv.stringValue;
} else {
throw std::runtime_error("Type mismatch");
}
}
};
您希望使用哪种类型的指针取决于您计划如何管理内存(对于垃圾收集方法来说,在自定义池中分配节点的裸指针可能还可以,否则一些引用计数的智能指针会更好)
使用这种方法,在计算表达式时,您只需执行所需的操作(而无需在运行时重新进行解析)。您可以获得的最大加速可能是(假设使用动态类型语言):
struct Value {
int type;
enum {
NUMBER = 1,
STRING = 2
};
double doubleValue; // Value in case it's a number
std::string stringValue; // Value in case it's a string
Value(double x) : type(NUMBER), doubleValue(x) { }
Value(const std::string& x) : type(STRING), stringValue(x) { }
};
然后AST节点可以是常量、变量查找、一元运算或二进制运算:
struct ASTNode {
virtual Value compute() = 0;
virtual ~ASTNode() {}
};
struct ConstantNode : ASTNode {
Value x;
ConstantNode(Value x) : x(x) {}
virtual Value compute() { return x; }
};
struct VariableNode : ASTNode {
std::string name;
VariableNode(const std::string& name) : name(name) {}
virtual value compute() { return lookup(name); }
};
struct AdditionNode : ASTNode {
ASTNode *a, *b;
AdditionNode(ASTNode *a, ASTNode *b) : a(a), b(b) {}
virtual Value compute() {
Value av = a->compute();
Value bv = b->compute();
if (av.type == Value::NUMBER && bv.type == Value::NUMBER) {
return av.doubleValue + bv.doubleValue;
} else if (av.type == Value::STRING && bv.type == Value::STRING) {
return av.stringValue + bv.stringValue;
} else {
throw std::runtime_error("Type mismatch");
}
}
};
您希望使用哪种类型的指针取决于您计划如何管理内存(对于垃圾收集方法来说,在自定义池中分配节点的裸指针可能还可以,否则一些引用计数的智能指针会更好)
使用这种方法,在计算表达式时,您只需执行所需的操作(而无需在运行时重新进行解析)。您应该尝试以gpu加速的方式进行表达式计算。使用opencl、cuda或amp++。所以大项目完成得更快。当然,这只适用于独立表达式。您应该尝试以gpu加速方式进行表达式求值。使用opencl、cuda或amp++。所以大项目完成得更快。当然,它只适用于独立的表达式。我强烈建议您阅读以下内容: 一个温和但基础很好的编译器编写介绍(也是一个副产品翻译)
不要因为它使用Pascal语言并且是在20多年前编写的而停滞不前:所提出的概念是通用的 我热烈推荐您阅读以下内容: 一个温和但基础很好的编译器编写介绍(也是一个副产品翻译)
不要因为它使用Pascal语言并且是在20多年前编写的而停滞不前:所提出的概念是通用的 为什么不读一本关于编译器设计的书呢?你需要一个更有条理的方案来存储变量。可能是某种哈希表,但是在您的语言具有“范围”的情况下,您需要考虑它的结构,因此在退出作用域时需要丢弃变量。有几种方法可以处理符号表中的作用域。这可能是因为这是一个非常困难的主题!“我可以对我的工作方式进行任何良好的优化吗?”只有“良好的优化”是关于您可以证明的代码片段的优化,或者由探查器指出的性能/内存方面存在问题的优化。否则你