diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8e362a1f..7f590bf6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -11,7 +11,13 @@ jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + ghc: ['8.10.4'] + cabal: ['3.4.0.0'] + steps: + - uses: actions/checkout@v1 - name: install erlang env: OTP_VERSION: 23 @@ -29,19 +35,51 @@ jobs: sudo locale-gen en_US.UTF-8 echo "LC_ALL=en_US.UTF-8" >> $GITHUB_ENV - name: install library + run: sudo apt update && sudo apt install -y libtinfo-dev + - uses: actions/cache@v2 + with: + path: | + ~/.stack + ~/.cabal/packages + ~/.cabal/store + dist-newstyle + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.cabal }}-v1-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.cabal }}-v1- + - name: Setup PATH + run: | + echo "$HOME/.ghcup/bin" >> $GITHUB_PATH + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Setup Haskell + uses: haskell/actions/setup@v1 + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + - name: install alex + run: | + cabal update + cabal install alex + - name: install happy run: | - sudo apt update && sudo apt install -y libtinfo-dev ghc - echo "~/.local/bin" >> $GITHUB_PATH curl -sSL https://get.haskellstack.org/ | sh -s - -f - - uses: actions/checkout@v2 - - name: make + stack install happy-1.19.9 --resolver lts-13.26 --allow-different-user + - name: make hamler + env: + HAMLER_HOME: /usr/lib/hamler run: | - make - make test - sudo make install + sudo mkdir -p $HAMLER_HOME/bin + cabal run hamler build -- -l -e + cabal install --overwrite-policy=always + sudo cp ~/.cabal/bin/hamler /usr/bin/ + sudo cp ~/.cabal/bin/hamler $HAMLER_HOME/bin + sudo cp repl/replsrv $HAMLER_HOME/bin/replsrv + sudo cp -r ebin $HAMLER_HOME + sudo cp -r lib $HAMLER_HOME + rm -rf ~/.cabal/bin/hamler - name: tests run: | - mkdir test + mkdir -p test cd test hamler init hamler build @@ -49,7 +87,7 @@ jobs: echo ":q" | hamler repl - name: create tgz file run: | - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) cd /usr/lib && sudo tar cvf hamler-${version}.tgz hamler && cd - mkdir -p _packages sudo mv /usr/lib/hamler-${version}.tgz _packages @@ -61,7 +99,7 @@ jobs: - name: update github release if: github.event_name == 'release' run: | - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) for var in $(ls _packages) ; do .github/workflows/script/upload_github_release_asset.sh owner=hamler-lang repo=hamler tag=$version filename=_packages/$var github_api_token=$(echo ${{ secrets.AccessToken }}) done @@ -69,10 +107,16 @@ jobs: build_deb: runs-on: ubuntu-latest + strategy: + matrix: + ghc: ['8.10.4'] + cabal: ['3.4.0.0'] + container: image: debian:9 steps: + - uses: actions/checkout@v1 - name: install erlang env: OTP_VERSION: 23 @@ -94,12 +138,40 @@ jobs: locale-gen en_US.UTF-8 echo "LC_ALL=en_US.UTF-8" >> $GITHUB_ENV echo "STACK_ROOT=$(pwd)" >> $GITHUB_ENV - echo "~/.local/bin" >> $GITHUB_PATH - name: install library + run: apt update && apt install -y libtinfo-dev build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 + - uses: actions/cache@v2 + with: + path: | + ~/.stack + ~/.cabal/packages + ~/.cabal/store + dist-newstyle + key: debian9-${{ matrix.ghc }}-${{ matrix.cabal }}-v1-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + debian9-${{ matrix.ghc }}-${{ matrix.cabal }}-v1- + - name: Setup PATH + run: | + echo "$HOME/.ghcup/bin" >> $GITHUB_PATH + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Setup Haskell + env: + BOOTSTRAP_HASKELL_NONINTERACTIVE: 1 + BOOTSTRAP_HASKELL_GHC_VERSION: ${{ matrix.ghc }} + BOOTSTRAP_HASKELL_CABAL_VERSION: ${{ matrix.cabal }} + run: | + wget -nv --no-check-certificate https://get-ghcup.haskell.org -O ghcup.sh + chmod +x ghcup.sh + ./ghcup.sh + - name: install alex + run: | + cabal update + cabal install alex + - name: install happy run: | - apt update && apt install -y libtinfo-dev ghc curl -sSL https://get.haskellstack.org/ | sh -s - -f - - uses: actions/checkout@v1 + stack install happy-1.19.9 --resolver lts-13.26 --allow-different-user - name: make run: | make pkg @@ -108,6 +180,7 @@ jobs: rm -rf /usr/bin/hamler - name: install pkg run: | + set -x dpkg -i deploy/packages/deb/_packages/*.deb if [ $(dpkg -l |grep hamler |awk '{print $1}') != "ii" ]; then echo "package install error" @@ -124,11 +197,14 @@ jobs: - name: uninstall pkg shell: bash run: | + set -x + dpkg -r hamler-dbgsym dpkg -r hamler if [ $(dpkg -l |grep hamler |awk '{print $1}') != "rc" ]; then echo "package remove error" exit 1 fi + dpkg -P hamler-dbgsym dpkg -P hamler if [ ! -z "$(dpkg -l |grep hamler)" ]; then echo "package uninstall error" @@ -141,7 +217,7 @@ jobs: - name: update github release if: github.event_name == 'release' run: | - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) for var in $(ls deploy/packages/deb/_packages) ; do .github/workflows/script/upload_github_release_asset.sh owner=hamler-lang repo=hamler tag=$version filename=deploy/packages/deb/_packages/$var github_api_token=$(echo ${{ secrets.AccessToken }}) done @@ -149,10 +225,16 @@ jobs: build_rpm: runs-on: ubuntu-latest + strategy: + matrix: + ghc: ['8.10.4'] + cabal: ['3.4.0.0'] + container: image: centos:7 steps: + - uses: actions/checkout@v1 - name: install erlang env: OTP_VERSION: 23.0.3 @@ -171,11 +253,48 @@ jobs: echo "~/.local/bin" >> $GITHUB_PATH - name: install library run: | - yum install -y ghc + yum install -y gmp gmp-devel make ncurses xz perl + yum install -y centos-release-scl-rh + yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++ + - uses: actions/cache@v2 + with: + path: | + ~/.stack + ~/.cabal/packages + ~/.cabal/store + dist-newstyle + key: centos7-${{ matrix.ghc }}-${{ matrix.cabal }}-v1-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + centos7-${{ matrix.ghc }}-${{ matrix.cabal }}-v1- + - name: Setup PATH + run: | + echo "$HOME/.ghcup/bin" >> $GITHUB_PATH + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Setup Haskell + env: + BOOTSTRAP_HASKELL_NONINTERACTIVE: 1 + BOOTSTRAP_HASKELL_GHC_VERSION: ${{ matrix.ghc }} + BOOTSTRAP_HASKELL_CABAL_VERSION: ${{ matrix.cabal }} + run: | + wget -nv --no-check-certificate https://get-ghcup.haskell.org -O ghcup.sh + chmod +x ghcup.sh + ./ghcup.sh + - name: install alex + run: | + . /opt/rh/devtoolset-8/enable + gcc -v + cabal update + cabal install alex + cabal install --lib cryptonite -f -use_target_attributes + - name: install happy + run: | curl -sSL https://get.haskellstack.org/ | sh -s - -f - - uses: actions/checkout@v1 + stack install happy-1.19.9 --resolver lts-13.26 --allow-different-user - name: make run: | + . /opt/rh/devtoolset-8/enable + gcc -v make pkg cd deploy/packages/rpm/_packages && for var in $(ls *.rpm); do bash -c "echo $(sha256sum $var | awk '{print $1}') > $var.sha256"; done && cd - rm -rf /usr/lib/hamler @@ -209,7 +328,7 @@ jobs: - name: update github release if: github.event_name == 'release' run: | - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) for var in $(ls deploy/packages/rpm/_packages) ; do .github/workflows/script/upload_github_release_asset.sh owner=hamler-lang repo=hamler tag=$version filename=deploy/packages/rpm/_packages/$var github_api_token=$(echo ${{ secrets.AccessToken }}) done @@ -217,21 +336,65 @@ jobs: build_mac: runs-on: macos-latest + strategy: + matrix: + ghc: ['8.10.4'] + cabal: ['3.4.0.0'] + steps: + - uses: actions/checkout@v1 - name: prepare run: | - /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" - brew install gnu-sed erlang@23 haskell-stack + brew install gnu-sed erlang@23 ln -s /usr/local/bin/gsed /usr/local/bin/sed echo "/usr/local/opt/erlang@23/bin" >> $GITHUB_PATH echo "/usr/local/lib/hamler/bin" >> $GITHUB_PATH echo "/usr/local/bin" >> $GITHUB_PATH - - uses: actions/checkout@v2 - - name: make + - uses: actions/cache@v2 + with: + path: | + ~/.stack + ~/.cabal/packages + ~/.cabal/store + dist-newstyle + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.cabal }}-v1-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.cabal }}-v1- + - name: Setup PATH + run: | + echo "$HOME/.ghcup/bin" >> $GITHUB_PATH + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Setup Haskell + env: + BOOTSTRAP_HASKELL_NONINTERACTIVE: 1 + BOOTSTRAP_HASKELL_GHC_VERSION: ${{ matrix.ghc }} + BOOTSTRAP_HASKELL_CABAL_VERSION: ${{ matrix.cabal }} + run: | + wget -nv --no-check-certificate https://get-ghcup.haskell.org -O ghcup.sh + chmod +x ghcup.sh + ./ghcup.sh + - name: install alex + run: | + cabal update + cabal install alex + - name: install happy + run: | + curl -sSL https://get.haskellstack.org/ | sh -s - -f + stack install happy-1.19.9 --resolver lts-13.26 --allow-different-user + - name: make hamler + env: + HAMLER_HOME: /usr/local/lib/hamler run: | - make - make test - sudo make install + sudo mkdir -p $HAMLER_HOME/bin + cabal run hamler build -- -l -e + cabal install --overwrite-policy=always + sudo cp ~/.cabal/bin/hamler /usr/local/bin + sudo cp ~/.cabal/bin/hamler $HAMLER_HOME/bin + sudo cp repl/replsrv $HAMLER_HOME/bin/replsrv + sudo cp -r ebin $HAMLER_HOME + sudo cp -r lib $HAMLER_HOME + rm -rf ~/.cabal/bin/hamler - name: tests run: | mkdir test @@ -242,7 +405,7 @@ jobs: echo ":q" | hamler repl - name: create tgz file run: | - version=$(echo ${{ github.ref }} | gsed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) cd /usr/local/lib && sudo tar cvf hamler-${version}.tgz hamler && cd - mkdir -p _packages sudo mv /usr/local/lib/hamler-${version}.tgz _packages @@ -253,9 +416,6 @@ jobs: - name: set aws cli if: github.event_name == 'release' run: | - curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" - sudo installer -pkg AWSCLIV2.pkg -target / - aws --version aws configure set aws_access_key_id ${{ secrets.AwsAccessKeyId }} aws configure set aws_secret_access_key ${{ secrets.AwsSecretAccessKey }} aws configure set default.region us-west-2 @@ -270,7 +430,7 @@ jobs: if: github.event_name == 'release' run: | git clone https://github.com/hamler-lang/homebrew-hamler homebrew-hamler - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) pkg=hamler-${version}.tgz aws s3 cp _packages/$pkg s3://packages.emqx/hamler/homebrew/ download="https://s3-us-west-2.amazonaws.com/packages.emqx/hamler/homebrew/$pkg" @@ -293,12 +453,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v1 - name: make docker image run: make docker - name: push docker image if: github.event_name == 'release' run: | echo ${{ secrets.DockerHubPassword }} | docker login -u ${{ secrets.DockerHubUser }} --password-stdin - version=$(echo ${{ github.ref }} | sed -r "s .*/.*/(.*) \1 g") + version=$(git describe --tags --always) docker push hamlerlang/hamler:$version diff --git a/Makefile b/Makefile index 3df5a7c5..b79c05b8 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,46 @@ package = hamler exe_target = hamler -stack_yaml = STACK_YAML="stack.yaml" -stack = $(stack_yaml) stack -ifeq ($(shell uname -s),Darwin) -export HAMLER_HOME ?= /usr/local/lib/hamler -else -export HAMLER_HOME ?= /usr/lib/hamler -endif +export HAMLER_HOME ?= $(shell $(CURDIR)/get-hamler-home.sh) all: build foreign build: - $(stack) run build -- -l + cabal run hamler build -- -l -e foreign: @erlc -o ebin lib/Foreign/*.erl clean: - $(stack) clean + cabal clean run: - $(stack) build --fast && $(stack) exec -- $(exe_target) + cabal build && cabal run $(exe_target) install: -ifeq ($(shell uname -s),Linux) - $(stack) install --local-bin-path /usr/bin --allow-different-user -endif - $(stack) install --local-bin-path $(HAMLER_HOME)/bin --allow-different-user + @mkdir -p $(HAMLER_HOME) + cabal install --installdir=$(HAMLER_HOME)/bin --overwrite-policy=always @cp repl/replsrv $(HAMLER_HOME)/bin/replsrv @cp -r ebin $(HAMLER_HOME) @cp -r lib $(HAMLER_HOME) test: - $(stack) run testDev + cabal run hamler testDev repl: - $(stack) run repldev + cabal run hamler repldev docker: docker build -t hamlerlang/hamler:$$(git describe --tags --always) -f deploy/docker/Dockerfile . -pkg:build test install +pkg: + mkdir -p /usr/lib/hamler/bin + HAMLER_HOME="/usr/lib/hamler" cabal run hamler build -- -l -e + cabal install --overwrite-policy=always + cp ~/.cabal/bin/hamler /usr/lib/hamler/bin + cp repl/replsrv /usr/lib/hamler/bin/replsrv + cp -r ebin /usr/lib/hamler + cp -r lib /usr/lib/hamler make -C deploy/packages .PHONY : build clean run install test repl docker pkg diff --git a/README.md b/README.md index cf3a2369..d84891b9 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,8 @@ $ brew install hamler **Required** + [Erlang/OTP](https://www.erlang.org) >= 23 -+ [Haskell Stack](https://haskellstack.org) ++ [Cabal](https://www.haskell.org/cabal) ++ happy-1.19.9 **Building** diff --git a/Setup.hs b/Setup.hs deleted file mode 100644 index cd7b151a..00000000 --- a/Setup.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Main where - -import Distribution.Simple - -main :: IO () -main = defaultMain diff --git a/app/Compile.hs b/app/Compile.hs index d469e2cd..4f4c0b46 100644 --- a/app/Compile.hs +++ b/app/Compile.hs @@ -30,6 +30,7 @@ import Version (hamlerEnv) import System.FilePath.Posix(()) import System.Environment (lookupEnv) import System.IO.Unsafe (unsafePerformIO) +import System.FilePath.Posix data PSCMakeOptions = PSCMakeOptions { pscmInput :: [(FilePath, Bool)] @@ -37,7 +38,7 @@ data PSCMakeOptions = PSCMakeOptions , pscmOpts :: P.Options , pscmUsePrefix :: Bool , pscmJSONErrors :: Bool - , isInline :: Bool + , isErlSource :: Bool } -- | Arguments: verbose, use JSON, warnings, errors @@ -72,7 +73,7 @@ compile PSCMakeOptions{..} = do ms <- CST.parseModulesFromFiles id moduleFiles let filePathMap = M.fromList $ map (\(fp, pm) -> (P.getModuleName $ CST.resPartial pm, inpRebPol fp pscmInput) ) ms foreigns <- inferForeignModules filePathMap - let makeActions = buildMakeActions hamlerFile isInline pscmOutputDir filePathMap foreigns pscmUsePrefix + let makeActions = buildMakeActions hamlerFile isErlSource pscmOutputDir filePathMap foreigns pscmUsePrefix -- P.make makeActions (map snd ms) make makeActions (map snd ms) printWarningsAndErrors (P.optionsVerboseErrors pscmOpts) pscmJSONErrors makeWarnings makeErrors @@ -103,11 +104,11 @@ howBuild= Opts.switch $ <> Opts.long "libraries" <> Opts.help "build the libraries to ebin (only develop)" -inline :: Opts.Parser Bool -inline= Opts.switch $ - Opts.short 'i' - <> Opts.long "inline" - <> Opts.help "Determine whether to inline functions (no effect at this stage)" +buildErlangSource :: Opts.Parser Bool +buildErlangSource = Opts.switch $ + Opts.short 'e' + <> Opts.long "erl" + <> Opts.help "build erlang source" keepCore :: Opts.Parser Bool keepCore = Opts.switch $ @@ -124,12 +125,12 @@ outputDirectory = Opts.strOption $ <> Opts.help "The output directory" command :: Opts.Parser (IO ()) -command = Opts.helper <*> (buildFun <$> inline <*> howBuild <*> keepCore <*> outputDirectory) +command = Opts.helper <*> (buildFun <$> buildErlangSource <*> howBuild <*> keepCore <*> outputDirectory) buildFun :: Bool -> Bool -> Bool -> FilePath -> IO () -buildFun isIn b k fp = if b - then buildlib isIn - else buildSrc isIn k fp +buildFun isES b k fp = if b + then buildlib isES + else buildSrc isES k fp buildSrc :: Bool -> Bool -> FilePath -> IO () buildSrc bl keepcore fpath = do @@ -149,7 +150,7 @@ buildSrc bl keepcore fpath = do , pscmOpts = (P.Options False False (S.fromList [P.CoreFn])) , pscmUsePrefix = False , pscmJSONErrors = False - , isInline = bl + , isErlSource = bl } ) cfs <- findFile1 ".core" tpath @@ -187,11 +188,13 @@ buildlib bl = do , pscmOpts = (P.Options False False (S.fromList [P.CoreFn])) , pscmUsePrefix = False , pscmJSONErrors = False - , isInline = bl + , isErlSource = bl } ) cfs <- findFile1 ".core" tpath + recRemoveP (dir <> "/lib") + SS.shelly $ SS.command_ "erlc" ["-o" ,T.pack tpath] (fmap (\fp -> T.pack $ tpath <> "/" <> fp) cfs) forM_ cfs $ \fp -> do @@ -218,10 +221,13 @@ buildTestDev = pure $ do , pscmOpts = (P.Options False False (S.fromList [P.CoreFn])) , pscmUsePrefix = False , pscmJSONErrors = False - , isInline = False + , isErlSource = False } ) cfs <- findFile1 ".core" tpath + + recRemoveP (dir <> "/lib") + SS.shelly $ SS.command_ "erlc" ["-o" ,T.pack tpath] (fmap (\fp -> T.pack $ tpath <> "/" <> fp) cfs) forM_ cfs $ \fp -> do SS.shelly $ SS.run_ "rm" [T.pack $ tpath <> "/" <> fp] @@ -257,7 +263,7 @@ buildTest = pure $ do , pscmOpts = (P.Options False False (S.fromList [P.CoreFn])) , pscmUsePrefix = False , pscmJSONErrors = False - , isInline = False + , isErlSource = False } ) cfs <- findFile1 ".core" tpath @@ -401,4 +407,19 @@ recErlc fp = do ls <- listDirectory fp (coreFiles,dires) <- foldM myt ([],[]) $ fmap (\t -> fp ++ "/" ++ t) ls SS.shelly $ SS.command_ "erlc" ["-o" ,T.pack fp,"+to_core" ] (fmap T.pack coreFiles) + SS.shelly $ SS.command_ "erlc" ["-o" ,T.pack fp,"-P" ] (fmap T.pack coreFiles) mapConcurrently_ recErlc dires + +recRemoveP = recRemove ".P" + +recRemove :: String -> FilePath -> IO () +recRemove ext basePath = do + ls <- listDirectory basePath + forM_ ls $ \fp -> do + let fp' = basePath fp + isE <- doesDirectoryExist fp' + if isE + then recRemove ext fp' + else do + let ex = takeExtension fp + when (ex == ext) $ removeFile (basePath fp) diff --git a/app/Main.hs b/app/Main.hs index 3fc2227e..8d0ca776 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -43,7 +43,7 @@ main = do -- | Displays full command help when invoked with no arguments. execParserPure :: Opts.ParserInfo a -> [String] -> Opts.ParserResult a execParserPure pinfo [] = Opts.Failure $ - Opts.parserFailure Opts.defaultPrefs pinfo Opts.ShowHelpText mempty + Opts.parserFailure Opts.defaultPrefs pinfo (Opts.ShowHelpText Nothing) mempty execParserPure pinfo args = Opts.execParserPure Opts.defaultPrefs pinfo args versionInfo :: Opts.Parser (a -> a) diff --git a/app/REPL.hs b/app/REPL.hs index facd04bd..d8d2d152 100644 --- a/app/REPL.hs +++ b/app/REPL.hs @@ -38,6 +38,8 @@ import Version (hamlerEnv) import System.FilePath.Posix (()) import System.Environment (lookupEnv) import System.IO.Unsafe (unsafePerformIO) +import Control.Monad.IO.Class +import Control.Monad.Catch hamlerlib :: String hamlerlib = let vp = unsafePerformIO $ lookupEnv "HAMLER_HOME" @@ -89,26 +91,27 @@ nodeBackend = Backend setup eval reload shutdown shutdown :: () -> IO () shutdown _ = return () + -- | Parses the input and returns either a command, or an error as a 'String'. -getCommand :: forall m. MonadException m => String -> InputT m (Either String [Command]) +getCommand :: forall m. (MonadMask m, MonadIO m) => String -> InputT m (Either String [Command]) getCommand s = handleInterrupt (return (Right [])) $ do line <- withInterrupt $ getInputLine $ addSpace s case line of Nothing -> return (Right [QuitPSCi]) -- Ctrl-D when input is empty Just "" -> return (Right []) - Just sv -> return (parseCommand sv) + Just s -> return (parseCommand s) addSpace :: String -> String addSpace w = case words w of [r] -> r++" " _ -> w -pasteMode :: forall m. MonadException m => InputT m (Either String [Command]) +pasteMode :: forall m. (MonadMask m, MonadIO m) => InputT m (Either String [Command]) pasteMode = - parseCommand <$> go [] + parseCommand <$> go [] where go :: [String] -> InputT m String - go ls = maybe (return . unlines $ reverse ls) (go . (: ls)) =<< getInputLine "… " + go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine "… " ishmFile :: String -> Bool ishmFile fname = (== "mh.") $ take 3 $ reverse $ fname diff --git a/cabal.project b/cabal.project new file mode 100644 index 00000000..cef0ddf9 --- /dev/null +++ b/cabal.project @@ -0,0 +1,17 @@ +packages:. + +source-repository-package + type: git + location: https://github.com/hamler-lang/Erlang.git + tag: b561aab73bbc60a19862d64b6eef48da18c0609f + +source-repository-package + type: git + location: https://github.com/hamler-lang/CoreErlang.git + tag: 765eb45afbba61df35dbb72da4630fbfee6bc8d7 + +source-repository-package + type: git + location: https://github.com/hamler-lang/purescript.git + tag: dbc46627e6c15aa57ebc9c69c6abe3c6d77edf3a + diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile index ddd6fc43..5391f34a 100644 --- a/deploy/docker/Dockerfile +++ b/deploy/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM erlang:23 +FROM erlang:23 as erlang RUN apt update && apt install -y locales \ && echo "LC_ALL=en_US.UTF-8" >> /etc/environment \ @@ -6,13 +6,24 @@ RUN apt update && apt install -y locales \ && echo "LANG=en_US.UTF-8" > /etc/locale.conf \ && locale-gen en_US.UTF-8 +RUN apt update && \ + apt install -y libtinfo-dev build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 && \ + rm -rf /var/lib/apt/lists/* + ENV LC_ALL=en_US.UTF-8 -RUN apt update && apt install -y libtinfo-dev ghc +ENV PATH=/root/.ghcup/bin:/root/.cabal/bin:/root/.local/bin:$PATH + +RUN wget -nv --no-check-certificate https://get-ghcup.haskell.org -O ghcup.sh && \ + chmod +x ghcup.sh && \ + BOOTSTRAP_HASKELL_NONINTERACTIVE=1 ./ghcup.sh + +RUN cabal update && cabal install alex -RUN wget -qO- https://get.haskellstack.org/ | sh +RUN curl -sSL https://get.haskellstack.org/ | sh -s - -f && \ + stack install happy-1.19.9 --resolver lts-13.26 --allow-different-user -ENV PATH=/root/.local/bin:$PATH +ENV PATH=/usr/lib/hamler/bin:$PATH COPY . hamler RUN make -C hamler \ @@ -21,7 +32,7 @@ RUN make -C hamler \ WORKDIR /tmp/tests -RUN hamler init \ +RUN hamler init \ && hamler build \ && hamler run \ && echo ":q" | hamler repl diff --git a/deploy/packages/deb/Makefile b/deploy/packages/deb/Makefile index b4636974..9a90a7a8 100644 --- a/deploy/packages/deb/Makefile +++ b/deploy/packages/deb/Makefile @@ -3,7 +3,7 @@ TOPDIR := /tmp/hamler SRCDIR := $(TOPDIR)/$(PKG_VSN) BUILT := $(SRCDIR)/BUILT -HAMLER_HOME ?= /usr/lib/hamler +HAMLER_HOME = /usr/lib/hamler .PHONY: all all: | $(BUILT) diff --git a/deploy/packages/rpm/Makefile b/deploy/packages/rpm/Makefile index 964aeefb..c83d4488 100644 --- a/deploy/packages/rpm/Makefile +++ b/deploy/packages/rpm/Makefile @@ -19,7 +19,7 @@ ifeq ($(ARCH),mips64) ARCH:=mips64el endif -HAMLER_HOME ?= /usr/lib/hamler +HAMLER_HOME = /usr/lib/hamler .PHONY: all all: | $(BUILT) diff --git a/get-hamler-home.sh b/get-hamler-home.sh new file mode 100755 index 00000000..787dc7c5 --- /dev/null +++ b/get-hamler-home.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +# --- use sudo if we are not already root --- +SUDO=sudo +if [ $(id -u) -eq 0 ]; then + SUDO= +fi + +if $SUDO cabal --help >/dev/null 2>&1; then + if [ "$(uname -s)" = 'Darwin' ]; then + HAMLER_HOME="/usr/local/lib/hamler" + else + HAMLER_HOME="/usr/lib/hamler" + fi +else + HAMLER_HOME="$HOME/.hamler" +fi + +if [ -z "$(echo $PATH |grep -o $HAMLER_HOME/bin)" ]; then + rc_file="$HOME/.$(basename $(echo $SHELL))rc" + echo "export PATH=$HAMLER_HOME/bin:\$PATH" >> $rc_file +fi +echo $HAMLER_HOME diff --git a/hamler.cabal b/hamler.cabal index c277a578..32c9bd62 100644 --- a/hamler.cabal +++ b/hamler.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: edb34f6d886d52dfc0c568cb6df41d2eb5ba33b1599f32cf132cf1aa02c0aa47 +-- hash: 730f833508fdf0326f4bf2001421a3ae4b607c5474cfb2ed524b8a08d4ba20cf name: hamler version: 0.3 @@ -22,7 +22,6 @@ license-file: LICENSE build-type: Simple extra-source-files: README.md - stack.yaml source-repository head type: git @@ -41,6 +40,10 @@ library Language.Hamler.Make Language.Hamler.Make.Actions Language.Hamler.Util + Language.Hamler.Erlang.SimpleType + Language.Hamler.Erlang.PrettySimpleType + Language.Hamler.Erlang.TranslateSimpleType + Language.Hamler.Erlang.CodeGen other-modules: Paths_hamler hs-source-dirs: @@ -68,17 +71,26 @@ library TupleSections ViewPatterns ghc-options: -Wall -O2 - build-tools: - happy ==1.19.9 + -- build-tools: + -- happy ==1.19.9 build-depends: - Cabal >=2.2 && <3.0 + Cabal >=2.2 && <3.3 + + , doclayout + , fused-effects + , fused-effects-optics + , optics + , Erlang + , recursion-schemes + + , exceptions , CoreErlang - , Glob ==0.9.* - , aeson >=1.0 && <1.5 + , Glob >=0.9 && <0.10 + , aeson >=1.0 && <1.6 , aeson-better-errors >=0.8 , aeson-pretty , ansi-terminal >=0.7.1 && <0.9 - , base >=4.11 && <4.13 + , base >=4.11 && <4.15 , base-compat >=0.6.0 , boxes >=0.1.4 && <0.2.0 , bytestring @@ -89,8 +101,8 @@ library , haskeline >=0.7.5.0 , language-javascript , lens - , lifted-async >=0.10.0.3 && <0.10.1 - , lifted-base ==0.2.3.* + , lifted-async >=0.10.0.3 && <0.10.3 + , lifted-base >=0.2.3 && <0.2.4 , monad-control >=1.0.0.0 && <1.1 , mtl >=2.1.0 && <2.3.0 , parsec >=3.1.10 @@ -98,10 +110,10 @@ library , pretty >=1.1 , pretty-simple , process >=1.2.0 && <1.7 - , protolude >=0.1.6 && <0.2.4 + , protolude >=0.1.6 && <0.3.4 , purescript , safe >=0.3.9 && <0.4 - , semialign >=1 && <1.1 + , semialign >=1 && <1.3 , semigroups >=0.16.2 && <0.19 , shelly , sourcemap >=0.1.6 @@ -125,18 +137,19 @@ executable hamler hs-source-dirs: app ghc-options: -Wall -O2 - build-tools: - happy ==1.19.9 + -- build-tools: + -- happy ==1.19.9 build-depends: - Cabal >=2.2 && <3.0 + Cabal >=2.2 && <3.3 , CoreErlang - , Glob ==0.9.* - , aeson >=1.0 && <1.5 + , exceptions + , Glob >=0.9 && <0.10 + , aeson >=1.0 && <1.6 , aeson-better-errors >=0.8 , aeson-pretty , ansi-terminal >=0.7.1 && <0.9 , ansi-wl-pprint - , base >=4.11 && <4.13 + , base >=4.11 && <4.15 , base-compat >=0.6.0 , boxes >=0.1.4 && <0.2.0 , bytestring @@ -148,8 +161,8 @@ executable hamler , haskeline >=0.7.5.0 , language-javascript , lens - , lifted-async >=0.10.0.3 && <0.10.1 - , lifted-base ==0.2.3.* + , lifted-async >=0.10.0.3 && <0.10.3 + , lifted-base >=0.2.3 && <0.2.4 , monad-control >=1.0.0.0 && <1.1 , mtl >=2.1.0 && <2.3.0 , optparse-applicative >=0.13.0 @@ -158,10 +171,10 @@ executable hamler , pretty >=1.1 , pretty-simple , process >=1.2.0 && <1.7 - , protolude >=0.1.6 && <0.2.4 + , protolude >=0.1.6 && <0.3.4 , purescript , safe >=0.3.9 && <0.4 - , semialign >=1 && <1.1 + , semialign >=1 && <1.3 , semigroups >=0.16.2 && <0.19 , shelly , sourcemap >=0.1.6 @@ -191,18 +204,19 @@ test-suite tests LambdaCase OverloadedStrings ghc-options: -Wall - build-tools: - happy ==1.19.9 + -- build-tools: + -- happy ==1.19.9 build-depends: - Cabal >=2.2 && <3.0 + Cabal >=2.2 && <3.3 , CoreErlang - , Glob ==0.9.* + , exceptions + , Glob >=0.9 && <0.10 , HUnit , aeson , aeson-better-errors >=0.8 , aeson-pretty , ansi-terminal - , base >=4.11 && <4.13 + , base >=4.11 && <4.15 , base-compat >=0.6.0 , boxes >=0.1.4 && <0.2.0 , bytestring @@ -216,8 +230,8 @@ test-suite tests , hspec-discover , language-javascript , lens - , lifted-async >=0.10.0.3 && <0.10.1 - , lifted-base ==0.2.3.* + , lifted-async >=0.10.0.3 && <0.10.3 + , lifted-base >=0.2.3 && <0.2.4 , monad-control >=1.0.0.0 && <1.1 , mtl >=2.1.0 && <2.3.0 , optparse-applicative @@ -226,10 +240,10 @@ test-suite tests , pretty >=1.1 , pretty-simple , process >=1.2.0 && <1.7 - , protolude >=0.1.6 && <0.2.4 + , protolude >=0.1.6 && <0.3.4 , purescript , safe >=0.3.9 && <0.4 - , semialign >=1 && <1.1 + , semialign >=1 && <1.3 , semigroups >=0.16.2 && <0.19 , shelly , sourcemap diff --git a/package.yaml b/package.yaml deleted file mode 100644 index 82ca262e..00000000 --- a/package.yaml +++ /dev/null @@ -1,137 +0,0 @@ -name: hamler -version: '0.3' -synopsis: The Hamler Programming Language -description: Hamler is a functional programming language inspired by Haskell and Standard ML, - that compiles to CoreErlang. -category: Language, Compiler -author: Feng Lee -maintainer: Feng Lee , Yang M -copyright: (c) 2013-17 Phil Freeman, (c) 2014-19 Gary Burgess, (c) 2020 Feng Lee, (c) other contributors (see CONTRIBUTORS.md) -license: BSD3 -github: hamler-lang/hamler -homepage: https://hamler-lang.org/ -extra-source-files: - - README.md - - stack.yaml -dependencies: - - aeson >=1.0 && <1.5 - - aeson-better-errors >=0.8 - - aeson-pretty - - ansi-terminal >=0.7.1 && <0.9 - - base >=4.11 && <4.13 - - base-compat >=0.6.0 - - boxes >=0.1.4 && <0.2.0 - - bytestring - - Cabal >= 2.2 && <3.0 - - containers - - CoreErlang - - directory >=1.2.3 - - filepath - - Glob >=0.9 && <0.10 - - language-javascript - - shelly - - lens - - template-haskell - - lifted-async >=0.10.0.3 && <0.10.1 - - lifted-base >=0.2.3 && <0.2.4 - - monad-control >=1.0.0.0 && <1.1 - - mtl >=2.1.0 && <2.3.0 - - parsec >=3.1.10 - - pattern-arrows >=0.0.2 && <0.1 - - pretty >= 1.1 - - pretty-simple - - process >=1.2.0 && <1.7 - - protolude >=0.1.6 && <0.2.4 - - purescript - - safe >=0.3.9 && <0.4 - - semigroups >=0.16.2 && <0.19 - - semialign >=1 && <1.1 - - sourcemap >=0.1.6 - - text - - time - - file-embed - - transformers >=0.3.0 && <0.6 - - transformers-base >=0.4.0 && <0.5 - - transformers-compat >=0.3.0 - - utf8-string >=1 && <2 - - haskeline >= 0.7.5.0 -build-tools: - - happy ==1.19.9 - -library: - source-dirs: src - ghc-options: -Wall -O2 - # other-modules: - default-extensions: - - ConstraintKinds - - DataKinds - - DeriveFunctor - - DeriveFoldable - - DeriveTraversable - - DeriveGeneric - - DerivingStrategies - - EmptyDataDecls - - FlexibleContexts - - KindSignatures - - LambdaCase - - MultiParamTypeClasses - - NoImplicitPrelude - - PatternGuards - - PatternSynonyms - - RankNTypes - - RecordWildCards - - OverloadedStrings - - ScopedTypeVariables - - TupleSections - - ViewPatterns - -executables: - hamler: - main: Main.hs - source-dirs: app - ghc-options: -Wall -O2 - dependencies: - - ansi-wl-pprint - - hamler - - optparse-applicative >=0.13.0 - when: - - condition: flag(release) - then: - cpp-options: -DRELEASE - else: - dependencies: - - gitrev >=1.2.0 && <1.4 - -tests: - tests: - main: Main.hs - source-dirs: tests - ghc-options: -Wall - dependencies: - - aeson - - ansi-terminal - - hamler - - hspec - - hspec-discover - - HUnit - - optparse-applicative - - shelly - - sourcemap - - tasty - - tasty-golden - - tasty-hspec - - tasty-quickcheck - default-extensions: - - NoImplicitPrelude - - LambdaCase - - OverloadedStrings - -flags: - release: - description: > - Mark this build as a release build: prevents inclusion of extra - info e.g. commit SHA in --version output) - manual: false - default: false - -stability: experimental diff --git a/src/Language/Hamler/CodeGen.hs b/src/Language/Hamler/CodeGen.hs index af326b72..20ed6ff8 100644 --- a/src/Language/Hamler/CodeGen.hs +++ b/src/Language/Hamler/CodeGen.hs @@ -63,12 +63,11 @@ instance MonadFail Translate where fail s = error s runTranslate :: - Bool -> (M.Map Text (M.Map Text Integer)) -> (Maybe (E.Module Text), ModuleName) -> Translate a -> (((Either P.MultipleErrors a), VarState), Text) -runTranslate _ moduleInfo (ffiModule, mn) (Translate translate) = +runTranslate moduleInfo (ffiModule, mn) (Translate translate) = runWriter $ runReaderT (runStateT (runExceptT translate) (VarState 0 M.empty)) (ffiModule, M.fromList $ moduleToFuns mn ffiModule, moduleInfo, mn) instance MonadVarState Translate where diff --git a/src/Language/Hamler/Erlang/CodeGen.hs b/src/Language/Hamler/Erlang/CodeGen.hs new file mode 100644 index 00000000..06d56f07 --- /dev/null +++ b/src/Language/Hamler/Erlang/CodeGen.hs @@ -0,0 +1,403 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wno-unused-imports #-} + +module Language.Hamler.Erlang.CodeGen where + +import Control.Algebra (type (:+:)) +import Control.Arrow (first) +import Control.Carrier.Reader +import Control.Carrier.State.Strict +import Control.Effect.Optics +import Control.Exception +import Control.Monad (forM) +import Control.Monad.Compat (foldM) +import qualified Data.Bifunctor +import qualified Data.List as L +import Data.Map (Map) +import qualified Data.Map as M +import Data.Set (Set) +import qualified Data.Set as S +import Data.Text (pack, unpack) +import Erlang.Pretty +import qualified Language.Hamler.Erlang.TranslateSimpleType as Erlang +import Erlang.Type (Forms) +import qualified Erlang.Type as Erlang +import Language.Hamler.Erlang.PrettySimpleType +import Language.Hamler.Erlang.SimpleType as E +import Language.Hamler.Erlang.TranslateSimpleType +import Language.PureScript (Constraint, Expr (UnaryMinus), Ident (GenIdent, Ident, UnusedIdent), ModuleName (ModuleName), ProperName (ProperName), Qualified (Qualified), moduleNameFromString, runModuleName, showQualified) +import Language.PureScript.CoreFn as C +import Language.PureScript.Names (ProperNameType (ConstructorName)) +import Language.PureScript.PSString (PSString, decodeStringWithReplacement) +import Optics hiding (view) +import Text.DocLayout +import Utils +import Prelude + +data Tenv = Tenv + { moduleName :: ModuleName, + otherModuelMap :: Map (Qualified Ident) Bool, + thisModuleMap :: Map (Qualified Ident) Bool, + ffiMap :: Map (Qualified Ident) Int + } + deriving (Show) + +makeFieldLabelsWith noPrefixFieldLabels ''Tenv + +createTenv :: Module Ann -> Map (Qualified Ident) Bool -> Map (Qualified Ident) Int -> Tenv +createTenv m other ffi = + Tenv + { moduleName = mn, + otherModuelMap = other, + thisModuleMap = this, + ffiMap = ffi + } + where + isLambda :: C.Expr Ann -> Bool + isLambda Abs {} = True + isLambda (Constructor _ _ _ ls) | not (null ls) = True + isLambda _ = False + + mn = C.moduleName m + + topBind :: Bind Ann -> [(Qualified Ident, Bool)] + topBind (NonRec _ i e) = [(Qualified (Just mn) i, isLambda e)] + topBind (Rec ls) = map (\((_, i), e) -> (Qualified (Just mn) i, isLambda e)) ls + + this = M.fromList $ concatMap topBind (moduleDecls m) + +runIdent :: Ident -> String +runIdent (Ident i) = unpack i +runIdent (GenIdent Nothing n) = error $ "$" <> show n +runIdent (GenIdent (Just name) n) = error $ "$" <> unpack name <> show n +runIdent UnusedIdent = "unused" + +rPropNameConstr :: ProperName 'ConstructorName -> E.Expr +rPropNameConstr (ProperName t) = ELAtom (unpack t) + +rIdentVar :: Ident -> E.Expr +rIdentVar = EVar . runIdent + +rIdentAtom :: Ident -> E.Expr +rIdentAtom = ELAtom . runIdent + +rExprPss :: Has (Reader Tenv) sig m => E.Expr -> (PSString, C.Expr Ann) -> m E.Expr +rExprPss e (s, e1) = do + e1' <- rExpr e1 + return $ EModuleCall (ELAtom "maps") (ELAtom "put") [ELAtom (decodeStringWithReplacement s), e1', e] + +rDecls :: Has (Reader Tenv) sig m => Bind Ann -> m [(Qualified Ident, E.Expr)] +rDecls (NonRec _ i e) = do + mn <- view @Tenv #moduleName + e' <- rExpr e + this <- view @Tenv #thisModuleMap + case (M.lookup (Qualified (Just mn) i) this, e') of + (Just False, ELambda _ _) -> return [(Qualified (Just mn) i, ELambda [] e')] + _ -> return [(Qualified (Just mn) i, e')] +-- return $ (Qualified (Just mn) i, e') +rDecls (Rec ls) = forM ls $ \((_, i), e) -> do + mn <- view @Tenv #moduleName + e' <- rExpr e + + this <- view @Tenv #thisModuleMap + case (M.lookup (Qualified (Just mn) i) this, e') of + (Just False, ELambda _ _) -> return (Qualified (Just mn) i, ELambda [] e') + _ -> return (Qualified (Just mn) i, e') + +-- return $ (Qualified (Just mn) i, e') + +rModule :: Has (Reader Tenv) sig m => Module Ann -> m Forms +rModule m = do + let mn = C.moduleName m + eps <- rExport m + decs <- concat <$> mapM rDecls (moduleDecls m) + exportFFi <- rExportFFi m + return $ + toFormsN + (moduleNameToString mn) + (map (first qiToString) eps) + (map (first qiToString) (decs ++ concat exportFFi)) + +moduleNameToString :: ModuleName -> String +moduleNameToString mn = unpack (runModuleName mn) + +qiToString :: Qualified Ident -> String +qiToString (Qualified (Just _) i) = runIdent i +qiToString e = error (show e) + +rExport :: Has (Reader Tenv) sig m => Module Ann -> m [(Qualified Ident, Bool)] +rExport m = do + let eps = moduleExports m + this <- view @Tenv #thisModuleMap + forM eps $ \i -> do + let qi = Qualified (Just (C.moduleName m)) i + case M.lookup qi this of + Just v -> return (qi, v) + Nothing -> do + ffi <- view @Tenv #ffiMap + case M.lookup qi ffi of + Just 0 -> return (qi, False) + Just _ -> return (qi, True) + Nothing -> error (show qi ++ show ffi) + +rExportFFi :: Has (Reader Tenv) sig m => Module Ann -> m [[(Qualified Ident, E.Expr)]] +rExportFFi m = do + let eps = moduleExports m + mn <- view @Tenv #moduleName + this <- view @Tenv #thisModuleMap + forM eps $ \i -> do + let qi = Qualified (Just (C.moduleName m)) i + case M.lookup qi this of + Just _ -> return [] + Nothing -> do + ffi <- view @Tenv #ffiMap + case M.lookup qi ffi of + Just 0 -> return [(qi, EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") (rIdentAtom i) [])] + Just ls -> + let cl :: [E.Expr] -> [E.Expr] -> E.Expr -> E.Expr + cl [] ys e = EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") e ys + cl (x : xs) ys e = ELambda [x] (cl xs ys e) + args = map (EVar . show) [1 .. ls] + in return [(qi, cl args args (rIdentAtom i))] + Nothing -> error "never happened" + +--- >>> toQi $ toString (Qualified (Just (ModuleName [ProperName "T"])) (Ident "help"), True) +-- (Qualified (Just [ "T"]) (Ident "help"),True) +toString :: (Qualified Ident, Bool) -> String +toString (Qualified (Just mn) i, b) = moduleNameToString mn ++ "~" ++ runIdent i ++ "~" ++ show b +toString _ = error "never happend" + +toStrings :: [(Qualified Ident, Bool)] -> String +toStrings ls = L.intercalate "\n" (map toString ls) + +--- >>> toQi "T~help~True" +-- (Qualified (Just [ "T"]) (Ident "help"),True) +toQi :: String -> (Qualified Ident, Bool) +toQi s = (Qualified (Just (moduleNameFromString (pack va))) (Ident (pack vb)), read vc) + where + bs [] = [] + bs vs = + let (a, b) = L.break (== '~') vs + in a : bs (drop 1 b) + [va, vb, vc] = bs s + +--- >>> toStrings $ toQis "T~help~True\nT~help~True" +-- "T~help~True\nT~help~True" +toQis :: String -> [(Qualified Ident, Bool)] +toQis ls = map toQi (lines ls) + +rExpr :: Has (Reader Tenv) sig m => C.Expr Ann -> m E.Expr +rExpr (Literal _ li) = rLiteral li +rExpr (Constructor _ _ pc []) = + return $ ELambda [] (ETuple [rPropNameConstr pc]) +rExpr (Constructor _ _ pc xs) = + let cl :: [E.Expr] -> [E.Expr] -> E.Expr + cl [] ys = ETuple (rPropNameConstr pc : ys) -- EApp e ys + cl (x : ks) ys = ELambda [x] (cl ks ys) + vrs = map rIdentVar xs + in return $ cl vrs vrs +rExpr (Accessor _ s e) = do + e' <- rExpr e + return $ EModuleCall (ELAtom "maps") (ELAtom "get") [ELAtom (decodeStringWithReplacement s), e'] +rExpr (ObjectUpdate _ e xs) = do + e' <- rExpr e + foldM rExprPss e' xs +rExpr (Abs _ arg e) = do + e' <- rExpr e + return $ ELambda [rIdentVar arg] e' +rExpr (App _ (Var _ qi@(Qualified (Just mn) i)) b) = do + mn' <- view @Tenv #moduleName + b' <- rExpr b + if mn == mn' + then do + this <- view @Tenv #thisModuleMap + case M.lookup qi this of + Just True -> return $ EApp (rIdentAtom i) [b'] + Just False -> return $ EApp (EApp (rIdentAtom i) []) [b'] + Nothing -> do + ffi <- view @Tenv #ffiMap + case M.lookup qi ffi of + Just v -> case v of + 0 -> return $ EApp (EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") (rIdentAtom i) []) [b'] + s -> + let cl :: [E.Expr] -> [E.Expr] -> E.Expr -> E.Expr + cl [] ys e = EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") e ys + cl (x : xs) ys e = ELambda [x] (cl xs ys e) + args = map (EVar . show) [1 .. s] + in return $ EApp (cl args args $ rIdentAtom i) [b'] + Nothing -> error (show qi ++ " " ++ show ffi) -- "never happened" + else do + other <- view @Tenv #otherModuelMap + case M.lookup qi other of + Nothing -> error "never happend" + Just True -> return $ EModuleCall (ELAtom $ unpack (runModuleName mn)) (rIdentAtom i) [b'] + Just False -> return $ EApp (EModuleCall (ELAtom $ unpack (runModuleName mn)) (rIdentAtom i) []) [b'] +rExpr (App _ ((Var _ (Qualified Nothing i))) b) = do + b' <- rExpr b + return $ EApp (rIdentVar i) [b'] +rExpr (App _ a b) = do + a' <- rExpr a + b' <- rExpr b + return $ EApp a' [b'] +rExpr (Var _ qi@(Qualified (Just mn) i)) = do + mn' <- view @Tenv #moduleName + if mn == mn' + then do + this <- view @Tenv #thisModuleMap + case M.lookup qi this of + Just True -> return $ ELambda [EVar "VAR"] (EApp (rIdentAtom i) [EVar "VAR"]) + Just False -> return (EApp (rIdentAtom i) []) + Nothing -> do + ffi <- view @Tenv #ffiMap + case M.lookup qi ffi of + Nothing -> error "never happened" + Just v -> case v of + 0 -> return (EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") (rIdentAtom i) []) + s -> + let cl :: [E.Expr] -> [E.Expr] -> E.Expr -> E.Expr + cl [] ys e = EModuleCall (ELAtom $ unpack (runModuleName mn) ++ "FFI") e ys + cl (x : xs) ys e = ELambda [x] (cl xs ys e) + + args = map (EVar . show) [1 .. s] + in return $ cl args args (rIdentAtom i) + else do + if qi == Qualified (Just (ModuleName [ProperName "Prim"])) (Ident "undefined") + then return $ ELambda [EVar "Prim_undef"] (EVar "Prim_undef") + else do + other <- view @Tenv #otherModuelMap + case M.lookup qi other of + Just True -> return $ ELambda [EVar "FFiVar"] (EModuleCall (ELAtom $ unpack (runModuleName mn)) (rIdentAtom i) [EVar "FFiVar"]) + Just False -> return (EModuleCall (ELAtom $ unpack (runModuleName mn)) (rIdentAtom i) []) + Nothing -> error (show qi ++ " " ++ show other) -- "never happend" +rExpr (Var _ (Qualified Nothing i)) = return $ rIdentVar i +rExpr (Case _ e caseAlts) = do + e' <- mapM rExpr e + cas <- mapM rCaseAlt $ expandCase e caseAlts + return $ ECase (toTupleOrUnchange e') cas +rExpr (C.Receive _ Nothing ls) = E.Receive <$> mapM rCaseAlt ls +rExpr (C.Receive _ (Just (i, e)) ls) = do + e' <- rExpr e + ls' <- mapM rCaseAlt ls + return $ ReceiveAfter ls' (ELInteger i, e') +rExpr (Let _ bs e) = snd <$> runState @(Map String [String]) M.empty (rLetBind bs e) +rExpr (List _ ls e) = EPList <$> mapM rExpr ls <*> rExpr e + +rLetBind :: Has (Reader Tenv :+: State (Map String [String])) sig m => [Bind Ann] -> C.Expr Ann -> m E.Expr +rLetBind binds e = do + decls <- concat <$> mapM rLetBind' binds + e' <- rExpr e + varMap <- get @(Map String [String]) + return $ ELet decls (handleLetRecApp varMap e') + +rLetBind' :: Has (Reader Tenv :+: State (Map String [String])) sig m => Bind Ann -> m [(E.Expr, E.Expr)] +rLetBind' (NonRec _ i e) = do + e' <- rExpr e + varMap <- get @(Map String [String]) + return [(rIdentVar i, handleLetRecApp varMap e')] +rLetBind' (Rec ls) = do + recls <- mapM (\((_, i), e) -> (runIdent i,) <$> rExpr e) ls + let names = map fst recls + varMap = M.fromList (zip names [1 ..]) + hand = handleLetRec (varMap, length recls) + modify @(Map String [String]) (M.union (M.fromList $ zip names (repeat names))) + return $ map (Data.Bifunctor.bimap EVar hand) recls + +expandCase :: [C.Expr Ann] -> [CaseAlternative Ann] -> [CaseAlternative Ann] +expandCase _ [] = [] +expandCase es (x@(CaseAlternative _ (Right _)) : xs) = x : expandCase es xs +expandCase es ((CaseAlternative bs (Left ls)) : xs) = CaseAlternative bs (Right (changeLeft (extractAnn (head es)) ls)) : expandCase es xs + where + changeLeft :: Ann -> [(Guard Ann, C.Expr Ann)] -> C.Expr Ann + changeLeft ann [] = if null xs then C.Literal ann (BooleanLiteral False) else Case ann es (expandCase es xs) + changeLeft ann ((gval, e) : ys) = + Case + ann + [gval] + [ CaseAlternative [C.LiteralBinder ann (BooleanLiteral True)] (Right e), + CaseAlternative [C.LiteralBinder ann (BooleanLiteral False)] (Right $ changeLeft ann ys) + ] + +toTupleOrUnchange :: [E.Expr] -> E.Expr +toTupleOrUnchange [] = error "never happend" +toTupleOrUnchange [e] = e +toTupleOrUnchange ls = ETuple ls + +toTupleOrUnchangeB :: [E.Binder] -> E.Binder +toTupleOrUnchangeB [] = error "never happend" +toTupleOrUnchangeB [e] = e +toTupleOrUnchangeB ls = BTuple ls + +rCaseAlt :: Has (Reader Tenv) sig m => C.CaseAlternative Ann -> m E.ECaseAlt +rCaseAlt (CaseAlternative b (Right e)) = do + e' <- rExpr e + return (toTupleOrUnchangeB $ map rBinder b, e') +rCaseAlt c = error (show c) + +rLiteral :: Has (Reader Tenv) sig m => Literal (C.Expr Ann) -> m E.Expr +rLiteral (BooleanLiteral True) = return $ ELAtom "true" +rLiteral (BooleanLiteral False) = return $ ELAtom "false" +rLiteral (NumericLiteral (Left i)) = return $ ELInteger i +rLiteral (NumericLiteral (Right i)) = return $ ELDouble i +rLiteral (CharLiteral c) = return $ ELChar c +rLiteral (StringLiteral s) = return $ ELString (decodeStringWithReplacement s) +rLiteral (ListLiteral xs) = EList <$> mapM rExpr xs +rLiteral (AtomLiteral s) = return $ ELAtom (decodeStringWithReplacement s) +rLiteral (ObjectLiteral xs) = + EMapA <$> mapM (\(s, e) -> (ELAtom $ decodeStringWithReplacement s,) <$> rExpr e) xs +rLiteral (BinaryLiteral _) = return EBinary -- TODO +rLiteral (Tuple2Literal a b) = ETuple <$> mapM rExpr [a, b] +rLiteral (TupleLiteral xs) = ETuple <$> mapM rExpr xs + +rIndentBVar :: Ident -> E.Binder +rIndentBVar = BVar . runIdent + +rPropNameConstrB :: ProperName 'ConstructorName -> E.Binder +rPropNameConstrB (ProperName t) = BLAtom (unpack t) + +-- erlang '=' +-- +-- ------1--------- +-- %%erlang local var defined +-- test(A) -> +-- T = fun(B) -> B end. +-- A. +-- +-- ------2--------- +-- %%pattern match PatternAlias +-- myfun(T) -> +-- case T of +-- {foo, Bar } = Var -> start(Var, stuff) end. +-- attention that the position of Var as left of '=' +rBinder :: C.Binder Ann -> E.Binder +rBinder (NullBinder _) = BPatNull +rBinder (LiteralBinder _ lb) = rLiteralBinder lb +rBinder (VarBinder _ i) = rIndentBVar i +rBinder (ConstructorBinder _ _ (Qualified _ p) ls) = BTuple (rPropNameConstrB p : map rBinder ls) +rBinder (NamedBinder _ a b) = + BEqualExpr (rBinder b) (rIndentBVar a) +rBinder (MapBinder _ ls) = BMapB $ map (Data.Bifunctor.bimap rBinder rBinder) ls +rBinder (BinaryBinder _ _) = undefined -- TODO +rBinder (ListBinder _ bs b) = BPList (map rBinder bs) (rBinder b) + +rLiteralBinder :: Literal (C.Binder Ann) -> E.Binder +rLiteralBinder (BooleanLiteral True) = BLAtom "true" +rLiteralBinder (BooleanLiteral False) = BLAtom "false" +rLiteralBinder (NumericLiteral (Left i)) = BLInteger i +rLiteralBinder (NumericLiteral (Right i)) = BLDouble i +rLiteralBinder (CharLiteral c) = BLChar c +rLiteralBinder (StringLiteral s) = BLString (decodeStringWithReplacement s) +rLiteralBinder (AtomLiteral s) = BLAtom (decodeStringWithReplacement s) +rLiteralBinder (ListLiteral xs) = BList (map rBinder xs) +rLiteralBinder (Tuple2Literal a b) = BTuple [rBinder a, rBinder b] +rLiteralBinder (TupleLiteral xs) = BTuple (map rBinder xs) +rLiteralBinder (ObjectLiteral xs) = + BMapB (fmap (\(s, e) -> (BLAtom $ decodeStringWithReplacement s, rBinder e)) xs) +rLiteralBinder e = error (show e) diff --git a/src/Language/Hamler/Erlang/PrettySimpleType.hs b/src/Language/Hamler/Erlang/PrettySimpleType.hs new file mode 100644 index 00000000..853153f2 --- /dev/null +++ b/src/Language/Hamler/Erlang/PrettySimpleType.hs @@ -0,0 +1,134 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} + +module Language.Hamler.Erlang.PrettySimpleType where + +import Data.Functor.Foldable +import Data.Map (Map) +import qualified Data.Map as M +import Language.Hamler.Erlang.SimpleType +import Text.DocLayout +import Utils +import Prelude + +instance Pretty Expr where + pretty = cata go + where + go (ELCharF c) = char c + go (ELIntegerF i) = text (show i) + go (ELDoubleF i) = text (show i) + go (ELAtomF i) = "" <> text i + go (ELStringF i) = text i + go EBinaryF = "EBinary" + go (ETupleF ls) = "{" <> mSepD ls <> "}" + go (EMapAF ls) = "#{" <> mSepD (map (\(a, b) -> a <> " => " <> b) ls) <> "}" + go (EMapBF ls) = "#{" <> mSepD (map (\(a, b) -> a <> " := " <> b) ls) <> "}" + go (EListF ls) = "[" <> mSepD ls <> "]" + go (EPListF ls e) = "[" <> mSepD ls <> "|" <> e <> "]" + go (EVarF s) = "V" <> text s + go (ELambdaF ls e) = "fun(" <> mSepD ls <> ") -> " <> e + go (EAppF e ls) = e <> "(" <> mSepD ls <> ")" + go (EModuleCallF a b ls) = a <> ":" <> b <> "(" <> mSepD ls <> ")" + go (ECaseF ls ecs) = + "case " <> ls <> " of " <> cr + <> nest 4 (mSepDCr $ map (\(bs, r) -> pretty bs <> " -> " <> r) ecs) + go (EEqualExprF a b) = a <> " = " <> b + go (ReceiveF ecs) = " reveive " <> cr <> nest 4 (mSepDCr $ map (\(bs, r) -> pretty bs <> " -> " <> r) ecs) + go (ReceiveAfterF ecs (e1, e2)) = " reveive " <> cr <> nest 4 (mSepDCr $ map (\(bs, r) -> pretty bs <> " -> " <> r) ecs) <> cr <> " after " <> e1 <> " -> " <> e2 + go (ELetF ls e) = " let " <> cr <> nest 4 (mSepDCr $ map (\(a, b) -> a <> " = " <> b <> cr) ls) <> " in " <> e + +removeMoreLambda :: Expr -> Expr +removeMoreLambda (ELambda ls e) = ELambda ls (removeMoreLambda' e) -- keep outside lambda +removeMoreLambda e = removeMoreLambda' e + +removeMoreLambda' :: Expr -> Expr +removeMoreLambda' = cata go + where + go :: ExprF Expr -> Expr + -- go (ELambdaF [] (EApp e ls')) = EApp e ls' -- \() -> f() ---> f () + -- go (ELambdaF a (EApp e ls')) | a == ls' = e -- error \(x,y) -> f (x,y) ---> f + -- go (EAppF (ELambda a (EApp e ls')) b) | a == ls' = EApp e b -- right (\(x,y) -> f(x,y)) (1,2) ---> f (1,2) + go e = embed e + +makeTopLetLam (ELet es (ELambda ls e)) = ELambda ls (ELet es e) -- change top let expr to lambda +makeTopLetLam (ELet es e) = ELambda [] (ELet es e) -- change top let expr to lambda +makeTopLetLam e = e + +expandRec :: Int -> Map String Int -> Expr -> Expr +expandRec len m e = handle e + where + -- expandRec len m e = e + + handle (ELambda ls e) = ELambda (map (\i -> EVar $ "C_" ++ show i) [1 .. len] ++ ls) (cata go e) + handle o = error (show o) + + go (EAppF (EVar s) ls) | M.member s m = + case M.lookup s m of + Nothing -> error (show e) + Just i -> EApp (EVar $ "C_" ++ show i) (map (\i -> EVar $ "C_" ++ show i) [1 .. len] ++ ls) + go e = embed e + +addArgs :: Map String [String] -> Expr -> Expr +addArgs m e = cata go e + where + go (EAppF (EVar s) ls) = + case M.lookup s m of + Nothing -> EApp (EVar s) ls + Just ns -> EApp (EVar s) (map EVar ns ++ ls) + go e = embed e + +instance Pretty Binder where + pretty = cata go + where + go (BLCharF c) = char c + go (BLIntegerF i) = text $ show i + go (BLDoubleF i) = text $ show i + go (BLAtomF i) = text i + go (BLStringF i) = text i + go (BListF ls) = "[" <> mSepD ls <> "]" + go (BTupleF ls) = "{" <> mSepD ls <> "}" + go (BMapBF ls) = "#{" <> mSepD (map (\(a, b) -> a <> " := " <> b) ls) <> "}" + go BBinaryF = "EBinary" + go (BPListF ls e) = "[" <> mSepD ls <> "|" <> e <> "]" + go (BVarF s) = "V" <> text s + go (BEqualExprF a b) = a <> " = " <> b + go BPatNullF = "_" + +handleLetRec :: (Map String Int, Int) -> Expr -> Expr +handleLetRec r@(varMap, len) = handle + where + cl :: [Expr] -> Expr -> Expr + cl [] e = e + cl (x : xs) e = ELambda [x] (cl xs e) + + handle e = + cl (map (\i -> EVar $ "RECVAR" ++ show i) [1 .. len]) (handleLetRec' r e) + +handleLetRec' :: (Map String Int, Int) -> Expr -> Expr +handleLetRec' (varMap, len) = cata go + where + ca :: [Expr] -> Expr -> Expr + ca [] e = e + ca (x : xs) e = ca xs $ EApp e [x] + + go (EVarF s) = + case M.lookup s varMap of + Nothing -> EVar s + Just i -> ca ( map (\i -> EVar $ "RECVAR" ++ show i) [1 .. len]) (EVar $ "RECVAR" ++ show i) + go e = embed e + +handleLetRecApp :: Map String [String] -> Expr -> Expr +handleLetRecApp varMap = cata go + where + go (EVarF s) = + case M.lookup s varMap of + Nothing -> EVar s + Just names -> + let ca :: [Expr] -> Expr -> Expr + ca [] e = e + ca (x : xs) e = ca xs $ EApp e [x] + + in ca (map EVar names) (EVar s) + go e = embed e diff --git a/src/Language/Hamler/Erlang/SimpleType.hs b/src/Language/Hamler/Erlang/SimpleType.hs new file mode 100644 index 00000000..3046d956 --- /dev/null +++ b/src/Language/Hamler/Erlang/SimpleType.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeFamilies #-} + +module Language.Hamler.Erlang.SimpleType where + +import Data.Functor.Foldable.TH (makeBaseFunctor) +import Text.DocLayout +import Utils +import Prelude + +data Expr + = ELChar Char + | ELInteger Integer + | ELDouble Double + | ELAtom String + | ELString String + | EBinary + | ETuple [Expr] + | -- aa => 1 map create + EMapA [(Expr, Expr)] + | EMapB [(Expr, Expr)] + | -- [1,2,3,4] + EList [Expr] + | -- [1,2,3 | a] + EPList [Expr] Expr + | EVar String + | ELambda [Expr] Expr + + | EApp Expr [Expr] + + | EModuleCall Expr Expr [Expr] + | ECase Expr [ECaseAlt] + | -- erlang '=' + -- + -- ------1--------- + -- %%erlang local var defined + -- test(A) -> + -- T = fun(B) -> B end. + -- A. + EEqualExpr Expr Expr + | Receive [ECaseAlt] + | ReceiveAfter [ECaseAlt] (Expr, Expr) + | ELet [(Expr, Expr)] Expr + deriving (Show, Eq) + +-- binders resultExpr +type ECaseAlt = (Binder, Expr) + +data Binder + = BLChar Char + | BLInteger Integer + | BLDouble Double + | BLAtom String + | BLString String + | BList [Binder] + | BTuple [Binder] + | BMapB [(Binder, Binder)] + | BBinary + | BPList [Binder] Binder + | BVar String + | -- ------2--------- + -- %%pattern match PatternAlias + -- myfun(T) -> + -- case T of + -- {foo, Bar } = Var -> start(Var, stuff) end. + -- attention that the position of Var as left of '=' + BEqualExpr Binder Binder + | BPatNull + deriving (Show, Eq) + +makeBaseFunctor ''Expr + +makeBaseFunctor ''Binder diff --git a/src/Language/Hamler/Erlang/TranslateSimpleType.hs b/src/Language/Hamler/Erlang/TranslateSimpleType.hs new file mode 100644 index 00000000..632e4ac2 --- /dev/null +++ b/src/Language/Hamler/Erlang/TranslateSimpleType.hs @@ -0,0 +1,268 @@ +{-# LANGUAGE PatternSynonyms #-} + +module Language.Hamler.Erlang.TranslateSimpleType where + +import qualified Data.Bifunctor +import Data.Char +import Language.Hamler.Erlang.SimpleType as S +import Erlang.Type as E +import Prelude + +toFormsN :: String -> [(String, Bool)] -> [(String, S.Expr)] -> Forms +toFormsN mn exports decls = listToForms (mnF : expF : declsF) + where + mnF = + Form0 $ + Attribute0 + (Atom "module") + ( AttrVal0 + ( Expr13 $ + ExprRemote1 $ + ExprMax7 $ + Expr13 $ + ExprRemote1 $ ExprMax1 $ Atomic3 $ Atom mn + ) + ) + expF = + Form0 $ + Attribute0 + (Atom "export") + ( AttrVal0 + ( Expr13 $ + ExprRemote1 $ + ExprMax7 $ + Expr13 $ + ExprRemote1 $ + ExprMax2 $ + listToList' + (map (\(n, b) -> Expr8 (atomToExpr n) Mx (intToExpr $ if b then 1 else 0)) exports) + ) + ) + declsF = map (uncurry toForm) decls + +pattern ExportList :: List -> Form +pattern ExportList l = + Form0 + ( Attribute0 + (Atom "export") + ( AttrVal0 + ( Expr13 + ( ExprRemote1 + (ExprMax7 (Expr13 (ExprRemote1 (ExprMax2 l)))) + ) + ) + ) + ) + +listToDec :: List -> [E.Expr] +listToDec List0 = [] +listToDec (List1 x xs) = x : go xs + where + go (Tail2 v ls) = v : go ls + go Tail0 = [] + +exprToDec :: E.Expr -> (String, Integer) +exprToDec + ( Expr8 + (Expr13 (ExprRemote1 (ExprMax1 (Atomic3 (Atom a))))) + Mx + (Expr13 (ExprRemote1 (ExprMax1 (Atomic1 i)))) + ) = (a, i) +exprToDec _ = error "never happend" + +dec :: List -> [(String, Integer)] +dec l = map exprToDec $ listToDec l + +formsToList :: Forms -> [Form] +formsToList (Forms0 x) = [x] +formsToList (Forms1 x ls) = x : formsToList ls + +atomToExpr :: String -> E.Expr +atomToExpr s = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic3 $ Atom s + +intToExpr :: Integer -> E.Expr +intToExpr i = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic1 i + +listToForms :: [Form] -> Forms +listToForms [] = error "never happend" +listToForms [x] = Forms0 x +listToForms (x : xs) = Forms1 x (listToForms xs) + +toForms :: [(String, S.Expr)] -> Forms +toForms [] = Forms0 $ Form0 (Attribute0 (Atom "nothing") (AttrVal0 (Expr13 (ExprRemote1 (ExprMax0 (Var "aa")))))) +toForms [(a, b)] = Forms0 $ toForm a b +toForms ((a, b) : xs) = Forms1 (toForm a b) (toForms xs) + +toForm :: String -> S.Expr -> Form +toForm s e = + Form1 $ + Function $ FunctionClauses0 $ toFunctionClause s e + +toFunctionClause :: String -> S.Expr -> FunctionClause +toFunctionClause name (ELambda [] e) = + FunctionClause + (Atom name) + (ClauseArgs PatArgumentList0) + ClauseGuard1 + (ClauseBody $ Exprs0 $ toExpr e) +toFunctionClause name (ELambda xs e) = + FunctionClause + (Atom name) + (ClauseArgs (PatArgumentList1 (listToPatExprs $ map (toPatExpr . eToB) xs))) + ClauseGuard1 + (ClauseBody $ Exprs0 $ toExpr e) +toFunctionClause name e = + FunctionClause + (Atom name) + (ClauseArgs PatArgumentList0) + ClauseGuard1 + (ClauseBody $ Exprs0 $ toExpr e) + +removeQ :: String -> String +removeQ "" = "" +removeQ s = case last s of + '\'' -> init s ++ "_Quotes" + _ -> s + +toExpr :: S.Expr -> E.Expr +toExpr (ELChar c) = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic0 c +toExpr (ELInteger i) = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic1 i +toExpr (ELDouble i) = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic2 i +toExpr (ELAtom i) = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic3 $ Atom i +toExpr (ELString i) = Expr13 $ ExprRemote1 $ ExprMax1 $ Atomic4 $ Strings0 i +toExpr EBinary = undefined +toExpr (ETuple []) = Expr13 $ ExprRemote1 $ ExprMax6 Tuple0 +toExpr (ETuple ls) = Expr13 $ ExprRemote1 $ ExprMax6 $ Tuple1 (listToExprs $ map toExpr ls) +toExpr (EMapA ls) = Expr10 $ MapExpr0 (listToMapTuple (\x y -> MapField0 $ MapFieldAssoc x y) ls) +toExpr (EMapB ls) = Expr10 $ MapExpr0 (listToMapTuple (\x y -> MapField1 $ MapFieldExact x y) ls) +toExpr (EList ls) = Expr13 $ ExprRemote1 $ ExprMax2 (listToList ls) +toExpr (EPList ls e) = Expr13 $ ExprRemote1 $ ExprMax2 $ listToList1 ls e +toExpr (EVar s) = Expr13 $ ExprRemote1 $ ExprMax0 (Var $ varToUpper $ removeQ s) +toExpr (ELambda [] e) = + Expr13 $ ExprRemote1 $ ExprMax12 $ FunExpr2 $ FunClauses0 $ FunClause0 PatArgumentList0 ClauseGuard1 (ClauseBody $ Exprs0 (toExpr e)) +toExpr (ELambda ls e) = + Expr13 $ ExprRemote1 $ ExprMax12 $ FunExpr2 $ FunClauses0 $ FunClause0 (PatArgumentList1 (listToPatExprs $ map (toPatExpr . eToB) ls)) ClauseGuard1 (ClauseBody $ Exprs0 (toExpr e)) +toExpr (EApp e []) = Expr11 $ FunctionCall (toExprRemote (toExpr e)) ArgumentList0 +toExpr (EApp e ls) = Expr11 $ FunctionCall (toExprRemote (toExpr e)) (ArgumentList1 (listToExprs $ map toExpr ls)) +toExpr (EModuleCall a b []) = Expr11 $ FunctionCall (toExprRemote2 a b) ArgumentList0 +toExpr (EModuleCall a b ls) = Expr11 $ FunctionCall (toExprRemote2 a b) (ArgumentList1 (listToExprs $ map toExpr ls)) +toExpr te@(ECase e eas) = + Expr13 $ + ExprRemote1 $ + ExprMax10 $ + CaseExpr + (toExpr e) + (listToCrClause te $ map (\(b, e) -> CrClause (toExpr $ bToE b) ClauseGuard1 (ClauseBody $ Exprs0 $ toExpr e)) eas) +toExpr (EEqualExpr a b) = Expr1 (toExpr a) (toExpr b) +toExpr te@(Receive eas) = + Expr13 $ + ExprRemote1 $ + ExprMax11 $ + ReceiveExpr0 + (listToCrClause te $ map (\(b, e) -> CrClause (toExpr $ bToE b) ClauseGuard1 (ClauseBody $ Exprs0 $ toExpr e)) eas) +toExpr te@(ReceiveAfter eas (a, b)) = + Expr13 $ + ExprRemote1 $ + ExprMax11 $ + ReceiveExpr2 + (listToCrClause te $ map (\(b, e) -> CrClause (toExpr $ bToE b) ClauseGuard1 (ClauseBody $ Exprs0 $ toExpr e)) eas) + (toExpr a) + (ClauseBody $ Exprs0 $ toExpr b) +toExpr (ELet ls e) = Expr13 $ ExprRemote1 $ ExprMax8 $ listToExprs $ map (\(a, b) -> Expr1 (toExpr a) (toExpr b)) ls ++ [toExpr e] + +listToCrClause :: S.Expr -> [CrClause] -> CrClauses +listToCrClause e [] = error (show e) +listToCrClause e [c] = CrClauses0 c +listToCrClause e (x : xs) = CrClauses1 x (listToCrClause e xs) + +toExprRemote :: E.Expr -> ExprRemote +toExprRemote (E.Expr13 r) = r +toExprRemote e = ExprRemote1 $ ExprMax7 e + +-- toExprRemote e = ExprRemote1 $ ExprMax7 e + +toExprRemote2 :: S.Expr -> S.Expr -> ExprRemote +toExprRemote2 a b = + let Expr13 (ExprRemote1 a') = toExpr a + Expr13 (ExprRemote1 b') = toExpr b + in ExprRemote0 a' b' + +listToExprs :: [E.Expr] -> Exprs +listToExprs [] = error "never happened" +listToExprs [e] = Exprs0 e +listToExprs (x : xs) = Exprs1 x (listToExprs xs) + +listToMapTuple :: (MapKey -> E.Expr -> MapField) -> [(S.Expr, S.Expr)] -> MapTuple +listToMapTuple f [] = MapTuple0 +listToMapTuple f xs = MapTuple1 (listToMapFields xs) + where + listToMapFields [] = error "never happend" + listToMapFields [(n, v)] = MapFields0 $ f (MapKey $ toExpr n) (toExpr v) + listToMapFields ((n, v) : xs) = MapFields1 (f (MapKey $ toExpr n) (toExpr v)) (listToMapFields xs) + +listToList :: [S.Expr] -> List +listToList [] = List0 +listToList (x : xs) = List1 (toExpr x) (listToTail xs) + where + listToTail [] = Tail0 + listToTail (v : vs) = Tail2 (toExpr v) (listToTail vs) + +listToList' :: [E.Expr] -> List +listToList' [] = List0 +listToList' (x : xs) = List1 x (listToTail xs) + where + listToTail [] = Tail0 + listToTail (v : vs) = Tail2 v (listToTail vs) + +listToList1 :: [S.Expr] -> S.Expr -> List +listToList1 [] _ = error "never happend" --- ???? [ | ls] +listToList1 (x : xs) e = List1 (toExpr x) (listToTail xs) + where + listToTail [] = Tail1 $ toExpr e + listToTail (v : vs) = Tail2 (toExpr v) (listToTail vs) + +listToPatExprs :: [PatExpr] -> PatExprs +listToPatExprs [] = error "never happend" +listToPatExprs [x] = PatExprs0 x +listToPatExprs (x : xs) = PatExprs1 x (listToPatExprs xs) + +-- listToArgumentList :: [Exprs] + +toPatExpr :: S.Binder -> PatExpr +toPatExpr (BLChar c) = PatExpr8 $ PatExprMax1 $ Atomic0 c +toPatExpr (BLInteger c) = PatExpr8 $ PatExprMax1 $ Atomic1 c +toPatExpr (BLDouble c) = PatExpr8 $ PatExprMax1 $ Atomic2 c +toPatExpr (BLAtom c) = PatExpr8 $ PatExprMax1 $ Atomic3 $ Atom c +toPatExpr (BLString c) = PatExpr8 $ PatExprMax1 $ Atomic4 $ Strings0 c +toPatExpr BBinary = PatExpr8 $ PatExprMax3 undefined +toPatExpr (BList ls) = PatExpr8 $ PatExprMax2 (listToList $ map bToE ls) +toPatExpr (BPList ls e) = PatExpr8 $ PatExprMax2 (listToList1 (map bToE ls) (bToE e)) +toPatExpr (BTuple []) = PatExpr8 $ PatExprMax4 Tuple0 +toPatExpr (BTuple ls) = PatExpr8 $ PatExprMax4 $ Tuple1 (listToExprs $ map (toExpr . bToE) ls) +toPatExpr (BMapB ls) = PatExpr6 $ MapPatExpr0 (listToMapTuple (\x y -> MapField1 $ MapFieldExact x y) (map (Data.Bifunctor.bimap bToE bToE) ls)) +toPatExpr (BVar s) = PatExpr8 $ PatExprMax0 $ Var (varToUpper s) +toPatExpr (BEqualExpr a b) = PatExpr0 (toPatExpr a) (toPatExpr b) +toPatExpr BPatNull = PatExpr8 $ PatExprMax1 $ Atomic3 (Atom "_") + +bToE :: S.Binder -> S.Expr +bToE (BLChar c) = ELChar c +bToE (BLInteger c) = ELInteger c +bToE (BLDouble c) = ELDouble c +bToE (BLAtom c) = ELAtom c +bToE (BLString s) = ELString s +bToE BBinary = undefined +bToE (BList ls) = EList (map bToE ls) +bToE (BPList ls b) = EPList (map bToE ls) (bToE b) +bToE (BTuple ls) = ETuple (map bToE ls) +bToE (BMapB ls) = EMapB $ map (Data.Bifunctor.bimap bToE bToE) ls +bToE (BVar s) = EVar $ removeQ s +bToE (BEqualExpr a b) = EEqualExpr (bToE a) (bToE b) +bToE BPatNull = ELAtom "_" + +eToB :: S.Expr -> S.Binder +eToB (EVar s) = BVar $ removeQ s +eToB e = error (show e) + +varToUpper :: String -> String +varToUpper [] = [] +varToUpper (x:xs) = toUpper x : xs diff --git a/src/Language/Hamler/Make/Actions.hs b/src/Language/Hamler/Make/Actions.hs index 3bbecda7..a042f03b 100644 --- a/src/Language/Hamler/Make/Actions.hs +++ b/src/Language/Hamler/Make/Actions.hs @@ -1,4 +1,5 @@ -- The module is copied from purescript compiler. +{-# LANGUAGE TypeApplications #-} module Language.Hamler.Make.Actions ( MakeActions (..), RebuildPolicy (..), @@ -39,6 +40,14 @@ import Language.PureScript.Options hiding (codegenTargets) import System.FilePath (()) import Prelude import qualified Language.PureScript.Bundle as B +import qualified Language.Hamler.Erlang.CodeGen as E +import qualified Language.Hamler.Erlang.TranslateSimpleType as E +import Utils as E +import qualified Erlang.Type as E +import qualified Erlang.B as E +import Text.DocLayout (render) +import Control.Carrier.Reader (run, runReader) + -- | Determines when to rebuild a module renderProgressMessage :: ProgressMessage -> String @@ -58,7 +67,7 @@ buildMakeActions :: -- | Generate a prefix comment? Bool -> MakeActions Make -buildMakeActions libfp isInline outputDir filePathMap foreigns _ = +buildMakeActions libfp isBuildErlSource outputDir filePathMap foreigns _ = MakeActions getInputTimestampsAndHashes getOutputTimestamp readExterns codegen ffiCodegen progress readCacheDb writeCacheDb outputPrimDocs where getInputTimestampsAndHashes :: @@ -117,6 +126,12 @@ buildMakeActions libfp isInline outputDir filePathMap foreigns _ = let list = read (unpack con) :: [(String, Integer)] return $ (mn', M.fromList $ fmap (\(a, b) -> (pack (unpack mn' <> "." <> a), b)) list) + readModuleInfo' :: HasCallStack => ModuleName -> SupplyT Make [(Qualified Ident, Bool)] + readModuleInfo' mn = do + let path = getFilePath ".infoErl" mn filePathMap + con <- lift $ makeIO "read module infor" $ readFile path + return $ E.toQis con + codegen :: HasCallStack => CF.Module CF.Ann -> Docs.Module -> ExternsFile -> SupplyT Make () codegen m _ exts = do let mn = CF.moduleName m @@ -133,7 +148,7 @@ buildMakeActions libfp isInline outputDir filePathMap foreigns _ = let mods = filter (/= mn) $ filter (/= ModuleName [ProperName "Prim"]) $ fmap snd $ CF.moduleImports m modInfoList <- mapM readModuleInfo mods let modInfoMap = M.fromList modInfoList - ((erl, _), _) = runTranslate isInline modInfoMap (ffiModule, mn) $ moduleToErl m + ((erl, _), _) = runTranslate modInfoMap (ffiModule, mn) $ moduleToErl m case erl of Left e -> throwError e Right e@(CE.Module _ exports _ _ _) -> do @@ -149,6 +164,47 @@ buildMakeActions libfp isInline outputDir filePathMap foreigns _ = (outputDir (unpack mn' <> ".info")) (pack $ show $ fmap (\(CE.FunName (CE.Atom s1 _) i _) -> (s1, i)) exports) + when isBuildErlSource $ do + mforms <- case M.lookup mn foreigns of + Nothing -> do return Nothing + Just fp -> do + con <- lift $ makeIO ".core -> .P" $ readFile (Prelude.reverse $ 'P' : drop 4 (Prelude.reverse fp)) + case E.runCalc con of + E.Failed r -> error r + E.OK fs -> return $ Just fs + + let getFFiDecArgs :: Maybe E.Forms -> [(Qualified Ident, Int)] + getFFiDecArgs Nothing = [] + getFFiDecArgs (Just fs) = + let ls = E.formsToList fs + getDec :: E.Form -> [(String, Integer)] + getDec (E.ExportList l) = E.dec l + getDec _ = [] + in map (\(s, i) -> (Qualified (Just mn) (Ident (pack s)), fromIntegral i)) $ concatMap getDec ls + ffiM = M.fromList $ getFFiDecArgs mforms + + let mods = filter (/= mn) $ filter (/= ModuleName [ProperName "Prim"]) $ fmap snd $ CF.moduleImports m + + modInfoList <- concat <$> mapM readModuleInfo' mods + let otherM = M.fromList modInfoList + + forms = run $ runReader (E.createTenv m otherM ffiM) (E.rModule m) + exports = run $ runReader (E.createTenv m otherM ffiM) (E.rExport m) + + lift $ + makeIO "write module information " $ + writeFile + (outputDir (unpack (runModuleName mn) <> ".infoErl")) + (E.toStrings exports) + + lift $ + makeIO "Write core erlang file" $ do + writeFile + (outputDir (unpack (runModuleName mn ) <> ".erl")) + (render @String Nothing $ pretty forms) + + + ffiCodegen :: CF.Module CF.Ann -> Make () ffiCodegen _ = return () diff --git a/stack.yaml b/stack.yaml deleted file mode 100644 index ba777400..00000000 --- a/stack.yaml +++ /dev/null @@ -1,21 +0,0 @@ -allow-different-user: true -resolver: lts-13.26 -pvp-bounds: upper -packages: -- '.' - -extra-deps: -- happy-1.19.9 -- language-javascript-0.7.0.0 -- network-3.0.1.1 -- these-1.0.1 -- semialign-1 -- github: hamler-lang/CoreErlang - commit: c0466e103b312f32afe23c4f484b534c1591a0b2 -- github: hamler-lang/purescript - commit: f5c61501b6c5d1d32054845f1745563c4f6f1333 -- megaparsec-8.0.0@sha256:362f85e243ecbcb550e1de6e5c74ba5a50c09edaa1208c99bc5b9fd242227fc6,3808 -flags: - these: - assoc: false - quickcheck: false diff --git a/tests/Test/System/FilePath.hm b/tests/Test/System/FilePath.hm index fe263d12..c2145adb 100644 --- a/tests/Test/System/FilePath.hm +++ b/tests/Test/System/FilePath.hm @@ -32,7 +32,7 @@ propIsDir :: IO Bool propIsDir = F.isDir "tests" propIsFile :: IO Bool -propIsFile = F.isFile "stack.yaml" +propIsFile = F.isFile "LICENSE" propJoinPath :: Bool propJoinPath = F.joinPath ["/usr", "bin", "shell.sh"] == "/usr/bin/shell.sh"