|
|
@@ -5,18 +5,88 @@
|
|
|
locations = opts.locations !== undefined ? opts.locations : false,
|
|
|
comments = opts.comments !== undefined ? opts.comments : false;
|
|
|
|
|
|
- var err = function err(msg, suffix) {
|
|
|
+ var err = function err(msg, suffix, opts) {
|
|
|
+ opts = opts || {};
|
|
|
if (typeof msg === "number") msg = "ERR" + msg + ": " + errors[msg];
|
|
|
if (suffix) msg += suffix;
|
|
|
- return new SyntaxError(msg, null, null, offset(), line(), column());
|
|
|
+ return new SyntaxError(msg,
|
|
|
+ opts.expected,
|
|
|
+ opts.found,
|
|
|
+ offset(),
|
|
|
+ opts.line || line(),
|
|
|
+ opts.col || column()
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ var scopify = function scopify(i, q) {
|
|
|
+ if (q === null) q = {min:1, max:1};
|
|
|
+ i.scope = q;
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+
|
|
|
+ var minmaxify = function minmaxify(lo, hi) {
|
|
|
+ var m = hi ? hi.v || null : lo;
|
|
|
+ return {min:lo, max:m};
|
|
|
+ }
|
|
|
+
|
|
|
+ var model = function model(statements) {
|
|
|
+
|
|
|
+ var model = {
|
|
|
+ systems: {},
|
|
|
+ behaviors: {},
|
|
|
+ interactions: [],
|
|
|
+ triggers: [],
|
|
|
+ };
|
|
|
+
|
|
|
+ for (var s in statements) {
|
|
|
+ var statement = statements[s];
|
|
|
+
|
|
|
+ switch(statements[s].type) {
|
|
|
+ case "System":
|
|
|
+ model.systems[statement.id] = statement;
|
|
|
+ break;
|
|
|
+ case "Behavior":
|
|
|
+ model.behaviors[statement.id] = statement;
|
|
|
+ break;
|
|
|
+ case "Order":
|
|
|
+ case "Join":
|
|
|
+ model.interactions.push(statement);
|
|
|
+ break;
|
|
|
+ case "Trigger":
|
|
|
+ model.triggers.push(statement);
|
|
|
+ break;
|
|
|
+ default: break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var r in ast.references) {
|
|
|
+ var ref = ast.references[r];
|
|
|
+
|
|
|
+ if (!model.behaviors[ref])
|
|
|
+ model.behaviors[ref] = { type:"Behavior", id:ref, body:[] };
|
|
|
+ }
|
|
|
+
|
|
|
+ return warnify(model);
|
|
|
+ };
|
|
|
+
|
|
|
+ var warnify = function warnify(model) {
|
|
|
+ model.errors = ast.errors;
|
|
|
+
|
|
|
+ for (var i in model.interactions) {
|
|
|
+ model.interactions[i].body.forEach(function (sys) {
|
|
|
+ if (!model.systems[sys.system])
|
|
|
+ model.errors.push(err("WARNING: reference to an undefined system (" + sys.system + ")", null, sys.location));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return model;
|
|
|
};
|
|
|
|
|
|
var ast = {
|
|
|
|
|
|
- errors: [], // tracks all of the non-grammatical errors
|
|
|
comments: [],
|
|
|
- eventRefs: [],
|
|
|
- properties: [],
|
|
|
+ errors: [],
|
|
|
+ references: [],
|
|
|
|
|
|
Base: function Base(type) {
|
|
|
this.type = type;
|
|
|
@@ -60,11 +130,13 @@
|
|
|
Sequence: function Sequence(body) {
|
|
|
ast.Base.call(this, "Sequence");
|
|
|
this.body = body;
|
|
|
+ this.scope = { min:1, max:1 };
|
|
|
},
|
|
|
|
|
|
Alternation: function Alternation(body) {
|
|
|
ast.Base.call(this, "Alternation");
|
|
|
this.body = body;
|
|
|
+ this.scope = { min:1, max:1 };
|
|
|
},
|
|
|
|
|
|
Group: function Group(async, body) {
|
|
|
@@ -94,20 +166,13 @@
|
|
|
|
|
|
};
|
|
|
|
|
|
- var errors = {
|
|
|
- 0: "System has been defined twice: ",
|
|
|
- 1: "Behavior has been defined twice: ",
|
|
|
- 2: "mixed operators used in single Interaction",
|
|
|
- };
|
|
|
-
|
|
|
} //jshint ignore:start
|
|
|
|
|
|
|
|
|
|
|
|
start
|
|
|
- = !{ ast.errors = []; }
|
|
|
- s:statement* EOF
|
|
|
- { return { errors: ast.errors, statements: s, comments: ast.comments }; }
|
|
|
+ = s:statement*
|
|
|
+ { return model(s); }
|
|
|
|
|
|
statement
|
|
|
= s:( system_statement
|
|
|
@@ -118,20 +183,25 @@ statement
|
|
|
{ return s; }
|
|
|
|
|
|
system_statement
|
|
|
- = _SYSTEM_ _COLON_ id:system_id _EQ_ body:behavior_pattern
|
|
|
+ = _SYSTEM_ _COLON_ id:system_id
|
|
|
+ !{ ast.toplevelname = id; }
|
|
|
+ _EQ_ body:behavior_pattern
|
|
|
{ return new ast.System(id, body); }
|
|
|
|
|
|
behavior_statement
|
|
|
- = _BEHAVIOR_ _COLON_ id:behavior_id _EQ_ body:behavior_pattern
|
|
|
+ = _BEHAVIOR_ _COLON_ id:behavior_id
|
|
|
+ !{ ast.toplevelname = id; }
|
|
|
+ _EQ_ body:behavior_pattern
|
|
|
{ return new ast.Behavior(id, body); }
|
|
|
|
|
|
interaction_statement
|
|
|
- = _INTERACTION_ _COLON_ first:( system_item_selector / interaction_expr )
|
|
|
- type:interaction_type !{ast.t=type} second:( system_item_selector / interaction_expr )
|
|
|
+ = _INTERACTION_ _COLON_ first:( system_item_selector / expression )
|
|
|
+ type:interaction_type second:( system_item_selector / expression )
|
|
|
+ !{ ast.interactionType = type; }
|
|
|
tail:(
|
|
|
type2:interaction_type
|
|
|
- !{ if (type2 != ast.t) ast.errors.push(err("Incompatible Interaction Types in same Interaction Statement")); }
|
|
|
- tail2:( system_item_selector / interaction_expr )
|
|
|
+ !{ if (type2 !== ast.interactionType) ast.errors.push(err("ERROR: Mixed interaction types", null, { expected:ast.interactionType, found:type2 })); }
|
|
|
+ tail2:( system_item_selector / expression )
|
|
|
{ return tail2; }
|
|
|
)*
|
|
|
{ return new ast.Interaction(type, [first, second].concat(tail)); }
|
|
|
@@ -141,12 +211,11 @@ interaction_type
|
|
|
{ return t; }
|
|
|
|
|
|
system_item_selector
|
|
|
- = !([a-z0-9 ]i+ operator)
|
|
|
- _ sys:system_id sub:(":" _ su:("*" / behavior_pattern) { return su; } )? _
|
|
|
- { return new ast.Selector(sys, sub); }
|
|
|
+ = !([a-z0-9 ]i+ operator) _ sys:system_id ":" _ p:behavior_pattern _
|
|
|
+ { return new ast.Selector(sys, p); }
|
|
|
|
|
|
-interaction_expr
|
|
|
- = term:term op:operator expr:interaction_expr
|
|
|
+expression
|
|
|
+ = term:term op:operator expr:expression
|
|
|
{ return new ast.Expression(op, term, expr); }
|
|
|
/ term
|
|
|
|
|
|
@@ -160,7 +229,7 @@ operator
|
|
|
/ _ o:"<" _ { return o; }
|
|
|
/ _ o:">=" _ { return o; }
|
|
|
/ _ o:"<=" _ { return o; }
|
|
|
- / _ o:"~=" _ { return o; }
|
|
|
+ / _ o:"==" _ { return o; }
|
|
|
|
|
|
behavior_pattern
|
|
|
= alternation
|
|
|
@@ -176,10 +245,7 @@ sequence
|
|
|
|
|
|
behavior_pattern_item
|
|
|
= _ i:( group / event_ref ) q:quantifier? _
|
|
|
- {
|
|
|
- if (q === null) q = {min:1, max:1};
|
|
|
- i.scope = q; return i;
|
|
|
- }
|
|
|
+ { return scopify(i, q); }
|
|
|
|
|
|
group
|
|
|
= _ async:ASYNC? "(" _ body:behavior_pattern _ ")"
|
|
|
@@ -193,21 +259,29 @@ quantifier
|
|
|
|
|
|
quantifier_range
|
|
|
= "{" _ lo:INT_GTE0 hi:( u:_COMMA_ v:INT_GT0? { return {u:u, v:v}; } )? _ "}" _
|
|
|
- { var m = hi ? hi.v || null : lo; return {min:lo, max:m}; }
|
|
|
+ { return minmaxify(lo, hi); }
|
|
|
|
|
|
trigger
|
|
|
= _WHEN_ _COLON_ selector:( system_item_selector / expression ) "{" body:embedded_code "}"
|
|
|
{ return new ast.Trigger(selector, body); }
|
|
|
|
|
|
-/* TODO: dies on nested braces */
|
|
|
+/* FIXME: dies on nested braces */
|
|
|
embedded_code
|
|
|
= (![{}] .)*
|
|
|
{ return text().trim(); }
|
|
|
|
|
|
behavior_id = id:ID { return id; }
|
|
|
system_id = id:ID { return id; }
|
|
|
-event_ref = id:ID { if (ast.eventRefs.indexOf(id) == -1) ast.eventRefs.push(id); return { type: "unknown", id:id }; }
|
|
|
-property = id:ID { if (ast.properties.indexOf(id) == -1) ast.properties.push(id); return id; }
|
|
|
+property = id:ID { return id; }
|
|
|
+/* FIXME: it's possible, though not correct, to reference systems from a behavior; this will mark them as behaviors */
|
|
|
+event_ref = id:ID
|
|
|
+ {
|
|
|
+ if (id === ast.toplevelname) {
|
|
|
+ ast.errors.push(err("ERROR: Definition is recursive (" + id + ")"));
|
|
|
+ ast.toplevelname = null;
|
|
|
+ } else ast.references.push(id);
|
|
|
+ return { type: "Behavior", id:id };
|
|
|
+ }
|
|
|
|
|
|
|
|
|
KEYWORD = SYSTEM / BEHAVIOR / INTERACTION / ASYNC
|
|
|
@@ -273,18 +347,3 @@ COMMENT_LINE "Comment Line"
|
|
|
COMMENT_BLOCK "Comment Block"
|
|
|
= "/*" c:(!"*/" c:. {return c})* "*/"
|
|
|
{ return new ast.Comment(true, c.join("")); }
|
|
|
-
|
|
|
-
|
|
|
-EOF = !. {
|
|
|
- //-- SECOND PASS --//
|
|
|
- // TODO: collect accurate dictionaries of systems, behaviors, and atoms
|
|
|
- // TODO: mutate Orders and Joins into an array of constraints
|
|
|
- /* output?:
|
|
|
- {
|
|
|
- errors,
|
|
|
- systems,
|
|
|
- behaviors,
|
|
|
- constraints
|
|
|
- }
|
|
|
- */
|
|
|
-}
|