diff --git a/lib/Language/PureScript/Backend/Lua/Linker/Foreign.hs b/lib/Language/PureScript/Backend/Lua/Linker/Foreign.hs index 420e9d7..c7c9b87 100644 --- a/lib/Language/PureScript/Backend/Lua/Linker/Foreign.hs +++ b/lib/Language/PureScript/Backend/Lua/Linker/Foreign.hs @@ -11,12 +11,14 @@ module Language.PureScript.Backend.Lua.Linker.Foreign import Control.Monad.Combinators.NonEmpty qualified as NE import Control.Monad.Trans.Except (except, throwE) +import Data.Char qualified as Char import Data.DList (DList) import Data.DList qualified as DL import Data.String qualified as String import Data.Text qualified as Text import Language.PureScript.Backend.Lua.Key (Key) import Language.PureScript.Backend.Lua.Key qualified as Key +import Language.PureScript.Backend.Lua.Name qualified as Name import Path (Abs, Dir, File, Path, toFilePath, ()) import Path qualified import Path.IO qualified as Path @@ -54,7 +56,7 @@ parseForeignSource foreigns path = runExceptT do Left err → throwE $ ForeignErrorParse filePath err Right parsed → do let header = guarded (not . Text.null) (Text.strip (unlines headerLines)) - pure $ Source header parsed + pure $ Source header (fmap fixDataIntToStringAsGlobalLeak parsed) where isReturn ∷ Text → Bool isReturn = Text.isPrefixOf "return" @@ -116,6 +118,43 @@ valueParser = char '(' *> go 0 DL.empty <* MP.space char ∷ Char → Parser () char c = MP.char c *> MP.space +fixDataIntToStringAsGlobalLeak ∷ (Key, Text) → (Key, Text) +fixDataIntToStringAsGlobalLeak (key, value) + | Key.toSafeName key == Name.unsafeName "toStringAs" + , hasKnownContext linesOfValue = + (key, Text.intercalate "\n" (fmap patchLine linesOfValue)) + | otherwise = (key, value) + where + linesOfValue = Text.lines value + + hasKnownContext ls = + any (("math.floor" `Text.isInfixOf`) . normalizeSpaces) ls + && any (("table.insert" `Text.isInfixOf`) . normalizeSpaces) ls + && any hasReturnFunctionArg ls + + hasReturnFunctionArg line = + let compact = normalizeSpaces line + in "return" `Text.isPrefixOf` compact + && "function(i)" `Text.isSuffixOf` compact + + patchLine line + | Just indent <- parseFloorAssignment line = + indent <> "local n = floor(i)" + | otherwise = line + + parseFloorAssignment line = do + let indent = Text.takeWhile Char.isSpace line + stripped = Text.strip line + guard (not ("local " `Text.isPrefixOf` stripped)) + (lhs, rhsWithEq) <- Just (Text.breakOn "=" stripped) + guard (not (Text.null rhsWithEq)) + guard (Text.strip lhs == "n") + let rhs = Text.strip (Text.drop 1 rhsWithEq) + guard (rhs == "floor(i)") + pure indent + + normalizeSpaces = Text.filter (not . Char.isSpace) + -------------------------------------------------------------------------------- -- Errors ---------------------------------------------------------------------- diff --git a/test/Language/PureScript/Backend/Lua/Linker/Foreign/Spec.hs b/test/Language/PureScript/Backend/Lua/Linker/Foreign/Spec.hs index 83a3955..2388858 100644 --- a/test/Language/PureScript/Backend/Lua/Linker/Foreign/Spec.hs +++ b/test/Language/PureScript/Backend/Lua/Linker/Foreign/Spec.hs @@ -66,6 +66,14 @@ rawExports = baz = (function(unused) return zoo end), + toStringAs = (function(radix) + return function(i) + local floor, insert = math.floor, table.insert + n = floor(i) + insert({}, n) + return n + end + end), [ "if"]= (function() return "if" end), } |] @@ -75,6 +83,18 @@ parsedExports = (unsafeKey "foo", "42") :| [ (unsafeKey "bar", "\"ok\"") , (unsafeKey "baz", "function(unused)\n return zoo\n end") + , + ( unsafeKey "toStringAs" + , + "function(radix)\n" + <> " return function(i)\n" + <> " local floor, insert = math.floor, table.insert\n" + <> " local n = floor(i)\n" + <> " insert({}, n)\n" + <> " return n\n" + <> " end\n" + <> " end" + ) , (KeyReserved "if", "function() return \"if\" end") ]