From 0b9caa2a72796acfd131f65c3e18d574666321c6 Mon Sep 17 00:00:00 2001 From: Franco Leonardo Bulgarelli Date: Thu, 16 Feb 2023 13:27:24 -0300 Subject: [PATCH 1/4] Add support for normalizing partial functions --- src/Language/Mulang/Analyzer/Analysis/Json.hs | 1 + src/Language/Mulang/Normalizers/JavaScript.hs | 3 ++- src/Language/Mulang/Normalizers/Python.hs | 3 ++- src/Language/Mulang/Transform/Normalizer.hs | 17 ++++++++++++++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Language/Mulang/Analyzer/Analysis/Json.hs b/src/Language/Mulang/Analyzer/Analysis/Json.hs index de10ded5d..931a82555 100644 --- a/src/Language/Mulang/Analyzer/Analysis/Json.hs +++ b/src/Language/Mulang/Analyzer/Analysis/Json.hs @@ -28,6 +28,7 @@ instance FromJSON NormalizationOptions where <*> v .:? "convertObjectLevelLambdaVariableIntoMethod" .!= convertObjectLevelLambdaVariableIntoMethod d <*> v .:? "convertObjectLevelVariableIntoAttribute" .!= convertObjectLevelVariableIntoAttribute d <*> v .:? "convertObjectIntoDict" .!= convertObjectIntoDict d + <*> v .:? "convertPartialProcedureIntoFunction" .!= convertPartialProcedureIntoFunction d <*> v .:? "sortSequenceDeclarations" .!= sortSequenceDeclarations d <*> v .:? "insertImplicitReturn" .!= insertImplicitReturn d <*> v .:? "compactSequences" .!= compactSequences d diff --git a/src/Language/Mulang/Normalizers/JavaScript.hs b/src/Language/Mulang/Normalizers/JavaScript.hs index f4af7e8a7..1f69d01c7 100644 --- a/src/Language/Mulang/Normalizers/JavaScript.hs +++ b/src/Language/Mulang/Normalizers/JavaScript.hs @@ -9,5 +9,6 @@ javaScriptNormalizationOptions = unnormalized { convertObjectLevelFunctionIntoMethod = True, convertObjectLevelLambdaVariableIntoMethod = True, convertObjectLevelVariableIntoAttribute = True, - sortSequenceDeclarations = SortUniqueNonVariables + sortSequenceDeclarations = SortUniqueNonVariables, + convertPartialProcedureIntoFunction = True } diff --git a/src/Language/Mulang/Normalizers/Python.hs b/src/Language/Mulang/Normalizers/Python.hs index f6a4de7db..b06fa17e1 100644 --- a/src/Language/Mulang/Normalizers/Python.hs +++ b/src/Language/Mulang/Normalizers/Python.hs @@ -4,5 +4,6 @@ import Language.Mulang.Transform.Normalizer (unnormalized, NormalizationOptions( pythonNormalizationOptions :: NormalizationOptions pythonNormalizationOptions = unnormalized { - sortSequenceDeclarations = SortAllNonVariables + sortSequenceDeclarations = SortAllNonVariables, + convertPartialProcedureIntoFunction = True } diff --git a/src/Language/Mulang/Transform/Normalizer.hs b/src/Language/Mulang/Transform/Normalizer.hs index daa61a080..86eadef1f 100644 --- a/src/Language/Mulang/Transform/Normalizer.hs +++ b/src/Language/Mulang/Transform/Normalizer.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveGeneric, ViewPatterns #-} module Language.Mulang.Transform.Normalizer ( normalize, @@ -25,6 +25,7 @@ data NormalizationOptions = NormalizationOptions { convertObjectLevelLambdaVariableIntoMethod :: Bool, convertObjectLevelVariableIntoAttribute :: Bool, convertObjectIntoDict :: Bool, + convertPartialProcedureIntoFunction :: Bool, sortSequenceDeclarations :: SequenceSortMode, insertImplicitReturn :: Bool, compactSequences :: Bool, @@ -46,6 +47,7 @@ unnormalized = NormalizationOptions { convertObjectLevelLambdaVariableIntoMethod = False, convertObjectLevelVariableIntoAttribute = False, convertObjectIntoDict = False, + convertPartialProcedureIntoFunction = False, sortSequenceDeclarations = SortNothing, insertImplicitReturn = False, compactSequences = False, @@ -59,6 +61,7 @@ normalize ops (Application (Primitive op) [e1, e2]) | isCommutative op = Applica normalize ops (LValue n (Lambda vars e)) | convertLambdaVariableIntoFunction ops = SimpleFunction n vars (normalize ops e) normalize ops (LValue n (MuObject e)) | convertObjectVariableIntoObject ops = Object n (normalizeObjectLevel ops e) normalize ops (MuObject e) | convertObjectIntoDict ops = MuDict . normalize ops . normalizeArrows $ e +normalize ops (SimpleProcedure name params body) | convertPartialProcedureIntoFunction ops && isPartialBody body = SimpleFunction name params (normalize ops body) normalize ops (Object n e) = Object n (normalizeObjectLevel ops e) normalize ops (Sequence es) = normalizeSequence ops . sortDeclarations ops . mapNormalize ops $ es -- @@ -106,6 +109,18 @@ normalizeObjectLevel ops (LValue n e) | convertObjectLevelVariab normalizeObjectLevel ops (Sequence es) = normalizeSequence ops (map (normalizeObjectLevel ops) es) normalizeObjectLevel ops e = normalize ops e +isPartialBody (If _ ifTrue ifFalse) = isPartialBranch ifTrue && isPartialBranch ifFalse +isPartialBody (Sequence + (reverse -> + (If _ ifTrue ifFalse) : _)) = isPartialBranch ifTrue && isPartialBranch ifFalse +isPartialBody _ = False + +isPartialBranch (Return _) = True +isPartialBranch (Sequence + (reverse -> + (Return _ : _))) = True +isPartialBranch _ = False + normalizeEquation :: NormalizationOptions -> Equation -> Equation normalizeEquation ops = mapEquation (normalize ops) (normalizeBody ops) From 6ad4fb1a3b842bcdd6215d6ee1d223c2c86904d1 Mon Sep 17 00:00:00 2001 From: Franco Leonardo Bulgarelli Date: Mon, 13 Mar 2023 17:09:02 -0300 Subject: [PATCH 2/4] Add some tests --- spec/GenericSpec.hs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/GenericSpec.hs b/spec/GenericSpec.hs index 6abd5b63b..72aa00752 100644 --- a/spec/GenericSpec.hs +++ b/spec/GenericSpec.hs @@ -86,6 +86,15 @@ spec = do it "is True when functions is declared" $ do declaresFunction (named "f") (js "function f(x) {return 1}") `shouldBe` True + it "is True when a function by parts is declared" $ do + declaresFunction (named "f") (js "function f(x) { if (x > 4) { return 1 } else { return 5 } }") `shouldBe` True + + it "is True when a function by parts is declared using an imperative style" $ do + declaresFunction (named "f") (js "function f(x) { if (x > 4) { return 1 } return 5 }") `shouldBe` True + + it "is False when a partial function is declared" $ do + declaresFunction (named "f") (js "function f(x) { if (x > 4) { return 1 } }") `shouldBe` False + it "is True when functions is declared" $ do declaresFunction (named "f") (js "function f(x) {return 1}") `shouldBe` True From 0b9a362e5aca16b717a1bc02af7313ee601ce186 Mon Sep 17 00:00:00 2001 From: Franco Leonardo Bulgarelli Date: Mon, 13 Mar 2023 17:18:47 -0300 Subject: [PATCH 3/4] Rename functions --- src/Language/Mulang/Analyzer/Analysis/Json.hs | 2 +- src/Language/Mulang/Normalizers/JavaScript.hs | 2 +- src/Language/Mulang/Normalizers/Python.hs | 2 +- src/Language/Mulang/Transform/Normalizer.hs | 26 +++++++++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Language/Mulang/Analyzer/Analysis/Json.hs b/src/Language/Mulang/Analyzer/Analysis/Json.hs index 931a82555..ec4424cbc 100644 --- a/src/Language/Mulang/Analyzer/Analysis/Json.hs +++ b/src/Language/Mulang/Analyzer/Analysis/Json.hs @@ -28,7 +28,7 @@ instance FromJSON NormalizationOptions where <*> v .:? "convertObjectLevelLambdaVariableIntoMethod" .!= convertObjectLevelLambdaVariableIntoMethod d <*> v .:? "convertObjectLevelVariableIntoAttribute" .!= convertObjectLevelVariableIntoAttribute d <*> v .:? "convertObjectIntoDict" .!= convertObjectIntoDict d - <*> v .:? "convertPartialProcedureIntoFunction" .!= convertPartialProcedureIntoFunction d + <*> v .:? "convertProcedureByPartsIntoFunction" .!= convertProcedureByPartsIntoFunction d <*> v .:? "sortSequenceDeclarations" .!= sortSequenceDeclarations d <*> v .:? "insertImplicitReturn" .!= insertImplicitReturn d <*> v .:? "compactSequences" .!= compactSequences d diff --git a/src/Language/Mulang/Normalizers/JavaScript.hs b/src/Language/Mulang/Normalizers/JavaScript.hs index 1f69d01c7..c8c615d12 100644 --- a/src/Language/Mulang/Normalizers/JavaScript.hs +++ b/src/Language/Mulang/Normalizers/JavaScript.hs @@ -10,5 +10,5 @@ javaScriptNormalizationOptions = unnormalized { convertObjectLevelLambdaVariableIntoMethod = True, convertObjectLevelVariableIntoAttribute = True, sortSequenceDeclarations = SortUniqueNonVariables, - convertPartialProcedureIntoFunction = True + convertProcedureByPartsIntoFunction = True } diff --git a/src/Language/Mulang/Normalizers/Python.hs b/src/Language/Mulang/Normalizers/Python.hs index b06fa17e1..54569e258 100644 --- a/src/Language/Mulang/Normalizers/Python.hs +++ b/src/Language/Mulang/Normalizers/Python.hs @@ -5,5 +5,5 @@ import Language.Mulang.Transform.Normalizer (unnormalized, NormalizationOptions( pythonNormalizationOptions :: NormalizationOptions pythonNormalizationOptions = unnormalized { sortSequenceDeclarations = SortAllNonVariables, - convertPartialProcedureIntoFunction = True + convertProcedureByPartsIntoFunction = True } diff --git a/src/Language/Mulang/Transform/Normalizer.hs b/src/Language/Mulang/Transform/Normalizer.hs index 86eadef1f..80da69e25 100644 --- a/src/Language/Mulang/Transform/Normalizer.hs +++ b/src/Language/Mulang/Transform/Normalizer.hs @@ -25,7 +25,7 @@ data NormalizationOptions = NormalizationOptions { convertObjectLevelLambdaVariableIntoMethod :: Bool, convertObjectLevelVariableIntoAttribute :: Bool, convertObjectIntoDict :: Bool, - convertPartialProcedureIntoFunction :: Bool, + convertProcedureByPartsIntoFunction :: Bool, sortSequenceDeclarations :: SequenceSortMode, insertImplicitReturn :: Bool, compactSequences :: Bool, @@ -47,7 +47,7 @@ unnormalized = NormalizationOptions { convertObjectLevelLambdaVariableIntoMethod = False, convertObjectLevelVariableIntoAttribute = False, convertObjectIntoDict = False, - convertPartialProcedureIntoFunction = False, + convertProcedureByPartsIntoFunction = False, sortSequenceDeclarations = SortNothing, insertImplicitReturn = False, compactSequences = False, @@ -61,7 +61,7 @@ normalize ops (Application (Primitive op) [e1, e2]) | isCommutative op = Applica normalize ops (LValue n (Lambda vars e)) | convertLambdaVariableIntoFunction ops = SimpleFunction n vars (normalize ops e) normalize ops (LValue n (MuObject e)) | convertObjectVariableIntoObject ops = Object n (normalizeObjectLevel ops e) normalize ops (MuObject e) | convertObjectIntoDict ops = MuDict . normalize ops . normalizeArrows $ e -normalize ops (SimpleProcedure name params body) | convertPartialProcedureIntoFunction ops && isPartialBody body = SimpleFunction name params (normalize ops body) +normalize ops (SimpleProcedure name params body) | convertProcedureByPartsIntoFunction ops && isBodyByParts body = SimpleFunction name params (normalize ops body) normalize ops (Object n e) = Object n (normalizeObjectLevel ops e) normalize ops (Sequence es) = normalizeSequence ops . sortDeclarations ops . mapNormalize ops $ es -- @@ -109,17 +109,17 @@ normalizeObjectLevel ops (LValue n e) | convertObjectLevelVariab normalizeObjectLevel ops (Sequence es) = normalizeSequence ops (map (normalizeObjectLevel ops) es) normalizeObjectLevel ops e = normalize ops e -isPartialBody (If _ ifTrue ifFalse) = isPartialBranch ifTrue && isPartialBranch ifFalse -isPartialBody (Sequence +isBodyByParts (If _ ifTrue ifFalse) = isBodyPart ifTrue && isBodyPart ifFalse +isBodyByParts (Sequence (reverse -> - (If _ ifTrue ifFalse) : _)) = isPartialBranch ifTrue && isPartialBranch ifFalse -isPartialBody _ = False - -isPartialBranch (Return _) = True -isPartialBranch (Sequence - (reverse -> - (Return _ : _))) = True -isPartialBranch _ = False + (If _ ifTrue ifFalse) : _)) = isBodyPart ifTrue && isBodyPart ifFalse +isBodyByParts _ = False + +isBodyPart (Return _) = True +isBodyPart (Sequence + (reverse -> + (Return _ : _))) = True +isBodyPart _ = False normalizeEquation :: NormalizationOptions -> Equation -> Equation normalizeEquation ops = mapEquation (normalize ops) (normalizeBody ops) From 566b3b91dc0e65b1342a2b7b711cdd96bd0b4bfa Mon Sep 17 00:00:00 2001 From: Franco Leonardo Bulgarelli Date: Tue, 21 Mar 2023 16:30:50 -0300 Subject: [PATCH 4/4] Add test for Python --- spec/GenericSpec.hs | 12 +++++++++++- spec/NormalizerSpec.hs | 8 +++----- src/Language/Mulang/Normalizers/Python.hs | 2 +- src/Language/Mulang/Parsers/Python.hs | 6 +++++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/spec/GenericSpec.hs b/spec/GenericSpec.hs index 72aa00752..206eb95ed 100644 --- a/spec/GenericSpec.hs +++ b/spec/GenericSpec.hs @@ -16,7 +16,7 @@ import Language.Mulang.Normalizers.Haskell (haskellNormalizationOption import Language.Mulang.Parsers.Haskell import Language.Mulang.Parsers.Java (java) import Language.Mulang.Parsers.JavaScript -import Language.Mulang.Parsers.Python (py2, py3) +import Language.Mulang.Parsers.Python (npy, py2, py3) import Language.Mulang.Transform.Normalizer nhs = normalize haskellNormalizationOptions . hs @@ -82,6 +82,16 @@ spec = do it "is False when constant is declared with a variable literal" $ do declaresFunction (named "f") (hs "f = snd") `shouldBe` False + describe "with function declarations, npy" $ do + it "is True when a function by parts is declared" $ do + declaresFunction (named "f") (npy "def f(x):\n\tif x > 4:\n\t\treturn 1\n\telse:\n\t\treturn 5") `shouldBe` True + + it "is True when a function by parts is declared using an imperative style" $ do + declaresFunction (named "f") (npy "def f(x):\n\tif x > 4:\n\t\treturn 1\n\treturn 5") `shouldBe` True + + it "is False when a partial function is declared" $ do + declaresFunction (named "f") (npy "def f(x):\n\tif x > 4:\n\t\treturn 1\n") `shouldBe` False + describe "with function declarations, js" $ do it "is True when functions is declared" $ do declaresFunction (named "f") (js "function f(x) {return 1}") `shouldBe` True diff --git a/spec/NormalizerSpec.hs b/spec/NormalizerSpec.hs index e00c0c055..a9af0e7fa 100644 --- a/spec/NormalizerSpec.hs +++ b/spec/NormalizerSpec.hs @@ -6,14 +6,12 @@ module NormalizerSpec (spec) where import Language.Mulang.Parsers.Haskell (hs) import Language.Mulang.Parsers.Java (java) import Language.Mulang.Parsers.JavaScript (js) - import Language.Mulang.Parsers.Python (py) + import Language.Mulang.Parsers.Python (npy, py) import Language.Mulang.Normalizers.Java (javaNormalizationOptions) - import Language.Mulang.Normalizers.Python (pythonNormalizationOptions) import Language.Mulang.Normalizers.Haskell (haskellNormalizationOptions) import Language.Mulang.Transform.Normalizer njava = normalize javaNormalizationOptions . java - npy = normalize pythonNormalizationOptions . py nhs = normalize haskellNormalizationOptions . hs spec :: Spec @@ -79,10 +77,10 @@ module NormalizerSpec (spec) where let n = normalize options it "does not insert return in single literal statement" $ do - n (py "def x(): x = 1") `shouldBe` SimpleProcedure "x" [] (Assignment "x" (MuNumber 1.0)) + n (npy "def x(): x = 1") `shouldBe` SimpleProcedure "x" [] (Assignment "x" (MuNumber 1.0)) it "inserts return in single literal expression" $ do - n (py "def x(): 3") `shouldBe` SimpleProcedure "x" [] (Return (MuNumber 3.0)) + n (npy "def x(): 3") `shouldBe` SimpleProcedure "x" [] (Return (MuNumber 3.0)) it "does not insert return in empty block" $ do n (SimpleFunction "x" [] None) `shouldBe` (SimpleFunction "x" [] None) diff --git a/src/Language/Mulang/Normalizers/Python.hs b/src/Language/Mulang/Normalizers/Python.hs index 54569e258..86d23ed49 100644 --- a/src/Language/Mulang/Normalizers/Python.hs +++ b/src/Language/Mulang/Normalizers/Python.hs @@ -4,6 +4,6 @@ import Language.Mulang.Transform.Normalizer (unnormalized, NormalizationOptions( pythonNormalizationOptions :: NormalizationOptions pythonNormalizationOptions = unnormalized { - sortSequenceDeclarations = SortAllNonVariables, + sortSequenceDeclarations = SortUniqueNonVariables, convertProcedureByPartsIntoFunction = True } diff --git a/src/Language/Mulang/Parsers/Python.hs b/src/Language/Mulang/Parsers/Python.hs index b4e0ecac4..e87eb291d 100644 --- a/src/Language/Mulang/Parsers/Python.hs +++ b/src/Language/Mulang/Parsers/Python.hs @@ -1,4 +1,5 @@ module Language.Mulang.Parsers.Python ( + npy, py, py2, py3, @@ -10,9 +11,11 @@ import qualified Language.Mulang.Ast as M import qualified Language.Mulang.Ast.Operator as O import Language.Mulang.Builder (compactMap, compactTuple) import Language.Mulang.Parsers +import Language.Mulang.Transform.Normalizer (normalize) import qualified Language.Python.Version3.Parser as Python3 import qualified Language.Python.Version2.Parser as Python2 +import Language.Mulang.Normalizers.Python (pythonNormalizationOptions) import Language.Python.Common.Token (Token) import Language.Python.Common.AST @@ -23,7 +26,8 @@ import Data.Maybe (fromMaybe, listToMaybe) import Control.Fallible import Control.Monad (msum) -py, py2, py3 :: Parser +npy, py, py2, py3 :: Parser +npy = normalize pythonNormalizationOptions . py py = py3 py2 = parsePythonOrFail Python2.parseModule py3 = parsePythonOrFail Python3.parseModule