diff --git a/pkgs/development/python-modules/tree-sitter-grammars/default.nix b/pkgs/development/python-modules/tree-sitter-grammars/default.nix new file mode 100644 index 000000000000000..fd1e5d8a10868a7 --- /dev/null +++ b/pkgs/development/python-modules/tree-sitter-grammars/default.nix @@ -0,0 +1,136 @@ +{ pkgs +, lib +, buildPythonPackage +, pytestCheckHook +, tree-sitter +}: +let + grammarToPythonPkg = name: grammarDrv: + let + inherit (grammarDrv) version; + + # `name`: grammar derivation pname in the format of `tree-sitter-` + + snakeCaseName = lib.replaceStrings [ "-" ] [ "_" ] name; + drvPrefix = "python-${name}"; + in + buildPythonPackage { + inherit version; + pname = drvPrefix; + + src = pkgs.symlinkJoin { + name = "${drvPrefix}-source"; + paths = [ + (pkgs.writeTextFile { + name = "${drvPrefix}-init"; + text = '' + from ._binding import language + + __all__ = ["language"] + ''; + destination = "/${snakeCaseName}/__init__.py"; + }) + (pkgs.writeTextFile { + name = "${drvPrefix}-binding"; + text = '' + #include + + typedef struct TSLanguage TSLanguage; + + TSLanguage *${snakeCaseName}(void); + + static PyObject* _binding_language(PyObject *self, PyObject *args) { + return PyLong_FromVoidPtr(${snakeCaseName}()); + } + + 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 = -1, + .m_methods = methods + }; + + PyMODINIT_FUNC PyInit__binding(void) { + return PyModule_Create(&module); + } + ''; + destination = "/${snakeCaseName}/binding.c"; + }) + (pkgs.writeTextFile { + name = "${drvPrefix}-setup.py"; + text = '' + from platform import system + from setuptools import Extension, setup + + + setup( + name="${snakeCaseName}", + version="${version}", + packages=["${snakeCaseName}"], + ext_package="${snakeCaseName}", + ext_modules=[ + Extension( + name="_binding", + sources=["${snakeCaseName}/binding.c"], + extra_objects = ["${grammarDrv}/parser"], + extra_compile_args=( + ["-std=c11"] if system() != 'Windows' else [] + ), + define_macros=[ + ("Py_LIMITED_API", "0x03080000"), + ("PY_SSIZE_T_CLEAN", None) + ], + py_limited_api=True, + ) + ], + ) + ''; + destination = "/setup.py"; + }) + (pkgs.writeTextFile { + name = "${drvPrefix}-test"; + text = '' + from ${snakeCaseName} import language + from tree_sitter import Language, Parser + + + def test_language(): + lang = Language(language()) + assert lang is not None + parser = Parser() + parser.language = lang + tree = parser.parse(bytes("", "utf-8")) + assert tree is not None + ''; + destination = "/tests/test_language.py"; + }) + ]; + }; + + preCheck = '' + rm -r ${snakeCaseName} + ''; + + nativeCheckInputs = [ tree-sitter pytestCheckHook ]; + pythonImportsCheck = [ snakeCaseName ]; + + meta = { + description = "Python bindings for ${name}"; + maintainers = with lib.maintainers; [ a-jay98 adfaure mightyiam stepbrobd ]; + license = lib.licenses.mit; + }; + }; +in +# TODO pkgset or flattened? +lib.mapAttrs grammarToPythonPkg (builtins.removeAttrs pkgs.tree-sitter.builtGrammars [ + "tree-sitter-perl" + "tree-sitter-ql-dbscheme" + "tree-sitter-org-nvim" +]) diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index 9c5ea1a8510079e..b2f751441df464d 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -15483,6 +15483,8 @@ self: super: with self; { tree-sitter0_21 = callPackage ../development/python-modules/tree-sitter0_21 { }; + tree-sitter-grammars = callPackage ../development/python-modules/tree-sitter-grammars { }; + tree-sitter-html = callPackage ../development/python-modules/tree-sitter-html { }; tree-sitter-python = callPackage ../development/python-modules/tree-sitter-python { };