c++ 如何在编译器中区分负号和减号

l2osamch  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(203)

我正在写一个小的PL/0编译器用于实践。
我在编写表达式求值部分时遇到了一些问题。
下面是一个小例子:

-2 + 1

字符串
我的程序逻辑如下:
1.词法分析
根据我找到的文档,在PL/0中没有“负整数”这样的东西,因此-2将被解析为-2。该步骤的结果是:-2+1
1.句法分析
我在这一步中使用SLR 1,因为表达式是正确的,所以不会发生任何事情。
1.语义分析
程序将在这一步中构建一个 * 抽象语法树 *。原理很简单,一个操作符栈和一个操作数栈共同完成这一部分。但是,“负”号在我的代码中会被当作“减号”处理,我没有好的办法来处理这个问题。
下面是一段显示逻辑的代码:

struct ASTNode {
  ASTNode(char op, int val, ASTNode *left = nullptr, ASTNode *right = nullptr);

  char _op;                   // operator
  int _val;                   // operand
  shared_ptr<ASTNode> _left;  // left child node
  shared_ptr<ASTNode> _right; // right child node
};

void semantic_analyzer::construct_tree(vector<string> &tokens) {
  stack<shared_ptr<ASTNode>> nodeStack;  // operand stack
  stack<char> opStack;                   // operator stack

  // iterate tokens and construct part of the AST
  for (auto &token : tokens) {
    // if the token is a positive integer
    if (str_opekit::is_digit(token)) {
      int val = stoi(token);
      nodeStack.push(make_shared<ASTNode>(' ', val)); // the children will be set as nullptr
    }
    // If the token is a left paren, push it into operator stack
    else if (token == "(") {
      opStack.push('(');
    }
    // If the token is a right paren, pop the operator in the operator stack and calculate result
    else if (token == ")") {
      // Keep popping operators in the operator stack until reach a left paren
      while (opStack.top() != '(') {
        // Pop two operands in the operand stack and construct them into a node
        shared_ptr<ASTNode> right = nodeStack.top();
        nodeStack.pop();
        shared_ptr<ASTNode> left = nodeStack.top();
        nodeStack.pop();
        // Construct a new ASTNode with the two nodes and push into the stack
        shared_ptr<ASTNode> node(new ASTNode(opStack.top(), 0, left, right));
        opStack.pop();
        nodeStack.push(node);
      }
      opStack.pop();  // pop left paren
    }
    // If the token is #, which stands for the end of the expression
    else if (token == "#") {
      break;
    }
    // If the token is an operator, compare its priority with the operator on the top
    // of the stack
    else {
      char op = token[0];
      // while:
      // 1. the operator stack is not empty
      // 2. the operator on the top of the stack is not a left paren
      // 3. the priority of the operator on the top of the stack is higher
      while (!opStack.empty() && opStack.top() != '(' &&
             is_prior(op, opStack.top()) ) {
        // Pop two operands in the stack and construct them into an ASTNode
        shared_ptr<ASTNode> right = nodeStack.top();
        nodeStack.pop();
        shared_ptr<ASTNode> left = nodeStack.top();
        nodeStack.pop();
        // Construct a new ASTNode with the two nodes and push it into the
        // operand stack
        shared_ptr<ASTNode> node =
            make_shared<ASTNode>(opStack.top(), 0, left, right);
        opStack.pop();
        nodeStack.push(node);
      }
      opStack.push(op);
    }
  }

  // Process operators and operands in the stack and construct the tree
  while (!opStack.empty()) {
    shared_ptr<ASTNode> right = nodeStack.top();
    nodeStack.pop();
    shared_ptr<ASTNode> left = nodeStack.top();
    nodeStack.pop();
    shared_ptr<ASTNode> node =
        make_shared<ASTNode>(opStack.top(), 0, left, right);
    opStack.pop();
    nodeStack.push(node);
  }

  this->_root = nodeStack.top();
  nodeStack.pop();
}


由于“negative”符号会被解析为“minus”符号,不仅执行结果会出错,而且程序会在空堆栈上调用pop(),从而导致崩溃。
如果您需要更多关于我的代码的详细信息,请通知我。欢迎任何建议。
EBNF是我以前学过的一门课程。与问题相关的部分如下:

  1. letter ::= a|b|...|X|Y|Z
  2. digit ::= 0|1|...|8|9
  3. identifier ::= <letter>{<letter>|<digit>}
  4. unsigned integer ::= <digit>{<digit>}
  5. factor ::= <identifier>|<unsigned integer>|...
  6. item ::= <factor>{<multiplying operator><factor>}
  7. expression ::= [+|-]<item>{<adding operator><item>}
    '|'代表'|“在正则表达式中
    '{...}'代表正则表达式中的“*”
    '[...]'代表“零或一”,与“?“在正则表达式中
bejyjqdl

bejyjqdl1#

解析器必须记下它解析的输入的每个元素。例如,当它看到一个expression时,它必须告诉后面的阶段是否只有一个item,或者是否有多个item s,中间有adding operator s以及哪些adding operator s。(最有可能的是,解析通过构建语法树来实现。)
同时,解析器还可以注意到它在第一个item之前是看到了+-还是没有看到。就这么简单

u91tlkcl

u91tlkcl2#

你的解析器必须构建例如表达式,由其他表达式和运算符(项)组成。
-运算符可以同时存在于

*prefix position(作为前缀运算符)在expression之前和
*中缀位置,在两个expression之间。

当解析器解析第一个标记-时,它必须检查它是否可以存在于 * 前缀位置 *,并解析它(使用运算符和表达式发出前缀AST节点)或生成错误。如果解析器在 * 中缀位置 * 检测到-标记,它必须检查标记是否可以在中缀位置,并生成一个带有leftright表达式以及operator中缀AST节点

相关问题