klam 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc3dcaef0efecdb916e9c2907f08db38c0ea5a5e
4
- data.tar.gz: a5751310c733519a4815040f0005fff1f36196b3
3
+ metadata.gz: 388bcd5f65c90c9afc48ad0953555aeec43a9c1d
4
+ data.tar.gz: fbfad96792a844628a80dde207ffa46e7ca83bf6
5
5
  SHA512:
6
- metadata.gz: 41d80904122f50bbd3b1d7c1586e5666e44f38135d680ae5ca81844df4ca100c693bdc9f031eaf6b698f76881fe5f2db916b80a9d340981839481d23ff725802
7
- data.tar.gz: 9635963254c84641c58e9d69f3bb4a803fd7e1702a0b7360b7b3a397ac940ffa04a3674f248be4d1224a8f964336420fe0da1590e1276e75f07fd4ea44b8209f
6
+ metadata.gz: e899e06dd8a393c2c0c7e7cd5d2a9b7ba1bf0a64d3d624663831589638e287ac829631372c52a9e14b52489406f0b51c0d34be6d4eec8aef759feef979a2a1ea
7
+ data.tar.gz: 9c9ceace49289e0326ee835d4db0570d5c93c148dcc11ffd5a492495aca4c74184a8f704e91c2d366132bdee5c7c2dc5601a4aa3cc7b506de00def53a343773a
@@ -1,6 +1,15 @@
1
1
  Relase History
2
2
  ==============
3
3
 
4
+ v0.0.8 -- March 15, 2015
5
+ ------------------------
6
+ Performance Improvements
7
+ ~~~~~~~~~~~~~~~~~~~~~~~~
8
+ * Self tail calls are now implemented with while loops rather than lambda/redo.
9
+ * Lists containing only constants are created once and cached.
10
+ * `Klam::Absvector` contains an `Array` instance rather than inheriting from
11
+ `Array`
12
+
4
13
  v0.0.7 -- February 24, 2015
5
14
  ---------------------------
6
15
  New Features
@@ -4,6 +4,8 @@ end
4
4
  require 'klam/version'
5
5
  require 'klam/error'
6
6
  require 'klam/template'
7
+ require 'klam/constant'
8
+ require 'klam/constant_generator'
7
9
  require 'klam/variable'
8
10
  require 'klam/variable_generator'
9
11
  require 'klam/absvector'
@@ -1,21 +1,33 @@
1
1
  module Klam
2
- class Absvector < Array
3
- def [](i)
4
- if i < 0 || i >= size
5
- raise Klam::Error, "index out of bounds: #{i}"
2
+ class Absvector
3
+ include Enumerable
4
+ attr_reader :size, :array
5
+
6
+ def initialize(n_or_array, fill = nil)
7
+ if n_or_array.kind_of?(Array)
8
+ # This is a convenience constructor for making Shen vectors from Ruby
9
+ # Arrays. Shen vectors use 1-based indexing and store the size in
10
+ # slot zero.
11
+ @array = Array.new(n_or_array)
12
+ @array.unshift(@array.size)
13
+ else
14
+ @array = Array.new(n_or_array, fill)
6
15
  end
7
- super(i)
16
+ @size = @array.size
8
17
  end
9
18
 
10
- def []=(i, x)
11
- if i < 0 || i >= size
19
+ def [](i)
20
+ if i < 0 || i >= @size
12
21
  raise Klam::Error, "index out of bounds: #{i}"
13
22
  end
14
- super(i, x)
23
+ @array[i]
15
24
  end
16
25
 
17
26
  def store(i, x)
18
- self[i] = x
27
+ if i < 0 || i >= @size
28
+ raise Klam::Error, "index out of bounds: #{i}"
29
+ end
30
+ @array[i] = x
19
31
  self
20
32
  end
21
33
 
@@ -27,9 +39,16 @@ module Klam
27
39
  end
28
40
 
29
41
  def to_a
30
- a = super
31
- a.shift
32
- a
42
+ @array[1..-1]
43
+ end
44
+
45
+ def ==(other)
46
+ other.kind_of?(Klam::Absvector) && other.size == @size &&
47
+ other.array == @array
48
+ end
49
+
50
+ def hash
51
+ @array.hash
33
52
  end
34
53
  end
35
54
  end
@@ -0,0 +1,63 @@
1
+ module Klam
2
+ module CompilationStages
3
+ module ConstantizeConstructedConstants
4
+ def constantize_constructed_constants(sexp)
5
+ constant_bindings = []
6
+ converted_sexp = extract_constructed_constants(sexp, constant_bindings)
7
+ if constant_bindings.empty?
8
+ sexp
9
+ else
10
+ bind_constants(converted_sexp, constant_bindings)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def extract_constructed_constants(sexp, constant_bindings)
17
+ if sexp.kind_of?(Array)
18
+ if sexp.size == 3 && sexp[0] == :cons
19
+ hd_expr = extract_constructed_constants(sexp[1], constant_bindings)
20
+ tl_expr = extract_constructed_constants(sexp[2], constant_bindings)
21
+ converted_cons = [:cons, hd_expr, tl_expr]
22
+ if constant?(hd_expr) && constant?(tl_expr)
23
+ const = fresh_constant
24
+ constant_bindings << [const, converted_cons]
25
+ const
26
+ else
27
+ converted_cons
28
+ end
29
+ else
30
+ sexp.map do |expr|
31
+ extract_constructed_constants(expr, constant_bindings)
32
+ end
33
+ end
34
+ else
35
+ sexp
36
+ end
37
+ end
38
+
39
+ def bind_constants(sexp, bindings)
40
+ if sexp.kind_of?(Array) && sexp[0] == :defun
41
+ sexp[0] = :"[DEFUN-CLOSURE]"
42
+ end
43
+
44
+ until bindings.empty?
45
+ binding = bindings.pop
46
+ sexp = [:let, binding[0], binding[1], sexp]
47
+ end
48
+ sexp
49
+ end
50
+
51
+ def constant?(sexp)
52
+ case sexp
53
+ when FalseClass, TrueClass, String, Symbol, Numeric, Klam::Constant
54
+ true
55
+ when []
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -75,17 +75,19 @@ module Klam
75
75
 
76
76
  def insert_loop_and_recur_into_defun(form)
77
77
  rator, name, params, body = form
78
- body_with_loop = [:"[LOOP]", name, params,
79
- insert_recur_into_expr(body, name, params)]
78
+ loop_var = fresh_variable
79
+ body_with_loop = [:"[LOOP]", loop_var,
80
+ insert_recur_into_expr(body, name, params,
81
+ loop_var)]
80
82
  [rator, name, params, body_with_loop]
81
83
  end
82
84
 
83
- def insert_recur_into_expr(sexp, name, params)
85
+ def insert_recur_into_expr(sexp, name, params, loop_var)
84
86
  if sexp.instance_of?(Array)
85
87
  case sexp[0]
86
88
  when name
87
89
  if sexp.length - 1 == params.length
88
- [:"[RECUR]", params, sexp[1..-1]]
90
+ [:"[RECUR]", params, sexp[1..-1], loop_var]
89
91
  else
90
92
  sexp
91
93
  end
@@ -93,17 +95,18 @@ module Klam
93
95
  rator, test_expr, true_expr, false_expr = sexp
94
96
  [rator,
95
97
  test_expr,
96
- insert_recur_into_expr(true_expr, name, params),
97
- insert_recur_into_expr(false_expr, name, params)]
98
+ insert_recur_into_expr(true_expr, name, params, loop_var),
99
+ insert_recur_into_expr(false_expr, name, params, loop_var)]
98
100
  when :let
99
101
  rator, var, val_expr, body_expr = sexp
100
102
  [rator,
101
103
  var,
102
104
  val_expr,
103
- insert_recur_into_expr(body_expr, name, params)]
105
+ insert_recur_into_expr(body_expr, name, params, loop_var)]
104
106
  when :do
105
107
  rator, first_expr, second_expr = sexp
106
- [rator, first_expr, insert_recur_into_expr(second_expr, name, params)]
108
+ [rator, first_expr, insert_recur_into_expr(second_expr, name,
109
+ params, loop_var)]
107
110
  else
108
111
  sexp
109
112
  end
@@ -40,7 +40,7 @@ module Klam
40
40
  emit_symbol(sexp)
41
41
  when String
42
42
  emit_string(sexp)
43
- when Klam::Variable, Numeric, true, false
43
+ when Klam::Constant, Klam::Variable, Numeric, true, false
44
44
  sexp.to_s
45
45
  when Array
46
46
  if sexp.empty?
@@ -68,6 +68,8 @@ module Klam
68
68
  emit_trap_error(form)
69
69
  when :do
70
70
  emit_do(form)
71
+ when :"[DEFUN-CLOSURE]"
72
+ emit_defun_closure(form)
71
73
  when :"[FIX-VARS]"
72
74
  emit_fix_vars(form)
73
75
  when :"[LOOP]"
@@ -129,7 +131,28 @@ module Klam
129
131
  @eigenclass.rename_method(:$4, $1)
130
132
  @arities[$1] = $5
131
133
  @curried_methods.delete($1)
132
- @loop_cache.delete($1)
134
+ $1
135
+ EOT
136
+ end
137
+
138
+ def emit_defun_closure(form)
139
+ _, name, params, body = form
140
+ name_rb = emit_ruby(name)
141
+ params_rb = params.map { |param| emit_ruby(param) }
142
+ body_rb = emit_ruby(body)
143
+
144
+ # Some valid Kl function names (e.g. foo-bar) are not valid when used
145
+ # with Ruby's def syntax. They will work with define_method, but the
146
+ # resulting methods are slower than if they had been defined via def.
147
+ # To maximize performance, methods are defined with def and then
148
+ # renamed to their intended name afterwards.
149
+ mangled_name = ('__klam_fn_' + name.to_s.gsub(/[^a-zA-Z0-9]/, '_')).intern
150
+ mangled_name_rb = emit_ruby(mangled_name)
151
+ render_string(<<-EOT, name_rb, params_rb, body_rb, mangled_name_rb, params.size)
152
+ @eigenclass.def_method($4, -> ($2) { $3 })
153
+ @eigenclass.rename_method($4, $1)
154
+ @arities[$1] = $5
155
+ @curried_methods.delete($1)
133
156
  $1
134
157
  EOT
135
158
  end
@@ -173,22 +196,36 @@ module Klam
173
196
  end
174
197
 
175
198
  def emit_loop(form)
176
- name_rb = emit_ruby(form[1])
177
- params_rb = form[2].map {|v| emit_ruby(v)}
178
- expr_rb = emit_ruby(form[3])
179
- render_string('(@loop_cache[$1] ||= ::Kernel.lambda { |$2| $3 }).call($2)', name_rb,
180
- params_rb, expr_rb)
199
+ _, loop_var, expr = form
200
+ val_var = fresh_variable
201
+
202
+ expr_rb = emit_ruby(expr)
203
+ loop_var_rb = emit_ruby(loop_var)
204
+ val_var_rb = emit_ruby(val_var)
205
+
206
+ render_string(<<-EOT, loop_var_rb, val_var_rb, expr_rb)
207
+ $1 = false
208
+ $2 = nil
209
+ begin
210
+ $1 = false
211
+ $2 = $3
212
+ end while $1
213
+ $2
214
+ EOT
181
215
  end
182
216
 
183
217
  def emit_recur(form)
184
- _, params, new_value_exprs = form
218
+ _, params, new_value_exprs, loop_var = form
219
+ loop_var_rb = emit_ruby(loop_var)
220
+
185
221
  if params.size > 0
186
222
  params_rb = params.map { |param| emit_ruby(param) }
187
223
  new_value_exprs_rb = new_value_exprs.map { |expr| emit_ruby(expr) }
188
224
 
189
- render_string('(($1 = $2); redo)', params_rb, new_value_exprs_rb)
225
+ render_string('(($1 = $2); $3 = true)', params_rb,
226
+ new_value_exprs_rb, loop_var_rb)
190
227
  else
191
- 'redo'
228
+ render_string('$1 = true', loop_var_rb)
192
229
  end
193
230
  end
194
231
 
@@ -16,12 +16,14 @@ module Klam
16
16
  include Klam::CompilationStages::ConvertPartialApplicationsToLambdas
17
17
  include Klam::CompilationStages::CurryAbstractionApplications
18
18
  include Klam::CompilationStages::MakeAbstractionsMonadic
19
+ include Klam::CompilationStages::ConstantizeConstructedConstants
19
20
  include Klam::CompilationStages::ConvertSelfTailCallsToLoops
20
21
  include Klam::CompilationStages::EmitRuby
21
22
 
22
23
  def initialize(environment)
23
24
  @environment = environment
24
- @generator = Klam::VariableGenerator.new
25
+ @constant_generator = Klam::ConstantGenerator.new
26
+ @variable_generator = Klam::VariableGenerator.new
25
27
  @ruby_interop_syntax_enabled = false
26
28
  end
27
29
 
@@ -36,6 +38,7 @@ module Klam
36
38
  :convert_partial_applications_to_lambdas,
37
39
  :curry_abstraction_applications,
38
40
  :make_abstractions_monadic,
41
+ :constantize_constructed_constants,
39
42
  :convert_self_tail_calls_to_loops,
40
43
  :emit_ruby
41
44
  ]
@@ -66,11 +69,12 @@ module Klam
66
69
  @environment.__arity(sym)
67
70
  end
68
71
 
69
- # Returns a new Klam::Variable object that is unique within this
70
- # instance of the compiler. Variables are never used in a global
71
- # context in Kl, so this is sufficient to avoid collisions.
72
+ def fresh_constant
73
+ @constant_generator.next
74
+ end
75
+
72
76
  def fresh_variable
73
- @generator.next
77
+ @variable_generator.next
74
78
  end
75
79
  end
76
80
  end
@@ -0,0 +1,21 @@
1
+ module Klam
2
+ class Constant
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def to_s
10
+ name
11
+ end
12
+
13
+ def ==(other)
14
+ other.kind_of?(Constant) && name == other.name
15
+ end
16
+
17
+ def hash
18
+ name.hash
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Klam
2
+ class ConstantGenerator
3
+ def initialize
4
+ @index = 0
5
+ end
6
+
7
+ def next
8
+ @index += 1
9
+ Klam::Constant.new('__KLAMC_%03d' % @index)
10
+ end
11
+ end
12
+ end
@@ -23,7 +23,6 @@ module Klam
23
23
 
24
24
  @arities = ::Hash.new { |h, k| h[k] = __arity(k) }
25
25
  @curried_methods = ::Hash.new { |h, k| h[k] = __method(k).to_proc.curry }
26
- @loop_cache = {}
27
26
 
28
27
  # Grab a handle to this object's eigenclass for use later when the
29
28
  # compiled code needs to reference it. It is used, e.g., when renaming
@@ -58,6 +57,11 @@ module Klam
58
57
  end
59
58
 
60
59
  class << self
60
+ # Define method is private, so open it up
61
+ def def_method(name, proc)
62
+ define_method(name, proc)
63
+ end
64
+
61
65
  def rename_method(old_name, new_name)
62
66
  alias_method(new_name, old_name)
63
67
  remove_method(old_name)
@@ -6,7 +6,7 @@ module Klam
6
6
 
7
7
  def next
8
8
  @index += 1
9
- Klam::Variable.new('__KLAM_%03d' % @index)
9
+ Klam::Variable.new('__KLAMV_%03d' % @index)
10
10
  end
11
11
  end
12
12
  end
@@ -1,3 +1,3 @@
1
1
  module Klam
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
@@ -17,11 +17,14 @@ describe 'extension: Ruby interop primitives', :type => :functional do
17
17
  eval_kl('(set vec (absvector 2))')
18
18
  result = eval_kl <<-EOKL
19
19
  (let V (absvector 2)
20
- (do (rb-send-block (cons 1 (cons 2 ())) each_with_index
20
+ (do (rb-send-block (cons 37 (cons 42 ())) each_with_index
21
21
  (lambda X (lambda Y (address-> V Y X))))
22
22
  V))
23
23
  EOKL
24
- expect(result).to eq([1, 2])
24
+ expected = Klam::Absvector.new(2)
25
+ expected.store(0, 37)
26
+ expected.store(1, 42)
27
+ expect(result).to eq(expected)
25
28
  end
26
29
 
27
30
  it 'supports symbols as blocks' do
@@ -4,9 +4,9 @@ describe Klam::Absvector do
4
4
  describe '#to_a' do
5
5
  it 'skips the first element' do
6
6
  a = Klam::Absvector.new(3)
7
- a[0] = 1
8
- a[1] = 2
9
- a[2] = 3
7
+ a.store(0, 1)
8
+ a.store(1, 2)
9
+ a.store(2, 3)
10
10
  expect(a.to_a).to eq([2,3])
11
11
  end
12
12
  end
@@ -14,9 +14,9 @@ describe Klam::Absvector do
14
14
  describe '#each' do
15
15
  it 'skips the first element' do
16
16
  a = Klam::Absvector.new(3)
17
- a[0] = 1
18
- a[1] = 2
19
- a[2] = 3
17
+ a.store(0, 1)
18
+ a.store(1, 2)
19
+ a.store(2, 3)
20
20
  b = []
21
21
  a.each { |x| b << x }
22
22
  expect(b).to eq([2,3])
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Klam::CompilationStages::ConstantizeConstructedConstants do
5
+ include Klam::CompilationStages::ConstantizeConstructedConstants
6
+
7
+ # ConstantizeConstructedConstants requires this function to be defined
8
+ # by its including class.
9
+ def fresh_constant
10
+ @generator.next
11
+ end
12
+
13
+ def reset_generator
14
+ @generator = Klam::ConstantGenerator.new
15
+ end
16
+
17
+ before(:each) do
18
+ reset_generator
19
+ end
20
+
21
+ it 'constantizes lists of constants' do
22
+ const1 = fresh_constant
23
+ const2 = fresh_constant
24
+ expr = [:defun, :f, [],
25
+ [:cons, 1, [:cons, 2, []]]]
26
+
27
+ expected = [:let, const1, [:cons, 2, []],
28
+ [:let, const2, [:cons, 1, const1],
29
+ [:"[DEFUN-CLOSURE]", :f, [],
30
+ const2]]]
31
+
32
+ reset_generator
33
+ expect(constantize_constructed_constants(expr)).to eq(expected)
34
+ end
35
+ end
@@ -2,32 +2,48 @@ require 'spec_helper'
2
2
 
3
3
  describe Klam::CompilationStages::ConvertSelfTailCallsToLoops do
4
4
  include Klam::CompilationStages::ConvertSelfTailCallsToLoops
5
+
6
+ # ConvertSelfTailCallsToLoops requires this function to be defined
7
+ # by its including class.
8
+ def fresh_variable
9
+ @generator.next
10
+ end
11
+
12
+ def reset_generator
13
+ @generator = Klam::VariableGenerator.new
14
+ end
15
+
16
+ before(:each) do
17
+ reset_generator
18
+ end
19
+
5
20
  it 'converts self tail calls to loop/recur' do
6
- # For simplicity the vars below are symbols, but will be
7
- # Klam::Variables in practice. They do not come into play for
8
- # the conversion, though.
21
+ loop_var = fresh_variable
9
22
  expr = [:defun, :f, [:X, :Y],
10
23
  [:if, true,
11
24
  [:f, 7, 8],
12
25
  [:let, :Z, 37,
13
26
  [:f, :Z, 99]]]]
14
27
  expected = [:defun, :f, [:X, :Y],
15
- [:"[LOOP]", :f, [:X, :Y],
28
+ [:"[LOOP]", loop_var,
16
29
  [:if, true,
17
- [:"[RECUR]", [:X, :Y], [7, 8]],
30
+ [:"[RECUR]", [:X, :Y], [7, 8], loop_var],
18
31
  [:let, :Z, 37,
19
- [:"[RECUR]", [:X, :Y], [:Z, 99]]]]]]
32
+ [:"[RECUR]", [:X, :Y], [:Z, 99], loop_var]]]]]
20
33
 
34
+ reset_generator
21
35
  expect(convert_self_tail_calls_to_loops(expr)).to eq(expected)
22
36
  end
23
37
 
24
38
  it 'does not convert partial calls' do
39
+ loop_var = fresh_variable
25
40
  expr = [:defun, :f, [:X, :Y],
26
41
  [:f, :X]]
27
42
  expected = [:defun, :f, [:X, :Y],
28
- [:"[LOOP]", :f, [:X, :Y],
43
+ [:"[LOOP]", loop_var,
29
44
  [:f, :X]]]
30
45
 
46
+ reset_generator
31
47
  expect(convert_self_tail_calls_to_loops(expr)).to eq(expected)
32
48
  end
33
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Spurrier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-25 00:00:00.000000000 Z
11
+ date: 2015-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -111,6 +111,7 @@ files:
111
111
  - lib/klam.rb
112
112
  - lib/klam/absvector.rb
113
113
  - lib/klam/compilation_stages.rb
114
+ - lib/klam/compilation_stages/constantize_constructed_constants.rb
114
115
  - lib/klam/compilation_stages/convert_freezes_to_lambdas.rb
115
116
  - lib/klam/compilation_stages/convert_lexical_variables.rb
116
117
  - lib/klam/compilation_stages/convert_partial_applications_to_lambdas.rb
@@ -124,6 +125,8 @@ files:
124
125
  - lib/klam/compilation_stages/strip_type_declarations.rb
125
126
  - lib/klam/compiler.rb
126
127
  - lib/klam/cons.rb
128
+ - lib/klam/constant.rb
129
+ - lib/klam/constant_generator.rb
127
130
  - lib/klam/converters.rb
128
131
  - lib/klam/converters/.list.rb.swp
129
132
  - lib/klam/converters/list.rb
@@ -159,6 +162,7 @@ files:
159
162
  - spec/functional/tail_call_optimization_spec.rb
160
163
  - spec/spec_helper.rb
161
164
  - spec/unit/klam/absvector_spec.rb
165
+ - spec/unit/klam/compilation_stages/constantize_constructed_constants_spec.rb
162
166
  - spec/unit/klam/compilation_stages/convert_lexical_variables_spec.rb
163
167
  - spec/unit/klam/compilation_stages/convert_self_tail_calls_to_loops_spec.rb
164
168
  - spec/unit/klam/compilation_stages/curry_abstraction_applications_spec.rb
@@ -213,6 +217,7 @@ test_files:
213
217
  - spec/functional/tail_call_optimization_spec.rb
214
218
  - spec/spec_helper.rb
215
219
  - spec/unit/klam/absvector_spec.rb
220
+ - spec/unit/klam/compilation_stages/constantize_constructed_constants_spec.rb
216
221
  - spec/unit/klam/compilation_stages/convert_lexical_variables_spec.rb
217
222
  - spec/unit/klam/compilation_stages/convert_self_tail_calls_to_loops_spec.rb
218
223
  - spec/unit/klam/compilation_stages/curry_abstraction_applications_spec.rb