From c3a175e808b279eb415bcb5dbcb9db5f34035f98 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Fri, 17 Oct 2025 09:52:06 +0200 Subject: feat: add basic application to evaluate "programs" The main simply reads from its stdin, evaluates its input, and dump the result of the evaluation. --- lambda-calcul/haskell/app/Main.hs | 6 ++++ lambda-calcul/haskell/minilang.cabal | 25 +++++++++++++++ lambda-calcul/haskell/src/Minilang/IO.hs | 32 +++++--------------- lambda-calcul/haskell/src/Minilang/Lambda/Eval.hs | 2 +- lambda-calcul/haskell/test/Minilang/IOSpec.hs | 37 +++++++++++++++++++++++ 5 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 lambda-calcul/haskell/app/Main.hs create mode 100644 lambda-calcul/haskell/test/Minilang/IOSpec.hs diff --git a/lambda-calcul/haskell/app/Main.hs b/lambda-calcul/haskell/app/Main.hs new file mode 100644 index 0000000..5b48c01 --- /dev/null +++ b/lambda-calcul/haskell/app/Main.hs @@ -0,0 +1,6 @@ +import Minilang.IO +import System.IO (stdin, stdout) + +main :: IO () +main = + runEval stdin stdout diff --git a/lambda-calcul/haskell/minilang.cabal b/lambda-calcul/haskell/minilang.cabal index 817672e..7c9c266 100644 --- a/lambda-calcul/haskell/minilang.cabal +++ b/lambda-calcul/haskell/minilang.cabal @@ -25,6 +25,7 @@ source-repository head library exposed-modules: + Minilang.IO Minilang.Lambda.Unify Minilang.Lambda.Eval Minilang.Lambda.Infer @@ -60,6 +61,7 @@ test-suite minilang-test type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: + Minilang.IOSpec Minilang.Lambda.InferSpec Minilang.Lambda.EvalSpec hs-source-dirs: @@ -91,5 +93,28 @@ test-suite minilang-test , minilang , mtl , text + , unix , unordered-containers default-language: Haskell2010 + +executable lambda + main-is: Main.hs + other-modules: + Paths_minilang + hs-source-dirs: + app + default-extensions: + DeriveGeneric + OverloadedStrings + FlexibleInstances + MultiParamTypeClasses + FlexibleContexts + RecordWildCards + NamedFieldPuns + GeneralizedNewtypeDeriving + ghc-options: -Wall -fno-warn-orphans -threaded -rtsopts -with-rtsopts=-N + build-depends: + base + , containers + , minilang + default-language: Haskell2010 diff --git a/lambda-calcul/haskell/src/Minilang/IO.hs b/lambda-calcul/haskell/src/Minilang/IO.hs index bfcb125..c468316 100644 --- a/lambda-calcul/haskell/src/Minilang/IO.hs +++ b/lambda-calcul/haskell/src/Minilang/IO.hs @@ -1,40 +1,24 @@ -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeSynonymInstances #-} -{-# OPTIONS_GHC "-fno-warn-orphans" #-} - module Minilang.IO where import qualified Data.ByteString as BS +import qualified Data.Text as Text import Data.Text.Encoding ( decodeUtf8With, encodeUtf8, ) import Data.Text.Encoding.Error (lenientDecode) -import Minilang.Lambda.Eval hiding (rho) -import Minilang.Parser -import Minilang.Type -import Prettyprinter -import Prettyprinter.Render.Text +import Minilang.Lambda.Eval (eval) import System.IO (Handle) --- | Read an `AST` from @hin@ handle, evaluate it and dump the result +-- | Read a "program" from @hin@ handle, evaluate it and dump the result -- on @hout@. runEval :: Handle -> Handle -> IO () runEval hin hout = do - programText <- decodeUtf8With lenientDecode <$> BS.hGetContents hin - let ast = parseProgram False programText - ρ = EmptyEnv - γ = EmptyContext - (ρ', _) <- loadProgram ast ρ γ - let val = eval ast ρ' + programText <- Text.unpack . decodeUtf8With lenientDecode <$> BS.hGetContents hin + let ast = read programText + env = mempty + result = eval ast env BS.hPut hout - ( encodeUtf8 - ( renderStrict $ layoutPretty defaultLayoutOptions $ pretty val - ) - <> "\n" - ) + (encodeUtf8 (Text.pack (show result) <> "\n")) diff --git a/lambda-calcul/haskell/src/Minilang/Lambda/Eval.hs b/lambda-calcul/haskell/src/Minilang/Lambda/Eval.hs index 68b01be..0829186 100644 --- a/lambda-calcul/haskell/src/Minilang/Lambda/Eval.hs +++ b/lambda-calcul/haskell/src/Minilang/Lambda/Eval.hs @@ -6,7 +6,7 @@ data Term = Var Text | Lam Text Term | App Term Term - deriving (Show, Eq) + deriving (Show, Read, Eq) type Env = [(Text, Term)] diff --git a/lambda-calcul/haskell/test/Minilang/IOSpec.hs b/lambda-calcul/haskell/test/Minilang/IOSpec.hs new file mode 100644 index 0000000..038877d --- /dev/null +++ b/lambda-calcul/haskell/test/Minilang/IOSpec.hs @@ -0,0 +1,37 @@ +module Minilang.IOSpec where + +import Control.Exception (bracket) +import Minilang.IO (runEval) +import System.Directory (getTemporaryDirectory, removeFile) +import System.FilePath ((<.>), ()) +import System.IO (IOMode (..), hClose, readFile, withFile, writeFile) +import System.Posix (mkstemp) +import Test.Hspec (Spec, around, describe, it, parallel, shouldBe) +import Prelude hiding (lines, readFile, writeFile) + +spec :: Spec +spec = parallel $ + describe "MiniLang I/O Evaluator" $ do + around withTempFile $ + it "evaluates a simple MiniLang 'program' and dumps result" $ \fileName -> do + let outputFileName = fileName <> ".out" + program = + [ "(App (Lam \"x\" (Var \"x\")) (Var \"y\"))" + ] + + writeFile fileName (unlines program) + + _ <- withFile fileName ReadMode $ \hin -> + withFile outputFileName AppendMode $ \hout -> + runEval hin hout + + out <- readFile outputFileName + + out `shouldBe` "Var \"y\"\n" + +withTempFile :: (String -> IO ()) -> IO () +withTempFile = + bracket mkTempFile rmTempFile + where + mkTempFile = getTemporaryDirectory >>= \dir -> mkstemp (dir "test-repl") >>= \(fp, h) -> hClose h >> pure fp + rmTempFile fn = removeFile fn >> removeFile (fn <.> "out") -- cgit v1.2.3