xash 0.0.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2ea6ec31fe55c1fad359d00416d8fa91a19067ee
4
+ data.tar.gz: b59f781bf66869db18f9920312793a7710c04381
5
+ SHA512:
6
+ metadata.gz: b0c76d221977d272ccddc6243d2002a83b4d964c094dfc8395b747e8adcc6804d70c2cb9cc42058f1ed050af9a8772d4a36b68d46bbc00b39c0af41df6354944
7
+ data.tar.gz: a71041ae1493cc35a094d93582ab744d1299b90b07bdfc634e314834be6abbd1f5af961e4e3edf17966efd60986201cee47af03ffff69ee5c0e2f9188112edd6
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xash.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 long-long-float
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Xash
2
+
3
+ ## Installation
4
+
5
+ $ gem install xash
6
+
7
+ ## Usage
8
+
9
+ $ xash file.yml
10
+
11
+ ## Testing
12
+
13
+ $ rake test
14
+
15
+ ## Example
16
+
17
+ samples/fizzbuzz.yml
18
+ ```ruby
19
+ #FizzBuzz
20
+ - for: [1..10,
21
+ do: [ ar: [i],
22
+ case: [
23
+ [$i, mod, 15, ==, 0], FizzBuzz,
24
+ [$i, mod, 3, ==, 0], Fizz,
25
+ [$i, mod, 5, ==, 0], Buzz,
26
+ do: [$i]
27
+ ]
28
+ ]
29
+ ]
30
+ ```
31
+
32
+ ## Document
33
+
34
+ see [wiki](https://github.com/long-long-float/xash/wiki/Document)
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new
5
+
6
+ desc 'Run tests'
7
+ task :default => :test
data/bin/xash ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'slop'
4
+ require 'xash'
5
+ require 'yaml'
6
+
7
+ opts = Slop.parse do |o|
8
+ o.banner = "usage: xash [options] file"
9
+ o.separator 'YAML file can be only accepted'
10
+ o.on '-v', '--version', 'print the version' do
11
+ puts "Xash #{XASH::VERSION}"
12
+ exit
13
+ end
14
+ o.on '-h', '--help', 'print this help' do
15
+ puts o
16
+ exit
17
+ end
18
+ end
19
+
20
+ args = opts.arguments
21
+ if args.empty?
22
+ puts opts
23
+ else
24
+ puts XASH.eval(YAML.load(ARGF.read)).to_yaml
25
+ end
data/lib/xash.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'xash/version'
2
+ require 'xash/evaluator'
3
+
4
+ module XASH
5
+ def self.eval(code)
6
+ e = Evaluator.new
7
+ e.eval(code)
8
+ end
9
+ end
@@ -0,0 +1,58 @@
1
+ module XASH
2
+ class Context
3
+ attr_accessor :name, :parent
4
+ attr_reader :lambda
5
+
6
+ def initialize(lambda, parent)
7
+ @variable_table = {}
8
+ @lambda = lambda['do']
9
+ @parent = parent
10
+ @name = '<anonymous>'
11
+ end
12
+
13
+ def exec(args)
14
+ exprs = @lambda
15
+ set_local_variable('it', args[0], false)
16
+ set_local_variable('args', args, false)
17
+
18
+ yield(exprs)
19
+ end
20
+
21
+ def attach(context)
22
+ @attaching_context = context
23
+ ret = yield
24
+ @attaching_context = nil
25
+ ret
26
+ end
27
+
28
+ def attaching_context_call(name, *args)
29
+ @attaching_context && @attaching_context.send(name, *args)
30
+ end
31
+
32
+ def exist_variable?(name)
33
+ attaching_context_call(:exist_variable?, name) || @variable_table.include?(name)
34
+ end
35
+
36
+ def variable(name)
37
+ attaching_context_call(:variable, name) || @variable_table[name]
38
+ end
39
+
40
+ def variables
41
+ @variable_table.merge(attaching_context_call(:variables) || {})
42
+ end
43
+
44
+ def set_local_variable(name, val, with_warn)
45
+ if with_warn && @variable_table.key?(name)
46
+ warn "`#{name}` has been already assigned!"
47
+ end
48
+ @variable_table[name] = val
49
+ end
50
+
51
+ def reset_local_variable(name, val)
52
+ unless @variable_table.include? name
53
+ error UndefinedLocalVariableError, "undefined local variable `#{name}`"
54
+ end
55
+ @variable_table[name] = val
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,86 @@
1
+ require 'xash/context'
2
+
3
+ module XASH
4
+ class ContextStack
5
+ include Enumerable
6
+
7
+ def initialize(evaluator)
8
+ @evaluator = evaluator
9
+
10
+ @context_stack = []
11
+
12
+ #root context
13
+ root = Context.new({ 'do' => [] }, nil)
14
+ root.name = '<root>'
15
+ @context_stack.push(root)
16
+ end
17
+
18
+ def each
19
+ if block_given?
20
+ @context_stack.each{ |c| yield c }
21
+ else
22
+ @context_stack.each
23
+ end
24
+ end
25
+
26
+ def [](index)
27
+ @context_stack[index]
28
+ end
29
+
30
+ def current
31
+ @context_stack.last
32
+ end
33
+
34
+ def meta
35
+ @context_stack[-2]
36
+ end
37
+
38
+ def push(context)
39
+ @context_stack.push context
40
+ ret = yield
41
+ @context_stack.pop
42
+ ret
43
+ end
44
+
45
+ def get_variable(name)
46
+ #search order
47
+ #parents -> context_stack(attached -> local)
48
+ @context_stack.reverse_each do |context|
49
+ if context.exist_variable?(name)
50
+ return context.variable(name)
51
+ end
52
+ end
53
+
54
+ cur = current
55
+ while cur
56
+ if cur.exist_variable?(name)
57
+ return cur.variable(name)
58
+ end
59
+ cur = cur.parent
60
+ end
61
+
62
+ raise UndefinedLocalVariableError, "undefined local variable `#{name}`"
63
+ end
64
+
65
+ def exist_variable?(name)
66
+ get_variable(name)
67
+ rescue
68
+ false
69
+ else
70
+ true
71
+ end
72
+
73
+ def variable(name)
74
+ get_variable(name)
75
+ rescue
76
+ @evaluator.error UndefinedLocalVariableError, "undefined local variable `#{name}`"
77
+ end
78
+
79
+ def meta_context
80
+ current = @context_stack.pop
81
+ ret = yield
82
+ @context_stack.push(current)
83
+ ret
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,386 @@
1
+ require 'xash/context'
2
+ require 'xash/context_stack'
3
+ require 'xash/type'
4
+ require 'xash/exception'
5
+
6
+ require 'yaml'
7
+
8
+ require 'pp'
9
+
10
+ module XASH
11
+ class Evaluator
12
+
13
+ def error(klass, msg)
14
+ cc = @context_stack.current
15
+ variables = cc.variables.dup
16
+ if cc.name == '<root>'
17
+ variables = variables.each do |name, value|
18
+ if @pseudo_functions.has_key? name
19
+ variables[name] = '<internal>'
20
+ end
21
+ end
22
+ end
23
+ raise klass, {
24
+ current_name: cc.name || 'nil',
25
+ current_variables: variables,
26
+ current_lambda: cc.lambda,
27
+ parent_name: cc.parent ? cc.parent.name : 'nil',
28
+ message: msg
29
+ }.to_yaml
30
+ end
31
+
32
+ def warn(msg)
33
+ STDERR.puts msg
34
+ end
35
+
36
+ def call_function(name, args)
37
+ { name => args }
38
+ end
39
+
40
+ def make_lambda(args, exprs)
41
+ { 'do' => [ { 'ar' => args }, *exprs] }
42
+ end
43
+
44
+ def wrap_pseudo_function(args, realname)
45
+ make_lambda(args, [ call_function(realname, args.map{|arg| "$#{arg}"}) ])
46
+ end
47
+
48
+ def import_yaml(mod_name)
49
+ context = boot({ 'do' => YAML.load_file(mod_name) })
50
+ exec_context(context, [])
51
+ end
52
+
53
+ def initialize
54
+ @context_stack = ContextStack.new(self)
55
+
56
+ @pseudo_functions = {
57
+ 'for' => wrap_pseudo_function(['collection', 'lambda'], '__for'),
58
+ 'if' => wrap_pseudo_function(['condition', 'lambda'], '__if'),
59
+ 'case' => wrap_pseudo_function([], '__case'),
60
+
61
+ 'assign' => wrap_pseudo_function(['name', 'value'], '__assign'),
62
+ 'reassign' => wrap_pseudo_function(['name', 'value'], '__reassign'),
63
+ 'alias' => wrap_pseudo_function(['old', 'new'], '__alias'),
64
+
65
+ 'import_yaml' => wrap_pseudo_function([], '__import_yaml'),
66
+
67
+ 'boot' => wrap_pseudo_function(['lambda'], '__boot'),
68
+ 'method' => wrap_pseudo_function([], '__method'),
69
+ 'get' => wrap_pseudo_function(['obj', 'name'], '__get'),
70
+
71
+ 'ar' => { 'do' => [ call_function('__ar', '$args') ] },
72
+
73
+ #for arrays
74
+ 'index' => wrap_pseudo_function(['ary', 'i'], '__index'),
75
+ 'tail' => wrap_pseudo_function(['ary'], '__tail'),
76
+
77
+ 'meta_context' => wrap_pseudo_function(%w(lambda_args lambda), '__meta_context'),
78
+ 'expr' => wrap_pseudo_function([], '__expr')
79
+ }
80
+ @pseudo_functions.each do |name, val|
81
+ @context_stack.current.set_local_variable(name, val, true)
82
+ end
83
+
84
+ #とりあえず
85
+ import_yaml("#{File::dirname(__FILE__)}/kernel.yml")
86
+ end
87
+
88
+ def check_args(args, *types)
89
+ args.zip(types) do |arg, type|
90
+ next unless type
91
+ check_arg(arg, type)
92
+ end
93
+ nil
94
+ end
95
+
96
+ def check_arg(arg, type)
97
+ error TypeError, "`#{arg}` is not `#{type}`" unless Type.send("#{type}?", arg)
98
+ nil
99
+ end
100
+
101
+ OPERATORS = {
102
+ '==' => ->(l, r){ l.eql? r },
103
+ '/=' => ->(l, r){ !(l.eql? r) },
104
+
105
+ '>=' => ->(l, r){ l >= r },
106
+ '<=' => ->(l, r){ l <= r },
107
+ '>' => ->(l, r){ l > r },
108
+ '<' => ->(l, r){ l < r },
109
+
110
+ 'or' => ->(l, r){ l || r },
111
+ 'and' => ->(l, r){ l && r },
112
+ 'xor' => ->(l, r){ l ^ r },
113
+
114
+ '+' => ->(l, r){ l + r },
115
+ '-' => ->(l, r){ l - r },
116
+
117
+ 'mul' => ->(l, r){ l * r },
118
+ 'div' => ->(l, r){ l / r },
119
+ 'mod' => ->(l, r){ l % r }
120
+ }
121
+
122
+ #convert tokens to "Reverse Polish Notation"
123
+ def to_rpn(tokens, rpn)
124
+ (OPERATORS.keys + [/\\(\w+)/, /(^[-=^~|@`+;*:<.>?!%&_]+$)/]).each do |pattern|
125
+ idx, ope = if pattern.is_a? Regexp
126
+ idx = tokens.index{|token| (token.is_a? String) && (m = pattern.match(token)) }
127
+ [ idx, idx && $~[1] ]
128
+ else
129
+ [ tokens.index(pattern), pattern ]
130
+ end
131
+ if idx
132
+ to_rpn(tokens[0...idx], rpn)
133
+ to_rpn(tokens[(idx + 1)...tokens.size], rpn)
134
+ rpn << ope
135
+ return
136
+ end
137
+ end
138
+ rpn << tokens[0]
139
+ end
140
+
141
+ def condition(cond)
142
+ !(cond == false or cond == 0 or cond == nil)
143
+ end
144
+
145
+ def boot(executable)
146
+ case executable
147
+ when Context
148
+ executable
149
+ when ->(l) { Type.lambda?(l) }
150
+ Context.new(executable, @context_stack.current)
151
+ else
152
+ boot(make_lambda([], [ executable ]))
153
+ end
154
+ end
155
+
156
+ def exec_context(context, args)
157
+ context.exec(args) do |exprs|
158
+ ret = nil
159
+ exprs.each do |expr|
160
+ ret = eval_expr(expr)
161
+ if context.exist_variable?('next_value')
162
+ ret = context.variable('next_value')
163
+ break
164
+ end
165
+ end
166
+ ret
167
+ end
168
+ end
169
+
170
+ def exec(executable, args, context_name = '<anonymous>')
171
+ case executable
172
+ when Context
173
+ executable.name = context_name
174
+
175
+ @context_stack.push(executable) do
176
+ exec_context(executable, args)
177
+ end
178
+ else #lambda and mono expr
179
+ exec(boot(executable), args, context_name)
180
+ end
181
+ end
182
+
183
+ def eval_expr(expr)
184
+ case expr
185
+ when Array
186
+ expr.map{ |e| eval_expr(e) }
187
+ when Hash
188
+ k, v = expr.to_a[0]
189
+
190
+ no_eval = %w(do object if for)
191
+
192
+ k = eval_expr(k)
193
+ unless no_eval.index(k)
194
+ v = eval_expr(v)
195
+ end
196
+
197
+ case k
198
+ #pseudo functions
199
+ when '__print'
200
+ print v.join
201
+ when '__for'
202
+ check_args(v, :collection)
203
+
204
+ collection, lambda = v
205
+
206
+ collection = Type.to_collection(eval_expr(collection))
207
+
208
+ collection.map do |e|
209
+ exec(lambda, [e])
210
+ end
211
+ when '__if'
212
+ check_args(v, :array)
213
+
214
+ condition, lambda = v
215
+ condition = eval_expr(call_function('expr', condition))
216
+
217
+ if condition(condition)
218
+ exec(lambda, [])
219
+ else
220
+ nil
221
+ end
222
+
223
+ when '__case'
224
+ args = @context_stack.variable('args')
225
+
226
+ #0 2 4 ...
227
+ 0.step(args.size, 2).map do |i|
228
+ unless Type.lambda?(args[i])
229
+ if condition(eval_expr(call_function('expr', args[i])))
230
+ break exec(args[i + 1], [])
231
+ end
232
+ else
233
+ break exec(args[i], [])
234
+ end
235
+ end
236
+
237
+ when '__assign'
238
+ check_args(v, :string)
239
+
240
+ name, value = v
241
+
242
+ @context_stack.meta.set_local_variable(name, value, true)
243
+ when '__reassign'
244
+ check_args(v, :string)
245
+
246
+ name, value = v
247
+
248
+ @context_stack.meta.reset_local_variable(name, value)
249
+
250
+ when '__alias'
251
+ check_args(v, :string, :string)
252
+
253
+ old, new = v
254
+
255
+ @context_stack.meta.set_local_variable(new, @context_stack.variable(old).dup, true)
256
+ when '__import_yaml'
257
+ modules = @context_stack.variable('args')
258
+
259
+ @context_stack.meta_context do
260
+ modules.each do |mod|
261
+ import_yaml(mod)
262
+ end
263
+ end
264
+
265
+ true
266
+
267
+ when '__boot'
268
+ check_args(v, :lambda)
269
+
270
+ lambda = v[0]
271
+ boot(lambda)
272
+
273
+ when '__method'
274
+ seq = @context_stack.variable('args').dup
275
+
276
+ obj = seq.shift
277
+ until seq.empty?
278
+ name, args = seq.shift, seq.shift
279
+ obj = exec(obj.variable(name), args)
280
+ end
281
+
282
+ obj
283
+
284
+ when '__get'
285
+ check_args(v, :context, :string)
286
+
287
+ context, name = v
288
+
289
+ context.variable(name)
290
+
291
+ when '__ar'
292
+ names = v
293
+ @context_stack.meta_context do
294
+ args = @context_stack.variable('args')
295
+ end
296
+ names.zip(args) do |name, arg|
297
+ @context_stack.meta.set_local_variable(name, arg, true)
298
+ end
299
+
300
+ when '__index'
301
+ check_args(v, :array, :integer)
302
+
303
+ ary, i = v
304
+
305
+ ary[i]
306
+
307
+ when '__tail'
308
+ check_args(v, :array)
309
+ ary = v[0]
310
+ ary[1...ary.size]
311
+
312
+ when '__meta_context'
313
+ check_args(v, :array, :executable)
314
+
315
+ args, lambda = v
316
+
317
+ #current
318
+ @context_stack.meta_context do
319
+ caller = @context_stack.current
320
+ #caller
321
+ @context_stack.meta_context do
322
+ #meta context
323
+
324
+ context = boot(lambda)
325
+ #context.parent = current
326
+ args.each do |arg|
327
+ context.set_local_variable(arg, caller.variable(arg), true)
328
+ end
329
+ @context_stack.current.attach(context) do
330
+ exec_context(context, args)
331
+ end
332
+ end
333
+ end
334
+
335
+ when '__expr'
336
+
337
+ tokens = @context_stack.variable('args')
338
+
339
+ rpn = []
340
+ to_rpn(tokens, rpn)
341
+
342
+ stack = []
343
+ rpn.each do |token|
344
+ stack << if OPERATORS.key? token
345
+ r, l = stack.pop, stack.pop
346
+ OPERATORS[token][l, r]
347
+ else
348
+ if @context_stack.exist_variable?(token)
349
+ r, l = stack.pop, stack.pop
350
+ eval_expr(call_function(token, [l, r]))
351
+ else
352
+ token
353
+ end
354
+ end
355
+ end
356
+
357
+ stack[0]
358
+
359
+ #for debug
360
+ when '__rb_inject'
361
+ code = v[0]
362
+ Kernel.eval(code, binding)
363
+
364
+ when 'do' #lambda
365
+ expr #for lazy evaluation
366
+
367
+ #function call
368
+ else #others
369
+ exec(@context_stack.variable(k), v, k)
370
+ end
371
+ when /\$(\w+)/ #local variables
372
+ var_name = $1
373
+ @context_stack.variable(var_name)
374
+ when /(\d+)(\.\.\.|\.\.)(\d+)/ #range expr
375
+ { 'range' => [$1, $2, $3] }
376
+ else
377
+ #primitives
378
+ expr
379
+ end
380
+ end
381
+
382
+ def eval(code)
383
+ exec({ 'do' => code }, [])
384
+ end
385
+ end
386
+ end
@@ -0,0 +1,13 @@
1
+ module XASH
2
+ class UndefinedFunctionError < StandardError
3
+ end
4
+
5
+ class UndefinedLocalVariableError < StandardError
6
+ end
7
+
8
+ class TypeError < StandardError
9
+ end
10
+
11
+ class InvalidContextIDError < StandardError
12
+ end
13
+ end
@@ -0,0 +1,78 @@
1
+ #kernel library
2
+
3
+ - alias: [assign, def]
4
+
5
+ - def: [print,
6
+ do: [
7
+ __print: [$args]
8
+ ]
9
+ ]
10
+
11
+ - def: [puts,
12
+ do: [
13
+ print: [$args, "\n"]
14
+ ]
15
+ ]
16
+
17
+ - def: [array,
18
+ do: [
19
+ $args
20
+ ]
21
+ ]
22
+
23
+ - def:
24
+ - meta_assign
25
+ - do:
26
+ - meta_context: [$args, $assign]
27
+
28
+ - def:
29
+ - expand
30
+ - do:
31
+ - ar: [context]
32
+ - assign: [names, tail: [$args]]
33
+ - for: [$names,
34
+ do: [ ar: [],
35
+ meta_assign: [$it, get: [$context, $it]]
36
+ ]
37
+ ]
38
+
39
+ #escapes lambda
40
+ - def: [next,
41
+ do: [[next_value],
42
+ meta_context: [[$next_value],
43
+ do: [
44
+ assign: [next_value, $it]
45
+ ]
46
+ ]
47
+ ]
48
+ ]
49
+
50
+ #for class system
51
+ - alias: [def, class]
52
+ - alias: [reassign, override]
53
+
54
+ - def:
55
+ - new
56
+ - do:
57
+ - ar: [klass]
58
+ - assign: [new_args, tail: [$args]]
59
+ - assign: [context, boot: [$klass]]
60
+ - context: $new_args
61
+ - $context
62
+
63
+ - def: [mdef,
64
+ do: [ ar: [name, lambda],
65
+ meta_context: [[name, lambda],#[$name, $lambda],
66
+ do: [ #ar: [name, lambda],
67
+ #__rb_inject: ["pp @context_stack.current.parent.name"],
68
+ def: [$name, boot: [$lambda] ]
69
+ ]
70
+ ]
71
+ ]
72
+ ]
73
+
74
+ - def: [extends,
75
+ do: [ ar: [klass],
76
+ meta_context: [[], $klass]
77
+ ]
78
+ ]
data/lib/xash/type.rb ADDED
@@ -0,0 +1,43 @@
1
+ module XASH
2
+ class Type
3
+ class << self
4
+
5
+ {
6
+ array: Array,
7
+ object: Hash,
8
+ string: String,
9
+ integer: Integer,
10
+ context: Context
11
+ }.each do |name, klass|
12
+ define_method "#{name}?" do |obj|
13
+ obj.is_a? klass
14
+ end
15
+ end
16
+
17
+ def lambda?(lambda)
18
+ lambda.is_a? Hash and lambda.to_a[0][0] == 'do'
19
+ end
20
+
21
+ def collection?(collection)
22
+ collection.is_a? String or collection.respond_to? :to_a
23
+ end
24
+
25
+ def executable?(executable)
26
+ lambda?(executable) or context?(executable)
27
+ end
28
+
29
+ def to_collection(collection)
30
+ case collection
31
+ when String
32
+ collection.each_char.to_a
33
+ when ->(c){ c.is_a? Hash and c.key? 'range' }
34
+ f, e, l = collection['range']
35
+ Range.new(f.to_i, l.to_i, e == '...').to_a
36
+ else
37
+ raise TypeError, "`#{collection}` is not collection" unless collection? collection
38
+ collection.to_a
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module XASH
2
+ VERSION = "0.0.2"
3
+ end
data/samples/class.yml ADDED
@@ -0,0 +1,5 @@
1
+ - import_yaml: ['samples/person.yml']
2
+
3
+ - assign: [p, new: [$Person, long_long_float, '18']]
4
+
5
+ - method: [$p, to_string, []]
@@ -0,0 +1,13 @@
1
+ - def: [make_get_name,
2
+ do: [ ar: [name],
3
+ boot: [
4
+ do: [
5
+ $name
6
+ ]
7
+ ]
8
+ ]
9
+ ]
10
+
11
+ - assign: [get_name, { make_get_name: [long_long_float]}]
12
+
13
+ - get_name: []
@@ -0,0 +1,10 @@
1
+ - assign: [a, 10]
2
+ - def:
3
+ - func
4
+ - do:
5
+ - assign: [a, 20]
6
+ - meta_context: [[],
7
+ do: [ $a ]
8
+ ]
9
+
10
+ - func: []
@@ -0,0 +1,4 @@
1
+ - import_yaml: ['samples/person.yml']
2
+
3
+ - assign: [p, new: [$Person, long_long_float, 18]]
4
+ - get: [$p, name]
@@ -0,0 +1,26 @@
1
+ - def: ['!!',
2
+ do: [
3
+ ar: [ary, idx],
4
+ index: [$ary, $idx]
5
+ ]
6
+ ]
7
+
8
+ - def: ['elem',
9
+ do: [
10
+ ar: [ary, e],
11
+ assign: [ret, false],
12
+ for: [$ary,
13
+ do: [
14
+ ar: [i],
15
+ puts: [$i, ' ', $e],
16
+ if: [[$i, ==, $e],
17
+ do: [ reassign: [ret, true] ]
18
+ ]
19
+ ]
20
+ ],
21
+ $ret
22
+ ]
23
+ ]
24
+
25
+ - assign: [ary, [1, 2, 3]]
26
+ - expr: [$ary, '!!', 1]
@@ -0,0 +1,15 @@
1
+ - class:
2
+ - Base
3
+ - do:
4
+ - mdef: [func1, do: ['func1']]
5
+ - mdef: [func2, do: ['func2']]
6
+
7
+ - class:
8
+ - Sub
9
+ - do:
10
+ - extends: [$Base]
11
+
12
+ - mdef: [func1, do: ['overridden']]
13
+
14
+ - assign: [obj, new: [$Sub]]
15
+ - [ method: [$obj, func1, []], method: [$obj, func2, []] ]
@@ -0,0 +1,11 @@
1
+ #FizzBuzz
2
+ - for: [1..10,
3
+ do: [ ar: [i],
4
+ case: [
5
+ [$i, mod, 15, ==, 0], FizzBuzz,
6
+ [$i, mod, 3, ==, 0], Fizz,
7
+ [$i, mod, 5, ==, 0], Buzz,
8
+ do: [$i]
9
+ ]
10
+ ]
11
+ ]
@@ -0,0 +1,3 @@
1
+ - for: [[1, 9, 9, 5],
2
+ do: [$it]
3
+ ]
@@ -0,0 +1,3 @@
1
+ - for: [1..10,
2
+ do: [$it]
3
+ ]
@@ -0,0 +1,6 @@
1
+ - for:
2
+ - object: [{ name: long_long_float, age: 9999 }]
3
+ - do:
4
+ - [k, v]
5
+ - print: [$k, ' => ', $v]
6
+ - puts: []
@@ -0,0 +1,7 @@
1
+ - class:
2
+ - Person
3
+ - do:
4
+ - ar: [name, age]
5
+
6
+ - mdef: [to_string, do: [expr: [ $name, +, ' : ', +, $age ] ]]
7
+ # - def: [to_string, boot: [ do: [expr: [ $name, +, " : ", +, $age ]]]]
@@ -0,0 +1,3 @@
1
+ - assign: [a, 1]
2
+ - reassign: [a, 10]
3
+ - $a
@@ -0,0 +1,10 @@
1
+ - def: [fact,
2
+ do: [[n],
3
+ case: [
4
+ [n, '>=', 1], 1,
5
+ expr: [{ self: [ expr: [$n, -, 1]] }, mul, $n]
6
+ ]
7
+ ]
8
+ ]
9
+
10
+ - puts: [fact: [4]] #=> 24
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+
3
+ class BasicTest < Test::Unit::TestCase
4
+ def test_reassign
5
+ assert_equal 10, XASH_eval(:reassign)
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+
3
+ class ContextTest < Test::Unit::TestCase
4
+ def test_meta_context
5
+ assert_equal 10, XASH_eval(:context)
6
+ end
7
+
8
+ def test_closure
9
+ assert_equal 'long_long_float', XASH_eval(:closure)
10
+ end
11
+
12
+ def test_class
13
+ assert_equal 'long_long_float : 18', XASH_eval(:class)
14
+ end
15
+
16
+ def test_extend
17
+ assert_equal %w(overridden func2), XASH_eval(:extend)
18
+ end
19
+
20
+ def test_get
21
+ assert_equal 'long_long_float', XASH_eval(:context_get)
22
+ end
23
+ end
data/test/test_expr.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'test/unit'
2
+
3
+ class ExprTest < Test::Unit::TestCase
4
+
5
+ def expr(*tokens)
6
+ XASH.eval [{ 'expr' => tokens }]
7
+ end
8
+
9
+ #+ - div mul
10
+ def test_basic_calc
11
+ assert_equal 6, expr(1, '+', 2, '+', 3)
12
+ assert_equal 26, expr(2, 'mul', 3, '+', 4, 'mul', 5)
13
+
14
+ assert_equal 1, expr(5, 'mod', 2)
15
+ end
16
+
17
+ #or and xor
18
+ def test_logical_calc
19
+ assert_equal true, expr(true, 'or', false)
20
+ assert_equal false, expr(true, 'and', false)
21
+ assert_equal true, expr(true, 'xor', false)
22
+ end
23
+
24
+ #== /=
25
+ def test_equivalent_calc
26
+ assert_equal true, expr(1, '+', 2, '==', 3)
27
+ assert_equal false, expr(1, '+', 2, '/=', 3)
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+
3
+ class FizzBuzzTest < Test::Unit::TestCase
4
+ def test_fizzbuzz
5
+ assert_equal [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz"], XASH_eval(:fizzbuzz)
6
+ end
7
+ end
data/test/test_for.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'test/unit'
2
+
3
+ class ForTest < Test::Unit::TestCase
4
+ def test_range
5
+ assert_equal [*1..10], XASH_eval(:for_range)
6
+ end
7
+
8
+ def test_array
9
+ assert_equal [1, 9, 9, 5], XASH_eval(:for_array)
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+ require 'xash'
3
+ require 'yaml'
4
+
5
+ class Test::Unit::TestCase
6
+ def XASH_eval(name)
7
+ XASH.eval(YAML.load_file("samples/#{name}.yml"))
8
+ end
9
+ end
data/test/test_if.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'test/unit'
2
+
3
+ class IfTest < Test::Unit::TestCase
4
+
5
+ def if_expr(cond, lambda)
6
+ XASH.eval [{ 'if' => [cond, lambda] }]
7
+ end
8
+
9
+ def test_basic
10
+ assert_equal 'ok', if_expr([true], 'ok')
11
+ assert_equal nil, if_expr([false], 'ok')
12
+
13
+ assert_equal 'ok', if_expr([5, 'mod', 2, '==', 1], 'ok')
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+
3
+ class OperatorTest < Test::Unit::TestCase
4
+ def test_definition
5
+ assert_equal 2, XASH_eval(:definition_operator)
6
+ end
7
+ end
data/xash.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xash/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xash"
8
+ spec.version = XASH::VERSION
9
+ spec.authors = ["long-long-float"]
10
+ spec.email = ["niinikazuki@yahoo.co.jp"]
11
+ spec.description = %q{XASH is a external DSL which can be written in YAML!}
12
+ spec.summary = %q{}
13
+ spec.homepage = "https://github.com/long-long-float/xash"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "slop", "~> 4.2"
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "test-unit"
25
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - long-long-float
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: XASH is a external DSL which can be written in YAML!
70
+ email:
71
+ - niinikazuki@yahoo.co.jp
72
+ executables:
73
+ - xash
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gemtest"
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/xash
84
+ - lib/xash.rb
85
+ - lib/xash/context.rb
86
+ - lib/xash/context_stack.rb
87
+ - lib/xash/evaluator.rb
88
+ - lib/xash/exception.rb
89
+ - lib/xash/kernel.yml
90
+ - lib/xash/type.rb
91
+ - lib/xash/version.rb
92
+ - samples/class.yml
93
+ - samples/closure.yml
94
+ - samples/context.yml
95
+ - samples/context_get.yml
96
+ - samples/definition_operator.yml
97
+ - samples/extend.yml
98
+ - samples/fizzbuzz.yml
99
+ - samples/for_array.yml
100
+ - samples/for_range.yml
101
+ - samples/object.yml
102
+ - samples/person.yml
103
+ - samples/reassign.yml
104
+ - samples/recursive.yml
105
+ - test/test_basic.rb
106
+ - test/test_context.rb
107
+ - test/test_expr.rb
108
+ - test/test_fizzbuzz.rb
109
+ - test/test_for.rb
110
+ - test/test_helper.rb
111
+ - test/test_if.rb
112
+ - test/test_operator.rb
113
+ - xash.gemspec
114
+ homepage: https://github.com/long-long-float/xash
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.5.1
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: ''
138
+ test_files:
139
+ - test/test_basic.rb
140
+ - test/test_context.rb
141
+ - test/test_expr.rb
142
+ - test/test_fizzbuzz.rb
143
+ - test/test_for.rb
144
+ - test/test_helper.rb
145
+ - test/test_if.rb
146
+ - test/test_operator.rb