cljdotrb 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/.document +5 -0
- data/.gitignore +48 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +51 -0
- data/README.markdown +13 -0
- data/Rakefile +13 -0
- data/cljdotrb.gemspec +27 -0
- data/lib/cljdotrb.rb +156 -0
- data/lib/cljdotrb/boolean.rb +7 -0
- data/lib/cljdotrb/collections.rb +27 -0
- data/lib/cljdotrb/nil.rb +7 -0
- data/lib/cljdotrb/number.rb +39 -0
- data/lib/cljdotrb/reader.citrus +98 -0
- data/lib/cljdotrb/reader.rb +13 -0
- data/lib/cljdotrb/string.rb +7 -0
- data/lib/cljdotrb/symbolic.rb +17 -0
- data/lib/cljdotrb/version.rb +3 -0
- data/lib/str_parse.rb +63 -0
- data/spec/cljdotrb/reader_spec.rb +133 -0
- data/spec/cljdotrb_spec.rb +104 -0
- data/spec/spec_helper.rb +1 -0
- metadata +152 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
|
14
|
+
# jeweler generated
|
15
|
+
pkg
|
16
|
+
|
17
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
18
|
+
#
|
19
|
+
# * Create a file at ~/.gitignore
|
20
|
+
# * Include files you want ignored
|
21
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
22
|
+
#
|
23
|
+
# After doing this, these files will be ignored in all your git projects,
|
24
|
+
# saving you from having to 'pollute' every project you touch with them
|
25
|
+
#
|
26
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
27
|
+
#
|
28
|
+
# For MacOS:
|
29
|
+
#
|
30
|
+
.DS_Store
|
31
|
+
|
32
|
+
# For TextMate
|
33
|
+
#*.tmproj
|
34
|
+
#tmtags
|
35
|
+
|
36
|
+
# For emacs:
|
37
|
+
#*~
|
38
|
+
#\#*
|
39
|
+
#.\#*
|
40
|
+
|
41
|
+
# For vim:
|
42
|
+
#*.swp
|
43
|
+
|
44
|
+
# For redcar:
|
45
|
+
#.redcar
|
46
|
+
|
47
|
+
# For rubinius:
|
48
|
+
#*.rbc
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.2@cljdotrb
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cljdotrb (0.0.1)
|
5
|
+
citrus
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
citrus (2.4.1)
|
11
|
+
coderay (0.9.8)
|
12
|
+
diff-lcs (1.1.3)
|
13
|
+
guard (0.8.8)
|
14
|
+
thor (~> 0.14.6)
|
15
|
+
guard-rspec (0.5.8)
|
16
|
+
guard (>= 0.8.4)
|
17
|
+
method_source (0.6.7)
|
18
|
+
ruby_parser (>= 2.3.1)
|
19
|
+
multi_json (1.0.3)
|
20
|
+
pry (0.9.7.4)
|
21
|
+
coderay (~> 0.9.8)
|
22
|
+
method_source (~> 0.6.7)
|
23
|
+
ruby_parser (>= 2.3.1)
|
24
|
+
slop (~> 2.1.0)
|
25
|
+
rspec (2.7.0)
|
26
|
+
rspec-core (~> 2.7.0)
|
27
|
+
rspec-expectations (~> 2.7.0)
|
28
|
+
rspec-mocks (~> 2.7.0)
|
29
|
+
rspec-core (2.7.1)
|
30
|
+
rspec-expectations (2.7.0)
|
31
|
+
diff-lcs (~> 1.1.2)
|
32
|
+
rspec-mocks (2.7.0)
|
33
|
+
ruby_parser (2.3.1)
|
34
|
+
sexp_processor (~> 3.0)
|
35
|
+
sexp_processor (3.0.8)
|
36
|
+
simplecov (0.5.4)
|
37
|
+
multi_json (~> 1.0.3)
|
38
|
+
simplecov-html (~> 0.5.3)
|
39
|
+
simplecov-html (0.5.3)
|
40
|
+
slop (2.1.0)
|
41
|
+
thor (0.14.6)
|
42
|
+
|
43
|
+
PLATFORMS
|
44
|
+
ruby
|
45
|
+
|
46
|
+
DEPENDENCIES
|
47
|
+
cljdotrb!
|
48
|
+
guard-rspec
|
49
|
+
pry
|
50
|
+
rspec
|
51
|
+
simplecov
|
data/README.markdown
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
desc "Run all specs"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.pattern = "spec/**/*_spec.rb"
|
9
|
+
t.ruby_opts = '-Ilib -Ispec -I.'
|
10
|
+
t.rspec_opts = '--color'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
data/cljdotrb.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cljdotrb/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cljdotrb"
|
7
|
+
s.version = Clojure::VERSION
|
8
|
+
s.authors = ["Michael Fogus", "Alex Redington"]
|
9
|
+
s.email = ["fogus@thinkrelevance.com", "alex.redington@thinkrelevance.com"]
|
10
|
+
s.homepage = "http://github.com/fogus/clj.rb"
|
11
|
+
s.summary = %q{A reader for Clojure strings.}
|
12
|
+
s.description = %q{clj.rb parses Clojure data into its Ruby equivalents.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "clj.rb"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency 'pry'
|
23
|
+
s.add_development_dependency 'rspec'
|
24
|
+
s.add_development_dependency 'guard-rspec'
|
25
|
+
s.add_development_dependency 'simplecov'
|
26
|
+
s.add_runtime_dependency 'citrus'
|
27
|
+
end
|
data/lib/cljdotrb.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'cljdotrb/number'
|
2
|
+
require 'cljdotrb/collections'
|
3
|
+
require 'cljdotrb/boolean'
|
4
|
+
require 'cljdotrb/nil'
|
5
|
+
require 'cljdotrb/symbolic'
|
6
|
+
require 'cljdotrb/string'
|
7
|
+
require 'cljdotrb/reader'
|
8
|
+
|
9
|
+
module Metadata
|
10
|
+
def meta
|
11
|
+
@meta
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_meta m
|
15
|
+
@meta = m
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'delegate'
|
21
|
+
|
22
|
+
module Clojure
|
23
|
+
module Types
|
24
|
+
class Sym < Delegator
|
25
|
+
include Metadata
|
26
|
+
|
27
|
+
def initialize n
|
28
|
+
super
|
29
|
+
@name = n
|
30
|
+
end
|
31
|
+
|
32
|
+
def __getobj__
|
33
|
+
@name
|
34
|
+
end
|
35
|
+
|
36
|
+
def __setobj__ obj
|
37
|
+
@name = obj
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'strscan'
|
44
|
+
require 'str_parse'
|
45
|
+
|
46
|
+
module Clojure
|
47
|
+
class Parser < StringScanner
|
48
|
+
include Clojure::StringParser # parsing unicode strings is dirty business
|
49
|
+
|
50
|
+
INTEGER = /([-+]?0)|([-+]?[1-9]\d*)(N)?/
|
51
|
+
HEX = /([-+]?)0[xX]([0-9A-Fa-f]+)(N)?/
|
52
|
+
OCTAL = /([-+]?)0([0-7]+)(N)?/
|
53
|
+
RADIX = /([-+]?)([1-9][0-9]?)[rR]([0-9A-Za-z]+)/
|
54
|
+
RATIO = /([-+]?[0-9]+)\/([0-9]+)/
|
55
|
+
FLOAT = /([-+]?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?/
|
56
|
+
SYMBOLIC = /([:]?)(([\D].*)(\/))?([\D][^\/]*)/
|
57
|
+
TRUE = /true/
|
58
|
+
FALSE = /false/
|
59
|
+
NIL = /nil/
|
60
|
+
MAPO = /\{/
|
61
|
+
MAPC = /\}/
|
62
|
+
VECO = /\[/
|
63
|
+
VECC = /\]/
|
64
|
+
LISTO = /\(/
|
65
|
+
LISTC = /\)/
|
66
|
+
SET_START = /#/
|
67
|
+
IGNORE = /(?:[ \t\r\n,]+)/mx
|
68
|
+
|
69
|
+
def initialize src, opts = {}
|
70
|
+
opts ||= {}
|
71
|
+
super src
|
72
|
+
|
73
|
+
@int_ctor = opts[:int_ctor] || lambda { |repr, radix| repr.to_i(radix) }
|
74
|
+
@bint_ctor = opts[:bint_ctor] || @int_ctor
|
75
|
+
@ratio_ctor = opts[:ratio_ctor] || lambda { |n,d| Rational(n.to_i, d.to_i) }
|
76
|
+
@float_ctor = opts[:float_ctor] || lambda { |repr| repr.to_f }
|
77
|
+
@bigdec_ctor = opts[:bigdec_ctor] || @float_ctor
|
78
|
+
@kw_ctor = opts[:kw_ctor] || lambda { |prefix, name| "#{prefix ? prefix + '/' : prefix}#{name}".to_sym }
|
79
|
+
@sym_ctor = opts[:sym_ctor] || lambda { |prefix, name| Clojure::Types::Sym.new(@kw_ctor.call(prefix, name)) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_scalar
|
83
|
+
case
|
84
|
+
when scan(RATIO)
|
85
|
+
parse_ratio
|
86
|
+
when scan(HEX)
|
87
|
+
parse_hex
|
88
|
+
when scan(OCTAL)
|
89
|
+
parse_octal
|
90
|
+
when scan(RADIX)
|
91
|
+
parse_radix
|
92
|
+
when scan(FLOAT)
|
93
|
+
parse_float
|
94
|
+
when scan(INTEGER)
|
95
|
+
parse_int
|
96
|
+
when scan(TRUE)
|
97
|
+
true
|
98
|
+
when scan(FALSE)
|
99
|
+
false
|
100
|
+
when scan(NIL)
|
101
|
+
nil
|
102
|
+
when (str = parse_string) != :no_string
|
103
|
+
str
|
104
|
+
when scan(SYMBOLIC)
|
105
|
+
parse_symbolic
|
106
|
+
else
|
107
|
+
throw "unparseable"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_symbolic
|
112
|
+
is_kw = !self[1].empty?
|
113
|
+
prefix = self[4] ? self[3] : nil
|
114
|
+
name = self[5]
|
115
|
+
|
116
|
+
if is_kw
|
117
|
+
@kw_ctor.call prefix, name
|
118
|
+
else
|
119
|
+
@sym_ctor.call prefix, name
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def parse_hex
|
124
|
+
ctor = self[3] ? @bint_ctor : @int_ctor
|
125
|
+
ctor.call "#{self[1]}" + self[2], 16
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_octal
|
129
|
+
ctor = self[3] ? @bint_ctor : @int_ctor
|
130
|
+
ctor.call "#{self[1]}" + self[2], 8
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_radix
|
134
|
+
@int_ctor.call "#{self[1]}" + self[3], self[2].to_i
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_float
|
138
|
+
ctor = self[4] ? @bigdec_ctor : @float_ctor
|
139
|
+
ctor.call self[1]
|
140
|
+
end
|
141
|
+
|
142
|
+
def parse_int
|
143
|
+
ctor = self[3] ? @bint_ctor : @int_ctor
|
144
|
+
|
145
|
+
if self[1]
|
146
|
+
ctor.call self[1], 10
|
147
|
+
else
|
148
|
+
ctor.call self[2], 10
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def parse_ratio
|
153
|
+
@ratio_ctor.call self[1], self[2]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Clojure
|
2
|
+
module List
|
3
|
+
def to_ruby
|
4
|
+
captures[:form].map(&:to_ruby)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
Vector = List
|
9
|
+
|
10
|
+
module Map
|
11
|
+
def to_ruby
|
12
|
+
ruby_val = {}
|
13
|
+
captures[:form].each_slice(2) do |slice|
|
14
|
+
key,value = slice
|
15
|
+
ruby_val[key.to_ruby] = value.to_ruby
|
16
|
+
end
|
17
|
+
ruby_val
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Set
|
22
|
+
def to_ruby
|
23
|
+
::Set.new(captures[:form].map(&:to_ruby))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/cljdotrb/nil.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Clojure
|
2
|
+
module Integer
|
3
|
+
def to_ruby
|
4
|
+
to_i
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module HexInteger
|
9
|
+
def to_ruby
|
10
|
+
to_i 16
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module OctalInteger
|
15
|
+
def to_ruby
|
16
|
+
to_i 8
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module RadixInteger
|
21
|
+
def to_ruby
|
22
|
+
a = to_a
|
23
|
+
(a[1]+a[4]).to_i a[2].to_i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Ratio
|
28
|
+
def to_ruby
|
29
|
+
a = to_a
|
30
|
+
Rational(a[1].to_i, a[3].to_i)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Float
|
35
|
+
def to_ruby
|
36
|
+
to_f
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
grammar Clojure::Reader
|
2
|
+
|
3
|
+
rule form
|
4
|
+
(collection | literal)
|
5
|
+
end
|
6
|
+
|
7
|
+
rule collection
|
8
|
+
(vector | list | map | set)
|
9
|
+
end
|
10
|
+
|
11
|
+
rule set
|
12
|
+
("#" "{" (whitespace* form whitespace*)* "}") <Clojure::Set>
|
13
|
+
end
|
14
|
+
|
15
|
+
rule map
|
16
|
+
("{" (whitespace* form whitespace+ form whitespace*)+ "}") <Clojure::Map>
|
17
|
+
end
|
18
|
+
|
19
|
+
rule list
|
20
|
+
("(" (whitespace* form whitespace*)* ")") <Clojure::List>
|
21
|
+
end
|
22
|
+
|
23
|
+
rule vector
|
24
|
+
("[" (whitespace* form whitespace*)* "]") <Clojure::Vector>
|
25
|
+
end
|
26
|
+
|
27
|
+
rule whitespace
|
28
|
+
" "
|
29
|
+
end
|
30
|
+
|
31
|
+
rule literal
|
32
|
+
(string | nil | boolean | number | symbolic)
|
33
|
+
end
|
34
|
+
|
35
|
+
rule nil
|
36
|
+
"nil" <Clojure::Nil>
|
37
|
+
end
|
38
|
+
|
39
|
+
rule boolean
|
40
|
+
("true" | "false") <Clojure::Boolean>
|
41
|
+
end
|
42
|
+
|
43
|
+
rule number
|
44
|
+
rational | integral
|
45
|
+
end
|
46
|
+
|
47
|
+
rule integral
|
48
|
+
(radix_integer | octal_integer | hex_integer | integer)
|
49
|
+
end
|
50
|
+
|
51
|
+
rule rational
|
52
|
+
(ratio | float)
|
53
|
+
end
|
54
|
+
|
55
|
+
rule string
|
56
|
+
("\"" content:string_content* "\"") <Clojure::String>
|
57
|
+
end
|
58
|
+
|
59
|
+
rule string_content
|
60
|
+
(/[^"\\]/ | /\\./)
|
61
|
+
end
|
62
|
+
|
63
|
+
rule symbolic
|
64
|
+
(colon:":"? (prefix:symbolic_token sep:"/")? symbol:symbolic_token) <Clojure::Symbolic>
|
65
|
+
end
|
66
|
+
|
67
|
+
rule symbolic_token
|
68
|
+
/[a-zA-Z\?\+!_-][a-zA-Z0-9\?\+!_-]*/
|
69
|
+
end
|
70
|
+
|
71
|
+
rule integer
|
72
|
+
/([-+]?0)|([-+]?[1-9]\d*)(N)?/ <Clojure::Integer>
|
73
|
+
end
|
74
|
+
|
75
|
+
rule hex_integer
|
76
|
+
/([-+]?)0[xX]([0-9A-Fa-f]+)(N)?/ <Clojure::HexInteger>
|
77
|
+
end
|
78
|
+
|
79
|
+
rule octal_integer
|
80
|
+
/([-+]?)0([0-7]+)(N)?/ <Clojure::OctalInteger>
|
81
|
+
end
|
82
|
+
|
83
|
+
rule radix_integer
|
84
|
+
(sign:/[-+]?/ radix:/[1-9][0-9]?/ delimeter:/[rR]/ number:/[0-9A-Za-z]+/) <Clojure::RadixInteger>
|
85
|
+
end
|
86
|
+
|
87
|
+
rule ratio
|
88
|
+
(numerator:/[-+]?[0-9]+/ "/" denominator:/[0-9]+/) <Clojure::Ratio>
|
89
|
+
end
|
90
|
+
|
91
|
+
rule float
|
92
|
+
#/([-+]?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?/
|
93
|
+
((number:/[-+]?[0-9]+\.[0-9]*(?:[eE][-+]?[0-9]+)?/ marker:"M"?)
|
94
|
+
| (number:/[-+]?[0-9]+M/)
|
95
|
+
| (number:/[-+]?[0-9][eE][-+]?[0-9]+M?/)) <Clojure::Float>
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Clojure
|
2
|
+
|
3
|
+
module Symbolic
|
4
|
+
def to_ruby
|
5
|
+
captures[:colon].first.empty? ? to_clojure_symbol : to_keyword
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_clojure_symbol
|
9
|
+
Clojure::Types::Sym.new to_keyword
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_keyword
|
13
|
+
to_a[2..3].reject(&:empty?).join.to_sym
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/str_parse.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
|
3
|
+
module Clojure
|
4
|
+
# Taken from the JSON parser at http://flori.github.com/json/
|
5
|
+
module StringParser
|
6
|
+
STRING = /" ((?:[^\x0-\x1f"\\] |
|
7
|
+
# escaped special characters:
|
8
|
+
\\["\\\/bfnrt] |
|
9
|
+
\\u[0-9a-fA-F]{4} |
|
10
|
+
# match all but escaped special characters:
|
11
|
+
\\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
|
12
|
+
"/nx
|
13
|
+
|
14
|
+
def parse_string
|
15
|
+
if scan(STRING)
|
16
|
+
return '' if self[1].empty?
|
17
|
+
|
18
|
+
string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
|
19
|
+
if u = UNESCAPE_MAP[$&[1]]
|
20
|
+
u
|
21
|
+
else # \uXXXX
|
22
|
+
bytes = EMPTY_8BIT_STRING.dup
|
23
|
+
i = 0
|
24
|
+
while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
|
25
|
+
bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
|
26
|
+
i += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
Iconv.iconv('utf-8', 'utf-16be', bytes)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if string.respond_to?(:force_encoding)
|
34
|
+
string.force_encoding(::Encoding::UTF_8)
|
35
|
+
end
|
36
|
+
|
37
|
+
string
|
38
|
+
else
|
39
|
+
:no_string
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
EMPTY_8BIT_STRING = ''
|
46
|
+
if ::String.method_defined?(:encode)
|
47
|
+
EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
|
48
|
+
end
|
49
|
+
|
50
|
+
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
|
51
|
+
UNESCAPE_MAP.update({
|
52
|
+
?" => '"',
|
53
|
+
?\\ => '\\',
|
54
|
+
?/ => '/',
|
55
|
+
?b => "\b",
|
56
|
+
?f => "\f",
|
57
|
+
?n => "\n",
|
58
|
+
?r => "\r",
|
59
|
+
?t => "\t",
|
60
|
+
?u => nil,
|
61
|
+
})
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. spec_helper.rb])
|
2
|
+
require 'pry'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
def read(string)
|
6
|
+
Clojure::Reader.read(string)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Clojure::Reader do
|
10
|
+
it 'parses positive integers' do
|
11
|
+
read("+42").should == 42
|
12
|
+
read("-42").should == -42
|
13
|
+
read("42").should == 42
|
14
|
+
read("-42").should == -42
|
15
|
+
read("-0").should == 0
|
16
|
+
read("+0").should == 0
|
17
|
+
read("-42N").should == -42
|
18
|
+
read("42N").should == 42
|
19
|
+
read("+42N").should == 42
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should recognize and parse hex integers" do
|
23
|
+
read("0xF").should == 15
|
24
|
+
read("+0xF").should == 15
|
25
|
+
read("-0xF").should == -15
|
26
|
+
read("0xFN").should == 15
|
27
|
+
read("-0xFN").should == -15
|
28
|
+
read("+0xFN").should == 15
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should recognize and parse octal integers" do
|
32
|
+
read("07").should == 7
|
33
|
+
read("+07").should == 7
|
34
|
+
read("-07").should == -7
|
35
|
+
read("07N").should == 7
|
36
|
+
read("-07N").should == -7
|
37
|
+
read("+07N").should == 7
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should recognize and parse radix integers" do
|
41
|
+
read("2r111").should == 7
|
42
|
+
read("+2r111").should == 7
|
43
|
+
read("-2r111").should == -7
|
44
|
+
read("32r1").should == 1
|
45
|
+
read("27rmj").should == 613
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should recognize and parse ratios" do
|
49
|
+
read("22/7").should == Rational(22,7)
|
50
|
+
read("+22/7").should == Rational(22,7)
|
51
|
+
read("-22/7").should == Rational(-22,7)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should recognize and parse floating point numbers" do
|
55
|
+
read("1.1").should == 1.1
|
56
|
+
read("1.1M").should == 1.1
|
57
|
+
read("-1.1").should == -1.1
|
58
|
+
read("-1.1M").should == -1.1
|
59
|
+
read("-1.21e10").should == -12_100_000_000.0
|
60
|
+
read("+1.21e10").should == 12_100_000_000.0
|
61
|
+
read("-1.21e10M").should == -12_100_000_000.0
|
62
|
+
read("+1.21e10M").should == 12_100_000_000.0
|
63
|
+
read("1M").should == 1.0
|
64
|
+
read("1e4").should == 10_000
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should recognize and parse true and false" do
|
68
|
+
read("true").should == true
|
69
|
+
read("false").should == false
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should recognize and parse nil" do
|
73
|
+
read("nil").should == nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should recognize and parse symbols" do
|
77
|
+
read("a").should == :a
|
78
|
+
read("a").class.should == Clojure::Types::Sym
|
79
|
+
read("a/b").should == :"a/b"
|
80
|
+
read("a/b").class.should == Clojure::Types::Sym
|
81
|
+
read("a").with_meta({:a => 1}).meta.should include(:a => 1)
|
82
|
+
read("a").with_meta({:a => 1}).should == :a
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
it 'parses keywords' do
|
87
|
+
read(":keyword").should == :keyword
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'parses vectors of single elements' do
|
91
|
+
read("[42]").should == [42]
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'ignores whitespace' do
|
95
|
+
read("[ 42 ]").should == [42]
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'parses vectors of multiple elements' do
|
99
|
+
read("[42 -1]").should == [42, -1]
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'parses lists' do
|
103
|
+
read("(42 -1)").should == [42, -1]
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'parses maps' do
|
107
|
+
read("{:a 7 :b 6}").should == {:a => 7, :b => 6}
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'parses sets' do
|
111
|
+
read("\#{:a :a :b :c :a :d :b :c :b :a}").should == Set.new([:a, :b, :c, :d])
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'parses nested collections' do
|
115
|
+
read("[[1 2] 3 4]").should == [[1, 2], 3, 4]
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'has no problem with maps of vectors' do
|
119
|
+
read("{:a [1 2] :b [3 4]}").should == {:a => [1, 2], :b => [3, 4]}
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'reads strings' do
|
123
|
+
read('"a string by any other name is just as tangly"').should == 'a string by any other name is just as tangly'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'reads strings with escaped quotes' do
|
127
|
+
read('"a string with an \"escaped quote\""').should == 'a string with an "escaped quote"'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'preserves other escaped characters' do
|
131
|
+
read('"a string with a\nnewline"').should == 'a string with a\nnewline'
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'cljdotrb'
|
3
|
+
|
4
|
+
describe Metadata do
|
5
|
+
class A
|
6
|
+
include Metadata
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should store metadata via mixin" do
|
10
|
+
a = A.new
|
11
|
+
|
12
|
+
a.should respond_to :with_meta
|
13
|
+
|
14
|
+
a.meta.should == nil
|
15
|
+
a.with_meta({:a => 1}).meta.should include(:a => 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def scalarize str
|
20
|
+
Clojure::Parser.new(str).parse_scalar
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Clojure::Parser do
|
24
|
+
context "when parsing scalar values" do
|
25
|
+
it "should recognize and parse integers" do
|
26
|
+
scalarize("+42").should == 42
|
27
|
+
scalarize("-42").should == -42
|
28
|
+
scalarize("42").should == 42
|
29
|
+
scalarize("-42").should == -42
|
30
|
+
scalarize("-0").should == 0
|
31
|
+
scalarize("+0").should == 0
|
32
|
+
scalarize("-42N").should == -42
|
33
|
+
scalarize("42N").should == 42
|
34
|
+
scalarize("+42N").should == 42
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should recognize and parse hex integers" do
|
38
|
+
scalarize("0xF").should == 15
|
39
|
+
scalarize("+0xF").should == 15
|
40
|
+
scalarize("-0xF").should == -15
|
41
|
+
scalarize("0xFN").should == 15
|
42
|
+
scalarize("-0xFN").should == -15
|
43
|
+
scalarize("+0xFN").should == 15
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should recognize and parse octal integers" do
|
47
|
+
scalarize("07").should == 7
|
48
|
+
scalarize("+07").should == 7
|
49
|
+
scalarize("-07").should == -7
|
50
|
+
scalarize("07N").should == 7
|
51
|
+
scalarize("-07N").should == -7
|
52
|
+
scalarize("+07N").should == 7
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should recognize and parse radix integers" do
|
56
|
+
scalarize("2r111").should == 7
|
57
|
+
scalarize("+2r111").should == 7
|
58
|
+
scalarize("-2r111").should == -7
|
59
|
+
scalarize("32r1").should == 1
|
60
|
+
scalarize("27rmj").should == 613
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should recognize and parse ratios" do
|
64
|
+
scalarize("22/7").should == Rational(22,7)
|
65
|
+
scalarize("+22/7").should == Rational(22,7)
|
66
|
+
scalarize("-22/7").should == Rational(-22,7)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should recognize and parse floating point numbers" do
|
70
|
+
scalarize("1.1").should == 1.1
|
71
|
+
scalarize("1.1M").should == 1.1
|
72
|
+
scalarize("-1.1").should == -1.1
|
73
|
+
scalarize("-1.1M").should == -1.1
|
74
|
+
scalarize("-1.21e10").should == -12100000000.0
|
75
|
+
scalarize("+1.21e10").should == 12100000000.0
|
76
|
+
scalarize("-1.21e10M").should == -12100000000.0
|
77
|
+
scalarize("+1.21e10M").should == 12100000000.0
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should recognize and parse true and false" do
|
81
|
+
scalarize("true").should == true
|
82
|
+
scalarize("false").should == false
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should recognize and parse nil" do
|
86
|
+
scalarize("nil").should == nil
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should recognize and parse strings" do
|
90
|
+
scalarize('""').should == ""
|
91
|
+
scalarize('"this is a string"').should == "this is a string"
|
92
|
+
scalarize('"this is \n string"').should == "this is \n string"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should recognize and parse symbols" do
|
96
|
+
scalarize("a").should == :a
|
97
|
+
scalarize("a").class.should == Clojure::Types::Sym
|
98
|
+
scalarize("a/b").should == :"a/b"
|
99
|
+
scalarize("a/b").class.should == Clojure::Types::Sym
|
100
|
+
scalarize("a").with_meta({:a => 1}).meta.should include(:a => 1)
|
101
|
+
scalarize("a").with_meta({:a => 1}).should == :a
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. lib cljdotrb])
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cljdotrb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael Fogus
|
9
|
+
- Alex Redington
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-06-13 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: pry
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rspec
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: guard-rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: simplecov
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '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: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: citrus
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :runtime
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
description: clj.rb parses Clojure data into its Ruby equivalents.
|
96
|
+
email:
|
97
|
+
- fogus@thinkrelevance.com
|
98
|
+
- alex.redington@thinkrelevance.com
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- .document
|
104
|
+
- .gitignore
|
105
|
+
- .rvmrc
|
106
|
+
- Gemfile
|
107
|
+
- Gemfile.lock
|
108
|
+
- README.markdown
|
109
|
+
- Rakefile
|
110
|
+
- cljdotrb.gemspec
|
111
|
+
- lib/cljdotrb.rb
|
112
|
+
- lib/cljdotrb/boolean.rb
|
113
|
+
- lib/cljdotrb/collections.rb
|
114
|
+
- lib/cljdotrb/nil.rb
|
115
|
+
- lib/cljdotrb/number.rb
|
116
|
+
- lib/cljdotrb/reader.citrus
|
117
|
+
- lib/cljdotrb/reader.rb
|
118
|
+
- lib/cljdotrb/string.rb
|
119
|
+
- lib/cljdotrb/symbolic.rb
|
120
|
+
- lib/cljdotrb/version.rb
|
121
|
+
- lib/str_parse.rb
|
122
|
+
- spec/cljdotrb/reader_spec.rb
|
123
|
+
- spec/cljdotrb_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
homepage: http://github.com/fogus/clj.rb
|
126
|
+
licenses: []
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project: clj.rb
|
145
|
+
rubygems_version: 1.8.22
|
146
|
+
signing_key:
|
147
|
+
specification_version: 3
|
148
|
+
summary: A reader for Clojure strings.
|
149
|
+
test_files:
|
150
|
+
- spec/cljdotrb/reader_spec.rb
|
151
|
+
- spec/cljdotrb_spec.rb
|
152
|
+
- spec/spec_helper.rb
|