// p4rse, Copyright 2026, Will Hawkins // // This file is part of p4rse. // This file is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . /// // @ts-check export default grammar({ name: 'p4', rules: { // Start symbol p4program: $ => optional(repeat(seq(choice($.declaration, $.instantiation), $._semicolon))), // Common // Common - Parameters typeParameters: $ => seq('<', $.typeParameterList, '>'), typeParameterList: $ => choice("[a-z]+", seq($.typeParameterList, ',', "[a-z]+")), parameterList: $ => choice($.parameter, seq($.parameterList, ',', $.parameter)), parameter: $ => choice(seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier), seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier, '=', $.expression)), direction: $ => choice($.in, $.out, $.inout), // Common - Types typeRef: $ => choice($.baseType, $.type_identifier), baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */), constructorParameters: $ => seq('(', optional($.parameterList), ')'), // Common - Parsers parserType: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.parameterList), ')'), // Mark with higher precedence so that the local states are preferred when parsing! // TODO: Test! parserLocalElements: $ => prec(2, repeat1($.parserLocalElement)), parserStates: $ => repeat1($.parserState), parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.parserStatements), $.parserTransitionStatement, '}'), parserLocalElement: $ => choice($.variableDeclaration, $.todo), selectBody: $ => repeat1(seq($.selectCase, $._semicolon)), selectCase: $ => seq($.keysetExpression, $.colon, $.identifier), annotations: $ => repeat1($.annotation), //annotation: $ => choice(seq('@', "[a-z]+"), seq('@', "[a-z]+", '(', $.annotationBody, ')'), seq('@', "[a-z]+", '[', $.structuredAnnotationBody, ']')), annotation: $ => choice($.annotation_literal), // seq('@', "[a-z]+", '(', /* empty for now*/ ')'), seq('@', "[a-z]+", '[', /* empty for now */ ']')), // Instantiation instantiation: $ => seq($.typeRef, '(', optional($.parameterList), ')', $.identifier), // Declarations declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration)), // Make separate productions for the parser type and the parser type declaration because the latter can have type parameters. parserTypeDeclaration: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.parameterList), ')'), parserDeclaration: $ => seq($.parserType, optional($.constructorParameters), '{', optional($.parserLocalElements), $.parserStates, '}'), variableDeclaration: $ => seq(optional($.annotations), $.typeRef, field('variable_name', $.identifier), optional(seq($.assignment, $.expression)), $._semicolon), // Statements // General statements statements: $ => repeat1($.statement), statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration),// Limited, so far. blockStatement: $ => seq(optional($.annotations), '{', optional($.statements), '}'), conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))), expressionStatement: $=> seq($.expression, $._semicolon), assignmentStatement: $=> seq($.expression, $.assignment, $.expression, $._semicolon), // Parser statements parserStatements: $ => repeat1($.parserStatement), parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration), // Limited, so far. parserBlockStatement: $ => seq(optional($.annotations), '{', $.parserStatements, '}'), parserTransitionStatement: $ => seq($.transition, $.transitionSelectionExpression, $._semicolon), // Expressions expression: $ => choice($.grouped_expression, $.simple_expression), grouped_expression: $ => seq('(', $.expression, ')'), simple_expression: $ => choice($.identifier, $.integer, $.booleanLiteralExpression, $.string_literal, $.binaryOperatorExpression, $.arrayAccessExpression, $.fieldAccessExpression), // Very limited. booleanLiteralExpression: $ => choice($.true, $.false), selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression transitionSelectionExpression: $ => choice($.identifier, $.selectExpression), keysetExpression: $ => choice($.expression, $.default_keyset), binaryOperatorExpression: $ => choice($.binaryEqualOperatorExpression, $.binaryLessThanOperatorExpression, $.binaryLessThanEqualOperatorExpression, $.binaryGreaterThanOperatorExpression, $.binaryGreaterThanEqualOperatorExpression, $.binaryAndOperatorExpression, $.binaryOrOperatorExpression, $.binaryAddOperatorExpression, $.binarySubtractOperatorExpression, $.binaryMultiplyOperatorExpression, $.binaryDivideOperatorExpression, ), arrayAccessExpression: $ => seq($.expression, $.open_bracket, $.expression, $.close_bracket), fieldAccessExpression: $=> prec.left(2, seq($.expression, $.field_access, $.identifier)), // Binary Operations binaryEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.double_equal, $.expression)), binaryLessThanOperatorExpression: $ => prec.left(2, seq($.expression, $.less_than, $.expression)), binaryLessThanEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.less_than_equal, $.expression)), binaryGreaterThanOperatorExpression: $ => prec.left(2, seq($.expression, $.greater_than, $.expression)), binaryGreaterThanEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.greater_than_equal, $.expression)), binaryAndOperatorExpression: $ => prec.left(2, seq($.expression, $.and, $.expression)), binaryOrOperatorExpression: $ => prec.left(2, seq($.expression, $.or, $.expression)), binaryAddOperatorExpression: $ => prec.left(2, seq($.expression, $.add, $.expression)), binarySubtractOperatorExpression: $ => prec.left(2, seq($.expression, $.subtract, $.expression)), binaryMultiplyOperatorExpression: $ => prec.left(2, seq($.expression, $.multiply, $.expression)), binaryDivideOperatorExpression: $ => prec.left(2, seq($.expression, $.divide, $.expression)), // Tokens _semicolon: $ => ";", colon: $ => ":", assignment: $ => "=", todo: $ => "todo", abstract: $ => "abstract", action: $ => "action", actions: $ => "actions", apply: $ => "apply", bool: $ => "bool", bit: $ => "bit", const: $ => "const", control: $ => "control", default: $ => "default", else: $ => "else", entries: $ => "entries", enum: $ => "enum", error: $ => "error", exit: $ => "exit", extern: $ => "extern", false: $ => "false", header: $ => "header", header_union: $ => "header_union", if: $ => "if", in: $ => "in", inout: $ => "inout", int: $ => "int", key: $ => "key", match_kind: $ => "match_kind", type: $ => "type", out: $ => "out", parser: $ => "parser", package: $ => "package", pragma: $ => "pragma", return: $ => "return", select: $ => "select", state: $ => "state", string: $ => "string", struct: $ => "struct", switch: $ => "switch", table: $ => "table", transition: $ => "transition", true: $ => "true", tuple: $ => "tuple", typedef: $ => "typedef", varbit: $ => "varbit", valueset: $ => "valueset", void: $ => "void", identifier: $ => /[A-Za-z_]+/, type_identifier: $ => /[A-Za-z_]+/, string_literal: $ => /"[^"]*"/, integer: $ => /[0-9]+/, annotation_literal: $ => /@[A-Za-z_]+/, default_keyset: $=> '_', double_equal: $=> '==', less_than: $=> '<', less_than_equal: $=> '<=', greater_than: $=> '>', greater_than_equal: $=> '>=', and: $=> "&&", or: $=> "||", add: $=> '+', subtract: $=> '-', multiply: $=> '*', divide: $=> '/', open_bracket: $=> '[', close_bracket: $=> ']', field_access: $=> '.', }, } ); /* p4program : // empty | p4program declaration | p4program ';' // empty declaration ; declaration : constantDeclaration | externDeclaration | actionDeclaration | parserDeclaration | typeDeclaration | controlDeclaration | instantiation | errorDeclaration | matchKindDeclaration | functionDeclaration ; nonTypeName : IDENTIFIER | APPLY | KEY | ACTIONS | STATE | ENTRIES | TYPE ; name : nonTypeName | TYPE_IDENTIFIER ; nonTableKwName : IDENTIFIER | TYPE_IDENTIFIER | APPLY | STATE | TYPE ; optAnnotations : // empty | annotations ; annotations : annotation | annotations annotation ; annotation : '@' name | '@' name '(' annotationBody ')' | '@' name '[' structuredAnnotationBody ']' ; parameterList : // empty | nonEmptyParameterList ; nonEmptyParameterList : parameter | nonEmptyParameterList ',' parameter ; parameter : optAnnotations direction typeRef name | optAnnotations direction typeRef name '=' expression ; direction : IN | OUT | INOUT | // empty ; packageTypeDeclaration : optAnnotations PACKAGE name optTypeParameters '(' parameterList ')' ; instantiation : typeRef '(' argumentList ')' name ';' | annotations typeRef '(' argumentList ')' name ';' | annotations typeRef '(' argumentList ')' name '=' objInitializer ';' | typeRef '(' argumentList ')' name '=' objInitializer ';' ; objInitializer : '{' objDeclarations '}' ; objDeclarations : // empty | objDeclarations objDeclaration ; objDeclaration : functionDeclaration | instantiation ; optConstructorParameters : // empty | '(' parameterList ')' ; dotPrefix : '.' ; // PARSER parserDeclaration : parserTypeDeclaration optConstructorParameters // no type parameters allowed in the parserTypeDeclaration '{' parserLocalElements parserStates '}' ; parserLocalElements : // empty | parserLocalElements parserLocalElement ; parserLocalElement : constantDeclaration | variableDeclaration | instantiation | valueSetDeclaration ; parserTypeDeclaration : optAnnotations PARSER name optTypeParameters '(' parameterList ')' ; parserStates : parserState | parserStates parserState ; parserState : optAnnotations STATE name '{' parserStatements transitionStatement '}' ; parserStatements : // empty | parserStatements parserStatement ; parserStatement : assignmentOrMethodCallStatement | directApplication | parserBlockStatement | constantDeclaration | variableDeclaration | emptyStatement | conditionalStatement ; parserBlockStatement : optAnnotations '{' parserStatements '}' ; transitionStatement : // empty | TRANSITION stateExpression ; stateExpression : name ';' | selectExpression ; selectExpression : SELECT '(' expressionList ')' '{' selectCaseList '}' ; selectCaseList : // empty | selectCaseList selectCase ; selectCase : keysetExpression ':' name ';' ; keysetExpression : tupleKeysetExpression | simpleKeysetExpression ; tupleKeysetExpression : "(" simpleKeysetExpression "," simpleExpressionList ")" | "(" reducedSimpleKeysetExpression ")" ; simpleExpressionList : simpleKeysetExpression | simpleExpressionList ',' simpleKeysetExpression ; reducedSimpleKeysetExpression : expression "&&&" expression | expression ".." expression | DEFAULT | "_" ; simpleKeysetExpression : expression | DEFAULT | DONTCARE | expression MASK expression | expression RANGE expression ; valueSetDeclaration : optAnnotations VALUESET '<' baseType '>' '(' expression ')' name ';' | optAnnotations VALUESET '<' tupleType '>' '(' expression ')' name ';' | optAnnotations VALUESET '<' typeName '>' '(' expression ')' name ';' ; // CONTROL controlDeclaration : controlTypeDeclaration optConstructorParameters // no type parameters allowed in controlTypeDeclaration '{' controlLocalDeclarations APPLY controlBody '}' ; controlTypeDeclaration : optAnnotations CONTROL name optTypeParameters '(' parameterList ')' ; controlLocalDeclarations : // empty | controlLocalDeclarations controlLocalDeclaration ; controlLocalDeclaration : constantDeclaration | actionDeclaration | tableDeclaration | instantiation | variableDeclaration ; controlBody : blockStatement ; // Extern externDeclaration : optAnnotations EXTERN nonTypeName optTypeParameters '{' methodPrototypes '}' | optAnnotations EXTERN functionPrototype ';' ; methodPrototypes : // empty | methodPrototypes methodPrototype ; functionPrototype : typeOrVoid name optTypeParameters '(' parameterList ')' ; methodPrototype : optAnnotations functionPrototype ';' | optAnnotations TYPE_IDENTIFIER '(' parameterList ')' ';' ; // TYPES typeRef : baseType | typeName | specializedType | headerStackType | tupleType ; namedType : typeName | specializedType ; prefixedType : TYPE_IDENTIFIER | dotPrefix TYPE_IDENTIFIER ; typeName : prefixedType ; tupleType : TUPLE '<' typeArgumentList '>' ; headerStackType : typeName '[' expression ']' | specializedType '[' expression ']' ; specializedType : prefixedType '<' typeArgumentList '>' ; baseType : BOOL | ERROR | STRING | INT | BIT | BIT '<' INTEGER '>' | INT '<' INTEGER '>' | VARBIT '<' INTEGER '>' | BIT '<' '(' expression ')' '>' | INT '<' '(' expression ')' '>' | VARBIT '<' '(' expression ')' '>' ; typeOrVoid : typeRef | VOID | IDENTIFIER // may be a type variable ; optTypeParameters : // empty | typeParameters ; typeParameters : '<' typeParameterList '>' ; typeParameterList : name | typeParameterList ',' name ; realTypeArg : DONTCARE | typeRef | VOID ; typeArg : DONTCARE | typeRef | nonTypeName | VOID ; realTypeArgumentList : realTypeArg | realTypeArgumentList COMMA typeArg ; typeArgumentList : // empty | typeArg | typeArgumentList ',' typeArg ; typeDeclaration : derivedTypeDeclaration | typedefDeclaration | parserTypeDeclaration ';' | controlTypeDeclaration ';' | packageTypeDeclaration ';' ; derivedTypeDeclaration : headerTypeDeclaration | headerUnionDeclaration | structTypeDeclaration | enumDeclaration ; headerTypeDeclaration : optAnnotations HEADER name optTypeParameters '{' structFieldList '}' ; headerUnionDeclaration : optAnnotations HEADER_UNION name optTypeParameters '{' structFieldList '}' ; structTypeDeclaration : optAnnotations STRUCT name optTypeParameters '{' structFieldList '}' ; structFieldList : // empty | structFieldList structField ; structField : optAnnotations typeRef name ';' ; enumDeclaration : optAnnotations ENUM name '{' identifierList '}' | optAnnotations ENUM typeRef name '{' specifiedIdentifierList '}' ; errorDeclaration : ERROR '{' identifierList '}' ; matchKindDeclaration : MATCH_KIND '{' identifierList '}' ; identifierList : name | identifierList ',' name ; specifiedIdentifierList : specifiedIdentifier | specifiedIdentifierList ',' specifiedIdentifier ; specifiedIdentifier : name '=' initializer ; typedefDeclaration : optAnnotations TYPEDEF typeRef name ';' | optAnnotations TYPEDEF derivedTypeDeclaration name ';' | optAnnotations TYPE typeRef name ';' | optAnnotations TYPE derivedTypeDeclaration name ';' ; // Statements assignmentOrMethodCallStatement : lvalue '(' argumentList ')' ';' | lvalue '<' typeArgumentList '>' '(' argumentList ')' ';' | lvalue '=' expression ';' ; emptyStatement : ';' ; returnStatement : RETURN ';' | RETURN expression ';' ; exitStatement : EXIT ';' ; conditionalStatement : IF '(' expression ')' statement | IF '(' expression ')' statement ELSE statement ; // To support direct invocation of a control or parser without instantiation directApplication : typeName '.' APPLY '(' argumentList ')' ';' ; statement : assignmentOrMethodCallStatement | directApplication | conditionalStatement | emptyStatement | blockStatement | exitStatement | returnStatement | switchStatement ; blockStatement : optAnnotations '{' statOrDeclList '}' ; statOrDeclList : // empty | statOrDeclList statementOrDeclaration ; switchStatement : SWITCH '(' expression ')' '{' switchCases '}' ; switchCases : // empty | switchCases switchCase ; switchCase : switchLabel ':' blockStatement | switchLabel ':' ; switchLabel : DEFAULT | nonBraceExpression ; statementOrDeclaration : variableDeclaration | constantDeclaration | statement | instantiation ; // Tables tableDeclaration : optAnnotations TABLE name '{' tablePropertyList '}' ; tablePropertyList : tableProperty | tablePropertyList tableProperty ; tableProperty : KEY '=' '{' keyElementList '}' | ACTIONS '=' '{' actionList '}' | optAnnotations CONST ENTRIES '=' '{' entriesList '}' // immutable entries | optAnnotations CONST nonTableKwName '=' initializer ';' | optAnnotations nonTableKwName '=' initializer ';' ; keyElementList : // empty | keyElementList keyElement ; keyElement : expression ':' name optAnnotations ';' ; actionList : // empty | actionList optAnnotations actionRef ';' ; actionRef : prefixedNonTypeName | prefixedNonTypeName '(' argumentList ')' ; entriesList : entry | entriesList entry ; entry : keysetExpression ':' actionRef optAnnotations ';' ; // Action actionDeclaration : optAnnotations ACTION name '(' parameterList ')' blockStatement ; // Variables variableDeclaration : annotations typeRef name optInitializer ';' | typeRef name optInitializer ';' ; constantDeclaration : optAnnotations CONST typeRef name '=' initializer ';' ; optInitializer : // empty | '=' initializer ; initializer : expression ; // Expressions functionDeclaration : functionPrototype blockStatement ; argumentList : // empty | nonEmptyArgList ; nonEmptyArgList : argument | nonEmptyArgList ',' argument ; argument : expression | name '=' expression | DONTCARE ; kvList : kvPair | kvList ',' kvPair ; kvPair : name '=' expression ; expressionList : // empty | expression | expressionList ',' expression ; annotationBody : // empty | annotationBody '(' annotationBody ')' | annotationBody annotationToken ; structuredAnnotationBody : expressionList | kvList ; annotationToken : ABSTRACT | ACTION | ACTIONS | APPLY | BOOL | BIT | CONST | CONTROL | DEFAULT | ELSE | ENTRIES | ENUM | ERROR | EXIT | EXTERN | FALSE | HEADER | HEADER_UNION | IF | IN | INOUT | INT | KEY | MATCH_KIND | TYPE | OUT | PARSER | PACKAGE | PRAGMA | RETURN | SELECT | STATE | STRING | STRUCT | SWITCH | TABLE | TRANSITION | TRUE | TUPLE | TYPEDEF | VARBIT | VALUESET | VOID | "_" | IDENTIFIER | TYPE_IDENTIFIER | STRING_LITERAL | INTEGER | "&&&" | ".." | "<<" | "&&" | "||" | "==" | "!=" | ">=" | "<=" | "++" | "+" | "|+|" | "-" | "|-|" | "*" | "/" | "%" | "|" | "&" | "^" | "~" | "[" | "]" | "{" | "}" | "<" | ">" | "!" | ":" | "," | "?" | "." | "=" | ";" | "@" | UNKNOWN_TOKEN ; member : name ; prefixedNonTypeName : nonTypeName | dotPrefix nonTypeName ; lvalue : prefixedNonTypeName | THIS | lvalue '.' member | lvalue '[' expression ']' | lvalue '[' expression ':' expression ']' ; %left ',' %nonassoc '?' %nonassoc ':' %left '||' %left '&&' %left '==' '!=' %left '<' '>' '<=' '>=' %left '|' %left '^' %left '&' %left '<<' '>>' %left '++' '+' '-' '|+|' '|-|' %left '*' '/' '%' %right PREFIX %nonassoc ']' '(' '[' %left '.' // Additional precedences need to be specified expression : INTEGER | TRUE | FALSE | THIS | STRING_LITERAL | nonTypeName | dotPrefix nonTypeName | expression '[' expression ']' | expression '[' expression ':' expression ']' | '{' expressionList '}' | '{' kvList '}' | '(' expression ')' | '!' expression %prec PREFIX | '~' expression %prec PREFIX | '-' expression %prec PREFIX | '+' expression %prec PREFIX | typeName '.' member | ERROR '.' member | expression '.' member | expression '*' expression | expression '/' expression | expression '%' expression | expression '+' expression | expression '-' expression | expression '|+|' expression | expression '|-|' expression | expression '<<' expression | expression '>>' expression | expression '<=' expression | expression '>=' expression | expression '<' expression | expression '>' expression | expression '!=' expression | expression '==' expression | expression '&' expression | expression '^' expression | expression '|' expression | expression '++' expression | expression '&&' expression | expression '||' expression | expression '?' expression ':' expression | expression '<' realTypeArgumentList '>' '(' argumentList ')' | expression '(' argumentList ')' | namedType '(' argumentList ')' | '(' typeRef ')' expression ; nonBraceExpression : INTEGER | STRING_LITERAL | TRUE | FALSE | THIS | nonTypeName | dotPrefix nonTypeName | nonBraceExpression '[' expression ']' | nonBraceExpression '[' expression ':' expression ']' | '(' expression ')' | '!' expression %prec PREFIX | '~' expression %prec PREFIX | '-' expression %prec PREFIX | '+' expression %prec PREFIX | typeName '.' member | ERROR '.' member | nonBraceExpression '.' member | nonBraceExpression '*' expression | nonBraceExpression '/' expression | nonBraceExpression '%' expression | nonBraceExpression '+' expression | nonBraceExpression '-' expression | nonBraceExpression '|+|' expression | nonBraceExpression '|-|' expression | nonBraceExpression '<<' expression | nonBraceExpression '>>' expression | nonBraceExpression '<=' expression | nonBraceExpression '>=' expression | nonBraceExpression '<' expression | nonBraceExpression '>' expression | nonBraceExpression '!=' expression | nonBraceExpression '==' expression | nonBraceExpression '&' expression | nonBraceExpression '^' expression | nonBraceExpression '|' expression | nonBraceExpression '++' expression | nonBraceExpression '&&' expression | nonBraceExpression '||' expression | nonBraceExpression '?' expression ':' expression | nonBraceExpression '<' realTypeArgumentList '>' '(' argumentList ')' | nonBraceExpression '(' argumentList ')' | namedType '(' argumentList ')' | '(' typeRef ')' expression ; */