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 +1 -0
- data/CREDITS +0 -1
- data/README +3 -3
- data/VERSION +1 -1
- data/lib/sxp/extensions.rb +13 -9
- data/lib/sxp/generator.rb +33 -11
- data/lib/sxp/reader.rb +29 -6
- data/lib/sxp/reader/common_lisp.rb +14 -1
- data/lib/sxp/reader/scheme.rb +26 -0
- data/lib/sxp/writer.rb +55 -48
- metadata +28 -9
data/AUTHORS
CHANGED
data/CREDITS
CHANGED
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.
|
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.
|
1
|
+
0.1.2
|
data/lib/sxp/extensions.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
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
|
data/lib/sxp/generator.rb
CHANGED
@@ -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 =
|
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
|
-
#
|
59
|
-
# @
|
60
|
-
def
|
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
|
-
|
65
|
+
buffer += do_indent + '('
|
64
66
|
first, *elems = @elements
|
65
67
|
unless first.sxp?
|
66
68
|
# It's atomic, write out after paren
|
67
|
-
|
69
|
+
buffer += first.to_sxp + "\n"
|
68
70
|
else
|
69
|
-
|
71
|
+
buffer += "\n"
|
70
72
|
elems.unshift(first)
|
71
73
|
end
|
72
74
|
elems.each do |e|
|
73
|
-
e.
|
75
|
+
buffer += e.formatted
|
74
76
|
end
|
75
|
-
|
77
|
+
buffer += do_indent + ")\n"
|
76
78
|
else
|
77
|
-
|
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.
|
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
|
data/lib/sxp/reader.rb
CHANGED
@@ -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).
|
142
|
-
|
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
|
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 [
|
155
|
-
|
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
|
-
|
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
|
##
|
data/lib/sxp/reader/scheme.rb
CHANGED
@@ -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
|
data/lib/sxp/writer.rb
CHANGED
@@ -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
|
-
|
153
|
+
begin
|
154
|
+
require 'rdf' # For SPARQL
|
152
155
|
|
153
|
-
class RDF::URI
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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:
|
80
|
+
email:
|
81
|
+
- arto@bendiken.net
|
82
|
+
- gregg@greggkellogg.net
|
64
83
|
executables:
|
65
84
|
- sxp2rdf
|
66
85
|
- sxp2json
|