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 http://www.cs.ucla.edu/~awarth/papers/dls07.pdf) 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 | | r(x, y) | (see note #3) | ^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 :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., 123 x foo.bar() 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 "{" * "}" 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 "foo" does not match the string 'foo'; it is instead shorthand for token('foo') 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). Rules ----- Here is a parameterized rule taken from the paper, in the original OMeta syntax: cRange x y ::= :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) and 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")