How To Build a Yacc?(8)

搞定lex后,很显然,我们要将它加入到Compiler中。

class Compiler
  def initialize(rule_file, src_file)
    @lex = ExtendLex.new(src_file)
  end

   def run
       return true
   end

end

要想在run里面真正的干点事,就需要一个shift-reduction算法来识别src_file中的符号流是否能符合rule_file
中所定义的规则。

我们目前只有@lex, 从它那儿我们只能得到符号流,要进行shift-reduction分析,我们需要从rule_file生成DFA,这一点才是关键。为了达到这个目的,得重新写一个类来完成这个功能。

根据这个类的功能,一个紧迫的工作是定义规则文件的格式,以function_decl文法为例:

##### File: ican.y  ###############

%%
%token function id
%token ; , = ( )
%%
nil := function_decl :
function_decl := function function_name ( argument_list ) ; :
function_name := id : p @lex.get_token_string(-1)
argument_list := argument_list , id : p @lex.get_token_string(-1)
argument_list := id :    p @lex.get_token_string(-1)

以'%%'为分割符,第1个'%%'后面是terminal定义,第2个‘%%’后面定义的是rule, rule的写法就是普通的BNF表达式,后面跟着一个:引出的action表达式,目前我们只执行ruby表达式。这里有几个特定约束:每个NONTERMINAL最终总能推出TERMINAL序列。开始符号由nil := Start_Symbol来定义。

好了,假设我们已经有了一个Yacc类,它所完成的工作就是读入rule_file生成DFA,我们该如何使用(测试)它?

#### test.rb
require 'rubyunit'

class TestCompiler < Test::Unit::TestCase 
    def create_rule_file
        File.open("rulefile","w") do |file|
      file.puts "%%/n%token function id/n%token ; , = ( )/n"
      file.puts "%%/nnil := function_decl : /n"
      file.puts "function_decl := function function_name ( argument_list ) ; : /n"
      file.puts "function_name := id : /n"
      file.puts "argument_list := argument_list , id : /n"
      file.puts "argument_list := id :"
    end   
  end

    def test_yacc
        create_rule_file
        yacc = Yacc.new("rulefile")
        yacc.generate
       assert(yacc.state[0].size == 2)
    end
end

在我们上面所定义的rulefile中,DFA的state[0](开始状态)应该是2个item:
item1:[nil = # function_decl]
item2:[function_decl = # function function_name ( argument_list ) ;]

当然我们可以编写更多的assert, 不过对于一个想象中的类,还是不要对它要求过多。

阅读更多
个人分类: 瞎编
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭