start = comment / tlabel

tlabel = _ trigger:trigger? _ guard:("[" _ guard _ "]")? _ actions:("/" _ actions )? _ {
  return {
    kind: "transitionLabel",
    trigger: trigger ? trigger : {kind: "triggerless"},
    guard: guard ? guard[2] : {kind: "literal", value: true},
    actions: actions ? actions[2] : [],
  };
}

trigger = afterTrigger / entryTrigger / exitTrigger / eventTrigger

eventTrigger = event:identifier param:("(" identifier ")")? {
	return {kind: "event", event, paramName: param ? param[1] : undefined};
}

afterTrigger = "after" _ dur:durationMs {
	return {kind: "after", durationMs: dur};
}

entryTrigger = "entry" {
	return {kind: "entry"};
}

exitTrigger = "exit" {
	return {kind: "exit"};
}


durationMs = num:number _ u:timeUnit {
	return num * (u === "s" ? 1000 : 1);
}

timeUnit = "ms" / "s" {
	return text();
}

guard = expr

actions = head:action tail:(_ ";" _ action)* _ ";"? {
	return [head, ...tail.map(t => t[3])];
}
action = assignment / raise

assignment = lhs:identifier _ "=" _ rhs:expr {
	return {kind: "assignment", lhs, rhs};
}

identifier = ("_" / [a-zA-Z0-9])+ {
	return text();
}

number = [0-9]+ {
	return parseInt(text());
}

expr = compare

compare = sum:sum rest:((_ ("==" / "!=" / "<=" / ">=" / "<" / ">") _) compare)? {
	if (rest === null) {
    	return sum;
    }
    return {
        kind: "binaryExpr",
        operator: rest[0][1],
        lhs: sum,
        rhs: rest[1],
    };
}

sum = prod:product rest:((_ ("+" / "-" / "||") _) sum)? {
	if (rest === null) {
    	return prod;
    }
    return {
        kind: "binaryExpr",
        operator: rest[0][1],
        lhs: prod,
        rhs: rest[1],
    };
}

product = atom:unary rest:((_ ("*" / "/" / "%" / "&&" ) _) product)? {
	if (rest === null) {
    	return atom;
    }
    return {
        kind: "binaryExpr",
        operator: rest[0][1],
        lhs: atom,
        rhs: rest[1],
    };
}

unary = operator:("!" / "-")? atom:atom {
	if (operator === null) {
       return atom;
    }
    return {
    	kind: "unaryExpr",
        operator,
        expr: atom,
    }
}

atom = fnCall / nested / literal / ref

nested = "(" _ expr:expr _ ")" {
	return expr;
}

fnCall = fn:ref param:nested {
	return {
    	kind: "call",
        fn,
        param,
    };
}

literal = value:(number / boolean / string) {
	return {kind: "literal", value}
}

ref = variable:identifier {
	return {kind: "ref", variable}
}

boolean = ("true" / "false") {
	return text() === "true";
}

string = '"' str:([^"]*) '"' {
	return str.join('');
}

raise = "^" _ event:identifier param:("(" expr ")")? {
	return {kind: "raise", event, param: param ? param[1] : undefined};
}

_ "whitespace"
  = (comment / [ \t\n\r])*
  { return null; }

comment = "//" _ text:.* _ ('\n' / !.) {
	return {
    	kind: "comment",
        text: text.join(''),
    };
}

