From c78afa54182e5cc136ad45866ad200418220fd02 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Sat, 17 Jan 2026 00:02:43 -0500 Subject: [PATCH] Flesh Out P4 Grammar Signed-off-by: Will Hawkins --- tree-sitter-p4/grammar.js | 65 ++++--- tree-sitter-p4/package.json | 4 + tree-sitter-p4/test/corpus/parsers.txt | 49 ++++-- tree-sitter-p4/test/corpus/statements.txt | 187 +++++++++++++++++++++ tree-sitter-p4/test/corpus/transitions.txt | 88 ++++++++++ 5 files changed, 360 insertions(+), 33 deletions(-) create mode 100644 tree-sitter-p4/test/corpus/statements.txt create mode 100644 tree-sitter-p4/test/corpus/transitions.txt diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 343c631..c1b6665 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -10,36 +10,31 @@ export default grammar({ name: 'p4', rules: { - // TODO: add the actual grammar rules + // Start symbol p4program: $ => optional(repeat($.declaration)), - declaration: $ => $.parserDeclaration, - parserTypeDeclaration: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.nonEmptyParameterList), ')'), - - //parserDeclaration: $ => seq($.parserTypeDeclaration, $.optConstructorParameters, '{', $.parserLocalElements, $.parserStates, '}'), - parserDeclaration: $ => seq($.parserTypeDeclaration, optional($.constructorParameters), '{', seq(optional($.parserLocalElements), $.parserStates), '}'), + // Common + // Common - Parameters typeParameters: $ => seq('<', $.typeParameterList, '>'), typeParameterList: $ => choice("[a-z]+", seq($.typeParameterList, ',', "[a-z]+")), - - nonEmptyParameterList: $ => choice($.parameter, seq($.nonEmptyParameterList, ',', $.parameter)), + 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: $ => $.baseType, baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */), + constructorParameters: $ => seq('(', optional($.parameterList), ')'), - constructorParameters: $ => seq('(', optional($.nonEmptyParameterList), ')'), - + // Common - Parsers + parserType: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.parameterList), ')'), parserLocalElements: $ => choice(seq($.parserLocalElements, $.parserLocalElement)), parserStates: $ => choice($.parserState, seq($.parserStates, $.parserState)), - - // parserState: $ => seq(optional($.annotations), $.state, $.identifier , '{', $.parserStatements, $.transitionStatement, '}'), - parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.todo), '}'), - - // Nothing, for now. - //parserLocalElement: $ => choice(constantDeclaration | variableDeclaration | instantiation | valueSetDeclaration) + parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.parserStatements), $.parserTransitionStatement, $.semicolon, '}'), parserLocalElement: $ => choice($.todo), + selectBody: $ => repeat1(seq($.selectCase, $.semicolon)), + selectCase: $ => seq($.keysetExpression, $.colon, $.identifier), annotations: $ => choice($.annotation, seq($.annotations, $.annotation)), @@ -47,7 +42,37 @@ export default grammar({ annotation: $ => choice(seq('@', "[a-z]+"), seq('@', "[a-z]+", '(', /* empty for now*/ ')'), seq('@', "[a-z]+", '[', /* empty for now */ ']')), + // Declarations + declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration), $.semicolon), + // 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), '{', seq(optional($.parserLocalElements), $.parserStates), '}'), + + // Statements + + // General statements + statements: $ => repeat1(seq($.statement, $.semicolon)), + statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement),// 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: $=> $.expression, + + // Parser statements + parserStatements: $ => repeat1(seq($.parserStatement, $.semicolon)), + parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement), // Limited, so far. + parserBlockStatement: $ => seq(optional($.annotations), '{', $.parserStatements, '}'), + parserTransitionStatement: $ => seq($.transition, $.transitionSelectionExpression), + + // Expressions + expression: $ => choice($.identifier, $.integer, $.true, $.false), // Very limited. + selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression + transitionSelectionExpression: $ => choice($.identifier, $.selectExpression), + keysetExpression: $ => $.expression, + + // Tokens + semicolon: $ => ";", + colon: $ => ":", todo: $ => "todo", abstract: $ => "abstract", action: $ => "action", @@ -75,7 +100,7 @@ export default grammar({ match_kind: $ => "match_kind", type: $ => "type", out: $ => "out", - parser: $ => token.immediate("parser"), + parser: $ => "parser", package: $ => "package", pragma: $ => "pragma", return: $ => "return", @@ -92,13 +117,11 @@ export default grammar({ varbit: $ => "varbit", valueset: $ => "valueset", void: $ => "void", - identifier: $ => /[a-z]+/, - type_identifier: $ => token.immediate(/[a-z]+/), + identifier: $ => /[a-z_]+/, + type_identifier: $ => /[a-z]+/, string_literal: $ => /".*"/, integer: $ => /[0-9]+/, - // Very limited. - expression: $ => choice($.integer, $.true), }, } ); diff --git a/tree-sitter-p4/package.json b/tree-sitter-p4/package.json index 84e349d..eabd63d 100644 --- a/tree-sitter-p4/package.json +++ b/tree-sitter-p4/package.json @@ -1,5 +1,9 @@ { "type": "module", + "scripts": { + "generate": "tree-sitter generate", + "test": "tree-sitter test" + }, "dependencies": { "tree-sitter-cli": "^0.26.3" } diff --git a/tree-sitter-p4/test/corpus/parsers.txt b/tree-sitter-p4/test/corpus/parsers.txt index ae19eb5..f8bce39 100644 --- a/tree-sitter-p4/test/corpus/parsers.txt +++ b/tree-sitter-p4/test/corpus/parsers.txt @@ -2,24 +2,34 @@ Parser (No Parameters) ========================= parser simple() { - state testing {} -} + state start { + transition accept; + } +}; --- (p4program (declaration (parserDeclaration - (parserTypeDeclaration + (parserType (parser) - parser_name: (identifier) + (identifier) ) (parserStates (parserState (state) (identifier) - ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) ) ) + (semicolon) ) ) @@ -28,28 +38,43 @@ parser simple() { Parser (Parameters) ========================= parser imple(bool pname) { - state testing {} -} + state start { + transition accept; + } +}; --- (p4program (declaration (parserDeclaration - (parserTypeDeclaration + (parserType (parser) (identifier) - (nonEmptyParameterList + (parameterList (parameter (typeRef (baseType - (bool))) - (identifier)))) + (bool) + ) + ) + (identifier) + ) + ) + ) (parserStates (parserState (state) (identifier) - ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) ) ) + (semicolon) ) ) diff --git a/tree-sitter-p4/test/corpus/statements.txt b/tree-sitter-p4/test/corpus/statements.txt new file mode 100644 index 0000000..713d0cc --- /dev/null +++ b/tree-sitter-p4/test/corpus/statements.txt @@ -0,0 +1,187 @@ +========================= +Simple If Statement (No Else) +========================= +parser simple() { + state start { + if (true) { + }; + transition accept; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserStatements + (parserStatement + (conditionalStatement + (if) + (expression + (true) + ) + (statement + (blockStatement) + ) + ) + ) + (semicolon) + ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) + ) + ) + (semicolon) + ) +) + +========================= +Simple If Statement (Else) +========================= +parser simple() { + state start { + if (true) { + } else { + }; + transition accept; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserStatements + (parserStatement + (conditionalStatement + (if) + (expression + (true) + ) + (statement + (blockStatement) + ) + (else) + (statement + (blockStatement) + ) + ) + ) + (semicolon) + ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) + ) + ) + (semicolon) + ) +) + +========================= +Simple If Statement (With Body) +========================= +parser simple() { + state start { + if (true) { + true; + } else { + false; + }; + transition accept; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserStatements + (parserStatement + (conditionalStatement + (if) + (expression + (true) + ) + (statement + (blockStatement + (statements + (statement + (expressionStatement + (expression + (true) + ) + ) + ) + (semicolon) + ) + ) + ) + (else) + (statement + (blockStatement + (statements + (statement + (expressionStatement + (expression + (false) + ) + ) + ) + (semicolon) + ) + ) + ) + ) + ) + (semicolon) + ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) + ) + ) + (semicolon) + ) +) + diff --git a/tree-sitter-p4/test/corpus/transitions.txt b/tree-sitter-p4/test/corpus/transitions.txt new file mode 100644 index 0000000..770492c --- /dev/null +++ b/tree-sitter-p4/test/corpus/transitions.txt @@ -0,0 +1,88 @@ +========================= +Simple Transition Statement (To Identifier) +========================= +parser simple() { + state start { + transition accept; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + (semicolon) + ) + ) + ) + (semicolon) + ) +) + +========================= +Simple Transition Statement (To Select Expression) +========================= +parser simple() { + state start { + transition select (se) { + true: next_state; + }; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (selectExpression + (select) + (expression + (identifier) + ) + (selectBody + (selectCase + (keysetExpression + (expression + (true) + ) + ) + (colon) + (identifier) + ) + (semicolon) + ) + ) + ) + ) + (semicolon) + ) + ) + ) + (semicolon) + ) +)