Defining new built-in operators
The standard GolfScript interpreter has a rarely used feature that allows interpolated Ruby code in double quoted string literals.
One reason why this feature isn't more commonly used is that, awkwardly, the interpolated code is executed at compile time, and the output is cached by the GolfScript interpreter so that the same string literal will thereafter always yield the same value, even inside string eval.
However, one thing this feature turns out to be good for is defining new GolfScript operators implemented in Ruby code. For example, here's how to define a new binary addition operator that works just like the standard built-in + operator:
"#{var'add','gpush a+b'.cc2}";
It doesn't really matter where you put the definition in your code; the new operator gets defined as soon as the double-quoted string containing the Ruby code is parsed. The add operator defined above works exactly like the built-in + operator, and can be used in exactly the same way:
1 2 add # evaluates to 3
"foo" "bar" add # evaluates to "foobar"
Of course, defining a new addition operator is pretty useless, unless you've done something silly like erase the built-in + operator. But you can use the same trick to define new operators that do things Golfscript cannot (easily) do natively such as, say, uniformly shuffling an array:
"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";
10,shuf # evaluates to 0,1,2,...,9 in random order
or printing the contents of the whole stack:
"#{var'debug','puts Garray.new($stack).ginspect'.cc}";
4,) ["foo" debug # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched
or interactive input:
"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";
]; { "> " print gets ~ ]p 1 } do # simple GolfScript REPL
or even web access:
"#{
require 'net/http'
require 'uri'
var'get','gpush Gstring.new(Net::HTTP.get_response(URI.parse(a.to_s)).body)'.cc1
}";
"http://example.com" get
Of course, a somewhat golfier (and riskier!) implementation of the latter would be e.g.:
"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";
While not particularly golfy in itself, this lets you extend the capabilities of GolfScript beyond what the built-in commands provide.
How does it work?
The authoritative reference on how to define new GolfScript operators in this way is, of course, the source code for the interpreter. That said, here's a few quick tips:
To define a new operator name that runs the Ruby code code, use:
var'name','code'.cc
Inside the code, use gpop to read a value off the stack, and gpush to push one back in. You can also access the stack directly via the array $stack. For example, to push both a and b onto the stack, it's golfier to do $stack<<a<<b than gpush a;gpush b.
- The positions of the
[ array start markers are stored in the $lb array. The gpop function takes care of adjusting these markers down if the stack shrinks below their position, but manipulating the $stack array directly does not.
The .cc string method that compiles Ruby code in a string into a GolfScript operator is just a convenience wrapper around Gblock.new(). It also has the variants .cc1, .cc2 and .cc3 that make the operator automatically pop 1, 2 or 3 arguments off the stack and assign them to the variables a, b and c. There's also an .order method that works like .cc2, except that it automatically sorts the arguments by type priority.
All values on the GolfScript stack are (and should be!) objects of type Gint, Garray, Gstring or Gblock. The underlying native integer or array, where needed, can be accessed via the .val method.
- However, note that
Gstring.val returns an array of Gints! To turn a Gstring into a native Ruby string, call .to_s on it instead (or use it in a context that does that automatically, like string interpolation). Calling .to_gs on any GS value turns it into a Gstring, so any GS value can be stringified with .to_gs.to_s.
The gpush function doesn't auto-wrap native Ruby numbers, strings or arrays into the corresponding GS types, so you'll often have to do it yourself by explicitly calling e.g. Gstring.new(). If you push anything other than one of the GS value types onto the stack, any code that later tries to manipulate it is likely to crash.
The GS value types also have a .factory method that calls the type's constructor, which can be useful e.g. for rewrapping arrays/strings after manipulating their contents. All the types also have a .coerce method that performs type coercion: a.coerce(b) returns a pair containing a and b coerced to the same type.
... xen... [x]? Le mieux que je puisse voir est[.;].