编译原理运算符
1. 编译原理:优先函数 f和g 到底怎么看啊,不懂怎么构造的 求解...
求算符优先函数的方法—迭代法
若已知运算符之间的优先关系,可按如下步骤构造优先函数:
1、对每个运算符a(包括#在内)令f(a)=g(a)=1
2、如果a⋗b且f(a)<=g(b),令f(a)=g(b)+1
3、如果a⋖b且f(a)>=g(b),令g(b)= f(a)+1
4、如果a≐b而f(a) ≠g(b),令min{f(a),g(b)}=max{f(a),g(b)}
5、重复2~4,直到过程收敛。如果重复过程中有一个值大于2n,则表明不存在算符优先函数。
2. 编译原理四元式
四元式的一般形式为(op, arg1, arg2, result),其中:op为一个二元(也可以是零元或一元)运算符。arg1和arg2为两个运算对象,可以是变量、常数或者系统定义的临时变量名。result为运算结果。
第一步:T1=a*b,
第二步:T2=c*d,
第三步:T3=T2/e,
第四步:T4=T1-T3,
第五步:f=T4.
3. java中的正则表达式跟编译原理有什么联系
首先,正则表达式不仅在Java里有,其它语言里面也有,它是一个数学上的概念,各个语言中的正则表达式是它的不同形式的实现。
其次,编译原理的词法分析里,会用到正则表达式去匹配源程序中的各种token(记号),比如说
int a = 8;
里识别出:
类型名:int
变量名:a
运算符:=
数字:8
结尾分号:;
总之,二者有联系,但不是一回事。
4. 编译原理有有符号un-1.u=un吗
编译程序把源程序翻译为目标程序。根据源程序的语言种类,翻译程序可以分为汇编程序与编译程序。与之相对,解释程序是对源程序进行解释执行的程序。相应的可以将高级语言分为
编译型 C/C++, Swift, etc.
解释型 Python, javascript, etc.
混合型 Java, etc.
本文重点放在编译程序的设计上。典型的编译程序具有 7 77 个逻辑部分
对源程序扫描一次被称为一遍 (pass)。典型的一遍扫描编译程序有如下形式
通常将中间代码生成前的分析部分称为编译器的前端,其后的综合部分则被称为后端。这样就把一个编译程序分为了与源语言相关和与目标机有关的两个独立的部分,降低了程序的耦合。假设 llvm 编译器 支持 M MM 种源语言到 N NN 种目标语言的编译
传统的编译器如 gcc 可能需要开发 M × N M \times NM×N 个不同的子模块。而 llvm 使用统一的中间语言 llvm Intermediate Representation 只需要 M MM 个前端与 N NN 个后端,大大降低了开发成本。
文法
设非空有穷集合 Σ \SigmaΣ 为一字母表,则其上的符号串为 ∀ s ∈ Σ ∗ \forall s \in \Sigma^*∀s∈Σ
∗
,其中 ∗ *∗ 表示集合的闭包。特别的记 Σ 0 = ε \Sigma^0 = {\varepsilon}Σ
0
=ε 为空串组成的集合。规则通常写作
U : : = x or U → x , ∣ U ∣ = 1 , ∣ x ∣ ≥ 0 U ::= x\text{ or }U\rightarrow x,\quad |U| = 1, |x| \ge 0U::=x or U→x,∣U∣=1,∣x∣≥0
其中左部 U UU 是符号,右部 x xx 是有穷符号串。规则的集合 P PP 即可确定一个文法 G GG
<程序> ::= <常量说明><变量说明><函数说明>
<常量说明> ::= {const<常量定义>;}
<常量定义> ::= int<标识符>=<整数>{,<标识符>=<整数>}|char<标识符>=<字符>{,<标识符>=<字符>}
<变量说明> ::= {<类型标识符><变量定义>;}
<变量定义> ::= <标识符>[<下标>]{,<标识符>[<下标>]}
<下标> ::= '['<无符号整数>']' // <无符号整数>表示数组元素的个数,其值需大于0
<函数说明> ::= {(<类型标识符>|void)<函数定义>}void<主函数>
<函数定义> ::= <标识符>'('<参数表>')'<复合语句>
<参数表> ::= [<类型标识符><标识符>{,<类型标识符><标识符>}]
<主函数> ::= main'('')'<复合语句>
<复合语句> ::= '{'<常量说明><变量说明>{<语句>}'}'
<语句> ::= <条件语句>|'{'{<语句>}'}'|<函数调用语句>;|<赋值语句>;|<读语句>;|<写语句>;|<返回语句>;|;
<条件语句> ::= <if语句>|<while语句>|<do语句>|<for语句>
<if语句> ::= if'('<条件>')'<语句>[else<语句>]
<while语句> ::= while'('<条件>')'<语句>
<do语句> ::= do<语句>while'('<条件>')'
<for语句> ::= for'('<标识符>=<表达式>;<条件>;<标识符>=<标识符><加法运算符><无符号整数>')'<语句>
<条件> ::= <表达式>[<关系运算符><表达式>] // 表达式为0条件为假,否则为真
<函数调用语句> ::= <标识符>'('[<表达式>{,<表达式>}]')'
<赋值语句> ::= <标识符>['['<表达式>']']=<表达式>
<读语句> ::= scanf'('<标识符>{,<标识符>}')'
<写语句> ::= printf'('<字符串>[,<表达式>]')'|printf'('<表达式>')'
<返回语句> ::= return['('<表达式>')']
<表达式> ::= [<加法运算符>]<项>{<加法运算符><项>} // [+|-]只作用于第一个<项>
<项> ::= <因子>{<乘法运算符><因子>}
<因子> ::= <标识符>['['<表达式>']']|'('<表达式>')'|<整数>|<字符>|<函数调用语句>
<整数> ::= [<加法运算符>]<无符号整数>
<标识符> ::= <字母>{<字母>|<数字>}
<无符号整数> ::= <非零数字>{<数字>}|0
<数字> ::= 0|<非零数字>
<非零数字> ::= 1|...|9
<字符> ::= '<加法运算符>'|'<乘法运算符>'|'<字母>'|'<数字>'
<字符串> ::= "{十进制编码为32,33,35-126的ASCII字符}"
<类型标识符> ::= int|char
<加法运算符> ::= +|-
<乘法运算符> ::= *|/
<关系运算符> ::= <|<=|>|>=|!=|==
<字母> ::= _|a|...|z|A|...|Z
复制
上述文法使用扩充的 BNF 表示法进行描述
符号 定义 说明
∣ \vert∣ 或 作用域由括号限定
{ t } n m \{t\}^m_n{t}
n
m
将 t tt 重复连接 n ∼ m n \sim mn∼m 次 缺省时 m = ∞ , n = 0 m = \infin,\ n = 0m=∞, n=0
[ t ] [t][t] 符号串 t tt 可有可无 等价于 { t } 1 \{t\}^1{t}
1
( t ) (t)(t) 局部作用域 主要用于限定 ∣ \vert∣ 范围
相关概念有
概念 符号 定义 示例
识别符号 Z ZZ 文法中第一条规则的左部符号 <程序>
字汇表 V VV 文法中出现的全部符号 { <程序>, <常量说明>, …, 0, 1, … }
非终结符号集 V n V_nV
n
全部规则的左部组成的集合 { <程序>, <常量说明>, <变量说明>, … }
终结符号集 V t V_tV
t
V − V n V - V_nV−V
n
{ 0, 1, …, _, a, b, … }
设 U : : = u ∈ P U ::= u \in PU::=u∈P 则对于 ∀ x , y ∈ V ∗ \forall x, y \in V^*∀x,y∈V
∗
有直接推导 x U y ⇒ x u y xUy \Rightarrow xuyxUy⇒xuy 。如果 y ∈ V t ∗ y \in V_t^*y∈V
t
∗
则 x U y ⤃ x u y xUy\ ⤃\ xuyxUy ⤃ xuy 称为规范推导。直接推导序列 u 0 ⇒ u 1 ⇒ ⋯ ⇒ u n u_0 \Rightarrow u_1 \Rightarrow \cdots \Rightarrow u_nu
0
⇒u
1
⇒⋯⇒u
n
可简记为
{ u 0 ⇒ + u n n > 0 u 0 ⇒ ∗ u n n ≥ 0 \begin{cases} u_0 \mathop\Rightarrow\limits^+ u_n & n > 0\\ u_0 \mathop\Rightarrow\limits^* u_n & n \ge 0\\ \end{cases}{
u
0
⇒
+
u
n
u
0
⇒
∗
u
n
n
>
0
n
≥
0
进一步定义
句型 V ∗ ∋ x ⇐ ∗ Z V^* \ni x \mathop\Leftarrow\limits^* ZV
∗
∋x
⇐
∗
Z
句子 V t ∗ ∋ x ⇐ + Z V_t^* \ni x \mathop\Leftarrow\limits^+ ZV
t
∗
∋x
⇐
+
Z
语言 L ( G ) = { x ∣ x is sentence } L(G) = \{ x| x\text{ is sentence} \}L(G)={x∣x is sentence}
如果文法 G GG 和 G ′ G'G
′
有 L ( G ) = L ( G ′ ) L(G) = L(G')L(G)=L(G
′
) ,则称这两个文法等价。设 w = x u y w=xuyw=xuy 为一句型,称 u uu 为一个相对于 U ∈ V n U \in V_nU∈V
n
的
w ww 的短语 如果 Z ⇒ ∗ x U y ∧ U ⇒ + u Z \mathop\Rightarrow\limits^* xUy \land U \mathop\Rightarrow\limits^+ uZ
⇒
∗
xUy∧U
⇒
+
u
w ww 的简单短语 如果 u uu 是短语且 U ⇒ u U \mathop\Rightarrow\limits uU⇒u
句型的最左简单短语称为句柄。
二义性
文法 G GG 是二义性的,如果 ∃ x ∈ L ( G ) \exist x \in L(G)∃x∈L(G) 使下列条件之一成立
x xx 可以对应两颗不同的语法树
x xx 有两个不同的规范推导
5. 闄堢伀镞 缂栬疟铡熺悊
绗涓绔狅细缂栬疟铡熺悊鍏ラ棬</
鍦ㄧ▼搴忕殑涓栫晫閲岋纴缈昏疟绋嫔簭鏄璇瑷闂寸殑妗ユ侊纴瀹冨皢婧愯瑷浠g爜杞鍖栦负阃昏緫绛変环镄勬墽琛屽舰寮忥纸婧愯瑷绋嫔簭 → 阃昏緫绛変环鐩镙囱瑷绋嫔簭</锛夈傜紪璇戠▼搴忓垯镓婕旂潃鍏抽敭瑙掕壊锛岄氲繃楂樼骇璇瑷锛埚侾ython锛夎浆鍖栦负浣庣骇璇瑷锛埚傛眹缂栨垨链哄櫒镰侊级锛岀粡杩囩紪璇戝啀镓ц岋纴鎻愬崌璁$畻鏁堢巼锛楂樼骇璇瑷 → 浣庣骇璇瑷 → 璁$畻镓ц</锛夈傜浉姣斾箣涓嬶纴瑙i喷绋嫔簭鍒椤疄镞惰В鏋愬苟镓ц岋纴娌℃湁棰勫厛缂栬疟镄勮繃绋嬨
缂栬疟杩囩▼阃氩父鍒嗕负浜斾釜阒舵碉细璇嶆硶鍒嗘瀽銆佽娉曞垎鏋愩佽涔夊垎鏋愩佷紭鍖栧拰鐩镙囦唬镰佺敓鎴愩傚叾涓锛岀﹀彿琛ㄦ槸绋嫔簭镄勪腑鏋锛岃板綍婧愪唬镰佺殑鍏抽敭淇℃伅锛屾秹鍙婂悇绉嶈〃镙硷纸绗﹀彿琛锛氭簮绋嫔簭淇℃伅镄勯泦鎴愪粨搴</锛夈傞亶铡嗘簮鏂囦欢鏄绋嫔簭澶勭悊镄勬牳蹇幂幆鑺傦纴缂栬疟鍣ㄥ垎涓哄墠绔鍜屽悗绔锛屽墠绔鍏虫敞婧愯瑷鐗规э纴钖庣鍒欓傚簲鐩镙囨満鍣ㄦ灦鏋勶纸缂栬疟鍓岖锛氭簮璇瑷鐗规х殑澶勭悊锛岀紪璇戝悗绔锛氱洰镙囨満鍣ㄧ殑阃傞厤</锛夈
璇瑷镄勫畾涔夌敱璇娉曞拰璇涔変袱閮ㄥ垎鏋勬垚锛屽寘𨰾鎶借薄镄勯昏緫缁撴瀯鍜岃$畻链烘墽琛岀殑缁呜妭銆傜▼搴忎腑镄勮鍙ュ垎涓烘墽琛屾у拰璇存槑镐э纴濡傝祴鍊笺佹带鍒舵祦鍜岃緭鍏/杈揿嚭鎸囦护锛镓ц屼笌璇存槑锛氲瑷镄勫熀鐭</锛夈备笂涓嬫枃镞犲叧鏂囨硶鐢辩粓缁撶﹀彿銆侀潪缁堢粨绗︺佸紑濮嬬﹀彿鍜屼骇鐢熷纺瀹氢箟锛屾瀯鎴愮▼搴忕粨鏋勭殑锘虹锛涓娄笅鏂囨棤鍏虫枃娉曪细缂栫▼璇瑷镄勯ㄦ灦</锛夈
鎺ヤ笅𨱒ョ殑绔犺妭娣卞叆鎺㈣ㄦ枃娉旷殑纭瀹氭у拰浜屼箟镐э纴浠ュ强鏂囨硶镄勭被鍨嫔垝鍒嗭纴濡傜煭璇鏂囨硶銆佷笂涓嬫枃链夊叧鏂囨硶绛夛纴杩欎簺閮芥槸璁捐″拰鐞呜В缂栬疟鍣ㄧ殑鍏抽敭姒傚康锛鏂囨硶绫诲瀷锛氢粠绠鍗曞埌澶嶆潅锛屽畾涔夎瑷镄勮竟鐣</锛夈
璇嶆硶鍒嗘瀽鍣ㄦ槸缂栬疟娴佺▼镄勮捣镣癸纴瀹冨皢婧愮▼搴忓垎瑙d负鍙璇嗗埆镄勫崟鍏冿纸璇嶆硶鍒嗘瀽锛氱紪璇戝熀鐭</锛夛纴骞朵笖阃氩父杈揿嚭浜屽厓寮忋备粠NFA鍒瘅FA镄勮浆鎹锛岄氲繃瀛愰泦娉曟秷闄や笉纭瀹氭э纴浼桦寲鐘舵佽〃绀猴纸NFA鍒瘅FA镄勮浆鎹锛氱簿纭鐘舵佽〃绀虹殑杞鎹</锛夈
LR鍒嗘瀽鍣ㄦ槸寮哄ぇ镄勫垎鏋愬伐鍏凤纴瀹冨湪LL鏂囨硶镄勬墿灞曚笂琛ㄧ幇鍑鸿壊锛屾棤锲炴函镄勭Щ杩-褰掔害链哄埗浣垮缑阌栾妫娴嬫洿鍙婃椂锛屽垎鏋愯〃璁捐″嶆潅锛LR鍒嗘瀽锛氶珮鏁埚垎鏋愪笌阌栾妫娴</锛夈侺R鍒嗘瀽鍣ㄧ敱锷ㄤ綔琛ㄥ拰杞绉昏〃鏋勬垚锛屾敮鎸佸嶆潅杩愮畻绗﹀拰宸﹂掑綊銆
灞炴ф枃娉曚綔涓虹紪璇戝师鐞嗙殑閲嶈佸垎鏀锛岄氲繃涓烘枃娉旷﹀彿璧嬩篑灞炴э纴濡傜被鍨嫔拰鍊硷纴甯锷╁勭悊璇涔変俊鎭銆傚畠浠鍦ㄧ紪璇戣繃绋嬩腑鍙戞尌鍏抽敭浣灭敤锛岀壒鍒鏄鍦ㄨ娉曞垎鏋愩佽涔夊勭悊鍜屼腑闂翠唬镰佺敓鎴愪腑锛灞炴ф枃娉曪细缂栬疟杩囩▼涓镄勮涔夊姪镓</锛夈
缂栬疟浼桦寲涓昏侀泦涓鍦ㄨ娉曞垎鏋愪箣钖庯纴鐩镙囦唬镰佺敓鎴愪箣鍓嶏纴杩芥眰镄勬槸楂樻晥銆佺瓑浠蜂笖缁忔祹镄勪唬镰佺敓鎴愩傚悎鐞嗙殑绗﹀彿琛ㄧ$悊鍜屼紭鍖栫瓥鐣ュ圭紪璇戞晥鐜囱呖鍏抽吨瑕侊纸浼桦寲绛栫暐锛氲拷姹傛晥鐜囦笌缁忔祹镐х殑骞宠</锛夈
6. 编译原理问题:求解
E是文法开头。ε代表终结符号(推理中代表终点或结果,程序语言中代表常量等)。E T 这些大写字母一般代表非终结符号(这些代表中间过程,非结果。程序中代表函数等等)。开始是E。因为有个G(E)。E就是文法开始符号。推导就有E开始,它也是一个非终结符(代表函数、或者一个推导过程,类似于程序中的main(c++)、winmain(vc++)、dllmain(dll)等主函数)。
1算术表达式文法:这个文法是一个递归文法。计算机进行逻辑推导时会走很多弯路(类似于遍历一颗树的过程)。为了不让计算机走弯路(提高效率的目的),可以变换为第二种文法。这种文法消除了递归(消除了歧义,类似于后缀表达式),使计算机可以一条直线走到底儿推导出结果。
我也很久没看编译原理了。 呵呵
7. 在编译原理中: 文法S——>SS+|SS*|a能产生什么语言,并验证! 求高人指导!
为了使问题简化,我们考虑文法S->ss+|a,考虑s->ss*时,只要把+换成*即可。
0层递归是,s->a,文法的语言是{a}。是后缀表达式。
1层以内递归时,文法语言是{a,aa+}。是后缀表达式。
2层以内递归时,文法语言是{a,aa+}.{a,aa+}.{+}。其中.表示连接,是后缀表达式。
依此类推,多少层的递归都是后缀表达式。
把表达式的+换成*后依然为后缀表达式。
下面证明文法产生的语言是所有的以a为变量,以+和*为运算符的后缀表达式。
因为每个表达式都对应一个常规的表达式(如1*2+3就是常规表达式),下面只需证明语言能产生的后缀表达式对应所有的常规表达式。当常规表达式只有一个运算符,对应aa+或aa*。当常规表达式有两个运算符,可写成(表达式1).{+|*}.(表达式2),因为表达式1和2都只含一个运算符,所以可以用语言表示,上述常规表达式可用后缀表达式(表达式1).(表达式2).{+l*}表示。所以不管常规表达式有多少个运算符,都可以由语言的后缀表达式对应。