From 7eff42993222ea88dde163e0b7baef316fe6a5a8 Mon Sep 17 00:00:00 2001 From: Michael Dmitriev Date: Wed, 4 Oct 2023 00:33:15 +0300 Subject: [PATCH 01/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] [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/52] 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/52] [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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] [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/52] [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/52] [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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] [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/52] 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 3faa18e91f32412c346a5840ad9ad33a4487de97 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Fri, 13 Sep 2024 15:48:21 +0300 Subject: [PATCH 50/52] refactor --- source/src/BNFC/Backend/Dart/CFtoDartAST.hs | 54 +++++++++---------- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 32 ++++++----- 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs index 74e94038..9b518418 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartAST.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartAST.hs @@ -83,43 +83,39 @@ cf2DartAST langName cf = -- 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 " &&") + prEquals className variables = + [ "@override" + , "bool operator ==(Object o) =>" + , " o is" +++ className +++ "&&" + , " o.runtimeType == runtimeType" + ++ ( case variables of + [] -> ";" + _ -> " &&" ) ] ++ checkChildren where - checkChildren = generateEqualities variables - generateEqualities [] = [] - generateEqualities (variable:rest) = - let name = buildVariableName variable - in [ - " " ++ name +++ "==" +++ "o." ++ name ++ - (if null rest then ";" else " &&") - ] ++ generateEqualities rest + checkChildren = buildLines $ map (eqCond . buildVariableName) variables + eqCond name = " " ++ name +++ "==" +++ "o." ++ name + buildLines [] = [] + buildLines [x] = [x ++ ";"] + buildLines (x:xs) = [x ++ " &&"] ++ (buildLines xs) -- 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 ++ ", " + prHashCode vars = + [ "@override" + , "int get hashCode => Object.hashAll([" + ++ (concatMap ((++ ", ") . buildVariableName) vars) + ++ "]);" ] -- Generate variable definitions for the class prInstanceVariables :: [DartVar] -> [String] - prInstanceVariables vars = map variableLine vars + prInstanceVariables vars = map variableAssignment vars where - variableLine variable = - let vType = buildVariableType variable - vName = buildVariableName variable + variableAssignment v = + let vType = buildVariableType v + vName = buildVariableName v in "final" +++ vType +++ vName ++ ";" @@ -134,6 +130,6 @@ cf2DartAST langName cf = assignment variable = "required this." ++ buildVariableName variable ++ ", " prPrettyPrint :: String -> [String] - prPrettyPrint name = [ - "@override", - "String get print => pp.print" ++ name ++ "(this);" ] + 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 153974d2..594c1d4e 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -23,8 +23,8 @@ cf2DartBuilder lang cf = concatMap generateBuilders rules where leftRecRuleMaker = (makeLeftRecRule cf) - rules = map - (\(cat, rules) -> (cat, (map leftRecRuleMaker rules))) $ ruleGroups cf + rules = map mkRule $ ruleGroups cf + mkRule cat rules = (cat, (map leftRecRuleMaker rules)) imports lang = [ "import 'package:antlr4/antlr4.dart' show Token;" , "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;" @@ -60,7 +60,8 @@ cf2DartBuilder lang cf = reformatRule :: Rule -> (String, [Cat]) - reformatRule rule = (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) + reformatRule rule = + (wpThing $ funRule rule, [normCat c | Left c <- rhsRule rule ]) generateRuntimeTypeMapping :: Cat -> [(Int, String, [Either Cat String])] -> [String] @@ -106,7 +107,6 @@ cf2DartBuilder lang cf = buildUniversalChild (contextName $ str2AntlrClassName name) name - -- (str2DartClassName' name) "ctx" suffix -> buildUniversalChild (contextName (className ++ "_" ++ suffix)) @@ -157,11 +157,11 @@ cf2DartBuilder lang cf = where generateReturnStatement :: Fun -> [DartVar] -> String -> [String] generateReturnStatement fun vars typeName - | isNilFun fun = ["return IList();"] + | isNilFun fun = [ "return IList();" ] | isOneFun fun = generateOneArgumentListReturn vars | isConsFun fun = generateTwoArgumentsListReturn vars - | otherwise = [ "return" +++ typeName ++ "(" ] ++ - (indent 1 $ generateArgumentsMapping vars ) ++ [");"] + | otherwise = [ "return" +++ typeName ++ "(" ] + ++ (indent 1 $ generateArgumentsMapping vars ) ++ [ ");" ] generateArguments :: Int -> Rule -> [(DartVar, Cat)] -> [String] @@ -181,7 +181,6 @@ cf2DartBuilder lang cf = rhs = buildArgument (precCat cat) (upperFirst $ identCat $ normCat varCat) - -- (cat2DartClassName' varCat) field in [ "final" +++ lhs +++ "=" +++ rhs ++ ";" ] ++ traverseRule ind1 (ind2 + 1) restTs restVars lines @@ -190,21 +189,20 @@ cf2DartBuilder lang cf = 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 + let precedence = case prec of + 0 -> "" + _ -> 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;" ]) ++ - [ "}" ] + [ "if (" ] + ++ (indent 1 [ intercalate " || " $ map condition vars ]) + ++ [ ") {" ] + ++ (indent 1 [ "return null;" ]) + ++ [ "}" ] where condition :: DartVar -> String condition var = buildVariableName var +++ "==" +++ "null" From e3a70047b4028f766544394032e74df6baf77595 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Fri, 13 Sep 2024 15:54:22 +0300 Subject: [PATCH 51/52] fix refactoring caused errors --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 23 ++++++------------- source/src/BNFC/Backend/Dart/Common.hs | 5 ++++ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 594c1d4e..1359f3d6 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -24,7 +24,7 @@ cf2DartBuilder lang cf = where leftRecRuleMaker = (makeLeftRecRule cf) rules = map mkRule $ ruleGroups cf - mkRule cat rules = (cat, (map leftRecRuleMaker rules)) + mkRule (cat, rules) = (cat, (map leftRecRuleMaker rules)) imports lang = [ "import 'package:antlr4/antlr4.dart' show Token;" , "import 'package:fast_immutable_collections/fast_immutable_collections.dart' show IList;" @@ -68,9 +68,7 @@ cf2DartBuilder lang cf = generateRuntimeTypeMapping cat rules = let ctxName = upperFirst $ identCat $ normCat cat astName = buildVariableTypeFromDartType $ cat2DartType' cat - prec = case precCat cat of - 0 -> "" - x -> show x + prec = showPrec $ precCat cat precedencedName = ctxName ++ prec in [ astName ++ "?" +++ "build" ++ precedencedName ++ "(" @@ -89,11 +87,9 @@ cf2DartBuilder lang cf = $ 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 + ( let catName = upperFirst $ identCat $ normCat cat + prec = showPrec $ precCat cat + in catName ++ prec , show i ) otherwise -> (className, "") -- error, no category in the coercion rule lineIndex = show index @@ -130,9 +126,7 @@ cf2DartBuilder lang cf = isConsFun fun) then let cat = valCat rule - prec = case (precCat cat) of - 0 -> "" - i -> show i + prec = showPrec $ precCat cat ctxName = (++ prec) $ upperFirst $ identCat $ normCat cat suffix = antlrListSuffix fun precedencedName = ctxName ++ suffix @@ -189,10 +183,7 @@ cf2DartBuilder lang cf = field = "ctx?.p_" ++ show ind1 ++ "_" ++ show ind2 buildArgument :: Integer -> String -> String -> String buildArgument prec typeName name = - let precedence = case prec of - 0 -> "" - _ -> show prec - in "build" ++ upperFirst typeName ++ precedence ++ "(" ++ name ++ ")" + "build" ++ upperFirst typeName ++ (showPrec prec) ++ "(" ++ name ++ ")" generateNullCheck :: [DartVar] -> [String] diff --git a/source/src/BNFC/Backend/Dart/Common.hs b/source/src/BNFC/Backend/Dart/Common.hs index f8181359..c103f03f 100644 --- a/source/src/BNFC/Backend/Dart/Common.hs +++ b/source/src/BNFC/Backend/Dart/Common.hs @@ -161,6 +161,11 @@ censorName langName name | checkRegistered name = langName ++ upperFirst name | otherwise = name +showPrec prec = + case prec of + 0 -> "" + _ -> show prec + taken = [ "Character" ] builtIn = [ "int" From 9550c2d3580f2db4cccd218248301b2f354e6845 Mon Sep 17 00:00:00 2001 From: xdkomel Date: Fri, 13 Sep 2024 16:51:18 +0300 Subject: [PATCH 52/52] use switch instead of if in the builder --- .../src/BNFC/Backend/Dart/CFtoDartBuilder.hs | 97 ++++++++++--------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs index 1359f3d6..db45b119 100644 --- a/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs +++ b/source/src/BNFC/Backend/Dart/CFtoDartBuilder.hs @@ -139,24 +139,31 @@ cf2DartBuilder lang cf = 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 ) ++ [ ");" ] - + typeName ++ "?" +++ "build" ++ className ++ "(" ++ ctxName ++ "?" +++ "ctx) =>" + ] ++ ( + indent 1 + $ generateSwitch + (generateArguments index rule $ zip vars cats) + (generateNullCheck vars) + (generateReturn fun vars typeName) ) + + generateSwitch :: [String] -> [String] -> [String] -> [String] + generateSwitch [] _ x = endListsListWith ";" x + generateSwitch arguments matching object = + [ "switch ((" ] + ++ (indent 1 arguments) + ++ [")) {"] + ++ [" ("] + ++ (indent 2 matching) + ++ [" ) =>"] + ++ (indent 2 $ endListsListWith "," object) + ++ [" _ => null,"] + ++ ["};"] + + endListsListWith :: [a] -> [[a]] -> [[a]] + endListsListWith _ [] = [] + endListsListWith s x = init x ++ [(last x) ++ s] + generateArguments :: Int -> Rule -> [(DartVar, Cat)] -> [String] generateArguments index r vars = @@ -166,17 +173,16 @@ cf2DartBuilder lang cf = traverseRule :: Int -> Int -> [Either Cat String] -> [(DartVar, Cat)] -> [String] -> [String] - traverseRule _ _ _ [] lines = lines - traverseRule _ _ [] _ lines = lines + traverseRule _ _ _ [] l = l + traverseRule _ _ [] _ l = l 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) - field - in [ "final" +++ lhs +++ "=" +++ rhs ++ ";" ] + [ ( buildArgument + (precCat cat) + (upperFirst $ identCat $ normCat varCat) + field + ) ++ "," ] ++ traverseRule ind1 (ind2 + 1) restTs restVars lines Right _ -> traverseRule ind1 (ind2 + 1) restTs (var:restVars) lines where @@ -187,40 +193,43 @@ cf2DartBuilder lang cf = generateNullCheck :: [DartVar] -> [String] - generateNullCheck [] = [] - generateNullCheck vars = - [ "if (" ] - ++ (indent 1 [ intercalate " || " $ map condition vars ]) - ++ [ ") {" ] - ++ (indent 1 [ "return null;" ]) - ++ [ "}" ] + generateNullCheck = map condition 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 ++ "," + condition var = "final" +++ buildVariableName var ++ "?," + + + generateReturn :: Fun -> [DartVar] -> String -> [String] + generateReturn fun vars typeName + | isNilFun fun = [ "IList()" ] + | isOneFun fun = generateOneArgumentListReturn vars + | isConsFun fun = generateTwoArgumentsListReturn vars + | otherwise = [ typeName ++ "(" ] + ++ (indent 1 $ generateArgumentsMapping vars ) ++ [ ")" ] generateOneArgumentListReturn :: [DartVar] -> [String] generateOneArgumentListReturn (v:_) = - ["return IList([" ++ buildVariableName v ++ "]);"] + ["IList([" ++ buildVariableName v ++ "])"] generateTwoArgumentsListReturn :: [DartVar] -> [String] generateTwoArgumentsListReturn (x:y:_) = let (a, b) = putListSecond x y - in ["return IList([" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",]);"] + in ["IList([" ++ buildVariableName a ++ ", ..." ++ buildVariableName b ++ ",])"] where putListSecond x@((0,_),_) y = (x, y) putListSecond x y = (y, x) + generateArgumentsMapping :: [DartVar] -> [String] + generateArgumentsMapping vars = map mapArgument vars + where + mapArgument variable = + let name = buildVariableName variable + in name ++ ":" +++ name ++ "," + + contextName :: String -> String contextName className = className ++ "Context"