搞定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, 不过对于一个想象中的类,还是不要对它要求过多。
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, 不过对于一个想象中的类,还是不要对它要求过多。