heist 0.2.1 → 0.3.0

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.
@@ -63,6 +63,13 @@ module Heist
63
63
  stack = @scope.runtime.stack
64
64
  stack << Frame.new(@current.car, @scope)
65
65
 
66
+ # If it's a Vector (it will be unquoted if it has arrived here, copy
67
+ # it and return it. We cannot freeze it for fear of breaking macros,
68
+ # and we must copy it so that vector-set! cannot modify the parse tree.
69
+ when Vector then
70
+ @complete = true
71
+ @expression.dup
72
+
66
73
  # If the expression is an +Identifier+, just look it up.
67
74
  when Identifier then
68
75
  @complete = true
@@ -11,7 +11,8 @@ module Heist
11
11
  #
12
12
  class Runtime
13
13
 
14
- %w[ data/expression data/identifier data/cons
14
+ %w[ data/expression data/identifier data/character
15
+ data/cons data/vector
15
16
  callable/function callable/syntax callable/macro callable/continuation
16
17
  frame stack stackless
17
18
  scope binding
@@ -20,7 +20,7 @@ module Heist
20
20
  # The parent may also be a +Runtime+ instance, indicating that the
21
21
  # new +Scope+ is being used as the top level of a runtime environment.
22
22
  def initialize(parent = {})
23
- @symbols = {}
23
+ @symbols = Trie.new
24
24
  is_runtime = (Runtime === parent)
25
25
  @parent = is_runtime ? {} : parent
26
26
  @runtime = is_runtime ? parent : parent.runtime
@@ -145,14 +145,10 @@ module Heist
145
145
  end
146
146
  alias :exec :eval
147
147
 
148
- # Returns all the variable names visible in the receiving +Scope+ that
149
- # match the given regex +pattern+. Used by the REPL for tab completion.
150
- def grep(pattern)
151
- base = (Scope === @parent) ? @parent.grep(pattern) : []
152
- @symbols.each do |key, value|
153
- base << key if key =~ pattern
154
- end
155
- base.uniq
148
+ # Returns the longest shared prefix match for the given variable name
149
+ # stub, used to support autocompletion in the REPL.
150
+ def longest_prefix(name)
151
+ @symbols.longest_prefix(to_name(name))
156
152
  end
157
153
 
158
154
  # Runs the given Scheme or Ruby definition file in the receiving
@@ -68,6 +68,13 @@ module Heist
68
68
  expression.replace(value.expression)
69
69
  return Frame.new(value.expression, scope)
70
70
 
71
+ # If the expression is a Vector (unquoted if found here), duplicate
72
+ # it and return it. We don't want to freeze unquoted vectors as this
73
+ # stops macros working, but also we don't want to allow procedures
74
+ # such as vector-set! to modify the parse tree.
75
+ when Vector then
76
+ expression.dup
77
+
71
78
  # If the expression is an identifier, look up its value in
72
79
  # the current scope
73
80
  when Identifier then
@@ -0,0 +1,141 @@
1
+ module Heist
2
+
3
+ # A +Trie+ is a special type of key-value store, one in which the keys are all
4
+ # sequences of some kind: they could be strings, or arrays, or lists. We use
5
+ # tries to store symbol tables in Heist because it makes autocompletion easier
6
+ # to implement, and one aim of Heist is to be a pleasant interactive Scheme.
7
+ #
8
+ # In our particular implementation, keys (strings representing variable names)
9
+ # are split into arrays and stored in a series of nested hashtables whose keys
10
+ # are single characters. Strings sharing a common prefix share part of the tree
11
+ # structure. The <tt>Trie#inspect</tt> method provides a good representation
12
+ # of this (values omitted for the sake of brevity):
13
+ #
14
+ # tree = Trie.new
15
+ # tree['foo'] = 1
16
+ #
17
+ # tree #=> {"f"=>{"o"=>{"o"=>{}}}}
18
+ #
19
+ # tree['bar'] = 2
20
+ # tree['baz'] = 3
21
+ #
22
+ # tree #=> {"b"=>{"a"=>{"z"=>{}, "r"=>{}}}, "f"=>{"o"=>{"o"=>{}}}}
23
+ #
24
+ # A trie is a recursive structure; each key in a trie points to another trie.
25
+ # Every trie contains a list of its children (a hash mapping characters to
26
+ # subtries) and may contain a value if it appears at the end of a sequence
27
+ # making up a full key of the root trie.
28
+ #
29
+ # For more information, see http://en.wikipedia.org/wiki/Trie
30
+ #
31
+ class Trie
32
+ attr_accessor :value
33
+
34
+ # A +Trie+ is initialized with an optional +value+. The value may be
35
+ # omitted if the trie does not represent the end of a key sequence.
36
+ def initialize(value = nil)
37
+ @value = value
38
+ @children = {}
39
+ end
40
+
41
+ # Returns +true+ iff the given +key+ is present in the +Trie+. They key
42
+ # must match a complete key sequence that maps to a value, not a partial
43
+ # prefix with no value attached.
44
+ def has_key?(key)
45
+ trie = traverse(key)
46
+ trie and not trie.value.nil?
47
+ end
48
+
49
+ # Returns the value corresponding to +key+, or +nil+ if none is found.
50
+ def [](key)
51
+ trie = traverse(key)
52
+ trie ? trie.value : nil
53
+ end
54
+
55
+ # Assigns +value+ to the given +key+.
56
+ def []=(key, value)
57
+ traverse(key, true).value = value
58
+ end
59
+
60
+ # Walks the +Trie+ structure using the given +key+, returning the
61
+ # resulting subtrie or +nil+ if the key is absent. Pass +true+ as the
62
+ # second argument to create a subtrie for the key if none exists.
63
+ def traverse(key, create_if_absent = false)
64
+ key = convert_key(key)
65
+ return self if key.empty?
66
+ trie = @children[key.first]
67
+ return nil if trie.nil? and not create_if_absent
68
+ trie = @children[key.first] = Trie.new if trie.nil?
69
+ trie.traverse(key[1..-1], create_if_absent)
70
+ end
71
+
72
+ # Returns an array of all the characters used as keys in this +Trie+.
73
+ # That is, this method returns the initial letters of all the keys
74
+ # stored in the +Trie+.
75
+ def prefixes
76
+ @children.keys
77
+ end
78
+
79
+ # Returns +true+ if the +Trie+ has no value and only one child. Used
80
+ # in +longest_prefix+ to find the longest unique prefix from some
81
+ # starting point.
82
+ def singular?
83
+ @value.nil? and @children.size == 1
84
+ end
85
+
86
+ # Returns the longest unique key prefix that starts with +key+. This
87
+ # is used for autocompletion in the REPL. Given a string, this method
88
+ # returns another string such that the start of the output is the
89
+ # same as the input, and the output may contain zero or more additional
90
+ # characters such that all the keys that begin with the input also begin
91
+ # with the output.
92
+ #
93
+ # tree = Trie.new
94
+ # tree['foo'] = 1
95
+ # tree['bar'] = 2
96
+ # tree['baz'] = 3
97
+ #
98
+ # tree.longest_prefix 'f'
99
+ # #=> "foo"
100
+ # tree.longest_prefix 'b'
101
+ # #=> "ba"
102
+ #
103
+ def longest_prefix(key)
104
+ prefix = convert_key(key).dup
105
+ trie = traverse(key)
106
+ return nil if trie.nil?
107
+ while trie.singular?
108
+ next_key = trie.prefixes.first
109
+ prefix << next_key
110
+ trie = trie.traverse(next_key)
111
+ end
112
+ case key
113
+ when String then prefix.join('')
114
+ when Symbol then prefix.join('').to_sym
115
+ else prefix
116
+ end
117
+ end
118
+
119
+ # Returns a string representation of the trie's internal structure.
120
+ # Values are not printed; the main purpose of this method is to inspect
121
+ # the internal tree structure.
122
+ def inspect
123
+ @children.inspect
124
+ end
125
+
126
+ private
127
+
128
+ # Returns an array from the given +key+, making it ready for use as
129
+ # a key sequence.
130
+ def convert_key(key)
131
+ case key
132
+ when Array then key
133
+ when Symbol then key.to_s.split('')
134
+ when String then key.split('')
135
+ when Enumerable then key.entries
136
+ end
137
+ end
138
+ end
139
+
140
+ end
141
+
@@ -10,10 +10,17 @@
10
10
  (assert (not (eqv? 42 #f)))
11
11
  (assert (not (eqv? 42 42.0)))
12
12
 
13
+ (assert (eqv? #\A #\A))
14
+ (assert (not (eqv? #\A #\B)))
15
+ (assert (not (eqv? #\A #\a)))
16
+
13
17
  (assert (eqv? '() '()))
14
18
  (assert (not (eqv? '(1 2) '(1 2))))
15
19
  (assert (not (eqv? '() '(1 2))))
16
20
 
21
+ (assert (not (eqv? #() #())))
22
+ (assert (not (eqv? #(4) #(4))))
23
+
17
24
  (assert (eqv? ceil ceiling))
18
25
 
19
26
 
@@ -40,3 +40,16 @@
40
40
 
41
41
  (assert-equal 720 (do-factorial 6))
42
42
 
43
+
44
+ ; R5RS compliance test -- http://okmij.org/ftp/Scheme/lambda-derived.txt
45
+ (assert-equal '(1 1 1 1 1 1 1)
46
+ (let ((foo (lambda (x) x)))
47
+ (list
48
+ (foo ((lambda () (define a 1) a)))
49
+ (foo (let () (define a 1) a))
50
+ (foo (let* () (define a 1) a))
51
+ (foo (letrec () (define a 1) a))
52
+ (foo (letrec () (define (b) (define a 1) a) (b)))
53
+ (foo (let-syntax () (define a 1) a))
54
+ (foo (letrec-syntax () (define a 1) a)))))
55
+
@@ -1,10 +1,9 @@
1
- (assert (eqv? '() '()))
2
- (assert (not (eqv? '(5) '(5))))
3
-
4
1
  (assert (null? '()))
5
2
  (assert (list? '()))
6
3
  (assert (not (pair? '())))
7
4
 
5
+ (assert (not (vector? '(1 2 3))))
6
+
8
7
  (define foo-list (list (+ 3 2) (* 4 5) 6))
9
8
  (assert (not (eqv? '(5 20 6) foo-list)))
10
9
  (assert (equal? '(5 20 6) foo-list))
@@ -88,12 +88,26 @@
88
88
  [(_ foo bar . rest)
89
89
  rest]))
90
90
  (assert-equal 10 (rest 4 5 + 3 7))
91
+
91
92
  (let-syntax ([foo (syntax-rules ()
92
93
  [(_ expr ...)
93
94
  (list expr ...)])])
94
95
  (assert-equal '(1 2 3) (foo 1 2 3))
95
96
  (assert-raise SyntaxError (foo 1 2 3 . 4)))
96
97
 
98
+ (let-syntax ([foo (syntax-rules ()
99
+ [(_ bindings body ...)
100
+ '(defun (proc . bindings) body ...)])])
101
+
102
+ (assert-equal '(defun (proc x y) (display x) y)
103
+ (foo (x y) (display x) y))
104
+
105
+ (assert-equal '(defun (proc x y . z) z)
106
+ (foo (x y . z) z))
107
+
108
+ (assert-equal '(defun (proc . z) z)
109
+ (foo z z)))
110
+
97
111
 
98
112
  ; Test input execution - example from R5RS
99
113
 
@@ -0,0 +1,110 @@
1
+ (assert (equal? (symbol->string 'foo) "foo"))
2
+ (assert (eqv? (string->symbol "foo") 'foo))
3
+
4
+ (assert (char? #\a))
5
+ (assert (char? #\ ))
6
+ (assert (char? #\)))
7
+ (assert (char? #\tab))
8
+
9
+ (assert (char=? #\F #\F))
10
+ (assert (char=? #\space #\space))
11
+ (assert (char=? #\space #\ ))
12
+ (assert (char=? #\ #\space))
13
+ (assert (not (char=? #\F #\f)))
14
+ (assert (not (char=? #\8 #\f)))
15
+
16
+ (assert (char<? #\A #\B))
17
+ (assert (char<? #\a #\b))
18
+ (assert (char<? #\0 #\9))
19
+
20
+ (assert (char=? (integer->char 97) #\a))
21
+ (assert-equal 100 (char->integer #\d))
22
+
23
+ (assert (char=? #\A (char-upcase #\a)))
24
+ (assert (char=? #\h (char-downcase #\H)))
25
+
26
+ (assert (char-alphabetic? #\A))
27
+ (assert (char-alphabetic? #\Z))
28
+ (assert (char-alphabetic? #\a))
29
+ (assert (char-alphabetic? #\z))
30
+ (assert (not (char-alphabetic? #\0)))
31
+ (assert (not (char-alphabetic? #\[)))
32
+
33
+ (assert (char-numeric? #\0))
34
+ (assert (char-numeric? #\9))
35
+ (assert (not (char-numeric? #\k)))
36
+
37
+ (assert (char-whitespace? #\ ))
38
+ (assert (char-whitespace? #\tab))
39
+ (assert (char-whitespace? #\newline))
40
+ (assert (char-whitespace? #\space))
41
+ (assert (not (char-whitespace? #\s)))
42
+
43
+ (assert (char-ci=? #\A #\a))
44
+ (assert (not (char-ci=? #\A #\b)))
45
+
46
+ (assert (string? "foo"))
47
+ (assert (not (string? 'foo)))
48
+ (assert (not (string? #\a)))
49
+ (assert (not (string? 9)))
50
+
51
+ (assert-equal " " (make-string 4))
52
+ (assert-equal ")))))))" (make-string 7 #\)))
53
+
54
+ (let ([g (lambda () "***")])
55
+ (assert-raise ImmutableError (string-set! (g) 0 #\?))
56
+ (assert-equal "***" (g)))
57
+
58
+ (assert-equal 13 (string-length "smoked salmon"))
59
+ (assert-equal #\o (string-ref "salmon" 4))
60
+ (assert-raise BadIndexError (string-ref "salmon" 7))
61
+
62
+ (let ([s (string-copy "saLMON")])
63
+ (string-set! s 4 #\k)
64
+ (assert-equal "saLMkN" s)
65
+ (assert (eqv? s s))
66
+ (assert (not (eqv? (string-copy s) s)))
67
+ (assert (equal? (string-copy s) s))
68
+ (string-fill! s #\G)
69
+ (assert-equal "GGGGGG" s))
70
+
71
+ (assert-equal "alm" (substring "salmon" 1 4))
72
+
73
+ (assert-equal "foo" (string #\f #\o #\o))
74
+ (assert-equal "" (string))
75
+
76
+ (assert-equal '(#\f #\o #\o) (string->list "foo"))
77
+ (assert-equal "foo" (list->string '(#\f #\o #\o)))
78
+
79
+ (assert-equal "foo bar baz" (string-append "foo " "ba" "r baz" ""))
80
+
81
+ (assert (string=? "foo" "foo"))
82
+ (assert (not (string=? "foo" "Foo")))
83
+ (assert (not (string=? "foo" "food")))
84
+ (assert (not (string=? "food" "foo")))
85
+ (assert (string-ci=? "foo" "Foo"))
86
+ (assert (not (string-ci=? "food" "Fool")))
87
+
88
+ (assert (string<? "abacus" "badger"))
89
+ (assert (string<? "badger" "badges"))
90
+ (assert (string<? "string" "stringify"))
91
+ (assert (not (string<? "stringify" "string")))
92
+ (assert (not (string<? "badger" "badger")))
93
+ (assert (not (string<? "badges" "badger")))
94
+
95
+ (assert (not (string>? "abacus" "badger")))
96
+ (assert (not (string>? "badger" "badges")))
97
+ (assert (not (string>? "string" "stringify")))
98
+ (assert (string>? "stringify" "string"))
99
+ (assert (not (string>? "badger" "badger")))
100
+ (assert (string>? "badges" "badger"))
101
+
102
+ (assert (string<=? "foo" "foo"))
103
+ (assert (string<=? "foo" "goo"))
104
+ (assert (string<=? "Foo" "foo"))
105
+ (assert (not (string<=? "foo" "Foo")))
106
+
107
+ (assert (string-ci<=? "foo" "Foo"))
108
+ (assert (not (string-ci>=? "abacus" "Badger")))
109
+ (assert (string>? "abacus" "Badger"))
110
+
@@ -30,6 +30,8 @@ Class.new(Test::Unit::TestCase) do
30
30
  %w[ booleans
31
31
  numbers
32
32
  lists
33
+ vectors
34
+ strings
33
35
  equivalence
34
36
  arithmetic
35
37
  define_values
@@ -0,0 +1,83 @@
1
+ (assert (equal? #() #()))
2
+ (assert (equal? #(1 8 7) #(1 8 7)))
3
+ (assert (equal? #(1 (2 3) 7) #(1 (2 3) 7)))
4
+ (assert (not (equal? #(1 (2 3) 7) #(1 (2 4) 7))))
5
+
6
+ (assert (vector? #(1 2 3)))
7
+ (assert (vector? #()))
8
+ (assert (vector? #((1 2 3))))
9
+
10
+ (for-each (lambda (predicate)
11
+ (assert (not (predicate #()))))
12
+ `(,pair? ,list? ,null?))
13
+
14
+ (assert (vector? (vector 1 3)))
15
+ (assert-equal #(1 4) (vector 1 4))
16
+ (assert (not (equal? #(2 4) (vector 2 4 5))))
17
+
18
+ (assert (not (equal? '(1 2) #[1 2])))
19
+ (assert (equal? '(1 2) (vector->list #(1 2))))
20
+ (assert (equal? (list->vector '(1 2)) #(1 2)))
21
+
22
+ (assert-equal #(() () ()) (make-vector 3))
23
+ (assert-equal #(foo foo foo foo) (make-vector 4 'foo))
24
+
25
+ (assert-equal 0 (vector-length #()))
26
+ (assert-equal 3 (vector-length #(1 8 9)))
27
+
28
+ (assert-equal 8 (vector-ref '#(1 1 2 3 5 8 13 21) 5))
29
+
30
+ (assert-equal 13 (vector-ref '#[1 1 2 3 5 8 13 21]
31
+ (round (* 2 (acos -1)))))
32
+
33
+ (assert-equal #(0 ("Sue" "Sue") "Anna") (let ((vec (vector 0 '(2 2 2 2) "Anna")))
34
+ (vector-set! vec 1 '("Sue" "Sue"))
35
+ vec))
36
+
37
+ (assert-raise ImmutableError (vector-set! '#(0 1 2) 1 "doe"))
38
+ (assert-equal #(0 "doe" 2) (let ([v #(0 1 2)])
39
+ (vector-set! v 1 "doe")
40
+ v))
41
+
42
+ (assert-equal #(23 23 23) (let ([v (vector 1 2 3)])
43
+ (vector-fill! v 23)
44
+ v))
45
+
46
+ (define (create-unquoted-vector)
47
+ #(1 2 3))
48
+
49
+ (let ([a (create-unquoted-vector)])
50
+ (vector-set! a 1 7)
51
+ (assert-equal #(1 2 3) (create-unquoted-vector))
52
+ (assert-equal '#(1 7 3) a))
53
+
54
+ (define (create-quoted-vector)
55
+ '#(1 2 3))
56
+
57
+ (let ([a (create-quoted-vector)])
58
+ (assert-raise ImmutableError (vector-set! a 1 7)))
59
+
60
+ (define-syntax vector-mac (syntax-rules ()
61
+ ((_ #(a (b c) d ...))
62
+ c)
63
+ ((_ #(a b c))
64
+ a)))
65
+
66
+ (assert-equal 3 (vector-mac #(3 8 7)))
67
+ (assert-raise SyntaxError (vector-mac #(1 2)))
68
+ (assert-raise SyntaxError (vector-mac #(1 2 3 4)))
69
+ (assert-raise SyntaxError (vector-mac (1 2 3)))
70
+
71
+ (assert-equal 5 (vector-mac #(3 (4 5))))
72
+ (assert-equal 5 (vector-mac #(3 (4 5) 6)))
73
+ (assert-equal 3 (vector-mac #(3 #(4 5) 6)))
74
+
75
+ (assert-equal #(1 2 3) '#(1 2 3))
76
+ (assert-equal #(1 2 3) `#(1 2 3))
77
+ (assert-equal #(1 (* 7 4) 3) `#(1 (* 7 4) 3))
78
+ (assert-equal #(1 28 3) `#(1 ,(* 7 4) 3))
79
+ (assert-equal #(1 28 9 5) `#(1 ,(* 7 4) ,@'(9 5)))
80
+ (assert-equal #(1 28 9 5 8) `#(1 ,(* 7 4) ,@'(9 5) 8))
81
+ (assert-equal #(9 5 8) `#(,@'(9 5) 8))
82
+ (assert-equal #((9 5) 8) `#(,'(9 5) 8))
83
+