The LambdaMOO server does not retain the source code after compiling MOOcode into a verb. If a programmer wants to subsequently edit/change the verb, the server must decompile the bytecode back into MOOcode. LambdaMOO's simple programming language and expressive bytecode make the roundtrip possible, but let's face it—this is an experiment that failed, to which the lack of similar functionality anywhere else attests.
The Stunt server does not (yet) directly address this. However, the Improvise project implements the planned solution. Aside from evolutionary changes, what I'm going to describe will eventually become part of the server.
Since we're no longer going to decompile bytecode into MOOcode, Improvise starts by breaking the assumption that a verb's source code must be traditional MOOcode.
On the input side, the set_verb_code()
function takes an additional argument that specifies various compilation options. The option content_type
specifies the source code programming language. Currently content_type
is the only supported option.
If called with the usual three arguments, set_verb_code()
behaves as it does in existing servers, does not permit enhanced MOOcode or other langugaes, and does not preserve the original source code. This is the legacy mode of operation.
set_verb_code(o, v, {"/* e.g. comments are discarded */", "return #1;"})
If the fourth argument is present and the content type "application/x-moocode" is specified, the original (built-in) MOOcode parser is used but the source code is preserved. Support for the type "application/x-moocode" is effectively built in to the server. Functionally, this is identical to the legacy mode, however it does preserve the formatting (including comments) and it is compatible with the enhanced programmer tools/commands described below.
set_verb_code(o, v, {"/* e.g. comments and formatting are preserved */", "return #1;"}, ["content_type" -> "application/x-moocode"])
Improvise also supports external compilers. These are "plugged in" to the database, and are transparently invoked to compile other kinds of source code into bytecode. Currently, the Mustache package provides a compiler for the Mustache template language, which is specified by the type "application/x-mustache"; and the Plastic package provides a compiler for the extended MOOcode language, which is specified by the type "application/x-moocode; version=0.0.2" (the explicit version triggers the use of the Plastic compiler instead of the built in compiler).
set_verb_code(o, v, {"// e.g. note the inline comments", "for x in [1..5]", "x += 10", "end"}, ["content_type" -> "application/x-moocode; version=0.0.2"])
The function verb_code()
either decompiles the bytecode into source code (in legacy and raw modes), or returns the current version of the source code.
verb_code(o, v)
Raw mode (invoked by passing "raw" as the third argument) returns the verb's decompiled bytecode. This is occasionally useful when troubleshooting a bug in code generation or when interpreting the output of a stack trace.
verb_code(o, v, "raw")
The verb $verb_details()
returns detailed information about a verb. At a minimum, this includes verb info and verb args. When used on a verb with a specific content type, the information also includes the content type, revision, state, sha1 hash, and updated at/by stamps.
; $verb_detail(#0, "verb_detail") ["content_type" -> "application/x-moocode", "dobj" -> "this", "iobj" -> "this", "names" -> "verb_detail", "owner" -> #5, "perms" -> "xd", "prep" -> "none", "revision" -> 2, "sha1" -> "172E3B6EFAB13F7D5C64774D3E6BBB8592442EF5", "state" -> "clean", "updated_at" -> "Sun May 27 13:06:44 2012 EDT", "updated_by" -> #5]
The status of a verb can either be "clean" or "dirty". If a verb is dirty, there are changes to the verb that have not been committed or reverted. The @diff
command displays the difference between the current dirty version and the previous clean version (if one exists) for review.
@diff #50:test
--- #50:test r2
+++ #50:test r3 (dirty)
@@ -22,2 +22,2 @@
*/
-if (player.programmer)
- tell('programmer')
+if (player.wizard)
+ tell('wizard') // yes, a wizard!
end
(done)
The @diff
command outputs a close variant of the unified diff format. Once editing is complete and differences reviewed, the changes can either be committed (@commit <object>:<verb>
) or reverted (@revert <object>:<verb>
) Committing the changes bumps up the revision number, updates the updated at and updated by stamps, and changes the state from "dirty" to "clean". Reverting replaces the changes with the previous version of the verb.
The combination of @diff
, @commit
and @revert
provides a very simple, but very useful form of in-MOO source code control.
The following example illustrates how these pieces all fit together.
; x = create($nothing, 1) *anonymous* "" ; add_verb(x, {x.owner, "xd", "test"}, {"this", "none", "this"}) 1 @ins x "" (*anonymous*) -rwfa Parents: {}, Children: {} Location: #-1, Contents: {} Owner: #50 -- Verbs -- test {#50, "xd", "test"} {"this", "none", "this"} (done) @program x:test as application/x-mustache <h1>Hello, {{name}}!</h1> . Programming succeeded for x:test @ins x "" (*anonymous*) -rwfa Parents: {}, Children: {} Location: #-1, Contents: {} Owner: #50 -- Verbs -- test [dirty] application/x-mustache {#50, "xd", "test"} {"this", "none", "this"} (done) ; x:test(["name" -> "Foo Bar"]) {"<h1>Hello, Foo Bar!</h1>"} @list x:test <h1>Hello, {{name}}!</h1> ; $verb_detail(x, "test") ["content_type" -> "application/x-mustache", "dobj" -> "this", "iobj" -> "this", "names" -> "test", "owner" -> #50, "perms" -> "xd", "prep" -> "none", "revision" -> 1, "sha1" -> "466440D276DAE064E348E68D122519D13500E731", "state" -> "dirty", "updated_at" -> "Mon Jan 14 20:35:46 2013 EST", "updated_by" -> #50] @diff x:test There is nothing to diff; there is only one revision. @commit x:test You committed the changes to x:test @ins x "" (*anonymous*) -rwfa Parents: {}, Children: {} Location: #-1, Contents: {} Owner: #50 -- Verbs -- test application/x-mustache {#50, "xd", "test"} {"this", "none", "this"} (done)
Enjoy!
Todd Sundsted