如何写一个简单的解释器(Interpreter)-6

今天将是不同寻常的一天。你可能会问为什么,因为我们要处理带括号的算数表达式了,比如 7 + 3 * (10 / (12 / (3 + 1) - 1))。

首先,我们修改一下我们的语法。用来支持括号里面的表达式。上一章我们有一个factor规则,用来处理表达式中的基本单位。在我们的例子里就是整数。今天,我们将要添加另一个基本的单位——括号表达式。


这是修改过后的语法:

expr和term的生成过程跟上一节,是一模一样的,唯一的区别就是:在factor生成过程中,LPARENT代表一个左括号,而终结符号。RPARENT代表一个右括号。括号中的非终结符expr使用expr规则。

这是升级后的语法图,你看,多了一个替代的。

expr 和 term 没有变化,所以他们俩的语法图一样。

这里出现了一个很有意思的特性,递归。如果你想处理,二乘以括号7+3。这个算术表达式。你将会调用expr开始处理,然后递归到括号7+3。
把2×(7+3)这个表达式,根据这个新的语法,来转换成矩形图。


顺便提一句,如果你不知道递归是什么,就去看看 Daniel P. Friedman 和 Matthias Felleisen’s写的 The Little Schemer 这本书。书写的很好。

下面我们继续,把我们这个新的语法翻译成代码,下面两点是两个主要的改进:

  • 词法分析器Lexer改进了,现在能返回两个多余的token。左括号和右括号。
  • 解释器的factor方法之前只能解析整数,现在,又能解析整数,也能解析括号。

下面是完整的代码,这个计算器可以计算包括,整数,加减乘除,和括号的表达式。你需要去仔细看看。

# Token types
#
# EOF (end-of-file) token is used to indicate that
# there is no more input left for lexical analysis
INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = (
    'INTEGER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', 'EOF'
)


class Token(object):
    def __init__(self, type, value):
        self.type = type
        self.value = value

    def __str__(self):
        """String representation of the class instance.

        Examples:
            Token(INTEGER, 3)
            Token(PLUS, '+')
            Token(MUL, '*')
        """
        return 'Token({type}, {value})'.format(
            type=self.type,
            value=repr(self.value)
        )

    def __repr__(self):
        return self.__str__()


class Lexer(object):
    def __init__(self, text):
        # client string input, e.g. "4 + 2 * 3 - 6 / 2"
        self.text = text
        # self.pos is an index into self.text
        self.pos = 0
        self.current_char = self.text[self.pos]

    def error(self):
        raise Exception('Invalid character')

    def advance(self):
        """Advance the `pos` pointer and set the `current_char` variable."""
        self.pos += 1
        if self.pos > len(self.text) - 1:
            self.current_char = None  # Indicates end of input
        else:
            self.current_char = self.text[self.pos]

    def skip_whitespace(self):
        while self.current_char is not None and self.current_char.isspace():
            self.advance()

    def integer(self):
        """Return a (multidigit) integer consumed from the input."""
        result = ''
        while self.current_char is not None and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)

    def get_next_token(self):
        """Lexical analyzer (also known as scanner or tokenizer)

        This method is responsible for breaking a sentence
        apart into tokens. One token at a time.
        """
        while self.current_char is not None:

            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            if self.current_char.isdigit():
                return Token(INTEGER, self.integer())

            if self.current_char == '+':
                self.advance()
                return Token(PLUS, '+')

            if self.current_char == '-':
                self.advance()
                return Token(MINUS, '-')

            if self.current_char == '*':
                self.advance()
                return Token(MUL, '*')

            if self.current_char == '/':
                self.advance()
                return Token(DIV, '/')

            if self.current_char == '(':
                self.advance()
                return Token(LPAREN, '(')

            if self.current_char == ')':
                self.advance()
                return Token(RPAREN, ')')

            self.error()

        return Token(EOF, None)


class Interpreter(object):
    def __init__(self, lexer):
        self.lexer = lexer
        # set current token to the first token taken from the input
        self.current_token = self.lexer.get_next_token()

    def error(self):
        raise Exception('Invalid syntax')

    def eat(self, token_type):
        # compare the current token type with the passed token
        # type and if they match then "eat" the current token
        # and assign the next token to the self.current_token,
        # otherwise raise an exception.
        if self.current_token.type == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            self.error()

    def factor(self):
        """factor : INTEGER | LPAREN expr RPAREN"""
        token = self.current_token
        if token.type == INTEGER:
            self.eat(INTEGER)
            return token.value
        elif token.type == LPAREN:
            self.eat(LPAREN)
            result = self.expr()
            self.eat(RPAREN)
            return result

    def term(self):
        """term : factor ((MUL | DIV) factor)*"""
        result = self.factor()

        while self.current_token.type in (MUL, DIV):
            token = self.current_token
            if token.type == MUL:
                self.eat(MUL)
                result = result * self.factor()
            elif token.type == DIV:
                self.eat(DIV)
                result = result / self.factor()

        return result

    def expr(self):
        """Arithmetic expression parser / interpreter.

        calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
        22

        expr   : term ((PLUS | MINUS) term)*
        term   : factor ((MUL | DIV) factor)*
        factor : INTEGER | LPAREN expr RPAREN
        """
        result = self.term()

        while self.current_token.type in (PLUS, MINUS):
            token = self.current_token
            if token.type == PLUS:
                self.eat(PLUS)
                result = result + self.term()
            elif token.type == MINUS:
                self.eat(MINUS)
                result = result - self.term()

        return result


def main():
    while True:
        try:
            # To run under Python3 replace 'raw_input' call
            # with 'input'
            text = raw_input('calc> ')
        except EOFError:
            break
        if not text:
            continue
        lexer = Lexer(text)
        interpreter = Interpreter(lexer)
        result = interpreter.expr()
        print(result)


if __name__ == '__main__':
    main()

运行看看:

$ python calc6.py
calc> 3
3
calc> 2 + 7 * 4
30
calc> 7 - 8 / 4
5
calc> 14 + 2 * 3 - 6 / 2
17
calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
22
calc> 7 + 3 * (10 / (12 / (3 + 1) - 1)) / (2 + 3) - 5 - 3 + (8)
10
calc> 7 + (((3 + 2)))
12

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的G代码解释器的QT实现: 1. 创建一个新的QT项目,选择“应用程序”模板。 2. 在项目中添加一个新的类,命名为“GCodeInterpreter”。 3. 在GCodeInterpreter类中添加以下变量和函数: ```c++ private: QString m_gCode; // 存储G代码 int m_lineNumber; // 当前执行的行号 QMap<QString, double> m_variables; // 存储变量及其值的映射表 double getVariableValue(const QString& variableName); // 获取变量的值 void setVariableValue(const QString& variableName, double value); // 设置变量的值 void executeCurrentLine(); // 执行当前行 ``` 4. 实现getVariableValue和setVariableValue函数,用于获取和设置变量的值: ```c++ double GCodeInterpreter::getVariableValue(const QString& variableName) { if (m_variables.contains(variableName)) { return m_variables[variableName]; } else { return 0.0; } } void GCodeInterpreter::setVariableValue(const QString& variableName, double value) { m_variables[variableName] = value; } ``` 5. 实现executeCurrentLine函数,用于执行当前行的G代码: ```c++ void GCodeInterpreter::executeCurrentLine() { QString line = m_gCode.split('\n')[m_lineNumber - 1].trimmed(); // 获取当前行的G代码 // 解析G代码 QStringList parts = line.split(' '); QString command = parts[0].toUpper(); QStringList arguments = parts.mid(1); // 根据G代码命令执行相应的操作 if (command == "G00" || command == "G01") { // 移动 double x = getVariableValue("X"); double y = getVariableValue("Y"); double z = getVariableValue("Z"); for (const QString& argument : arguments) { QStringList argParts = argument.split('='); QString argName = argParts[0].toUpper(); double argValue = argParts[1].toDouble(); if (argName == "X") { x = argValue; } else if (argName == "Y") { y = argValue; } else if (argName == "Z") { z = argValue; } } // 执行移动操作 qDebug() << "Moving to (" << x << ", " << y << ", " << z << ")"; } else if (command == "G02" || command == "G03") { // 圆弧 double x = getVariableValue("X"); double y = getVariableValue("Y"); double z = getVariableValue("Z"); double i = getVariableValue("I"); double j = getVariableValue("J"); double k = getVariableValue("K"); double radius = qSqrt(qPow(i, 2) + qPow(j, 2) + qPow(k, 2)); for (const QString& argument : arguments) { QStringList argParts = argument.split('='); QString argName = argParts[0].toUpper(); double argValue = argParts[1].toDouble(); if (argName == "X") { x = argValue; } else if (argName == "Y") { y = argValue; } else if (argName == "Z") { z = argValue; } else if (argName == "I") { i = argValue; } else if (argName == "J") { j = argValue; } else if (argName == "K") { k = argValue; radius = qSqrt(qPow(i, 2) + qPow(j, 2) + qPow(k, 2)); } } // 执行圆弧操作 qDebug() << "Drawing arc with radius" << radius << "from (" << x << ", " << y << ", " << z << ")"; } else if (command == "M02" || command == "M30") { // 停止 qDebug() << "Stopping execution"; } else if (command == "M03" || command == "M04") { // 开启刀具 qDebug() << "Turning on tool"; } else if (command == "M05") { // 关闭刀具 qDebug() << "Turning off tool"; } else if (command == "M06") { // 更换刀具 qDebug() << "Changing tool"; } else if (command == "M08") { // 开启冷却液 qDebug() << "Turning on coolant"; } else if (command == "M09") { // 关闭冷却液 qDebug() << "Turning off coolant"; } m_lineNumber++; // 前往下一行 } ``` 6. 在GCodeInterpreter类中添加以下公共函数,用于设置G代码和执行G代码: ```c++ public: void setGCode(const QString& gCode); // 设置G代码 void execute(); // 执行G代码 ``` 7. 实现setGCode函数,用于设置G代码: ```c++ void GCodeInterpreter::setGCode(const QString& gCode) { m_gCode = gCode.trimmed(); m_lineNumber = 1; m_variables.clear(); setVariableValue("X", 0.0); setVariableValue("Y", 0.0); setVariableValue("Z", 0.0); } ``` 8. 实现execute函数,用于执行G代码: ```c++ void GCodeInterpreter::execute() { while (m_lineNumber <= m_gCode.split('\n').count()) { executeCurrentLine(); } } ``` 9. 在QT项目中的主窗口中添加一个文本框和一个按钮,用于输入G代码和执行G代码。 10. 在主窗口的构造函数中连接按钮的clicked信号到一个槽函数,该槽函数中创建一个GCodeInterpreter对象并调用setGCode和execute函数,将文本框中的G代码作为参数传递给GCodeInterpreter对象。 ```c++ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // ... connect(ui->executeButton, &QPushButton::clicked, this, [this]() { GCodeInterpreter interpreter; interpreter.setGCode(ui->gCodeTextEdit->toPlainText()); interpreter.execute(); }); } ``` 11. 运行QT项目,输入一些G代码并点击执行按钮,查看输出结果。 注意:这只是一个简单的G代码解释器的实现,实际中可能需要添加更多的G代码命令和参数解析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值