start: program

program: separator* line_or_comment (separator+ line_or_comment)* separator*

?line_or_comment: simple_stmt comment?
                | compound_stmt
                | comment
                | BLOCK_COMMENT

?separator: EOL+

comment: DOC_COMMENT | LINE_COMMENT

EOL: /\r?\n/

DOC_COMMENT.2: /\/\/\/[^\r\n]*/
LINE_COMMENT.1: /\/\/[^\r\n]*/
BLOCK_COMMENT: /\/\*[\s\S]*?\*\//

?simple_stmt: assignment
            | return_stmt
            | system_command
            | io_command
            | async_command
            | connector_cmd
            | db_command
            | http_command
            | util_command
            | modularity_cmd
            | call_stmt

?compound_stmt: function_decl
              | if_stmt
              | loop_stmt
              | try_stmt

assignment: identifier "=" expression

call_stmt: identifier "(" argument_list? ")"
         | identifier "=" identifier "." identifier "(" argument_list? ")"
         | identifier "." identifier "(" argument_list? ")"

system_command: register_cmd
              | addvar_cmd

register_cmd: "registerEndpoint" "(" stringliteral "," stringliteral "," list_display "," stringliteral "," identifier "," identifier ")"

addvar_cmd: "addVar" "(" addvar_arg "," addvar_arg ")"

addvar_arg: identifier
          | literal
          | "$" identifier

identifier: IDENTIFIER

system_variable: "_status"

io_command: addparam_cmd
          | getlistlen_cmd
          | addresult_cmd
          | getparamlist_cmd

addparam_cmd: "addParam" "(" stringliteral "," identifier ")"
getlistlen_cmd: "getListLen" "(" identifier "," identifier ")"
getparamlist_cmd: "getQueryParamList" "(" stringliteral "," identifier ")"
addresult_cmd: "addResult" "(" identifier ")"

if_stmt: "if" "(" if_condition ")" separator block ("else" "(" ")" separator block)? "end" "(" ")"

if_condition: if_atom "," if_atom "," stringliteral
            | "None" "," "None" "," stringliteral

if_atom: identifier
       | literal

loop_stmt: "startLoop" "(" identifier "," expression "," expression ")" separator block "endLoop" "(" ")"

try_stmt: "try" "(" ")" separator block "exception" "(" identifier ")" separator block "end" "(" ")"

block: separator* line_or_comment (separator+ line_or_comment)* separator*

async_command: go_stmt
             | gather_stmt

go_stmt: identifier "=" "go" identifier "(" argument_list? ")"
gather_stmt: identifier "=" "gather" "(" identifier ("," expression)? ")"

connector_cmd: connector_instantiation

connector_instantiation: identifier "=" "avapConnector" "(" stringliteral ")"

http_command: req_post_cmd
            | req_get_cmd

req_post_cmd: "RequestPost" "(" expression "," expression "," expression "," expression "," identifier "," expression ")"
req_get_cmd: "RequestGet" "(" expression "," expression "," expression "," identifier "," expression ")"

db_command: orm_direct
          | orm_check
          | orm_create
          | orm_select
          | orm_insert
          | orm_update

orm_direct: "ormDirect" "(" expression "," identifier ")"
orm_check: "ormCheckTable" "(" expression "," identifier ")"
orm_create: "ormCreateTable" "(" expression "," expression "," expression "," identifier ")"

orm_select: "ormAccessSelect" "(" orm_fields "," expression ("," expression)? "," identifier ")"

orm_fields: "*"
          | expression

orm_insert: "ormAccessInsert" "(" expression "," expression "," identifier ")"
orm_update: "ormAccessUpdate" "(" expression "," expression "," expression "," expression "," identifier ")"

util_command: json_list_cmd
            | crypto_cmd
            | regex_cmd
            | datetime_cmd
            | stamp_cmd
            | string_cmd
            | replace_cmd

json_list_cmd: "variableToList" "(" expression "," identifier ")"
             | "itemFromList" "(" identifier "," expression "," identifier ")"
             | "variableFromJSON" "(" identifier "," expression "," identifier ")"
             | "AddVariableToJSON" "(" expression "," expression "," identifier ")"

crypto_cmd: "encodeSHA256" "(" identifier_or_string "," identifier ")"
          | "encodeMD5" "(" identifier_or_string "," identifier ")"

regex_cmd: "getRegex" "(" identifier "," stringliteral "," identifier ")"

datetime_cmd: "getDateTime" "(" stringliteral "," expression "," stringliteral "," identifier ")"

stamp_cmd: "stampToDatetime" "(" expression "," stringliteral "," expression "," identifier ")"
         | "getTimeStamp" "(" stringliteral "," stringliteral "," expression "," identifier ")"

string_cmd: "randomString" "(" expression "," expression "," identifier ")"

replace_cmd: "replace" "(" identifier_or_string "," stringliteral "," stringliteral "," identifier ")"

function_decl: "function" identifier "(" param_list? ")" "{" separator block "}"

param_list: identifier ("," identifier)*

return_stmt: "return" "(" expression? ")"

modularity_cmd: include_stmt
              | import_stmt

include_stmt: "include" stringliteral
import_stmt: "import" ("<" identifier ">" | stringliteral)

?expression: logical_or

?logical_or: logical_and ("or" logical_and)*
?logical_and: logical_not ("and" logical_not)*

?logical_not: "not" logical_not
            | comparison

?comparison: arithmetic (comp_op arithmetic)*

comp_op: "==" | "!=" | "<" | ">" | "<=" | ">=" | "in" | "is"

?arithmetic: term (("+" | "-") term)*
?term: factor (("*" | "/" | "%") factor)*

?factor: ("+" | "-") factor
       | power

?power: primary ("**" factor)?

?primary: atom postfix*

postfix: "." identifier
       | "[" expression "]"
       | "[" expression? ":" expression? (":" expression?)? "]"
       | "(" argument_list? ")"

?atom: identifier
     | "$" identifier
     | literal
     | "(" expression ")"
     | list_display
     | dict_display

list_display: "[" argument_list? "]"
            | "[" expression "for" identifier "in" expression if_clause? "]"

if_clause: "if" expression

dict_display: "{" key_datum_list? "}"

key_datum_list: key_datum ("," key_datum)*
key_datum: expression ":" expression

argument_list: expression ("," expression)*

number: FLOATNUMBER
      | INTEGER

literal: stringliteral
       | number
       | boolean
       | "None"

boolean: "True" | "False"

INTEGER: /[0-9]+/
FLOATNUMBER: /(?:[0-9]+\.[0-9]*|\.[0-9]+)/

stringliteral: STRING_DOUBLE
             | STRING_SINGLE

# STRING_DOUBLE: /"([^"\\]|\\["'\\ntr0])*"/
# STRING_SINGLE: /'([^'\\]|\\["'\\ntr0])*'/
STRING_DOUBLE: /"([^"\\]|\\.)*"/
STRING_SINGLE: /'([^'\\]|\\.)*'/

identifier_or_string: identifier
                    | stringliteral

IDENTIFIER: /[A-Za-z_][A-Za-z0-9_]*/

%ignore /[ \t]+/