fOOrth 0.5.3 → 0.6.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.
@@ -71,11 +71,11 @@ class ThreadLibraryTester < Minitest::Test
71
71
  foorth_run('$tmtx .lock $tmtx .unlock ')
72
72
  foorth_equal('$tmtx .do{{ 3 4 + }}', [7])
73
73
 
74
- foorth_run('"" val$: $tmtx_str')
74
+ foorth_run('""* val$: $tmtx_str')
75
75
  foorth_equal('{{ $tmtx .do{{ 0 10 do $tmtx_str "@" << loop }} }} ' +
76
76
  '.start drop $tmtx .do{{ $tmtx_str }} ', ["@"*10])
77
77
 
78
- foorth_run('"" val$: $tmtx_str')
78
+ foorth_run('""* val$: $tmtx_str')
79
79
  foorth_equal('{{ Mutex .do{{ 0 10 do $tmtx_str "@" << loop }} }} ' +
80
80
  '.start drop Mutex .do{{ $tmtx_str }} ', ["@"*10])
81
81
  end
@@ -12,7 +12,14 @@ module XfOOrth
12
12
  #* word - The text of the word.
13
13
  def string_parms(token, word)
14
14
  if word.end_with?('"')
15
- token.add("vm.push(#{parser.get_string.foorth_embed}); ")
15
+ string_value = parser.get_string.foorth_embed
16
+
17
+ if parser.source.peek == '*'
18
+ parser.source.get
19
+ token.add("vm.push(StringBuffer.new(#{string_value})); ")
20
+ else
21
+ token.add("vm.push(#{string_value}.freeze); ")
22
+ end
16
23
  end
17
24
  end
18
25
 
@@ -13,21 +13,23 @@ module XfOOrth
13
13
  def initialize
14
14
  reset_read_point
15
15
  @eof = false
16
+ @peek_buffer = nil
16
17
  end
17
18
 
18
19
  #Close the source.
19
20
  def close
20
21
  @eoln = true
21
22
  @eof = true
23
+ @peek_buffer = nil
22
24
  end
23
25
 
24
26
  #Get the next character of input data
25
27
  #<br>Returns:
26
28
  #* The next character or nil if none are available.
27
29
  def get
28
- return nil if @eof
30
+ return nil if (@eof && !@peek_buffer)
29
31
 
30
- read do
32
+ @peek_buffer || read do
31
33
  begin
32
34
  @read_step.next.rstrip
33
35
  rescue StopIteration
@@ -35,6 +37,15 @@ module XfOOrth
35
37
  nil
36
38
  end
37
39
  end
40
+ ensure
41
+ @peek_buffer = nil
42
+ end
43
+
44
+ #Peek ahead by one character.
45
+ #<br>Returns:
46
+ #* A peek at next character or nil if none are available.
47
+ def peek
48
+ @peek_buffer ||= get
38
49
  end
39
50
 
40
51
  #Has the source reached the end of the available data?
@@ -11,6 +11,8 @@ module XfOOrth
11
11
 
12
12
  #Initialize a new console command source.
13
13
  def initialize
14
+ @peek_buffer = nil
15
+
14
16
  reset_read_point
15
17
 
16
18
  auto_src = lambda { SymbolMap.forward_map.keys.sort }
@@ -22,16 +24,30 @@ module XfOOrth
22
24
  eoi_detect: true)
23
25
  end
24
26
 
25
- alias close reset_read_point
26
- alias flush reset_read_point
27
+ #Close the console source.
28
+ def close
29
+ @peek_buffer = nil
30
+ reset_read_point
31
+ end
32
+
33
+ alias flush close
27
34
 
28
35
  #Get the next character of command text from the user.
29
36
  #<br>Returns
30
37
  #* The next character of user input as a string.
31
38
  def get
32
- read do
39
+ @peek_buffer || read do
33
40
  @edit.readline(prompt: prompt).rstrip
34
41
  end
42
+ ensure
43
+ @peek_buffer = nil
44
+ end
45
+
46
+ #Peek ahead by one character.
47
+ #<br>Returns:
48
+ #* A peek at next character or nil if none are available.
49
+ def peek
50
+ @peek_buffer ||= get
35
51
  end
36
52
 
37
53
  #Has the scanning of the text reached the end of input?
@@ -7,14 +7,14 @@ class Class
7
7
  #<br>Decree!
8
8
  #* These are to be the only references to @_private_foorth_name!
9
9
  def foorth_name
10
- @_private_foorth_name ||= name
10
+ @_private_foorth_name ||= name.freeze
11
11
  end
12
12
 
13
13
  #Set the foorth name of this class.
14
14
  #<br>Decree!
15
15
  #* These are to be the only references to @_private_foorth_name!
16
16
  def foorth_name=(new_name)
17
- @_private_foorth_name = new_name
17
+ @_private_foorth_name = new_name.freeze
18
18
  end
19
19
 
20
20
  #Access/create the class's shared fOOrth dictionary.
@@ -119,6 +119,10 @@ module XfOOrth
119
119
  Array.create_shared_method('<<', NosSpec, [],
120
120
  &lambda {|vm| vm.poke(self << vm.peek); })
121
121
 
122
+ # [ [ 3 1 2 ] n ] >> [ [ n 3 1 2 ] ]
123
+ Array.create_shared_method('>>', NosSpec, [],
124
+ &lambda {|vm| vm.poke(self.insert(0, vm.peek)); })
125
+
122
126
  # [[3 1 2] n] + [[3 1 2 n]]
123
127
  Array.create_shared_method('+', NosSpec, [],
124
128
  &lambda {|vm| vm.poke(self + vm.peek.in_array); })
@@ -474,7 +478,7 @@ module XfOOrth
474
478
  end
475
479
  end
476
480
 
477
- vm.push(result + "]")
481
+ vm.push((result + "]").freeze)
478
482
  })
479
483
 
480
484
  # [ l 2 3 ... n ] .strmax [ widest ]
@@ -11,7 +11,7 @@ module XfOOrth
11
11
  #Get the class as a string.
12
12
  # [cls] .to_s ["cls as a string"]
13
13
  Class.create_shared_method('.to_s', TosSpec, [], &lambda {|vm|
14
- vm.push(self.foorth_name || "AnonymousClass<#{self.object_id}>")
14
+ vm.push(self.foorth_name || "AnonymousClass<#{self.object_id}>".freeze)
15
15
  })
16
16
 
17
17
  #The .parent_class method. Retrieves the parent class of a class.
@@ -117,7 +117,7 @@ module XfOOrth
117
117
  error "F50: Unable to locate file #{file_name}"
118
118
  end
119
119
 
120
- vm.process_file(file_name)
120
+ vm.process_file(file_name.freeze)
121
121
 
122
122
  puts "Completed in #{Time.now - start_time} seconds"
123
123
  })
@@ -180,7 +180,7 @@ module XfOOrth
180
180
  #List the globals defined in fOOrth.
181
181
  VirtualMachine.create_shared_method(')globals', VmSpec, [], &lambda {|vm|
182
182
  $FOORTH_GLOBALS.keys.
183
- select {|key| !($FOORTH_GLOBALS[key].has_tag?(:class))}.
183
+ select {|key| !($FOORTH_GLOBALS[key].has_tag?(:class))}.
184
184
  collect {|key| "#{XfOOrth::SymbolMap.unmap(key)} (#{key.to_s})"}.
185
185
  foorth_pretty(vm)
186
186
  })
@@ -47,6 +47,6 @@ module XfOOrth
47
47
 
48
48
  #Default conversion to string. See duration/formatter for formatted output.
49
49
  Duration.create_shared_method('.to_s', TosSpec, [], &lambda {|vm|
50
- vm.push("Duration instance <#{self.period.to_f} seconds>" )
50
+ vm.push("Duration instance <#{self.period.to_f} seconds>".freeze )
51
51
  })
52
52
  end
@@ -123,7 +123,7 @@ module XfOOrth
123
123
  result << (vm.pop || value.inspect) + " -> "
124
124
  end
125
125
 
126
- vm.push(result + "}")
126
+ vm.push((result + "}").freeze)
127
127
  })
128
128
 
129
129
  #[a_hash] .to_h [a_hash]
@@ -25,13 +25,13 @@ module XfOOrth
25
25
 
26
26
  # ["file_name", InStream] .open [an_instream]
27
27
  in_stream.create_exclusive_method('.open', TosSpec, [], &lambda {|vm|
28
- file_name = vm.pop.to_s
28
+ file_name = vm.pop.to_s.freeze
29
29
  vm.push(XfOOrth_InStream.new(file_name))
30
30
  })
31
31
 
32
32
  in_stream.create_exclusive_method('.open{{', NosSpec, [], &lambda {|vm|
33
33
  block = vm.pop
34
- file_name = vm.pop.to_s
34
+ file_name = vm.pop.to_s.freeze
35
35
  in_stream = XfOOrth_InStream.new(file_name)
36
36
 
37
37
  begin
@@ -71,7 +71,7 @@ module XfOOrth
71
71
  # [file_name InStream] .get_all [["line 1", "line 2", ... "line n"]]
72
72
  in_stream.create_exclusive_method('.get_all', TosSpec, [], &lambda {|vm|
73
73
  begin
74
- file_name = vm.pop.to_s
74
+ file_name = vm.pop.to_s.freeze
75
75
  vm.push(IO.readlines(file_name).map{|line| line.chomp })
76
76
  rescue
77
77
  error "F50: Unable to open the file #{file_name} for reading all."
@@ -28,7 +28,17 @@ module XfOOrth
28
28
  #Get the object as a string.
29
29
  # [obj] .to_s ["obj as a string"]
30
30
  Object.create_shared_method('.to_s', TosSpec, [],
31
- &lambda {|vm| vm.push(self.to_s)})
31
+ &lambda {|vm| vm.push(self.to_s.freeze)})
32
+
33
+ #Get the object as a string.
34
+ # [obj] .to_s ["obj as a string"]
35
+ Object.create_shared_method('.to_s*', TosSpec, [],
36
+ &lambda {|vm| vm.push(StringBuffer.new(self.to_s.dup))})
37
+
38
+ #Get the object as a string.
39
+ # [obj] .to_s ["obj as a string"]
40
+ String.create_shared_method('.to_s*', TosSpec, [],
41
+ &lambda {|vm| vm.push(StringBuffer.new(self.dup))})
32
42
 
33
43
  #Get the length of the object as a string.
34
44
  # [obj] .strlen [n]; the length of the object's to_s string
@@ -28,14 +28,14 @@ module XfOOrth
28
28
 
29
29
  # ["file_name" OutStream] .create [an_outstream]
30
30
  out_stream.create_exclusive_method('.create', TosSpec, [], &lambda {|vm|
31
- file_name = vm.pop.to_s
31
+ file_name = vm.pop.to_s.freeze
32
32
  vm.push(XfOOrth_OutStream.new(file_name, 'w'))
33
33
  })
34
34
 
35
35
  # ["file_name" OutStream] .create{{ ... }} []
36
36
  out_stream.create_exclusive_method('.create{{', TosSpec, [], &lambda {|vm|
37
37
  block = vm.pop
38
- file_name = vm.pop.to_s
38
+ file_name = vm.pop.to_s.freeze
39
39
  out_stream = XfOOrth_OutStream.new(file_name, 'w')
40
40
 
41
41
  begin
@@ -48,7 +48,7 @@ module XfOOrth
48
48
  # ["file_name" OutStream] .append{{ ... }} []
49
49
  out_stream.create_exclusive_method('.append{{', TosSpec, [], &lambda {|vm|
50
50
  block = vm.pop
51
- file_name = vm.pop.to_s
51
+ file_name = vm.pop.to_s.freeze
52
52
  out_stream = XfOOrth_OutStream.new(file_name, 'a')
53
53
 
54
54
  begin
@@ -60,7 +60,7 @@ module XfOOrth
60
60
 
61
61
  # ["file_name" OutStream] .append [an_outstream]
62
62
  out_stream.create_exclusive_method('.append', TosSpec, [], &lambda {|vm|
63
- file_name = vm.pop.to_s
63
+ file_name = vm.pop.to_s.freeze
64
64
  vm.push(XfOOrth_OutStream.new(file_name, 'a'))
65
65
  })
66
66
 
@@ -119,7 +119,7 @@ module XfOOrth
119
119
 
120
120
  # [text_array "file_name" OutStream] .put_all []; Write the array to file_name
121
121
  out_stream.create_exclusive_method('.put_all', TosSpec, [], &lambda {|vm|
122
- file_name = vm.pop.to_s
122
+ file_name = vm.pop.to_s.freeze
123
123
  file_source = vm.pop
124
124
  out_stream = XfOOrth_OutStream.new(file_name, 'w')
125
125
 
@@ -132,7 +132,7 @@ module XfOOrth
132
132
 
133
133
  # [text_array "file_name" OutStream] .append_all []; Append the array to file_name
134
134
  out_stream.create_exclusive_method('.append_all', TosSpec, [], &lambda {|vm|
135
- file_name = vm.pop.to_s
135
+ file_name = vm.pop.to_s.freeze
136
136
  file_source = vm.pop
137
137
  out_stream = XfOOrth_OutStream.new(file_name, 'a')
138
138
 
@@ -36,7 +36,7 @@ module XfOOrth
36
36
 
37
37
  # [name procedure] .start_named [a_thread]
38
38
  Proc.create_shared_method('.start_named', TosSpec, [], &lambda {|vm|
39
- vm.push(self.do_thread_start(vm, vm.pop.to_s))
39
+ vm.push(self.do_thread_start(vm, vm.pop.to_s.freeze))
40
40
  })
41
41
 
42
42
  end
@@ -47,7 +47,7 @@ module XfOOrth
47
47
  #Get a string from the console.
48
48
  # [] accept"prompt" [string]; gets a string from the console.
49
49
  VirtualMachine.create_shared_method('accept"', VmSpec, [],
50
- &lambda {|vm| poke(MiniReadline.readline(peek.to_s, true)); })
50
+ &lambda {|vm| poke(MiniReadline.readline(peek.to_s, true).freeze); })
51
51
 
52
52
  #Get a string from the console.
53
53
  # "prompt" [] .accept [string]; gets a string from the console.
@@ -6,13 +6,21 @@ module XfOOrth
6
6
  #Connect the String class to the fOOrth class system.
7
7
  String.create_foorth_proxy
8
8
 
9
+ #Connect the StringBuffer class to the fOOrth class system.
10
+ StringBuffer.create_foorth_proxy
11
+
9
12
  # A no operation place holder for string literals
10
13
  VirtualMachine.create_shared_method('"', MacroSpec, [:macro, " "])
11
14
 
15
+ #Is this mutable? StringBuffers are, Strings are not.
16
+ # [a] .mutable? [flag]
17
+ String.create_shared_method('.mutable?', TosSpec, [],
18
+ &lambda {|vm| vm.push(!self.frozen?); })
19
+
12
20
  # [string] .each{{ ... }} [unspecified]
13
21
  String.create_shared_method('.each{{', NosSpec, [], &lambda { |vm|
14
22
  block, idx = vm.pop, 0
15
- self.chars { |val| block.call(vm, val, idx); idx += 1 }
23
+ self.chars { |val| block.call(vm, val.freeze, idx); idx += 1 }
16
24
  })
17
25
 
18
26
  #Some comparison operators
@@ -39,32 +47,48 @@ module XfOOrth
39
47
 
40
48
  #Some string manipulation methods.
41
49
  # [n a] .ljust ['a ']; left justify
42
- String.create_shared_method('.ljust', TosSpec, [],
43
- &lambda {|vm| vm.poke(self.ljust(Integer.foorth_coerce(vm.peek))); })
50
+ String.create_shared_method('.ljust', TosSpec, [], &lambda {|vm|
51
+ vm.poke(self.to_s.ljust(Integer.foorth_coerce(vm.peek)).freeze);
52
+ })
44
53
 
45
54
  # [n a] .cjust [' a ']; center justify
46
- String.create_shared_method('.cjust', TosSpec, [],
47
- &lambda {|vm| vm.poke(self.center(Integer.foorth_coerce(vm.peek))); })
55
+ String.create_shared_method('.cjust', TosSpec, [], &lambda {|vm|
56
+ vm.poke(self.to_s.center(Integer.foorth_coerce(vm.peek)).freeze);
57
+ })
48
58
 
49
59
  # [n a] .rjust [' a']; right justify
50
- String.create_shared_method('.rjust', TosSpec, [],
51
- &lambda {|vm| vm.poke(self.rjust(Integer.foorth_coerce(vm.peek))); })
60
+ String.create_shared_method('.rjust', TosSpec, [], &lambda {|vm|
61
+ vm.poke(self.to_s.rjust(Integer.foorth_coerce(vm.peek)).freeze);
62
+ })
52
63
 
53
64
  # [" a "] .lstrip ["a "]; left strip
54
65
  String.create_shared_method('.lstrip', TosSpec, [],
55
- &lambda {|vm| vm.push(self.lstrip); })
66
+ &lambda {|vm| vm.push(self.to_s.lstrip.freeze); })
67
+
68
+ # [" a "*] .lstrip* []; left strip in place.
69
+ StringBuffer.create_shared_method('.lstrip*', TosSpec, [],
70
+ &lambda {|vm| self.lstrip! })
56
71
 
57
72
  # [" a "] .strip ["a"]; left and right strip
58
73
  String.create_shared_method('.strip', TosSpec, [],
59
- &lambda {|vm| vm.push(self.strip); })
74
+ &lambda {|vm| vm.push(self.to_s.strip.freeze); })
75
+
76
+ # [" a "*] .strip* []; left and right strip in place
77
+ StringBuffer.create_shared_method('.strip*', TosSpec, [],
78
+ &lambda {|vm| self.strip! })
60
79
 
61
80
  # [" a "] .rstrip [" a"]; right strip
62
81
  String.create_shared_method('.rstrip', TosSpec, [],
63
- &lambda {|vm| vm.push(self.rstrip); })
82
+ &lambda {|vm| vm.push(self.to_s.rstrip.freeze); })
83
+
84
+ # [" a "*] .rstrip* []; right strip in place
85
+ StringBuffer.create_shared_method('.rstrip*', TosSpec, [],
86
+ &lambda {|vm| self.rstrip! })
64
87
 
88
+ #The shared block for string formatting.
65
89
  format_action = lambda do |vm|
66
90
  begin
67
- vm.poke(vm.peek % self.in_array)
91
+ vm.poke((vm.peek % self.in_array).freeze)
68
92
  rescue => err
69
93
  vm.data_stack.pop
70
94
  error "F40: Formating error: #{err.message}."
@@ -77,7 +101,14 @@ module XfOOrth
77
101
  # [object_or_array] f"fmt_str" ['a formatted string']
78
102
  Object.create_shared_method('f"', NosSpec, [], &format_action)
79
103
 
80
- parse_action = lambda {|vm| vm.poke(self.sscanf(vm.peek))}
104
+ parse_action = lambda do |vm|
105
+ begin
106
+ vm.poke(self.sscanf(vm.peek).map{|obj| obj.foorth_string_freeze} )
107
+ rescue => err
108
+ vm.data_stack.pop
109
+ error "F40: Parsing error: #{err.message}."
110
+ end
111
+ end
81
112
 
82
113
  # [a_str fmt_str] parse [result_array]
83
114
  String.create_shared_method('parse', NosSpec, [], &parse_action)
@@ -90,16 +121,16 @@ module XfOOrth
90
121
 
91
122
  # [w 'abcdefgh'] .left ['ab'] // Assumes w = 2
92
123
  String.create_shared_method('.left', TosSpec, [],
93
- &lambda {|vm| vm.poke(self[0...(Integer.foorth_coerce(vm.peek))]); })
124
+ &lambda {|vm| vm.poke(self.to_s[0...(Integer.foorth_coerce(vm.peek))].freeze); })
94
125
 
95
126
  # [w 'abcdefgh'] .-left ['cdefgh'] // Assumes w = 2
96
127
  String.create_shared_method('.-left', TosSpec, [],
97
- &lambda {|vm| vm.poke(self[(Integer.foorth_coerce(vm.peek))..-1]); })
128
+ &lambda {|vm| vm.poke(self.to_s[(Integer.foorth_coerce(vm.peek))..-1].freeze); })
98
129
 
99
130
  # [w '123''abcdefgh'] .+left ['123cdefgh'] // Assumes w = 2
100
131
  String.create_shared_method('.+left', TosSpec, [], &lambda {|vm|
101
132
  ins = vm.pop.to_s
102
- vm.poke(ins + self[(Integer.foorth_coerce(vm.peek))..-1])
133
+ vm.poke((ins + self[(Integer.foorth_coerce(vm.peek))..-1]).freeze)
103
134
  })
104
135
 
105
136
  # ['abc' 'abcdefgh'] .left? [boolean]
@@ -113,14 +144,14 @@ module XfOOrth
113
144
  String.create_shared_method('.mid', TosSpec, [], &lambda {|vm|
114
145
  width = Integer.foorth_coerce(vm.pop)
115
146
  posn = Integer.foorth_coerce(vm.pop)
116
- vm.push(self[posn...(posn+width)])
147
+ vm.push(self.to_s[posn...(posn+width)].freeze)
117
148
  })
118
149
 
119
150
  # [n w 'abcdefgh'] .-mid ['abgh'] // Assumes n = 2, w = 4
120
151
  String.create_shared_method('.-mid', TosSpec, [], &lambda {|vm|
121
152
  width = Integer.foorth_coerce(vm.pop)
122
153
  posn = Integer.foorth_coerce(vm.pop)
123
- vm.push(self[0...posn] + self[(posn+width)..-1])
154
+ vm.push((self[0...posn] + self[(posn+width)..-1]).freeze)
124
155
  })
125
156
 
126
157
  # [n w "123" "abcdefgh"] .+mid ["ab123gh"] // Assumes n = 2, w = 4
@@ -128,7 +159,7 @@ module XfOOrth
128
159
  ins = vm.pop.to_s
129
160
  width = Integer.foorth_coerce(vm.pop)
130
161
  posn = Integer.foorth_coerce(vm.pop)
131
- vm.push(self[0...posn] + ins + self[(posn+width)..-1])
162
+ vm.push((self[0...posn] + ins + self[(posn+width)..-1]).freeze)
132
163
  })
133
164
 
134
165
  # [n 'cde' 'abcdefgh'] .mid? [true] // Assumes n = 2
@@ -146,14 +177,14 @@ module XfOOrth
146
177
  String.create_shared_method('.midlr', TosSpec, [], &lambda {|vm|
147
178
  right = Integer.foorth_coerce(vm.pop)
148
179
  left = Integer.foorth_coerce(vm.pop)
149
- vm.push(self[left...(0-right)])
180
+ vm.push(self.to_s[left...(0-right)].freeze)
150
181
  })
151
182
 
152
183
  # [l r 'abcdefgh'] .-midlr ['ah'] // Assumes l = 1, r = 1
153
184
  String.create_shared_method('.-midlr', TosSpec, [], &lambda {|vm|
154
185
  right = Integer.foorth_coerce(vm.pop)
155
186
  left = Integer.foorth_coerce(vm.pop)
156
- vm.push(self[0...left] + self[((0-right))..-1])
187
+ vm.push((self[0...left] + self[((0-right))..-1]).freeze)
157
188
  })
158
189
 
159
190
  # [l r "123" 'abcdefgh'] .+midlr ['a123h'] // Assumes l = 1, r = 1
@@ -161,24 +192,26 @@ module XfOOrth
161
192
  ins = vm.pop.to_s
162
193
  right = Integer.foorth_coerce(vm.pop)
163
194
  left = Integer.foorth_coerce(vm.pop)
164
- vm.push(self[0...left] + ins + self[((0-right))..-1])
195
+ vm.push((self[0...left] + ins + self[((0-right))..-1]).freeze)
165
196
  })
166
197
 
167
198
  #RIGHT Group
168
199
 
169
200
  # [w 'abcdefgh'] .right ['gh'] // Assumes w = 2
170
- String.create_shared_method('.right', TosSpec, [],
171
- &lambda {|vm| vm.poke(self[(0-(Integer.foorth_coerce(vm.peek)))..-1]); })
201
+ String.create_shared_method('.right', TosSpec, [], &lambda {|vm|
202
+ vm.poke(self.to_s[(0-(Integer.foorth_coerce(vm.peek)))..-1].freeze);
203
+ })
172
204
 
173
205
  # [w 'abcdefgh'] .-right ['abcdef'] // Assumes w = 2
174
- String.create_shared_method('.-right', TosSpec, [],
175
- &lambda {|vm| vm.poke(self[0...(0-(Integer.foorth_coerce(vm.peek)))]); })
206
+ String.create_shared_method('.-right', TosSpec, [], &lambda {|vm|
207
+ vm.poke(self.to_s[0...(0-(Integer.foorth_coerce(vm.peek)))].freeze);
208
+ })
176
209
 
177
210
  # [w "123" 'abcdefgh'] .+right ['abcdef123'] // Assumes w = 2
178
211
  String.create_shared_method('.+right', TosSpec, [], &lambda {|vm|
179
212
  ins = vm.pop.to_s
180
213
  width = Integer.foorth_coerce(vm.pop)
181
- vm.push(self[0...(0-width)] + ins)
214
+ vm.push((self[0...(0-width)] + ins).freeze)
182
215
  })
183
216
 
184
217
  # ['fgh' 'abcdefgh'] .right? [boolean]
@@ -201,16 +234,20 @@ module XfOOrth
201
234
 
202
235
  # ["b", a] + ["ba"]; "ba" is a new object, distinct from "b"
203
236
  String.create_shared_method('+', NosSpec, [],
204
- &lambda {|vm| vm.poke(self + vm.peek.to_s); })
237
+ &lambda {|vm| vm.poke((self + vm.peek.to_s).freeze) })
205
238
 
206
- # ["b", a] << ["ba"]; "ba" is the same object as "b"
207
- String.create_shared_method('<<', NosSpec, [],
239
+ # ["b"*, a] << ["ba"*]; "ba"* is the same object as "b"*
240
+ StringBuffer.create_shared_method('<<', NosSpec, [],
208
241
  &lambda {|vm| vm.poke(self << vm.peek.to_s); })
209
242
 
243
+ # ["b"*, a] >> ["ab"*]; "ab"* is the same object as "b"*
244
+ StringBuffer.create_shared_method('>>', NosSpec, [],
245
+ &lambda {|vm| vm.poke(self.prepend(vm.peek.to_s)); })
246
+
210
247
  # ["b", n] * ["bbb..."]
211
248
  String.create_shared_method('*', NosSpec, [], &lambda {|vm|
212
249
  begin
213
- vm.poke(self * Integer.foorth_coerce(vm.peek))
250
+ vm.poke((self.to_s * Integer.foorth_coerce(vm.peek)).freeze)
214
251
  rescue
215
252
  vm.data_stack.pop
216
253
  raise
@@ -219,15 +256,27 @@ module XfOOrth
219
256
 
220
257
  # ["abCD"] .to_upper ["ABCD"]
221
258
  String.create_shared_method('.to_upper', TosSpec, [],
222
- &lambda {|vm| vm.push(self.upcase); })
259
+ &lambda {|vm| vm.push(self.to_s.upcase.freeze) })
260
+
261
+ # ["abCD"*] .to_upper* [] #Convert to upper case in place.
262
+ StringBuffer.create_shared_method('.to_upper*', TosSpec, [],
263
+ &lambda {|vm| self.upcase! })
223
264
 
224
265
  # ["abCD"] .to_lower ["abcd"]
225
266
  String.create_shared_method('.to_lower', TosSpec, [],
226
- &lambda {|vm| vm.push(self.downcase); })
267
+ &lambda {|vm| vm.push(self.to_s.downcase.freeze) })
268
+
269
+ # ["abCD"*] .to_lower* [] #Convert to lower case in place.
270
+ StringBuffer.create_shared_method('.to_lower*', TosSpec, [],
271
+ &lambda {|vm| self.downcase! })
227
272
 
228
273
  # ["stressed"] .reverse ["desserts"]
229
274
  String.create_shared_method('.reverse', TosSpec, [],
230
- &lambda {|vm| vm.push(self.reverse); })
275
+ &lambda {|vm| vm.push(self.to_s.reverse.freeze); })
276
+
277
+ # ["stressed"*] .reverse* [] #Reverse the string in place.
278
+ StringBuffer.create_shared_method('.reverse*', TosSpec, [],
279
+ &lambda {|vm| self.reverse! })
231
280
 
232
281
  # ["abc\\ndef\\n123"] .lines [["abc", "def", "123"]]
233
282
  String.create_shared_method('.lines', TosSpec, [],