sexpistol 0.0.1
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/MIT-LICENSE +20 -0
- data/README.rdoc +71 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/sexpistol/sexpistol.rb +137 -0
- data/lib/sexpistol.rb +1 -0
- data/test/setup/test_unit_extensions.rb +21 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/float_literal_test.rb +30 -0
- data/test/unit/integer_literal_test.rb +24 -0
- data/test/unit/ruby_keyword_literals_test.rb +45 -0
- data/test/unit/string_literal_test.rb +29 -0
- data/test/unit/structure_test.rb +20 -0
- data/test/unit/symbol_test.rb +49 -0
- data/test/unit/to_sexp_test.rb +15 -0
- metadata +89 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010 Aaron Gough (http://thingsaaronmade.com/)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
= Sexpistol
|
|
2
|
+
|
|
3
|
+
Sexpistol is a simple library for parsing S-Expressions in Ruby. Sexpistol takes an S-Expression and turns it into a native Ruby data structure made up of nested sets of arrays.
|
|
4
|
+
|
|
5
|
+
=== Example
|
|
6
|
+
|
|
7
|
+
(define test (lambda () (
|
|
8
|
+
(print "blah")
|
|
9
|
+
(print 1)
|
|
10
|
+
(print 9.01)
|
|
11
|
+
(print (+ 10 12 13))
|
|
12
|
+
)))
|
|
13
|
+
|
|
14
|
+
would be parsed by Sexpistol like so:
|
|
15
|
+
|
|
16
|
+
[:define, :test, [:lambda, [], [
|
|
17
|
+
[:print, "blah"],
|
|
18
|
+
[:print, 1],
|
|
19
|
+
[:print, 9.01],
|
|
20
|
+
[:print, [:"+", 10, 12, 13]]
|
|
21
|
+
]]]
|
|
22
|
+
|
|
23
|
+
=== Type mappings
|
|
24
|
+
|
|
25
|
+
Sexpistol supports all of the standard datatypes and converts them directly to their Ruby equivalents:
|
|
26
|
+
|
|
27
|
+
* Lists (a b c)
|
|
28
|
+
* Integers (1 2 3)
|
|
29
|
+
* Floats (1.0 2.0 42.9)
|
|
30
|
+
* Strings ("\t\"Hello world!\"\n")
|
|
31
|
+
* Symbols (symbol Symbol __symbol__ symbo_l symbol? symbol!)
|
|
32
|
+
|
|
33
|
+
Sexpistol also supports mapping the Ruby keyword literals (nil, true, false) to their native Ruby types, although this is disabled by default for compatibility. To enable it use `@parser.ruby_keyword_literals = true`, eg:
|
|
34
|
+
|
|
35
|
+
@parser = Sexpistol.new
|
|
36
|
+
@parser.parse_string("nil false true")
|
|
37
|
+
#=> [:nil, :false, :true]
|
|
38
|
+
|
|
39
|
+
@parser.ruby_keyword_literals = true
|
|
40
|
+
@parser.parse_string("nil false true")
|
|
41
|
+
#=> [nil, false, true]
|
|
42
|
+
|
|
43
|
+
=== Installation
|
|
44
|
+
|
|
45
|
+
For convenience Sexpistol is packaged as a RubyGem, to install it simply enter the following at your command line:
|
|
46
|
+
|
|
47
|
+
gem install sexpistol
|
|
48
|
+
|
|
49
|
+
=== Usage
|
|
50
|
+
|
|
51
|
+
# Create a new parser instance
|
|
52
|
+
@parser = Sexpistol.new
|
|
53
|
+
|
|
54
|
+
# Parse a string
|
|
55
|
+
ast = @parser.parse_string("(string (to (parse)))")
|
|
56
|
+
#=> [:string, [:to, [:parse]]]
|
|
57
|
+
|
|
58
|
+
# Change the representation
|
|
59
|
+
ast[1][0] = :is
|
|
60
|
+
ast[1][1][0] = :parsed
|
|
61
|
+
#=> [:string, [:is, [:parsed]]]
|
|
62
|
+
|
|
63
|
+
# Turn the array structure back into an S-Expression
|
|
64
|
+
@parser.to_sexp( ast )
|
|
65
|
+
#=> "( string ( is ( parsed ) ) )"
|
|
66
|
+
|
|
67
|
+
=== Author & Credits
|
|
68
|
+
|
|
69
|
+
Author:: {Aaron Gough}[mailto:aaron@aarongough.com]
|
|
70
|
+
|
|
71
|
+
Copyright (c) 2010 {Aaron Gough}[http://thingsaaronmade.com/] ({thingsaaronmade.com}[http://thingsaaronmade.com/]), released under the MIT license
|
data/Rakefile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
require 'rake/rdoctask'
|
|
4
|
+
|
|
5
|
+
desc 'Default: run unit tests.'
|
|
6
|
+
task :default => :test
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'jeweler'
|
|
10
|
+
Jeweler::Tasks.new do |gemspec|
|
|
11
|
+
gemspec.name = "sexpistol"
|
|
12
|
+
gemspec.summary = "An S-Expression Parser Library for Ruby"
|
|
13
|
+
gemspec.description = "Sexpistol is an easy-to-use S-Expression parser for Ruby. It is fast and has no dependencies."
|
|
14
|
+
gemspec.email = "aaron@aarongough.com"
|
|
15
|
+
gemspec.homepage = "http://github.com/aarongough/sexpistol"
|
|
16
|
+
gemspec.authors = ["Aaron Gough"]
|
|
17
|
+
gemspec.rdoc_options << '--line-numbers' << '--inline-source'
|
|
18
|
+
gemspec.extra_rdoc_files = ['README.rdoc', 'MIT-LICENSE']
|
|
19
|
+
end
|
|
20
|
+
rescue LoadError
|
|
21
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
desc 'Test sexpistol.'
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'lib/*.rb'
|
|
28
|
+
t.libs << 'test'
|
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
|
30
|
+
t.verbose = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
desc 'Generate documentation for sexpistol.'
|
|
35
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
36
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
37
|
+
rdoc.title = 'Koi'
|
|
38
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
39
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
41
|
+
rdoc.rdoc_files.include('app/**/*.rb')
|
|
42
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.1
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# This class contains our logic for parsing
|
|
2
|
+
# S-Expressions. They are turned into a
|
|
3
|
+
# native Ruby representation like:
|
|
4
|
+
# [:def, :something [:lambda, [:a], [:do_something]]]
|
|
5
|
+
class Sexpistol
|
|
6
|
+
attr_accessor :ruby_keyword_literals
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@ruby_keyword_literals = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Parse a string containing an S-Expression into a
|
|
13
|
+
# nested set of Ruby arrays
|
|
14
|
+
def parse_string( string )
|
|
15
|
+
string.gsub!("(", " ( ")
|
|
16
|
+
string.gsub!(")", " ) ")
|
|
17
|
+
string_array = string.split
|
|
18
|
+
tokens = process_tokens( string_array )
|
|
19
|
+
check_tokens( tokens )
|
|
20
|
+
structure( tokens )
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Check and array of tokens to make sure that the number
|
|
24
|
+
# of open and closing parentheses match
|
|
25
|
+
def check_tokens( tokens )
|
|
26
|
+
unless( (tokens.reject {|x| x == "("}).length == (tokens.reject {|x| x == ")"}).length)
|
|
27
|
+
raise Exception, "Invalid S-Expression. The number of opening and closing parentheses do not match."
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Iterate over an array of strings and turn each
|
|
32
|
+
# item into it's relevant token. ie: "string" -> :string, "1" -> 1
|
|
33
|
+
def process_tokens( token_array )
|
|
34
|
+
tokens = []
|
|
35
|
+
token_array.each do |t|
|
|
36
|
+
if(@ruby_keyword_literals)
|
|
37
|
+
tokens << nil and next if(is_nil?(t))
|
|
38
|
+
tokens << true and next if(is_true?(t))
|
|
39
|
+
tokens << false and next if(is_false?(t))
|
|
40
|
+
end
|
|
41
|
+
tokens << t and next if(is_paren?(t))
|
|
42
|
+
tokens << t.to_f and next if( is_float?(t))
|
|
43
|
+
tokens << t.to_i and next if( is_integer?(t))
|
|
44
|
+
tokens << t.to_sym and next if( is_identifier?(t) || is_symbol?(t))
|
|
45
|
+
tokens << eval(t) and next if( is_string_literal?(t))
|
|
46
|
+
raise "\nUnrecognized token: #{t}\n"
|
|
47
|
+
end
|
|
48
|
+
return tokens
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Iterate over a flat array of tokens and turn it
|
|
52
|
+
# a nested set of arrays by detecting '(' and ')'
|
|
53
|
+
def structure( token_array, offset = 0, internal = false )
|
|
54
|
+
program = []
|
|
55
|
+
while(offset < token_array.length)
|
|
56
|
+
if( token_array[offset] == "(" )
|
|
57
|
+
offset, array = structure( token_array, offset + 1, true )
|
|
58
|
+
program << array
|
|
59
|
+
elsif( token_array[offset] == ")" )
|
|
60
|
+
break
|
|
61
|
+
else
|
|
62
|
+
program << token_array[offset]
|
|
63
|
+
end
|
|
64
|
+
offset += 1
|
|
65
|
+
end
|
|
66
|
+
if(internal)
|
|
67
|
+
return [offset, program]
|
|
68
|
+
else
|
|
69
|
+
return program
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Test to see whether or not a string represents the 'nil' literal
|
|
74
|
+
def is_nil?( string )
|
|
75
|
+
true if(string == "nil")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Test to see whether or not a string represents the 'true' literal
|
|
79
|
+
def is_true?( string )
|
|
80
|
+
true if(string == "true")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Test to see whether or not a string represents the 'false' literal
|
|
84
|
+
def is_false?( string )
|
|
85
|
+
true if(string == "false")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Test to see whether a string represents a parentheses
|
|
89
|
+
def is_paren?( string )
|
|
90
|
+
is_match?( string, /[\(\)]+/ )
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Test to see whether or not a string represents an integer
|
|
94
|
+
def is_integer?( string )
|
|
95
|
+
is_match?( string, /[\-\+]?[0-9]+/ )
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Test to see whether or not a string represents a float
|
|
99
|
+
def is_float?( string )
|
|
100
|
+
is_match?( string, /[\-\+]?[0-9]+\.[0-9]+/ )
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Test to see whether or not a string represents an identifier
|
|
104
|
+
def is_identifier?( string )
|
|
105
|
+
is_match?( string, /_*[a-zA-Z]+[a-zA-Z0-9_]*\??\!?/ )
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Test to see whether or not a string represents a string literal
|
|
109
|
+
def is_string_literal?( string )
|
|
110
|
+
is_match?( string, /".*"/)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Test to see whether or not a string represents a symbol
|
|
114
|
+
def is_symbol?( string )
|
|
115
|
+
is_match?( string, /[\!\*\^=\/\+\-]+/ )
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Convert a set of nested arrays back into an S-Expression
|
|
119
|
+
def to_sexp( data )
|
|
120
|
+
mapped = data.map do |item|
|
|
121
|
+
if( item.is_a?(Array))
|
|
122
|
+
to_sexp(item)
|
|
123
|
+
else
|
|
124
|
+
item.to_s
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
"( " + mapped.join(" ") + " )"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def is_match?( string, pattern )
|
|
133
|
+
match = string.match(pattern)
|
|
134
|
+
return false unless match
|
|
135
|
+
match[0].length == string.length
|
|
136
|
+
end
|
|
137
|
+
end
|
data/lib/sexpistol.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'sexpistol', 'sexpistol.rb'))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Test::Unit
|
|
2
|
+
# Used to fix a minor minitest/unit incompatibility in flexmock
|
|
3
|
+
AssertionFailedError = Class.new(StandardError)
|
|
4
|
+
|
|
5
|
+
class TestCase
|
|
6
|
+
|
|
7
|
+
def self.test(name, &block)
|
|
8
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
|
9
|
+
defined = instance_method(test_name) rescue false
|
|
10
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
|
11
|
+
if block_given?
|
|
12
|
+
define_method(test_name, &block)
|
|
13
|
+
else
|
|
14
|
+
define_method(test_name) do
|
|
15
|
+
flunk "No implementation provided for #{name}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
|
|
4
|
+
require_files = []
|
|
5
|
+
require_files << File.join(File.dirname(__FILE__), '..', 'lib', 'sexpistol.rb')
|
|
6
|
+
require_files.concat Dir[File.join(File.dirname(__FILE__), 'setup', '*.rb')]
|
|
7
|
+
|
|
8
|
+
require_files.each do |file|
|
|
9
|
+
require File.expand_path(file)
|
|
10
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class FloatLiteralTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should parse sexp containing an implicitly positive float literal" do
|
|
10
|
+
ast = @parser.parse_string("10.00")
|
|
11
|
+
assert_equal [10.00], ast
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "should parse sexp containing an explicitly positive float literal" do
|
|
15
|
+
ast = @parser.parse_string("+910.00")
|
|
16
|
+
assert_equal [910.00], ast
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "should parse sexp containing an explicitly negative float literal" do
|
|
20
|
+
ast = @parser.parse_string("-10.00")
|
|
21
|
+
assert_equal [-10.00], ast
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test "should parse sexp containing a large float literal" do
|
|
25
|
+
ast = @parser.parse_string("1.0000127829")
|
|
26
|
+
assert_equal [1.0000127829], ast
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class IntegerLiteralTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should parse sexp containing an implicitly positive integer literal" do
|
|
10
|
+
ast = @parser.parse_string("10")
|
|
11
|
+
assert_equal [10], ast
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "should parse sexp containing an explicitly positive integer literal" do
|
|
15
|
+
ast = @parser.parse_string("+910")
|
|
16
|
+
assert_equal [910], ast
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "should parse sexp containing an explicitly negative integer literal" do
|
|
20
|
+
ast = @parser.parse_string("-10")
|
|
21
|
+
assert_equal [-10], ast
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class RubyKeywordLiteralsTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should parse nil as literal" do
|
|
10
|
+
@parser.ruby_keyword_literals = true
|
|
11
|
+
ast = @parser.parse_string('nil')
|
|
12
|
+
assert_equal [nil], ast
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test "should not parse nil as literal" do
|
|
16
|
+
@parser.ruby_keyword_literals = false
|
|
17
|
+
ast = @parser.parse_string('nil')
|
|
18
|
+
assert_equal [:nil], ast
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test "should parse true as literal" do
|
|
22
|
+
@parser.ruby_keyword_literals = true
|
|
23
|
+
ast = @parser.parse_string('true')
|
|
24
|
+
assert_equal [true], ast
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test "should not parse true as literal" do
|
|
28
|
+
@parser.ruby_keyword_literals = false
|
|
29
|
+
ast = @parser.parse_string('true')
|
|
30
|
+
assert_equal [:true], ast
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
test "should parse false as literal" do
|
|
34
|
+
@parser.ruby_keyword_literals = true
|
|
35
|
+
ast = @parser.parse_string('false')
|
|
36
|
+
assert_equal [false], ast
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test "should notparse false as literal" do
|
|
40
|
+
@parser.ruby_keyword_literals = false
|
|
41
|
+
ast = @parser.parse_string('false')
|
|
42
|
+
assert_equal [:false], ast
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class StringLiteralTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should parse empty string literal" do
|
|
10
|
+
ast = @parser.parse_string('""')
|
|
11
|
+
assert_equal [""], ast
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "should parse string literal" do
|
|
15
|
+
ast = @parser.parse_string('"test"')
|
|
16
|
+
assert_equal ["test"], ast
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "should parse string literal containing escaped quotes" do
|
|
20
|
+
ast = @parser.parse_string('"te\"st"')
|
|
21
|
+
assert_equal ["te\"st"], ast
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test "should parse string literal containing escaped characters" do
|
|
25
|
+
ast = @parser.parse_string('"\n\t\r"')
|
|
26
|
+
assert_equal ["\n\t\r"], ast
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class RubyKeywordLiteralsTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should create nested set of arrays from s-expression" do
|
|
10
|
+
ast = @parser.parse_string('(this (is (an (s_expression))))')
|
|
11
|
+
assert_equal [[:this, [:is, [:an, [:s_expression]]]]], ast
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "should raise error on broken s-expression" do
|
|
15
|
+
assert_raises Exception do
|
|
16
|
+
ast = @parser.parse_string('(this (is (an (s_expression) too)')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class SymbolTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should parse simple symbol" do
|
|
10
|
+
ast = @parser.parse_string("test")
|
|
11
|
+
assert_equal [:test], ast
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "should parse symbol with trailing exclamation mark" do
|
|
15
|
+
ast = @parser.parse_string("test!")
|
|
16
|
+
assert_equal [:test!], ast
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "should parse symbol with trailing question mark" do
|
|
20
|
+
ast = @parser.parse_string("test?")
|
|
21
|
+
assert_equal [:test?], ast
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test "should parse symbol containing underscores" do
|
|
25
|
+
ast = @parser.parse_string("te__st")
|
|
26
|
+
assert_equal [:te__st], ast
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
test "should parse symbol with leading underscores" do
|
|
30
|
+
ast = @parser.parse_string("__test")
|
|
31
|
+
assert_equal [:__test], ast
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test "should parse symbol with trailing underscores" do
|
|
35
|
+
ast = @parser.parse_string("test__")
|
|
36
|
+
assert_equal [:test__], ast
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test "should parse CamelCase symbol" do
|
|
40
|
+
ast = @parser.parse_string("TestSymbol")
|
|
41
|
+
assert_equal [:TestSymbol], ast
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test "should parse complex symbol" do
|
|
45
|
+
ast = @parser.parse_string("__TestSymbol_TEST__?")
|
|
46
|
+
assert_equal [:__TestSymbol_TEST__?], ast
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
|
|
2
|
+
|
|
3
|
+
class ToSexpTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@parser = Sexpistol.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "should convert nested arrays back into an S-Expression" do
|
|
10
|
+
ast = [:string, [:is, [:parsed]]]
|
|
11
|
+
sexp = @parser.to_sexp(ast)
|
|
12
|
+
assert_equal "( string ( is ( parsed ) ) )", sexp
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sexpistol
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: false
|
|
5
|
+
segments:
|
|
6
|
+
- 0
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.0.1
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Aaron Gough
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-10-02 00:00:00 -04:00
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies: []
|
|
20
|
+
|
|
21
|
+
description: Sexpistol is an easy-to-use S-Expression parser for Ruby. It is fast and has no dependencies.
|
|
22
|
+
email: aaron@aarongough.com
|
|
23
|
+
executables: []
|
|
24
|
+
|
|
25
|
+
extensions: []
|
|
26
|
+
|
|
27
|
+
extra_rdoc_files:
|
|
28
|
+
- MIT-LICENSE
|
|
29
|
+
- README.rdoc
|
|
30
|
+
files:
|
|
31
|
+
- MIT-LICENSE
|
|
32
|
+
- README.rdoc
|
|
33
|
+
- Rakefile
|
|
34
|
+
- VERSION
|
|
35
|
+
- lib/sexpistol.rb
|
|
36
|
+
- lib/sexpistol/sexpistol.rb
|
|
37
|
+
- test/setup/test_unit_extensions.rb
|
|
38
|
+
- test/test_helper.rb
|
|
39
|
+
- test/unit/float_literal_test.rb
|
|
40
|
+
- test/unit/integer_literal_test.rb
|
|
41
|
+
- test/unit/ruby_keyword_literals_test.rb
|
|
42
|
+
- test/unit/string_literal_test.rb
|
|
43
|
+
- test/unit/structure_test.rb
|
|
44
|
+
- test/unit/symbol_test.rb
|
|
45
|
+
- test/unit/to_sexp_test.rb
|
|
46
|
+
has_rdoc: true
|
|
47
|
+
homepage: http://github.com/aarongough/sexpistol
|
|
48
|
+
licenses: []
|
|
49
|
+
|
|
50
|
+
post_install_message:
|
|
51
|
+
rdoc_options:
|
|
52
|
+
- --charset=UTF-8
|
|
53
|
+
- --line-numbers
|
|
54
|
+
- --inline-source
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
none: false
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
segments:
|
|
63
|
+
- 0
|
|
64
|
+
version: "0"
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
none: false
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
segments:
|
|
71
|
+
- 0
|
|
72
|
+
version: "0"
|
|
73
|
+
requirements: []
|
|
74
|
+
|
|
75
|
+
rubyforge_project:
|
|
76
|
+
rubygems_version: 1.3.7
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 3
|
|
79
|
+
summary: An S-Expression Parser Library for Ruby
|
|
80
|
+
test_files:
|
|
81
|
+
- test/setup/test_unit_extensions.rb
|
|
82
|
+
- test/test_helper.rb
|
|
83
|
+
- test/unit/float_literal_test.rb
|
|
84
|
+
- test/unit/integer_literal_test.rb
|
|
85
|
+
- test/unit/ruby_keyword_literals_test.rb
|
|
86
|
+
- test/unit/string_literal_test.rb
|
|
87
|
+
- test/unit/structure_test.rb
|
|
88
|
+
- test/unit/symbol_test.rb
|
|
89
|
+
- test/unit/to_sexp_test.rb
|