精华内容
下载资源
问答
  • 传智播客ThinkPHP后端代码生成器.大型电子商城
  • JAVA后端代码生成器(100%能用),自动生成service、mapper,自带增删改查常用方法
  • 一步一步使用freemarker实现代码生成的小项目|后端代码生成篇 前言 使用freemarker来生成代码,先定义代码的模板,使用定义的模板和传入的模型变量来生成相应的代码 ...

    使用freemarker实现代码生成的小项目|后端代码生成篇

    前言

    使用freemarker来生成代码,先定义代码的模板,使用定义的模板和传入的模型变量来生成相应的代码。

    源码地址:https://github.com/lizhongxiang12138/code-main.git

    下一篇是 使用freemarker实现代码生成的小项目|前端代码篇

    先看效果

    效果

    项目结构

    ├─.idea
    │  └─libraries
    ├─.mvn
    │  └─wrapper
    ├─code-generation   代码生成业务模块
    ├─code-generation-template  代码模板模块
    ├─code-generation-ui  代码生成UI模块
    └─common
    
    

    代码模板模块(code-generation-template)

    代码模板模块用来定义代码的模板

    模块结构

    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─lzx
    │  │  │          └─code
    │  │  │              └─codegenerationtemplate
    │  │  │                  │  CodeGenerationTemplateApplication.java
    │  │  │                  │
    │  │  │                  └─template
    │  │  │                          Controller.ftl				api接口类模板
    │  │  │                          Dao.ftl							DAO模板
    │  │  │                          EntityTemple.ftl			实体类模板
    │  │  │                          Service.ftl           		业务接口类模板
    │  │  │                          ServiceImpl.ftl     		业务实现类模板
    │  │  │                          TemplateUtils.java    	模板工具类
    │  │  │
    │  │  └─resources
    │  │          application.yml
    │  │
    │  └─test
    │      └─java
    │          └─com
    │              └─lzx
    │                  └─code
    │                      └─codegenerationtemplate
    │                          │  CodeGenerationTemplateApplicationTests.java
    │                          │
    │                          └─template
    │                                  TemplateUtilsTest.java
    
    

    代码生成业务模块(code-generation)

    代码生成业务模块提供生成代码的功能

    模块结构

    │  .gitignore
    │  code-generation.iml
    │  HELP.md
    │  mvnw
    │  mvnw.cmd
    │  pom.xml
    │
    ├─.mvn
    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─lzx
    │  │  │          └─code
    │  │  │              └─codegeneration
    │  │  │                  │  CodeGenerationApplication.java
    │  │  │                  │
    │  │  │                  ├─config
    │  │  │                  │      CorsFilter.java  									过滤器,对跨域的支持
    │  │  │                  │
    │  │  │                  ├─controller
    │  │  │                  │      CodeGenerationController.java			代码生成的api接口
    │  │  │                  │
    │  │  │                  ├─entity
    │  │  │                  │      ClassBean.java									代码生成的模型定义1
    │  │  │                  │      ClassBeanField.java							代码生成的模型定义2
    │  │  │                  │
    │  │  │                  ├─service
    │  │  │                  │  │  CodeGenerationService.java				代码生成功能业务接口
    │  │  │                  │  │
    │  │  │                  │  └─impl
    │  │  │                  │          CodeGenerationServiceImpl.java	代码生成功能业务实现
    │  │  │                  │
    │  │  │                  └─vo
    │  │  │                          DataTypeVO.java
    │  │  │
    │  │  └─resources
    │  │          application.yml
    
    

    主要业务代码

    package com.lzx.code.codegeneration.service.impl;
    
    import com.lzx.code.codegeneration.entity.ClassBean;
    import com.lzx.code.codegeneration.service.CodeGenerationService;
    import com.lzx.code.codegenerationtemplate.template.TemplateUtils;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import java.io.IOException;
    import java.io.StringWriter;
    
    /**
     * 描述: 代码服务生成业务实现
     *
     * @Auther: lzx
     * @Date: 2019/7/18 13:10
     */
    @Service
    @Slf4j
    public class CodeGenerationServiceImpl implements CodeGenerationService {
    
        private Configuration cfg;
    
        public CodeGenerationServiceImpl() throws IOException {
            cfg = TemplateUtils.getConfiguration();
        }
    
        /**
         * 生成实体类
         * @param classBean
         * @return
         */
        @Override
        public String generationEntiry(ClassBean classBean) throws IOException, TemplateException {
            Template template = cfg.getTemplate("EntityTemple.ftl", "UTF-8");
    
            //输出
            StringWriter out = new StringWriter();
            template.process(classBean,out);
            return out.toString();
        }
    
        @Override
        public String generationDao(ClassBean classBean) throws IOException, TemplateException {
            Template template = cfg.getTemplate("Dao.ftl", "UTF-8");
    
            //输出
            StringWriter out = new StringWriter();
            template.process(classBean,out);
            return out.toString();
        }
    
        /**
         * 生成业务接口代码
         * @param classBean
         * @return
         */
        @Override
        public String generationService(ClassBean classBean) throws IOException, TemplateException {
            Template template = cfg.getTemplate("Service.ftl", "UTF-8");
    
            //输出
            StringWriter out = new StringWriter();
            template.process(classBean,out);
            return out.toString();
        }
    
        /**
         * 生成业务的实现
         * @param classBean
         * @return
         */
        @Override
        public String generationServiceImpl(ClassBean classBean) throws IOException, TemplateException {
            Template template = cfg.getTemplate("ServiceImpl.ftl", "UTF-8");
    
            //输出
            StringWriter out = new StringWriter();
            template.process(classBean,out);
            return out.toString();
        }
    
        @Override
        public String generationController(ClassBean classBean) throws IOException, TemplateException {
            Template template = cfg.getTemplate("Controller.ftl", "UTF-8");
    
            //输出
            StringWriter out = new StringWriter();
            template.process(classBean,out);
            return out.toString();
        }
    }
    
    

    UI模块(code-generation-ui)

    UI模块提供代码生成功能的界面

    模块结构

    ├─src
    │  ├─main
    │  │  ├─java
    │  │  │  └─com
    │  │  │      └─lzx
    │  │  │          └─code
    │  │  │              └─codegenerationui
    │  │  │                      CodeGenerationUiApplication.java
    │  │  │
    │  │  └─resources
    │  │      │  application.yml
    │  │      │
    │  │      ├─static
    │  │      │      generaCode-ui.html			操作管理界面
    │  │      │
    │  │      └─templates
    

    界面代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 引入样式 -->
        <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
        <style>
    
        </style>
    </head>
    <body>
    <div id="app">
        <!-- 类基本信息 -->
        <el-form :inline="true" :model="form" class="demo-form-inline" label-width="150px">
            <el-form-item label="类描述">
                <el-input v-model="form.classDescribe" placeholder="类描述"></el-input>
            </el-form-item>
            <el-form-item label="类名">
                <el-input v-model="form.className" placeholder="类名"></el-input>
            </el-form-item>
            <el-form-item label="bean名称">
                <el-input v-model="form.springBeanName" placeholder="bean名称"></el-input>
            </el-form-item>
            <el-form-item label="表名称">
                <el-input v-model="form.tableName" placeholder="表名称"></el-input>
            </el-form-item>
            <el-form-item label="主键在表中的字段名">
                <el-input v-model="form.idColumnName" placeholder="主键在表中的字段名"></el-input>
            </el-form-item>
            <el-form-item label="主键的描述">
                <el-input v-model="form.idDescribe" placeholder="主键的描述"></el-input>
            </el-form-item>
            <el-form-item label="主键字段名">
                <el-input v-model="form.idName" placeholder="主键字段名"></el-input>
            </el-form-item>
            <el-form-item label="主键类型">
                <el-input v-model="form.idType" placeholder="主键类型"></el-input>
                <template slot-scope="{row,$index}">
                    <el-select v-model="form.idType" filterable clearable placeholder="请选择">
                        <el-option
                                v-for="item in type"
                                :key="item.name"
                                :label="item.name"
                                :value="item.name">
                        </el-option>
                    </el-select>
                </template>
            </el-form-item>
        </el-form>
        <!-- 类基本信息 edn -->
    
        <!-- 操作按钮 -->
        <el-form :inline="true" :model="form" class="demo-form-inline" label-width="0px">
            <el-form-item>
                <el-button type="primary" @click="onAdd">添加字段</el-button>
            </el-form-item>
    
            <el-form-item>
                <el-button type="primary" @click="getCode('ENTITY')">生成实体</el-button>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="getCode('DAO')">生成DAO</el-button>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="getCode('SERVICE')">生成Service</el-button>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="getCode('SERVICE_IMPL')">生成Service实现</el-button>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="getCode('CONTROLLER')">生成Controller</el-button>
            </el-form-item>
        </el-form>
        <!-- 操作按钮 end -->
    
    
    
        <!-- 类字段维护 -->
        <el-table
                :data="form.fields"
                style="width: 100%">
            <el-table-column
                    prop="describe"
                    label="字段描述"
                    width="180">
                <template slot-scope="{row,$index}">
                    <el-input v-model='row.describe'></el-input>
                </template>
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="字段名称">
                <template slot-scope="{row,$index}">
                    <el-input v-model='row.name'></el-input>
                </template>
            </el-table-column>
            <el-table-column
                    prop="type"
                    label="字段类型">
                <template slot-scope="{row,$index}">
                    <el-select v-model="row.type" filterable clearable placeholder="请选择">
                        <el-option
                                v-for="item in type"
                                :key="item.name"
                                :label="item.name"
                                :value="item.name">
                        </el-option>
                    </el-select>
                </template>
            </el-table-column>
            <el-table-column
                    prop="columnName"
                    label="表的字段名称"
                    width="180">
                <template slot-scope="{row,$index}">
                    <el-input v-model='row.columnName'></el-input>
                </template>
            </el-table-column>
            <el-table-column
                    fixed="right"
                    label="操作"
                    width="150">
                <template slot-scope="scope">
                    <el-button @click="deleteField(scope.$index)" type="danger" size="small">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 类字段维护 end-->
    
        <!-- 代码生成结果 -->
        <el-dialog title="生成的代码" :visible.sync="dialogTableVisible">
            <!-- 生成的代码 -->
            <el-form ref="form" :model="form" label-width="0px">
                <el-form-item>
                    <el-input type="textarea" :rows="30" v-model="returnData.data"></el-input>
                </el-form-item>
            </el-form>
        </el-dialog>
        <!-- 代码生成结果 end -->
    
    </div>
    </body>
    <!-- 引入组件库 -->
    <!-- import Vue before Element -->
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script>
        new Vue({
            el:"#app",
            data:{
                dialogTableVisible: false,
                type:[],
                form:{
                    "classDescribe": "学生表",
                    "className": "Student",
                    "idColumnName": "ID",
                    "idDescribe": "主键",
                    "idName": "id",
                    "idType": "String",
                    "springBeanName": "student",
                    "tableName": "STUDENT",
                    "fields": []
                },
                returnData:{
    
                }
            },
            mounted(){
                this.init();
            },
            methods:{
                /**
                 * 初始化数据
                 */
                init(){
                    //初始化数据
                    var _this = this;
                    axios.get(
                        "http://localhost:30007/codeGeneration/getAllDataType"
                    ).then(function(rsp){
                        debugger;
                        _this.type = rsp.data.data;
                        console.log(this.application);
                    }).catch(function(error){
                        debugger;
                    });
                },
                /**
                 * 添加字段
                 */
                onAdd(){
                    //添加字段
                    var _this = this;
                    _this.form.fields.push({});
                },
                /**
                 * 删除字段
                 */
                deleteField(index){
                    debugger;
                    var _this = this;
                    _this.form.fields.splice(index,1);
                },
                /**
                 *  获取生成的代码
                 * @param code 需要生成的文件代码
                 */
                getCode(code) {
                    var _this = this;
                    axios.post(
                        "http://localhost:30007/codeGeneration/generationCode/"+code,_this.form
                    ).then(function(rsp){
                        debugger;
                        if(rsp){
                            _this.returnData=rsp.data;
                            _this.dialogTableVisible = true;
                        }
                    }).catch(function (error) {
    
                    });
                }
            }
        });
    </script>
    </html>
    
    展开全文
  • 自制编译器:后端代码生成

    千次阅读 2014-10-06 10:42:38
    所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,因此先设计一个虚拟机,并翻译成这个虚拟机的机器码。 对于虚拟机以及其指令格式可...

    所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,因此先设计一个虚拟机,并翻译成这个虚拟机的机器码。

    对于虚拟机以及其指令格式可参考这篇文章http://blog.csdn.net/roger__wong/article/details/8947720,如何去尝试实现这个虚拟机是在我的另外一个系列的博客里进行论述。


    本篇文章从以下是那个方面来论述:后端架构与关键数据结构、节点翻译方式。

    1、后端架构和关键数据结构

    后端接受前端的语法树作为输入,对于其每一个节点根据节点类型的不同产生不同的代码。但在实现过程中为了简单方便,我并没有把后端抽象出一个单独的模块,而是在语法树每一个节点的基础上增加了一个genCode方法,通过调用这个方法来生成该节点及其所有孩子节点(通过递归)的代码。

    其次编译器后端直接生成Class文件(文件结构也在上文提到的博客中有说明),程序中后端首先构造一个ClassOfClass的实体,然后再调用此类的方法生成Class文件:

    [java] view plaincopy
    1. public class ClassOfClass {  
    2.     public static int isPublic=1;  
    3.     public static int isStatic=2;  
    4.     public ArrayList<field> fields;  
    5.     public ArrayList<function> functions;  
    6.     public ArrayList<String> constPool;  
    7.     public String name;  
    8.     public ClassOfClass()  
    9.     {  
    10.         constPool=new ArrayList<String>();  
    11.         fields=new ArrayList<field>();  
    12.         functions=new ArrayList<function>();  
    13.           
    14.     }  
    15.     public void WriteClassFile(String path)  
    16.     {  
    17.         try {  
    18.             PrintWriter pw=new PrintWriter(new FileOutputStream(path));  
    19.             pw.println(name);  
    20.             pw.println(fields.size());  
    21.             for(field f:fields)  
    22.             {  
    23.                 pw.println(f.toString());  
    24.             }  
    25.             pw.println(functions.size());  
    26.             for(function f:functions)  
    27.             {  
    28.                 pw.println(f.toString());  
    29.             }  
    30.         } catch (FileNotFoundException e) {  
    31.             // TODO Auto-generated catch block  
    32.             e.printStackTrace();  
    33.         }  
    34.     }  
    35. }  
    其中field结构:

    [java] view plaincopy
    1. public class field {  
    2.     public int head;  
    3.     public String type;  
    4.     public String fieldname;  
    5.     @Override  
    6.     public String toString()  
    7.     {  
    8.         StringBuffer sb=new StringBuffer();  
    9.         sb.append(head);  
    10.         sb.append(" ");  
    11.         sb.append(type);  
    12.         sb.append(" ");  
    13.         sb.append(fieldname);  
    14.         return sb.toString();  
    15.     }  
    16. }  

    其中Function结构

    [java] view plaincopy
    1. public class function {  
    2.     public int head;  
    3.     public String rettype;  
    4.     public int argnum;  
    5.     public ArrayList<String> args;  
    6.     public ArrayList<Code> codes;  
    7.     public function()  
    8.     {  
    9.         args=new ArrayList<String>();  
    10.         codes=new ArrayList<Code>();  
    11.     }  
    12.      @Override  
    13.         public String toString()  
    14.         {  
    15.             StringBuffer sb=new StringBuffer();  
    16.             sb.append(head);  
    17.             sb.append(" ");  
    18.             sb.append(rettype);  
    19.             sb.append(" ");  
    20.             sb.append(args.size());  
    21.             sb.append(" ");  
    22.             for(String s:args)  
    23.             {  
    24.                 sb.append(s+" ");  
    25.             }  
    26.             sb.append("\r\n");  
    27.             sb.append("{");  
    28.             for(int i=0;i<=codes.size()-1;i++)  
    29.             {  
    30.                 sb.append(i+":"+codes.get(i).toString()+"\r\n");  
    31.             }  
    32.             sb.append("\r\n");  
    33.             sb.append("}");  
    34.             return sb.toString();  
    35.         }  
    36. }  
    其中Code结构

    [java] view plaincopy
    1. public class Code {  
    2.     public int Opcode;//操作码  
    3.     public ArrayList<String> Operands;  
    4.     public Code(int op)  
    5.     {  
    6.         Operands=new ArrayList<String>();  
    7.         Opcode=op;  
    8.     }  
    9.      @Override  
    10.         public String toString()  
    11.         {  
    12.             StringBuffer sb=new StringBuffer();  
    13.             sb.append(Opcode);  
    14.             sb.append(" ");  
    15.             for(String s:Operands)  
    16.             {  
    17.                 sb.append(s+" ");  
    18.             }  
    19.             return sb.toString();  
    20.         }  
    [java] view plaincopy
    1. }  

    完成一个Class的后端代码的生成工作,只需要调用语法树根节点(classdef)的genCode即可完成,因为根节点会不断的递归调用其子树的genCode方法,因此在递归调用的时候需要某些机制进行各方法之间信息的传递,这里建立新类BackendClassManager来进行信息的传递,消除耦合。

    [java] view plaincopy
    1. public class BackendClassManager {  
    2.     public static ClassOfClass cc=new ClassOfClass();//正在生成的class对象  
    3.     public static function tFunc;//正在生成代码的函数  
    4.     public static memberfuncdeclare mfc;//正在生成代码的语法树中的memberfuncdeclare节点  
    5.     public static constructor ct;//正在生成代码的构造函数节点  
    6.     public static HashMap<String,Integer> nameSlot=new HashMap<String,Integer>();//局部变量和局部变量表槽的对应hash  
    7.     public static expr expr1,expr2;//正在翻译的 expr  
    8.     public static Stack<ArrayList<Code>> loopcontinue=new Stack<ArrayList<Code>>();//在循环语句中出现的continue语句,用于回填地址  
    9.     public static Stack<ArrayList<Code>> loopbreak=new Stack<ArrayList<Code>>();//在循环语句中出现的break语句,用于回填地址  
    10.     public static void WriteToClassFile()  
    11.     {  
    12.         String path="E:/test.class";  
    13.         cc.WriteClassFile(path);  
    14.     }  
    15.     public static void genCode(classdef cd)  
    16.     {  
    17.         cd.genCode();  
    18.     }  
    19. }  
    cc代表目前正在编译的class。

    tFunc代表正在生成代码的函数,也就是所有genCode的方法都要把生成的代码填充到tFunc的codes域中。

    memberfuncdeclare代表语法树中正在生成后端代码的memberfuncdeclare节点,该节点和其子树包含此函数的所有代码。

    nameSlot对应源码中出现的局部变量与目标代码中的局部变量表槽的一个关系,因为目标代码将不再会出现局部变量名这个概念,所以需要一个hash在编译时进行对应。

    expr1和expr2对应正在翻译的expr,在某些运算符需要进行类型转换时需要用到正在翻译的表达式的信息。

    loopcontinue和loopbreak用于循环语句的地址回填,因为一个循环在翻译的过程中,其break需要跳转到的地址是还未确定的,需要整个循环翻译完之后对目标地址进行回填。continue虽然可以确定目标地址,但是在continue对应的语句stmt节点无法知道循环的开始地址,需要通过某些机制让stmt节点知道此循环的开始地址,因此也把continue语句进行回填处理。

    除此之外还是用了一个类CodeGenHelper来封装一些常用的代码序列,比如i2d,jmp等,目的是为了简化之后的目标代码的生成。


    2、节点代码生成

    按照从顶至低的方式依次分析。

    (1)classdef节点

    [java] view plaincopy
    1. public void genCode() {  
    2.         BackendClassManager.cc.name=cn.toString();  
    3.         cb.genCode();  
    4.     }  
    代码很简单,首先把正在编译的类名设置成classdef中出现的类名,然后调用classbody的genCode方法。


    (2)classbody

    [java] view plaincopy
    1. public void genCode() {  
    2.         if(cb!=null)  
    3.         {  
    4.             cb.genCode();  
    5.         }  
    6.           
    7.     }  
    依然很简单,如果classmembers不为空,则调用classmembers的genCode方法。

    值得注意的是,这些方法本身并没有生成目标代码乃是因为一个类的定义本身并不包含任何逻辑,而代码本身是对逻辑的阐述,所以在类声明、函数声明、成员变量声明等没有生成任何有意义的代码也就不值得奇怪了。


    (3)classmembers

    [java] view plaincopy
    1. public void genCode() {  
    2.         if(type==0)  
    3.         {  
    4.             ((membervardeclare)declare).genCode();  
    5.         }  
    6.         else if(type==1)  
    7.         {  
    8.             ((memberfuncdeclare)declare).genCode();  
    9.         }  
    10.         else if(type==2)  
    11.         {  
    12.             ct.genCode();  
    13.         }  
    14.         if(cm!=null)  
    15.         {  
    16.             cm.genCode();  
    17.         }  
    18.     }  
    根据此classmembers的类型,对membervardeclare、memberfuncdeclare、constructor调用genCode方法,最后对下一个classmemebers调用genCode方法,这和本系列第一篇博客中的递推式是对应的。


    (4)membervardeclare

    [java] view plaincopy
    1. public void genCode() {  
    2.         // TODO Auto-generated method stub  
    3.           
    4.         field fd=new field();  
    5.           
    6.         ArrayList<field> fs=BackendClassManager.cc.fields;  
    7.         if(af.toString().equals("public"))  
    8.         {  
    9.         fd.head+=ClassOfClass.isPublic;  
    10.         }  
    11.         if(isstatic==true)  
    12.         {  
    13.             fd.head+=ClassOfClass.isStatic;  
    14.         }  
    15.         fd.type=tp.toString();  
    16.         fd.fieldname=ID.toString();  
    17.         fs.add(fd);  
    18.     }  
    依然没有任何代码生成,只是将成员变量的信息放到ClassOfClass对象的fields域中。

    (5)memberfundeclare

    [java] view plaincopy
    1. public void genCode() {  
    2.         function func=new function();  
    3.         BackendClassManager.tFunc=func;  
    4.         BackendClassManager.cc.functions.add(func);  
    5.         BackendClassManager.ct=null;  
    6.         BackendClassManager.mfc=this;  
    7.         BackendClassManager.nameSlot=new HashMap<String,Integer>();  
    8.         if(af.toString().equals("public"))  
    9.         {  
    10.         func.head+=ClassOfClass.isPublic;  
    11.         }  
    12.         func.rettype=tp.toString();  
    13.         if(da!=null)  
    14.         {  
    15.         ArrayList<type> al=da.gettypelist();  
    16.         func.argnum=al.size();  
    17.           
    18.         for(type tp:al)  
    19.         {  
    20.             func.args.add(tp.toString());  
    21.         }  
    22.         ArrayList<id> tal=da.getidlist();  
    23.         BackendClassManager.nameSlot.put("this"0);  
    24.         for(int i=0;i<=tal.size()-1;i++)  
    25.         {  
    26.             BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);  
    27.         }  
    28.         }  
    29.         else  
    30.         {  
    31.             func.argnum=0;  
    32.         }  
    33.         fb.genCode();  
    34.       
    35.     }  
    成员函数的稍微有点复杂。

    首先建立一个新的function对象,并把该对象设置为BackendClassManager.tFun,说明之后所有genCode都为这个函数生成的代码,并把这个函数加到classofClass对象的functions域中;判断该函数的返回值类型、是否是public、是否是静态,并把相关信息记录到function对象中重置nameslot,将函数所有参数压入nameslot表中,并注意this也当做参数放入表中,然后调用functionbody的genCode,为该函数生成代码。

    (6)constructor

    [java] view plaincopy
    1. public void genCode() {  
    2.           
    3.         function func=new function();  
    4.         BackendClassManager.tFunc=func;  
    5.         BackendClassManager.ct=this;  
    6.         BackendClassManager.nameSlot=new HashMap<String,Integer>();  
    7.         if(af.toString().equals("public"))  
    8.         {  
    9.         func.head+=ClassOfClass.isPublic;  
    10.         }  
    11.         func.rettype="NULL";  
    12.         if(da!=null)  
    13.         {  
    14.         ArrayList<type> al=da.gettypelist();  
    15.         func.argnum=al.size();  
    16.         func.argnum=al.size();  
    17.         for(type tp:al)  
    18.         {  
    19.             func.args.add(tp.toString());  
    20.         }  
    21.         ArrayList<id> tal=da.getidlist();  
    22.         BackendClassManager.nameSlot.put("this"0);  
    23.         for(int i=0;i<=tal.size()-1;i++)  
    24.         {  
    25.             BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);  
    26.         }  
    27.         }  
    28.         else  
    29.         {  
    30.             func.argnum=0;  
    31.         }  
    32.         ss.genCode();  
    33.           
    34.     }  

    (7)funcbody

    [java] view plaincopy
    1. public void genCode() {  
    2.     // TODO Auto-generated method stub  
    3.     ss.genCode();  
    4.     returnexpr.genCode();  
    5.     ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    6.     Code code=new Code(0x19);  
    7.     al.add(code);  
    8. }  

    首先stmts生成代码,然后为返回表达式生成代码,最后在codes中加入Code(0x19),也就是返回指令。

    值得注意的是,这里做了一个约定,在expr的genCode中,总是把该expr的结果放在expr代码执行后的栈顶,因此函数返回时实际上返回的是栈顶元素的值。

    (8)stmts

    stmts逻辑上代表一个语句块或一组语句块,对应的生成式和在节点中使用的type如下:

    stmts --> NUL|            type-->0


                      stmt stmts|        type-->1


                      if(expr) { stmts} stmts|   type-->2


                      if(expr) {stmts} else {stmts}  stmts|       type-->3


                      while(expr) { stmts}     stmts|    type-->4

    接下来给出生成后端代码的代码:

    [html] view plaincopy
    1. public void genCode() {  
    2.         if(type==1)  
    3.         {  
    4.             st.genCode();  
    5.             stmts1.genCode();  
    6.         }  
    7.         else if(type==2)  
    8.         {  
    9.             condition.genCode();  
    10.             Code code=new Code(0x18);  
    11.             BackendClassManager.tFunc.codes.add(code);  
    12.             stmts1.genCode();  
    13.             code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));  
    14.             stmts3.genCode();  
    15.         }  
    16.         else if(type==3)  
    17.         {  
    18.             condition.genCode();  
    19.             Code code=new Code(0x18);  
    20.             code.Opcode=0x18;  
    21.             BackendClassManager.tFunc.codes.add(code);  
    22.             stmts1.genCode();  
    23.             Code code1=new Code(0x01);  
    24.             code1.Operands.add(String.valueOf(0));  
    25.             BackendClassManager.tFunc.codes.add(code1);  
    26.             Code code2=new Code(0x18);  
    27.             BackendClassManager.tFunc.codes.add(code2);  
    28.             code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));  
    29.             stmts2.genCode();  
    30.             code2.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));  
    31.             stmts3.genCode();  
    32.         }  
    33.         else if(type==4)  
    34.         {  
    35.             ArrayList<Code> albreak=new ArrayList<Code>();  
    36.             ArrayList<Code> alcontinue=new ArrayList<Code>();  
    37.             BackendClassManager.loopbreak.add(albreak);  
    38.             BackendClassManager.loopcontinue.add(alcontinue);//循环入口,首先判断expr  
    39.             int pos=BackendClassManager.tFunc.codes.size();  
    40.             condition.genCode();  
    41.             //跳转指令  
    42.             Code code=new Code(0x18);  
    43.             BackendClassManager.tFunc.codes.add(code);  
    44.             //循环体  
    45.             stmts1.genCode();  
    46.               
    47.         //  code.Operands.add(String.valueOf(end));//表达式回填  
    48.               
    49.             Code code1=new Code(0x01);  
    50.             code1.Operands.add(String.valueOf(0));  
    51.             BackendClassManager.tFunc.codes.add(code1);//压入0  
    52.               
    53.             Code code2=new Code(0x18);  
    54.             code2.Operands.add(String.valueOf(pos));  
    55.             BackendClassManager.tFunc.codes.add(code2);//跳转到循环入口  
    56.               
    57.             int end=BackendClassManager.tFunc.codes.size();  
    58.             code.Operands.add(String.valueOf(end));//表达式回填  
    59.               
    60.             for(Code c:albreak)  
    61.             {  
    62.                 c.Operands.add(String.valueOf(end));  
    63.             }  
    64.             for(Code c:alcontinue)  
    65.             {  
    66.                c.Operands.add(String.valueOf(pos));  
    67.             }  
    68.             BackendClassManager.loopbreak.pop();  
    69.             BackendClassManager.loopcontinue.pop();  
    70.             stmts3.genCode();  
    71.         }  
    72.           
    73.     }  
    对于type=0,没必要生成任何代码;对于type=1,生成stmt的代码然后递归再生成stmts的代码;对于type=2,首先生成条件expr的代码,在这段代码执行过后,会将结果放在堆栈顶,然后加入code(0x18)进行跳转,但此刻的跳转地址还不能确定,因此先要生成stmts1的代码,之后回填跳转地址,再生成stmts3的代码;对于type=3,和type=2类似,但要在stmts1代码之后加入无条件跳转指令,跳转到else块后面,无条件跳转的方法是首先压入0,再使用0x18也就是ifz进行跳转;对于type=4要稍微复杂些,首先要计算循环开始的地址(包括判断expr),然后生成循环体代码,得到循环结束地址,再回填循环判断的相关代码,除此之外,还要回填此循环体内出现的所有break和continue语句的地址。


    (9)stmt

    首先给出语句stmt的生成式:

     stmt -->   continue;|   type=0
     
                    break;|    type=1


                    var-declare;|   type=2


                   class-init;|    type=3


                   setvalue;|   type=4


                   expr;|       type=5


    接下来给出代码:

    [java] view plaincopy
    1. public void genCode() {  
    2.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    3.         if(type==0)  
    4.         {  
    5.             CodeGenHelper.JMP(al, 0);  
    6.             BackendClassManager.loopcontinue.peek().add(al.get(al.size()-1));  
    7.         }  
    8.         if(type==1)  
    9.         {  
    10.             CodeGenHelper.JMP(al, 0);  
    11.             BackendClassManager.loopbreak.peek().add(al.get(al.size()-1));  
    12.         }  
    13.         if(type==5)  
    14.         {  
    15.             ep.genCode();  
    16.         }  
    17.         if(type==2)  
    18.         {  
    19.             vc.genCode();  
    20.         }  
    21.         if(type==3)  
    22.         {  
    23.             ci.genCode();  
    24.         }  
    25.         if(type==4)  
    26.         {  
    27.             sv.genCode();  
    28.         }  
    29.     }  
    stmt代码比较简单,对于break和continue,只需要加入一个跳转语句,然后把此跳转语句加入到相应的列表里(因为循环是嵌套的,所以使用堆栈来表示这一关系),由外层循环体进行地址回填即可,使用了CodeGenHelper的JMP方法作为辅助函数;对于其它类型则调用其相应节点的genCode方法。

    另外给出CodeGenHelper的JMP方法,封装了压入0和ifz两条指令:

    [java] view plaincopy
    1. public static void JMP(ArrayList<Code> al,int pos)  
    2.     {  
    3.         Code code1=new Code(0x01);  
    4.         code1.Operands.add(String.valueOf(0));  
    5.         BackendClassManager.tFunc.codes.add(code1);//压入0  
    6.           
    7.         Code code2=new Code(0x18);  
    8.         code2.Operands.add(String.valueOf(pos));  
    9.         BackendClassManager.tFunc.codes.add(code2);//跳转  
    10.     }  

    (10)classinit

    递推式:class-init --> ids = new 


                type ( args )| type[expr]
            type:     0            1              

    代码:

    [java] view plaincopy
    1. public void genCode() {  
    2.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    3.         ag.genCode();  
    4.         if(type==0)  
    5.         {  
    6.             Code code=new Code(0x1C);  
    7.             code.Operands.add(tp.toString());  
    8.             al.add(code);  
    9.         }  
    10.         else  
    11.         {  
    12.             Code code=new Code(0x1D);  
    13.             code.Operands.add(tp.toString());  
    14.             al.add(code);  
    15.         }  
    16.         if(is.type==1)  
    17.         {  
    18.             int slot=CodeGenHelper.SearchByName(is.ID.toString());  
    19.             Code code=new Code(0x05);  
    20.             code.Operands.add(String.valueOf(slot));  
    21.             al.add(code);  
    22.         }  
    23.         if(is.type==2)  
    24.         {  
    25.             if(is.getLastIDS().type!=3)  
    26.             {  
    27.             CodeGenHelper.PutObjectToStack(al, is);  
    28.             CodeGenHelper.ChangeStackTopEle(al);  
    29.               
    30.             CodeGenHelper.CodePutField(al, is.getLastID().toString());  
    31.             }  
    32.             else  
    33.             {  
    34.                 int value=CodeGenHelper.StoreToLocalTable(al);  
    35.                 CodeGenHelper.PutObjectToStack(al, is);  
    36.                 CodeGenHelper.CodeGetField(al, is.getLastID().toString());  
    37.                 int array=CodeGenHelper.StoreToLocalTable(al);  
    38.                 is.getLastIDS().EXPR.genCode();  
    39.                 CodeGenHelper.LoadToStack(al, array);  
    40.   
    41.                 CodeGenHelper.LoadToStack(al, value);  
    42.                 Code code=new Code(0x26);  
    43.                 al.add(code);  
    44.             }  
    45.         }  
    46.         if(is.type==3)  
    47.         {  
    48.             int slot=CodeGenHelper.StoreToLocalTable(al);  
    49.             is.EXPR.genCode();  
    50.             CodeGenHelper.LoadToStackByName(al, is.ID.toString());  
    51.             CodeGenHelper.LoadToStack(al, slot);  
    52.             Code code=new Code(0x26);  
    53.             al.add(code);  
    54.         }  
    55.     }  
    首先ag.genCode()把所有参数压栈,然后根据类别判断是初始化对象还是数组来使用0x1C或者0x1D指令并把类型作为操作数,当此条指令执行完之后,堆栈顶此时放着返回的对象或数组的句柄,接下来的任务是将该句柄赋值给ids。

    接着判断ids的类型,ids的推导式如下:

    ids-> 
    *       id|       type 1
              id.ids|      type  2
             id[expr]     type 3
              this|       type=4

    如果type=1,则说明是一个局部变量,从nameSlot中找到该局部变量在局部变量表里的位置(相关操作封装到了CodeGenHelper的searchByName中),赋值给这个槽即可。

    如果type=2,说明是某个对象的成员变量,则需要先把这个对象压入堆栈,再通过putfield指令给此对象赋值,若是某个对象的成员变量中的某个元素(即该成员变量是个数组),则需要使用给数组赋值的指令。

    如果type=3,说明给数组某个元素赋值,需要先把下标计算出来,然后通过相应指令给数组赋值。

    其中的很多操作都封装在了CodeGenHelper中。


    (11)vardeclare

    递推式:

    var-declare --> type args|type[] args

    代码:

    [java] view plaincopy
    1. public void genCode() {  
    2.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    3.         ArrayList<ids> idlist=ags.getidsList();  
    4.         for(ids id:idlist)  
    5.         {  
    6.         BackendClassManager.nameSlot.put(id.getLastID().toString(), BackendClassManager.nameSlot.size());  
    7.         }  
    8.           
    9.           
    10.     }  
    可以看到,在变量声明时并没有生成任何实际代码,只是在局部变量表中给其开辟了一个存储位置而已。


    (12)setvalue

    setvalue--> ids = expr   

    [java] view plaincopy
    1. public void genCode() {  
    2.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    3.           ep.genCode();  
    4.         if(is.type==1)  
    5.         {  
    6.             int slot=CodeGenHelper.SearchByName(is.ID.toString());  
    7.             Code code=new Code(0x05);  
    8.             code.Operands.add(String.valueOf(slot));  
    9.             al.add(code);  
    10.         }  
    11.         if(is.type==2)  
    12.         {  
    13.             if(is.getLastIDS().type!=3)  
    14.             {  
    15.             CodeGenHelper.PutObjectToStack(al, is);  
    16.             CodeGenHelper.ChangeStackTopEle(al);  
    17.               
    18.             CodeGenHelper.CodePutField(al, is.getLastID().toString());  
    19.             }  
    20.             else  
    21.             {  
    22.                 int value=CodeGenHelper.StoreToLocalTable(al);  
    23.                 CodeGenHelper.PutObjectToStack(al, is);  
    24.                 CodeGenHelper.CodeGetField(al, is.getLastID().toString());  
    25.                 int array=CodeGenHelper.StoreToLocalTable(al);  
    26.                 is.getLastIDS().EXPR.genCode();  
    27.                 CodeGenHelper.LoadToStack(al, array);  
    28.   
    29.                 CodeGenHelper.LoadToStack(al, value);  
    30.                 Code code=new Code(0x26);  
    31.                 al.add(code);  
    32.             }  
    33.         }  
    34.         if(is.type==3)  
    35.         {  
    36.             int slot=CodeGenHelper.StoreToLocalTable(al);  
    37.             is.EXPR.genCode();  
    38.             CodeGenHelper.LoadToStackByName(al, is.ID.toString());  
    39.             CodeGenHelper.LoadToStack(al, slot);  
    40.             Code code=new Code(0x26);  
    41.             al.add(code);  
    42.         }  
    43.           
    44.     }  
    setvalue和classinit的代码几乎一样,唯一的区别是classinit中需要调用new和newarray指令得到需要给ids赋的值,而setvalue则是通过计算expr来得到,即使用ep.genCode();


    (13)ids

    ids的genCode逻辑是指将此ids的值放到堆栈顶,此值或许是个int、double,也可能是个对象的句柄。

    递推式:

    [java] view plaincopy
    1. ids->   
    2.      *       id|       type 1  
    3.           id.ids|      type  2  
    4.          id[expr]     type 3  
    5.           this|       type=4  

    代码:

    [java] view plaincopy
    1. public void genCode() {  
    2.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    3.         if(type==1)  
    4.         {  
    5.             Integer slot=BackendClassManager.nameSlot.get(ID.toString());  
    6.             if(slot!=null)  
    7.             {  
    8.             Code code=new Code(0x03);  
    9.             code.Operands.add(String.valueOf(slot));  
    10.             al.add(code);  
    11.             }  
    12.             else  
    13.             {  
    14.                 //error  
    15.             }  
    16.         }  
    17.         if(type==2)  
    18.         {  
    19.             int slot=BackendClassManager.nameSlot.get(ID.toString());  
    20.             Code code=new Code(0x03);  
    21.             code.Operands.add(String.valueOf(slot));  
    22.             al.add(code);  
    23.             IDS.genCode2nd();  
    24.         }  
    25.         if(type==3)  
    26.         {  
    27.             EXPR.genCode();//压入expr值  
    28.             int slot=BackendClassManager.nameSlot.get(ID.toString());  
    29.             Code code=new Code(0x03);  
    30.             code.Operands.add(String.valueOf(slot));  
    31.             al.add(code);//压入array  
    32.               
    33.             code=new Code(0x04);  
    34.             al.add(code);//读取数组元素  
    35.               
    36.               
    37.               
    38.         }  
    39.         if(type==4)  
    40.         {  
    41.             int slot=BackendClassManager.nameSlot.get("this");  
    42.             Code code=new Code(0x01);  
    43.             code.Operands.add(String.valueOf(slot));  
    44.         }  
    45.     }  
    46.       
    47.       
    48.     public void genCode2nd() {  
    49.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    50.         if(type==1)  
    51.         {  
    52.             Code code=new Code(0x20);  
    53.             code.Operands.add(ID.toString());  
    54.             al.add(code);  
    55.         }  
    56.         if(type==2)  
    57.         {  
    58.             Code code=new Code(0x20);  
    59.             code.Operands.add(ID.toString());  
    60.             al.add(code);  
    61.             IDS.genCode2nd();  
    62.         }  
    63.         if(type==3)  
    64.         {  
    65.             EXPR.genCode();//压入expr值  
    66.             Code code=new Code(0x20);  
    67.             code.Operands.add(ID.toString());  
    68.             al.add(code);//压入数组  
    69.             code=new Code(0x04);  
    70.             al.add(code);//读取数组元素  
    71.         }  
    72.         if(type==4)  
    73.         {  
    74.          //error  
    75.         }  
    76.     }  

    由于ids的结构不对称,所以不能使用递归的方式来获得其值。举例:id.id,第一个id的处理方式是通过局部变量表中拿到其句柄,第二个id的处理方式要通过getfield来拿到其值。因此这两种处理方式对应的是genCode和genCode2nd。

    只有当第一次调用的时候(也就是其它节点调用)调用genCode,ids节点调用均调用genCode2nd。

    (14)expr

    /*                         Type
    * expr -->  (expr)           0
                    ids|             1
                  number|           2
                  literal|           3
                 func-call|         4
                expr  ops expr|      5
    */

    [java] view plaincopy
    1. public void genCode() {  
    2.           
    3.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    4.         if(Type==0)  
    5.         {  
    6.             expr1.genCode();  
    7.         }  
    8.         else if(Type==1)  
    9.         {  
    10.             ids.genCode();  
    11.         }  
    12.         else if(Type==2)  
    13.         {  
    14.             Code code;  
    15.             if(number.isInt==true)  
    16.             {  
    17.                 code=new Code(0x01);  
    18.                 code.Operands.add(number.toString());  
    19.             }  
    20.             else  
    21.             {  
    22.                 code=new Code(0x02);  
    23.                 code.Operands.add(number.toString());  
    24.             }  
    25.             al.add(code);  
    26.         }  
    27.         else if(Type==3)  
    28.         {  
    29.             literal.genCode();  
    30.         }  
    31.         else if(Type==4)  
    32.         {  
    33.             fc.genCode();  
    34.         }  
    35.         else if(Type==5)  
    36.         {  
    37.             expr1.genCode();  
    38.             Code code=new Code(0x05);  
    39.             int pos=BackendClassManager.nameSlot.keySet().size();  
    40.             BackendClassManager.nameSlot.put(this.toString(), pos);  
    41.             code.Operands.add(String.valueOf(pos));  
    42.             al.add(code);  
    43.             expr2.genCode();  
    44.             code=new Code(0x03);  
    45.             code.Operands.add(String.valueOf(pos));  
    46.             al.add(code);  
    47.             BackendClassManager.expr1=expr1;  
    48.             BackendClassManager.expr2=expr2;  
    49.             op.genCode();  
    50.         }  
    51.     }  

    expr的genCode方法产生的效果是把此expr的值放到栈顶。

    如果type=0,则递归调用括号里的expr的genCode;如果type=1,则把ids的值放到栈顶;如果type=2,先判断立即数的类型之后再压入立即数;如果type=3,把字符串的句柄压入栈顶;type=4调用funccall的genCode;type=5,先生成expr1的字节码,然后将结果暂存到局部变量表,之后生成expr2的字节码,再把之前的结果压栈,调用op的genCode代码为运算符生成字节码。

    值得注意的是,调用op的genCode之前首先要把参与运算的expr放到全局变量里,op的字节码生成过程需要使用这两个expr的信息。


    (15)funccall

    func-call --> ids . func-name(NUL|args)

    [java] view plaincopy
    1. public void genCode() {  
    2.         // TODO Auto-generated method stub  
    3.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    4.         memberfuncdeclare mfc=(memberfuncdeclare)SyntaxTreeGenerator.getFunctions().get(fn.toString()).value;  
    5.         ArrayList<ids> ags=ag.getidsList();  
    6.         ArrayList<Integer> poses=new ArrayList<Integer>();  
    7.         for(int i=0;i<=ags.size()-1;i++)  
    8.         {  
    9.             ags.get(i).genCode();  
    10.             poses.add(CodeGenHelper.StoreToLocalTable(al));  
    11.         }  
    12.         for(int i=0;i<=poses.size()-1;i++)  
    13.         {  
    14.             CodeGenHelper.LoadToStack(al, poses.get(i));  
    15.         }  
    16.         if(mfc.isstatic==true)  
    17.         {  
    18.             Code code=new Code(0x1B);  
    19.             code.Operands.add(IDS.toString()+"."+fn.toString());  
    20.             al.add(code);  
    21.           
    22.         }  
    23.         else  
    24.         {  
    25.             IDS.genCode();  
    26.             Code code=new Code(0x1A);  
    27.             code.Operands.add(fn.toString());  
    28.             al.add(code);  
    29.         }  
    30.     }  
    函数调用的过程如下:

    首先所有参数压栈,然后虚拟机执行call指令,给新函数分配执行环境(栈帧),把之前压栈的参数放到局部变量表中(如果有this参数的话this也要以参数的形式放到局部变量表中);函数执行完之后虚拟机释放资源,并把堆栈顶元素(返回值)放到其调用者的堆栈顶。

    首先先从符号表中找到这个函数(封装在了SyntaxTreeGenerator中),然后得到所有参数,并存储到局部变量表中(之所以这么做因为参数的获取过程可能会把堆栈顺序打乱),接着把之前存过的所有参数压入堆栈,再判断该函数是否是个静态函数来使用不同的字节码进行调用。


    (16)ops

    ops --> bitop | logiop | artmop | cprop

    [java] view plaincopy
    1. public void genCode() {  
    2.         if(type==TYPE_BITOP)  
    3.         {  
    4.             bo.genCode();  
    5.         }  
    6.         else if(type==TYPE_LOGIOP)  
    7.         {  
    8.             lo.genCode();  
    9.         }  
    10.         else if(type==TYPE_ARMTOP)  
    11.         {  
    12.             ao.genCode();  
    13.         }  
    14.         else if(type==TYPE_CPROP)  
    15.         {  
    16.             co.genCode();  
    17.         }  
    18.           
    19.     }  

    根据不同的类型调用不同op的genCode方法。


    (17)cprop

    [java] view plaincopy
    1. public void genCode() {  
    2.         expr expr1,expr2;  
    3.         ArrayList<Code> al=BackendClassManager.tFunc.codes;  
    4.         expr1=BackendClassManager.expr1;  
    5.         expr2=BackendClassManager.expr2;  
    6.         boolean intint=true;  
    7.         if(expr1.tp.toString().equals("int") && expr2.tp.toString().equals("int"))  
    8.         {  
    9.             intint=true;  
    10.         }  
    11.         if(expr1.tp.toString().equals("double") && expr2.tp.toString().equals("double"))  
    12.         {  
    13.             intint=false;  
    14.         }  
    15.         if(expr1.tp.toString().equals("int") && expr2.tp.toString().equals("double"))  
    16.         {  
    17.             CodeGenHelper.i2d(al);  
    18.             intint=false;  
    19.               
    20.         }  
    21.         if(expr1.tp.toString().equals("double") && expr2.tp.toString().equals("int"))  
    22.         {  
    23.             int pos=CodeGenHelper.StoreToLocalTable(al);  
    24.             Code code=new Code(0x23);  
    25.             al.add(code);  
    26.             CodeGenHelper.i2d(al);  
    27.             CodeGenHelper.LoadToStack(al, pos);  
    28.             intint=false;  
    29.               
    30.         }  
    31.         if(value.equals(">"))  
    32.         {  
    33.             if(intint==true)  
    34.             {  
    35.                 Code code=new Code(0x16);//比较  
    36.                 al.add(code);  
    37.                 code=new Code(0x01);  
    38.                 code.Operands.add("1");//压入1  
    39.                 al.add(code);  
    40.                 code=new Code(0x16);//和1比较  
    41.                 al.add(code);  
    42.                 code=new Code(0x24);//取非  
    43.                 al.add(code);  
    44.             }  
    45.             else  
    46.             {  
    47.                 Code code=new Code(0x17);//比较  
    48.                 al.add(code);  
    49.                 code=new Code(0x02);  
    50.                 code.Operands.add("1");//压入1  
    51.                 al.add(code);  
    52.                 code=new Code(0x17);//和1比较  
    53.                 al.add(code);  
    54.                 code=new Code(0x24);//取非  
    55.                 al.add(code);  
    56.             }  
    57.         }  
    58.         if(value.equals("<"))  
    59.         {  
    60.             if(intint==true)  
    61.             {  
    62.                 Code code=new Code(0x16);//比较  
    63.                 al.add(code);  
    64.                 code=new Code(0x01);  
    65.                 code.Operands.add("-1");//压入1  
    66.                 al.add(code);  
    67.                 code=new Code(0x16);//和1比较  
    68.                 al.add(code);  
    69.                 code=new Code(0x24);//取非  
    70.                 al.add(code);  
    71.             }  
    72.             else  
    73.             {  
    74.                 Code code=new Code(0x17);//比较  
    75.                 al.add(code);  
    76.                 code=new Code(0x02);  
    77.                 code.Operands.add("-1");//压入1  
    78.                 al.add(code);  
    79.                 code=new Code(0x17);//和1比较  
    80.                 al.add(code);  
    81.                 code=new Code(0x24);//取非  
    82.                 al.add(code);  
    83.             }  
    84.         }  
    85.         if(value.equals(">="))  
    86.         {  
    87.             if(intint==true)  
    88.             {  
    89.                 Code code=new Code(0x16);//比较  
    90.                 al.add(code);  
    91.                 code=new Code(0x01);  
    92.                 code.Operands.add("-1");//压入1  
    93.                 al.add(code);  
    94.                 code=new Code(0x16);//和1比较  
    95.                 al.add(code);  
    96.                   
    97.             }  
    98.             else  
    99.             {  
    100.                 Code code=new Code(0x17);//比较  
    101.                 al.add(code);  
    102.                 code=new Code(0x02);  
    103.                 code.Operands.add("-1");//压入1  
    104.                 al.add(code);  
    105.                 code=new Code(0x17);//和1比较  
    106.                 al.add(code);  
    107.                 code=new Code(0x24);//取非  
    108.                 al.add(code);  
    109.             }  
    110.         }  
    111.         if(value.equals("<="))  
    112.         {  
    113.             if(intint==true)  
    114.             {  
    115.                 Code code=new Code(0x16);//比较  
    116.                 al.add(code);  
    117.                 code=new Code(0x01);  
    118.                 code.Operands.add("1");//压入1  
    119.                 al.add(code);  
    120.                 code=new Code(0x16);//和1比较  
    121.                 al.add(code);  
    122.                   
    123.             }  
    124.             else  
    125.             {  
    126.                 Code code=new Code(0x17);//比较  
    127.                 al.add(code);  
    128.                 code=new Code(0x02);  
    129.                 code.Operands.add("1");//压入1  
    130.                 al.add(code);  
    131.                 code=new Code(0x17);//和1比较  
    132.                 al.add(code);  
    133.                   
    134.             }  
    135.         }  
    136.         if(value.equals("=="))  
    137.         {  
    138.             if(intint==true)  
    139.             {  
    140.                 Code code=new Code(0x16);//比较  
    141.                 al.add(code);  
    142.                 code=new Code(0x01);  
    143.                 code.Operands.add("0");//压入1  
    144.                 al.add(code);  
    145.                 code=new Code(0x16);//和0比较  
    146.                 al.add(code);  
    147.                 code=new Code(0x24);//取非  
    148.                 al.add(code);  
    149.             }  
    150.             else  
    151.             {  
    152.                 Code code=new Code(0x17);//比较  
    153.                 al.add(code);  
    154.                 code=new Code(0x02);  
    155.                 code.Operands.add("0");//压入1  
    156.                 al.add(code);  
    157.                 code=new Code(0x17);//和0比较  
    158.                 al.add(code);  
    159.                 code=new Code(0x24);//取非  
    160.                 al.add(code);  
    161.             }  
    162.         }  
    163.         if(value.equals("!="))  
    164.         {  
    165.             if(intint==true)  
    166.             {  
    167.                 Code code=new Code(0x16);//比较  
    168.                 al.add(code);  
    169.                 code=new Code(0x01);  
    170.                 code.Operands.add("0");//压入1  
    171.                 al.add(code);  
    172.                 code=new Code(0x16);//和0比较  
    173.                 al.add(code);  
    174.               
    175.             }  
    176.             else  
    177.             {  
    178.                 Code code=new Code(0x17);//比较  
    179.                 al.add(code);  
    180.                 code=new Code(0x02);  
    181.                 code.Operands.add("0");//压入1  
    182.                 al.add(code);  
    183.                 code=new Code(0x17);//和0比较  
    184.                 al.add(code);  
    185.               
    186.             }  
    187.         }  
    188.           
    189.     }  
    比较运算符的genCode虽然代码比较长但是逻辑很简单。

    首先需要根据待比较的expr来决定是否进行类型转换,目前在语言的设计中,只有double和int是可比的,其它类型的比较虽然在编译器不会报错但在运行时中会进行报错。

    其次根据类型来调用不同的比较指令,再次根据逻辑需要进行二次比较(因为比较指令会根据大于小于等于返回1、0或-1,和运算符并不等价,需要通过比较两次使之和运算符等价)。


    大体上代码就这么多,目前的编译器已经可以编译出正确的字节码了,但目前代码还无法执行,需要等到Runtime写好之后才能运行。


    因此本系列博客到这里暂时告一段落,若给语言再添加一些高级点的特性,也需要等到我把Runtime写好再说了。


    展开全文
  • 自制编译器:后端代码生成(一)

    千次阅读 2013-06-12 23:53:02
    后端早就已经弄的差不多了,因为学校论文的事情耽搁的比较久,一直到现在才发博客。 所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,...

    
    
    后端早就已经弄的差不多了,因为学校论文的事情耽搁的比较久,一直到现在才发博客。

    所谓的编译器后端的作用就是将语法树翻译成目标机器码。所谓目标机器码,考虑到直接翻译成具体平台(如X86,ARM等)过于复杂,因此先设计一个虚拟机,并翻译成这个虚拟机的机器码。

    对于虚拟机以及其指令格式可参考这篇文章http://blog.csdn.net/roger__wong/article/details/8947720,如何去尝试实现这个虚拟机是在我的另外一个系列的博客里进行论述。


    本篇文章从以下是那个方面来论述:后端架构与关键数据结构、节点翻译方式。

    1、后端架构和关键数据结构

    后端接受前端的语法树作为输入,对于其每一个节点根据节点类型的不同产生不同的代码。但在实现过程中为了简单方便,我并没有把后端抽象出一个单独的模块,而是在语法树每一个节点的基础上增加了一个genCode方法,通过调用这个方法来生成该节点及其所有孩子节点(通过递归)的代码。

    其次编译器后端直接生成Class文件(文件结构也在上文提到的博客中有说明),程序中后端首先构造一个ClassOfClass的实体,然后再调用此类的方法生成Class文件:

    public class ClassOfClass {
    	public static int isPublic=1;
    	public static int isStatic=2;
    	public ArrayList<field> fields;
    	public ArrayList<function> functions;
    	public ArrayList<String> constPool;
    	public String name;
    	public ClassOfClass()
    	{
    		constPool=new ArrayList<String>();
    		fields=new ArrayList<field>();
    		functions=new ArrayList<function>();
    		
    	}
    	public void WriteClassFile(String path)
    	{
    		try {
    			PrintWriter pw=new PrintWriter(new FileOutputStream(path));
    			pw.println(name);
    			pw.println(fields.size());
    			for(field f:fields)
    			{
    				pw.println(f.toString());
    			}
    			pw.println(functions.size());
    			for(function f:functions)
    			{
    				pw.println(f.toString());
    			}
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    其中field结构:

    public class field {
    	public int head;
    	public String type;
    	public String fieldname;
        @Override
        public String toString()
        {
        	StringBuffer sb=new StringBuffer();
        	sb.append(head);
        	sb.append(" ");
        	sb.append(type);
        	sb.append(" ");
        	sb.append(fieldname);
        	return sb.toString();
        }
    }

    其中Function结构

    public class function {
    	public int head;
    	public String rettype;
    	public int argnum;
    	public ArrayList<String> args;
    	public ArrayList<Code> codes;
    	public function()
    	{
    		args=new ArrayList<String>();
    		codes=new ArrayList<Code>();
    	}
    	 @Override
    	    public String toString()
    	    {
    	    	StringBuffer sb=new StringBuffer();
    	    	sb.append(head);
    	    	sb.append(" ");
    	    	sb.append(rettype);
    	    	sb.append(" ");
    	    	sb.append(args.size());
    	    	sb.append(" ");
    	    	for(String s:args)
    	    	{
    	    		sb.append(s+" ");
    	    	}
    	    	sb.append("\r\n");
    	    	sb.append("{");
    	    	for(int i=0;i<=codes.size()-1;i++)
    	    	{
    	    		sb.append(i+":"+codes.get(i).toString()+"\r\n");
    	    	}
    	    	sb.append("\r\n");
    	    	sb.append("}");
    	    	return sb.toString();
    	    }
    }
    其中Code结构

    public class Code {
    	public int Opcode;//操作码
    	public ArrayList<String> Operands;
    	public Code(int op)
    	{
    		Operands=new ArrayList<String>();
    		Opcode=op;
    	}
    	 @Override
    	    public String toString()
    	    {
    	    	StringBuffer sb=new StringBuffer();
    	    	sb.append(Opcode);
    	    	sb.append(" ");
    	    	for(String s:Operands)
    	    	{
    	    		sb.append(s+" ");
    	    	}
    	    	return sb.toString();
    	    }
    }

    完成一个Class的后端代码的生成工作,只需要调用语法树根节点(classdef)的genCode即可完成,因为根节点会不断的递归调用其子树的genCode方法,因此在递归调用的时候需要某些机制进行各方法之间信息的传递,这里建立新类BackendClassManager来进行信息的传递,消除耦合。

    public class BackendClassManager {
    	public static ClassOfClass cc=new ClassOfClass();//正在生成的class对象
    	public static function tFunc;//正在生成代码的函数
    	public static memberfuncdeclare mfc;//正在生成代码的语法树中的memberfuncdeclare节点
    	public static constructor ct;//正在生成代码的构造函数节点
    	public static HashMap<String,Integer> nameSlot=new HashMap<String,Integer>();//局部变量和局部变量表槽的对应hash
    	public static expr expr1,expr2;//正在翻译的 expr
    	public static Stack<ArrayList<Code>> loopcontinue=new Stack<ArrayList<Code>>();//在循环语句中出现的continue语句,用于回填地址
    	public static Stack<ArrayList<Code>> loopbreak=new Stack<ArrayList<Code>>();//在循环语句中出现的break语句,用于回填地址
    	public static void WriteToClassFile()
    	{
    		String path="E:/test.class";
    		cc.WriteClassFile(path);
    	}
    	public static void genCode(classdef cd)
    	{
    		cd.genCode();
    	}
    }
    
    cc代表目前正在编译的class。

    tFunc代表正在生成代码的函数,也就是所有genCode的方法都要把生成的代码填充到tFunc的codes域中。

    memberfuncdeclare代表语法树中正在生成后端代码的memberfuncdeclare节点,该节点和其子树包含此函数的所有代码。

    nameSlot对应源码中出现的局部变量与目标代码中的局部变量表槽的一个关系,因为目标代码将不再会出现局部变量名这个概念,所以需要一个hash在编译时进行对应。

    expr1和expr2对应正在翻译的expr,在某些运算符需要进行类型转换时需要用到正在翻译的表达式的信息。

    loopcontinue和loopbreak用于循环语句的地址回填,因为一个循环在翻译的过程中,其break需要跳转到的地址是还未确定的,需要整个循环翻译完之后对目标地址进行回填。continue虽然可以确定目标地址,但是在continue对应的语句stmt节点无法知道循环的开始地址,需要通过某些机制让stmt节点知道此循环的开始地址,因此也把continue语句进行回填处理。

    除此之外还是用了一个类CodeGenHelper来封装一些常用的代码序列,比如i2d,jmp等,目的是为了简化之后的目标代码的生成。


    2、节点代码生成

    按照从顶至低的方式依次分析。

    (1)classdef节点

    public void genCode() {
    		BackendClassManager.cc.name=cn.toString();
    		cb.genCode();
    	}
    代码很简单,首先把正在编译的类名设置成classdef中出现的类名,然后调用classbody的genCode方法。


    (2)classbody

    public void genCode() {
    		if(cb!=null)
    		{
    			cb.genCode();
    		}
    		
    	}
    依然很简单,如果classmembers不为空,则调用classmembers的genCode方法。

    值得注意的是,这些方法本身并没有生成目标代码乃是因为一个类的定义本身并不包含任何逻辑,而代码本身是对逻辑的阐述,所以在类声明、函数声明、成员变量声明等没有生成任何有意义的代码也就不值得奇怪了。


    (3)classmembers

    public void genCode() {
    		if(type==0)
    		{
    			((membervardeclare)declare).genCode();
    		}
    		else if(type==1)
    		{
    			((memberfuncdeclare)declare).genCode();
    		}
    		else if(type==2)
    		{
    			ct.genCode();
    		}
    		if(cm!=null)
    		{
    			cm.genCode();
    		}
    	}
    根据此classmembers的类型,对membervardeclare、memberfuncdeclare、constructor调用genCode方法,最后对下一个classmemebers调用genCode方法,这和本系列第一篇博客中的递推式是对应的。


    (4)membervardeclare

    public void genCode() {
    		// TODO Auto-generated method stub
    		
    		field fd=new field();
    		
    		ArrayList<field> fs=BackendClassManager.cc.fields;
    		if(af.toString().equals("public"))
    		{
    		fd.head+=ClassOfClass.isPublic;
    		}
    		if(isstatic==true)
    		{
    			fd.head+=ClassOfClass.isStatic;
    		}
    		fd.type=tp.toString();
    		fd.fieldname=ID.toString();
    		fs.add(fd);
    	}
    依然没有任何代码生成,只是将成员变量的信息放到ClassOfClass对象的fields域中。

    (5)memberfundeclare

    public void genCode() {
    		function func=new function();
    		BackendClassManager.tFunc=func;
    		BackendClassManager.cc.functions.add(func);
    		BackendClassManager.ct=null;
    		BackendClassManager.mfc=this;
    		BackendClassManager.nameSlot=new HashMap<String,Integer>();
    		if(af.toString().equals("public"))
    		{
    		func.head+=ClassOfClass.isPublic;
    		}
    		func.rettype=tp.toString();
    		if(da!=null)
    		{
    		ArrayList<type> al=da.gettypelist();
    		func.argnum=al.size();
    		
    		for(type tp:al)
    		{
    			func.args.add(tp.toString());
    		}
    		ArrayList<id> tal=da.getidlist();
    		BackendClassManager.nameSlot.put("this", 0);
    		for(int i=0;i<=tal.size()-1;i++)
    		{
    			BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);
    		}
    		}
    		else
    		{
    			func.argnum=0;
    		}
    		fb.genCode();
    	
    	}
    成员函数的稍微有点复杂。

    首先建立一个新的function对象,并把该对象设置为BackendClassManager.tFun,说明之后所有genCode都为这个函数生成的代码,并把这个函数加到classofClass对象的functions域中;判断该函数的返回值类型、是否是public、是否是静态,并把相关信息记录到function对象中重置nameslot,将函数所有参数压入nameslot表中,并注意this也当做参数放入表中,然后调用functionbody的genCode,为该函数生成代码。

    (6)constructor

    public void genCode() {
    		
    		function func=new function();
    		BackendClassManager.tFunc=func;
    		BackendClassManager.ct=this;
    		BackendClassManager.nameSlot=new HashMap<String,Integer>();
    		if(af.toString().equals("public"))
    		{
    		func.head+=ClassOfClass.isPublic;
    		}
    		func.rettype="NULL";
    		if(da!=null)
    		{
    		ArrayList<type> al=da.gettypelist();
    		func.argnum=al.size();
    		func.argnum=al.size();
    		for(type tp:al)
    		{
    			func.args.add(tp.toString());
    		}
    		ArrayList<id> tal=da.getidlist();
    		BackendClassManager.nameSlot.put("this", 0);
    		for(int i=0;i<=tal.size()-1;i++)
    		{
    			BackendClassManager.nameSlot.put(tal.get(i).toString(), i+1);
    		}
    		}
    		else
    		{
    			func.argnum=0;
    		}
    		ss.genCode();
    		
    	}

    (7)funcbody

    	public void genCode() {
    		// TODO Auto-generated method stub
    		ss.genCode();
    		returnexpr.genCode();
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		Code code=new Code(0x19);
    		al.add(code);
    	}

    首先stmts生成代码,然后为返回表达式生成代码,最后在codes中加入Code(0x19),也就是返回指令。

    值得注意的是,这里做了一个约定,在expr的genCode中,总是把该expr的结果放在expr代码执行后的栈顶,因此函数返回时实际上返回的是栈顶元素的值。


    (未完待续)



    展开全文
  • 自制编译器:后端代码生成(二)

    千次阅读 2013-06-15 14:41:39
    (8)stmts ...stmts逻辑上代表一个语句块或一组语句块,对应的生成式和在节点中使用的type如下: stmts --> NUL| type-->0  stmt stmts| type-->1  if(expr) { stmts} stmts| type-->2

    (8)stmts

    stmts逻辑上代表一个语句块或一组语句块,对应的生成式和在节点中使用的type如下:

    stmts --> NUL|            type-->0


                      stmt stmts|        type-->1


                      if(expr) { stmts} stmts|   type-->2


                      if(expr) {stmts} else {stmts}  stmts|       type-->3


                      while(expr) { stmts}     stmts|    type-->4

    接下来给出生成后端代码的代码:

    public void genCode() {
    		if(type==1)
    		{
    			st.genCode();
    			stmts1.genCode();
    		}
    		else if(type==2)
    		{
    			condition.genCode();
    			Code code=new Code(0x18);
    			BackendClassManager.tFunc.codes.add(code);
    			stmts1.genCode();
    			code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
    			stmts3.genCode();
    		}
    		else if(type==3)
    		{
    			condition.genCode();
    			Code code=new Code(0x18);
    			code.Opcode=0x18;
    			BackendClassManager.tFunc.codes.add(code);
    			stmts1.genCode();
    			Code code1=new Code(0x01);
    			code1.Operands.add(String.valueOf(0));
    			BackendClassManager.tFunc.codes.add(code1);
    			Code code2=new Code(0x18);
    			BackendClassManager.tFunc.codes.add(code2);
    			code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
    			stmts2.genCode();
    			code2.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
    			stmts3.genCode();
    		}
    		else if(type==4)
    		{
    			ArrayList<Code> albreak=new ArrayList<Code>();
    			ArrayList<Code> alcontinue=new ArrayList<Code>();
    			BackendClassManager.loopbreak.add(albreak);
    			BackendClassManager.loopcontinue.add(alcontinue);//循环入口,首先判断expr
    			int pos=BackendClassManager.tFunc.codes.size();
    			condition.genCode();
    			//跳转指令
    			Code code=new Code(0x18);
    			BackendClassManager.tFunc.codes.add(code);
    			//循环体
    			stmts1.genCode();
    			
    		//	code.Operands.add(String.valueOf(end));//表达式回填
    			
    			Code code1=new Code(0x01);
    			code1.Operands.add(String.valueOf(0));
    			BackendClassManager.tFunc.codes.add(code1);//压入0
    			
    			Code code2=new Code(0x18);
    			code2.Operands.add(String.valueOf(pos));
    			BackendClassManager.tFunc.codes.add(code2);//跳转到循环入口
    			
    			int end=BackendClassManager.tFunc.codes.size();
    			code.Operands.add(String.valueOf(end));//表达式回填
    			
    			for(Code c:albreak)
    			{
    				c.Operands.add(String.valueOf(end));
    			}
    			for(Code c:alcontinue)
    			{
    			   c.Operands.add(String.valueOf(pos));
    			}
    			BackendClassManager.loopbreak.pop();
    			BackendClassManager.loopcontinue.pop();
    			stmts3.genCode();
    		}
    		
    	}
    对于type=0,没必要生成任何代码;对于type=1,生成stmt的代码然后递归再生成stmts的代码;对于type=2,首先生成条件expr的代码,在这段代码执行过后,会将结果放在堆栈顶,然后加入code(0x18)进行跳转,但此刻的跳转地址还不能确定,因此先要生成stmts1的代码,之后回填跳转地址,再生成stmts3的代码;对于type=3,和type=2类似,但要在stmts1代码之后加入无条件跳转指令,跳转到else块后面,无条件跳转的方法是首先压入0,再使用0x18也就是ifz进行跳转;对于type=4要稍微复杂些,首先要计算循环开始的地址(包括判断expr),然后生成循环体代码,得到循环结束地址,再回填循环判断的相关代码,除此之外,还要回填此循环体内出现的所有break和continue语句的地址。


    (9)stmt

    首先给出语句stmt的生成式:

     stmt -->   continue;|   type=0
     
                    break;|    type=1


                    var-declare;|   type=2


                   class-init;|    type=3


                   setvalue;|   type=4


                   expr;|       type=5


    接下来给出代码:

    public void genCode() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		if(type==0)
    		{
    			CodeGenHelper.JMP(al, 0);
    			BackendClassManager.loopcontinue.peek().add(al.get(al.size()-1));
    		}
    		if(type==1)
    		{
    			CodeGenHelper.JMP(al, 0);
    			BackendClassManager.loopbreak.peek().add(al.get(al.size()-1));
    		}
    		if(type==5)
    		{
    			ep.genCode();
    		}
    		if(type==2)
    		{
    			vc.genCode();
    		}
    		if(type==3)
    		{
    			ci.genCode();
    		}
    		if(type==4)
    		{
    			sv.genCode();
    		}
    	}
    stmt代码比较简单,对于break和continue,只需要加入一个跳转语句,然后把此跳转语句加入到相应的列表里(因为循环是嵌套的,所以使用堆栈来表示这一关系),由外层循环体进行地址回填即可,使用了CodeGenHelper的JMP方法作为辅助函数;对于其它类型则调用其相应节点的genCode方法。

    另外给出CodeGenHelper的JMP方法,封装了压入0和ifz两条指令:

    public static void JMP(ArrayList<Code> al,int pos)
    	{
    		Code code1=new Code(0x01);
    		code1.Operands.add(String.valueOf(0));
    		BackendClassManager.tFunc.codes.add(code1);//压入0
    		
    		Code code2=new Code(0x18);
    		code2.Operands.add(String.valueOf(pos));
    		BackendClassManager.tFunc.codes.add(code2);//跳转
    	}

    (10)classinit

    递推式:class-init --> ids = new 


                type ( args )| type[expr]
            type:     0            1              

    代码:

    public void genCode() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		ag.genCode();
    		if(type==0)
    		{
    			Code code=new Code(0x1C);
    			code.Operands.add(tp.toString());
    			al.add(code);
    		}
    		else
    		{
    			Code code=new Code(0x1D);
    			code.Operands.add(tp.toString());
    			al.add(code);
    		}
    		if(is.type==1)
    		{
    			int slot=CodeGenHelper.SearchByName(is.ID.toString());
    			Code code=new Code(0x05);
    			code.Operands.add(String.valueOf(slot));
    			al.add(code);
    		}
    		if(is.type==2)
    		{
    			if(is.getLastIDS().type!=3)
    			{
    			CodeGenHelper.PutObjectToStack(al, is);
    			CodeGenHelper.ChangeStackTopEle(al);
    			
    			CodeGenHelper.CodePutField(al, is.getLastID().toString());
    			}
    			else
    			{
    				int value=CodeGenHelper.StoreToLocalTable(al);
    				CodeGenHelper.PutObjectToStack(al, is);
    				CodeGenHelper.CodeGetField(al, is.getLastID().toString());
    				int array=CodeGenHelper.StoreToLocalTable(al);
    				is.getLastIDS().EXPR.genCode();
    				CodeGenHelper.LoadToStack(al, array);
    
    				CodeGenHelper.LoadToStack(al, value);
    				Code code=new Code(0x26);
    				al.add(code);
    			}
    		}
    		if(is.type==3)
    		{
    			int slot=CodeGenHelper.StoreToLocalTable(al);
    			is.EXPR.genCode();
    			CodeGenHelper.LoadToStackByName(al, is.ID.toString());
    			CodeGenHelper.LoadToStack(al, slot);
    			Code code=new Code(0x26);
    			al.add(code);
    		}
    	}
    首先ag.genCode()把所有参数压栈,然后根据类别判断是初始化对象还是数组来使用0x1C或者0x1D指令并把类型作为操作数,当此条指令执行完之后,堆栈顶此时放着返回的对象或数组的句柄,接下来的任务是将该句柄赋值给ids。

    接着判断ids的类型,ids的推导式如下:

    ids-> 
    *       id|       type 1
              id.ids|      type  2
             id[expr]     type 3
              this|       type=4

    如果type=1,则说明是一个局部变量,从nameSlot中找到该局部变量在局部变量表里的位置(相关操作封装到了CodeGenHelper的searchByName中),赋值给这个槽即可。

    如果type=2,说明是某个对象的成员变量,则需要先把这个对象压入堆栈,再通过putfield指令给此对象赋值,若是某个对象的成员变量中的某个元素(即该成员变量是个数组),则需要使用给数组赋值的指令。

    如果type=3,说明给数组某个元素赋值,需要先把下标计算出来,然后通过相应指令给数组赋值。

    其中的很多操作都封装在了CodeGenHelper中。


    (11)vardeclare

    递推式:

    var-declare --> type args|type[] args

    代码:

    public void genCode() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		ArrayList<ids> idlist=ags.getidsList();
    		for(ids id:idlist)
    		{
    		BackendClassManager.nameSlot.put(id.getLastID().toString(), BackendClassManager.nameSlot.size());
    		}
    		
    		
    	}
    可以看到,在变量声明时并没有生成任何实际代码,只是在局部变量表中给其开辟了一个存储位置而已。


    (12)setvalue

    setvalue--> ids = expr   

    public void genCode() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    	      ep.genCode();
    	  	if(is.type==1)
    		{
    			int slot=CodeGenHelper.SearchByName(is.ID.toString());
    			Code code=new Code(0x05);
    			code.Operands.add(String.valueOf(slot));
    			al.add(code);
    		}
    		if(is.type==2)
    		{
    			if(is.getLastIDS().type!=3)
    			{
    			CodeGenHelper.PutObjectToStack(al, is);
    			CodeGenHelper.ChangeStackTopEle(al);
    			
    			CodeGenHelper.CodePutField(al, is.getLastID().toString());
    			}
    			else
    			{
    				int value=CodeGenHelper.StoreToLocalTable(al);
    				CodeGenHelper.PutObjectToStack(al, is);
    				CodeGenHelper.CodeGetField(al, is.getLastID().toString());
    				int array=CodeGenHelper.StoreToLocalTable(al);
    				is.getLastIDS().EXPR.genCode();
    				CodeGenHelper.LoadToStack(al, array);
    
    				CodeGenHelper.LoadToStack(al, value);
    				Code code=new Code(0x26);
    				al.add(code);
    			}
    		}
    		if(is.type==3)
    		{
    			int slot=CodeGenHelper.StoreToLocalTable(al);
    			is.EXPR.genCode();
    			CodeGenHelper.LoadToStackByName(al, is.ID.toString());
    			CodeGenHelper.LoadToStack(al, slot);
    			Code code=new Code(0x26);
    			al.add(code);
    		}
    		
    	}
    setvalue和classinit的代码几乎一样,唯一的区别是classinit中需要调用new和newarray指令得到需要给ids赋的值,而setvalue则是通过计算expr来得到,即使用ep.genCode();


    (13)ids

    ids的genCode逻辑是指将此ids的值放到堆栈顶,此值或许是个int、double,也可能是个对象的句柄。

    递推式:

    ids-> 
    	 *       id|       type 1
              id.ids|      type  2
             id[expr]     type 3
              this|       type=4

    代码:

    public void genCode() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		if(type==1)
    		{
    			Integer slot=BackendClassManager.nameSlot.get(ID.toString());
    			if(slot!=null)
    			{
    			Code code=new Code(0x03);
    			code.Operands.add(String.valueOf(slot));
    			al.add(code);
    			}
    			else
    			{
    				//error
    			}
    		}
    		if(type==2)
    		{
    			int slot=BackendClassManager.nameSlot.get(ID.toString());
    			Code code=new Code(0x03);
    			code.Operands.add(String.valueOf(slot));
    			al.add(code);
    			IDS.genCode2nd();
    		}
    		if(type==3)
    		{
    			EXPR.genCode();//压入expr值
    			int slot=BackendClassManager.nameSlot.get(ID.toString());
    			Code code=new Code(0x03);
    			code.Operands.add(String.valueOf(slot));
    			al.add(code);//压入array
    			
    			code=new Code(0x04);
    			al.add(code);//读取数组元素
    			
    			
    			
    		}
    		if(type==4)
    		{
    			int slot=BackendClassManager.nameSlot.get("this");
    			Code code=new Code(0x01);
    			code.Operands.add(String.valueOf(slot));
    		}
    	}
    	
    	
    	public void genCode2nd() {
    		ArrayList<Code> al=BackendClassManager.tFunc.codes;
    		if(type==1)
    		{
    			Code code=new Code(0x20);
    			code.Operands.add(ID.toString());
    			al.add(code);
    		}
    		if(type==2)
    		{
    			Code code=new Code(0x20);
    			code.Operands.add(ID.toString());
    			al.add(code);
    			IDS.genCode2nd();
    		}
    		if(type==3)
    		{
    			EXPR.genCode();//压入expr值
    			Code code=new Code(0x20);
    			code.Operands.add(ID.toString());
    			al.add(code);//压入数组
    			code=new Code(0x04);
    			al.add(code);//读取数组元素
    		}
    		if(type==4)
    		{
    		 //error
    		}
    	}

    由于ids的结构不对称,所以不能使用递归的方式来获得其值。举例:id.id,第一个id的处理方式是通过局部变量表中拿到其句柄,第二个id的处理方式要通过getfield来拿到其值。因此这两种处理方式对应的是genCode和genCode2nd。

    只有当第一次调用的时候(也就是其它节点调用)调用genCode,ids节点调用均调用genCode2nd。


    (未完待续)




    展开全文
  • 自制编译器:后端代码生成(三)

    千次阅读 2013-06-16 13:39:50
    type=5,先生成expr1的字节码,然后将结果暂存到局部变量表,之后生成expr2的字节码,再把之前的结果压栈,调用op的genCode代码为运算符生成字节码。 值得注意的是,调用op的genCode之前首先要把参与运算的expr放...
  • 本工具用于java web后端基础代码自动生成,包括controller层、service层、dao层、实体类、mybatis xml代码; 文档:https://blog.csdn.net/weixin_42686388/article/details/84203062
  • 上一篇解决了用了JQUERY-EASYUI时 后端注册脚本重复执行的问题.今天又发现,通过后端代码 生成的界面有错位现象.
  • 本工具用于java web后端基础CRUD接口代码自动生成,包括controller层、service层、dao层、实体类、mybatis xml代码; 文档:https://blog.csdn.net/weixin_42686388/article/details/89486643
  • java web后端基础CRUD接口代码自动生成,包括controller层、service层、dao层、实体类、mybatis xml代码;支持多表操作;支持自定义模板;扩展功能:口令管理、环境管理、MD文档管理,详细操作文档,请移步我的博客...
  • JavaWeb后端代码自动生成工具V0.0.1

    万次阅读 热门讨论 2018-11-18 18:48:54
    1、本工具用于java web后端基础代码自动生成,包括controller层、service层、dao层、实体类、mybatis xml代码。 2、本工具基于mybatis generator 1.3.6,修改了源码,针对我大天朝的习惯优化了一下; 2、本工具...
  • 【实例简介】本工具用于java web后端基础代码自动生成,包括controller层、service层、dao层、实体类、mybatis xml代码;文档:https://blog.csdn.net/weixin_42686388/article/details/84203062【实例截图】【核心...
  • 主要介绍了Python生成并下载文件后端代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • spring boot 与 iview 前后端分离架构之开发环境基于docker的后端的部署的实现(番外篇)代码快速生成插件安装代码生成的maven插件到本地的仓库快速生成代码 代码快速生成插件 在前面三十六章我们已经讲解了如何编写...
  • 自动生成后端代码

    千次阅读 2018-07-18 20:54:52
    为了提高开发效率,写了一个生成代码的项目。根据数据库表自动生成Entity,Dao,Service,Controller。 generator.properties package 配置基础包 package=com.xx.xx 配置数据源 druid: url: jdbc:mysql://...
  • 今天又发现,通过后端代码 生成的界面有错位现象.如图: 上图的右边框不见了,使用 web开发工具调试,发现是内容器过大.于是想到了是jquery-easyui 对界面的"渲染"不正确引起. 起初以为是因为加了 处理...
  • txt文件生成并下载txt文件:@app.route('/download', methods=['GET'])def download():content = "long text"response = make_response(content)response.headers["Content-Disposition"] = "attachment;filename=...
  • AngularJS也是mvc架构的,我们可以把service和controller分离开来。在 AngularJS 中,服务是一个函数...我们在不同的控制器中可以调用同一个服务,这样服务的代码将会被重用。 注意service要有return返回值,不然co...
  • 在json中进行设计,并为服务+多个实用程序生成90%以上的代码 facebook / google auth,oauth 2,交易令牌,加密,... twilio,邮件枪,其他客户,其他工作,... iOS,Android,... 地理网格,反向地理编码,地理...
  • 后端java生成验证码

    2020-11-19 10:32:54
    验证码生成代码 package com.inspur; import java.util.Scanner; /** * @author l * @time 2020.11.12 9:17 * @ 产生一个区分大小写的四位数字字母组合验证码 */ public class Captcha { /** * @return 产生...
  • BOS开发再服务端调用BOTP生成目标单据,记录下 (自用备忘) @Override protected void _audit(Context ctx, IObjectValue model) throws BOSException { try { this._submit(ctx, model); } catch ...
  • 后端代码实例

    2019-03-26 11:08:17
    代码自动生成mybatis-generator-maven-plugin的idea自动生成插件 <!--mybatis-generator插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-...
  • 第一步:idea连接数据库 先连接上自己的数据库,... 此处的package一定要自己指定,不然生成的文件是不带有包名的 至此,后端代码生成完毕 别忘记一个最重要的点! 记得在Springboot的启动类上加上@MapperScan注解!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,589
精华内容 1,435
关键字:

后端代码生成