diff --git a/README.md b/README.md index 5c430632..d4e03acc 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,16 @@ UPDATE_REMOTE_DECISION_SERVICE_ON_SAVE_STATUS= DEMO_OIA_DATA_MODEL_XML_PATH= COMPILED_SIMALA_OUTPUT_DIR="generated/simala" +NLG_EN_OUTPUT_DIR="generated/nlg_en" +NLG_EN_OUTPUT_FILENAME="nlg_en_output.json" +GF_PORTABLE_GRAMMAR_FORMAT_FILENAME="Lam4.pgf" ``` ## How to compile Lam4 files Use the `lam4-cli`. -EG, from the root `lam4` dir: +EG, from the root `lam4` dir: > `cabal run lam4-cli -- examples/arithmetic.l4` @@ -100,9 +103,9 @@ When things update... * You can update your VSCode extension from the command line using the commands mentioned earlier. Yongming has a personal script for this that can be adapted (`yongminghan` is the publisher). If the changes don't show up, try restarting the extension host from VSCode: ```bash -code --uninstall-extension yongminghan.lam4; -rm lam4-0.0.1.vsix; -vsce package; +code --uninstall-extension yongminghan.lam4; +rm lam4-0.0.1.vsix; +vsce package; code --install-extension lam4*.vsix ``` @@ -125,7 +128,7 @@ After building: #### Typechecker, evaluator, backend related -The type checker is currently implemented in Typescript, but in the medium term I want to just move that to the Haskell backend. +The type checker is currently implemented in Typescript, but in the medium term I want to just move that to the Haskell backend. I also want to have some kind of JSON RPC going with the Haskell backend, so that we'll be able to, e.g., parse the program, run the evaluator, and see nice feedback in a webview, all within the IDE. @@ -136,4 +139,4 @@ I also want to have some kind of JSON RPC going with the Haskell backend, so tha When time permits -- e.g., shortly after the first end-to-end system is done --- we should try cutting down on symbols like braces if that will confuse non-professional programmers. EG, we could instead go for something that uses indentation. -This is already possible with Langium; I just haven't done it because it requires a bit more effort and would complicate the codebase more than I would like at this stage. \ No newline at end of file +This is already possible with Langium; I just haven't done it because it requires a bit more effort and would complicate the codebase more than I would like at this stage. diff --git a/lam4-cli/app/Main.hs b/lam4-cli/app/Main.hs index b1335bbe..85f32d4b 100644 --- a/lam4-cli/app/Main.hs +++ b/lam4-cli/app/Main.hs @@ -1,7 +1,7 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE TemplateHaskell #-} module Main where @@ -14,6 +14,8 @@ import Data.ByteString as BS hiding (concat, concatMap, map, null, putStr) import qualified Data.Text as T +import Configuration.Dotenv (loadFile, defaultConfig, onMissingFile) + import Cradle import qualified Lam4.Expr.ConcreteSyntax as CST (Decl) import Lam4.Expr.ExtractProgramInfo @@ -21,12 +23,17 @@ import Lam4.Expr.Parser (parseProgramByteStr) import Lam4.Expr.ToConcreteEvalAST (cstProgramToConEvalProgram) import Lam4.Expr.ToSimala () import qualified Lam4.Expr.ToSimala as ToSimala -import qualified Lam4.Render.Render as Render -import Lam4.Render.Render (NLGConfig(..)) import Lam4.Parser.Monad (evalParserFromScratch) +import Lam4.Render.Render (NLGConfig (..)) +import qualified Lam4.Render.Render as Render import Options.Applicative as Options import System.Directory import System.FilePath (()) +import System.Environment.Blank (getEnv) + +------------------------------ + -- The key config types +------------------------------- data FrontendConfig = MkFrontendConfig { frontendDir :: FilePath @@ -34,6 +41,10 @@ data FrontendConfig = , args :: [String] } +data OutputConfig = + MkOutputConfig { simalaOutputConfig :: SimalaOutputConfig + , nlgOutputConfig :: NLGConfig } + data SimalaOutputConfig = MkSimalaOutputConfig { outputDir :: FilePath @@ -41,6 +52,12 @@ data SimalaOutputConfig = , programInfoFilename :: FilePath } +---------------------------------- + -- Constants / default values +---------------------------------- + +-- TODO: Most of the following should be put in, and read from, the .env file + lam4FrontendDir :: FilePath lam4FrontendDir = "lam4-frontend" @@ -49,22 +66,24 @@ frontendConfig = MkFrontendConfig { runner = "node" , frontendDir = lam4FrontendDir , args = [lam4FrontendDir "bin" "cli", "toMinimalAst"] } --- TODO: Most of the following should be put in, and read from, the .env file -simalaOutputConfig :: SimalaOutputConfig -simalaOutputConfig = MkSimalaOutputConfig { +defaultSimalaOutputConfig :: SimalaOutputConfig +defaultSimalaOutputConfig = MkSimalaOutputConfig { outputDir = "generated" "simala" , programFilename = "output.simala" , programInfoFilename = "program_info.json" } --- TODO: read outputDir in from a .env file -nlgConfig :: NLGConfig -nlgConfig = MkNLGConfig { +defaultNlgConfig :: NLGConfig +defaultNlgConfig = MkNLGConfig { outputDir = "generated" "nlg_en" - , resultFilename = "output.txt" - , abstractSyntaxFilename = "Lam4.pgf" + , outputFilename = "nlg_en_output.json" + , pgfFilename = "Lam4.pgf" , concreteSyntaxName = "Lam4Eng" } +-------------------- + -- CLI Options +-------------------- + -- TODO: Think about exposing a tracing option? data Options = MkOptions @@ -95,6 +114,32 @@ optionsConfig = <> header "Lam4 (an experimental variant of the L4 legal DSL)" ) +-- | Load output configs from environment variables if .env present; use default configs if not +loadConfigsFromEnv :: IO OutputConfig +loadConfigsFromEnv = do + -- Try to load .env file, warn if not found + _ <- onMissingFile + (loadFile defaultConfig) + (hPutStrLn stderr "Warning: .env file not found, using default configuration") + + mSimalaOutDir <- getEnv "COMPILED_SIMALA_OUTPUT_DIR" + mNlgEnOutDir <- getEnv "NLG_EN_OUTPUT_DIR" + mGfPgfFilename <- getEnv "GF_PORTABLE_GRAMMAR_FORMAT_FILENAME" + mNlgOutFilename <- getEnv "NLG_EN_OUTPUT_FILENAME" + + let simalaOutConfig = MkSimalaOutputConfig { + outputDir = fromMaybe defaultSimalaOutputConfig.outputDir mSimalaOutDir + , programFilename = defaultSimalaOutputConfig.programFilename -- TODO: move to .env in the future (and update the lsp client accordingly) + , programInfoFilename = defaultSimalaOutputConfig.programInfoFilename -- TODO: move to .env in the future (and update the lsp client accordingly) + } + nlgOutConfig = MkNLGConfig { outputDir = fromMaybe defaultNlgConfig.outputDir mNlgEnOutDir + , outputFilename = fromMaybe defaultNlgConfig.outputFilename mNlgOutFilename + , pgfFilename = fromMaybe defaultNlgConfig.pgfFilename mGfPgfFilename + , concreteSyntaxName = defaultNlgConfig.concreteSyntaxName -- TODO: Figure out a better way to handle this + } + pure $ MkOutputConfig { simalaOutputConfig = simalaOutConfig + , nlgOutputConfig = nlgOutConfig } + main :: IO () main = do options <- Options.execParser optionsConfig @@ -108,18 +153,23 @@ main = do simalaProgram = ToSimala.compile conEvalProgram programInfo = extractProgramInfo conEvalProgram + -- Load output configs + outConfig <- loadConfigsFromEnv + let simalaOutConfig = outConfig.simalaOutputConfig + nlgOutConfig = outConfig.nlgOutputConfig + -- Create output directory and write files first - createDirectoryIfMissing True simalaOutputConfig.outputDir + createDirectoryIfMissing True outConfig.simalaOutputConfig.outputDir -- save simala program and program info to disk - writeFileUtf8 (simalaOutputConfig.outputDir simalaOutputConfig.programFilename) (ToSimala.render simalaProgram) - writeFileLBS (simalaOutputConfig.outputDir simalaOutputConfig.programInfoFilename) (encodePretty programInfo) + writeFileUtf8 (simalaOutConfig.outputDir simalaOutConfig.programFilename) (ToSimala.render simalaProgram) + writeFileLBS (simalaOutConfig.outputDir simalaOutConfig.programInfoFilename) (encodePretty programInfo) -- NLG (put this behind an option later) -- TODO: Make a ToNLG monad - nlgEnv <- Render.makeNLGEnv nlgConfig - let nlRendering = Render.renderCstProgramToNL nlgEnv cstProgram - createDirectoryIfMissing True nlgConfig.outputDir - writeFileUtf8 (nlgConfig.outputDir nlgConfig.resultFilename) nlRendering + nlgEnv <- Render.makeNLGEnv nlgOutConfig + let nlRendering = Render.renderCstProgramToNL nlgEnv cstProgram + createDirectoryIfMissing True nlgOutConfig.outputDir + writeFileUtf8 (nlgOutConfig.outputDir nlgOutConfig.outputFilename) nlRendering -- TODO: Save a json version with the nlg output as a member -- Perform evaluation (if needed) diff --git a/lam4-cli/lam4-cli.cabal b/lam4-cli/lam4-cli.cabal index e6512b65..f5e3cab6 100644 --- a/lam4-cli/lam4-cli.cabal +++ b/lam4-cli/lam4-cli.cabal @@ -47,6 +47,7 @@ executable lam4-cli , filepath , bytestring , lens-regex-pcre + , dotenv hs-source-dirs: app default-language: GHC2021