cljdotrb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|