-
Notifications
You must be signed in to change notification settings - Fork 11
Description
I have tought a bit about using template haskell with uuagc.
Initial working version
First we could use a file quasi quoter (made with the quoteFile function) to load and compile an ag file.
Options can be passed to this quasi quoter by defining a specific variable (say agOptions) as, for example, a certain string constant. This variable can be looked up by the quasi quoter and the string constant can be read.
Edit: an annoyance is that due to the stage restriction you cannot lookup variables that are declared in the module itself, so you need to put this configuration variable in a separate module.
Edit2: Maybe we can avoid the stage restriction by using addModFinalizer in combination with addTopDecls. I wonder if those work together.
Edit3: No, lookupValueName works in addModFinalizer, but reify doesn't. Instead maybe we could just read a file with an IO action, maybe even the module itself (we can get the file name with loc_filename <$> location) and store the options in a comment?
Edit4: We can work around the whole problem by using plain TH functions instead of quasi quoters:
-- TH.hs
module TH where
import Language.Haskell.TH
useOption :: String -> Q [Dec]
useOption x = return [ValD (VarP (mkName "result")) (NormalB (LitE (StringL x))) []]-- Main.hs
{-# LANGUAGE TemplateHaskell #-}
import TH
useOption "semfuns"
main :: IO ()
main = print resultWhat is also required is a way to convert an execution plan to template haskell.
It should be pretty straightforward, copy over ExecutionPlan2Hs.ag to ExecutionPlan2TH.ag and rewrite all the functions to generate template haskell declarations.
Edit: I just noticed that there is ExecutionPlan2Hs.ag and PrintCode.ag which both function as the final step to Haskell code. The execution plan is used when loag or kennedy warren is enabled, and the code is used otherwise. I started with rewriting the execution plan printer, but now I realize I should probably start with the code printer.
A difficult part will probably be the conversion of embedded haskell snippets to template haskell data types. The haskell-src-meta package is probably our best bet.
The suggested plan up to this point seems to me like it should be enough to build single file grammars, but I don't think it will be able to handle grammars that include other grammars.
A more modular solution
To go even further we could investigate using the Haskell module system instead of the ad-hoc #include system to make the attribute grammars more modular (and possibly allow importing attribute grammars across Haskell packages.
The idea is to save generated attribute grammar information in special deterministically named variables that are unique for each module and uuagc version perhaps. Any quasi quoter can lookup this variable which could simply store all necessary information in one big string literal, but perhaps we can do better.
To get such unique names we could use a hashing function of the module name and uuagc name and version. That can then be base64 encoded with the characters A-Z, a-z, 0-9, and _ and ' which are legal characters in variable names. Using a good hash function should ensure that no person can accidentally use the same variable name while still ensuring that this name can be generated by quasi quoters in other modules.
Question: how to make sure that that unique variable is always exported? Answer: We could make a special template haskell splice that takes a list of imports as input and adds the special variable to that list. It is not possible to generate an export list (or even an import list) with Template Haskell.
This is actually a pretty important point. We can just write these imports in the Haskell module that contains the template haskell expression to generate the attribute grammar code. But if we want to import semantic functions from other modules then we need to make sure that all the Haskell code in those semantic modules has the right imports. A solution might be to bind each Haskell code block to a Haskell variable which uses imports from the module that it is defined in. Those variables can then be exported and imported in other modules and the names can be transferred via the type literal trick as described in the comment below. I think we can require users to use implicit exports so that everything is automatically exported.
Extras
If this system if successfully implemented then it might also make sense to implement template haskell functions that can automatically derive attribute grammar nonterminals from existing data types. That would make it much easier to integrate attribute grammars into existing projects.
Update: I just found out that this would be similar to the Haskell2AG tool that is in the tools directory.
Edit: The whole passing of information through constant variables cannot be done, see the note here:
The Maybe Dec field contains Just the declaration which defined the variable - including the RHS of the declaration - or else Nothing, in the case where the RHS is unavailable to the compiler. At present, this value is always Nothing: returning the RHS has not yet been implemented because of lack of interest.
That means we cannot access the constant value even if we know the name.
Maybe an easy workaround is to use type-level string literals and store the information in the types instead of in the RHS.