Initial Commit

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-01-14 12:49:49 -05:00
commit 08beba6cb1
24 changed files with 1816 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
root = true
[*]
charset = utf-8
[*.{json,toml,yml,gyp,xml}]
indent_style = space
indent_size = 2
[*.{js,ts}]
indent_style = space
indent_size = 2
[*.scm]
indent_style = space
indent_size = 2
[*.{c,cc,h}]
indent_style = space
indent_size = 4
[*.rs]
indent_style = space
indent_size = 4
[*.{py,pyi}]
indent_style = space
indent_size = 4
[*.swift]
indent_style = space
indent_size = 4
[*.java]
indent_style = space
indent_size = 4
[*.go]
indent_style = tab
indent_size = 8
[Makefile]
indent_style = tab
indent_size = 8
[parser.c]
indent_size = 2
[{alloc,array,parser}.h]
indent_size = 2
+46
View File
@@ -0,0 +1,46 @@
* text=auto eol=lf
# Generated source files
src/*.json linguist-generated
src/parser.c linguist-generated
src/tree_sitter/* linguist-generated
# C bindings
bindings/c/** linguist-generated
CMakeLists.txt linguist-generated
Makefile linguist-generated
# Rust bindings
bindings/rust/* linguist-generated
Cargo.toml linguist-generated
Cargo.lock linguist-generated
# Node.js bindings
bindings/node/* linguist-generated
binding.gyp linguist-generated
package.json linguist-generated
package-lock.json linguist-generated
# Python bindings
bindings/python/** linguist-generated
setup.py linguist-generated
pyproject.toml linguist-generated
# Go bindings
bindings/go/* linguist-generated
go.mod linguist-generated
go.sum linguist-generated
# Swift bindings
bindings/swift/** linguist-generated
Package.swift linguist-generated
Package.resolved linguist-generated
# Zig bindings
bindings/zig/* linguist-generated
build.zig linguist-generated
build.zig.zon linguist-generated
# Java bindings
pom.xml linguist-generated
bindings/java/** linguist-generated
+54
View File
@@ -0,0 +1,54 @@
# Rust artifacts
target/
# Node artifacts
build/
prebuilds/
node_modules/
# Swift artifacts
.build/
# Go artifacts
_obj/
# Python artifacts
.venv/
dist/
*.egg-info
*.whl
# C artifacts
*.a
*.so
*.so.*
*.dylib
*.dll
*.pc
*.exp
*.lib
# Zig artifacts
.zig-cache/
zig-cache/
zig-out/
# Example dirs
/examples/*/
# Grammar volatiles
*.wasm
*.obj
*.o
# Archives
*.tar.gz
*.tgz
*.zip
*.jar
# backup files
/bak/**
/src/**
+76
View File
@@ -0,0 +1,76 @@
cmake_minimum_required(VERSION 3.13)
project(tree-sitter-p4
VERSION "0.1.0"
DESCRIPTION "The P4 Language"
HOMEPAGE_URL "https://github.com/tree-sitter/tree-sitter-p4"
LANGUAGES C)
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF)
set(TREE_SITTER_ABI_VERSION 15 CACHE STRING "Tree-sitter ABI version")
if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$")
unset(TREE_SITTER_ABI_VERSION CACHE)
message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer")
endif()
include(GNUInstallDirs)
find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI")
add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json"
"${CMAKE_CURRENT_SOURCE_DIR}/src/node-types.json"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grammar.js"
COMMAND "${TREE_SITTER_CLI}" generate grammar.js --no-parser
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Generating grammar.json")
add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c"
BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/parser.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/alloc.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/array.h"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json"
COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json
--abi=${TREE_SITTER_ABI_VERSION}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Generating parser.c")
add_library(tree-sitter-p4 src/parser.c)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c)
target_sources(tree-sitter-p4 PRIVATE src/scanner.c)
endif()
target_include_directories(tree-sitter-p4
PRIVATE src
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/bindings/c>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_compile_definitions(tree-sitter-p4 PRIVATE
$<$<BOOL:${TREE_SITTER_REUSE_ALLOCATOR}>:TREE_SITTER_REUSE_ALLOCATOR>
$<$<CONFIG:Debug>:TREE_SITTER_DEBUG>)
set_target_properties(tree-sitter-p4
PROPERTIES
C_STANDARD 11
POSITION_INDEPENDENT_CODE ON
SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}"
DEFINE_SYMBOL "")
configure_file(bindings/c/tree-sitter-p4.pc.in
"${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-p4.pc" @ONLY)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bindings/c/tree_sitter"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
FILES_MATCHING PATTERN "*.h")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-p4.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
install(TARGETS tree-sitter-p4
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
file(GLOB QUERIES queries/*.scm)
install(FILES ${QUERIES}
DESTINATION "${CMAKE_INSTALL_DATADIR}/tree-sitter/queries/p4")
add_custom_target(ts-test "${TREE_SITTER_CLI}" test
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "tree-sitter test")
+115
View File
@@ -0,0 +1,115 @@
LANGUAGE_NAME := tree-sitter-p4
HOMEPAGE_URL := https://github.com/tree-sitter/tree-sitter-p4
VERSION := 0.1.0
# repository
SRC_DIR := src
TS ?= tree-sitter
# install directory layout
PREFIX ?= /usr/local
DATADIR ?= $(PREFIX)/share
INCLUDEDIR ?= $(PREFIX)/include
LIBDIR ?= $(PREFIX)/lib
BINDIR ?= $(PREFIX)/bin
PCLIBDIR ?= $(LIBDIR)/pkgconfig
# source/object files
PARSER := $(SRC_DIR)/parser.c
EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c))
OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS))
# flags
ARFLAGS ?= rcs
override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC
# ABI versioning
SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER))
SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION)))
# OS-specific bits
MACHINE := $(shell $(CC) -dumpmachine)
ifneq ($(findstring darwin,$(MACHINE)),)
SOEXT = dylib
SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT)
SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT)
LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks
else ifneq ($(findstring mingw32,$(MACHINE)),)
SOEXT = dll
LINKSHARED += -s -shared -Wl,--out-implib,lib$(LANGUAGE_NAME).dll.a
else
SOEXT = so
SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR)
SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR)
LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER)
ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),)
PCLIBDIR := $(PREFIX)/libdata/pkgconfig
endif
endif
all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc
lib$(LANGUAGE_NAME).a: $(OBJS)
$(AR) $(ARFLAGS) $@ $^
lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS)
$(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@
ifneq ($(STRIP),)
$(STRIP) $@
endif
ifneq ($(findstring mingw32,$(MACHINE)),)
lib$(LANGUAGE_NAME).dll.a: lib$(LANGUAGE_NAME).$(SOEXT)
endif
$(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in
sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \
-e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \
-e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \
-e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \
-e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \
-e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@
$(SRC_DIR)/grammar.json: grammar.js
$(TS) generate --no-parser $^
$(PARSER): $(SRC_DIR)/grammar.json
$(TS) generate $^
install: all
install -d '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/p4 '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)'
install -m644 bindings/c/tree_sitter/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h
install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc
install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a
install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER)
ifneq ($(findstring mingw32,$(MACHINE)),)
install -d '$(DESTDIR)$(BINDIR)'
install -m755 lib$(LANGUAGE_NAME).dll '$(DESTDIR)$(BINDIR)'/lib$(LANGUAGE_NAME).dll
install -m755 lib$(LANGUAGE_NAME).dll.a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).dll.a
else
install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER)
cd '$(DESTDIR)$(LIBDIR)' && ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR)
cd '$(DESTDIR)$(LIBDIR)' && ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) lib$(LANGUAGE_NAME).$(SOEXT)
endif
ifneq ($(wildcard queries/*.scm),)
install -m644 queries/*.scm '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/p4
endif
uninstall:
$(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \
'$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \
'$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \
'$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \
'$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \
'$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc
$(RM) -r '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/p4
clean:
$(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) lib$(LANGUAGE_NAME).dll.a
test:
$(TS) test
.PHONY: all install uninstall clean test
+24
View File
@@ -0,0 +1,24 @@
{
"originHash" : "834c3b88aca13e3319797ef2380244db35d5afd8d06f08e671f5882981d29e80",
"pins" : [
{
"identity" : "swift-tree-sitter",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tree-sitter/swift-tree-sitter",
"state" : {
"branch" : "main",
"revision" : "769f4770fe8197e2f4ce810375e9b21398ae36d5"
}
},
{
"identity" : "tree-sitter",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tree-sitter/tree-sitter",
"state" : {
"revision" : "da6fe9beb4f7f67beb75914ca8e0d48ae48d6406",
"version" : "0.25.10"
}
}
],
"version" : 3
}
+41
View File
@@ -0,0 +1,41 @@
// swift-tools-version: 6.2
import Foundation
import PackageDescription
var sources = ["src/parser.c"]
if FileManager.default.fileExists(atPath: "src/scanner.c") {
sources.append("src/scanner.c")
}
let package = Package(
name: "TreeSitterP4",
products: [
.library(name: "TreeSitterP4", targets: ["TreeSitterP4"]),
],
dependencies: [
.package(url: "https://github.com/tree-sitter/swift-tree-sitter", revision: "main"),
],
targets: [
.target(
name: "TreeSitterP4",
dependencies: [],
path: ".",
sources: sources,
resources: [
.copy("queries")
],
publicHeadersPath: "bindings/swift",
cSettings: [.headerSearchPath("src")]
),
.testTarget(
name: "TreeSitterP4Tests",
dependencies: [
"TreeSitterP4",
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
],
path: "bindings/swift/TreeSitterP4Tests"
)
],
cLanguageStandard: .c11
)
+35
View File
@@ -0,0 +1,35 @@
{
"targets": [
{
"target_name": "tree_sitter_p4_binding",
"dependencies": [
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
],
"include_dirs": [
"src",
],
"sources": [
"bindings/node/binding.cc",
"src/parser.c",
],
"variables": {
"has_scanner": "<!(node -p \"fs.existsSync('src/scanner.c')\")"
},
"conditions": [
["has_scanner=='true'", {
"sources+": ["src/scanner.c"],
}],
["OS!='win'", {
"cflags_c": [
"-std=c11",
],
}, { # OS == "win"
"cflags_c": [
"/std:c11",
"/utf-8",
],
}],
],
}
]
}
+19
View File
@@ -0,0 +1,19 @@
#include <napi.h>
typedef struct TSLanguage TSLanguage;
extern "C" TSLanguage *tree_sitter_p4();
// "tree-sitter", "language" hashed with BLAKE2
const napi_type_tag LANGUAGE_TYPE_TAG = {
0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
};
Napi::Object Init(Napi::Env env, Napi::Object exports) {
auto language = Napi::External<TSLanguage>::New(env, tree_sitter_p4());
language.TypeTag(&LANGUAGE_TYPE_TAG);
exports["language"] = language;
return exports;
}
NODE_API_MODULE(tree_sitter_p4_binding, Init)
+11
View File
@@ -0,0 +1,11 @@
import assert from "node:assert";
import { test } from "node:test";
import Parser from "tree-sitter";
test("can load grammar", () => {
const parser = new Parser();
assert.doesNotReject(async () => {
const { default: language } = await import("./index.js");
parser.setLanguage(language);
});
});
+60
View File
@@ -0,0 +1,60 @@
type BaseNode = {
type: string;
named: boolean;
};
type ChildNode = {
multiple: boolean;
required: boolean;
types: BaseNode[];
};
type NodeInfo =
| (BaseNode & {
subtypes: BaseNode[];
})
| (BaseNode & {
fields: { [name: string]: ChildNode };
children: ChildNode[];
});
/**
* The tree-sitter language object for this grammar.
*
* @see {@linkcode https://tree-sitter.github.io/node-tree-sitter/interfaces/Parser.Language.html Parser.Language}
*
* @example
* import Parser from "tree-sitter";
* import P4 from "tree-sitter-p4";
*
* const parser = new Parser();
* parser.setLanguage(P4);
*/
declare const binding: {
/**
* The inner language object.
* @private
*/
language: unknown;
/**
* The content of the `node-types.json` file for this grammar.
*
* @see {@linkplain https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types Static Node Types}
*/
nodeTypeInfo: NodeInfo[];
/** The syntax highlighting query for this grammar. */
HIGHLIGHTS_QUERY?: string;
/** The language injection query for this grammar. */
INJECTIONS_QUERY?: string;
/** The local variable query for this grammar. */
LOCALS_QUERY?: string;
/** The symbol tagging query for this grammar. */
TAGS_QUERY?: string;
};
export default binding;
+37
View File
@@ -0,0 +1,37 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const root = fileURLToPath(new URL("../..", import.meta.url));
const binding = typeof process.versions.bun === "string"
// Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time
? await import(`${root}/prebuilds/${process.platform}-${process.arch}/tree-sitter-p4.node`)
: (await import("node-gyp-build")).default(root);
try {
const nodeTypes = await import(`${root}/src/node-types.json`, { with: { type: "json" } });
binding.nodeTypeInfo = nodeTypes.default;
} catch { }
const queries = [
["HIGHLIGHTS_QUERY", `${root}/queries/highlights.scm`],
["INJECTIONS_QUERY", `${root}/queries/injections.scm`],
["LOCALS_QUERY", `${root}/queries/locals.scm`],
["TAGS_QUERY", `${root}/queries/tags.scm`],
];
for (const [prop, path] of queries) {
Object.defineProperty(binding, prop, {
configurable: true,
enumerable: true,
get() {
delete binding[prop];
try {
binding[prop] = readFileSync(path, "utf8");
} catch { }
return binding[prop];
}
});
}
export default binding;
+16
View File
@@ -0,0 +1,16 @@
#ifndef TREE_SITTER_P4_H_
#define TREE_SITTER_P4_H_
typedef struct TSLanguage TSLanguage;
#ifdef __cplusplus
extern "C" {
#endif
const TSLanguage *tree_sitter_p4(void);
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_P4_H_
@@ -0,0 +1,12 @@
import XCTest
import SwiftTreeSitter
import TreeSitterP4
final class TreeSitterP4Tests: XCTestCase {
func testCanLoadGrammar() throws {
let parser = Parser()
let language = Language(language: tree_sitter_p4())
XCTAssertNoThrow(try parser.setLanguage(language),
"Error loading P4 grammar")
}
}
+966
View File
@@ -0,0 +1,966 @@
/**
* @file Grammar for (a subset of) P4
* @author Will Hawkins
* @license GPLv3
*/
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
export default grammar({
name: 'p4',
rules: {
// TODO: add the actual grammar rules
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), '}'),
typeParameters: $ => seq('<', $.typeParameterList, '>'),
typeParameterList: $ => choice("[a-z]+", seq($.typeParameterList, ',', "[a-z]+")),
nonEmptyParameterList: $ => choice($.parameter, seq($.nonEmptyParameterList, ',', $.parameter)),
parameter: $ => choice(seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier), seq(optional($.annotations), optional($.direction), $.typeRef, $.identifier, '=', $.expression)),
direction: $ => choice($.in, $.out, $.inout),
typeRef: $ => $.baseType,
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
constructorParameters: $ => seq('(', optional($.nonEmptyParameterList), ')'),
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)
parserLocalElement: $ => choice($.todo),
annotations: $ => choice($.annotation, seq($.annotations, $.annotation)),
//annotation: $ => choice(seq('@', "[a-z]+"), seq('@', "[a-z]+", '(', $.annotationBody, ')'), seq('@', "[a-z]+", '[', $.structuredAnnotationBody, ']')),
annotation: $ => choice(seq('@', "[a-z]+"), seq('@', "[a-z]+", '(', /* empty for now*/ ')'), seq('@', "[a-z]+", '[', /* empty for now */ ']')),
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: $ => token.immediate("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-z]+/,
type_identifier: $ => token.immediate(/[a-z]+/),
string_literal: $ => /".*"/,
integer: $ => /[0-9]+/,
// Very limited.
expression: $ => choice($.integer, $.true),
},
}
);
/*
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
;
*/
+25
View File
@@ -0,0 +1,25 @@
{
"name": "tree-sitter-p4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"tree-sitter-cli": "^0.26.3"
}
},
"node_modules/tree-sitter-cli": {
"version": "0.26.3",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.26.3.tgz",
"integrity": "sha512-1VHpmjnTsYJk03HDqzLGn9dmJaLsJ7YeGsnnSudC6XOZu5oasz0GEVOIVCTe6hA01YZJgHd1XGO6XJZe0Sj7tw==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"tree-sitter": "cli.js"
},
"engines": {
"node": ">=12.0.0"
}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"type": "module",
"dependencies": {
"tree-sitter-cli": "^0.26.3"
}
}
+55
View File
@@ -0,0 +1,55 @@
=========================
Parser (No Parameters)
=========================
parser simple() {
state testing {}
}
---
(p4program
(declaration
(parserDeclaration
(parserTypeDeclaration
(parser)
parser_name: (identifier)
)
(parserStates
(parserState
(state)
(identifier)
)
)
)
)
)
=========================
Parser (Parameters)
=========================
parser imple(bool pname) {
state testing {}
}
---
(p4program
(declaration
(parserDeclaration
(parserTypeDeclaration
(parser)
(identifier)
(nonEmptyParameterList
(parameter
(typeRef
(baseType
(bool)))
(identifier))))
(parserStates
(parserState
(state)
(identifier)
)
)
)
)
)
+41
View File
@@ -0,0 +1,41 @@
{
"$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json",
"grammars": [
{
"name": "p4",
"camelcase": "P4",
"title": "P4",
"scope": "source.p4",
"file-types": [
"p4"
],
"injection-regex": "^p4$",
"class-name": "TreeSitterP4"
}
],
"metadata": {
"version": "0.1.0",
"license": "GPLv3",
"description": "A P4 Parser",
"authors": [
{
"name": "Will Hawkins",
"url": ""
}
],
"links": {
"repository": "https://github.com/tree-sitter/tree-sitter-p4",
"funding": ""
}
},
"bindings": {
"c": false,
"go": false,
"java": false,
"node": true,
"python": false,
"rust": false,
"swift": true,
"zig": false
}
}