Plastic is a pluggable, flexible, extensible MOOcode parser/compiler.  Plastic currently implements all of LambdaMOO's existing language features, with a few slight deviations from and/or extensions to the original syntax.

TDOP

Plastic is a Top Down Operator Precedence, or TDOP parser, also known as a "Pratt Parser" after Vaughan Pratt, who first described this class of parsers. If you want to learn more about TDOP parsers in general, I recommend Pratt's original paper and all of the following:

The Plastic parser generates a syntax tree from source conforming to the supported grammar. The grammar is a near superset of the LambdaMOO language grammar. Plastic comes with a compiler that translates the syntax tree into plain-old-MOOcode (minus the language enhancements, of course). See the Rationale, below, for the justification for this approach. The examples in the section below illustrate how this works in practice.

Usage

Installing Plastic via Composed, the package manager, automatically makes the MOOcode parser/compiler available.

If you invoke the compile() method on $plastic.compiler and pass in a list of lines of source, it will transform the Plastic syntax into plain-old-MOOcode. Notice that the missing semicolons are added and end becomes endif in the output:

; $plastic.compiler:compile({"if (player.wizard)", "notify(player, 'you are a wizard')", "end"})
=> {"if (player.wizard)", "notify(player, \"you are a wizard\");", "endif"}

The compiler is automatically added to the map of external compilers. When programming a verb, the source type is still "application/x-moocode" but the version must be set to the correct version of Plastic syntax (current "1.0.0").

Assuming there is a verb on player named "test", the following compiles:

@program me:test as application/x-moocode;version=1.0.0
if (player.wizard)
  player:tell('wizard')
end
if (player.programmer)
  player:tell('programmer')
end
if (player.name != "Bob")
  player:tell('Your name is not "Bob".')
else
  player:tell('Your name is "Bob".')
end
.

And runs!

; me:test()
wizard
programmer
Your name is not "Bob".

The original source is available for editing.

@list me:test
if (player.wizard)
  player:tell('wizard')
end
if (player.programmer)
  player:tell('programmer')
end
if (player.name != "Bob")
  player:tell('Your name is not "Bob".')
else
  player:tell('Your name is "Bob".')
end

Rationale

The in-MOO, MOOcode-to-MOOcode strategy may seem questionable. Why not build it into the server? The Plastic approach has several advantages over in-server parsers:

  1. it's far easier, faster, and safer to hack in MOOcode than C
  2. the output is plain-old-moocode, which cuts down on database-level incompatibilities as the language evolves

TDOP is not as widely used or understood as alternative approaches. However, in an experimental, dynamic environment like Stunt/LambdaMOO, a TDOP parser has several advantages:

  1. it's token-based orientation maps naturally to how we (me, at least) think about languages (as keywords, operators, identifiers, etc. rather than rules)
  2. it is very, very easy to extend by plugging in new types of tokens
  3. it is very, very easy to extend during parsing itself

I will be writing about extending Plastic in the near future.