{-
    BNF Converter: TreeSitter Grammar Generator
    Copyright (C) 2004  Author:  Markus Forsberg, Michael Pellauer,
                                 Bjorn Bringert

    Description   : This module converts BNFC Reg to Javascript regular
                    expressions that is used in

    Author        : Kangjing Huang (huangkangjing@gmail.com)
    Created       : 23 Nov, 2023

-}
{-# LANGUAGE LambdaCase #-}

module BNFC.Backend.TreeSitter.RegToJSReg (printRegJSReg) where

import BNFC.Abs

printRegJSReg :: Reg -> String
printRegJSReg :: Reg -> String
printRegJSReg Reg
r = String
"/" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
render (Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
0 Reg
r) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/"

-- There is no space-based formatting for javascript regex
-- We just concat everything together
render :: [String] -> String
render :: [String] -> String
render = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat

parenth :: [String] -> [String]
parenth :: [String] -> [String]
parenth [String]
s = [String
"("] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
s [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
")"]

-- Printer class
class Print a where
  prt :: Int -> a -> [String]

prPrec :: Int -> Int -> [String] -> [String]
prPrec :: Int -> Int -> [String] -> [String]
prPrec Int
i Int
j = if Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
j then [String] -> [String]
parenth else [String] -> [String]
forall a. a -> a
id

-- | reserved characters for Javascript regex format, sourced from
--   https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
reserved :: String
reserved :: String
reserved = String
".*+?^${}()|[]\\/"

-- | reserved characters for alt expressions in Javascript regex format
reservedAlt :: String
reservedAlt :: String
reservedAlt = String
"^[]-\\/"

-- | Pattern for matching empty string in Javascript regex format
emptyPat :: String
emptyPat :: String
emptyPat = String
" [^\\u0000-\\uFFFF]?"

-- | escape character according to Javascript regex format
escapeCharFrom :: String -> Char -> String
escapeCharFrom :: String -> Char -> String
escapeCharFrom String
reservedChars Char
x
  | Char
x Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
reservedChars = Char
'\\' Char -> String -> String
forall a. a -> [a] -> [a]
: [Char
x]
  | Bool
otherwise = [Char
x]

escapeChar :: Char -> String
escapeChar :: Char -> String
escapeChar = String -> Char -> String
escapeCharFrom String
reserved

escapeStr :: String -> String
escapeStr :: String -> String
escapeStr = (Char -> String) -> String -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
escapeChar

escapeCharAlt :: Char -> String
escapeCharAlt :: Char -> String
escapeCharAlt = String -> Char -> String
escapeCharFrom String
reservedAlt

escapeStrAlt :: String -> String
escapeStrAlt :: String -> String
escapeStrAlt = (Char -> String) -> String -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
escapeChar

instance Print Identifier where
  prt :: Int -> Identifier -> [String]
prt Int
_ (Identifier ((Int, Int)
_, String
i)) = [String
i]

instance Print Reg where
  prt :: Int -> Reg -> [String]
prt Int
i = \case
    RSeq Reg
reg0 Reg
reg -> Int -> Int -> [String] -> [String]
prPrec Int
i Int
2 ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
2 Reg
reg0 [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
3 Reg
reg
    RAlt Reg
reg0 Reg
reg -> Int -> Int -> [String] -> [String]
prPrec Int
i Int
1 ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
1 Reg
reg0 [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++  [String
"|"] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
2 Reg
reg
    -- Javascript regex does not support general set difference
    RMinus Reg
reg0 Reg
REps -> Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
i Reg
reg0 -- REps is identity for difference
    RMinus Reg
RAny (RChar Char
c) -> [String
"[^" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
escapeCharAlt Char
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"]
    RMinus Reg
RAny (RAlts String
s) -> [String
"[^" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeStrAlt String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"]
    -- TODO: It is possible to use external scanners in tree-sitter:
    -- https://tree-sitter.github.io/tree-sitter/creating-parsers#external-scanners
    -- to support general set differences in the future
    RMinus Reg
_ Reg
_ -> String -> [String]
forall a. HasCallStack => String -> a
error String
"Javascript regex does not support general set differences"
    RStar Reg
reg -> Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
3 Reg
reg [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"*"]
    RPlus Reg
reg -> Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
3 Reg
reg [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"+"]
    ROpt Reg
reg -> Int -> Reg -> [String]
forall a. Print a => Int -> a -> [String]
prt Int
3 Reg
reg [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"?"]
    Reg
REps -> [String
emptyPat]
    RChar Char
c -> [Char -> String
escapeChar Char
c]
    RAlts String
str -> [String
"[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeStrAlt String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"]
    RSeqs String
str -> [String -> String
escapeStr String
str]
    Reg
RDigit -> [String
"\\d"]
    Reg
RLetter -> [String
"[a-zA-Z]"]
    Reg
RUpper -> [String
"[A-Z]"]
    Reg
RLower -> [String
"[a-z]"]
    Reg
RAny -> [String
"."]