grammar Certus;

start   : globalBind EOF 
        | localBind assignment EOF
        | assignment EOF
        ;

// WITH
//  sym1 as ... ;
//  sym2 as ... ;
//  ...
// END
localBind : WITH definition (STMT_SEP definition)* STMT_SEP? END ;

// GLOBAL
//  sym1 as ... ;
//  sym2 as ... ;
//  ...
// END
globalBind : GLOBAL definition (STMT_SEP definition)* STMT_SEP? END ;

// c0 is ... 
assignment : ID ASSIGN fuzzyExpr 
           | ID ASSIGN macroExpr;

definition : ID AS fuzzyExpr 
           | opDef AS fuzzyExpr ;

opDef : ID '('opTypedParam (ARG_SEP opTypedParam)*')' ;
opTypedParam : ID ':' nodeType ;
nodeType : ID ;

booleanExpr
        : '(' booleanExpr ')'
        | indicatorExpr
        | artifactExpr
        | NOT booleanExpr
        | booleanExpr AND booleanExpr // C0 < high AND C1 > low 
        | booleanExpr OR booleanExpr // C0 < high OR C1 > low 
        | fuzzyExpr LT fuzzyExpr  // C0 < C1
        | fuzzyExpr LEQ fuzzyExpr  // C0 <= C1
        | fuzzyExpr GT fuzzyExpr  // C0 > C1
        | fuzzyExpr GEQ fuzzyExpr  // C0 >= C1
        | fuzzyExpr EQ fuzzyExpr // C0 = C1, comparison in ranking
        | fuzzyExpr NEQ fuzzyExpr // C0 != C1, comparison in ranking
        | fuzzyExpr EXACTLY fuzzyExpr // C0 exactly C1, value matching
        | fuzzyExpr ASSIGN fuzzyExpr // C0 is C1
        | fuzzyExpr CONTAIN fuzzyExpr // C0 contains C1 
        | fuzzyExpr OVERLAP fuzzyExpr // C0 overlaps C1
        ; 

indicator : INDICATOR_PREFIX '"' (ID WS?)+ '"'
          | INDICATOR_PREFIX ID 
          ;

indicatorCategory: ID ; // User defined: warning, danger, etc.

indicatorExpr 
        : indicator ASSIGN indicatorCategory
        | indicator (EQ | EXACTLY) indicator
        | indicator LT indicator
        | indicator LEQ indicator
        | indicator GT indicator
        | indicator GEQ indicator
        | indicator NEQ indicator
        | indicator LT indicator
        | indicator (EQ | EXACTLY) FLOAT 
        | indicator LT FLOAT 
        | indicator LEQ FLOAT 
        | indicator GT FLOAT 
        | indicator GEQ FLOAT 
        | indicator NEQ FLOAT 
        ;

artifact : ARTIFACT_PREFIX '"' (ID WS?)+ '"'
         | ARTIFACT_PREFIX ID
         ;

artifactExpr : HAS artifact ;

// C0 is cases A < foo -> bar, B > foo -> bin, otherwise baz
casesExpr : CASES (case ARG_SEP)+ OTHERWISE fuzzyExpr ;
case : booleanExpr ARROW fuzzyExpr ;

macroExpr : MACRO_PREFIX ID ; // e.g., C0 is #foo 

fuzzyExpr 
        : fuzzyExpr INTERSECT fuzzyExpr 
        | fuzzyExpr UNION fuzzyExpr 
        | casesExpr
        | defineSet
        | builtInOp
        | invokedOp 
        | fuzzySym 
        ;

invokedOp : ID '(' ID (ARG_SEP ID)* ')'; // C0 is foo(C1, C2, C3)

fuzzySym : ID ;  // Could be node id or a set name

// Built-in Operators
builtInOp : minOp | maxOp | invertOp | compOp ;
minOp : 'min' '(' fuzzyExpr (ARG_SEP fuzzyExpr)* ')' ;
maxOp : 'max' '(' fuzzyExpr (ARG_SEP fuzzyExpr)* ')' ;
invertOp : INVERT '(' fuzzyExpr ')' ;
compOp : COMP '(' fuzzyExpr ')' ;


// User-Defined Sets
defineSet : setTrap | setTri | setUp | setDown | setConst | setCrisp ;
setTrap : 'trap' '(' FLOAT ARG_SEP FLOAT ARG_SEP FLOAT ARG_SEP FLOAT ')' ;
setTri : 'tri' '(' FLOAT ARG_SEP FLOAT ARG_SEP FLOAT ')' ;
setUp : 'up' '(' FLOAT ARG_SEP FLOAT ')' ;
setDown : 'down' '(' FLOAT ARG_SEP FLOAT ')' ;
setConst : 'const' '(' FLOAT ')' ;
setCrisp : '[' FLOAT ARG_SEP FLOAT ']' | 'crisp' '(' FLOAT ARG_SEP FLOAT ')';

// Operators and constants
ARROW : '->' ;
ASSIGN : 'is' | 'IS';
HAS : 'has' | 'HAS';
CONTAIN : 'contains' | 'CONTAINS';
OVERLAP : 'overlaps' | 'OVERLAPS';
EXACTLY : 'exactly' | '==' | 'EXACTLY' ;
UNION : 'union' | 'UNION';
INTERSECT : 'intersect' | 'INTERSECT' ;
WITH : 'with' | 'WITH';
GLOBAL : 'global' | 'GLOBAL';
END : 'end' | 'END';
AS : 'as' | 'AS';
NOT : '!' | 'not' | 'NOT' ;
INVERT : 'invert' | 'INVERT' ;
COMP : 'comp' | 'COMP' ;
AND : '&' | 'and' | 'AND' ;
OR :  '|' | 'or' | 'OR' ;
NEQ : '!=' | 'neq' | 'NEQ' ;
EQ : '=' | 'eq' | 'EQ' ;
GT : '>' | 'gt' | 'GT' ;
LT : '<' | 'lt' | 'LT' ;
GEQ : '>=' | 'geq' | 'GEQ' ;
LEQ : '<=' | 'leq' | 'LEQ' ;
MACRO_PREFIX : '#' ;
INDICATOR_PREFIX : '$' ;
ARTIFACT_PREFIX : '@' ;
CASES : 'cases' | 'CASES';
OTHERWISE : 'otherwise' | 'OTHERWISE' ;
FLOAT : ('-')?[0-9]+(.[0-9]+)? ;
ARG_SEP : ',' ;
STMT_SEP : ';' ;
ID : [a-zA-Z][a-zA-Z0-9_]* ; // Should be last rule to avoid greedy matching on IDs
WS : [ \t\r\n]+ -> skip ;