clj 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/clj.rb +24 -0
  2. data/lib/clj/parser.rb +246 -0
  3. data/lib/clj/types.rb +45 -0
  4. metadata +70 -0
data/lib/clj.rb ADDED
@@ -0,0 +1,24 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'clj/types'
12
+ require 'clj/parser'
13
+
14
+ class Clojure
15
+ def self.parse (*args)
16
+ Clojure::Parser.new(*args).parse
17
+ end
18
+
19
+ def self.dump (what)
20
+ raise ArgumentError, 'cannot convert the passed value to clojure' unless what.respond_to? :to_clj
21
+
22
+ what.to_clj
23
+ end
24
+ end
data/lib/clj/parser.rb ADDED
@@ -0,0 +1,246 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'strscan'
12
+
13
+ class Clojure
14
+
15
+ class Parser < StringScanner
16
+ STRING = /" ((?:[^\x0-\x1f"\\] |
17
+ # escaped special characters:
18
+ \\["\\\/bfnrt] |
19
+ \\u[0-9a-fA-F]{4} |
20
+ # match all but escaped special characters:
21
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
22
+ "/nx
23
+
24
+ KEYWORD = /:([^(\[{'^@`~\"\\,\s;)\]}])/
25
+
26
+ INTEGER = /(-?0|-?[1-9]\d*)/
27
+
28
+ FLOAT = /(-?
29
+ (?:0|[1-9]\d*)
30
+ (?:
31
+ \.\d+(?i:e[+-]?\d+) |
32
+ \.\d+ |
33
+ (?i:e[+-]?\d+)
34
+ )
35
+ )/x
36
+
37
+ RATIONAL = /(#{INTEGER}\/#{INTEGER})/
38
+
39
+ REGEXP = /#"((\\.|[^"])+)"/
40
+
41
+ VECTOR_OPEN = /\[/
42
+ VECTOR_CLOSE = /\]/
43
+
44
+ LIST_OPEN = /\(/
45
+ LIST_CLOSE = /\)/
46
+
47
+ SET_OPEN = /\#\{/
48
+ SET_CLOSE = /\}/
49
+
50
+ HASH_OPEN = /\{/
51
+ HASH_CLOSE = /\}/
52
+
53
+ TRUE = /true/
54
+ FALSE = /false/
55
+ NIL = /nil/
56
+
57
+ IGNORE = %r(
58
+ (?:
59
+ //[^\n\r]*[\n\r]| # line comments
60
+ /\* # c-style comments
61
+ (?:
62
+ [^*/]| # normal chars
63
+ /[^*]| # slashes that do not start a nested comment
64
+ \*[^/]| # asterisks that do not end this comment
65
+ /(?=\*/) # single slash before this comment's end
66
+ )*
67
+ \*/ # the End of this comment
68
+ |[ \t\r\n,]+ # whitespaces: space, horicontal tab, lf, cr, and comma
69
+ )+
70
+ )mx
71
+
72
+ UNPARSED = Object.new
73
+
74
+ def initialize (source, options = {})
75
+ super(source)
76
+
77
+ @hash_class = options[:hash_class] || Hash
78
+ @vector_class = options[:vector_class] || Array
79
+ @list_class = options[:list_class] || Array
80
+ @set_class = options[:set_class] || Array
81
+ end
82
+
83
+ alias source string
84
+
85
+ def parsable? (what)
86
+ !!case what
87
+ when :vector then scan(VECTOR_OPEN)
88
+ when :list then scan(LIST_OPEN)
89
+ when :set then scan(SET_OPEN)
90
+ when :hash then scan(HASH_OPEN)
91
+ end
92
+ end
93
+
94
+ def parse (check = true)
95
+ reset if check
96
+
97
+ result = case
98
+ when parsable?(:vector) then parse_vector
99
+ when parsable?(:list) then parse_list
100
+ when parsable?(:set) then parse_set
101
+ when parsable?(:hash) then parse_hash
102
+ else parse_value
103
+ end
104
+
105
+ if check && result == UNPARSED
106
+ raise SyntaxError, 'the string does not contain proper clojure'
107
+ end
108
+
109
+ result
110
+ end
111
+
112
+ def parse_value
113
+ case
114
+ when scan(RATIONAL) then Rational(self[1])
115
+ when scan(FLOAT) then Float(self[1])
116
+ when scan(INTEGER) then Integer(self[1])
117
+ when scan(REGEXP) then /#{self[1]}/
118
+ when scan(STRING) then parse_string
119
+ when scan(KEYWORD) then self[1].to_sym
120
+ when scan(TRUE) then true
121
+ when scan(FALSE) then false
122
+ when scan(NIL) then nil
123
+ else UNPARSED
124
+ end
125
+ end
126
+
127
+ # Unescape characters in strings.
128
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
129
+ UNESCAPE_MAP.update({
130
+ ?" => '"',
131
+ ?\\ => '\\',
132
+ ?/ => '/',
133
+ ?b => "\b",
134
+ ?f => "\f",
135
+ ?n => "\n",
136
+ ?r => "\r",
137
+ ?t => "\t",
138
+ ?u => nil,
139
+ })
140
+
141
+ def parse_string
142
+ return '' if self[1].empty?
143
+
144
+ self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
145
+ if u = UNESCAPE_MAP[$&[1]]
146
+ u
147
+ else # \uXXXX
148
+ bytes = EMPTY_8BIT_STRING.dup
149
+ i = 0
150
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
151
+ bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
152
+ i += 1
153
+ end
154
+ JSON.iconv('utf-8', 'utf-16be', bytes)
155
+ end
156
+ end
157
+ end
158
+
159
+ def parse_vector
160
+ result = @vector_class.new
161
+
162
+ until eos?
163
+ case
164
+ when (value = parse(false)) != UNPARSED
165
+ result << value
166
+ when scan(VECTOR_CLOSE)
167
+ break
168
+ when skip(IGNORE)
169
+ ;
170
+ else
171
+ raise SyntaxError, 'wat do'
172
+ end
173
+ end
174
+
175
+ result
176
+ end
177
+
178
+ def parse_list
179
+ result = @list_class.new
180
+
181
+ until eos?
182
+ case
183
+ when (value = parse(false)) != UNPARSED
184
+ result << value
185
+ when scan(LIST_CLOSE)
186
+ break
187
+ when skip(IGNORE)
188
+ ;
189
+ else
190
+ raise SyntaxError, 'wat do'
191
+ end
192
+ end
193
+
194
+ result
195
+ end
196
+
197
+ def parse_set
198
+ result = @set_class.new
199
+
200
+ until eos?
201
+ case
202
+ when (value = parse(false)) != UNPARSED
203
+ result << value
204
+ when scan(SET_CLOSE)
205
+ break
206
+ when skip(IGNORE)
207
+ ;
208
+ else
209
+ raise SyntaxError, 'wat do'
210
+ end
211
+ end
212
+
213
+ if result.uniq!
214
+ raise SyntaxError, 'the set contains non unique values'
215
+ end
216
+
217
+ result
218
+ end
219
+
220
+ def parse_hash
221
+ result = @hash_class.new
222
+
223
+ until eos?
224
+ case
225
+ when (key = parse(false)) != UNPARSED
226
+ skip(IGNORE)
227
+
228
+ if (value = parse(false)) == UNPARSED
229
+ raise SyntaxError, 'no value for the hash'
230
+ end
231
+
232
+ result[key] = value
233
+ when scan(HASH_CLOSE)
234
+ break
235
+ when skip(IGNORE)
236
+ ;
237
+ else
238
+ raise SyntaxError, 'wat do'
239
+ end
240
+ end
241
+
242
+ result
243
+ end
244
+ end
245
+
246
+ end
data/lib/clj/types.rb ADDED
@@ -0,0 +1,45 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ [String, Symbol, Numeric, TrueClass, FalseClass, NilClass].each {|klass|
12
+ klass.instance_eval {
13
+ alias_method :to_clj, :inspect
14
+ }
15
+ }
16
+
17
+ class Rational
18
+ alias to_clj to_s
19
+ end
20
+
21
+ class Regexp
22
+ def to_clj
23
+ '#"' + inspect[1 .. -2] + '"'
24
+ end
25
+ end
26
+
27
+ if defined? BigDecimal
28
+ class BigDecimal
29
+ def to_clj
30
+ inspect + 'M'
31
+ end
32
+ end
33
+ end
34
+
35
+ class Array
36
+ def to_clj
37
+ '[' + map { |o| o.to_clj }.join(' ') + ']'
38
+ end
39
+ end
40
+
41
+ class Hash
42
+ def to_clj
43
+ '{' + map { |k, v| k.to_clj + ' ' + v.to_clj }.join(' ') + '}'
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clj
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - meh.
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: &14424 !ruby/object:Gem::Requirement
16
+ none: false
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ prerelease: false
22
+ requirement: *14424
23
+ name: rake
24
+ type: :development
25
+ - !ruby/object:Gem::Dependency
26
+ version_requirements: &14488 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
32
+ prerelease: false
33
+ requirement: *14488
34
+ name: rspec
35
+ type: :development
36
+ description:
37
+ email: meh@paranoici.org
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - lib/clj.rb
43
+ - lib/clj/types.rb
44
+ - lib/clj/parser.rb
45
+ homepage: http://github.com/meh/ruby-clj
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.12
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Like json, but with clojure sexps.
69
+ test_files: []
70
+ has_rdoc: