From ccd44c8172e9afc648b89d360659aa1d090945ec Mon Sep 17 00:00:00 2001 From: Matthias Unterrainer Date: Sat, 17 Jan 2026 15:37:53 +0100 Subject: [PATCH] Initial commit --- .editorconfig | 46 +++++++++ .gitattributes | 42 ++++++++ .gitignore | 50 ++++++++++ CMakeLists.txt | 66 +++++++++++++ Cargo.toml | 34 +++++++ Makefile | 99 +++++++++++++++++++ Package.swift | 41 ++++++++ binding.gyp | 35 +++++++ bindings/c/tree-sitter-mc.pc.in | 10 ++ bindings/c/tree_sitter/tree-sitter-mc.h | 16 +++ bindings/go/binding.go | 15 +++ bindings/go/binding_test.go | 15 +++ bindings/node/binding.cc | 19 ++++ bindings/node/binding_test.js | 9 ++ bindings/node/index.d.ts | 27 +++++ bindings/node/index.js | 11 +++ bindings/python/tests/test_binding.py | 12 +++ bindings/python/tree_sitter_mc/__init__.py | 42 ++++++++ bindings/python/tree_sitter_mc/__init__.pyi | 10 ++ bindings/python/tree_sitter_mc/binding.c | 35 +++++++ bindings/python/tree_sitter_mc/py.typed | 0 bindings/rust/build.rs | 21 ++++ bindings/rust/lib.rs | 51 ++++++++++ bindings/swift/TreeSitterMc/mc.h | 16 +++ .../TreeSitterMcTests/TreeSitterMcTests.swift | 12 +++ go.mod | 5 + grammar.js | 17 ++++ package.json | 51 ++++++++++ pyproject.toml | 29 ++++++ setup.py | 77 +++++++++++++++ tree-sitter.json | 39 ++++++++ 31 files changed, 952 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 Package.swift create mode 100644 binding.gyp create mode 100644 bindings/c/tree-sitter-mc.pc.in create mode 100644 bindings/c/tree_sitter/tree-sitter-mc.h create mode 100644 bindings/go/binding.go create mode 100644 bindings/go/binding_test.go create mode 100644 bindings/node/binding.cc create mode 100644 bindings/node/binding_test.js create mode 100644 bindings/node/index.d.ts create mode 100644 bindings/node/index.js create mode 100644 bindings/python/tests/test_binding.py create mode 100644 bindings/python/tree_sitter_mc/__init__.py create mode 100644 bindings/python/tree_sitter_mc/__init__.pyi create mode 100644 bindings/python/tree_sitter_mc/binding.c create mode 100644 bindings/python/tree_sitter_mc/py.typed create mode 100644 bindings/rust/build.rs create mode 100644 bindings/rust/lib.rs create mode 100644 bindings/swift/TreeSitterMc/mc.h create mode 100644 bindings/swift/TreeSitterMcTests/TreeSitterMcTests.swift create mode 100644 go.mod create mode 100644 grammar.js create mode 100644 package.json create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 tree-sitter.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..65330c4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,46 @@ +root = true + +[*] +charset = utf-8 + +[*.{json,toml,yml,gyp}] +indent_style = space +indent_size = 2 + +[*.js] +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 + +[*.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/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7772c94 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +* 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87a0c80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Rust artifacts +target/ +Cargo.lock + +# Node artifacts +build/ +prebuilds/ +node_modules/ +package-lock.json + +# Swift artifacts +.build/ +Package.resolved + +# 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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d0ef279 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.13) + +project(tree-sitter-mc + VERSION "0.1.0" + DESCRIPTION "A parser for the MC programming language" + HOMEPAGE_URL "https://git.matthunter.de/matth0930/tree-sitter-mc.git" + 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/parser.c" + 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-mc src/parser.c) +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) + target_sources(tree-sitter-mc PRIVATE src/scanner.c) +endif() +target_include_directories(tree-sitter-mc + PRIVATE src + INTERFACE $ + $) + +target_compile_definitions(tree-sitter-mc PRIVATE + $<$:TREE_SITTER_REUSE_ALLOCATOR> + $<$:TREE_SITTER_DEBUG>) + +set_target_properties(tree-sitter-mc + PROPERTIES + C_STANDARD 11 + POSITION_INDEPENDENT_CODE ON + SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" + DEFINE_SYMBOL "") + +configure_file(bindings/c/tree-sitter-mc.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-mc.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-mc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +install(TARGETS tree-sitter-mc + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + +file(GLOB QUERIES queries/*.scm) +install(FILES ${QUERIES} + DESTINATION "${CMAKE_INSTALL_DATADIR}/tree-sitter/queries/mc") + +add_custom_target(ts-test "${TREE_SITTER_CLI}" test + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "tree-sitter test") diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c42c9ba --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "tree-sitter-mc" +description = "A parser for the MC programming language" +version = "0.1.0" +authors = ["Matthias Unterrainer"] +license = "Apache-2.0" +readme = "README.md" +keywords = ["incremental", "parsing", "tree-sitter", "mc"] +categories = ["parser-implementations", "parsing", "text-editors"] +repository = "https://git.matthunter.de/matth0930/tree-sitter-mc.git" +edition = "2021" +autoexamples = false + +build = "bindings/rust/build.rs" +include = [ + "bindings/rust/*", + "grammar.js", + "queries/*", + "src/*", + "tree-sitter.json", + "/LICENSE", +] + +[lib] +path = "bindings/rust/lib.rs" + +[dependencies] +tree-sitter-language = "0.1" + +[build-dependencies] +cc = "1.2" + +[dev-dependencies] +tree-sitter = "0.25.10" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d715e4d --- /dev/null +++ b/Makefile @@ -0,0 +1,99 @@ +ifeq ($(OS),Windows_NT) +$(error Windows is not supported) +endif + +LANGUAGE_NAME := tree-sitter-mc +HOMEPAGE_URL := https://git.matthunter.de/matth0930/tree-sitter-mc.git +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 +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 +ifeq ($(shell uname),Darwin) + 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 + SOEXT = so + SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) + SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) + LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) +endif +ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) + PCLIBDIR := $(PREFIX)/libdata/pkgconfig +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 + +$(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)|' $< > $@ + +$(PARSER): $(SRC_DIR)/grammar.json + $(TS) generate $^ + +install: all + install -d '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/mc '$(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) + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) +ifneq ($(wildcard queries/*.scm),) + install -m644 queries/*.scm '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/mc +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/mc + +clean: + $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) + +test: + $(TS) test + +.PHONY: all install uninstall clean test diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..5078e4c --- /dev/null +++ b/Package.swift @@ -0,0 +1,41 @@ +// swift-tools-version:5.3 + +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: "TreeSitterMc", + products: [ + .library(name: "TreeSitterMc", targets: ["TreeSitterMc"]), + ], + dependencies: [ + .package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.9.0"), + ], + targets: [ + .target( + name: "TreeSitterMc", + dependencies: [], + path: ".", + sources: sources, + resources: [ + .copy("queries") + ], + publicHeadersPath: "bindings/swift", + cSettings: [.headerSearchPath("src")] + ), + .testTarget( + name: "TreeSitterMcTests", + dependencies: [ + "SwiftTreeSitter", + "TreeSitterMc", + ], + path: "bindings/swift/TreeSitterMcTests" + ) + ], + cLanguageStandard: .c11 +) diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..f6c6f14 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,35 @@ +{ + "targets": [ + { + "target_name": "tree_sitter_mc_binding", + "dependencies": [ + " + +typedef struct TSLanguage TSLanguage; + +extern "C" TSLanguage *tree_sitter_mc(); + +// "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_mc()); + language.TypeTag(&LANGUAGE_TYPE_TAG); + exports["language"] = language; + return exports; +} + +NODE_API_MODULE(tree_sitter_mc_binding, Init) diff --git a/bindings/node/binding_test.js b/bindings/node/binding_test.js new file mode 100644 index 0000000..55becac --- /dev/null +++ b/bindings/node/binding_test.js @@ -0,0 +1,9 @@ +const assert = require("node:assert"); +const { test } = require("node:test"); + +const Parser = require("tree-sitter"); + +test("can load grammar", () => { + const parser = new Parser(); + assert.doesNotThrow(() => parser.setLanguage(require("."))); +}); diff --git a/bindings/node/index.d.ts b/bindings/node/index.d.ts new file mode 100644 index 0000000..528e060 --- /dev/null +++ b/bindings/node/index.d.ts @@ -0,0 +1,27 @@ +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[]; + }); + +type Language = { + language: unknown; + nodeTypeInfo: NodeInfo[]; +}; + +declare const language: Language; +export = language; diff --git a/bindings/node/index.js b/bindings/node/index.js new file mode 100644 index 0000000..39e098d --- /dev/null +++ b/bindings/node/index.js @@ -0,0 +1,11 @@ +const root = require("path").join(__dirname, "..", ".."); + +module.exports = + typeof process.versions.bun === "string" + // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time + ? require(`../../prebuilds/${process.platform}-${process.arch}/tree-sitter-mc.node`) + : require("node-gyp-build")(root); + +try { + module.exports.nodeTypeInfo = require("../../src/node-types.json"); +} catch (_) {} diff --git a/bindings/python/tests/test_binding.py b/bindings/python/tests/test_binding.py new file mode 100644 index 0000000..f99be81 --- /dev/null +++ b/bindings/python/tests/test_binding.py @@ -0,0 +1,12 @@ +from unittest import TestCase + +from tree_sitter import Language, Parser +import tree_sitter_mc + + +class TestLanguage(TestCase): + def test_can_load_grammar(self): + try: + Parser(Language(tree_sitter_mc.language())) + except Exception: + self.fail("Error loading MC grammar") diff --git a/bindings/python/tree_sitter_mc/__init__.py b/bindings/python/tree_sitter_mc/__init__.py new file mode 100644 index 0000000..071d522 --- /dev/null +++ b/bindings/python/tree_sitter_mc/__init__.py @@ -0,0 +1,42 @@ +"""A parser for the MC programming language""" + +from importlib.resources import files as _files + +from ._binding import language + + +def _get_query(name, file): + query = _files(f"{__package__}.queries") / file + globals()[name] = query.read_text() + return globals()[name] + + +def __getattr__(name): + # NOTE: uncomment these to include any queries that this grammar contains: + + # if name == "HIGHLIGHTS_QUERY": + # return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") + # if name == "INJECTIONS_QUERY": + # return _get_query("INJECTIONS_QUERY", "injections.scm") + # if name == "LOCALS_QUERY": + # return _get_query("LOCALS_QUERY", "locals.scm") + # if name == "TAGS_QUERY": + # return _get_query("TAGS_QUERY", "tags.scm") + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +__all__ = [ + "language", + # "HIGHLIGHTS_QUERY", + # "INJECTIONS_QUERY", + # "LOCALS_QUERY", + # "TAGS_QUERY", +] + + +def __dir__(): + return sorted(__all__ + [ + "__all__", "__builtins__", "__cached__", "__doc__", "__file__", + "__loader__", "__name__", "__package__", "__path__", "__spec__", + ]) diff --git a/bindings/python/tree_sitter_mc/__init__.pyi b/bindings/python/tree_sitter_mc/__init__.pyi new file mode 100644 index 0000000..abf6633 --- /dev/null +++ b/bindings/python/tree_sitter_mc/__init__.pyi @@ -0,0 +1,10 @@ +from typing import Final + +# NOTE: uncomment these to include any queries that this grammar contains: + +# HIGHLIGHTS_QUERY: Final[str] +# INJECTIONS_QUERY: Final[str] +# LOCALS_QUERY: Final[str] +# TAGS_QUERY: Final[str] + +def language() -> object: ... diff --git a/bindings/python/tree_sitter_mc/binding.c b/bindings/python/tree_sitter_mc/binding.c new file mode 100644 index 0000000..e7369e6 --- /dev/null +++ b/bindings/python/tree_sitter_mc/binding.c @@ -0,0 +1,35 @@ +#include + +typedef struct TSLanguage TSLanguage; + +TSLanguage *tree_sitter_mc(void); + +static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { + return PyCapsule_New(tree_sitter_mc(), "tree_sitter.Language", NULL); +} + +static struct PyModuleDef_Slot slots[] = { +#ifdef Py_GIL_DISABLED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL} +}; + +static PyMethodDef methods[] = { + {"language", _binding_language, METH_NOARGS, + "Get the tree-sitter language for this grammar."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_binding", + .m_doc = NULL, + .m_size = 0, + .m_methods = methods, + .m_slots = slots, +}; + +PyMODINIT_FUNC PyInit__binding(void) { + return PyModuleDef_Init(&module); +} diff --git a/bindings/python/tree_sitter_mc/py.typed b/bindings/python/tree_sitter_mc/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs new file mode 100644 index 0000000..d8e32ef --- /dev/null +++ b/bindings/rust/build.rs @@ -0,0 +1,21 @@ +fn main() { + let src_dir = std::path::Path::new("src"); + + let mut c_config = cc::Build::new(); + c_config.std("c11").include(src_dir); + + #[cfg(target_env = "msvc")] + c_config.flag("-utf-8"); + + let parser_path = src_dir.join("parser.c"); + c_config.file(&parser_path); + println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); + + let scanner_path = src_dir.join("scanner.c"); + if scanner_path.exists() { + c_config.file(&scanner_path); + println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); + } + + c_config.compile("tree-sitter-mc"); +} diff --git a/bindings/rust/lib.rs b/bindings/rust/lib.rs new file mode 100644 index 0000000..2a2d8db --- /dev/null +++ b/bindings/rust/lib.rs @@ -0,0 +1,51 @@ +//! This crate provides MC language support for the [tree-sitter] parsing library. +//! +//! Typically, you will use the [`LANGUAGE`] constant to add this language to a +//! tree-sitter [`Parser`], and then use the parser to parse some code: +//! +//! ``` +//! let code = r#" +//! "#; +//! let mut parser = tree_sitter::Parser::new(); +//! let language = tree_sitter_mc::LANGUAGE; +//! parser +//! .set_language(&language.into()) +//! .expect("Error loading MC parser"); +//! let tree = parser.parse(code, None).unwrap(); +//! assert!(!tree.root_node().has_error()); +//! ``` +//! +//! [`Parser`]: https://docs.rs/tree-sitter/0.25.10/tree_sitter/struct.Parser.html +//! [tree-sitter]: https://tree-sitter.github.io/ + +use tree_sitter_language::LanguageFn; + +extern "C" { + fn tree_sitter_mc() -> *const (); +} + +/// The tree-sitter [`LanguageFn`] for this grammar. +pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_mc) }; + +/// The content of the [`node-types.json`] file for this grammar. +/// +/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types +pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); + +// NOTE: uncomment these to include any queries that this grammar contains: + +// pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); +// pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); +// pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); +// pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm"); + +#[cfg(test)] +mod tests { + #[test] + fn test_can_load_grammar() { + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(&super::LANGUAGE.into()) + .expect("Error loading MC parser"); + } +} diff --git a/bindings/swift/TreeSitterMc/mc.h b/bindings/swift/TreeSitterMc/mc.h new file mode 100644 index 0000000..83bf9f7 --- /dev/null +++ b/bindings/swift/TreeSitterMc/mc.h @@ -0,0 +1,16 @@ +#ifndef TREE_SITTER_MC_H_ +#define TREE_SITTER_MC_H_ + +typedef struct TSLanguage TSLanguage; + +#ifdef __cplusplus +extern "C" { +#endif + +const TSLanguage *tree_sitter_mc(void); + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_MC_H_ diff --git a/bindings/swift/TreeSitterMcTests/TreeSitterMcTests.swift b/bindings/swift/TreeSitterMcTests/TreeSitterMcTests.swift new file mode 100644 index 0000000..31b15be --- /dev/null +++ b/bindings/swift/TreeSitterMcTests/TreeSitterMcTests.swift @@ -0,0 +1,12 @@ +import XCTest +import SwiftTreeSitter +import TreeSitterMc + +final class TreeSitterMcTests: XCTestCase { + func testCanLoadGrammar() throws { + let parser = Parser() + let language = Language(language: tree_sitter_mc()) + XCTAssertNoThrow(try parser.setLanguage(language), + "Error loading MC grammar") + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bb6fced --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.matthunter.de/matth0930/tree-sitter-mc.git + +go 1.22 + +require github.com/tree-sitter/go-tree-sitter v0.24.0 diff --git a/grammar.js b/grammar.js new file mode 100644 index 0000000..1c7d2b0 --- /dev/null +++ b/grammar.js @@ -0,0 +1,17 @@ +/** + * @file A parser for the MC programming language + * @author Matthias Unterrainer + * @license Apache-2.0 + */ + +/// +// @ts-check + +module.exports = grammar({ + name: "mc", + + rules: { + // TODO: add the actual grammar rules + source_file: $ => "hello" + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..5cd77ef --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "tree-sitter-mc", + "version": "0.1.0", + "description": "A parser for the MC programming language", + "repository": "https://git.matthunter.de/matth0930/tree-sitter-mc.git", + "license": "Apache-2.0", + "author": { + "name": "Matthias Unterrainer" + }, + "main": "bindings/node", + "types": "bindings/node", + "keywords": [ + "incremental", + "parsing", + "tree-sitter", + "mc" + ], + "files": [ + "grammar.js", + "tree-sitter.json", + "binding.gyp", + "prebuilds/**", + "bindings/node/*", + "queries/*", + "src/**", + "*.wasm" + ], + "dependencies": { + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4" + }, + "devDependencies": { + "prebuildify": "^6.0.1", + "tree-sitter": "^0.22.4", + "tree-sitter-cli": "^0.25.10" + }, + "peerDependencies": { + "tree-sitter": "^0.22.4" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + }, + "scripts": { + "install": "node-gyp-build", + "prestart": "tree-sitter build --wasm", + "start": "tree-sitter playground", + "test": "node --test bindings/node/*_test.js" + } +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..29b7fb1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = ["setuptools>=62.4.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "tree-sitter-mc" +description = "A parser for the MC programming language" +version = "0.1.0" +keywords = ["incremental", "parsing", "tree-sitter", "mc"] +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Compilers", + "Topic :: Text Processing :: Linguistic", + "Typing :: Typed", +] +authors = [{ name = "Matthias Unterrainer" }] +requires-python = ">=3.10" +license.text = "Apache-2.0" +readme = "README.md" + +[project.urls] +Homepage = "https://git.matthunter.de/matth0930/tree-sitter-mc.git" + +[project.optional-dependencies] +core = ["tree-sitter~=0.24"] + +[tool.cibuildwheel] +build = "cp310-*" +build-frontend = "build" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..74f1150 --- /dev/null +++ b/setup.py @@ -0,0 +1,77 @@ +from os import path +from sysconfig import get_config_var + +from setuptools import Extension, find_packages, setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.egg_info import egg_info +from wheel.bdist_wheel import bdist_wheel + + +class Build(build): + def run(self): + if path.isdir("queries"): + dest = path.join(self.build_lib, "tree_sitter_mc", "queries") + self.copy_tree("queries", dest) + super().run() + + +class BuildExt(build_ext): + def build_extension(self, ext: Extension): + if self.compiler.compiler_type != "msvc": + ext.extra_compile_args = ["-std=c11", "-fvisibility=hidden"] + else: + ext.extra_compile_args = ["/std:c11", "/utf-8"] + if path.exists("src/scanner.c"): + ext.sources.append("src/scanner.c") + if ext.py_limited_api: + ext.define_macros.append(("Py_LIMITED_API", "0x030A0000")) + super().build_extension(ext) + + +class BdistWheel(bdist_wheel): + def get_tag(self): + python, abi, platform = super().get_tag() + if python.startswith("cp"): + python, abi = "cp310", "abi3" + return python, abi, platform + + +class EggInfo(egg_info): + def find_sources(self): + super().find_sources() + self.filelist.recursive_include("queries", "*.scm") + self.filelist.include("src/tree_sitter/*.h") + + +setup( + packages=find_packages("bindings/python"), + package_dir={"": "bindings/python"}, + package_data={ + "tree_sitter_mc": ["*.pyi", "py.typed"], + "tree_sitter_mc.queries": ["*.scm"], + }, + ext_package="tree_sitter_mc", + ext_modules=[ + Extension( + name="_binding", + sources=[ + "bindings/python/tree_sitter_mc/binding.c", + "src/parser.c", + ], + define_macros=[ + ("PY_SSIZE_T_CLEAN", None), + ("TREE_SITTER_HIDE_SYMBOLS", None), + ], + include_dirs=["src"], + py_limited_api=not get_config_var("Py_GIL_DISABLED"), + ) + ], + cmdclass={ + "build": Build, + "build_ext": BuildExt, + "bdist_wheel": BdistWheel, + "egg_info": EggInfo, + }, + zip_safe=False +) diff --git a/tree-sitter.json b/tree-sitter.json new file mode 100644 index 0000000..a2ebf83 --- /dev/null +++ b/tree-sitter.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json", + "grammars": [ + { + "name": "mc", + "camelcase": "Mc", + "title": "MC", + "scope": "source.mc", + "file-types": [ + "mc", + "c" + ], + "injection-regex": "^mc$", + "class-name": "TreeSitterMc" + } + ], + "metadata": { + "version": "0.1.0", + "license": "Apache-2.0", + "description": "A parser for the MC programming language", + "authors": [ + { + "name": "Matthias Unterrainer" + } + ], + "links": { + "repository": "https://git.matthunter.de/matth0930/tree-sitter-mc.git" + } + }, + "bindings": { + "c": true, + "go": true, + "node": true, + "python": true, + "rust": true, + "swift": true, + "zig": false + } +} \ No newline at end of file