sxp 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1 +1,2 @@
1
1
  * Arto Bendiken <arto@bendiken.net>
2
+ * Gregg Kellogg <gregg@kellogg-assoc.com>
data/CREDITS CHANGED
@@ -1,2 +1 @@
1
1
  * Ben Lavender <blavender@gmail.com>
2
- * Gregg Kellogg <gregg@kellogg-assoc.com>
data/README CHANGED
@@ -10,7 +10,7 @@ This is a Ruby implementation of a universal [S-expression][] parser.
10
10
  * Parses S-expressions in universal, [Scheme][], [Common Lisp][], or
11
11
  [SPARQL][] syntax.
12
12
  * Adds a `#to_sxp` method to Ruby objects.
13
- * Compatible with Ruby 1.8.7+, Ruby 1.9.x, and JRuby 1.4/1.5.
13
+ * Compatible with Ruby 1.8.7+, Ruby 1.9.x, Ruby 2.0.x, and JRuby 1.4/1.5.
14
14
 
15
15
  ##Examples
16
16
 
@@ -75,7 +75,7 @@ Dependencies
75
75
  ------------
76
76
 
77
77
  * [Ruby](http://ruby-lang.org/) (>= 1.8.7) or (>= 1.8.1 with [Backports][])
78
- * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.2.0), only needed for SPARQL
78
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 1.0.0), only needed for SPARQL
79
79
  S-expressions
80
80
 
81
81
  Installation
@@ -112,12 +112,12 @@ Author
112
112
  ------
113
113
 
114
114
  * [Arto Bendiken](https://github.com/bendiken) - <http://ar.to/>
115
+ * [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
115
116
 
116
117
  Contributors
117
118
  ------------
118
119
 
119
120
  * [Ben Lavender](https://github.com/bhuga) - <http://bhuga.net/>
120
- * [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
121
121
 
122
122
  License
123
123
  -------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.2
@@ -1,5 +1,3 @@
1
- require 'rdf'
2
-
3
1
  ##
4
2
  # Extensions for Ruby's `Symbol` class.
5
3
  class Symbol
@@ -12,11 +10,17 @@ class Symbol
12
10
  end
13
11
  end
14
12
 
15
- ##
16
- # Extensions for RDF::URI
17
- class RDF::URI
18
- # Original lexical value of this URI to allow for round-trip serialization.
19
- def lexical=(value); @lexical = value; end
20
- def lexical; @lexical; end
21
- end
13
+ # Update RDF::URI if RDF is loaded
14
+ begin
15
+ require 'rdf'
22
16
 
17
+ ##
18
+ # Extensions for RDF::URI
19
+ class RDF::URI
20
+ # Original lexical value of this URI to allow for round-trip serialization.
21
+ def lexical=(value); @lexical = value; end
22
+ def lexical; @lexical; end
23
+ end
24
+ rescue LoadError
25
+ # Ignore
26
+ end
@@ -8,7 +8,7 @@ module SXP
8
8
  # A basic block containing constituent
9
9
  # objects, either blocks or strings.
10
10
  class Block
11
- BLOCK_MIN_LENGTH = 40
11
+ BLOCK_MIN_LENGTH = 80
12
12
 
13
13
  # @attr [Integer] amount of indent applied to this block
14
14
  attr_reader :indent
@@ -55,27 +55,30 @@ module SXP
55
55
  end
56
56
 
57
57
  ##
58
- # Output block applying indent recursively
59
- # @param [#write] io
60
- def write(io)
58
+ # Format block
59
+ # @return [String]
60
+ def formatted
61
61
  # Output individual block elements on separate lines
62
+ buffer = ""
63
+
62
64
  if sxp? && length > BLOCK_MIN_LENGTH
63
- io.write(do_indent + '(')
65
+ buffer += do_indent + '('
64
66
  first, *elems = @elements
65
67
  unless first.sxp?
66
68
  # It's atomic, write out after paren
67
- io.puts(first.to_sxp)
69
+ buffer += first.to_sxp + "\n"
68
70
  else
69
- io.write("\n")
71
+ buffer += "\n"
70
72
  elems.unshift(first)
71
73
  end
72
74
  elems.each do |e|
73
- e.write(io)
75
+ buffer += e.formatted
74
76
  end
75
- io.puts(do_indent + ")\n")
77
+ buffer += do_indent + ")\n"
76
78
  else
77
- io.puts(do_indent + @elements.to_sxp)
79
+ buffer += do_indent + @elements.to_sxp + "\n"
78
80
  end
81
+ buffer
79
82
  end
80
83
 
81
84
  private
@@ -140,7 +143,26 @@ module SXP
140
143
  def render(sexp)
141
144
  block = Block.new(sexp, 0)
142
145
  if block.length > 40
143
- block.write(@output)
146
+ buffer = block.formatted
147
+
148
+ # Attempt to fold symbols and strings onto proceeding line
149
+ output = ""
150
+ prev_length = 0
151
+ buffer.lines.each do |line|
152
+ if (stripped = line.strip)[0,1] != '(' &&
153
+ prev_length + stripped.length + 1 < Block::BLOCK_MIN_LENGTH
154
+
155
+ # Append to previous line
156
+ start, match, rem = output.rpartition(/\S/)
157
+ output = start + match + " " + stripped + rem
158
+ prev_length += stripped.length + 1
159
+ else
160
+ # Terminate line and append this line
161
+ output += line
162
+ prev_length = line.length - 1
163
+ end
164
+ end
165
+ @output.write output.gsub(/\)\s+\)/, '))')
144
166
  else
145
167
  @output.puts(block.to_sxp)
146
168
  end
@@ -19,6 +19,7 @@ module SXP
19
19
  #
20
20
  # @param [String, #to_s] url
21
21
  # @param [Hash{Symbol => Object}] options
22
+ # See {#read}
22
23
  # @return [Enumerable<Object>]
23
24
  def self.read_url(url, options = {})
24
25
  require 'open-uri'
@@ -34,6 +35,7 @@ module SXP
34
35
  # @overload read_files(*filenames, options)
35
36
  # @param [Enumerable<String>] filenames
36
37
  # @param [Hash{Symbol => Object}] options
38
+ # See {#read}
37
39
  #
38
40
  # @return [Enumerable<Object>]
39
41
  def read_files(*filenames)
@@ -46,6 +48,7 @@ module SXP
46
48
  #
47
49
  # @param [String, #to_s] filename
48
50
  # @param [Hash{Symbol => Object}] options
51
+ # See {#read}
49
52
  # @return [Enumerable<Object>]
50
53
  def self.read_file(filename, options = {})
51
54
  File.open(filename.to_s, 'rb') { |io| read_all(io, options).first }
@@ -56,6 +59,7 @@ module SXP
56
59
  #
57
60
  # @param [IO, StringIO, String] input
58
61
  # @param [Hash{Symbol => Object}] options
62
+ # See {#read}
59
63
  # @return [Enumerable<Object>]
60
64
  def self.read_all(input, options = {})
61
65
  self.new(input, options).read_all
@@ -66,6 +70,7 @@ module SXP
66
70
  #
67
71
  # @param [IO, StringIO, String] input
68
72
  # @param [Hash{Symbol => Object}] options
73
+ # See {#read}
69
74
  # @return [Object]
70
75
  def self.read(input, options = {})
71
76
  self.new(input, options).read
@@ -118,6 +123,7 @@ module SXP
118
123
 
119
124
  ##
120
125
  # @param [Hash{Symbol => Object}] options
126
+ # See {#read}
121
127
  # @return [Array]
122
128
  def read_all(options = {})
123
129
  list = []
@@ -129,6 +135,13 @@ module SXP
129
135
 
130
136
  ##
131
137
  # @param [Hash{Symbol => Object}] options
138
+ # @option options [:throw] :eof
139
+ # If `:throw`, throw `:eof` on end of file.
140
+ # @option options [:throw] :eol
141
+ # If `:throw`, throw `:eol` on end of line.
142
+ # @option options [String] :list_term
143
+ # Expected list terminator; it's an error
144
+ # if another terminator is found
132
145
  # @return [Object]
133
146
  def read(options = {})
134
147
  skip_comments
@@ -138,10 +151,11 @@ module SXP
138
151
  throw :eof if options[:eof] == :throw
139
152
  raise EOF, "unexpected end of input"
140
153
  when :list
141
- if self.class.const_get(:LPARENS).include?(value)
142
- read_list
154
+ if ndx = self.class.const_get(:LPARENS).index(value)
155
+ list_term = self.class.const_get(:RPARENS)[ndx]
156
+ read_list(list_term)
143
157
  else
144
- throw :eol if options[:eol] == :throw # end of list
158
+ throw :eol if options[:eol] == :throw && value == options[:list_term]
145
159
  raise Error, "unexpected list terminator: ?#{value.chr}"
146
160
  end
147
161
  else value
@@ -151,11 +165,13 @@ module SXP
151
165
  alias_method :skip, :read
152
166
 
153
167
  ##
154
- # @param [Array]
155
- def read_list
168
+ # @param [String] list_term (nil)
169
+ # String expected to terminate the list
170
+ # @return [Array]
171
+ def read_list(list_term = nil)
156
172
  list = []
157
173
  catch (:eol) do
158
- list << read(:eol => :throw) while true
174
+ list << read(:eol => :throw, :list_term => list_term) while true
159
175
  end
160
176
  list
161
177
  end
@@ -258,6 +274,13 @@ module SXP
258
274
  char
259
275
  end
260
276
 
277
+ ##
278
+ # Unread the string, putting back into the input stream
279
+ # @param [String] string
280
+ def unread(string)
281
+ string.reverse.each_char {|c| @input.ungetc(c)}
282
+ end
283
+
261
284
  ##
262
285
  # @return [Boolean]
263
286
  def eof?
@@ -13,6 +13,8 @@ module SXP; class Reader
13
13
  INTEGER_BASE_16 = /^[+-]?[\da-z]+$/i
14
14
  RATIONAL = /^([+-]?\d+)\/(\d+)$/
15
15
 
16
+ # Escape characters, used in the form `#\Backspace`. Case is treated
17
+ # insensitively
16
18
  # @see http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node22.html
17
19
  CHARACTERS = {
18
20
  'newline' => "\n",
@@ -106,10 +108,21 @@ module SXP; class Reader
106
108
  end
107
109
 
108
110
  ##
111
+ # Read characters sequences like `#\backspace`. Otherwise,
112
+ # reads a single character. Requires the ability to put
113
+ # eroneously read characters back in the input stream
114
+ #
109
115
  # @return [String]
110
116
  # @see http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node22.html
111
117
  def read_character
112
- super
118
+ lit = read_literal
119
+
120
+ return " " if lit.empty? && peek_char == " "
121
+ CHARACTERS.fetch(lit.downcase) do |string|
122
+ # Return just the first character
123
+ unread(string[1..-1])
124
+ string[0,1]
125
+ end
113
126
  end
114
127
 
115
128
  ##
@@ -11,6 +11,14 @@ module SXP; class Reader
11
11
  INTEGER_BASE_16 = /^[+-]?[\da-z]+$/i
12
12
  RATIONAL = /^([+-]?\d+)\/(\d+)$/
13
13
 
14
+ # Escape characters, used in the form `#\newline`. Case is treated
15
+ # insensitively
16
+ # @see http://people.csail.mit.edu/jaffer/r4rs_9.html#SEC65
17
+ CHARACTERS = {
18
+ 'newline' => "\n",
19
+ 'space' => " ",
20
+ }
21
+
14
22
  ##
15
23
  # Initializes the reader.
16
24
  #
@@ -60,5 +68,23 @@ module SXP; class Reader
60
68
  else raise Error, "invalid sharp-sign read syntax: ##{char.chr}"
61
69
  end
62
70
  end
71
+
72
+ ##
73
+ # Read characters sequences like `#\space`. Otherwise,
74
+ # reads a single character. Requires the ability to put
75
+ # eroneously read characters back in the input stream
76
+ #
77
+ # @return [String]
78
+ # @see http://people.csail.mit.edu/jaffer/r4rs_9.html#SEC65
79
+ def read_character
80
+ lit = read_literal
81
+
82
+ return " " if lit.empty? && peek_char == " "
83
+ CHARACTERS.fetch(lit.downcase) do
84
+ # Return just the first character
85
+ unread(lit[1..-1])
86
+ lit[0,1]
87
+ end
88
+ end
63
89
  end # Scheme
64
90
  end; end # SXP::Reader
@@ -1,4 +1,6 @@
1
1
  require 'json'
2
+ require 'bigdecimal'
3
+ require 'time'
2
4
 
3
5
  ##
4
6
  # Extensions for Ruby's `Object` class.
@@ -148,62 +150,67 @@ class Regexp
148
150
  end
149
151
  end
150
152
 
151
- require 'rdf' # For SPARQL
153
+ begin
154
+ require 'rdf' # For SPARQL
152
155
 
153
- class RDF::URI
154
- ##
155
- # Returns the SXP representation of this object.
156
- #
157
- # @return [String]
158
- def to_sxp; lexical || "<#{self}>"; end
159
- end
156
+ class RDF::URI
157
+ ##
158
+ # Returns the SXP representation of this object.
159
+ #
160
+ # @return [String]
161
+ def to_sxp; lexical || "<#{self}>"; end
162
+ end
160
163
 
161
- class RDF::Node
162
- ##
163
- # Returns the SXP representation of this object.
164
- #
165
- # @return [String]
166
- def to_sxp; to_s; end
167
- end
164
+ class RDF::Node
165
+ ##
166
+ # Returns the SXP representation of this object.
167
+ #
168
+ # @return [String]
169
+ def to_sxp; to_s; end
170
+ end
168
171
 
169
- class RDF::Literal
170
- ##
171
- # Returns the SXP representation of a Literal.
172
- #
173
- # @return [String]
174
- def to_sxp
175
- case datatype
176
- when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.time
177
- object.to_sxp
178
- else
179
- text = value.dump
180
- text << "@#{language}" if self.has_language?
181
- text << "^^#{datatype.to_sxp}" if self.has_datatype?
182
- text
172
+ class RDF::Literal
173
+ ##
174
+ # Returns the SXP representation of a Literal.
175
+ #
176
+ # @return [String]
177
+ def to_sxp
178
+ case datatype
179
+ when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.time
180
+ # Retain stated lexical form if possible
181
+ valid? ? to_s : object.to_sxp
182
+ else
183
+ text = value.dump
184
+ text << "@#{language}" if self.has_language?
185
+ text << "^^#{datatype.to_sxp}" if self.has_datatype?
186
+ text
187
+ end
183
188
  end
184
189
  end
185
- end
186
190
 
187
- class RDF::Query
188
- # Transform Query into an Array form of an SXP
189
- #
190
- # If Query is named, it's treated as a GroupGraphPattern, otherwise, a BGP
191
- #
192
- # @return [Array]
193
- def to_sxp
194
- res = [:bgp] + patterns
195
- (respond_to?(:named?) && named? ? [:graph, context, res] : res).to_sxp
191
+ class RDF::Query
192
+ # Transform Query into an Array form of an SXP
193
+ #
194
+ # If Query is named, it's treated as a GroupGraphPattern, otherwise, a BGP
195
+ #
196
+ # @return [Array]
197
+ def to_sxp
198
+ res = [:bgp] + patterns
199
+ (named? ? [:graph, context, res] : res).to_sxp
200
+ end
196
201
  end
197
- end
198
202
 
199
- class RDF::Query::Pattern
200
- # Transform Query Pattern into an SXP
201
- # @return [String]
202
- def to_sxp
203
- [:triple, subject, predicate, object].to_sxp
203
+ class RDF::Query::Pattern
204
+ # Transform Query Pattern into an SXP
205
+ # @return [String]
206
+ def to_sxp
207
+ [:triple, subject, predicate, object].to_sxp
208
+ end
204
209
  end
205
- end
206
210
 
207
- class RDF::Query::Variable
208
- def to_sxp; to_s; end
211
+ class RDF::Query::Variable
212
+ def to_sxp; to_s; end
213
+ end
214
+ rescue LoadError => e
215
+ # Ignore if RDF not loaded
209
216
  end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sxp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Arto Bendiken
9
+ - Gregg Kellogg
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-11-11 00:00:00.000000000 Z
13
+ date: 2013-03-12 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: json
@@ -18,7 +19,7 @@ dependencies:
18
19
  requirements:
19
20
  - - ! '>='
20
21
  - !ruby/object:Gem::Version
21
- version: 1.4.6
22
+ version: 1.1.1
22
23
  type: :runtime
23
24
  prerelease: false
24
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +27,7 @@ dependencies:
26
27
  requirements:
27
28
  - - ! '>='
28
29
  - !ruby/object:Gem::Version
29
- version: 1.4.6
30
+ version: 1.1.1
30
31
  - !ruby/object:Gem::Dependency
31
32
  name: rspec
32
33
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +35,7 @@ dependencies:
34
35
  requirements:
35
36
  - - ! '>='
36
37
  - !ruby/object:Gem::Version
37
- version: 2.12.0
38
+ version: 2.13.0
38
39
  type: :development
39
40
  prerelease: false
40
41
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +43,7 @@ dependencies:
42
43
  requirements:
43
44
  - - ! '>='
44
45
  - !ruby/object:Gem::Version
45
- version: 2.12.0
46
+ version: 2.13.0
46
47
  - !ruby/object:Gem::Dependency
47
48
  name: yard
48
49
  requirement: !ruby/object:Gem::Requirement
@@ -50,7 +51,7 @@ dependencies:
50
51
  requirements:
51
52
  - - ! '>='
52
53
  - !ruby/object:Gem::Version
53
- version: 0.8.3
54
+ version: 0.8.5
54
55
  type: :development
55
56
  prerelease: false
56
57
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,9 +59,27 @@ dependencies:
58
59
  requirements:
59
60
  - - ! '>='
60
61
  - !ruby/object:Gem::Version
61
- version: 0.8.3
62
+ version: 0.8.5
63
+ - !ruby/object:Gem::Dependency
64
+ name: rdf
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: 1.0.0
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 1.0.0
62
79
  description: A pure-Ruby implementation of a universal S-expression parser.
63
- email: arto@bendiken.net
80
+ email:
81
+ - arto@bendiken.net
82
+ - gregg@greggkellogg.net
64
83
  executables:
65
84
  - sxp2rdf
66
85
  - sxp2json