C++ 对于口译员来说,良好的优化

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++11为自己的编程语言构建一个解释器。到目前为止,我的解释器可以理解变量、表达式和打印语句

我的解释器只是一个直接的转换程序,它将源文件转换成令牌列表,而解析器只是在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
来识别标记顺序背后的含义


我在做事的方式上有什么好的优化吗?我从来没有学过计算机科学或任何编译器课程,所以我希望能得到一些改进我的解释器的建议。如果您需要,我可以向您展示代码,尽管它相当长。

您可以获得的最大加速可能是(假设使用动态类型语言):

  • 创建一个通用值对象,该对象可以存储数字、字符串、bool和其他要用您的语言处理的值类型

  • 将表达式解析为AST

  • 比如说

    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");
            }
        }
    };
    
    您希望使用哪种类型的指针取决于您计划如何管理内存(对于垃圾收集方法来说,在自定义池中分配节点的裸指针可能还可以,否则一些引用计数的智能指针会更好)


    使用这种方法,在计算表达式时,您只需执行所需的操作(而无需在运行时重新进行解析)。

    您可以获得的最大加速可能是(假设使用动态类型语言):

  • 创建一个通用值对象,该对象可以存储数字、字符串、bool和其他要用您的语言处理的值类型

  • 将表达式解析为AST

  • 比如说

    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多年前编写的而停滞不前:所提出的概念是通用的

    为什么不读一本关于编译器设计的书呢?你需要一个更有条理的方案来存储变量。可能是某种哈希表,但是在您的语言具有“范围”的情况下,您需要考虑它的结构,因此在退出作用域时需要丢弃变量。有几种方法可以处理符号表中的作用域。这可能是因为这是一个非常困难的主题!“我可以对我的工作方式进行任何良好的优化吗?”只有“良好的优化”是关于您可以证明的代码片段的优化,或者由探查器指出的性能/内存方面存在问题的优化。否则你