• 编译器一日一练(DIY系列之语法树打印)


    【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

            代码地址:https://github.com/feixiaoxing/DIYCompiler/blob/master/day08/Parse1.jj 

            之前我们谈到了词法分析、语法分析、语义分析,但是没有讨论,如果自己写编译器的话,应该如何来调试。其实,对于编译器来说,它也是有自己的调试方法。

            一般来说,语法分析结束了,这个时候一个完整的语法树就被构建出来了。所以,这个时候可以通过打印语法树的方法,来判断构建的语法树是否正确。等到语义分析确认没有问题,转成中间代码的时候,这个时候又可以通过打印中间代码的方法,判断生成的中间代码是否正确。中间代码生成后,一般会进行一次代码优化,剔除冗余代码和垃圾代码,这个时候又会生成一遍优化后的中间代码,所以还是可以继续通过打印,查看优化后的中间代码是否正确。等这些都完成后,就可以将优化后的中间代码生成汇编,所以可以直接查看汇编代码是否正确。

            所以,在编写编译器的过程当中,有四处地方可以打印,这分别是语法树、中间代码、优化后的中间代码、汇编。当然,如果没有优化的话,直接就变成了三处,即语法树、中间代码和汇编。

            今天,我们就来看看语法树如何打印。这些内容基本都是二叉树数据结构的部分。首先,需要计算节点的深度,其实主要还是为了计算根节点的深度,

    1. public int calculate_depth() { // node depth should be calculated before print it, so just calculate root node
    2. int left_depth = 0;
    3. int right_depth = 0;
    4. int final_depth = 0;
    5. if(get_left_node() == null && get_right_node() == null) {
    6. set_depth(1);
    7. return 1;
    8. }
    9. if(null != get_left_node()) {
    10. left_depth = get_left_node().calculate_depth();
    11. }
    12. if(null != get_right_node()){
    13. right_depth = get_right_node().calculate_depth();
    14. }
    15. final_depth = (left_depth > right_depth) ? (left_depth+1) :(right_depth +1);
    16. set_depth(final_depth);
    17. return final_depth;
    18. }

            再根据根节点的深度,按照根、左、右的方法依次打印,

    1. public void print_node(int start_depth, int start_point) // add print node function
    2. {
    3. int i = 0;
    4. for(i =0; i < (start_point - start_depth*5); i++){
    5. System.out.print(" ");
    6. }
    7. if(get_node_type() != "value_node")
    8. {
    9. System.out.println("/");
    10. }
    11. else
    12. {
    13. System.out.println(((value_node)this).get_value());
    14. }
    15. if(get_left_node() != null) get_left_node().print_node(start_depth -1, start_point);
    16. if(get_right_node() != null) get_right_node().print_node(start_depth -1, start_point);
    17. }

            这部分代码有点拗口。start_depth代表了节点深度信息,而start_point代表单行最大缩进的信息。所以第一个for循环当中,深度越深,前面的空格就越少。接着,就是打印除号或者数据。最后继续递归打印左子树和右子树。

    1. public static void main(String[] args) {
    2. for (String arg : args) {
    3. try {
    4. div_node div = evaluate(arg);
    5. System.out.println();
    6. div.calculate_depth();
    7. div.print_node(div.get_depth(), div.get_depth()*5);
    8. } catch (ParseException ex) {
    9. System.err.println(ex.getMessage());
    10. }
    11. }
    12. }

            调用方法其实比较简单,首先利用evalute函数生成语法树。其次调用calculate_depth计算根节点的深度。最后就是调用print_node打印节点,注意深度信息和start_point的调用方法。

            下面可以看一下最后的效果,

    1. C:\Users\feixiaoxing\Desktop\DIYCompiler\day08>java Parse 2/1
    2. /
    3. 2
    4. 1
    5. C:\Users\feixiaoxing\Desktop\DIYCompiler\day08>java Parse 3/1/2
    6. /
    7. /
    8. 3
    9. 1
    10. 2
    11. C:\Users\feixiaoxing\Desktop\DIYCompiler\day08>java Parse 3/2/1/1/2/3/4/5/6
    12. /
    13. /
    14. /
    15. /
    16. /
    17. /
    18. /
    19. /
    20. 3
    21. 2
    22. 1
    23. 1
    24. 2
    25. 3
    26. 4
    27. 5
    28. 6

            上面的打印还是比较直观的。大家可以根据自己的需要灵活修改和调试。最后,给出完整的jj文件,供大家调试和参考,

    1. options {
    2. STATIC = false;
    3. }
    4. PARSER_BEGIN(Parse)
    5. import java.io.*;
    6. class node
    7. {
    8. public node left;
    9. public node right;
    10. public String node_type;
    11. public int depth;
    12. node() {this.left = this.right = null;}
    13. public void set_left_node(node left) { this.left = left;}
    14. public node get_left_node() { return this.left;}
    15. public void set_right_node(node right) { this.right = right;}
    16. public node get_right_node() {return this.right;}
    17. public void set_node_type(String node_type) {this.node_type = node_type;}
    18. public String get_node_type() {return this.node_type;}
    19. public void set_depth(int depth) {this.depth = depth;}
    20. public int get_depth() {return this.depth;}
    21. public int calculate_depth() { // node depth should be calculated before print it, so just calculate root node
    22. int left_depth = 0;
    23. int right_depth = 0;
    24. int final_depth = 0;
    25. if(get_left_node() == null && get_right_node() == null) {
    26. set_depth(1);
    27. return 1;
    28. }
    29. if(null != get_left_node()) {
    30. left_depth = get_left_node().calculate_depth();
    31. }
    32. if(null != get_right_node()){
    33. right_depth = get_right_node().calculate_depth();
    34. }
    35. final_depth = (left_depth > right_depth) ? (left_depth+1) :(right_depth +1);
    36. set_depth(final_depth);
    37. return final_depth;
    38. }
    39. public void print_node(int start_depth, int start_point) // add print node function
    40. {
    41. int i = 0;
    42. for(i =0; i < (start_point - start_depth*5); i++){
    43. System.out.print(" ");
    44. }
    45. if(get_node_type() != "value_node")
    46. {
    47. System.out.println("/");
    48. }
    49. else
    50. {
    51. System.out.println(((value_node)this).get_value());
    52. }
    53. if(get_left_node() != null) get_left_node().print_node(start_depth -1, start_point);
    54. if(get_right_node() != null) get_right_node().print_node(start_depth -1, start_point);
    55. }
    56. }
    57. class value_node extends node
    58. {
    59. public int value;
    60. value_node() { set_node_type("value_node");}
    61. public int get_value() { return this.value;}
    62. public void set_value(int value) {this.value = value;}
    63. }
    64. class div_node extends node
    65. {
    66. div_node() { set_node_type("/");}
    67. public int get_value() {
    68. int left = 0, right = 0;
    69. // get left node
    70. if(get_left_node().get_node_type() == "/")
    71. {
    72. left = ((div_node)get_left_node()).get_value();
    73. }
    74. else
    75. {
    76. left = ((value_node)get_left_node()).get_value();
    77. }
    78. // get right node
    79. if(get_right_node() == null)
    80. {
    81. return left;
    82. }
    83. else
    84. {
    85. right = ((value_node)get_right_node()).get_value();
    86. return left/right;
    87. }
    88. }
    89. // add semantic check
    90. public int check_value() {
    91. if(get_right_node() != null)
    92. {
    93. if( 0 == ((value_node)get_right_node()).get_value())
    94. {
    95. System.out.println("Div by Zero");
    96. return -1;
    97. }
    98. }
    99. if(get_left_node().get_node_type() == "/")
    100. {
    101. return ((div_node)(get_left_node())).check_value();
    102. }
    103. return 1;
    104. }
    105. }
    106. public class Parse {
    107. public static void main(String[] args) {
    108. for (String arg : args) {
    109. try {
    110. div_node div = evaluate(arg);
    111. System.out.println();
    112. div.calculate_depth();
    113. div.print_node(div.get_depth(), div.get_depth()*5);
    114. } catch (ParseException ex) {
    115. System.err.println(ex.getMessage());
    116. }
    117. }
    118. }
    119. public static div_node evaluate(String src) throws ParseException {
    120. Reader reader = new StringReader(src);
    121. return new Parse(reader).expr();
    122. }
    123. }
    124. PARSER_END(Parse)
    125. SKIP: { <[" ", "\t", "\r", "\n"]> }
    126. TOKEN: {
    127. <INTEGER: (["0"-"9"])+>
    128. }
    129. div_node expr() throws NumberFormatException :
    130. {
    131. Token a ;
    132. Token b ;
    133. div_node div;
    134. }
    135. {
    136. a = <INTEGER>
    137. {
    138. value_node node_a = new value_node();
    139. node_a.set_value(Integer.parseInt( a.image ));
    140. div = new div_node();
    141. div.set_left_node(node_a);
    142. }
    143. (
    144. "/" b = <INTEGER>
    145. {
    146. value_node node_b = new value_node();
    147. node_b.set_value(Integer.parseInt( b.image ));
    148. // important code about node adding
    149. if(div.get_right_node() == null)
    150. {
    151. div.set_right_node(node_b);
    152. }
    153. else
    154. {
    155. div_node prev = div;
    156. div = new div_node();
    157. div.set_left_node(prev);
    158. div.set_right_node(node_b);
    159. }
    160. }
    161. )*
    162. <EOF>
    163. { return div ; }
    164. }

  • 相关阅读:
    国产5G手机20天销量不及苹果一天,被iPhone15按在地上摩擦
    巨头游戏:Instagram 将运行 Polygon 支持的 NFT 市场
    R语言使用append函数在向量数据中的指定位置插入新的元素(一个或者多个数值)
    电脑回收站为什么自动清空?win10回收站自动清理的东西怎么找回
    Revit一款主要用于进行建筑信息建模的软件
    【mmDetection框架解读】入门篇三、VOC数据集转COCO数据集,在MMDetection中成功运行
    RationalDMIS2022校验测头
    网络安全(黑客)自学
    算法题--华为od机试考试(开源项目热度榜单、API集群负载统计、分月饼)
    【Python】初始Python
  • 原文地址:https://blog.csdn.net/feixiaoxing/article/details/127662656