sexpistol 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc DELETED
@@ -1,89 +0,0 @@
1
- = Sexpistol
2
-
3
- Sexpistol is a very fast and easy-to-use library for parsing S-Expressions in Ruby. Sexpistol takes an S-Expression in string form 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 "Hello world!\n")
9
- (print 1)
10
- (print 9.01)
11
- (print 2.0e10)
12
- (print (+ 10 12 13))
13
- )))
14
-
15
- would be parsed by Sexpistol like so:
16
-
17
- [:define, :test, [:lambda, [], [
18
- [:print, "Hello world!\n"],
19
- [:print, 1],
20
- [:print, 9.01],
21
- [:print, 2.0e10],
22
- [:print, [:+, 10, 12, 13]]
23
- ]]]
24
-
25
- === Type mappings
26
-
27
- Sexpistol supports all of the standard datatypes and converts them directly to their Ruby equivalents:
28
-
29
- * Lists (a b c)
30
- * Integers (1 2 3)
31
- * Floats (1.0 2.0 42.9 3e6 1.2e2)
32
- * Strings ("\t\"Hello world!\"\n")
33
- * Symbols (symbol Symbol ____symbol____ symbo_l symbol? symbol! + - / ++ a+ e$, etc...)
34
-
35
- 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:
36
-
37
- @parser = Sexpistol.new
38
- @parser.parse_string("nil false true")
39
- #=> [:nil, :false, :true]
40
-
41
- @parser.ruby_keyword_literals = true
42
- @parser.parse_string("nil false true")
43
- #=> [nil, false, true]
44
-
45
- == Scheme compatibility
46
-
47
- Above all Sexpistol strives to be compatible with Scheme-style S-Expressions. This means that Sexpistol supports comma quoting, though quasi-quoting is not yet implemented. Sexpistol can also generate Scheme compatible external representations when the 'scheme_compatability' options is set to true:
48
-
49
- @parser = Sexpistol.new
50
- @parser.scheme_compatability = true
51
- @parser.to_sexp([:test, false, true, nil])
52
- #=> "(test #f #t ())"
53
-
54
- === Installation
55
-
56
- For convenience Sexpistol is packaged as a RubyGem, to install it simply enter the following at your command line:
57
-
58
- gem install sexpistol
59
-
60
- === Usage
61
-
62
- # Create a new parser instance
63
- @parser = Sexpistol.new
64
-
65
- # Parse a string
66
- ast = @parser.parse_string("(string (to (parse)))")
67
- #=> [:string, [:to, [:parse]]]
68
-
69
- # Change the representation
70
- ast[1][0] = :is
71
- ast[1][1][0] = :parsed
72
- #=> [:string, [:is, [:parsed]]]
73
-
74
- # Turn the array structure back into an S-Expression
75
- @parser.to_sexp( ast )
76
- #=> "( string ( is ( parsed ) ) )"
77
-
78
- === Performance
79
-
80
- The core of Sexpistol was recently re-written using StringScanner and the new version is roughly twice as fast as the older ones.
81
-
82
- Parsing throughput on my test machine (2Ghz Core 2 Duo, 4GB RAM, Ruby 1.9) is approximately 1 Megabytes/sec. This is fairly high given that Sexpistol is pure Ruby. Benchmarking Sexpistol against other popular S-Expression parser gems shows that it is roughly 8x faster than the nearest competitor.
83
-
84
- === Author & Credits
85
-
86
- Author:: {Aaron Gough}[mailto:aaron@aarongough.com]
87
- Contributors:: {Shane Hanna}[http://github.com/shanna]
88
-
89
- Copyright (c) 2010 {Aaron Gough}[http://thingsaaronmade.com/] ({thingsaaronmade.com}[http://thingsaaronmade.com/]), released under the MIT license
data/Rakefile DELETED
@@ -1,42 +0,0 @@
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 DELETED
@@ -1 +0,0 @@
1
- 0.0.7
@@ -1,76 +0,0 @@
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
-
7
- attr_accessor :ruby_keyword_literals, :scheme_compatability
8
-
9
- # Parse a string containing an S-Expression into a
10
- # nested set of Ruby arrays
11
- def parse_string(string)
12
- tree = SexpistolParser.new(string).parse
13
- return convert_ruby_keyword_literals(tree) if(@ruby_keyword_literals)
14
- return tree
15
- end
16
-
17
- # Convert symbols corresponding to Ruby's keyword literals
18
- # into their literal forms
19
- def convert_ruby_keyword_literals(expression)
20
- return recursive_map(expression) do |x|
21
- case x
22
- when :'nil' then nil
23
- when :'true' then true
24
- when :'false' then false
25
- else x
26
- end
27
- end
28
- end
29
-
30
- # Convert nil, true and false into (), #t and #f for compatability
31
- # with Scheme
32
- def convert_scheme_literals(data)
33
- return recursive_map(data) do |x|
34
- case x
35
- when nil then []
36
- when true then :"#t"
37
- when false then :"#f"
38
- else x
39
- end
40
- end
41
- end
42
-
43
- # Convert a set of nested arrays back into an S-Expression
44
- def to_sexp(data)
45
- data = convert_scheme_literals(data) if(@scheme_compatability)
46
- if( data.is_a?(Array))
47
- mapped = data.map do |item|
48
- if( item.is_a?(Array))
49
- to_sexp(item)
50
- else
51
- item.to_s
52
- end
53
- end
54
- "(" + mapped.join(" ") + ")"
55
- else
56
- data.to_s
57
- end
58
- end
59
-
60
- private
61
-
62
- def recursive_map(data, &block)
63
- if(data.is_a?(Array))
64
- return data.map do |x|
65
- if(x.is_a?(Array))
66
- recursive_map(x, &block)
67
- else
68
- block.call(x)
69
- end
70
- end
71
- else
72
- block.call(data)
73
- end
74
- end
75
-
76
- end
@@ -1,64 +0,0 @@
1
- require 'strscan'
2
-
3
- class SexpistolParser < StringScanner
4
-
5
- def initialize(string)
6
- unless(string.count('(') == string.count(')'))
7
- raise Exception, "Missing closing parentheses"
8
- end
9
- super(string)
10
- end
11
-
12
- def parse
13
- exp = []
14
- while true
15
- case fetch_token
16
- when '('
17
- exp << parse
18
- when ')'
19
- break
20
- when :"'"
21
- case fetch_token
22
- when '(' then exp << [:quote].concat([parse])
23
- else exp << [:quote, @token]
24
- end
25
- when String, Fixnum, Float, Symbol
26
- exp << @token
27
- when nil
28
- break
29
- end
30
- end
31
- exp
32
- end
33
-
34
- def fetch_token
35
- skip(/\s+/)
36
- return nil if(eos?)
37
-
38
- @token =
39
- # Match parentheses
40
- if scan(/[\(\)]/)
41
- matched
42
- # Match a string literal
43
- elsif scan(/"([^"\\]|\\.)*"/)
44
- eval(matched)
45
- # Match a float literal
46
- elsif scan(/[\-\+]? [0-9]+ ((e[0-9]+) | (\.[0-9]+(e[0-9]+)?))/x)
47
- matched.to_f
48
- # Match an integer literal
49
- elsif scan(/[\-\+]?[0-9]+/)
50
- matched.to_i
51
- # Match a comma (for comma quoting)
52
- elsif scan(/'/)
53
- matched.to_sym
54
- # Match a symbol
55
- elsif scan(/[^\(\)\s]+/)
56
- matched.to_sym
57
- # If we've gotten here then we have an invalid token
58
- else
59
- near = scan %r{.{0,20}}
60
- raise "Invalid character at position #{pos} near '#{near}'."
61
- end
62
- end
63
-
64
- end
@@ -1,46 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
2
-
3
- class BenchmarkTest < Test::Unit::TestCase
4
-
5
- require 'benchmark'
6
-
7
- def setup
8
- @example_sexp = <<-EOD
9
- (display "This is a test string!")
10
-
11
- (define test (lambda () (begin
12
- (display (== 1 1))
13
- (display (== true true))
14
- (display (== false false))
15
- (display (== nil nil))
16
- (display (== 2.09 1.08))
17
- (display (== 2e6 2e12))
18
- )))
19
- EOD
20
- end
21
-
22
- test "benchmark sexpistol" do
23
- puts "\nRunning performance test...\n"
24
-
25
- parser = Sexpistol.new
26
- parser.ruby_keyword_literals = true
27
-
28
- Benchmark.bmbm do |b|
29
- b.report("Parse") do
30
- 5000.times do
31
- parser.parse_string(@example_sexp)
32
- end
33
- end
34
-
35
- b.report("to_sexp") do
36
- ast = parser.parse_string(@example_sexp)
37
- 5000.times do
38
- parser.to_sexp(ast)
39
- end
40
- end
41
- end
42
-
43
- puts
44
- end
45
-
46
- end
@@ -1,21 +0,0 @@
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 DELETED
@@ -1,10 +0,0 @@
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
@@ -1,40 +0,0 @@
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
- test "should parse sexp containing a float defined in scientific notation" do
30
- ast = @parser.parse_string("(1.0e6)")
31
- assert_equal [[1.0e6]], ast
32
- end
33
-
34
- test "should parse sexp containing a float defined in scientific notation with no decimal place" do
35
- ast = @parser.parse_string("(10e2)")
36
- assert_equal [[10e2]], ast
37
- end
38
-
39
-
40
- end
@@ -1,24 +0,0 @@
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
@@ -1,45 +0,0 @@
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
@@ -1,46 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
2
-
3
- class SchemeCompatabilityTest < Test::Unit::TestCase
4
-
5
- def setup
6
- @parser = Sexpistol.new
7
- @parser.scheme_compatability = true
8
- end
9
-
10
- test "should parse #t as symbol" do
11
- ast = @parser.parse_string('(#t)')
12
- assert_equal [[:"#t"]], ast
13
- end
14
-
15
- test "should parse #f as symbol" do
16
- ast = @parser.parse_string('(#f)')
17
- assert_equal [[:"#f"]], ast
18
- end
19
-
20
- test "should allow comma quoting" do
21
- ast = @parser.parse_string("(this is '( a test) too foo)(foo)")
22
- assert_equal [[:this, :is, [:quote, [:a, :test]], :too, :foo ],[:foo]], ast
23
- end
24
-
25
- test "should allow complicated comma quoting" do
26
- ast = @parser.parse_string("(this is '( a test) (also))")
27
- assert_equal [[:this, :is, [:quote, [:a, :test]], [:also]]], ast
28
- end
29
-
30
- test "should allow comma quoting of integer literal" do
31
- ast = @parser.parse_string("(this is '1 (also))")
32
- assert_equal [[:this, :is, [:quote, 1], [:also]]], ast
33
- end
34
-
35
- test "should allow comma quoting of string literal" do
36
- ast = @parser.parse_string("(this is '\"test\" (also))")
37
- assert_equal [[:this, :is, [:quote, "test"], [:also]]], ast
38
- end
39
-
40
- test "should return scheme compatible external representation" do
41
- ast = [true, false, nil]
42
- string = @parser.to_sexp(ast)
43
- assert_equal "(#t #f ())", string
44
- end
45
-
46
- end
@@ -1,39 +0,0 @@
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
- test "should parse string literal containing spaces" do
30
- ast = @parser.parse_string('("blah foo")')
31
- assert_equal [["blah foo"]], ast
32
- end
33
-
34
- test "should parse string literal containing newlines" do
35
- ast = @parser.parse_string('("blah' + "\n" + 'foo")')
36
- assert_equal [["blah\nfoo"]], ast
37
- end
38
-
39
- end
@@ -1,30 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper.rb'))
2
-
3
- class StructureTest < 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) (also) blah) foo) (test))')
11
- assert_equal [[:this, [:is, [:an, [:s_expression], [:also], :blah], :foo], [:test]]], ast
12
- end
13
-
14
- test "should create nested set of arrays from s-expression with string literals" do
15
- ast = @parser.parse_string('(this (is (an ("s_expression"))))')
16
- assert_equal [[:this, [:is, [:an, ["s_expression"]]]]], ast
17
- end
18
-
19
- test "should raise error on broken s-expression" do
20
- assert_raises Exception do
21
- ast = @parser.parse_string('(this (is (an (s_expression) too)')
22
- end
23
- end
24
-
25
- test "should parser () as empty list" do
26
- ast = @parser.parse_string('()')
27
- assert_equal [[]], ast
28
- end
29
-
30
- end
@@ -1,74 +0,0 @@
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
- test "should parse symbol containing addition operators" do
50
- ast = @parser.parse_string("(+)")
51
- assert_equal [[:+]], ast
52
- end
53
-
54
- test "should parse symbol containing multiplication operators" do
55
- ast = @parser.parse_string("(*)")
56
- assert_equal [[:*]], ast
57
- end
58
-
59
- test "should parse symbol containing subtraction operators" do
60
- ast = @parser.parse_string("(-)")
61
- assert_equal [[:-]], ast
62
- end
63
-
64
- test "should parse symbol containing division operators" do
65
- ast = @parser.parse_string("(/)")
66
- assert_equal [[:"/"]], ast
67
- end
68
-
69
- test "should parse symbol containing any character except single and double quotes, backquote, parentheses and comma" do
70
- ast = @parser.parse_string("(~1!2@3#4$%5^6&7*890-_+=|\]}[{poiuytrewqasdfghjklmnbvcxzZXCVBNMLKJHGFDSAQWERTYUIOP:;/?><)")
71
- assert_equal [[:"~1!2@3#4$%5^6&7*890-_+=|\]}[{poiuytrewqasdfghjklmnbvcxzZXCVBNMLKJHGFDSAQWERTYUIOP:;/?><"]], ast
72
- end
73
-
74
- end