From 08beba6cb105e0cc5b3d5e7d9c73013f50cc08e4 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Wed, 14 Jan 2026 12:49:49 -0500 Subject: [PATCH] Initial Commit Signed-off-by: Will Hawkins --- .gitignore | 8 + Package.resolved | 24 + Package.swift | 35 + Sources/p4lm/p4lm.swift | 25 + Tests/p4lmTests/p4lmTests.swift | 35 + tree-sitter-p4/.editorconfig | 50 + tree-sitter-p4/.gitattributes | 46 + tree-sitter-p4/.gitignore | 54 + tree-sitter-p4/CMakeLists.txt | 76 ++ tree-sitter-p4/Makefile | 115 +++ tree-sitter-p4/Package.resolved | 24 + tree-sitter-p4/Package.swift | 41 + tree-sitter-p4/binding.gyp | 35 + tree-sitter-p4/bindings/node/binding.cc | 19 + tree-sitter-p4/bindings/node/binding_test.js | 11 + tree-sitter-p4/bindings/node/index.d.ts | 60 ++ tree-sitter-p4/bindings/node/index.js | 37 + .../bindings/swift/TreeSitterP4/p4.h | 16 + .../TreeSitterP4Tests/TreeSitterP4Tests.swift | 12 + tree-sitter-p4/grammar.js | 966 ++++++++++++++++++ tree-sitter-p4/package-lock.json | 25 + tree-sitter-p4/package.json | 6 + tree-sitter-p4/test/corpus/parsers.txt | 55 + tree-sitter-p4/tree-sitter.json | 41 + 24 files changed, 1816 insertions(+) create mode 100644 .gitignore create mode 100644 Package.resolved create mode 100644 Package.swift create mode 100644 Sources/p4lm/p4lm.swift create mode 100644 Tests/p4lmTests/p4lmTests.swift create mode 100644 tree-sitter-p4/.editorconfig create mode 100644 tree-sitter-p4/.gitattributes create mode 100644 tree-sitter-p4/.gitignore create mode 100644 tree-sitter-p4/CMakeLists.txt create mode 100644 tree-sitter-p4/Makefile create mode 100644 tree-sitter-p4/Package.resolved create mode 100644 tree-sitter-p4/Package.swift create mode 100644 tree-sitter-p4/binding.gyp create mode 100644 tree-sitter-p4/bindings/node/binding.cc create mode 100644 tree-sitter-p4/bindings/node/binding_test.js create mode 100644 tree-sitter-p4/bindings/node/index.d.ts create mode 100644 tree-sitter-p4/bindings/node/index.js create mode 100644 tree-sitter-p4/bindings/swift/TreeSitterP4/p4.h create mode 100644 tree-sitter-p4/bindings/swift/TreeSitterP4Tests/TreeSitterP4Tests.swift create mode 100644 tree-sitter-p4/grammar.js create mode 100644 tree-sitter-p4/package-lock.json create mode 100644 tree-sitter-p4/package.json create mode 100644 tree-sitter-p4/test/corpus/parsers.txt create mode 100644 tree-sitter-p4/tree-sitter.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..7e6d713 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "d869273861f8c92503fe779caad20e06370fb63e8b7a69370c22a6a56c4b602f", + "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 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..d215cf4 --- /dev/null +++ b/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "p4lm", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "p4lm", + targets: ["p4lm"] + ) + ], + dependencies: [ + .package(path: "./tree-sitter-p4"), + .package(url: "https://github.com/tree-sitter/swift-tree-sitter", revision: "main"), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "p4lm", + dependencies: [ + .product(name: "SwiftTreeSitter", package: "swift-tree-sitter"), + .product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"), + .product(name: "TreeSitterP4", package: "tree-sitter-p4"), + ], + ), + .testTarget( + name: "p4lmTests", + dependencies: ["p4lm"] + ), + ] +) diff --git a/Sources/p4lm/p4lm.swift b/Sources/p4lm/p4lm.swift new file mode 100644 index 0000000..18e8e36 --- /dev/null +++ b/Sources/p4lm/p4lm.swift @@ -0,0 +1,25 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import SwiftTreeSitter +import TreeSitterP4 + +func parse(_ source: String) -> Optional { + + let p4lang = Language(tree_sitter_p4()) + + let p = Parser.init() + + do { + try p.setLanguage(p4lang) + } catch { + return .none + } + + let result = p.parse(source) + + guard let tree = result else { + return .none + } + return tree +} diff --git a/Tests/p4lmTests/p4lmTests.swift b/Tests/p4lmTests/p4lmTests.swift new file mode 100644 index 0000000..cc19fbf --- /dev/null +++ b/Tests/p4lmTests/p4lmTests.swift @@ -0,0 +1,35 @@ +import Testing +import TreeSitter +import SwiftTreeSitter +import TreeSitterP4 +import Foundation + +@testable import p4lm + +@Test func example() async throws { + let simple_parser_declaration = """ + parser simple() { + state testing {} + } + """ + + guard let tree = p4lm.parse(simple_parser_declaration) else { + assert(false, "Could not parse the simple parser declaration.") + } + + let p4lang = Language(tree_sitter_p4()) + let query = try! SwiftTreeSitter.Query(language: p4lang, data: String("(parserDeclaration (parserTypeDeclaration (parser) parser_name: (identifier) @parser-name))").data(using: String.Encoding.utf8)!) + + let qr = query.execute(in: tree) + + // TODO: Figure out how to actually determine the number of matches. + + guard let parser_declaration = qr.next() else { + assert(false, "Could not parse the simple parser declaration (No parser declaration).") + } + + let parser_name_capture = parser_declaration.captures(named: "parser-name")[0] + + #expect(parser_name_capture.node.text == Optional.some("simple")) + +} diff --git a/tree-sitter-p4/.editorconfig b/tree-sitter-p4/.editorconfig new file mode 100644 index 0000000..ff17b12 --- /dev/null +++ b/tree-sitter-p4/.editorconfig @@ -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 diff --git a/tree-sitter-p4/.gitattributes b/tree-sitter-p4/.gitattributes new file mode 100644 index 0000000..027ac70 --- /dev/null +++ b/tree-sitter-p4/.gitattributes @@ -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 diff --git a/tree-sitter-p4/.gitignore b/tree-sitter-p4/.gitignore new file mode 100644 index 0000000..23fc686 --- /dev/null +++ b/tree-sitter-p4/.gitignore @@ -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/** + diff --git a/tree-sitter-p4/CMakeLists.txt b/tree-sitter-p4/CMakeLists.txt new file mode 100644 index 0000000..403d842 --- /dev/null +++ b/tree-sitter-p4/CMakeLists.txt @@ -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 $ + $) + +target_compile_definitions(tree-sitter-p4 PRIVATE + $<$:TREE_SITTER_REUSE_ALLOCATOR> + $<$: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") diff --git a/tree-sitter-p4/Makefile b/tree-sitter-p4/Makefile new file mode 100644 index 0000000..06d6b14 --- /dev/null +++ b/tree-sitter-p4/Makefile @@ -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 diff --git a/tree-sitter-p4/Package.resolved b/tree-sitter-p4/Package.resolved new file mode 100644 index 0000000..b1c3180 --- /dev/null +++ b/tree-sitter-p4/Package.resolved @@ -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 +} diff --git a/tree-sitter-p4/Package.swift b/tree-sitter-p4/Package.swift new file mode 100644 index 0000000..3d25b8f --- /dev/null +++ b/tree-sitter-p4/Package.swift @@ -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 +) diff --git a/tree-sitter-p4/binding.gyp b/tree-sitter-p4/binding.gyp new file mode 100644 index 0000000..38c2cce --- /dev/null +++ b/tree-sitter-p4/binding.gyp @@ -0,0 +1,35 @@ +{ + "targets": [ + { + "target_name": "tree_sitter_p4_binding", + "dependencies": [ + " + +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::New(env, tree_sitter_p4()); + language.TypeTag(&LANGUAGE_TYPE_TAG); + exports["language"] = language; + return exports; +} + +NODE_API_MODULE(tree_sitter_p4_binding, Init) diff --git a/tree-sitter-p4/bindings/node/binding_test.js b/tree-sitter-p4/bindings/node/binding_test.js new file mode 100644 index 0000000..7a91a84 --- /dev/null +++ b/tree-sitter-p4/bindings/node/binding_test.js @@ -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); + }); +}); diff --git a/tree-sitter-p4/bindings/node/index.d.ts b/tree-sitter-p4/bindings/node/index.d.ts new file mode 100644 index 0000000..56452ff --- /dev/null +++ b/tree-sitter-p4/bindings/node/index.d.ts @@ -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; diff --git a/tree-sitter-p4/bindings/node/index.js b/tree-sitter-p4/bindings/node/index.js new file mode 100644 index 0000000..7ed15ce --- /dev/null +++ b/tree-sitter-p4/bindings/node/index.js @@ -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; diff --git a/tree-sitter-p4/bindings/swift/TreeSitterP4/p4.h b/tree-sitter-p4/bindings/swift/TreeSitterP4/p4.h new file mode 100644 index 0000000..264bed9 --- /dev/null +++ b/tree-sitter-p4/bindings/swift/TreeSitterP4/p4.h @@ -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_ diff --git a/tree-sitter-p4/bindings/swift/TreeSitterP4Tests/TreeSitterP4Tests.swift b/tree-sitter-p4/bindings/swift/TreeSitterP4Tests/TreeSitterP4Tests.swift new file mode 100644 index 0000000..1f8ef62 --- /dev/null +++ b/tree-sitter-p4/bindings/swift/TreeSitterP4Tests/TreeSitterP4Tests.swift @@ -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") + } +} diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js new file mode 100644 index 0000000..343c631 --- /dev/null +++ b/tree-sitter-p4/grammar.js @@ -0,0 +1,966 @@ +/** + * @file Grammar for (a subset of) P4 + * @author Will Hawkins + * @license GPLv3 + */ + +/// +// @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 + ; +*/ diff --git a/tree-sitter-p4/package-lock.json b/tree-sitter-p4/package-lock.json new file mode 100644 index 0000000..f721108 --- /dev/null +++ b/tree-sitter-p4/package-lock.json @@ -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" + } + } + } +} diff --git a/tree-sitter-p4/package.json b/tree-sitter-p4/package.json new file mode 100644 index 0000000..84e349d --- /dev/null +++ b/tree-sitter-p4/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "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 new file mode 100644 index 0000000..ae19eb5 --- /dev/null +++ b/tree-sitter-p4/test/corpus/parsers.txt @@ -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) + ) + ) + ) + ) +) diff --git a/tree-sitter-p4/tree-sitter.json b/tree-sitter-p4/tree-sitter.json new file mode 100644 index 0000000..bfca5e5 --- /dev/null +++ b/tree-sitter-p4/tree-sitter.json @@ -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 + } +}