////
// 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
}