//// // LDPL 4.4 // ////////////////////////////////////////////////////////////////////// // PEST CHEAT SHEET // https://pest.rs/book/grammars/syntax.html#cheat-sheet // // PREFIX | MEANING // _ Silent: Doesn't appear in parse tree // @ Atomic: Ignore whitespace, inner matches are silent // $ Compound Atomic: No whitespace, inner matches aren't silent // & Positive non-capture // ! Negative non-capture // ^ Case insensitive (terminals only) ////////////////////////////////////////////////////////////////////// WHITESPACE = _{ " " | "\t" | "\r" } COMMENT = _{ "#" ~ (!"\n" ~ ANY)* } program = _{ SOI // start of input ~ "\n"* ~ (header_stmt ~ "\n"+)* ~ "\n"* ~ data_section? ~ "\n"* ~ procedure_section? ~ "\n"* ~ EOI // end of input } //// // C++ EXTENSION // header_stmt = { include_stmt | using_stmt | extension_stmt | flag_stmt } include_stmt = { ^"INCLUDE" ~ text } using_stmt = { ^"USING" ~ ^"PACKAGE" ~ ident } extension_stmt = { ^"EXTENSION" ~ text } flag_stmt = { ^"FLAG" ~ text ~ text | ^"FLAG" ~ text } //// // DATA: // data_section = { ^"DATA:" ~ "\n"+ ~ ((type_def | external_type_def) ~ "\n"+)* } type_def = { ident ~ ^"IS" ~ type_name } external_type_def = { ident ~ ^"IS" ~ ^"EXTERNAL" ~ type_name } // Ident: person, something.with.dots, _, __INIT__, etc // https://docs.ldpl-lang.org/naming/ banned = { ":" | "(" | ")" | "\"" | " " | "\t" | "\n" | "\r"} ident = @{ (!banned ~ ANY)+ } // TODO: x OF y.. type_name = { // legacy ^"NUMBER VECTOR" | ^"TEXT VECTOR" | // hip and cool ^"NUMBER LIST" | ^"NUMBER MAP" | ^"NUMBER" | ^"TEXT LIST" | ^"TEXT MAP" | ^"TEXT" } //// // PROCEDURE: // procedure_section = { ^"PROCEDURE:" ~ "\n"+ ~ (proc_stmt ~ "\n"+)* ~ proc_stmt? } proc_stmt = _{ create_stmt_stmt | sub_def_stmt | subproc_stmt } // Number sign = { "+" | "-" } digit = { ASCII_DIGIT } number = @{ sign? ~ digit+ ~ ("." ~ digit+)? } number_list = @{ number ~ (" " ~ number)* } // Text text = ${ "\"" ~ text_inner ~ "\"" } text_inner = @{ char* } char = { !("\"" | "\\") ~ ANY | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "e" | "033") | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } // Linefeed: lf, crlf linefeed = ${ ^"LF" | ^"CRLF" } // Lookup: person:"Name", list:5, etc lookup = ${ ident ~ (":" ~ (text | number | ident))+ } // Variable: name, something.with.dots, person:"Name", etc var = ${ lookup | ident } // expressions are either variables, text, or numbers. expr = _{ linefeed | number | text | var } // expr list: one two "three" 4 5 "six" expr_list = _{ " "* ~ (expr ~ " "*)+ } // create a statement... statment create_stmt_stmt = { ^"CREATE" ~ ^"STATEMENT" ~ text ~ ^"EXECUTING" ~ ident } // sub-procedure aka function sub_def_stmt = { external? ~ (^"SUB-PROCEDURE" | ^"SUB") ~ ident ~ "\n"+ ~ sub_param_section? ~ sub_data_section? ~ ^"PROCEDURE:"? ~ "\n"* ~ (subproc_stmt ~ "\n"+)* ~ "\n"* ~ (^"END SUB-PROCEDURE" | ^"END SUB") } external = { ^"EXTERNAL" } sub_param_section = { ^"PARAMETERS:" ~ "\n"+ ~ (type_def ~ "\n"+)+ } sub_data_section = { ^"LOCAL DATA:" ~ "\n"+ ~ (type_def ~ "\n"+)+ } // valid procedure: statments subproc_stmt = _{ flow_stmt | arithmetic_stmt | text_stmt | list_stmt | map_stmt | list_and_map_stmt | io_stmt | user_stmt } //// // FLOW // flow_stmt = _{ call_stmt | if_stmt | while_stmt | for_each_stmt | for_stmt | loop_kw_stmt | return_stmt | goto_stmt | label_stmt | store_quote_stmt | store_stmt | exit_stmt | wait_stmt } if_stmt = { ^"IF" ~ test_expr ~ ^"THEN" ~ "\n"+ ~ (else_stmt ~ "\n"+ | subproc_stmt ~ "\n"+)* ~ "\n"* ~ (^"END" ~ ^"IF" | ^"END-IF") } else_stmt = { ^"ELSE" ~ ^"IF" ~ test_expr ~ ^"THEN" | ^"ELSE" } while_stmt = { ^"WHILE" ~ test_expr ~ ^"DO" ~ "\n"+ ~ (subproc_stmt ~ "\n"+)* ~ "\n"* ~ ^"REPEAT" } for_each_stmt = { ^"FOR" ~ ^"EACH" ~ ident ~ ^"IN" ~ expr ~ ^"DO" ~ "\n" ~ (subproc_stmt ~ "\n"+)* ~ "\n"* ~ ^"REPEAT" } for_stmt = { ^"FOR" ~ ident ~ ^"FROM" ~ expr ~ ^"TO" ~ expr ~ ^"STEP" ~ expr ~ ^"DO" ~ "\n" ~ (subproc_stmt ~ "\n"+)* ~ "\n"* ~ ^"REPEAT" } // used in if, while, for test_expr = { or_test_expr | and_test_expr | one_test_expr } or_test_expr = { one_test_expr ~ ^"OR" ~ test_expr } and_test_expr = { one_test_expr ~ ^"AND" ~ test_expr } one_test_expr = { expr ~ ( equal_expr | not_equal_expr | gte_expr | gt_expr | lte_expr | lt_expr ) ~ expr } equal_expr = { ^"IS" ~ ^"EQUAL" ~ ^"TO" } not_equal_expr = { ^"IS" ~ ^"NOT" ~ ^"EQUAL" ~ ^"TO" } gte_expr = { ^"IS" ~ ^"GREATER" ~ ^"THAN" ~ ^"OR" ~ ^"EQUAL" ~ ^"TO" } gt_expr = { ^"IS" ~ ^"GREATER" ~ ^"THAN" } lt_expr = { ^"IS" ~ ^"LESS" ~ ^"THAN" } lte_expr = { ^"IS" ~ ^"LESS" ~ ^"THAN" ~ ^"OR" ~ ^"EQUAL" ~ ^"TO" } loop_kw_stmt = { ^"BREAK" | ^"CONTINUE" } return_stmt = { ^"RETURN" } goto_stmt = { ^"GOTO" ~ ident } label_stmt = { ^"LABEL" ~ ident } store_stmt = { ^"STORE" ~ expr ~ ^"IN" ~ var } exit_stmt = { ^"EXIT" } wait_stmt = { ^"WAIT" ~ expr ~ ^"MILLISECONDS" } call_stmt = { call_external_stmt | call_sub_stmt } call_sub_stmt = { call_legacy_stmt | call_newstyle_stmt } call_external_stmt = { ^"CALL" ~ ^"EXTERNAL" ~ var ~ (^"WITH" ~ expr_list)? } call_legacy_stmt = _{ ^"CALL" ~ ^"SUB-PROCEDURE" ~ var ~ (^"WITH" ~ expr_list)? } call_newstyle_stmt = _{ ^"CALL" ~ var ~ (^"WITH" ~ expr_list)? } //// // ARITHMETIC // arithmetic_stmt = _{ solve_stmt | floor_stmt | ceil_stmt | modulo_stmt | get_rand_stmt | raise_stmt | log_stmt | sin_stmt | cos_stmt | tan_stmt } math_op = { "+" | "-" | "*" | "/" | "^" | "(" | ")"} solve_expr = { ((math_op | expr) ~ " "*)+ } solve_stmt = { ^"IN" ~ var ~ ^"SOLVE" ~ solve_expr } floor_stmt = { floor_in_stmt | floor_mut_stmt } floor_mut_stmt = { ^"FLOOR" ~ expr } floor_in_stmt = { ^"FLOOR" ~ expr ~ ^"IN" ~ var } ceil_stmt = { ceil_in_stmt | ceil_expr_stmt } ceil_expr_stmt = { ^"CEIL" ~ expr } ceil_in_stmt = { ^"CEIL" ~ expr ~ ^"IN" ~ var } modulo_stmt = { ^"MODULO" ~ expr ~ ^"BY" ~ expr ~ ^"IN" ~ var } get_rand_stmt = { ^"GET" ~ ^"RANDOM" ~ ^"IN" ~ var } raise_stmt = { ^"RAISE" ~ expr ~ ^"TO" ~ ^"THE" ~ expr ~ ^"IN" ~ var } log_stmt = { ^"LOG" ~ expr ~ ^"IN" ~ var } sin_stmt = { ^"SIN" ~ expr ~ ^"IN" ~ var } cos_stmt = { ^"COS" ~ expr ~ ^"IN" ~ var } tan_stmt = { ^"TAN" ~ expr ~ ^"IN" ~ var } //// // TEXT // text_stmt = _{ join_stmt | old_join_stmt | replace_stmt | split_stmt | get_char_stmt | get_ascii_stmt | get_char_code_stmt | get_index_stmt | count_stmt | substr_stmt | trim_stmt } join_stmt = { ^"IN" ~ var ~ ^"JOIN" ~ expr_list } old_join_stmt = { ^"JOIN" ~ expr ~ ^"AND" ~ expr ~ ^"IN" ~ var } replace_stmt = { ^"REPLACE" ~ expr ~ ^"FROM" ~ expr ~ ^"WITH" ~ expr ~ ^"IN" ~ var } split_stmt = { ^"SPLIT" ~ expr ~ ^"BY" ~ expr ~ ^"IN" ~ var } get_char_stmt = { ^"GET" ~ ^"CHARACTER" ~ ^"AT" ~ expr ~ ^"FROM" ~ expr ~ ^"IN" ~ var } get_ascii_stmt = { ^"GET" ~ ^"ASCII" ~ ^"CHARACTER" ~ expr ~ ^"IN" ~ var } get_char_code_stmt = { ^"GET" ~ ^"CHARACTER" ~ ^"CODE" ~ ^"OF" ~ expr ~ ^"IN" ~ var } get_index_stmt = { ^"GET" ~ ^"INDEX" ~ ^"OF" ~ expr ~ ^"FROM" ~ expr ~ ^"IN" ~ var } count_stmt = { ^"COUNT" ~ expr ~ ^"FROM" ~ expr ~ ^"IN" ~ var } substr_stmt = { ^"SUBSTRING" ~ expr ~ ^"FROM" ~ expr ~ ^"LENGTH" ~ expr ~ ^"IN" ~ var } trim_stmt = { ^"TRIM" ~ expr ~ ^"IN" ~ var } store_quote_stmt = { ^"STORE" ~ ^"QUOTE" ~ ^"IN" ~ var ~ store_quote_inner ~ "\n" ~ ^"END QUOTE" } store_quote_inner = { (!("\n" ~ ^"END QUOTE") ~ ANY)* } //// // LIST // list_stmt = _{ get_length_stmt // (also TEXT) | push_stmt | delete_stmt } get_length_stmt = { ^"GET" ~ ^"LENGTH" ~ ^"OF" ~ expr ~ ^"IN" ~ var } push_stmt = { ^"PUSH" ~ expr ~ ^"TO" ~ expr } delete_stmt = { ^"DELETE" ~ ^"LAST" ~ ^"ELEMENT" ~ ^"OF" ~ expr } //// // LIST & MAP // list_and_map_stmt = _{ clear_stmt | copy_stmt } clear_stmt = { ^"CLEAR" ~ expr } copy_stmt = { ^"COPY" ~ expr ~ ^"TO" ~ var } //// // MAP // map_stmt = _{ get_keys_count_stmt | get_keys_stmt } get_keys_count_stmt = { ^"GET" ~ ^"KEY" ~ ^"COUNT" ~ ^"OF" ~ expr ~ ^"IN" ~ var } get_keys_stmt = { ^"GET" ~ ^"KEYS" ~ ^"OF" ~ expr ~ ^"IN" ~ var } //// // IO // io_stmt = _{ display_stmt | load_stmt | write_stmt | append_stmt | accept_stmt | execute_stmt } display_stmt = { ^"DISPLAY" ~ expr_list } load_stmt = { ^"LOAD" ~ ^"FILE" ~ expr ~ ^"IN" ~ var } write_stmt = { ^"WRITE" ~ expr ~ ^"TO" ~ ^"FILE" ~ expr } append_stmt = { ^"APPEND" ~ expr ~ ^"TO" ~ ^"FILE" ~ expr } accept_stmt = { accept_eof_stmt | accept_var_stmt } accept_var_stmt = { ^"ACCEPT" ~ var } accept_eof_stmt = { ^"ACCEPT" ~ var ~ ^"UNTIL" ~ ^"EOF" } execute_stmt = { execute_exit_code_stmt | execute_output_stmt | execute_expr_stmt } execute_expr_stmt = { ^"EXECUTE" ~ expr } execute_output_stmt = { ^"EXECUTE" ~ expr ~ ^"AND" ~ ^"STORE" ~ ^"OUTPUT" ~ ^"IN" ~ var } execute_exit_code_stmt = { ^"EXECUTE" ~ expr ~ ^"AND" ~ ^"STORE" ~ ^"EXIT" ~ ^"CODE" ~ ^"IN" ~ var } //// // USER DEFINED STATEMENTS // user_stmt = { !(^"END" | ^"END-IF" | ^"REPEAT") ~ expr_list }