klam 0.0.1

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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +0 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +1 -0
  6. data/LICENSE.txt +19 -0
  7. data/PROGRESS.asciidoc +105 -0
  8. data/README.asciidoc +11 -0
  9. data/Rakefile +5 -0
  10. data/klam.gemspec +28 -0
  11. data/lib/klam.rb +16 -0
  12. data/lib/klam/compilation_stages.rb +4 -0
  13. data/lib/klam/compilation_stages/convert_freezes_to_lambdas.rb +17 -0
  14. data/lib/klam/compilation_stages/convert_lexical_variables.rb +79 -0
  15. data/lib/klam/compilation_stages/convert_partial_applications_to_lambdas.rb +35 -0
  16. data/lib/klam/compilation_stages/convert_self_tail_calls_to_loops.rb +147 -0
  17. data/lib/klam/compilation_stages/curry_abstraction_applications.rb +33 -0
  18. data/lib/klam/compilation_stages/emit_ruby.rb +232 -0
  19. data/lib/klam/compilation_stages/kl_to_internal_representation.rb +21 -0
  20. data/lib/klam/compilation_stages/make_abstractions_monadic.rb +26 -0
  21. data/lib/klam/compilation_stages/make_abstractions_variadic.rb +23 -0
  22. data/lib/klam/compilation_stages/simplify_boolean_operations.rb +74 -0
  23. data/lib/klam/compilation_stages/strip_type_declarations.rb +24 -0
  24. data/lib/klam/compiler.rb +63 -0
  25. data/lib/klam/cons.rb +18 -0
  26. data/lib/klam/converters.rb +4 -0
  27. data/lib/klam/converters/.list.rb.swp +0 -0
  28. data/lib/klam/converters/list.rb +29 -0
  29. data/lib/klam/environment.rb +61 -0
  30. data/lib/klam/error.rb +4 -0
  31. data/lib/klam/lexer.rb +185 -0
  32. data/lib/klam/primitives.rb +4 -0
  33. data/lib/klam/primitives/arithmetic.rb +49 -0
  34. data/lib/klam/primitives/assignments.rb +13 -0
  35. data/lib/klam/primitives/boolean_operations.rb +22 -0
  36. data/lib/klam/primitives/error_handling.rb +19 -0
  37. data/lib/klam/primitives/generic_functions.rb +19 -0
  38. data/lib/klam/primitives/lists.rb +23 -0
  39. data/lib/klam/primitives/streams.rb +32 -0
  40. data/lib/klam/primitives/strings.rb +58 -0
  41. data/lib/klam/primitives/symbols.rb +16 -0
  42. data/lib/klam/primitives/time.rb +19 -0
  43. data/lib/klam/primitives/vectors.rb +26 -0
  44. data/lib/klam/reader.rb +46 -0
  45. data/lib/klam/template.rb +38 -0
  46. data/lib/klam/variable.rb +21 -0
  47. data/lib/klam/variable_generator.rb +12 -0
  48. data/lib/klam/version.rb +3 -0
  49. data/spec/functional/application_spec.rb +94 -0
  50. data/spec/functional/atoms_spec.rb +56 -0
  51. data/spec/functional/extensions/do_spec.rb +22 -0
  52. data/spec/functional/primitives/assignments_spec.rb +38 -0
  53. data/spec/functional/primitives/boolean_operations_spec.rb +133 -0
  54. data/spec/functional/primitives/error_handling_spec.rb +22 -0
  55. data/spec/functional/primitives/generic_functions_spec.rb +82 -0
  56. data/spec/functional/tail_call_optimization_spec.rb +71 -0
  57. data/spec/spec_helper.rb +27 -0
  58. data/spec/unit/klam/compilation_stages/convert_lexical_variables_spec.rb +58 -0
  59. data/spec/unit/klam/compilation_stages/convert_self_tail_calls_to_loops_spec.rb +33 -0
  60. data/spec/unit/klam/compilation_stages/curry_abstraction_applications_spec.rb +19 -0
  61. data/spec/unit/klam/compilation_stages/make_abstractions_variadic_spec.rb +12 -0
  62. data/spec/unit/klam/converters/list_spec.rb +57 -0
  63. data/spec/unit/klam/lexer_spec.rb +149 -0
  64. data/spec/unit/klam/primitives/arithmetic_spec.rb +153 -0
  65. data/spec/unit/klam/primitives/boolean_operations_spec.rb +39 -0
  66. data/spec/unit/klam/primitives/error_handling_spec.rb +19 -0
  67. data/spec/unit/klam/primitives/lists_spec.rb +49 -0
  68. data/spec/unit/klam/primitives/strings_spec.rb +53 -0
  69. data/spec/unit/klam/primitives/symbols_spec.rb +19 -0
  70. data/spec/unit/klam/primitives/time_spec.rb +16 -0
  71. data/spec/unit/klam/primitives/vectors_spec.rb +55 -0
  72. data/spec/unit/klam/reader_spec.rb +47 -0
  73. data/spec/unit/klam/template_spec.rb +28 -0
  74. data/spec/unit/klam/variable_spec.rb +22 -0
  75. data/spec/unit/klam/version_spec.rb +7 -0
  76. metadata +225 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d6bfb59d34a33d476f312e82821a8daa9c23a766
4
+ data.tar.gz: 1e17a5d167569095f128e551504e48ccfc5c3f84
5
+ SHA512:
6
+ metadata.gz: f2adf161608625ab5b3a7c7d38cb406df8152d98a55b85fa7d77f338c543f7c21a98df1923d507b52e863a2aeb47f3672dce37b18e3c28cc6226e1664b036b97
7
+ data.tar.gz: 2949ef6dcb1086f138bdd8059cf8419de98eca82c2054754244050511147c6d1119bda3969397e0b436b8b1a49dde962a470d1d5f299cb1e146b7cdcba9e46cd
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ klam-*.gem
data/.rspec ADDED
File without changes
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.1.1
7
+ - jruby-1.7.17
8
+ - rbx
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Greg Spurrier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/PROGRESS.asciidoc ADDED
@@ -0,0 +1,105 @@
1
+ Klam Implementation Progress
2
+ ============================
3
+
4
+ This document tracks the progress of Klam towards a feature-complete 1.0.0
5
+ release.
6
+
7
+ Implemented
8
+ -----------
9
+
10
+ Language Features
11
+ ~~~~~~~~~~~~~~~~~
12
+ * Reader
13
+ * Environment
14
+ ** Global assignment namespace
15
+ ** Global function namespace
16
+ * Code Generation
17
+ ** Atoms
18
+ ** Abstractions
19
+ ** Functions
20
+ ** Special forms (see marked primimitives below)
21
+ ** Application
22
+ *** Functions and abstractions:
23
+ **** Full application
24
+ **** Partial application
25
+ **** Currying
26
+ ** Self tail-call optimization
27
+
28
+ Primitives
29
+ ~~~~~~~~~~
30
+ As defined in
31
+ http://www.shenlanguage.org/learn-shen/shendoc.htm#The%20Primitive%20Functions%20of%20K%20Lambda[The
32
+ Primitive Functions of Kl]:
33
+
34
+ * Boolean Operations
35
+ ** +if+ (special form and normal function)
36
+ ** +and+ (special form and normal function)
37
+ ** +or+ (special form and normal function)
38
+ ** +cond+ (special form)
39
+ * Symbols
40
+ ** +intern+
41
+ * Strings
42
+ ** +pos+
43
+ ** +tlstr+
44
+ ** +cn+
45
+ ** +str+
46
+ ** +string?+
47
+ ** +n\->string+
48
+ ** +string\->n+
49
+ * Assignments
50
+ ** +set+
51
+ ** +value+
52
+ * Lists
53
+ ** +cons+
54
+ ** +hd+
55
+ ** +tl+
56
+ ** +cons?+
57
+ * Error Handling
58
+ ** +simple-error+
59
+ ** +trap-error+ (special form)
60
+ ** +error-to-string+
61
+ * Generic Functions
62
+ ** +defun+ (special form)
63
+ ** +lambda+ (special form)
64
+ ** +let+ (special form)
65
+ ** +=+
66
+ ** +eval-kl+
67
+ ** +freeze+ (special form)
68
+ ** +type+ (special form)
69
+ * Vectors
70
+ ** +absvector+
71
+ ** +address\->+
72
+ ** +\<-address+
73
+ ** +absvector?+
74
+ * Streams and I/O
75
+ ** +write-byte+
76
+ ** +read-byte+
77
+ ** +open+
78
+ ** +close+
79
+ * Time
80
+ ** +get-time+
81
+ * Arithmetic
82
+ ** +++
83
+ ** +-+
84
+ ** +*+
85
+ ** +/+
86
+ ** +>+
87
+ ** +<+
88
+ ** +>=+
89
+ ** +\<=+
90
+ ** +number?+
91
+
92
+ Ruby Interoperation
93
+ ~~~~~~~~~~~~~~~~~~~
94
+ * Ruby \<\-> Kl converters
95
+ ** Array \<\-> List
96
+
97
+ Not Yet Implemented
98
+ -------------------
99
+
100
+ Ruby Interoperation
101
+ ~~~~~~~~~~~~~~~~~~~
102
+ * Invoking Kl functions from Ruby
103
+ * Invoking Ruby functions from Kl
104
+ * Ruby \<\-> Kl converters
105
+ ** Array \<\-> Absvector
data/README.asciidoc ADDED
@@ -0,0 +1,11 @@
1
+ Klam
2
+ ====
3
+
4
+ Klam is a Ruby implementation of Kl, the small Lisp on top of which the
5
+ http://www.shenlanguage.org[Shen] programming language is implemented.
6
+
7
+ image:https://travis-ci.org/gregspurrier/klam.png?branch=master["Build Status", link="https://travis-ci.org/gregspurrier/klam"]
8
+
9
+ License
10
+ -------
11
+ Klam is Copyright (C) 2014-2015 Greg Spurrier. It is distributed under the terms of the MIT License. See link:LICENSE.txt[] for the details.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :default => :spec
data/klam.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ require 'klam/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'klam'
8
+ s.version = Klam::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.license = 'MIT'
11
+ s.authors = ['Greg Spurrier']
12
+ s.email = ['greg@sourcematters.org']
13
+ s.homepage = 'https://github.com/gregspurrier/klam'
14
+ s.summary = %q{Klam is a Ruby implementation of the Kl.}
15
+ s.description = %q{Klam is a Ruby implementation of Kl, the small Lisp on top of which the Shen programming language is implemented.}
16
+
17
+ s.required_ruby_version = '>= 1.9.3'
18
+
19
+ s.add_development_dependency 'rake', '~> 10.4', '>= 10.4'
20
+ s.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
21
+ s.add_development_dependency 'rspec-autotest', '~> 1.0', '>= 1.0.0'
22
+ s.add_development_dependency 'ZenTest', '~> 4.11', '>= 4.11'
23
+
24
+ git_files = `git ls-files`.split("\n") rescue ''
25
+ s.files = git_files
26
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
+ s.require_paths = ['lib']
28
+ end
data/lib/klam.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Klam
2
+ end
3
+
4
+ require 'klam/version'
5
+ require 'klam/error'
6
+ require 'klam/template'
7
+ require 'klam/variable'
8
+ require 'klam/variable_generator'
9
+ require 'klam/cons'
10
+ require 'klam/primitives'
11
+ require 'klam/converters'
12
+ require 'klam/compilation_stages'
13
+ require 'klam/compiler'
14
+ require 'klam/environment'
15
+ require 'klam/lexer'
16
+ require 'klam/reader'
@@ -0,0 +1,4 @@
1
+ this_dir = File.dirname(__FILE__)
2
+ Dir[File.join(this_dir, 'compilation_stages', '*.rb')].each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,17 @@
1
+ module Klam
2
+ module CompilationStages
3
+ module ConvertFreezesToLambdas
4
+ def convert_freezes_to_lambdas(sexp)
5
+ if sexp.instance_of?(Array)
6
+ if sexp[0] == :freeze
7
+ [:lambda, [], convert_freezes_to_lambdas(sexp[1])]
8
+ else
9
+ sexp.map { |form| convert_freezes_to_lambdas(form) }
10
+ end
11
+ else
12
+ sexp
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,79 @@
1
+ module Klam
2
+ module CompilationStages
3
+ # Convert Lexical Variables
4
+ #
5
+ # Lexically bound variable names are symbols in Kl. In Ruby, variable and
6
+ # parameter names are represented differently than symbol literals. The
7
+ # latter are indicated with a leading ':' (e.g., :foo) and allow an
8
+ # extended set of characters when using the :"foo-bar" literal syntax.
9
+ # Ruby variables have no such provision for using additional characters.
10
+ #
11
+ # Therefore, if lexical variables remain as symbols, the emission of Ruby
12
+ # code for symbols would be complicated by the need to differentiate
13
+ # between symbol literals and lexical variables and to transform Kl
14
+ # variable names to legal Ruby variable names. Instead, this stage converts
15
+ # lexical variables to instances of Klam::Variable. This is essentially
16
+ # alpha conversion and the names of Klam::Variable are generated to be
17
+ # locally unique and using only allowed Ruby variable names.
18
+ #
19
+ # Alpha conversion also avoids potential problems when the let primitive is
20
+ # used to re-bind an already bound lexical var.
21
+ module ConvertLexicalVariables
22
+ def convert_lexical_variables(sexp)
23
+ convert_lexical_vars(sexp, {})
24
+ end
25
+
26
+ private
27
+
28
+ def convert_lexical_vars(sexp, var_map)
29
+ if sexp.instance_of? Array
30
+ case sexp[0]
31
+ when :defun
32
+ convert_lexical_vars_defun(sexp, var_map)
33
+ when :lambda
34
+ convert_lexical_vars_lambda(sexp, var_map)
35
+ when :let
36
+ convert_lexical_vars_let(sexp, var_map)
37
+ else
38
+ sexp.map { |form| convert_lexical_vars(form, var_map) }
39
+ end
40
+ else
41
+ var_map[sexp] || sexp
42
+ end
43
+ end
44
+
45
+ def convert_lexical_vars_defun(sexp, var_map)
46
+ rator, name, params, expr = sexp
47
+ var_map = extend_var_map(var_map, params)
48
+
49
+ params = params.map { |p| var_map[p] }
50
+ expr = convert_lexical_vars(expr, var_map)
51
+ [rator, name, params, expr]
52
+ end
53
+
54
+ def convert_lexical_vars_lambda(sexp, var_map)
55
+ rator, params, expr = sexp
56
+ var_map = extend_var_map(var_map, params)
57
+
58
+ params = params.map { |p| var_map[p] }
59
+ expr = convert_lexical_vars(expr, var_map)
60
+ [rator, params, expr]
61
+ end
62
+
63
+ def convert_lexical_vars_let(sexp, var_map)
64
+ rator, var_sym, value_expr, expr = sexp
65
+ extended_var_map = extend_var_map(var_map, [var_sym])
66
+
67
+ value_expr = convert_lexical_vars(value_expr, var_map)
68
+ expr = convert_lexical_vars(expr, extended_var_map)
69
+ [rator, extended_var_map[var_sym], value_expr, expr]
70
+ end
71
+
72
+ def extend_var_map(var_map, syms)
73
+ new_var_map = Hash.new { |_, k| var_map[k] }
74
+ syms.each { |sym| new_var_map[sym] = fresh_variable }
75
+ new_var_map
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,35 @@
1
+ module Klam
2
+ module CompilationStages
3
+ module ConvertPartialApplicationsToLambdas
4
+ def convert_partial_applications_to_lambdas(sexp)
5
+ if sexp.instance_of?(Array)
6
+ rator = sexp[0]
7
+ if rator.kind_of?(Symbol)
8
+ rands = sexp[1..-1]
9
+ converted_rands = rands.map do |rand|
10
+ convert_partial_applications_to_lambdas(rand)
11
+ end
12
+
13
+ rator_arity = arity(rator)
14
+ if rator_arity == -1 || rator_arity == rands.length
15
+ converted_rands.unshift(rator)
16
+ elsif rator_arity > rands.length
17
+ # Partial application
18
+ vars = (rator_arity - rands.length).times.map { fresh_variable }
19
+ [:lambda, vars, [rator] + converted_rands + vars]
20
+ else
21
+ # Uncurrying
22
+ now_rands = rands[0...rator_arity]
23
+ deferred_rands = rands[rator_arity..-1]
24
+ [[rator] + now_rands, *deferred_rands]
25
+ end
26
+ else
27
+ sexp.map { |form| convert_partial_applications_to_lambdas(form) }
28
+ end
29
+ else
30
+ sexp
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,147 @@
1
+ module Klam
2
+ module CompilationStages
3
+ # Convert Self Tail Calls To Loops
4
+ #
5
+ # The Shen specification requires that self tail calls be optimized. This
6
+ # stage transforms defuns containing self tail calls into a loop/recur form
7
+ # akin to Clojure's loop/recur. The code emitter knows how to handle these
8
+ # synthetic special forms.
9
+ #
10
+ # For example, this code with tail calls:
11
+ #
12
+ # (defun fact-iter (N Accum)
13
+ # (if (= N 0)
14
+ # Accum
15
+ # (fact-iter (- N 1) (* N Accum))))
16
+ #
17
+ # is converted to:
18
+ #
19
+ # (defun fact-iter (N Accum)
20
+ # ([LOOP]
21
+ # (if (= N 0)
22
+ # Accum
23
+ # ([RECUR] (N Accum) ((- N 1) (* N Accum))))))
24
+ #
25
+ # The variable names are carried along with their new binding expressions
26
+ # in the [RECUR] form so that they can be rebound before looping.
27
+ #
28
+ # Note that this rebinding of variables causes a wrinkle with respect to
29
+ # closures created in the body. Those closures should close over the value
30
+ # at the point of closing rather than the ultimate values after rebinding.
31
+ # To solve this, another sythetic primitive, [FIX-VARS], is used to wrap
32
+ # these cases and the emitted Ruby code samples those variables.
33
+ #
34
+ # This compilation stage must come _after_ SimplifyBooleanOperations and
35
+ # ConvertFreezesToLambdas.
36
+ module ConvertSelfTailCallsToLoops
37
+ def convert_self_tail_calls_to_loops(sexp)
38
+ # Self tail calls can only be found in functions defined by defun.
39
+ # defun is only allowed at the top level, so there's no need to
40
+ # walk the tree if this form is not a defun.
41
+ if sexp.kind_of?(Array) && sexp[0] == :defun
42
+ _, name, params, body = sexp
43
+ if contains_self_tail_calls?(body, name)
44
+ insert_loop_and_recur_into_defun(fix_vars(sexp, params))
45
+ else
46
+ sexp
47
+ end
48
+ else
49
+ sexp
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def contains_self_tail_calls?(body, name)
56
+ if body.instance_of?(Array)
57
+ case body[0]
58
+ when name
59
+ true
60
+ when :if
61
+ _, _, true_expr, false_expr = body
62
+ contains_self_tail_calls?(true_expr, name) ||
63
+ contains_self_tail_calls?(false_expr, name)
64
+ when :let
65
+ contains_self_tail_calls?(body[3], name)
66
+ when :do
67
+ contains_self_tail_calls?(body[2], name)
68
+ else
69
+ false
70
+ end
71
+ else
72
+ false
73
+ end
74
+ end
75
+
76
+ def insert_loop_and_recur_into_defun(form)
77
+ rator, name, params, body = form
78
+ body_with_loop = [:"[LOOP]", name, params,
79
+ insert_recur_into_expr(body, name, params)]
80
+ [rator, name, params, body_with_loop]
81
+ end
82
+
83
+ def insert_recur_into_expr(sexp, name, params)
84
+ if sexp.instance_of?(Array)
85
+ case sexp[0]
86
+ when name
87
+ if sexp.length - 1 == params.length
88
+ [:"[RECUR]", params, sexp[1..-1]]
89
+ else
90
+ sexp
91
+ end
92
+ when :if
93
+ rator, test_expr, true_expr, false_expr = sexp
94
+ [rator,
95
+ test_expr,
96
+ insert_recur_into_expr(true_expr, name, params),
97
+ insert_recur_into_expr(false_expr, name, params)]
98
+ when :let
99
+ rator, var, val_expr, body_expr = sexp
100
+ [rator,
101
+ var,
102
+ val_expr,
103
+ insert_recur_into_expr(body_expr, name, params)]
104
+ when :do
105
+ rator, first_expr, second_expr = sexp
106
+ [rator, first_expr, insert_recur_into_expr(second_expr, name, params)]
107
+ else
108
+ sexp
109
+ end
110
+ else
111
+ sexp
112
+ end
113
+ end
114
+
115
+ def fix_vars(sexp, params)
116
+ if sexp.kind_of?(Array)
117
+ if sexp[0] == :lambda
118
+ referenced_vars = vars_referenced_in(sexp, params)
119
+ if referenced_vars.empty?
120
+ sexp
121
+ else
122
+ [:"[FIX-VARS]", referenced_vars, sexp]
123
+ end
124
+ else
125
+ # Local variables must also be fixed
126
+ if sexp[0] == :let
127
+ params = params + [sexp[1]]
128
+ end
129
+ sexp.map { |x| fix_vars(x, params) }
130
+ end
131
+ else
132
+ sexp
133
+ end
134
+ end
135
+
136
+ def vars_referenced_in(sexp, vars)
137
+ if sexp.kind_of?(Array)
138
+ sexp.map { |x| vars_referenced_in(x, vars) }.flatten.uniq
139
+ elsif vars.include?(sexp)
140
+ [sexp]
141
+ else
142
+ []
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end