klam 0.0.7 → 0.0.8

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 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