Grammar

Formal PEG grammar for the Plumbing language. Comments are in OCaml style (* ... *).

(* Plumbing grammar — PEG notation *)
(* Generated from parser.cmly *)

raw_program                                 <- top_decl* EOF
program                                     <- top_decl* EOF
wiring_stmt                                 <- wire_element SEMI wire_element (SEMI wire_element)*
wire_element                                <- wire_atom dot_field*
wire_chain                                  <- wire_element (SEMI wire_element)*
wire_atom                                   <- IDENT '@' port_name '(' IDENT ')'
                                               / IDENT '@' port_name '(' 'string' ')'
                                               / IDENT '@' port_name '(' 'int' ')'
                                               / IDENT '@' port_name '(' 'number' ')'
                                               / IDENT '@' port_name '(' 'bool' ')'
                                               / IDENT '@' port_name '(' PRIM_JSON ')'
                                               / IDENT '@' port_name '(' 'unit' ')'
                                               / KW_ID '@' port_name '(' IDENT ')'
                                               / KW_ID '@' port_name '(' 'string' ')'
                                               / KW_ID '@' port_name '(' 'int' ')'
                                               / KW_ID '@' port_name '(' 'number' ')'
                                               / KW_ID '@' port_name '(' 'bool' ')'
                                               / KW_ID '@' port_name '(' PRIM_JSON ')'
                                               / KW_ID '@' port_name '(' 'unit' ')'
                                               / KW_COPY '@' port_name '(' IDENT ')'
                                               / KW_COPY '@' port_name '(' 'string' ')'
                                               / KW_COPY '@' port_name '(' 'int' ')'
                                               / KW_COPY '@' port_name '(' 'number' ')'
                                               / KW_COPY '@' port_name '(' 'bool' ')'
                                               / KW_COPY '@' port_name '(' PRIM_JSON ')'
                                               / KW_COPY '@' port_name '(' 'unit' ')'
                                               / KW_MERGE '@' port_name '(' IDENT ')'
                                               / KW_MERGE '@' port_name '(' 'string' ')'
                                               / KW_MERGE '@' port_name '(' 'int' ')'
                                               / KW_MERGE '@' port_name '(' 'number' ')'
                                               / KW_MERGE '@' port_name '(' 'bool' ')'
                                               / KW_MERGE '@' port_name '(' PRIM_JSON ')'
                                               / KW_MERGE '@' port_name '(' 'unit' ')'
                                               / KW_DISCARD '@' port_name '(' IDENT ')'
                                               / KW_DISCARD '@' port_name '(' 'string' ')'
                                               / KW_DISCARD '@' port_name '(' 'int' ')'
                                               / KW_DISCARD '@' port_name '(' 'number' ')'
                                               / KW_DISCARD '@' port_name '(' 'bool' ')'
                                               / KW_DISCARD '@' port_name '(' PRIM_JSON ')'
                                               / KW_DISCARD '@' port_name '(' 'unit' ')'
                                               / KW_BARRIER '@' port_name '(' IDENT ')'
                                               / KW_BARRIER '@' port_name '(' 'string' ')'
                                               / KW_BARRIER '@' port_name '(' 'int' ')'
                                               / KW_BARRIER '@' port_name '(' 'number' ')'
                                               / KW_BARRIER '@' port_name '(' 'bool' ')'
                                               / KW_BARRIER '@' port_name '(' PRIM_JSON ')'
                                               / KW_BARRIER '@' port_name '(' 'unit' ')'
                                               / KW_MERGE_ANY '@' port_name '(' IDENT ')'
                                               / KW_MERGE_ANY '@' port_name '(' 'string' ')'
                                               / KW_MERGE_ANY '@' port_name '(' 'int' ')'
                                               / KW_MERGE_ANY '@' port_name '(' 'number' ')'
                                               / KW_MERGE_ANY '@' port_name '(' 'bool' ')'
                                               / KW_MERGE_ANY '@' port_name '(' PRIM_JSON ')'
                                               / KW_MERGE_ANY '@' port_name '(' 'unit' ')'
                                               / IDENT '@' port_name
                                               / KW_ID '@' port_name
                                               / KW_COPY '@' port_name
                                               / KW_MERGE '@' port_name
                                               / KW_DISCARD '@' port_name
                                               / KW_BARRIER '@' port_name
                                               / KW_MERGE_ANY '@' port_name
                                               / IDENT
                                               / KW_ID
                                               / KW_COPY
                                               / KW_MERGE
                                               / KW_DISCARD
                                               / KW_BARRIER
                                               / KW_MERGE_ANY
                                               / IDENT '(' expr ')'
                                               / '(' wire_chain STAR wire_chain (STAR wire_chain)* ')'
type_expr                                   <- type_atom
                                               / type_atom '|' type_atom ('|' type_atom)*
type_decl                                   <- process_name (DOT process_name)* '=' type_expr
type_atom                                   <- BANG type_atom
                                               / '{' field_items '}'
                                               / '[' type_expr ']'
                                               / '(' type_expr ')'
                                               / '(' type_expr ',' type_expr (',' type_expr)* ')'
                                               / 'string'
                                               / 'int'
                                               / 'number'
                                               / 'bool'
                                               / PRIM_JSON
                                               / 'unit'
                                               / process_name (DOT process_name)*
top_decl                                    <- annotation* 'type' type_decl
                                               / annotation* 'let' process_name (DOT process_name)* ':' type_expr '->' type_expr '=' impl
                                               / annotation* 'let' process_name (DOT process_name)* ':' type_expr '=' config_value
                                               / annotation* 'let' process_name (DOT process_name)* '=' config_value
                                               / USE IDENT
                                               / USE STRING
                                               / MODULE IDENT
                                               / annotation* protocol_decl
spawn_stmt                                  <- 'spawn' process_name (DOT process_name)* '(' spawn_arg (',' spawn_arg)* ')'
spawn_arg                                   <- IDENT
                                               / IDENT '=' IDENT
session_wire_stmt                           <- '(' IDENT ',' IDENT ')' BIDIR process_name (DOT process_name)* AS IDENT
session_type                                <- '{' session_branch (',' session_branch)* '}'
                                               / session_seq
session_seq                                 <- SEND session_msg_type DOT session_type
                                               / RECV session_msg_type DOT session_type
                                               / END
                                               / LOOP
session_msg_type                            <- BANG session_msg_type
                                               / '{' field_items '}'
                                               / '[' type_expr ']'
                                               / '(' type_expr ')'
                                               / '(' type_expr ',' type_expr (',' type_expr)* ')'
                                               / 'string'
                                               / 'int'
                                               / 'number'
                                               / 'bool'
                                               / PRIM_JSON
                                               / 'unit'
                                               / IDENT
session_branch                              <- IDENT ':' session_seq
protocol_decl                               <- PROTOCOL IDENT '=' session_type
port_name                                   <- IDENT
                                               / SEND
                                               / RECV
                                               / KW_MERGE_ANY
                                               / KW_ID
                                               / KW_COPY
                                               / KW_MERGE
                                               / KW_DISCARD
                                               / KW_BARRIER
                                               / END
plumb_body_item                             <- annotation* 'let' process_name (DOT process_name)* ':' type_expr '->' type_expr '=' impl
                                               / annotation* channel_decl
                                               / spawn_stmt
                                               / wiring_stmt
                                               / session_wire_stmt
loption(separated_nonempty_list(COMMA,NUM)) <- ''
                                               / NUM (',' NUM)*
impl                                        <- IDENT '(' '[' loption(separated_nonempty_list(COMMA,NUM)) ']' ')'
                                               / IDENT '(' '[' loption(separated_nonempty_list(COMMA,NUM)) ']' ',' IDENT ')'
                                               / IDENT '(' expr ')'
                                               / KW_ID
                                               / KW_COPY
                                               / KW_MERGE
                                               / KW_MERGE_ANY
                                               / KW_DISCARD
                                               / KW_BARRIER
                                               / 'agent' '{' config_items '}'
                                               / 'agent' process_name (DOT process_name)*
                                               / TOOL '{' config_items '}'
                                               / TOOL process_name (DOT process_name)*
                                               / BUILTIN '(' IDENT ')'
                                               / 'plumb' '(' IDENT (',' IDENT)* ')' '{' plumb_body_item* '}'
                                               / IDENT
field_rest                                  <- ''
                                               / ',' field_items
                                               / field field_rest
field_items                                 <- ''
                                               / field field_rest
field                                       <- IDENT '?'? ':' type_expr
expr_unary                                  <- MINUS expr_unary
                                               / NOT expr_unary
                                               / expr_atom
expr_or                                     <- expr_and PIPEPIPE expr_or
                                               / expr_and
expr_mul                                    <- expr_mul STAR expr_unary
                                               / expr_unary
expr_fields_rest                            <- ''
                                               / ',' expr_fields
                                               / expr_field expr_fields_rest
expr_fields                                 <- ''
                                               / expr_field expr_fields_rest
expr_field                                  <- IDENT ':' expr
expr_cmp                                    <- expr_add '=' expr_add
                                               / expr_add EQEQ expr_add
                                               / expr_add BANGEQ expr_add
                                               / expr_add '<' expr_add
                                               / expr_add '>' expr_add
                                               / expr_add LEQ expr_add
                                               / expr_add GEQ expr_add
                                               / expr_add
expr_atom                                   <- NUM
                                               / STRING
                                               / BOOL
                                               / NULL
                                               / '(' expr ')'
                                               / '{' expr_fields '}'
                                               / DOT
                                               / IDENT (DOT IDENT)*
expr_and                                    <- expr_cmp AMPAMP expr_and
                                               / expr_cmp
expr_add                                    <- expr_add PLUS expr_mul
                                               / expr_add MINUS expr_mul
                                               / expr_mul
expr                                        <- expr_or
dot_field                                   <- DOT IDENT
config_value                                <- STRING
                                               / NUM
                                               / BOOL
                                               / process_name (DOT process_name)*
                                               / '[' config_arr_items ']'
                                               / '{' config_items '}'
config_rest                                 <- ''
                                               / ',' config_items
                                               / config_pair config_rest
config_pair                                 <- IDENT ':' config_value
config_items                                <- ''
                                               / config_pair config_rest
config_arr_rest                             <- ''
                                               / ',' config_arr_items
                                               / config_value config_arr_rest
config_arr_items                            <- ''
                                               / config_value config_arr_rest
channel_decl                                <- 'let' process_name (DOT process_name)* ':' BANG type_atom '=' 'channel'
annotation                                  <- '@' IDENT config_value

(* Lexical rules — derived from lexer.mll *)

spacing        <- ([ \t\r\n]+ / comment)*
comment        <- '(*' (comment / !'*)' .)* '*)'
IDENT          <- !keyword [a-zA-Z_] [a-zA-Z0-9_]* spacing
STRING         <- '"' ([^"\\] / '\\' .)* '"' spacing
NUM            <- [0-9]+ ('.' [0-9]+)? spacing
BOOL           <- ('true' / 'false') ![a-zA-Z0-9_] spacing
keyword        <- ('type' / 'plumb' / 'let' / 'agent' / 'tool'
                 / 'builtin' / 'channel' / 'spawn' / 'use'
                 / 'module' / 'not' / 'true' / 'false'
                 / 'protocol' / 'send' / 'recv' / 'end'
                 / 'loop' / 'string' / 'int' / 'number'
                 / 'bool' / 'unit')
                 ![a-zA-Z0-9_]