Class: LinkParser::Definition

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/linkparser/definition.rb

Constant Summary collapse

Token =

Constants for definition parsing.

/\s+|@?[A-Z*^]+[a-z^*]*[+-]|\(|\[|\{|\)|\]|\}|[Aa][Nn][Dd]|&|[Oo][Rr]|\|/
Connect =
/(@)?([A-Z*^]+[a-z^*]*)([+-])/
Conjunction =
/(and)|(&)|(or)|(\|)/i
Open =
/\(|\[|\{/
Close =
/\)|\]|\}/
Space =
/\s+/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(an_arg) ⇒ Definition

The single argument may be either a String or an Array. If the argument is a string, it is treated as a definition string, and won’t be parsed until needed. If it is an array, no reverse engineering will be done to create the corresponding definition string.



151
152
153
154
155
156
157
158
159
# File 'lib/linkparser/definition.rb', line 151

def initialize( an_arg )
	if an_arg.kind_of? String
		@raw = an_arg
		@connector_sets = []
	else
		@raw = nil
		@connector_sets = an_arg
	end
end

Instance Attribute Details

#rawObject (readonly)

The raw string from which this definition is derived.



162
163
164
# File 'lib/linkparser/definition.rb', line 162

def raw
  @raw
end

Class Method Details

.re_satisfy(definition) ⇒ Object

The main body of satisfy,



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/linkparser/definition.rb', line 55

def Definition.re_satisfy(definition)
	definition	= StringScanner.new( definition, false ) unless
		definition.kind_of?(StringScanner)
	results = []
	side = nil
	current = nil
	multi = false
	
	while definition.rest?
		case current = definition.scan(Token)
		when Connect
			# Connectors - puts the connector alone in a set, and
			# puts that into the results.
			connector_m = Connect.match( current )
			side = connector_m[3] =~ /-/ ? Connector::LEFT : Connector::RIGHT
			multi = connector_m[1] ? Connector::PLUS : Connector::ONCE
			name_scan = StringScanner::new(connector_m[2])
			# Slurp in all the characters up to the last capitol
			# letter and up to one star or caret.
			name = name_scan.scan(/.*[A-Z][*^]?/)
			sub_name = name_scan.rest or ''
#					Log.debug( "Found connector '%s'" % current )
			results << [ Connector::new(name, sub_name, side, multi) ]
		when Conjunction
			# Conjunctions (and, or) - combine the results so far with the
			# results of what would be the rest of the parse, as per the
			# rules of the conjunction used.
			conj_m = Conjunction.match( current )
			raise ParseError, "conjunction with nothing preceeding it: '#{definition.string}'" if
				results.empty?
			if conj_m[1] or conj_m[2]
				# and, & - the new results will be every combination of the
				# previous results and the results of the rest of the parse.
				rest = re_satisfy(definition)
				raise ParseError, "conjunction with nothing following: '#{definition.string}'" if
					rest.empty?
				results.combine!(rest)
			else
				# or, | - the new results will be the union of the previous
				# results and the results of the rest of the parse.
				rest = re_satisfy(definition)
				raise ParseError, "conjunction with nothing following: '#{definition.string}'" if
					rest.empty?
				results.add!(rest)
			end
			return results
		when Open
			# Open parens - treated the same as a single connector, but
			# concerning the entire contents of the parens.
			if current =~ /\[/
				re_satisfy( definition ).each {|set| 
					set.map! {|conn|
						Connector.new(conn.name,conn.subName,conn.side,conn.multiplicity,conn.cost+1)
					}
					results << set
				}
			elsif current =~ /\{/
				re_satisfy( definition ).each {|set|
					set.map! {|conn|
						if conn.multiplicity == Connector::ONCE
							Connector.new(conn.name,conn.subName,conn.side,Connector::OPTIONAL,conn.cost)
						elsif conn.multiplicity == Connector::PLUS
							Connector.new(conn.name,conn.subName,conn.side,Connector::STAR,conn.cost)
						else
							conn
						end
					}
					results << set
				}
			else
				results.add!(re_satisfy( definition ))
			end
			raise ParseError, "improper parentheses usage: '#{definition.string}'" if
				results.empty?
		when Close
			# Close parens - if this is found, the method returns, checking
			# for the null-connector condition first.
			return results.empty? ? [ [Connector::new("*")] ] : results
		when Space
			# Spaces - are ignored
		when nil
			# Nil - means the parse has encountered something not matching
			# the Token regex.
			raise ParseError, "Definition unparsable before end of definition: '#{definition.rest}'"
		end
	end
	
	return results
end

.satisfy(definition) ⇒ Object

Takes a definition string and returns a Definition object populated with the set of sets of connectors that will satisfy that the provided string. args: definition - the string to be parsed for meaning, or a StringScanner on that string, possibly in mid-parse



50
51
52
# File 'lib/linkparser/definition.rb', line 50

def Definition.satisfy( definition )
	Definition::new(Definition::re_satisfy(definition))
end

Instance Method Details

#==(other) ⇒ Object Also known as: ===, eql?

tests for equality



195
196
197
198
199
200
201
# File 'lib/linkparser/definition.rb', line 195

def ==(other)
		return(false) unless
			other.kind_of?(self.class)
		return(@raw == other.raw) if
			(@raw && other.raw) and (@raw == other.raw)
	return self.connector_sets.set_of_sets_equal?(other.connector_sets)
end

#connector_setsObject Also known as: connectorSets

The sets of sets of connectors that is the algorithmicly useful version of this object.



166
167
168
169
170
171
172
# File 'lib/linkparser/definition.rb', line 166

def connector_sets 
	if @connector_sets and @connector_sets.empty?
		@connector_sets = Definition::re_satisfy(@raw)
	else
		@connector_sets
	end
end

#includes_match?(connector) ⇒ Boolean

Returns whether or not there exists a matching connector to a given connector.

Returns:

  • (Boolean)


216
217
218
219
220
221
222
# File 'lib/linkparser/definition.rb', line 216

def includes_match?(connector)
	self.find {|set|
		set.find {|c|
			c.match(connector)
		}
	}
end

#inspectObject

Stringification with a ‘sensible’ opposite of the one for to_s.



190
191
192
# File 'lib/linkparser/definition.rb', line 190

def inspect 
	(@connector_sets && @connector_sets.empty?) ? @raw : @connector_sets.inspect
end

#sets_matching(connector) ⇒ Object

Returns those connector_sets which include a connector which matches the provided connector.



207
208
209
210
211
212
213
# File 'lib/linkparser/definition.rb', line 207

def sets_matching(connector)
	self.find_all {|set|
		set.find {|c|
			c.match(connector)
		}
	}
end

#to_connectorsObject Also known as: two_s

Stringification of the connector sets.



184
185
186
# File 'lib/linkparser/definition.rb', line 184

def to_connectors 
	self.connector_sets.inspect
end

#to_sObject

The most sensible stringification of this definition.



179
180
181
# File 'lib/linkparser/definition.rb', line 179

def to_s 
	@raw ? @raw : self.to_connectors
end