require 'lib/mapper' require 'lib/abnf_file' require 'lib/semantic_functions' require 'lib/semantic_edges' module Semantic # The semantically extended Abnf::File. # # This class adds the semantic attributes and semantic functions to the syntactic description of the grammar. # The semantic functions for context free grammars are described in: # http://cms.dc.uba.ar/materias/tl/2009/c2/practicas/Knuth-1968-SemanticsCFL.pdf # # Semantic functions are added to the tree (triggered) by the expansion of a nonterminal node. # Attributes and functions are defined in the YAML file (see AttributeGrammar#semantic= ). # class AttributeGrammar < Abnf::File # Load the semantic file. For the description see Functions#initialize def semantic= filename @semantic_functions = Functions.new( IO.read( filename ) ) end # The instance of the Function class (all parsed semantic functions). attr_reader :semantic_functions end # This mapper class implements the _modified_ version of the attribute grammar described in: # http://www.cs.bham.ac.uk/~wbl/biblio/gecco2004/WGEW003.pdf and # http://ncra.ucd.ie/downloads/pub/thesisExtGEwithAGs-CRC.pdf # # If there is the boolean p._valid attribute defined for the nonterminal symbol expansion, # the expansion is considered only if the value of such attribute is 'true'. # # The implementation differs from the original thesis in this aspect: # There is no "rollback phase" of semantic attributes processing when the invalid node # expansion is reached (ie node._valid==false). Invalid expansions are simply ignored beforehand, # no codons are wasted (in another words there are no introns due to the semantic restrictions # present in the genotype string. # # See the AttributeGrammar class for detailed description of the semantic YAML file. # See the Mapper::DepthFirst class for detailed description of the mapper behavior. # class AttrGrDepthFirst < Mapper::DepthFirst # See Mapper::DepthFirst#new def initialize( grammar ) super grammar @functions = grammar.semantic_functions clear end # Semantic attributes hash. It maps AttrKey identification to the attribute values. # Mainly for debugging and logging purposes. attr_reader :attributes # See Mapper::DepthFirst#phenotype def phenotype genome clear super( genome ) end # See Mapper::DepthFirst#generate def generate( recursivity, required_depth ) clear super( recursivity, required_depth ) end protected def clear @edges = Edges.new @attributes = {} end def pick_expansions( parent_token, genome ) rules = super( parent_token, genome ) allowed = [] rules.each do |expansion| edges = @functions.node_expansion( parent_token, expansion ).map do |attr_fn| AttrEdge.create( parent_token, expansion, attr_fn ) end # process all edges for the _valid attribute new_attrs = Edges.reduce_batch( edges, @attributes ) next if found_invalid? new_attrs allowed << expansion end raise "AttrGrDepthFirst: all possible expansions semantically restricted" if allowed.empty? allowed end def filter_expansions_by_depth( rule, allowed_depth ) rule end def use_expansion( parent_token, alt ) expansion = super( parent_token, alt ) edges = @functions.node_expansion( parent_token, expansion ).map do |attr_fn| AttrEdge.create( parent_token, expansion, attr_fn ) end # process the current edges first new_attrs1 = Edges.reduce_batch( edges, @attributes ) @edges.concat edges @attributes.update new_attrs1 # process older edges with joined_attributes new_attrs2 = @edges.reduce_batch( @attributes ) @attributes.update new_attrs2 expansion end def found_invalid? attrs attrs.each_pair do |key,attr| next unless key.attr_idx == AttrIndexValid return true if attr == false end false end end end