subtle-lang 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - ruby-head
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,29 +1,37 @@
1
- # Subtle
1
+ # Subtle [![Build Status](https://travis-ci.org/utkarshkukreti/subtle-lang.png)](https://travis-ci.org/utkarshkukreti/subtle-lang)
2
2
 
3
- TODO: Write a gem description
3
+ Subtle is a Terse, Array based Programming Language, heavily inspired by the K
4
+ Programming Language, and partly by APL and J.
4
5
 
5
6
  ## Installation
6
7
 
7
- Add this line to your application's Gemfile:
8
+ ### Using Git (Recommended)
8
9
 
9
- gem 'subtle'
10
+ git clone https://github.com/utkarshkukreti/subtle-lang.git
11
+ cd subtle-lang
12
+ bundle install
13
+ rake install
10
14
 
11
- And then execute:
15
+ ### From RubyGems
12
16
 
13
- $ bundle
17
+ gem install subtle-lang
14
18
 
15
- Or install it yourself as:
19
+ ## Usage
16
20
 
17
- $ gem install subtle
21
+ Run
18
22
 
19
- ## Usage
23
+ subtle
24
+
25
+ to start the interactive REPL.
26
+
27
+ ## Documentation
28
+
29
+ No docs yet, but the language is **heavily** tested.
30
+
31
+ Best way to learn more is to read the specs in `spec/`.
20
32
 
21
- TODO: Write usage instructions here
33
+ ## License
22
34
 
23
- ## Contributing
35
+ The MIT License
24
36
 
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
37
+ Copyright (c) 2012 Utkarsh Kukreti
@@ -1,6 +1,7 @@
1
1
  module Subtle
2
2
  class Evaluator
3
3
  def initialize
4
+ @state = {}
4
5
  @parser = Parser.new
5
6
  @transform = Transform.new
6
7
  end
@@ -15,7 +16,45 @@ module Subtle
15
16
 
16
17
  if Hash === t
17
18
  type = t[:type]
19
+
18
20
  case type
21
+ when :assignment
22
+ identifier = t[:identifier]
23
+ right = try_eval t[:right]
24
+ @state[identifier] = right
25
+ when :deassignment
26
+ identifier = t[:identifier]
27
+ @state[identifier]
28
+ when :function
29
+ t[:function]
30
+ when :function_call
31
+ function = t[:function]
32
+ adverb = t[:adverb]
33
+ right = try_eval t[:right]
34
+ if adverb
35
+ case adverb
36
+ when "/:"
37
+ right = [right] unless Array === right
38
+ right.map do |r|
39
+ eval type: :function_call, function: function, right: r
40
+ end
41
+ else
42
+ ae! t, "Invalid adverb #{adverb.inspect} for :function_call."
43
+ end
44
+ else
45
+ _x = @state["x"]
46
+ @state["x"] = right
47
+ eval(function).tap do
48
+ @state["x"] = _x
49
+ end
50
+ end
51
+ when :variable_call
52
+ identifier = t[:identifier]
53
+ adverb = t[:adverb]
54
+ arguments = t[:arguments]
55
+ function = @state[identifier]
56
+ eval type: :function_call, function: function, adverb: adverb,
57
+ right: arguments
19
58
  when :monad
20
59
  verb = t[:verb]
21
60
  adverb = t[:adverb]
@@ -35,6 +74,10 @@ module Subtle
35
74
  " You passed in #{right.class}."
36
75
  end
37
76
  case adverb
77
+ when "//:"
78
+ right.map do |r|
79
+ eval type: :monad, verb: verb, adverb: "/", right: r
80
+ end
38
81
  when "/"
39
82
  right.reduce do |fold, r|
40
83
  eval type: :dyad, verb: verb, left: fold, right: r
@@ -76,21 +119,16 @@ module Subtle
76
119
  end
77
120
  end
78
121
  when :dyad
122
+ # Evaluate right first.
123
+ right = try_eval t[:right]
79
124
  left = try_eval t[:left]
80
125
  verb = t[:verb]
81
126
  adverb = t[:adverb]
82
- right = try_eval t[:right]
83
127
 
84
128
  # `^` in Subtle is `**` in Ruby.
85
129
  verb = "**" if verb == "^"
86
130
 
87
131
  if adverb
88
- if Array === left && Array === right
89
- else
90
- ae! t, "Adverb `#{adverb}` must have arrays on both left and" +
91
- " right. You passed in #{left.class} and #{right.class}."
92
- end
93
-
94
132
  case adverb
95
133
  when "/:" # Map each over right
96
134
  right.map do |r|
@@ -125,34 +163,31 @@ module Subtle
125
163
  end
126
164
  else
127
165
  left.zip(right).map do |l, r|
128
- l.send(verb, r)
166
+ (try_eval l).send(verb, try_eval(r))
129
167
  end
130
168
  end
131
169
 
132
170
  elsif Array === left && Numeric === right
133
- # Multi-dimensional arrays
134
- if Array === left.first
135
- left.map do |l|
171
+ left.map do |l|
172
+ # Multi-dimensional arrays
173
+ if Array === l
136
174
  eval type: :dyad, verb: verb, left: l, right: right
137
- end
138
- else
139
- left.map do |l|
140
- l.send(verb, right)
175
+ else
176
+ (try_eval l).send(verb, right)
141
177
  end
142
178
  end
143
179
  elsif Numeric === left && Array === right
144
- # Multi-dimensional arrays
145
- if Array === right.first
146
- right.map do |r|
180
+ right.map do |r|
181
+ # Multi-dimensional arrays
182
+ if Array === r
147
183
  eval type: :dyad, verb: verb, left: left, right: r
148
- end
149
- else
150
- right.map do |r|
184
+ else
151
185
  left.send(verb, r)
152
186
  end
153
187
  end
154
188
  else
155
- nie! t
189
+ nie! t, "Left and Array must be Numeric or Arrays." +
190
+ " You passed in #{left.class} and #{right.class}."
156
191
  end
157
192
  when "&", "|"
158
193
  verb = "min" if verb == "&"
@@ -197,10 +232,11 @@ module Subtle
197
232
  if Numeric === last
198
233
  (0...last.floor).to_a
199
234
  else
200
- nie! t
235
+ nie! t, "`last` must be Numeric for type: :enumerate. You passed" +
236
+ " in #{last.class}."
201
237
  end
202
238
  else
203
- nie! t
239
+ nie! t, "Type #{t[:type].inspect} not implemented."
204
240
  end
205
241
  else
206
242
  t
data/lib/subtle/parser.rb CHANGED
@@ -2,9 +2,10 @@ module Subtle
2
2
  class Parser < Parslet::Parser
3
3
  def initialize
4
4
  @monadic_verbs = %w{+ - * / % ^ | & ~}
5
- @monadic_adverbs = %w{/: /}
5
+ @monadic_adverbs = %w{//: /: /}
6
6
  @dyadic_verbs = %w{+ - * / % ^ | & !}
7
7
  @dyadic_adverbs = %w{/: \:}
8
+ @function_adverbs = %w{/:}
8
9
  end
9
10
 
10
11
  rule(:spaces) { match["\\s"].repeat(1) }
@@ -12,10 +13,39 @@ module Subtle
12
13
  rule(:digits) { match["0-9"].repeat(1) }
13
14
  rule(:minus) { str("-") }
14
15
 
15
- rule(:integer) { (minus.maybe >> digits).as(:integer) >> spaces? }
16
- rule(:float) { (minus.maybe >> digits >> str(".") >> digits).as(:float) >>
17
- spaces? }
18
- rule(:atom) { float | integer }
16
+ rule(:integer) { (minus.maybe >> digits).as(:integer) >> spaces? }
17
+ rule(:float) { (minus.maybe >> digits >> str(".") >> digits).
18
+ as(:float) >> spaces? }
19
+ rule(:identifier) { (match["a-zA-Z"] >> match["a-zA-Z0-9_"].repeat).
20
+ as(:identifier) }
21
+ rule(:assignment) { (identifier >> spaces? >> str(":") >> spaces? >>
22
+ word.as(:right)).as(:assignment) >> spaces? }
23
+ rule(:deassignment) { identifier.as(:deassignment) >> spaces? }
24
+
25
+ rule(:function) do
26
+ str("{") >> spaces? >> word.as(:function) >> spaces? >> str("}") >>
27
+ spaces?
28
+ end
29
+
30
+ rule :function_adverb do
31
+ @function_adverbs.map { |adverb| str(adverb) }.reduce(:|).as(:adverb) >>
32
+ spaces?
33
+ end
34
+
35
+ rule(:function_call) do
36
+ (function >> function_adverb.maybe >> word.as(:right)).
37
+ as(:function_call) >> spaces?
38
+ end
39
+
40
+ rule(:variable_call) do
41
+ (identifier >> spaces? >> function_adverb.maybe >>
42
+ (assignment | dyad | noun).as(:arguments)).as(:variable_call)
43
+ end
44
+
45
+ rule(:atom) do
46
+ variable_call | function_call | function | float | integer | pword |
47
+ deassignment
48
+ end
19
49
 
20
50
  rule :array do
21
51
  atom_or_array = (array | (atom >> spaces?).repeat.as(:array)) >> spaces?
@@ -27,7 +57,7 @@ module Subtle
27
57
  end
28
58
 
29
59
  rule :enumerate do
30
- (str("!") >> spaces? >> (float | integer).as(:last)).as(:enumerate)
60
+ (str("!") >> spaces? >> (word).as(:last)).as(:enumerate)
31
61
  end
32
62
 
33
63
  rule(:noun) { enumerate | array | atom }
@@ -60,7 +90,12 @@ module Subtle
60
90
  end
61
91
 
62
92
  rule :word do
63
- dyad | noun | monad
93
+ assignment | dyad | noun | monad
94
+ end
95
+
96
+ # This is the last option of rule(:atom) above ^
97
+ rule :pword do
98
+ str("(") >> spaces? >> word >> spaces? >> str(")") >> spaces?
64
99
  end
65
100
 
66
101
  rule :sentence do
@@ -1,8 +1,8 @@
1
1
  module Subtle
2
2
  class Transform < Parslet::Transform
3
- rule(integer: simple(:x)) { x.to_i }
4
- rule(float: simple(:x)) { x.to_f }
5
- rule(array: subtree(:x)) { x }
3
+ rule(integer: simple(:x)) { x.to_i }
4
+ rule(float: simple(:x)) { x.to_f }
5
+ rule(array: subtree(:x)) { x.size == 1 ? x.first : x }
6
6
 
7
7
  rule monad: { verb: simple(:verb), right: subtree(:right) } do
8
8
  { type: :monad, verb: verb.to_s, right: right }
@@ -20,11 +20,49 @@ module Subtle
20
20
 
21
21
  rule dyad: { left: subtree(:left), verb: simple(:verb),
22
22
  adverb: simple(:adverb), right: subtree(:right) } do
23
- { type: :dyad, left: left, verb: verb.to_s, adverb: adverb, right: right }
23
+ { type: :dyad, left: left, verb: verb.to_s, adverb: adverb.to_s,
24
+ right: right }
24
25
  end
25
26
 
26
27
  rule enumerate: { last: subtree(:last) } do
27
28
  { type: :enumerate, last: last }
28
29
  end
30
+
31
+ rule assignment: { identifier: simple(:identifier),
32
+ right: subtree(:right) } do
33
+ { type: :assignment, identifier: identifier.to_s, right: right }
34
+ end
35
+
36
+ rule deassignment: { identifier: simple(:identifier) } do
37
+ { type: :deassignment, identifier: identifier.to_s }
38
+ end
39
+
40
+ rule function: subtree(:function) do
41
+ { type: :function, function: function }
42
+ end
43
+
44
+ rule function_call: { function: subtree(:function),
45
+ right: subtree(:right) } do
46
+ { type: :function_call, function: function, right: right }
47
+ end
48
+
49
+ rule function_call: { function: subtree(:function), adverb: simple(:adverb),
50
+ right: subtree(:right) } do
51
+ { type: :function_call, function: function, adverb: adverb.to_s,
52
+ right: right }
53
+ end
54
+
55
+ rule variable_call: { identifier: simple(:identifier),
56
+ arguments: subtree(:arguments) } do
57
+ { type: :variable_call, identifier: identifier.to_s,
58
+ arguments: arguments }
59
+ end
60
+
61
+ rule variable_call: { identifier: simple(:identifier),
62
+ adverb: simple(:adverb),
63
+ arguments: subtree(:arguments) } do
64
+ { type: :variable_call, identifier: identifier.to_s, adverb: adverb.to_s,
65
+ arguments: arguments }
66
+ end
29
67
  end
30
68
  end
@@ -1,3 +1,3 @@
1
1
  module Subtle
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -5,7 +5,13 @@ describe Subtle do
5
5
  describe "Project Euler" do
6
6
  describe "Problem 1:" +
7
7
  " Find the sum of all the multiples of 3 or 5 below 1000." do
8
- e "+/&~&/!1000%/:3 5", 233168
8
+ e "+/&~&/(!1000)%/:3 5", 233168
9
+ end
10
+
11
+ describe "Problem 6:" +
12
+ " Find the difference between the sum of the squares of the first" +
13
+ " one hundred natural numbers and the square of the sum." do
14
+ e "((+/!101)^2)-+/(!101)^2", 25164150
9
15
  end
10
16
  end
11
17
  end
data/spec/spec_helper.rb CHANGED
@@ -8,10 +8,13 @@ end
8
8
  require "subtle"
9
9
 
10
10
  RSpec.configure do |config|
11
- def e(input, output)
11
+ def e(input, output, evaluator = nil)
12
12
  it "should evaluate #{input.inspect} to #{output.inspect}" do
13
- evaluator = Subtle::Evaluator.new
14
- evaluator.eval(input).should eq output
13
+ evaluator ||= Subtle::Evaluator.new
14
+ evaluated = evaluator.eval(input)
15
+ if output
16
+ evaluated.should eq output
17
+ end
15
18
  end
16
19
  end
17
20
 
@@ -37,17 +37,17 @@ describe Subtle::Evaluator do
37
37
  describe "Rotate (`!`) (on left: Integer, right: Array)" do
38
38
  e "2 ! 1 2 3", [3, 1, 2]
39
39
  e "2 ! (1 2)", [1, 2]
40
- e "2 ! (2 3; 4 5; 8)", [[8], [2, 3], [4, 5]]
40
+ e "2 ! (2 3; 4 5; 8)", [8, [2, 3], [4, 5]]
41
41
  e "-2 ! 1 2 3", [2, 3, 1]
42
42
  e "-2 ! (1 2)", [1, 2]
43
- e "-2 ! (2 3; 4 5; 8)", [[4, 5], [8], [2, 3]]
43
+ e "-2 ! (2 3; 4 5; 8)", [[4, 5], 8, [2, 3]]
44
44
  end
45
45
  end
46
46
 
47
47
  describe "Enumerate (`!`)" do
48
48
  describe "Precedence" do
49
49
  e "!4", [0, 1, 2, 3]
50
- e "1 - !2 + 2", [-1, -2]
50
+ e "5 - !2 + 2", [5, 4, 3, 2]
51
51
  end
52
52
 
53
53
  describe "on Floats" do
@@ -63,6 +63,7 @@ describe Subtle::Evaluator do
63
63
  e "3 2 3 ^/: 2 3", [[9, 4, 9], [27, 8, 27]]
64
64
  e "3 2 3 &/: 2 3", [[2, 2, 2], [3, 2, 3]]
65
65
  e "1 2 3 |/: 0 3", [[1, 2, 3], [3, 3, 3]]
66
+ e "2 !/: (1 2 3; 2 3 4; 4 5 6)", [[3, 1, 2], [4, 2, 3], [6, 4, 5]]
66
67
  end
67
68
 
68
69
  describe "Map over each left (`\:`)" do
@@ -91,6 +92,11 @@ describe Subtle::Evaluator do
91
92
  e "+/: ((1 2; 3 4); (5 6; 7 8))", [[[1, 3], [2, 4]], [[5, 7], [6, 8]]]
92
93
  e "|/: (0 1; 0 2)", [[1, 0], [2, 0]]
93
94
  end
95
+
96
+ describe "Map over and fold (`//:`)" do
97
+ e "+//: (2 4; 3 1)", [6, 4]
98
+ e "+//:+(2 4; 3 1)", [5, 5]
99
+ end
94
100
  end
95
101
  end
96
102
 
@@ -111,7 +117,7 @@ describe Subtle::Evaluator do
111
117
 
112
118
  describe "Reverse (`|`)" do
113
119
  e "|1 2", [2, 1]
114
- e "|(1 2; 3; (6 7; 8))", [[[6, 7], [8]], [3], [1, 2]]
120
+ e "|(1 2; 3; (6 7; 8))", [[[6, 7], 8], 3, [1, 2]]
115
121
  end
116
122
  end
117
123
 
@@ -120,12 +126,13 @@ describe Subtle::Evaluator do
120
126
  e "(1 2; 3 4; 5 6.6 7)", [[1, 2], [3, 4], [5, 6.6, 7]]
121
127
  e "(;;;)", [[], [], [], []]
122
128
  e "(;;3 8;)", [[], [], [3, 8], []]
123
- e "(1;2.2 3.3;(;;3 8;);)", [[1], [2.2, 3.3], [[], [], [3, 8], []], []]
129
+ e "(1;2.2 3.3;(;;3 8;);)", [1, [2.2, 3.3], [[], [], [3, 8], []], []]
124
130
  end
125
131
 
126
132
  describe "Dyads" do
127
133
  describe "on Atoms and 2D/3D Arrays" do
128
134
  e "1 + (;1 2; 3 4)", [[], [2, 3], [4, 5]]
135
+ e "(1; 2 3) + 1", [2, [3, 4]]
129
136
  e "2 ^ ((5 6; 0 1);1 2; 3 4)", [[[32, 64], [1, 2]], [2, 4], [8, 16]]
130
137
  e "(;1 2; 3 4) - 1", [[], [0, 1], [2, 3]]
131
138
  e "((5 6; 0 1);1 2; 3 4) ^ 2", [[[25, 36], [0, 1]], [1, 4], [9, 16]]
@@ -134,6 +141,7 @@ describe Subtle::Evaluator do
134
141
  describe "on 1D/2D/3D Arrays with 2D/3D Arrays" do
135
142
  e "(1 2; 3 4) + (2 3; 4 5)", [[3, 5], [7, 9]]
136
143
  e "(1 2; 3 4) % (2 3; 4 5)", [[1, 2], [3, 4]]
144
+ e "(1 2; 3) + (1; 2 3)", [[2, 3], [5, 6]]
137
145
  e "((1 2); 5 6) + (3 1)", [[4, 5], [6, 7]]
138
146
  e "(((2 4); (6 8)); ((2 4); (6 8))) + 1 2", [[[3, 5], [7, 9]],
139
147
  [[4, 6], [8, 10]]]
@@ -141,10 +149,63 @@ describe Subtle::Evaluator do
141
149
  [[[4, 16], [36, 64]], [[4, 16], [36, 64]]]
142
150
  e "1 2 + (((2 4); (6 8)); ((2 4); (6 8)))", [[[3, 5], [7, 9]],
143
151
  [[4, 6], [8, 10]]]
152
+ e "1 2 + ((2; (6 8)); ((2 4 3); 6))", [[3, [7, 9]],
153
+ [[4, 6, 5], 8]]
144
154
  end
145
155
  end
146
156
  end
147
157
 
158
+ describe "Parentheses" do
159
+ e "(1 + 1) * 2", 4
160
+ e "3 + (1 + 1) * 2", 7
161
+ e "(1 + 2) * 3", 9
162
+ e "(((((1 + 1) * 2) + 2) * 3) + 3) * 4", 84
163
+ end
164
+
165
+ describe "Variables" do
166
+ @e = Subtle::Evaluator.new
167
+ e "a: 10", 10, @e
168
+ e "a", 10, @e
169
+ e "b", nil, @e
170
+ e "a: 1 + 2", 3, @e
171
+ e "b: a + 2", 5, @e
172
+ # Tests whether dyads are evaluated from right to left.
173
+ e "a: 1", 1, @e
174
+ e "(a: 10) + (b: 6) * a * (a: 3) + a", 82, @e
175
+ end
176
+
177
+ describe "Functions" do
178
+ describe "Map" do
179
+ e "{x * 2 - x} 4", -8
180
+ e "{x * 2 - x} 4 6 8", [-8, -24, -48]
181
+ e "{x + 1} {x * 2 - x} 4 6 8", [-7, -23, -47]
182
+ e "{x + {x + x} x} 1 2 3", [3, 6, 9]
183
+ e "{!x} 2", [0, 1]
184
+ e "{!x + 1} 2", [0, 1, 2]
185
+ end
186
+
187
+ describe "Map over each right (`/:`)" do
188
+ e "{!x}/:1 2 3", [[0], [0, 1], [0, 1, 2]]
189
+ e "{!x + 1}/:!3", [[0], [0, 1], [0, 1, 2]]
190
+ e "{+x}/:((1 2; 3 4); (4 5; 6 7))", [[[1, 3], [2, 4]], [[4, 6], [5, 7]]]
191
+ end
192
+
193
+ describe "Storing in variables" do
194
+ @e = Subtle::Evaluator.new
195
+ e "a: {x + !x}", nil, @e
196
+ e "a 3", [3, 4, 5], @e
197
+ e "a /: 1 2 3", [[1], [2, 3], [3, 4, 5]], @e
198
+ e "b: {x + 1}", nil, @e
199
+ e "c: 4", 4, @e
200
+ e "c: b b b c", 7, @e
201
+
202
+ e "d: {x * 2}", nil, @e
203
+ e "e: {2 * d x}", nil, @e
204
+ e "f: {(d x) + e x}", nil, @e
205
+ e "d e f f d e 6", 13824, @e
206
+ end
207
+ end
208
+
148
209
  describe "Errors" do
149
210
  describe "on Arrays" do
150
211
  ae! "1 2 + 2 3 4"
data/subtle.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
23
23
 
24
24
  gem.add_dependency "parslet"
25
25
 
26
- %w{rspec guard-rspec simplecov pry pry-debugger}.each do |name|
26
+ %w{rspec guard-rspec simplecov pry}.each do |name|
27
27
  gem.add_development_dependency name
28
28
  end
29
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: subtle-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-09 00:00:00.000000000 Z
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parslet
@@ -91,22 +91,6 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
- - !ruby/object:Gem::Dependency
95
- name: pry-debugger
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
94
  description: ! "Subtle is a Terse, Array based Programming Language,\n heavily
111
95
  inspired by the K Programming Language, and\n partly by
112
96
  APL and J."
@@ -119,6 +103,7 @@ extra_rdoc_files: []
119
103
  files:
120
104
  - .gitignore
121
105
  - .rspec
106
+ - .travis.yml
122
107
  - Gemfile
123
108
  - Guardfile
124
109
  - LICENSE.txt