日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 8.)(笔记)一元运算符正负(+,-)

發布時間:2025/3/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 8.)(笔记)一元运算符正负(+,-) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【編譯原理】讓我們來構建一個簡單的解釋器(Let’s Build A Simple Interpreter. Part 8.)

文章目錄

    • C語言代碼(作者沒提供完整的python代碼,關鍵的改動提供了,在上面,完整的python代碼這里略掉)

今天我們將討論一元運算符,即一元加(+)和一元減(-)運算符。

今天的很多資料都基于上一篇文章中的資料,因此如果您需要復習,請返回第 7 部分并再次閱讀。請記住:重復是所有學習之母。

話雖如此,這就是你今天要做的:

  • 擴展語法以處理一元加號和一元減號運算符
  • 添加一個新的UnaryOp AST節點類
  • 擴展解析器以生成具有UnaryOp 節點的AST
  • 擴展解釋器并添加一個新的visit_UnaryOp方法來解釋一元運算符

讓我們開始吧,好嗎?

到目前為止,我們只使用了二元運算符(+、-、*、/),即對兩個操作數進行運算的運算符。

那什么是一元運算符呢?一元運算符是 僅對一個操作數進行運算的運算符。

以下是一元加號和一元減號運算符的規則:

  • 一元減號 (-) 運算符產生其數值操作數的否定
  • 一元加號 (+) 運算符生成其數字操作數而無需更改
  • 一元運算符的優先級高于二元運算符 +、-、* 和 /

在表達式“+ - 3”中,第一個’+‘運算符代表一元加運算,第二個’-'運算符代表一元減運算。表達式“+ - 3”等價于“+ (- (3))”,它等于-3。也可以說表達式中的-3是一個負整數,但在我們的例子中,我們將其視為一元減運算符,其中 3 作為其正整數操作數:


我們再來看看另一個表達式,“5 - - 2”:


在表達式“5 - - 2”中,第一個’-‘代表二元減法運算,第二個’-'代表一元減法運算,即否定。

還有一些例子:



現在讓我們更新我們的語法以包含一元加號和一元減號運算符。我們將修改因子規則并在那里添加一元運算符,因為一元運算符的優先級高于二元 +、-、* 和 / 運算符。

這是我們當前的因子 規則:

這是我們處理一元加和一元減運算符的更新因子規則:

如您所見,我將因子規則擴展為引用自身,這使我們能夠推導出諸如“- - - + - 3”之類的表達式,這是一個帶有許多一元運算符的合法表達式。

這是現在可以使用一元加號和一元減號運算符派生表達式的完整語法:

下一步是添加一個AST節點類來表示一元運算符。

這個會做:

class UnaryOp(AST):def __init__(self, op, expr):self.token = self.op = opself.expr = expr

構造函數接受兩個參數:op,它代表一元運算符標記(加號或減號)和expr,它代表一個AST 節點。

我們更新的語法對factor規則進行了更改,因此這就是我們要在解析器中修改的內容 -factor方法。我們將添加代碼來處理方法“(PLUS | MINUS)factor”分治:

def factor(self):"""factor : (PLUS | MINUS) factor | INTEGER | LPAREN expr RPAREN"""token = self.current_tokenif token.type == PLUS:self.eat(PLUS)node = UnaryOp(token, self.factor())return nodeelif token.type == MINUS:self.eat(MINUS)node = UnaryOp(token, self.factor())return nodeelif token.type == INTEGER:self.eat(INTEGER)return Num(token)elif token.type == LPAREN:self.eat(LPAREN)node = self.expr()self.eat(RPAREN)return node

現在我們需要擴展Interpreter類并添加一個visit_UnaryOp方法來解釋一元節點:

def visit_UnaryOp(self, node):op = node.op.typeif op == PLUS:return +self.visit(node.expr)elif op == MINUS:return -self.visit(node.expr)

向前!

讓我們手動為表達式“5 - - - 2”構建一個AST并將其傳遞給我們的解釋器以驗證新的visit_UnaryOp方法是否有效。以下是從 Python shell 執行此操作的方法:

>>> from spi import BinOp, UnaryOp, Num, MINUS, INTEGER, Token >>> five_tok = Token(INTEGER, 5) >>> two_tok = Token(INTEGER, 2) >>> minus_tok = Token(MINUS, '-') >>> expr_node = BinOp( ... Num(five_tok), ... minus_tok, ... UnaryOp(minus_token, UnaryOp(minus_token, Num(two_tok))) ... ) >>> from spi import Interpreter >>> inter = Interpreter(None) >>> inter.visit(expr_node) 3

從視覺上看,上面的AST樹是這樣的:

直接從GitHub下載本文解釋器的完整源代碼。親自嘗試一下,看看您更新的基于樹的解釋器是否正確評估包含一元運算符的算術表達式。

這是一個示例會話:

$ python spi.py spi> - 3 -3 spi> + 3 3 spi> 5 - - - + - 3 8 spi> 5 - - - + - ( 3 + 4 ) - +210

我還更新了genastdot.py實用程序來處理一元運算符。以下是為帶有一元運算符的表達式生成的AST圖像的一些示例:

$ python genastdot.py "- 3" > ast.dot && dot -Tpng -o ast.png ast.dot

$ python genastdot.py "+ 3" > ast.dot && dot -Tpng -o ast.png ast.dot

$ python genastdot.py "5 - - - + - 3" > ast.dot && dot -Tpng -o ast.png ast.dot

$ python genastdot.py "5 - - - + - (3 + 4) - +2" \ > ast.dot && dot -Tpng -o ast.png ast.dot

這是給你的一個新練習:

  • 安裝Free Pascal,編譯并運行testunary.pas,并驗證結果與使用spi 解釋器生成的結果相同。

這就是今天的全部內容。在下一篇文章中,我們將處理賦值語句。請繼續關注,很快就會見到你。

C語言代碼(作者沒提供完整的python代碼,關鍵的改動提供了,在上面,完整的python代碼這里略掉)

#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <string.h> #include<math.h>#define flag_integer 0 #define flag_plus 1 #define flag_minus 2 #define flag_multiply 3 #define flag_divide 4 #define flag_LPAREN 5 #define flag_RPAREN 6 #define flag_plus_sign 7 #define flag_minus_sign 8#define flag_EOF 7void error() {printf("程序不對勁!\n");exit(-1); }//給字符數組賦值 void StrAssign(char* T, const char* chars) {int i;for (i = 0; i < strlen(chars); i++)T[i] = *(chars + i);T[i] = '\0'; }struct Token {int type;int value; };//Token初始化函數 struct Token* mallocToken(int type, int value) {struct Token* token = (Token*)malloc(sizeof(Token));if (NULL != token) {token->type = type;token->value = value;return token;}elseerror(); }//樹的節點 struct BinOpOrNum {struct BinOpOrNum* left;struct Token* op_or_num;struct BinOpOrNum* right; };//樹結點初始化函數 struct BinOpOrNum* mallocOpOrNum(struct BinOpOrNum* left, struct Token* op_or_num, struct BinOpOrNum* right) {struct BinOpOrNum* node = (BinOpOrNum*)malloc(sizeof(BinOpOrNum));if (NULL != node) {node->left = left;node->op_or_num = op_or_num;node->right = right;return node;}elseerror(); }struct Lexer {char* text;int pos; };struct Parser {struct Lexer* lexer;struct Token current_token; };struct Interpreter {struct Parser* parser; };void skip_whitespace(struct Lexer* le) {while (le->text[le->pos] == ' ') {le->pos++;} }//判斷Interpreter中當前pos是不是數字 int is_digital(char c) {if (c >= '0' && c <= '9')return 1;elsereturn 0; }void advance(struct Lexer* le) {le->pos++; }char current_char(struct Lexer* le) {return(le->text[le->pos]); }//獲取數字token的數值(把數字字符數組轉換為數字) int integer(struct Lexer* le) {char temp[20];int i = 0;while (is_digital(le->text[le->pos])) {temp[i] = le->text[le->pos];i++;advance(le);}int result = 0;int j = 0;int len = i;while (j < len) {result += (temp[j] - '0') * pow(10, len - j - 1);j++;}return result; }void get_next_token(struct Parser* par) {//先跳空格,再判斷有沒有結束符if (current_char(par->lexer) == ' ')skip_whitespace(par->lexer);if (par->lexer->pos > (strlen(par->lexer->text) - 1)) {par->current_token = { flag_EOF, NULL };return;}char current = current_char(par->lexer);if (is_digital(current)) {par->current_token = { flag_integer, integer(par->lexer) };return;}if (current == '+') {par->current_token = { flag_plus, NULL };advance(par->lexer);return;}if (current == '-') {par->current_token = { flag_minus, NULL };advance(par->lexer);;return;}if (current == '*') {par->current_token = { flag_multiply, NULL };advance(par->lexer);;return;}if (current == '/') {par->current_token = { flag_divide, NULL };advance(par->lexer);;return;}if (current == '(') {par->current_token = { flag_LPAREN, NULL };advance(par->lexer);;return;}if (current == ')') {par->current_token = { flag_RPAREN, NULL };advance(par->lexer);;return;}error();//如果都不是以上的字符,則報錯并退出程序 }int eat(struct Parser* par, int type) {int current_token_value = par->current_token.value;if (par->current_token.type == type) {get_next_token(par);return current_token_value;}else {error();} }//遍歷樹 int visit_BinOp(struct BinOpOrNum* node) {if (node->op_or_num->type == flag_plus)return visit_BinOp(node->left) + visit_BinOp(node->right);else if (node->op_or_num->type == flag_minus)return visit_BinOp(node->left) - visit_BinOp(node->right);else if (node->op_or_num->type == flag_multiply)return visit_BinOp(node->left) * visit_BinOp(node->right);else if (node->op_or_num->type == flag_divide)return visit_BinOp(node->left) / visit_BinOp(node->right);else if (node->op_or_num->type == flag_integer)return node->op_or_num->value;else if (node->op_or_num->type == flag_plus_sign)return visit_BinOp(node->right);else if (node->op_or_num->type == flag_minus_sign)return (-1) * visit_BinOp(node->right); }struct BinOpOrNum* expr(struct Parser* par);//expr定義在后面,在這里要聲明才能使用 //判斷數字或括號或正負號(加減號后出現的正負號) struct BinOpOrNum* factor(struct Parser* par) {if (par->current_token.type == flag_plus) {eat(par, flag_plus);struct Token* token = mallocToken(flag_plus_sign , NULL);struct BinOpOrNum* node = mallocOpOrNum(NULL, token, factor(par));return node;}else if (par->current_token.type == flag_minus) {eat(par, flag_minus);struct Token* token = mallocToken(flag_minus_sign, NULL);struct BinOpOrNum* node = mallocOpOrNum(NULL, token, factor(par));return node;}else if (par->current_token.type == flag_integer) {struct Token* token = mallocToken(par->current_token.type, par->current_token.value);eat(par, flag_integer);struct BinOpOrNum* node = mallocOpOrNum(NULL, token, NULL);return node;}else if (par->current_token.type == flag_LPAREN) {eat(par, flag_LPAREN);//遇到括號先吃掉,然后回到exprstruct BinOpOrNum* node = expr(par);eat(par, flag_RPAREN);return node;} }//判斷乘除 struct BinOpOrNum* term(struct Parser* par) {struct BinOpOrNum* node = factor(par);while (par->current_token.type == flag_multiply or par->current_token.type == flag_divide) {struct Token* token = mallocToken(par->current_token.type, par->current_token.value);eat(par, par->current_token.type);node = mallocOpOrNum(node, token, factor(par));}return node; }//判斷加減 struct BinOpOrNum* expr(struct Parser* par) {struct BinOpOrNum* node = term(par);while (par->current_token.type == flag_plus or par->current_token.type == flag_minus) {struct Token* token = mallocToken(par->current_token.type, par->current_token.value);eat(par, par->current_token.type);//struct BinOpOrNum* node = mallocOpOrNum(node, token, term(par));//重定義了,還報錯使用未經初始化的指針,找了半天才發現問題node = mallocOpOrNum(node, token, term(par));}return node; }struct BinOpOrNum* parser(struct Parser* par) {return expr(par); }int interpreter(struct Interpreter* ipt) {struct BinOpOrNum* tree = parser(ipt->parser);return visit_BinOp(tree); }int main() {char text[50];//StrAssign(text, "(3+2) *7");//StrAssign(text, " 3+2*(3+7)+(1 - (3+7)/5)* (2/2+3 )* 1");StrAssign(text, "5 - - - + - ( 3 + 4 ) - +2");struct Lexer le = { text, 0 };struct Parser par = { &le };get_next_token(&par);struct Interpreter ipt = { &par };int result = interpreter(&ipt);printf("%s = %d\n\n", text, result);return 0; }

較上一課,主要改動了factor函數中添加正負號結點的代碼,以及visit_BinOp函數中訪問正負號結點的代碼

運行結果:

5 - - - + - ( 3 + 4 ) - +2 = 10

總結

以上是生活随笔為你收集整理的【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 8.)(笔记)一元运算符正负(+,-)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 小视频免费在线观看 | 欧美日韩不卡一区二区 | 日日日日日日 | 久热国产在线 | 国产黄频在线观看 | 国产成人欧美一区二区三区的 | 超碰人人超 | 丁香婷婷综合网 | av福利网址| 欧美激情精品久久久久久变态 | 免费网站成人 | 永久av在线 | 水蜜桃91| 国产在线播放一区二区 | 中文字幕15页 | av地址在线 | 欧美一级特黄视频 | 欧美一区综合 | 嫩草视频免费观看 | 色翁荡息又大又硬又粗又爽 | 男人添女人囗交视频 | 性欧美videos另类hd | 最新日本中文字幕 | 中文在线国产 | 中国女人一级一次看片 | 黄色av一区 | 国产精品白虎 | 久草免费资源站 | 久久黄色免费视频 | av综合网站 | 午夜国产福利在线 | 日韩三区四区 | 一级黄色片在线看 | 久久久精品福利 | 插插看 | 天天插天天狠天天透 | 亚洲伦理精品 | 国产在线一区二区 | 欧美日韩在线视频观看 | 岛国裸体写真hd在线 | 日韩一区二区三区视频在线观看 | 国产一区二区小说 | 日韩中文在线观看 | 日韩高清在线一区二区 | 色哟哟入口 | 亚洲大片在线观看 | 亚洲精品激情视频 | 亚洲中文字幕视频一区 | 国产女人18毛片水真多18 | 国产无套视频 | 一区二区乱码 | 久久久老司机 | 成人一区二区电影 | 激情xxxx | 日本免费电影一区二区三区 | 精品国产午夜 | 国产一区二区在线播放视频 | 欧美三级午夜理伦三级老人 | 久久国产精品偷 | 国产日韩欧美精品一区二区 | av在线网址观看 | 黄色天堂av | 国产sm主人调教女m视频 | 人妻丰满熟妇aⅴ无码 | 反差在线观看免费版全集完整版 | 丁香六月av | 色欲一区二区三区精品a片 在线观看黄网站 | 91av视频在线免费观看 | aa毛片视频 | 欧洲做受高潮免费看 | 自拍偷自拍亚洲精品播放 | 少妇高潮大叫好爽喷水 | 欧美成人免费一级 | 亚欧洲精品在线视频免费观看 | 超碰caoprom | 亚洲国产97| 中文字幕一区二区三区在线观看 | 欧美天天射 | 青青草国产成人av片免费 | 亚洲视频自拍偷拍 | 久久9966 | 夜夜躁狠狠躁 | 五月婷婷一区二区三区 | 精品人妻无码一区二区三 | 91网站免费 | 秋霞影院一区二区 | 亚洲综合丁香 | 亚洲激情小视频 | 久久韩国 | 午夜成人亚洲理伦片在线观看 | 少妇15p | 偷拍第一页 | 亚洲系列在线 | 台湾佬中文在线 | 嫩草视频在线免费观看 | 久久久久久69 | 琪琪色网 | 91美女啪啪 | 亚洲精品日日夜夜 |