Coder Social home page Coder Social logo

ruby2glang_ir's Introduction

编译实验:AST转IR——declaration

RUBY小组成员

  • 黄宇悦 20300246005
  • 黄婧媛 21300246010
  • 田娜 21307130437

实验思路

本次实验需要将ruby语法中的 declaration 部分转化为GLang IR。基于之前实验的经验,我们根据已定义的一个方法check_declaration_handler,该方法接受一个node作为参数。在方法内部,我们定义了一个名为DECLARATION_HANDLER_MAP的字典,该字典将不同类型的表达式(declaration)映射到相应的处理方法上。如下:

def check_declaration_handler(self, node):
    DECLARATION_HANDLER_MAP = {
        "method": self.method_declaration,
        "lambda": self.lambda_declaration,
        "class": self.class_declaration,
        "singleton_class": self.singleton_class_declaration,
        "module": self.module_declaration,
    }
    return DECLARATION_HANDLER_MAP.get(node.type, None)

接着,我们参考java_parser.py以及ruby_grammar.js来完成ruby_parser.py中所需要的方法。


(1) method_declaration

method节点,调用method_declaration用于解析函数声明,先去了解GLang IR文档中关于method_decl的描述,主要包括以下组成部分:

  • attr: 函数的属性,例如public\static
  • data_type: 返回值的数据类型
  • name: 函数名称
  • parameters: 参数列表,为列表
  • init: 参数初始化内容,为列表
  • body: 内部具体指令,为列表

对于ruby语言,

def sum(a, b)
  a + b  
end
  • attr: Ruby没有像Java那样的修饰符(如public、static等)来控制方法的属性,所以为""
  • data_type: 在Ruby中,方法声明中没有指定返回值的数据类型,所以也为""
  • name: 可以通过先找到当前节点下名为name的子节点,读取文本信息即为函数的名称,如示例中的"sum"
name = self.find_child_by_field(node, "name")
shadow_name = self.read_node_text(name)
  • parameters: 先找到名为parameters的子节点。由于最终对应的是个list,里面每一项都是parameter_decl: []。通过对ruby_grammar.js的分析,如下,parameters字段是可选的,所以要先判断这个子节点是否存在,如果是,则进行后面的处理。
// ruby_grammar.js
method: $ => seq('def', $._method_rest),

_method_rest: $ => seq(
      field('name', $._method_name),
      choice(
        $._body_expr,
        seq(
          field('parameters', alias($.parameters, $.method_parameters)),
          choice(
            seq(optional($._terminator), optional(field('body', $.body_statement)), 'end'),
            $._body_expr,
          ),
        ),
        seq(
          optional(field('parameters', alias($.bare_parameters, $.method_parameters)),),
          $._terminator,
          optional(field('body', $.body_statement)),
          'end',
        ),
      ),
    ),

依次遍历所有的parameters节点的所有子节点。判断参数类型,如果是identifier则直接读取节点内容加入new_parameters参数列表,这种情况属于固定参数。

# def lambda_declaration(self, node, statements): 
# ...
for parameter in parameters.named_children:
    if parameter.type == 'identifier':
        shadow_parameter = self.read_node_text(parameter)
        new_parameters.append(shadow_parameter)

如果类型是optional_parameter,这种该情况下是默认参数,如果调用方法时没有提供对应的参数,则使用默认值。该参数节点下还有子节点,名称分别为namevalue。见如下的ruby例子:

def greet(name = "stranger")
  puts "Hello, #{name}!"
end

这个例子中,name就是namevalue"stranger"。可以看做assign语句。处理方式如下:

获取名为name的子节点,获取节点内容,加入参数列表中。然后获取名为value的子节点,调用parser函数进一步解析,且新生成的语句保存在new_init中,返回值保存在变量shadow_value中。

最后,使用shadow_parametershadow_value生成新的语句assign_stmt(两个变量分别表示targetoperand),加入new_init中。

    elif parameter.type == 'optional_parameter':
        parameter_name = self.find_child_by_field(parameter, 'name')
        shadow_parameter = self.read_node_text(parameter_name)
        new_parameters.append(shadow_parameter)
    
        new_init = []
    
        value = self.find_child_by_field(parameter, 'value')
        shadow_value = self.parse(value, new_init)
    
        new_init.append({'assign_stmt': {'target': shadow_parameter, 'operand': shadow_value}})
    
        init.extend(new_init)
  • init: 参数初始化内容,在之前对parameters的分析中以及得到,如果有参数初始化,即各个parameter的类型为optional_parameter,将new_init依次添加到init中。
  • body: 为具体的指令。只需在node下找到名为body的子节点,如果节点存在,则依次遍历它之下的子节点,如果是注释则跳过,否则调用parser函数解析, 所有生成的语句加入new_body中。
new_body = []
child = self.find_child_by_field(node, "body")
if child:
    for stmt in child.named_children:
        if self.is_comment(stmt):
            continue

        self.parse(stmt, new_body)

最后,使用通过以上操作获得的shadow_namenew_parametersinitnew_body生成method_decl语句并加入statements中。

statements.append(
    {"method_decl": {"attr": "", "data_type": "", "name": shadow_name,
                     "parameters": new_parameters, "init": init, "body": new_body}})

(2) lambda_declaration

lambda节点,调用lambda_declaration用于解析匿名函数声明。 Lambda函数(或匿名函数)是一段没有名字的代码块,可以存储在变量中并多次调用。

lambda: $ => seq(
  '->',
  field('parameters', optional(choice(
    alias($.parameters, $.lambda_parameters),
    alias($.bare_parameters, $.lambda_parameters),
  ))),
  field('body', choice($.block, $.do_block)),
),

ruby_grammar.js中的语法规则,以"->"起始,然后包括parametersbody两个字段。

my_lambda = ->(x) { x * 2 }
puts my_lambda.call(5)  # 输出:10

lambda_declaration也需要处理为函数声明method_decl指令。

  • method_declaration一样,attrdata_type依旧为:""。 因为是匿名函数,所以需要一些处理转为有名函数。调用tmp_method()生成函数名tmp_method作为name
def lambda_declaration(self, node, statements):
    tmp_method = self.tmp_method()
  • parameters相关信息包含在名为parameters的子节点中,同method_declaration中的处理一样,parameters应为一个列表,依次遍历其下的所有子节点,对每个子节点的类型进行判断,若是普通的identifier,则将子节点内容读取出来加入new_parameters变量中。若是带默认值的参数,则要进一步读取名为namevalue的子节点,name子节点的内容读取出来加入new_parameters中,并根据value生成assign_stmt语句并保存在init中。
def lambda_declaration(self, node, statements):
    # ...
    parameters = self.find_child_by_field(node, "parameters")
    new_parameters = []
    init = []
    if parameters:
        # need to deal with parameters
        for parameter in parameters.named_children:
            if parameter.type == 'identifier':
                shadow_parameter = self.read_node_text(parameter)
                new_parameters.append(shadow_parameter)
            elif parameter.type == 'optional_parameter':
                parameter_name = self.find_child_by_field(parameter, 'name')
                shadow_parameter = self.read_node_text(parameter_name)
                new_parameters.append(shadow_parameter)
                new_init = []
                value = self.find_child_by_field(parameter, 'value')
                shadow_value = self.parse(value, new_init)
                new_init.append({'assign_stmt': {'target': shadow_parameter, 'operand': shadow_value}})
                init.extend(new_init)

    
  • body部分与method_declaration一样,只需在node下找到名为body的子节点,如果节点存在,则依次遍历它之下的子节点,如果是注释则跳过,否则调用parser函数解析,所有生成的语句加入new_body中。
def lambda_declaration(self, node, statements):
    # ...
    new_body = []
    child = self.find_child_by_field(node, "body")
    if child:
        for stmt in child.named_children:
            if self.is_comment(stmt):
                continue
            self.parse(stmt, new_body)

最后,使用通过以上操作获得的tmp_methodnew_parametersinitnew_body生成method_decl语句并加入statements中。

statements.append(
    {"method_decl": {"attr": "", "data_type": "", "name": tmp_method,
                     "parameters": new_parameters, "init": init, "body": new_body}})

(3) class_declaration

对于class节点,ruby_parser.py 里的 class_declaration 函数来处理类的声明。用于解析表示类声明的树节点,并将这些信息转化为一个特定的字典,然后将这个字典加入到 statements 列表中。

def class_declaration(self, node, statements):
        glang_node = defaultdict(list)

        name = self.find_child_by_field(node, 'name')
        shadow_name = self.parse(name)  

        glang_node['name'] = shadow_name

        superclass = self.find_child_by_field(node, 'superclass')
        if superclass:
            superclass = superclass.children[1]
            shadow_superclass = self.read_node_text(superclass)
            glang_node['supers'].append(shadow_superclass)

        body = self.find_child_by_field(node, 'body')

        for child in body.named_children:
            if child.type == 'method':
                method = []
                self.method_declaration(child, method)
                glang_node['member_methods'].append(method.pop())
            if child.type == 'assignment':
                left = self.find_child_by_field(child, 'left')

                right = self.find_child_by_field(child, 'right')
                shadow_right = self.parse(right)

                if left.type == 'instance_variable':
                    shadow_left = self.read_node_text(left)[1:]
                    glang_node['fields'].append({ 'variable_decl': { 'name': shadow_left }}) 
                    glang_node['init'].append({ 'field_write': { 'receiver_object': self.global_self(), 'field': shadow_left, 'source': shadow_right }})
                elif left.type == 'class_variable':
                    shadow_left = self.read_node_text(left)[2:]
                    glang_node['static_init'].append({ 'assign_stmt': { 'target': shadow_left, 'operand': shadow_right }})

        statements.append({ 'class_decl': dict(glang_node) })

首先,初始化一个 defaultdict,其代表Glang IR的节点,里面包含了类声明的信息。接下来我们从 node 中找到表示类名的子节点。使用 parse 方法解析这个子节点,获取类名的字符串表示,并存储到 glang_nodename 键中。接着从 node 中找到表示父类的子节点。如果找到了父类节点,获取其文本表示并添加到 glang_nodesupers 列表中。接下来从 node 中找到表示类体的子节点,遍历 body 的每个命名子节点。如果子节点是一个方法声明,调用 method_declaration 方法处理这个方法,并将其结果加入到 glang_node 的 'member_methods' 列表中。如果子节点是一个赋值操作,从中提取左赋值表达式和右赋值表达式,并解析右赋值表达式。

如果是左赋值表达式是实例变量,去掉前缀提取其名字,并将其加入到 glang_nodefields 列表中。同时,将初始化操作加入到 init 列表中。如果左赋值表达式是类变量,去掉前缀提取其名字,并将其赋值操作加入到 glang_nodestatic_init 列表中。

最后,将 glang_node 转换为一个普通字典,并以 class_decl 为键,将其添加到 statements 列表中。

(4) singleton_class_declaration

对于singleton_class节点,使用singleton_class_declaration 函数来处理。用于处理单例类的声明。

def singleton_class_declaration(self, node, statements):
        self.class_declaration(node, statements)
        statements[-1]['class_decl']['attr'] = ['singleton']

singleton_class的语法和普通类是一样的,只是在这个类声明的字典中,添加一个 attr 键,并将其值设为包含字符串 singleton 的列表。

(5) module_declaration

对于module节点,使用module_declaration 函数来处理。用于解析表示模块声明的树节点,并将这些信息转化为一个特定的字典,然后将这个表示形式加入到 statements 列表中。

def module_declaration(self, node, statements):
        body = self.find_child_by_field(node, 'body')
        new_body = []
        self.parse(body, new_body)
        statements.append({ 'namespace_decl': { 'body': new_body }})

首先使用 find_child_by_field 方法到表示模块体的子节点 body。接着初始化一个空列表 new_body,用于存储解析后的模块体内容。调用 parse 方法解析 body 节点,并将解析后的结果存储到 new_body 列表中。

最后,将一个包含 namespace_decl 键的字典添加到 statements 列表中。namespace_decl 键对应的值是一个字典,其中包含 body 键,body 键的值是解析后的 new_body 列表。

ruby2glang_ir's People

Contributors

nyycs avatar jinnywong avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.