xash 0.0.2

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