fOOrth 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, [],