Add your language

Teach this build to index a language it does not ship, with a seed under .orch/langs, and contribute it upstream when it works.

Goal#

Make the symbol index see your language. Twenty-nine ship embedded; a thirtieth — an in-house DSL, a dialect, a language nobody has sent us yet — goes in .orch/langs/<id>.json, beside the gates you write in .orch/gates/user/.

Until a seed exists for it, a file in that language is a file Orchemax cannot read: search_shared_symbols returns nothing for it, the duplicate gate has nothing to compare, and trace_path stops at its edge.

The seed#

One JSON document per language:

{
  "id": "cobol",
  "exts": [".cbl", ".cob"],
  "comment": "*>",
  "rules": [
    {
      "kind": "unit",
      "pattern": "(?mi)^\\s*PROGRAM-ID\\.\\s+([A-Z0-9-]+)",
      "name_group": 1
    },
    {
      "kind": "func",
      "pattern": "(?mi)^\\s*([A-Z0-9-]+)\\s+SECTION\\.",
      "name_group": 1
    }
  ]
}
Field Meaning
id The language id. Defaults to the file name when omitted
exts Extensions it claims. An extension already claimed by a shipped language is taken over
comment Line-comment marker (//, #, --, *>) — what the comment gates strip before judging
aliases Other names that resolve to this language
rules The declaration patterns. Omit for a language with a native engine
engine go-ast only, and only Go uses it: a real parser instead of regexes
imposed_names Member names the language or its runtime fixes — see below
bom_required The compiler reads a file with no byte-order mark in the machine’s legacy code page — see below

A rule#

pattern is a Go regexp run over every file of that language; name_group is the capture holding the declared name; kind labels it (func, type, unit, const, …).

params_group is the capture holding the parameter list, and it is the difference between comparing signatures and comparing names. Without it two overloads of one name normalize to the same string, so the duplicate gate reports a duplicate for every overload, and "this exists in another project, promote it to shared" fires on homonyms. Twenty-six of the twenty-eight shipped regex seeds carry one; shell and haskell do not, because neither declares parameters in the line that names the function, and a rule that cannot see a parameter list omits it rather than guessing.

imposed_names are names the language chose, not the author: Delphi frees resources in Destroy and only there, so ninety classes with resources means ninety Destroy, which is conformance and not duplication. Names listed here are exempt from the name half of the duplicate gate; the similar-body half still judges them, because two destructors that free different fields read differently.

bom_required is true for Delphi and for nothing else that ships. Delphi reads a unit with no UTF-8 byte-order mark in whatever code page the machine runs, so a file holding any byte above ASCII — any script, not only accents — compiles and ships every one of those characters as two. With the flag set, orch guard encoding denies that file and says so. Set it only where the toolchain really behaves this way: a BOM is noise everywhere else, and Go rejects one outright.

Install it and check#

mkdir .orch/langs
# write .orch/langs/cobol.json
orch workspace
own languages: cobol (.cbl .cob) — from .orch/langs

That line appears only when a seed loaded. Then index and ask for a symbol:

orch index

A seed that will not compile fails the load, naming the file and the rule:

language seed: C:\ws\.orch\langs\cobol.json: cobol rule 0: error parsing regexp: …

Nothing else runs until it is fixed — a regex nobody can read means the index is wrong for every file of that language, and that is not a quiet condition.

Override a language that already ships#

Use the id it ships under. The seed replaces the shipped one rule for rule for this workshop, which is how you narrow a pattern that is too loose for your codebase without waiting for a release.

To point one more extension at a language whose rules are already right, you do not need a seed at all — parsers in orch.yaml maps an extension onto an existing id:

parsers:
  .inc: php

Send it upstream#

The embedded seeds are a copy of the public orchemax-orch repository's langs/, and a test fails when the two disagree. A seed that works in your workshop is one PR away from being one everybody gets: add langs/<id>.json, append the id to langs/catalog.json, and say in the PR whether Orchemax should embed it. CONTRIBUTING.md there has the steps.