mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-12 06:19:33 -06:00
Traditionally, GF_LIB_PATH points to something like
`.../share/ghc-8.0.2-x86_64/gf-3.9/lib`
and if you want prelude and alltenses and present, you add a
`--# -path=.:present`
compiler pragma to the top of your .gf file
But if you are developing some kind of application grammar
library or contrib of your own, you might find yourself
repeating your library path at the top of all your .gf files.
After painstakingly maintaining the same library path at the
top of all your .gf files, you might say, let's factor this
out into GF_LIB_PATH.
Then you might then find to your surprise that GF_LIB_PATH
doesn't accept the usual colon:separated:path notation
familiar from, say, unix PATH and MANPATH.
This patch allows you to define
`GF_LIB_PATH=gf-3.9.lib:$HOME/gf-contrib/whatever/lib`
in a more natural way.
If you are an RGL hacker and have your own version of the
RGL tree sitting somewhere, you should be able to have both
paths in the GF_LIB_PATH, for added convenience. This minor
convenience will probably lead to obscure bugs and great
frustration when you find that your changes are mysteriously
not being picked up by GF; so keep this in mind and use it
cautiously.
This caution should probably sit in the documentation
somewhere. A subsequent commit will do that.
If you use zsh, you can do this to quickly build up a big
GF_LIB_PATH:
% gf_lib_path=( $HOME/src/GF/lib/src/{api,abstract,common,english,api/libraryBrowser,prelude,..} )
% typeset -xT GF_LIB_PATH gf_lib_path
132 lines
5.7 KiB
Haskell
132 lines
5.7 KiB
Haskell
module GF.Compile (compileToPGF, link, batchCompile, srcAbsName) where
|
|
|
|
import GF.Compile.GrammarToPGF(mkCanon2pgf)
|
|
import GF.Compile.ReadFiles(ModEnv,getOptionsFromFile,getAllFiles,
|
|
importsOfModule)
|
|
import GF.CompileOne(compileOne)
|
|
|
|
import GF.Grammar.Grammar(Grammar,emptyGrammar,
|
|
abstractOfConcrete,prependModule)--,msrc,modules
|
|
|
|
import GF.Infra.Ident(ModuleName,moduleNameS)--,showIdent
|
|
import GF.Infra.Option
|
|
import GF.Infra.UseIO(IOE,FullPath,liftIO,getLibraryDirectory,putIfVerb,
|
|
justModuleName,extendPathEnv,putStrE,putPointE)
|
|
import GF.Data.Operations(raise,(+++),err)
|
|
|
|
import Control.Monad(foldM,when,(<=<),filterM,liftM)
|
|
import GF.System.Directory(doesFileExist,getModificationTime)
|
|
import System.FilePath((</>),isRelative,dropFileName)
|
|
import qualified Data.Map as Map(empty,insert,elems) --lookup
|
|
import Data.List(nub)
|
|
import Data.Time(UTCTime)
|
|
import GF.Text.Pretty(render,($$),(<+>),nest)
|
|
|
|
import PGF.Internal(optimizePGF)
|
|
import PGF(PGF,defaultProbabilities,setProbabilities,readProbabilitiesFromFile)
|
|
|
|
-- | Compiles a number of source files and builds a 'PGF' structure for them.
|
|
-- This is a composition of 'link' and 'batchCompile'.
|
|
compileToPGF :: Options -> [FilePath] -> IOE PGF
|
|
compileToPGF opts fs = link opts . snd =<< batchCompile opts fs
|
|
|
|
-- | Link a grammar into a 'PGF' that can be used to 'PGF.linearize' and
|
|
-- 'PGF.parse' with the "PGF" run-time system.
|
|
link :: Options -> (ModuleName,Grammar) -> IOE PGF
|
|
link opts (cnc,gr) =
|
|
putPointE Normal opts "linking ... " $ do
|
|
let abs = srcAbsName gr cnc
|
|
pgf <- mkCanon2pgf opts gr abs
|
|
probs <- liftIO (maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf)
|
|
when (verbAtLeast opts Normal) $ putStrE "OK"
|
|
return $ setProbabilities probs
|
|
$ if flag optOptimizePGF opts then optimizePGF pgf else pgf
|
|
|
|
-- | Returns the name of the abstract syntax corresponding to the named concrete syntax
|
|
srcAbsName gr cnc = err (const cnc) id $ abstractOfConcrete gr cnc
|
|
|
|
-- | Compile the given grammar files and everything they depend on.
|
|
-- Compiled modules are stored in @.gfo@ files (unless the @-tags@ option is
|
|
-- used, in which case tags files are produced instead).
|
|
-- Existing @.gfo@ files are reused if they are up-to-date
|
|
-- (unless the option @-src@ aka @-force-recomp@ is used).
|
|
batchCompile :: Options -> [FilePath] -> IOE (UTCTime,(ModuleName,Grammar))
|
|
batchCompile opts files = do
|
|
(gr,menv) <- foldM (compileModule opts) emptyCompileEnv files
|
|
let cnc = moduleNameS (justModuleName (last files))
|
|
t = maximum . map fst $ Map.elems menv
|
|
return (t,(cnc,gr))
|
|
{-
|
|
-- to compile a set of modules, e.g. an old GF or a .cf file
|
|
compileSourceGrammar :: Options -> Grammar -> IOE Grammar
|
|
compileSourceGrammar opts gr = do
|
|
cwd <- getCurrentDirectory
|
|
(_,gr',_) <- foldM (\env -> compileSourceModule opts cwd env Nothing)
|
|
emptyCompileEnv
|
|
(modules gr)
|
|
return gr'
|
|
-}
|
|
|
|
-- | compile with one module as starting point
|
|
-- command-line options override options (marked by --#) in the file
|
|
-- As for path: if it is read from file, the file path is prepended to each name.
|
|
-- If from command line, it is used as it is.
|
|
|
|
compileModule :: Options -- ^ Options from program command line and shell command.
|
|
-> CompileEnv -> FilePath -> IOE CompileEnv
|
|
compileModule opts1 env@(_,rfs) file =
|
|
do file <- getRealFile file
|
|
opts0 <- getOptionsFromFile file
|
|
let curr_dir = dropFileName file
|
|
lib_dirs <- getLibraryDirectory (addOptions opts0 opts1)
|
|
let opts = addOptions (fixRelativeLibPaths curr_dir lib_dirs opts0) opts1
|
|
-- putIfVerb opts $ "curr_dir:" +++ show curr_dir ----
|
|
-- putIfVerb opts $ "lib_dir:" +++ show lib_dirs ----
|
|
ps0 <- extendPathEnv opts
|
|
let ps = nub (curr_dir : ps0)
|
|
-- putIfVerb opts $ "options from file: " ++ show opts0
|
|
-- putIfVerb opts $ "augmented options: " ++ show opts
|
|
putIfVerb opts $ "module search path:" +++ show ps ----
|
|
files <- getAllFiles opts ps rfs file
|
|
putIfVerb opts $ "files to read:" +++ show files ----
|
|
let names = map justModuleName files
|
|
putIfVerb opts $ "modules to include:" +++ show names ----
|
|
foldM (compileOne' opts) env files
|
|
where
|
|
getRealFile file = do
|
|
exists <- doesFileExist file
|
|
if exists
|
|
then return file
|
|
else if isRelative file
|
|
then do
|
|
lib_dirs <- getLibraryDirectory opts1
|
|
let candidates = [ lib_dir </> file | lib_dir <- lib_dirs ]
|
|
putIfVerb opts1 (render ("looking for: " $$ nest 2 candidates))
|
|
file1s <- filterM doesFileExist candidates
|
|
case length file1s of
|
|
0 -> raise (render ("Unable to find: " $$ nest 2 candidates))
|
|
1 -> do return $ head file1s
|
|
_ -> do putIfVerb opts1 ("matched multiple candidates: " +++ show file1s)
|
|
return $ head file1s
|
|
else raise (render ("File" <+> file <+> "does not exist"))
|
|
|
|
compileOne' :: Options -> CompileEnv -> FullPath -> IOE CompileEnv
|
|
compileOne' opts env@(gr,_) = extendCompileEnv env <=< compileOne opts gr
|
|
|
|
-- auxiliaries
|
|
|
|
-- | The environment
|
|
type CompileEnv = (Grammar,ModEnv)
|
|
|
|
emptyCompileEnv :: CompileEnv
|
|
emptyCompileEnv = (emptyGrammar,Map.empty)
|
|
|
|
extendCompileEnv (gr,menv) (mfile,mo) =
|
|
do menv2 <- case mfile of
|
|
Just file ->
|
|
do let (mod,imps) = importsOfModule mo
|
|
t <- getModificationTime file
|
|
return $ Map.insert mod (t,imps) menv
|
|
_ -> return menv
|
|
return (prependModule gr mo,menv2)
|