From 7eff42993222ea88dde163e0b7baef316fe6a5a8 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Wed, 4 Oct 2023 00:33:15 +0300 Subject: [PATCH 01/70] [ANTLR4] added generation for ANTLR4 files without target-specific code and with rule labels --- source/BNFC.cabal | 7 + source/main/Main.hs | 2 + source/src/BNFC/Backend/Antlr.hs | 35 ++++ .../src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs | 173 ++++++++++++++++++ .../BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 154 ++++++++++++++++ .../src/BNFC/Backend/Antlr/RegToAntlrLexer.hs | 90 +++++++++ source/src/BNFC/Backend/Antlr/Utils.hs | 15 ++ source/src/BNFC/Options.hs | 7 +- 8 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 source/src/BNFC/Backend/Antlr.hs create mode 100644 source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs create mode 100644 source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs create mode 100644 source/src/BNFC/Backend/Antlr/RegToAntlrLexer.hs create mode 100644 source/src/BNFC/Backend/Antlr/Utils.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 582a5dd1..3801b87b 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -254,6 +254,13 @@ library BNFC.Backend.Java.RegToAntlrLexer BNFC.Backend.Java.Utils + -- Antlr4 backend + BNFC.Backend.Antlr + BNFC.Backend.Antlr.CFtoAntlr4Lexer + BNFC.Backend.Antlr.CFtoAntlr4Parser + BNFC.Backend.Antlr.RegToAntlrLexer + BNFC.Backend.Antlr.Utils + -- XML backend BNFC.Backend.XML diff --git a/source/main/Main.hs b/source/main/Main.hs index 5486d60b..1f273a93 100644 --- a/source/main/Main.hs +++ b/source/main/Main.hs @@ -25,6 +25,7 @@ import BNFC.Backend.Java import BNFC.Backend.Latex import BNFC.Backend.OCaml import BNFC.Backend.Pygments +import BNFC.Backend.Antlr import BNFC.CF (CF) import BNFC.GetCF import BNFC.Options hiding (make, Backend) @@ -80,4 +81,5 @@ maketarget = \case TargetJava -> makeJava TargetOCaml -> makeOCaml TargetPygments -> makePygments + TargetAntlr -> makeAntlr TargetCheck -> error "impossible" diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs new file mode 100644 index 00000000..dcea77ab --- /dev/null +++ b/source/src/BNFC/Backend/Antlr.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE RecordWildCards #-} + +module BNFC.Backend.Antlr ( makeAntlr ) where + +import Prelude hiding ((<>)) +import System.FilePath ((), pathSeparator) + +import BNFC.Utils +import BNFC.CF +import BNFC.Options as Options +import BNFC.Backend.Base +import BNFC.Backend.Antlr.CFtoAntlr4Lexer +import BNFC.Backend.Antlr.CFtoAntlr4Parser + +makeAntlr :: SharedOptions -> CF -> MkFiles () +makeAntlr Options{..} cf = do + let packageBase = maybe id (+.+) inPackage pkg + dirBase = pkgToDir packageBase + + let (lex, env) = lexerFun packageBase cf + -- Where the lexer file is created. lex is the content! + mkfile (dirBase mkG4Name "Lexer") mkAntlrComment lex + -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake + -- +++ toolversion lexmake ++ ")" + let parserContent = parserFun packageBase cf linenumbers env + mkfile (dirBase mkG4Name "Parser") mkAntlrComment parserContent + where + lexerFun = cf2AntlrLex + parserFun = cf2AntlrParse + pkg = mkName [] CamelCase lang + pkgToDir = replace '.' pathSeparator + mkG4Name name = lang ++ name ++ ".g4" + +mkAntlrComment :: String -> String +mkAntlrComment = ("// -*- Antlr4 -*- " ++) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs new file mode 100644 index 00000000..a08dc020 --- /dev/null +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs @@ -0,0 +1,173 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} + +{- + BNF Converter: Java Antlr4 Lexer generator + Copyright (C) 2015 Author: Gabriele Paganelli + + Description : This module generates the Antlr4 input file. + Based on CFtoJLex15.hs + + Author : Gabriele Paganelli (gapag@distruzione.org) + Created : 15 Oct, 2015 + +-} + +module BNFC.Backend.Antlr.CFtoAntlr4Lexer ( cf2AntlrLex ) where + +import Prelude hiding ((<>)) + +import Text.PrettyPrint +import BNFC.CF +import BNFC.Backend.Antlr.RegToAntlrLexer +import BNFC.Backend.Common.NamedVariables + +-- | Creates a lexer grammar. +-- Since antlr token identifiers must start with an uppercase symbol, +-- I prepend "Surrogate_id_SYMB_" to the identifier. +-- This introduces risks of clashes if somebody uses the same identifier for +-- user defined tokens. This is not handled. +-- returns the environment because the parser uses it. +cf2AntlrLex :: String -> CF -> (Doc, KeywordEnv) +cf2AntlrLex lang cf = (,env) $ vcat + [ prelude lang + , cMacros + -- unnamed symbols (those in quotes, not in token definitions) + , lexSymbols env + , restOfLexerGrammar cf + ] + where + env = zip (cfgSymbols cf ++ reservedWords cf) $ map (("Surrogate_id_SYMB_" ++) . show) [0 :: Int ..] + + +-- | File prelude +prelude :: String -> Doc +prelude lang = vcat + [ "// Lexer definition for use with Antlr4" + , "lexer grammar" <+> text lang <> "Lexer;" + ] + +--For now all categories are included. +--Optimally only the ones that are used should be generated. +cMacros :: Doc +cMacros = vcat + [ "// Predefined regular expressions in BNFC" + , frg "LETTER : CAPITAL | SMALL" + , frg "CAPITAL : [A-Z\\u00C0-\\u00D6\\u00D8-\\u00DE]" + , frg "SMALL : [a-z\\u00DF-\\u00F6\\u00F8-\\u00FF]" + , frg "DIGIT : [0-9]" + ] + where frg a = "fragment" <+> a <+> ";" + +escapeChars :: String -> String +escapeChars = concatMap escapeCharInSingleQuotes + +-- | +-- >>> lexSymbols [("foo","bar")] +-- bar : 'foo' ; +-- >>> lexSymbols [("\\","bar")] +-- bar : '\\' ; +-- >>> lexSymbols [("/","bar")] +-- bar : '/' ; +-- >>> lexSymbols [("~","bar")] +-- bar : '~' ; +lexSymbols :: KeywordEnv -> Doc +lexSymbols ss = vcat $ map transSym ss + where + transSym (s,r) = text r <> " : '" <> text (escapeChars s) <> "' ;" + +-- | Writes rules for user defined tokens, and, if used, the predefined BNFC tokens. +restOfLexerGrammar :: CF -> Doc +restOfLexerGrammar cf = vcat + [ lexComments (comments cf) + , "" + , userDefTokens + , ifString strdec + , ifChar chardec + , ifC catDouble [ + "// Double predefined token type", + "DOUBLE : DIGIT+ '.' DIGIT+ ('e' '-'? DIGIT+)?;" + ] + , ifC catInteger [ + "//Integer predefined token type", + "INTEGER : DIGIT+;" + ] + , ifC catIdent [ + "// Identifier token type" , + "fragment" , + "IDENTIFIER_FIRST : LETTER | '_';", + "IDENT : IDENTIFIER_FIRST (IDENTIFIER_FIRST | DIGIT)*;" + ] + , "// Whitespace" + , "WS : (' ' | '\\r' | '\\t' | '\\n' | '\\f')+ -> skip;" + , "// Escapable sequences" + , "fragment" + , "Escapable : ('\"' | '\\\\' | 'n' | 't' | 'r' | 'f');" + , "ErrorToken : . ;" + , ifString stringmodes + , ifChar charmodes + ] + where + ifC cat s = if isUsedCat cf (TokenCat cat) then vcat s else "" + ifString = ifC catString + ifChar = ifC catChar + strdec = [ "// String token type" + , "STRING : '\"' -> more, mode(STRINGMODE);" + ] + chardec = ["CHAR : '\\'' -> more, mode(CHARMODE);"] + userDefTokens = vcat + [ text name <> " : " <> text (printRegJLex exp) <> ";" + | (name, exp) <- tokenPragmas cf ] + stringmodes = [ "mode STRESCAPE;" + , "STRESCAPED : Escapable -> more, popMode ;" + , "mode STRINGMODE;" + , "STRINGESC : '\\\\' -> more , pushMode(STRESCAPE);" + , "STRINGEND : '\"' -> type(STRING), mode(DEFAULT_MODE);" + , "STRINGTEXT : ~[\"\\\\] -> more;" + ] + charmodes = [ "mode CHARMODE;" + , "CHARANY : ~['\\\\] -> more, mode(CHAREND);" + , "CHARESC : '\\\\' -> more, pushMode(CHAREND),pushMode(ESCAPE);" + , "mode ESCAPE;" + , "ESCAPED : (Escapable | '\\'') -> more, popMode ;" + , "mode CHAREND;" + , "CHARENDC : '\\'' -> type(CHAR), mode(DEFAULT_MODE);" + ] + +lexComments :: ([(String, String)], [String]) -> Doc +lexComments ([],[]) = "" +lexComments (m,s) = vcat + (prod "COMMENT_antlr_builtin" lexSingleComment s ++ + prod "MULTICOMMENT_antlr_builtin" lexMultiComment m ) + + where + prod bg lc ty = [bg, ": ("] ++ punctuate "|" (map lc ty) ++ skiplex + skiplex = [") -> skip;"] + +-- | Create lexer rule for single-line comments. +-- +-- >>> lexSingleComment "--" +-- '--' ~[\r\n]* (('\r'? '\n')|EOF) +-- +-- >>> lexSingleComment "\"" +-- '"' ~[\r\n]* (('\r'? '\n')|EOF) +lexSingleComment :: String -> Doc +lexSingleComment c = + "'" <>text (escapeChars c) <> "' ~[\\r\\n]* (('\\r'? '\\n')|EOF)" + +-- | Create lexer rule for multi-lines comments. +-- +-- There might be a possible bug here if a language includes 2 multi-line +-- comments. They could possibly start a comment with one character and end it +-- with another. However this seems rare. +-- +-- >>> lexMultiComment ("{-", "-}") +-- '{-' (.)*? '-}' +-- +-- >>> lexMultiComment ("\"'", "'\"") +-- '"\'' (.)*? '\'"' +lexMultiComment :: (String, String) -> Doc +lexMultiComment (b,e) = + "'" <> text (escapeChars b) + <>"' (.)*? '"<> text (escapeChars e) + <> "'" diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs new file mode 100644 index 00000000..e22f4e76 --- /dev/null +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -0,0 +1,154 @@ +{-# LANGUAGE LambdaCase #-} + +module BNFC.Backend.Antlr.CFtoAntlr4Parser ( cf2AntlrParse ) where + +import Data.Foldable ( toList ) +import Data.Maybe + +import BNFC.CF +import BNFC.Options ( RecordPositions(..) ) +import BNFC.Utils ( (+++), applyWhen ) + +import BNFC.Backend.Antlr.Utils +import BNFC.Backend.Common.NamedVariables + +-- Type declarations + +-- | A definition of a non-terminal by all its rhss, +-- together with parse actions. +data PDef = PDef + { _pdNT :: Maybe String + -- ^ If given, the name of the lhss. Usually computed from 'pdCat'. + , _pdCat :: Cat + -- ^ The category to parse. + , _pdAlts :: [(Pattern, Maybe Fun)] + -- ^ The possible rhss with actions. If 'null', skip this 'PDef'. + -- Where 'Nothing', skip ANTLR rule label. + } +type Rules = [PDef] +type Pattern = String +type IndexedCat = (Cat, Int) + +-- | Creates the ANTLR parser grammar for this CF. +--The environment comes from CFtoAntlr4Lexer +cf2AntlrParse :: String -> CF -> RecordPositions -> KeywordEnv -> String +cf2AntlrParse lang cf _ env = unlines + [ header + , tokens + , "" + -- Generate start rules [#272] + -- _X returns [ dX result ] : x=X EOF { $result = $x.result; } + , prRules $ map entrypoint catsWithIdx + -- Generate regular rules + , prRules $ rulesForAntlr4 cf env + ] + + where + header :: String + header = unlines + [ "// Parser definition for use with ANTLRv4" + , "parser grammar" +++ lang ++ "Parser;" + ] + tokens :: String + tokens = unlines + [ "options {" + , " tokenVocab = " ++ lang ++ "Lexer;" + , "}" + ] + catsWithIdx :: [IndexedCat] + catsWithIdx = zip (toList $ allEntryPoints cf) [1..] + +-- | Generate start rule to help ANTLR. +-- +-- @start_X returns [ X result ] : x=X EOF { $result = $x.result; } # Start_X@ +-- +entrypoint :: IndexedCat -> PDef +entrypoint (cat, idx) = + PDef (Just nt) cat [(pat, fun)] + where + nt = firstLowerCase $ startSymbol $ identCat cat + pat = catToNT cat +++ "EOF" + fun = Just (startSymbol $ identCat cat ++ show idx) + +--The following functions are a (relatively) straightforward translation +--of the ones in CFtoHappy.hs +rulesForAntlr4 :: CF -> KeywordEnv -> Rules +rulesForAntlr4 cf env = map mkOne getrules + where + getrules = ruleGroups cf + mkOne (cat,rules) = constructRule cf env rules cat + +-- | For every non-terminal, we construct a set of rules. A rule is a sequence of +-- terminals and non-terminals, and an action to be performed. +constructRule :: CF -> KeywordEnv -> [Rule] -> NonTerminal -> PDef +constructRule cf env rules nt = + PDef Nothing nt $ + [ ( p, Just label ) + | (index, r0) <- zip [1..] rules + , let b = isConsFun (funRule r0) && elem (valCat r0) (cfgReversibleCats cf) + , let r = applyWhen b revSepListRule r0 + , let p = generatePattern index env r + , let label = wpThing (funRule r) + ] + +-- | Generate patterns and a set of metavariables indicating +-- where in the pattern the non-terminal +-- >>> generatePatterns 2 [] $ npRule "myfun" (Cat "A") [] Parsable +-- (" /* empty */ ",[]) +-- >>> generatePatterns 3 [("def", "_SYMB_1")] $ npRule "myfun" (Cat "A") [Right "def", Left (Cat "B")] Parsable +-- ("_SYMB_1 p_3_2=b",[("p_3_2",B)]) +generatePattern :: Int -> KeywordEnv -> Rule -> Pattern +generatePattern ind env r = + case rhsRule r of + [] -> " /* empty */ " + its -> unwords $ mapMaybe (uncurry mkIt) nits + where + nits = zip [1 :: Int ..] its + var i = "p_" ++ show ind ++"_"++ show i -- TODO: is ind needed for ANTLR? + mkIt i = \case + Left c -> Just $ var i ++ "=" ++ catToNT c + Right s -> lookup s env + +catToNT :: Cat -> String +catToNT = \case + TokenCat "Ident" -> "IDENT" + TokenCat "Integer" -> "INTEGER" + TokenCat "Char" -> "CHAR" + TokenCat "Double" -> "DOUBLE" + TokenCat "String" -> "STRING" + c | isTokenCat c -> identCat c + | otherwise -> firstLowerCase $ getRuleName $ identCat c + +-- | Puts together the pattern and actions and returns a string containing all +-- the rules. +prRules :: Rules -> String +prRules = concatMap $ \case + + -- No rules: skip. + PDef _mlhs _nt [] -> "" + + -- At least one rule: print! + PDef mlhs nt (rhs : rhss) -> unlines $ concat + + -- The definition header: lhs and type. + [ [ unwords [fromMaybe nt' mlhs] + ] + -- The first rhs. + , alternative " :" rhs + -- The other rhss. + , concatMap (alternative " |") rhss + -- The definition footer. + , [ " ;" ] + ] + where + alternative sep (p, label) = unwords [ sep , p ] : [ unwords [ " #" , antlrRuleLabel l ] | Just l <- [label] ] + + catid = identCat nt + nt' = getRuleName $ firstLowerCase catid + antlrRuleLabel :: Fun -> String + antlrRuleLabel fnc + | isNilFun fnc = catid ++ "_Empty" + | isOneFun fnc = catid ++ "_AppendLast" + | isConsFun fnc = catid ++ "_PrependFirst" + | isCoercion fnc = "Coercion_" ++ catid + | otherwise = getLabelName fnc diff --git a/source/src/BNFC/Backend/Antlr/RegToAntlrLexer.hs b/source/src/BNFC/Backend/Antlr/RegToAntlrLexer.hs new file mode 100644 index 00000000..3a1c50bd --- /dev/null +++ b/source/src/BNFC/Backend/Antlr/RegToAntlrLexer.hs @@ -0,0 +1,90 @@ +module BNFC.Backend.Antlr.RegToAntlrLexer (printRegJLex, escapeCharInSingleQuotes) where + +-- modified from RegToJLex.hs + +import Data.Char (ord) +import Numeric (showHex) + +import BNFC.Abs + +-- the top-level printing method +printRegJLex :: Reg -> String +printRegJLex = render . prt 0 + +-- you may want to change render and parenth + +render :: [String] -> String +render = rend (0 :: Int) where + rend i ss = case ss of + "[" :ts -> cons "[" $ rend i ts + "(" :ts -> cons "(" $ rend i ts + t : "," :ts -> cons t $ space "," $ rend i ts + t : ")" :ts -> cons t $ cons ")" $ rend i ts + t : "]" :ts -> cons t $ cons "]" $ rend i ts + t :ts -> space t $ rend i ts + _ -> "" + cons s t = s ++ t + space t s = if null s then t else t ++ s + +parenth :: [String] -> [String] +parenth ss = ["("] ++ ss ++ [")"] + +-- the printer class does the job +class Print a where + prt :: Int -> a -> [String] + +-- | Print char according to ANTLR regex format. +escapeChar :: [Char] -> Char -> String +escapeChar reserved x + | x `elem` reserved = '\\' : [x] + | i >= 65536 = "\\u{" ++ h ++ "}" + | i >= 256 || i < 32 = "\\u" ++ replicate (4 - length h) '0' ++ h + | otherwise = [x] -- issue #329, don't escape in the usual way! + where + i = ord x + h = showHex i "" + +-- | Escape character for use inside single quotes. +escapeCharInSingleQuotes :: Char -> String +escapeCharInSingleQuotes = escapeChar ['\'','\\'] + +-- The ANTLR definition of what can be in a [char set] is here: +-- https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md#lexer-rule-elements +-- > The following escaped characters are interpreted as single special characters: +-- > \n, \r, \b, \t, \f, \uXXXX, and \u{XXXXXX}. +-- > To get ], \, or - you must escape them with \. + +-- | Escape character for use inside @[char set]@. +escapeInCharSet :: Char -> String +escapeInCharSet = escapeChar [ ']', '\\', '-' ] + +prPrec :: Int -> Int -> [String] -> [String] +prPrec i j = if j prPrec i 2 (concat [prt 2 reg0 , [" "], prt 3 reg]) + RAlt reg0 reg + -> prPrec i 1 (concat [prt 1 reg0 , ["|"] , prt 2 reg]) + RMinus reg0 REps -> prt i reg0 -- REps is identity for set difference + RMinus RAny (RChar c) + -> ["~'", escapeCharInSingleQuotes c, "'"] + RMinus RAny (RAlts str) + -> concat [["~["], map escapeInCharSet str ,["]"]] + RMinus _ _ -> error "Antlr does not support general set difference" + RStar reg -> prt 3 reg ++ ["*"] + RPlus reg -> prt 3 reg ++ ["+"] + ROpt reg -> prt 3 reg ++ ["?"] + REps -> [""] + RChar c -> ["'", escapeCharInSingleQuotes c, "'"] + RAlts str -> concat [ ["["], map escapeInCharSet str, ["]"] ] + RSeqs str -> prPrec i 2 $ map show str + RDigit -> ["DIGIT"] + RLetter -> ["LETTER"] + RUpper -> ["CAPITAL"] + RLower -> ["SMALL"] + RAny -> ["[\\u0000-\\u00FF]"] diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs new file mode 100644 index 00000000..2ca252f1 --- /dev/null +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -0,0 +1,15 @@ +module BNFC.Backend.Antlr.Utils where + +import BNFC.CF (Fun) +import BNFC.Utils ( mkName, NameStyle(..)) + +getRuleName :: String -> String +getRuleName z = if z == "grammar" then z ++ "_" else z + +getLabelName :: Fun -> String +getLabelName = mkName ["Rule"] CamelCase + +-- | Make a new entrypoint NT for an existing NT. + +startSymbol :: String -> String +startSymbol = ("Start_" ++) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 09cd477e..7fca4df7 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -63,7 +63,7 @@ data Mode data Target = TargetC | TargetCpp | TargetCppNoStl | TargetHaskell | TargetHaskellGadt | TargetLatex | TargetJava | TargetOCaml | TargetPygments - | TargetCheck + | TargetCheck | TargetAntlr deriving (Eq, Bounded, Enum, Ord) -- | List of Haskell target. @@ -81,6 +81,7 @@ instance Show Target where show TargetOCaml = "OCaml" show TargetPygments = "Pygments" show TargetCheck = "Check LBNF file" + show TargetAntlr = "Antlr4" -- | Which version of Alex is targeted? data AlexVersion = Alex3 @@ -259,6 +260,7 @@ printTargetOption = ("--" ++) . \case TargetOCaml -> "ocaml" TargetPygments -> "pygments" TargetCheck -> "check" + TargetAntlr -> "antlr4" printAlexOption :: AlexVersion -> String printAlexOption = ("--" ++) . \case @@ -311,6 +313,8 @@ targetOptions = "Output a Python lexer for Pygments" , Option "" ["check"] (NoArg (\ o -> o{target = TargetCheck })) "No output. Just check input LBNF file" + , Option "" ["antlr"] (NoArg (\o -> o{target = TargetAntlr})) + "Not implemented yet." ] -- | A list of the options and for each of them, the target language @@ -525,6 +529,7 @@ instance Maintained Target where TargetOCaml -> True TargetPygments -> True TargetCheck -> True + TargetAntlr -> True instance Maintained AlexVersion where printFeature = printAlexOption From 8fa226c629a0fc59d3168d28920b6ae8c4a640bc Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Sun, 8 Oct 2023 13:36:37 +0300 Subject: [PATCH 02/70] [ANTLR4] erased rule labels for start rules --- source/main/Main.hs | 2 +- source/src/BNFC/Backend/Antlr.hs | 2 +- .../BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 19 +++++++------------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/source/main/Main.hs b/source/main/Main.hs index 1f273a93..1f2ece36 100644 --- a/source/main/Main.hs +++ b/source/main/Main.hs @@ -81,5 +81,5 @@ maketarget = \case TargetJava -> makeJava TargetOCaml -> makeOCaml TargetPygments -> makePygments - TargetAntlr -> makeAntlr + TargetAntlr -> makeAntlr TargetCheck -> error "impossible" diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index dcea77ab..fa669fca 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -32,4 +32,4 @@ makeAntlr Options{..} cf = do mkG4Name name = lang ++ name ++ ".g4" mkAntlrComment :: String -> String -mkAntlrComment = ("// -*- Antlr4 -*- " ++) +mkAntlrComment = ("// -*- ANTLRv4 -*- " ++) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index e22f4e76..5700e1c2 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -14,8 +14,7 @@ import BNFC.Backend.Common.NamedVariables -- Type declarations --- | A definition of a non-terminal by all its rhss, --- together with parse actions. +-- | A definition of a non-terminal by all its rhss data PDef = PDef { _pdNT :: Maybe String -- ^ If given, the name of the lhss. Usually computed from 'pdCat'. @@ -27,7 +26,6 @@ data PDef = PDef } type Rules = [PDef] type Pattern = String -type IndexedCat = (Cat, Int) -- | Creates the ANTLR parser grammar for this CF. --The environment comes from CFtoAntlr4Lexer @@ -36,9 +34,8 @@ cf2AntlrParse lang cf _ env = unlines [ header , tokens , "" - -- Generate start rules [#272] - -- _X returns [ dX result ] : x=X EOF { $result = $x.result; } - , prRules $ map entrypoint catsWithIdx + -- Generate start rules + , prRules $ map entrypoint $ toList $ allEntryPoints cf -- Generate regular rules , prRules $ rulesForAntlr4 cf env ] @@ -55,20 +52,18 @@ cf2AntlrParse lang cf _ env = unlines , " tokenVocab = " ++ lang ++ "Lexer;" , "}" ] - catsWithIdx :: [IndexedCat] - catsWithIdx = zip (toList $ allEntryPoints cf) [1..] -- | Generate start rule to help ANTLR. -- --- @start_X returns [ X result ] : x=X EOF { $result = $x.result; } # Start_X@ +-- @start_X : X EOF -- -entrypoint :: IndexedCat -> PDef -entrypoint (cat, idx) = +entrypoint :: Cat -> PDef +entrypoint cat = PDef (Just nt) cat [(pat, fun)] where nt = firstLowerCase $ startSymbol $ identCat cat pat = catToNT cat +++ "EOF" - fun = Just (startSymbol $ identCat cat ++ show idx) + fun = Nothing --The following functions are a (relatively) straightforward translation --of the ones in CFtoHappy.hs From aee84f207634fe6686e660cd500f8c06aa979aad Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Mon, 9 Oct 2023 22:50:55 +0300 Subject: [PATCH 03/70] [ANTLRv4] added generation of Makefile for ANTLR backend and several ANTLR targets --- source/src/BNFC/Backend/Antlr.hs | 66 +++++++++++++++++++++++++++++++- source/src/BNFC/Options.hs | 44 ++++++++++++++++++++- 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index fa669fca..c0b61978 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -3,7 +3,8 @@ module BNFC.Backend.Antlr ( makeAntlr ) where import Prelude hiding ((<>)) -import System.FilePath ((), pathSeparator) +import System.FilePath ((), pathSeparator, (<.>)) +import Text.PrettyPrint.HughesPJ (vcat) import BNFC.Utils import BNFC.CF @@ -11,6 +12,7 @@ import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser +import BNFC.Backend.Common.Makefile as MakeFile makeAntlr :: SharedOptions -> CF -> MkFiles () makeAntlr Options{..} cf = do @@ -24,12 +26,72 @@ makeAntlr Options{..} cf = do -- +++ toolversion lexmake ++ ")" let parserContent = parserFun packageBase cf linenumbers env mkfile (dirBase mkG4Name "Parser") mkAntlrComment parserContent + + MakeFile.mkMakefile optMake makefileContent where lexerFun = cf2AntlrLex parserFun = cf2AntlrParse pkg = mkName [] CamelCase lang pkgToDir = replace '.' pathSeparator - mkG4Name name = lang ++ name ++ ".g4" + mkG4Name name = lang ++ name <.> "g4" + + makeVars x = [MakeFile.mkVar n v | (n,v) <- x] + makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] + + otherFlags = unwords $ getFlags [("no-listener", not listener), ("visitor", visitor), ("Werror", wError)] + + langRef = MakeFile.refVar "LANG" + + makefileVars = vcat $ makeVars + [ ("LANG", lang) + , ("LEXER_NAME", langRef ++ "Lexer") + , ("PARSER_NAME", langRef ++ "Parser") + , ("ANTLR4", "java org.antlr.v4.Tool") + , ("DLANGUAGE", parseAntlrTarget dLanguage) + , ("OTHER_FLAGS", otherFlags) + ] + + refVarWithPrefix :: String -> String + refVarWithPrefix refVar = langRef MakeFile.refVar refVar + + rmFile :: String -> String -> String + rmFile refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext + + makefileRules = vcat $ makeRules + [ (".PHONY", ["all", "clean-g4", "remove"], []) + , ("all", [langRef], []) + , ("lexer", [refVarWithPrefix "LEXER_NAME" <.> "g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS" +++ refVarWithPrefix "LEXER_NAME" <.> "g4"]) + , ("parser", [refVarWithPrefix "PARSER_NAME" <.> "g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS" +++ refVarWithPrefix "PARSER_NAME" <.> "g4"]) + , (langRef, ["lexer", "parser"], []) + , ("clean-g4", [], + [ rmFile "LEXER_NAME" ".interp" + , rmFile "LEXER_NAME" ".tokens" + , rmFile "PARSER_NAME" ".interp" + , rmFile "PARSER_NAME" ".tokens" + ]) + , ("remove", [], ["rm -rf" +++ langRef]) + ] + + makefileContent _ = vcat [makefileVars, "", makefileRules, ""] mkAntlrComment :: String -> String mkAntlrComment = ("// -*- ANTLRv4 -*- " ++) + +parseAntlrTarget :: AntlrTarget -> String +parseAntlrTarget Java = "Java" +parseAntlrTarget CPP = "Cpp" +parseAntlrTarget CSharp = "CSharp" +parseAntlrTarget JS = "JavaScript" +parseAntlrTarget TS = "TypeScript" +parseAntlrTarget Dart = "Dart" +parseAntlrTarget Python3 = "Python3" +parseAntlrTarget PHP = "PHP" +parseAntlrTarget Go = "Go" +parseAntlrTarget Swift = "Swift" + +getFlags :: [(String, Bool)] -> [String] +getFlags (x : xs) = case x of + (flag, True) -> ("-" ++ flag) : getFlags xs + (_, False) -> getFlags xs + +getFlags [] = [] diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 7fca4df7..6b77a5cf 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -14,6 +14,7 @@ module BNFC.Options , AlexVersion(..), HappyMode(..), OCamlParser(..), JavaLexerParser(..) , RecordPositions(..), TokenText(..) , Ansi(..) + , AntlrTarget(..) , InPackage , removedIn290 , translateOldOptions @@ -81,7 +82,7 @@ instance Show Target where show TargetOCaml = "OCaml" show TargetPygments = "Pygments" show TargetCheck = "Check LBNF file" - show TargetAntlr = "Antlr4" + show TargetAntlr = "ANTLRv4" -- | Which version of Alex is targeted? data AlexVersion = Alex3 @@ -110,6 +111,10 @@ data Ansi = Ansi | BeyondAnsi -- | Package name (C++ and Java backends). type InPackage = Maybe String +-- | ANTLRv4 targets +data AntlrTarget = CPP | CSharp | Dart | Java | JS | PHP | Python3 | Swift | TS | Go + deriving (Eq, Ord, Show) + -- | How to represent token content in the Haskell backend? data TokenText @@ -146,6 +151,11 @@ data SharedOptions = Options --- C# specific , visualStudio :: Bool -- ^ Generate Visual Studio solution/project files. , wcf :: Bool -- ^ Windows Communication Foundation. + --- ANTLRv4 specific + , listener :: Bool + , visitor :: Bool + , wError :: Bool + , dLanguage :: AntlrTarget } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -180,6 +190,11 @@ defaultOptions = Options -- C# specific , visualStudio = False , wcf = False + -- ANTLRv4 specific + , listener = True + , visitor = False + , wError = False + , dLanguage = Java } -- | Check whether an option is unchanged from the default. @@ -389,8 +404,33 @@ specificOptions = , ( Option [] ["agda"] (NoArg (\o -> o { agda = True, tokenText = TextToken })) "Also generate Agda bindings for the abstract syntax" , [TargetHaskell] ) + , (Option [] ["no-listener"] (NoArg (\o -> o { listener = False })) + "Generate visitor for ANTLR result" + , [TargetAntlr]) + , (Option [] ["visitor"] (NoArg (\o -> o { visitor = True })) + "Generate visitor for ANTLR result" + , [TargetAntlr]) + , (Option [] ["Werror"] (NoArg (\o -> o { wError = True })) + "Make ANTLR treat warnings as errors" + , [TargetAntlr]) + , (Option [] ["language"] (ReqArg (\lang o -> o {dLanguage = mkAntlrTarget lang}) "Dlanguage") + "Specify target language for ANTLR" + , [TargetAntlr]) ] +mkAntlrTarget :: String -> AntlrTarget +mkAntlrTarget "java" = Java +mkAntlrTarget "cpp" = CPP +mkAntlrTarget "typescript" = TS +mkAntlrTarget "javascript" = JS +mkAntlrTarget "dart" = Dart +mkAntlrTarget "go" = Go +mkAntlrTarget "php" = PHP +mkAntlrTarget "swift" = Swift +mkAntlrTarget "python" = Python3 +mkAntlrTarget "csharp" = CSharp +mkAntlrTarget _ = Java + -- | The list of specific options for a target. specificOptions' :: Target -> [OptDescr (SharedOptions -> SharedOptions)] specificOptions' t = map fst $ filter (elem t . snd) specificOptions @@ -451,7 +491,7 @@ help = unlines $ title ++ , usageInfo "TARGET languages" targetOptions ] ++ map targetUsage helpTargets where - helpTargets = [ TargetHaskell, TargetJava, TargetC, TargetCpp ] + helpTargets = [ TargetHaskell, TargetJava, TargetC, TargetCpp, TargetAntlr ] targetUsage t = usageInfo (printf "Special options for the %s backend" (show t)) (specificOptions' t) From ac40bf2bafed8d823d3b6ec0597a4bb93cd9aef4 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Mon, 9 Oct 2023 23:25:29 +0300 Subject: [PATCH 04/70] [ANTLRv4] some refactoring of antlr files --- source/src/BNFC/Backend/Antlr.hs | 50 +++++++++++--------------- source/src/BNFC/Backend/Antlr/Utils.hs | 22 ++++++++++++ 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index c0b61978..edd01752 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -3,7 +3,7 @@ module BNFC.Backend.Antlr ( makeAntlr ) where import Prelude hiding ((<>)) -import System.FilePath ((), pathSeparator, (<.>)) +import System.FilePath ((), pathSeparator) import Text.PrettyPrint.HughesPJ (vcat) import BNFC.Utils @@ -12,34 +12,31 @@ import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser +import BNFC.Backend.Antlr.Utils (getAntlrFlags, dotG4) import BNFC.Backend.Common.Makefile as MakeFile makeAntlr :: SharedOptions -> CF -> MkFiles () -makeAntlr Options{..} cf = do +makeAntlr opts@Options{..} cf = do let packageBase = maybe id (+.+) inPackage pkg dirBase = pkgToDir packageBase - let (lex, env) = lexerFun packageBase cf + let (lex, env) = cf2AntlrLex packageBase cf -- Where the lexer file is created. lex is the content! - mkfile (dirBase mkG4Name "Lexer") mkAntlrComment lex - -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake - -- +++ toolversion lexmake ++ ")" - let parserContent = parserFun packageBase cf linenumbers env - mkfile (dirBase mkG4Name "Parser") mkAntlrComment parserContent + mkfile (dirBase mkG4Filename "Lexer") mkAntlrComment lex + + let parserContent = cf2AntlrParse packageBase cf linenumbers env + mkfile (dirBase mkG4Filename "Parser") mkAntlrComment parserContent MakeFile.mkMakefile optMake makefileContent where - lexerFun = cf2AntlrLex - parserFun = cf2AntlrParse pkg = mkName [] CamelCase lang pkgToDir = replace '.' pathSeparator - mkG4Name name = lang ++ name <.> "g4" + mkG4Filename = dotG4 . (lang ++) makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] - otherFlags = unwords $ getFlags [("no-listener", not listener), ("visitor", visitor), ("Werror", wError)] - + otherFlags = getAntlrFlags opts langRef = MakeFile.refVar "LANG" makefileVars = vcat $ makeVars @@ -51,23 +48,23 @@ makeAntlr Options{..} cf = do , ("OTHER_FLAGS", otherFlags) ] - refVarWithPrefix :: String -> String - refVarWithPrefix refVar = langRef MakeFile.refVar refVar + refVarWithPrefix = (langRef ) . MakeFile.refVar + + genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS") +++) . refVarWithPrefix - rmFile :: String -> String -> String - rmFile refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext + rmFileRecipe refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext makefileRules = vcat $ makeRules [ (".PHONY", ["all", "clean-g4", "remove"], []) , ("all", [langRef], []) - , ("lexer", [refVarWithPrefix "LEXER_NAME" <.> "g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS" +++ refVarWithPrefix "LEXER_NAME" <.> "g4"]) - , ("parser", [refVarWithPrefix "PARSER_NAME" <.> "g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS" +++ refVarWithPrefix "PARSER_NAME" <.> "g4"]) + , ("lexer", [dotG4 $ refVarWithPrefix "LEXER_NAME"], [genAntlrRecipe "LEXER_NAME"]) + , ("parser", [dotG4 $ refVarWithPrefix "PARSER_NAME"], [genAntlrRecipe "PARSER_NAME"]) , (langRef, ["lexer", "parser"], []) , ("clean-g4", [], - [ rmFile "LEXER_NAME" ".interp" - , rmFile "LEXER_NAME" ".tokens" - , rmFile "PARSER_NAME" ".interp" - , rmFile "PARSER_NAME" ".tokens" + [ rmFileRecipe "LEXER_NAME" ".interp" + , rmFileRecipe "LEXER_NAME" ".tokens" + , rmFileRecipe "PARSER_NAME" ".interp" + , rmFileRecipe "PARSER_NAME" ".tokens" ]) , ("remove", [], ["rm -rf" +++ langRef]) ] @@ -88,10 +85,3 @@ parseAntlrTarget Python3 = "Python3" parseAntlrTarget PHP = "PHP" parseAntlrTarget Go = "Go" parseAntlrTarget Swift = "Swift" - -getFlags :: [(String, Bool)] -> [String] -getFlags (x : xs) = case x of - (flag, True) -> ("-" ++ flag) : getFlags xs - (_, False) -> getFlags xs - -getFlags [] = [] diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 2ca252f1..45c49ac9 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -1,7 +1,12 @@ +{-# LANGUAGE RecordWildCards #-} + module BNFC.Backend.Antlr.Utils where +import System.FilePath ((<.>)) + import BNFC.CF (Fun) import BNFC.Utils ( mkName, NameStyle(..)) +import BNFC.Options as Options getRuleName :: String -> String getRuleName z = if z == "grammar" then z ++ "_" else z @@ -13,3 +18,20 @@ getLabelName = mkName ["Rule"] CamelCase startSymbol :: String -> String startSymbol = ("Start_" ++) + +getAntlrFlags :: SharedOptions -> String +getAntlrFlags Options{..} = unwords $ getFlags + [ ("no-listener", not listener) + , ("visitor", visitor) + , ("Werror", wError) + ] + +getFlags :: [(String, Bool)] -> [String] +getFlags (x : xs) = case x of + (flag, True) -> ("-" ++ flag) : getFlags xs + (_, False) -> getFlags xs + +getFlags [] = [] + +dotG4 :: String -> String +dotG4 = (<.> "g4") From 1473ec21ce45397045d36b5437b6ce4e6cf7a9d1 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 00:19:07 +0300 Subject: [PATCH 05/70] [ANTLRv4] changes for antlr Makefile generation --- source/src/BNFC/Backend/Antlr.hs | 39 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index edd01752..05026a4e 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -36,35 +36,46 @@ makeAntlr opts@Options{..} cf = do makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] - otherFlags = getAntlrFlags opts + antlrFlags = getAntlrFlags opts + langRef = MakeFile.refVar "LANG" + lexerVarName = "LEXER_FILENAME" + lexerFilename = langRef ++ "Lexer" + + parserVarName = "PARSER_FILENAME" + parserFilename = langRef ++ "Parser" + + prefix = "PREFIXED_" + prefixedLexerVarName = prefix ++ lexerVarName + prefixedParserVarName = prefix ++ parserVarName + makefileVars = vcat $ makeVars [ ("LANG", lang) - , ("LEXER_NAME", langRef ++ "Lexer") - , ("PARSER_NAME", langRef ++ "Parser") + , (lexerVarName, lexerFilename) + , (parserVarName, parserFilename) + , (prefixedLexerVarName, langRef MakeFile.refVar lexerVarName) + , (prefixedParserVarName, langRef MakeFile.refVar parserVarName) , ("ANTLR4", "java org.antlr.v4.Tool") , ("DLANGUAGE", parseAntlrTarget dLanguage) - , ("OTHER_FLAGS", otherFlags) + , ("OTHER_FLAGS", antlrFlags) ] - refVarWithPrefix = (langRef ) . MakeFile.refVar - - genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS") +++) . refVarWithPrefix + genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS") +++) . MakeFile.refVar - rmFileRecipe refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext + rmFileRecipe refVar ext = "rm -f" +++ MakeFile.refVar refVar ++ ext makefileRules = vcat $ makeRules [ (".PHONY", ["all", "clean-g4", "remove"], []) , ("all", [langRef], []) - , ("lexer", [dotG4 $ refVarWithPrefix "LEXER_NAME"], [genAntlrRecipe "LEXER_NAME"]) - , ("parser", [dotG4 $ refVarWithPrefix "PARSER_NAME"], [genAntlrRecipe "PARSER_NAME"]) + , ("lexer", [dotG4 $ MakeFile.refVar prefixedLexerVarName], [genAntlrRecipe prefixedLexerVarName]) + , ("parser", [dotG4 $ MakeFile.refVar prefixedParserVarName], [genAntlrRecipe prefixedParserVarName]) , (langRef, ["lexer", "parser"], []) , ("clean-g4", [], - [ rmFileRecipe "LEXER_NAME" ".interp" - , rmFileRecipe "LEXER_NAME" ".tokens" - , rmFileRecipe "PARSER_NAME" ".interp" - , rmFileRecipe "PARSER_NAME" ".tokens" + [ rmFileRecipe prefixedLexerVarName ".interp" + , rmFileRecipe prefixedLexerVarName ".tokens" + , rmFileRecipe prefixedParserVarName ".interp" + , rmFileRecipe prefixedParserVarName ".tokens" ]) , ("remove", [], ["rm -rf" +++ langRef]) ] From b0c0c4073e141d9c3ddb7ddbb0768c2cca195010 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 00:54:18 +0300 Subject: [PATCH 06/70] [ANTLRv4] created new function for parsing ANTLR CLI options --- source/src/BNFC/Backend/Antlr.hs | 14 +---------- source/src/BNFC/Backend/Antlr/Utils.hs | 32 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 05026a4e..874e78df 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -12,7 +12,7 @@ import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser -import BNFC.Backend.Antlr.Utils (getAntlrFlags, dotG4) +import BNFC.Backend.Antlr.Utils (getAntlrFlags, dotG4, parseAntlrTarget) import BNFC.Backend.Common.Makefile as MakeFile makeAntlr :: SharedOptions -> CF -> MkFiles () @@ -84,15 +84,3 @@ makeAntlr opts@Options{..} cf = do mkAntlrComment :: String -> String mkAntlrComment = ("// -*- ANTLRv4 -*- " ++) - -parseAntlrTarget :: AntlrTarget -> String -parseAntlrTarget Java = "Java" -parseAntlrTarget CPP = "Cpp" -parseAntlrTarget CSharp = "CSharp" -parseAntlrTarget JS = "JavaScript" -parseAntlrTarget TS = "TypeScript" -parseAntlrTarget Dart = "Dart" -parseAntlrTarget Python3 = "Python3" -parseAntlrTarget PHP = "PHP" -parseAntlrTarget Go = "Go" -parseAntlrTarget Swift = "Swift" diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 45c49ac9..8a013e69 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -35,3 +35,35 @@ getFlags [] = [] dotG4 :: String -> String dotG4 = (<.> "g4") + +-- maybe should use instead of "getAntlrFlags" +getAntlrOptions :: SharedOptions -> String +getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts + where + parsedOpts = getAntlrOptions' + [ ("no-listener", Left $ not listener) + , ("visitor", Left visitor) + , ("Werror", Left wError) + , ("Dlanguage", Right $ parseAntlrTarget dLanguage) + ] + +getAntlrOptions' :: [(String, Either Bool String)] -> [String] +getAntlrOptions' [] = [] +getAntlrOptions' (opt : opts) = case opt of + (_, Left False) -> otherOpts + (flag, Left True) -> flag : otherOpts + (flag, Right value) -> (flag ++ "=" ++ value) : otherOpts + where + otherOpts = getAntlrOptions' opts + +parseAntlrTarget :: AntlrTarget -> String +parseAntlrTarget Java = "Java" +parseAntlrTarget CPP = "Cpp" +parseAntlrTarget CSharp = "CSharp" +parseAntlrTarget JS = "JavaScript" +parseAntlrTarget TS = "TypeScript" +parseAntlrTarget Dart = "Dart" +parseAntlrTarget Python3 = "Python3" +parseAntlrTarget PHP = "PHP" +parseAntlrTarget Go = "Go" +parseAntlrTarget Swift = "Swift" From 0c5c47a0d854a5d21b36151942435ba9ab25625d Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 00:59:05 +0300 Subject: [PATCH 07/70] [ANTLRv4] added support for -Xlog ANTLR flag --- source/src/BNFC/Backend/Antlr/Utils.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 8a013e69..323c7932 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -22,8 +22,9 @@ startSymbol = ("Start_" ++) getAntlrFlags :: SharedOptions -> String getAntlrFlags Options{..} = unwords $ getFlags [ ("no-listener", not listener) - , ("visitor", visitor) - , ("Werror", wError) + , ("visitor", visitor) + , ("Werror", wError) + , ("Xlog", xlog) ] getFlags :: [(String, Bool)] -> [String] @@ -42,9 +43,10 @@ getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts where parsedOpts = getAntlrOptions' [ ("no-listener", Left $ not listener) - , ("visitor", Left visitor) - , ("Werror", Left wError) - , ("Dlanguage", Right $ parseAntlrTarget dLanguage) + , ("visitor", Left visitor) + , ("Werror", Left wError) + , ("Dlanguage", Right $ parseAntlrTarget dLanguage) + , ("Xlog", Left xlog) ] getAntlrOptions' :: [(String, Either Bool String)] -> [String] From f14b8908fd374a570c8dd482fc997d69cc1d90af Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 00:59:39 +0300 Subject: [PATCH 08/70] [ANTLRv4] -Xlog flag added to BNFC.Options --- source/src/BNFC/Options.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 6b77a5cf..03726ad2 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -156,6 +156,7 @@ data SharedOptions = Options , visitor :: Bool , wError :: Bool , dLanguage :: AntlrTarget + , xlog :: Bool } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -195,6 +196,7 @@ defaultOptions = Options , visitor = False , wError = False , dLanguage = Java + , xlog = False } -- | Check whether an option is unchanged from the default. @@ -413,9 +415,12 @@ specificOptions = , (Option [] ["Werror"] (NoArg (\o -> o { wError = True })) "Make ANTLR treat warnings as errors" , [TargetAntlr]) - , (Option [] ["language"] (ReqArg (\lang o -> o {dLanguage = mkAntlrTarget lang}) "Dlanguage") + , (Option [] ["language"] (ReqArg (\lang o -> o { dLanguage = mkAntlrTarget lang }) "Dlanguage") "Specify target language for ANTLR" , [TargetAntlr]) + , (Option [] ["Xlog"] (NoArg (\o -> o { xlog = True })) + "Create log file with information of grammar processing" + , [TargetAntlr]) ] mkAntlrTarget :: String -> AntlrTarget From 6b13f6f3b74a55fb6e6b0c4958632c51ce7698c4 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 01:13:13 +0300 Subject: [PATCH 09/70] [ANTLRv4] added support for -listener and -no-visitor ANTLR flags --- source/src/BNFC/Backend/Antlr.hs | 9 +++------ source/src/BNFC/Backend/Antlr/Utils.hs | 19 +++---------------- source/src/BNFC/Options.hs | 6 ++++++ 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 874e78df..6278b36e 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -12,7 +12,7 @@ import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser -import BNFC.Backend.Antlr.Utils (getAntlrFlags, dotG4, parseAntlrTarget) +import BNFC.Backend.Antlr.Utils (dotG4, getAntlrOptions) import BNFC.Backend.Common.Makefile as MakeFile makeAntlr :: SharedOptions -> CF -> MkFiles () @@ -36,8 +36,6 @@ makeAntlr opts@Options{..} cf = do makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] - antlrFlags = getAntlrFlags opts - langRef = MakeFile.refVar "LANG" lexerVarName = "LEXER_FILENAME" @@ -57,11 +55,10 @@ makeAntlr opts@Options{..} cf = do , (prefixedLexerVarName, langRef MakeFile.refVar lexerVarName) , (prefixedParserVarName, langRef MakeFile.refVar parserVarName) , ("ANTLR4", "java org.antlr.v4.Tool") - , ("DLANGUAGE", parseAntlrTarget dLanguage) - , ("OTHER_FLAGS", antlrFlags) + , ("ANTLR_OPTIONS", getAntlrOptions opts) ] - genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ "-Dlanguage=" ++ MakeFile.refVar "DLANGUAGE" +++ MakeFile.refVar "OTHER_FLAGS") +++) . MakeFile.refVar + genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" )+++) . MakeFile.refVar rmFileRecipe refVar ext = "rm -f" +++ MakeFile.refVar refVar ++ ext diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 323c7932..a943afdf 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -19,21 +19,6 @@ getLabelName = mkName ["Rule"] CamelCase startSymbol :: String -> String startSymbol = ("Start_" ++) -getAntlrFlags :: SharedOptions -> String -getAntlrFlags Options{..} = unwords $ getFlags - [ ("no-listener", not listener) - , ("visitor", visitor) - , ("Werror", wError) - , ("Xlog", xlog) - ] - -getFlags :: [(String, Bool)] -> [String] -getFlags (x : xs) = case x of - (flag, True) -> ("-" ++ flag) : getFlags xs - (_, False) -> getFlags xs - -getFlags [] = [] - dotG4 :: String -> String dotG4 = (<.> "g4") @@ -42,8 +27,10 @@ getAntlrOptions :: SharedOptions -> String getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts where parsedOpts = getAntlrOptions' - [ ("no-listener", Left $ not listener) + [ ("listener", Left listener) + , ("no-listener", Left $ not listener) , ("visitor", Left visitor) + , ("no-visitor", Left $ not visitor) , ("Werror", Left wError) , ("Dlanguage", Right $ parseAntlrTarget dLanguage) , ("Xlog", Left xlog) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 03726ad2..517a7e8e 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -406,12 +406,18 @@ specificOptions = , ( Option [] ["agda"] (NoArg (\o -> o { agda = True, tokenText = TextToken })) "Also generate Agda bindings for the abstract syntax" , [TargetHaskell] ) + , (Option [] ["listener"] (NoArg (\o -> o { listener = True })) + "Generate visitor for ANTLR result" + , [TargetAntlr]) , (Option [] ["no-listener"] (NoArg (\o -> o { listener = False })) "Generate visitor for ANTLR result" , [TargetAntlr]) , (Option [] ["visitor"] (NoArg (\o -> o { visitor = True })) "Generate visitor for ANTLR result" , [TargetAntlr]) + , (Option [] ["no-visitor"] (NoArg (\o -> o { visitor = False })) + "Generate visitor for ANTLR result" + , [TargetAntlr]) , (Option [] ["Werror"] (NoArg (\o -> o { wError = True })) "Make ANTLR treat warnings as errors" , [TargetAntlr]) From f5809ea86cb107c6f9bbdcde01c10df5f611109b Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 01:27:07 +0300 Subject: [PATCH 10/70] [ANTLRv4] added support for -XdbgST ANTLR flag --- source/src/BNFC/Backend/Antlr/Utils.hs | 1 + source/src/BNFC/Options.hs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index a943afdf..3fe8ef1d 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -34,6 +34,7 @@ getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts , ("Werror", Left wError) , ("Dlanguage", Right $ parseAntlrTarget dLanguage) , ("Xlog", Left xlog) + , ("XdbgST", Left xDbgST) ] getAntlrOptions' :: [(String, Either Bool String)] -> [String] diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 517a7e8e..95da01fc 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -157,6 +157,7 @@ data SharedOptions = Options , wError :: Bool , dLanguage :: AntlrTarget , xlog :: Bool + , xDbgST :: Bool } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -197,6 +198,7 @@ defaultOptions = Options , wError = False , dLanguage = Java , xlog = False + , xDbgST = False } -- | Check whether an option is unchanged from the default. @@ -424,9 +426,14 @@ specificOptions = , (Option [] ["language"] (ReqArg (\lang o -> o { dLanguage = mkAntlrTarget lang }) "Dlanguage") "Specify target language for ANTLR" , [TargetAntlr]) - , (Option [] ["Xlog"] (NoArg (\o -> o { xlog = True })) + , (Option [] ["Xlog"] (NoArg (\o -> o { xlog = True })) "Create log file with information of grammar processing" , [TargetAntlr]) + , (Option [] ["XdbgST"] (NoArg (\o -> o { xDbgST = True })) $ unlines + [ "Open window with generated code and templates used to generate this code" + , "It invokes the StringTemplate inspector window." + ] + , [TargetAntlr]) ] mkAntlrTarget :: String -> AntlrTarget From 1cdfcd38a4049d49897c8ff34695b58c2e2faa29 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 01:30:31 +0300 Subject: [PATCH 11/70] [ANTLRv4] fixed description for -listener and -visitor ANTLR options --- source/src/BNFC/Options.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 95da01fc..ac58befd 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -409,16 +409,16 @@ specificOptions = "Also generate Agda bindings for the abstract syntax" , [TargetHaskell] ) , (Option [] ["listener"] (NoArg (\o -> o { listener = True })) - "Generate visitor for ANTLR result" + "Generate parse tree listener for ANTLR result. True by default" , [TargetAntlr]) , (Option [] ["no-listener"] (NoArg (\o -> o { listener = False })) - "Generate visitor for ANTLR result" + "Do NOT generate parse tree listener" , [TargetAntlr]) , (Option [] ["visitor"] (NoArg (\o -> o { visitor = True })) - "Generate visitor for ANTLR result" + "Generate parse tree visitor for ANTLR result. False by default" , [TargetAntlr]) , (Option [] ["no-visitor"] (NoArg (\o -> o { visitor = False })) - "Generate visitor for ANTLR result" + "Do NOT generate parse tree visitor" , [TargetAntlr]) , (Option [] ["Werror"] (NoArg (\o -> o { wError = True })) "Make ANTLR treat warnings as errors" From ef3056aa014892a22e8ee0231a6fd644ba9b5a8b Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 09:20:10 +0300 Subject: [PATCH 12/70] [ANTLRv4] added support for -XdbgSTWait ANTLR flag --- source/src/BNFC/Backend/Antlr/Utils.hs | 2 +- source/src/BNFC/Options.hs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 3fe8ef1d..6974aaa6 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -22,7 +22,6 @@ startSymbol = ("Start_" ++) dotG4 :: String -> String dotG4 = (<.> "g4") --- maybe should use instead of "getAntlrFlags" getAntlrOptions :: SharedOptions -> String getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts where @@ -35,6 +34,7 @@ getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts , ("Dlanguage", Right $ parseAntlrTarget dLanguage) , ("Xlog", Left xlog) , ("XdbgST", Left xDbgST) + , ("XdbgSTWait", Left xDbgSTWait) ] getAntlrOptions' :: [(String, Either Bool String)] -> [String] diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index ac58befd..abc22987 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -158,6 +158,7 @@ data SharedOptions = Options , dLanguage :: AntlrTarget , xlog :: Bool , xDbgST :: Bool + , xDbgSTWait :: Bool } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -199,6 +200,7 @@ defaultOptions = Options , dLanguage = Java , xlog = False , xDbgST = False + , xDbgSTWait = False } -- | Check whether an option is unchanged from the default. @@ -434,6 +436,9 @@ specificOptions = , "It invokes the StringTemplate inspector window." ] , [TargetAntlr]) + , (Option [] ["XdbgSTWait"] (NoArg (\o -> o { xDbgSTWait = True })) + "Wait for ST visualizer to close before continuing" + , [TargetAntlr]) ] mkAntlrTarget :: String -> AntlrTarget From baf1186ff9635a3f8e79d8e8ea17f263cdf98d23 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 09:35:45 +0300 Subject: [PATCH 13/70] [ANTLRv4] added support for -atn ANTLR flag --- source/src/BNFC/Backend/Antlr/Utils.hs | 1 + source/src/BNFC/Options.hs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 6974aaa6..bdc23ab9 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -35,6 +35,7 @@ getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts , ("Xlog", Left xlog) , ("XdbgST", Left xDbgST) , ("XdbgSTWait", Left xDbgSTWait) + , ("atn", Left atn) ] getAntlrOptions' :: [(String, Either Bool String)] -> [String] diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index abc22987..41765778 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -159,6 +159,7 @@ data SharedOptions = Options , xlog :: Bool , xDbgST :: Bool , xDbgSTWait :: Bool + , atn :: Bool } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -201,6 +202,7 @@ defaultOptions = Options , xlog = False , xDbgST = False , xDbgSTWait = False + , atn = False } -- | Check whether an option is unchanged from the default. @@ -439,6 +441,11 @@ specificOptions = , (Option [] ["XdbgSTWait"] (NoArg (\o -> o { xDbgSTWait = True })) "Wait for ST visualizer to close before continuing" , [TargetAntlr]) + , (Option [] ["atn"] (NoArg (\o -> o { atn = True })) $ unlines + [ "Generate DOT graph files that represent the internal ATN (augmented transition network) data structures that ANTLR uses to represent grammars." + , "The files come out as Grammar.rule .dot. If the grammar is a combined grammar, the lexer rules are named Grammar Lexer.rule .dot." + ] + , [TargetAntlr]) ] mkAntlrTarget :: String -> AntlrTarget From 6a3b6c6f19c0bbced54f66d16df852102a4349a1 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 09:41:42 +0300 Subject: [PATCH 14/70] [ANTLRv4] minor formatting fix --- source/src/BNFC/Options.hs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 41765778..a1bd9a6c 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -115,6 +115,19 @@ type InPackage = Maybe String data AntlrTarget = CPP | CSharp | Dart | Java | JS | PHP | Python3 | Swift | TS | Go deriving (Eq, Ord, Show) +mkAntlrTarget :: String -> AntlrTarget +mkAntlrTarget "java" = Java +mkAntlrTarget "cpp" = CPP +mkAntlrTarget "typescript" = TS +mkAntlrTarget "javascript" = JS +mkAntlrTarget "dart" = Dart +mkAntlrTarget "go" = Go +mkAntlrTarget "php" = PHP +mkAntlrTarget "swift" = Swift +mkAntlrTarget "python" = Python3 +mkAntlrTarget "csharp" = CSharp +mkAntlrTarget _ = Java + -- | How to represent token content in the Haskell backend? data TokenText @@ -448,19 +461,6 @@ specificOptions = , [TargetAntlr]) ] -mkAntlrTarget :: String -> AntlrTarget -mkAntlrTarget "java" = Java -mkAntlrTarget "cpp" = CPP -mkAntlrTarget "typescript" = TS -mkAntlrTarget "javascript" = JS -mkAntlrTarget "dart" = Dart -mkAntlrTarget "go" = Go -mkAntlrTarget "php" = PHP -mkAntlrTarget "swift" = Swift -mkAntlrTarget "python" = Python3 -mkAntlrTarget "csharp" = CSharp -mkAntlrTarget _ = Java - -- | The list of specific options for a target. specificOptions' :: Target -> [OptDescr (SharedOptions -> SharedOptions)] specificOptions' t = map fst $ filter (elem t . snd) specificOptions From 066f725c5b4fbac2c6b68f778962ab4b1ac96dd7 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 14:24:41 +0300 Subject: [PATCH 15/70] [ANTLRv4] added support for ANTLR options of type 'name value' --- source/src/BNFC/Backend/Antlr/Utils.hs | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index bdc23ab9..edfd869c 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -2,10 +2,11 @@ module BNFC.Backend.Antlr.Utils where +import Prelude hiding (Either, Left, Right) import System.FilePath ((<.>)) import BNFC.CF (Fun) -import BNFC.Utils ( mkName, NameStyle(..)) +import BNFC.Utils ( mkName, NameStyle(..), (+++)) import BNFC.Options as Options getRuleName :: String -> String @@ -22,28 +23,36 @@ startSymbol = ("Start_" ++) dotG4 :: String -> String dotG4 = (<.> "g4") +-- Left | Middle | Rigth +data Either3 a b c = L a | M b | R c + +-- There are three variants of ANTLRv4 options: +-- "-OptName", "-OptName=OptValue", "-OptName Optvalue" +type OptionType = Either3 Bool String String + getAntlrOptions :: SharedOptions -> String getAntlrOptions Options{..} = unwords $ map ("-" ++) parsedOpts where parsedOpts = getAntlrOptions' - [ ("listener", Left listener) - , ("no-listener", Left $ not listener) - , ("visitor", Left visitor) - , ("no-visitor", Left $ not visitor) - , ("Werror", Left wError) - , ("Dlanguage", Right $ parseAntlrTarget dLanguage) - , ("Xlog", Left xlog) - , ("XdbgST", Left xDbgST) - , ("XdbgSTWait", Left xDbgSTWait) - , ("atn", Left atn) + [ ("listener", L listener) + , ("no-listener", L $ not listener) + , ("visitor", L visitor) + , ("no-visitor", L $ not visitor) + , ("Werror", L wError) + , ("Dlanguage", M $ parseAntlrTarget dLanguage) + , ("Xlog", L xlog) + , ("XdbgST", L xDbgST) + , ("XdbgSTWait", L xDbgSTWait) + , ("atn", L atn) ] -getAntlrOptions' :: [(String, Either Bool String)] -> [String] +getAntlrOptions' :: [(String, OptionType)] -> [String] getAntlrOptions' [] = [] getAntlrOptions' (opt : opts) = case opt of - (_, Left False) -> otherOpts - (flag, Left True) -> flag : otherOpts - (flag, Right value) -> (flag ++ "=" ++ value) : otherOpts + (_, L False) -> otherOpts + (optName, L True) -> optName : otherOpts + (optName, M value) -> (optName ++ "=" ++ value) : otherOpts + (optName, R value) -> (optName +++ value) : otherOpts where otherOpts = getAntlrOptions' opts From 413f351dc0122a64379adc0ff35b8faccdaeba1e Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 15:00:24 +0300 Subject: [PATCH 16/70] [ANTLRv4] added option for passing string with ANTLR options directly to Makefile rule --- source/src/BNFC/Backend/Antlr.hs | 3 ++- source/src/BNFC/Options.hs | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 6278b36e..b254d12a 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -56,9 +56,10 @@ makeAntlr opts@Options{..} cf = do , (prefixedParserVarName, langRef MakeFile.refVar parserVarName) , ("ANTLR4", "java org.antlr.v4.Tool") , ("ANTLR_OPTIONS", getAntlrOptions opts) + , ("DIRECT_OPTIONS", antlrOpts) ] - genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" )+++) . MakeFile.refVar + genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" +++ MakeFile.refVar "DIRECT_OPTIONS") +++) . MakeFile.refVar rmFileRecipe refVar ext = "rm -f" +++ MakeFile.refVar refVar ++ ext diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index a1bd9a6c..72c46a0c 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -172,7 +172,8 @@ data SharedOptions = Options , xlog :: Bool , xDbgST :: Bool , xDbgSTWait :: Bool - , atn :: Bool + , atn :: Bool + , antlrOpts :: String } deriving (Eq, Ord, Show) -- We take this opportunity to define the type of the backend functions. @@ -216,6 +217,7 @@ defaultOptions = Options , xDbgST = False , xDbgSTWait = False , atn = False + , antlrOpts = "" } -- | Check whether an option is unchanged from the default. @@ -459,6 +461,12 @@ specificOptions = , "The files come out as Grammar.rule .dot. If the grammar is a combined grammar, the lexer rules are named Grammar Lexer.rule .dot." ] , [TargetAntlr]) + , (Option [] ["opts"] (ReqArg (\strOpts o -> o { antlrOpts = strOpts }) "OPTIONS") $ unlines + [ "String of ANTLRv4 options which will be directly embedded to Makefile ANTLR call" + , "Options from this string override directly specified options" + , "Usage: --opts=\"-no-listener -visitor -Xlog\"" + ] + , [TargetAntlr]) ] -- | The list of specific options for a target. From d51337c854cb90ce2a00ac05c6d7e286b3d060c7 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Tue, 10 Oct 2023 15:18:06 +0300 Subject: [PATCH 17/70] [ANTLRv4] update description for ANTLR output --- source/src/BNFC/Options.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 72c46a0c..2ca90a8a 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -329,7 +329,7 @@ targetOptions :: [ OptDescr (SharedOptions -> SharedOptions)] targetOptions = [ Option "" ["java"] (NoArg (\o -> o {target = TargetJava})) "Output Java code [default: for use with JLex and CUP]" - , Option "" ["java-antlr"] (NoArg (\ o -> o{ target = TargetJava, javaLexerParser = Antlr4 })) + , Option "" ["java-antlr"] (NoArg (\o -> o {target = TargetJava, javaLexerParser = Antlr4})) "Output Java code for use with ANTLR (short for --java --antlr)" , Option "" ["haskell"] (NoArg (\o -> o {target = TargetHaskell})) "Output Haskell code for use with Alex and Happy (default)" @@ -345,14 +345,14 @@ targetOptions = "Output C++ code (without STL) for use with FLex and Bison" , Option "" ["ocaml"] (NoArg (\o -> o {target = TargetOCaml})) "Output OCaml code for use with ocamllex and ocamlyacc" - , Option "" ["ocaml-menhir"] (NoArg (\ o -> o{ target = TargetOCaml, ocamlParser = Menhir })) + , Option "" ["ocaml-menhir"] (NoArg (\o -> o {target = TargetOCaml, ocamlParser = Menhir})) "Output OCaml code for use with ocamllex and menhir (short for --ocaml --menhir)" , Option "" ["pygments"] (NoArg (\o -> o {target = TargetPygments})) "Output a Python lexer for Pygments" - , Option "" ["check"] (NoArg (\ o -> o{target = TargetCheck })) + , Option "" ["check"] (NoArg (\o -> o {target = TargetCheck})) "No output. Just check input LBNF file" - , Option "" ["antlr"] (NoArg (\o -> o{target = TargetAntlr})) - "Not implemented yet." + , Option "" ["antlr"] (NoArg (\o -> o {target = TargetAntlr})) + "Output lexer and parser grammars for ANTLRv4" ] -- | A list of the options and for each of them, the target language From d34f4b2c2939b419f7b2d66ed7b3fd44ac933e74 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Wed, 11 Oct 2023 10:28:25 +0300 Subject: [PATCH 18/70] [ANTLRv4] Makefile LANG variable is set to be equal package name --- source/src/BNFC/Backend/Antlr.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index b254d12a..b54f8104 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -49,7 +49,7 @@ makeAntlr opts@Options{..} cf = do prefixedParserVarName = prefix ++ parserVarName makefileVars = vcat $ makeVars - [ ("LANG", lang) + [ ("LANG", pkg) , (lexerVarName, lexerFilename) , (parserVarName, parserFilename) , (prefixedLexerVarName, langRef MakeFile.refVar lexerVarName) From 38ff8fb72c76cfcd8613f46ecebab7c60e57b77a Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Mon, 16 Oct 2023 19:18:26 +0300 Subject: [PATCH 19/70] [ANTLRv4] stylistic changes --- source/src/BNFC/Backend/Antlr.hs | 4 ++-- source/src/BNFC/Backend/Antlr/Utils.hs | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index b54f8104..8adda15c 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -69,7 +69,7 @@ makeAntlr opts@Options{..} cf = do , ("lexer", [dotG4 $ MakeFile.refVar prefixedLexerVarName], [genAntlrRecipe prefixedLexerVarName]) , ("parser", [dotG4 $ MakeFile.refVar prefixedParserVarName], [genAntlrRecipe prefixedParserVarName]) , (langRef, ["lexer", "parser"], []) - , ("clean-g4", [], + , ("clean-antlr", [], [ rmFileRecipe prefixedLexerVarName ".interp" , rmFileRecipe prefixedLexerVarName ".tokens" , rmFileRecipe prefixedParserVarName ".interp" @@ -78,7 +78,7 @@ makeAntlr opts@Options{..} cf = do , ("remove", [], ["rm -rf" +++ langRef]) ] - makefileContent _ = vcat [makefileVars, "", makefileRules, ""] + makefileContent _ = vcat [makefileVars, "", makefileRules] mkAntlrComment :: String -> String mkAntlrComment = ("// -*- ANTLRv4 -*- " ++) diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index edfd869c..178753a7 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -2,12 +2,14 @@ module BNFC.Backend.Antlr.Utils where +import Text.PrettyPrint.HughesPJ (Doc, text, vcat) import Prelude hiding (Either, Left, Right) -import System.FilePath ((<.>)) +import System.FilePath ((<.>), ()) import BNFC.CF (Fun) import BNFC.Utils ( mkName, NameStyle(..), (+++)) import BNFC.Options as Options +import BNFC.Backend.Common.Makefile as MakeFile getRuleName :: String -> String getRuleName z = if z == "grammar" then z ++ "_" else z @@ -57,13 +59,13 @@ getAntlrOptions' (opt : opts) = case opt of otherOpts = getAntlrOptions' opts parseAntlrTarget :: AntlrTarget -> String -parseAntlrTarget Java = "Java" -parseAntlrTarget CPP = "Cpp" -parseAntlrTarget CSharp = "CSharp" -parseAntlrTarget JS = "JavaScript" -parseAntlrTarget TS = "TypeScript" -parseAntlrTarget Dart = "Dart" +parseAntlrTarget Java = "Java" +parseAntlrTarget CPP = "Cpp" +parseAntlrTarget CSharp = "CSharp" +parseAntlrTarget JS = "JavaScript" +parseAntlrTarget TS = "TypeScript" +parseAntlrTarget Dart = "Dart" parseAntlrTarget Python3 = "Python3" -parseAntlrTarget PHP = "PHP" -parseAntlrTarget Go = "Go" -parseAntlrTarget Swift = "Swift" +parseAntlrTarget PHP = "PHP" +parseAntlrTarget Go = "Go" +parseAntlrTarget Swift = "Swift" From cf895ce484c095d686b93925dd8690b4e7fa7f8e Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Mon, 16 Oct 2023 20:49:29 +0300 Subject: [PATCH 20/70] [ANTLRv4] permanent camel case for .g4 files --- source/src/BNFC/Backend/Antlr.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 8adda15c..6dcda63f 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -31,7 +31,7 @@ makeAntlr opts@Options{..} cf = do where pkg = mkName [] CamelCase lang pkgToDir = replace '.' pathSeparator - mkG4Filename = dotG4 . (lang ++) + mkG4Filename = dotG4 . (pkg ++) makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] @@ -64,7 +64,7 @@ makeAntlr opts@Options{..} cf = do rmFileRecipe refVar ext = "rm -f" +++ MakeFile.refVar refVar ++ ext makefileRules = vcat $ makeRules - [ (".PHONY", ["all", "clean-g4", "remove"], []) + [ (".PHONY", ["all", "clean-antlr", "remove"], []) , ("all", [langRef], []) , ("lexer", [dotG4 $ MakeFile.refVar prefixedLexerVarName], [genAntlrRecipe prefixedLexerVarName]) , ("parser", [dotG4 $ MakeFile.refVar prefixedParserVarName], [genAntlrRecipe prefixedParserVarName]) From a9142790af7676c44bc70cc7cdcec2a8952b889d Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Thu, 19 Oct 2023 22:37:55 +0300 Subject: [PATCH 21/70] [ANTLRv4] Some improvements for Makefile gen --- source/src/BNFC/Backend/Antlr.hs | 100 ++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 23 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 6dcda63f..5dc9841b 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -3,10 +3,18 @@ module BNFC.Backend.Antlr ( makeAntlr ) where import Prelude hiding ((<>)) -import System.FilePath ((), pathSeparator) +import System.FilePath ((), pathSeparator, (<.>)) import Text.PrettyPrint.HughesPJ (vcat) +import Data.Bifunctor (second) +import Data.Char (toUpper, toLower) import BNFC.Utils + ( NameStyle(CamelCase, SnakeCase), + mkName, + replace, + (+.+), + (+++), + mkNames ) import BNFC.CF import BNFC.Options as Options import BNFC.Backend.Base @@ -14,6 +22,7 @@ import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser import BNFC.Backend.Antlr.Utils (dotG4, getAntlrOptions) import BNFC.Backend.Common.Makefile as MakeFile + ( mkMakefile, mkVar, mkRule, refVar ) makeAntlr :: SharedOptions -> CF -> MkFiles () makeAntlr opts@Options{..} cf = do @@ -38,47 +47,92 @@ makeAntlr opts@Options{..} cf = do langRef = MakeFile.refVar "LANG" - lexerVarName = "LEXER_FILENAME" - lexerFilename = langRef ++ "Lexer" + lexerVarName = "LEXER_GRAMMAR_FILENAME" + lexerGrammarFile = (langRef ) . dotG4 $ langRef ++ "Lexer" - parserVarName = "PARSER_FILENAME" - parserFilename = langRef ++ "Parser" + parserVarName = "PARSER_GRAMMAR_FILENAME" + parserGrammarFile = (langRef ) . dotG4 $ langRef ++ "Parser" - prefix = "PREFIXED_" - prefixedLexerVarName = prefix ++ lexerVarName - prefixedParserVarName = prefix ++ parserVarName + generatedFilesVars = map (second (langRef )) $ getVars dLanguage pkg - makefileVars = vcat $ makeVars + makefileVars = vcat $ makeVars $ [ ("LANG", pkg) - , (lexerVarName, lexerFilename) - , (parserVarName, parserFilename) - , (prefixedLexerVarName, langRef MakeFile.refVar lexerVarName) - , (prefixedParserVarName, langRef MakeFile.refVar parserVarName) + , (lexerVarName, lexerGrammarFile) + , (parserVarName, parserGrammarFile) , ("ANTLR4", "java org.antlr.v4.Tool") , ("ANTLR_OPTIONS", getAntlrOptions opts) , ("DIRECT_OPTIONS", antlrOpts) ] + ++ generatedFilesVars - genAntlrRecipe = dotG4 . ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" +++ MakeFile.refVar "DIRECT_OPTIONS") +++) . MakeFile.refVar + genAntlrRecipe = ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" +++ MakeFile.refVar "DIRECT_OPTIONS") +++) . MakeFile.refVar - rmFileRecipe refVar ext = "rm -f" +++ MakeFile.refVar refVar ++ ext + rmFile refVar = "rm -f" +++ MakeFile.refVar refVar + + antlrFiles = map (langRef ) + [ (mkName [] CamelCase $ pkg +++ "Lexer") <.> "interp" + , (mkName [] CamelCase $ pkg +++ "Parser") <.> "interp" + , (mkName [] CamelCase $ pkg +++ "Lexer") <.> "tokens" + , (mkName [] CamelCase $ pkg +++ "Parser") <.> "tokens" + ] makefileRules = vcat $ makeRules [ (".PHONY", ["all", "clean-antlr", "remove"], []) , ("all", [langRef], []) - , ("lexer", [dotG4 $ MakeFile.refVar prefixedLexerVarName], [genAntlrRecipe prefixedLexerVarName]) - , ("parser", [dotG4 $ MakeFile.refVar prefixedParserVarName], [genAntlrRecipe prefixedParserVarName]) + , ("lexer", [MakeFile.refVar lexerVarName], [genAntlrRecipe lexerVarName]) + , ("parser", [MakeFile.refVar parserVarName], [genAntlrRecipe parserVarName]) , (langRef, ["lexer", "parser"], []) , ("clean-antlr", [], - [ rmFileRecipe prefixedLexerVarName ".interp" - , rmFileRecipe prefixedLexerVarName ".tokens" - , rmFileRecipe prefixedParserVarName ".interp" - , rmFileRecipe prefixedParserVarName ".tokens" - ]) + map rmFile targetLanguageFiles + ++ + map ("rm -f" +++) antlrFiles ) , ("remove", [], ["rm -rf" +++ langRef]) ] makefileContent _ = vcat [makefileVars, "", makefileRules] mkAntlrComment :: String -> String -mkAntlrComment = ("// -*- ANTLRv4 -*- " ++) +mkAntlrComment = ("// ANTLRv4 " ++) + +targetLanguageFiles :: [String] +targetLanguageFiles = ["LEXER", "PARSER", "LISTENER", "VISITOR", "BASE_LISTENER", "BASE_VISITOR"] + +getVars :: AntlrTarget -> [Char] -> [(String, FilePath)] +getVars target lang = zip targetLanguageFiles files + where + files = map (<.> ext) names + names = mkNames [] namestyle + [ filename "lexer" + , filename "parser" + , filename "parser listener" + , filename "parser visitor" + , filename "parser base listener" + , filename "parser base visitor" + ] + + filename = case target of + Go -> (toLowerCase lang ++) + _ -> (lang +++) + + namestyle = case target of + Go -> SnakeCase + _ -> CamelCase + + ext = getExt target + +-- file ext. depending on target +getExt :: AntlrTarget -> String +getExt Java = "java" +getExt CPP = "cpp" +getExt CSharp = "cs" +getExt JS = "js" +getExt TS = "ts" +getExt Dart = "dart" +getExt Python3 = "py" +getExt PHP = "php" +getExt Go = "go" +getExt Swift = "swift" + +toUppercase :: [Char] -> [Char] +toUppercase = map toUpper +toLowerCase = map toLower From 1c6074f251eb5ef3760eafde24a6b0bc3f1a025d Mon Sep 17 00:00:00 2001 From: Camille Date: Sun, 29 Oct 2023 03:21:15 +0300 Subject: [PATCH 22/70] added AST for Dart --- .vscode/tasks.json | 50 ++ source/BNFC.cabal | 4 + source/main/Main.hs | 2 + source/src/BNFC/Backend/Dart.hs | 656 ++++++++++++++++++++ source/src/BNFC/Backend/Dart/CFtoDartAbs.hs | 219 +++++++ source/src/BNFC/Options.hs | 7 +- 6 files changed, 937 insertions(+), 1 deletion(-) create mode 100644 .vscode/tasks.json create mode 100644 source/src/BNFC/Backend/Dart.hs create mode 100644 source/src/BNFC/Backend/Dart/CFtoDartAbs.hs diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..c7efda6e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,50 @@ + +{ + // Automatically created by phoityne-vscode extension. + + "version": "2.0.0", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "tasks": [ + { + // F7 + "group": { + "kind": "build", + "isDefault": true + }, + "label": "haskell build", + "type": "shell", + //"command": "cabal configure && cabal build" + "command": "stack build" + }, + { + // F6 + "group": "build", + "type": "shell", + "label": "haskell clean & build", + //"command": "cabal clean && cabal configure && cabal build" + "command": "stack clean && stack build" + //"command": "stack clean ; stack build" // for powershell + }, + { + // F8 + "group": { + "kind": "test", + "isDefault": true + }, + "type": "shell", + "label": "haskell test", + //"command": "cabal test" + "command": "stack test" + }, + { + // F6 + "isBackground": true, + "type": "shell", + "label": "haskell watch", + "command": "stack build --test --no-run-tests --file-watch" + } + ] +} diff --git a/source/BNFC.cabal b/source/BNFC.cabal index a072d3d3..5792d9ec 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -254,6 +254,10 @@ library BNFC.Backend.Java.RegToAntlrLexer BNFC.Backend.Java.Utils + -- Dart backend + BNFC.Backend.Dart + BNFC.Backend.Dart.CFtoDartAbs + -- XML backend BNFC.Backend.XML diff --git a/source/main/Main.hs b/source/main/Main.hs index 5486d60b..6b1773e5 100644 --- a/source/main/Main.hs +++ b/source/main/Main.hs @@ -25,6 +25,7 @@ import BNFC.Backend.Java import BNFC.Backend.Latex import BNFC.Backend.OCaml import BNFC.Backend.Pygments +import BNFC.Backend.Dart (makeDart) import BNFC.CF (CF) import BNFC.GetCF import BNFC.Options hiding (make, Backend) @@ -80,4 +81,5 @@ maketarget = \case TargetJava -> makeJava TargetOCaml -> makeOCaml TargetPygments -> makePygments + TargetDart -> makeDart TargetCheck -> error "impossible" diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs new file mode 100644 index 00000000..df625946 --- /dev/null +++ b/source/src/BNFC/Backend/Dart.hs @@ -0,0 +1,656 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} + +module BNFC.Backend.Dart ( makeDart ) where + +import Prelude hiding ((<>)) + +import System.FilePath ((), (<.>), pathSeparator, isPathSeparator) +import System.Cmd (system) +import Data.Foldable (toList) +import Data.List ( intersperse ) + +import BNFC.Utils +import BNFC.CF +import BNFC.Options as Options +import BNFC.Backend.Base +import BNFC.Backend.Java.Utils +import BNFC.Backend.Java.CFtoCup15 ( cf2Cup ) +import BNFC.Backend.Java.CFtoJLex15 +import BNFC.Backend.Java.CFtoAntlr4Lexer +import BNFC.Backend.Java.CFtoAntlr4Parser +import BNFC.Backend.Dart.CFtoDartAbs ( cf2DartAbs ) +import BNFC.Backend.Java.CFtoJavaPrinter15 +import BNFC.Backend.Java.CFtoVisitSkel15 +import BNFC.Backend.Java.CFtoComposVisitor +import BNFC.Backend.Java.CFtoAbstractVisitor +import BNFC.Backend.Java.CFtoFoldVisitor +import BNFC.Backend.Java.CFtoAllVisitor +import BNFC.Backend.Common.NamedVariables (SymEnv, firstLowerCase) +import qualified BNFC.Backend.Common.Makefile as Makefile +import BNFC.PrettyPrint + + +makeDart :: SharedOptions -> CF -> MkFiles () +makeDart opt = makeDart' pkg opt{ lang = lang' } + where + pkg = mkName javaReserved SnakeCase $ lang opt + lang' = capitalize $ mkName javaReserved CamelCase $ lang opt + +makeDart' :: String -> SharedOptions -> CF -> MkFiles () +makeDart' pkg options@Options{..} cf = do + -- Create the package directories if necessary. + let packageBase = maybe id (+.+) inPackage pkg + packageAbsyn = packageBase +.+ "Absyn" + dirBase = pkgToDir packageBase + dirAbsyn = pkgToDir packageAbsyn + javaex str = dirBase str <.> "dart" + bnfcfiles = + bnfcVisitorsAndTests + packageBase + packageAbsyn + cf + cf2JavaPrinter + cf2VisitSkel + cf2ComposVisitor + cf2AbstractVisitor + cf2FoldVisitor + cf2AllVisitor + (testclass parselexspec + (head $ results lexmake) -- lexer class + (head $ results parmake) -- parser class + ) + makebnfcfile x = mkfile (javaex (fst $ x bnfcfiles)) comment + (snd $ x bnfcfiles) + let absynContent = cf2DartAbs cf rp + absynDir = dirAbsyn ++ ".dart" + absynFileNames = [ absynDir ] + mkfile absynDir comment absynContent + -- makebnfcfile bprettyprinter + -- makebnfcfile bskel + -- makebnfcfile bcompos + -- makebnfcfile babstract + -- makebnfcfile bfold + -- makebnfcfile ball + -- makebnfcfile btest + -- let (lex, env) = lexfun packageBase cf + -- -- Where the lexer file is created. lex is the content! + -- mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex + -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake + -- +++ toolversion lexmake ++ ")" + -- -- where the parser file is created. + -- mkfile (dirBase inputfile parmake) commentWithEmacsModeHint + -- $ parsefun packageBase packageAbsyn cf rp env + -- liftIO $ putStrLn $ + -- if supportsEntryPoints parmake + -- then "(Parser created for all categories)" + -- else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" + -- liftIO $ putStrLn $ " (Tested with" +++ toolname parmake + -- +++ toolversion parmake ++ ")" + -- Makefile.mkMakefile optMake $ + -- makefile dirBase dirAbsyn absynFileNames parselexspec + where + remDups [] = [] + remDups ((a,b):as) = case lookup a as of + Just {} -> remDups as + Nothing -> (a, b) : remDups as + pkgToDir :: String -> FilePath + pkgToDir = replace '.' pathSeparator + + parselexspec = parserLexerSelector lang javaLexerParser rp + lexfun = cf2lex $ lexer parselexspec + parsefun = cf2parse $ parser parselexspec + parmake = makeparserdetails (parser parselexspec) + lexmake = makelexerdetails (lexer parselexspec) + rp = (Options.linenumbers options) + commentWithEmacsModeHint = comment . ("-*- Java -*- " ++) + +makefile :: FilePath -> FilePath -> [String] -> ParserLexerSpecification -> String -> Doc +makefile dirBase dirAbsyn absynFileNames jlexpar basename = vcat $ + makeVars [ ("JAVAC", "javac"), + ("JAVAC_FLAGS", "-sourcepath ."), + ( "JAVA", "java"), + ( "JAVA_FLAGS", ""), + -- parser executable + ( "PARSER", executable parmake), + -- parser flags + ( "PARSER_FLAGS", flags parmake dirBase), + -- lexer executable (and flags?) + ( "LEXER", executable lexmake), + ( "LEXER_FLAGS", flags lexmake dirBase) + ] + ++ + makeRules [ ("all", [ "test" ], []), + ( "test", "absyn" : classes, []), + ( ".PHONY", ["absyn"], []), + ("%.class", [ "%.java" ], [ runJavac "$^" ]), + ("absyn", [absynJavaSrc],[ runJavac "$^" ]) + ]++ + [-- running the lexergen: output of lexer -> input of lexer : calls lexer + let ff = filename lexmake -- name of input file without extension + dirBaseff = dirBase ff -- prepend directory + inp = dirBase inputfile lexmake in + Makefile.mkRule (dirBaseff <.> "java") [ inp ] + [ "${LEXER} ${LEXER_FLAGS} "++ inp ] + + -- running the parsergen, these there are its outputs + -- output of parser -> input of parser : calls parser + , let inp = dirBase inputfile parmake in + Makefile.mkRule (unwords (map (dirBase ) (dotJava $ results parmake))) + [ inp ] $ + ("${PARSER} ${PARSER_FLAGS} " ++ inp) : + ["mv " ++ unwords (dotJava $ results parmake) +++ dirBase ++ [pathSeparator] + | moveresults parmake] + -- Class of the output of lexer generator wants java of : + -- output of lexer and parser generator + , let lexerOutClass = dirBase filename lexmake <.> "class" + outname x = dirBase x <.> "java" + deps = map outname (results lexmake ++ results parmake) in + Makefile.mkRule lexerOutClass deps [] + ]++ + reverse [Makefile.mkRule tar dep [] | + (tar,dep) <- partialParserGoals dirBase (results parmake)] + ++[ Makefile.mkRule (dirBase "PrettyPrinter.class") + [ dirBase "PrettyPrinter.java" ] [] + -- Removes all the class files created anywhere + , Makefile.mkRule "clean" [] [ "rm -f " ++ dirAbsyn "*.class" ++ " " + ++ dirBase "*.class" ] + -- Remains the same + , Makefile.mkRule "distclean" [ "vclean" ] [] + -- removes everything + , Makefile.mkRule "vclean" [] + [ " rm -f " ++ absynJavaSrc ++ " " ++ absynJavaClass + , " rm -f " ++ dirAbsyn "*.class" + , " rmdir " ++ dirAbsyn + , " rm -f " ++ unwords (map (dirBase ) $ + [ inputfile lexmake + , inputfile parmake + ] + ++ dotJava (results lexmake) + ++ [ "VisitSkel.java" + , "ComposVisitor.java" + , "AbstractVisitor.java" + , "FoldVisitor.java" + , "AllVisitor.java" + , "PrettyPrinter.java" + , "Skeleton.java" + , "Test.java" + ] + ++ dotJava (results parmake) + ++ ["*.class"] + ++ other_results lexmake + ++ other_results parmake) + , " rm -f " ++ basename + , " rmdir -p " ++ dirBase + ] + ] + where + makeVars x = [Makefile.mkVar n v | (n,v) <- x] + makeRules x = [Makefile.mkRule tar dep recipe | (tar, dep, recipe) <- x] + parmake = makeparserdetails (parser jlexpar) + lexmake = makelexerdetails (lexer jlexpar) + absynJavaSrc = unwords (dotJava absynFileNames) + absynJavaClass = unwords (dotClass absynFileNames) + classes = map (dirBase ) lst + lst = dotClass (results lexmake) ++ [ "PrettyPrinter.class", "Test.class" + , "VisitSkel.class" + , "ComposVisitor.class", "AbstractVisitor.class" + , "FoldVisitor.class", "AllVisitor.class"]++ + dotClass (results parmake) ++ ["Test.class"] + +type TestClass = String + -- ^ class of the lexer + -> String + -- ^ class of the parser + -> String + -- ^ package where the non-abstract syntax classes are created + -> String + -- ^ package where the abstract syntax classes are created + -> CF + -- ^ the CF bundle + -> String + +-- | Record to name arguments of 'javaTest'. +data JavaTestParams = JavaTestParams + { jtpImports :: [Doc] + -- ^ List of imported packages. + , jtpErr :: String + -- ^ Name of the exception thrown in case of parsing failure. + , jtpErrHand :: (String -> [Doc]) + -- ^ Handler for the exception thrown. + , jtpLexerConstruction :: (Doc -> Doc -> Doc) + -- ^ Function formulating the construction of the lexer object. + , jtpParserConstruction :: (Doc -> Doc -> Doc) + -- ^ As above, for parser object. + , jtpShowAlternatives :: ([Cat] -> [Doc]) + -- ^ Pretty-print the names of the methods corresponding to entry points to the user. + , jtpInvocation :: (Doc -> Doc -> Doc -> Doc -> Doc) + -- ^ Function formulating the invocation of the parser tool within Java. + , jtpErrMsg :: String + -- ^ Error string output in consequence of a parsing failure. + } + +-- | Test class details for J(F)Lex + CUP +cuptest :: TestClass +cuptest = javaTest $ JavaTestParams + { jtpImports = ["java_cup.runtime"] + , jtpErr = "Throwable" + , jtpErrHand = const [] + , jtpLexerConstruction = \ x i -> x <> i <> ";" + , jtpParserConstruction = \ x i -> x <> "(" <> i <> ", " <> i <> ".getSymbolFactory());" + , jtpShowAlternatives = const $ ["not available."] + , jtpInvocation = \ _ pabs dat enti -> hcat [ pabs, ".", dat, " ast = p.p", enti, "();" ] + , jtpErrMsg = unwords $ + [ "At line \" + String.valueOf(t.l.line_num()) + \"," + , "near \\\"\" + t.l.buff() + \"\\\" :" + ] + } + +-- | Test class details for ANTLR4 +antlrtest :: TestClass +antlrtest = javaTest $ JavaTestParams + { jtpImports = + [ "org.antlr.v4.runtime" + , "org.antlr.v4.runtime.atn" + , "org.antlr.v4.runtime.dfa" + , "java.util" + ] + , jtpErr = + "TestError" + , jtpErrHand = + antlrErrorHandling + , jtpLexerConstruction = + \ x i -> vcat + [ x <> "(new ANTLRInputStream" <> i <>");" + , "l.addErrorListener(new BNFCErrorListener());" + ] + , jtpParserConstruction = + \ x i -> vcat + [ x <> "(new CommonTokenStream(" <> i <>"));" + , "p.addErrorListener(new BNFCErrorListener());" + ] + , jtpShowAlternatives = + showOpts + , jtpInvocation = + \ pbase pabs dat enti -> vcat + [ + let rulename = getRuleName $ startSymbol $ render enti + typename = text rulename + methodname = text $ firstLowerCase rulename + in + pbase <> "." <> typename <> "Context pc = p." <> methodname <> "();" + , pabs <> "." <> dat <+> "ast = pc.result;" + ] + , jtpErrMsg = + "At line \" + e.line + \", column \" + e.column + \" :" + } + where + showOpts [] = [] + showOpts (x:xs) + | normCat x /= x = showOpts xs + | otherwise = text (firstLowerCase $ identCat x) : showOpts xs + +parserLexerSelector :: + String + -> JavaLexerParser + -> RecordPositions -- ^Pass line numbers to the symbols + -> ParserLexerSpecification +parserLexerSelector _ JLexCup rp = ParseLexSpec + { lexer = cf2JLex rp + , parser = cf2cup rp + , testclass = cuptest + } +parserLexerSelector _ JFlexCup rp = + (parserLexerSelector "" JLexCup rp){lexer = cf2JFlex rp} +parserLexerSelector l Antlr4 _ = ParseLexSpec + { lexer = cf2AntlrLex' l + , parser = cf2AntlrParse' l + , testclass = antlrtest + } + +data ParserLexerSpecification = ParseLexSpec + { parser :: CFToParser + , lexer :: CFToLexer + , testclass :: TestClass + } + +-- |CF -> LEXER GENERATION TOOL BRIDGE +-- | function translating the CF to an appropriate lexer generation tool. +type CF2LexerFunction = String -> CF -> (Doc, SymEnv) + +-- Chooses the translation from CF to the lexer +data CFToLexer = CF2Lex + { cf2lex :: CF2LexerFunction + , makelexerdetails :: MakeFileDetails + } + +-- | Instances of cf-lexergen bridges + +cf2JLex :: RecordPositions -> CFToLexer +cf2JLex rp = CF2Lex + { cf2lex = cf2jlex JLexCup rp + , makelexerdetails = jlexmakedetails + } + +cf2JFlex :: RecordPositions -> CFToLexer +cf2JFlex rp = CF2Lex + { cf2lex = cf2jlex JFlexCup rp + , makelexerdetails = jflexmakedetails + } + +cf2AntlrLex' :: String -> CFToLexer +cf2AntlrLex' l = CF2Lex + { cf2lex = const $ cf2AntlrLex l + , makelexerdetails = antlrmakedetails $ l ++ "Lexer" + } + +-- | CF -> PARSER GENERATION TOOL BRIDGE +-- | function translating the CF to an appropriate parser generation tool. +type CF2ParserFunction = String -> String -> CF -> RecordPositions -> SymEnv -> String + +-- | Chooses the translation from CF to the parser +data CFToParser = CF2Parse + { cf2parse :: CF2ParserFunction + , makeparserdetails :: MakeFileDetails + } + +-- | Instances of cf-parsergen bridges +cf2cup :: RecordPositions -> CFToParser +cf2cup rp = CF2Parse + { cf2parse = cf2Cup + , makeparserdetails = cupmakedetails rp + } + +cf2AntlrParse' :: String -> CFToParser +cf2AntlrParse' l = CF2Parse + { cf2parse = const $ cf2AntlrParse l + , makeparserdetails = antlrmakedetails $ l ++ "Parser" + } + + +-- | shorthand for Makefile command running javac or java +runJavac , runJava:: String -> String +runJava = mkRunProgram "JAVA" +runJavac = mkRunProgram "JAVAC" + +-- | function returning a string executing a program contained in a variable j +-- on input s +mkRunProgram :: String -> String -> String +mkRunProgram j s = Makefile.refVar j +++ Makefile.refVar (j +-+ "FLAGS") +++ s + +type OutputDirectory = String + +-- | Makefile details from running the parser-lexer generation tools. +data MakeFileDetails = MakeDetails + { -- | The string that executes the generation tool. + executable :: String + , -- | Flags to pass to the tool. + flags :: OutputDirectory -> String + , -- | Input file to the tool. + filename :: String + , -- | Extension of input file to the tool. + fileextension :: String + , -- | Name of the tool. + toolname :: String + , -- | Tool version. + toolversion :: String + , -- | True if the tool is a parser and supports entry points, + -- False otherwise. + supportsEntryPoints :: Bool + , -- | List of names (without extension!) of files resulting from the + -- application of the tool which are relevant to a make rule. + results :: [String] + , -- | List of names of files resulting from the application of + -- the tool which are irrelevant to the make rules but need to be cleaned. + other_results :: [String] + , -- | If True, the files are moved to the base directory, otherwise + -- they are left where they are. + moveresults :: Bool + } + + +-- Instances of makefile details. + +jlexmakedetails :: MakeFileDetails +jlexmakedetails = MakeDetails + { executable = runJava "JLex.Main" + , flags = const "" + , filename = "Yylex" + , fileextension = "" + , toolname = "JLex" + , toolversion = "1.2.6" + , supportsEntryPoints = False + , results = ["Yylex"] + , other_results = [] + , moveresults = False + } + +jflexmakedetails :: MakeFileDetails +jflexmakedetails = jlexmakedetails + { executable = "jflex" + , toolname = "JFlex" + , toolversion = "1.4.3 - 1.9.1" + } + +cupmakedetails :: RecordPositions -> MakeFileDetails +cupmakedetails rp = MakeDetails + { executable = runJava "java_cup.Main" + , flags = const (lnFlags ++ " -expect 100") + , filename = "_cup" + , fileextension = "cup" + , toolname = "CUP" + , toolversion = "0.11b" + , supportsEntryPoints = False + , results = ["parser", "sym"] + , other_results = [] + , moveresults = True + } + where + lnFlags = if rp == RecordPositions then "-locations" else "-nopositions" + + +antlrmakedetails :: String -> MakeFileDetails +antlrmakedetails l = MakeDetails + { executable = runJava "org.antlr.v4.Tool" + , flags = \ path -> unwords $ + let pointed = map cnv path + cnv y = if isPathSeparator y + then '.' + else y + in [ "-lib", path + , "-package", pointed] + , filename = l + , fileextension = "g4" + , toolname = "ANTLRv4" + , toolversion = "4.9" + , supportsEntryPoints = True + , results = [l] + , other_results = map (l ++) + [ ".interp" -- added after ANTLR 4.5 + , ".tokens" + , "BaseListener.java" + ,"Listener.java" + ] + , moveresults = False + } + +dotJava :: [String] -> [String] +dotJava = map (<.> "java") + +dotClass :: [String] -> [String] +dotClass = map (<.> "class") + +type CFToJava = String -> String -> CF -> String + +-- | Contains the pairs filename/content for all the non-abstract syntax files +-- generated by BNFC. +data BNFCGeneratedEntities = BNFCGenerated + { bprettyprinter :: (String, String) + , btest :: (String, String) + , bcompos :: (String, String) + , babstract :: (String, String) + , bfold :: (String, String) + , ball :: (String, String) + , bskel :: (String, String) + } + +bnfcVisitorsAndTests :: String -> String -> CF -> + CFToJava -> CFToJava -> CFToJava -> + CFToJava -> CFToJava -> CFToJava -> + CFToJava -> BNFCGeneratedEntities +bnfcVisitorsAndTests pbase pabsyn cf cf0 cf1 cf2 cf3 cf4 cf5 cf6 = + BNFCGenerated + { bprettyprinter = ( "PrettyPrinter" , app cf0) + , bskel = ( "VisitSkel", app cf1) + , bcompos = ( "ComposVisitor" , app cf2) + , babstract = ( "AbstractVisitor" , app cf3) + , bfold = ( "FoldVisitor", app cf4) + , ball = ( "AllVisitor", app cf5) + , btest = ( "Test" , app cf6) + } + where app x = x pbase pabsyn cf + +inputfile :: MakeFileDetails -> String +inputfile x + | null (fileextension x) = filename x + | otherwise = filename x <.> fileextension x + +-- | constructs the rules regarding the parser in the makefile +partialParserGoals :: String -> [String] -> [(String, [String])] +partialParserGoals _ [] = [] +partialParserGoals dirBase (x:rest) = + (dirBase x <.> "class", map (\ y -> dirBase y <.> "java") (x:rest)) + : partialParserGoals dirBase rest + +-- | Creates the Test.java class. +javaTest :: JavaTestParams -> TestClass +javaTest (JavaTestParams + imports + err + errhand + lexerconstruction + parserconstruction + showOpts + invocation + errmsg) + lexer + parser + packageBase + packageAbsyn + cf = + render $ vcat $ concat $ + [ [ "package" <+> text packageBase <> ";" + , "" + , "import" <+> text packageBase <> ".*;" + , "import java.io.*;" + ] + , map importfun imports + , [ "" ] + , errhand err + , [ "" + , "public class Test" + , codeblock 2 + [ lx <+> "l;" + , px <+> "p;" + , "" + , "public Test(String[] args)" + , codeblock 2 + [ "try" + , codeblock 2 + [ "Reader input;" + , "if (args.length == 0) input = new InputStreamReader(System.in);" + , "else input = new FileReader(args[0]);" + , "l = new " <> lexerconstruction lx "(input)" + ] + , "catch(IOException e)" + , codeblock 2 + [ "System.err.println(\"Error: File not found: \" + args[0]);" + , "System.exit(1);" + ] + , "p = new "<> parserconstruction px "l" + ] + , "" + , "public" <+> text packageAbsyn <> "." <> dat + <+> "parse() throws Exception" + , codeblock 2 $ concat + [ [ "/* The default parser is the first-defined entry point. */" ] + , unlessNull (drop 1 eps) $ \ eps' -> + [ "/* Other options are: */" + , "/* " <> fsep (punctuate "," (showOpts eps')) <> " */" + ] + , [ invocation px (text packageAbsyn) dat absentity + , printOuts + [ "\"Parse Successful!\"" + , "\"[Abstract Syntax]\"" + , "PrettyPrinter.show(ast)" + , "\"[Linearized Tree]\"" + , "PrettyPrinter.print(ast)" + ] + , "return ast;" + ] + ] + , "" + , "public static void main(String args[]) throws Exception" + , codeblock 2 + [ "Test t = new Test(args);" + , "try" + , codeblock 2 [ "t.parse();" ] + ,"catch(" <> text err <+> "e)" + , codeblock 2 + [ "System.err.println(\"" <> text errmsg <> "\");" + , "System.err.println(\" \" + e.getMessage());" + , "System.exit(1);" + ] + ] + ] + ] + ] + where + printOuts x = vcat $ map javaPrintOut (messages x) + messages x = "" : intersperse "" x + javaPrintOut x = text $ "System.out.println(" ++ x ++ ");" + importfun x = "import" <+> x <> ".*;" + lx = text lexer + px = text parser + dat = text $ identCat $ normCat def -- Use for AST types. + absentity = text $ identCat def -- Use for parser/printer name. + eps = toList $ allEntryPoints cf + def = head eps + +-- | Error handling in ANTLR. +-- By default, ANTLR does not stop after any parsing error and attempts to go +-- on, delivering what it has been able to parse. +-- It does not throw any exception, unlike J(F)lex+CUP. +-- The below code makes the test class behave as with J(F)lex+CUP. +antlrErrorHandling :: String -> [Doc] +antlrErrorHandling te = + [ "class"<+>tedoc<+>"extends RuntimeException" + , codeblock 2 [ "int line;" + , "int column;" + , "public"<+>tedoc<>"(String msg, int l, int c)" + , codeblock 2 [ "super(msg);" + , "line = l;" + , "column = c;" + ] + ] + , "class BNFCErrorListener implements ANTLRErrorListener" + , codeblock 2 [ "@Override" + , "public void syntaxError(Recognizer recognizer, Object o, int i" + <> ", int i1, String s, RecognitionException e)" + , codeblock 2 [ "throw new"<+>tedoc<>"(s,i,i1);"] + , "@Override" + , "public void reportAmbiguity(Parser parser, DFA dfa, int i, int i1, " + <>"boolean b, BitSet bitSet, ATNConfigSet atnConfigSet)" + , codeblock 2[ "throw new"<+>tedoc<>"(\"Ambiguity at\",i,i1);" ] + , "@Override" + , "public void reportAttemptingFullContext(Parser parser, DFA dfa, " + <>"int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet)" + , codeblock 2 [] + , "@Override" + ,"public void reportContextSensitivity(Parser parser, DFA dfa, int i, " + <>"int i1, int i2, ATNConfigSet atnConfigSet)" + ,codeblock 2 [] + ] + ] + where tedoc = text te diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs new file mode 100644 index 00000000..aea399b1 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs @@ -0,0 +1,219 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Dart.CFtoDartAbs (cf2DartAbs) where + +import qualified Data.Char as Char +import Data.Maybe ( mapMaybe ) +import qualified Data.Map as Map + +import BNFC.CF +import BNFC.Options ( RecordPositions(..) ) +import BNFC.Utils ( (+++) ) + +import BNFC.Backend.Common.NamedVariables ( UserDef ) + +--Produces abstract data types in Dart + +-- The type of an instance variable. +-- Variable type, and its name +type DartVar = (String, DartVarName) + +-- The name of a variable. +-- the name generated from the type, +-- and the number making this variable unique +type DartVarName = (String, Int) + + +cf2DartAbs :: CF -> RecordPositions -> String +cf2DartAbs cf rp = + let userTokens = [ n | (n,_) <- tokenPragmas cf ] + in unlines $ + imports ++ -- import some libraries if needed + generateTokens userTokens ++ -- generate user-defined types + concatMap (prData rp) rules + where + rules = getAbstractSyntax cf + imports = [] + + +generateTokens :: [UserDef] -> [String] +generateTokens tokens = map toClass tokens + where + toClass token = + let name = censorName token + in unlines [ + "final class" +++ name +++ "{", -- A user defined type is a wrapper around the String + " final String value;", + " const" +++ name ++ "(this.value);", + "}" + ] + + +-- | Generates a (possibly abstract) category class, and classes for all its rules. +prData :: RecordPositions -> Data -> [String] +prData rp (cat, rules) = + categoryClass ++ mapMaybe (prRule rp cat) rules + where + funs = map fst rules + categoryClass + | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + | otherwise = [ "sealed class" +++ cat2DartClassName cat +++ "{}" ] + + +-- | Generates classes for a rule, depending on what type of rule it is. +prRule :: RecordPositions -> Cat -> (Fun, [Cat]) -> Maybe (String) +prRule rp cat (fun, cats) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing -- these are not represented in the Absyn + | otherwise = -- a standard rule + let + className = str2DartClassName fun + vars = getVars cats + in Just . unlines $ + [ unwords [ "class", className, extending, "{" ] ] ++ + concatMap addIndent [ + prInstanceVariables rp vars, + prConstructor className vars, + prEquals className vars, + prHashCode vars + ] ++ [ "}" ] + where + addIndent line = map (" " ++) line + extending + | fun == catToStr cat = "" + | otherwise = "extends" +++ cat2DartClassName cat + + +-- Because of the different type representing variables, a different `getVars` is used. +getVars :: [Cat] -> [DartVar] +getVars cats = concatMap mapEntryToVariable $ + Map.toList $ + foldl countVariables Map.empty $ + map toNames cats + where + toNames cat = ((cat2DartType cat), (cat2DartName cat)) + countVariables varsMap entry = + let current = Map.findWithDefault 0 entry varsMap + next = 1 + current + in Map.insert entry next varsMap + mapEntryToVariable ((varType, name), amount) + | amount <= 1 = [ toDartVar varType name 0 ] + | otherwise = + let variableNameBase = toDartVar varType name + in map variableNameBase $ [1..amount] + toDartVar varType name number = (varType, (name, number)) + + +-- Override the equality `==` +prEquals :: String -> [DartVar] -> [String] +prEquals className variables = [ + "@override", + "bool operator ==(Object o) =>", + " o is" +++ className +++ "&&", + " o.runtimeType == runtimeType" ++ + (if null variables then ";" else " &&") + ] ++ checkChildren + where + checkChildren = generateEqualities variables + generateEqualities [] = [] + generateEqualities (variable:rest) = + let name = buildVariableName variable + in [ + " " ++ name +++ "==" +++ "o." ++ name ++ + (if null rest then ";" else " &&") + ] ++ generateEqualities rest + + +-- Override the hashCode, combining all instance variables +prHashCode :: [DartVar] -> [String] +prHashCode vars = [ + "@override", + "int get hashCode => Object.hashAll([" ++ + concatMap variableHash vars ++ + "]);" + ] + where + variableHash variable = buildVariableName variable ++ ", " + + +-- Generate variable definitions for the class +prInstanceVariables :: RecordPositions -> [DartVar] -> [String] +prInstanceVariables rp vars = case rp of + RecordPositions -> ["int? line_num, col_num, offset;"] ++ generateVariables + NoRecordPositions -> generateVariables + where + generateVariables = map variableLine vars + variableLine variable@(varType, _) = + "final" +++ varType +++ buildVariableName variable ++ ";" + + +-- Generate the class constructor +prConstructor :: String -> [DartVar] -> [String] +prConstructor className vars = + [ className ++ "(" ++ variablesAssignment ++ ");" ] + where + variablesAssignment = concatMap assignment vars + assignment variable = "this." ++ buildVariableName variable ++ ", " + + +-- From a DartVar build its string representation +buildVariableName :: DartVar -> String +buildVariableName (_, (name, num)) = lowerFirst appendNumber + where + appendNumber + | num <= 0 = name + | otherwise = name ++ show num + + +-- Prevent some type or variable name to be called as some built-in Dart type +censorName :: String -> String +censorName name + | name `elem` builtInTypes = "My" ++ upperFirst name + | otherwise = name + where + builtInTypes = [ "int", "double", "String", "bool", "List", "Set", "Map", + "Runes", "Symbol", "null", "Null" ] + + +cat2DartClassName :: Cat -> String +cat2DartClassName cat = str2DartClassName $ identCat $ normCat cat + + +str2DartClassName :: String -> String +str2DartClassName str = upperFirst $ censorName str + + +cat2DartType :: Cat -> String +cat2DartType cat = toList $ normCat cat + where + toList (ListCat name) = "List<" ++ toList name ++ ">" + toList name = name2DartBuiltIn $ censorName $ catToStr name + + +cat2DartName :: Cat -> String +cat2DartName cat = toList $ normCat cat + where + toList (ListCat name) = toList name ++ "List" + toList name = censorName $ catToStr name + + +name2DartBuiltIn :: String -> String +name2DartBuiltIn name + | name == "Integer" = "int" + | name == "Double" = "double" + | name == "Ident" = "String" + | name == "Char" = "String" -- TODO + | otherwise = name + + +upperFirst :: [Char] -> [Char] +upperFirst [] = [] +upperFirst (letter:rest) = Char.toUpper letter : rest + + +lowerFirst :: [Char] -> [Char] +lowerFirst [] = [] +lowerFirst (letter:rest) = Char.toLower letter : rest \ No newline at end of file diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 09cd477e..1ee3d61a 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -63,7 +63,7 @@ data Mode data Target = TargetC | TargetCpp | TargetCppNoStl | TargetHaskell | TargetHaskellGadt | TargetLatex | TargetJava | TargetOCaml | TargetPygments - | TargetCheck + | TargetCheck | TargetDart deriving (Eq, Bounded, Enum, Ord) -- | List of Haskell target. @@ -80,6 +80,7 @@ instance Show Target where show TargetJava = "Java" show TargetOCaml = "OCaml" show TargetPygments = "Pygments" + show TargetDart = "Dart" show TargetCheck = "Check LBNF file" -- | Which version of Alex is targeted? @@ -258,6 +259,7 @@ printTargetOption = ("--" ++) . \case TargetJava -> "java" TargetOCaml -> "ocaml" TargetPygments -> "pygments" + TargetDart -> "dart" TargetCheck -> "check" printAlexOption :: AlexVersion -> String @@ -309,6 +311,8 @@ targetOptions = "Output OCaml code for use with ocamllex and menhir (short for --ocaml --menhir)" , Option "" ["pygments"] (NoArg (\o -> o {target = TargetPygments})) "Output a Python lexer for Pygments" + , Option "" ["dart"] (NoArg (\ o -> o{target = TargetDart })) + "Output Dart code for use with ANTLR" , Option "" ["check"] (NoArg (\ o -> o{target = TargetCheck })) "No output. Just check input LBNF file" ] @@ -524,6 +528,7 @@ instance Maintained Target where TargetJava -> True TargetOCaml -> True TargetPygments -> True + TargetDart -> True TargetCheck -> True instance Maintained AlexVersion where From 95cba43b75c76fe22175d4b05a17bef76bec1c0f Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Mon, 6 Nov 2023 14:13:09 +0300 Subject: [PATCH 23/70] [ANTLRv4] remove unworking code --- source/src/BNFC/Backend/Antlr.hs | 67 +++----------------------- source/src/BNFC/Backend/Antlr/Utils.hs | 6 +-- 2 files changed, 9 insertions(+), 64 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 5dc9841b..45f31597 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -5,16 +5,13 @@ module BNFC.Backend.Antlr ( makeAntlr ) where import Prelude hiding ((<>)) import System.FilePath ((), pathSeparator, (<.>)) import Text.PrettyPrint.HughesPJ (vcat) -import Data.Bifunctor (second) -import Data.Char (toUpper, toLower) import BNFC.Utils - ( NameStyle(CamelCase, SnakeCase), + ( NameStyle(CamelCase), mkName, replace, (+.+), - (+++), - mkNames ) + (+++) ) import BNFC.CF import BNFC.Options as Options import BNFC.Backend.Base @@ -53,9 +50,7 @@ makeAntlr opts@Options{..} cf = do parserVarName = "PARSER_GRAMMAR_FILENAME" parserGrammarFile = (langRef ) . dotG4 $ langRef ++ "Parser" - generatedFilesVars = map (second (langRef )) $ getVars dLanguage pkg - - makefileVars = vcat $ makeVars $ + makefileVars = vcat $ makeVars [ ("LANG", pkg) , (lexerVarName, lexerGrammarFile) , (parserVarName, parserGrammarFile) @@ -63,17 +58,14 @@ makeAntlr opts@Options{..} cf = do , ("ANTLR_OPTIONS", getAntlrOptions opts) , ("DIRECT_OPTIONS", antlrOpts) ] - ++ generatedFilesVars genAntlrRecipe = ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" +++ MakeFile.refVar "DIRECT_OPTIONS") +++) . MakeFile.refVar - rmFile refVar = "rm -f" +++ MakeFile.refVar refVar - antlrFiles = map (langRef ) - [ (mkName [] CamelCase $ pkg +++ "Lexer") <.> "interp" - , (mkName [] CamelCase $ pkg +++ "Parser") <.> "interp" - , (mkName [] CamelCase $ pkg +++ "Lexer") <.> "tokens" - , (mkName [] CamelCase $ pkg +++ "Parser") <.> "tokens" + [ mkName [] CamelCase (pkg +++ "Lexer") <.> "interp" + , mkName [] CamelCase (pkg +++ "Parser") <.> "interp" + , mkName [] CamelCase (pkg +++ "Lexer") <.> "tokens" + , mkName [] CamelCase (pkg +++ "Parser") <.> "tokens" ] makefileRules = vcat $ makeRules @@ -83,8 +75,6 @@ makeAntlr opts@Options{..} cf = do , ("parser", [MakeFile.refVar parserVarName], [genAntlrRecipe parserVarName]) , (langRef, ["lexer", "parser"], []) , ("clean-antlr", [], - map rmFile targetLanguageFiles - ++ map ("rm -f" +++) antlrFiles ) , ("remove", [], ["rm -rf" +++ langRef]) ] @@ -93,46 +83,3 @@ makeAntlr opts@Options{..} cf = do mkAntlrComment :: String -> String mkAntlrComment = ("// ANTLRv4 " ++) - -targetLanguageFiles :: [String] -targetLanguageFiles = ["LEXER", "PARSER", "LISTENER", "VISITOR", "BASE_LISTENER", "BASE_VISITOR"] - -getVars :: AntlrTarget -> [Char] -> [(String, FilePath)] -getVars target lang = zip targetLanguageFiles files - where - files = map (<.> ext) names - names = mkNames [] namestyle - [ filename "lexer" - , filename "parser" - , filename "parser listener" - , filename "parser visitor" - , filename "parser base listener" - , filename "parser base visitor" - ] - - filename = case target of - Go -> (toLowerCase lang ++) - _ -> (lang +++) - - namestyle = case target of - Go -> SnakeCase - _ -> CamelCase - - ext = getExt target - --- file ext. depending on target -getExt :: AntlrTarget -> String -getExt Java = "java" -getExt CPP = "cpp" -getExt CSharp = "cs" -getExt JS = "js" -getExt TS = "ts" -getExt Dart = "dart" -getExt Python3 = "py" -getExt PHP = "php" -getExt Go = "go" -getExt Swift = "swift" - -toUppercase :: [Char] -> [Char] -toUppercase = map toUpper -toLowerCase = map toLower diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 178753a7..0d052dda 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -2,14 +2,12 @@ module BNFC.Backend.Antlr.Utils where -import Text.PrettyPrint.HughesPJ (Doc, text, vcat) -import Prelude hiding (Either, Left, Right) -import System.FilePath ((<.>), ()) +import Prelude +import System.FilePath ((<.>)) import BNFC.CF (Fun) import BNFC.Utils ( mkName, NameStyle(..), (+++)) import BNFC.Options as Options -import BNFC.Backend.Common.Makefile as MakeFile getRuleName :: String -> String getRuleName z = if z == "grammar" then z ++ "_" else z From 1636c9e037ee6c79426700bf575e4c7732b4406a Mon Sep 17 00:00:00 2001 From: Camille Date: Tue, 7 Nov 2023 02:44:25 +0300 Subject: [PATCH 24/70] added an AST builder from the ANTLR parser --- source/BNFC.cabal | 2 + source/src/BNFC/Backend/Dart.hs | 6 +- source/src/BNFC/Backend/Dart/CFtoDartAbs.hs | 101 +-------------- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 100 ++++++++++++++ source/src/BNFC/Backend/Dart/Common.hs | 122 ++++++++++++++++++ 5 files changed, 235 insertions(+), 96 deletions(-) create mode 100644 source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs create mode 100644 source/src/BNFC/Backend/Dart/Common.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 5792d9ec..5a8bbaf7 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -257,6 +257,8 @@ library -- Dart backend BNFC.Backend.Dart BNFC.Backend.Dart.CFtoDartAbs + BNFC.Backend.Dart.CFtoDartBuilder + BNFC.Backend.Dart.Common -- XML backend BNFC.Backend.XML diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index df625946..a5458c45 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -21,6 +21,7 @@ import BNFC.Backend.Java.CFtoJLex15 import BNFC.Backend.Java.CFtoAntlr4Lexer import BNFC.Backend.Java.CFtoAntlr4Parser import BNFC.Backend.Dart.CFtoDartAbs ( cf2DartAbs ) +import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Java.CFtoJavaPrinter15 import BNFC.Backend.Java.CFtoVisitSkel15 import BNFC.Backend.Java.CFtoComposVisitor @@ -42,7 +43,7 @@ makeDart' :: String -> SharedOptions -> CF -> MkFiles () makeDart' pkg options@Options{..} cf = do -- Create the package directories if necessary. let packageBase = maybe id (+.+) inPackage pkg - packageAbsyn = packageBase +.+ "Absyn" + packageAbsyn = packageBase +.+ "ast" dirBase = pkgToDir packageBase dirAbsyn = pkgToDir packageAbsyn javaex str = dirBase str <.> "dart" @@ -66,7 +67,10 @@ makeDart' pkg options@Options{..} cf = do let absynContent = cf2DartAbs cf rp absynDir = dirAbsyn ++ ".dart" absynFileNames = [ absynDir ] + builderContent = cf2DartBuilder cf + builderDir = dirAbsyn ++ "Builder.dart" mkfile absynDir comment absynContent + mkfile builderDir comment builderContent -- makebnfcfile bprettyprinter -- makebnfcfile bskel -- makebnfcfile bcompos diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs index aea399b1..7528f938 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs @@ -4,27 +4,17 @@ module BNFC.Backend.Dart.CFtoDartAbs (cf2DartAbs) where -import qualified Data.Char as Char import Data.Maybe ( mapMaybe ) -import qualified Data.Map as Map import BNFC.CF import BNFC.Options ( RecordPositions(..) ) import BNFC.Utils ( (+++) ) import BNFC.Backend.Common.NamedVariables ( UserDef ) +import BNFC.Backend.Dart.Common --Produces abstract data types in Dart --- The type of an instance variable. --- Variable type, and its name -type DartVar = (String, DartVarName) - --- The name of a variable. --- the name generated from the type, --- and the number making this variable unique -type DartVarName = (String, Int) - cf2DartAbs :: CF -> RecordPositions -> String cf2DartAbs cf rp = @@ -74,39 +64,18 @@ prRule rp cat (fun, cats) vars = getVars cats in Just . unlines $ [ unwords [ "class", className, extending, "{" ] ] ++ - concatMap addIndent [ + concatMap (indent 1) [ prInstanceVariables rp vars, prConstructor className vars, prEquals className vars, prHashCode vars ] ++ [ "}" ] where - addIndent line = map (" " ++) line extending | fun == catToStr cat = "" | otherwise = "extends" +++ cat2DartClassName cat --- Because of the different type representing variables, a different `getVars` is used. -getVars :: [Cat] -> [DartVar] -getVars cats = concatMap mapEntryToVariable $ - Map.toList $ - foldl countVariables Map.empty $ - map toNames cats - where - toNames cat = ((cat2DartType cat), (cat2DartName cat)) - countVariables varsMap entry = - let current = Map.findWithDefault 0 entry varsMap - next = 1 + current - in Map.insert entry next varsMap - mapEntryToVariable ((varType, name), amount) - | amount <= 1 = [ toDartVar varType name 0 ] - | otherwise = - let variableNameBase = toDartVar varType name - in map variableNameBase $ [1..amount] - toDartVar varType name number = (varType, (name, number)) - - -- Override the equality `==` prEquals :: String -> [DartVar] -> [String] prEquals className variables = [ @@ -146,8 +115,10 @@ prInstanceVariables rp vars = case rp of NoRecordPositions -> generateVariables where generateVariables = map variableLine vars - variableLine variable@(varType, _) = - "final" +++ varType +++ buildVariableName variable ++ ";" + variableLine variable = + let vType = buildVariableType variable + vName = buildVariableName variable + in "final" +++ vType +++ vName ++ ";" -- Generate the class constructor @@ -157,63 +128,3 @@ prConstructor className vars = where variablesAssignment = concatMap assignment vars assignment variable = "this." ++ buildVariableName variable ++ ", " - - --- From a DartVar build its string representation -buildVariableName :: DartVar -> String -buildVariableName (_, (name, num)) = lowerFirst appendNumber - where - appendNumber - | num <= 0 = name - | otherwise = name ++ show num - - --- Prevent some type or variable name to be called as some built-in Dart type -censorName :: String -> String -censorName name - | name `elem` builtInTypes = "My" ++ upperFirst name - | otherwise = name - where - builtInTypes = [ "int", "double", "String", "bool", "List", "Set", "Map", - "Runes", "Symbol", "null", "Null" ] - - -cat2DartClassName :: Cat -> String -cat2DartClassName cat = str2DartClassName $ identCat $ normCat cat - - -str2DartClassName :: String -> String -str2DartClassName str = upperFirst $ censorName str - - -cat2DartType :: Cat -> String -cat2DartType cat = toList $ normCat cat - where - toList (ListCat name) = "List<" ++ toList name ++ ">" - toList name = name2DartBuiltIn $ censorName $ catToStr name - - -cat2DartName :: Cat -> String -cat2DartName cat = toList $ normCat cat - where - toList (ListCat name) = toList name ++ "List" - toList name = censorName $ catToStr name - - -name2DartBuiltIn :: String -> String -name2DartBuiltIn name - | name == "Integer" = "int" - | name == "Double" = "double" - | name == "Ident" = "String" - | name == "Char" = "String" -- TODO - | otherwise = name - - -upperFirst :: [Char] -> [Char] -upperFirst [] = [] -upperFirst (letter:rest) = Char.toUpper letter : rest - - -lowerFirst :: [Char] -> [Char] -lowerFirst [] = [] -lowerFirst (letter:rest) = Char.toLower letter : rest \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs new file mode 100644 index 00000000..66013e32 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -0,0 +1,100 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Dart.CFtoDartBuilder (cf2DartBuilder) where + +import BNFC.CF +import BNFC.Backend.Dart.Common +import Data.Maybe ( mapMaybe ) +import BNFC.Utils ( (+++) ) + +cf2DartBuilder :: CF -> String +cf2DartBuilder cf = + let userTokens = [ n | (n,_) <- tokenPragmas cf ] + in + unlines $ + imports ++ + helperFunctions ++ + concatMap generateBuilders rules + where + rules = getAbstractSyntax cf + imports = [ + "import 'package:antlr4/antlr4.dart';", + "import 'ast.dart';", + "import 'stellaParser.dart'; // fix this line depending on where the stellaParser is being lcated" ] + helperFunctions = [ + "extension IList on List {", + " List iMap(T Function(E e) toElement) =>", + " map(toElement).toList(growable: false);", + "}" ] + + +generateBuilders :: Data -> [String] +generateBuilders (cat, rules) = + runtimeTypeMapping ++ concatMap (generateConcreteMapping cat) rules + where + funs = map fst rules + runtimeTypeMapping + | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + | otherwise = generateRuntimeTypeMapping cat rules + + +generateRuntimeTypeMapping :: Cat -> [(Fun, [Cat])] -> [String] +generateRuntimeTypeMapping cat rules = + let className = upperFirst $ cat2DartClassName cat + in + generateFunctionHeader className ++ + indent 2 ( + [ "switch (ctx.runtimeType) {" ] ++ + (indent 1 $ map buildChild $ map buildClassName rules) ++ + [ "};" ] + ) + where + buildClassName (fun, _) = str2DartClassName fun + buildChild name = (contextName name) +++ "c => build" ++ name ++ "(c)," + + + +generateConcreteMapping :: Cat -> (Fun, [Cat]) -> [String] +generateConcreteMapping cat (fun, cats) + | isNilFun fun || + isOneFun fun || + isConsFun fun = [] -- these are not represented in the ast + | otherwise = -- a standard rule + let + className = upperFirst $ cat2DartClassName cat + vars = getVars cats + in + generateFunctionHeader className ++ + indent 2 ( + [ className ++ "(" ] ++ + (indent 1 $ generateArgumentsMapping vars) ++ + [ ");" ] + ) + + +generateArgumentsMapping :: [DartVar] -> [String] +generateArgumentsMapping vars = map convertArgument vars + where + convertArgument var@(vType, _) = + let name = buildVariableName var + field = "ctx." ++ name -- TODO + in name ++ ":" +++ buildArgument vType field + buildArgument :: DartVarType -> String -> String + buildArgument (0, typeName) name = + "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," + buildArgument (n, typeName) name = + "name.iMap((e" ++ show n ++ ") =>" +++ buildArgument (n - 1, typeName) name ++ ")," + + +generateFunctionHeader :: String -> [String] +generateFunctionHeader className = [ + className +++ "build" ++ className ++ "(", + " " ++ contextName className +++ "ctx,", + ") =>" + ] + + +contextName :: String -> String +contextName className = className ++ "Context" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs new file mode 100644 index 00000000..ae1f33b3 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -0,0 +1,122 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Dart.Common where + +import qualified Data.Map as Map +import BNFC.CF +import qualified Data.Char as Char + + +cat2DartClassName :: Cat -> String +cat2DartClassName cat = str2DartClassName $ identCat $ normCat cat + + +str2DartClassName :: String -> String +str2DartClassName str = upperFirst $ censorName str + + +cat2DartType :: Cat -> (Int, String) +cat2DartType cat = toList (0, normCat cat) + where + toList :: (Int, Cat) -> (Int, String) + toList (n, (ListCat name)) = toList (n + 1, name) + toList (n, name) = (n, (name2DartBuiltIn $ censorName $ catToStr name)) + + +cat2DartName :: Cat -> String +cat2DartName cat = toList $ normCat cat + where + toList (ListCat name) = toList name ++ "List" + toList name = censorName $ catToStr name + + +name2DartBuiltIn :: String -> String +name2DartBuiltIn name + | name == "Integer" = "int" + | name == "Double" = "double" + | name == "Ident" = "String" + | name == "Char" = "String" -- TODO + | otherwise = name + + +upperFirst :: [Char] -> [Char] +upperFirst [] = [] +upperFirst (letter:rest) = Char.toUpper letter : rest + + +lowerFirst :: [Char] -> [Char] +lowerFirst [] = [] +lowerFirst (letter:rest) = Char.toLower letter : rest + + +indent :: Int -> [String] -> [String] +indent n lines = map addSpaces lines + where + addSpaces :: String -> String + addSpaces line = (replicate (2 * n) ' ') ++ line + + +-- The type of an instance variable. +-- Variable type, and its name +type DartVar = (DartVarType, DartVarName) + + +-- The type of a variable type in Dart. +-- The amount of nestings, and the underlying type name. +-- Example: List> is (2, Point). +-- This helps to build the AST builder +type DartVarType = (Int, String) + + +-- The name of a variable. +-- the name generated from the type, +-- and the number making this variable unique +type DartVarName = (String, Int) + + +-- Because of the different type representing variables, a different `getVars` is used. +getVars :: [Cat] -> [DartVar] +getVars cats = concatMap mapEntryToVariable $ + Map.toList $ + foldl countVariables Map.empty $ + map toNames cats + where + toNames cat = ((cat2DartType cat), (cat2DartName cat)) + countVariables varsMap entry = + let current = Map.findWithDefault 0 entry varsMap + next = 1 + current + in Map.insert entry next varsMap + mapEntryToVariable ((varType, name), amount) + | amount <= 1 = [ toDartVar varType name 0 ] + | otherwise = + let variableNameBase = toDartVar varType name + in map variableNameBase $ [1..amount] + toDartVar varType name number = (varType, (name, number)) + + +-- From a DartVar build its string representation +buildVariableName :: DartVar -> String +buildVariableName (_, (name, num)) = lowerFirst appendNumber + where + appendNumber + | num <= 0 = name + | otherwise = name ++ show num + + +buildVariableType :: DartVar -> String +buildVariableType (vType, _) = unpack vType + where + unpack (0, name) = name + unpack (n, name) = "List<" ++ unpack (n - 1, name) ++ ">" + + +-- Prevent some type or variable name to be called as some built-in Dart type +censorName :: String -> String +censorName name + | name `elem` builtInTypes = "My" ++ upperFirst name + | otherwise = name + where + builtInTypes = [ "int", "double", "String", "bool", "List", "Set", "Map", + "Runes", "Symbol", "null", "Null" ] \ No newline at end of file From 57ddbc3483506966ccee4cdbde88e662702a26ea Mon Sep 17 00:00:00 2001 From: xdkomel Date: Wed, 22 Nov 2023 18:19:40 +0300 Subject: [PATCH 25/70] fix ast, remove the custom parser generator --- source/BNFC.cabal | 1 + source/src/BNFC/Backend/Dart.hs | 61 +++-- .../src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs | 225 ++++++++++++++++++ source/src/BNFC/Backend/Dart/CFtoDartAbs.hs | 4 +- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 22 +- 5 files changed, 270 insertions(+), 43 deletions(-) create mode 100644 source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 5a8bbaf7..ba4ceaf9 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -258,6 +258,7 @@ library BNFC.Backend.Dart BNFC.Backend.Dart.CFtoDartAbs BNFC.Backend.Dart.CFtoDartBuilder + BNFC.Backend.Dart.CFtoAntlr4Parser BNFC.Backend.Dart.Common -- XML backend diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index a5458c45..3829beff 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -19,7 +19,7 @@ import BNFC.Backend.Java.Utils import BNFC.Backend.Java.CFtoCup15 ( cf2Cup ) import BNFC.Backend.Java.CFtoJLex15 import BNFC.Backend.Java.CFtoAntlr4Lexer -import BNFC.Backend.Java.CFtoAntlr4Parser +import BNFC.Backend.Dart.CFtoAntlr4Parser import BNFC.Backend.Dart.CFtoDartAbs ( cf2DartAbs ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Java.CFtoJavaPrinter15 @@ -64,36 +64,35 @@ makeDart' pkg options@Options{..} cf = do ) makebnfcfile x = mkfile (javaex (fst $ x bnfcfiles)) comment (snd $ x bnfcfiles) - let absynContent = cf2DartAbs cf rp - absynDir = dirAbsyn ++ ".dart" - absynFileNames = [ absynDir ] - builderContent = cf2DartBuilder cf - builderDir = dirAbsyn ++ "Builder.dart" - mkfile absynDir comment absynContent - mkfile builderDir comment builderContent - -- makebnfcfile bprettyprinter - -- makebnfcfile bskel - -- makebnfcfile bcompos - -- makebnfcfile babstract - -- makebnfcfile bfold - -- makebnfcfile ball - -- makebnfcfile btest - -- let (lex, env) = lexfun packageBase cf - -- -- Where the lexer file is created. lex is the content! - -- mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex - -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake - -- +++ toolversion lexmake ++ ")" - -- -- where the parser file is created. - -- mkfile (dirBase inputfile parmake) commentWithEmacsModeHint - -- $ parsefun packageBase packageAbsyn cf rp env - -- liftIO $ putStrLn $ - -- if supportsEntryPoints parmake - -- then "(Parser created for all categories)" - -- else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" - -- liftIO $ putStrLn $ " (Tested with" +++ toolname parmake - -- +++ toolversion parmake ++ ")" + let locate str ext = dirBase str <.> ext + -- (lex, env) = cf2AntlrLex "Stella" cf + mkfile (locate "ast" "dart") comment (cf2DartAbs cf rp) + mkfile (locate "builder" "dart") comment (cf2DartBuilder cf) + -- mkfile (locate "StellaLexer" "g4") comment lex + -- mkfile (locate "StellaParser" "g4") comment (cf2AntlrParse lang dirBase cf rp env) + makebnfcfile bprettyprinter + makebnfcfile bskel + makebnfcfile bcompos + makebnfcfile babstract + makebnfcfile bfold + makebnfcfile ball + makebnfcfile btest + let (lex, env) = lexfun packageBase cf + -- Where the lexer file is created. lex is the content! + mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex + liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake + +++ toolversion lexmake ++ ")" + -- where the parser file is created. + mkfile (dirBase inputfile parmake) commentWithEmacsModeHint + $ parsefun packageBase packageAbsyn cf rp env + liftIO $ putStrLn $ + if supportsEntryPoints parmake + then "(Parser created for all categories)" + else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" + liftIO $ putStrLn $ " (Tested with" +++ toolname parmake + +++ toolversion parmake ++ ")" -- Makefile.mkMakefile optMake $ - -- makefile dirBase dirAbsyn absynFileNames parselexspec + -- makefile dirBase dirAbsyn ["stella/ast.dart"] parselexspec where remDups [] = [] remDups ((a,b):as) = case lookup a as of @@ -102,7 +101,7 @@ makeDart' pkg options@Options{..} cf = do pkgToDir :: String -> FilePath pkgToDir = replace '.' pathSeparator - parselexspec = parserLexerSelector lang javaLexerParser rp + parselexspec = parserLexerSelector lang Antlr4 rp lexfun = cf2lex $ lexer parselexspec parsefun = cf2parse $ parser parselexspec parmake = makeparserdetails (parser parselexspec) diff --git a/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs new file mode 100644 index 00000000..783733b1 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs @@ -0,0 +1,225 @@ +-- {-# LANGUAGE LambdaCase #-} + +-- module BNFC.Backend.Dart.CFtoAntlr4Parser ( cf2AntlrParse ) where + +-- import Data.Foldable ( toList ) +-- import Data.List ( intercalate ) +-- import Data.Maybe + +-- import BNFC.CF +-- import BNFC.Options ( RecordPositions(..) ) +-- import BNFC.Utils ( (+++), (+.+), applyWhen ) + +-- import BNFC.Backend.Java.Utils +-- import BNFC.Backend.Common.NamedVariables + +-- -- Type declarations + +-- -- | A definition of a non-terminal by all its rhss, +-- -- together with parse actions. +-- data PDef = PDef +-- { _pdNT :: Maybe String +-- -- ^ If given, the name of the lhss. Usually computed from 'pdCat'. +-- , _pdCat :: Cat +-- -- ^ The category to parse. +-- , _pdAlts :: [(Pattern, Action, Maybe Fun)] +-- -- ^ The possible rhss with actions. If 'null', skip this 'PDef'. +-- -- Where 'Nothing', skip ANTLR rule label. +-- } +-- type Rules = [PDef] +-- type Pattern = String +-- type Action = String +-- type MetaVar = (String, Cat) + +-- -- | Creates the ANTLR parser grammar for this CF. +-- --The environment comes from CFtoAntlr4Lexer +-- cf2AntlrParse :: String -> String -> CF -> RecordPositions -> KeywordEnv -> String +-- cf2AntlrParse lang packageAbsyn cf _ env = unlines $ concat +-- [ [ header +-- , tokens +-- , "" +-- -- Generate start rules [#272] +-- -- _X returns [ dX result ] : x=X EOF { $result = $x.result; } +-- -- , prRules packageAbsyn $ map entrypoint $ toList $ allEntryPoints cf +-- -- Generate regular rules +-- , prRules packageAbsyn $ rulesForAntlr4 packageAbsyn cf env +-- ] +-- ] +-- where +-- header :: String +-- header = unlines +-- [ "// Parser definition for use with ANTLRv4" +-- , "parser grammar" +++ lang ++ "Parser;" +-- ] +-- tokens :: String +-- tokens = unlines +-- [ "options {" +-- , " tokenVocab = " ++ lang ++ "Lexer;" +-- , "}" +-- ] + +-- -- | Generate start rule to help ANTLR. +-- -- +-- -- @start_X returns [ X result ] : x=X EOF { $result = $x.result; } # Start_X@ +-- -- +-- entrypoint :: Cat -> PDef +-- entrypoint cat = +-- PDef (Just nt) cat [(pat, act, fun)] +-- where +-- nt = firstLowerCase $ startSymbol $ identCat cat +-- pat = "x=" ++ catToNT cat +++ "EOF" +-- act = "$result = $x.result;" +-- fun = Nothing -- No ANTLR Rule label, ("Start_" ++ identCat cat) conflicts with lhs. + +-- --The following functions are a (relatively) straightforward translation +-- --of the ones in CFtoHappy.hs +-- rulesForAntlr4 :: String -> CF -> KeywordEnv -> Rules +-- rulesForAntlr4 packageAbsyn cf env = map mkOne getrules +-- where +-- getrules = ruleGroups cf +-- mkOne (cat,rules) = constructRule packageAbsyn cf env rules cat + +-- -- | For every non-terminal, we construct a set of rules. A rule is a sequence of +-- -- terminals and non-terminals, and an action to be performed. +-- constructRule :: String -> CF -> KeywordEnv -> [Rule] -> NonTerminal -> PDef +-- constructRule packageAbsyn cf env rules nt = +-- PDef Nothing nt $ +-- [ ( p +-- , generateAction packageAbsyn nt (funRule r) m b +-- , Nothing -- labels not needed for BNFC-generated AST parser +-- -- , Just label +-- -- -- Did not work: +-- -- -- , if firstLowerCase (getLabelName label) +-- -- -- == getRuleName (firstLowerCase $ identCat nt) then Nothing else Just label +-- ) +-- | (index, r0) <- zip [1..] rules +-- , let b = isConsFun (funRule r0) && elem (valCat r0) (cfgReversibleCats cf) +-- , let r = applyWhen b revSepListRule r0 +-- , let (p,m0) = generatePatterns index env r +-- , let m = applyWhen b reverse m0 +-- -- , let label = funRule r +-- ] + +-- -- Generates a string containing the semantic action. +-- generateAction :: IsFun f => String -> NonTerminal -> f -> [MetaVar] +-- -> Bool -- ^ Whether the list should be reversed or not. +-- -- Only used if this is a list rule. +-- -> Action +-- generateAction packageAbsyn nt f ms rev +-- | isNilFun f = "$result = " ++ c ++ "();" +-- | isOneFun f = "$result = " ++ c ++ "(); $result.addLast(" +-- ++ p_1 ++ ");" +-- | isConsFun f = "$result = " ++ p_2 ++ "; " +-- ++ "$result." ++ add ++ "(" ++ p_1 ++ ");" +-- | isCoercion f = "$result = " ++ p_1 ++ ";" +-- | isDefinedRule f = "$result = " ++ packageAbsyn ++ "Def." ++ sanitize (funName f) +-- ++ "(" ++ intercalate "," (map resultvalue ms) ++ ");" +-- | otherwise = "$result = " ++ c +-- ++ "(" ++ intercalate "," (map resultvalue ms) ++ ");" +-- where +-- sanitize = getRuleName +-- c = if isNilFun f || isOneFun f || isConsFun f +-- then identCat (normCat nt) else funName f +-- p_1 = resultvalue $ ms!!0 +-- p_2 = resultvalue $ ms!!1 +-- add = if rev then "addLast" else "addFirst" +-- removeQuotes x = x +.+ "substring(1, " ++ x +.+ "length()-1)" +-- unescape x = removeQuotes x +.+ "translateEscapes()" -- Java 15 and higher +-- resultvalue (n,c) = case c of +-- TokenCat "Double" -> concat [ "double.parse(", txt, ")" ] +-- TokenCat "Integer" -> concat [ "int.parse(" , txt, ")" ] +-- TokenCat "Char" -> unescape txt +.+ "charAt(0)" +-- TokenCat "String" -> unescape txt +-- TokenCat "Ident" -> txt +-- c | isTokenCat c -> txt +-- | otherwise -> concat [ "$", n, ".result" ] +-- where txt = '$':n +.+ "text" + +-- -- | Generate patterns and a set of metavariables indicating +-- -- where in the pattern the non-terminal +-- -- >>> generatePatterns 2 [] $ npRule "myfun" (Cat "A") [] Parsable +-- -- (" /* empty */ ",[]) +-- -- >>> generatePatterns 3 [("def", "_SYMB_1")] $ npRule "myfun" (Cat "A") [Right "def", Left (Cat "B")] Parsable +-- -- ("_SYMB_1 p_3_2=b",[("p_3_2",B)]) +-- generatePatterns :: Int -> KeywordEnv -> Rule -> (Pattern,[MetaVar]) +-- generatePatterns ind env r = +-- case rhsRule r of +-- [] -> (" /* empty */ ", []) +-- its -> patternsFor its ("", []) 1 +-- -- let nonTerminals = filter isNonTerminal its +-- -- in ( + +-- -- ) +-- -- ( +-- -- unwords $ mapMaybe (uncurry mkIt) nits, +-- -- [ (var i, cat) | (i, Left cat) <- nits ] +-- -- ) +-- where +-- -- isNonTerminal (Left _) = True +-- -- isNonTerminal _ = False +-- -- nits = zip [1 :: Int ..] its +-- -- var i = "p_" ++ show ind ++"_"++ show i -- TODO: is ind needed for ANTLR? +-- -- mkIt i = \case +-- -- Left c -> Just $ var i ++ "=" ++ catToNT c +-- -- Right s -> lookup s env +-- maybeString Nothing = "" +-- maybeString (Just v) = v +-- encode s = maybeString $ lookup s env +-- patternsFor :: [Either Cat String] -> (Pattern, [MetaVar]) -> Int -> (Pattern, [MetaVar]) +-- patternsFor [] result n = result +-- patternsFor ((Right s):rest) (pattern, vars) n = +-- patternsFor rest (pattern +++ (encode s), vars) n +-- patternsFor ((Left c):rest) (pattern, vars) n = +-- let arg = "p_" ++ show ind ++ "_" ++ show n +-- in patternsFor rest (pattern +++ arg ++ "=" ++ catToNT c, vars ++ [(arg, c)]) (n + 1) + + +-- catToNT :: Cat -> String +-- catToNT = \case +-- TokenCat "Ident" -> "IDENT" +-- TokenCat "Integer" -> "INTEGER" +-- TokenCat "Char" -> "CHAR" +-- TokenCat "Double" -> "DOUBLE" +-- TokenCat "String" -> "STRING" +-- c | isTokenCat c -> identCat c +-- | otherwise -> firstLowerCase $ getRuleName $ identCat c + +-- -- | Puts together the pattern and actions and returns a string containing all +-- -- the rules. +-- prRules :: String -> Rules -> String +-- prRules packabs = concatMap $ \case + +-- -- No rules: skip. +-- PDef _mlhs _nt [] -> "" + +-- -- At least one rule: print! +-- PDef mlhs nt (rhs : rhss) -> unlines $ concat + +-- -- The definition header: lhs and type. +-- [ [ unwords [ fromMaybe nt' mlhs +-- , "returns" , "[" , normcat , "result" , "]" +-- ] +-- ] +-- -- The first rhs. +-- , alternative " :" rhs +-- -- The other rhss. +-- , concatMap (alternative " |") rhss +-- -- The definition footer. +-- , [ " ;" ] +-- ] +-- where +-- alternative sep (p, a, label) = concat +-- [ [ unwords [ sep , p ] ] +-- , [ unwords [ " {" , a , "}" ] ] +-- , [ unwords [ " #" , antlrRuleLabel l ] | Just l <- [label] ] +-- ] +-- catid = identCat nt +-- normcat = identCat (normCat nt) +-- nt' = getRuleName $ firstLowerCase catid +-- antlrRuleLabel :: Fun -> String +-- antlrRuleLabel fnc +-- | isNilFun fnc = catid ++ "_Empty" +-- | isOneFun fnc = catid ++ "_AppendLast" +-- | isConsFun fnc = catid ++ "_PrependFirst" +-- | isCoercion fnc = "Coercion_" ++ catid +-- | otherwise = getLabelName fnc diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs index 7528f938..567875b0 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs @@ -124,7 +124,7 @@ prInstanceVariables rp vars = case rp of -- Generate the class constructor prConstructor :: String -> [DartVar] -> [String] prConstructor className vars = - [ className ++ "(" ++ variablesAssignment ++ ");" ] + [ className ++ "({" ++ variablesAssignment ++ "});" ] where variablesAssignment = concatMap assignment vars - assignment variable = "this." ++ buildVariableName variable ++ ", " + assignment variable = "required this." ++ buildVariableName variable ++ ", " diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 66013e32..72197f5b 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -32,7 +32,7 @@ cf2DartBuilder cf = generateBuilders :: Data -> [String] generateBuilders (cat, rules) = - runtimeTypeMapping ++ concatMap (generateConcreteMapping cat) rules + runtimeTypeMapping ++ concatMap generateConcreteMapping (zip [1..] rules) where funs = map fst rules runtimeTypeMapping @@ -42,7 +42,7 @@ generateBuilders (cat, rules) = generateRuntimeTypeMapping :: Cat -> [(Fun, [Cat])] -> [String] generateRuntimeTypeMapping cat rules = - let className = upperFirst $ cat2DartClassName cat + let className = cat2DartClassName cat in generateFunctionHeader className ++ indent 2 ( @@ -56,36 +56,38 @@ generateRuntimeTypeMapping cat rules = -generateConcreteMapping :: Cat -> (Fun, [Cat]) -> [String] -generateConcreteMapping cat (fun, cats) +generateConcreteMapping :: (Int, (Fun, [Cat])) -> [String] +generateConcreteMapping (index, (fun, cats)) | isNilFun fun || isOneFun fun || isConsFun fun = [] -- these are not represented in the ast | otherwise = -- a standard rule let - className = upperFirst $ cat2DartClassName cat + className = str2DartClassName fun vars = getVars cats in generateFunctionHeader className ++ indent 2 ( [ className ++ "(" ] ++ - (indent 1 $ generateArgumentsMapping vars) ++ + (indent 1 $ generateArgumentsMapping index vars) ++ [ ");" ] ) -generateArgumentsMapping :: [DartVar] -> [String] -generateArgumentsMapping vars = map convertArgument vars +generateArgumentsMapping :: Int -> [DartVar] -> [String] +generateArgumentsMapping index vars = map convertArgument vars where convertArgument var@(vType, _) = let name = buildVariableName var - field = "ctx." ++ name -- TODO + field = "ctx.p_" ++ show index ++ "_" ++ "1" in name ++ ":" +++ buildArgument vType field buildArgument :: DartVarType -> String -> String buildArgument (0, typeName) name = "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," buildArgument (n, typeName) name = - "name.iMap((e" ++ show n ++ ") =>" +++ buildArgument (n - 1, typeName) name ++ ")," + let nextName = "e" ++ show n + argument = buildArgument (n - 1, typeName) nextName + in name ++ ".iMap((" ++ nextName ++ ") =>" +++ argument ++ ")," generateFunctionHeader :: String -> [String] From d72f4ba0d0d86248703e966f9c62eb8a0e5386ee Mon Sep 17 00:00:00 2001 From: xdkomel Date: Thu, 23 Nov 2023 17:53:17 +0300 Subject: [PATCH 26/70] make builder use extensions, match arguments in the g4 --- source/BNFC.cabal | 5 +- source/src/BNFC/Backend/Dart.hs | 1217 +++++++++-------- .../src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs | 225 --- .../Dart/{CFtoDartAbs.hs => CFtoDartAST.hs} | 12 +- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 134 +- source/src/BNFC/Options.hs | 3 +- 6 files changed, 712 insertions(+), 884 deletions(-) delete mode 100644 source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs rename source/src/BNFC/Backend/Dart/{CFtoDartAbs.hs => CFtoDartAST.hs} (93%) diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 2a5f0a57..59366e0a 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -256,11 +256,10 @@ library -- Dart backend BNFC.Backend.Dart - BNFC.Backend.Dart.CFtoDartAbs + BNFC.Backend.Dart.CFtoDartAST BNFC.Backend.Dart.CFtoDartBuilder - BNFC.Backend.Dart.CFtoAntlr4Parser BNFC.Backend.Dart.Common - + -- Antlr4 backend BNFC.Backend.Antlr BNFC.Backend.Antlr.CFtoAntlr4Lexer diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 3829beff..b32d90de 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -18,9 +18,9 @@ import BNFC.Backend.Base import BNFC.Backend.Java.Utils import BNFC.Backend.Java.CFtoCup15 ( cf2Cup ) import BNFC.Backend.Java.CFtoJLex15 -import BNFC.Backend.Java.CFtoAntlr4Lexer -import BNFC.Backend.Dart.CFtoAntlr4Parser -import BNFC.Backend.Dart.CFtoDartAbs ( cf2DartAbs ) +import BNFC.Backend.Antlr.CFtoAntlr4Lexer +import BNFC.Backend.Antlr.CFtoAntlr4Parser +import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Java.CFtoJavaPrinter15 import BNFC.Backend.Java.CFtoVisitSkel15 @@ -42,618 +42,619 @@ makeDart opt = makeDart' pkg opt{ lang = lang' } makeDart' :: String -> SharedOptions -> CF -> MkFiles () makeDart' pkg options@Options{..} cf = do -- Create the package directories if necessary. - let packageBase = maybe id (+.+) inPackage pkg - packageAbsyn = packageBase +.+ "ast" + let + packageBase = maybe id (+.+) inPackage pkg + -- packageAbsyn = packageBase +.+ "ast" dirBase = pkgToDir packageBase - dirAbsyn = pkgToDir packageAbsyn - javaex str = dirBase str <.> "dart" - bnfcfiles = - bnfcVisitorsAndTests - packageBase - packageAbsyn - cf - cf2JavaPrinter - cf2VisitSkel - cf2ComposVisitor - cf2AbstractVisitor - cf2FoldVisitor - cf2AllVisitor - (testclass parselexspec - (head $ results lexmake) -- lexer class - (head $ results parmake) -- parser class - ) - makebnfcfile x = mkfile (javaex (fst $ x bnfcfiles)) comment - (snd $ x bnfcfiles) + -- dirAbsyn = pkgToDir packageAbsyn + -- javaex str = dirBase str <.> "dart" + -- bnfcfiles = + -- bnfcVisitorsAndTests + -- packageBase + -- packageAbsyn + -- cf + -- cf2JavaPrinter + -- cf2VisitSkel + -- cf2ComposVisitor + -- cf2AbstractVisitor + -- cf2FoldVisitor + -- cf2AllVisitor + -- (testclass parselexspec + -- (head $ results lexmake) -- lexer class + -- (head $ results parmake) -- parser class + -- ) + -- makebnfcfile x = mkfile (javaex (fst $ x bnfcfiles)) comment + -- (snd $ x bnfcfiles) let locate str ext = dirBase str <.> ext - -- (lex, env) = cf2AntlrLex "Stella" cf - mkfile (locate "ast" "dart") comment (cf2DartAbs cf rp) + (lex, env) = cf2AntlrLex "Stella" cf + mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) mkfile (locate "builder" "dart") comment (cf2DartBuilder cf) - -- mkfile (locate "StellaLexer" "g4") comment lex - -- mkfile (locate "StellaParser" "g4") comment (cf2AntlrParse lang dirBase cf rp env) - makebnfcfile bprettyprinter - makebnfcfile bskel - makebnfcfile bcompos - makebnfcfile babstract - makebnfcfile bfold - makebnfcfile ball - makebnfcfile btest - let (lex, env) = lexfun packageBase cf - -- Where the lexer file is created. lex is the content! - mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex - liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake - +++ toolversion lexmake ++ ")" - -- where the parser file is created. - mkfile (dirBase inputfile parmake) commentWithEmacsModeHint - $ parsefun packageBase packageAbsyn cf rp env - liftIO $ putStrLn $ - if supportsEntryPoints parmake - then "(Parser created for all categories)" - else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" - liftIO $ putStrLn $ " (Tested with" +++ toolname parmake - +++ toolversion parmake ++ ")" + mkfile (locate (lang ++ "Lexer") "g4") comment lex + mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) + -- makebnfcfile bprettyprinter + -- makebnfcfile bskel + -- makebnfcfile bcompos + -- makebnfcfile babstract + -- makebnfcfile bfold + -- makebnfcfile ball + -- makebnfcfile btest + -- let (lex, env) = lexfun packageBase cf + -- -- Where the lexer file is created. lex is the content! + -- mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex + -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake + -- +++ toolversion lexmake ++ ")" + -- -- where the parser file is created. + -- mkfile (dirBase inputfile parmake) commentWithEmacsModeHint + -- $ parsefun packageBase packageAbsyn cf rp env + -- liftIO $ putStrLn $ + -- if supportsEntryPoints parmake + -- then "(Parser created for all categories)" + -- else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" + -- liftIO $ putStrLn $ " (Tested with" +++ toolname parmake + -- +++ toolversion parmake ++ ")" -- Makefile.mkMakefile optMake $ -- makefile dirBase dirAbsyn ["stella/ast.dart"] parselexspec where - remDups [] = [] - remDups ((a,b):as) = case lookup a as of - Just {} -> remDups as - Nothing -> (a, b) : remDups as +-- remDups [] = [] +-- remDups ((a,b):as) = case lookup a as of +-- Just {} -> remDups as +-- Nothing -> (a, b) : remDups as pkgToDir :: String -> FilePath pkgToDir = replace '.' pathSeparator - parselexspec = parserLexerSelector lang Antlr4 rp - lexfun = cf2lex $ lexer parselexspec - parsefun = cf2parse $ parser parselexspec - parmake = makeparserdetails (parser parselexspec) - lexmake = makelexerdetails (lexer parselexspec) +-- parselexspec = parserLexerSelector lang Antlr4 rp +-- lexfun = cf2lex $ lexer parselexspec +-- parsefun = cf2parse $ parser parselexspec +-- parmake = makeparserdetails (parser parselexspec) +-- lexmake = makelexerdetails (lexer parselexspec) rp = (Options.linenumbers options) - commentWithEmacsModeHint = comment . ("-*- Java -*- " ++) - -makefile :: FilePath -> FilePath -> [String] -> ParserLexerSpecification -> String -> Doc -makefile dirBase dirAbsyn absynFileNames jlexpar basename = vcat $ - makeVars [ ("JAVAC", "javac"), - ("JAVAC_FLAGS", "-sourcepath ."), - ( "JAVA", "java"), - ( "JAVA_FLAGS", ""), - -- parser executable - ( "PARSER", executable parmake), - -- parser flags - ( "PARSER_FLAGS", flags parmake dirBase), - -- lexer executable (and flags?) - ( "LEXER", executable lexmake), - ( "LEXER_FLAGS", flags lexmake dirBase) - ] - ++ - makeRules [ ("all", [ "test" ], []), - ( "test", "absyn" : classes, []), - ( ".PHONY", ["absyn"], []), - ("%.class", [ "%.java" ], [ runJavac "$^" ]), - ("absyn", [absynJavaSrc],[ runJavac "$^" ]) - ]++ - [-- running the lexergen: output of lexer -> input of lexer : calls lexer - let ff = filename lexmake -- name of input file without extension - dirBaseff = dirBase ff -- prepend directory - inp = dirBase inputfile lexmake in - Makefile.mkRule (dirBaseff <.> "java") [ inp ] - [ "${LEXER} ${LEXER_FLAGS} "++ inp ] - - -- running the parsergen, these there are its outputs - -- output of parser -> input of parser : calls parser - , let inp = dirBase inputfile parmake in - Makefile.mkRule (unwords (map (dirBase ) (dotJava $ results parmake))) - [ inp ] $ - ("${PARSER} ${PARSER_FLAGS} " ++ inp) : - ["mv " ++ unwords (dotJava $ results parmake) +++ dirBase ++ [pathSeparator] - | moveresults parmake] - -- Class of the output of lexer generator wants java of : - -- output of lexer and parser generator - , let lexerOutClass = dirBase filename lexmake <.> "class" - outname x = dirBase x <.> "java" - deps = map outname (results lexmake ++ results parmake) in - Makefile.mkRule lexerOutClass deps [] - ]++ - reverse [Makefile.mkRule tar dep [] | - (tar,dep) <- partialParserGoals dirBase (results parmake)] - ++[ Makefile.mkRule (dirBase "PrettyPrinter.class") - [ dirBase "PrettyPrinter.java" ] [] - -- Removes all the class files created anywhere - , Makefile.mkRule "clean" [] [ "rm -f " ++ dirAbsyn "*.class" ++ " " - ++ dirBase "*.class" ] - -- Remains the same - , Makefile.mkRule "distclean" [ "vclean" ] [] - -- removes everything - , Makefile.mkRule "vclean" [] - [ " rm -f " ++ absynJavaSrc ++ " " ++ absynJavaClass - , " rm -f " ++ dirAbsyn "*.class" - , " rmdir " ++ dirAbsyn - , " rm -f " ++ unwords (map (dirBase ) $ - [ inputfile lexmake - , inputfile parmake - ] - ++ dotJava (results lexmake) - ++ [ "VisitSkel.java" - , "ComposVisitor.java" - , "AbstractVisitor.java" - , "FoldVisitor.java" - , "AllVisitor.java" - , "PrettyPrinter.java" - , "Skeleton.java" - , "Test.java" - ] - ++ dotJava (results parmake) - ++ ["*.class"] - ++ other_results lexmake - ++ other_results parmake) - , " rm -f " ++ basename - , " rmdir -p " ++ dirBase - ] - ] - where - makeVars x = [Makefile.mkVar n v | (n,v) <- x] - makeRules x = [Makefile.mkRule tar dep recipe | (tar, dep, recipe) <- x] - parmake = makeparserdetails (parser jlexpar) - lexmake = makelexerdetails (lexer jlexpar) - absynJavaSrc = unwords (dotJava absynFileNames) - absynJavaClass = unwords (dotClass absynFileNames) - classes = map (dirBase ) lst - lst = dotClass (results lexmake) ++ [ "PrettyPrinter.class", "Test.class" - , "VisitSkel.class" - , "ComposVisitor.class", "AbstractVisitor.class" - , "FoldVisitor.class", "AllVisitor.class"]++ - dotClass (results parmake) ++ ["Test.class"] - -type TestClass = String - -- ^ class of the lexer - -> String - -- ^ class of the parser - -> String - -- ^ package where the non-abstract syntax classes are created - -> String - -- ^ package where the abstract syntax classes are created - -> CF - -- ^ the CF bundle - -> String - --- | Record to name arguments of 'javaTest'. -data JavaTestParams = JavaTestParams - { jtpImports :: [Doc] - -- ^ List of imported packages. - , jtpErr :: String - -- ^ Name of the exception thrown in case of parsing failure. - , jtpErrHand :: (String -> [Doc]) - -- ^ Handler for the exception thrown. - , jtpLexerConstruction :: (Doc -> Doc -> Doc) - -- ^ Function formulating the construction of the lexer object. - , jtpParserConstruction :: (Doc -> Doc -> Doc) - -- ^ As above, for parser object. - , jtpShowAlternatives :: ([Cat] -> [Doc]) - -- ^ Pretty-print the names of the methods corresponding to entry points to the user. - , jtpInvocation :: (Doc -> Doc -> Doc -> Doc -> Doc) - -- ^ Function formulating the invocation of the parser tool within Java. - , jtpErrMsg :: String - -- ^ Error string output in consequence of a parsing failure. - } - --- | Test class details for J(F)Lex + CUP -cuptest :: TestClass -cuptest = javaTest $ JavaTestParams - { jtpImports = ["java_cup.runtime"] - , jtpErr = "Throwable" - , jtpErrHand = const [] - , jtpLexerConstruction = \ x i -> x <> i <> ";" - , jtpParserConstruction = \ x i -> x <> "(" <> i <> ", " <> i <> ".getSymbolFactory());" - , jtpShowAlternatives = const $ ["not available."] - , jtpInvocation = \ _ pabs dat enti -> hcat [ pabs, ".", dat, " ast = p.p", enti, "();" ] - , jtpErrMsg = unwords $ - [ "At line \" + String.valueOf(t.l.line_num()) + \"," - , "near \\\"\" + t.l.buff() + \"\\\" :" - ] - } - --- | Test class details for ANTLR4 -antlrtest :: TestClass -antlrtest = javaTest $ JavaTestParams - { jtpImports = - [ "org.antlr.v4.runtime" - , "org.antlr.v4.runtime.atn" - , "org.antlr.v4.runtime.dfa" - , "java.util" - ] - , jtpErr = - "TestError" - , jtpErrHand = - antlrErrorHandling - , jtpLexerConstruction = - \ x i -> vcat - [ x <> "(new ANTLRInputStream" <> i <>");" - , "l.addErrorListener(new BNFCErrorListener());" - ] - , jtpParserConstruction = - \ x i -> vcat - [ x <> "(new CommonTokenStream(" <> i <>"));" - , "p.addErrorListener(new BNFCErrorListener());" - ] - , jtpShowAlternatives = - showOpts - , jtpInvocation = - \ pbase pabs dat enti -> vcat - [ - let rulename = getRuleName $ startSymbol $ render enti - typename = text rulename - methodname = text $ firstLowerCase rulename - in - pbase <> "." <> typename <> "Context pc = p." <> methodname <> "();" - , pabs <> "." <> dat <+> "ast = pc.result;" - ] - , jtpErrMsg = - "At line \" + e.line + \", column \" + e.column + \" :" - } - where - showOpts [] = [] - showOpts (x:xs) - | normCat x /= x = showOpts xs - | otherwise = text (firstLowerCase $ identCat x) : showOpts xs - -parserLexerSelector :: - String - -> JavaLexerParser - -> RecordPositions -- ^Pass line numbers to the symbols - -> ParserLexerSpecification -parserLexerSelector _ JLexCup rp = ParseLexSpec - { lexer = cf2JLex rp - , parser = cf2cup rp - , testclass = cuptest - } -parserLexerSelector _ JFlexCup rp = - (parserLexerSelector "" JLexCup rp){lexer = cf2JFlex rp} -parserLexerSelector l Antlr4 _ = ParseLexSpec - { lexer = cf2AntlrLex' l - , parser = cf2AntlrParse' l - , testclass = antlrtest - } - -data ParserLexerSpecification = ParseLexSpec - { parser :: CFToParser - , lexer :: CFToLexer - , testclass :: TestClass - } - --- |CF -> LEXER GENERATION TOOL BRIDGE --- | function translating the CF to an appropriate lexer generation tool. -type CF2LexerFunction = String -> CF -> (Doc, SymEnv) - --- Chooses the translation from CF to the lexer -data CFToLexer = CF2Lex - { cf2lex :: CF2LexerFunction - , makelexerdetails :: MakeFileDetails - } - --- | Instances of cf-lexergen bridges - -cf2JLex :: RecordPositions -> CFToLexer -cf2JLex rp = CF2Lex - { cf2lex = cf2jlex JLexCup rp - , makelexerdetails = jlexmakedetails - } - -cf2JFlex :: RecordPositions -> CFToLexer -cf2JFlex rp = CF2Lex - { cf2lex = cf2jlex JFlexCup rp - , makelexerdetails = jflexmakedetails - } - -cf2AntlrLex' :: String -> CFToLexer -cf2AntlrLex' l = CF2Lex - { cf2lex = const $ cf2AntlrLex l - , makelexerdetails = antlrmakedetails $ l ++ "Lexer" - } - --- | CF -> PARSER GENERATION TOOL BRIDGE --- | function translating the CF to an appropriate parser generation tool. -type CF2ParserFunction = String -> String -> CF -> RecordPositions -> SymEnv -> String - --- | Chooses the translation from CF to the parser -data CFToParser = CF2Parse - { cf2parse :: CF2ParserFunction - , makeparserdetails :: MakeFileDetails - } - --- | Instances of cf-parsergen bridges -cf2cup :: RecordPositions -> CFToParser -cf2cup rp = CF2Parse - { cf2parse = cf2Cup - , makeparserdetails = cupmakedetails rp - } - -cf2AntlrParse' :: String -> CFToParser -cf2AntlrParse' l = CF2Parse - { cf2parse = const $ cf2AntlrParse l - , makeparserdetails = antlrmakedetails $ l ++ "Parser" - } - - --- | shorthand for Makefile command running javac or java -runJavac , runJava:: String -> String -runJava = mkRunProgram "JAVA" -runJavac = mkRunProgram "JAVAC" - --- | function returning a string executing a program contained in a variable j --- on input s -mkRunProgram :: String -> String -> String -mkRunProgram j s = Makefile.refVar j +++ Makefile.refVar (j +-+ "FLAGS") +++ s - -type OutputDirectory = String - --- | Makefile details from running the parser-lexer generation tools. -data MakeFileDetails = MakeDetails - { -- | The string that executes the generation tool. - executable :: String - , -- | Flags to pass to the tool. - flags :: OutputDirectory -> String - , -- | Input file to the tool. - filename :: String - , -- | Extension of input file to the tool. - fileextension :: String - , -- | Name of the tool. - toolname :: String - , -- | Tool version. - toolversion :: String - , -- | True if the tool is a parser and supports entry points, - -- False otherwise. - supportsEntryPoints :: Bool - , -- | List of names (without extension!) of files resulting from the - -- application of the tool which are relevant to a make rule. - results :: [String] - , -- | List of names of files resulting from the application of - -- the tool which are irrelevant to the make rules but need to be cleaned. - other_results :: [String] - , -- | If True, the files are moved to the base directory, otherwise - -- they are left where they are. - moveresults :: Bool - } - - --- Instances of makefile details. - -jlexmakedetails :: MakeFileDetails -jlexmakedetails = MakeDetails - { executable = runJava "JLex.Main" - , flags = const "" - , filename = "Yylex" - , fileextension = "" - , toolname = "JLex" - , toolversion = "1.2.6" - , supportsEntryPoints = False - , results = ["Yylex"] - , other_results = [] - , moveresults = False - } - -jflexmakedetails :: MakeFileDetails -jflexmakedetails = jlexmakedetails - { executable = "jflex" - , toolname = "JFlex" - , toolversion = "1.4.3 - 1.9.1" - } - -cupmakedetails :: RecordPositions -> MakeFileDetails -cupmakedetails rp = MakeDetails - { executable = runJava "java_cup.Main" - , flags = const (lnFlags ++ " -expect 100") - , filename = "_cup" - , fileextension = "cup" - , toolname = "CUP" - , toolversion = "0.11b" - , supportsEntryPoints = False - , results = ["parser", "sym"] - , other_results = [] - , moveresults = True - } - where - lnFlags = if rp == RecordPositions then "-locations" else "-nopositions" - - -antlrmakedetails :: String -> MakeFileDetails -antlrmakedetails l = MakeDetails - { executable = runJava "org.antlr.v4.Tool" - , flags = \ path -> unwords $ - let pointed = map cnv path - cnv y = if isPathSeparator y - then '.' - else y - in [ "-lib", path - , "-package", pointed] - , filename = l - , fileextension = "g4" - , toolname = "ANTLRv4" - , toolversion = "4.9" - , supportsEntryPoints = True - , results = [l] - , other_results = map (l ++) - [ ".interp" -- added after ANTLR 4.5 - , ".tokens" - , "BaseListener.java" - ,"Listener.java" - ] - , moveresults = False - } - -dotJava :: [String] -> [String] -dotJava = map (<.> "java") - -dotClass :: [String] -> [String] -dotClass = map (<.> "class") - -type CFToJava = String -> String -> CF -> String - --- | Contains the pairs filename/content for all the non-abstract syntax files --- generated by BNFC. -data BNFCGeneratedEntities = BNFCGenerated - { bprettyprinter :: (String, String) - , btest :: (String, String) - , bcompos :: (String, String) - , babstract :: (String, String) - , bfold :: (String, String) - , ball :: (String, String) - , bskel :: (String, String) - } - -bnfcVisitorsAndTests :: String -> String -> CF -> - CFToJava -> CFToJava -> CFToJava -> - CFToJava -> CFToJava -> CFToJava -> - CFToJava -> BNFCGeneratedEntities -bnfcVisitorsAndTests pbase pabsyn cf cf0 cf1 cf2 cf3 cf4 cf5 cf6 = - BNFCGenerated - { bprettyprinter = ( "PrettyPrinter" , app cf0) - , bskel = ( "VisitSkel", app cf1) - , bcompos = ( "ComposVisitor" , app cf2) - , babstract = ( "AbstractVisitor" , app cf3) - , bfold = ( "FoldVisitor", app cf4) - , ball = ( "AllVisitor", app cf5) - , btest = ( "Test" , app cf6) - } - where app x = x pbase pabsyn cf - -inputfile :: MakeFileDetails -> String -inputfile x - | null (fileextension x) = filename x - | otherwise = filename x <.> fileextension x - --- | constructs the rules regarding the parser in the makefile -partialParserGoals :: String -> [String] -> [(String, [String])] -partialParserGoals _ [] = [] -partialParserGoals dirBase (x:rest) = - (dirBase x <.> "class", map (\ y -> dirBase y <.> "java") (x:rest)) - : partialParserGoals dirBase rest - --- | Creates the Test.java class. -javaTest :: JavaTestParams -> TestClass -javaTest (JavaTestParams - imports - err - errhand - lexerconstruction - parserconstruction - showOpts - invocation - errmsg) - lexer - parser - packageBase - packageAbsyn - cf = - render $ vcat $ concat $ - [ [ "package" <+> text packageBase <> ";" - , "" - , "import" <+> text packageBase <> ".*;" - , "import java.io.*;" - ] - , map importfun imports - , [ "" ] - , errhand err - , [ "" - , "public class Test" - , codeblock 2 - [ lx <+> "l;" - , px <+> "p;" - , "" - , "public Test(String[] args)" - , codeblock 2 - [ "try" - , codeblock 2 - [ "Reader input;" - , "if (args.length == 0) input = new InputStreamReader(System.in);" - , "else input = new FileReader(args[0]);" - , "l = new " <> lexerconstruction lx "(input)" - ] - , "catch(IOException e)" - , codeblock 2 - [ "System.err.println(\"Error: File not found: \" + args[0]);" - , "System.exit(1);" - ] - , "p = new "<> parserconstruction px "l" - ] - , "" - , "public" <+> text packageAbsyn <> "." <> dat - <+> "parse() throws Exception" - , codeblock 2 $ concat - [ [ "/* The default parser is the first-defined entry point. */" ] - , unlessNull (drop 1 eps) $ \ eps' -> - [ "/* Other options are: */" - , "/* " <> fsep (punctuate "," (showOpts eps')) <> " */" - ] - , [ invocation px (text packageAbsyn) dat absentity - , printOuts - [ "\"Parse Successful!\"" - , "\"[Abstract Syntax]\"" - , "PrettyPrinter.show(ast)" - , "\"[Linearized Tree]\"" - , "PrettyPrinter.print(ast)" - ] - , "return ast;" - ] - ] - , "" - , "public static void main(String args[]) throws Exception" - , codeblock 2 - [ "Test t = new Test(args);" - , "try" - , codeblock 2 [ "t.parse();" ] - ,"catch(" <> text err <+> "e)" - , codeblock 2 - [ "System.err.println(\"" <> text errmsg <> "\");" - , "System.err.println(\" \" + e.getMessage());" - , "System.exit(1);" - ] - ] - ] - ] - ] - where - printOuts x = vcat $ map javaPrintOut (messages x) - messages x = "" : intersperse "" x - javaPrintOut x = text $ "System.out.println(" ++ x ++ ");" - importfun x = "import" <+> x <> ".*;" - lx = text lexer - px = text parser - dat = text $ identCat $ normCat def -- Use for AST types. - absentity = text $ identCat def -- Use for parser/printer name. - eps = toList $ allEntryPoints cf - def = head eps - --- | Error handling in ANTLR. --- By default, ANTLR does not stop after any parsing error and attempts to go --- on, delivering what it has been able to parse. --- It does not throw any exception, unlike J(F)lex+CUP. --- The below code makes the test class behave as with J(F)lex+CUP. -antlrErrorHandling :: String -> [Doc] -antlrErrorHandling te = - [ "class"<+>tedoc<+>"extends RuntimeException" - , codeblock 2 [ "int line;" - , "int column;" - , "public"<+>tedoc<>"(String msg, int l, int c)" - , codeblock 2 [ "super(msg);" - , "line = l;" - , "column = c;" - ] - ] - , "class BNFCErrorListener implements ANTLRErrorListener" - , codeblock 2 [ "@Override" - , "public void syntaxError(Recognizer recognizer, Object o, int i" - <> ", int i1, String s, RecognitionException e)" - , codeblock 2 [ "throw new"<+>tedoc<>"(s,i,i1);"] - , "@Override" - , "public void reportAmbiguity(Parser parser, DFA dfa, int i, int i1, " - <>"boolean b, BitSet bitSet, ATNConfigSet atnConfigSet)" - , codeblock 2[ "throw new"<+>tedoc<>"(\"Ambiguity at\",i,i1);" ] - , "@Override" - , "public void reportAttemptingFullContext(Parser parser, DFA dfa, " - <>"int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet)" - , codeblock 2 [] - , "@Override" - ,"public void reportContextSensitivity(Parser parser, DFA dfa, int i, " - <>"int i1, int i2, ATNConfigSet atnConfigSet)" - ,codeblock 2 [] - ] - ] - where tedoc = text te +-- commentWithEmacsModeHint = comment . ("-*- Java -*- " ++) + +-- makefile :: FilePath -> FilePath -> [String] -> ParserLexerSpecification -> String -> Doc +-- makefile dirBase dirAbsyn absynFileNames jlexpar basename = vcat $ +-- makeVars [ ("JAVAC", "javac"), +-- ("JAVAC_FLAGS", "-sourcepath ."), +-- ( "JAVA", "java"), +-- ( "JAVA_FLAGS", ""), +-- -- parser executable +-- ( "PARSER", executable parmake), +-- -- parser flags +-- ( "PARSER_FLAGS", flags parmake dirBase), +-- -- lexer executable (and flags?) +-- ( "LEXER", executable lexmake), +-- ( "LEXER_FLAGS", flags lexmake dirBase) +-- ] +-- ++ +-- makeRules [ ("all", [ "test" ], []), +-- ( "test", "absyn" : classes, []), +-- ( ".PHONY", ["absyn"], []), +-- ("%.class", [ "%.java" ], [ runJavac "$^" ]), +-- ("absyn", [absynJavaSrc],[ runJavac "$^" ]) +-- ]++ +-- [-- running the lexergen: output of lexer -> input of lexer : calls lexer +-- let ff = filename lexmake -- name of input file without extension +-- dirBaseff = dirBase ff -- prepend directory +-- inp = dirBase inputfile lexmake in +-- Makefile.mkRule (dirBaseff <.> "java") [ inp ] +-- [ "${LEXER} ${LEXER_FLAGS} "++ inp ] + +-- -- running the parsergen, these there are its outputs +-- -- output of parser -> input of parser : calls parser +-- , let inp = dirBase inputfile parmake in +-- Makefile.mkRule (unwords (map (dirBase ) (dotJava $ results parmake))) +-- [ inp ] $ +-- ("${PARSER} ${PARSER_FLAGS} " ++ inp) : +-- ["mv " ++ unwords (dotJava $ results parmake) +++ dirBase ++ [pathSeparator] +-- | moveresults parmake] +-- -- Class of the output of lexer generator wants java of : +-- -- output of lexer and parser generator +-- , let lexerOutClass = dirBase filename lexmake <.> "class" +-- outname x = dirBase x <.> "java" +-- deps = map outname (results lexmake ++ results parmake) in +-- Makefile.mkRule lexerOutClass deps [] +-- ]++ +-- reverse [Makefile.mkRule tar dep [] | +-- (tar,dep) <- partialParserGoals dirBase (results parmake)] +-- ++[ Makefile.mkRule (dirBase "PrettyPrinter.class") +-- [ dirBase "PrettyPrinter.java" ] [] +-- -- Removes all the class files created anywhere +-- , Makefile.mkRule "clean" [] [ "rm -f " ++ dirAbsyn "*.class" ++ " " +-- ++ dirBase "*.class" ] +-- -- Remains the same +-- , Makefile.mkRule "distclean" [ "vclean" ] [] +-- -- removes everything +-- , Makefile.mkRule "vclean" [] +-- [ " rm -f " ++ absynJavaSrc ++ " " ++ absynJavaClass +-- , " rm -f " ++ dirAbsyn "*.class" +-- , " rmdir " ++ dirAbsyn +-- , " rm -f " ++ unwords (map (dirBase ) $ +-- [ inputfile lexmake +-- , inputfile parmake +-- ] +-- ++ dotJava (results lexmake) +-- ++ [ "VisitSkel.java" +-- , "ComposVisitor.java" +-- , "AbstractVisitor.java" +-- , "FoldVisitor.java" +-- , "AllVisitor.java" +-- , "PrettyPrinter.java" +-- , "Skeleton.java" +-- , "Test.java" +-- ] +-- ++ dotJava (results parmake) +-- ++ ["*.class"] +-- ++ other_results lexmake +-- ++ other_results parmake) +-- , " rm -f " ++ basename +-- , " rmdir -p " ++ dirBase +-- ] +-- ] +-- where +-- makeVars x = [Makefile.mkVar n v | (n,v) <- x] +-- makeRules x = [Makefile.mkRule tar dep recipe | (tar, dep, recipe) <- x] +-- parmake = makeparserdetails (parser jlexpar) +-- lexmake = makelexerdetails (lexer jlexpar) +-- absynJavaSrc = unwords (dotJava absynFileNames) +-- absynJavaClass = unwords (dotClass absynFileNames) +-- classes = map (dirBase ) lst +-- lst = dotClass (results lexmake) ++ [ "PrettyPrinter.class", "Test.class" +-- , "VisitSkel.class" +-- , "ComposVisitor.class", "AbstractVisitor.class" +-- , "FoldVisitor.class", "AllVisitor.class"]++ +-- dotClass (results parmake) ++ ["Test.class"] + +-- type TestClass = String +-- -- ^ class of the lexer +-- -> String +-- -- ^ class of the parser +-- -> String +-- -- ^ package where the non-abstract syntax classes are created +-- -> String +-- -- ^ package where the abstract syntax classes are created +-- -> CF +-- -- ^ the CF bundle +-- -> String + +-- -- | Record to name arguments of 'javaTest'. +-- data JavaTestParams = JavaTestParams +-- { jtpImports :: [Doc] +-- -- ^ List of imported packages. +-- , jtpErr :: String +-- -- ^ Name of the exception thrown in case of parsing failure. +-- , jtpErrHand :: (String -> [Doc]) +-- -- ^ Handler for the exception thrown. +-- , jtpLexerConstruction :: (Doc -> Doc -> Doc) +-- -- ^ Function formulating the construction of the lexer object. +-- , jtpParserConstruction :: (Doc -> Doc -> Doc) +-- -- ^ As above, for parser object. +-- , jtpShowAlternatives :: ([Cat] -> [Doc]) +-- -- ^ Pretty-print the names of the methods corresponding to entry points to the user. +-- , jtpInvocation :: (Doc -> Doc -> Doc -> Doc -> Doc) +-- -- ^ Function formulating the invocation of the parser tool within Java. +-- , jtpErrMsg :: String +-- -- ^ Error string output in consequence of a parsing failure. +-- } + +-- -- | Test class details for J(F)Lex + CUP +-- cuptest :: TestClass +-- cuptest = javaTest $ JavaTestParams +-- { jtpImports = ["java_cup.runtime"] +-- , jtpErr = "Throwable" +-- , jtpErrHand = const [] +-- , jtpLexerConstruction = \ x i -> x <> i <> ";" +-- , jtpParserConstruction = \ x i -> x <> "(" <> i <> ", " <> i <> ".getSymbolFactory());" +-- , jtpShowAlternatives = const $ ["not available."] +-- , jtpInvocation = \ _ pabs dat enti -> hcat [ pabs, ".", dat, " ast = p.p", enti, "();" ] +-- , jtpErrMsg = unwords $ +-- [ "At line \" + String.valueOf(t.l.line_num()) + \"," +-- , "near \\\"\" + t.l.buff() + \"\\\" :" +-- ] +-- } + +-- -- | Test class details for ANTLR4 +-- antlrtest :: TestClass +-- antlrtest = javaTest $ JavaTestParams +-- { jtpImports = +-- [ "org.antlr.v4.runtime" +-- , "org.antlr.v4.runtime.atn" +-- , "org.antlr.v4.runtime.dfa" +-- , "java.util" +-- ] +-- , jtpErr = +-- "TestError" +-- , jtpErrHand = +-- antlrErrorHandling +-- , jtpLexerConstruction = +-- \ x i -> vcat +-- [ x <> "(new ANTLRInputStream" <> i <>");" +-- , "l.addErrorListener(new BNFCErrorListener());" +-- ] +-- , jtpParserConstruction = +-- \ x i -> vcat +-- [ x <> "(new CommonTokenStream(" <> i <>"));" +-- , "p.addErrorListener(new BNFCErrorListener());" +-- ] +-- , jtpShowAlternatives = +-- showOpts +-- , jtpInvocation = +-- \ pbase pabs dat enti -> vcat +-- [ +-- let rulename = getRuleName $ startSymbol $ render enti +-- typename = text rulename +-- methodname = text $ firstLowerCase rulename +-- in +-- pbase <> "." <> typename <> "Context pc = p." <> methodname <> "();" +-- , pabs <> "." <> dat <+> "ast = pc.result;" +-- ] +-- , jtpErrMsg = +-- "At line \" + e.line + \", column \" + e.column + \" :" +-- } +-- where +-- showOpts [] = [] +-- showOpts (x:xs) +-- | normCat x /= x = showOpts xs +-- | otherwise = text (firstLowerCase $ identCat x) : showOpts xs + +-- parserLexerSelector :: +-- String +-- -> JavaLexerParser +-- -> RecordPositions -- ^Pass line numbers to the symbols +-- -> ParserLexerSpecification +-- parserLexerSelector _ JLexCup rp = ParseLexSpec +-- { lexer = cf2JLex rp +-- , parser = cf2cup rp +-- , testclass = cuptest +-- } +-- parserLexerSelector _ JFlexCup rp = +-- (parserLexerSelector "" JLexCup rp){lexer = cf2JFlex rp} +-- parserLexerSelector l Antlr4 _ = ParseLexSpec +-- { lexer = cf2AntlrLex' l +-- , parser = cf2AntlrParse' l +-- , testclass = antlrtest +-- } + +-- data ParserLexerSpecification = ParseLexSpec +-- { parser :: CFToParser +-- , lexer :: CFToLexer +-- , testclass :: TestClass +-- } + +-- -- |CF -> LEXER GENERATION TOOL BRIDGE +-- -- | function translating the CF to an appropriate lexer generation tool. +-- type CF2LexerFunction = String -> CF -> (Doc, SymEnv) + +-- -- Chooses the translation from CF to the lexer +-- data CFToLexer = CF2Lex +-- { cf2lex :: CF2LexerFunction +-- , makelexerdetails :: MakeFileDetails +-- } + +-- -- | Instances of cf-lexergen bridges + +-- cf2JLex :: RecordPositions -> CFToLexer +-- cf2JLex rp = CF2Lex +-- { cf2lex = cf2jlex JLexCup rp +-- , makelexerdetails = jlexmakedetails +-- } + +-- cf2JFlex :: RecordPositions -> CFToLexer +-- cf2JFlex rp = CF2Lex +-- { cf2lex = cf2jlex JFlexCup rp +-- , makelexerdetails = jflexmakedetails +-- } + +-- cf2AntlrLex' :: String -> CFToLexer +-- cf2AntlrLex' l = CF2Lex +-- { cf2lex = const $ cf2AntlrLex l +-- , makelexerdetails = antlrmakedetails $ l ++ "Lexer" +-- } + +-- -- | CF -> PARSER GENERATION TOOL BRIDGE +-- -- | function translating the CF to an appropriate parser generation tool. +-- type CF2ParserFunction = String -> String -> CF -> RecordPositions -> SymEnv -> String + +-- -- | Chooses the translation from CF to the parser +-- data CFToParser = CF2Parse +-- { cf2parse :: CF2ParserFunction +-- , makeparserdetails :: MakeFileDetails +-- } + +-- -- | Instances of cf-parsergen bridges +-- cf2cup :: RecordPositions -> CFToParser +-- cf2cup rp = CF2Parse +-- { cf2parse = cf2Cup +-- , makeparserdetails = cupmakedetails rp +-- } + +-- cf2AntlrParse' :: String -> CFToParser +-- cf2AntlrParse' l = CF2Parse +-- { cf2parse = const $ cf2AntlrParse l +-- , makeparserdetails = antlrmakedetails $ l ++ "Parser" +-- } + + +-- -- | shorthand for Makefile command running javac or java +-- runJavac , runJava:: String -> String +-- runJava = mkRunProgram "JAVA" +-- runJavac = mkRunProgram "JAVAC" + +-- -- | function returning a string executing a program contained in a variable j +-- -- on input s +-- mkRunProgram :: String -> String -> String +-- mkRunProgram j s = Makefile.refVar j +++ Makefile.refVar (j +-+ "FLAGS") +++ s + +-- type OutputDirectory = String + +-- -- | Makefile details from running the parser-lexer generation tools. +-- data MakeFileDetails = MakeDetails +-- { -- | The string that executes the generation tool. +-- executable :: String +-- , -- | Flags to pass to the tool. +-- flags :: OutputDirectory -> String +-- , -- | Input file to the tool. +-- filename :: String +-- , -- | Extension of input file to the tool. +-- fileextension :: String +-- , -- | Name of the tool. +-- toolname :: String +-- , -- | Tool version. +-- toolversion :: String +-- , -- | True if the tool is a parser and supports entry points, +-- -- False otherwise. +-- supportsEntryPoints :: Bool +-- , -- | List of names (without extension!) of files resulting from the +-- -- application of the tool which are relevant to a make rule. +-- results :: [String] +-- , -- | List of names of files resulting from the application of +-- -- the tool which are irrelevant to the make rules but need to be cleaned. +-- other_results :: [String] +-- , -- | If True, the files are moved to the base directory, otherwise +-- -- they are left where they are. +-- moveresults :: Bool +-- } + + +-- -- Instances of makefile details. + +-- jlexmakedetails :: MakeFileDetails +-- jlexmakedetails = MakeDetails +-- { executable = runJava "JLex.Main" +-- , flags = const "" +-- , filename = "Yylex" +-- , fileextension = "" +-- , toolname = "JLex" +-- , toolversion = "1.2.6" +-- , supportsEntryPoints = False +-- , results = ["Yylex"] +-- , other_results = [] +-- , moveresults = False +-- } + +-- jflexmakedetails :: MakeFileDetails +-- jflexmakedetails = jlexmakedetails +-- { executable = "jflex" +-- , toolname = "JFlex" +-- , toolversion = "1.4.3 - 1.9.1" +-- } + +-- cupmakedetails :: RecordPositions -> MakeFileDetails +-- cupmakedetails rp = MakeDetails +-- { executable = runJava "java_cup.Main" +-- , flags = const (lnFlags ++ " -expect 100") +-- , filename = "_cup" +-- , fileextension = "cup" +-- , toolname = "CUP" +-- , toolversion = "0.11b" +-- , supportsEntryPoints = False +-- , results = ["parser", "sym"] +-- , other_results = [] +-- , moveresults = True +-- } +-- where +-- lnFlags = if rp == RecordPositions then "-locations" else "-nopositions" + + +-- antlrmakedetails :: String -> MakeFileDetails +-- antlrmakedetails l = MakeDetails +-- { executable = runJava "org.antlr.v4.Tool" +-- , flags = \ path -> unwords $ +-- let pointed = map cnv path +-- cnv y = if isPathSeparator y +-- then '.' +-- else y +-- in [ "-lib", path +-- , "-package", pointed] +-- , filename = l +-- , fileextension = "g4" +-- , toolname = "ANTLRv4" +-- , toolversion = "4.9" +-- , supportsEntryPoints = True +-- , results = [l] +-- , other_results = map (l ++) +-- [ ".interp" -- added after ANTLR 4.5 +-- , ".tokens" +-- , "BaseListener.java" +-- ,"Listener.java" +-- ] +-- , moveresults = False +-- } + +-- dotJava :: [String] -> [String] +-- dotJava = map (<.> "java") + +-- dotClass :: [String] -> [String] +-- dotClass = map (<.> "class") + +-- type CFToJava = String -> String -> CF -> String + +-- -- | Contains the pairs filename/content for all the non-abstract syntax files +-- -- generated by BNFC. +-- data BNFCGeneratedEntities = BNFCGenerated +-- { bprettyprinter :: (String, String) +-- , btest :: (String, String) +-- , bcompos :: (String, String) +-- , babstract :: (String, String) +-- , bfold :: (String, String) +-- , ball :: (String, String) +-- , bskel :: (String, String) +-- } + +-- bnfcVisitorsAndTests :: String -> String -> CF -> +-- CFToJava -> CFToJava -> CFToJava -> +-- CFToJava -> CFToJava -> CFToJava -> +-- CFToJava -> BNFCGeneratedEntities +-- bnfcVisitorsAndTests pbase pabsyn cf cf0 cf1 cf2 cf3 cf4 cf5 cf6 = +-- BNFCGenerated +-- { bprettyprinter = ( "PrettyPrinter" , app cf0) +-- , bskel = ( "VisitSkel", app cf1) +-- , bcompos = ( "ComposVisitor" , app cf2) +-- , babstract = ( "AbstractVisitor" , app cf3) +-- , bfold = ( "FoldVisitor", app cf4) +-- , ball = ( "AllVisitor", app cf5) +-- , btest = ( "Test" , app cf6) +-- } +-- where app x = x pbase pabsyn cf + +-- inputfile :: MakeFileDetails -> String +-- inputfile x +-- | null (fileextension x) = filename x +-- | otherwise = filename x <.> fileextension x + +-- -- | constructs the rules regarding the parser in the makefile +-- partialParserGoals :: String -> [String] -> [(String, [String])] +-- partialParserGoals _ [] = [] +-- partialParserGoals dirBase (x:rest) = +-- (dirBase x <.> "class", map (\ y -> dirBase y <.> "java") (x:rest)) +-- : partialParserGoals dirBase rest + +-- -- | Creates the Test.java class. +-- javaTest :: JavaTestParams -> TestClass +-- javaTest (JavaTestParams +-- imports +-- err +-- errhand +-- lexerconstruction +-- parserconstruction +-- showOpts +-- invocation +-- errmsg) +-- lexer +-- parser +-- packageBase +-- packageAbsyn +-- cf = +-- render $ vcat $ concat $ +-- [ [ "package" <+> text packageBase <> ";" +-- , "" +-- , "import" <+> text packageBase <> ".*;" +-- , "import java.io.*;" +-- ] +-- , map importfun imports +-- , [ "" ] +-- , errhand err +-- , [ "" +-- , "public class Test" +-- , codeblock 2 +-- [ lx <+> "l;" +-- , px <+> "p;" +-- , "" +-- , "public Test(String[] args)" +-- , codeblock 2 +-- [ "try" +-- , codeblock 2 +-- [ "Reader input;" +-- , "if (args.length == 0) input = new InputStreamReader(System.in);" +-- , "else input = new FileReader(args[0]);" +-- , "l = new " <> lexerconstruction lx "(input)" +-- ] +-- , "catch(IOException e)" +-- , codeblock 2 +-- [ "System.err.println(\"Error: File not found: \" + args[0]);" +-- , "System.exit(1);" +-- ] +-- , "p = new "<> parserconstruction px "l" +-- ] +-- , "" +-- , "public" <+> text packageAbsyn <> "." <> dat +-- <+> "parse() throws Exception" +-- , codeblock 2 $ concat +-- [ [ "/* The default parser is the first-defined entry point. */" ] +-- , unlessNull (drop 1 eps) $ \ eps' -> +-- [ "/* Other options are: */" +-- , "/* " <> fsep (punctuate "," (showOpts eps')) <> " */" +-- ] +-- , [ invocation px (text packageAbsyn) dat absentity +-- , printOuts +-- [ "\"Parse Successful!\"" +-- , "\"[Abstract Syntax]\"" +-- , "PrettyPrinter.show(ast)" +-- , "\"[Linearized Tree]\"" +-- , "PrettyPrinter.print(ast)" +-- ] +-- , "return ast;" +-- ] +-- ] +-- , "" +-- , "public static void main(String args[]) throws Exception" +-- , codeblock 2 +-- [ "Test t = new Test(args);" +-- , "try" +-- , codeblock 2 [ "t.parse();" ] +-- ,"catch(" <> text err <+> "e)" +-- , codeblock 2 +-- [ "System.err.println(\"" <> text errmsg <> "\");" +-- , "System.err.println(\" \" + e.getMessage());" +-- , "System.exit(1);" +-- ] +-- ] +-- ] +-- ] +-- ] +-- where +-- printOuts x = vcat $ map javaPrintOut (messages x) +-- messages x = "" : intersperse "" x +-- javaPrintOut x = text $ "System.out.println(" ++ x ++ ");" +-- importfun x = "import" <+> x <> ".*;" +-- lx = text lexer +-- px = text parser +-- dat = text $ identCat $ normCat def -- Use for AST types. +-- absentity = text $ identCat def -- Use for parser/printer name. +-- eps = toList $ allEntryPoints cf +-- def = head eps + +-- -- | Error handling in ANTLR. +-- -- By default, ANTLR does not stop after any parsing error and attempts to go +-- -- on, delivering what it has been able to parse. +-- -- It does not throw any exception, unlike J(F)lex+CUP. +-- -- The below code makes the test class behave as with J(F)lex+CUP. +-- antlrErrorHandling :: String -> [Doc] +-- antlrErrorHandling te = +-- [ "class"<+>tedoc<+>"extends RuntimeException" +-- , codeblock 2 [ "int line;" +-- , "int column;" +-- , "public"<+>tedoc<>"(String msg, int l, int c)" +-- , codeblock 2 [ "super(msg);" +-- , "line = l;" +-- , "column = c;" +-- ] +-- ] +-- , "class BNFCErrorListener implements ANTLRErrorListener" +-- , codeblock 2 [ "@Override" +-- , "public void syntaxError(Recognizer recognizer, Object o, int i" +-- <> ", int i1, String s, RecognitionException e)" +-- , codeblock 2 [ "throw new"<+>tedoc<>"(s,i,i1);"] +-- , "@Override" +-- , "public void reportAmbiguity(Parser parser, DFA dfa, int i, int i1, " +-- <>"boolean b, BitSet bitSet, ATNConfigSet atnConfigSet)" +-- , codeblock 2[ "throw new"<+>tedoc<>"(\"Ambiguity at\",i,i1);" ] +-- , "@Override" +-- , "public void reportAttemptingFullContext(Parser parser, DFA dfa, " +-- <>"int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet)" +-- , codeblock 2 [] +-- , "@Override" +-- ,"public void reportContextSensitivity(Parser parser, DFA dfa, int i, " +-- <>"int i1, int i2, ATNConfigSet atnConfigSet)" +-- ,codeblock 2 [] +-- ] +-- ] +-- where tedoc = text te diff --git a/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs deleted file mode 100644 index 783733b1..00000000 --- a/source/src/BNFC/Backend/Dart/CFtoAntlr4Parser.hs +++ /dev/null @@ -1,225 +0,0 @@ --- {-# LANGUAGE LambdaCase #-} - --- module BNFC.Backend.Dart.CFtoAntlr4Parser ( cf2AntlrParse ) where - --- import Data.Foldable ( toList ) --- import Data.List ( intercalate ) --- import Data.Maybe - --- import BNFC.CF --- import BNFC.Options ( RecordPositions(..) ) --- import BNFC.Utils ( (+++), (+.+), applyWhen ) - --- import BNFC.Backend.Java.Utils --- import BNFC.Backend.Common.NamedVariables - --- -- Type declarations - --- -- | A definition of a non-terminal by all its rhss, --- -- together with parse actions. --- data PDef = PDef --- { _pdNT :: Maybe String --- -- ^ If given, the name of the lhss. Usually computed from 'pdCat'. --- , _pdCat :: Cat --- -- ^ The category to parse. --- , _pdAlts :: [(Pattern, Action, Maybe Fun)] --- -- ^ The possible rhss with actions. If 'null', skip this 'PDef'. --- -- Where 'Nothing', skip ANTLR rule label. --- } --- type Rules = [PDef] --- type Pattern = String --- type Action = String --- type MetaVar = (String, Cat) - --- -- | Creates the ANTLR parser grammar for this CF. --- --The environment comes from CFtoAntlr4Lexer --- cf2AntlrParse :: String -> String -> CF -> RecordPositions -> KeywordEnv -> String --- cf2AntlrParse lang packageAbsyn cf _ env = unlines $ concat --- [ [ header --- , tokens --- , "" --- -- Generate start rules [#272] --- -- _X returns [ dX result ] : x=X EOF { $result = $x.result; } --- -- , prRules packageAbsyn $ map entrypoint $ toList $ allEntryPoints cf --- -- Generate regular rules --- , prRules packageAbsyn $ rulesForAntlr4 packageAbsyn cf env --- ] --- ] --- where --- header :: String --- header = unlines --- [ "// Parser definition for use with ANTLRv4" --- , "parser grammar" +++ lang ++ "Parser;" --- ] --- tokens :: String --- tokens = unlines --- [ "options {" --- , " tokenVocab = " ++ lang ++ "Lexer;" --- , "}" --- ] - --- -- | Generate start rule to help ANTLR. --- -- --- -- @start_X returns [ X result ] : x=X EOF { $result = $x.result; } # Start_X@ --- -- --- entrypoint :: Cat -> PDef --- entrypoint cat = --- PDef (Just nt) cat [(pat, act, fun)] --- where --- nt = firstLowerCase $ startSymbol $ identCat cat --- pat = "x=" ++ catToNT cat +++ "EOF" --- act = "$result = $x.result;" --- fun = Nothing -- No ANTLR Rule label, ("Start_" ++ identCat cat) conflicts with lhs. - --- --The following functions are a (relatively) straightforward translation --- --of the ones in CFtoHappy.hs --- rulesForAntlr4 :: String -> CF -> KeywordEnv -> Rules --- rulesForAntlr4 packageAbsyn cf env = map mkOne getrules --- where --- getrules = ruleGroups cf --- mkOne (cat,rules) = constructRule packageAbsyn cf env rules cat - --- -- | For every non-terminal, we construct a set of rules. A rule is a sequence of --- -- terminals and non-terminals, and an action to be performed. --- constructRule :: String -> CF -> KeywordEnv -> [Rule] -> NonTerminal -> PDef --- constructRule packageAbsyn cf env rules nt = --- PDef Nothing nt $ --- [ ( p --- , generateAction packageAbsyn nt (funRule r) m b --- , Nothing -- labels not needed for BNFC-generated AST parser --- -- , Just label --- -- -- Did not work: --- -- -- , if firstLowerCase (getLabelName label) --- -- -- == getRuleName (firstLowerCase $ identCat nt) then Nothing else Just label --- ) --- | (index, r0) <- zip [1..] rules --- , let b = isConsFun (funRule r0) && elem (valCat r0) (cfgReversibleCats cf) --- , let r = applyWhen b revSepListRule r0 --- , let (p,m0) = generatePatterns index env r --- , let m = applyWhen b reverse m0 --- -- , let label = funRule r --- ] - --- -- Generates a string containing the semantic action. --- generateAction :: IsFun f => String -> NonTerminal -> f -> [MetaVar] --- -> Bool -- ^ Whether the list should be reversed or not. --- -- Only used if this is a list rule. --- -> Action --- generateAction packageAbsyn nt f ms rev --- | isNilFun f = "$result = " ++ c ++ "();" --- | isOneFun f = "$result = " ++ c ++ "(); $result.addLast(" --- ++ p_1 ++ ");" --- | isConsFun f = "$result = " ++ p_2 ++ "; " --- ++ "$result." ++ add ++ "(" ++ p_1 ++ ");" --- | isCoercion f = "$result = " ++ p_1 ++ ";" --- | isDefinedRule f = "$result = " ++ packageAbsyn ++ "Def." ++ sanitize (funName f) --- ++ "(" ++ intercalate "," (map resultvalue ms) ++ ");" --- | otherwise = "$result = " ++ c --- ++ "(" ++ intercalate "," (map resultvalue ms) ++ ");" --- where --- sanitize = getRuleName --- c = if isNilFun f || isOneFun f || isConsFun f --- then identCat (normCat nt) else funName f --- p_1 = resultvalue $ ms!!0 --- p_2 = resultvalue $ ms!!1 --- add = if rev then "addLast" else "addFirst" --- removeQuotes x = x +.+ "substring(1, " ++ x +.+ "length()-1)" --- unescape x = removeQuotes x +.+ "translateEscapes()" -- Java 15 and higher --- resultvalue (n,c) = case c of --- TokenCat "Double" -> concat [ "double.parse(", txt, ")" ] --- TokenCat "Integer" -> concat [ "int.parse(" , txt, ")" ] --- TokenCat "Char" -> unescape txt +.+ "charAt(0)" --- TokenCat "String" -> unescape txt --- TokenCat "Ident" -> txt --- c | isTokenCat c -> txt --- | otherwise -> concat [ "$", n, ".result" ] --- where txt = '$':n +.+ "text" - --- -- | Generate patterns and a set of metavariables indicating --- -- where in the pattern the non-terminal --- -- >>> generatePatterns 2 [] $ npRule "myfun" (Cat "A") [] Parsable --- -- (" /* empty */ ",[]) --- -- >>> generatePatterns 3 [("def", "_SYMB_1")] $ npRule "myfun" (Cat "A") [Right "def", Left (Cat "B")] Parsable --- -- ("_SYMB_1 p_3_2=b",[("p_3_2",B)]) --- generatePatterns :: Int -> KeywordEnv -> Rule -> (Pattern,[MetaVar]) --- generatePatterns ind env r = --- case rhsRule r of --- [] -> (" /* empty */ ", []) --- its -> patternsFor its ("", []) 1 --- -- let nonTerminals = filter isNonTerminal its --- -- in ( - --- -- ) --- -- ( --- -- unwords $ mapMaybe (uncurry mkIt) nits, --- -- [ (var i, cat) | (i, Left cat) <- nits ] --- -- ) --- where --- -- isNonTerminal (Left _) = True --- -- isNonTerminal _ = False --- -- nits = zip [1 :: Int ..] its --- -- var i = "p_" ++ show ind ++"_"++ show i -- TODO: is ind needed for ANTLR? --- -- mkIt i = \case --- -- Left c -> Just $ var i ++ "=" ++ catToNT c --- -- Right s -> lookup s env --- maybeString Nothing = "" --- maybeString (Just v) = v --- encode s = maybeString $ lookup s env --- patternsFor :: [Either Cat String] -> (Pattern, [MetaVar]) -> Int -> (Pattern, [MetaVar]) --- patternsFor [] result n = result --- patternsFor ((Right s):rest) (pattern, vars) n = --- patternsFor rest (pattern +++ (encode s), vars) n --- patternsFor ((Left c):rest) (pattern, vars) n = --- let arg = "p_" ++ show ind ++ "_" ++ show n --- in patternsFor rest (pattern +++ arg ++ "=" ++ catToNT c, vars ++ [(arg, c)]) (n + 1) - - --- catToNT :: Cat -> String --- catToNT = \case --- TokenCat "Ident" -> "IDENT" --- TokenCat "Integer" -> "INTEGER" --- TokenCat "Char" -> "CHAR" --- TokenCat "Double" -> "DOUBLE" --- TokenCat "String" -> "STRING" --- c | isTokenCat c -> identCat c --- | otherwise -> firstLowerCase $ getRuleName $ identCat c - --- -- | Puts together the pattern and actions and returns a string containing all --- -- the rules. --- prRules :: String -> Rules -> String --- prRules packabs = concatMap $ \case - --- -- No rules: skip. --- PDef _mlhs _nt [] -> "" - --- -- At least one rule: print! --- PDef mlhs nt (rhs : rhss) -> unlines $ concat - --- -- The definition header: lhs and type. --- [ [ unwords [ fromMaybe nt' mlhs --- , "returns" , "[" , normcat , "result" , "]" --- ] --- ] --- -- The first rhs. --- , alternative " :" rhs --- -- The other rhss. --- , concatMap (alternative " |") rhss --- -- The definition footer. --- , [ " ;" ] --- ] --- where --- alternative sep (p, a, label) = concat --- [ [ unwords [ sep , p ] ] --- , [ unwords [ " {" , a , "}" ] ] --- , [ unwords [ " #" , antlrRuleLabel l ] | Just l <- [label] ] --- ] --- catid = identCat nt --- normcat = identCat (normCat nt) --- nt' = getRuleName $ firstLowerCase catid --- antlrRuleLabel :: Fun -> String --- antlrRuleLabel fnc --- | isNilFun fnc = catid ++ "_Empty" --- | isOneFun fnc = catid ++ "_AppendLast" --- | isConsFun fnc = catid ++ "_PrependFirst" --- | isCoercion fnc = "Coercion_" ++ catid --- | otherwise = getLabelName fnc diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs similarity index 93% rename from source/src/BNFC/Backend/Dart/CFtoDartAbs.hs rename to source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 567875b0..3225c0b1 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAbs.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} -module BNFC.Backend.Dart.CFtoDartAbs (cf2DartAbs) where +module BNFC.Backend.Dart.CFtoDartAST (cf2DartAST) where import Data.Maybe ( mapMaybe ) @@ -16,8 +16,8 @@ import BNFC.Backend.Dart.Common --Produces abstract data types in Dart -cf2DartAbs :: CF -> RecordPositions -> String -cf2DartAbs cf rp = +cf2DartAST :: CF -> RecordPositions -> String +cf2DartAST cf rp = let userTokens = [ n | (n,_) <- tokenPragmas cf ] in unlines $ imports ++ -- import some libraries if needed @@ -124,7 +124,9 @@ prInstanceVariables rp vars = case rp of -- Generate the class constructor prConstructor :: String -> [DartVar] -> [String] prConstructor className vars = - [ className ++ "({" ++ variablesAssignment ++ "});" ] + [ className ++ "(" ++ variablesAssignment ++ ");" ] where - variablesAssignment = concatMap assignment vars + variablesAssignment + | null vars = "" + | otherwise = "{" ++ (concatMap assignment vars) ++ "}" assignment variable = "required this." ++ buildVariableName variable ++ ", " diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 72197f5b..cbb430d6 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -18,7 +18,8 @@ cf2DartBuilder cf = helperFunctions ++ concatMap generateBuilders rules where - rules = getAbstractSyntax cf + rules = ruleGroups cf + -- getAbstractSyntax cf imports = [ "import 'package:antlr4/antlr4.dart';", "import 'ast.dart';", @@ -30,34 +31,55 @@ cf2DartBuilder cf = "}" ] -generateBuilders :: Data -> [String] -generateBuilders (cat, rules) = - runtimeTypeMapping ++ concatMap generateConcreteMapping (zip [1..] rules) - where +generateBuilders :: (Cat, [Rule]) -> [String] +generateBuilders (cat, rawRules) = + let + rules = map reformatRule rawRules funs = map fst rules - runtimeTypeMapping - | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + in + runtimeTypeMapping funs rules ++ concatMap concreteMapping (zip [1..] rawRules) + where + + -- funs = map funRule rawRules + -- cats = map + -- runtimeTypeMapping = generateRuntimeTypeMapping cat rules + runtimeTypeMapping funs rules + | isList cat || catToStr cat `elem` funs = [] -- the category is also a function or a list | otherwise = generateRuntimeTypeMapping cat rules + concreteMapping (index, rule) = generateConcreteMapping index rule + + +reformatRule :: Rule -> (String, [Cat]) +reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) -generateRuntimeTypeMapping :: Cat -> [(Fun, [Cat])] -> [String] +generateRuntimeTypeMapping :: Cat -> [(String, [Cat])] -> [String] generateRuntimeTypeMapping cat rules = let className = cat2DartClassName cat - in - generateFunctionHeader className ++ - indent 2 ( - [ "switch (ctx.runtimeType) {" ] ++ - (indent 1 $ map buildChild $ map buildClassName rules) ++ - [ "};" ] - ) + in [ + "extension on" +++ contextName className +++ "{" + ] ++ indent 1 [ + className ++ "?" +++ "build" ++ className ++ "() =>" + ] ++ indent 2 ( + [ "switch (runtimeType) {" ] ++ + (indent 1 $ addDefaultCase $ map buildChild $ map buildClassName rules) ++ + [ "};" ] + ) ++ [ + "}" + ] where buildClassName (fun, _) = str2DartClassName fun - buildChild name = (contextName name) +++ "c => build" ++ name ++ "(c)," + buildChild name = (contextName name) +++ "c => c.build" ++ name ++ "()," + addDefaultCase cases = cases ++ [ "_ => null," ] + +generateConcreteMapping :: Int -> Rule -> [String] +generateConcreteMapping index rule = + generateConcreteMappingHelper index rule $ reformatRule rule -generateConcreteMapping :: (Int, (Fun, [Cat])) -> [String] -generateConcreteMapping (index, (fun, cats)) +generateConcreteMappingHelper :: Int -> Rule -> (String, [Cat]) -> [String] +generateConcreteMappingHelper index rule (fun, cats) | isNilFun fun || isOneFun fun || isConsFun fun = [] -- these are not represented in the ast @@ -65,37 +87,67 @@ generateConcreteMapping (index, (fun, cats)) let className = str2DartClassName fun vars = getVars cats - in - generateFunctionHeader className ++ - indent 2 ( - [ className ++ "(" ] ++ - (indent 1 $ generateArgumentsMapping index vars) ++ - [ ");" ] - ) - - -generateArgumentsMapping :: Int -> [DartVar] -> [String] -generateArgumentsMapping index vars = map convertArgument vars - where - convertArgument var@(vType, _) = - let name = buildVariableName var - field = "ctx.p_" ++ show index ++ "_" ++ "1" - in name ++ ":" +++ buildArgument vType field + in [ + "extension on" +++ contextName className +++ "{" + ] ++ indent 1 [ + className +++ "build" ++ className ++ "() =>" + ] ++ indent 2 ( + [ className ++ "(" ] ++ + (indent 1 $ generateArgumentsMapping index rule vars) ++ + [ ");" ] + ) ++ [ + "}" + ] + + +generateArgumentsMapping :: Int -> Rule -> [DartVar] -> [String] +generateArgumentsMapping index r vars = + case rhsRule r of + [] -> ["/* empty */"] + its -> traverseRule index 1 its vars [] + -- unwords $ mapMaybe (uncurry mkIt) $ zip [1 :: Int ..] $ zip its + -- where + -- var i = "p_" ++ show index ++"_"++ show i + -- mkIt i = \case + -- Left c -> Just $ var i ++ "=" ++ catToNT c + -- Right s -> lookup s env + + +traverseRule :: Int -> Int -> [Either Cat String] -> [DartVar] -> [String] -> [String] +traverseRule _ _ _ [] lines = lines +traverseRule _ _ [] _ lines = lines +traverseRule ind1 ind2 (terminal:restTerminals) (variable@(vType, _):restVariables) lines = + case terminal of + Left cat -> traverseRule ind1 (ind2 + 1) restTerminals restVariables lines ++ [ + buildVariableName variable ++ ":" +++ buildArgument vType field ] + Right _ -> traverseRule ind1 (ind2 + 1) restTerminals (variable:restVariables) lines + where + field = "p_" ++ show ind1 ++ "_" ++ show ind2 buildArgument :: DartVarType -> String -> String buildArgument (0, typeName) name = - "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," + name ++ ".build" ++ upperFirst typeName ++ "()," + -- "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," buildArgument (n, typeName) name = let nextName = "e" ++ show n argument = buildArgument (n - 1, typeName) nextName in name ++ ".iMap((" ++ nextName ++ ") =>" +++ argument ++ ")," -generateFunctionHeader :: String -> [String] -generateFunctionHeader className = [ - className +++ "build" ++ className ++ "(", - " " ++ contextName className +++ "ctx,", - ") =>" - ] + +-- generateArgumentsMapping :: Int -> [DartVar] -> [String] +-- generateArgumentsMapping index vars = map convertArgument vars +-- where +-- convertArgument var@(vType, _) = +-- let name = buildVariableName var +-- field = "ctx.p_" ++ show index ++ "_" ++ "1" +-- in name ++ ":" +++ buildArgument vType field +-- buildArgument :: DartVarType -> String -> String +-- buildArgument (0, typeName) name = +-- "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," +-- buildArgument (n, typeName) name = +-- let nextName = "e" ++ show n +-- argument = buildArgument (n - 1, typeName) nextName +-- in name ++ ".iMap((" ++ nextName ++ ") =>" +++ argument ++ ")," contextName :: String -> String diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index d572a4b1..aaf629a3 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -64,8 +64,7 @@ data Mode data Target = TargetC | TargetCpp | TargetCppNoStl | TargetHaskell | TargetHaskellGadt | TargetLatex | TargetJava | TargetOCaml | TargetPygments - | TargetCheck | TargetDart - | TargetCheck | TargetAntlr + | TargetCheck | TargetDart | TargetAntlr deriving (Eq, Bounded, Enum, Ord) -- | List of Haskell target. From a55d2e2963e88baa44d973242e07167358297354 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Tue, 28 Nov 2023 00:34:20 +0300 Subject: [PATCH 27/70] use functions instead of extensions, resolve common issues, leaving only parser generator problems --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 97 +++++++++---------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index cbb430d6..9c61b7ca 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -8,6 +8,7 @@ import BNFC.CF import BNFC.Backend.Dart.Common import Data.Maybe ( mapMaybe ) import BNFC.Utils ( (+++) ) +import Data.List ( intercalate ) cf2DartBuilder :: CF -> String cf2DartBuilder cf = @@ -56,20 +57,16 @@ reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule generateRuntimeTypeMapping :: Cat -> [(String, [Cat])] -> [String] generateRuntimeTypeMapping cat rules = let className = cat2DartClassName cat - in [ - "extension on" +++ contextName className +++ "{" - ] ++ indent 1 [ - className ++ "?" +++ "build" ++ className ++ "() =>" - ] ++ indent 2 ( - [ "switch (runtimeType) {" ] ++ + in [ + className ++ "?" +++ "build" ++ className ++ "(" ++ contextName className ++ "?" +++ "ctx" ++ ") =>" + ] ++ indent 1 ( + [ "switch (ctx?.runtimeType) {" ] ++ (indent 1 $ addDefaultCase $ map buildChild $ map buildClassName rules) ++ [ "};" ] - ) ++ [ - "}" - ] + ) where buildClassName (fun, _) = str2DartClassName fun - buildChild name = (contextName name) +++ "c => c.build" ++ name ++ "()," + buildChild name = (contextName name) +++ "c => build" ++ name ++ "(c)," addDefaultCase cases = cases ++ [ "_ => null," ] @@ -88,29 +85,26 @@ generateConcreteMappingHelper index rule (fun, cats) className = str2DartClassName fun vars = getVars cats in [ - "extension on" +++ contextName className +++ "{" - ] ++ indent 1 [ - className +++ "build" ++ className ++ "() =>" - ] ++ indent 2 ( - [ className ++ "(" ] ++ - (indent 1 $ generateArgumentsMapping index rule vars) ++ - [ ");" ] - ) ++ [ + className ++ "?" +++ "build" ++ className ++ "(" ++ contextName className ++ "?" +++ "ctx) {" + ] ++ ( + indent 1 $ + (generateArguments index rule vars) ++ + (generateNullCheck vars) ++ + [ "return" +++ className ++ "(" ] + ) ++ ( + indent 2 $ generateArgumentsMapping vars + ) ++ indent 1 [ + ");" + ] ++ [ "}" ] + - -generateArgumentsMapping :: Int -> Rule -> [DartVar] -> [String] -generateArgumentsMapping index r vars = +generateArguments :: Int -> Rule -> [DartVar] -> [String] +generateArguments index r vars = case rhsRule r of - [] -> ["/* empty */"] + [] -> [] its -> traverseRule index 1 its vars [] - -- unwords $ mapMaybe (uncurry mkIt) $ zip [1 :: Int ..] $ zip its - -- where - -- var i = "p_" ++ show index ++"_"++ show i - -- mkIt i = \case - -- Left c -> Just $ var i ++ "=" ++ catToNT c - -- Right s -> lookup s env traverseRule :: Int -> Int -> [Either Cat String] -> [DartVar] -> [String] -> [String] @@ -119,35 +113,38 @@ traverseRule _ _ [] _ lines = lines traverseRule ind1 ind2 (terminal:restTerminals) (variable@(vType, _):restVariables) lines = case terminal of Left cat -> traverseRule ind1 (ind2 + 1) restTerminals restVariables lines ++ [ - buildVariableName variable ++ ":" +++ buildArgument vType field ] + "final" +++ buildVariableName variable +++ "=" +++ buildArgument vType field ++ ";" ] Right _ -> traverseRule ind1 (ind2 + 1) restTerminals (variable:restVariables) lines where - field = "p_" ++ show ind1 ++ "_" ++ show ind2 + field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 buildArgument :: DartVarType -> String -> String buildArgument (0, typeName) name = - name ++ ".build" ++ upperFirst typeName ++ "()," - -- "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," + "build" ++ upperFirst typeName ++ "(" ++ name ++ ")" buildArgument (n, typeName) name = let nextName = "e" ++ show n argument = buildArgument (n - 1, typeName) nextName - in name ++ ".iMap((" ++ nextName ++ ") =>" +++ argument ++ ")," - - - --- generateArgumentsMapping :: Int -> [DartVar] -> [String] --- generateArgumentsMapping index vars = map convertArgument vars --- where --- convertArgument var@(vType, _) = --- let name = buildVariableName var --- field = "ctx.p_" ++ show index ++ "_" ++ "1" --- in name ++ ":" +++ buildArgument vType field --- buildArgument :: DartVarType -> String -> String --- buildArgument (0, typeName) name = --- "build" ++ upperFirst typeName ++ "(" ++ name ++ ")," --- buildArgument (n, typeName) name = --- let nextName = "e" ++ show n --- argument = buildArgument (n - 1, typeName) nextName --- in name ++ ".iMap((" ++ nextName ++ ") =>" +++ argument ++ ")," + in name ++ "?.iMap((" ++ nextName ++ ") =>" +++ argument ++ ")" + + +generateNullCheck :: [DartVar] -> [String] +generateNullCheck [] = [] +generateNullCheck vars = + [ "if (" ] ++ + (indent 1 [ intercalate " || " $ map condition vars ]) ++ + [ ") {" ] ++ + (indent 1 [ "return null;" ]) ++ + [ "}" ] + where + condition :: DartVar -> String + condition var = buildVariableName var +++ "==" +++ "null" + + +generateArgumentsMapping :: [DartVar] -> [String] +generateArgumentsMapping vars = map mapArgument vars + where + mapArgument variable = + let name = buildVariableName variable + in name ++ ":" +++ name ++ "," contextName :: String -> String From 40b47feaa8181212164976725c7b2f2ffee91641 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Mon, 4 Dec 2023 23:39:58 +0300 Subject: [PATCH 28/70] builder 85% done --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 95 ++++++++++++------- source/src/BNFC/Backend/Dart/Common.hs | 54 +++++++---- 2 files changed, 96 insertions(+), 53 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 9c61b7ca..15b30d7a 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -6,9 +6,9 @@ module BNFC.Backend.Dart.CFtoDartBuilder (cf2DartBuilder) where import BNFC.CF import BNFC.Backend.Dart.Common -import Data.Maybe ( mapMaybe ) import BNFC.Utils ( (+++) ) -import Data.List ( intercalate ) +import Data.List ( intercalate, find ) +import Data.Either ( isLeft ) cf2DartBuilder :: CF -> String cf2DartBuilder cf = @@ -17,10 +17,10 @@ cf2DartBuilder cf = unlines $ imports ++ helperFunctions ++ + map buildUserToken userTokens ++ concatMap generateBuilders rules where rules = ruleGroups cf - -- getAbstractSyntax cf imports = [ "import 'package:antlr4/antlr4.dart';", "import 'ast.dart';", @@ -29,44 +29,71 @@ cf2DartBuilder cf = "extension IList on List {", " List iMap(T Function(E e) toElement) =>", " map(toElement).toList(growable: false);", - "}" ] + "}", + "int? buildInt(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;", + "double? buildDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;", + "String? buildString(Token? t) => t?.text;" ] + buildUserToken token = + let name = censorName token + in token ++ "? build" ++ token ++ "(Token? t) =>" +++ "t?.text != null ?" +++ token ++ "(t!.text!) : null;" generateBuilders :: (Cat, [Rule]) -> [String] generateBuilders (cat, rawRules) = let - rules = map reformatRule rawRules - funs = map fst rules - in - runtimeTypeMapping funs rules ++ concatMap concreteMapping (zip [1..] rawRules) + numeratedRawRules = zip [1..] rawRules + in + runtimeTypeMapping numeratedRawRules ++ + concatMap (\(index, rule) -> generateConcreteMapping index rule) numeratedRawRules where - - -- funs = map funRule rawRules - -- cats = map - -- runtimeTypeMapping = generateRuntimeTypeMapping cat rules - runtimeTypeMapping funs rules - | isList cat || catToStr cat `elem` funs = [] -- the category is also a function or a list - | otherwise = generateRuntimeTypeMapping cat rules - concreteMapping (index, rule) = generateConcreteMapping index rule + runtimeTypeMapping numeratedRawRules + | isList cat || + catToStr cat `elem` (map (\(_, rule) -> wpThing $ funRule rule) numeratedRawRules) = [] -- the category is also a function or a list + | otherwise = generateRuntimeTypeMapping cat [ + (index, wpThing $ funRule rule, rhsRule rule) | + (index, rule) <- numeratedRawRules ] +-- TODO get rid of this reformating and pass the actual sturcture everywhere reformatRule :: Rule -> (String, [Cat]) reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) -generateRuntimeTypeMapping :: Cat -> [(String, [Cat])] -> [String] +generateRuntimeTypeMapping :: Cat -> [(Int, String, [Either Cat String])] -> [String] generateRuntimeTypeMapping cat rules = - let className = cat2DartClassName cat + let astName = cat2DartClassName cat + prec = precCat cat + precedencedName = astName ++ (if prec == 0 then "" else show prec) in [ - className ++ "?" +++ "build" ++ className ++ "(" ++ contextName className ++ "?" +++ "ctx" ++ ") =>" + astName ++ "?" +++ "build" ++ precedencedName ++ "(" ++ contextName precedencedName ++ "?" +++ "ctx" ++ ") =>" ] ++ indent 1 ( [ "switch (ctx?.runtimeType) {" ] ++ - (indent 1 $ addDefaultCase $ map buildChild $ map buildClassName rules) ++ + (indent 1 $ addDefaultCase $ map (buildChild precedencedName) rules) ++ [ "};" ] ) where - buildClassName (fun, _) = str2DartClassName fun - buildChild name = (contextName name) +++ "c => build" ++ name ++ "(c)," + -- TODO FIX make this synchronized with the parser generator + -- TODO one antlr context class may have multiple arguments from different rules + buildUniversalChild name fun arg = name +++ "c => build" ++ fun ++ "(" ++ arg ++ ")," + buildChild className (index, name, rhs) + | isNilFun name = + buildUniversalChild (contextName className ++ "_Empty") className "c" + | isOneFun name = + buildUniversalChild (contextName className ++ "_AppendLast") className "c" + | isConsFun name = + buildUniversalChild (contextName className ++ "_PrependFirst") className "c" + | isCoercion name = + let + (coercionType, ind2) = case (find (\(_, value) -> isLeft value) $ zip [1..] rhs) of + Just (i, Left cat) -> ( + let prec = precCat cat in (cat2DartClassName cat) ++ (if prec == 0 then "" else show prec), + show i ) + otherwise -> (className, "") -- error, no category for the coercion + argument = "p_" ++ (show index) ++ "_" ++ ind2 + in + buildUniversalChild ("Coercion_" ++ contextName className) coercionType ("c." ++ argument) + | otherwise = + buildUniversalChild (contextName $ str2AntlrClassName name) (str2DartClassName name) "c" addDefaultCase cases = cases ++ [ "_ => null," ] @@ -77,15 +104,17 @@ generateConcreteMapping index rule = generateConcreteMappingHelper :: Int -> Rule -> (String, [Cat]) -> [String] generateConcreteMappingHelper index rule (fun, cats) - | isNilFun fun || + | isCoercion fun || + isNilFun fun || isOneFun fun || isConsFun fun = [] -- these are not represented in the ast | otherwise = -- a standard rule let className = str2DartClassName fun + antlrContextName = contextName $ str2AntlrClassName fun vars = getVars cats in [ - className ++ "?" +++ "build" ++ className ++ "(" ++ contextName className ++ "?" +++ "ctx) {" + className ++ "?" +++ "build" ++ className ++ "(" ++ antlrContextName ++ "?" +++ "ctx) {" ] ++ ( indent 1 $ (generateArguments index rule vars) ++ @@ -112,17 +141,19 @@ traverseRule _ _ _ [] lines = lines traverseRule _ _ [] _ lines = lines traverseRule ind1 ind2 (terminal:restTerminals) (variable@(vType, _):restVariables) lines = case terminal of - Left cat -> traverseRule ind1 (ind2 + 1) restTerminals restVariables lines ++ [ - "final" +++ buildVariableName variable +++ "=" +++ buildArgument vType field ++ ";" ] + Left cat -> [ + "final" +++ buildVariableName variable +++ "=" +++ buildArgument (precCat cat) vType field ++ ";" + ] ++ traverseRule ind1 (ind2 + 1) restTerminals restVariables lines Right _ -> traverseRule ind1 (ind2 + 1) restTerminals (variable:restVariables) lines where field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 - buildArgument :: DartVarType -> String -> String - buildArgument (0, typeName) name = - "build" ++ upperFirst typeName ++ "(" ++ name ++ ")" - buildArgument (n, typeName) name = + buildArgument :: Integer -> DartVarType -> String -> String + buildArgument prec (0, typeName) name = + let precedence = if prec == 0 then "" else show prec + in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" + buildArgument prec (n, typeName) name = let nextName = "e" ++ show n - argument = buildArgument (n - 1, typeName) nextName + argument = buildArgument prec (n - 1, typeName) nextName in name ++ "?.iMap((" ++ nextName ++ ") =>" +++ argument ++ ")" @@ -148,4 +179,4 @@ generateArgumentsMapping vars = map mapArgument vars contextName :: String -> String -contextName className = className ++ "Context" \ No newline at end of file +contextName className = className ++ "Context" diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index ae1f33b3..e59218da 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -13,16 +13,22 @@ cat2DartClassName :: Cat -> String cat2DartClassName cat = str2DartClassName $ identCat $ normCat cat +-- Pick a class name that is appropriate for the Dart str2DartClassName :: String -> String str2DartClassName str = upperFirst $ censorName str -cat2DartType :: Cat -> (Int, String) -cat2DartType cat = toList (0, normCat cat) +-- Pick a class name that is appropriate for the Antlr +str2AntlrClassName :: String -> String +str2AntlrClassName str = upperFirst str + + +cat2DartType :: Cat -> DartVarType +cat2DartType cat = toList (0, cat) where - toList :: (Int, Cat) -> (Int, String) + toList :: (Int, Cat) -> DartVarType toList (n, (ListCat name)) = toList (n + 1, name) - toList (n, name) = (n, (name2DartBuiltIn $ censorName $ catToStr name)) + toList (n, name) = (n, (name2DartBuiltIn $ catToStr name)) cat2DartName :: Cat -> String @@ -64,8 +70,8 @@ type DartVar = (DartVarType, DartVarName) -- The type of a variable type in Dart. --- The amount of nestings, and the underlying type name. --- Example: List> is (2, Point). +-- The amount of nestings, and the underlying type name without precedence. +-- Example: List> is (2, Expr). -- This helps to build the AST builder type DartVarType = (Int, String) @@ -78,22 +84,27 @@ type DartVarName = (String, Int) -- Because of the different type representing variables, a different `getVars` is used. getVars :: [Cat] -> [DartVar] -getVars cats = concatMap mapEntryToVariable $ - Map.toList $ - foldl countVariables Map.empty $ - map toNames cats +getVars cats = + let variables = map toUnnamedVariable cats + namesMap = foldl countNames Map.empty variables + scoreMap = Map.map addScore namesMap + (_, vars) = foldl toDartVar (scoreMap, []) variables + in vars where - toNames cat = ((cat2DartType cat), (cat2DartName cat)) - countVariables varsMap entry = - let current = Map.findWithDefault 0 entry varsMap + toUnnamedVariable cat = ((cat2DartType cat), (cat2DartName cat)) + countNames namesMap (_, name) = + let current = Map.findWithDefault 0 name namesMap next = 1 + current - in Map.insert entry next varsMap - mapEntryToVariable ((varType, name), amount) - | amount <= 1 = [ toDartVar varType name 0 ] - | otherwise = - let variableNameBase = toDartVar varType name - in map variableNameBase $ [1..amount] - toDartVar varType name number = (varType, (name, number)) + in Map.insert name next namesMap + addScore n = (1, n) + toDartVar (namesMap, vars) (vType, name) = + case (Map.lookup name namesMap) of + Nothing -> (namesMap, vars ++ [(vType, (name, 0))]) + Just (seen, total) -> if total <= 1 + then (namesMap, vars ++ [(vType, (name, 0))]) + else ( + Map.insert name (seen + 1, total) namesMap, + vars ++ [(vType, (name, seen))]) -- From a DartVar build its string representation @@ -105,6 +116,7 @@ buildVariableName (_, (name, num)) = lowerFirst appendNumber | otherwise = name ++ show num +-- From a DartVar make a name for the AST buildVariableType :: DartVar -> String buildVariableType (vType, _) = unpack vType where @@ -115,7 +127,7 @@ buildVariableType (vType, _) = unpack vType -- Prevent some type or variable name to be called as some built-in Dart type censorName :: String -> String censorName name - | name `elem` builtInTypes = "My" ++ upperFirst name + | (lowerFirst name) `elem` (map lowerFirst builtInTypes) = "My" ++ upperFirst name | otherwise = name where builtInTypes = [ "int", "double", "String", "bool", "List", "Set", "Map", From 1f8aee79bd5bc3d500375862a8960cfe820e3c6f Mon Sep 17 00:00:00 2001 From: xdkomel Date: Mon, 11 Dec 2023 12:38:42 +0300 Subject: [PATCH 29/70] added pretty printer first draft --- source/BNFC.cabal | 1 + source/src/BNFC/Backend/Dart.hs | 2 + .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 3 +- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 192 ++++++++++++++++++ 4 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 59366e0a..c46a3c64 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -259,6 +259,7 @@ library BNFC.Backend.Dart.CFtoDartAST BNFC.Backend.Dart.CFtoDartBuilder BNFC.Backend.Dart.Common + BNFC.Backend.Dart.CFtoDartPrinter -- Antlr4 backend BNFC.Backend.Antlr diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index b32d90de..239f33c1 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -22,6 +22,7 @@ import BNFC.Backend.Antlr.CFtoAntlr4Lexer import BNFC.Backend.Antlr.CFtoAntlr4Parser import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) +import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) import BNFC.Backend.Java.CFtoJavaPrinter15 import BNFC.Backend.Java.CFtoVisitSkel15 import BNFC.Backend.Java.CFtoComposVisitor @@ -69,6 +70,7 @@ makeDart' pkg options@Options{..} cf = do (lex, env) = cf2AntlrLex "Stella" cf mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) mkfile (locate "builder" "dart") comment (cf2DartBuilder cf) + mkfile (locate "pretty_printer" "dart") comment (cf2DartPrinter cf) mkfile (locate (lang ++ "Lexer") "g4") comment lex mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) -- makebnfcfile bprettyprinter diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 15b30d7a..cc25c812 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -46,9 +46,10 @@ generateBuilders (cat, rawRules) = runtimeTypeMapping numeratedRawRules ++ concatMap (\(index, rule) -> generateConcreteMapping index rule) numeratedRawRules where + funs numeratedRawRules = (map (\(_, rule) -> wpThing $ funRule rule) numeratedRawRules) runtimeTypeMapping numeratedRawRules | isList cat || - catToStr cat `elem` (map (\(_, rule) -> wpThing $ funRule rule) numeratedRawRules) = [] -- the category is also a function or a list + (catToStr cat) `elem` (funs numeratedRawRules) = [] -- the category is also a function or a list | otherwise = generateRuntimeTypeMapping cat [ (index, wpThing $ funRule rule, rhsRule rule) | (index, rule) <- numeratedRawRules ] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs new file mode 100644 index 00000000..b8040c14 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -0,0 +1,192 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Dart.CFtoDartPrinter (cf2DartPrinter) where + +import BNFC.CF +import BNFC.Backend.Dart.Common +import BNFC.Utils ( (+++) ) +import Data.Maybe ( mapMaybe ) +import Data.List ( intercalate, find ) +import Data.Either ( isLeft ) + +cf2DartPrinter :: CF -> String +cf2DartPrinter cf = + let userTokens = [ n | (n,_) <- tokenPragmas cf ] + in + unlines $ + imports ++ + helperFunctions ++ + stringRenderer ++ + (map buildUserToken userTokens) ++ + (concatMap generatePrettifiers $ ruleGroups cf) + +imports :: [String] +imports = [ + "import 'ast.dart' as ast;", + "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] + +helperFunctions :: [String] +helperFunctions = [ + "sealed class Token {}", + "", + "class Space extends Token {}", + "", + "class NewLine extends Token {", + " int indentDifference;", + " NewLine.indent(this.indentDifference);", + " NewLine() : indentDifference = 0;", + " NewLine.nest() : indentDifference = 1;", + " NewLine.unnest() : indentDifference = -1;", + "}", + "", + "class Text extends Token {", + " String text;", + " Text(this.text);", + "}" ] + +stringRenderer :: [String] +stringRenderer = [ + "class StringRenderer {", + " // Change this value if you want to change the indentation length", + " static const _indentInSpaces = 2;", + "", + " String show(IList tokens) => tokens", + " .fold(IList(), _render)", + " .fold(IList<(int, IList)>(), _split)", + " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))", + " .fold(IList<(int, String)>(), _convertIndentation)", + " .map(_addIndentation)", + " .join();", + "", + " IList<(int, IList)> _split(", + " IList<(int, IList)> lists,", + " Token token,", + " ) =>", + " switch (token) {", + " NewLine nl => lists.add(", + " (", + " nl.indentDifference,", + " IList([]),", + " ),", + " ),", + " _ => lists.put(", + " lists.length - 1,", + " (", + " lists.last.$1,", + " lists.last.$2.add(token),", + " ),", + " )", + " };", + "", + " String _tokenToString(Token t) => switch (t) {", + " Text t => t.text,", + " Space _ => ' ',", + " _ => '',", + " };", + "", + " IList<(int, String)> _convertIndentation(", + " IList<(int, String)> lines,", + " (int, String) line,", + " ) =>", + " lines.add(", + " (", + " line.$1 + (lines.lastOrNull?.$1 ?? 0),", + " line.$2,", + " ),", + " );", + "", + " String _addIndentation((int, String) indentedLine) =>", + " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;", + "", + " // This function is supposed to be edited", + " // in order to adjust the pretty printer behavior", + " IList _render(IList tokens, String token) => switch (token) {", + " '{' => tokens.addAll([Text(token), NewLine.nest()]),", + " '}' => tokens.addAll([NewLine.unnest(), Text(token)]),", + " ';' => tokens.addAll([NewLine(), Text(token)]),", + " ',' ||", + " '.' ||", + " ':' ||", + " '<' ||", + " '>' ||", + " '[' ||", + " ']' ||", + " '(' ||", + " ')' =>", + " tokens.removeTrailingSpaces.addAll([Text(token), Space()]),", + " '\\$' || '&' || '@' || '!' || '#' => tokens.add(Text(token)),", + " _ => tokens.addAll([Text(token), Space()])", + " };", + "}", + "", + "extension TokensList on IList {", + " IList get removeTrailingSpaces =>", + " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;", + "}", + "", + "final _renderer = StringRenderer();" ] + +buildUserToken :: String -> String +buildUserToken token = "extension on ast." ++ token +++ "{\n String get show => value;\n}" + +generatePrettifiers :: (Cat, [Rule]) -> [String] +generatePrettifiers (cat, rawRules) = + let rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] + funs = [ fst rule | rule <- rules ] + in categoryClass rules funs ++ + mapMaybe (generateConcreteMapping cat) rules ++ + concatMap generateExtensionShow funs + where + categoryClass rules funs + | isList cat || + (catToStr cat) `elem` funs = [] -- the category is not presented in the AST + | otherwise = + let className = cat2DartClassName cat + in (genrateRuntimeMapping className rules) ++ + (generateExtensionShow className) + +genrateRuntimeMapping :: String -> [(String, [Either Cat String])] -> [String] +genrateRuntimeMapping name rules = [ + "IList _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ + (indent 2 $ map mapRule $ map str2DartClassName $ map fst rules) ++ + (indent 1 [ "};" ]) + where + mapRule name = "ast." ++ name +++ "a => _prettify" ++ name ++ "(a)," + +generateConcreteMapping :: Cat -> (String, [Either Cat String]) -> Maybe (String) +generateConcreteMapping cat (label, tokens) + | isNilFun label || + isOneFun label || + isConsFun label = Nothing -- these are not represented in the AST + | otherwise = -- a standard rule + let + className = str2DartClassName label + cats = [ cat | Left cat <- tokens ] + vars = getVars cats + in Just . unlines $ [ + "IList _prettify" ++ className ++ "(ast." ++ className ++ " a) => IList([" ] ++ + (indent 1 $ generateRuleRHS tokens vars []) ++ + ["]);"] + +generateRuleRHS :: [Either Cat String] -> [DartVar] -> [String] -> [String] +generateRuleRHS [] _ _ = [] +generateRuleRHS _ [] _ = [] +generateRuleRHS (token:rTokens) (variable@(vType, _):rVariables) lines = case token of + Right terminal -> + generateRuleRHS rTokens (variable:rVariables) $ lines ++ [terminal ++ ","] + Left _ -> generateRuleRHS rTokens rVariables $ + lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ] + +buildArgument :: DartVarType -> String -> String +buildArgument (0, typeName) name = name ++ ".show" +-- TODO add correct separators from the CF +buildArgument (n, typeName) name = + "..." ++ name ++ ".expand((e" ++ show n ++ ") => [\'\', " ++ (buildArgument (n-1, typeName) ("e" ++ show n)) ++ "]).skip(1)," + +generateExtensionShow :: String -> [String] +generateExtensionShow name = [ + "extension on ast." ++ name +++ "{", + " String get show => _renderer.show(_prettify" ++ name ++ "(this));", + "}" ] \ No newline at end of file From 2358676958ce2ab0630581a043d0f46be7aef342 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Mon, 11 Dec 2023 13:01:03 +0300 Subject: [PATCH 30/70] fixed show methods collisions --- source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index b8040c14..77c99ae3 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -129,7 +129,7 @@ stringRenderer = [ "final _renderer = StringRenderer();" ] buildUserToken :: String -> String -buildUserToken token = "extension on ast." ++ token +++ "{\n String get show => value;\n}" +buildUserToken token = "extension on ast." ++ token +++ "{\n String get show" ++ token +++ "=> value;\n}" generatePrettifiers :: (Cat, [Rule]) -> [String] generatePrettifiers (cat, rawRules) = @@ -171,22 +171,22 @@ generateConcreteMapping cat (label, tokens) ["]);"] generateRuleRHS :: [Either Cat String] -> [DartVar] -> [String] -> [String] -generateRuleRHS [] _ _ = [] -generateRuleRHS _ [] _ = [] +generateRuleRHS [] _ lines = lines +generateRuleRHS _ [] lines = lines generateRuleRHS (token:rTokens) (variable@(vType, _):rVariables) lines = case token of Right terminal -> - generateRuleRHS rTokens (variable:rVariables) $ lines ++ [terminal ++ ","] + generateRuleRHS rTokens (variable:rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] Left _ -> generateRuleRHS rTokens rVariables $ lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ] buildArgument :: DartVarType -> String -> String -buildArgument (0, typeName) name = name ++ ".show" +buildArgument (0, typeName) name = name ++ ".show" ++ typeName ++ "," -- TODO add correct separators from the CF buildArgument (n, typeName) name = "..." ++ name ++ ".expand((e" ++ show n ++ ") => [\'\', " ++ (buildArgument (n-1, typeName) ("e" ++ show n)) ++ "]).skip(1)," generateExtensionShow :: String -> [String] generateExtensionShow name = [ - "extension on ast." ++ name +++ "{", - " String get show => _renderer.show(_prettify" ++ name ++ "(this));", + "extension" +++ name ++ "Show" +++ "on ast." ++ name +++ "{", + " String get show" ++ name +++ "=> _renderer.show(_prettify" ++ name ++ "(this));", "}" ] \ No newline at end of file From df4f1b2706db278d0c601c87d782fea0393bfca5 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Mon, 11 Dec 2023 13:09:06 +0300 Subject: [PATCH 31/70] fixed different precedence printers --- source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 77c99ae3..2f0e31dc 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -163,7 +163,7 @@ generateConcreteMapping cat (label, tokens) | otherwise = -- a standard rule let className = str2DartClassName label - cats = [ cat | Left cat <- tokens ] + cats = [ normCat cat | Left cat <- tokens ] vars = getVars cats in Just . unlines $ [ "IList _prettify" ++ className ++ "(ast." ++ className ++ " a) => IList([" ] ++ From 091f72bd9f9f9603b2d563cd9ca7aebd27193a0a Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Thu, 7 Dec 2023 02:03:20 +0300 Subject: [PATCH 32/70] [ANTLRv4] expose antlrRuleLabel --- .../BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index 5700e1c2..1802be37 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -1,6 +1,6 @@ {-# LANGUAGE LambdaCase #-} -module BNFC.Backend.Antlr.CFtoAntlr4Parser ( cf2AntlrParse ) where +module BNFC.Backend.Antlr.CFtoAntlr4Parser ( cf2AntlrParse, antlrRuleLabel ) where import Data.Foldable ( toList ) import Data.Maybe @@ -136,14 +136,17 @@ prRules = concatMap $ \case , [ " ;" ] ] where - alternative sep (p, label) = unwords [ sep , p ] : [ unwords [ " #" , antlrRuleLabel l ] | Just l <- [label] ] + alternative sep (p, label) = unwords [ sep , p ] : [ unwords [ " #" , antlrRuleLabel nt l] | Just l <- [label] ] catid = identCat nt nt' = getRuleName $ firstLowerCase catid - antlrRuleLabel :: Fun -> String - antlrRuleLabel fnc - | isNilFun fnc = catid ++ "_Empty" - | isOneFun fnc = catid ++ "_AppendLast" - | isConsFun fnc = catid ++ "_PrependFirst" - | isCoercion fnc = "Coercion_" ++ catid - | otherwise = getLabelName fnc + +antlrRuleLabel :: Cat -> Fun -> String +antlrRuleLabel cat fnc + | isNilFun fnc = catid ++ "_Empty" + | isOneFun fnc = catid ++ "_AppendLast" + | isConsFun fnc = catid ++ "_PrependFirst" + | isCoercion fnc = "Coercion_" ++ catid + | otherwise = getLabelName fnc + where + catid = identCat cat From 82e3385e88f4950db2008061b1112fc053f0a8c1 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Sat, 16 Dec 2023 18:06:56 +0300 Subject: [PATCH 33/70] [ANTLRv4] expose function for making left-recursive rules --- source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index 1802be37..095a7493 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -1,6 +1,6 @@ {-# LANGUAGE LambdaCase #-} -module BNFC.Backend.Antlr.CFtoAntlr4Parser ( cf2AntlrParse, antlrRuleLabel ) where +module BNFC.Backend.Antlr.CFtoAntlr4Parser ( cf2AntlrParse, antlrRuleLabel, makeLeftRecRule ) where import Data.Foldable ( toList ) import Data.Maybe @@ -80,12 +80,16 @@ constructRule cf env rules nt = PDef Nothing nt $ [ ( p, Just label ) | (index, r0) <- zip [1..] rules - , let b = isConsFun (funRule r0) && elem (valCat r0) (cfgReversibleCats cf) - , let r = applyWhen b revSepListRule r0 + , let r = makeLeftRecRule cf r0 , let p = generatePattern index env r , let label = wpThing (funRule r) ] +makeLeftRecRule :: CF -> Rule -> Rule +makeLeftRecRule cf rule = applyWhen canBeLeftRecursive revSepListRule rule + where + canBeLeftRecursive = isConsFun (funRule rule) && elem (valCat rule) (cfgReversibleCats cf) + -- | Generate patterns and a set of metavariables indicating -- where in the pattern the non-terminal -- >>> generatePatterns 2 [] $ npRule "myfun" (Cat "A") [] Parsable From 0d0c28263ee7355a24f1b0d9b877046b439b4701 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Sat, 16 Dec 2023 22:34:40 +0300 Subject: [PATCH 34/70] [ANTLRv4] resolve name collision for coercion types for ANTLR rule labels --- .../src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index 095a7493..7e13eeaa 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -127,30 +127,33 @@ prRules = concatMap $ \case PDef _mlhs _nt [] -> "" -- At least one rule: print! - PDef mlhs nt (rhs : rhss) -> unlines $ concat + PDef mlhs nt rhss -> unlines $ concat -- The definition header: lhs and type. [ [ unwords [fromMaybe nt' mlhs] ] -- The first rhs. - , alternative " :" rhs + , alternative " :" $ head indexedRhss -- The other rhss. - , concatMap (alternative " |") rhss + , concatMap (alternative " |") $ tail indexedRhss -- The definition footer. , [ " ;" ] ] where - alternative sep (p, label) = unwords [ sep , p ] : [ unwords [ " #" , antlrRuleLabel nt l] | Just l <- [label] ] + alternative sep ((p, label), idx) = unwords [ sep , p ] : [ unwords [ " #" , antlrRuleLabel nt l idx] | Just l <- [label] ] + indexedRhss = zipWith (\rule idx -> if (maybe False isCoercion (snd rule)) then (rule, Just idx) else (rule, Nothing)) rhss [1..] catid = identCat nt nt' = getRuleName $ firstLowerCase catid -antlrRuleLabel :: Cat -> Fun -> String -antlrRuleLabel cat fnc +-- we use rule's index as prefix for ANTLR label +-- in order to avoid name collisions for coercion types +antlrRuleLabel :: Cat -> Fun -> Maybe Integer -> String +antlrRuleLabel cat fnc int | isNilFun fnc = catid ++ "_Empty" | isOneFun fnc = catid ++ "_AppendLast" | isConsFun fnc = catid ++ "_PrependFirst" - | isCoercion fnc = "Coercion_" ++ catid + | isCoercion fnc = "Coercion_" ++ catid ++ maybe "" (("_" ++) . show) int | otherwise = getLabelName fnc where catid = identCat cat From 7e51440338c95da65aa9763c89961c808218d627 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Tue, 30 Jan 2024 11:46:25 +0300 Subject: [PATCH 35/70] a bit reorganized --- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 2f0e31dc..3af16118 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -20,7 +20,8 @@ cf2DartPrinter cf = helperFunctions ++ stringRenderer ++ (map buildUserToken userTokens) ++ - (concatMap generatePrettifiers $ ruleGroups cf) + (concatMap generateRulePrettifiers $ getAbstractSyntax cf) ++ + (concatMap generateLabelPrettifiers $ ruleGroups cf) imports :: [String] imports = [ @@ -131,26 +132,30 @@ stringRenderer = [ buildUserToken :: String -> String buildUserToken token = "extension on ast." ++ token +++ "{\n String get show" ++ token +++ "=> value;\n}" -generatePrettifiers :: (Cat, [Rule]) -> [String] -generatePrettifiers (cat, rawRules) = +generateLabelPrettifiers :: (Cat, [Rule]) -> [String] +generateLabelPrettifiers (cat, rawRules) = let rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] funs = [ fst rule | rule <- rules ] - in categoryClass rules funs ++ - mapMaybe (generateConcreteMapping cat) rules ++ + in mapMaybe (generateConcreteMapping cat) rules ++ concatMap generateExtensionShow funs - where - categoryClass rules funs - | isList cat || - (catToStr cat) `elem` funs = [] -- the category is not presented in the AST - | otherwise = + +generateRulePrettifiers :: Data -> [String] +generateRulePrettifiers (cat, rules) = + let funs = map fst rules + in if + isList cat || + (catToStr cat) `elem` funs + then + [] -- the category is not presented in the AST + else let className = cat2DartClassName cat - in (genrateRuntimeMapping className rules) ++ + in (generateRuntimeMapping className $ map fst rules) ++ (generateExtensionShow className) -genrateRuntimeMapping :: String -> [(String, [Either Cat String])] -> [String] -genrateRuntimeMapping name rules = [ +generateRuntimeMapping :: String -> [String] -> [String] +generateRuntimeMapping name ruleNames = [ "IList _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ - (indent 2 $ map mapRule $ map str2DartClassName $ map fst rules) ++ + (indent 2 $ map mapRule $ map str2DartClassName ruleNames) ++ (indent 1 [ "};" ]) where mapRule name = "ast." ++ name +++ "a => _prettify" ++ name ++ "(a)," From 7a0148ba0c8626428a0937dc872dd7380b84bf68 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Tue, 30 Jan 2024 13:37:22 +0300 Subject: [PATCH 36/70] support coercion number --- source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index cc25c812..b348bd99 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -35,7 +35,9 @@ cf2DartBuilder cf = "String? buildString(Token? t) => t?.text;" ] buildUserToken token = let name = censorName token - in token ++ "? build" ++ token ++ "(Token? t) =>" +++ "t?.text != null ?" +++ token ++ "(t!.text!) : null;" + in token ++ "? build" ++ token ++ "(Token? t) {\n" ++ + " final text = t?.text;\n" ++ + " return text != null ?" +++ token ++ "(text) : null;\n}" generateBuilders :: (Cat, [Rule]) -> [String] @@ -90,9 +92,10 @@ generateRuntimeTypeMapping cat rules = let prec = precCat cat in (cat2DartClassName cat) ++ (if prec == 0 then "" else show prec), show i ) otherwise -> (className, "") -- error, no category for the coercion - argument = "p_" ++ (show index) ++ "_" ++ ind2 + lineIndex = show index + argument = "p_" ++ lineIndex ++ "_" ++ ind2 in - buildUniversalChild ("Coercion_" ++ contextName className) coercionType ("c." ++ argument) + buildUniversalChild ("Coercion_" ++ contextName (className ++ "_" ++ lineIndex)) coercionType ("c." ++ argument) | otherwise = buildUniversalChild (contextName $ str2AntlrClassName name) (str2DartClassName name) "c" addDefaultCase cases = cases ++ [ "_ => null," ] From af360173853da4f9ba7554b8b3a07088debf973c Mon Sep 17 00:00:00 2001 From: xdkomel Date: Tue, 30 Jan 2024 21:30:28 +0300 Subject: [PATCH 37/70] pretty printer w/o coercions & lists --- source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 29 ++++++++-- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 56 ++++++++++++++----- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 3225c0b1..51b47600 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -25,7 +25,9 @@ cf2DartAST cf rp = concatMap (prData rp) rules where rules = getAbstractSyntax cf - imports = [] + imports = [ + "import 'package:fast_immutable_collections/fast_immutable_collections.dart';", + "import \'pretty_printer.dart\' as pp;" ] generateTokens :: [UserDef] -> [String] @@ -34,14 +36,17 @@ generateTokens tokens = map toClass tokens toClass token = let name = censorName token in unlines [ - "final class" +++ name +++ "{", -- A user defined type is a wrapper around the String + "final class" +++ name +++ "with pp.Printable {", -- A user defined type is a wrapper around the String " final String value;", " const" +++ name ++ "(this.value);", + "", + " @override", + " String get print => pp.print" ++ name ++ "(this);", "}" ] --- | Generates a (possibly abstract) category class, and classes for all its rules. +-- | Generates a category class, and classes for all its rules. prData :: RecordPositions -> Data -> [String] prData rp (cat, rules) = categoryClass ++ mapMaybe (prRule rp cat) rules @@ -49,7 +54,13 @@ prData rp (cat, rules) = funs = map fst rules categoryClass | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list - | otherwise = [ "sealed class" +++ cat2DartClassName cat +++ "{}" ] + | otherwise = + let name = cat2DartClassName cat + in [ + "sealed class" +++ name +++ "with pp.Printable {", + " @override", + " String get print => pp.print" ++ name ++ "(this);", + "}" ] -- | Generates classes for a rule, depending on what type of rule it is. @@ -63,12 +74,13 @@ prRule rp cat (fun, cats) className = str2DartClassName fun vars = getVars cats in Just . unlines $ - [ unwords [ "class", className, extending, "{" ] ] ++ + [ unwords [ "class", className, extending, "with pp.Printable {" ] ] ++ concatMap (indent 1) [ prInstanceVariables rp vars, prConstructor className vars, prEquals className vars, - prHashCode vars + prHashCode vars, + prPrettyPrint className ] ++ [ "}" ] where extending @@ -130,3 +142,8 @@ prConstructor className vars = | null vars = "" | otherwise = "{" ++ (concatMap assignment vars) ++ "}" assignment variable = "required this." ++ buildVariableName variable ++ ", " + +prPrettyPrint :: String -> [String] +prPrettyPrint name = [ + "@override", + "String get print => pp.print" ++ name ++ "(this);" ] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 3af16118..a4d6c54d 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -53,7 +53,7 @@ stringRenderer = [ " // Change this value if you want to change the indentation length", " static const _indentInSpaces = 2;", "", - " String show(IList tokens) => tokens", + " String print(IList tokens) => tokens", " .fold(IList(), _render)", " .fold(IList<(int, IList)>(), _split)", " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))", @@ -127,30 +127,56 @@ stringRenderer = [ " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;", "}", "", - "final _renderer = StringRenderer();" ] + "extension PrintableInt on int {", + " String get print => toString();", + "}", + "", + "extension PrintableDouble on double {", + " String get print => toString();", + "}", + "", + "final _renderer = StringRenderer();", + "", + "mixin Printable {", + " String get print => \'[not implemented]\';", + "}" ] buildUserToken :: String -> String -buildUserToken token = "extension on ast." ++ token +++ "{\n String get show" ++ token +++ "=> value;\n}" +buildUserToken token = "String print" ++ token ++ "(x) => x.value;" generateLabelPrettifiers :: (Cat, [Rule]) -> [String] generateLabelPrettifiers (cat, rawRules) = let rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] funs = [ fst rule | rule <- rules ] in mapMaybe (generateConcreteMapping cat) rules ++ - concatMap generateExtensionShow funs + (concatMap generatePrintFunction $ map str2DartClassName $ filter representedInAst funs) + where + representedInAst :: String -> Bool + representedInAst fun = not ( + isNilFun fun || + isOneFun fun || + isConsFun fun || + isConcatFun fun || + isCoercion fun ) generateRulePrettifiers :: Data -> [String] generateRulePrettifiers (cat, rules) = let funs = map fst rules + fun = catToStr cat in if isList cat || - (catToStr cat) `elem` funs + isNilFun fun || + isOneFun fun || + isConsFun fun || + isConcatFun fun || + isCoercion fun || + fun `elem` funs then [] -- the category is not presented in the AST else let className = cat2DartClassName cat in (generateRuntimeMapping className $ map fst rules) ++ - (generateExtensionShow className) + (generatePrintFunction className) generateRuntimeMapping :: String -> [String] -> [String] generateRuntimeMapping name ruleNames = [ @@ -162,9 +188,11 @@ generateRuntimeMapping name ruleNames = [ generateConcreteMapping :: Cat -> (String, [Either Cat String]) -> Maybe (String) generateConcreteMapping cat (label, tokens) - | isNilFun label || - isOneFun label || - isConsFun label = Nothing -- these are not represented in the AST + | isNilFun label || + isOneFun label || + isConsFun label || + isConcatFun label || + isCoercion label = Nothing -- these are not represented in the AST | otherwise = -- a standard rule let className = str2DartClassName label @@ -185,13 +213,11 @@ generateRuleRHS (token:rTokens) (variable@(vType, _):rVariables) lines = case to lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ] buildArgument :: DartVarType -> String -> String -buildArgument (0, typeName) name = name ++ ".show" ++ typeName ++ "," +buildArgument (0, typeName) name = name ++ ".print" ++ "," -- TODO add correct separators from the CF buildArgument (n, typeName) name = "..." ++ name ++ ".expand((e" ++ show n ++ ") => [\'\', " ++ (buildArgument (n-1, typeName) ("e" ++ show n)) ++ "]).skip(1)," -generateExtensionShow :: String -> [String] -generateExtensionShow name = [ - "extension" +++ name ++ "Show" +++ "on ast." ++ name +++ "{", - " String get show" ++ name +++ "=> _renderer.show(_prettify" ++ name ++ "(this));", - "}" ] \ No newline at end of file +generatePrintFunction :: String -> [String] +generatePrintFunction name = [ + "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] \ No newline at end of file From 45c86d8cc528342460028c6fe742f60b6164a3f7 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Mon, 12 Feb 2024 12:40:47 +0300 Subject: [PATCH 38/70] added sep&term, bug w/ coercions --- source/src/BNFC/Backend/Dart.hs | 2 +- source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 4 +- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 10 +- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 109 +++++++++++++----- source/src/BNFC/Backend/Dart/Common.hs | 5 +- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 239f33c1..349098e7 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -69,7 +69,7 @@ makeDart' pkg options@Options{..} cf = do let locate str ext = dirBase str <.> ext (lex, env) = cf2AntlrLex "Stella" cf mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) - mkfile (locate "builder" "dart") comment (cf2DartBuilder cf) + mkfile (locate "builder" "dart") comment (cf2DartBuilder cf lang) mkfile (locate "pretty_printer" "dart") comment (cf2DartPrinter cf) mkfile (locate (lang ++ "Lexer") "g4") comment lex mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 51b47600..86b03d22 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -25,9 +25,7 @@ cf2DartAST cf rp = concatMap (prData rp) rules where rules = getAbstractSyntax cf - imports = [ - "import 'package:fast_immutable_collections/fast_immutable_collections.dart';", - "import \'pretty_printer.dart\' as pp;" ] + imports = [ "import \'pretty_printer.dart\' as pp;" ] generateTokens :: [UserDef] -> [String] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index b348bd99..a1781498 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -10,21 +10,21 @@ import BNFC.Utils ( (+++) ) import Data.List ( intercalate, find ) import Data.Either ( isLeft ) -cf2DartBuilder :: CF -> String -cf2DartBuilder cf = +cf2DartBuilder :: CF -> String -> String +cf2DartBuilder cf lang = let userTokens = [ n | (n,_) <- tokenPragmas cf ] in unlines $ - imports ++ + imports lang ++ helperFunctions ++ map buildUserToken userTokens ++ concatMap generateBuilders rules where rules = ruleGroups cf - imports = [ + imports lang = [ "import 'package:antlr4/antlr4.dart';", "import 'ast.dart';", - "import 'stellaParser.dart'; // fix this line depending on where the stellaParser is being lcated" ] + "import '" ++ lang ++ "Parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] helperFunctions = [ "extension IList on List {", " List iMap(T Function(E e) toElement) =>", diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index a4d6c54d..7e319ada 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -20,8 +20,8 @@ cf2DartPrinter cf = helperFunctions ++ stringRenderer ++ (map buildUserToken userTokens) ++ - (concatMap generateRulePrettifiers $ getAbstractSyntax cf) ++ - (concatMap generateLabelPrettifiers $ ruleGroups cf) + (concatMap generateRulePrinters $ getAbstractSyntax cf) ++ + (concatMap generateLabelPrinters $ ruleGroups cf) imports :: [String] imports = [ @@ -144,12 +144,24 @@ stringRenderer = [ buildUserToken :: String -> String buildUserToken token = "String print" ++ token ++ "(x) => x.value;" -generateLabelPrettifiers :: (Cat, [Rule]) -> [String] -generateLabelPrettifiers (cat, rawRules) = +generateLabelPrinters :: (Cat, [Rule]) -> [String] +generateLabelPrinters (cat, rawRules) = let rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] - funs = [ fst rule | rule <- rules ] - in mapMaybe (generateConcreteMapping cat) rules ++ - (concatMap generatePrintFunction $ map str2DartClassName $ filter representedInAst funs) + in if isList cat + then + let + sep = findSep rules + term = findTerm rules + vType = cat2DartType $ normCat cat + in if sep == "" && term == "" + then [] + else [ + generateListPrettifier vType sep term, + generateListPrintFunction vType ] + else + let funs = [ fst rule | rule <- rules ] + in mapMaybe (generateConcreteMapping cat) rules ++ + (concatMap generatePrintFunction $ map str2DartClassName $ filter representedInAst funs) where representedInAst :: String -> Bool representedInAst fun = not ( @@ -158,25 +170,40 @@ generateLabelPrettifiers (cat, rawRules) = isConsFun fun || isConcatFun fun || isCoercion fun ) + findSep :: [(String, [Either Cat String])] -> String + findSep [] = "" + findSep ((name, rhs):rest) + | isConsFun name = case [ sep | Right sep <- rhs ] of + (a:_) -> a + [] -> findSep rest + | otherwise = findSep rest + findTerm :: [(String, [Either Cat String])] -> String + findTerm [] = "" + findTerm ((name, rhs):rest) + | isOneFun name = case [ sep | Right sep <- rhs ] of + (a:_) -> a + [] -> findTerm rest + | otherwise = findTerm rest -generateRulePrettifiers :: Data -> [String] -generateRulePrettifiers (cat, rules) = +generateRulePrinters :: Data -> [String] +generateRulePrinters (cat, rules) = let funs = map fst rules fun = catToStr cat - in if - isList cat || - isNilFun fun || - isOneFun fun || - isConsFun fun || - isConcatFun fun || - isCoercion fun || - fun `elem` funs - then - [] -- the category is not presented in the AST - else - let className = cat2DartClassName cat - in (generateRuntimeMapping className $ map fst rules) ++ - (generatePrintFunction className) + in + if + isList cat || + isNilFun fun || + isOneFun fun || + isConsFun fun || + isConcatFun fun || + isCoercion fun || + fun `elem` funs + then + [] -- the category is not presented in the AST + else + let className = cat2DartClassName cat + in (generateRuntimeMapping className $ map fst rules) ++ + (generatePrintFunction className) generateRuntimeMapping :: String -> [String] -> [String] generateRuntimeMapping name ruleNames = [ @@ -199,10 +226,18 @@ generateConcreteMapping cat (label, tokens) cats = [ normCat cat | Left cat <- tokens ] vars = getVars cats in Just . unlines $ [ - "IList _prettify" ++ className ++ "(ast." ++ className ++ " a) => IList([" ] ++ + "IList _prettify" ++ className ++ "(ast." ++ className +++ "a) => IList([" ] ++ (indent 1 $ generateRuleRHS tokens vars []) ++ ["]);"] +generateListPrettifier :: DartVarType -> String -> String -> String +generateListPrettifier vType@(n, name) separator terminator = + "IList _prettify" ++ printerListName vType ++ "(" ++ + printerListType vType +++ "a) => IList([...a.expand((e" ++ show n ++ + ") => [\'" ++ separator ++ "\'," +++ + (buildArgument (n - 1, name) ("e" ++ show n)) ++ + "],).skip(1)," +++ "\'" ++ terminator ++ "\',]);" + generateRuleRHS :: [Either Cat String] -> [DartVar] -> [String] -> [String] generateRuleRHS [] _ lines = lines generateRuleRHS _ [] lines = lines @@ -210,14 +245,26 @@ generateRuleRHS (token:rTokens) (variable@(vType, _):rVariables) lines = case to Right terminal -> generateRuleRHS rTokens (variable:rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] Left _ -> generateRuleRHS rTokens rVariables $ - lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ] - + lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ++ "," ] + buildArgument :: DartVarType -> String -> String -buildArgument (0, typeName) name = name ++ ".print" ++ "," --- TODO add correct separators from the CF -buildArgument (n, typeName) name = - "..." ++ name ++ ".expand((e" ++ show n ++ ") => [\'\', " ++ (buildArgument (n-1, typeName) ("e" ++ show n)) ++ "]).skip(1)," +buildArgument (0, _) argument = + argument ++ ".print" +buildArgument vType@(n, name) argument = + "print" ++ printerListName vType ++ "(" ++ argument ++ ")" generatePrintFunction :: String -> [String] generatePrintFunction name = [ - "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] \ No newline at end of file + "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] + +generateListPrintFunction :: DartVarType -> String +generateListPrintFunction dvt = + "String print" ++ printerListName dvt ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt ++ "(x));" + +printerListName :: DartVarType -> String +printerListName (0, name) = str2DartClassName name +printerListName (n, name) = "List" ++ (printerListName (n - 1, name)) + +printerListType :: DartVarType -> String +printerListType (0, name) = "ast." ++ (str2DartClassName name) +printerListType (n, name) = "List<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index e59218da..cb15c5e7 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -118,7 +118,10 @@ buildVariableName (_, (name, num)) = lowerFirst appendNumber -- From a DartVar make a name for the AST buildVariableType :: DartVar -> String -buildVariableType (vType, _) = unpack vType +buildVariableType (vType, _) = buildVariableTypeFromDartType vType + +buildVariableTypeFromDartType :: DartVarType -> String +buildVariableTypeFromDartType vType = unpack vType where unpack (0, name) = name unpack (n, name) = "List<" ++ unpack (n - 1, name) ++ ">" From fa14c03c0fa81723bbd79fde6e6c6721f913b559 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Sun, 18 Feb 2024 17:37:24 +0300 Subject: [PATCH 39/70] add precedence to the list printers --- source/src/BNFC/Backend/Dart.hs | 6 ++- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 51 +++++++++---------- source/src/BNFC/Backend/Dart/Common.hs | 2 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 349098e7..e18aa583 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -13,6 +13,7 @@ import Data.List ( intersperse ) import BNFC.Utils import BNFC.CF +import BNFC.Backend.Antlr ( makeAntlr ) import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Java.Utils @@ -71,8 +72,9 @@ makeDart' pkg options@Options{..} cf = do mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) mkfile (locate "builder" "dart") comment (cf2DartBuilder cf lang) mkfile (locate "pretty_printer" "dart") comment (cf2DartPrinter cf) - mkfile (locate (lang ++ "Lexer") "g4") comment lex - mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) + makeAntlr (options {dLanguage = TS, optMake = Nothing}) cf + -- mkfile (locate (lang ++ "Lexer") "g4") comment lex + -- mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) -- makebnfcfile bprettyprinter -- makebnfcfile bskel -- makebnfcfile bcompos diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 7e319ada..6df53310 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -153,11 +153,10 @@ generateLabelPrinters (cat, rawRules) = sep = findSep rules term = findTerm rules vType = cat2DartType $ normCat cat - in if sep == "" && term == "" - then [] - else [ - generateListPrettifier vType sep term, - generateListPrintFunction vType ] + precedence = precCat cat + in [ + generateListPrettifier vType precedence sep term, + generateListPrintFunction vType precedence ] else let funs = [ fst rule | rule <- rules ] in mapMaybe (generateConcreteMapping cat) rules ++ @@ -223,47 +222,47 @@ generateConcreteMapping cat (label, tokens) | otherwise = -- a standard rule let className = str2DartClassName label - cats = [ normCat cat | Left cat <- tokens ] - vars = getVars cats + cats = [ cat | Left cat <- tokens ] + vars = zip (map precCat cats) (getVars cats) in Just . unlines $ [ "IList _prettify" ++ className ++ "(ast." ++ className +++ "a) => IList([" ] ++ (indent 1 $ generateRuleRHS tokens vars []) ++ ["]);"] -generateListPrettifier :: DartVarType -> String -> String -> String -generateListPrettifier vType@(n, name) separator terminator = - "IList _prettify" ++ printerListName vType ++ "(" ++ +generateListPrettifier :: DartVarType -> Integer -> String -> String -> String +generateListPrettifier vType@(n, name) prec separator terminator = + "IList _prettify" ++ printerListName vType prec ++ "(" ++ printerListType vType +++ "a) => IList([...a.expand((e" ++ show n ++ ") => [\'" ++ separator ++ "\'," +++ - (buildArgument (n - 1, name) ("e" ++ show n)) ++ + (buildArgument (n - 1, name) prec ("e" ++ show n)) ++ "],).skip(1)," +++ "\'" ++ terminator ++ "\',]);" -generateRuleRHS :: [Either Cat String] -> [DartVar] -> [String] -> [String] +generateRuleRHS :: [Either Cat String] -> [(Integer, DartVar)] -> [String] -> [String] generateRuleRHS [] _ lines = lines generateRuleRHS _ [] lines = lines -generateRuleRHS (token:rTokens) (variable@(vType, _):rVariables) lines = case token of +generateRuleRHS (token:rTokens) ((prec, variable@(vType, _)):rVariables) lines = case token of Right terminal -> - generateRuleRHS rTokens (variable:rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] + generateRuleRHS rTokens ((prec, variable):rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] Left _ -> generateRuleRHS rTokens rVariables $ - lines ++ [ buildArgument vType ("a." ++ buildVariableName variable) ++ "," ] + lines ++ [ buildArgument vType prec ("a." ++ buildVariableName variable) ++ "," ] -buildArgument :: DartVarType -> String -> String -buildArgument (0, _) argument = - argument ++ ".print" -buildArgument vType@(n, name) argument = - "print" ++ printerListName vType ++ "(" ++ argument ++ ")" +buildArgument :: DartVarType -> Integer -> String -> String +buildArgument (0, _) prec argument = argument ++ ".print" +buildArgument vType@(n, name) prec argument = + "print" ++ printerListName vType prec ++ "(" ++ argument ++ ")" generatePrintFunction :: String -> [String] generatePrintFunction name = [ "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] -generateListPrintFunction :: DartVarType -> String -generateListPrintFunction dvt = - "String print" ++ printerListName dvt ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt ++ "(x));" +generateListPrintFunction :: DartVarType -> Integer -> String +generateListPrintFunction dvt prec = + "String print" ++ printerListName dvt prec ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt prec ++ "(x));" -printerListName :: DartVarType -> String -printerListName (0, name) = str2DartClassName name -printerListName (n, name) = "List" ++ (printerListName (n - 1, name)) +printerListName :: DartVarType -> Integer -> String +printerListName (0, name) prec = + (str2DartClassName name) ++ if prec <= 0 then "" else (show prec) +printerListName (n, name) prec = "List" ++ (printerListName (n - 1, name) prec) printerListType :: DartVarType -> String printerListType (0, name) = "ast." ++ (str2DartClassName name) diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index cb15c5e7..cb23d518 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -28,7 +28,7 @@ cat2DartType cat = toList (0, cat) where toList :: (Int, Cat) -> DartVarType toList (n, (ListCat name)) = toList (n + 1, name) - toList (n, name) = (n, (name2DartBuiltIn $ catToStr name)) + toList (n, name) = (n, (name2DartBuiltIn $ catToStr $ normCat name)) cat2DartName :: Cat -> String From 3629ec05dda064a7d2163055d6957eb21c6480a0 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Wed, 21 Feb 2024 12:54:23 +0300 Subject: [PATCH 40/70] finish mvp --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 144 +++++++++++------- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 88 ++++++----- 2 files changed, 137 insertions(+), 95 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index a1781498..9ec2c6a4 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -6,6 +6,7 @@ module BNFC.Backend.Dart.CFtoDartBuilder (cf2DartBuilder) where import BNFC.CF import BNFC.Backend.Dart.Common +import BNFC.Backend.Antlr.CFtoAntlr4Parser (makeLeftRecRule) import BNFC.Utils ( (+++) ) import Data.List ( intercalate, find ) import Data.Either ( isLeft ) @@ -20,16 +21,14 @@ cf2DartBuilder cf lang = map buildUserToken userTokens ++ concatMap generateBuilders rules where - rules = ruleGroups cf + leftRecRuleMaker = (makeLeftRecRule cf) + rules = map + (\(cat, rules) -> (cat, (map leftRecRuleMaker rules))) $ ruleGroups cf imports lang = [ "import 'package:antlr4/antlr4.dart';", "import 'ast.dart';", "import '" ++ lang ++ "Parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] helperFunctions = [ - "extension IList on List {", - " List iMap(T Function(E e) toElement) =>", - " map(toElement).toList(growable: false);", - "}", "int? buildInt(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;", "double? buildDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;", "String? buildString(Token? t) => t?.text;" ] @@ -50,55 +49,47 @@ generateBuilders (cat, rawRules) = where funs numeratedRawRules = (map (\(_, rule) -> wpThing $ funRule rule) numeratedRawRules) runtimeTypeMapping numeratedRawRules - | isList cat || - (catToStr cat) `elem` (funs numeratedRawRules) = [] -- the category is also a function or a list + | (catToStr cat) `elem` (funs numeratedRawRules) = [] -- the category is also a function or a list | otherwise = generateRuntimeTypeMapping cat [ (index, wpThing $ funRule rule, rhsRule rule) | (index, rule) <- numeratedRawRules ] --- TODO get rid of this reformating and pass the actual sturcture everywhere reformatRule :: Rule -> (String, [Cat]) reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) generateRuntimeTypeMapping :: Cat -> [(Int, String, [Either Cat String])] -> [String] generateRuntimeTypeMapping cat rules = - let astName = cat2DartClassName cat + let ctxName = cat2DartClassName cat + astName = buildVariableTypeFromDartType $ cat2DartType cat prec = precCat cat - precedencedName = astName ++ (if prec == 0 then "" else show prec) + precedencedName = ctxName ++ (if prec == 0 then "" else show prec) in [ - astName ++ "?" +++ "build" ++ precedencedName ++ "(" ++ contextName precedencedName ++ "?" +++ "ctx" ++ ") =>" + astName ++ "?" +++ "build" ++ precedencedName ++ "(" ++ (contextName precedencedName) ++ "?" +++ "ctx" ++ ") {" ] ++ indent 1 ( - [ "switch (ctx?.runtimeType) {" ] ++ - (indent 1 $ addDefaultCase $ map (buildChild precedencedName) rules) ++ - [ "};" ] - ) + (map (buildChild precedencedName) rules) ++ + ["return null;"] + ) ++ ["}"] where - -- TODO FIX make this synchronized with the parser generator - -- TODO one antlr context class may have multiple arguments from different rules - buildUniversalChild name fun arg = name +++ "c => build" ++ fun ++ "(" ++ arg ++ ")," - buildChild className (index, name, rhs) - | isNilFun name = - buildUniversalChild (contextName className ++ "_Empty") className "c" - | isOneFun name = - buildUniversalChild (contextName className ++ "_AppendLast") className "c" - | isConsFun name = - buildUniversalChild (contextName className ++ "_PrependFirst") className "c" - | isCoercion name = - let - (coercionType, ind2) = case (find (\(_, value) -> isLeft value) $ zip [1..] rhs) of - Just (i, Left cat) -> ( - let prec = precCat cat in (cat2DartClassName cat) ++ (if prec == 0 then "" else show prec), - show i ) - otherwise -> (className, "") -- error, no category for the coercion - lineIndex = show index - argument = "p_" ++ lineIndex ++ "_" ++ ind2 - in - buildUniversalChild ("Coercion_" ++ contextName (className ++ "_" ++ lineIndex)) coercionType ("c." ++ argument) - | otherwise = - buildUniversalChild (contextName $ str2AntlrClassName name) (str2DartClassName name) "c" - addDefaultCase cases = cases ++ [ "_ => null," ] + buildUniversalChild name fun arg = + "if (ctx is" +++ name ++ ") return build" ++ fun ++ "(" ++ arg ++ ");" + buildChild className (index, name, rhs) = case (antlrListSuffix name) of + "" -> if (isCoercion name) + then + let (coercionType, ind2) = case (find (\(_, value) -> isLeft value) $ zip [1..] rhs) of + Just (i, Left cat) -> ( + let prec = precCat cat in (cat2DartClassName cat) ++ (if prec == 0 then "" else show prec), + show i ) + otherwise -> (className, "") -- error, no category for the coercion + lineIndex = show index + argument = "p_" ++ lineIndex ++ "_" ++ ind2 + in + buildUniversalChild ("Coercion_" ++ contextName (className ++ "_" ++ lineIndex)) coercionType ("ctx." ++ argument) + else + buildUniversalChild (contextName $ str2AntlrClassName name) (str2DartClassName name) "ctx" + suffix -> + buildUniversalChild (contextName (className ++ "_" ++ suffix)) (className ++ suffix) "ctx" generateConcreteMapping :: Int -> Rule -> [String] @@ -108,29 +99,47 @@ generateConcreteMapping index rule = generateConcreteMappingHelper :: Int -> Rule -> (String, [Cat]) -> [String] generateConcreteMappingHelper index rule (fun, cats) - | isCoercion fun || - isNilFun fun || - isOneFun fun || - isConsFun fun = [] -- these are not represented in the ast - | otherwise = -- a standard rule + | isCoercion fun = [] + | otherwise = let - className = str2DartClassName fun - antlrContextName = contextName $ str2AntlrClassName fun + (typeName, className, ctxName) = + if (isNilFun fun || + isOneFun fun || + isConsFun fun) + then + let cat = valCat rule + prec = case (precCat cat) of + 0 -> "" + i -> show i + ctxName = (cat2DartClassName cat) ++ prec + suffix = antlrListSuffix fun + precedencedName = ctxName ++ suffix + suffixedCtxName = contextName (ctxName ++ "_" ++ suffix) + astName = buildVariableTypeFromDartType $ cat2DartType cat + in (astName, precedencedName, suffixedCtxName) + else + let name = str2DartClassName fun + ctxName = contextName $ str2AntlrClassName fun + in (name, name, ctxName) vars = getVars cats in [ - className ++ "?" +++ "build" ++ className ++ "(" ++ antlrContextName ++ "?" +++ "ctx) {" + typeName ++ "?" +++ "build" ++ className ++ "(" ++ ctxName ++ "?" +++ "ctx) {" ] ++ ( indent 1 $ (generateArguments index rule vars) ++ (generateNullCheck vars) ++ - [ "return" +++ className ++ "(" ] - ) ++ ( - indent 2 $ generateArgumentsMapping vars - ) ++ indent 1 [ - ");" - ] ++ [ + (generateReturnStatement fun vars typeName) + ) ++ [ "}" ] + where + generateReturnStatement :: Fun -> [DartVar] -> String -> [String] + generateReturnStatement fun vars typeName + | isNilFun fun = ["return [];"] + | isOneFun fun = generateOneArgumentListReturn vars + | isConsFun fun = generateTwoArgumentsListReturn vars + | otherwise = [ "return" +++ typeName ++ "(" ] ++ + (indent 1 $ generateArgumentsMapping vars ) ++ [");"] generateArguments :: Int -> Rule -> [DartVar] -> [String] @@ -155,10 +164,9 @@ traverseRule ind1 ind2 (terminal:restTerminals) (variable@(vType, _):restVariabl buildArgument prec (0, typeName) name = let precedence = if prec == 0 then "" else show prec in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" - buildArgument prec (n, typeName) name = - let nextName = "e" ++ show n - argument = buildArgument prec (n - 1, typeName) nextName - in name ++ "?.iMap((" ++ nextName ++ ") =>" +++ argument ++ ")" + buildArgument prec (_, typeName) name = + let precedence = if prec == 0 then "" else show prec + in "buildList" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" generateNullCheck :: [DartVar] -> [String] @@ -182,5 +190,27 @@ generateArgumentsMapping vars = map mapArgument vars in name ++ ":" +++ name ++ "," +generateOneArgumentListReturn :: [DartVar] -> [String] +generateOneArgumentListReturn (v:_) = + ["return [" ++ buildVariableName v ++ "];"] + + +generateTwoArgumentsListReturn :: [DartVar] -> [String] +generateTwoArgumentsListReturn (x:y:_) = + let (a, b) = putListSecond x y + in ["return [" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",];"] + where + putListSecond x@((0,_),_) y = (x, y) + putListSecond x y = (y, x) + + contextName :: String -> String contextName className = className ++ "Context" + + +antlrListSuffix :: Fun -> String +antlrListSuffix fun + | isNilFun fun = "Empty" + | isOneFun fun = "AppendLast" + | isConsFun fun = "PrependFirst" + | otherwise = "" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 6df53310..6f690f69 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -19,7 +19,7 @@ cf2DartPrinter cf = imports ++ helperFunctions ++ stringRenderer ++ - (map buildUserToken userTokens) ++ + (concatMap buildUserToken userTokens) ++ (concatMap generateRulePrinters $ getAbstractSyntax cf) ++ (concatMap generateLabelPrinters $ ruleGroups cf) @@ -54,31 +54,31 @@ stringRenderer = [ " static const _indentInSpaces = 2;", "", " String print(IList tokens) => tokens", + " .map((element) => element.trim())", " .fold(IList(), _render)", " .fold(IList<(int, IList)>(), _split)", " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))", " .fold(IList<(int, String)>(), _convertIndentation)", " .map(_addIndentation)", - " .join();", + " .join('\\n');", "", " IList<(int, IList)> _split(", " IList<(int, IList)> lists,", " Token token,", " ) =>", " switch (token) {", - " NewLine nl => lists.add(", - " (", - " nl.indentDifference,", - " IList([]),", - " ),", - " ),", - " _ => lists.put(", - " lists.length - 1,", - " (", - " lists.last.$1,", - " lists.last.$2.add(token),", - " ),", - " )", + " NewLine nl => lists.add((", + " nl.indentDifference,", + " IList([]),", + " )),", + " _ => lists.isEmpty", + " ? IList([", + " (0, IList([token]))", + " ])", + " : lists.put(", + " lists.length - 1,", + " (lists.last.$1, lists.last.$2.add(token)),", + " ),", " };", "", " String _tokenToString(Token t) => switch (t) {", @@ -91,12 +91,10 @@ stringRenderer = [ " IList<(int, String)> lines,", " (int, String) line,", " ) =>", - " lines.add(", - " (", - " line.$1 + (lines.lastOrNull?.$1 ?? 0),", - " line.$2,", - " ),", - " );", + " lines.add((", + " line.$1 + (lines.lastOrNull?.$1 ?? 0),", + " line.$2,", + " ));", "", " String _addIndentation((int, String) indentedLine) =>", " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;", @@ -104,25 +102,31 @@ stringRenderer = [ " // This function is supposed to be edited", " // in order to adjust the pretty printer behavior", " IList _render(IList tokens, String token) => switch (token) {", + " '' || ' ' => tokens,", " '{' => tokens.addAll([Text(token), NewLine.nest()]),", - " '}' => tokens.addAll([NewLine.unnest(), Text(token)]),", - " ';' => tokens.addAll([NewLine(), Text(token)]),", - " ',' ||", - " '.' ||", - " ':' ||", - " '<' ||", - " '>' ||", - " '[' ||", - " ']' ||", + " '}' => tokens.removeTrailingLines", + " .addAll([NewLine.unnest(), Text(token), NewLine()]),", + " ';' => tokens.removeTrailingSpaces.addAll([Text(token), NewLine()]),", + " ')' || ']' || '>' || ',' => tokens", + " .removeTrailingSpaces.removeTrailingLines", + " .addAll([Text(token), Space()]),", + " '\\$' ||", + " '&' ||", + " '@' ||", + " '!' ||", + " '#' ||", " '(' ||", - " ')' =>", - " tokens.removeTrailingSpaces.addAll([Text(token), Space()]),", - " '\\$' || '&' || '@' || '!' || '#' => tokens.add(Text(token)),", + " '[' ||", + " '<' ||", + " '.' =>", + " tokens.removeTrailingLines.add(Text(token)),", " _ => tokens.addAll([Text(token), Space()])", " };", "}", "", "extension TokensList on IList {", + " IList get removeTrailingLines =>", + " isNotEmpty && last is NewLine ? removeLast().removeTrailingLines : this;", " IList get removeTrailingSpaces =>", " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;", "}", @@ -141,8 +145,10 @@ stringRenderer = [ " String get print => \'[not implemented]\';", "}" ] -buildUserToken :: String -> String -buildUserToken token = "String print" ++ token ++ "(x) => x.value;" +buildUserToken :: String -> [String] +buildUserToken token = [ + "String print" ++ token ++ "(x) => x.value;", + "IList _prettify" ++ token ++ "(x) => IList([x.value]);"] generateLabelPrinters :: (Cat, [Rule]) -> [String] generateLabelPrinters (cat, rawRules) = @@ -239,7 +245,11 @@ generateListPrettifier vType@(n, name) prec separator terminator = generateRuleRHS :: [Either Cat String] -> [(Integer, DartVar)] -> [String] -> [String] generateRuleRHS [] _ lines = lines -generateRuleRHS _ [] lines = lines +generateRuleRHS (token:rTokens) [] lines = case token of + Right terminal -> + generateRuleRHS rTokens [] $ lines ++ ["\"" ++ terminal ++ "\","] + Left _ -> + generateRuleRHS rTokens [] lines generateRuleRHS (token:rTokens) ((prec, variable@(vType, _)):rVariables) lines = case token of Right terminal -> generateRuleRHS rTokens ((prec, variable):rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] @@ -247,9 +257,11 @@ generateRuleRHS (token:rTokens) ((prec, variable@(vType, _)):rVariables) lines = lines ++ [ buildArgument vType prec ("a." ++ buildVariableName variable) ++ "," ] buildArgument :: DartVarType -> Integer -> String -> String -buildArgument (0, _) prec argument = argument ++ ".print" +buildArgument (0, name) prec argument = if (censorName name) /= name + then argument ++ ".print" + else "..._prettify" ++ (str2DartClassName name) ++ "(" ++ argument ++ ")" buildArgument vType@(n, name) prec argument = - "print" ++ printerListName vType prec ++ "(" ++ argument ++ ")" + "..._prettify" ++ printerListName vType prec ++ "(" ++ argument ++ ")" generatePrintFunction :: String -> [String] generatePrintFunction name = [ From c11d327d389c9c2c7e79643595c2ef12c211ccfe Mon Sep 17 00:00:00 2001 From: xdkomel Date: Thu, 2 May 2024 00:34:43 +0300 Subject: [PATCH 41/70] add skeleton generator & use Iterable where possible --- source/BNFC.cabal | 1 + source/src/BNFC/Backend/Dart.hs | 2 + source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 8 +- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 7 +- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 20 ++--- .../src/BNFC/Backend/Dart/CFtoDartSkeleton.hs | 74 +++++++++++++++++++ source/src/BNFC/Backend/Dart/Common.hs | 10 ++- 7 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index c46a3c64..5bbdf5a6 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -260,6 +260,7 @@ library BNFC.Backend.Dart.CFtoDartBuilder BNFC.Backend.Dart.Common BNFC.Backend.Dart.CFtoDartPrinter + BNFC.Backend.Dart.CFtoDartSkeleton -- Antlr4 backend BNFC.Backend.Antlr diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index e18aa583..046f150e 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -24,6 +24,7 @@ import BNFC.Backend.Antlr.CFtoAntlr4Parser import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) +import BNFC.Backend.Dart.CFtoDartSkeleton ( cf2DartSkeleton ) import BNFC.Backend.Java.CFtoJavaPrinter15 import BNFC.Backend.Java.CFtoVisitSkel15 import BNFC.Backend.Java.CFtoComposVisitor @@ -72,6 +73,7 @@ makeDart' pkg options@Options{..} cf = do mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) mkfile (locate "builder" "dart") comment (cf2DartBuilder cf lang) mkfile (locate "pretty_printer" "dart") comment (cf2DartPrinter cf) + mkfile (locate "skeleton" "dart") comment (cf2DartSkeleton cf) makeAntlr (options {dLanguage = TS, optMake = Nothing}) cf -- mkfile (locate (lang ++ "Lexer") "g4") comment lex -- mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 86b03d22..e627b066 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -13,9 +13,7 @@ import BNFC.Utils ( (+++) ) import BNFC.Backend.Common.NamedVariables ( UserDef ) import BNFC.Backend.Dart.Common ---Produces abstract data types in Dart - - +-- Produces abstract data types in Dart cf2DartAST :: CF -> RecordPositions -> String cf2DartAST cf rp = let userTokens = [ n | (n,_) <- tokenPragmas cf ] @@ -25,7 +23,9 @@ cf2DartAST cf rp = concatMap (prData rp) rules where rules = getAbstractSyntax cf - imports = [ "import \'pretty_printer.dart\' as pp;" ] + imports = [ + "import 'pretty_printer.dart' as pp;", + "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] generateTokens :: [UserDef] -> [String] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 9ec2c6a4..4f00bc86 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -26,6 +26,7 @@ cf2DartBuilder cf lang = (\(cat, rules) -> (cat, (map leftRecRuleMaker rules))) $ ruleGroups cf imports lang = [ "import 'package:antlr4/antlr4.dart';", + "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;", "import 'ast.dart';", "import '" ++ lang ++ "Parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] helperFunctions = [ @@ -135,7 +136,7 @@ generateConcreteMappingHelper index rule (fun, cats) where generateReturnStatement :: Fun -> [DartVar] -> String -> [String] generateReturnStatement fun vars typeName - | isNilFun fun = ["return [];"] + | isNilFun fun = ["return IList();"] | isOneFun fun = generateOneArgumentListReturn vars | isConsFun fun = generateTwoArgumentsListReturn vars | otherwise = [ "return" +++ typeName ++ "(" ] ++ @@ -192,13 +193,13 @@ generateArgumentsMapping vars = map mapArgument vars generateOneArgumentListReturn :: [DartVar] -> [String] generateOneArgumentListReturn (v:_) = - ["return [" ++ buildVariableName v ++ "];"] + ["return IList([" ++ buildVariableName v ++ "]);"] generateTwoArgumentsListReturn :: [DartVar] -> [String] generateTwoArgumentsListReturn (x:y:_) = let (a, b) = putListSecond x y - in ["return [" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",];"] + in ["return IList([" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",]);"] where putListSecond x@((0,_),_) y = (x, y) putListSecond x y = (y, x) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 6f690f69..5998aeb8 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -53,7 +53,7 @@ stringRenderer = [ " // Change this value if you want to change the indentation length", " static const _indentInSpaces = 2;", "", - " String print(IList tokens) => tokens", + " String print(Iterable tokens) => tokens", " .map((element) => element.trim())", " .fold(IList(), _render)", " .fold(IList<(int, IList)>(), _split)", @@ -69,7 +69,7 @@ stringRenderer = [ " switch (token) {", " NewLine nl => lists.add((", " nl.indentDifference,", - " IList([]),", + " IList(),", " )),", " _ => lists.isEmpty", " ? IList([", @@ -148,7 +148,7 @@ stringRenderer = [ buildUserToken :: String -> [String] buildUserToken token = [ "String print" ++ token ++ "(x) => x.value;", - "IList _prettify" ++ token ++ "(x) => IList([x.value]);"] + "Iterable _prettify" ++ token ++ "(ast." ++ token +++ "x) => [x.value];"] generateLabelPrinters :: (Cat, [Rule]) -> [String] generateLabelPrinters (cat, rawRules) = @@ -212,7 +212,7 @@ generateRulePrinters (cat, rules) = generateRuntimeMapping :: String -> [String] -> [String] generateRuntimeMapping name ruleNames = [ - "IList _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ + "Iterable _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ (indent 2 $ map mapRule $ map str2DartClassName ruleNames) ++ (indent 1 [ "};" ]) where @@ -231,17 +231,17 @@ generateConcreteMapping cat (label, tokens) cats = [ cat | Left cat <- tokens ] vars = zip (map precCat cats) (getVars cats) in Just . unlines $ [ - "IList _prettify" ++ className ++ "(ast." ++ className +++ "a) => IList([" ] ++ + "Iterable _prettify" ++ className ++ "(ast." ++ className +++ "a) => [" ] ++ (indent 1 $ generateRuleRHS tokens vars []) ++ - ["]);"] + ["];"] generateListPrettifier :: DartVarType -> Integer -> String -> String -> String generateListPrettifier vType@(n, name) prec separator terminator = - "IList _prettify" ++ printerListName vType prec ++ "(" ++ - printerListType vType +++ "a) => IList([...a.expand((e" ++ show n ++ + "Iterable _prettify" ++ printerListName vType prec ++ "(" ++ + printerListType vType +++ "a) => [...a.expand((e" ++ show n ++ ") => [\'" ++ separator ++ "\'," +++ (buildArgument (n - 1, name) prec ("e" ++ show n)) ++ - "],).skip(1)," +++ "\'" ++ terminator ++ "\',]);" + "],).skip(1)," +++ "\'" ++ terminator ++ "\',];" generateRuleRHS :: [Either Cat String] -> [(Integer, DartVar)] -> [String] -> [String] generateRuleRHS [] _ lines = lines @@ -278,4 +278,4 @@ printerListName (n, name) prec = "List" ++ (printerListName (n - 1, name) prec) printerListType :: DartVarType -> String printerListType (0, name) = "ast." ++ (str2DartClassName name) -printerListType (n, name) = "List<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file +printerListType (n, name) = "Iterable<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs new file mode 100644 index 00000000..c758daf4 --- /dev/null +++ b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Dart.CFtoDartSkeleton (cf2DartSkeleton) where + +import Data.Maybe ( mapMaybe ) + +import BNFC.CF +import BNFC.Utils ( (+++) ) + +import BNFC.Backend.Common.NamedVariables ( UserDef ) +import BNFC.Backend.Dart.Common + + +cf2DartSkeleton :: CF -> String +cf2DartSkeleton cf = + unlines $ + imports + ++ identityFn + ++ (map buildUserToken [ n | (n,_) <- tokenPragmas cf ]) -- generate user-defined types + ++ (concatMap genData $ getAbstractSyntax cf) + where + imports = [ "import \'ast.dart\';" ] + identityFn = [ "A identityFn(A a) => a;" ] + +buildUserToken :: UserDef -> String +buildUserToken token = + "String interpret" ++ (censorName token) ++ "(x) => x.value;" + +genData :: Data -> [String] +genData (cat, rules) + | (catToStr cat) `elem` (map fst rules) || isList cat = [] -- the category is also a function or a list + | otherwise = + let name = cat2DartClassName cat + in [ "String interpret" ++ name ++ "(" ++ name +++ "e) => switch (e) {" ] + ++ (indent 1 $ mapMaybe genBranch rules) + ++ [ "};" ] + +genBranch :: (Fun, [Cat]) -> Maybe (String) +genBranch (fun, rhs) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing -- these are not represented in the Absyn + | otherwise = -- a standard rule + let + className = str2DartClassName fun + varName = lowerFirst $ censorName className + vars = getVars rhs + in Just $ + className +++ varName +++ "=> \"" ++ className ++ "(" + ++ (concat $ (drop 1) $ arguments (genVarRepr varName) vars) + ++ ")\"," + where + arguments _ [] = [] + arguments generator (x:vars) = + [ ", ", "${" ++ (generator x) ++ "}" ] ++ (arguments generator vars) + +genVarRepr :: String -> DartVar -> String +genVarRepr varName variable@((n, varType), _) = let + varCall = varName ++ "." ++ (buildVariableName variable) + interp = interpreter varType in + if n > 0 then + varCall ++ ".map(" ++ (unpack interp (n - 1)) ++ ")" + else + interp ++ "(" ++ varCall ++ ")" + where + unpack funName n + | n <= 0 = funName + | otherwise = let varName = "e" ++ show n in + "(" ++ varName ++ ") => " ++ varName ++ ".map(" ++ (unpack funName (n - 1)) ++ ")" + interpreter varType + | varType /= (censorName varType) = "identityFn" + | otherwise = "interpret" ++ varType diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index cb23d518..da5584f0 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -124,14 +124,16 @@ buildVariableTypeFromDartType :: DartVarType -> String buildVariableTypeFromDartType vType = unpack vType where unpack (0, name) = name - unpack (n, name) = "List<" ++ unpack (n - 1, name) ++ ">" + unpack (n, name) = "IList<" ++ unpack (n - 1, name) ++ ">" -- Prevent some type or variable name to be called as some built-in Dart type censorName :: String -> String censorName name - | (lowerFirst name) `elem` (map lowerFirst builtInTypes) = "My" ++ upperFirst name + | (lowerFirst name) `elem` (map lowerFirst builtIn) = "My" ++ upperFirst name | otherwise = name where - builtInTypes = [ "int", "double", "String", "bool", "List", "Set", "Map", - "Runes", "Symbol", "null", "Null" ] \ No newline at end of file + builtIn = [ "int", "double", "String", "bool", "List", "Set", "Map", + "Runes", "Symbol", "Record", "Future", "null", "Null", "if", "else", + "return", "throw", "try", "catch", "on", "switch", "var", "final", "sync", + "async", "for", "while", "continue", "break" ] \ No newline at end of file From e4d5c96c7989c511de71b1bda0c044743516af10 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Thu, 2 May 2024 09:20:35 +0300 Subject: [PATCH 42/70] generate project structure, undone --- .../src/BNFC/Backend/Common/NamedVariables.hs | 6 +- source/src/BNFC/Backend/Dart.hs | 776 +++--------------- source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 26 +- 3 files changed, 141 insertions(+), 667 deletions(-) diff --git a/source/src/BNFC/Backend/Common/NamedVariables.hs b/source/src/BNFC/Backend/Common/NamedVariables.hs index a76b7261..ba5e3f67 100644 --- a/source/src/BNFC/Backend/Common/NamedVariables.hs +++ b/source/src/BNFC/Backend/Common/NamedVariables.hs @@ -53,7 +53,7 @@ This is what this module does. module BNFC.Backend.Common.NamedVariables where import Control.Arrow (left, (&&&)) -import Data.Char (toLower) +import Data.Char (toLower, toUpper) import Data.Either (lefts) import Data.List (nub) import Data.Map (Map) @@ -157,3 +157,7 @@ showNum n = if n == 0 then "" else show n firstLowerCase :: String -> String firstLowerCase "" = "" firstLowerCase (a:b) = toLower a:b + +firstUpperCase :: String -> String +firstUpperCase "" = "" +firstUpperCase (a:b) = toUpper a:b \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 046f150e..510711d3 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -1,666 +1,140 @@ -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module BNFC.Backend.Dart ( makeDart ) where -import Prelude hiding ((<>)) - -import System.FilePath ((), (<.>), pathSeparator, isPathSeparator) -import System.Cmd (system) -import Data.Foldable (toList) -import Data.List ( intersperse ) +import Text.PrettyPrint ( text, vcat, render, nest ) -import BNFC.Utils -import BNFC.CF -import BNFC.Backend.Antlr ( makeAntlr ) -import BNFC.Options as Options -import BNFC.Backend.Base -import BNFC.Backend.Java.Utils -import BNFC.Backend.Java.CFtoCup15 ( cf2Cup ) -import BNFC.Backend.Java.CFtoJLex15 -import BNFC.Backend.Antlr.CFtoAntlr4Lexer -import BNFC.Backend.Antlr.CFtoAntlr4Parser +import Prelude hiding ((<>)) +import System.FilePath ((), pathSeparator) +import System.Directory ( createDirectoryIfMissing ) +import Data.Char (toLower) + +import BNFC.Backend.Base (MkFiles, mkfile,liftIO) +import BNFC.CF (CF, getAbstractSyntax) +import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Dart)) +import BNFC.Utils (mkName, NameStyle (CamelCase), replace, (+.+), (+++)) +import BNFC.Backend.Common.Makefile as MakeFile +import BNFC.Backend.Antlr (makeAntlr) import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) import BNFC.Backend.Dart.CFtoDartSkeleton ( cf2DartSkeleton ) -import BNFC.Backend.Java.CFtoJavaPrinter15 -import BNFC.Backend.Java.CFtoVisitSkel15 -import BNFC.Backend.Java.CFtoComposVisitor -import BNFC.Backend.Java.CFtoAbstractVisitor -import BNFC.Backend.Java.CFtoFoldVisitor -import BNFC.Backend.Java.CFtoAllVisitor -import BNFC.Backend.Common.NamedVariables (SymEnv, firstLowerCase) -import qualified BNFC.Backend.Common.Makefile as Makefile -import BNFC.PrettyPrint - +import BNFC.Backend.Common.NamedVariables (firstUpperCase) makeDart :: SharedOptions -> CF -> MkFiles () -makeDart opt = makeDart' pkg opt{ lang = lang' } - where - pkg = mkName javaReserved SnakeCase $ lang opt - lang' = capitalize $ mkName javaReserved CamelCase $ lang opt +makeDart opts@Options{..} cf = do + let packageBase = maybe id (+.+) inPackage pkgName + dirBase = pkgToDir packageBase + langBase = dirBase lang + libLang = langBase "lib" + srcLang = libLang "src" + libBase = dirBase "lib" + binBase = dirBase "bin" + + -- Generates files in an incorrect place + makeAntlr (opts {dLanguage = Dart, optMake = Nothing}) cf + MakeFile.mkMakefile optMake makefileContent + + mkfile (srcLang "ast.dart") makeDartComment astContent + mkfile (srcLang "builder.dart") makeDartComment builderContent + mkfile (srcLang "printer.dart") makeDartComment printerContent + mkfile (libLang "stella.dart") makeDartComment stellaExportsContent + mkfile (langBase "pubspec.yaml") makeDartCommentYaml + $ pubspecContent + lang + ("A module with the AST, Pretty-Printer and AST-builder for" +++ lang) + [] + mkfile (libBase "runner.dart") makeDartComment runnerContent + mkfile (binBase "main.dart") makeDartComment mainContent + mkfile (dirBase "pubspec.yaml" ) makeDartCommentYaml + $ pubspecContent + (lang ++ "_example") + ("A simple project for" +++ lang) + [ lang ++ ":", " path:" +++ lang ] -makeDart' :: String -> SharedOptions -> CF -> MkFiles () -makeDart' pkg options@Options{..} cf = do - -- Create the package directories if necessary. - let - packageBase = maybe id (+.+) inPackage pkg - -- packageAbsyn = packageBase +.+ "ast" - dirBase = pkgToDir packageBase - -- dirAbsyn = pkgToDir packageAbsyn - -- javaex str = dirBase str <.> "dart" - -- bnfcfiles = - -- bnfcVisitorsAndTests - -- packageBase - -- packageAbsyn - -- cf - -- cf2JavaPrinter - -- cf2VisitSkel - -- cf2ComposVisitor - -- cf2AbstractVisitor - -- cf2FoldVisitor - -- cf2AllVisitor - -- (testclass parselexspec - -- (head $ results lexmake) -- lexer class - -- (head $ results parmake) -- parser class - -- ) - -- makebnfcfile x = mkfile (javaex (fst $ x bnfcfiles)) comment - -- (snd $ x bnfcfiles) - let locate str ext = dirBase str <.> ext - (lex, env) = cf2AntlrLex "Stella" cf - mkfile (locate "ast" "dart") comment (cf2DartAST cf rp) - mkfile (locate "builder" "dart") comment (cf2DartBuilder cf lang) - mkfile (locate "pretty_printer" "dart") comment (cf2DartPrinter cf) - mkfile (locate "skeleton" "dart") comment (cf2DartSkeleton cf) - makeAntlr (options {dLanguage = TS, optMake = Nothing}) cf - -- mkfile (locate (lang ++ "Lexer") "g4") comment lex - -- mkfile (locate (lang ++ "Parser") "g4") comment (cf2AntlrParse lang cf rp env) - -- makebnfcfile bprettyprinter - -- makebnfcfile bskel - -- makebnfcfile bcompos - -- makebnfcfile babstract - -- makebnfcfile bfold - -- makebnfcfile ball - -- makebnfcfile btest - -- let (lex, env) = lexfun packageBase cf - -- -- Where the lexer file is created. lex is the content! - -- mkfile (dirBase inputfile lexmake ) commentWithEmacsModeHint lex - -- liftIO $ putStrLn $ " (Tested with" +++ toolname lexmake - -- +++ toolversion lexmake ++ ")" - -- -- where the parser file is created. - -- mkfile (dirBase inputfile parmake) commentWithEmacsModeHint - -- $ parsefun packageBase packageAbsyn cf rp env - -- liftIO $ putStrLn $ - -- if supportsEntryPoints parmake - -- then "(Parser created for all categories)" - -- else " (Parser created only for category " ++ prettyShow (firstEntry cf) ++ ")" - -- liftIO $ putStrLn $ " (Tested with" +++ toolname parmake - -- +++ toolversion parmake ++ ")" - -- Makefile.mkMakefile optMake $ - -- makefile dirBase dirAbsyn ["stella/ast.dart"] parselexspec where --- remDups [] = [] --- remDups ((a,b):as) = case lookup a as of --- Just {} -> remDups as --- Nothing -> (a, b) : remDups as - pkgToDir :: String -> FilePath + astContent = cf2DartAST cf + builderContent = cf2DartBuilder cf lang + printerContent = cf2DartPrinter cf + stellaExportsContent = unlines + [ "export 'src/ast.dart';" + , "export 'src/builder.dart';" + , "export 'src/printer.dart';" ] + runnerContent = unlines + [ "import 'package:stella/stella.dart';" + , "class Runner {" + , "}" ] + mainContent = unlines + [ "import '../lib/runner.dart'" + , "void main(List args) {" + , " final runner = Runner();" + , " runner.run();" + , "}" ] + pkgName = mkName [] SnakeCase lang pkgToDir = replace '.' pathSeparator --- parselexspec = parserLexerSelector lang Antlr4 rp --- lexfun = cf2lex $ lexer parselexspec --- parsefun = cf2parse $ parser parselexspec --- parmake = makeparserdetails (parser parselexspec) --- lexmake = makelexerdetails (lexer parselexspec) - rp = (Options.linenumbers options) --- commentWithEmacsModeHint = comment . ("-*- Java -*- " ++) - --- makefile :: FilePath -> FilePath -> [String] -> ParserLexerSpecification -> String -> Doc --- makefile dirBase dirAbsyn absynFileNames jlexpar basename = vcat $ --- makeVars [ ("JAVAC", "javac"), --- ("JAVAC_FLAGS", "-sourcepath ."), --- ( "JAVA", "java"), --- ( "JAVA_FLAGS", ""), --- -- parser executable --- ( "PARSER", executable parmake), --- -- parser flags --- ( "PARSER_FLAGS", flags parmake dirBase), --- -- lexer executable (and flags?) --- ( "LEXER", executable lexmake), --- ( "LEXER_FLAGS", flags lexmake dirBase) --- ] --- ++ --- makeRules [ ("all", [ "test" ], []), --- ( "test", "absyn" : classes, []), --- ( ".PHONY", ["absyn"], []), --- ("%.class", [ "%.java" ], [ runJavac "$^" ]), --- ("absyn", [absynJavaSrc],[ runJavac "$^" ]) --- ]++ --- [-- running the lexergen: output of lexer -> input of lexer : calls lexer --- let ff = filename lexmake -- name of input file without extension --- dirBaseff = dirBase ff -- prepend directory --- inp = dirBase inputfile lexmake in --- Makefile.mkRule (dirBaseff <.> "java") [ inp ] --- [ "${LEXER} ${LEXER_FLAGS} "++ inp ] - --- -- running the parsergen, these there are its outputs --- -- output of parser -> input of parser : calls parser --- , let inp = dirBase inputfile parmake in --- Makefile.mkRule (unwords (map (dirBase ) (dotJava $ results parmake))) --- [ inp ] $ --- ("${PARSER} ${PARSER_FLAGS} " ++ inp) : --- ["mv " ++ unwords (dotJava $ results parmake) +++ dirBase ++ [pathSeparator] --- | moveresults parmake] --- -- Class of the output of lexer generator wants java of : --- -- output of lexer and parser generator --- , let lexerOutClass = dirBase filename lexmake <.> "class" --- outname x = dirBase x <.> "java" --- deps = map outname (results lexmake ++ results parmake) in --- Makefile.mkRule lexerOutClass deps [] --- ]++ --- reverse [Makefile.mkRule tar dep [] | --- (tar,dep) <- partialParserGoals dirBase (results parmake)] --- ++[ Makefile.mkRule (dirBase "PrettyPrinter.class") --- [ dirBase "PrettyPrinter.java" ] [] --- -- Removes all the class files created anywhere --- , Makefile.mkRule "clean" [] [ "rm -f " ++ dirAbsyn "*.class" ++ " " --- ++ dirBase "*.class" ] --- -- Remains the same --- , Makefile.mkRule "distclean" [ "vclean" ] [] --- -- removes everything --- , Makefile.mkRule "vclean" [] --- [ " rm -f " ++ absynJavaSrc ++ " " ++ absynJavaClass --- , " rm -f " ++ dirAbsyn "*.class" --- , " rmdir " ++ dirAbsyn --- , " rm -f " ++ unwords (map (dirBase ) $ --- [ inputfile lexmake --- , inputfile parmake --- ] --- ++ dotJava (results lexmake) --- ++ [ "VisitSkel.java" --- , "ComposVisitor.java" --- , "AbstractVisitor.java" --- , "FoldVisitor.java" --- , "AllVisitor.java" --- , "PrettyPrinter.java" --- , "Skeleton.java" --- , "Test.java" --- ] --- ++ dotJava (results parmake) --- ++ ["*.class"] --- ++ other_results lexmake --- ++ other_results parmake) --- , " rm -f " ++ basename --- , " rmdir -p " ++ dirBase --- ] --- ] --- where --- makeVars x = [Makefile.mkVar n v | (n,v) <- x] --- makeRules x = [Makefile.mkRule tar dep recipe | (tar, dep, recipe) <- x] --- parmake = makeparserdetails (parser jlexpar) --- lexmake = makelexerdetails (lexer jlexpar) --- absynJavaSrc = unwords (dotJava absynFileNames) --- absynJavaClass = unwords (dotClass absynFileNames) --- classes = map (dirBase ) lst --- lst = dotClass (results lexmake) ++ [ "PrettyPrinter.class", "Test.class" --- , "VisitSkel.class" --- , "ComposVisitor.class", "AbstractVisitor.class" --- , "FoldVisitor.class", "AllVisitor.class"]++ --- dotClass (results parmake) ++ ["Test.class"] - --- type TestClass = String --- -- ^ class of the lexer --- -> String --- -- ^ class of the parser --- -> String --- -- ^ package where the non-abstract syntax classes are created --- -> String --- -- ^ package where the abstract syntax classes are created --- -> CF --- -- ^ the CF bundle --- -> String - --- -- | Record to name arguments of 'javaTest'. --- data JavaTestParams = JavaTestParams --- { jtpImports :: [Doc] --- -- ^ List of imported packages. --- , jtpErr :: String --- -- ^ Name of the exception thrown in case of parsing failure. --- , jtpErrHand :: (String -> [Doc]) --- -- ^ Handler for the exception thrown. --- , jtpLexerConstruction :: (Doc -> Doc -> Doc) --- -- ^ Function formulating the construction of the lexer object. --- , jtpParserConstruction :: (Doc -> Doc -> Doc) --- -- ^ As above, for parser object. --- , jtpShowAlternatives :: ([Cat] -> [Doc]) --- -- ^ Pretty-print the names of the methods corresponding to entry points to the user. --- , jtpInvocation :: (Doc -> Doc -> Doc -> Doc -> Doc) --- -- ^ Function formulating the invocation of the parser tool within Java. --- , jtpErrMsg :: String --- -- ^ Error string output in consequence of a parsing failure. --- } - --- -- | Test class details for J(F)Lex + CUP --- cuptest :: TestClass --- cuptest = javaTest $ JavaTestParams --- { jtpImports = ["java_cup.runtime"] --- , jtpErr = "Throwable" --- , jtpErrHand = const [] --- , jtpLexerConstruction = \ x i -> x <> i <> ";" --- , jtpParserConstruction = \ x i -> x <> "(" <> i <> ", " <> i <> ".getSymbolFactory());" --- , jtpShowAlternatives = const $ ["not available."] --- , jtpInvocation = \ _ pabs dat enti -> hcat [ pabs, ".", dat, " ast = p.p", enti, "();" ] --- , jtpErrMsg = unwords $ --- [ "At line \" + String.valueOf(t.l.line_num()) + \"," --- , "near \\\"\" + t.l.buff() + \"\\\" :" --- ] --- } - --- -- | Test class details for ANTLR4 --- antlrtest :: TestClass --- antlrtest = javaTest $ JavaTestParams --- { jtpImports = --- [ "org.antlr.v4.runtime" --- , "org.antlr.v4.runtime.atn" --- , "org.antlr.v4.runtime.dfa" --- , "java.util" --- ] --- , jtpErr = --- "TestError" --- , jtpErrHand = --- antlrErrorHandling --- , jtpLexerConstruction = --- \ x i -> vcat --- [ x <> "(new ANTLRInputStream" <> i <>");" --- , "l.addErrorListener(new BNFCErrorListener());" --- ] --- , jtpParserConstruction = --- \ x i -> vcat --- [ x <> "(new CommonTokenStream(" <> i <>"));" --- , "p.addErrorListener(new BNFCErrorListener());" --- ] --- , jtpShowAlternatives = --- showOpts --- , jtpInvocation = --- \ pbase pabs dat enti -> vcat --- [ --- let rulename = getRuleName $ startSymbol $ render enti --- typename = text rulename --- methodname = text $ firstLowerCase rulename --- in --- pbase <> "." <> typename <> "Context pc = p." <> methodname <> "();" --- , pabs <> "." <> dat <+> "ast = pc.result;" --- ] --- , jtpErrMsg = --- "At line \" + e.line + \", column \" + e.column + \" :" --- } --- where --- showOpts [] = [] --- showOpts (x:xs) --- | normCat x /= x = showOpts xs --- | otherwise = text (firstLowerCase $ identCat x) : showOpts xs - --- parserLexerSelector :: --- String --- -> JavaLexerParser --- -> RecordPositions -- ^Pass line numbers to the symbols --- -> ParserLexerSpecification --- parserLexerSelector _ JLexCup rp = ParseLexSpec --- { lexer = cf2JLex rp --- , parser = cf2cup rp --- , testclass = cuptest --- } --- parserLexerSelector _ JFlexCup rp = --- (parserLexerSelector "" JLexCup rp){lexer = cf2JFlex rp} --- parserLexerSelector l Antlr4 _ = ParseLexSpec --- { lexer = cf2AntlrLex' l --- , parser = cf2AntlrParse' l --- , testclass = antlrtest --- } - --- data ParserLexerSpecification = ParseLexSpec --- { parser :: CFToParser --- , lexer :: CFToLexer --- , testclass :: TestClass --- } - --- -- |CF -> LEXER GENERATION TOOL BRIDGE --- -- | function translating the CF to an appropriate lexer generation tool. --- type CF2LexerFunction = String -> CF -> (Doc, SymEnv) - --- -- Chooses the translation from CF to the lexer --- data CFToLexer = CF2Lex --- { cf2lex :: CF2LexerFunction --- , makelexerdetails :: MakeFileDetails --- } - --- -- | Instances of cf-lexergen bridges - --- cf2JLex :: RecordPositions -> CFToLexer --- cf2JLex rp = CF2Lex --- { cf2lex = cf2jlex JLexCup rp --- , makelexerdetails = jlexmakedetails --- } - --- cf2JFlex :: RecordPositions -> CFToLexer --- cf2JFlex rp = CF2Lex --- { cf2lex = cf2jlex JFlexCup rp --- , makelexerdetails = jflexmakedetails --- } - --- cf2AntlrLex' :: String -> CFToLexer --- cf2AntlrLex' l = CF2Lex --- { cf2lex = const $ cf2AntlrLex l --- , makelexerdetails = antlrmakedetails $ l ++ "Lexer" --- } - --- -- | CF -> PARSER GENERATION TOOL BRIDGE --- -- | function translating the CF to an appropriate parser generation tool. --- type CF2ParserFunction = String -> String -> CF -> RecordPositions -> SymEnv -> String - --- -- | Chooses the translation from CF to the parser --- data CFToParser = CF2Parse --- { cf2parse :: CF2ParserFunction --- , makeparserdetails :: MakeFileDetails --- } - --- -- | Instances of cf-parsergen bridges --- cf2cup :: RecordPositions -> CFToParser --- cf2cup rp = CF2Parse --- { cf2parse = cf2Cup --- , makeparserdetails = cupmakedetails rp --- } - --- cf2AntlrParse' :: String -> CFToParser --- cf2AntlrParse' l = CF2Parse --- { cf2parse = const $ cf2AntlrParse l --- , makeparserdetails = antlrmakedetails $ l ++ "Parser" --- } - - --- -- | shorthand for Makefile command running javac or java --- runJavac , runJava:: String -> String --- runJava = mkRunProgram "JAVA" --- runJavac = mkRunProgram "JAVAC" - --- -- | function returning a string executing a program contained in a variable j --- -- on input s --- mkRunProgram :: String -> String -> String --- mkRunProgram j s = Makefile.refVar j +++ Makefile.refVar (j +-+ "FLAGS") +++ s - --- type OutputDirectory = String - --- -- | Makefile details from running the parser-lexer generation tools. --- data MakeFileDetails = MakeDetails --- { -- | The string that executes the generation tool. --- executable :: String --- , -- | Flags to pass to the tool. --- flags :: OutputDirectory -> String --- , -- | Input file to the tool. --- filename :: String --- , -- | Extension of input file to the tool. --- fileextension :: String --- , -- | Name of the tool. --- toolname :: String --- , -- | Tool version. --- toolversion :: String --- , -- | True if the tool is a parser and supports entry points, --- -- False otherwise. --- supportsEntryPoints :: Bool --- , -- | List of names (without extension!) of files resulting from the --- -- application of the tool which are relevant to a make rule. --- results :: [String] --- , -- | List of names of files resulting from the application of --- -- the tool which are irrelevant to the make rules but need to be cleaned. --- other_results :: [String] --- , -- | If True, the files are moved to the base directory, otherwise --- -- they are left where they are. --- moveresults :: Bool --- } - - --- -- Instances of makefile details. - --- jlexmakedetails :: MakeFileDetails --- jlexmakedetails = MakeDetails --- { executable = runJava "JLex.Main" --- , flags = const "" --- , filename = "Yylex" --- , fileextension = "" --- , toolname = "JLex" --- , toolversion = "1.2.6" --- , supportsEntryPoints = False --- , results = ["Yylex"] --- , other_results = [] --- , moveresults = False --- } - --- jflexmakedetails :: MakeFileDetails --- jflexmakedetails = jlexmakedetails --- { executable = "jflex" --- , toolname = "JFlex" --- , toolversion = "1.4.3 - 1.9.1" --- } - --- cupmakedetails :: RecordPositions -> MakeFileDetails --- cupmakedetails rp = MakeDetails --- { executable = runJava "java_cup.Main" --- , flags = const (lnFlags ++ " -expect 100") --- , filename = "_cup" --- , fileextension = "cup" --- , toolname = "CUP" --- , toolversion = "0.11b" --- , supportsEntryPoints = False --- , results = ["parser", "sym"] --- , other_results = [] --- , moveresults = True --- } --- where --- lnFlags = if rp == RecordPositions then "-locations" else "-nopositions" - - --- antlrmakedetails :: String -> MakeFileDetails --- antlrmakedetails l = MakeDetails --- { executable = runJava "org.antlr.v4.Tool" --- , flags = \ path -> unwords $ --- let pointed = map cnv path --- cnv y = if isPathSeparator y --- then '.' --- else y --- in [ "-lib", path --- , "-package", pointed] --- , filename = l --- , fileextension = "g4" --- , toolname = "ANTLRv4" --- , toolversion = "4.9" --- , supportsEntryPoints = True --- , results = [l] --- , other_results = map (l ++) --- [ ".interp" -- added after ANTLR 4.5 --- , ".tokens" --- , "BaseListener.java" --- ,"Listener.java" --- ] --- , moveresults = False --- } - --- dotJava :: [String] -> [String] --- dotJava = map (<.> "java") - --- dotClass :: [String] -> [String] --- dotClass = map (<.> "class") - --- type CFToJava = String -> String -> CF -> String - --- -- | Contains the pairs filename/content for all the non-abstract syntax files --- -- generated by BNFC. --- data BNFCGeneratedEntities = BNFCGenerated --- { bprettyprinter :: (String, String) --- , btest :: (String, String) --- , bcompos :: (String, String) --- , babstract :: (String, String) --- , bfold :: (String, String) --- , ball :: (String, String) --- , bskel :: (String, String) --- } - --- bnfcVisitorsAndTests :: String -> String -> CF -> --- CFToJava -> CFToJava -> CFToJava -> --- CFToJava -> CFToJava -> CFToJava -> --- CFToJava -> BNFCGeneratedEntities --- bnfcVisitorsAndTests pbase pabsyn cf cf0 cf1 cf2 cf3 cf4 cf5 cf6 = --- BNFCGenerated --- { bprettyprinter = ( "PrettyPrinter" , app cf0) --- , bskel = ( "VisitSkel", app cf1) --- , bcompos = ( "ComposVisitor" , app cf2) --- , babstract = ( "AbstractVisitor" , app cf3) --- , bfold = ( "FoldVisitor", app cf4) --- , ball = ( "AllVisitor", app cf5) --- , btest = ( "Test" , app cf6) --- } --- where app x = x pbase pabsyn cf - --- inputfile :: MakeFileDetails -> String --- inputfile x --- | null (fileextension x) = filename x --- | otherwise = filename x <.> fileextension x - --- -- | constructs the rules regarding the parser in the makefile --- partialParserGoals :: String -> [String] -> [(String, [String])] --- partialParserGoals _ [] = [] --- partialParserGoals dirBase (x:rest) = --- (dirBase x <.> "class", map (\ y -> dirBase y <.> "java") (x:rest)) --- : partialParserGoals dirBase rest - --- -- | Creates the Test.java class. --- javaTest :: JavaTestParams -> TestClass --- javaTest (JavaTestParams --- imports --- err --- errhand --- lexerconstruction --- parserconstruction --- showOpts --- invocation --- errmsg) --- lexer --- parser --- packageBase --- packageAbsyn --- cf = --- render $ vcat $ concat $ --- [ [ "package" <+> text packageBase <> ";" --- , "" --- , "import" <+> text packageBase <> ".*;" --- , "import java.io.*;" --- ] --- , map importfun imports --- , [ "" ] --- , errhand err --- , [ "" --- , "public class Test" --- , codeblock 2 --- [ lx <+> "l;" --- , px <+> "p;" --- , "" --- , "public Test(String[] args)" --- , codeblock 2 --- [ "try" --- , codeblock 2 --- [ "Reader input;" --- , "if (args.length == 0) input = new InputStreamReader(System.in);" --- , "else input = new FileReader(args[0]);" --- , "l = new " <> lexerconstruction lx "(input)" --- ] --- , "catch(IOException e)" --- , codeblock 2 --- [ "System.err.println(\"Error: File not found: \" + args[0]);" --- , "System.exit(1);" --- ] --- , "p = new "<> parserconstruction px "l" --- ] --- , "" --- , "public" <+> text packageAbsyn <> "." <> dat --- <+> "parse() throws Exception" --- , codeblock 2 $ concat --- [ [ "/* The default parser is the first-defined entry point. */" ] --- , unlessNull (drop 1 eps) $ \ eps' -> --- [ "/* Other options are: */" --- , "/* " <> fsep (punctuate "," (showOpts eps')) <> " */" --- ] --- , [ invocation px (text packageAbsyn) dat absentity --- , printOuts --- [ "\"Parse Successful!\"" --- , "\"[Abstract Syntax]\"" --- , "PrettyPrinter.show(ast)" --- , "\"[Linearized Tree]\"" --- , "PrettyPrinter.print(ast)" --- ] --- , "return ast;" --- ] --- ] --- , "" --- , "public static void main(String args[]) throws Exception" --- , codeblock 2 --- [ "Test t = new Test(args);" --- , "try" --- , codeblock 2 [ "t.parse();" ] --- ,"catch(" <> text err <+> "e)" --- , codeblock 2 --- [ "System.err.println(\"" <> text errmsg <> "\");" --- , "System.err.println(\" \" + e.getMessage());" --- , "System.exit(1);" --- ] --- ] --- ] --- ] --- ] --- where --- printOuts x = vcat $ map javaPrintOut (messages x) --- messages x = "" : intersperse "" x --- javaPrintOut x = text $ "System.out.println(" ++ x ++ ");" --- importfun x = "import" <+> x <> ".*;" --- lx = text lexer --- px = text parser --- dat = text $ identCat $ normCat def -- Use for AST types. --- absentity = text $ identCat def -- Use for parser/printer name. --- eps = toList $ allEntryPoints cf --- def = head eps - --- -- | Error handling in ANTLR. --- -- By default, ANTLR does not stop after any parsing error and attempts to go --- -- on, delivering what it has been able to parse. --- -- It does not throw any exception, unlike J(F)lex+CUP. --- -- The below code makes the test class behave as with J(F)lex+CUP. --- antlrErrorHandling :: String -> [Doc] --- antlrErrorHandling te = --- [ "class"<+>tedoc<+>"extends RuntimeException" --- , codeblock 2 [ "int line;" --- , "int column;" --- , "public"<+>tedoc<>"(String msg, int l, int c)" --- , codeblock 2 [ "super(msg);" --- , "line = l;" --- , "column = c;" --- ] --- ] --- , "class BNFCErrorListener implements ANTLRErrorListener" --- , codeblock 2 [ "@Override" --- , "public void syntaxError(Recognizer recognizer, Object o, int i" --- <> ", int i1, String s, RecognitionException e)" --- , codeblock 2 [ "throw new"<+>tedoc<>"(s,i,i1);"] --- , "@Override" --- , "public void reportAmbiguity(Parser parser, DFA dfa, int i, int i1, " --- <>"boolean b, BitSet bitSet, ATNConfigSet atnConfigSet)" --- , codeblock 2[ "throw new"<+>tedoc<>"(\"Ambiguity at\",i,i1);" ] --- , "@Override" --- , "public void reportAttemptingFullContext(Parser parser, DFA dfa, " --- <>"int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet)" --- , codeblock 2 [] --- , "@Override" --- ,"public void reportContextSensitivity(Parser parser, DFA dfa, int i, " --- <>"int i1, int i2, ATNConfigSet atnConfigSet)" --- ,codeblock 2 [] --- ] --- ] --- where tedoc = text te + pubspecContent moduleName desc deps = unlines + ([ "name:" +++ moduleName + , "description:" +++ desc + , "version: 1.0.0" + , "publish_to: 'none'" + , "environment:" + , " sdk: ^3.3.4" + , "dependencies:" + , " antlr4: ^4.13.1" + , " fast_immutable_collections: ^10.2.2" + ] ++ (map (" " ++) deps) ++ [ "dev_dependencies:" + , " lints: ^3.0.0" ]) + + lexerClassName = lang ++ "GrammarLexer" + parserClassName = lang ++ "GrammarParser" + + makeVars x = [MakeFile.mkVar n v | (n,v) <- x] + makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] + + makefileVars = vcat $ makeVars + [("LANG", lang) + , ("LEXER_NAME", lang ++ "Lexer") + , ("PARSER_NAME", lang ++ "Parser") + , ("ANTLR4", "java org.antlr.v4.Tool") + ] + + refVarWithPrefix :: String -> String + refVarWithPrefix refVar = MakeFile.refVar "LANG" MakeFile.refVar refVar + + rmFile :: String -> String -> String + rmFile refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext + + makefileRules = vcat $ makeRules + [ (".PHONY", ["all", "clean", "remove"], []) + , ("all", [MakeFile.refVar "LANG"], []) + , ("lexer", [refVarWithPrefix "LEXER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "LEXER_NAME" ++ ".g4"]) + , ("parser", [refVarWithPrefix "PARSER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "PARSER_NAME" ++ ".g4"]) + -- , ("install-deps", [MakeFile.refVar "LANG" "package.json"], ["npm --prefix ./" ++ MakeFile.refVar "LANG" +++ "install" +++ MakeFile.refVar "LANG"]) + -- , ("init-ts-project", [MakeFile.refVar "LANG" "package.json"], ["cd" +++ MakeFile.refVar "LANG" +++ "&& npm run init" ]) + , (MakeFile.refVar "LANG", ["lexer", "parser", "install-deps", "init-ts-project"], []) + , ("clean", [], + [ + -- "rm -rf" +++ MakeFile.refVar "LANG" "node_modules" + -- , + rmFile "LEXER_NAME" ".interp" + , rmFile "LEXER_NAME" ".tokens" + , rmFile "PARSER_NAME" ".interp" + , rmFile "PARSER_NAME" ".tokens" + , rmFile "LEXER_NAME" ".dart" + , rmFile "PARSER_NAME" ".dart" + , rmFile "PARSER_NAME" "Listener.dart" + ]) + , ("remove", [], ["rm -rf" +++ MakeFile.refVar "LANG"]) + ] + + makefileContent _ = vcat [makefileVars, "", makefileRules, ""] + +makeDartComment :: String -> String +makeDartComment = ("// Dart " ++) + +makeDartCommentYaml :: String -> String +makeDartCommentYaml = ("# Dart" ++) + +toLowerCase :: String -> String +toLowerCase = map toLower diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index e627b066..2f6bcc91 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -7,20 +7,19 @@ module BNFC.Backend.Dart.CFtoDartAST (cf2DartAST) where import Data.Maybe ( mapMaybe ) import BNFC.CF -import BNFC.Options ( RecordPositions(..) ) import BNFC.Utils ( (+++) ) import BNFC.Backend.Common.NamedVariables ( UserDef ) import BNFC.Backend.Dart.Common -- Produces abstract data types in Dart -cf2DartAST :: CF -> RecordPositions -> String -cf2DartAST cf rp = +cf2DartAST :: CF -> String +cf2DartAST cf = let userTokens = [ n | (n,_) <- tokenPragmas cf ] in unlines $ imports ++ -- import some libraries if needed generateTokens userTokens ++ -- generate user-defined types - concatMap (prData rp) rules + concatMap prData rules where rules = getAbstractSyntax cf imports = [ @@ -45,9 +44,9 @@ generateTokens tokens = map toClass tokens -- | Generates a category class, and classes for all its rules. -prData :: RecordPositions -> Data -> [String] -prData rp (cat, rules) = - categoryClass ++ mapMaybe (prRule rp cat) rules +prData :: Data -> [String] +prData (cat, rules) = + categoryClass ++ mapMaybe (prRule cat) rules where funs = map fst rules categoryClass @@ -62,8 +61,8 @@ prData rp (cat, rules) = -- | Generates classes for a rule, depending on what type of rule it is. -prRule :: RecordPositions -> Cat -> (Fun, [Cat]) -> Maybe (String) -prRule rp cat (fun, cats) +prRule :: Cat -> (Fun, [Cat]) -> Maybe (String) +prRule cat (fun, cats) | isNilFun fun || isOneFun fun || isConsFun fun = Nothing -- these are not represented in the Absyn @@ -74,7 +73,7 @@ prRule rp cat (fun, cats) in Just . unlines $ [ unwords [ "class", className, extending, "with pp.Printable {" ] ] ++ concatMap (indent 1) [ - prInstanceVariables rp vars, + prInstanceVariables vars, prConstructor className vars, prEquals className vars, prHashCode vars, @@ -119,12 +118,9 @@ prHashCode vars = [ -- Generate variable definitions for the class -prInstanceVariables :: RecordPositions -> [DartVar] -> [String] -prInstanceVariables rp vars = case rp of - RecordPositions -> ["int? line_num, col_num, offset;"] ++ generateVariables - NoRecordPositions -> generateVariables +prInstanceVariables :: [DartVar] -> [String] +prInstanceVariables vars = map variableLine vars where - generateVariables = map variableLine vars variableLine variable = let vType = buildVariableType variable vName = buildVariableName variable From 98898ce5096d768a54740b2cf16e4a08e1fda7c6 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Thu, 2 May 2024 09:29:36 +0300 Subject: [PATCH 43/70] add skeleton and makefile --- source/src/BNFC/Backend/Dart.hs | 12 +++++------- source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 510711d3..35e90ebf 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -12,14 +12,13 @@ import Data.Char (toLower) import BNFC.Backend.Base (MkFiles, mkfile,liftIO) import BNFC.CF (CF, getAbstractSyntax) import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Dart)) -import BNFC.Utils (mkName, NameStyle (CamelCase), replace, (+.+), (+++)) +import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) import BNFC.Backend.Common.Makefile as MakeFile import BNFC.Backend.Antlr (makeAntlr) import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) import BNFC.Backend.Dart.CFtoDartSkeleton ( cf2DartSkeleton ) -import BNFC.Backend.Common.NamedVariables (firstUpperCase) makeDart :: SharedOptions -> CF -> MkFiles () makeDart opts@Options{..} cf = do @@ -45,6 +44,7 @@ makeDart opts@Options{..} cf = do ("A module with the AST, Pretty-Printer and AST-builder for" +++ lang) [] mkfile (libBase "runner.dart") makeDartComment runnerContent + mkfile (libBase "skeleton.dart") makeDartComment skeletonContent mkfile (binBase "main.dart") makeDartComment mainContent mkfile (dirBase "pubspec.yaml" ) makeDartCommentYaml $ pubspecContent @@ -56,6 +56,7 @@ makeDart opts@Options{..} cf = do astContent = cf2DartAST cf builderContent = cf2DartBuilder cf lang printerContent = cf2DartPrinter cf + skeletonContent = cf2DartSkeleton cf stellaExportsContent = unlines [ "export 'src/ast.dart';" , "export 'src/builder.dart';" @@ -110,13 +111,10 @@ makeDart opts@Options{..} cf = do , ("all", [MakeFile.refVar "LANG"], []) , ("lexer", [refVarWithPrefix "LEXER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "LEXER_NAME" ++ ".g4"]) , ("parser", [refVarWithPrefix "PARSER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "PARSER_NAME" ++ ".g4"]) - -- , ("install-deps", [MakeFile.refVar "LANG" "package.json"], ["npm --prefix ./" ++ MakeFile.refVar "LANG" +++ "install" +++ MakeFile.refVar "LANG"]) - -- , ("init-ts-project", [MakeFile.refVar "LANG" "package.json"], ["cd" +++ MakeFile.refVar "LANG" +++ "&& npm run init" ]) - , (MakeFile.refVar "LANG", ["lexer", "parser", "install-deps", "init-ts-project"], []) + , ("install-deps", [MakeFile.refVar "LANG" "pubspec.yaml"], ["cd" +++ (MakeFile.refVar "LANG") ++ "; dart pub get"]) + , (MakeFile.refVar "LANG", ["lexer", "parser", "install-deps"], []) , ("clean", [], [ - -- "rm -rf" +++ MakeFile.refVar "LANG" "node_modules" - -- , rmFile "LEXER_NAME" ".interp" , rmFile "LEXER_NAME" ".tokens" , rmFile "PARSER_NAME" ".interp" diff --git a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs index c758daf4..14e5d0d9 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs @@ -21,7 +21,7 @@ cf2DartSkeleton cf = ++ (map buildUserToken [ n | (n,_) <- tokenPragmas cf ]) -- generate user-defined types ++ (concatMap genData $ getAbstractSyntax cf) where - imports = [ "import \'ast.dart\';" ] + imports = [ "import 'package:stella/stella.dart';" ] identityFn = [ "A identityFn(A a) => a;" ] buildUserToken :: UserDef -> String From 77ad7e2df24205412212cf793c6f7f5c7c5fbec0 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Tue, 21 May 2024 10:22:13 +0300 Subject: [PATCH 44/70] add naming and diretory options to antlr --- source/src/BNFC/Backend/Antlr.hs | 46 +++++++++++++------ .../src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs | 8 ++-- .../BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 8 ++-- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr.hs b/source/src/BNFC/Backend/Antlr.hs index 45f31597..b83f661a 100644 --- a/source/src/BNFC/Backend/Antlr.hs +++ b/source/src/BNFC/Backend/Antlr.hs @@ -1,10 +1,11 @@ {-# LANGUAGE RecordWildCards #-} -module BNFC.Backend.Antlr ( makeAntlr ) where +module BNFC.Backend.Antlr ( makeAntlr, makeAntlr', DirectoryOptions(..) ) where import Prelude hiding ((<>)) import System.FilePath ((), pathSeparator, (<.>)) import Text.PrettyPrint.HughesPJ (vcat) +import Data.Maybe (fromMaybe) import BNFC.Utils ( NameStyle(CamelCase), @@ -21,23 +22,36 @@ import BNFC.Backend.Antlr.Utils (dotG4, getAntlrOptions) import BNFC.Backend.Common.Makefile as MakeFile ( mkMakefile, mkVar, mkRule, refVar ) +data DirectoryOptions = DirectoryOptions + { baseDirectory :: Maybe String + , nameStyle :: Maybe NameStyle } + makeAntlr :: SharedOptions -> CF -> MkFiles () -makeAntlr opts@Options{..} cf = do +makeAntlr opts cf = makeAntlr' opts cf DirectoryOptions { + baseDirectory=Nothing + , nameStyle=Nothing } + +makeAntlr' :: SharedOptions -> CF -> DirectoryOptions -> MkFiles () +makeAntlr' opts@Options{..} cf DirectoryOptions{..} = do let packageBase = maybe id (+.+) inPackage pkg - dirBase = pkgToDir packageBase + dirBase = fromMaybe (pkgToDir packageBase) baseDirectory - let (lex, env) = cf2AntlrLex packageBase cf + let lexerName = mkFilename "Lexer" + lexerFile = dotG4 lexerName + (lex, env) = cf2AntlrLex lexerName cf -- Where the lexer file is created. lex is the content! - mkfile (dirBase mkG4Filename "Lexer") mkAntlrComment lex + mkfile (dirBase lexerFile) mkAntlrComment lex - let parserContent = cf2AntlrParse packageBase cf linenumbers env - mkfile (dirBase mkG4Filename "Parser") mkAntlrComment parserContent + let parserName = mkFilename "Parser" + parserFile = dotG4 parserName + parserContent = cf2AntlrParse lexerName parserName cf linenumbers env + mkfile (dirBase parserFile) mkAntlrComment parserContent MakeFile.mkMakefile optMake makefileContent where - pkg = mkName [] CamelCase lang + pkg = mkName [] (fromMaybe CamelCase nameStyle) lang pkgToDir = replace '.' pathSeparator - mkG4Filename = dotG4 . (pkg ++) + mkFilename ending = mkName [] (fromMaybe CamelCase nameStyle) (pkg ++ ending) makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] @@ -61,12 +75,14 @@ makeAntlr opts@Options{..} cf = do genAntlrRecipe = ((MakeFile.refVar "ANTLR4" +++ MakeFile.refVar "ANTLR_OPTIONS" +++ MakeFile.refVar "DIRECT_OPTIONS") +++) . MakeFile.refVar - antlrFiles = map (langRef ) - [ mkName [] CamelCase (pkg +++ "Lexer") <.> "interp" - , mkName [] CamelCase (pkg +++ "Parser") <.> "interp" - , mkName [] CamelCase (pkg +++ "Lexer") <.> "tokens" - , mkName [] CamelCase (pkg +++ "Parser") <.> "tokens" - ] + antlrFiles = + let ns = fromMaybe CamelCase nameStyle + in map (langRef ) + [ mkName [] ns (pkg +++ "Lexer") <.> "interp" + , mkName [] ns (pkg +++ "Parser") <.> "interp" + , mkName [] ns (pkg +++ "Lexer") <.> "tokens" + , mkName [] ns (pkg +++ "Parser") <.> "tokens" + ] makefileRules = vcat $ makeRules [ (".PHONY", ["all", "clean-antlr", "remove"], []) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs index a08dc020..c35d34a8 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Lexer.hs @@ -29,8 +29,8 @@ import BNFC.Backend.Common.NamedVariables -- user defined tokens. This is not handled. -- returns the environment because the parser uses it. cf2AntlrLex :: String -> CF -> (Doc, KeywordEnv) -cf2AntlrLex lang cf = (,env) $ vcat - [ prelude lang +cf2AntlrLex lexerName cf = (,env) $ vcat + [ prelude lexerName , cMacros -- unnamed symbols (those in quotes, not in token definitions) , lexSymbols env @@ -42,9 +42,9 @@ cf2AntlrLex lang cf = (,env) $ vcat -- | File prelude prelude :: String -> Doc -prelude lang = vcat +prelude lexerName = vcat [ "// Lexer definition for use with Antlr4" - , "lexer grammar" <+> text lang <> "Lexer;" + , "lexer grammar" <+> text lexerName <> ";" ] --For now all categories are included. diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index 7e13eeaa..df83060a 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -29,8 +29,8 @@ type Pattern = String -- | Creates the ANTLR parser grammar for this CF. --The environment comes from CFtoAntlr4Lexer -cf2AntlrParse :: String -> CF -> RecordPositions -> KeywordEnv -> String -cf2AntlrParse lang cf _ env = unlines +cf2AntlrParse :: String -> String -> CF -> RecordPositions -> KeywordEnv -> String +cf2AntlrParse lexerName parserName cf _ env = unlines [ header , tokens , "" @@ -44,12 +44,12 @@ cf2AntlrParse lang cf _ env = unlines header :: String header = unlines [ "// Parser definition for use with ANTLRv4" - , "parser grammar" +++ lang ++ "Parser;" + , "parser grammar" +++ parserName ++ ";" ] tokens :: String tokens = unlines [ "options {" - , " tokenVocab = " ++ lang ++ "Lexer;" + , " tokenVocab =" +++ lexerName ++ ";" , "}" ] From 558deeca0263de66d4782f66d4682d0c1649d94c Mon Sep 17 00:00:00 2001 From: xdkomel Date: Sun, 2 Jun 2024 15:19:28 +0300 Subject: [PATCH 45/70] add project structure and makefile --- source/src/BNFC/Backend/Dart.hs | 158 +++++++++++------- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 2 +- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 4 + .../src/BNFC/Backend/Dart/CFtoDartSkeleton.hs | 11 +- 4 files changed, 105 insertions(+), 70 deletions(-) diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index 35e90ebf..b0528f62 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -10,72 +10,97 @@ import System.Directory ( createDirectoryIfMissing ) import Data.Char (toLower) import BNFC.Backend.Base (MkFiles, mkfile,liftIO) -import BNFC.CF (CF, getAbstractSyntax) +import BNFC.CF (CF, getAbstractSyntax, firstEntry, catToStr) import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Dart)) import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) -import BNFC.Backend.Common.Makefile as MakeFile -import BNFC.Backend.Antlr (makeAntlr) +import BNFC.Backend.Common.Makefile as MakeFile +import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) +import BNFC.Backend.Antlr (makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) import BNFC.Backend.Dart.CFtoDartSkeleton ( cf2DartSkeleton ) +import BNFC.Backend.Dart.Common ( indent ) makeDart :: SharedOptions -> CF -> MkFiles () makeDart opts@Options{..} cf = do - let packageBase = maybe id (+.+) inPackage pkgName - dirBase = pkgToDir packageBase - langBase = dirBase lang + let dirBase = replace '.' pathSeparator $ packageName + langBase = dirBase (langName ++ "_generated") libLang = langBase "lib" srcLang = libLang "src" libBase = dirBase "lib" binBase = dirBase "bin" + directoryOptions = DirectoryOptions{baseDirectory = Just srcLang, nameStyle = Just SnakeCase} -- Generates files in an incorrect place - makeAntlr (opts {dLanguage = Dart, optMake = Nothing}) cf - MakeFile.mkMakefile optMake makefileContent + + makeAntlr' (opts {dLanguage = Dart, optMake = Nothing}) cf directoryOptions + MakeFile.mkMakefile optMake $ makefileContent srcLang mkfile (srcLang "ast.dart") makeDartComment astContent mkfile (srcLang "builder.dart") makeDartComment builderContent - mkfile (srcLang "printer.dart") makeDartComment printerContent - mkfile (libLang "stella.dart") makeDartComment stellaExportsContent + mkfile (srcLang "pretty_printer.dart") makeDartComment printerContent + mkfile (libLang (langName ++ "_generated.dart")) makeDartComment exportsContent mkfile (langBase "pubspec.yaml") makeDartCommentYaml $ pubspecContent - lang - ("A module with the AST, Pretty-Printer and AST-builder for" +++ lang) + (langName ++ "_generated") + ("A module with the AST, Pretty-Printer and AST-builder for" +++ langName) [] mkfile (libBase "runner.dart") makeDartComment runnerContent mkfile (libBase "skeleton.dart") makeDartComment skeletonContent mkfile (binBase "main.dart") makeDartComment mainContent mkfile (dirBase "pubspec.yaml" ) makeDartCommentYaml $ pubspecContent - (lang ++ "_example") - ("A simple project for" +++ lang) - [ lang ++ ":", " path:" +++ lang ] + (langName ++ "_example") + ("A simple project for" +++ langName) + [ langName ++ "_generated:", " path:" +++ langName ++ "_generated" ] where astContent = cf2DartAST cf - builderContent = cf2DartBuilder cf lang + builderContent = cf2DartBuilder cf langName printerContent = cf2DartPrinter cf - skeletonContent = cf2DartSkeleton cf - stellaExportsContent = unlines + skeletonContent = cf2DartSkeleton cf importLangName + exportsContent = unlines [ "export 'src/ast.dart';" , "export 'src/builder.dart';" - , "export 'src/printer.dart';" ] - runnerContent = unlines - [ "import 'package:stella/stella.dart';" - , "class Runner {" - , "}" ] + , "export 'src/pretty_printer.dart';" + , "export 'src/" ++ langName ++ "_lexer.dart';" + , "export 'src/" ++ langName ++ "_parser.dart';" ] + runnerContent = let firstCat = catToStr $ firstEntry cf in unlines ( + [ "import 'package:antlr4/antlr4.dart';" + , importLangName + , "import 'skeleton.dart';" + , "class Runner {" + , " Future run(List arguments) async {" ] + ++ ( indent 2 + [ "final input = await InputStream.fromString(arguments[0]);" + , "final lexer =" +++ langName ++ "_lexer(input);" + , "final tokens = CommonTokenStream(lexer);" + , "final parser =" +++ langName ++ "_parser(tokens);" + , "parser.addErrorListener(DiagnosticErrorListener());" + , "final output = build" ++ (firstUpperCase firstCat) ++ "(parser." ++ (firstLowerCase firstCat) ++ "());" + , "print('\"Parse Successful!\"');" + , "print('\"[Linearized Tree]\"');" + , "print(switch (output) {" + , " null => '" ++ (firstUpperCase firstCat) ++ " is null'," + , " " ++ (firstUpperCase firstCat) ++ " p => interpret" ++ (firstUpperCase firstCat) ++ "(p)," + , "});" + , "print('\"[Abstract Syntax]\"');" + , "print(output?.print);" + ] ) + ++ [ " }", "}" ] ) mainContent = unlines - [ "import '../lib/runner.dart'" + [ "import '../lib/runner.dart';" , "void main(List args) {" , " final runner = Runner();" - , " runner.run();" + , " runner.run(args);" , "}" ] - pkgName = mkName [] SnakeCase lang - pkgToDir = replace '.' pathSeparator + packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang + langName = firstLowerCase $ mkName [] SnakeCase lang + importLangName = "import 'package:" ++ langName ++ "_generated/" ++ langName ++ "_generated.dart';" - pubspecContent moduleName desc deps = unlines - ([ "name:" +++ moduleName + pubspecContent moduleName desc deps = unlines ( + [ "name:" +++ moduleName , "description:" +++ desc , "version: 1.0.0" , "publish_to: 'none'" @@ -84,8 +109,8 @@ makeDart opts@Options{..} cf = do , "dependencies:" , " antlr4: ^4.13.1" , " fast_immutable_collections: ^10.2.2" - ] ++ (map (" " ++) deps) ++ [ "dev_dependencies:" - , " lints: ^3.0.0" ]) + ] ++ (indent 1 deps) ++ [ "dev_dependencies:" + , " lints: ^4.0.0" ]) lexerClassName = lang ++ "GrammarLexer" parserClassName = lang ++ "GrammarParser" @@ -94,39 +119,48 @@ makeDart opts@Options{..} cf = do makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] makefileVars = vcat $ makeVars - [("LANG", lang) - , ("LEXER_NAME", lang ++ "Lexer") - , ("PARSER_NAME", lang ++ "Parser") - , ("ANTLR4", "java org.antlr.v4.Tool") - ] - - refVarWithPrefix :: String -> String - refVarWithPrefix refVar = MakeFile.refVar "LANG" MakeFile.refVar refVar - - rmFile :: String -> String -> String - rmFile refVar ext = "rm -f" +++ refVarWithPrefix refVar ++ ext - - makefileRules = vcat $ makeRules - [ (".PHONY", ["all", "clean", "remove"], []) - , ("all", [MakeFile.refVar "LANG"], []) - , ("lexer", [refVarWithPrefix "LEXER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "LEXER_NAME" ++ ".g4"]) - , ("parser", [refVarWithPrefix "PARSER_NAME" ++ ".g4"], [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refVarWithPrefix "PARSER_NAME" ++ ".g4"]) - , ("install-deps", [MakeFile.refVar "LANG" "pubspec.yaml"], ["cd" +++ (MakeFile.refVar "LANG") ++ "; dart pub get"]) - , (MakeFile.refVar "LANG", ["lexer", "parser", "install-deps"], []) - , ("clean", [], - [ - rmFile "LEXER_NAME" ".interp" - , rmFile "LEXER_NAME" ".tokens" - , rmFile "PARSER_NAME" ".interp" - , rmFile "PARSER_NAME" ".tokens" - , rmFile "LEXER_NAME" ".dart" - , rmFile "PARSER_NAME" ".dart" - , rmFile "PARSER_NAME" "Listener.dart" - ]) - , ("remove", [], ["rm -rf" +++ MakeFile.refVar "LANG"]) + [("LANG", langName) + , ("LEXER_NAME", langName ++ "_lexer") + , ("PARSER_NAME", langName ++ "_parser") + , ("ANTLR4", "java -Xmx500M -cp \"/usr/local/lib/antlr-4.13.1-complete.jar:$CLASSPATH\" org.antlr.v4.Tool") ] - makefileContent _ = vcat [makefileVars, "", makefileRules, ""] + refVarInSrc srcLang refVar = srcLang MakeFile.refVar refVar + + rmFile :: (String -> String) -> String -> String -> String + rmFile refSrcVar refVar ext = "rm -f" +++ refSrcVar refVar ++ ext + + makefileRules refSrcVar = + let rmInSrc = rmFile refSrcVar + in vcat $ makeRules + [ (".PHONY", ["all", "clean", "remove"], []) + , ("all", [MakeFile.refVar "LANG"], []) + , ("lexer" + , [refSrcVar "LEXER_NAME" ++ ".g4"] + , [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ refSrcVar "LEXER_NAME" ++ ".g4"]) + , ("parser" + , [refSrcVar "PARSER_NAME" ++ ".g4"] + , [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Dart" +++ "-no-listener" +++ "-no-visitor" +++ refSrcVar "PARSER_NAME" ++ ".g4"]) + , ("install-deps-external" + , [MakeFile.refVar "LANG" "pubspec.yaml"] + , ["cd" +++ (MakeFile.refVar "LANG") ++ "; dart pub get"]) + , ("install-deps-internal" + , [MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated") "pubspec.yaml"] + , ["cd" +++ (MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated")) ++ "; dart pub get"]) + , (MakeFile.refVar "LANG", ["lexer", "parser", "clean", "install-deps-external", "install-deps-internal"], []) + , ("clean", [], + [ + rmInSrc "LEXER_NAME" ".interp" + , rmInSrc "LEXER_NAME" ".tokens" + , rmInSrc "PARSER_NAME" ".interp" + , rmInSrc "PARSER_NAME" ".tokens" + , rmInSrc "LEXER_NAME" ".g4" + , rmInSrc "PARSER_NAME" ".g4" + ]) + , ("remove", [], ["rm -rf" +++ MakeFile.refVar "LANG"]) + ] + + makefileContent srcLang _ = vcat [makefileVars, "", makefileRules $ refVarInSrc srcLang, ""] makeDartComment :: String -> String makeDartComment = ("// Dart " ++) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 4f00bc86..6ce4b450 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -28,7 +28,7 @@ cf2DartBuilder cf lang = "import 'package:antlr4/antlr4.dart';", "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;", "import 'ast.dart';", - "import '" ++ lang ++ "Parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] + "import '" ++ lang ++ "_parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] helperFunctions = [ "int? buildInt(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;", "double? buildDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;", diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 5998aeb8..3405a8fd 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -139,6 +139,10 @@ stringRenderer = [ " String get print => toString();", "}", "", + "extension PrintableString on String {", + " String get print => this;", + "}", + "", "final _renderer = StringRenderer();", "", "mixin Printable {", diff --git a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs index 14e5d0d9..9455609e 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs @@ -13,16 +13,13 @@ import BNFC.Backend.Common.NamedVariables ( UserDef ) import BNFC.Backend.Dart.Common -cf2DartSkeleton :: CF -> String -cf2DartSkeleton cf = +cf2DartSkeleton :: CF -> String -> String +cf2DartSkeleton cf importLang = unlines $ - imports - ++ identityFn + [ importLang + , "A identityFn(A a) => a;" ] ++ (map buildUserToken [ n | (n,_) <- tokenPragmas cf ]) -- generate user-defined types ++ (concatMap genData $ getAbstractSyntax cf) - where - imports = [ "import 'package:stella/stella.dart';" ] - identityFn = [ "A identityFn(A a) => a;" ] buildUserToken :: UserDef -> String buildUserToken token = From 2899ef0b24f026916ac7d848f86d36627b3f4b16 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Sun, 2 Jun 2024 15:46:32 +0300 Subject: [PATCH 46/70] fix bug when merged --- source/src/BNFC/Options.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index 4b7b17b5..de69e412 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -359,9 +359,9 @@ targetOptions = , Option "" ["tree-sitter"] (NoArg (\o -> o {target = TargetTreeSitter})) "Output grammar.js file for use with tree-sitter" , Option "" ["check"] (NoArg (\o -> o{target = TargetCheck })) - , Option "" ["dart"] (NoArg (\ o -> o{target = TargetDart })) - "Output Dart code for use with ANTLR" "No output. Just check input LBNF file" + , Option "" ["dart"] (NoArg (\ o -> o{target = TargetDart })) + "Output Dart code for use with ANTLR" , Option "" ["antlr"] (NoArg (\o -> o {target = TargetAntlr})) "Output lexer and parser grammars for ANTLRv4" ] From 6359382f0efff9b895a96ec18e33a87b4bf0e009 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Sun, 2 Jun 2024 17:54:27 +0300 Subject: [PATCH 47/70] fix builder and PP naming issues --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 37 +-- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 275 +++++++++--------- source/src/BNFC/Backend/Dart/Common.hs | 10 +- 3 files changed, 164 insertions(+), 158 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 6ce4b450..20df4893 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -29,10 +29,11 @@ cf2DartBuilder cf lang = "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;", "import 'ast.dart';", "import '" ++ lang ++ "_parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] - helperFunctions = [ - "int? buildInt(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;", - "double? buildDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;", - "String? buildString(Token? t) => t?.text;" ] + helperFunctions = + [ "int? buildInteger(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;" + , "double? buildMyDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;" + , "String? buildMyString(Token? t) => t?.text;" + , "String? buildChar(Token? t) => t?.text;" ] buildUserToken token = let name = censorName token in token ++ "? build" ++ token ++ "(Token? t) {\n" ++ @@ -127,7 +128,7 @@ generateConcreteMappingHelper index rule (fun, cats) typeName ++ "?" +++ "build" ++ className ++ "(" ++ ctxName ++ "?" +++ "ctx) {" ] ++ ( indent 1 $ - (generateArguments index rule vars) ++ + (generateArguments index rule $ zip vars cats) ++ (generateNullCheck vars) ++ (generateReturnStatement fun vars typeName) ) ++ [ @@ -143,31 +144,33 @@ generateConcreteMappingHelper index rule (fun, cats) (indent 1 $ generateArgumentsMapping vars ) ++ [");"] -generateArguments :: Int -> Rule -> [DartVar] -> [String] +generateArguments :: Int -> Rule -> [(DartVar, Cat)] -> [String] generateArguments index r vars = case rhsRule r of [] -> [] its -> traverseRule index 1 its vars [] -traverseRule :: Int -> Int -> [Either Cat String] -> [DartVar] -> [String] -> [String] +traverseRule :: Int -> Int -> [Either Cat String] -> [(DartVar, Cat)] -> [String] -> [String] traverseRule _ _ _ [] lines = lines traverseRule _ _ [] _ lines = lines -traverseRule ind1 ind2 (terminal:restTerminals) (variable@(vType, _):restVariables) lines = +traverseRule ind1 ind2 (terminal:restTs) (var@(varDart, varCat):restVars) lines = case terminal of - Left cat -> [ - "final" +++ buildVariableName variable +++ "=" +++ buildArgument (precCat cat) vType field ++ ";" - ] ++ traverseRule ind1 (ind2 + 1) restTerminals restVariables lines - Right _ -> traverseRule ind1 (ind2 + 1) restTerminals (variable:restVariables) lines + Left cat -> + let lhs = buildVariableName varDart + rhs = buildArgument (precCat cat) (cat2DartClassName varCat) field + in [ "final" +++ lhs +++ "=" +++ rhs ++ ";" ] + ++ traverseRule ind1 (ind2 + 1) restTs restVars lines + Right _ -> traverseRule ind1 (ind2 + 1) restTs (var:restVars) lines where field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 - buildArgument :: Integer -> DartVarType -> String -> String - buildArgument prec (0, typeName) name = + buildArgument :: Integer -> String -> String -> String + buildArgument prec typeName name = let precedence = if prec == 0 then "" else show prec in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" - buildArgument prec (_, typeName) name = - let precedence = if prec == 0 then "" else show prec - in "buildList" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" + -- buildArgument prec (_, _) typeName name = + -- let precedence = if prec == 0 then "" else show prec + -- in "buildList" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" generateNullCheck :: [DartVar] -> [String] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index 3405a8fd..aaab2620 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -24,130 +24,130 @@ cf2DartPrinter cf = (concatMap generateLabelPrinters $ ruleGroups cf) imports :: [String] -imports = [ - "import 'ast.dart' as ast;", - "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] +imports = + [ "import 'ast.dart' as ast;" + , "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] helperFunctions :: [String] -helperFunctions = [ - "sealed class Token {}", - "", - "class Space extends Token {}", - "", - "class NewLine extends Token {", - " int indentDifference;", - " NewLine.indent(this.indentDifference);", - " NewLine() : indentDifference = 0;", - " NewLine.nest() : indentDifference = 1;", - " NewLine.unnest() : indentDifference = -1;", - "}", - "", - "class Text extends Token {", - " String text;", - " Text(this.text);", - "}" ] +helperFunctions = + [ "sealed class Token {}" + , "" + , "class Space extends Token {}" + , "" + , "class NewLine extends Token {" + , " int indentDifference;" + , " NewLine.indent(this.indentDifference);" + , " NewLine() : indentDifference = 0;" + , " NewLine.nest() : indentDifference = 1;" + , " NewLine.unnest() : indentDifference = -1;" + , "}" + , "" + , "class Text extends Token {" + , " String text;" + , " Text(this.text);" + , "}" ] stringRenderer :: [String] -stringRenderer = [ - "class StringRenderer {", - " // Change this value if you want to change the indentation length", - " static const _indentInSpaces = 2;", - "", - " String print(Iterable tokens) => tokens", - " .map((element) => element.trim())", - " .fold(IList(), _render)", - " .fold(IList<(int, IList)>(), _split)", - " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))", - " .fold(IList<(int, String)>(), _convertIndentation)", - " .map(_addIndentation)", - " .join('\\n');", - "", - " IList<(int, IList)> _split(", - " IList<(int, IList)> lists,", - " Token token,", - " ) =>", - " switch (token) {", - " NewLine nl => lists.add((", - " nl.indentDifference,", - " IList(),", - " )),", - " _ => lists.isEmpty", - " ? IList([", - " (0, IList([token]))", - " ])", - " : lists.put(", - " lists.length - 1,", - " (lists.last.$1, lists.last.$2.add(token)),", - " ),", - " };", - "", - " String _tokenToString(Token t) => switch (t) {", - " Text t => t.text,", - " Space _ => ' ',", - " _ => '',", - " };", - "", - " IList<(int, String)> _convertIndentation(", - " IList<(int, String)> lines,", - " (int, String) line,", - " ) =>", - " lines.add((", - " line.$1 + (lines.lastOrNull?.$1 ?? 0),", - " line.$2,", - " ));", - "", - " String _addIndentation((int, String) indentedLine) =>", - " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;", - "", - " // This function is supposed to be edited", - " // in order to adjust the pretty printer behavior", - " IList _render(IList tokens, String token) => switch (token) {", - " '' || ' ' => tokens,", - " '{' => tokens.addAll([Text(token), NewLine.nest()]),", - " '}' => tokens.removeTrailingLines", - " .addAll([NewLine.unnest(), Text(token), NewLine()]),", - " ';' => tokens.removeTrailingSpaces.addAll([Text(token), NewLine()]),", - " ')' || ']' || '>' || ',' => tokens", - " .removeTrailingSpaces.removeTrailingLines", - " .addAll([Text(token), Space()]),", - " '\\$' ||", - " '&' ||", - " '@' ||", - " '!' ||", - " '#' ||", - " '(' ||", - " '[' ||", - " '<' ||", - " '.' =>", - " tokens.removeTrailingLines.add(Text(token)),", - " _ => tokens.addAll([Text(token), Space()])", - " };", - "}", - "", - "extension TokensList on IList {", - " IList get removeTrailingLines =>", - " isNotEmpty && last is NewLine ? removeLast().removeTrailingLines : this;", - " IList get removeTrailingSpaces =>", - " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;", - "}", - "", - "extension PrintableInt on int {", - " String get print => toString();", - "}", - "", - "extension PrintableDouble on double {", - " String get print => toString();", - "}", - "", - "extension PrintableString on String {", - " String get print => this;", - "}", - "", - "final _renderer = StringRenderer();", - "", - "mixin Printable {", - " String get print => \'[not implemented]\';", - "}" ] +stringRenderer = + [ "class StringRenderer {" + , " // Change this value if you want to change the indentation length" + , " static const _indentInSpaces = 2;" + , "" + , " String print(Iterable tokens) => tokens" + , " .map((element) => element.trim())" + , " .fold(IList(), _render)" + , " .fold(IList<(int, IList)>(), _split)" + , " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))" + , " .fold(IList<(int, String)>(), _convertIndentation)" + , " .map(_addIndentation)" + , " .join('\\n');" + , "" + , " IList<(int, IList)> _split(" + , " IList<(int, IList)> lists," + , " Token token," + , " ) =>" + , " switch (token) {" + , " NewLine nl => lists.add((" + , " nl.indentDifference," + , " IList()," + , " ))," + , " _ => lists.isEmpty" + , " ? IList([" + , " (0, IList([token]))" + , " ])" + , " : lists.put(" + , " lists.length - 1," + , " (lists.last.$1, lists.last.$2.add(token))," + , " )," + , " };" + , "" + , " String _tokenToString(Token t) => switch (t) {" + , " Text t => t.text," + , " Space _ => ' '," + , " _ => ''," + , " };" + , "" + , " IList<(int, String)> _convertIndentation(" + , " IList<(int, String)> lines," + , " (int, String) line," + , " ) =>" + , " lines.add((" + , " line.$1 + (lines.lastOrNull?.$1 ?? 0)," + , " line.$2," + , " ));" + , "" + , " String _addIndentation((int, String) indentedLine) =>" + , " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;" + , "" + , " // This function is supposed to be edited" + , " // in order to adjust the pretty printer behavior" + , " IList _render(IList tokens, String token) => switch (token) {" + , " '' || ' ' => tokens," + , " '{' => tokens.addAll([Text(token), NewLine.nest()])," + , " '}' => tokens.removeTrailingLines" + , " .addAll([NewLine.unnest(), Text(token), NewLine()])," + , " ';' => tokens.removeTrailingSpaces.addAll([Text(token), NewLine()])," + , " ')' || ']' || '>' || ',' => tokens" + , " .removeTrailingSpaces.removeTrailingLines" + , " .addAll([Text(token), Space()])," + , " '\\$' ||" + , " '&' ||" + , " '@' ||" + , " '!' ||" + , " '#' ||" + , " '(' ||" + , " '[' ||" + , " '<' ||" + , " '.' =>" + , " tokens.removeTrailingLines.add(Text(token))," + , " _ => tokens.addAll([Text(token), Space()])" + , " };" + , "}" + , "" + , "extension TokensList on IList {" + , " IList get removeTrailingLines =>" + , " isNotEmpty && last is NewLine ? removeLast().removeTrailingLines : this;" + , " IList get removeTrailingSpaces =>" + , " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;" + , "}" + , "" + , "extension PrintableInt on int {" + , " String get print => toString();" + , "}" + , "" + , "extension PrintableDouble on double {" + , " String get print => toString();" + , "}" + , "" + , "extension PrintableString on String {" + , " String get print => this;" + , "}" + , "" + , "final _renderer = StringRenderer();" + , "" + , "mixin Printable {" + , " String get print => \'[not implemented]\';" + , "}" ] buildUserToken :: String -> [String] buildUserToken token = [ @@ -198,17 +198,15 @@ generateRulePrinters :: Data -> [String] generateRulePrinters (cat, rules) = let funs = map fst rules fun = catToStr cat - in - if - isList cat || - isNilFun fun || - isOneFun fun || - isConsFun fun || - isConcatFun fun || - isCoercion fun || - fun `elem` funs - then - [] -- the category is not presented in the AST + in + if isList cat + || isNilFun fun + || isOneFun fun + || isConsFun fun + || isConcatFun fun + || isCoercion fun + || fun `elem` funs + then [] -- the category is not presented in the AST else let className = cat2DartClassName cat in (generateRuntimeMapping className $ map fst rules) ++ @@ -234,10 +232,10 @@ generateConcreteMapping cat (label, tokens) className = str2DartClassName label cats = [ cat | Left cat <- tokens ] vars = zip (map precCat cats) (getVars cats) - in Just . unlines $ [ - "Iterable _prettify" ++ className ++ "(ast." ++ className +++ "a) => [" ] ++ - (indent 1 $ generateRuleRHS tokens vars []) ++ - ["];"] + in Just . unlines $ + [ "Iterable _prettify" ++ className ++ "(ast." ++ className +++ "a) => [" ] + ++ (indent 1 $ generateRuleRHS tokens vars []) + ++ ["];"] generateListPrettifier :: DartVarType -> Integer -> String -> String -> String generateListPrettifier vType@(n, name) prec separator terminator = @@ -276,10 +274,11 @@ generateListPrintFunction dvt prec = "String print" ++ printerListName dvt prec ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt prec ++ "(x));" printerListName :: DartVarType -> Integer -> String -printerListName (0, name) prec = - (str2DartClassName name) ++ if prec <= 0 then "" else (show prec) +printerListName (0, name) prec = name ++ if prec <= 0 then "" else (show prec) printerListName (n, name) prec = "List" ++ (printerListName (n - 1, name) prec) printerListType :: DartVarType -> String -printerListType (0, name) = "ast." ++ (str2DartClassName name) +printerListType (0, name) + | censorName name /= name = name + | otherwise = "ast." ++ name printerListType (n, name) = "Iterable<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index da5584f0..0a3e4d4e 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -43,7 +43,7 @@ name2DartBuiltIn name | name == "Integer" = "int" | name == "Double" = "double" | name == "Ident" = "String" - | name == "Char" = "String" -- TODO + | name == "Char" = "String" | otherwise = name @@ -99,9 +99,13 @@ getVars cats = addScore n = (1, n) toDartVar (namesMap, vars) (vType, name) = case (Map.lookup name namesMap) of - Nothing -> (namesMap, vars ++ [(vType, (name, 0))]) + Nothing -> ( + namesMap, + vars ++ [(vType, (name, 0))]) Just (seen, total) -> if total <= 1 - then (namesMap, vars ++ [(vType, (name, 0))]) + then ( + namesMap, + vars ++ [(vType, (name, 0))]) else ( Map.insert name (seen + 1, total) namesMap, vars ++ [(vType, (name, seen))]) From 5dfd109c11cc44a9bfd93bfff8adf2d7b1844e11 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Wed, 5 Jun 2024 14:32:03 +0300 Subject: [PATCH 48/70] [ANTLRv4]: change label generation function to funName --- source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs | 2 +- source/src/BNFC/Backend/Antlr/Utils.hs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs index df83060a..1d77f37b 100644 --- a/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs +++ b/source/src/BNFC/Backend/Antlr/CFtoAntlr4Parser.hs @@ -154,6 +154,6 @@ antlrRuleLabel cat fnc int | isOneFun fnc = catid ++ "_AppendLast" | isConsFun fnc = catid ++ "_PrependFirst" | isCoercion fnc = "Coercion_" ++ catid ++ maybe "" (("_" ++) . show) int - | otherwise = getLabelName fnc + | otherwise = funName fnc where catid = identCat cat diff --git a/source/src/BNFC/Backend/Antlr/Utils.hs b/source/src/BNFC/Backend/Antlr/Utils.hs index 0d052dda..c5869d0e 100644 --- a/source/src/BNFC/Backend/Antlr/Utils.hs +++ b/source/src/BNFC/Backend/Antlr/Utils.hs @@ -5,16 +5,12 @@ module BNFC.Backend.Antlr.Utils where import Prelude import System.FilePath ((<.>)) -import BNFC.CF (Fun) -import BNFC.Utils ( mkName, NameStyle(..), (+++)) +import BNFC.Utils ((+++)) import BNFC.Options as Options getRuleName :: String -> String getRuleName z = if z == "grammar" then z ++ "_" else z -getLabelName :: Fun -> String -getLabelName = mkName ["Rule"] CamelCase - -- | Make a new entrypoint NT for an existing NT. startSymbol :: String -> String From f6fcda2588894b7e7c5360d35cd781b52043904c Mon Sep 17 00:00:00 2001 From: xdkomel Date: Sun, 1 Sep 2024 13:05:32 +0300 Subject: [PATCH 49/70] mostly adequate version --- source/src/BNFC/Backend/Dart.hs | 75 +-- source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 248 ++++----- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 412 +++++++------- .../src/BNFC/Backend/Dart/CFtoDartPrinter.hs | 520 +++++++++--------- .../src/BNFC/Backend/Dart/CFtoDartSkeleton.hs | 108 ++-- source/src/BNFC/Backend/Dart/Common.hs | 199 +++++-- 6 files changed, 867 insertions(+), 695 deletions(-) diff --git a/source/src/BNFC/Backend/Dart.hs b/source/src/BNFC/Backend/Dart.hs index b0528f62..556148f4 100644 --- a/source/src/BNFC/Backend/Dart.hs +++ b/source/src/BNFC/Backend/Dart.hs @@ -10,7 +10,7 @@ import System.Directory ( createDirectoryIfMissing ) import Data.Char (toLower) import BNFC.Backend.Base (MkFiles, mkfile,liftIO) -import BNFC.CF (CF, getAbstractSyntax, firstEntry, catToStr) +import BNFC.CF (CF, getAbstractSyntax, firstEntry, catToStr, identCat, normCat ) import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Dart)) import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) import BNFC.Backend.Common.Makefile as MakeFile @@ -20,7 +20,7 @@ import BNFC.Backend.Dart.CFtoDartAST ( cf2DartAST ) import BNFC.Backend.Dart.CFtoDartBuilder ( cf2DartBuilder ) import BNFC.Backend.Dart.CFtoDartPrinter ( cf2DartPrinter ) import BNFC.Backend.Dart.CFtoDartSkeleton ( cf2DartSkeleton ) -import BNFC.Backend.Dart.Common ( indent ) +import BNFC.Backend.Dart.Common ( indent, buildVariableTypeFromDartType, cat2DartType, cat2DartClassName ) makeDart :: SharedOptions -> CF -> MkFiles () makeDart opts@Options{..} cf = do @@ -46,7 +46,7 @@ makeDart opts@Options{..} cf = do (langName ++ "_generated") ("A module with the AST, Pretty-Printer and AST-builder for" +++ langName) [] - mkfile (libBase "runner.dart") makeDartComment runnerContent + mkfile (libBase "test.dart") makeDartComment testContent mkfile (libBase "skeleton.dart") makeDartComment skeletonContent mkfile (binBase "main.dart") makeDartComment mainContent mkfile (dirBase "pubspec.yaml" ) makeDartCommentYaml @@ -56,44 +56,51 @@ makeDart opts@Options{..} cf = do [ langName ++ "_generated:", " path:" +++ langName ++ "_generated" ] where - astContent = cf2DartAST cf - builderContent = cf2DartBuilder cf langName - printerContent = cf2DartPrinter cf - skeletonContent = cf2DartSkeleton cf importLangName + astContent = cf2DartAST (firstUpperCase langName) cf + builderContent = cf2DartBuilder (firstUpperCase langName) cf + printerContent = cf2DartPrinter (firstUpperCase langName) cf + skeletonContent = cf2DartSkeleton (firstUpperCase langName) cf importLangName exportsContent = unlines [ "export 'src/ast.dart';" , "export 'src/builder.dart';" , "export 'src/pretty_printer.dart';" , "export 'src/" ++ langName ++ "_lexer.dart';" , "export 'src/" ++ langName ++ "_parser.dart';" ] - runnerContent = let firstCat = catToStr $ firstEntry cf in unlines ( - [ "import 'package:antlr4/antlr4.dart';" - , importLangName - , "import 'skeleton.dart';" - , "class Runner {" - , " Future run(List arguments) async {" ] - ++ ( indent 2 - [ "final input = await InputStream.fromString(arguments[0]);" - , "final lexer =" +++ langName ++ "_lexer(input);" - , "final tokens = CommonTokenStream(lexer);" - , "final parser =" +++ langName ++ "_parser(tokens);" - , "parser.addErrorListener(DiagnosticErrorListener());" - , "final output = build" ++ (firstUpperCase firstCat) ++ "(parser." ++ (firstLowerCase firstCat) ++ "());" - , "print('\"Parse Successful!\"');" - , "print('\"[Linearized Tree]\"');" - , "print(switch (output) {" - , " null => '" ++ (firstUpperCase firstCat) ++ " is null'," - , " " ++ (firstUpperCase firstCat) ++ " p => interpret" ++ (firstUpperCase firstCat) ++ "(p)," - , "});" - , "print('\"[Abstract Syntax]\"');" - , "print(output?.print);" - ] ) - ++ [ " }", "}" ] ) + testContent = + let + firstCat = firstEntry cf + varType = buildVariableTypeFromDartType $ cat2DartType (firstUpperCase langName) firstCat + varName = cat2DartClassName langName firstCat + rawVarName = firstLowerCase $ identCat $ normCat firstCat + in unlines ( + [ "import 'package:antlr4/antlr4.dart';" + , "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" + , importLangName + , "import 'skeleton.dart';" + , "class Test {" + , " Future run(List arguments) async {" ] + ++ ( indent 2 + [ "final input = await InputStream.fromString(arguments[0]);" + , "final lexer =" +++ langName ++ "_lexer(input);" + , "final tokens = CommonTokenStream(lexer);" + , "final parser =" +++ langName ++ "_parser(tokens);" + , "parser.addErrorListener(DiagnosticErrorListener());" + , "final output = build" ++ varName ++ "(parser." ++ rawVarName ++ "());" + , "print('\"Parse Successful!\"\\n');" + , "print('\"[Abstract Syntax]\"\\n');" + , "print('${output?.print}\\n');" + , "print('\"[Linearized Tree]\"\\n');" + , "print(switch (output) {" + , " null => '" ++ varType ++ " is null'," + , " " ++ varType ++ " p => interpret" ++ varName ++ "(p)," + , "});" + ] ) + ++ [ " }", "}" ] ) mainContent = unlines - [ "import '../lib/runner.dart';" + [ "import '../lib/test.dart';" , "void main(List args) {" - , " final runner = Runner();" - , " runner.run(args);" + , " final test = Test();" + , " test.run(args);" , "}" ] packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang langName = firstLowerCase $ mkName [] SnakeCase lang @@ -105,7 +112,7 @@ makeDart opts@Options{..} cf = do , "version: 1.0.0" , "publish_to: 'none'" , "environment:" - , " sdk: ^3.3.4" + , " sdk: ^3.4.0" , "dependencies:" , " antlr4: ^4.13.1" , " fast_immutable_collections: ^10.2.2" diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 2f6bcc91..74e94038 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -13,131 +13,127 @@ import BNFC.Backend.Common.NamedVariables ( UserDef ) import BNFC.Backend.Dart.Common -- Produces abstract data types in Dart -cf2DartAST :: CF -> String -cf2DartAST cf = +cf2DartAST :: String -> CF -> String +cf2DartAST langName cf = let userTokens = [ n | (n,_) <- tokenPragmas cf ] - in unlines $ - imports ++ -- import some libraries if needed - generateTokens userTokens ++ -- generate user-defined types - concatMap prData rules + in unlines + $ imports -- import some libraries if needed + ++ characterTypedef + ++ generateTokens userTokens + ++ concatMap prData rules -- generate user-defined types where - rules = getAbstractSyntax cf - imports = [ - "import 'pretty_printer.dart' as pp;", - "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] - - -generateTokens :: [UserDef] -> [String] -generateTokens tokens = map toClass tokens - where - toClass token = - let name = censorName token - in unlines [ - "final class" +++ name +++ "with pp.Printable {", -- A user defined type is a wrapper around the String - " final String value;", - " const" +++ name ++ "(this.value);", - "", - " @override", - " String get print => pp.print" ++ name ++ "(this);", - "}" - ] - - --- | Generates a category class, and classes for all its rules. -prData :: Data -> [String] -prData (cat, rules) = - categoryClass ++ mapMaybe (prRule cat) rules - where - funs = map fst rules - categoryClass - | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list - | otherwise = - let name = cat2DartClassName cat - in [ - "sealed class" +++ name +++ "with pp.Printable {", - " @override", - " String get print => pp.print" ++ name ++ "(this);", - "}" ] - - --- | Generates classes for a rule, depending on what type of rule it is. -prRule :: Cat -> (Fun, [Cat]) -> Maybe (String) -prRule cat (fun, cats) - | isNilFun fun || - isOneFun fun || - isConsFun fun = Nothing -- these are not represented in the Absyn - | otherwise = -- a standard rule - let - className = str2DartClassName fun - vars = getVars cats - in Just . unlines $ - [ unwords [ "class", className, extending, "with pp.Printable {" ] ] ++ - concatMap (indent 1) [ - prInstanceVariables vars, - prConstructor className vars, - prEquals className vars, - prHashCode vars, - prPrettyPrint className - ] ++ [ "}" ] - where - extending - | fun == catToStr cat = "" - | otherwise = "extends" +++ cat2DartClassName cat - - --- Override the equality `==` -prEquals :: String -> [DartVar] -> [String] -prEquals className variables = [ - "@override", - "bool operator ==(Object o) =>", - " o is" +++ className +++ "&&", - " o.runtimeType == runtimeType" ++ - (if null variables then ";" else " &&") - ] ++ checkChildren - where - checkChildren = generateEqualities variables - generateEqualities [] = [] - generateEqualities (variable:rest) = - let name = buildVariableName variable - in [ - " " ++ name +++ "==" +++ "o." ++ name ++ - (if null rest then ";" else " &&") - ] ++ generateEqualities rest - - --- Override the hashCode, combining all instance variables -prHashCode :: [DartVar] -> [String] -prHashCode vars = [ - "@override", - "int get hashCode => Object.hashAll([" ++ - concatMap variableHash vars ++ - "]);" - ] - where - variableHash variable = buildVariableName variable ++ ", " - - --- Generate variable definitions for the class -prInstanceVariables :: [DartVar] -> [String] -prInstanceVariables vars = map variableLine vars - where - variableLine variable = - let vType = buildVariableType variable - vName = buildVariableName variable - in "final" +++ vType +++ vName ++ ";" - - --- Generate the class constructor -prConstructor :: String -> [DartVar] -> [String] -prConstructor className vars = - [ className ++ "(" ++ variablesAssignment ++ ");" ] - where - variablesAssignment - | null vars = "" - | otherwise = "{" ++ (concatMap assignment vars) ++ "}" - assignment variable = "required this." ++ buildVariableName variable ++ ", " - -prPrettyPrint :: String -> [String] -prPrettyPrint name = [ - "@override", - "String get print => pp.print" ++ name ++ "(this);" ] + rules = getAbstractSyntax cf + imports = + [ "import 'pretty_printer.dart' as pp;" + , "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] + characterTypedef = [ "typedef Character = String;" ] + censorName' = censorName langName + str2DartClassName' = str2DartClassName langName + cat2DartClassName' = cat2DartClassName langName + getVars' = getVars langName + + + generateTokens :: [UserDef] -> [String] + generateTokens = map $ \token -> + let name = censorName' token + in "typedef" +++ name +++ "= String;" + + + -- | Generates a category class, and classes for all its rules. + prData :: Data -> [String] + prData (cat, rules) = + categoryClass ++ mapMaybe (prRule cat) rules + where + funs = map fst rules + categoryClass + | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + | otherwise = + let name = cat2DartClassName' cat + in + [ "sealed class" +++ name +++ "with pp.Printable {" + , " @override" + , " String get print => pp.print" ++ name ++ "(this);" + , "}" ] + + + -- | Generates classes for a rule, depending on what type of rule it is. + prRule :: Cat -> (Fun, [Cat]) -> Maybe (String) + prRule cat (fun, cats) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing -- these are not represented in the Absyn + | otherwise = -- a standard rule + let + className = str2DartClassName' fun + vars = getVars' cats + in Just . unlines $ + [ unwords [ "class", className, extending, "with pp.Printable {" ] ] ++ + concatMap (indent 1) [ + prInstanceVariables vars, + prConstructor className vars, + prEquals className vars, + prHashCode vars, + prPrettyPrint className + ] ++ [ "}" ] + where + extending + | fun == catToStr cat = "" + | otherwise = "extends" +++ cat2DartClassName' cat + + + -- Override the equality `==` + prEquals :: String -> [DartVar] -> [String] + prEquals className variables = [ + "@override", + "bool operator ==(Object o) =>", + " o is" +++ className +++ "&&", + " o.runtimeType == runtimeType" ++ + (if null variables then ";" else " &&") + ] ++ checkChildren + where + checkChildren = generateEqualities variables + generateEqualities [] = [] + generateEqualities (variable:rest) = + let name = buildVariableName variable + in [ + " " ++ name +++ "==" +++ "o." ++ name ++ + (if null rest then ";" else " &&") + ] ++ generateEqualities rest + + + -- Override the hashCode, combining all instance variables + prHashCode :: [DartVar] -> [String] + prHashCode vars = [ + "@override", + "int get hashCode => Object.hashAll([" ++ + concatMap variableHash vars ++ + "]);" + ] + where + variableHash variable = buildVariableName variable ++ ", " + + + -- Generate variable definitions for the class + prInstanceVariables :: [DartVar] -> [String] + prInstanceVariables vars = map variableLine vars + where + variableLine variable = + let vType = buildVariableType variable + vName = buildVariableName variable + in "final" +++ vType +++ vName ++ ";" + + + -- Generate the class constructor + prConstructor :: String -> [DartVar] -> [String] + prConstructor className vars = + [ className ++ "(" ++ variablesAssignment ++ ");" ] + where + variablesAssignment + | null vars = "" + | otherwise = "{" ++ (concatMap assignment vars) ++ "}" + assignment variable = "required this." ++ buildVariableName variable ++ ", " + + prPrettyPrint :: String -> [String] + prPrettyPrint name = [ + "@override", + "String get print => pp.print" ++ name ++ "(this);" ] diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 20df4893..153974d2 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -5,14 +5,15 @@ module BNFC.Backend.Dart.CFtoDartBuilder (cf2DartBuilder) where import BNFC.CF +import BNFC.Backend.Common.NamedVariables (firstLowerCase) import BNFC.Backend.Dart.Common import BNFC.Backend.Antlr.CFtoAntlr4Parser (makeLeftRecRule) import BNFC.Utils ( (+++) ) import Data.List ( intercalate, find ) import Data.Either ( isLeft ) -cf2DartBuilder :: CF -> String -> String -cf2DartBuilder cf lang = +cf2DartBuilder :: String -> CF -> String +cf2DartBuilder lang cf = let userTokens = [ n | (n,_) <- tokenPragmas cf ] in unlines $ @@ -24,197 +25,220 @@ cf2DartBuilder cf lang = leftRecRuleMaker = (makeLeftRecRule cf) rules = map (\(cat, rules) -> (cat, (map leftRecRuleMaker rules))) $ ruleGroups cf - imports lang = [ - "import 'package:antlr4/antlr4.dart';", - "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;", - "import 'ast.dart';", - "import '" ++ lang ++ "_parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] + imports lang = + [ "import 'package:antlr4/antlr4.dart' show Token;" + , "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;" + , "import 'ast.dart';" + , "import '" ++ (firstLowerCase lang) ++ "_parser.dart'; // fix this line depending on where the stellaParser is being lcated" ] helperFunctions = [ "int? buildInteger(Token? t) => t?.text != null ? int.tryParse(t!.text!) : null;" - , "double? buildMyDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;" - , "String? buildMyString(Token? t) => t?.text;" - , "String? buildChar(Token? t) => t?.text;" ] - buildUserToken token = - let name = censorName token - in token ++ "? build" ++ token ++ "(Token? t) {\n" ++ - " final text = t?.text;\n" ++ - " return text != null ?" +++ token ++ "(text) : null;\n}" - - -generateBuilders :: (Cat, [Rule]) -> [String] -generateBuilders (cat, rawRules) = - let - numeratedRawRules = zip [1..] rawRules - in - runtimeTypeMapping numeratedRawRules ++ - concatMap (\(index, rule) -> generateConcreteMapping index rule) numeratedRawRules - where - funs numeratedRawRules = (map (\(_, rule) -> wpThing $ funRule rule) numeratedRawRules) - runtimeTypeMapping numeratedRawRules - | (catToStr cat) `elem` (funs numeratedRawRules) = [] -- the category is also a function or a list - | otherwise = generateRuntimeTypeMapping cat [ - (index, wpThing $ funRule rule, rhsRule rule) | - (index, rule) <- numeratedRawRules ] - - -reformatRule :: Rule -> (String, [Cat]) -reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) - - -generateRuntimeTypeMapping :: Cat -> [(Int, String, [Either Cat String])] -> [String] -generateRuntimeTypeMapping cat rules = - let ctxName = cat2DartClassName cat - astName = buildVariableTypeFromDartType $ cat2DartType cat - prec = precCat cat - precedencedName = ctxName ++ (if prec == 0 then "" else show prec) - in [ - astName ++ "?" +++ "build" ++ precedencedName ++ "(" ++ (contextName precedencedName) ++ "?" +++ "ctx" ++ ") {" - ] ++ indent 1 ( - (map (buildChild precedencedName) rules) ++ - ["return null;"] - ) ++ ["}"] - where - buildUniversalChild name fun arg = - "if (ctx is" +++ name ++ ") return build" ++ fun ++ "(" ++ arg ++ ");" - buildChild className (index, name, rhs) = case (antlrListSuffix name) of - "" -> if (isCoercion name) - then - let (coercionType, ind2) = case (find (\(_, value) -> isLeft value) $ zip [1..] rhs) of - Just (i, Left cat) -> ( - let prec = precCat cat in (cat2DartClassName cat) ++ (if prec == 0 then "" else show prec), - show i ) - otherwise -> (className, "") -- error, no category for the coercion - lineIndex = show index - argument = "p_" ++ lineIndex ++ "_" ++ ind2 - in - buildUniversalChild ("Coercion_" ++ contextName (className ++ "_" ++ lineIndex)) coercionType ("ctx." ++ argument) - else - buildUniversalChild (contextName $ str2AntlrClassName name) (str2DartClassName name) "ctx" - suffix -> - buildUniversalChild (contextName (className ++ "_" ++ suffix)) (className ++ suffix) "ctx" - - -generateConcreteMapping :: Int -> Rule -> [String] -generateConcreteMapping index rule = - generateConcreteMappingHelper index rule $ reformatRule rule - - -generateConcreteMappingHelper :: Int -> Rule -> (String, [Cat]) -> [String] -generateConcreteMappingHelper index rule (fun, cats) - | isCoercion fun = [] - | otherwise = - let - (typeName, className, ctxName) = - if (isNilFun fun || - isOneFun fun || - isConsFun fun) - then - let cat = valCat rule - prec = case (precCat cat) of - 0 -> "" - i -> show i - ctxName = (cat2DartClassName cat) ++ prec - suffix = antlrListSuffix fun - precedencedName = ctxName ++ suffix - suffixedCtxName = contextName (ctxName ++ "_" ++ suffix) - astName = buildVariableTypeFromDartType $ cat2DartType cat - in (astName, precedencedName, suffixedCtxName) - else - let name = str2DartClassName fun - ctxName = contextName $ str2AntlrClassName fun - in (name, name, ctxName) - vars = getVars cats - in [ - typeName ++ "?" +++ "build" ++ className ++ "(" ++ ctxName ++ "?" +++ "ctx) {" - ] ++ ( - indent 1 $ - (generateArguments index rule $ zip vars cats) ++ - (generateNullCheck vars) ++ - (generateReturnStatement fun vars typeName) - ) ++ [ - "}" - ] - where - generateReturnStatement :: Fun -> [DartVar] -> String -> [String] - generateReturnStatement fun vars typeName - | isNilFun fun = ["return IList();"] - | isOneFun fun = generateOneArgumentListReturn vars - | isConsFun fun = generateTwoArgumentsListReturn vars - | otherwise = [ "return" +++ typeName ++ "(" ] ++ - (indent 1 $ generateArgumentsMapping vars ) ++ [");"] - - -generateArguments :: Int -> Rule -> [(DartVar, Cat)] -> [String] -generateArguments index r vars = - case rhsRule r of - [] -> [] - its -> traverseRule index 1 its vars [] - - -traverseRule :: Int -> Int -> [Either Cat String] -> [(DartVar, Cat)] -> [String] -> [String] -traverseRule _ _ _ [] lines = lines -traverseRule _ _ [] _ lines = lines -traverseRule ind1 ind2 (terminal:restTs) (var@(varDart, varCat):restVars) lines = - case terminal of - Left cat -> - let lhs = buildVariableName varDart - rhs = buildArgument (precCat cat) (cat2DartClassName varCat) field - in [ "final" +++ lhs +++ "=" +++ rhs ++ ";" ] - ++ traverseRule ind1 (ind2 + 1) restTs restVars lines - Right _ -> traverseRule ind1 (ind2 + 1) restTs (var:restVars) lines - where - field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 - buildArgument :: Integer -> String -> String -> String - buildArgument prec typeName name = - let precedence = if prec == 0 then "" else show prec - in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" - -- buildArgument prec (_, _) typeName name = - -- let precedence = if prec == 0 then "" else show prec - -- in "buildList" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" - - -generateNullCheck :: [DartVar] -> [String] -generateNullCheck [] = [] -generateNullCheck vars = - [ "if (" ] ++ - (indent 1 [ intercalate " || " $ map condition vars ]) ++ - [ ") {" ] ++ - (indent 1 [ "return null;" ]) ++ - [ "}" ] - where - condition :: DartVar -> String - condition var = buildVariableName var +++ "==" +++ "null" - - -generateArgumentsMapping :: [DartVar] -> [String] -generateArgumentsMapping vars = map mapArgument vars - where - mapArgument variable = - let name = buildVariableName variable - in name ++ ":" +++ name ++ "," - - -generateOneArgumentListReturn :: [DartVar] -> [String] -generateOneArgumentListReturn (v:_) = - ["return IList([" ++ buildVariableName v ++ "]);"] - - -generateTwoArgumentsListReturn :: [DartVar] -> [String] -generateTwoArgumentsListReturn (x:y:_) = - let (a, b) = putListSecond x y - in ["return IList([" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",]);"] - where - putListSecond x@((0,_),_) y = (x, y) - putListSecond x y = (y, x) - - -contextName :: String -> String -contextName className = className ++ "Context" - - -antlrListSuffix :: Fun -> String -antlrListSuffix fun - | isNilFun fun = "Empty" - | isOneFun fun = "AppendLast" - | isConsFun fun = "PrependFirst" - | otherwise = "" \ No newline at end of file + , "double? buildDouble(Token? t) => t?.text != null ? double.tryParse(t!.text!) : null;" + , "String? buildString(Token? t) => t?.text;" + , "String? buildChar(Token? t) => t?.text;" + , "String? buildIdent(Token? t) => t?.text;" ] + buildUserToken token = + let name = censorName lang token + in name ++ "? build" ++ name ++ "(Token? t) => t?.text;" + str2DartClassName' = str2DartClassName lang + getVars' = getVars lang + cat2DartClassName' = cat2DartClassName lang + cat2DartType' = cat2DartType lang + + generateBuilders :: (Cat, [Rule]) -> [String] + generateBuilders (cat, rawRules) = + let + numeratedRawRules = zip [1..] rawRules + in runtimeTypeMapping numeratedRawRules + ++ (concatMap (uncurry generateConcreteMapping) numeratedRawRules) + where + funsFrom = map (\(_, rule) -> wpThing $ funRule rule) + runtimeTypeMapping numeratedRawRules + | (catToStr cat) `elem` (funsFrom numeratedRawRules) = [] + | otherwise = generateRuntimeTypeMapping cat [ + (index, wpThing $ funRule rule, rhsRule rule) | + (index, rule) <- numeratedRawRules ] + + + reformatRule :: Rule -> (String, [Cat]) + reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) + + + generateRuntimeTypeMapping :: Cat -> [(Int, String, [Either Cat String])] -> [String] + generateRuntimeTypeMapping cat rules = + let ctxName = upperFirst $ identCat $ normCat cat + astName = buildVariableTypeFromDartType $ cat2DartType' cat + prec = case precCat cat of + 0 -> "" + x -> show x + precedencedName = ctxName ++ prec + in + [ astName ++ "?" +++ "build" ++ precedencedName ++ "(" + ++ (contextName precedencedName) ++ "?" +++ "ctx" ++ ") {" ] + ++ indent 1 ( (map (buildChild precedencedName) rules) + ++ ["return null;"] ) + ++ [ "}" ] + where + buildUniversalChild name fun arg = + "if (ctx is" +++ name ++ ") return build" ++ fun ++ "(" ++ arg ++ ");" + buildChild className (index, name, rhs) = case (antlrListSuffix name) of + "" -> if (isCoercion name) + then + let firstCat = find + (\(_, value) -> isLeft value) + $ zip [1..] rhs + (coercionType, ind2) = case (firstCat) of + Just (i, Left cat) -> + ( let precStr = case precCat cat of + 0 -> "" + x -> show x + catName = upperFirst $ identCat $ normCat cat + in catName ++ precStr + , show i ) + otherwise -> (className, "") -- error, no category in the coercion rule + lineIndex = show index + argument = "p_" ++ lineIndex ++ "_" ++ ind2 + in + buildUniversalChild + ("Coercion_" ++ contextName (className ++ "_" ++ lineIndex)) + coercionType + ("ctx." ++ argument) + else + buildUniversalChild + (contextName $ str2AntlrClassName name) + name + -- (str2DartClassName' name) + "ctx" + suffix -> buildUniversalChild + (contextName (className ++ "_" ++ suffix)) + (className ++ suffix) + "ctx" + + + generateConcreteMapping :: Int -> Rule -> [String] + generateConcreteMapping index rule = + generateConcreteMappingHelper index rule $ reformatRule rule + + + generateConcreteMappingHelper :: Int -> Rule -> (String, [Cat]) -> [String] + generateConcreteMappingHelper index rule (fun, cats) + | isCoercion fun = [] + | otherwise = + let + (typeName, className, ctxName) = + if (isNilFun fun || + isOneFun fun || + isConsFun fun) + then + let cat = valCat rule + prec = case (precCat cat) of + 0 -> "" + i -> show i + ctxName = (++ prec) $ upperFirst $ identCat $ normCat cat + suffix = antlrListSuffix fun + precedencedName = ctxName ++ suffix + suffixedCtxName = contextName (ctxName ++ "_" ++ suffix) + astName = buildVariableTypeFromDartType $ cat2DartType' cat + in (astName, precedencedName, suffixedCtxName) + else + let name = str2DartClassName' fun + ctxName = contextName $ str2AntlrClassName fun + in (name, fun, ctxName) + vars = getVars' cats + in [ + typeName ++ "?" +++ "build" ++ className ++ "(" ++ ctxName ++ "?" +++ "ctx) {" + ] ++ ( + indent 1 $ + (generateArguments index rule $ zip vars cats) ++ + (generateNullCheck vars) ++ + (generateReturnStatement fun vars typeName) + ) ++ [ + "}" + ] + where + generateReturnStatement :: Fun -> [DartVar] -> String -> [String] + generateReturnStatement fun vars typeName + | isNilFun fun = ["return IList();"] + | isOneFun fun = generateOneArgumentListReturn vars + | isConsFun fun = generateTwoArgumentsListReturn vars + | otherwise = [ "return" +++ typeName ++ "(" ] ++ + (indent 1 $ generateArgumentsMapping vars ) ++ [");"] + + + generateArguments :: Int -> Rule -> [(DartVar, Cat)] -> [String] + generateArguments index r vars = + case rhsRule r of + [] -> [] + its -> traverseRule index 1 its vars [] + + + traverseRule :: Int -> Int -> [Either Cat String] -> [(DartVar, Cat)] -> [String] -> [String] + traverseRule _ _ _ [] lines = lines + traverseRule _ _ [] _ lines = lines + traverseRule ind1 ind2 (terminal:restTs) (var@(varDart, varCat):restVars) lines = + case terminal of + Left cat -> + let lhs = buildVariableName varDart + rhs = buildArgument + (precCat cat) + (upperFirst $ identCat $ normCat varCat) + -- (cat2DartClassName' varCat) + field + in [ "final" +++ lhs +++ "=" +++ rhs ++ ";" ] + ++ traverseRule ind1 (ind2 + 1) restTs restVars lines + Right _ -> traverseRule ind1 (ind2 + 1) restTs (var:restVars) lines + where + field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 + buildArgument :: Integer -> String -> String -> String + buildArgument prec typeName name = + let precedence = if prec == 0 then "" else show prec + in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" + -- buildArgument prec (_, _) typeName name = + -- let precedence = if prec == 0 then "" else show prec + -- in "buildList" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" + + + generateNullCheck :: [DartVar] -> [String] + generateNullCheck [] = [] + generateNullCheck vars = + [ "if (" ] ++ + (indent 1 [ intercalate " || " $ map condition vars ]) ++ + [ ") {" ] ++ + (indent 1 [ "return null;" ]) ++ + [ "}" ] + where + condition :: DartVar -> String + condition var = buildVariableName var +++ "==" +++ "null" + + + generateArgumentsMapping :: [DartVar] -> [String] + generateArgumentsMapping vars = map mapArgument vars + where + mapArgument variable = + let name = buildVariableName variable + in name ++ ":" +++ name ++ "," + + + generateOneArgumentListReturn :: [DartVar] -> [String] + generateOneArgumentListReturn (v:_) = + ["return IList([" ++ buildVariableName v ++ "]);"] + + + generateTwoArgumentsListReturn :: [DartVar] -> [String] + generateTwoArgumentsListReturn (x:y:_) = + let (a, b) = putListSecond x y + in ["return IList([" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",]);"] + where + putListSecond x@((0,_),_) y = (x, y) + putListSecond x y = (y, x) + + + contextName :: String -> String + contextName className = className ++ "Context" + + + antlrListSuffix :: Fun -> String + antlrListSuffix fun + | isNilFun fun = "Empty" + | isOneFun fun = "AppendLast" + | isConsFun fun = "PrependFirst" + | otherwise = "" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs index aaab2620..d11e5ef5 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartPrinter.hs @@ -11,8 +11,8 @@ import Data.Maybe ( mapMaybe ) import Data.List ( intercalate, find ) import Data.Either ( isLeft ) -cf2DartPrinter :: CF -> String -cf2DartPrinter cf = +cf2DartPrinter :: String -> CF -> String +cf2DartPrinter langName cf = let userTokens = [ n | (n,_) <- tokenPragmas cf ] in unlines $ @@ -21,264 +21,288 @@ cf2DartPrinter cf = stringRenderer ++ (concatMap buildUserToken userTokens) ++ (concatMap generateRulePrinters $ getAbstractSyntax cf) ++ - (concatMap generateLabelPrinters $ ruleGroups cf) + (concatMap generateLabelPrinters $ ruleGroupsInternals cf ) + where + str2DartClassName' = str2DartClassName langName + getVars' = getVars langName + cat2DartClassName' = cat2DartClassName langName + cat2DartType' = cat2DartType langName -imports :: [String] -imports = - [ "import 'ast.dart' as ast;" - , "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] + imports :: [String] + imports = + [ "import 'ast.dart' as ast;" + , "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" ] -helperFunctions :: [String] -helperFunctions = - [ "sealed class Token {}" - , "" - , "class Space extends Token {}" - , "" - , "class NewLine extends Token {" - , " int indentDifference;" - , " NewLine.indent(this.indentDifference);" - , " NewLine() : indentDifference = 0;" - , " NewLine.nest() : indentDifference = 1;" - , " NewLine.unnest() : indentDifference = -1;" - , "}" - , "" - , "class Text extends Token {" - , " String text;" - , " Text(this.text);" - , "}" ] + helperFunctions :: [String] + helperFunctions = + [ "sealed class Token {}" + , "" + , "class Space extends Token {}" + , "" + , "class NewLine extends Token {" + , " int indentDifference;" + , " NewLine.indent(this.indentDifference);" + , " NewLine() : indentDifference = 0;" + , " NewLine.nest() : indentDifference = 1;" + , " NewLine.unnest() : indentDifference = -1;" + , "}" + , "" + , "class Text extends Token {" + , " String text;" + , " Text(this.text);" + , "}" ] -stringRenderer :: [String] -stringRenderer = - [ "class StringRenderer {" - , " // Change this value if you want to change the indentation length" - , " static const _indentInSpaces = 2;" - , "" - , " String print(Iterable tokens) => tokens" - , " .map((element) => element.trim())" - , " .fold(IList(), _render)" - , " .fold(IList<(int, IList)>(), _split)" - , " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))" - , " .fold(IList<(int, String)>(), _convertIndentation)" - , " .map(_addIndentation)" - , " .join('\\n');" - , "" - , " IList<(int, IList)> _split(" - , " IList<(int, IList)> lists," - , " Token token," - , " ) =>" - , " switch (token) {" - , " NewLine nl => lists.add((" - , " nl.indentDifference," - , " IList()," - , " ))," - , " _ => lists.isEmpty" - , " ? IList([" - , " (0, IList([token]))" - , " ])" - , " : lists.put(" - , " lists.length - 1," - , " (lists.last.$1, lists.last.$2.add(token))," - , " )," - , " };" - , "" - , " String _tokenToString(Token t) => switch (t) {" - , " Text t => t.text," - , " Space _ => ' '," - , " _ => ''," - , " };" - , "" - , " IList<(int, String)> _convertIndentation(" - , " IList<(int, String)> lines," - , " (int, String) line," - , " ) =>" - , " lines.add((" - , " line.$1 + (lines.lastOrNull?.$1 ?? 0)," - , " line.$2," - , " ));" - , "" - , " String _addIndentation((int, String) indentedLine) =>" - , " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;" - , "" - , " // This function is supposed to be edited" - , " // in order to adjust the pretty printer behavior" - , " IList _render(IList tokens, String token) => switch (token) {" - , " '' || ' ' => tokens," - , " '{' => tokens.addAll([Text(token), NewLine.nest()])," - , " '}' => tokens.removeTrailingLines" - , " .addAll([NewLine.unnest(), Text(token), NewLine()])," - , " ';' => tokens.removeTrailingSpaces.addAll([Text(token), NewLine()])," - , " ')' || ']' || '>' || ',' => tokens" - , " .removeTrailingSpaces.removeTrailingLines" - , " .addAll([Text(token), Space()])," - , " '\\$' ||" - , " '&' ||" - , " '@' ||" - , " '!' ||" - , " '#' ||" - , " '(' ||" - , " '[' ||" - , " '<' ||" - , " '.' =>" - , " tokens.removeTrailingLines.add(Text(token))," - , " _ => tokens.addAll([Text(token), Space()])" - , " };" - , "}" - , "" - , "extension TokensList on IList {" - , " IList get removeTrailingLines =>" - , " isNotEmpty && last is NewLine ? removeLast().removeTrailingLines : this;" - , " IList get removeTrailingSpaces =>" - , " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;" - , "}" - , "" - , "extension PrintableInt on int {" - , " String get print => toString();" - , "}" - , "" - , "extension PrintableDouble on double {" - , " String get print => toString();" - , "}" - , "" - , "extension PrintableString on String {" - , " String get print => this;" - , "}" - , "" - , "final _renderer = StringRenderer();" - , "" - , "mixin Printable {" - , " String get print => \'[not implemented]\';" - , "}" ] + stringRenderer :: [String] + stringRenderer = + [ "class StringRenderer {" + , " // Change this value if you want to change the indentation length" + , " static const _indentInSpaces = 2;" + , "" + , " String print(Iterable tokens) => tokens" + , " .map((element) => element.trim())" + , " .fold(IList(), _render)" + , " .fold(IList<(int, IList)>(), _split)" + , " .map((line) => (line.$1, line.$2.map(_tokenToString).join()))" + , " .fold(IList<(int, String)>(), _convertIndentation)" + , " .map(_addIndentation)" + , " .join('\\n');" + , "" + , " IList<(int, IList)> _split(" + , " IList<(int, IList)> lists," + , " Token token," + , " ) =>" + , " switch (token) {" + , " NewLine nl => lists.add((" + , " nl.indentDifference," + , " IList()," + , " ))," + , " _ => lists.isEmpty" + , " ? IList([" + , " (0, IList([token]))" + , " ])" + , " : lists.put(" + , " lists.length - 1," + , " (lists.last.$1, lists.last.$2.add(token))," + , " )," + , " };" + , "" + , " String _tokenToString(Token t) => switch (t) {" + , " Text t => t.text," + , " Space _ => ' '," + , " _ => ''," + , " };" + , "" + , " IList<(int, String)> _convertIndentation(" + , " IList<(int, String)> lines," + , " (int, String) line," + , " ) =>" + , " lines.add((" + , " line.$1 + (lines.lastOrNull?.$1 ?? 0)," + , " line.$2," + , " ));" + , "" + , " String _addIndentation((int, String) indentedLine) =>" + , " ' ' * (_indentInSpaces * indentedLine.$1) + indentedLine.$2;" + , "" + , " // This function is supposed to be edited" + , " // in order to adjust the pretty printer behavior" + , " IList _render(IList tokens, String token) => switch (token) {" + , " '' || ' ' => tokens," + , " '{' => tokens.addAll([Text(token), NewLine.nest()])," + , " '}' => tokens.removeTrailingLines" + , " .addAll([NewLine.unnest(), Text(token), NewLine()])," + , " ';' => tokens.removeTrailingSpaces.addAll([Text(token), NewLine()])," + , " ')' || ']' || '>' || ',' => tokens" + , " .removeTrailingSpaces.removeTrailingLines" + , " .addAll([Text(token), Space()])," + , " '\\$' ||" + , " '&' ||" + , " '@' ||" + , " '!' ||" + , " '#' ||" + , " '(' ||" + , " '[' ||" + , " '<' ||" + , " '.' =>" + , " tokens.removeTrailingLines.add(Text(token))," + , " _ => tokens.addAll([Text(token), Space()])" + , " };" + , "}" + , "" + , "extension TokensList on IList {" + , " IList get removeTrailingLines =>" + , " isNotEmpty && last is NewLine ? removeLast().removeTrailingLines : this;" + , " IList get removeTrailingSpaces =>" + , " isNotEmpty && last is Space ? removeLast().removeTrailingSpaces : this;" + , "}" + , "" + , "extension PrintableInt on int {" + , " String get print => toString();" + , "}" + , "" + , "extension PrintableDouble on double {" + , " String get print => toString();" + , "}" + , "" + , "extension PrintableString on String {" + , " String get print => this;" + , "}" + , "" + , "extension PrintableIList on IList {" + , " String get print => toString();" + , "}" + , "" + , "final _renderer = StringRenderer();" + , "" + , "mixin Printable {" + , " String get print => \'[not implemented]\';" + , "}" ] -buildUserToken :: String -> [String] -buildUserToken token = [ - "String print" ++ token ++ "(x) => x.value;", - "Iterable _prettify" ++ token ++ "(ast." ++ token +++ "x) => [x.value];"] + buildUserToken :: String -> [String] + buildUserToken token = + let name = censorName langName token + in [ "Iterable _prettify" ++ name ++ "(ast." ++ name +++ "x) => [x];" ] -generateLabelPrinters :: (Cat, [Rule]) -> [String] -generateLabelPrinters (cat, rawRules) = - let rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] - in if isList cat - then - let - sep = findSep rules - term = findTerm rules - vType = cat2DartType $ normCat cat - precedence = precCat cat - in [ - generateListPrettifier vType precedence sep term, - generateListPrintFunction vType precedence ] - else - let funs = [ fst rule | rule <- rules ] - in mapMaybe (generateConcreteMapping cat) rules ++ - (concatMap generatePrintFunction $ map str2DartClassName $ filter representedInAst funs) - where - representedInAst :: String -> Bool - representedInAst fun = not ( - isNilFun fun || - isOneFun fun || - isConsFun fun || - isConcatFun fun || - isCoercion fun ) - findSep :: [(String, [Either Cat String])] -> String - findSep [] = "" - findSep ((name, rhs):rest) - | isConsFun name = case [ sep | Right sep <- rhs ] of - (a:_) -> a - [] -> findSep rest - | otherwise = findSep rest - findTerm :: [(String, [Either Cat String])] -> String - findTerm [] = "" - findTerm ((name, rhs):rest) - | isOneFun name = case [ sep | Right sep <- rhs ] of - (a:_) -> a - [] -> findTerm rest - | otherwise = findTerm rest + generateLabelPrinters :: (Cat, [Rule]) -> [String] + generateLabelPrinters (cat, rawRules) = let + rules = [ (wpThing $ funRule rule, rhsRule rule) | rule <- rawRules ] + in if isList cat + then let + sep = findSep rules + term = findTerm rules + vType = cat2DartType' $ normCat cat + precedence = precCat cat + in [ + generateListPrettifier vType precedence sep term, + generateListPrintFunction vType precedence ] + else let + funs = [ fst rule | rule <- rules ] + in mapMaybe (generateConcreteMapping cat) rules + ++ (concatMap generatePrintFunction $ map str2DartClassName' $ filter representedInAst funs) + where + representedInAst :: String -> Bool + representedInAst fun = not ( + isNilFun fun || + isOneFun fun || + isConsFun fun || + isConcatFun fun || + isCoercion fun ) + findSep :: [(String, [Either Cat String])] -> String + findSep [] = "" + findSep ((name, rhs):rest) + | isConsFun name = case [ sep | Right sep <- rhs ] of + (a:_) -> a + [] -> findSep rest + | otherwise = findSep rest + findTerm :: [(String, [Either Cat String])] -> String + findTerm [] = "" + findTerm ((name, rhs):rest) + | isOneFun name = case [ sep | Right sep <- rhs ] of + (a:_) -> a + [] -> findTerm rest + | otherwise = findTerm rest -generateRulePrinters :: Data -> [String] -generateRulePrinters (cat, rules) = - let funs = map fst rules - fun = catToStr cat - in - if isList cat - || isNilFun fun - || isOneFun fun - || isConsFun fun - || isConcatFun fun - || isCoercion fun - || fun `elem` funs - then [] -- the category is not presented in the AST - else - let className = cat2DartClassName cat - in (generateRuntimeMapping className $ map fst rules) ++ - (generatePrintFunction className) + generateRulePrinters :: Data -> [String] + generateRulePrinters (cat, rules) = + let funs = map fst rules + fun = catToStr cat + in + if isList cat + || isNilFun fun + || isOneFun fun + || isConsFun fun + || isConcatFun fun + || isCoercion fun + || fun `elem` funs + then [] -- the category is not presented in the AST + else + let className = cat2DartClassName' cat + in (generateRuntimeMapping className $ map fst rules) ++ + (generatePrintFunction className) -generateRuntimeMapping :: String -> [String] -> [String] -generateRuntimeMapping name ruleNames = [ - "Iterable _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ - (indent 2 $ map mapRule $ map str2DartClassName ruleNames) ++ - (indent 1 [ "};" ]) - where - mapRule name = "ast." ++ name +++ "a => _prettify" ++ name ++ "(a)," + generateRuntimeMapping :: String -> [String] -> [String] + generateRuntimeMapping name ruleNames = [ + "Iterable _prettify" ++ name ++ "(ast." ++ name +++ "a) => switch (a) {" ] ++ + (indent 2 $ map mapRule $ map str2DartClassName' ruleNames) ++ + (indent 1 [ "};" ]) + where + mapRule name = "ast." ++ name +++ "a => _prettify" ++ name ++ "(a)," + + generateConcreteMapping :: Cat -> (String, [Either Cat String]) -> Maybe (String) + generateConcreteMapping cat (label, tokens) + | isNilFun label || + isOneFun label || + isConsFun label || + isConcatFun label || + isCoercion label = Nothing -- these are not represented in the AST + | otherwise = -- a standard rule + let + tokensReversed = foldl (\acc x -> x : acc) [] tokens + className = str2DartClassName' label + cats = [ cat | Left cat <- tokensReversed ] + vars = zip (map precCat cats) (getVars' cats) + in Just . unlines $ + [ "Iterable _prettify" ++ className ++ "(ast." ++ className +++ "a) => [" ] + ++ (indent 1 $ generateRuleRHS tokensReversed vars []) + ++ ["];"] -generateConcreteMapping :: Cat -> (String, [Either Cat String]) -> Maybe (String) -generateConcreteMapping cat (label, tokens) - | isNilFun label || - isOneFun label || - isConsFun label || - isConcatFun label || - isCoercion label = Nothing -- these are not represented in the AST - | otherwise = -- a standard rule - let - className = str2DartClassName label - cats = [ cat | Left cat <- tokens ] - vars = zip (map precCat cats) (getVars cats) - in Just . unlines $ - [ "Iterable _prettify" ++ className ++ "(ast." ++ className +++ "a) => [" ] - ++ (indent 1 $ generateRuleRHS tokens vars []) - ++ ["];"] + generateListPrettifier :: DartVarType -> Integer -> String -> String -> String + generateListPrettifier vType@(n, name) prec separator terminator = + "Iterable _prettify" ++ printerListName vType prec ++ "(" ++ + printerListType vType +++ "a) => [...a.expand((e" ++ show n ++ + ") => [\'" ++ separator ++ "\'," +++ + (buildArgument (n - 1, name) prec ("e" ++ show n)) ++ + "],).skip(1)," +++ "\'" ++ terminator ++ "\',];" -generateListPrettifier :: DartVarType -> Integer -> String -> String -> String -generateListPrettifier vType@(n, name) prec separator terminator = - "Iterable _prettify" ++ printerListName vType prec ++ "(" ++ - printerListType vType +++ "a) => [...a.expand((e" ++ show n ++ - ") => [\'" ++ separator ++ "\'," +++ - (buildArgument (n - 1, name) prec ("e" ++ show n)) ++ - "],).skip(1)," +++ "\'" ++ terminator ++ "\',];" + generateRuleRHS :: [Either Cat String] -> [(Integer, DartVar)] -> [String] -> [String] + generateRuleRHS [] _ lines = lines + generateRuleRHS (token:rTokens) [] lines = case token of + Right terminal -> generateRuleRHS + rTokens + [] + lines ++ (buildTerminal terminal) + Left _ -> generateRuleRHS rTokens [] lines + generateRuleRHS + (token:rTokens) + ((prec, variable@(vType, _)):rVariables) + lines = case token of + Right terminal -> generateRuleRHS + rTokens + ((prec, variable):rVariables) + lines ++ (buildTerminal terminal) + Left _ -> generateRuleRHS + rTokens + rVariables + lines ++ [ buildArgument vType prec ("a." ++ buildVariableName variable) ++ "," ] -generateRuleRHS :: [Either Cat String] -> [(Integer, DartVar)] -> [String] -> [String] -generateRuleRHS [] _ lines = lines -generateRuleRHS (token:rTokens) [] lines = case token of - Right terminal -> - generateRuleRHS rTokens [] $ lines ++ ["\"" ++ terminal ++ "\","] - Left _ -> - generateRuleRHS rTokens [] lines -generateRuleRHS (token:rTokens) ((prec, variable@(vType, _)):rVariables) lines = case token of - Right terminal -> - generateRuleRHS rTokens ((prec, variable):rVariables) $ lines ++ ["\"" ++ terminal ++ "\","] - Left _ -> generateRuleRHS rTokens rVariables $ - lines ++ [ buildArgument vType prec ("a." ++ buildVariableName variable) ++ "," ] + buildTerminal :: String -> [String] + buildTerminal = (\x -> [x]) + . ("'" ++) + . (++ "',") + . (concatMap (\c -> if c == '\\' then ['\\', '\\'] else [c])) -buildArgument :: DartVarType -> Integer -> String -> String -buildArgument (0, name) prec argument = if (censorName name) /= name - then argument ++ ".print" - else "..._prettify" ++ (str2DartClassName name) ++ "(" ++ argument ++ ")" -buildArgument vType@(n, name) prec argument = - "..._prettify" ++ printerListName vType prec ++ "(" ++ argument ++ ")" + buildArgument :: DartVarType -> Integer -> String -> String + buildArgument (0, name) prec argument = + if checkRegistered name + then argument ++ ".print" + else "..._prettify" ++ (str2DartClassName' name) ++ "(" ++ argument ++ ")" + buildArgument vType@(n, name) prec argument = "..._prettify" + ++ printerListName vType prec ++ "(" ++ argument ++ ")" -generatePrintFunction :: String -> [String] -generatePrintFunction name = [ - "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] + generatePrintFunction :: String -> [String] + generatePrintFunction name = [ + "String print" ++ name ++ "(ast." ++ name +++ "x)" +++ "=> _renderer.print(_prettify" ++ name ++ "(x));" ] -generateListPrintFunction :: DartVarType -> Integer -> String -generateListPrintFunction dvt prec = - "String print" ++ printerListName dvt prec ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt prec ++ "(x));" + generateListPrintFunction :: DartVarType -> Integer -> String + generateListPrintFunction dvt prec = + "String print" ++ printerListName dvt prec ++ "(" ++ printerListType dvt +++ "x)" +++ "=> _renderer.print(_prettify" ++ printerListName dvt prec ++ "(x));" -printerListName :: DartVarType -> Integer -> String -printerListName (0, name) prec = name ++ if prec <= 0 then "" else (show prec) -printerListName (n, name) prec = "List" ++ (printerListName (n - 1, name) prec) + printerListName :: DartVarType -> Integer -> String + printerListName (0, name) prec = name ++ if prec <= 0 then "" else (show prec) + printerListName (n, name) prec = "List" ++ (printerListName (n - 1, name) prec) -printerListType :: DartVarType -> String -printerListType (0, name) - | censorName name /= name = name - | otherwise = "ast." ++ name -printerListType (n, name) = "Iterable<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file + printerListType :: DartVarType -> String + printerListType (0, name) + | checkBuiltIn name = name + | otherwise = "ast." ++ name + printerListType (n, name) = "Iterable<" ++ printerListType (n - 1, name) ++ ">" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs index 9455609e..a1809bc4 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartSkeleton.hs @@ -13,59 +13,69 @@ import BNFC.Backend.Common.NamedVariables ( UserDef ) import BNFC.Backend.Dart.Common -cf2DartSkeleton :: CF -> String -> String -cf2DartSkeleton cf importLang = +cf2DartSkeleton :: String -> CF -> String -> String +cf2DartSkeleton langName cf importLang = unlines $ - [ importLang + [ "import 'package:fast_immutable_collections/fast_immutable_collections.dart';" + , importLang , "A identityFn(A a) => a;" ] ++ (map buildUserToken [ n | (n,_) <- tokenPragmas cf ]) -- generate user-defined types ++ (concatMap genData $ getAbstractSyntax cf) + where + censorName' = censorName langName + str2DartClassName' = str2DartClassName langName + getVars' = getVars langName + cat2DartClassName' = cat2DartClassName langName + cat2DartType' = cat2DartType langName + buildUserToken :: UserDef -> String + buildUserToken token = + "String interpret" ++ (censorName' token) ++ "(x) => x;" -buildUserToken :: UserDef -> String -buildUserToken token = - "String interpret" ++ (censorName token) ++ "(x) => x.value;" + genData :: Data -> [String] + genData (cat, rules) + | (catToStr cat) `elem` (map fst rules) = [] -- the category is also a function + | otherwise = + let name = cat2DartClassName' cat + varType = buildVariableTypeFromDartType $ cat2DartType' cat + in [ "String interpret" ++ name ++ "(" ++ varType +++ "e) =>" ] + ++ (indent 1 $ if isList cat + then [ "\"$e\";" ] + else [ "switch (e) {" ] + ++ (indent 1 $ mapMaybe genBranch rules) + ++ [ "};" ]) -genData :: Data -> [String] -genData (cat, rules) - | (catToStr cat) `elem` (map fst rules) || isList cat = [] -- the category is also a function or a list - | otherwise = - let name = cat2DartClassName cat - in [ "String interpret" ++ name ++ "(" ++ name +++ "e) => switch (e) {" ] - ++ (indent 1 $ mapMaybe genBranch rules) - ++ [ "};" ] + genBranch :: (Fun, [Cat]) -> Maybe (String) + genBranch (fun, rhs) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing -- these are not represented in the Absyn + | otherwise = -- a standard rule + let + className = str2DartClassName' fun + varName = lowerFirst $ censorName' className + vars = getVars' rhs + in Just $ + className +++ varName +++ "=> \"" ++ className ++ "(" + ++ (concat $ (drop 1) $ arguments (genVarRepr varName) vars) + ++ ")\"," + where + arguments _ [] = [] + arguments generator (x:vars) = + [ ", ", "${" ++ (generator x) ++ "}" ] ++ (arguments generator vars) -genBranch :: (Fun, [Cat]) -> Maybe (String) -genBranch (fun, rhs) - | isNilFun fun || - isOneFun fun || - isConsFun fun = Nothing -- these are not represented in the Absyn - | otherwise = -- a standard rule - let - className = str2DartClassName fun - varName = lowerFirst $ censorName className - vars = getVars rhs - in Just $ - className +++ varName +++ "=> \"" ++ className ++ "(" - ++ (concat $ (drop 1) $ arguments (genVarRepr varName) vars) - ++ ")\"," - where - arguments _ [] = [] - arguments generator (x:vars) = - [ ", ", "${" ++ (generator x) ++ "}" ] ++ (arguments generator vars) - -genVarRepr :: String -> DartVar -> String -genVarRepr varName variable@((n, varType), _) = let - varCall = varName ++ "." ++ (buildVariableName variable) - interp = interpreter varType in - if n > 0 then - varCall ++ ".map(" ++ (unpack interp (n - 1)) ++ ")" - else - interp ++ "(" ++ varCall ++ ")" - where - unpack funName n - | n <= 0 = funName - | otherwise = let varName = "e" ++ show n in - "(" ++ varName ++ ") => " ++ varName ++ ".map(" ++ (unpack funName (n - 1)) ++ ")" - interpreter varType - | varType /= (censorName varType) = "identityFn" - | otherwise = "interpret" ++ varType + genVarRepr :: String -> DartVar -> String + genVarRepr varName variable@((n, varType), _) = let + varCall = varName ++ "." ++ (buildVariableName variable) + interp = interpreter varType in + if n > 0 then + varCall ++ ".map(" ++ (unpack interp (n - 1)) ++ ")" + else + interp ++ "(" ++ varCall ++ ")" + where + unpack funName n + | n <= 0 = funName + | otherwise = let varName = "e" ++ show n in + "(" ++ varName ++ ") => " ++ varName ++ ".map(" ++ (unpack funName (n - 1)) ++ ")" + interpreter varType + | varType /= (censorName' varType) = "identityFn" + | otherwise = "interpret" ++ varType diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index 0a3e4d4e..f8181359 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -6,16 +6,17 @@ module BNFC.Backend.Dart.Common where import qualified Data.Map as Map import BNFC.CF +import Data.Maybe import qualified Data.Char as Char -cat2DartClassName :: Cat -> String -cat2DartClassName cat = str2DartClassName $ identCat $ normCat cat +cat2DartClassName :: String -> Cat -> String +cat2DartClassName langName cat = str2DartClassName langName $ identCat $ normCat cat -- Pick a class name that is appropriate for the Dart -str2DartClassName :: String -> String -str2DartClassName str = upperFirst $ censorName str +str2DartClassName :: String -> String -> String +str2DartClassName langName str = upperFirst $ censorName langName str -- Pick a class name that is appropriate for the Antlr @@ -23,28 +24,35 @@ str2AntlrClassName :: String -> String str2AntlrClassName str = upperFirst str -cat2DartType :: Cat -> DartVarType -cat2DartType cat = toList (0, cat) +cat2DartType :: String -> Cat -> DartVarType +cat2DartType langName cat = toList (0, cat) where toList :: (Int, Cat) -> DartVarType toList (n, (ListCat name)) = toList (n + 1, name) - toList (n, name) = (n, (name2DartBuiltIn $ catToStr $ normCat name)) + toList (n, name) = + ( n + , let n = catToStr $ normCat name + in case (name2DartBuiltIn n) of + Just bn -> bn + Nothing -> censor n ) + censor = censorName langName -cat2DartName :: Cat -> String -cat2DartName cat = toList $ normCat cat +cat2DartName :: String -> Cat -> String +cat2DartName langName cat = toList $ normCat cat where toList (ListCat name) = toList name ++ "List" - toList name = censorName $ catToStr name + toList name = censorName langName $ catToStr name -name2DartBuiltIn :: String -> String +name2DartBuiltIn :: String -> Maybe String name2DartBuiltIn name - | name == "Integer" = "int" - | name == "Double" = "double" - | name == "Ident" = "String" - | name == "Char" = "String" - | otherwise = name + | name == "Integer" = Just "int" + | name == "Double" = Just "double" + | name == "Ident" = Just "String" + | name == "String" = Just "String" + | name == "Char" = Just "Character" + | otherwise = Nothing upperFirst :: [Char] -> [Char] @@ -83,32 +91,34 @@ type DartVarName = (String, Int) -- Because of the different type representing variables, a different `getVars` is used. -getVars :: [Cat] -> [DartVar] -getVars cats = +getVars :: String -> [Cat] -> [DartVar] +getVars langName cats = let variables = map toUnnamedVariable cats namesMap = foldl countNames Map.empty variables scoreMap = Map.map addScore namesMap (_, vars) = foldl toDartVar (scoreMap, []) variables in vars - where - toUnnamedVariable cat = ((cat2DartType cat), (cat2DartName cat)) - countNames namesMap (_, name) = - let current = Map.findWithDefault 0 name namesMap - next = 1 + current - in Map.insert name next namesMap - addScore n = (1, n) - toDartVar (namesMap, vars) (vType, name) = - case (Map.lookup name namesMap) of - Nothing -> ( - namesMap, - vars ++ [(vType, (name, 0))]) - Just (seen, total) -> if total <= 1 - then ( + where + cat2DartName' = cat2DartName langName + cat2DartType' = cat2DartType langName + toUnnamedVariable cat = ((cat2DartType' cat), (cat2DartName' cat)) + countNames namesMap (_, name) = + let current = Map.findWithDefault 0 name namesMap + next = 1 + current + in Map.insert name next namesMap + addScore n = (1, n) + toDartVar (namesMap, vars) (vType, name) = + case (Map.lookup name namesMap) of + Nothing -> ( namesMap, vars ++ [(vType, (name, 0))]) - else ( - Map.insert name (seen + 1, total) namesMap, - vars ++ [(vType, (name, seen))]) + Just (seen, total) -> if total <= 1 + then ( + namesMap, + vars ++ [(vType, (name, 0))]) + else ( + Map.insert name (seen + 1, total) namesMap, + vars ++ [(vType, (name, seen))]) -- From a DartVar build its string representation @@ -131,13 +141,114 @@ buildVariableTypeFromDartType vType = unpack vType unpack (n, name) = "IList<" ++ unpack (n - 1, name) ++ ">" --- Prevent some type or variable name to be called as some built-in Dart type -censorName :: String -> String -censorName name - | (lowerFirst name) `elem` (map lowerFirst builtIn) = "My" ++ upperFirst name +checkBuiltIn :: String -> Bool +checkBuiltIn name = + (lowerFirst name) `elem` concatMap + (map lowerFirst) + [ builtIn, keywords ] + + +checkRegistered :: String -> Bool +checkRegistered name = + (lowerFirst name) `elem` concatMap + (map lowerFirst) + [ builtIn, keywords, taken ] + + +-- Prevent some type or variable name to be called as some already used type or keyword +censorName :: String -> String -> String +censorName langName name + | checkRegistered name = langName ++ upperFirst name | otherwise = name - where - builtIn = [ "int", "double", "String", "bool", "List", "Set", "Map", - "Runes", "Symbol", "Record", "Future", "null", "Null", "if", "else", - "return", "throw", "try", "catch", "on", "switch", "var", "final", "sync", - "async", "for", "while", "continue", "break" ] \ No newline at end of file + +taken = [ "Character" ] + +builtIn = [ "int" + , "double" + , "num" + , "String" + , "bool" + , "List" + , "Set" + , "Map" + , "Runes" + , "Symbol" + , "null" + , "Null" + , "Object" + , "Enum" + , "Future" + , "Stream" + , "Iterable" + , "Never" + , "dynamic" + , "void" ] + +keywords = [ "abstract" + , "as" + , "assert" + , "async" + , "await" + , "base" + , "break" + , "case" + , "catch" + , "class" + , "const" + , "continue" + , "covariant" + , "default" + , "deferred" + , "do" + , "dynamic" + , "else" + , "enum" + , "export" + , "extends" + , "extension" + , "external" + , "factory" + , "false" + , "final" + , "finally" + , "for" + , "Function" + , "get" + , "hide" + , "if" + , "implements" + , "import" + , "in" + , "interface" + , "is" + , "late" + , "library" + , "mixin" + , "new" + , "null" + , "of" + , "on" + , "operator" + , "part" + , "required" + , "rethrow" + , "return" + , "sealed" + , "set" + , "show" + , "static" + , "super" + , "switch" + , "sync" + , "this" + , "throw" + , "true" + , "try" + , "type" + , "typedef" + , "var" + , "void" + , "when" + , "with" + , "while" + , "yield" ] \ No newline at end of file From 28fed6974d588004bc071e112b2d858f5d17f00e Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 1 Sep 2024 13:28:20 +0300 Subject: [PATCH 50/70] add: CLI Swift option --- source/src/BNFC/Options.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index de69e412..a52c0389 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -68,6 +68,7 @@ data Target = TargetC | TargetCpp | TargetCppNoStl | TargetTreeSitter | TargetCheck | TargetDart + | TargetSwift deriving (Eq, Bounded, Enum, Ord) -- | List of Haskell target. @@ -86,6 +87,7 @@ instance Show Target where show TargetPygments = "Pygments" show TargetTreeSitter = "Tree-sitter" show TargetDart = "Dart" + show TargetSwift = "Swift" show TargetCheck = "Check LBNF file" show TargetAntlr = "ANTLRv4" @@ -304,6 +306,7 @@ printTargetOption = ("--" ++) . \case TargetPygments -> "pygments" TargetTreeSitter -> "tree-sitter" TargetDart -> "dart" + TargetSwift -> "swift" TargetCheck -> "check" TargetAntlr -> "antlr4" @@ -362,6 +365,8 @@ targetOptions = "No output. Just check input LBNF file" , Option "" ["dart"] (NoArg (\ o -> o{target = TargetDart })) "Output Dart code for use with ANTLR" + , Option "" ["swift"] (NoArg (\o -> o{target = TargetSwift})) + "Not implemented yet." , Option "" ["antlr"] (NoArg (\o -> o {target = TargetAntlr})) "Output lexer and parser grammars for ANTLRv4" ] @@ -619,6 +624,7 @@ instance Maintained Target where TargetPygments -> True TargetTreeSitter -> True TargetDart -> True + TargetSwift -> True TargetCheck -> True TargetAntlr -> True @@ -728,6 +734,7 @@ translateOldOptions = mapM $ \ o -> do [ ("-agda" , "--agda") , ("-java" , "--java") , ("-java1.5" , "--java") + , ("-swift" , "--swift") , ("-c" , "--c") , ("-cpp" , "--cpp") , ("-cpp_stl" , "--cpp") From 21498bf6c3e64023b258383ebbf9252d0e739f25 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 1 Sep 2024 15:18:07 +0300 Subject: [PATCH 51/70] Add: AST and Make file generation for Swift --- source/BNFC.cabal | 8 + source/main/Main.hs | 2 + source/src/BNFC/Backend/Swift.hs | 121 +++++++++ source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 74 ++++++ source/src/BNFC/Backend/Swift/Common.hs | 246 ++++++++++++++++++ source/src/BNFC/Options.hs | 2 +- 6 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 source/src/BNFC/Backend/Swift.hs create mode 100644 source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs create mode 100644 source/src/BNFC/Backend/Swift/Common.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 49307084..9e40372a 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -272,6 +272,14 @@ library BNFC.Backend.Dart.CFtoDartPrinter BNFC.Backend.Dart.CFtoDartSkeleton + -- Swift backend + BNFC.Backend.Swift + BNFC.Backend.Swift.CFtoSwiftAST + -- BNFC.Backend.Swift.CFtoSwiftBuilder + BNFC.Backend.Swift.Common + -- BNFC.Backend.Swift.CFtoSwiftPrinter + -- BNFC.Backend.Swift.CFtoSwiftSkeleton + -- Antlr4 backend BNFC.Backend.Antlr BNFC.Backend.Antlr.CFtoAntlr4Lexer diff --git a/source/main/Main.hs b/source/main/Main.hs index eb8e8a3d..554841d9 100644 --- a/source/main/Main.hs +++ b/source/main/Main.hs @@ -26,6 +26,7 @@ import BNFC.Backend.Latex import BNFC.Backend.OCaml import BNFC.Backend.Pygments import BNFC.Backend.Dart (makeDart) +import BNFC.Backend.Swift (makeSwift) import BNFC.Backend.Antlr import BNFC.Backend.TreeSitter import BNFC.CF (CF) @@ -84,6 +85,7 @@ maketarget = \case TargetOCaml -> makeOCaml TargetPygments -> makePygments TargetDart -> makeDart + TargetSwift -> makeSwift TargetAntlr -> makeAntlr TargetCheck -> error "impossible" TargetTreeSitter -> makeTreeSitter diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs new file mode 100644 index 00000000..b1b939e0 --- /dev/null +++ b/source/src/BNFC/Backend/Swift.hs @@ -0,0 +1,121 @@ +{-# LANGUAGE RecordWildCards #-} + +module BNFC.Backend.Swift ( makeSwift ) where + +import Text.PrettyPrint ( text, vcat, render, nest ) + +import Prelude hiding ((<>)) +import System.FilePath ((), pathSeparator) +import System.Directory ( createDirectoryIfMissing ) +import Data.Char (toLower) + +import BNFC.Backend.Base (MkFiles, mkfile,liftIO) +import BNFC.CF (CF, getAbstractSyntax, firstEntry, catToStr, identCat, normCat ) +import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Swift)) +import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) +import BNFC.Backend.Common.Makefile as MakeFile +import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) +import BNFC.Backend.Antlr (makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) +import BNFC.Backend.Swift.CFtoSwiftAST ( cf2SwiftAST ) +import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName ) + +makeSwift :: SharedOptions -> CF -> MkFiles () +makeSwift opts@Options{..} cf = do + let dirBase = replace '.' pathSeparator $ packageName + langBase = dirBase (langName ++ "_generated") + libLang = langBase "lib" + srcLang = libLang "src" + libBase = dirBase "lib" + binBase = dirBase "bin" + directoryOptions = DirectoryOptions{baseDirectory = Just srcLang, nameStyle = Just SnakeCase} + + -- Generates files in an incorrect place + + makeAntlr' (opts {dLanguage = Swift, optMake = Nothing}) cf directoryOptions + MakeFile.mkMakefile optMake $ makefileContent srcLang + + mkfile (srcLang "ast.swift") makeSwiftComment astContent + + where + astContent = cf2SwiftAST (firstUpperCase langName) cf + mainContent = unlines + [ "import '../lib/test.swift';" + , "void main(List args) {" + , " final test = Test();" + , " test.run(args);" + , "}" ] + packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang + langName = firstLowerCase $ mkName [] SnakeCase lang + importLangName = "import 'package:" ++ langName ++ "_generated/" ++ langName ++ "_generated.Swift';" + + pubspecContent moduleName desc deps = unlines ( + [ "name:" +++ moduleName + , "description:" +++ desc + , "version: 1.0.0" + , "publish_to: 'none'" + , "environment:" + , " sdk: ^3.4.0" + , "dependencies:" + , " antlr4: ^4.13.1" + , " fast_immutable_collections: ^10.2.2" + ] ++ (indent 1 deps) ++ [ "dev_dependencies:" + , " lints: ^4.0.0" ]) + + lexerClassName = lang ++ "GrammarLexer" + parserClassName = lang ++ "GrammarParser" + + makeVars x = [MakeFile.mkVar n v | (n,v) <- x] + makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] + + makefileVars = vcat $ makeVars + [("LANG", langName) + , ("LEXER_NAME", langName ++ "_lexer") + , ("PARSER_NAME", langName ++ "_parser") + , ("ANTLR4", "java -Xmx500M -cp \"/usr/local/lib/antlr-4.13.1-complete.jar:$CLASSPATH\" org.antlr.v4.Tool") + ] + + refVarInSrc srcLang refVar = srcLang MakeFile.refVar refVar + + rmFile :: (String -> String) -> String -> String -> String + rmFile refSrcVar refVar ext = "rm -f" +++ refSrcVar refVar ++ ext + + makefileRules refSrcVar = + let rmInSrc = rmFile refSrcVar + in vcat $ makeRules + [ (".PHONY", ["all", "clean", "remove"], []) + , ("all", [MakeFile.refVar "LANG"], []) + , ("lexer" + , [refSrcVar "LEXER_NAME" ++ ".g4"] + , [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Swift" +++ refSrcVar "LEXER_NAME" ++ ".g4"]) + , ("parser" + , [refSrcVar "PARSER_NAME" ++ ".g4"] + , [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Swift" +++ "-no-listener" +++ "-no-visitor" +++ refSrcVar "PARSER_NAME" ++ ".g4"]) + , ("install-deps-external" + , [MakeFile.refVar "LANG" "pubspec.yaml"] + , ["cd" +++ (MakeFile.refVar "LANG") ++ "; Swift pub get"]) + , ("install-deps-internal" + , [MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated") "pubspec.yaml"] + , ["cd" +++ (MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated")) ++ "; Swift pub get"]) + , (MakeFile.refVar "LANG", ["lexer", "parser", "clean", "install-deps-external", "install-deps-internal"], []) + , ("clean", [], + [ + rmInSrc "LEXER_NAME" ".interp" + , rmInSrc "LEXER_NAME" ".tokens" + , rmInSrc "PARSER_NAME" ".interp" + , rmInSrc "PARSER_NAME" ".tokens" + , rmInSrc "LEXER_NAME" ".g4" + , rmInSrc "PARSER_NAME" ".g4" + ]) + , ("remove", [], ["rm -rf" +++ MakeFile.refVar "LANG"]) + ] + + makefileContent srcLang _ = vcat [makefileVars, "", makefileRules $ refVarInSrc srcLang, ""] + +makeSwiftComment :: String -> String +makeSwiftComment = ("// Swift " ++) + +makeSwiftCommentYaml :: String -> String +makeSwiftCommentYaml = ("# Swift" ++) + +toLowerCase :: String -> String +toLowerCase = map toLower diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs new file mode 100644 index 00000000..2ea0601e --- /dev/null +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Swift.CFtoSwiftAST (cf2SwiftAST) where + +import Data.Maybe ( mapMaybe ) + +import BNFC.CF +import BNFC.Utils ( (+++) ) + +import BNFC.Backend.Common.NamedVariables ( UserDef ) +import BNFC.Backend.Swift.Common +import Data.List (intercalate) + +-- Produces abstract data types in Swift +cf2SwiftAST :: String -> CF -> String +cf2SwiftAST langName cf = + let userTokens = [ n | (n,_) <- tokenPragmas cf ] + in unlines + $ imports ++ [""]-- import some libraries if needed + ++ characterTypedef + ++ generateTokens userTokens ++ [""] + ++ concatMap prData rules -- generate user-defined types + where + rules = getAbstractSyntax cf + imports = [ + "import Foundation" + ] + characterTypedef = [ "typealias Character = String"] + censorName' = censorName langName + str2SwiftClassName' = str2SwiftClassName langName + str2SwiftCaseName' = str2SwiftCaseName langName + cat2SwiftClassName' = cat2SwiftClassName langName + getVars' = getVars langName + + + generateTokens :: [UserDef] -> [String] + generateTokens = map $ \token -> + let name = censorName' token + in "typealias" +++ name +++ "= String;" + + + -- | Generates a category class, and classes for all its rules. + prData :: Data -> [String] + prData (cat, rules) = categoryClass + where + funs = map fst rules + cases = mapMaybe (prRule cat) rules + categoryClass + | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + | otherwise = + let name = cat2SwiftClassName' cat + in + [ "indirect enum" +++ name +++ "{" + ] ++ indent 1 cases ++ ["}\n"] + + + -- | Generates classes for a rule, depending on what type of rule it is. + prRule :: Cat -> (Fun, [Cat]) -> Maybe (String) + prRule cat (fun, cats) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing -- these are not represented in the Absyn + | otherwise = -- a standard rule + Just result + where + caseName = str2SwiftCaseName' fun + vars = getVars' cats + caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars + resultAssociatedValuesConcatenated + | null vars = "" + | otherwise = "(" ++ (intercalate ", " caseAssociatedValues) ++ ")" + result = unwords $ ["case", caseName ++ resultAssociatedValuesConcatenated] \ No newline at end of file diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs new file mode 100644 index 00000000..688ab585 --- /dev/null +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -0,0 +1,246 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Swift.Common where + +import qualified Data.Map as Map +import BNFC.CF +import Data.Maybe +import qualified Data.Char as Char + + +cat2SwiftClassName :: String -> Cat -> String +cat2SwiftClassName langName cat = str2SwiftClassName langName $ identCat $ normCat cat + + +-- Pick a class name that is appropriate for the Swift +str2SwiftClassName :: String -> String -> String +str2SwiftClassName langName str = upperFirst $ censorName langName str + +-- Pick a case name that is appropriate for the Swift +str2SwiftCaseName :: String -> String -> String +str2SwiftCaseName langName str = lowerFirst $ censorName langName str + +-- Pick a class name that is appropriate for the Antlr +str2AntlrClassName :: String -> String +str2AntlrClassName str = upperFirst str + + +cat2SwiftType :: String -> Cat -> SwiftVarType +cat2SwiftType langName cat = toList (0, cat) + where + toList :: (Int, Cat) -> SwiftVarType + toList (n, (ListCat name)) = toList (n + 1, name) + toList (n, name) = + ( n + , let n = catToStr $ normCat name + in case (name2SwiftBuiltIn n) of + Just bn -> bn + Nothing -> censor n ) + censor = censorName langName + + +cat2SwiftName :: String -> Cat -> String +cat2SwiftName langName cat = toList $ normCat cat + where + toList (ListCat name) = toList name ++ "List" + toList name = censorName langName $ catToStr name + + +name2SwiftBuiltIn :: String -> Maybe String +name2SwiftBuiltIn name + | name == "Integer" = Just "Int" + | name == "Double" = Just "Double" + | name == "Ident" = Just "String" + | name == "String" = Just "String" + | name == "Char" = Just "Character" + | otherwise = Nothing + + +upperFirst :: [Char] -> [Char] +upperFirst [] = [] +upperFirst (letter:rest) = Char.toUpper letter : rest + + +lowerFirst :: [Char] -> [Char] +lowerFirst [] = [] +lowerFirst (letter:rest) = Char.toLower letter : rest + + +indent :: Int -> [String] -> [String] +indent n lines = map addSpaces lines + where + addSpaces :: String -> String + addSpaces line = (replicate (2 * n) ' ') ++ line + + +-- The type of an instance variable. +-- Variable type, and its name +type SwiftVar = (SwiftVarType, SwiftVarName) + + +-- The type of a variable type in Swift. +-- The amount of nestings, and the underlying type name without precedence. +-- Example: List> is (2, Expr). +-- This helps to build the AST builder +type SwiftVarType = (Int, String) + + +-- The name of a variable. +-- the name generated from the type, +-- and the number making this variable unique +type SwiftVarName = (String, Int) + + +-- Because of the different type representing variables, a different `getVars` is used. +getVars :: String -> [Cat] -> [SwiftVar] +getVars langName cats = + let variables = map toUnnamedVariable cats + namesMap = foldl countNames Map.empty variables + scoreMap = Map.map addScore namesMap + (_, vars) = foldl toSwiftVar (scoreMap, []) variables + in vars + where + cat2SwiftName' = cat2SwiftName langName + cat2SwiftType' = cat2SwiftType langName + toUnnamedVariable cat = ((cat2SwiftType' cat), (cat2SwiftName' cat)) + countNames namesMap (_, name) = + let current = Map.findWithDefault 0 name namesMap + next = 1 + current + in Map.insert name next namesMap + addScore n = (1, n) + toSwiftVar (namesMap, vars) (vType, name) = + case (Map.lookup name namesMap) of + Nothing -> ( + namesMap, + vars ++ [(vType, (name, 0))]) + Just (seen, total) -> if total <= 1 + then ( + namesMap, + vars ++ [(vType, (name, 0))]) + else ( + Map.insert name (seen + 1, total) namesMap, + vars ++ [(vType, (name, seen))]) + + +-- From a SwiftVar build its string representation +buildVariableName :: SwiftVar -> String +buildVariableName (_, (name, num)) = lowerFirst appendNumber + where + appendNumber + | num <= 0 = name + | otherwise = name ++ show num + + +-- From a SwiftVar make a name for the AST +buildVariableType :: SwiftVar -> String +buildVariableType (vType, _) = buildVariableTypeFromSwiftType vType + +buildVariableTypeFromSwiftType :: SwiftVarType -> String +buildVariableTypeFromSwiftType vType = unpack vType + where + unpack (0, name) = name + unpack (n, name) = "[" ++ unpack (n - 1, name) ++ "]" + + +checkBuiltIn :: String -> Bool +checkBuiltIn name = + (lowerFirst name) `elem` concatMap + (map lowerFirst) + [ builtIn, keywords ] + + +checkRegistered :: String -> Bool +checkRegistered name = + (lowerFirst name) `elem` concatMap + (map lowerFirst) + [ builtIn, keywords, taken ] + + +-- Prevent some type or variable name to be called as some already used type or keyword +censorName :: String -> String -> String +censorName langName name + | checkRegistered name = langName ++ upperFirst name + | otherwise = name + +taken = [ "Character" ] + +builtIn = [ "Int" + , "Double" + , "Float" + , "String" + , "Bool" + , "Set" + , "Void" + , "Dictionary" + , "Optional" + , "Any" ] + +keywords = [ "abstract" + , "as" + , "assert" + , "async" + , "await" + , "base" + , "break" + , "case" + , "catch" + , "class" + , "const" + , "continue" + , "covariant" + , "default" + , "deferred" + , "do" + , "dynamic" + , "else" + , "enum" + , "export" + , "extends" + , "extension" + , "external" + , "factory" + , "false" + , "final" + , "finally" + , "for" + , "Function" + , "get" + , "hide" + , "if" + , "implements" + , "import" + , "in" + , "interface" + , "is" + , "late" + , "library" + , "mixin" + , "new" + , "null" + , "of" + , "on" + , "operator" + , "part" + , "required" + , "rethrow" + , "return" + , "set" + , "show" + , "static" + , "super" + , "switch" + , "sync" + , "this" + , "throw" + , "true" + , "try" + , "type" + , "typealias" + , "var" + , "void" + , "when" + , "with" + , "while" + , "yield" ] \ No newline at end of file diff --git a/source/src/BNFC/Options.hs b/source/src/BNFC/Options.hs index a52c0389..db33d22d 100644 --- a/source/src/BNFC/Options.hs +++ b/source/src/BNFC/Options.hs @@ -366,7 +366,7 @@ targetOptions = , Option "" ["dart"] (NoArg (\ o -> o{target = TargetDart })) "Output Dart code for use with ANTLR" , Option "" ["swift"] (NoArg (\o -> o{target = TargetSwift})) - "Not implemented yet." + "Output Swift code for use with ANTLR" , Option "" ["antlr"] (NoArg (\o -> o {target = TargetAntlr})) "Output lexer and parser grammars for ANTLRv4" ] From 18b748a3ea181d9c06448003bd1fea92c4b8da38 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Mon, 2 Sep 2024 17:01:56 +0300 Subject: [PATCH 52/70] add first steps in builder, change generation structure, minor changes in AST generation --- source/BNFC.cabal | 2 +- source/src/BNFC/Backend/Swift.hs | 44 +++-- .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 156 ++++++++++++++++++ source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 9 +- source/src/BNFC/Backend/Swift/Common.hs | 75 ++++++++- 5 files changed, 258 insertions(+), 28 deletions(-) create mode 100644 source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 9e40372a..8e2f4b7a 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -275,7 +275,7 @@ library -- Swift backend BNFC.Backend.Swift BNFC.Backend.Swift.CFtoSwiftAST - -- BNFC.Backend.Swift.CFtoSwiftBuilder + BNFC.Backend.Swift.CFtoSwiftBuilder BNFC.Backend.Swift.Common -- BNFC.Backend.Swift.CFtoSwiftPrinter -- BNFC.Backend.Swift.CFtoSwiftSkeleton diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index b1b939e0..4de0573f 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -15,29 +15,35 @@ import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) import BNFC.Backend.Common.Makefile as MakeFile import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) -import BNFC.Backend.Antlr (makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) +import BNFC.Backend.Antlr (makeAntlr, makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) import BNFC.Backend.Swift.CFtoSwiftAST ( cf2SwiftAST ) +import BNFC.Backend.Swift.CFtoSwiftBuilder ( cf2SwiftBuilder ) import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName ) makeSwift :: SharedOptions -> CF -> MkFiles () makeSwift opts@Options{..} cf = do let dirBase = replace '.' pathSeparator $ packageName - langBase = dirBase (langName ++ "_generated") - libLang = langBase "lib" - srcLang = libLang "src" - libBase = dirBase "lib" - binBase = dirBase "bin" - directoryOptions = DirectoryOptions{baseDirectory = Just srcLang, nameStyle = Just SnakeCase} + -- langBase = dirBase (langName ++ "_generated") + -- libLang = langBase "lib" + -- srcLang = libLang "src" + -- libBase = dirBase "lib" + -- binBase = dirBase "bin" + + -- directoryOptions = DirectoryOptions{baseDirectory = Just dirBase, nameStyle = Just SnakeCase} -- Generates files in an incorrect place - makeAntlr' (opts {dLanguage = Swift, optMake = Nothing}) cf directoryOptions - MakeFile.mkMakefile optMake $ makefileContent srcLang + makeAntlr (opts {dLanguage = Swift, optMake = Nothing}) cf + -- makeAntlr (opts {dLanguage = Swift, optMake = Nothing}) cf + MakeFile.mkMakefile optMake $ makefileContent dirBase - mkfile (srcLang "ast.swift") makeSwiftComment astContent + mkfile (dirBase "ast.swift") makeSwiftComment astContent + mkfile (dirBase "builder.swift") makeSwiftComment builderContent where astContent = cf2SwiftAST (firstUpperCase langName) cf + -- builderContent = cf2SwiftBuilder (firstUpperCase langName) cf + builderContent = cf2SwiftBuilder cf opts mainContent = unlines [ "import '../lib/test.swift';" , "void main(List args) {" @@ -46,7 +52,8 @@ makeSwift opts@Options{..} cf = do , "}" ] packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang langName = firstLowerCase $ mkName [] SnakeCase lang - importLangName = "import 'package:" ++ langName ++ "_generated/" ++ langName ++ "_generated.Swift';" + langNameUpperCased = firstUpperCase langName + importLangName = "import 'package:" ++ langName ++ "_generated/" ++ langName ++ "_generated.swift';" pubspecContent moduleName desc deps = unlines ( [ "name:" +++ moduleName @@ -58,7 +65,7 @@ makeSwift opts@Options{..} cf = do , "dependencies:" , " antlr4: ^4.13.1" , " fast_immutable_collections: ^10.2.2" - ] ++ (indent 1 deps) ++ [ "dev_dependencies:" + ] ++ deps ++ [ "dev_dependencies:" , " lints: ^4.0.0" ]) lexerClassName = lang ++ "GrammarLexer" @@ -68,13 +75,14 @@ makeSwift opts@Options{..} cf = do makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] makefileVars = vcat $ makeVars - [("LANG", langName) - , ("LEXER_NAME", langName ++ "_lexer") - , ("PARSER_NAME", langName ++ "_parser") - , ("ANTLR4", "java -Xmx500M -cp \"/usr/local/lib/antlr-4.13.1-complete.jar:$CLASSPATH\" org.antlr.v4.Tool") + [("LANG", langNameUpperCased) + , ("LEXER_NAME", langNameUpperCased ++ "Lexer") + , ("PARSER_NAME", langNameUpperCased ++ "Parser") + -- , ("ANTLR4", "java -Xmx500M -cp \"/usr/local/lib/antlr-4.13.1-complete.jar:$CLASSPATH\" org.antlr.v4.Tool") + , ("ANTLR4", "antlr4") -- installed using pip ] - refVarInSrc srcLang refVar = srcLang MakeFile.refVar refVar + refVarInSrc dirBase refVar = dirBase MakeFile.refVar refVar rmFile :: (String -> String) -> String -> String -> String rmFile refSrcVar refVar ext = "rm -f" +++ refSrcVar refVar ++ ext @@ -109,7 +117,7 @@ makeSwift opts@Options{..} cf = do , ("remove", [], ["rm -rf" +++ MakeFile.refVar "LANG"]) ] - makefileContent srcLang _ = vcat [makefileVars, "", makefileRules $ refVarInSrc srcLang, ""] + makefileContent dirBase _ = vcat [makefileVars, "", makefileRules $ refVarInSrc dirBase, ""] makeSwiftComment :: String -> String makeSwiftComment = ("// Swift " ++) diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs new file mode 100644 index 00000000..fae2adc6 --- /dev/null +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -0,0 +1,156 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Swift.CFtoSwiftBuilder (cf2SwiftBuilder) where + +import Data.Bifunctor (Bifunctor(second)) +import Data.List (intercalate, nub, intersperse) +import Data.Maybe (mapMaybe) + +import Text.PrettyPrint.HughesPJClass (Doc, text, vcat) + +import BNFC.Utils ((+++), camelCase_) +import BNFC.CF (CF, Cat (ListCat, TokenCat, Cat), identCat, isList, IsFun (isNilFun, isOneFun, isConsFun, isCoercion), catToStr, ruleGroups, Rul (rhsRule, funRule), SentForm, WithPosition (wpThing)) +import BNFC.Backend.Swift.Common (indentStr, wrapSQ, catToSwiftType, getVarsFromCats, mkTokenNodeName, indent, getAllTokenCats, getAllTokenTypenames) +import BNFC.Options (SharedOptions (lang)) +import BNFC.Backend.Antlr.CFtoAntlr4Parser (antlrRuleLabel, makeLeftRecRule) +import BNFC.Backend.Common.NamedVariables (firstUpperCase) + +type RuleData = (Cat, [(String, SentForm)]) + +cf2SwiftBuilder :: CF -> SharedOptions -> Doc +cf2SwiftBuilder cf opts = vcat $ intersperse (text "") + [ importDecls + , errorsDecl + , tokenDecls + , buildFnDecls + ] + where + language = lang opts + importDecls = mkImportDecls cf language + + errorsDecl = buildErrors + tokenDecls = vcat $ intersperse (text "") buildTokensFuns + buildFnDecls = vcat $ intersperse (text "") buildFuns + + buildFuns = map (mkBuildFunction language) datas + buildTokensFuns = map mkBuildTokenFunction allTokenCats + + allTokenCats = getAllTokenCats cf + datas = cfToGroups cf + +buildErrors :: Doc +buildErrors = vcat + [ "enum BuildError: Error {" + , indent 2 "case UnexpectedParseContext(String)" + , "}" + ] + +mkThrowErrorStmt :: Cat -> String +mkThrowErrorStmt cat = "throw BuildError.UnexpectedParseContext(\"Error: ctx should be an instance of" +++ camelCase_ (identCat cat) ++ "Context" ++ "\")" + +-- | generates function code for building appropriate node for TokenCat. +mkBuildTokenFunction :: Cat -> Doc +mkBuildTokenFunction tokenCat = vcat + [ text $ "func" +++ fnName ++ "(ctx: Token) throws ->" +++ returnType +++ "{" + , indent 2 "return {" + , indent 4 $ "type:" +++ mkTokenNodeName tokenName ++ "," + , indent 4 $ "value:" +++ value + , indent 2 "}" + , "}" + ] + where + tokenName = catToStr tokenCat + fnName = mkBuildFnName tokenCat + returnType = catToSwiftType tokenCat + value = case tokenName of + "Integer" -> "Int(ctx.INTEGER()!.getText())!" + "Double" -> "Float(ctx.text)!" + _ -> "ctx.text" + +-- | generate name for function which will build node for some cat. +mkBuildFnName :: Cat -> String +mkBuildFnName cat = "build" ++ firstUpperCase (restName cat) + where + restName cat = case cat of + ListCat cat -> restName cat ++ "List" + TokenCat cat -> cat ++ "Token" + otherCat -> catToStr otherCat + +-- | generates import declarations for antlr nodes and AST nodes. +mkImportDecls :: CF -> String -> Doc +mkImportDecls cf lang = vcat + [ "import Foundation" + , "import Antlr4" + ] + +mkBuildFunction :: String -> RuleData -> Doc +mkBuildFunction lang (cat, rulesWithLabels) = vcat + [ text $ "func" +++ mkBuildFnName cat ++ "(_ ctx: " ++ (addParserPrefix lang $ identCat cat) ++ "Context) throws ->" +++ catToSwiftType cat +++ "{" + , indent 2 "switch ctx {" + , vcat $ map mkCaseStmt datas + , indent 4 "default:" + , indent 6 $ mkThrowErrorStmt cat + , indent 2 "}" + , "}" + ] + where + datas = zip rulesWithLabels [1..] + + mkCaseStmt :: ((String, SentForm), Integer) -> Doc + mkCaseStmt ((ruleLabel, rhsRule), ifIdx) = vcat + [ indent 4 $ "case let ctx as" +++ addParserPrefix lang (antlrRuleLabel cat ruleLabel antlrRuleLabelIdx) ++ "Context:" + , vcat $ map text $ mCaseBody ruleLabel + ] + + where + antlrRuleLabelIdx = if isCoercion ruleLabel then Just ifIdx else Nothing + rhsRuleWithIdx = mapMaybe (\(rule, idx) -> either (\cat -> Just (cat, idx)) (\_ -> Nothing) rule) $ zip rhsRule [1..] + mkPattern idx = "p_" ++ show ifIdx ++ "_" ++ show idx + -- mkPattern idx = "expr(" ++ show idx ++ ")!" + + mCaseBody ruleLabel + | isCoercion ruleLabel = map (\(cat, idx) -> indentStr 6 $ "return try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx + | isNilFun ruleLabel = emptyListBody + | isOneFun ruleLabel = oneListBody + | isConsFun ruleLabel = consListBody + | otherwise = + concat + [ zipWith + (\ (cat, idx) varName + -> indentStr 6 + $ "let" +++ varName + +++ "= try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") + rhsRuleWithIdx varNames + , [ indentStr 6 "return" +++ "." ++ ruleLabel ++ "(" ++ intercalate ", " varNames ++ ")"] + ] + where + varNames = getVarsFromCats rhsCats + rhsCats = map fst rhsRuleWithIdx + + emptyListBody = [indentStr 4 "return []"] + oneListBody = map (\(cat, idx) -> indentStr 6 $ "let data = try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx ++ [ indentStr 4 "return [data]"] + consListBody = + [ indentStr 4 $ "let value1 = try" +++ mkBuildFnName firstCat ++ "(ctx." ++ mkPattern firstIdx ++ ")" + , indentStr 4 $ "let value2 = try" +++ mkBuildFnName secondCat ++ "(ctx." ++ mkPattern secondIdx ++ ")" + , indentStr 4 $ "let" +++ resultList + ] + where + (firstCat, firstIdx) = head rhsRuleWithIdx + (secondCat, secondIdx) = rhsRuleWithIdx !! 1 + (itemVar, listVar) = if isList firstCat then ("value2", "value1") else ("value1", "value2") + resultList = if isList firstCat + then + "[..." ++ listVar ++ ", " ++ itemVar ++ "]" + else + "[" ++ itemVar ++ ", ..." ++ listVar ++ "]" + +cfToGroups :: CF -> [RuleData] +cfToGroups cf = map (second (map (ruleToData . makeLeftRecRule cf))) $ ruleGroups cf + where + ruleToData rule = ((wpThing . funRule) rule, rhsRule rule) + + +addParserPrefix :: String -> String -> String +addParserPrefix lang name = lang ++ "Parser." ++ name \ No newline at end of file diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index 2ea0601e..b292bf68 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -32,7 +32,7 @@ cf2SwiftAST langName cf = str2SwiftClassName' = str2SwiftClassName langName str2SwiftCaseName' = str2SwiftCaseName langName cat2SwiftClassName' = cat2SwiftClassName langName - getVars' = getVars langName + getVars' = getVars_ langName generateTokens :: [UserDef] -> [String] @@ -53,7 +53,7 @@ cf2SwiftAST langName cf = let name = cat2SwiftClassName' cat in [ "indirect enum" +++ name +++ "{" - ] ++ indent 1 cases ++ ["}\n"] + ] ++ indent_ 1 cases ++ ["}\n"] -- | Generates classes for a rule, depending on what type of rule it is. @@ -65,9 +65,10 @@ cf2SwiftAST langName cf = | otherwise = -- a standard rule Just result where - caseName = str2SwiftCaseName' fun + caseName = str2SwiftClassName' fun vars = getVars' cats - caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars + -- caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars + caseAssociatedValues = map (\var -> buildVariableType var) vars resultAssociatedValuesConcatenated | null vars = "" | otherwise = "(" ++ (intercalate ", " caseAssociatedValues) ++ ")" diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index 688ab585..f13513e1 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -4,10 +4,15 @@ module BNFC.Backend.Swift.Common where +import Text.PrettyPrint (Doc, text) import qualified Data.Map as Map import BNFC.CF import Data.Maybe import qualified Data.Char as Char +import Data.Char (toLower) +import BNFC.CF (Cat (TokenCat, ListCat), catToStr, normCat, Data, CF, isList, getAbstractSyntax, literals) +import BNFC.Utils (mkName, NameStyle (OrigCase, MixedCase), mkNames) +import BNFC.Backend.Common.NamedVariables (getVars, firstLowerCase, firstUpperCase) cat2SwiftClassName :: String -> Cat -> String @@ -68,8 +73,15 @@ lowerFirst [] = [] lowerFirst (letter:rest) = Char.toLower letter : rest -indent :: Int -> [String] -> [String] -indent n lines = map addSpaces lines +indent_ :: Int -> [String] -> [String] +indent_ n lines = map addSpaces lines + where + addSpaces :: String -> String + addSpaces line = (replicate (2 * n) ' ') ++ line + + +indentString :: Int -> String -> String +indentString n line = addSpaces line where addSpaces :: String -> String addSpaces line = (replicate (2 * n) ' ') ++ line @@ -94,8 +106,8 @@ type SwiftVarName = (String, Int) -- Because of the different type representing variables, a different `getVars` is used. -getVars :: String -> [Cat] -> [SwiftVar] -getVars langName cats = +getVars_ :: String -> [Cat] -> [SwiftVar] +getVars_ langName cats = let variables = map toUnnamedVariable cats namesMap = foldl countNames Map.empty variables scoreMap = Map.map addScore namesMap @@ -243,4 +255,57 @@ keywords = [ "abstract" , "when" , "with" , "while" - , "yield" ] \ No newline at end of file + , "yield" ] + +-- from TS implementation + +reservedKeywords :: [String] +reservedKeywords = builtIn ++ keywords + +toMixedCase :: String -> String +-- toMixedCase = firstLowerCase . mkName reservedKeywords MixedCase +toMixedCase = firstUpperCase . mkName reservedKeywords MixedCase + +-- | wrap string into single quotes. +wrapSQ :: String -> String +wrapSQ str = "'" ++ str ++ "'" + +-- | indent string with N spaces. +indentStr :: Int -> String -> String +indentStr size = (replicate size ' ' ++) + +mkTokenNodeName :: String -> String +mkTokenNodeName tokenName = wrapSQ (tokenName ++ "Token") + +-- | derive name for TS type from category. +catToSwiftType :: Cat -> String +catToSwiftType (ListCat c) = "[" ++ catToSwiftType c ++ "]" +catToSwiftType (TokenCat c) = toMixedCase (c ++ "Token") +catToSwiftType cat = toMixedCase (catToStr cat) + +-- | get variable names which will be used in node structure +-- for categories used in production rule. +getVarsFromCats :: [Cat] -> [String] +getVarsFromCats cats = mkNames ["type"] OrigCase normalizedVars + where + normalizedCats = map normCat cats + indexedVars = getVars normalizedCats + + normalizeVar :: (String, Int) -> String + normalizeVar (varName, idx) = map toLower varName ++ varNameSuffix + where + varNameSuffix = if idx == 0 then "" else show idx + + normalizedVars = map normalizeVar indexedVars + +-- | indent string with N spaces and transform to Doc. +indent :: Int -> String -> Doc +indent size str = text (indentStr size str) + +-- | get used tokens represented as cats +getAllTokenCats :: CF -> [Cat] +getAllTokenCats cf = map TokenCat (literals cf) + +-- | get TS type names for all tokens +getAllTokenTypenames :: CF -> [String] +getAllTokenTypenames cf = map catToSwiftType (getAllTokenCats cf) \ No newline at end of file From 5cb52d999f2e4dd2dcc86f9ca1c4801843c70005 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Tue, 3 Sep 2024 16:36:00 +0300 Subject: [PATCH 53/70] Add: wrappers for build in Swift types --- source/src/BNFC/Backend/Swift.hs | 1 + .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 11 +++---- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 29 ++++++++++++++----- source/src/BNFC/Backend/Swift/Common.hs | 15 +++++----- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 4de0573f..17d0095b 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -42,6 +42,7 @@ makeSwift opts@Options{..} cf = do where astContent = cf2SwiftAST (firstUpperCase langName) cf + -- astContent = cf2SwiftAST cf -- builderContent = cf2SwiftBuilder (firstUpperCase langName) cf builderContent = cf2SwiftBuilder cf opts mainContent = unlines diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index fae2adc6..7d24377b 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -53,11 +53,8 @@ mkThrowErrorStmt cat = "throw BuildError.UnexpectedParseContext(\"Error: ctx sho -- | generates function code for building appropriate node for TokenCat. mkBuildTokenFunction :: Cat -> Doc mkBuildTokenFunction tokenCat = vcat - [ text $ "func" +++ fnName ++ "(ctx: Token) throws ->" +++ returnType +++ "{" - , indent 2 "return {" - , indent 4 $ "type:" +++ mkTokenNodeName tokenName ++ "," - , indent 4 $ "value:" +++ value - , indent 2 "}" + [ text $ "func" +++ fnName ++ "(_ ctx: Token) throws ->" +++ returnType +++ "{" + , indent 2 $ "return" +++ returnType ++ "(" ++ value ++ ")" , "}" ] where @@ -65,8 +62,8 @@ mkBuildTokenFunction tokenCat = vcat fnName = mkBuildFnName tokenCat returnType = catToSwiftType tokenCat value = case tokenName of - "Integer" -> "Int(ctx.INTEGER()!.getText())!" - "Double" -> "Float(ctx.text)!" + "Integer" -> "Int(ctx.getText()!)!" + "Double" -> "Double(ctx.getText()!)!" _ -> "ctx.text" -- | generate name for function which will build node for some cat. diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index b292bf68..a6d7d8ac 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -15,12 +15,10 @@ import Data.List (intercalate) -- Produces abstract data types in Swift cf2SwiftAST :: String -> CF -> String -cf2SwiftAST langName cf = - let userTokens = [ n | (n,_) <- tokenPragmas cf ] - in unlines +cf2SwiftAST langName cf = unlines $ imports ++ [""]-- import some libraries if needed - ++ characterTypedef - ++ generateTokens userTokens ++ [""] + -- ++ characterTypedef + ++ map mkTokenDecl allTokenNames ++ concatMap prData rules -- generate user-defined types where rules = getAbstractSyntax cf @@ -33,12 +31,29 @@ cf2SwiftAST langName cf = str2SwiftCaseName' = str2SwiftCaseName langName cat2SwiftClassName' = cat2SwiftClassName langName getVars' = getVars_ langName - + allTokenNames = literals cf generateTokens :: [UserDef] -> [String] generateTokens = map $ \token -> let name = censorName' token in "typealias" +++ name +++ "= String;" + + -- | valueType is a string which represents Swift basic type. + mkTokenDecl :: String -> String + mkTokenDecl tokenName = unlines + [ "struct" +++ catToSwiftType (TokenCat tokenName) +++ "{" + , indentStr 2 $ "let value: " ++ value + , "" + , indentStr 2 $ "init(_ value:" +++ value ++ ") {" + , indentStr 4 $ "self.value = value" + , indentStr 2 $ "}" + , "}" + ] + where + value + | tokenName == catInteger = "Int" + | tokenName == catDouble = "Double" + | otherwise = "String" -- | Generates a category class, and classes for all its rules. @@ -68,7 +83,7 @@ cf2SwiftAST langName cf = caseName = str2SwiftClassName' fun vars = getVars' cats -- caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars - caseAssociatedValues = map (\var -> buildVariableType var) vars + caseAssociatedValues = map (\var -> catToSwiftType var) cats resultAssociatedValuesConcatenated | null vars = "" | otherwise = "(" ++ (intercalate ", " caseAssociatedValues) ++ ")" diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index f13513e1..9b5cba5b 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -275,13 +275,7 @@ indentStr :: Int -> String -> String indentStr size = (replicate size ' ' ++) mkTokenNodeName :: String -> String -mkTokenNodeName tokenName = wrapSQ (tokenName ++ "Token") - --- | derive name for TS type from category. -catToSwiftType :: Cat -> String -catToSwiftType (ListCat c) = "[" ++ catToSwiftType c ++ "]" -catToSwiftType (TokenCat c) = toMixedCase (c ++ "Token") -catToSwiftType cat = toMixedCase (catToStr cat) +mkTokenNodeName tokenName = tokenName ++ "Token" -- | get variable names which will be used in node structure -- for categories used in production rule. @@ -308,4 +302,9 @@ getAllTokenCats cf = map TokenCat (literals cf) -- | get TS type names for all tokens getAllTokenTypenames :: CF -> [String] -getAllTokenTypenames cf = map catToSwiftType (getAllTokenCats cf) \ No newline at end of file +getAllTokenTypenames cf = map catToSwiftType (getAllTokenCats cf) + +catToSwiftType :: Cat -> String +catToSwiftType (ListCat c) = "[" ++ catToSwiftType c ++ "]" +catToSwiftType (TokenCat c) = toMixedCase (c ++ "Token") +catToSwiftType cat = toMixedCase (catToStr cat) \ No newline at end of file From 0add675d6a14d9016a1123c7998b658cf165bedc Mon Sep 17 00:00:00 2001 From: NAD777 Date: Tue, 3 Sep 2024 20:39:52 +0300 Subject: [PATCH 54/70] fix: builder (no parant for empty assosiated values list), ast (wrap in for build in keyword and STL) --- .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 20 +++++++++++-------- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 7 ++++--- source/src/BNFC/Backend/Swift/Common.hs | 14 ++++++++----- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index 7d24377b..3a27c738 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -64,7 +64,7 @@ mkBuildTokenFunction tokenCat = vcat value = case tokenName of "Integer" -> "Int(ctx.getText()!)!" "Double" -> "Double(ctx.getText()!)!" - _ -> "ctx.text" + _ -> "ctx.getText()!" -- | generate name for function which will build node for some cat. mkBuildFnName :: Cat -> String @@ -120,18 +120,22 @@ mkBuildFunction lang (cat, rulesWithLabels) = vcat $ "let" +++ varName +++ "= try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx varNames - , [ indentStr 6 "return" +++ "." ++ ruleLabel ++ "(" ++ intercalate ", " varNames ++ ")"] + , [ indentStr 6 returnStatement] ] where varNames = getVarsFromCats rhsCats rhsCats = map fst rhsRuleWithIdx + returnStatementBase = "return" +++ "." ++ ruleLabel + returnStatement + | null varNames = returnStatementBase + | otherwise = returnStatementBase ++ "(" ++ intercalate ", " varNames ++ ")" - emptyListBody = [indentStr 4 "return []"] + emptyListBody = [indentStr 6 "return []"] oneListBody = map (\(cat, idx) -> indentStr 6 $ "let data = try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx ++ [ indentStr 4 "return [data]"] consListBody = - [ indentStr 4 $ "let value1 = try" +++ mkBuildFnName firstCat ++ "(ctx." ++ mkPattern firstIdx ++ ")" - , indentStr 4 $ "let value2 = try" +++ mkBuildFnName secondCat ++ "(ctx." ++ mkPattern secondIdx ++ ")" - , indentStr 4 $ "let" +++ resultList + [ indentStr 6 $ "let value1 = try" +++ mkBuildFnName firstCat ++ "(ctx." ++ mkPattern firstIdx ++ ")" + , indentStr 6 $ "let value2 = try" +++ mkBuildFnName secondCat ++ "(ctx." ++ mkPattern secondIdx ++ ")" + , indentStr 6 $ "return" +++ resultList ] where (firstCat, firstIdx) = head rhsRuleWithIdx @@ -139,9 +143,9 @@ mkBuildFunction lang (cat, rulesWithLabels) = vcat (itemVar, listVar) = if isList firstCat then ("value2", "value1") else ("value1", "value2") resultList = if isList firstCat then - "[..." ++ listVar ++ ", " ++ itemVar ++ "]" + listVar +++ "+" +++ "[" ++ itemVar ++ "]" else - "[" ++ itemVar ++ ", ..." ++ listVar ++ "]" + "[" ++ itemVar ++ "]" +++ "+" +++ listVar cfToGroups :: CF -> [RuleData] cfToGroups cf = map (second (map (ruleToData . makeLeftRecRule cf))) $ ruleGroups cf diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index a6d7d8ac..7cb47ca5 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -65,9 +65,10 @@ cf2SwiftAST langName cf = unlines categoryClass | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list | otherwise = - let name = cat2SwiftClassName' cat + let name = catToSwiftType cat + -- let name = cat2SwiftClassName' cat // TODO: refactor, merge functions in - [ "indirect enum" +++ name +++ "{" + [ "indirect enum" +++ wrapIfNeeded name +++ "{" ] ++ indent_ 1 cases ++ ["}\n"] @@ -83,7 +84,7 @@ cf2SwiftAST langName cf = unlines caseName = str2SwiftClassName' fun vars = getVars' cats -- caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars - caseAssociatedValues = map (\var -> catToSwiftType var) cats + caseAssociatedValues = map (\var -> wrapIfNeeded $ catToSwiftType var) cats resultAssociatedValuesConcatenated | null vars = "" | otherwise = "(" ++ (intercalate ", " caseAssociatedValues) ++ ")" diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index 9b5cba5b..a69cd50f 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -21,7 +21,8 @@ cat2SwiftClassName langName cat = str2SwiftClassName langName $ identCat $ normC -- Pick a class name that is appropriate for the Swift str2SwiftClassName :: String -> String -> String -str2SwiftClassName langName str = upperFirst $ censorName langName str +-- str2SwiftClassName langName str = upperFirst $ censorName langName str +str2SwiftClassName langName str = wrapIfNeeded $ upperFirst str -- Pick a case name that is appropriate for the Swift str2SwiftCaseName :: String -> String -> String @@ -165,9 +166,7 @@ checkBuiltIn name = checkRegistered :: String -> Bool checkRegistered name = - (lowerFirst name) `elem` concatMap - (map lowerFirst) - [ builtIn, keywords, taken ] + name `elem` (builtIn ++ keywords) -- Prevent some type or variable name to be called as some already used type or keyword @@ -176,7 +175,12 @@ censorName langName name | checkRegistered name = langName ++ upperFirst name | otherwise = name -taken = [ "Character" ] +wrapIfNeeded :: String -> String +wrapIfNeeded name + | checkRegistered name = "`" ++ name ++ "`" + | otherwise = name + +taken = [] builtIn = [ "Int" , "Double" From fbde94c96d524c69a95643a83a640c2e2afe878e Mon Sep 17 00:00:00 2001 From: NAD777 Date: Tue, 3 Sep 2024 20:59:26 +0300 Subject: [PATCH 55/70] refactoring: clean up and refactoring --- .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 20 +++++++++---------- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 6 ------ source/src/BNFC/Backend/Swift/Common.hs | 7 ++++--- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index 3a27c738..8918d56c 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -5,14 +5,14 @@ module BNFC.Backend.Swift.CFtoSwiftBuilder (cf2SwiftBuilder) where import Data.Bifunctor (Bifunctor(second)) -import Data.List (intercalate, nub, intersperse) +import Data.List (intercalate, intersperse) import Data.Maybe (mapMaybe) import Text.PrettyPrint.HughesPJClass (Doc, text, vcat) import BNFC.Utils ((+++), camelCase_) -import BNFC.CF (CF, Cat (ListCat, TokenCat, Cat), identCat, isList, IsFun (isNilFun, isOneFun, isConsFun, isCoercion), catToStr, ruleGroups, Rul (rhsRule, funRule), SentForm, WithPosition (wpThing)) -import BNFC.Backend.Swift.Common (indentStr, wrapSQ, catToSwiftType, getVarsFromCats, mkTokenNodeName, indent, getAllTokenCats, getAllTokenTypenames) +import BNFC.CF +import BNFC.Backend.Swift.Common import BNFC.Options (SharedOptions (lang)) import BNFC.Backend.Antlr.CFtoAntlr4Parser (antlrRuleLabel, makeLeftRecRule) import BNFC.Backend.Common.NamedVariables (firstUpperCase) @@ -28,7 +28,12 @@ cf2SwiftBuilder cf opts = vcat $ intersperse (text "") ] where language = lang opts - importDecls = mkImportDecls cf language + + importDecls :: Doc + importDecls = vcat + [ "import Foundation" + , "import Antlr4" + ] errorsDecl = buildErrors tokenDecls = vcat $ intersperse (text "") buildTokensFuns @@ -75,13 +80,6 @@ mkBuildFnName cat = "build" ++ firstUpperCase (restName cat) TokenCat cat -> cat ++ "Token" otherCat -> catToStr otherCat --- | generates import declarations for antlr nodes and AST nodes. -mkImportDecls :: CF -> String -> Doc -mkImportDecls cf lang = vcat - [ "import Foundation" - , "import Antlr4" - ] - mkBuildFunction :: String -> RuleData -> Doc mkBuildFunction lang (cat, rulesWithLabels) = vcat [ text $ "func" +++ mkBuildFnName cat ++ "(_ ctx: " ++ (addParserPrefix lang $ identCat cat) ++ "Context) throws ->" +++ catToSwiftType cat +++ "{" diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index 7cb47ca5..1ad0ec64 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -25,18 +25,12 @@ cf2SwiftAST langName cf = unlines imports = [ "import Foundation" ] - characterTypedef = [ "typealias Character = String"] censorName' = censorName langName str2SwiftClassName' = str2SwiftClassName langName str2SwiftCaseName' = str2SwiftCaseName langName cat2SwiftClassName' = cat2SwiftClassName langName getVars' = getVars_ langName allTokenNames = literals cf - - generateTokens :: [UserDef] -> [String] - generateTokens = map $ \token -> - let name = censorName' token - in "typealias" +++ name +++ "= String;" -- | valueType is a string which represents Swift basic type. mkTokenDecl :: String -> String diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index a69cd50f..5a46d983 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -7,12 +7,10 @@ module BNFC.Backend.Swift.Common where import Text.PrettyPrint (Doc, text) import qualified Data.Map as Map import BNFC.CF -import Data.Maybe import qualified Data.Char as Char import Data.Char (toLower) -import BNFC.CF (Cat (TokenCat, ListCat), catToStr, normCat, Data, CF, isList, getAbstractSyntax, literals) import BNFC.Utils (mkName, NameStyle (OrigCase, MixedCase), mkNames) -import BNFC.Backend.Common.NamedVariables (getVars, firstLowerCase, firstUpperCase) +import BNFC.Backend.Common.NamedVariables (getVars, firstUpperCase) cat2SwiftClassName :: String -> Cat -> String @@ -180,8 +178,10 @@ wrapIfNeeded name | checkRegistered name = "`" ++ name ++ "`" | otherwise = name +taken :: [String] taken = [] +builtIn :: [String] builtIn = [ "Int" , "Double" , "Float" @@ -193,6 +193,7 @@ builtIn = [ "Int" , "Optional" , "Any" ] +keywords :: [String] keywords = [ "abstract" , "as" , "assert" From 7b077524ab0197e582b519d767f659fbfc23cfff Mon Sep 17 00:00:00 2001 From: NAD777 Date: Wed, 4 Sep 2024 10:40:56 +0300 Subject: [PATCH 56/70] refactoring: clean up make file, entry point for swift --- source/src/BNFC/Backend/Swift.hs | 51 ++------------------------------ 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 17d0095b..cf2828a1 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -23,18 +23,9 @@ import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2S makeSwift :: SharedOptions -> CF -> MkFiles () makeSwift opts@Options{..} cf = do let dirBase = replace '.' pathSeparator $ packageName - -- langBase = dirBase (langName ++ "_generated") - -- libLang = langBase "lib" - -- srcLang = libLang "src" - -- libBase = dirBase "lib" - -- binBase = dirBase "bin" - - -- directoryOptions = DirectoryOptions{baseDirectory = Just dirBase, nameStyle = Just SnakeCase} - - -- Generates files in an incorrect place makeAntlr (opts {dLanguage = Swift, optMake = Nothing}) cf - -- makeAntlr (opts {dLanguage = Swift, optMake = Nothing}) cf + MakeFile.mkMakefile optMake $ makefileContent dirBase mkfile (dirBase "ast.swift") makeSwiftComment astContent @@ -42,35 +33,11 @@ makeSwift opts@Options{..} cf = do where astContent = cf2SwiftAST (firstUpperCase langName) cf - -- astContent = cf2SwiftAST cf - -- builderContent = cf2SwiftBuilder (firstUpperCase langName) cf builderContent = cf2SwiftBuilder cf opts - mainContent = unlines - [ "import '../lib/test.swift';" - , "void main(List args) {" - , " final test = Test();" - , " test.run(args);" - , "}" ] + packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang langName = firstLowerCase $ mkName [] SnakeCase lang langNameUpperCased = firstUpperCase langName - importLangName = "import 'package:" ++ langName ++ "_generated/" ++ langName ++ "_generated.swift';" - - pubspecContent moduleName desc deps = unlines ( - [ "name:" +++ moduleName - , "description:" +++ desc - , "version: 1.0.0" - , "publish_to: 'none'" - , "environment:" - , " sdk: ^3.4.0" - , "dependencies:" - , " antlr4: ^4.13.1" - , " fast_immutable_collections: ^10.2.2" - ] ++ deps ++ [ "dev_dependencies:" - , " lints: ^4.0.0" ]) - - lexerClassName = lang ++ "GrammarLexer" - parserClassName = lang ++ "GrammarParser" makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] @@ -99,13 +66,7 @@ makeSwift opts@Options{..} cf = do , ("parser" , [refSrcVar "PARSER_NAME" ++ ".g4"] , [MakeFile.refVar "ANTLR4" +++ "-Dlanguage=Swift" +++ "-no-listener" +++ "-no-visitor" +++ refSrcVar "PARSER_NAME" ++ ".g4"]) - , ("install-deps-external" - , [MakeFile.refVar "LANG" "pubspec.yaml"] - , ["cd" +++ (MakeFile.refVar "LANG") ++ "; Swift pub get"]) - , ("install-deps-internal" - , [MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated") "pubspec.yaml"] - , ["cd" +++ (MakeFile.refVar "LANG" (MakeFile.refVar "LANG" ++ "_generated")) ++ "; Swift pub get"]) - , (MakeFile.refVar "LANG", ["lexer", "parser", "clean", "install-deps-external", "install-deps-internal"], []) + , (MakeFile.refVar "LANG", ["lexer", "parser", "clean"], []) , ("clean", [], [ rmInSrc "LEXER_NAME" ".interp" @@ -122,9 +83,3 @@ makeSwift opts@Options{..} cf = do makeSwiftComment :: String -> String makeSwiftComment = ("// Swift " ++) - -makeSwiftCommentYaml :: String -> String -makeSwiftCommentYaml = ("# Swift" ++) - -toLowerCase :: String -> String -toLowerCase = map toLower From 8a03a243a189cfb7bd96145dd331cb9196c515fa Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 20 Oct 2024 19:36:20 +0300 Subject: [PATCH 57/70] Move to lib generation instead of just files, improve generated files structure --- source/src/BNFC/Backend/Swift.hs | 54 +++++++++++++++---- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 6 +-- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index cf2828a1..4319e738 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -12,7 +12,7 @@ import Data.Char (toLower) import BNFC.Backend.Base (MkFiles, mkfile,liftIO) import BNFC.CF (CF, getAbstractSyntax, firstEntry, catToStr, identCat, normCat ) import BNFC.Options (SharedOptions (Options, inPackage, lang, optMake, dLanguage, antlrOpts, outDir), AntlrTarget (Swift)) -import BNFC.Utils (mkName, NameStyle (SnakeCase), replace, (+.+), (+++)) +import BNFC.Utils (mkName, NameStyle (SnakeCase, CamelCase), replace, (+.+), (+++)) import BNFC.Backend.Common.Makefile as MakeFile import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) import BNFC.Backend.Antlr (makeAntlr, makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) @@ -23,20 +23,23 @@ import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2S makeSwift :: SharedOptions -> CF -> MkFiles () makeSwift opts@Options{..} cf = do let dirBase = replace '.' pathSeparator $ packageName + sourcesDir = dirBase "Sources" + targetDir = sourcesDir langNameUpperCased + directoryOptions = DirectoryOptions{baseDirectory = Just targetDir, nameStyle = Just CamelCase} - makeAntlr (opts {dLanguage = Swift, optMake = Nothing}) cf + makeAntlr' (opts {dLanguage = Swift, optMake = Nothing}) cf directoryOptions - MakeFile.mkMakefile optMake $ makefileContent dirBase - - mkfile (dirBase "ast.swift") makeSwiftComment astContent - mkfile (dirBase "builder.swift") makeSwiftComment builderContent + MakeFile.mkMakefile optMake $ makefileContent targetDir + mkfile (targetDir "ast.swift") makeSwiftComment astContent + mkfile (targetDir "builder.swift") makeSwiftComment builderContent + mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) where astContent = cf2SwiftAST (firstUpperCase langName) cf builderContent = cf2SwiftBuilder cf opts - packageName = maybe id (+.+) inPackage $ mkName [] SnakeCase lang - langName = firstLowerCase $ mkName [] SnakeCase lang + packageName = maybe id (+.+) inPackage $ mkName [] CamelCase lang + langName = firstLowerCase $ mkName [] CamelCase lang langNameUpperCased = firstUpperCase langName makeVars x = [MakeFile.mkVar n v | (n,v) <- x] @@ -80,6 +83,39 @@ makeSwift opts@Options{..} cf = do ] makefileContent dirBase _ = vcat [makefileVars, "", makefileRules $ refVarInSrc dirBase, ""] - + + -- Content of Package.swift, uses to declare swift package + packageFileContent langName = vcat + [ "import PackageDescription" + , "" + , "let package = Package(" + , nest 2 $ vcat + [ text $ "name: \"" ++ langName ++ "\"," + , "products: [" + , nest 2 $ vcat + [ ".library(" + , text $ " name: \"" ++ langName ++ "\"," + , text $ " targets: [\"" ++ langName ++ "\"]" + , ")" + ] + , "]," + ] + , nest 2 $ vcat + [ "dependencies: [" + , " .package(name: \"Antlr4\", url: \"https://github.com/antlr/antlr4\", from: \"4.12.0\")" + , "]," + ] + , nest 2 $ vcat + [ "targets: [" + , text $ " .target(name: \"" ++ langName ++ "\", dependencies: [\"Antlr4\"])" + , "]" + ] + , ")" + ] makeSwiftComment :: String -> String makeSwiftComment = ("// Swift " ++) + +makePackageHeader :: String -> String +makePackageHeader str = toolingVersion ++ "\n" ++ (makeSwiftComment str) + where + toolingVersion = "// swift-tools-version: 5.9" \ No newline at end of file diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index 1ad0ec64..b5eb5058 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -35,10 +35,10 @@ cf2SwiftAST langName cf = unlines -- | valueType is a string which represents Swift basic type. mkTokenDecl :: String -> String mkTokenDecl tokenName = unlines - [ "struct" +++ catToSwiftType (TokenCat tokenName) +++ "{" + [ "public struct" +++ catToSwiftType (TokenCat tokenName) +++ "{" , indentStr 2 $ "let value: " ++ value , "" - , indentStr 2 $ "init(_ value:" +++ value ++ ") {" + , indentStr 2 $ "public init(_ value:" +++ value ++ ") {" , indentStr 4 $ "self.value = value" , indentStr 2 $ "}" , "}" @@ -62,7 +62,7 @@ cf2SwiftAST langName cf = unlines let name = catToSwiftType cat -- let name = cat2SwiftClassName' cat // TODO: refactor, merge functions in - [ "indirect enum" +++ wrapIfNeeded name +++ "{" + [ "public indirect enum" +++ wrapIfNeeded name +++ "{" ] ++ indent_ 1 cases ++ ["}\n"] From 2de0de041c1ffde4da7559980481c59bb4ee1d51 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 20 Oct 2024 21:20:02 +0300 Subject: [PATCH 58/70] Add public api, fix builder generation, fix package file generation according to target language name --- source/src/BNFC/Backend/Swift.hs | 29 ++++++++++++++++++- .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 11 +------ source/src/BNFC/Backend/Swift/Common.hs | 11 ++++++- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 4319e738..5f7cd80d 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -18,7 +18,7 @@ import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) import BNFC.Backend.Antlr (makeAntlr, makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) import BNFC.Backend.Swift.CFtoSwiftAST ( cf2SwiftAST ) import BNFC.Backend.Swift.CFtoSwiftBuilder ( cf2SwiftBuilder ) -import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName ) +import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName, mkBuildFnName ) makeSwift :: SharedOptions -> CF -> MkFiles () makeSwift opts@Options{..} cf = do @@ -33,6 +33,7 @@ makeSwift opts@Options{..} cf = do mkfile (targetDir "ast.swift") makeSwiftComment astContent mkfile (targetDir "builder.swift") makeSwiftComment builderContent + mkfile (targetDir langNameUpperCased ++ ".swift") makeSwiftComment (publicApiContent langNameUpperCased) mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) where astContent = cf2SwiftAST (firstUpperCase langName) cf @@ -112,6 +113,32 @@ makeSwift opts@Options{..} cf = do ] , ")" ] + + publicApiContent langName = vcat + [ "import Antlr4" + , "" + , text $ "public func getAst(from text: String) -> Result<" ++ catToStr firstCat ++", Error> {" + , nest 2 $ vcat + [ "let input = ANTLRInputStream(text)" + , text $ "let lexer =" +++ langName ++ "Lexer(input)" + , "let tokens = CommonTokenStream(lexer)" + , "do {" + , nest 2 $ vcat + [ text $ "let parser = try" +++ langName ++ "Parser(tokens)" + , text $ "let ctx = try parser." ++ (firstLowerCase $ identCat $ normCat firstCat) ++ "()" + , text $ "let program = try" +++ mkBuildFnName firstCat ++ "(ctx)" + , "return .success(program)" + ] + , "} catch {" + , " return .failure(error)" + , "}" + ] + , "}" + ] + where + firstCat = firstEntry cf + + makeSwiftComment :: String -> String makeSwiftComment = ("// Swift " ++) diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index 8918d56c..10a038bc 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -71,15 +71,6 @@ mkBuildTokenFunction tokenCat = vcat "Double" -> "Double(ctx.getText()!)!" _ -> "ctx.getText()!" --- | generate name for function which will build node for some cat. -mkBuildFnName :: Cat -> String -mkBuildFnName cat = "build" ++ firstUpperCase (restName cat) - where - restName cat = case cat of - ListCat cat -> restName cat ++ "List" - TokenCat cat -> cat ++ "Token" - otherCat -> catToStr otherCat - mkBuildFunction :: String -> RuleData -> Doc mkBuildFunction lang (cat, rulesWithLabels) = vcat [ text $ "func" +++ mkBuildFnName cat ++ "(_ ctx: " ++ (addParserPrefix lang $ identCat cat) ++ "Context) throws ->" +++ catToSwiftType cat +++ "{" @@ -152,4 +143,4 @@ cfToGroups cf = map (second (map (ruleToData . makeLeftRecRule cf))) $ ruleGroup addParserPrefix :: String -> String -> String -addParserPrefix lang name = lang ++ "Parser." ++ name \ No newline at end of file +addParserPrefix lang name = (firstUpperCase lang) ++ "Parser." ++ name \ No newline at end of file diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index 5a46d983..e2c2d71a 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -312,4 +312,13 @@ getAllTokenTypenames cf = map catToSwiftType (getAllTokenCats cf) catToSwiftType :: Cat -> String catToSwiftType (ListCat c) = "[" ++ catToSwiftType c ++ "]" catToSwiftType (TokenCat c) = toMixedCase (c ++ "Token") -catToSwiftType cat = toMixedCase (catToStr cat) \ No newline at end of file +catToSwiftType cat = toMixedCase (catToStr cat) + +-- | generate name for function which will build node for some cat. +mkBuildFnName :: Cat -> String +mkBuildFnName cat = "build" ++ firstUpperCase (restName cat) + where + restName cat = case cat of + ListCat cat -> restName cat ++ "List" + TokenCat cat -> cat ++ "Token" + otherCat -> catToStr otherCat \ No newline at end of file From 59f0c5597ffa9fb45bc3735fe4230a1a0a90160f Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 15 Dec 2024 16:50:24 +0300 Subject: [PATCH 59/70] Create files for skeleton, connect to Swift's part of BNFC --- source/BNFC.cabal | 2 +- source/src/BNFC/Backend/Swift.hs | 9 +++-- .../BNFC/Backend/Swift/CFtoSwiftSkeleton.hs | 33 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 8e2f4b7a..1458b2e4 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -278,7 +278,7 @@ library BNFC.Backend.Swift.CFtoSwiftBuilder BNFC.Backend.Swift.Common -- BNFC.Backend.Swift.CFtoSwiftPrinter - -- BNFC.Backend.Swift.CFtoSwiftSkeleton + BNFC.Backend.Swift.CFtoSwiftSkeleton -- Antlr4 backend BNFC.Backend.Antlr diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 5f7cd80d..e15e0c1a 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -18,6 +18,7 @@ import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) import BNFC.Backend.Antlr (makeAntlr, makeAntlr', DirectoryOptions (DirectoryOptions, baseDirectory, nameStyle)) import BNFC.Backend.Swift.CFtoSwiftAST ( cf2SwiftAST ) import BNFC.Backend.Swift.CFtoSwiftBuilder ( cf2SwiftBuilder ) +import BNFC.Backend.Swift.CFtoSwiftSkeleton ( cf2SwiftSkeleton ) import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName, mkBuildFnName ) makeSwift :: SharedOptions -> CF -> MkFiles () @@ -35,14 +36,16 @@ makeSwift opts@Options{..} cf = do mkfile (targetDir "builder.swift") makeSwiftComment builderContent mkfile (targetDir langNameUpperCased ++ ".swift") makeSwiftComment (publicApiContent langNameUpperCased) mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) + mkfile (dirBase "Skeleton.swift") makeSwiftComment skeletonContent where - astContent = cf2SwiftAST (firstUpperCase langName) cf - builderContent = cf2SwiftBuilder cf opts - packageName = maybe id (+.+) inPackage $ mkName [] CamelCase lang langName = firstLowerCase $ mkName [] CamelCase lang langNameUpperCased = firstUpperCase langName + astContent = cf2SwiftAST langNameUpperCased cf + builderContent = cf2SwiftBuilder cf opts + skeletonContent = cf2SwiftSkeleton langNameUpperCased cf opts + makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs new file mode 100644 index 00000000..b5432680 --- /dev/null +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module BNFC.Backend.Swift.CFtoSwiftSkeleton (cf2SwiftSkeleton) where + +import Data.Bifunctor (Bifunctor(second)) +import Data.List (intercalate, intersperse) +import Data.Maybe (mapMaybe) + +import Text.PrettyPrint.HughesPJClass (Doc, text, vcat) + +import BNFC.Utils ((+++), camelCase_) +import BNFC.CF +import BNFC.Backend.Swift.Common +import BNFC.Options (SharedOptions (lang)) +import BNFC.Backend.Antlr.CFtoAntlr4Parser (antlrRuleLabel, makeLeftRecRule) +import BNFC.Backend.Common.NamedVariables (firstUpperCase) + +cf2SwiftSkeleton :: String -> CF -> SharedOptions -> Doc +cf2SwiftSkeleton packageName cf opts = vcat $ intersperse (text "") + [ importDecls + -- , errorsDecl + -- , tokenDecls + -- , buildFnDecls + ] + where + importDecls :: Doc + importDecls = vcat + [ "import Foundation" + , text $ "import" +++ packageName + ] + \ No newline at end of file From 521583bb6f2c92d7b43bf0310484352d664f7756 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sat, 11 Jan 2025 18:29:57 +0300 Subject: [PATCH 60/70] Add first iteration of Skeleton generation, small fixes --- source/src/BNFC/Backend/Swift.hs | 2 +- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 2 +- .../BNFC/Backend/Swift/CFtoSwiftSkeleton.hs | 104 ++++++++++++++---- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index e15e0c1a..e96d63ea 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -44,7 +44,7 @@ makeSwift opts@Options{..} cf = do astContent = cf2SwiftAST langNameUpperCased cf builderContent = cf2SwiftBuilder cf opts - skeletonContent = cf2SwiftSkeleton langNameUpperCased cf opts + skeletonContent = cf2SwiftSkeleton langNameUpperCased cf makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index b5eb5058..143788a5 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -15,7 +15,7 @@ import Data.List (intercalate) -- Produces abstract data types in Swift cf2SwiftAST :: String -> CF -> String -cf2SwiftAST langName cf = unlines +cf2SwiftAST langName cf = unlines $ imports ++ [""]-- import some libraries if needed -- ++ characterTypedef ++ map mkTokenDecl allTokenNames diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs index b5432680..6a56212c 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs @@ -4,30 +4,86 @@ module BNFC.Backend.Swift.CFtoSwiftSkeleton (cf2SwiftSkeleton) where -import Data.Bifunctor (Bifunctor(second)) -import Data.List (intercalate, intersperse) -import Data.Maybe (mapMaybe) +import Data.Maybe ( mapMaybe ) -import Text.PrettyPrint.HughesPJClass (Doc, text, vcat) - -import BNFC.Utils ((+++), camelCase_) import BNFC.CF -import BNFC.Backend.Swift.Common -import BNFC.Options (SharedOptions (lang)) -import BNFC.Backend.Antlr.CFtoAntlr4Parser (antlrRuleLabel, makeLeftRecRule) -import BNFC.Backend.Common.NamedVariables (firstUpperCase) - -cf2SwiftSkeleton :: String -> CF -> SharedOptions -> Doc -cf2SwiftSkeleton packageName cf opts = vcat $ intersperse (text "") - [ importDecls - -- , errorsDecl - -- , tokenDecls - -- , buildFnDecls - ] +import BNFC.Utils ( (+++) ) + +import BNFC.Backend.Common.NamedVariables ( UserDef ) +import BNFC.Backend.Swift.Common +import Data.List (intercalate) + + +cf2SwiftSkeleton :: String -> CF -> String +cf2SwiftSkeleton langName cf = + unlines $ + [ "import Foundation" + , "import" +++ langName + , "" + , "func identityFn(_ a: A) -> A { a }" + , "" ] + ++ (map buildUserToken [ n | (n,_) <- tokenPragmas cf ]) + ++ (concatMap genData $ getAbstractSyntax cf) where - importDecls :: Doc - importDecls = vcat - [ "import Foundation" - , text $ "import" +++ packageName - ] - \ No newline at end of file + censorName' = censorName langName + str2SwiftClassName' = str2SwiftClassName langName + getVars' = getVars_ langName + cat2SwiftClassName' = cat2SwiftClassName langName + cat2SwiftType' = cat2SwiftType langName + buildUserToken :: UserDef -> String + buildUserToken token = + "String interpret" ++ (censorName' token) ++ "(x) => x;" + + genData :: Data -> [String] + genData (cat, rules) + | (catToStr cat) `elem` (map fst rules) = [] + | otherwise = + let name = cat2SwiftClassName' cat + varType = buildVariableTypeFromSwiftType $ cat2SwiftType' cat + in [ "func interpret" ++ name ++ "(_ e:" +++ varType ++ ") -> String {" ] + ++ (indent_ 1 $ if isList cat + then [ "\"\\(e)\"" ] + else [ "switch (e) {" ] + ++ (indent_ 1 $ mapMaybe genBranch rules) + ++ [ "}" ]) + ++ ["}"] + ++ [""] + + genBranch :: (Fun, [Cat]) -> Maybe (String) + genBranch (fun, rhs) + | isNilFun fun || + isOneFun fun || + isConsFun fun = Nothing + | otherwise = + let + className = str2SwiftClassName' fun + varName = lowerFirst $ censorName' className + vars = getVars' rhs + in Just $ + "case let ." ++ className ++ "(" ++ (intercalate ", " (associatedValues vars)) ++ "): \"" ++ className ++ "(" + ++ (concat $ (drop 1) $ arguments (genVarRepr varName) vars) + ++ ")\"" + where + associatedValues [] = [] + associatedValues (x: vars) = [buildVariableName x] ++ (associatedValues vars) + + arguments _ [] = [] + arguments generator (x:vars) = + [ ", ", "\\(" ++ (generator x) ++ ")" ] ++ (arguments generator vars) + + genVarRepr :: String -> SwiftVar -> String + genVarRepr varName variable@((n, varType), _) = let + varCall = buildVariableName variable + interp = interpreter varType in + if n > 0 then + varCall ++ ".map(" ++ (unpack interp (n - 1)) ++ ")" -- TODO: check this + else + interp ++ "(" ++ varCall ++ ")" + where + unpack funName n + | n <= 0 = funName + | otherwise = let varName = "e" ++ show n in + "{ " ++ varName ++ " in " ++ varName ++ ".map { " ++ (unpack funName (n - 1)) ++ " } }" -- TODO: check this + interpreter varType + | varType /= (censorName' varType) = "identityFn" + | otherwise = "interpret" ++ varType From e6dfed3f2b33e4ba215f9ee91c830ba0fd23178a Mon Sep 17 00:00:00 2001 From: NAD777 Date: Wed, 15 Jan 2025 15:36:16 +0300 Subject: [PATCH 61/70] feat(AST): refactoring, make properties public in wrappers for basic types --- source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs | 105 ++++++++---------- 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs index 143788a5..b90cfd95 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftAST.hs @@ -1,85 +1,72 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TupleSections #-} module BNFC.Backend.Swift.CFtoSwiftAST (cf2SwiftAST) where -import Data.Maybe ( mapMaybe ) +import Data.Maybe (mapMaybe) +import Data.List (intercalate, intersperse) +import Text.PrettyPrint.HughesPJClass (Doc, text, vcat, nest, ($$)) import BNFC.CF -import BNFC.Utils ( (+++) ) +import BNFC.Utils ((+++)) +import BNFC.Backend.Swift.Common +import BNFC.Backend.Common.NamedVariables (UserDef) -import BNFC.Backend.Common.NamedVariables ( UserDef ) -import BNFC.Backend.Swift.Common -import Data.List (intercalate) - --- Produces abstract data types in Swift -cf2SwiftAST :: String -> CF -> String -cf2SwiftAST langName cf = unlines - $ imports ++ [""]-- import some libraries if needed - -- ++ characterTypedef - ++ map mkTokenDecl allTokenNames - ++ concatMap prData rules -- generate user-defined types +-- | Produces abstract data types in Swift +cf2SwiftAST :: String -> CF -> Doc +cf2SwiftAST langName cf = vcat + [ imports + , empty + , vcat (intersperse empty (map mkTokenDecl allTokenNames)) + , empty + , vcat (intersperse empty (concatMap prData rules)) + ] where - rules = getAbstractSyntax cf - imports = [ - "import Foundation" - ] - censorName' = censorName langName - str2SwiftClassName' = str2SwiftClassName langName - str2SwiftCaseName' = str2SwiftCaseName langName - cat2SwiftClassName' = cat2SwiftClassName langName - getVars' = getVars_ langName + empty = text "" + rules = getAbstractSyntax cf + imports = vcat [text "import Foundation"] allTokenNames = literals cf - - -- | valueType is a string which represents Swift basic type. - mkTokenDecl :: String -> String - mkTokenDecl tokenName = unlines - [ "public struct" +++ catToSwiftType (TokenCat tokenName) +++ "{" - , indentStr 2 $ "let value: " ++ value - , "" - , indentStr 2 $ "public init(_ value:" +++ value ++ ") {" - , indentStr 4 $ "self.value = value" - , indentStr 2 $ "}" - , "}" + + -- | Generates a Swift struct for a token. + mkTokenDecl :: String -> Doc + mkTokenDecl tokenName = vcat + [ text $ "public struct" +++ catToSwiftType (TokenCat tokenName) +++ "{" + , nest 2 $ text $ "public let value: " ++ value + , empty + , nest 2 $ text $ "public init(_ value:" +++ value ++ ") {" + , nest 4 $ text "self.value = value" + , nest 2 $ text "}" + , text "}" ] where value | tokenName == catInteger = "Int" | tokenName == catDouble = "Double" | otherwise = "String" - - -- | Generates a category class, and classes for all its rules. - prData :: Data -> [String] + -- | Generates enums and cases for a given data type. + prData :: Data -> [Doc] prData (cat, rules) = categoryClass - where + where funs = map fst rules cases = mapMaybe (prRule cat) rules categoryClass - | catToStr cat `elem` funs || isList cat = [] -- the category is also a function or a list + | catToStr cat `elem` funs || isList cat = [] | otherwise = - let name = catToSwiftType cat - -- let name = cat2SwiftClassName' cat // TODO: refactor, merge functions - in - [ "public indirect enum" +++ wrapIfNeeded name +++ "{" - ] ++ indent_ 1 cases ++ ["}\n"] - + let name = catToSwiftType cat + in [ vcat $ text ("public indirect enum" +++ wrapIfNeeded name +++ "{") + : nest 2 (vcat cases) + : [text "}"] + ] - -- | Generates classes for a rule, depending on what type of rule it is. - prRule :: Cat -> (Fun, [Cat]) -> Maybe (String) + -- | Generates individual cases for enum definitions. + prRule :: Cat -> (Fun, [Cat]) -> Maybe Doc prRule cat (fun, cats) - | isNilFun fun || - isOneFun fun || - isConsFun fun = Nothing -- these are not represented in the Absyn - | otherwise = -- a standard rule - Just result + | isNilFun fun || isOneFun fun || isConsFun fun = Nothing + | otherwise = Just $ text $ "case" +++ caseName ++ resultAssociatedValuesConcatenated where - caseName = str2SwiftClassName' fun - vars = getVars' cats - -- caseAssociatedValues = map (\var -> buildVariableName var ++ ": " ++ buildVariableType var) vars - caseAssociatedValues = map (\var -> wrapIfNeeded $ catToSwiftType var) cats + caseName = str2SwiftClassName langName fun + caseAssociatedValues = map (wrapIfNeeded . catToSwiftType) cats resultAssociatedValuesConcatenated - | null vars = "" - | otherwise = "(" ++ (intercalate ", " caseAssociatedValues) ++ ")" - result = unwords $ ["case", caseName ++ resultAssociatedValuesConcatenated] \ No newline at end of file + | null cats = "" + | otherwise = "(" ++ intercalate ", " caseAssociatedValues ++ ")" From 9564801aa88a6e9a2dc3b170e84725056e92634a Mon Sep 17 00:00:00 2001 From: NAD777 Date: Thu, 16 Jan 2025 17:03:49 +0300 Subject: [PATCH 62/70] feat(pretty-printer, builder): add first iteration of pretty-printer, fix name collision with build-ins in builder --- source/BNFC.cabal | 2 +- source/src/BNFC/Backend/Swift.hs | 3 + .../BNFC/Backend/Swift/CFToSwiftBuilder.hs | 6 +- .../BNFC/Backend/Swift/CFtoSwiftPrinter.hs | 378 ++++++++++++++++++ source/src/BNFC/Backend/Swift/Common.hs | 22 +- 5 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs diff --git a/source/BNFC.cabal b/source/BNFC.cabal index 1458b2e4..00a6b9c7 100644 --- a/source/BNFC.cabal +++ b/source/BNFC.cabal @@ -277,7 +277,7 @@ library BNFC.Backend.Swift.CFtoSwiftAST BNFC.Backend.Swift.CFtoSwiftBuilder BNFC.Backend.Swift.Common - -- BNFC.Backend.Swift.CFtoSwiftPrinter + BNFC.Backend.Swift.CFtoSwiftPrinter BNFC.Backend.Swift.CFtoSwiftSkeleton -- Antlr4 backend diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index e96d63ea..ab495f36 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -19,6 +19,7 @@ import BNFC.Backend.Antlr (makeAntlr, makeAntlr', DirectoryOptions (DirectoryOpt import BNFC.Backend.Swift.CFtoSwiftAST ( cf2SwiftAST ) import BNFC.Backend.Swift.CFtoSwiftBuilder ( cf2SwiftBuilder ) import BNFC.Backend.Swift.CFtoSwiftSkeleton ( cf2SwiftSkeleton ) +import BNFC.Backend.Swift.CFtoSwiftPrinter ( cf2SwiftPrinter ) import BNFC.Backend.Swift.Common ( indent, buildVariableTypeFromSwiftType, cat2SwiftType, cat2SwiftClassName, mkBuildFnName ) makeSwift :: SharedOptions -> CF -> MkFiles () @@ -34,6 +35,7 @@ makeSwift opts@Options{..} cf = do mkfile (targetDir "ast.swift") makeSwiftComment astContent mkfile (targetDir "builder.swift") makeSwiftComment builderContent + mkfile (targetDir "Printer.swift") makeSwiftComment printerContent mkfile (targetDir langNameUpperCased ++ ".swift") makeSwiftComment (publicApiContent langNameUpperCased) mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) mkfile (dirBase "Skeleton.swift") makeSwiftComment skeletonContent @@ -45,6 +47,7 @@ makeSwift opts@Options{..} cf = do astContent = cf2SwiftAST langNameUpperCased cf builderContent = cf2SwiftBuilder cf opts skeletonContent = cf2SwiftSkeleton langNameUpperCased cf + printerContent = cf2SwiftPrinter cf makeVars x = [MakeFile.mkVar n v | (n,v) <- x] makeRules x = [MakeFile.mkRule tar dep recipe | (tar, dep, recipe) <- x] diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index 10a038bc..08471f26 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -106,7 +106,7 @@ mkBuildFunction lang (cat, rulesWithLabels) = vcat [ zipWith (\ (cat, idx) varName -> indentStr 6 - $ "let" +++ varName + $ "let" +++ wrapIfNeeded varName +++ "= try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx varNames , [ indentStr 6 returnStatement] @@ -116,8 +116,8 @@ mkBuildFunction lang (cat, rulesWithLabels) = vcat rhsCats = map fst rhsRuleWithIdx returnStatementBase = "return" +++ "." ++ ruleLabel returnStatement - | null varNames = returnStatementBase - | otherwise = returnStatementBase ++ "(" ++ intercalate ", " varNames ++ ")" + | null varNames = returnStatementBase + | otherwise = returnStatementBase ++ "(" ++ intercalate ", " (map wrapIfNeeded varNames) ++ ")" emptyListBody = [indentStr 6 "return []"] oneListBody = map (\(cat, idx) -> indentStr 6 $ "let data = try" +++ mkBuildFnName cat ++ "(ctx." ++ mkPattern idx ++ ")") rhsRuleWithIdx ++ [ indentStr 4 "return [data]"] diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs new file mode 100644 index 00000000..a71d5cb0 --- /dev/null +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs @@ -0,0 +1,378 @@ +module BNFC.Backend.Swift.CFtoSwiftPrinter (cf2SwiftPrinter) where + +import Data.Either (lefts, rights) +import Data.List (nub, intercalate, find, uncons, intersperse) + +import Text.PrettyPrint.HughesPJClass (Doc, text, vcat, hcat, nest) + +import BNFC.CF (CF, ruleGroups, Rul (rhsRule, funRule), Cat (Cat, ListCat, TokenCat, CoercCat), WithPosition (wpThing), IsFun (isCoercion, isConsFun, isOneFun, isNilFun), catToStr, SentForm, rulesForNormalizedCat, normCat, normCatOfList, catOfList, isList, allParserCats, rulesForCat) +import BNFC.Utils ((+++)) +import BNFC.Backend.Swift.Common (catToSwiftType, indent, wrapSQ, getVarsFromCats, getAbsynWithoutLists, getAllTokenTypenames, getAllTokenCats, mkTypeName, wrapIfNeeded) +import BNFC.Backend.Common.NamedVariables (firstUpperCase) +import Data.Maybe (isJust, isNothing, fromMaybe) + +prettyPrintProtocolName :: String +prettyPrintProtocolName = "PrettyPrintable" + +prettyPrintPropertyName :: String +prettyPrintPropertyName = "printed" + +prettyPrintRenderClassName :: String +prettyPrintRenderClassName = "Rendered" + +prettyPrintRenderCall :: String +prettyPrintRenderCall = prettyPrintRenderClassName ++ ".shared.render" + +-- | generate pretty-printers for nodes of an AST +cf2SwiftPrinter :: CF -> Doc +cf2SwiftPrinter cf = vcat + [ protocolDeclaration + , rendererDeclaration + , tokenPrinterDecl + , nodesPrintersDecls + , "" + , nodesPrettifiersDecls + ] + where + tokenPrinterDecl = mkTokenPrinter cf + + -- we intentionally want to have rules for list cats, which have items of type Coerc + cats = let isCoercCat (CoercCat _ _) = True + isCoercCat _ = False + in filter (not . isCoercCat) $ allParserCats cf + + nodesPrettifiersDecls = vcat $ intersperse (text "") $ map (mkNodePrettifier cf) cats + nodesPrintersDecls = vcat $ intersperse (text "") $ (map mkNodePrinter cats) + + rules = map (wpThing . funRule) $ + concatMap + (filter (not . isCoercion) . rulesForNormalizedCat cf) + (filter (not . isList) cats) + +protocolDeclaration :: Doc +protocolDeclaration = vcat + [ text $ "public protocol" +++ prettyPrintProtocolName +++ "{" + , nest 2 $ text $ "var" +++ prettyPrintPropertyName +++ ": String { get }" + , "}" + , "" + ] + +rendererDeclaration :: Doc +rendererDeclaration = vcat + [ text $ "final class" +++ className +++ "{" + , nest 2 $ tokenEnumDeclaration + , nest 2 $ text $ "static let shared =" +++ className ++ "()" + , nest 2 $ text $ "private let indentSize = 2" + , text "" + , nest 2 "private init() {}" + , text "" + , nest 2 renderFunctionDeclaration + , nest 2 transformFunctionDeclaration + , nest 2 groupTokensFunctionDeclaration + , nest 2 addIndentationFunctionDeclaration + , nest 2 dropTrailingSpacesFunctionDeclaration + , nest 2 dropTrailingNewlinesFunctionDeclaration + , "}" + , "" + ] + where + className = prettyPrintRenderClassName + tokenEnumDeclaration = vcat + [ "private enum Token {" + , nest 2 body + , "}" + , "" + ] + where + body = vcat + [ "case text(value: String)" + , "case newline(indentShift: Int)" + , "case space" + , "" + , "func toString() -> String {" + , indent 2 "switch self {" + , indent 4 "case .text(let value):" + , indent 6 "return value" + , indent 4 "case .newline:" + , indent 6 "return \"\\n\"" + , indent 4 "case .space:" + , indent 6 "return \" \"" + , indent 2 "}" + , "}" + ] + + renderFunctionDeclaration = vcat + [ "func render(_ tokens: [String]) -> String {" + , nest 2 body + , "}" + , "" + ] + where + body = vcat + [ "let transformedTokens = transform(tokens)" + , "let groupedTokens = groupTokens(transformedTokens)" + , "return groupedTokens" + , indent 2 ".map { addIndentation(to: $0) }" + , indent 2 ".map { $0.map { $0.toString() }.joined() }" + , indent 2 ".joined(separator: \"\\n\")" + ] + + transformFunctionDeclaration = vcat + [ "private func transform(_ tokens: [String]) -> [Token] {" + , nest 2 body + , "}" + , "" + ] + where + body = vcat + [ "var result: [Token] = []" + , "for token in tokens {" + , indent 2 "switch token {" + , nest 4 casesDeclaration + , indent 2 "}" + , "}" + , "dropTrailingSpaces(from: &result)" + , "dropTrailingNewlines(from: &result)" + , "return result" + ] + casesDeclaration = vcat + [ "case \"\", \" \":" + , indent 2 "continue" + , "case \"{\":" + , indent 2 "result.append(.text(value: token))" + , indent 2 "result.append(.newline(indentShift: 1))" + , "case \"}\":" + , indent 2 "dropTrailingNewlines(from: &result)" + , indent 2 "result.append(.newline(indentShift: -1))" + , indent 2 "result.append(.text(value: token))" + , indent 2 "result.append(.newline(indentShift: 0))" + , "case \"(\", \")\", \"[\", \"]\", \"<\", \">\", \",\", \".\":" + , indent 2 "dropTrailingSpaces(from: &result)" + , indent 2 "if token == \")\" || token == \"]\" || token == \"}\" {" + , indent 4 "dropTrailingNewlines(from: &result)" + , indent 2 "}" + , indent 2 "result.append(.text(value: token))" + , indent 2 "if token != \".\" {" + , indent 4 "result.append(.space)" + , indent 2 "}" + , "case \";\":" + , indent 2 "dropTrailingSpaces(from: &result)" + , indent 2 "dropTrailingNewlines(from: &result)" + , indent 2 "result.append(.text(value: token))" + , indent 2 "result.append(.newline(indentShift: 0))" + , "default:" + , indent 2 "result.append(.text(value: token))" + , indent 2 "result.append(.space)" + ] + + groupTokensFunctionDeclaration = vcat + [ "private func groupTokens(_ tokens: [Token]) -> [(indentationLevel: Int, tokens: [Token])] {" + , indent 2 "var groups: [(indentationLevel: Int, tokens: [Token])] = []" + , indent 2 "var currentIndentation = 0" + , indent 2 "for token in tokens {" + , indent 4 "if case .newline(let shift) = token {" + , indent 6 "currentIndentation += shift" + , indent 6 "groups.append((currentIndentation, []))" + , indent 4 "} else {" + , indent 6 "if groups.isEmpty {" + , indent 8 "groups.append((currentIndentation, []))" + , indent 6 "}" + , indent 6 "groups[groups.count - 1].tokens.append(token)" + , indent 4 "}" + , indent 2 "}" + , indent 2 "return groups" + , "}" + , "" + ] + + addIndentationFunctionDeclaration = vcat + [ "private func addIndentation(to group: (indentationLevel: Int, tokens: [Token])) -> [Token] {" + , indent 2 "var tokens = group.tokens" + , indent 2 "if group.indentationLevel > 0 {" + , indent 4 "tokens.insert(.text(value: String(repeating: \" \", count: indentSize * group.indentationLevel)), at: 0)" + , indent 2 "}" + , indent 2 "return tokens" + , "}" + , "" + ] + + dropTrailingSpacesFunctionDeclaration = vcat + [ "private func dropTrailingSpaces(from tokens: inout [Token]) {" + , indent 2 "while let last = tokens.last, case .space = last {" + , indent 4 "tokens.removeLast()" + , indent 2 "}" + , "}" + , "" + ] + + dropTrailingNewlinesFunctionDeclaration = vcat + [ "private func dropTrailingNewlines(from tokens: inout [Token]) {" + , indent 2 "while let last = tokens.last, case .newline = last {" + , indent 4 "tokens.removeLast()" + , indent 2 "}" + , "}" + ] + +-- | generate function which will print user-defined and predefined tokens. +mkTokenPrinter :: CF -> Doc +mkTokenPrinter cf = vcat + [ tokenPrinters ] + where + -- allTokenTypes = getAllTokenTypenames cf + -- tokensUnionType = intercalate " | " allTokenTypes + + tokenPrinters = vcat $ map mkTokenPrinter (getAllTokenCats cf) + mkTokenPrinter tokenCat = vcat + [ text $ "extension" +++ catToSwiftType tokenCat ++ ":" +++ prettyPrintProtocolName ++ "{" + , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" + , indent 4 "String(value)" + , indent 2 "}" + , "}" + , "" + ] + +mkNodePrinter :: Cat -> Doc +mkNodePrinter cat@(Cat _) = vcat + [ text $ "extension" +++ catToSwiftType cat ++ ":" +++ prettyPrintProtocolName ++ "{" + , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" + , indent 4 $ prettyPrintRenderCall ++ "(" ++ prettifyFnName ++ "(self))" + , indent 2 "}" + , "}" + ] + where + printFnName = mkPrintFnName cat + prettifyFnName = mkPrettifyFnName cat + +mkNodePrinter listCat@(ListCat _) = vcat + [ text $ "extension" +++ catToSwiftType listCat ++ ":" +++ prettyPrintProtocolName ++ "{" + , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" + , indent 4 $ prettyPrintRenderCall ++ "(" ++ prettifyFnName ++ "(self))" + , indent 2 "}" + , "}" + ] + where + prettifyFnName = mkPrettifyFnName listCat + printFnName = mkPrintFnName listCat + catOfListType = catToSwiftType (normCatOfList listCat) + +mkNodePrinter otherCat = error $ "Unknown category for making node printer" +++ catToStr otherCat + +mkRulePrinter :: String -> Doc +mkRulePrinter ruleLabel = vcat + [ text $ "func" +++ printFnName ++ "(node:" +++ mkTypeName ruleLabel ++ ") -> String {" + , indent 2 $ "return" +++ prettyPrintRenderCall ++ "(" ++ prettifyFnName ++ "(node))" + , "}" + ] + where + printFnName = "print" ++ firstUpperCase ruleLabel + prettifyFnName = "prettify" ++ firstUpperCase ruleLabel + +mkNodePrettifier :: CF -> Cat -> Doc +mkNodePrettifier cf cat@(Cat _) = vcat $ concat + [ [text $ "func" +++ prettifyFnName ++ "(_ node:" +++ catToSwiftType cat ++ ") -> [String] {" ] + , [indent 2 $ "switch node {"] + , prettifyRulesCondition + , [indent 2 "}"] + , ["}"] + -- , rulesPrettifiers + ] + where + rules = map (\rule -> (wpThing (funRule rule), rhsRule rule)) $ + filter (not . isCoercion . funRule) $ + rulesForNormalizedCat cf cat + + mkCaseStmt rule@(ruleLabel, sentForm) = vcat + [ indent 4 $ caseDeclaration ++ ruleLabel ++ (associatedValues varNames) ++ ":" + , nest 6 $ hcat [ text "return ", (mkRulePrettifier rule)] + ] + where + varNames = map wrapIfNeeded $ getVarsFromCats (lefts sentForm) + + caseDeclaration + | null varNames = "case ." + | otherwise = "case let ." + + associatedValues varNames + | null varNames = "" + | otherwise = "(" ++ (intercalate ", " varNames) ++ ")" + + + prettifyRulesCondition = map mkCaseStmt rules + prettifyFnName = mkPrettifyFnName cat + -- rulesPrettifiers = map mkRulePrettifier rules + -- add getVarsFromCats + +mkNodePrettifier cf listCat@(ListCat _) = vcat + [ text $ "func " ++ prettifyFnName ++ "(_ list: [" ++ catOfListType ++ "]) -> [String] {" + , nest 2 returnStmt + , "}" + ] + where + prettifyFnName = mkPrettifyFnName listCat + catOfListType = catToSwiftType (normCatOfList listCat) + + rules = rulesForCat cf listCat + consRule = find (isConsFun . funRule) rules + consSeparator = maybe Nothing findSeparator consRule + + oneRule = find (isOneFun . funRule) rules + oneSeparator = maybe Nothing findSeparator oneRule + + nilRule = find (isNilFun . funRule) rules + + findSeparator :: Rul a -> Maybe String + findSeparator rule = fmap fst (uncons terminals) + where + terminals = rights (rhsRule rule) + + separator = fromMaybe "" consSeparator + isTerminator = (isJust nilRule && isNothing oneRule && isJust consRule && isJust consSeparator) + || (isNothing nilRule && isJust oneRule && isJust oneSeparator && isJust consRule && isJust consSeparator) + + itemCat = catOfList listCat + printItemFn tokenCat@(TokenCat _) = mkPrintFnName tokenCat + printItemFn cat = mkPrettifyFnName cat + + returnStmt = text listTokens + where + listMapping = "list.flatMap { item in " ++ printItemFn itemCat ++ "(item) + " ++ "[" ++ wrapSQ separator ++ "] }" + listTokens = listMapping + -- listMapping ++ if isTerminator then "" else ".dropLast()" -- TODO: check + +mkNodePrettifier _ otherCat = error $ "Unknown category for making node prettifier" +++ catToStr otherCat + +mkRulePrettifier :: (String, SentForm) -> Doc +mkRulePrettifier (ruleLabel, sentForm) = vcat + [ text prettifyBody ] + where + varNames = map wrapIfNeeded $ getVarsFromCats (lefts sentForm) + + addVarNames :: [Either Cat String] -> [String] -> [Either (Cat, String) String] + addVarNames [] _ = [] + addVarNames list [] = map (either (\cat -> Left (cat, "")) Right) list + addVarNames (x:xs) allVars@(var:vars) = case x of + (Right terminal) -> Right terminal : addVarNames xs allVars + (Left cat) -> Left (cat, var) : addVarNames xs vars + + sentFormWithVarNames = addVarNames sentForm varNames + prettifiedRule = intercalate " + " $ map (either getPrettifierForCat (\x -> "[" ++ (wrapSQ x) ++ "]")) sentFormWithVarNames + where + getPrettifierForCat :: (Cat, String) -> String + getPrettifierForCat (tokenCat@(TokenCat _), varName) = "[" ++ varName ++ "." ++ prettyPrintPropertyName ++ "]" + getPrettifierForCat (cat, varName) = mkPrettifyFnName cat ++ "(" ++ varName ++ ")" + + prettifyBody + | null sentFormWithVarNames = "[]" + | otherwise = prettifiedRule + +mkPrettifyFnName :: Cat -> String +mkPrettifyFnName cat = "prettify" ++ mkName cat + where + mkName (ListCat cat) = ("ListOf"++) $ firstUpperCase (catToStr cat) + mkName otherCat = firstUpperCase $ catToStr (normCat otherCat) + +mkPrintFnName :: Cat -> String +mkPrintFnName cat = "print" ++ mkName cat + where + mkName (ListCat itemCat) = "ListOf" ++ firstUpperCase (catToStr itemCat) + mkName otherCat = firstUpperCase $ catToStr (normCat otherCat) \ No newline at end of file diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index e2c2d71a..49fce489 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -273,7 +273,7 @@ toMixedCase = firstUpperCase . mkName reservedKeywords MixedCase -- | wrap string into single quotes. wrapSQ :: String -> String -wrapSQ str = "'" ++ str ++ "'" +wrapSQ str = "\"" ++ str ++ "\"" -- | indent string with N spaces. indentStr :: Int -> String -> String @@ -321,4 +321,22 @@ mkBuildFnName cat = "build" ++ firstUpperCase (restName cat) restName cat = case cat of ListCat cat -> restName cat ++ "List" TokenCat cat -> cat ++ "Token" - otherCat -> catToStr otherCat \ No newline at end of file + otherCat -> catToStr otherCat + +-- | we don't need to declare nodes, which will represent list +-- because they will be referenced directly with TS type Array. +getAbsynWithoutLists :: CF -> [Data] +getAbsynWithoutLists = filter (not . isList . fst) . getAbstractSyntax + +-- | produces a type name for rule label +mkTypeName :: String -> String +mkTypeName = mkName reservedKeywords OrigCase + +-- -- | generate name for function which will interpret node for some cat. +-- mkInterpretFnName :: Cat -> String +-- mkInterpretFnName cat = "interpret" ++ firstUpperCase (restName cat) +-- where +-- restName cat = case cat of +-- ListCat cat -> restName cat ++ "List" +-- TokenCat cat -> cat ++ "Token" +-- otherCat -> catToStr otherCat \ No newline at end of file From b97510777f5fa7fbeca55245f7274da64bf58610 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Thu, 23 Jan 2025 15:45:54 +0300 Subject: [PATCH 63/70] feat(pretty-printer): fix swift type-checker problem, delete protocol, use node name in print computed variable to fix problem with redeclaration --- .../BNFC/Backend/Swift/CFtoSwiftPrinter.hs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs index a71d5cb0..83d17197 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs @@ -11,9 +11,6 @@ import BNFC.Backend.Swift.Common (catToSwiftType, indent, wrapSQ, getVarsFromCat import BNFC.Backend.Common.NamedVariables (firstUpperCase) import Data.Maybe (isJust, isNothing, fromMaybe) -prettyPrintProtocolName :: String -prettyPrintProtocolName = "PrettyPrintable" - prettyPrintPropertyName :: String prettyPrintPropertyName = "printed" @@ -26,8 +23,7 @@ prettyPrintRenderCall = prettyPrintRenderClassName ++ ".shared.render" -- | generate pretty-printers for nodes of an AST cf2SwiftPrinter :: CF -> Doc cf2SwiftPrinter cf = vcat - [ protocolDeclaration - , rendererDeclaration + [ rendererDeclaration , tokenPrinterDecl , nodesPrintersDecls , "" @@ -49,14 +45,6 @@ cf2SwiftPrinter cf = vcat (filter (not . isCoercion) . rulesForNormalizedCat cf) (filter (not . isList) cats) -protocolDeclaration :: Doc -protocolDeclaration = vcat - [ text $ "public protocol" +++ prettyPrintProtocolName +++ "{" - , nest 2 $ text $ "var" +++ prettyPrintPropertyName +++ ": String { get }" - , "}" - , "" - ] - rendererDeclaration :: Doc rendererDeclaration = vcat [ text $ "final class" +++ className +++ "{" @@ -152,7 +140,7 @@ rendererDeclaration = vcat , indent 4 "dropTrailingNewlines(from: &result)" , indent 2 "}" , indent 2 "result.append(.text(value: token))" - , indent 2 "if token != \".\" {" + , indent 2 "if token != \".\" && token != \"(\" {" , indent 4 "result.append(.space)" , indent 2 "}" , "case \";\":" @@ -223,7 +211,7 @@ mkTokenPrinter cf = vcat tokenPrinters = vcat $ map mkTokenPrinter (getAllTokenCats cf) mkTokenPrinter tokenCat = vcat - [ text $ "extension" +++ catToSwiftType tokenCat ++ ":" +++ prettyPrintProtocolName ++ "{" + [ text $ "extension" +++ catToSwiftType tokenCat +++ "{" , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" , indent 4 "String(value)" , indent 2 "}" @@ -233,8 +221,8 @@ mkTokenPrinter cf = vcat mkNodePrinter :: Cat -> Doc mkNodePrinter cat@(Cat _) = vcat - [ text $ "extension" +++ catToSwiftType cat ++ ":" +++ prettyPrintProtocolName ++ "{" - , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" + [ text $ "extension" +++ catToSwiftType cat +++ "{" + , nest 2 $ text $ "public var" +++ printFnName ++ ": String {" , indent 4 $ prettyPrintRenderCall ++ "(" ++ prettifyFnName ++ "(self))" , indent 2 "}" , "}" @@ -244,8 +232,8 @@ mkNodePrinter cat@(Cat _) = vcat prettifyFnName = mkPrettifyFnName cat mkNodePrinter listCat@(ListCat _) = vcat - [ text $ "extension" +++ catToSwiftType listCat ++ ":" +++ prettyPrintProtocolName ++ "{" - , nest 2 $ text $ "public var" +++ prettyPrintPropertyName ++ ": String {" + [ text $ "extension" +++ catToSwiftType listCat +++ "{" + , nest 2 $ text $ "public var" +++ printFnName ++ ": String {" , indent 4 $ prettyPrintRenderCall ++ "(" ++ prettifyFnName ++ "(self))" , indent 2 "}" , "}" @@ -283,7 +271,9 @@ mkNodePrettifier cf cat@(Cat _) = vcat $ concat mkCaseStmt rule@(ruleLabel, sentForm) = vcat [ indent 4 $ caseDeclaration ++ ruleLabel ++ (associatedValues varNames) ++ ":" - , nest 6 $ hcat [ text "return ", (mkRulePrettifier rule)] + , indent 6 "var result = [String]()" + , nest 6 $ mkRulePrettifier rule + , indent 6 $ "return result" ] where varNames = map wrapIfNeeded $ getVarsFromCats (lefts sentForm) @@ -330,20 +320,19 @@ mkNodePrettifier cf listCat@(ListCat _) = vcat || (isNothing nilRule && isJust oneRule && isJust oneSeparator && isJust consRule && isJust consSeparator) itemCat = catOfList listCat - printItemFn tokenCat@(TokenCat _) = mkPrintFnName tokenCat - printItemFn cat = mkPrettifyFnName cat + printItemCall tokenCat@(TokenCat _) = "item.printed" + printItemCall cat = mkPrettifyFnName cat ++ "(item)" returnStmt = text listTokens where - listMapping = "list.flatMap { item in " ++ printItemFn itemCat ++ "(item) + " ++ "[" ++ wrapSQ separator ++ "] }" - listTokens = listMapping - -- listMapping ++ if isTerminator then "" else ".dropLast()" -- TODO: check + listMapping = "list.flatMap { item in " ++ printItemCall itemCat ++ " + " ++ "[" ++ wrapSQ separator ++ "] }" + -- listTokens = listMapping -- it depends on target language, but we do not have this knowledge in advance + listTokens = listMapping ++ if isTerminator then "" else ".dropLast()" mkNodePrettifier _ otherCat = error $ "Unknown category for making node prettifier" +++ catToStr otherCat mkRulePrettifier :: (String, SentForm) -> Doc -mkRulePrettifier (ruleLabel, sentForm) = vcat - [ text prettifyBody ] +mkRulePrettifier (ruleLabel, sentForm) = vcat $ map text prettifyBody where varNames = map wrapIfNeeded $ getVarsFromCats (lefts sentForm) @@ -355,14 +344,14 @@ mkRulePrettifier (ruleLabel, sentForm) = vcat (Left cat) -> Left (cat, var) : addVarNames xs vars sentFormWithVarNames = addVarNames sentForm varNames - prettifiedRule = intercalate " + " $ map (either getPrettifierForCat (\x -> "[" ++ (wrapSQ x) ++ "]")) sentFormWithVarNames + prettifiedRule = map (("result +=" +++) . (either getPrettifierForCat (\x -> "[" ++ (wrapSQ x) ++ "]"))) sentFormWithVarNames where getPrettifierForCat :: (Cat, String) -> String getPrettifierForCat (tokenCat@(TokenCat _), varName) = "[" ++ varName ++ "." ++ prettyPrintPropertyName ++ "]" getPrettifierForCat (cat, varName) = mkPrettifyFnName cat ++ "(" ++ varName ++ ")" prettifyBody - | null sentFormWithVarNames = "[]" + | null sentFormWithVarNames = [""] | otherwise = prettifiedRule mkPrettifyFnName :: Cat -> String From 596a556c473235d134ee1b01ecfaeaf55cb57ead Mon Sep 17 00:00:00 2001 From: NAD777 Date: Thu, 23 Jan 2025 17:20:15 +0300 Subject: [PATCH 64/70] feat(pretty-printer): adjust render according to stella, fix problem with array of tokens --- .../BNFC/Backend/Swift/CFtoSwiftPrinter.hs | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs index 83d17197..d1ddddbd 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs @@ -114,6 +114,7 @@ rendererDeclaration = vcat where body = vcat [ "var result: [Token] = []" + , "var insideBrackets = false" , "for token in tokens {" , indent 2 "switch token {" , nest 4 casesDeclaration @@ -125,7 +126,9 @@ rendererDeclaration = vcat ] casesDeclaration = vcat [ "case \"\", \" \":" - , indent 2 "continue" + , indent 2 "if insideBrackets {" + , indent 4 "continue" + , indent 2 "}" , "case \"{\":" , indent 2 "result.append(.text(value: token))" , indent 2 "result.append(.newline(indentShift: 1))" @@ -134,13 +137,23 @@ rendererDeclaration = vcat , indent 2 "result.append(.newline(indentShift: -1))" , indent 2 "result.append(.text(value: token))" , indent 2 "result.append(.newline(indentShift: 0))" - , "case \"(\", \")\", \"[\", \"]\", \"<\", \">\", \",\", \".\":" + , "case \"[\":" + , indent 2 "dropTrailingSpaces(from: &result)" + , indent 2 "result.append(.space)" + , indent 2 "result.append(.text(value: token))" + , indent 2 "insideBrackets = true" + , "case \"]\":" + , indent 2 "dropTrailingSpaces(from: &result)" + , indent 2 "result.append(.text(value: token))" + , indent 2 "insideBrackets = false" + , indent 2 "result.append(.space)" + , "case \"(\", \")\", \"<\", \">\", \",\", \".\":" , indent 2 "dropTrailingSpaces(from: &result)" , indent 2 "if token == \")\" || token == \"]\" || token == \"}\" {" - , indent 4 "dropTrailingNewlines(from: &result)" + , indent 4 "dropTrailingNewlines(from: &result)" , indent 2 "}" , indent 2 "result.append(.text(value: token))" - , indent 2 "if token != \".\" && token != \"(\" {" + , indent 2 "if token != \".\" && token != \"(\" && !insideBrackets {" , indent 4 "result.append(.space)" , indent 2 "}" , "case \";\":" @@ -148,10 +161,16 @@ rendererDeclaration = vcat , indent 2 "dropTrailingNewlines(from: &result)" , indent 2 "result.append(.text(value: token))" , indent 2 "result.append(.newline(indentShift: 0))" + , "case \"return\":" + , indent 2 "result.append(.text(value: token))" + , indent 2 "result.append(.space)" , "default:" , indent 2 "result.append(.text(value: token))" - , indent 2 "result.append(.space)" + , indent 2 "if !insideBrackets {" + , indent 4 "result.append(.space)" + , indent 2 "}" ] + groupTokensFunctionDeclaration = vcat [ "private func groupTokens(_ tokens: [Token]) -> [(indentationLevel: Int, tokens: [Token])] {" @@ -320,7 +339,7 @@ mkNodePrettifier cf listCat@(ListCat _) = vcat || (isNothing nilRule && isJust oneRule && isJust oneSeparator && isJust consRule && isJust consSeparator) itemCat = catOfList listCat - printItemCall tokenCat@(TokenCat _) = "item.printed" + printItemCall tokenCat@(TokenCat _) = "[item.printed]" printItemCall cat = mkPrettifyFnName cat ++ "(item)" returnStmt = text listTokens From b566c39b7c94a434b0ece8de4211330bd9094fb5 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 6 Apr 2025 22:14:15 +0300 Subject: [PATCH 65/70] feat(utils): adjust keywords and build ins --- source/src/BNFC/Backend/Swift/Common.hs | 146 ++++++++++++++++++------ 1 file changed, 113 insertions(+), 33 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index 49fce489..22aed021 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -183,84 +183,164 @@ taken = [] builtIn :: [String] builtIn = [ "Int" + , "Int8" + , "Int16" + , "Int32" + , "Int64" + , "UInt" + , "UInt8" + , "UInt16" + , "UInt32" + , "UInt64" , "Double" , "Float" + , "Float80" , "String" + , "Character" , "Bool" + , "Array" , "Set" , "Void" , "Dictionary" , "Optional" - , "Any" ] + , "Any" + , "Never" + , "Result" + , "Error" + , "AnyObject" + , "AnyClass" + , "ClosedRange" + , "Range" + , "Strideable" + , "RawRepresentable" + , "Hashable" + , "Codable" + , "Encodable" + , "Decodable" + , "Equatable" + , "Comparable" + , "Identifiable" + , "CaseIterable" + , "RandomNumberGenerator" + , "Sequence" + , "Collection" + , "IteratorProtocol" + ] keywords :: [String] -keywords = [ "abstract" +keywords = [ "some" + , "any" , "as" , "assert" , "async" , "await" - , "base" , "break" , "case" , "catch" , "class" - , "const" + , "struct" + , "actor" + , "var" + , "let" , "continue" - , "covariant" , "default" - , "deferred" + , "defer" , "do" , "dynamic" , "else" , "enum" - , "export" - , "extends" , "extension" - , "external" - , "factory" , "false" , "final" - , "finally" , "for" - , "Function" , "get" - , "hide" + , "set" , "if" - , "implements" + , "where" , "import" , "in" - , "interface" + , "protocol" , "is" - , "late" - , "library" - , "mixin" - , "new" - , "null" - , "of" - , "on" - , "operator" - , "part" - , "required" - , "rethrow" + , "nil" + , "rethrows" , "return" - , "set" - , "show" , "static" , "super" , "switch" - , "sync" - , "this" + , "self" + , "Self" + , "super" , "throw" + , "throws" , "true" , "try" , "type" , "typealias" - , "var" - , "void" , "when" , "with" , "while" - , "yield" ] + , "yield" + , "fallthrough" + , "guard" + , "repeat" + , "associativity" + , "convenience" + , "didSet" + , "willSet" + , "indirect" + , "infix" + , "lazy" + , "left" + , "mutating" + , "none" + , "nonmutating" + , "optional" + , "override" + , "postfix" + , "precedence" + , "prefix" + , "Protocol" + , "required" + , "right" + , "Type" + , "unowned" + , "weak" + , "associatedtype" + , "inout" + , "func" + , "init" + , "deinit" + , "open" + , "public" + , "package" + , "internal" + , "fileprivate" + , "private" + , "private(set)" + , "subscript" + , "package" + , "macro" + , "#available" + , "#else" + , "#elseif" + , "#endif" + , "#if" + , "#file" + , "#function" + , "#line" + , "#column" + , "#fileID" + , "#filePath" + , "#selector" + , "#keyPath" + , "@discardableResult" + , "escaping" + , "noescape" + , "borrowing" + , "consuming" + , "each" + , "#main" + ] -- from TS implementation From 197bf4e3a22d96317b1866d0ecc39ae9967b61ee Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 27 Apr 2025 18:17:09 +0300 Subject: [PATCH 66/70] feat(skeleton): change skeleton file dir --- source/src/BNFC/Backend/Swift.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index ab495f36..5b43d02b 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -38,7 +38,7 @@ makeSwift opts@Options{..} cf = do mkfile (targetDir "Printer.swift") makeSwiftComment printerContent mkfile (targetDir langNameUpperCased ++ ".swift") makeSwiftComment (publicApiContent langNameUpperCased) mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) - mkfile (dirBase "Skeleton.swift") makeSwiftComment skeletonContent + mkfile "Skeleton.swift" makeSwiftComment skeletonContent where packageName = maybe id (+.+) inPackage $ mkName [] CamelCase lang langName = firstLowerCase $ mkName [] CamelCase lang From 553ff43996e8b0353d406eab8f0232d03f27e096 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 27 Apr 2025 22:27:00 +0300 Subject: [PATCH 67/70] feat(builder, ast): change filenames according to swift conventions --- source/src/BNFC/Backend/Swift.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 5b43d02b..607df0b0 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -33,8 +33,8 @@ makeSwift opts@Options{..} cf = do MakeFile.mkMakefile optMake $ makefileContent targetDir - mkfile (targetDir "ast.swift") makeSwiftComment astContent - mkfile (targetDir "builder.swift") makeSwiftComment builderContent + mkfile (targetDir "AbstractSyntaxTree.swift") makeSwiftComment astContent + mkfile (targetDir "Builder.swift") makeSwiftComment builderContent mkfile (targetDir "Printer.swift") makeSwiftComment printerContent mkfile (targetDir langNameUpperCased ++ ".swift") makeSwiftComment (publicApiContent langNameUpperCased) mkfile (dirBase "Package.swift") makePackageHeader (packageFileContent langNameUpperCased) From 876ca2deefaca3f199f472bff9f52621e6cd9f92 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Sun, 11 May 2025 23:10:11 +0300 Subject: [PATCH 68/70] feat(skeleton): fix name collisions, user-defined types --- source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs index 6a56212c..119efb4a 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs @@ -32,7 +32,7 @@ cf2SwiftSkeleton langName cf = cat2SwiftType' = cat2SwiftType langName buildUserToken :: UserDef -> String buildUserToken token = - "String interpret" ++ (censorName' token) ++ "(x) => x;" + "func interpret" ++ (censorName' token) ++ "(_ x: " ++ (censorName' token) ++ "Token) -> String { x.value }" genData :: Data -> [String] genData (cat, rules) @@ -65,7 +65,7 @@ cf2SwiftSkeleton langName cf = ++ ")\"" where associatedValues [] = [] - associatedValues (x: vars) = [buildVariableName x] ++ (associatedValues vars) + associatedValues (x: vars) = [wrapIfNeeded $ buildVariableName x] ++ (associatedValues vars) arguments _ [] = [] arguments generator (x:vars) = @@ -78,7 +78,7 @@ cf2SwiftSkeleton langName cf = if n > 0 then varCall ++ ".map(" ++ (unpack interp (n - 1)) ++ ")" -- TODO: check this else - interp ++ "(" ++ varCall ++ ")" + interp ++ "(" ++ wrapIfNeeded varCall ++ ")" where unpack funName n | n <= 0 = funName From a2cf53ce3b301183e83ef70d7dbb071f2ebafe9a Mon Sep 17 00:00:00 2001 From: NAD777 Date: Mon, 23 Jun 2025 04:34:48 +0300 Subject: [PATCH 69/70] feat(all, skeleton): make lower first case letter, try to fix skeleton --- source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs | 4 ++-- source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs | 4 ++-- source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs | 12 +++++++----- source/src/BNFC/Backend/Swift/Common.hs | 7 +++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs index 08471f26..82abae52 100644 --- a/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs +++ b/source/src/BNFC/Backend/Swift/CFToSwiftBuilder.hs @@ -15,7 +15,7 @@ import BNFC.CF import BNFC.Backend.Swift.Common import BNFC.Options (SharedOptions (lang)) import BNFC.Backend.Antlr.CFtoAntlr4Parser (antlrRuleLabel, makeLeftRecRule) -import BNFC.Backend.Common.NamedVariables (firstUpperCase) +import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) type RuleData = (Cat, [(String, SentForm)]) @@ -114,7 +114,7 @@ mkBuildFunction lang (cat, rulesWithLabels) = vcat where varNames = getVarsFromCats rhsCats rhsCats = map fst rhsRuleWithIdx - returnStatementBase = "return" +++ "." ++ ruleLabel + returnStatementBase = "return" +++ "." ++ (firstLowerCase ruleLabel) returnStatement | null varNames = returnStatementBase | otherwise = returnStatementBase ++ "(" ++ intercalate ", " (map wrapIfNeeded varNames) ++ ")" diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs index d1ddddbd..c16e2fc9 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftPrinter.hs @@ -8,7 +8,7 @@ import Text.PrettyPrint.HughesPJClass (Doc, text, vcat, hcat, nest) import BNFC.CF (CF, ruleGroups, Rul (rhsRule, funRule), Cat (Cat, ListCat, TokenCat, CoercCat), WithPosition (wpThing), IsFun (isCoercion, isConsFun, isOneFun, isNilFun), catToStr, SentForm, rulesForNormalizedCat, normCat, normCatOfList, catOfList, isList, allParserCats, rulesForCat) import BNFC.Utils ((+++)) import BNFC.Backend.Swift.Common (catToSwiftType, indent, wrapSQ, getVarsFromCats, getAbsynWithoutLists, getAllTokenTypenames, getAllTokenCats, mkTypeName, wrapIfNeeded) -import BNFC.Backend.Common.NamedVariables (firstUpperCase) +import BNFC.Backend.Common.NamedVariables (firstUpperCase, firstLowerCase) import Data.Maybe (isJust, isNothing, fromMaybe) prettyPrintPropertyName :: String @@ -289,7 +289,7 @@ mkNodePrettifier cf cat@(Cat _) = vcat $ concat rulesForNormalizedCat cf cat mkCaseStmt rule@(ruleLabel, sentForm) = vcat - [ indent 4 $ caseDeclaration ++ ruleLabel ++ (associatedValues varNames) ++ ":" + [ indent 4 $ caseDeclaration ++ (firstLowerCase ruleLabel) ++ (associatedValues varNames) ++ ":" , indent 6 "var result = [String]()" , nest 6 $ mkRulePrettifier rule , indent 6 $ "return result" diff --git a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs index 119efb4a..af2f8726 100644 --- a/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs +++ b/source/src/BNFC/Backend/Swift/CFtoSwiftSkeleton.hs @@ -9,7 +9,7 @@ import Data.Maybe ( mapMaybe ) import BNFC.CF import BNFC.Utils ( (+++) ) -import BNFC.Backend.Common.NamedVariables ( UserDef ) +import BNFC.Backend.Common.NamedVariables ( UserDef, firstUpperCase ) import BNFC.Backend.Swift.Common import Data.List (intercalate) @@ -28,7 +28,6 @@ cf2SwiftSkeleton langName cf = censorName' = censorName langName str2SwiftClassName' = str2SwiftClassName langName getVars' = getVars_ langName - cat2SwiftClassName' = cat2SwiftClassName langName cat2SwiftType' = cat2SwiftType langName buildUserToken :: UserDef -> String buildUserToken token = @@ -38,9 +37,9 @@ cf2SwiftSkeleton langName cf = genData (cat, rules) | (catToStr cat) `elem` (map fst rules) = [] | otherwise = - let name = cat2SwiftClassName' cat + let name = identCat $ normCat cat varType = buildVariableTypeFromSwiftType $ cat2SwiftType' cat - in [ "func interpret" ++ name ++ "(_ e:" +++ varType ++ ") -> String {" ] + in [ "func interpret" ++ (firstUpperCase name) ++ "(_ e:" +++ varType ++ ") -> String {" ] ++ (indent_ 1 $ if isList cat then [ "\"\\(e)\"" ] else [ "switch (e) {" ] @@ -60,13 +59,16 @@ cf2SwiftSkeleton langName cf = varName = lowerFirst $ censorName' className vars = getVars' rhs in Just $ - "case let ." ++ className ++ "(" ++ (intercalate ", " (associatedValues vars)) ++ "): \"" ++ className ++ "(" + caseDecl className vars ++ " \"" ++ className ++ "(" ++ (concat $ (drop 1) $ arguments (genVarRepr varName) vars) ++ ")\"" where associatedValues [] = [] associatedValues (x: vars) = [wrapIfNeeded $ buildVariableName x] ++ (associatedValues vars) + caseDecl className [] = "case ." ++ className ++ ":" + caseDecl className vars = "case let ." ++ className ++ "(" ++ (intercalate ", " (associatedValues vars)) ++ "):" + arguments _ [] = [] arguments generator (x:vars) = [ ", ", "\\(" ++ (generator x) ++ ")" ] ++ (arguments generator vars) diff --git a/source/src/BNFC/Backend/Swift/Common.hs b/source/src/BNFC/Backend/Swift/Common.hs index 22aed021..1476d723 100644 --- a/source/src/BNFC/Backend/Swift/Common.hs +++ b/source/src/BNFC/Backend/Swift/Common.hs @@ -10,21 +10,20 @@ import BNFC.CF import qualified Data.Char as Char import Data.Char (toLower) import BNFC.Utils (mkName, NameStyle (OrigCase, MixedCase), mkNames) -import BNFC.Backend.Common.NamedVariables (getVars, firstUpperCase) +import BNFC.Backend.Common.NamedVariables (getVars, firstUpperCase, firstLowerCase) cat2SwiftClassName :: String -> Cat -> String cat2SwiftClassName langName cat = str2SwiftClassName langName $ identCat $ normCat cat - -- Pick a class name that is appropriate for the Swift str2SwiftClassName :: String -> String -> String -- str2SwiftClassName langName str = upperFirst $ censorName langName str -str2SwiftClassName langName str = wrapIfNeeded $ upperFirst str +str2SwiftClassName langName str = wrapIfNeeded $ firstLowerCase str -- Pick a case name that is appropriate for the Swift str2SwiftCaseName :: String -> String -> String -str2SwiftCaseName langName str = lowerFirst $ censorName langName str +str2SwiftCaseName langName str = firstLowerCase $ censorName langName str -- Pick a class name that is appropriate for the Antlr str2AntlrClassName :: String -> String From a02d025bdc9d8adb0446bcc692db279b8da7ab70 Mon Sep 17 00:00:00 2001 From: NAD777 Date: Mon, 23 Jun 2025 11:14:46 +0300 Subject: [PATCH 70/70] feat(public-api): change ast provider function name --- source/src/BNFC/Backend/Swift.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/src/BNFC/Backend/Swift.hs b/source/src/BNFC/Backend/Swift.hs index 607df0b0..622a4ee5 100644 --- a/source/src/BNFC/Backend/Swift.hs +++ b/source/src/BNFC/Backend/Swift.hs @@ -123,7 +123,7 @@ makeSwift opts@Options{..} cf = do publicApiContent langName = vcat [ "import Antlr4" , "" - , text $ "public func getAst(from text: String) -> Result<" ++ catToStr firstCat ++", Error> {" + , text $ "public func ast(from text: String) -> Result<" ++ catToStr firstCat ++ ", Error> {" , nest 2 $ vcat [ "let input = ANTLRInputStream(text)" , text $ "let lexer =" +++ langName ++ "Lexer(input)"