#~/usr/bin/ruby # # connector.rb - this file contains the Connector class. This describes the # behavior of an un-linked link, as they exist in the Definition of a word. # # == Synopsis # # # == Rcsid # # $Id: connector.rb,v 1.5 2003/05/18 19:26:06 stillflame Exp $ # # == Authors # # Martin Chase # #:include: COPYRIGHT # #--- # # Please see the file COPYRIGHT for licensing details. # class LinkParser class Connector LEFT = 'l' # left side RIGHT = 'r' # right side BOTH = '*' # both sides n = 1.0/0.0 ONCE = 1..1 # matches once OPTIONAL = 0..1 # matches zero or one times PLUS = 1..n # matches one or more times STAR = 0..n # matches zero or more times # The names 'star' and 'plus' are taken from regexen. @@connectors = [] # Creates and returns a connector. Creation only happens once for each # unique set of arguments. # name - major name of the connector ('A','AA','P*',&c.) # subName - minor name of the connector ('s' or 'p*x' from 'MVs' or # 'MVp*x', respectively) # side - which side the connector accepts from # multiplicity - the number of connections required to satisfy this # connector. def Connector.new( name, subName='', side=BOTH, multiplicity=ONCE, cost=0 ) subName = '' unless subName connector = super(name,subName,side,multiplicity,cost) if(already = (@@connectors.find {|c| c.eql? connector})) return already else @@connectors << connector return connector end end # Initializes a new Connector object. def initialize( name, subName, side, multiplicity, cost ) @name = name @subName = subName @side = side @multiplicity = multiplicity @cost = cost end # The name of the connector. attr_reader :name # The sub name of the connector, if any. attr_reader :subName # The linking cost of the connector. attr_reader :cost # How many connections it takes to satisfy this connector. attr_reader :multiplicity # Whether or not the connector is optional. def optional? @multiplicity === 0 end alias :optional :optional? # Whether or not the connector accepts multiple connections. def multiple? @multiplicity === 2 end # Which side the connector links to (LEFT, RIGHT or BOTH). attr_reader :side # Whether or not the connector links to the LEFT. def left? @side == LEFT or @side == BOTH end # Whether or not the connector links to the RIGHT. def right? @side == RIGHT or @side == BOTH end # Whether or not the connector links to both sides def both? @side == BOTH end # Returns self def dup self end alias :clone :dup # Returns a simple stringy representation def to_s result = @name + @subName result = "@" + result if self.multiple? result += (@side == LEFT ? "-" : "+") unless @side == BOTH result += "(#{@cost})" if self.cost.nonzero? result = "{" + result + "}" if self.optional? return result end alias :inspect :to_s # Checks to see if the two connectors can match. rules for matching: # 1: a '*' matches any character # 2: a '^' matches only the '*' character # 3: a 'a' matches either a 'a' or a '*' (for any character 'a-Z') # 4: a Connector is considered to be followed by an infinite number of # '*'s def match(other) return false unless (@side != other.side || @side == BOTH) i = 0 while( i <= @name.length && i <= other.name.length ) return( false ) unless ( @name[i] == '*'[0] || other.name[i] == '*'[0] || ( (@name[i] == other.name[i]) && (@name[i] != '^'[0]) ) || (@name[i].nil? || other.name[i].nil?) ) i += 1 end i = 0 while( i <= @subName.length && i <= other.subName.length ) return( false ) unless ( @subName[i] == '*'[0] || other.subName[i] == '*'[0] || ( (@subName[i] == other.subName[i]) && (@subName[i] != '^'[0]) ) || (@subName[i].nil? || other.subName[i].nil? ) ) i += 1 end return true end # Generalizes this connector with the given other connector in a # position-based comparison according to the following rules: # Names are compared separately from subNames. # Matching characters: left the same. # '*' and any character: becomes a '*'. # '^' matches only the '*' character # Non-matching characters: turned into a '*'. def generalize(other) name = @name subName = @subName i = 0 while( i < name.length && i < other.name.length ) name[i] = '*' if name[i] != other.name[i] or name[i,1] == '*' or other.name[i,1] == '*' i += 1 end if other.subName.empty? subName = '*' * subName.length elsif subName.empty? subName = '*' * other.subName.length else i = 0 while( i < subName.length && i < other.subName.length ) subName[i] = '*' unless subName[i] == other.subName[i] i += 1 end end return [name,subName] end # Tests for equality by value. def eql?(other) @name == other.name and @subName == other.subName and @cost == other.cost and @multiplicity == other.multiplicity and @side == other.side end alias :equal? :eql?; alias :== :eql?; end # class Connector end # module LinkParser