Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
150 lines (93 sloc) 6.08 KB
OMeta/JS is a new version of OMeta, a language for pattern-directed metaprogramming first described in
Alessandro Warth and Ian Piumarta, "OMeta: An Object-Oriented Language for Pattern-Matching," in Proceedings of the Dynamic
Languages Symposium, 2007. (Available at
This page contains the information necessary for someone who has read the OMeta paper to be able to use OMeta/JS.
Pattern Syntax
| "kind of thing" OMeta OMeta/JS |
| boolean true true |
| number 123 123 |
| character 'x' 'x' | (see note #1)
| string "foo" 'foo' |
| `foo |
| #foo |
| atom foo N/A |
| rule application <expr> expr |
| <r x y> r(x, y) | (see note #3)
| <super stmt> ^stmt | (see note #4)
| list ("hello" 42 answer ()) ['hello' 42 `answer []] |
| negation ~'x' ~'x' |
| look-ahead ~~'x' ~~'x' |
| &'x' |
| semantic predicate ?(> x y) ?(x > y) | (see note #2)
| semantic action => (+ x y) -> (x + y) |
| !(+ x y) !(x + y) |
| binding <expr>:x expr:x | (in OMeta/JS, spaces are not allowed before the colon)
| :x | (this is shorthand for "anything:x")
Note #1: There is no such thing as a character in JavaScript. Even though the language lets you access each "character" of a string via indexing, e.g, "foo"[0], the answer is not a character, but rather a string of length 1.
Note #2: In the version of OMeta described in the paper, semantic actions and predicates were written in COLA (kind of a mix between Scheme and Smalltalk). In OMeta/JS, they are written in JavaScript. More specifically, they are either primary expressions, e.g.,
new Person()
(x + y) // note that you need parentheses around "x + y" in order to make it into a primary expression
or something I made up called "statement expressions", which have the form
"{" <statement>* <expr> "}"
For example,
{ x += 2; y = "foo"; f(x) }
The value of a statement expression is equal to that of its last expression.
Note #3: The arguments you pass to a rule don't have to be statement expressions - they can be any JavaScript expression.
Note #4: In OMeta/JS, "super" is just like any other rule (not a special form), so you have to quote the rule name that you pass in as an argument, e.g., both ^r(1, 2) and super("r", 1, 2) are valid super-sends.
A "Handy" New Shorthand
In OMeta/JS, the pattern
does not match the string 'foo'; it is instead shorthand for
The Parser grammar provides a definition for token that skips any number of spaces, then tries to match the sequence of characters that was passed to it as an argument. I have used this in many of the example projects, and have found it to be very useful.
Still, there are times when this is not what you want. But that's not a problem, because you can define it to do whatever you want (see the JavaScript Compiler project for an example).
Here is a parameterized rule taken from the paper, in the original OMeta syntax:
cRange x y ::= <char>:c ?(>= c x)
?(<= c y) => c;
And here is the same rule rule, in the new OMeta/JS syntax:
cRange :x :y = char:c ?(c >= x)
?(c <= y) -> c
A couple of (purely syntactic) differences:
(1) rule declarations now use "=" instead of "::=", and
(2) they are no longer terminated with a ";"
A more significant difference has to do with the rule's arguments; note that in the OMeta/JS version, they are preceded by a ':'. This is actually shorthand for
cRange anything:x anything:y = ...
This change has to do with an improvement in the parameter-passing mechanism, which now allows a rule's parameters to be pattern-matched against. (See the paper's "Future Work" section for more details.)
The "=" is actually optional in rule declarations... this, combined with some new syntax that allows a rule to have multiple definitions that are tried in lexicographic order, allows programmers to write rules that have an "ML flavor":
ometa M {
fact 0 -> 1,
fact :n ?(n > 0) fact(n - 1):m -> (n * m)
M.match(5, "fact")
Grammar Syntax
The only change here has to do with rule declarations, which now must be separated by commas:
ometa M {
x = y z,
y = "foo" "bar",
z = "baz"
Using Grammars "from the outside"
The public interface provided by an OMeta/JS grammar object to the rest of the world consists of two methods:
match(object, ruleName)
matchAll(arrayOrStringObject, ruleName)
Here's an example that hopefully explains the difference between the two. The key to understanding it is that a string is just a list of characters.
ometa M <: Parser {
theCharacters = "the" "cat" "sat" "on" "the" "mat",
theWholeString = [theCharacters]
input = "the cat sat on the mat"
M.matchAll(input, "theCharacters")
M.match(input, "theWholeString")