#!/usr/bin/ruby require 'stringio' keywords_str =<<__KEYWORDS__ abs access after alias all and arch architecture array assert attr attribute begin block body buffer bus case comp component cond conditional conf configuration cons constant disconnect downto else elseif elsif end entity exit file for func function generic generate group guarded if impure in inertial inout inst instance is label library linkage literal loop map mod nand new next nor not null of on open or others out pack package port postponed procedure process pure range record register reject rem report return rol ror select severity shared sig signal sla sll sra srl subtype then to transport type unaffected units until use var variable wait when while with xnor xor __KEYWORDS__ lexical_elements =<<__L_ELEMENTS__ BASIC_IDENTIFIER EXTENDED_IDENTIFIER BASED_LITERAL DECIMAL_LITERAL CHARACTER_LITERAL STRING_LITERAL BIT_STRING_LITERAL __L_ELEMENTS__ keywords = [] require "stringio" StringIO::new(keywords_str).each_line{|line| keywords << line.chop! } StringIO::new(lexical_elements).each_line{|line| keywords << line.chop!.downcase! } #p keywords #tokenに切り出し tokens = [] if ARGV.size < 1 then exit -1 end tokens = open(ARGV.shift).read.split(/\s+/) #p tokens # rule単位に rules = {} previous_def_index = nil tokens.each_with_index{|token, i| if token == '::=' then if previous_def_index then rules[tokens[previous_def_index - 1]] = tokens[(previous_def_index + 1)..(i - 2)] end previous_def_index = i end } if previous_def_index then rules[tokens[previous_def_index - 1]] = tokens[(previous_def_index + 1)..-1] end #p rules.sort{|a, b| a <=> b} # keywordの置換 rule_names = [] rules.each{|rule_name, rule| rule.collect!{|item| if keywords.include?(item) then item.upcase elsif item == '\'' then '\'\\\'\'' elsif item == '"' then '\'\\"\'' elsif item == '\\' then '\'\\\\\\\\\'' elsif item =~ /^[^\w\s|\[\]\{\}]+$/ then '\'' + $& + '\'' else rule_names << item item end } } # ORの処理 rules.each{|rule_name, rule| splitted = [[]] rule.each{|item| if item == '|' then splitted << [] else splitted.last << item end } rules[rule_name] = splitted } #p rules.sort{|a, b| a <=> b} # lexical_elements定義の削除、これらはスキャナ側で対応する # 同時に下位ルールについても削除を行う StringIO::new(lexical_elements).each_line{|line| deleted = rules.delete(line.chop!.downcase!) || [] deleted.each{|exp| exp.each{|item| rules.delete(item)} } } loop_count = 0 # brace(省略可能かつ繰り返し)の処理 while true additional_rules = {} rules.each{|rule_name, rule| rule.each{|exp| #p exp # '}'を探す to_index = exp.index('}') next unless to_index # 対応する'{'を探す next unless (from_index = exp[0..to_index].rindex('{')) additional_rule_name = "#{rule_name}_loop#{loop_count}" additional_rule = [] additional_rule << '[' additional_rule.push(*exp[(from_index + 1)..(to_index - 1)]) additional_rule << additional_rule_name additional_rule << ']' additional_rules[additional_rule_name] = [additional_rule] (to_index - from_index + 1).times{|i| exp.delete_at(from_index)} exp.insert(from_index, additional_rule_name) break } } if additional_rules.empty? then break else loop_count += 1 additional_rules.each{|rule_name, rule| rules[rule_name] = rule } #p rules end end #p rules.sort{|a, b| a <=> b} # blacket(省略可能)の処理 while true modified_rules = [] rules.each{|rule_name, rule| rule.each_with_index{|exp, exp_index| #p rule # ']'をうしろから探す to_index = exp.rindex(']') next unless to_index # 対応する'['を探す from_index = nil index = to_index - 1 nest_level = 0 exp[0..(to_index - 1)].reverse_each{|item| if item == '[' then if nest_level == 0 then from_index = index else nest_level -= 1 end elsif item == ']' then nest_level += 1 end index -= 1 } next unless from_index modified_rules << rule_name #p rule[to_index + 1] #p "before:", rule # remove '[', ']' exp.delete_at(to_index) exp.delete_at(from_index) to_index -= 2 exclusive_range = (from_index..to_index) copy_vals = [] exp.each_with_index{|item, i| copy_vals << item unless exclusive_range.include?(i) } #p copy_vals rule.insert(exp_index + 1, copy_vals) #p "after:", rule break } } if modified_rules.empty? then break else loop_count += 1 end end #p rules.sort{|a, b| a <=> b} # 未定義ルールを定義ルールへ可能ならばマップ target_rule_names = rule_names.uniq - rules.keys - ['{', '}', '[', ']', '|'] rule_names_map = {} loop_depth = 1 =begin while true loop_more = false while target_rule_names.reject!{|name| target_str = name loop_depth.times{|i| target_str =~ /_(.+)$/ target_str = $1 } if !target_str then nil else loop_more = true if rules.include?(target_str) or rule_names_map.include?(target_str) then rule_names_map[name] = target_str else nil end end } end break unless loop_more loop_depth += 1 end #p rule_names_map #p target_rule_names rule_names_map.each{|rule_name, mapped| rules[rule_name] = [[mapped]] } =end rules["formal_port_clause"] = [["port_clause"]] rules["port_interface_list"] = [["interface_list"]] rules["type_name"] = [["name"]] rules["entity_simple_name"] = [["name"]] rules["entity_name"] = [["name"]] rules["local_generic_clause"] = [["generic_clause"]] rules["local_port_clause"] = [["port_clause"]] rules["generic_interface_list"] = [["interface_list"]] rules["static_expression"] = [["expression"]] rules["attribute_simple_name"] = [["simple_name"]] rules["instantiation_label"] = [["label"]] rules["component_name"] = [["name"]] rules["generic_association_list"] = [["association_list"]] rules["generic_name"] = [["name"]] rules["port_name"] = [["name"]] rules["parameter_name"] = [["name"]] rules["port_association_list"] = [["association_list"]] # 重複ルールの削除 while true unique_rules = {} redundant_rules = [] # ルール内での重複を圧縮 rules.each{|rule_name, rule| rule.uniq!} # 全体で重複を検索 rules.each{|rule_name, rule| if exist_rule_name = unique_rules.index(rule) then #rules[rule_name] = [[exist_rule_name]] rules.values.each{|rule| rule.each{|exp| exp.collect!{|item| (item == rule_name) ? exist_rule_name : item } } } redundant_rules << rule_name else unique_rules[rule_name] = rule end } break if redundant_rules.empty? redundant_rules.each{|rule_name| rules.delete(rule_name) } end # pretty_print puts <<-__HEADER__ class VHDLParser rule vhdl : design_file __HEADER__ puts rules.sort{|a, b| a <=> b}.collect{|rule_name, rule| text = StringIO::new text.puts("#{rule_name} : ") rule.each_with_index{|exp, i| text.puts " #{'| ' if i > 0}" + exp.join(' ') + "#{exp.empty? ? '' : ' '}" } text.puts text.string }.join puts <<-'__FOOTER__' end ---- header require 'strscan' require 'stringio' ---- inner UPPERCASE_LETTERS = 'A-Z' DIGITS = '0-9' SPECIAL_CHARS = '"\s#&\'\(\)*+,-\.\/\:\;<=>\[\]_|' SPACE_CHARS = ' ' LOWERCASE_LETTERS = 'a-z' OTHER_SPECIAL_CHARS = '!$%@?\\^`{}~' FORMAT_EFFECTOR = '\t\x0B\r\n\x0C' BASIC_GRAPHIC_CHAR = "#{UPPERCASE_LETTERS}#{DIGITS}#{SPECIAL_CHARS}#{SPACE_CHARS}" GRAPHIC_CHARS = "#{BASIC_GRAPHIC_CHAR}#{LOWERCASE_LETTERS}#{OTHER_SPECIAL_CHARS}" BASIC_CHARS = "#{BASIC_GRAPHIC_CHAR}#{FORMAT_EFFECTOR}" BASIC_IDENT_RE = /[A-Za-z][A-Za-z0-9_]*/ EXTENDED_IDENT_RE = /\\([#{GRAPHIC_CHARS}]+)\\/ INTEGER = '[0-9][0-9_]*' EXPONENT = 'E[+-]?[0-9][0-9_]*' BASED_INTEGER = '[0-9A-Za-z][0-9A-Za-z_]*' BIT_VALUE = '[0-9A-Za-z][0-9A-Za-z_]*' DECIMAL_LITERAL_RE = /#{INTEGER}(?:\.#{INTEGER})?(?:#{EXPONENT})?/ BASED_LITERAL_RE = /#{INTEGER}#(?:#{BASED_INTEGER})(?:\.#{BASED_INTEGER})?#(?:#{EXPONENT})?/ CHAR_LITERAL_RE = /'([#{GRAPHIC_CHARS}])'/ STRING_LITERAL_RE = /"([#{GRAPHIC_CHARS}]*)"/ BIT_STRING_LITERAL_RE = /[BOX]"#{BIT_VALUE}"/ COMMENT_RE = /--.*[\r\n]*/ DELIMITER = '=>|\*\*|:=|\/=|>=|<=|<>|[&\'()*+,-.\/\:;<=>|\[\]]' DELIMITER_RE = /(?:#{DELIMITER})/ def parse(str) @yydebug = true s = StringScanner.new(str) def s.collect_token tokens = [] until eos? token = yield(self) tokens << token if token end tokens end @tokens = s.collect_token{|scanner| case when scanner.skip(/\s+/) # ignore when scanner.skip(COMMENT_RE) # ignore when scanner.scan(EXTENDED_IDENT_RE) [:EXTENDED_IDENTIFIER, scanner[1]] when scanner.scan(BASED_LITERAL_RE) [:BASED_LITERAL, scanner[0]] when scanner.scan(DECIMAL_LITERAL_RE) [:DECIMAL_LITERAL, scanner[0]] when scanner.scan(CHAR_LITERAL_RE) [:CHARACTER_LITERAL, scanner[1]] when scanner.scan(STRING_LITERAL_RE) [:STRING_LITERAL, scanner[1]] when scanner.scan(BIT_STRING_LITERAL_RE) [:BIT_STRING_LITERAL, scanner[0]] when scanner.scan(DELIMITER_RE) [scanner[0], scanner[0]] else before = scanner.pos scanner.check_until(/\s|#{DELIMITER}/) v = scanner.pre_match[before..-1] scanner.pos += v.size case v __FOOTER__ StringIO::new(keywords_str).each_line{|line| line.chop! puts <<__FOOTER__ when /^#{line}$/i then [:#{line.upcase}, "#{line}"] __FOOTER__ } puts <<-'__FOOTER__' else [:BASIC_IDENTIFIER, v] end end } do_parse end def next_token @tokens.shift end ---- footer parser = VHDLParser::new begin parser.parse(open(ARGV.shift).read) rescue ParseError puts $! end __FOOTER__