ometa A <: Parser { //numbers and words d = digit, n = d:x n:y -> (x+y) | d, a = letter, w = a:x w:y -> (x+y) | a, space = ~'\n' super('space'), variable = spaces w:x ? (x != 'v') -> ['?', x], literal = spaces n:x -> [parseInt(x)], //number lists nlist = listOf('literal', ''), //tune element = literal | variable, //for tunes, we have the extra ^ and v operations tune_op = "(" tune_op_add:x ")" -> x | "^" tune_expr_list:x -> ['^'].concat(x) | "v" tune_expr_list:x -> ['v'].concat(x) | element:x -> x, tune_op_mul = tune_op_mul:x "*" tune_op:y -> ['*'].concat([x]).concat([y]) | tune_op_mul:x "/" tune_op:y -> ['/'].concat([x]).concat([y]) | tune_op, tune_op_add = tune_op_add:x "+" tune_op_mul:y -> ['+'].concat([x]).concat([y]) | tune_op_add:x "-" tune_op_mul:y -> ['-'].concat([x]).concat([y]) | tune_op_mul, tune_expr = tune_op_add, tune_expr_list = listOf('tune_expr', ''), //for other parts of the melody, it's just math op = "(" op_add:x ")" -> x | element:x -> x, op_mul = op_mul:x "*" op:y -> ['*'].concat([x]).concat([y]) | op_mul:x "/" op:y -> ['/'].concat([x]).concat([y]) | op, op_add = op_add:x "+" op_mul:y -> ['+'].concat([x]).concat([y]) | op_add:x "-" op_mul:y -> ['-'].concat([x]).concat([y]) | op_mul, expr = op_add, expr_list = listOf('expr', ''), tune = "\"" tune_expr_list:x "\"" -> x, timing = ":" expr_list:x ":" -> x, scale = "$" expr_list:x "$" -> x, position = "@" expr_list:x ? (x.length == 1) -> x, melody_literal = tune:x1 "\n" timing:x2 "\n" scale:x3 "\n" position:x4 -> ['!', [x1, x2, x3, x4]], melody_variable = variable, tune_optional = tune:x "\n" -> x | "" -> [[]], timing_optional = timing:x "\n" -> x | "" -> [[]], scale_optional = scale:x "\n" -> x | "" -> [[]], position_optional = position:x "\n" -> x | "" -> [[]], melody_extend = variable:x1 "except" "\n" tune_optional:x2 timing_optional:x3 scale_optional:x4 position_optional:x5 ? ( x2[0].length > 0 || x3[0].length > 0 || x4[0].length > 0 || x5[0].length > 0) -> { var tune var timing var scale var position if (x2[0].length > 0) tune = x2 else tune = x1 if (x3[0].length > 0) timing = x2 else timing = x1 if (x4[0].length > 0) scale = x2 else scale = x1 if (x5[0].length > 0) position = x2 else position = x1 ['!', [tune, timing, scale, position]] }, melody_element = melody_literal | melody_extend | melody_variable, optional_newlines = "\n" optional_newlines | "", melody_op = "(" melody_op_concat:x ")" -> x | melody_element, melody_op_and = melody_op_and:x optional_newlines "&" optional_newlines melody_op:y -> ['&', x, y] | melody_op, melody_op_concat = melody_op_concat:x optional_newlines "." optional_newlines melody_op_and:y -> ['.', x, y] | melody_op_and, melody_expr = melody_op_concat, //definitions definition = optional_newlines variable:x "=" optional_newlines melody_expr:y -> y, definitions = listOf('definition', '\n'), program = definitions } x = 'x except " 1 2 3 4 5" ' A.matchAll(x,'melody_element')