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_]