sxp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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