Flesh Out P4 Grammar

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-01-17 00:02:43 -05:00
parent 08beba6cb1
commit c78afa5418
5 changed files with 360 additions and 33 deletions
+44 -21
View File
@@ -10,36 +10,31 @@
export default grammar({ export default grammar({
name: 'p4', name: 'p4',
rules: { rules: {
// TODO: add the actual grammar rules // Start symbol
p4program: $ => optional(repeat($.declaration)), p4program: $ => optional(repeat($.declaration)),
declaration: $ => $.parserDeclaration,
parserTypeDeclaration: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.nonEmptyParameterList), ')'), // Common
//parserDeclaration: $ => seq($.parserTypeDeclaration, $.optConstructorParameters, '{', $.parserLocalElements, $.parserStates, '}'),
parserDeclaration: $ => seq($.parserTypeDeclaration, optional($.constructorParameters), '{', seq(optional($.parserLocalElements), $.parserStates), '}'),
// Common - Parameters
typeParameters: $ => seq('<', $.typeParameterList, '>'), typeParameters: $ => seq('<', $.typeParameterList, '>'),
typeParameterList: $ => choice("[a-z]+", seq($.typeParameterList, ',', "[a-z]+")), typeParameterList: $ => choice("[a-z]+", seq($.typeParameterList, ',', "[a-z]+")),
parameterList: $ => choice($.parameter, seq($.parameterList, ',', $.parameter)),
nonEmptyParameterList: $ => choice($.parameter, seq($.nonEmptyParameterList, ',', $.parameter)),
parameter: $ => choice(seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier), seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier, '=', $.expression)), parameter: $ => choice(seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier), seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier, '=', $.expression)),
direction: $ => choice($.in, $.out, $.inout), direction: $ => choice($.in, $.out, $.inout),
// Common - Types
typeRef: $ => $.baseType, typeRef: $ => $.baseType,
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */), 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)), parserLocalElements: $ => choice(seq($.parserLocalElements, $.parserLocalElement)),
parserStates: $ => choice($.parserState, seq($.parserStates, $.parserState)), parserStates: $ => choice($.parserState, seq($.parserStates, $.parserState)),
parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.parserStatements), $.parserTransitionStatement, $.semicolon, '}'),
// 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)
parserLocalElement: $ => choice($.todo), parserLocalElement: $ => choice($.todo),
selectBody: $ => repeat1(seq($.selectCase, $.semicolon)),
selectCase: $ => seq($.keysetExpression, $.colon, $.identifier),
annotations: $ => choice($.annotation, seq($.annotations, $.annotation)), 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 */ ']')), 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", todo: $ => "todo",
abstract: $ => "abstract", abstract: $ => "abstract",
action: $ => "action", action: $ => "action",
@@ -75,7 +100,7 @@ export default grammar({
match_kind: $ => "match_kind", match_kind: $ => "match_kind",
type: $ => "type", type: $ => "type",
out: $ => "out", out: $ => "out",
parser: $ => token.immediate("parser"), parser: $ => "parser",
package: $ => "package", package: $ => "package",
pragma: $ => "pragma", pragma: $ => "pragma",
return: $ => "return", return: $ => "return",
@@ -92,13 +117,11 @@ export default grammar({
varbit: $ => "varbit", varbit: $ => "varbit",
valueset: $ => "valueset", valueset: $ => "valueset",
void: $ => "void", void: $ => "void",
identifier: $ => /[a-z]+/, identifier: $ => /[a-z_]+/,
type_identifier: $ => token.immediate(/[a-z]+/), type_identifier: $ => /[a-z]+/,
string_literal: $ => /".*"/, string_literal: $ => /".*"/,
integer: $ => /[0-9]+/, integer: $ => /[0-9]+/,
// Very limited.
expression: $ => choice($.integer, $.true),
}, },
} }
); );
+4
View File
@@ -1,5 +1,9 @@
{ {
"type": "module", "type": "module",
"scripts": {
"generate": "tree-sitter generate",
"test": "tree-sitter test"
},
"dependencies": { "dependencies": {
"tree-sitter-cli": "^0.26.3" "tree-sitter-cli": "^0.26.3"
} }
+33 -8
View File
@@ -2,24 +2,34 @@
Parser (No Parameters) Parser (No Parameters)
========================= =========================
parser simple() { parser simple() {
state testing {} state start {
transition accept;
} }
};
--- ---
(p4program (p4program
(declaration (declaration
(parserDeclaration (parserDeclaration
(parserTypeDeclaration (parserType
(parser) (parser)
parser_name: (identifier) (identifier)
) )
(parserStates (parserStates
(parserState (parserState
(state) (state)
(identifier) (identifier)
(parserTransitionStatement
(transition)
(transitionSelectionExpression
(identifier)
)
)
(semicolon)
) )
) )
) )
(semicolon)
) )
) )
@@ -28,28 +38,43 @@ parser simple() {
Parser (Parameters) Parser (Parameters)
========================= =========================
parser imple(bool pname) { parser imple(bool pname) {
state testing {} state start {
transition accept;
} }
};
--- ---
(p4program (p4program
(declaration (declaration
(parserDeclaration (parserDeclaration
(parserTypeDeclaration (parserType
(parser) (parser)
(identifier) (identifier)
(nonEmptyParameterList (parameterList
(parameter (parameter
(typeRef (typeRef
(baseType (baseType
(bool))) (bool)
(identifier)))) )
)
(identifier)
)
)
)
(parserStates (parserStates
(parserState (parserState
(state) (state)
(identifier) (identifier)
(parserTransitionStatement
(transition)
(transitionSelectionExpression
(identifier)
)
)
(semicolon)
) )
) )
) )
(semicolon)
) )
) )
+187
View File
@@ -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)
)
)
@@ -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)
)
)