aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig50
-rw-r--r--.gitattributes46
-rw-r--r--.gitignore48
-rw-r--r--CMakeLists.txt76
-rw-r--r--Cargo.toml34
-rw-r--r--Makefile115
-rw-r--r--Package.swift41
-rw-r--r--README.md22
-rw-r--r--binding.gyp35
-rw-r--r--bindings/c/tree-sitter-wmap.pc.in10
-rw-r--r--bindings/c/tree_sitter/tree-sitter-wmap.h16
-rw-r--r--bindings/go/binding.go15
-rw-r--r--bindings/go/binding_test.go15
-rw-r--r--bindings/node/binding.cc19
-rw-r--r--bindings/node/binding_test.js11
-rw-r--r--bindings/node/index.d.ts60
-rw-r--r--bindings/node/index.js37
-rw-r--r--bindings/python/tests/test_binding.py12
-rw-r--r--bindings/python/tree_sitter_wmap/__init__.py43
-rw-r--r--bindings/python/tree_sitter_wmap/__init__.pyi17
-rw-r--r--bindings/python/tree_sitter_wmap/binding.c35
-rw-r--r--bindings/python/tree_sitter_wmap/py.typed0
-rw-r--r--bindings/rust/build.rs56
-rw-r--r--bindings/rust/lib.rs60
-rw-r--r--bindings/swift/TreeSitterWmap/wmap.h16
-rw-r--r--bindings/swift/TreeSitterWmapTests/TreeSitterWmapTests.swift12
-rw-r--r--example.wmap91
-rw-r--r--go.mod5
-rw-r--r--grammar.js133
-rw-r--r--lua/tree-sitter-wmap.lua21
-rw-r--r--package.json58
-rw-r--r--plugin/tree-sitter-wmap.lua1
-rw-r--r--pyproject.toml30
-rw-r--r--queries/highlights.scm14
l---------queries/wmap/highlights.scm1
-rw-r--r--setup.py77
-rw-r--r--src/grammar.json479
-rw-r--r--src/node-types.json375
-rw-r--r--src/parser.c1553
-rw-r--r--src/tree_sitter/alloc.h54
-rw-r--r--src/tree_sitter/array.h291
-rw-r--r--src/tree_sitter/parser.h286
-rw-r--r--test/corpus/entities.txt495
-rw-r--r--test/corpus/ignored_lines.txt84
-rw-r--r--test_simple.wmap1
-rw-r--r--tree-sitter.json42
-rw-r--r--wmap-spec.ebnf52
47 files changed, 5044 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..ff17b12
--- /dev/null
+++ b/.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/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..027ac70
--- /dev/null
+++ b/.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/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c0cb7f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+# 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
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..990b901
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,76 @@
+cmake_minimum_required(VERSION 3.13)
+
+project(tree-sitter-wmap
+ VERSION "1.0.0"
+ DESCRIPTION "Parser for wmap Formatted Wardley Maps"
+ HOMEPAGE_URL "https://git.sr.ht/~rbdr/tree-sitter-wmap"
+ 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-wmap src/parser.c)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c)
+ target_sources(tree-sitter-wmap PRIVATE src/scanner.c)
+endif()
+target_include_directories(tree-sitter-wmap
+ PRIVATE src
+ INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/bindings/c>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+target_compile_definitions(tree-sitter-wmap PRIVATE
+ $<$<BOOL:${TREE_SITTER_REUSE_ALLOCATOR}>:TREE_SITTER_REUSE_ALLOCATOR>
+ $<$<CONFIG:Debug>:TREE_SITTER_DEBUG>)
+
+set_target_properties(tree-sitter-wmap
+ PROPERTIES
+ C_STANDARD 11
+ POSITION_INDEPENDENT_CODE ON
+ SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}"
+ DEFINE_SYMBOL "")
+
+configure_file(bindings/c/tree-sitter-wmap.pc.in
+ "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-wmap.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-wmap.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+install(TARGETS tree-sitter-wmap
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+
+file(GLOB QUERIES queries/*.scm)
+install(FILES ${QUERIES}
+ DESTINATION "${CMAKE_INSTALL_DATADIR}/tree-sitter/queries/wmap")
+
+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..6e32810
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "tree-sitter-wmap"
+description = "Parser for wmap Formatted Wardley Maps"
+version = "1.0.0"
+authors = ["Rubén Beltrán del Río <wmap.parser@r.bdr.sh>"]
+license = "AGPL-v3"
+readme = "README.md"
+keywords = ["incremental", "parsing", "tree-sitter", "wmap"]
+categories = ["parser-implementations", "parsing", "text-editors"]
+repository = "https://git.sr.ht/~rbdr/tree-sitter-wmap"
+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.26.3"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..df524b9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,115 @@
+LANGUAGE_NAME := tree-sitter-wmap
+HOMEPAGE_URL := https://git.sr.ht/~rbdr/tree-sitter-wmap
+VERSION := 1.0.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/wmap '$(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/wmap
+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/wmap
+
+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/Package.swift b/Package.swift
new file mode 100644
index 0000000..c35e933
--- /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: "TreeSitterWmap",
+ products: [
+ .library(name: "TreeSitterWmap", targets: ["TreeSitterWmap"]),
+ ],
+ dependencies: [
+ .package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.9.0"),
+ ],
+ targets: [
+ .target(
+ name: "TreeSitterWmap",
+ dependencies: [],
+ path: ".",
+ sources: sources,
+ resources: [
+ .copy("queries")
+ ],
+ publicHeadersPath: "bindings/swift",
+ cSettings: [.headerSearchPath("src")]
+ ),
+ .testTarget(
+ name: "TreeSitterWmapTests",
+ dependencies: [
+ "SwiftTreeSitter",
+ "TreeSitterWmap",
+ ],
+ path: "bindings/swift/TreeSitterWmapTests"
+ )
+ ],
+ cLanguageStandard: .c11
+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..39ea591
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# tree-sitter-wmap
+
+This is a tree-sitter parser for the `wmap` notation for wardley maps with a
+built-in neovim plugin if you use `nvim-treesitter`.
+
+Learn more about the notation at the [map website][map].
+
+## Neovim plugin
+
+If you use `nvim-treesitter`, you can also include this as a plugin and it will
+configure the file types and highlights.
+
+```
+require('lazy').setup({
+ ...
+ 'nvim-treesitter/nvim-treesitter',
+ 'https://git.sr.ht/~rbdr/tree-sitter-api-notation'
+ ...
+}
+```
+
+[map]: https://map.tranquil.systems
diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000..bc7d078
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,35 @@
+{
+ "targets": [
+ {
+ "target_name": "tree_sitter_wmap_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",
+ ],
+ }],
+ ],
+ }
+ ]
+}
diff --git a/bindings/c/tree-sitter-wmap.pc.in b/bindings/c/tree-sitter-wmap.pc.in
new file mode 100644
index 0000000..028c4a1
--- /dev/null
+++ b/bindings/c/tree-sitter-wmap.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: tree-sitter-wmap
+Description: @PROJECT_DESCRIPTION@
+URL: @PROJECT_HOMEPAGE_URL@
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -ltree-sitter-wmap
+Cflags: -I${includedir}
diff --git a/bindings/c/tree_sitter/tree-sitter-wmap.h b/bindings/c/tree_sitter/tree-sitter-wmap.h
new file mode 100644
index 0000000..b477b35
--- /dev/null
+++ b/bindings/c/tree_sitter/tree-sitter-wmap.h
@@ -0,0 +1,16 @@
+#ifndef TREE_SITTER_WMAP_H_
+#define TREE_SITTER_WMAP_H_
+
+typedef struct TSLanguage TSLanguage;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const TSLanguage *tree_sitter_wmap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_SITTER_WMAP_H_
diff --git a/bindings/go/binding.go b/bindings/go/binding.go
new file mode 100644
index 0000000..80fe78d
--- /dev/null
+++ b/bindings/go/binding.go
@@ -0,0 +1,15 @@
+package tree_sitter_wmap
+
+// #cgo CFLAGS: -std=c11 -fPIC
+// #include "../../src/parser.c"
+// #if __has_include("../../src/scanner.c")
+// #include "../../src/scanner.c"
+// #endif
+import "C"
+
+import "unsafe"
+
+// Get the tree-sitter Language for this grammar.
+func Language() unsafe.Pointer {
+ return unsafe.Pointer(C.tree_sitter_wmap())
+}
diff --git a/bindings/go/binding_test.go b/bindings/go/binding_test.go
new file mode 100644
index 0000000..911fd9b
--- /dev/null
+++ b/bindings/go/binding_test.go
@@ -0,0 +1,15 @@
+package tree_sitter_wmap_test
+
+import (
+ "testing"
+
+ tree_sitter "github.com/tree-sitter/go-tree-sitter"
+ tree_sitter_wmap "git.sr.ht/~rbdr/tree-sitter-wmap/bindings/go"
+)
+
+func TestCanLoadGrammar(t *testing.T) {
+ language := tree_sitter.NewLanguage(tree_sitter_wmap.Language())
+ if language == nil {
+ t.Errorf("Error loading Wmap grammar")
+ }
+}
diff --git a/bindings/node/binding.cc b/bindings/node/binding.cc
new file mode 100644
index 0000000..03bd34c
--- /dev/null
+++ b/bindings/node/binding.cc
@@ -0,0 +1,19 @@
+#include <napi.h>
+
+typedef struct TSLanguage TSLanguage;
+
+extern "C" TSLanguage *tree_sitter_wmap();
+
+// "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_wmap());
+ language.TypeTag(&LANGUAGE_TYPE_TAG);
+ exports["language"] = language;
+ return exports;
+}
+
+NODE_API_MODULE(tree_sitter_wmap_binding, Init)
diff --git a/bindings/node/binding_test.js b/bindings/node/binding_test.js
new file mode 100644
index 0000000..7a91a84
--- /dev/null
+++ b/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/bindings/node/index.d.ts b/bindings/node/index.d.ts
new file mode 100644
index 0000000..876252a
--- /dev/null
+++ b/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 Wmap from "tree-sitter-wmap";
+ *
+ * const parser = new Parser();
+ * parser.setLanguage(Wmap);
+ */
+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/bindings/node/index.js b/bindings/node/index.js
new file mode 100644
index 0000000..efa6bea
--- /dev/null
+++ b/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-wmap.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/bindings/python/tests/test_binding.py b/bindings/python/tests/test_binding.py
new file mode 100644
index 0000000..35b3845
--- /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_wmap
+
+
+class TestLanguage(TestCase):
+ def test_can_load_grammar(self):
+ try:
+ Parser(Language(tree_sitter_wmap.language()))
+ except Exception:
+ self.fail("Error loading Wmap grammar")
diff --git a/bindings/python/tree_sitter_wmap/__init__.py b/bindings/python/tree_sitter_wmap/__init__.py
new file mode 100644
index 0000000..fa35926
--- /dev/null
+++ b/bindings/python/tree_sitter_wmap/__init__.py
@@ -0,0 +1,43 @@
+"""Parser for wmap Formatted Wardley Maps"""
+
+from importlib.resources import files as _files
+
+from ._binding import language
+
+
+def _get_query(name, file):
+ try:
+ query = _files(f"{__package__}") / file
+ globals()[name] = query.read_text()
+ except FileNotFoundError:
+ globals()[name] = None
+ return globals()[name]
+
+
+def __getattr__(name):
+ if name == "HIGHLIGHTS_QUERY":
+ return _get_query("HIGHLIGHTS_QUERY", "queries/highlights.scm")
+ if name == "INJECTIONS_QUERY":
+ return _get_query("INJECTIONS_QUERY", "queries/injections.scm")
+ if name == "LOCALS_QUERY":
+ return _get_query("LOCALS_QUERY", "queries/locals.scm")
+ if name == "TAGS_QUERY":
+ return _get_query("TAGS_QUERY", "queries/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_wmap/__init__.pyi b/bindings/python/tree_sitter_wmap/__init__.pyi
new file mode 100644
index 0000000..5c88ff6
--- /dev/null
+++ b/bindings/python/tree_sitter_wmap/__init__.pyi
@@ -0,0 +1,17 @@
+from typing import Final
+from typing_extensions import CapsuleType
+
+HIGHLIGHTS_QUERY: Final[str] | None
+"""The syntax highlighting query for this grammar."""
+
+INJECTIONS_QUERY: Final[str] | None
+"""The language injection query for this grammar."""
+
+LOCALS_QUERY: Final[str] | None
+"""The local variable query for this grammar."""
+
+TAGS_QUERY: Final[str] | None
+"""The symbol tagging query for this grammar."""
+
+def language() -> CapsuleType:
+ """The tree-sitter language function for this grammar."""
diff --git a/bindings/python/tree_sitter_wmap/binding.c b/bindings/python/tree_sitter_wmap/binding.c
new file mode 100644
index 0000000..bbc4012
--- /dev/null
+++ b/bindings/python/tree_sitter_wmap/binding.c
@@ -0,0 +1,35 @@
+#include <Python.h>
+
+typedef struct TSLanguage TSLanguage;
+
+TSLanguage *tree_sitter_wmap(void);
+
+static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
+ return PyCapsule_New(tree_sitter_wmap(), "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_wmap/py.typed b/bindings/python/tree_sitter_wmap/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bindings/python/tree_sitter_wmap/py.typed
diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs
new file mode 100644
index 0000000..545b483
--- /dev/null
+++ b/bindings/rust/build.rs
@@ -0,0 +1,56 @@
+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");
+
+ if std::env::var("TARGET").unwrap() == "wasm32-unknown-unknown" {
+ let Ok(wasm_headers) = std::env::var("DEP_TREE_SITTER_LANGUAGE_WASM_HEADERS") else {
+ panic!("Environment variable DEP_TREE_SITTER_LANGUAGE_WASM_HEADERS must be set by the language crate");
+ };
+ let Ok(wasm_src) =
+ std::env::var("DEP_TREE_SITTER_LANGUAGE_WASM_SRC").map(std::path::PathBuf::from)
+ else {
+ panic!("Environment variable DEP_TREE_SITTER_LANGUAGE_WASM_SRC must be set by the language crate");
+ };
+
+ c_config.include(&wasm_headers);
+ c_config.files([
+ wasm_src.join("stdio.c"),
+ wasm_src.join("stdlib.c"),
+ wasm_src.join("string.c"),
+ ]);
+ }
+
+ 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-wmap");
+
+ println!("cargo:rustc-check-cfg=cfg(with_highlights_query)");
+ if !"queries/highlights.scm".is_empty() && std::path::Path::new("queries/highlights.scm").exists() {
+ println!("cargo:rustc-cfg=with_highlights_query");
+ }
+ println!("cargo:rustc-check-cfg=cfg(with_injections_query)");
+ if !"queries/injections.scm".is_empty() && std::path::Path::new("queries/injections.scm").exists() {
+ println!("cargo:rustc-cfg=with_injections_query");
+ }
+ println!("cargo:rustc-check-cfg=cfg(with_locals_query)");
+ if !"queries/locals.scm".is_empty() && std::path::Path::new("queries/locals.scm").exists() {
+ println!("cargo:rustc-cfg=with_locals_query");
+ }
+ println!("cargo:rustc-check-cfg=cfg(with_tags_query)");
+ if !"queries/tags.scm".is_empty() && std::path::Path::new("queries/tags.scm").exists() {
+ println!("cargo:rustc-cfg=with_tags_query");
+ }
+}
diff --git a/bindings/rust/lib.rs b/bindings/rust/lib.rs
new file mode 100644
index 0000000..3517b5f
--- /dev/null
+++ b/bindings/rust/lib.rs
@@ -0,0 +1,60 @@
+//! This crate provides Wmap 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_wmap::LANGUAGE;
+//! parser
+//! .set_language(&language.into())
+//! .expect("Error loading Wmap parser");
+//! let tree = parser.parse(code, None).unwrap();
+//! assert!(!tree.root_node().has_error());
+//! ```
+//!
+//! [`Parser`]: https://docs.rs/tree-sitter/0.26.3/tree_sitter/struct.Parser.html
+//! [tree-sitter]: https://tree-sitter.github.io/
+
+use tree_sitter_language::LanguageFn;
+
+extern "C" {
+ fn tree_sitter_wmap() -> *const ();
+}
+
+/// The tree-sitter [`LanguageFn`] for this grammar.
+pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_wmap) };
+
+/// 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");
+
+#[cfg(with_highlights_query)]
+/// The syntax highlighting query for this grammar.
+pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
+
+#[cfg(with_injections_query)]
+/// The language injection query for this grammar.
+pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm");
+
+#[cfg(with_locals_query)]
+/// The local variable query for this grammar.
+pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm");
+
+#[cfg(with_tags_query)]
+/// The symbol tagging query for this grammar.
+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 Wmap parser");
+ }
+}
diff --git a/bindings/swift/TreeSitterWmap/wmap.h b/bindings/swift/TreeSitterWmap/wmap.h
new file mode 100644
index 0000000..b477b35
--- /dev/null
+++ b/bindings/swift/TreeSitterWmap/wmap.h
@@ -0,0 +1,16 @@
+#ifndef TREE_SITTER_WMAP_H_
+#define TREE_SITTER_WMAP_H_
+
+typedef struct TSLanguage TSLanguage;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const TSLanguage *tree_sitter_wmap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_SITTER_WMAP_H_
diff --git a/bindings/swift/TreeSitterWmapTests/TreeSitterWmapTests.swift b/bindings/swift/TreeSitterWmapTests/TreeSitterWmapTests.swift
new file mode 100644
index 0000000..d596362
--- /dev/null
+++ b/bindings/swift/TreeSitterWmapTests/TreeSitterWmapTests.swift
@@ -0,0 +1,12 @@
+import XCTest
+import SwiftTreeSitter
+import TreeSitterWmap
+
+final class TreeSitterWmapTests: XCTestCase {
+ func testCanLoadGrammar() throws {
+ let parser = Parser()
+ let language = Language(language: tree_sitter_wmap())
+ XCTAssertNoThrow(try parser.setLanguage(language),
+ "Error loading Wmap grammar")
+ }
+}
diff --git a/example.wmap b/example.wmap
new file mode 100644
index 0000000..1e6098f
--- /dev/null
+++ b/example.wmap
@@ -0,0 +1,91 @@
+Anchor (40, 0)
+Tinker (4,30) [x]
+Tailor (7.03, 49.21) [Square]
+Soldier (28, 15) [Triangle]
+AMnchor (28.22, 45.27)
+Tonker (67.3, 4.4) [X]
+Tailor1 (1,1) [Square]
+Tailor2 (10,1) [Square]
+Tailor3 (1,10) [Square]
+Tailor4 (20,1) [Square]
+Tailor5 (1,20) [Square]
+Tailor7 (20,10) [Square]
+Tailor8 (9.36, 16.97) [Square]
+Tailor777(30,10) [Square]
+Tailor10 (18.77, 26.25) [Square]
+Tailor11 (10,10) [Square]
+Tailor12 (30,20)
+Tailor13 (31.10, 28.89)
+Tailor14 (13.44, 21.48) [Square]
+Tailor19 (45, 2) [triangle]
+B (50, 50)
+
+TailorX (20, 50) [Square]
+
+Hello World (90, 20) [x]
+
+Anchor -> Soldier… this is changing.
+Anchor -> A
+Masdlajksd -- Tailor13
+
+Mabanchorn -- Anchornalsdjalks
+
+X (58.52, 27.07)
+
+Tailor11--Tailor12
+Tailor12--Tailor13
+Tailor777--Tailor8
+Tailor777--Tailor7
+Tailor777--Tailor6
+
+Tinker -- Tailor
+Soldier -- Tinker
+
+[Evolution] Tinker +20
+[Inertia] Soldier
+
+[i] 15
+[ii] 35
+[iii] 80
+
+we have deleted this (20, 40) [triangle]
+
+a(12, 12)
+
+b(75,75)
+c(9, 90)
+
+a -- b
+b -- c
+
+d (45, 88)
+
+c -- d
+d -- a
+d -- b
+
+q (90, 60)
+b -- q
+q -- a
+q -- c
+
+[Evolution] c +20
+[Evolution] d +10
+[Evolution] a -20
+[Evolution] q +5
+
+m(50, 60)
+n (65, 65)
+o (45, 63)
+
+m -- o
+o -- n
+n -- m
+o -- c
+n -- q
+m -- d
+
+[Note] (5, 35) Notes also influence the placement of\n labels.
+
+[Group] Tailor2, Tailor7, Tailor11, Tailor12, Tailor10, Anchor, Tinker
+[Group] Tailor3, Tailor5, Tailor14pr
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..02ddae4
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module git.sr.ht/~rbdr/tree-sitter-wmap
+
+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..e35473f
--- /dev/null
+++ b/grammar.js
@@ -0,0 +1,133 @@
+/**
+ * @file Parser for wmap Formatted Wardley Maps
+ * @author Rubén Beltrán del Río <wmap.parser@r.bdr.sh>
+ * @license AGPL-v3
+ */
+
+/// <reference types="tree-sitter-cli/dsl" />
+// @ts-check
+
+export default grammar({
+ name: "wmap",
+
+ extras: $ => [/[ \t]/],
+
+ rules: {
+ source_file: $ => repeat(
+ choice(
+ seq($.entity, $._line_ending),
+ $._line_ending
+ )
+ ),
+
+ entity: $ => choice(
+ $.component,
+ $.dependency,
+ $.note,
+ $.stage,
+ $.group,
+ $.inertia,
+ $.evolution,
+ ),
+
+ // Entities
+ component: $ => seq(
+ field('label', $.component_label),
+ field('position', $.position),
+ optional(field('shape', $.shape))
+ ),
+
+ dependency: $ => seq(
+ field('from', $.component_label),
+ field('type', $.dependency_type),
+ field('to', $.component_label)
+ ),
+
+ note: $ => seq(
+ alias($._note_keyword, 'keyword'),
+ field('position', $.position),
+ field('text', $.text)
+ ),
+
+ stage: $ => seq(
+ '[',
+ field('number', $.stage_number),
+ ']',
+ field('value', $.real_number)
+ ),
+
+ group: $ => seq(
+ alias($._group_keyword, 'keyword'),
+ field('component', $.component_label),
+ repeat(seq(',', field('component', $.component_label)))
+ ),
+
+ inertia: $ => seq(
+ alias($._inertia_keyword, 'keyword'),
+ field('component', $.component_label)
+ ),
+
+ evolution: $ => seq(
+ alias($._evolution_keyword, 'keyword'),
+ field('component', $.component_label),
+ field('sign', $.sign),
+ field('value', $.real_number)
+ ),
+
+ // Component definitions
+ position: $ => seq(
+ '(',
+ field('x', $.real_number),
+ ',',
+ field('y', $.real_number),
+ ')'
+ ),
+
+ shape: $ => seq(
+ '[',
+ $.shape_label,
+ ']'
+ ),
+
+ dependency_type: $ => choice('--', '->'),
+
+ sign: $ => choice('+', '-'),
+
+ component_label: $ => /[^\-+,\[\]()\n\r]+/,
+
+ text: $ => /[^\n\r]+/,
+
+ real_number: $ => choice(
+ /[0-9]+\.[0-9]*/,
+ /[0-9]+/,
+ /\.[0-9]+/
+ ),
+
+ stage_number: $ => choice(
+ /[Ii][Vv]/, // IV (must come before single I)
+ /[Ii][Ii][Ii]/, // III
+ /[Ii][Ii]/, // II
+ /[Ii]/ // I
+ ),
+
+ shape_label: $ => choice(
+ /[Xx]/,
+ /[Ss][Qq][Uu][Aa][Rr][Ee]/,
+ /[Tt][Rr][Ii][Aa][Nn][Gg][Ll][Ee]/,
+ /[Cc][Ii][Rr][Cc][Ll][Ee]/
+ ),
+
+ // Keywords (case-insensitive)
+ _note_keyword: $ => /\[[Nn][Oo][Tt][Ee]\]/,
+ _group_keyword: $ => /\[[Gg][Rr][Oo][Uu][Pp]\]/,
+ _inertia_keyword: $ => /\[[Ii][Nn][Ee][Rr][Tt][Ii][Aa]\]/,
+ _evolution_keyword: $ => /\[[Ee][Vv][Oo][Ll][Uu][Tt][Ii][Oo][Nn]\]/,
+
+ // Line endings (Unix LF, Windows CRLF, classic Mac CR)
+ _line_ending: $ => choice(
+ '\n', // LF
+ '\r\n', // CRLF
+ '\r' // CR
+ ),
+ }
+});
diff --git a/lua/tree-sitter-wmap.lua b/lua/tree-sitter-wmap.lua
new file mode 100644
index 0000000..7c4a53d
--- /dev/null
+++ b/lua/tree-sitter-wmap.lua
@@ -0,0 +1,21 @@
+local M = {}
+
+function M.setup()
+ local config_file_path = debug.getinfo(1).source:sub(2)
+ local plugin_dir = vim.fn.fnamemodify(config_file_path, ':h:h')
+ local parser_config = require 'nvim-treesitter.parsers'.get_parser_configs()
+ parser_config.wmap = {
+ install_info = {
+ url = plugin_dir,
+ files = {'src/parser.c'},
+ branch = 'main'
+ }
+ }
+ vim.filetype.add({
+ extension = {
+ wmap = 'wmap',
+ }
+ })
+end
+
+return M
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ce20f94
--- /dev/null
+++ b/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "tree-sitter-wmap",
+ "version": "1.0.0",
+ "description": "Parser for wmap Formatted Wardley Maps",
+ "type": "module",
+ "repository": {
+ "type": "git",
+ "url": "git+https://git.sr.ht/~rbdr/tree-sitter-wmap.git"
+ },
+ "funding": "",
+ "license": "AGPL-v3",
+ "author": {
+ "name": "Rubén Beltrán del Río",
+ "email": "wmap.parser@r.bdr.sh",
+ "url": "https://r.bdr.sh"
+ },
+ "main": "bindings/node",
+ "types": "bindings/node",
+ "keywords": [
+ "incremental",
+ "parsing",
+ "tree-sitter",
+ "wmap"
+ ],
+ "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.25.0",
+ "tree-sitter-cli": "^0.26.3"
+ },
+ "peerDependencies": {
+ "tree-sitter": "^0.25.0"
+ },
+ "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/plugin/tree-sitter-wmap.lua b/plugin/tree-sitter-wmap.lua
new file mode 100644
index 0000000..c8781d5
--- /dev/null
+++ b/plugin/tree-sitter-wmap.lua
@@ -0,0 +1 @@
+require('tree-sitter-wmap').setup()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b9c7427
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,30 @@
+[build-system]
+requires = ["setuptools>=62.4.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "tree-sitter-wmap"
+description = "Parser for wmap Formatted Wardley Maps"
+version = "1.0.0"
+keywords = ["incremental", "parsing", "tree-sitter", "wmap"]
+classifiers = [
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Compilers",
+ "Topic :: Text Processing :: Linguistic",
+ "Typing :: Typed",
+]
+authors = [{ name = "Rubén Beltrán del Río", email = "wmap.parser@r.bdr.sh" }]
+requires-python = ">=3.10"
+license.text = "AGPL-v3"
+readme = "README.md"
+
+[project.urls]
+Homepage = "https://git.sr.ht/~rbdr/tree-sitter-wmap"
+Funding = ""
+
+[project.optional-dependencies]
+core = ["tree-sitter~=0.24"]
+
+[tool.cibuildwheel]
+build = "cp310-*"
+build-frontend = "build"
diff --git a/queries/highlights.scm b/queries/highlights.scm
new file mode 100644
index 0000000..c695f73
--- /dev/null
+++ b/queries/highlights.scm
@@ -0,0 +1,14 @@
+(component_label) @variable.component_label
+(position) @punctuation.position
+(real_number) @number.real_number
+(shape) @punctuation.shape
+(shape_label) @parameter.optional
+(dependency_type) @punctuation.dependency_type
+(evolution) @keyword.evolution
+(sign) @operator.sign
+(inertia) @keyword.inertia
+(stage) @keyword.stage
+(stage_number) @keyword.stage_number
+(note) @keyword.note
+(text) @string.text
+(group) @keyword.group
diff --git a/queries/wmap/highlights.scm b/queries/wmap/highlights.scm
new file mode 120000
index 0000000..8365d50
--- /dev/null
+++ b/queries/wmap/highlights.scm
@@ -0,0 +1 @@
+../highlights.scm \ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..47b6a3e
--- /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_wmap", "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_wmap": ["*.pyi", "py.typed"],
+ "tree_sitter_wmap.queries": ["*.scm"],
+ },
+ ext_package="tree_sitter_wmap",
+ ext_modules=[
+ Extension(
+ name="_binding",
+ sources=[
+ "bindings/python/tree_sitter_wmap/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/src/grammar.json b/src/grammar.json
new file mode 100644
index 0000000..de08260
--- /dev/null
+++ b/src/grammar.json
@@ -0,0 +1,479 @@
+{
+ "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/grammar.schema.json",
+ "name": "wmap",
+ "rules": {
+ "source_file": {
+ "type": "REPEAT",
+ "content": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "SYMBOL",
+ "name": "entity"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "_line_ending"
+ }
+ ]
+ },
+ {
+ "type": "SYMBOL",
+ "name": "_line_ending"
+ }
+ ]
+ }
+ },
+ "entity": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "SYMBOL",
+ "name": "component"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "dependency"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "note"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "stage"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "group"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "inertia"
+ },
+ {
+ "type": "SYMBOL",
+ "name": "evolution"
+ }
+ ]
+ },
+ "component": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "FIELD",
+ "name": "label",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "position",
+ "content": {
+ "type": "SYMBOL",
+ "name": "position"
+ }
+ },
+ {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "FIELD",
+ "name": "shape",
+ "content": {
+ "type": "SYMBOL",
+ "name": "shape"
+ }
+ },
+ {
+ "type": "BLANK"
+ }
+ ]
+ }
+ ]
+ },
+ "dependency": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "FIELD",
+ "name": "from",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "type",
+ "content": {
+ "type": "SYMBOL",
+ "name": "dependency_type"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "to",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ }
+ ]
+ },
+ "note": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "ALIAS",
+ "content": {
+ "type": "SYMBOL",
+ "name": "_note_keyword"
+ },
+ "named": false,
+ "value": "keyword"
+ },
+ {
+ "type": "FIELD",
+ "name": "position",
+ "content": {
+ "type": "SYMBOL",
+ "name": "position"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "text",
+ "content": {
+ "type": "SYMBOL",
+ "name": "text"
+ }
+ }
+ ]
+ },
+ "stage": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "["
+ },
+ {
+ "type": "FIELD",
+ "name": "number",
+ "content": {
+ "type": "SYMBOL",
+ "name": "stage_number"
+ }
+ },
+ {
+ "type": "STRING",
+ "value": "]"
+ },
+ {
+ "type": "FIELD",
+ "name": "value",
+ "content": {
+ "type": "SYMBOL",
+ "name": "real_number"
+ }
+ }
+ ]
+ },
+ "group": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "ALIAS",
+ "content": {
+ "type": "SYMBOL",
+ "name": "_group_keyword"
+ },
+ "named": false,
+ "value": "keyword"
+ },
+ {
+ "type": "FIELD",
+ "name": "component",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ },
+ {
+ "type": "REPEAT",
+ "content": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "STRING",
+ "value": ","
+ },
+ {
+ "type": "FIELD",
+ "name": "component",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "inertia": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "ALIAS",
+ "content": {
+ "type": "SYMBOL",
+ "name": "_inertia_keyword"
+ },
+ "named": false,
+ "value": "keyword"
+ },
+ {
+ "type": "FIELD",
+ "name": "component",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ }
+ ]
+ },
+ "evolution": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "ALIAS",
+ "content": {
+ "type": "SYMBOL",
+ "name": "_evolution_keyword"
+ },
+ "named": false,
+ "value": "keyword"
+ },
+ {
+ "type": "FIELD",
+ "name": "component",
+ "content": {
+ "type": "SYMBOL",
+ "name": "component_label"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "sign",
+ "content": {
+ "type": "SYMBOL",
+ "name": "sign"
+ }
+ },
+ {
+ "type": "FIELD",
+ "name": "value",
+ "content": {
+ "type": "SYMBOL",
+ "name": "real_number"
+ }
+ }
+ ]
+ },
+ "position": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "("
+ },
+ {
+ "type": "FIELD",
+ "name": "x",
+ "content": {
+ "type": "SYMBOL",
+ "name": "real_number"
+ }
+ },
+ {
+ "type": "STRING",
+ "value": ","
+ },
+ {
+ "type": "FIELD",
+ "name": "y",
+ "content": {
+ "type": "SYMBOL",
+ "name": "real_number"
+ }
+ },
+ {
+ "type": "STRING",
+ "value": ")"
+ }
+ ]
+ },
+ "shape": {
+ "type": "SEQ",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "["
+ },
+ {
+ "type": "SYMBOL",
+ "name": "shape_label"
+ },
+ {
+ "type": "STRING",
+ "value": "]"
+ }
+ ]
+ },
+ "dependency_type": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "--"
+ },
+ {
+ "type": "STRING",
+ "value": "->"
+ }
+ ]
+ },
+ "sign": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "+"
+ },
+ {
+ "type": "STRING",
+ "value": "-"
+ }
+ ]
+ },
+ "component_label": {
+ "type": "PATTERN",
+ "value": "[^\\-+,\\[\\]()\\n\\r]+"
+ },
+ "text": {
+ "type": "PATTERN",
+ "value": "[^\\n\\r]+"
+ },
+ "real_number": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "PATTERN",
+ "value": "[0-9]+\\.[0-9]*"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[0-9]+"
+ },
+ {
+ "type": "PATTERN",
+ "value": "\\.[0-9]+"
+ }
+ ]
+ },
+ "stage_number": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "PATTERN",
+ "value": "[Ii][Vv]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Ii][Ii][Ii]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Ii][Ii]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Ii]"
+ }
+ ]
+ },
+ "shape_label": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "PATTERN",
+ "value": "[Xx]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Ss][Qq][Uu][Aa][Rr][Ee]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Tt][Rr][Ii][Aa][Nn][Gg][Ll][Ee]"
+ },
+ {
+ "type": "PATTERN",
+ "value": "[Cc][Ii][Rr][Cc][Ll][Ee]"
+ }
+ ]
+ },
+ "_note_keyword": {
+ "type": "PATTERN",
+ "value": "\\[[Nn][Oo][Tt][Ee]\\]"
+ },
+ "_group_keyword": {
+ "type": "PATTERN",
+ "value": "\\[[Gg][Rr][Oo][Uu][Pp]\\]"
+ },
+ "_inertia_keyword": {
+ "type": "PATTERN",
+ "value": "\\[[Ii][Nn][Ee][Rr][Tt][Ii][Aa]\\]"
+ },
+ "_evolution_keyword": {
+ "type": "PATTERN",
+ "value": "\\[[Ee][Vv][Oo][Ll][Uu][Tt][Ii][Oo][Nn]\\]"
+ },
+ "_line_ending": {
+ "type": "CHOICE",
+ "members": [
+ {
+ "type": "STRING",
+ "value": "\n"
+ },
+ {
+ "type": "STRING",
+ "value": "\r\n"
+ },
+ {
+ "type": "STRING",
+ "value": "\r"
+ }
+ ]
+ }
+ },
+ "extras": [
+ {
+ "type": "PATTERN",
+ "value": "[ \\t]"
+ }
+ ],
+ "conflicts": [],
+ "precedences": [],
+ "externals": [],
+ "inline": [],
+ "supertypes": [],
+ "reserved": {}
+} \ No newline at end of file
diff --git a/src/node-types.json b/src/node-types.json
new file mode 100644
index 0000000..c328029
--- /dev/null
+++ b/src/node-types.json
@@ -0,0 +1,375 @@
+[
+ {
+ "type": "component",
+ "named": true,
+ "fields": {
+ "label": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ },
+ "position": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "position",
+ "named": true
+ }
+ ]
+ },
+ "shape": {
+ "multiple": false,
+ "required": false,
+ "types": [
+ {
+ "type": "shape",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "dependency",
+ "named": true,
+ "fields": {
+ "from": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ },
+ "to": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ },
+ "type": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "dependency_type",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "dependency_type",
+ "named": true,
+ "fields": {}
+ },
+ {
+ "type": "entity",
+ "named": true,
+ "fields": {},
+ "children": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component",
+ "named": true
+ },
+ {
+ "type": "dependency",
+ "named": true
+ },
+ {
+ "type": "evolution",
+ "named": true
+ },
+ {
+ "type": "group",
+ "named": true
+ },
+ {
+ "type": "inertia",
+ "named": true
+ },
+ {
+ "type": "note",
+ "named": true
+ },
+ {
+ "type": "stage",
+ "named": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "evolution",
+ "named": true,
+ "fields": {
+ "component": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ },
+ "sign": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "sign",
+ "named": true
+ }
+ ]
+ },
+ "value": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "real_number",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "group",
+ "named": true,
+ "fields": {
+ "component": {
+ "multiple": true,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "inertia",
+ "named": true,
+ "fields": {
+ "component": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "component_label",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "note",
+ "named": true,
+ "fields": {
+ "position": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "position",
+ "named": true
+ }
+ ]
+ },
+ "text": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "text",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "position",
+ "named": true,
+ "fields": {
+ "x": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "real_number",
+ "named": true
+ }
+ ]
+ },
+ "y": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "real_number",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "real_number",
+ "named": true,
+ "fields": {}
+ },
+ {
+ "type": "shape",
+ "named": true,
+ "fields": {},
+ "children": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "shape_label",
+ "named": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "shape_label",
+ "named": true,
+ "fields": {}
+ },
+ {
+ "type": "sign",
+ "named": true,
+ "fields": {}
+ },
+ {
+ "type": "source_file",
+ "named": true,
+ "root": true,
+ "fields": {},
+ "children": {
+ "multiple": true,
+ "required": false,
+ "types": [
+ {
+ "type": "entity",
+ "named": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "stage",
+ "named": true,
+ "fields": {
+ "number": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "stage_number",
+ "named": true
+ }
+ ]
+ },
+ "value": {
+ "multiple": false,
+ "required": true,
+ "types": [
+ {
+ "type": "real_number",
+ "named": true
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "stage_number",
+ "named": true,
+ "fields": {}
+ },
+ {
+ "type": "\n",
+ "named": false
+ },
+ {
+ "type": "\r",
+ "named": false
+ },
+ {
+ "type": "\r\n",
+ "named": false
+ },
+ {
+ "type": "(",
+ "named": false
+ },
+ {
+ "type": ")",
+ "named": false
+ },
+ {
+ "type": "+",
+ "named": false
+ },
+ {
+ "type": ",",
+ "named": false
+ },
+ {
+ "type": "-",
+ "named": false
+ },
+ {
+ "type": "--",
+ "named": false
+ },
+ {
+ "type": "->",
+ "named": false
+ },
+ {
+ "type": "[",
+ "named": false
+ },
+ {
+ "type": "]",
+ "named": false
+ },
+ {
+ "type": "component_label",
+ "named": true
+ },
+ {
+ "type": "keyword",
+ "named": false
+ },
+ {
+ "type": "text",
+ "named": true
+ }
+] \ No newline at end of file
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..71363fc
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,1553 @@
+/* Automatically @generated by tree-sitter */
+
+#include "tree_sitter/parser.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+#define LANGUAGE_VERSION 15
+#define STATE_COUNT 50
+#define LARGE_STATE_COUNT 2
+#define SYMBOL_COUNT 49
+#define ALIAS_COUNT 0
+#define TOKEN_COUNT 30
+#define EXTERNAL_TOKEN_COUNT 0
+#define FIELD_COUNT 13
+#define MAX_ALIAS_SEQUENCE_LENGTH 5
+#define MAX_RESERVED_WORD_SET_SIZE 0
+#define PRODUCTION_ID_COUNT 11
+#define SUPERTYPE_COUNT 0
+
+enum ts_symbol_identifiers {
+ anon_sym_LBRACK = 1,
+ anon_sym_RBRACK = 2,
+ anon_sym_COMMA = 3,
+ anon_sym_LPAREN = 4,
+ anon_sym_RPAREN = 5,
+ anon_sym_DASH_DASH = 6,
+ anon_sym_DASH_GT = 7,
+ anon_sym_PLUS = 8,
+ anon_sym_DASH = 9,
+ sym_component_label = 10,
+ sym_text = 11,
+ aux_sym_real_number_token1 = 12,
+ aux_sym_real_number_token2 = 13,
+ aux_sym_real_number_token3 = 14,
+ aux_sym_stage_number_token1 = 15,
+ aux_sym_stage_number_token2 = 16,
+ aux_sym_stage_number_token3 = 17,
+ aux_sym_stage_number_token4 = 18,
+ aux_sym_shape_label_token1 = 19,
+ aux_sym_shape_label_token2 = 20,
+ aux_sym_shape_label_token3 = 21,
+ aux_sym_shape_label_token4 = 22,
+ sym__note_keyword = 23,
+ sym__group_keyword = 24,
+ sym__inertia_keyword = 25,
+ sym__evolution_keyword = 26,
+ anon_sym_LF = 27,
+ anon_sym_CR_LF = 28,
+ anon_sym_CR = 29,
+ sym_source_file = 30,
+ sym_entity = 31,
+ sym_component = 32,
+ sym_dependency = 33,
+ sym_note = 34,
+ sym_stage = 35,
+ sym_group = 36,
+ sym_inertia = 37,
+ sym_evolution = 38,
+ sym_position = 39,
+ sym_shape = 40,
+ sym_dependency_type = 41,
+ sym_sign = 42,
+ sym_real_number = 43,
+ sym_stage_number = 44,
+ sym_shape_label = 45,
+ sym__line_ending = 46,
+ aux_sym_source_file_repeat1 = 47,
+ aux_sym_group_repeat1 = 48,
+};
+
+static const char * const ts_symbol_names[] = {
+ [ts_builtin_sym_end] = "end",
+ [anon_sym_LBRACK] = "[",
+ [anon_sym_RBRACK] = "]",
+ [anon_sym_COMMA] = ",",
+ [anon_sym_LPAREN] = "(",
+ [anon_sym_RPAREN] = ")",
+ [anon_sym_DASH_DASH] = "--",
+ [anon_sym_DASH_GT] = "->",
+ [anon_sym_PLUS] = "+",
+ [anon_sym_DASH] = "-",
+ [sym_component_label] = "component_label",
+ [sym_text] = "text",
+ [aux_sym_real_number_token1] = "real_number_token1",
+ [aux_sym_real_number_token2] = "real_number_token2",
+ [aux_sym_real_number_token3] = "real_number_token3",
+ [aux_sym_stage_number_token1] = "stage_number_token1",
+ [aux_sym_stage_number_token2] = "stage_number_token2",
+ [aux_sym_stage_number_token3] = "stage_number_token3",
+ [aux_sym_stage_number_token4] = "stage_number_token4",
+ [aux_sym_shape_label_token1] = "shape_label_token1",
+ [aux_sym_shape_label_token2] = "shape_label_token2",
+ [aux_sym_shape_label_token3] = "shape_label_token3",
+ [aux_sym_shape_label_token4] = "shape_label_token4",
+ [sym__note_keyword] = "keyword",
+ [sym__group_keyword] = "keyword",
+ [sym__inertia_keyword] = "keyword",
+ [sym__evolution_keyword] = "keyword",
+ [anon_sym_LF] = "\n",
+ [anon_sym_CR_LF] = "\r\n",
+ [anon_sym_CR] = "\r",
+ [sym_source_file] = "source_file",
+ [sym_entity] = "entity",
+ [sym_component] = "component",
+ [sym_dependency] = "dependency",
+ [sym_note] = "note",
+ [sym_stage] = "stage",
+ [sym_group] = "group",
+ [sym_inertia] = "inertia",
+ [sym_evolution] = "evolution",
+ [sym_position] = "position",
+ [sym_shape] = "shape",
+ [sym_dependency_type] = "dependency_type",
+ [sym_sign] = "sign",
+ [sym_real_number] = "real_number",
+ [sym_stage_number] = "stage_number",
+ [sym_shape_label] = "shape_label",
+ [sym__line_ending] = "_line_ending",
+ [aux_sym_source_file_repeat1] = "source_file_repeat1",
+ [aux_sym_group_repeat1] = "group_repeat1",
+};
+
+static const TSSymbol ts_symbol_map[] = {
+ [ts_builtin_sym_end] = ts_builtin_sym_end,
+ [anon_sym_LBRACK] = anon_sym_LBRACK,
+ [anon_sym_RBRACK] = anon_sym_RBRACK,
+ [anon_sym_COMMA] = anon_sym_COMMA,
+ [anon_sym_LPAREN] = anon_sym_LPAREN,
+ [anon_sym_RPAREN] = anon_sym_RPAREN,
+ [anon_sym_DASH_DASH] = anon_sym_DASH_DASH,
+ [anon_sym_DASH_GT] = anon_sym_DASH_GT,
+ [anon_sym_PLUS] = anon_sym_PLUS,
+ [anon_sym_DASH] = anon_sym_DASH,
+ [sym_component_label] = sym_component_label,
+ [sym_text] = sym_text,
+ [aux_sym_real_number_token1] = aux_sym_real_number_token1,
+ [aux_sym_real_number_token2] = aux_sym_real_number_token2,
+ [aux_sym_real_number_token3] = aux_sym_real_number_token3,
+ [aux_sym_stage_number_token1] = aux_sym_stage_number_token1,
+ [aux_sym_stage_number_token2] = aux_sym_stage_number_token2,
+ [aux_sym_stage_number_token3] = aux_sym_stage_number_token3,
+ [aux_sym_stage_number_token4] = aux_sym_stage_number_token4,
+ [aux_sym_shape_label_token1] = aux_sym_shape_label_token1,
+ [aux_sym_shape_label_token2] = aux_sym_shape_label_token2,
+ [aux_sym_shape_label_token3] = aux_sym_shape_label_token3,
+ [aux_sym_shape_label_token4] = aux_sym_shape_label_token4,
+ [sym__note_keyword] = sym__note_keyword,
+ [sym__group_keyword] = sym__note_keyword,
+ [sym__inertia_keyword] = sym__note_keyword,
+ [sym__evolution_keyword] = sym__note_keyword,
+ [anon_sym_LF] = anon_sym_LF,
+ [anon_sym_CR_LF] = anon_sym_CR_LF,
+ [anon_sym_CR] = anon_sym_CR,
+ [sym_source_file] = sym_source_file,
+ [sym_entity] = sym_entity,
+ [sym_component] = sym_component,
+ [sym_dependency] = sym_dependency,
+ [sym_note] = sym_note,
+ [sym_stage] = sym_stage,
+ [sym_group] = sym_group,
+ [sym_inertia] = sym_inertia,
+ [sym_evolution] = sym_evolution,
+ [sym_position] = sym_position,
+ [sym_shape] = sym_shape,
+ [sym_dependency_type] = sym_dependency_type,
+ [sym_sign] = sym_sign,
+ [sym_real_number] = sym_real_number,
+ [sym_stage_number] = sym_stage_number,
+ [sym_shape_label] = sym_shape_label,
+ [sym__line_ending] = sym__line_ending,
+ [aux_sym_source_file_repeat1] = aux_sym_source_file_repeat1,
+ [aux_sym_group_repeat1] = aux_sym_group_repeat1,
+};
+
+static const TSSymbolMetadata ts_symbol_metadata[] = {
+ [ts_builtin_sym_end] = {
+ .visible = false,
+ .named = true,
+ },
+ [anon_sym_LBRACK] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_RBRACK] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_COMMA] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_LPAREN] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_RPAREN] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_DASH_DASH] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_DASH_GT] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_PLUS] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_DASH] = {
+ .visible = true,
+ .named = false,
+ },
+ [sym_component_label] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_text] = {
+ .visible = true,
+ .named = true,
+ },
+ [aux_sym_real_number_token1] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_real_number_token2] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_real_number_token3] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_stage_number_token1] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_stage_number_token2] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_stage_number_token3] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_stage_number_token4] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_shape_label_token1] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_shape_label_token2] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_shape_label_token3] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_shape_label_token4] = {
+ .visible = false,
+ .named = false,
+ },
+ [sym__note_keyword] = {
+ .visible = true,
+ .named = false,
+ },
+ [sym__group_keyword] = {
+ .visible = true,
+ .named = false,
+ },
+ [sym__inertia_keyword] = {
+ .visible = true,
+ .named = false,
+ },
+ [sym__evolution_keyword] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_LF] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_CR_LF] = {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_CR] = {
+ .visible = true,
+ .named = false,
+ },
+ [sym_source_file] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_entity] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_component] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_dependency] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_note] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_stage] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_group] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_inertia] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_evolution] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_position] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_shape] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_dependency_type] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_sign] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_real_number] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_stage_number] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym_shape_label] = {
+ .visible = true,
+ .named = true,
+ },
+ [sym__line_ending] = {
+ .visible = false,
+ .named = true,
+ },
+ [aux_sym_source_file_repeat1] = {
+ .visible = false,
+ .named = false,
+ },
+ [aux_sym_group_repeat1] = {
+ .visible = false,
+ .named = false,
+ },
+};
+
+enum ts_field_identifiers {
+ field_component = 1,
+ field_from = 2,
+ field_label = 3,
+ field_number = 4,
+ field_position = 5,
+ field_shape = 6,
+ field_sign = 7,
+ field_text = 8,
+ field_to = 9,
+ field_type = 10,
+ field_value = 11,
+ field_x = 12,
+ field_y = 13,
+};
+
+static const char * const ts_field_names[] = {
+ [0] = NULL,
+ [field_component] = "component",
+ [field_from] = "from",
+ [field_label] = "label",
+ [field_number] = "number",
+ [field_position] = "position",
+ [field_shape] = "shape",
+ [field_sign] = "sign",
+ [field_text] = "text",
+ [field_to] = "to",
+ [field_type] = "type",
+ [field_value] = "value",
+ [field_x] = "x",
+ [field_y] = "y",
+};
+
+static const TSMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = {
+ [1] = {.index = 0, .length = 2},
+ [2] = {.index = 2, .length = 1},
+ [3] = {.index = 3, .length = 3},
+ [4] = {.index = 6, .length = 3},
+ [5] = {.index = 9, .length = 2},
+ [6] = {.index = 11, .length = 2},
+ [7] = {.index = 13, .length = 2},
+ [8] = {.index = 15, .length = 2},
+ [9] = {.index = 17, .length = 3},
+ [10] = {.index = 20, .length = 2},
+};
+
+static const TSFieldMapEntry ts_field_map_entries[] = {
+ [0] =
+ {field_label, 0},
+ {field_position, 1},
+ [2] =
+ {field_component, 1},
+ [3] =
+ {field_label, 0},
+ {field_position, 1},
+ {field_shape, 2},
+ [6] =
+ {field_from, 0},
+ {field_to, 2},
+ {field_type, 1},
+ [9] =
+ {field_position, 1},
+ {field_text, 2},
+ [11] =
+ {field_component, 1},
+ {field_component, 2, .inherited = true},
+ [13] =
+ {field_number, 1},
+ {field_value, 3},
+ [15] =
+ {field_component, 0, .inherited = true},
+ {field_component, 1, .inherited = true},
+ [17] =
+ {field_component, 1},
+ {field_sign, 2},
+ {field_value, 3},
+ [20] =
+ {field_x, 1},
+ {field_y, 3},
+};
+
+static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = {
+ [0] = {0},
+};
+
+static const uint16_t ts_non_terminal_alias_map[] = {
+ 0,
+};
+
+static const TSStateId ts_primary_state_ids[STATE_COUNT] = {
+ [0] = 0,
+ [1] = 1,
+ [2] = 2,
+ [3] = 3,
+ [4] = 4,
+ [5] = 5,
+ [6] = 6,
+ [7] = 7,
+ [8] = 8,
+ [9] = 9,
+ [10] = 10,
+ [11] = 11,
+ [12] = 12,
+ [13] = 13,
+ [14] = 14,
+ [15] = 15,
+ [16] = 14,
+ [17] = 17,
+ [18] = 18,
+ [19] = 19,
+ [20] = 20,
+ [21] = 18,
+ [22] = 22,
+ [23] = 23,
+ [24] = 24,
+ [25] = 25,
+ [26] = 26,
+ [27] = 27,
+ [28] = 28,
+ [29] = 29,
+ [30] = 30,
+ [31] = 31,
+ [32] = 32,
+ [33] = 33,
+ [34] = 34,
+ [35] = 35,
+ [36] = 36,
+ [37] = 37,
+ [38] = 38,
+ [39] = 39,
+ [40] = 40,
+ [41] = 41,
+ [42] = 42,
+ [43] = 43,
+ [44] = 44,
+ [45] = 20,
+ [46] = 44,
+ [47] = 47,
+ [48] = 33,
+ [49] = 49,
+};
+
+static bool ts_lex(TSLexer *lexer, TSStateId state) {
+ START_LEXER();
+ eof = lexer->eof(lexer);
+ switch (state) {
+ case 0:
+ if (eof) ADVANCE(49);
+ ADVANCE_MAP(
+ '\n', 80,
+ '\r', 82,
+ '(', 54,
+ ')', 55,
+ '+', 58,
+ ',', 53,
+ '-', 60,
+ '.', 47,
+ '[', 51,
+ ']', 52,
+ );
+ if (lookahead == '\t' ||
+ lookahead == ' ') SKIP(0);
+ if (lookahead == 'C' ||
+ lookahead == 'c') ADVANCE(20);
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(71);
+ if (lookahead == 'S' ||
+ lookahead == 's') ADVANCE(34);
+ if (lookahead == 'T' ||
+ lookahead == 't') ADVANCE(36);
+ if (lookahead == 'X' ||
+ lookahead == 'x') ADVANCE(72);
+ if (('0' <= lookahead && lookahead <= '9')) ADVANCE(66);
+ END_STATE();
+ case 1:
+ if (lookahead == '\n') ADVANCE(80);
+ if (lookahead == '\r') ADVANCE(82);
+ if (lookahead == '(') ADVANCE(54);
+ if (lookahead == '-') ADVANCE(3);
+ if (lookahead == '[') ADVANCE(50);
+ if (lookahead == '\t' ||
+ lookahead == ' ') SKIP(1);
+ END_STATE();
+ case 2:
+ if (lookahead == '+') ADVANCE(58);
+ if (lookahead == '-') ADVANCE(59);
+ if (lookahead == '\t' ||
+ lookahead == ' ') SKIP(2);
+ END_STATE();
+ case 3:
+ if (lookahead == '-') ADVANCE(56);
+ if (lookahead == '>') ADVANCE(57);
+ END_STATE();
+ case 4:
+ if (lookahead == ']') ADVANCE(76);
+ END_STATE();
+ case 5:
+ if (lookahead == ']') ADVANCE(77);
+ END_STATE();
+ case 6:
+ if (lookahead == ']') ADVANCE(78);
+ END_STATE();
+ case 7:
+ if (lookahead == ']') ADVANCE(79);
+ END_STATE();
+ case 8:
+ if (lookahead == '\t' ||
+ lookahead == ' ') ADVANCE(63);
+ if (lookahead != 0 &&
+ lookahead != '\t' &&
+ lookahead != '\n' &&
+ lookahead != '\r') ADVANCE(64);
+ END_STATE();
+ case 9:
+ if (lookahead == 'A' ||
+ lookahead == 'a') ADVANCE(27);
+ END_STATE();
+ case 10:
+ if (lookahead == 'A' ||
+ lookahead == 'a') ADVANCE(6);
+ END_STATE();
+ case 11:
+ if (lookahead == 'A' ||
+ lookahead == 'a') ADVANCE(39);
+ END_STATE();
+ case 12:
+ if (lookahead == 'C' ||
+ lookahead == 'c') ADVANCE(24);
+ END_STATE();
+ case 13:
+ if (lookahead == 'E' ||
+ lookahead == 'e') ADVANCE(4);
+ END_STATE();
+ case 14:
+ if (lookahead == 'E' ||
+ lookahead == 'e') ADVANCE(75);
+ END_STATE();
+ case 15:
+ if (lookahead == 'E' ||
+ lookahead == 'e') ADVANCE(73);
+ END_STATE();
+ case 16:
+ if (lookahead == 'E' ||
+ lookahead == 'e') ADVANCE(74);
+ END_STATE();
+ case 17:
+ if (lookahead == 'E' ||
+ lookahead == 'e') ADVANCE(37);
+ END_STATE();
+ case 18:
+ if (lookahead == 'G' ||
+ lookahead == 'g') ADVANCE(25);
+ END_STATE();
+ case 19:
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(9);
+ END_STATE();
+ case 20:
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(35);
+ END_STATE();
+ case 21:
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(10);
+ END_STATE();
+ case 22:
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(32);
+ END_STATE();
+ case 23:
+ if (lookahead == 'L' ||
+ lookahead == 'l') ADVANCE(45);
+ END_STATE();
+ case 24:
+ if (lookahead == 'L' ||
+ lookahead == 'l') ADVANCE(14);
+ END_STATE();
+ case 25:
+ if (lookahead == 'L' ||
+ lookahead == 'l') ADVANCE(16);
+ END_STATE();
+ case 26:
+ if (lookahead == 'N' ||
+ lookahead == 'n') ADVANCE(17);
+ END_STATE();
+ case 27:
+ if (lookahead == 'N' ||
+ lookahead == 'n') ADVANCE(18);
+ END_STATE();
+ case 28:
+ if (lookahead == 'N' ||
+ lookahead == 'n') ADVANCE(7);
+ END_STATE();
+ case 29:
+ if (lookahead == 'O' ||
+ lookahead == 'o') ADVANCE(40);
+ END_STATE();
+ case 30:
+ if (lookahead == 'O' ||
+ lookahead == 'o') ADVANCE(23);
+ END_STATE();
+ case 31:
+ if (lookahead == 'O' ||
+ lookahead == 'o') ADVANCE(44);
+ END_STATE();
+ case 32:
+ if (lookahead == 'O' ||
+ lookahead == 'o') ADVANCE(28);
+ END_STATE();
+ case 33:
+ if (lookahead == 'P' ||
+ lookahead == 'p') ADVANCE(5);
+ END_STATE();
+ case 34:
+ if (lookahead == 'Q' ||
+ lookahead == 'q') ADVANCE(43);
+ END_STATE();
+ case 35:
+ if (lookahead == 'R' ||
+ lookahead == 'r') ADVANCE(12);
+ END_STATE();
+ case 36:
+ if (lookahead == 'R' ||
+ lookahead == 'r') ADVANCE(19);
+ END_STATE();
+ case 37:
+ if (lookahead == 'R' ||
+ lookahead == 'r') ADVANCE(42);
+ END_STATE();
+ case 38:
+ if (lookahead == 'R' ||
+ lookahead == 'r') ADVANCE(31);
+ END_STATE();
+ case 39:
+ if (lookahead == 'R' ||
+ lookahead == 'r') ADVANCE(15);
+ END_STATE();
+ case 40:
+ if (lookahead == 'T' ||
+ lookahead == 't') ADVANCE(13);
+ END_STATE();
+ case 41:
+ if (lookahead == 'T' ||
+ lookahead == 't') ADVANCE(22);
+ END_STATE();
+ case 42:
+ if (lookahead == 'T' ||
+ lookahead == 't') ADVANCE(21);
+ END_STATE();
+ case 43:
+ if (lookahead == 'U' ||
+ lookahead == 'u') ADVANCE(11);
+ END_STATE();
+ case 44:
+ if (lookahead == 'U' ||
+ lookahead == 'u') ADVANCE(33);
+ END_STATE();
+ case 45:
+ if (lookahead == 'U' ||
+ lookahead == 'u') ADVANCE(41);
+ END_STATE();
+ case 46:
+ if (lookahead == 'V' ||
+ lookahead == 'v') ADVANCE(30);
+ END_STATE();
+ case 47:
+ if (('0' <= lookahead && lookahead <= '9')) ADVANCE(67);
+ END_STATE();
+ case 48:
+ if (eof) ADVANCE(49);
+ if (lookahead == '\n') ADVANCE(80);
+ if (lookahead == '\r') ADVANCE(82);
+ if (lookahead == '[') ADVANCE(51);
+ if (lookahead == '\t' ||
+ lookahead == ' ') ADVANCE(61);
+ if (lookahead != 0 &&
+ lookahead != '(' &&
+ lookahead != ')' &&
+ (lookahead < '+' || '-' < lookahead) &&
+ lookahead != ']') ADVANCE(62);
+ END_STATE();
+ case 49:
+ ACCEPT_TOKEN(ts_builtin_sym_end);
+ END_STATE();
+ case 50:
+ ACCEPT_TOKEN(anon_sym_LBRACK);
+ END_STATE();
+ case 51:
+ ACCEPT_TOKEN(anon_sym_LBRACK);
+ ADVANCE_MAP(
+ 'E', 46,
+ 'e', 46,
+ 'G', 38,
+ 'g', 38,
+ 'I', 26,
+ 'i', 26,
+ 'N', 29,
+ 'n', 29,
+ );
+ END_STATE();
+ case 52:
+ ACCEPT_TOKEN(anon_sym_RBRACK);
+ END_STATE();
+ case 53:
+ ACCEPT_TOKEN(anon_sym_COMMA);
+ END_STATE();
+ case 54:
+ ACCEPT_TOKEN(anon_sym_LPAREN);
+ END_STATE();
+ case 55:
+ ACCEPT_TOKEN(anon_sym_RPAREN);
+ END_STATE();
+ case 56:
+ ACCEPT_TOKEN(anon_sym_DASH_DASH);
+ END_STATE();
+ case 57:
+ ACCEPT_TOKEN(anon_sym_DASH_GT);
+ END_STATE();
+ case 58:
+ ACCEPT_TOKEN(anon_sym_PLUS);
+ END_STATE();
+ case 59:
+ ACCEPT_TOKEN(anon_sym_DASH);
+ END_STATE();
+ case 60:
+ ACCEPT_TOKEN(anon_sym_DASH);
+ if (lookahead == '-') ADVANCE(56);
+ if (lookahead == '>') ADVANCE(57);
+ END_STATE();
+ case 61:
+ ACCEPT_TOKEN(sym_component_label);
+ if (lookahead == '\t' ||
+ lookahead == ' ') ADVANCE(61);
+ if (lookahead != 0 &&
+ lookahead != '\t' &&
+ lookahead != '\n' &&
+ lookahead != '\r' &&
+ lookahead != '(' &&
+ lookahead != ')' &&
+ (lookahead < '+' || '-' < lookahead) &&
+ lookahead != '[' &&
+ lookahead != ']') ADVANCE(62);
+ END_STATE();
+ case 62:
+ ACCEPT_TOKEN(sym_component_label);
+ if (lookahead != 0 &&
+ lookahead != '\n' &&
+ lookahead != '\r' &&
+ lookahead != '(' &&
+ lookahead != ')' &&
+ (lookahead < '+' || '-' < lookahead) &&
+ lookahead != '[' &&
+ lookahead != ']') ADVANCE(62);
+ END_STATE();
+ case 63:
+ ACCEPT_TOKEN(sym_text);
+ if (lookahead == '\t' ||
+ lookahead == ' ') ADVANCE(63);
+ if (lookahead != 0 &&
+ lookahead != '\t' &&
+ lookahead != '\n' &&
+ lookahead != '\r') ADVANCE(64);
+ END_STATE();
+ case 64:
+ ACCEPT_TOKEN(sym_text);
+ if (lookahead != 0 &&
+ lookahead != '\n' &&
+ lookahead != '\r') ADVANCE(64);
+ END_STATE();
+ case 65:
+ ACCEPT_TOKEN(aux_sym_real_number_token1);
+ if (('0' <= lookahead && lookahead <= '9')) ADVANCE(65);
+ END_STATE();
+ case 66:
+ ACCEPT_TOKEN(aux_sym_real_number_token2);
+ if (lookahead == '.') ADVANCE(65);
+ if (('0' <= lookahead && lookahead <= '9')) ADVANCE(66);
+ END_STATE();
+ case 67:
+ ACCEPT_TOKEN(aux_sym_real_number_token3);
+ if (('0' <= lookahead && lookahead <= '9')) ADVANCE(67);
+ END_STATE();
+ case 68:
+ ACCEPT_TOKEN(aux_sym_stage_number_token1);
+ END_STATE();
+ case 69:
+ ACCEPT_TOKEN(aux_sym_stage_number_token2);
+ END_STATE();
+ case 70:
+ ACCEPT_TOKEN(aux_sym_stage_number_token3);
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(69);
+ END_STATE();
+ case 71:
+ ACCEPT_TOKEN(aux_sym_stage_number_token4);
+ if (lookahead == 'I' ||
+ lookahead == 'i') ADVANCE(70);
+ if (lookahead == 'V' ||
+ lookahead == 'v') ADVANCE(68);
+ END_STATE();
+ case 72:
+ ACCEPT_TOKEN(aux_sym_shape_label_token1);
+ END_STATE();
+ case 73:
+ ACCEPT_TOKEN(aux_sym_shape_label_token2);
+ END_STATE();
+ case 74:
+ ACCEPT_TOKEN(aux_sym_shape_label_token3);
+ END_STATE();
+ case 75:
+ ACCEPT_TOKEN(aux_sym_shape_label_token4);
+ END_STATE();
+ case 76:
+ ACCEPT_TOKEN(sym__note_keyword);
+ END_STATE();
+ case 77:
+ ACCEPT_TOKEN(sym__group_keyword);
+ END_STATE();
+ case 78:
+ ACCEPT_TOKEN(sym__inertia_keyword);
+ END_STATE();
+ case 79:
+ ACCEPT_TOKEN(sym__evolution_keyword);
+ END_STATE();
+ case 80:
+ ACCEPT_TOKEN(anon_sym_LF);
+ END_STATE();
+ case 81:
+ ACCEPT_TOKEN(anon_sym_CR_LF);
+ END_STATE();
+ case 82:
+ ACCEPT_TOKEN(anon_sym_CR);
+ if (lookahead == '\n') ADVANCE(81);
+ END_STATE();
+ default:
+ return false;
+ }
+}
+
+static const TSLexerMode ts_lex_modes[STATE_COUNT] = {
+ [0] = {.lex_state = 0},
+ [1] = {.lex_state = 48},
+ [2] = {.lex_state = 48},
+ [3] = {.lex_state = 48},
+ [4] = {.lex_state = 48},
+ [5] = {.lex_state = 0},
+ [6] = {.lex_state = 0},
+ [7] = {.lex_state = 1},
+ [8] = {.lex_state = 1},
+ [9] = {.lex_state = 0},
+ [10] = {.lex_state = 0},
+ [11] = {.lex_state = 0},
+ [12] = {.lex_state = 0},
+ [13] = {.lex_state = 0},
+ [14] = {.lex_state = 0},
+ [15] = {.lex_state = 0},
+ [16] = {.lex_state = 0},
+ [17] = {.lex_state = 0},
+ [18] = {.lex_state = 0},
+ [19] = {.lex_state = 0},
+ [20] = {.lex_state = 1},
+ [21] = {.lex_state = 0},
+ [22] = {.lex_state = 0},
+ [23] = {.lex_state = 0},
+ [24] = {.lex_state = 0},
+ [25] = {.lex_state = 0},
+ [26] = {.lex_state = 0},
+ [27] = {.lex_state = 0},
+ [28] = {.lex_state = 0},
+ [29] = {.lex_state = 2},
+ [30] = {.lex_state = 0},
+ [31] = {.lex_state = 0},
+ [32] = {.lex_state = 0},
+ [33] = {.lex_state = 0},
+ [34] = {.lex_state = 48},
+ [35] = {.lex_state = 48},
+ [36] = {.lex_state = 0},
+ [37] = {.lex_state = 48},
+ [38] = {.lex_state = 8},
+ [39] = {.lex_state = 48},
+ [40] = {.lex_state = 0},
+ [41] = {.lex_state = 48},
+ [42] = {.lex_state = 0},
+ [43] = {.lex_state = 0},
+ [44] = {.lex_state = 0},
+ [45] = {.lex_state = 8},
+ [46] = {.lex_state = 0},
+ [47] = {.lex_state = 48},
+ [48] = {.lex_state = 0},
+ [49] = {.lex_state = 0},
+};
+
+static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = {
+ [STATE(0)] = {
+ [ts_builtin_sym_end] = ACTIONS(1),
+ [anon_sym_LBRACK] = ACTIONS(1),
+ [anon_sym_RBRACK] = ACTIONS(1),
+ [anon_sym_COMMA] = ACTIONS(1),
+ [anon_sym_LPAREN] = ACTIONS(1),
+ [anon_sym_RPAREN] = ACTIONS(1),
+ [anon_sym_DASH_DASH] = ACTIONS(1),
+ [anon_sym_DASH_GT] = ACTIONS(1),
+ [anon_sym_PLUS] = ACTIONS(1),
+ [anon_sym_DASH] = ACTIONS(1),
+ [aux_sym_real_number_token1] = ACTIONS(1),
+ [aux_sym_real_number_token2] = ACTIONS(1),
+ [aux_sym_real_number_token3] = ACTIONS(1),
+ [aux_sym_stage_number_token1] = ACTIONS(1),
+ [aux_sym_stage_number_token2] = ACTIONS(1),
+ [aux_sym_stage_number_token3] = ACTIONS(1),
+ [aux_sym_stage_number_token4] = ACTIONS(1),
+ [aux_sym_shape_label_token1] = ACTIONS(1),
+ [aux_sym_shape_label_token2] = ACTIONS(1),
+ [aux_sym_shape_label_token3] = ACTIONS(1),
+ [aux_sym_shape_label_token4] = ACTIONS(1),
+ [sym__note_keyword] = ACTIONS(1),
+ [sym__group_keyword] = ACTIONS(1),
+ [sym__inertia_keyword] = ACTIONS(1),
+ [sym__evolution_keyword] = ACTIONS(1),
+ [anon_sym_LF] = ACTIONS(1),
+ [anon_sym_CR_LF] = ACTIONS(1),
+ [anon_sym_CR] = ACTIONS(1),
+ },
+ [STATE(1)] = {
+ [sym_source_file] = STATE(49),
+ [sym_entity] = STATE(15),
+ [sym_component] = STATE(25),
+ [sym_dependency] = STATE(25),
+ [sym_note] = STATE(25),
+ [sym_stage] = STATE(25),
+ [sym_group] = STATE(25),
+ [sym_inertia] = STATE(25),
+ [sym_evolution] = STATE(25),
+ [sym__line_ending] = STATE(2),
+ [aux_sym_source_file_repeat1] = STATE(2),
+ [ts_builtin_sym_end] = ACTIONS(3),
+ [anon_sym_LBRACK] = ACTIONS(5),
+ [sym_component_label] = ACTIONS(7),
+ [sym__note_keyword] = ACTIONS(9),
+ [sym__group_keyword] = ACTIONS(11),
+ [sym__inertia_keyword] = ACTIONS(13),
+ [sym__evolution_keyword] = ACTIONS(15),
+ [anon_sym_LF] = ACTIONS(17),
+ [anon_sym_CR_LF] = ACTIONS(17),
+ [anon_sym_CR] = ACTIONS(17),
+ },
+};
+
+static const uint16_t ts_small_parse_table[] = {
+ [0] = 11,
+ ACTIONS(5), 1,
+ anon_sym_LBRACK,
+ ACTIONS(7), 1,
+ sym_component_label,
+ ACTIONS(9), 1,
+ sym__note_keyword,
+ ACTIONS(11), 1,
+ sym__group_keyword,
+ ACTIONS(13), 1,
+ sym__inertia_keyword,
+ ACTIONS(15), 1,
+ sym__evolution_keyword,
+ ACTIONS(19), 1,
+ ts_builtin_sym_end,
+ STATE(15), 1,
+ sym_entity,
+ STATE(3), 2,
+ sym__line_ending,
+ aux_sym_source_file_repeat1,
+ ACTIONS(21), 3,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ anon_sym_CR,
+ STATE(25), 7,
+ sym_component,
+ sym_dependency,
+ sym_note,
+ sym_stage,
+ sym_group,
+ sym_inertia,
+ sym_evolution,
+ [43] = 11,
+ ACTIONS(23), 1,
+ ts_builtin_sym_end,
+ ACTIONS(25), 1,
+ anon_sym_LBRACK,
+ ACTIONS(28), 1,
+ sym_component_label,
+ ACTIONS(31), 1,
+ sym__note_keyword,
+ ACTIONS(34), 1,
+ sym__group_keyword,
+ ACTIONS(37), 1,
+ sym__inertia_keyword,
+ ACTIONS(40), 1,
+ sym__evolution_keyword,
+ STATE(15), 1,
+ sym_entity,
+ STATE(3), 2,
+ sym__line_ending,
+ aux_sym_source_file_repeat1,
+ ACTIONS(43), 3,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ anon_sym_CR,
+ STATE(25), 7,
+ sym_component,
+ sym_dependency,
+ sym_note,
+ sym_stage,
+ sym_group,
+ sym_inertia,
+ sym_evolution,
+ [86] = 2,
+ ACTIONS(23), 2,
+ ts_builtin_sym_end,
+ sym_component_label,
+ ACTIONS(46), 8,
+ anon_sym_LBRACK,
+ sym__note_keyword,
+ sym__group_keyword,
+ sym__inertia_keyword,
+ sym__evolution_keyword,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ anon_sym_CR,
+ [101] = 4,
+ ACTIONS(48), 1,
+ anon_sym_COMMA,
+ ACTIONS(52), 1,
+ anon_sym_CR,
+ STATE(11), 1,
+ aux_sym_group_repeat1,
+ ACTIONS(50), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [115] = 3,
+ STATE(43), 1,
+ sym_stage_number,
+ ACTIONS(54), 2,
+ aux_sym_stage_number_token1,
+ aux_sym_stage_number_token2,
+ ACTIONS(56), 2,
+ aux_sym_stage_number_token3,
+ aux_sym_stage_number_token4,
+ [127] = 4,
+ ACTIONS(58), 1,
+ anon_sym_LBRACK,
+ ACTIONS(62), 1,
+ anon_sym_CR,
+ STATE(27), 1,
+ sym_shape,
+ ACTIONS(60), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [141] = 4,
+ ACTIONS(64), 1,
+ anon_sym_LPAREN,
+ STATE(7), 1,
+ sym_position,
+ STATE(34), 1,
+ sym_dependency_type,
+ ACTIONS(66), 2,
+ anon_sym_DASH_DASH,
+ anon_sym_DASH_GT,
+ [155] = 2,
+ ACTIONS(70), 1,
+ anon_sym_CR,
+ ACTIONS(68), 4,
+ anon_sym_COMMA,
+ anon_sym_RPAREN,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [165] = 2,
+ STATE(40), 1,
+ sym_shape_label,
+ ACTIONS(72), 4,
+ aux_sym_shape_label_token1,
+ aux_sym_shape_label_token2,
+ aux_sym_shape_label_token3,
+ aux_sym_shape_label_token4,
+ [175] = 4,
+ ACTIONS(48), 1,
+ anon_sym_COMMA,
+ ACTIONS(76), 1,
+ anon_sym_CR,
+ STATE(12), 1,
+ aux_sym_group_repeat1,
+ ACTIONS(74), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [189] = 4,
+ ACTIONS(78), 1,
+ anon_sym_COMMA,
+ ACTIONS(83), 1,
+ anon_sym_CR,
+ STATE(12), 1,
+ aux_sym_group_repeat1,
+ ACTIONS(81), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [203] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(23), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [214] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(48), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [225] = 3,
+ ACTIONS(91), 1,
+ anon_sym_CR,
+ STATE(4), 1,
+ sym__line_ending,
+ ACTIONS(89), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [236] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(33), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [247] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(28), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [258] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(44), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [269] = 2,
+ ACTIONS(95), 1,
+ anon_sym_CR,
+ ACTIONS(93), 3,
+ anon_sym_COMMA,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [278] = 2,
+ ACTIONS(99), 1,
+ anon_sym_CR,
+ ACTIONS(97), 3,
+ anon_sym_LBRACK,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [287] = 3,
+ ACTIONS(87), 1,
+ aux_sym_real_number_token2,
+ STATE(46), 1,
+ sym_real_number,
+ ACTIONS(85), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [298] = 2,
+ ACTIONS(103), 1,
+ anon_sym_CR,
+ ACTIONS(101), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [306] = 2,
+ ACTIONS(107), 1,
+ anon_sym_CR,
+ ACTIONS(105), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [314] = 2,
+ ACTIONS(111), 1,
+ aux_sym_real_number_token2,
+ ACTIONS(109), 2,
+ aux_sym_real_number_token1,
+ aux_sym_real_number_token3,
+ [322] = 2,
+ ACTIONS(115), 1,
+ anon_sym_CR,
+ ACTIONS(113), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [330] = 2,
+ ACTIONS(119), 1,
+ anon_sym_CR,
+ ACTIONS(117), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [338] = 2,
+ ACTIONS(123), 1,
+ anon_sym_CR,
+ ACTIONS(121), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [346] = 2,
+ ACTIONS(127), 1,
+ anon_sym_CR,
+ ACTIONS(125), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [354] = 2,
+ STATE(17), 1,
+ sym_sign,
+ ACTIONS(129), 2,
+ anon_sym_PLUS,
+ anon_sym_DASH,
+ [362] = 2,
+ ACTIONS(133), 1,
+ anon_sym_CR,
+ ACTIONS(131), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [370] = 2,
+ ACTIONS(137), 1,
+ anon_sym_CR,
+ ACTIONS(135), 2,
+ anon_sym_LF,
+ anon_sym_CR_LF,
+ [378] = 2,
+ ACTIONS(139), 1,
+ anon_sym_LPAREN,
+ STATE(38), 1,
+ sym_position,
+ [385] = 1,
+ ACTIONS(141), 1,
+ anon_sym_COMMA,
+ [389] = 1,
+ ACTIONS(143), 1,
+ sym_component_label,
+ [393] = 1,
+ ACTIONS(145), 1,
+ sym_component_label,
+ [397] = 1,
+ ACTIONS(147), 1,
+ anon_sym_RBRACK,
+ [401] = 1,
+ ACTIONS(149), 1,
+ sym_component_label,
+ [405] = 1,
+ ACTIONS(151), 1,
+ sym_text,
+ [409] = 1,
+ ACTIONS(153), 1,
+ sym_component_label,
+ [413] = 1,
+ ACTIONS(155), 1,
+ anon_sym_RBRACK,
+ [417] = 1,
+ ACTIONS(157), 1,
+ sym_component_label,
+ [421] = 1,
+ ACTIONS(159), 1,
+ anon_sym_RBRACK,
+ [425] = 1,
+ ACTIONS(161), 1,
+ anon_sym_RBRACK,
+ [429] = 1,
+ ACTIONS(163), 1,
+ anon_sym_RPAREN,
+ [433] = 1,
+ ACTIONS(97), 1,
+ sym_text,
+ [437] = 1,
+ ACTIONS(165), 1,
+ anon_sym_RPAREN,
+ [441] = 1,
+ ACTIONS(167), 1,
+ sym_component_label,
+ [445] = 1,
+ ACTIONS(169), 1,
+ anon_sym_COMMA,
+ [449] = 1,
+ ACTIONS(171), 1,
+ ts_builtin_sym_end,
+};
+
+static const uint32_t ts_small_parse_table_map[] = {
+ [SMALL_STATE(2)] = 0,
+ [SMALL_STATE(3)] = 43,
+ [SMALL_STATE(4)] = 86,
+ [SMALL_STATE(5)] = 101,
+ [SMALL_STATE(6)] = 115,
+ [SMALL_STATE(7)] = 127,
+ [SMALL_STATE(8)] = 141,
+ [SMALL_STATE(9)] = 155,
+ [SMALL_STATE(10)] = 165,
+ [SMALL_STATE(11)] = 175,
+ [SMALL_STATE(12)] = 189,
+ [SMALL_STATE(13)] = 203,
+ [SMALL_STATE(14)] = 214,
+ [SMALL_STATE(15)] = 225,
+ [SMALL_STATE(16)] = 236,
+ [SMALL_STATE(17)] = 247,
+ [SMALL_STATE(18)] = 258,
+ [SMALL_STATE(19)] = 269,
+ [SMALL_STATE(20)] = 278,
+ [SMALL_STATE(21)] = 287,
+ [SMALL_STATE(22)] = 298,
+ [SMALL_STATE(23)] = 306,
+ [SMALL_STATE(24)] = 314,
+ [SMALL_STATE(25)] = 322,
+ [SMALL_STATE(26)] = 330,
+ [SMALL_STATE(27)] = 338,
+ [SMALL_STATE(28)] = 346,
+ [SMALL_STATE(29)] = 354,
+ [SMALL_STATE(30)] = 362,
+ [SMALL_STATE(31)] = 370,
+ [SMALL_STATE(32)] = 378,
+ [SMALL_STATE(33)] = 385,
+ [SMALL_STATE(34)] = 389,
+ [SMALL_STATE(35)] = 393,
+ [SMALL_STATE(36)] = 397,
+ [SMALL_STATE(37)] = 401,
+ [SMALL_STATE(38)] = 405,
+ [SMALL_STATE(39)] = 409,
+ [SMALL_STATE(40)] = 413,
+ [SMALL_STATE(41)] = 417,
+ [SMALL_STATE(42)] = 421,
+ [SMALL_STATE(43)] = 425,
+ [SMALL_STATE(44)] = 429,
+ [SMALL_STATE(45)] = 433,
+ [SMALL_STATE(46)] = 437,
+ [SMALL_STATE(47)] = 441,
+ [SMALL_STATE(48)] = 445,
+ [SMALL_STATE(49)] = 449,
+};
+
+static const TSParseActionEntry ts_parse_actions[] = {
+ [0] = {.entry = {.count = 0, .reusable = false}},
+ [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(),
+ [3] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 0, 0, 0),
+ [5] = {.entry = {.count = 1, .reusable = false}}, SHIFT(6),
+ [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8),
+ [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(32),
+ [11] = {.entry = {.count = 1, .reusable = false}}, SHIFT(37),
+ [13] = {.entry = {.count = 1, .reusable = false}}, SHIFT(41),
+ [15] = {.entry = {.count = 1, .reusable = false}}, SHIFT(47),
+ [17] = {.entry = {.count = 1, .reusable = false}}, SHIFT(2),
+ [19] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 1, 0, 0),
+ [21] = {.entry = {.count = 1, .reusable = false}}, SHIFT(3),
+ [23] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0),
+ [25] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(6),
+ [28] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(8),
+ [31] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(32),
+ [34] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(37),
+ [37] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(41),
+ [40] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(47),
+ [43] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(3),
+ [46] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0),
+ [48] = {.entry = {.count = 1, .reusable = true}}, SHIFT(35),
+ [50] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_group, 2, 0, 2),
+ [52] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_group, 2, 0, 2),
+ [54] = {.entry = {.count = 1, .reusable = true}}, SHIFT(36),
+ [56] = {.entry = {.count = 1, .reusable = false}}, SHIFT(36),
+ [58] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10),
+ [60] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_component, 2, 0, 1),
+ [62] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_component, 2, 0, 1),
+ [64] = {.entry = {.count = 1, .reusable = true}}, SHIFT(16),
+ [66] = {.entry = {.count = 1, .reusable = true}}, SHIFT(39),
+ [68] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_real_number, 1, 0, 0),
+ [70] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_real_number, 1, 0, 0),
+ [72] = {.entry = {.count = 1, .reusable = true}}, SHIFT(42),
+ [74] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_group, 3, 0, 6),
+ [76] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_group, 3, 0, 6),
+ [78] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_group_repeat1, 2, 0, 8), SHIFT_REPEAT(35),
+ [81] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_group_repeat1, 2, 0, 8),
+ [83] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_group_repeat1, 2, 0, 8),
+ [85] = {.entry = {.count = 1, .reusable = true}}, SHIFT(9),
+ [87] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9),
+ [89] = {.entry = {.count = 1, .reusable = true}}, SHIFT(4),
+ [91] = {.entry = {.count = 1, .reusable = false}}, SHIFT(4),
+ [93] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_group_repeat1, 2, 0, 2),
+ [95] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_group_repeat1, 2, 0, 2),
+ [97] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_position, 5, 0, 10),
+ [99] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_position, 5, 0, 10),
+ [101] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_note, 3, 0, 5),
+ [103] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_note, 3, 0, 5),
+ [105] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_stage, 4, 0, 7),
+ [107] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_stage, 4, 0, 7),
+ [109] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_sign, 1, 0, 0),
+ [111] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_sign, 1, 0, 0),
+ [113] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_entity, 1, 0, 0),
+ [115] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_entity, 1, 0, 0),
+ [117] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dependency, 3, 0, 4),
+ [119] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dependency, 3, 0, 4),
+ [121] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_component, 3, 0, 3),
+ [123] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_component, 3, 0, 3),
+ [125] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_evolution, 4, 0, 9),
+ [127] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_evolution, 4, 0, 9),
+ [129] = {.entry = {.count = 1, .reusable = true}}, SHIFT(24),
+ [131] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_inertia, 2, 0, 2),
+ [133] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_inertia, 2, 0, 2),
+ [135] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_shape, 3, 0, 0),
+ [137] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_shape, 3, 0, 0),
+ [139] = {.entry = {.count = 1, .reusable = true}}, SHIFT(14),
+ [141] = {.entry = {.count = 1, .reusable = true}}, SHIFT(18),
+ [143] = {.entry = {.count = 1, .reusable = true}}, SHIFT(26),
+ [145] = {.entry = {.count = 1, .reusable = true}}, SHIFT(19),
+ [147] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_stage_number, 1, 0, 0),
+ [149] = {.entry = {.count = 1, .reusable = true}}, SHIFT(5),
+ [151] = {.entry = {.count = 1, .reusable = true}}, SHIFT(22),
+ [153] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dependency_type, 1, 0, 0),
+ [155] = {.entry = {.count = 1, .reusable = true}}, SHIFT(31),
+ [157] = {.entry = {.count = 1, .reusable = true}}, SHIFT(30),
+ [159] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_shape_label, 1, 0, 0),
+ [161] = {.entry = {.count = 1, .reusable = true}}, SHIFT(13),
+ [163] = {.entry = {.count = 1, .reusable = true}}, SHIFT(20),
+ [165] = {.entry = {.count = 1, .reusable = true}}, SHIFT(45),
+ [167] = {.entry = {.count = 1, .reusable = true}}, SHIFT(29),
+ [169] = {.entry = {.count = 1, .reusable = true}}, SHIFT(21),
+ [171] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(),
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef TREE_SITTER_HIDE_SYMBOLS
+#define TS_PUBLIC
+#elif defined(_WIN32)
+#define TS_PUBLIC __declspec(dllexport)
+#else
+#define TS_PUBLIC __attribute__((visibility("default")))
+#endif
+
+TS_PUBLIC const TSLanguage *tree_sitter_wmap(void) {
+ static const TSLanguage language = {
+ .abi_version = LANGUAGE_VERSION,
+ .symbol_count = SYMBOL_COUNT,
+ .alias_count = ALIAS_COUNT,
+ .token_count = TOKEN_COUNT,
+ .external_token_count = EXTERNAL_TOKEN_COUNT,
+ .state_count = STATE_COUNT,
+ .large_state_count = LARGE_STATE_COUNT,
+ .production_id_count = PRODUCTION_ID_COUNT,
+ .supertype_count = SUPERTYPE_COUNT,
+ .field_count = FIELD_COUNT,
+ .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH,
+ .parse_table = &ts_parse_table[0][0],
+ .small_parse_table = ts_small_parse_table,
+ .small_parse_table_map = ts_small_parse_table_map,
+ .parse_actions = ts_parse_actions,
+ .symbol_names = ts_symbol_names,
+ .field_names = ts_field_names,
+ .field_map_slices = ts_field_map_slices,
+ .field_map_entries = ts_field_map_entries,
+ .symbol_metadata = ts_symbol_metadata,
+ .public_symbol_map = ts_symbol_map,
+ .alias_map = ts_non_terminal_alias_map,
+ .alias_sequences = &ts_alias_sequences[0][0],
+ .lex_modes = (const void*)ts_lex_modes,
+ .lex_fn = ts_lex,
+ .primary_state_ids = ts_primary_state_ids,
+ .name = "wmap",
+ .max_reserved_word_set_size = 0,
+ .metadata = {
+ .major_version = 1,
+ .minor_version = 0,
+ .patch_version = 0,
+ },
+ };
+ return &language;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/tree_sitter/alloc.h b/src/tree_sitter/alloc.h
new file mode 100644
index 0000000..1abdd12
--- /dev/null
+++ b/src/tree_sitter/alloc.h
@@ -0,0 +1,54 @@
+#ifndef TREE_SITTER_ALLOC_H_
+#define TREE_SITTER_ALLOC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// Allow clients to override allocation functions
+#ifdef TREE_SITTER_REUSE_ALLOCATOR
+
+extern void *(*ts_current_malloc)(size_t size);
+extern void *(*ts_current_calloc)(size_t count, size_t size);
+extern void *(*ts_current_realloc)(void *ptr, size_t size);
+extern void (*ts_current_free)(void *ptr);
+
+#ifndef ts_malloc
+#define ts_malloc ts_current_malloc
+#endif
+#ifndef ts_calloc
+#define ts_calloc ts_current_calloc
+#endif
+#ifndef ts_realloc
+#define ts_realloc ts_current_realloc
+#endif
+#ifndef ts_free
+#define ts_free ts_current_free
+#endif
+
+#else
+
+#ifndef ts_malloc
+#define ts_malloc malloc
+#endif
+#ifndef ts_calloc
+#define ts_calloc calloc
+#endif
+#ifndef ts_realloc
+#define ts_realloc realloc
+#endif
+#ifndef ts_free
+#define ts_free free
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_SITTER_ALLOC_H_
diff --git a/src/tree_sitter/array.h b/src/tree_sitter/array.h
new file mode 100644
index 0000000..a17a574
--- /dev/null
+++ b/src/tree_sitter/array.h
@@ -0,0 +1,291 @@
+#ifndef TREE_SITTER_ARRAY_H_
+#define TREE_SITTER_ARRAY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "./alloc.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4101)
+#elif defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+
+#define Array(T) \
+ struct { \
+ T *contents; \
+ uint32_t size; \
+ uint32_t capacity; \
+ }
+
+/// Initialize an array.
+#define array_init(self) \
+ ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
+
+/// Create an empty array.
+#define array_new() \
+ { NULL, 0, 0 }
+
+/// Get a pointer to the element at a given `index` in the array.
+#define array_get(self, _index) \
+ (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
+
+/// Get a pointer to the first element in the array.
+#define array_front(self) array_get(self, 0)
+
+/// Get a pointer to the last element in the array.
+#define array_back(self) array_get(self, (self)->size - 1)
+
+/// Clear the array, setting its size to zero. Note that this does not free any
+/// memory allocated for the array's contents.
+#define array_clear(self) ((self)->size = 0)
+
+/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is
+/// less than the array's current capacity, this function has no effect.
+#define array_reserve(self, new_capacity) \
+ _array__reserve((Array *)(self), array_elem_size(self), new_capacity)
+
+/// Free any memory allocated for this array. Note that this does not free any
+/// memory allocated for the array's contents.
+#define array_delete(self) _array__delete((Array *)(self))
+
+/// Push a new `element` onto the end of the array.
+#define array_push(self, element) \
+ (_array__grow((Array *)(self), 1, array_elem_size(self)), \
+ (self)->contents[(self)->size++] = (element))
+
+/// Increase the array's size by `count` elements.
+/// New elements are zero-initialized.
+#define array_grow_by(self, count) \
+ do { \
+ if ((count) == 0) break; \
+ _array__grow((Array *)(self), count, array_elem_size(self)); \
+ memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \
+ (self)->size += (count); \
+ } while (0)
+
+/// Append all elements from one array to the end of another.
+#define array_push_all(self, other) \
+ array_extend((self), (other)->size, (other)->contents)
+
+/// Append `count` elements to the end of the array, reading their values from the
+/// `contents` pointer.
+#define array_extend(self, count, contents) \
+ _array__splice( \
+ (Array *)(self), array_elem_size(self), (self)->size, \
+ 0, count, contents \
+ )
+
+/// Remove `old_count` elements from the array starting at the given `index`. At
+/// the same index, insert `new_count` new elements, reading their values from the
+/// `new_contents` pointer.
+#define array_splice(self, _index, old_count, new_count, new_contents) \
+ _array__splice( \
+ (Array *)(self), array_elem_size(self), _index, \
+ old_count, new_count, new_contents \
+ )
+
+/// Insert one `element` into the array at the given `index`.
+#define array_insert(self, _index, element) \
+ _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element))
+
+/// Remove one element from the array at the given `index`.
+#define array_erase(self, _index) \
+ _array__erase((Array *)(self), array_elem_size(self), _index)
+
+/// Pop the last element off the array, returning the element by value.
+#define array_pop(self) ((self)->contents[--(self)->size])
+
+/// Assign the contents of one array to another, reallocating if necessary.
+#define array_assign(self, other) \
+ _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self))
+
+/// Swap one array with another
+#define array_swap(self, other) \
+ _array__swap((Array *)(self), (Array *)(other))
+
+/// Get the size of the array contents
+#define array_elem_size(self) (sizeof *(self)->contents)
+
+/// Search a sorted array for a given `needle` value, using the given `compare`
+/// callback to determine the order.
+///
+/// If an existing element is found to be equal to `needle`, then the `index`
+/// out-parameter is set to the existing value's index, and the `exists`
+/// out-parameter is set to true. Otherwise, `index` is set to an index where
+/// `needle` should be inserted in order to preserve the sorting, and `exists`
+/// is set to false.
+#define array_search_sorted_with(self, compare, needle, _index, _exists) \
+ _array__search_sorted(self, 0, compare, , needle, _index, _exists)
+
+/// Search a sorted array for a given `needle` value, using integer comparisons
+/// of a given struct field (specified with a leading dot) to determine the order.
+///
+/// See also `array_search_sorted_with`.
+#define array_search_sorted_by(self, field, needle, _index, _exists) \
+ _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists)
+
+/// Insert a given `value` into a sorted array, using the given `compare`
+/// callback to determine the order.
+#define array_insert_sorted_with(self, compare, value) \
+ do { \
+ unsigned _index, _exists; \
+ array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
+ if (!_exists) array_insert(self, _index, value); \
+ } while (0)
+
+/// Insert a given `value` into a sorted array, using integer comparisons of
+/// a given struct field (specified with a leading dot) to determine the order.
+///
+/// See also `array_search_sorted_by`.
+#define array_insert_sorted_by(self, field, value) \
+ do { \
+ unsigned _index, _exists; \
+ array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
+ if (!_exists) array_insert(self, _index, value); \
+ } while (0)
+
+// Private
+
+typedef Array(void) Array;
+
+/// This is not what you're looking for, see `array_delete`.
+static inline void _array__delete(Array *self) {
+ if (self->contents) {
+ ts_free(self->contents);
+ self->contents = NULL;
+ self->size = 0;
+ self->capacity = 0;
+ }
+}
+
+/// This is not what you're looking for, see `array_erase`.
+static inline void _array__erase(Array *self, size_t element_size,
+ uint32_t index) {
+ assert(index < self->size);
+ char *contents = (char *)self->contents;
+ memmove(contents + index * element_size, contents + (index + 1) * element_size,
+ (self->size - index - 1) * element_size);
+ self->size--;
+}
+
+/// This is not what you're looking for, see `array_reserve`.
+static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) {
+ if (new_capacity > self->capacity) {
+ if (self->contents) {
+ self->contents = ts_realloc(self->contents, new_capacity * element_size);
+ } else {
+ self->contents = ts_malloc(new_capacity * element_size);
+ }
+ self->capacity = new_capacity;
+ }
+}
+
+/// This is not what you're looking for, see `array_assign`.
+static inline void _array__assign(Array *self, const Array *other, size_t element_size) {
+ _array__reserve(self, element_size, other->size);
+ self->size = other->size;
+ memcpy(self->contents, other->contents, self->size * element_size);
+}
+
+/// This is not what you're looking for, see `array_swap`.
+static inline void _array__swap(Array *self, Array *other) {
+ Array swap = *other;
+ *other = *self;
+ *self = swap;
+}
+
+/// This is not what you're looking for, see `array_push` or `array_grow_by`.
+static inline void _array__grow(Array *self, uint32_t count, size_t element_size) {
+ uint32_t new_size = self->size + count;
+ if (new_size > self->capacity) {
+ uint32_t new_capacity = self->capacity * 2;
+ if (new_capacity < 8) new_capacity = 8;
+ if (new_capacity < new_size) new_capacity = new_size;
+ _array__reserve(self, element_size, new_capacity);
+ }
+}
+
+/// This is not what you're looking for, see `array_splice`.
+static inline void _array__splice(Array *self, size_t element_size,
+ uint32_t index, uint32_t old_count,
+ uint32_t new_count, const void *elements) {
+ uint32_t new_size = self->size + new_count - old_count;
+ uint32_t old_end = index + old_count;
+ uint32_t new_end = index + new_count;
+ assert(old_end <= self->size);
+
+ _array__reserve(self, element_size, new_size);
+
+ char *contents = (char *)self->contents;
+ if (self->size > old_end) {
+ memmove(
+ contents + new_end * element_size,
+ contents + old_end * element_size,
+ (self->size - old_end) * element_size
+ );
+ }
+ if (new_count > 0) {
+ if (elements) {
+ memcpy(
+ (contents + index * element_size),
+ elements,
+ new_count * element_size
+ );
+ } else {
+ memset(
+ (contents + index * element_size),
+ 0,
+ new_count * element_size
+ );
+ }
+ }
+ self->size += new_count - old_count;
+}
+
+/// A binary search routine, based on Rust's `std::slice::binary_search_by`.
+/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`.
+#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
+ do { \
+ *(_index) = start; \
+ *(_exists) = false; \
+ uint32_t size = (self)->size - *(_index); \
+ if (size == 0) break; \
+ int comparison; \
+ while (size > 1) { \
+ uint32_t half_size = size / 2; \
+ uint32_t mid_index = *(_index) + half_size; \
+ comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
+ if (comparison <= 0) *(_index) = mid_index; \
+ size -= half_size; \
+ } \
+ comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
+ if (comparison == 0) *(_exists) = true; \
+ else if (comparison < 0) *(_index) += 1; \
+ } while (0)
+
+/// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
+/// parameter by reference in order to work with the generic sorting function above.
+#define _compare_int(a, b) ((int)*(a) - (int)(b))
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_SITTER_ARRAY_H_
diff --git a/src/tree_sitter/parser.h b/src/tree_sitter/parser.h
new file mode 100644
index 0000000..858107d
--- /dev/null
+++ b/src/tree_sitter/parser.h
@@ -0,0 +1,286 @@
+#ifndef TREE_SITTER_PARSER_H_
+#define TREE_SITTER_PARSER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define ts_builtin_sym_error ((TSSymbol)-1)
+#define ts_builtin_sym_end 0
+#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
+
+#ifndef TREE_SITTER_API_H_
+typedef uint16_t TSStateId;
+typedef uint16_t TSSymbol;
+typedef uint16_t TSFieldId;
+typedef struct TSLanguage TSLanguage;
+typedef struct TSLanguageMetadata {
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint8_t patch_version;
+} TSLanguageMetadata;
+#endif
+
+typedef struct {
+ TSFieldId field_id;
+ uint8_t child_index;
+ bool inherited;
+} TSFieldMapEntry;
+
+// Used to index the field and supertype maps.
+typedef struct {
+ uint16_t index;
+ uint16_t length;
+} TSMapSlice;
+
+typedef struct {
+ bool visible;
+ bool named;
+ bool supertype;
+} TSSymbolMetadata;
+
+typedef struct TSLexer TSLexer;
+
+struct TSLexer {
+ int32_t lookahead;
+ TSSymbol result_symbol;
+ void (*advance)(TSLexer *, bool);
+ void (*mark_end)(TSLexer *);
+ uint32_t (*get_column)(TSLexer *);
+ bool (*is_at_included_range_start)(const TSLexer *);
+ bool (*eof)(const TSLexer *);
+ void (*log)(const TSLexer *, const char *, ...);
+};
+
+typedef enum {
+ TSParseActionTypeShift,
+ TSParseActionTypeReduce,
+ TSParseActionTypeAccept,
+ TSParseActionTypeRecover,
+} TSParseActionType;
+
+typedef union {
+ struct {
+ uint8_t type;
+ TSStateId state;
+ bool extra;
+ bool repetition;
+ } shift;
+ struct {
+ uint8_t type;
+ uint8_t child_count;
+ TSSymbol symbol;
+ int16_t dynamic_precedence;
+ uint16_t production_id;
+ } reduce;
+ uint8_t type;
+} TSParseAction;
+
+typedef struct {
+ uint16_t lex_state;
+ uint16_t external_lex_state;
+} TSLexMode;
+
+typedef struct {
+ uint16_t lex_state;
+ uint16_t external_lex_state;
+ uint16_t reserved_word_set_id;
+} TSLexerMode;
+
+typedef union {
+ TSParseAction action;
+ struct {
+ uint8_t count;
+ bool reusable;
+ } entry;
+} TSParseActionEntry;
+
+typedef struct {
+ int32_t start;
+ int32_t end;
+} TSCharacterRange;
+
+struct TSLanguage {
+ uint32_t abi_version;
+ uint32_t symbol_count;
+ uint32_t alias_count;
+ uint32_t token_count;
+ uint32_t external_token_count;
+ uint32_t state_count;
+ uint32_t large_state_count;
+ uint32_t production_id_count;
+ uint32_t field_count;
+ uint16_t max_alias_sequence_length;
+ const uint16_t *parse_table;
+ const uint16_t *small_parse_table;
+ const uint32_t *small_parse_table_map;
+ const TSParseActionEntry *parse_actions;
+ const char * const *symbol_names;
+ const char * const *field_names;
+ const TSMapSlice *field_map_slices;
+ const TSFieldMapEntry *field_map_entries;
+ const TSSymbolMetadata *symbol_metadata;
+ const TSSymbol *public_symbol_map;
+ const uint16_t *alias_map;
+ const TSSymbol *alias_sequences;
+ const TSLexerMode *lex_modes;
+ bool (*lex_fn)(TSLexer *, TSStateId);
+ bool (*keyword_lex_fn)(TSLexer *, TSStateId);
+ TSSymbol keyword_capture_token;
+ struct {
+ const bool *states;
+ const TSSymbol *symbol_map;
+ void *(*create)(void);
+ void (*destroy)(void *);
+ bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
+ unsigned (*serialize)(void *, char *);
+ void (*deserialize)(void *, const char *, unsigned);
+ } external_scanner;
+ const TSStateId *primary_state_ids;
+ const char *name;
+ const TSSymbol *reserved_words;
+ uint16_t max_reserved_word_set_size;
+ uint32_t supertype_count;
+ const TSSymbol *supertype_symbols;
+ const TSMapSlice *supertype_map_slices;
+ const TSSymbol *supertype_map_entries;
+ TSLanguageMetadata metadata;
+};
+
+static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) {
+ uint32_t index = 0;
+ uint32_t size = len - index;
+ while (size > 1) {
+ uint32_t half_size = size / 2;
+ uint32_t mid_index = index + half_size;
+ const TSCharacterRange *range = &ranges[mid_index];
+ if (lookahead >= range->start && lookahead <= range->end) {
+ return true;
+ } else if (lookahead > range->end) {
+ index = mid_index;
+ }
+ size -= half_size;
+ }
+ const TSCharacterRange *range = &ranges[index];
+ return (lookahead >= range->start && lookahead <= range->end);
+}
+
+/*
+ * Lexer Macros
+ */
+
+#ifdef _MSC_VER
+#define UNUSED __pragma(warning(suppress : 4101))
+#else
+#define UNUSED __attribute__((unused))
+#endif
+
+#define START_LEXER() \
+ bool result = false; \
+ bool skip = false; \
+ UNUSED \
+ bool eof = false; \
+ int32_t lookahead; \
+ goto start; \
+ next_state: \
+ lexer->advance(lexer, skip); \
+ start: \
+ skip = false; \
+ lookahead = lexer->lookahead;
+
+#define ADVANCE(state_value) \
+ { \
+ state = state_value; \
+ goto next_state; \
+ }
+
+#define ADVANCE_MAP(...) \
+ { \
+ static const uint16_t map[] = { __VA_ARGS__ }; \
+ for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \
+ if (map[i] == lookahead) { \
+ state = map[i + 1]; \
+ goto next_state; \
+ } \
+ } \
+ }
+
+#define SKIP(state_value) \
+ { \
+ skip = true; \
+ state = state_value; \
+ goto next_state; \
+ }
+
+#define ACCEPT_TOKEN(symbol_value) \
+ result = true; \
+ lexer->result_symbol = symbol_value; \
+ lexer->mark_end(lexer);
+
+#define END_STATE() return result;
+
+/*
+ * Parse Table Macros
+ */
+
+#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
+
+#define STATE(id) id
+
+#define ACTIONS(id) id
+
+#define SHIFT(state_value) \
+ {{ \
+ .shift = { \
+ .type = TSParseActionTypeShift, \
+ .state = (state_value) \
+ } \
+ }}
+
+#define SHIFT_REPEAT(state_value) \
+ {{ \
+ .shift = { \
+ .type = TSParseActionTypeShift, \
+ .state = (state_value), \
+ .repetition = true \
+ } \
+ }}
+
+#define SHIFT_EXTRA() \
+ {{ \
+ .shift = { \
+ .type = TSParseActionTypeShift, \
+ .extra = true \
+ } \
+ }}
+
+#define REDUCE(symbol_name, children, precedence, prod_id) \
+ {{ \
+ .reduce = { \
+ .type = TSParseActionTypeReduce, \
+ .symbol = symbol_name, \
+ .child_count = children, \
+ .dynamic_precedence = precedence, \
+ .production_id = prod_id \
+ }, \
+ }}
+
+#define RECOVER() \
+ {{ \
+ .type = TSParseActionTypeRecover \
+ }}
+
+#define ACCEPT_INPUT() \
+ {{ \
+ .type = TSParseActionTypeAccept \
+ }}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_SITTER_PARSER_H_
diff --git a/test/corpus/entities.txt b/test/corpus/entities.txt
new file mode 100644
index 0000000..09e9903
--- /dev/null
+++ b/test/corpus/entities.txt
@@ -0,0 +1,495 @@
+==================
+Simple component
+==================
+
+User (0.9, 0.5)
+
+---
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number)))))
+
+==================
+Component with shape - square
+==================
+
+Database (0.3, 0.2) [square]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component with shape - triangle (case insensitive)
+==================
+
+Soldier (28, 15) [Triangle]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component with shape - x
+==================
+
+Tinker (4, 30) [x]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component with shape - circle
+==================
+
+Node (10, 20) [Circle]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component with spaces in name
+==================
+
+Hello World (90, 20) [x]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component with decimal positions
+==================
+
+Tailor (7.03, 49.21) [Square]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Component no spaces
+==================
+
+Tailor777(30,10) [Square]
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label)))))
+
+==================
+Dependency with arrow
+==================
+
+User -> Database
+
+---
+
+(source_file
+ (entity
+ (dependency
+ from: (component_label)
+ type: (dependency_type)
+ to: (component_label))))
+
+==================
+Dependency with double dash
+==================
+
+Tinker -- Tailor
+
+---
+
+(source_file
+ (entity
+ (dependency
+ from: (component_label)
+ type: (dependency_type)
+ to: (component_label))))
+
+==================
+Dependency no spaces
+==================
+
+Tailor11--Tailor12
+
+---
+
+(source_file
+ (entity
+ (dependency
+ from: (component_label)
+ type: (dependency_type)
+ to: (component_label))))
+
+==================
+Note
+==================
+
+[Note] (5, 35) This is a test note
+
+---
+
+(source_file
+ (entity
+ (note
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ text: (text))))
+
+==================
+Note with case variation
+==================
+
+[note] (10, 20) Another note
+
+---
+
+(source_file
+ (entity
+ (note
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ text: (text))))
+
+==================
+Evolution positive
+==================
+
+[Evolution] Tinker +20
+
+---
+
+(source_file
+ (entity
+ (evolution
+ component: (component_label)
+ sign: (sign)
+ value: (real_number))))
+
+==================
+Evolution negative
+==================
+
+[Evolution] c -20
+
+---
+
+(source_file
+ (entity
+ (evolution
+ component: (component_label)
+ sign: (sign)
+ value: (real_number))))
+
+==================
+Evolution with decimal
+==================
+
+[evolution] Node +0.5
+
+---
+
+(source_file
+ (entity
+ (evolution
+ component: (component_label)
+ sign: (sign)
+ value: (real_number))))
+
+==================
+Inertia
+==================
+
+[Inertia] Soldier
+
+---
+
+(source_file
+ (entity
+ (inertia
+ component: (component_label))))
+
+==================
+Inertia case variation
+==================
+
+[inertia] Database
+
+---
+
+(source_file
+ (entity
+ (inertia
+ component: (component_label))))
+
+==================
+Stage I
+==================
+
+[i] 15
+
+---
+
+(source_file
+ (entity
+ (stage
+ number: (stage_number)
+ value: (real_number))))
+
+==================
+Stage II
+==================
+
+[ii] 35
+
+---
+
+(source_file
+ (entity
+ (stage
+ number: (stage_number)
+ value: (real_number))))
+
+==================
+Stage III
+==================
+
+[iii] 80
+
+---
+
+(source_file
+ (entity
+ (stage
+ number: (stage_number)
+ value: (real_number))))
+
+==================
+Stage IV
+==================
+
+[IV] 95
+
+---
+
+(source_file
+ (entity
+ (stage
+ number: (stage_number)
+ value: (real_number))))
+
+==================
+Stage with decimal
+==================
+
+[I] 12.5
+
+---
+
+(source_file
+ (entity
+ (stage
+ number: (stage_number)
+ value: (real_number))))
+
+==================
+Group simple
+==================
+
+[Group] User, Database
+
+---
+
+(source_file
+ (entity
+ (group
+ component: (component_label)
+ component: (component_label))))
+
+==================
+Group multiple components
+==================
+
+[Group] Tailor2, Tailor7, Tailor11, Tailor12
+
+---
+
+(source_file
+ (entity
+ (group
+ component: (component_label)
+ component: (component_label)
+ component: (component_label)
+ component: (component_label))))
+
+==================
+Group case variation
+==================
+
+[group] A, B, C
+
+---
+
+(source_file
+ (entity
+ (group
+ component: (component_label)
+ component: (component_label)
+ component: (component_label))))
+
+==================
+Multiple entities
+==================
+
+User (0.9, 0.5)
+Database (0.3, 0.2) [square]
+User -> Database
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))
+ shape: (shape
+ (shape_label))))
+ (entity
+ (dependency
+ from: (component_label)
+ type: (dependency_type)
+ to: (component_label))))
+
+==================
+Empty lines
+==================
+
+User (0.9, 0.5)
+
+Database (0.3, 0.2)
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number)))))
+
+==================
+Real number variations
+==================
+
+a (1, 2)
+b (.5, .75)
+c (0.5, 10)
+d (12.34, 56.78)
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number)))))
diff --git a/test/corpus/ignored_lines.txt b/test/corpus/ignored_lines.txt
new file mode 100644
index 0000000..1075122
--- /dev/null
+++ b/test/corpus/ignored_lines.txt
@@ -0,0 +1,84 @@
+==================
+Invalid line ignored
+==================
+User (0.9, 0.5)
+This is not a valid entity
+Database (0.3, 0.2)
+
+---
+
+(source_file
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (ERROR
+ (component_label))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number)))))
+
+==================
+Multiple invalid lines
+==================
+test line
+another test
+User (0.9, 0.5)
+random text here
+
+---
+
+(source_file
+ (ERROR
+ (component_label))
+ (ERROR
+ (component_label))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (ERROR
+ (component_label)))
+
+==================
+Mixed valid and invalid
+==================
+OK so this is text
+User (0.9, 0.5)
+More random text
+User -> Database
+[Evolution] User +10
+Some more invalid stuff
+
+---
+
+(source_file
+ (ERROR
+ (component_label))
+ (entity
+ (component
+ label: (component_label)
+ position: (position
+ x: (real_number)
+ y: (real_number))))
+ (ERROR
+ (component_label))
+ (entity
+ (dependency
+ from: (component_label)
+ type: (dependency_type)
+ to: (component_label)))
+ (entity
+ (evolution
+ component: (component_label)
+ sign: (sign)
+ value: (real_number)))
+ (ERROR
+ (component_label)))
diff --git a/test_simple.wmap b/test_simple.wmap
new file mode 100644
index 0000000..5163569
--- /dev/null
+++ b/test_simple.wmap
@@ -0,0 +1 @@
+User (0.9, 0.5)
diff --git a/tree-sitter.json b/tree-sitter.json
new file mode 100644
index 0000000..b0d4a0b
--- /dev/null
+++ b/tree-sitter.json
@@ -0,0 +1,42 @@
+{
+ "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json",
+ "grammars": [
+ {
+ "name": "wmap",
+ "camelcase": "Wmap",
+ "title": "Wmap",
+ "scope": "source.wmap",
+ "file-types": [
+ "wmap"
+ ],
+ "injection-regex": "^wmap$",
+ "class-name": "TreeSitterWmap"
+ }
+ ],
+ "metadata": {
+ "version": "1.0.0",
+ "license": "AGPL-v3",
+ "description": "Parser for wmap Formatted Wardley Maps",
+ "authors": [
+ {
+ "name": "Rubén Beltrán del Río",
+ "email": "wmap.parser@r.bdr.sh",
+ "url": "https://r.bdr.sh"
+ }
+ ],
+ "links": {
+ "repository": "https://git.sr.ht/~rbdr/tree-sitter-wmap",
+ "funding": ""
+ }
+ },
+ "bindings": {
+ "c": true,
+ "go": true,
+ "java": false,
+ "node": true,
+ "python": true,
+ "rust": true,
+ "swift": true,
+ "zig": false
+ }
+} \ No newline at end of file
diff --git a/wmap-spec.ebnf b/wmap-spec.ebnf
new file mode 100644
index 0000000..6ad79c6
--- /dev/null
+++ b/wmap-spec.ebnf
@@ -0,0 +1,52 @@
+(* wmap Language Specification *)
+
+(* Top-level structure *)
+program = { line } ;
+line = ( entity line_ending ) | ( ignored_line line_ending ) | line_ending ;
+
+entity = component | dependency | note | stage | group | inertia | evolution ;
+ignored_line = ? any sequence of tokens that doesn't match entity grammar ? ;
+
+(* Entities *)
+component = component_label position [ shape ] ;
+dependency = component_label dependency_type component_label ;
+note = case_insensitive("[Note]") position text ;
+stage = "[" stage_number "]" real_number ;
+group = case_insensitive("[Group]") component_label { "," component_label } ;
+inertia = case_insensitive("[Inertia]") component_label ;
+evolution = case_insensitive("[Evolution]") component_label sign real_number ;
+
+(* Component definitions *)
+position = "(" real_number "," real_number ")" ;
+shape = "[" shape_label "]" ;
+dependency_type = "--" | "->" ;
+sign = "+" | "-" ;
+
+(* Terminal symbols *)
+component_label = preserved_case( { character - ( "-" | "+" | "," | "[" | "]" | "(" | ")" | line_ending ) } ) ;
+text = preserved_case( { character - line_ending } ) ;
+real_number = [ digit ] { digit } [ "." { digit } ] | "." digit { digit } ;
+stage_number = case_insensitive( "i" | "ii" | "iii" | "iv" ) ;
+shape_label = case_insensitive( "x" | "square" | "triangle" | "circle" ) ;
+
+(* Lexical elements *)
+character = ? any Unicode character ? ;
+digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
+line_ending = lf | crlf | cr ;
+lf = ? U+000A (Line Feed) ? ;
+cr = ? U+000D (Carriage Return) ? ;
+crlf = cr lf ;
+
+(* Lexical rules *)
+case_insensitive(x) = ? case-insensitive match of x ? ;
+preserved_case(x) = ? case-insensitive match of x, preserving original case for display ? ;
+
+(* Notes *)
+(* - Keywords and shape labels are case-insensitive *)
+(* - Component labels are case-insensitive for matching but preserve original case *)
+(* - Text content preserves original case *)
+(* - Whitespace (except line endings) may appear between any tokens *)
+(* - Each line contains exactly one entity *)
+(* - Invalid lines should be ignored *)
+(* - Line endings support Unix (LF), Windows (CRLF), and classic Mac (CR) formats *)
+(* - CRLF is treated as a single line ending, not separate CR and LF *)