Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/BNFC.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Description:
a pretty-printer as a Haskell, Agda, C, C++, Java, or Ocaml module,
an XML representation,
a LaTeX file containing a readable specification of the language.
data-files:
test/BNFC/Backend/TreeSitter/*.cf
test/BNFC/Backend/TreeSitter/*.expected.js

-- Support range when build with cabal
tested-with:
Expand Down Expand Up @@ -272,6 +275,7 @@ library
-- Tree-sitter backend
BNFC.Backend.TreeSitter
BNFC.Backend.TreeSitter.CFtoTreeSitter
BNFC.Backend.TreeSitter.MatchesEmpty
BNFC.Backend.TreeSitter.RegToJSReg

----- Testing --------------------------------------------------------------
Expand Down
72 changes: 69 additions & 3 deletions source/src/BNFC/Backend/TreeSitter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,86 @@

module BNFC.Backend.TreeSitter where

import Prelude hiding ((<>))
import System.FilePath

import BNFC.Backend.Base
import BNFC.Backend.TreeSitter.CFtoTreeSitter (cfToTreeSitter)
import BNFC.Backend.Common.Makefile(mkMakefile, mkRule, mkVar)
import BNFC.GetCF(fixTokenCats)
import BNFC.Utils(cstring, kebabCase_, snakeCase_, camelCase_)
import BNFC.CF
import BNFC.Options hiding (Backend)
import BNFC.PrettyPrint

-- | Entry point: create grammar.js file
makeTreeSitter :: SharedOptions -> CF -> Backend
makeTreeSitter opts cf = do
mkfile "grammar.js" comment (render $ cfToTreeSitter name cf)

mkfile (dir </> "grammar.js") comment $
render (cfToTreeSitter name wordCat cf)

mkfile (dir </> "tree-sitter.json") (const "") $
render (treeSitterJson name)

mkMakefile (fmap (dir </>) (optMake opts)) $
const treeSitterMakefile

where
name = lang opts
name = snakeCase_ (lang opts)
dir = "tree-sitter-" ++ kebabCase_ (lang opts)
wordCat = fixTokenCats (tokenNames cf) (strToCat (treeSitterWord opts))

comment :: String -> String
comment = ("// " ++)

-- | TODO: Add Makefile generation for tree-sitter
treeSitterMakefile :: Doc
treeSitterMakefile = vcat'
[ mkVar "TREE_SITTER" "tree-sitter"
, ".PHONY: parse clean"
, mkRule "src/parser.c" ["grammar.js"] ["$(TREE_SITTER) generate"]
, mkRule "parse" ["src/parser.c"] ["$(TREE_SITTER) parse --cst"]
, mkRule "clean" [] ["rm -rfv src"]
]

treeSitterJson :: String -> Doc
treeSitterJson name =
jsonObject
[ p "$schema" $ str "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json"
, p "grammars" $ jsonArray1 $ jsonObject
[ p "name" $ str $ snakeCase_ name
, p "camelcase" $ str $ camelCase_ name
, p "scope" $ str $ "source." ++ snakeCase_ name
]
, p "metadata" $ jsonObject
[ p "version" $ str "0.1.0"
, p "authors" $ jsonArray1 $ jsonObject [p "name" $ str "BNFC"]
]
, p "bindings" $ jsonObject
[ p "c" "false"
, p "go" "false"
, p "java" "false"
, p "node" "false"
, p "python" "false"
, p "rust" "false"
, p "swift" "false"
, p "zig" "false"
]
]
where
str = cstring
p = (,)

jsonLines :: [Doc] -> Doc
jsonLines = nest 2 . sep . punctuate ","

jsonObject :: [(String, Doc)] -> Doc
jsonObject pairs = "{" $$ jsonLines (map jsonPair pairs) <+> "}"
where
jsonPair (k, v) = cstring k <> ":" <+> v

jsonArray :: [Doc] -> Doc
jsonArray elems = "[" $$ jsonLines elems <+> "]"

jsonArray1 :: Doc -> Doc
jsonArray1 = jsonArray . pure
Loading