Shuffle Documentation
Ehc
1 Introduction and tutorial
Shuffle takes files with chunks of text defined in shuffle notation, and filters these chunks according to a requested selection of variant and aspects. This allows for the following typical use:
- Transform a chunked source file to a compilation unit (for ghc, C, etc). One file then is transformed one-to-one to the corresponding output without inclusion of chunks from other files. All variants and aspects are defined in the same file. The convention is to let a chunked file producing a file with suffix .X have the suffix .cX
- Transform a set of chunked source files to a single file for further processing. This is typically done for LaTeX files where various topics are spread over multiple files. On the commandline the set of files is specified, the first file being the root, other chunks are referred to by a naming mechanism.
1.1 Basics
A minimal input to shuffle looks like the following:
%%[1
some text
%%]
The two percent symbols
%% at the beginning of a line start shuffle commands.
%%[1 .. %%] delimits a chunk for variant 1. With this content placed in file
doc-ex1, the following invocation of shuffle produces the content of the chunk:
shuffle --gen-reqm=1 --variant-order=1 --plain --lhs2tex=no doc-ex1
With
--gen-reqm the required variant is specified, where variants are ordered according to the ordering passed via
--variant-order (here just a single variant),
--plain means that no further interpretation of chunks should be done, and
--lhs2tex=no that no chunk delimiting for lhs2tex should be done. Although shuffle is invoked by
shuffle in the above, shuffle is not installed globally as part of the ehc install. It can be found in
EHC/bin, where
EHC is the ehc directory. Text outside chunks is considered shuffle comment and not copied to output. If there are no lines between chunks then no lines are generated; if there are >= 1 lines between chunks one line of space is printed between generated individual chunks. The original order of chunks as it appears in the chunked source file is maintained. None of the commandline parameters currently can be omitted; in future versions of shuffle it is likely that default values will be assumed.
A variant order allows for a hierarchical ordering of variants, where later variants build on top of earlier variants, possibly overriding earlier definitions:
%%[1
some text
%%]
%%[2
more text
%%]
With the following commandline we get both text fragments, where variant 2 builds on top of variant 1.
shuffle --gen-reqm=2 --variant-order="1 < 2" --plain --lhs2tex=no doc-ex1
Variant 1 can still be extracted by
--gen-reqm=1.
Chunks can be explicitly overridden, say a new variant 3 wants to replace the text for variant 1. We then have to give a name to the overridden chunk so we can refer to it when overriding the chunk:
%%[1.sometext
some text
%%]
%%[2
more text
%%]
%%[3 -1.sometext
other text
%%]
With the following commandline:
shuffle --gen-reqm=3 --variant-order="1 < 2 < 3" --plain --lhs2tex=no doc-ex1
this will give:
more text
other text
Again, previous variants can still be retrieved via the
--gen-reqm option.
1.2 Output specific configuration
The output of shuffle can be tailored to more specific targets, for example haskell source code. Chunks then contain haskell source text with optional meta information about name of the module, the imports, and the exports:
%%[1 module Some import(SomeImport)
%%]
%%[1 export(someFunction)
someFunction :: Int -> Int
someFunction x = x
%%]
On the commandline we then need to specify that we want Haskell source text to be generated; the
--hs option is used for that purpose:
shuffle --gen-reqm=1 --variant-order="1" --hs --lhs2tex=no doc-ex1
The corresponding output then is:
module Some
( someFunction )
where
import SomeImport
someFunction :: Int -> Int
someFunction x = x
Similarly we can specifically can generate for the AG system. The input then looks like the following:
%%[1 ag module Some import(SomeAGImport)
%%]
%%[1 hs import(SomeHaskellImport)
%%]
%%[1 ag
DATA SomeData
| SomeAlt1
ATTR SomeData [ | | someAttr: Int ]
SEM SomeData
| SomeAlt1 lhs.someAttr = 1
%%]
%%[1 hs export(someFunction)
someFunction :: Int -> Int
someFunction x = x
%%]
Because the AG system process both AG notation and escaped+copied Haskell code, it is necessary to tag each chunk with its source code type. It then ends up in the appropriate part.
We now get wiith the following commandline:
shuffle --gen-reqm=1 --variant-order="1" --ag --lhs2tex=no doc-ex1
we now get:
{
module doc-ex1
( someFunction )
where
import SomeHaskellImport
}
INCLUDE "SomeAGImport.ag"
{
}
DATA SomeData
| SomeAlt1
ATTR SomeData [ | | someAttr: Int ]
SEM SomeData
| SomeAlt1 lhs.someAttr = 1
{
someFunction :: Int -> Int
someFunction x = x
}
1.3 Reusing and including chunks in other files
Chunks can be included at arbitrary places by referring to them by name. For example, given the content of the files
doc1 and
doc2.
In
doc2 the content of the chunk is defined.
%%[1.someText2
some text2
%%]
In
doc1 we refer to the chunk in
doc2 by
%%@doc2.1.someText2:
%%[1
some text
%%@doc2.1.someText2
%%]
The reference must start at the beginning of the line and follows the syntax
file.variant.name.
The corresponding output then is:
some text
some text2
when shuffle is invoked with:
shuffle --gen-reqm=1 --variant-order=1 --plain --lhs2tex=no doc1 doc2
The first file given to
shuffle acts as the root file. The remaining files (and the root file) act as a repository of chunks, for which output is only generated when included within the root file.
2 Variants and Aspects
A chunk contains content to be used for specific variants and aspects. A variant is denoted by a number, an aspect by an alphanumeric identifier. Variants and aspects are used and specified independently and represent the two dimensions over which variation can be specified. Variants and aspects though are used in a different way and with a different purpose. Variants are used to describe stepwise increments thus forming a hierarchy. Aspects are used to isolate such groups of variants. A variant is to be used for adding language features; an aspect is to be used for a global property valid for all language features. For example, in EHC, codegeneration and the type system are considered seperate aspects.
Variants and aspects are offered by chunks. When shuffle is invoked a specific combination of variants and aspects is required/requested. For example, the following respectively specifies a general chunk, 2 chunks for 2 different aspects, and a chunk to be used when both aspects are requested for:
%%[1
some general text
%%]
%%[(2 asp1)
some asp1 text
%%]
%%[(1 asp2)
some asp2 text
%%]
%%[(1 asp1 asp2)
some asp1 && asp2 text
%%]
2.1 Specifying required chunks
When we ask for all aspects with:
shuffle --gen-reqm="(2 asp1 asp2)" --variant-order="1 < 2" --plain --lhs2tex=no doc1
we indeed get the content for all aspects:
some general text
some asp1 text
some asp2 text
some asp1 && asp2 text
Via option
--gen-reqm="(2 asp1 asp2)" we specified the required variant and aspects. The syntax of the required variant and aspects is
variant | '(' variant aspect* ')', where the absence of aspects means all aspects. Variant and aspects are defined independently and have the effect that only chunks offering both variant and all required aspects are generated on output. For example, the following shuffle invocation only produces the general text:
shuffle --gen-reqm="(1 asp1)" --variant-order="1 < 2" --plain --lhs2tex=no doc1
The invocation
shuffle --gen-reqm="(2 asp2)" --variant-order="1 < 2" --plain --lhs2tex=no doc1
produces both the general text and asp2 only text:
some general text
some asp2 text
2.2 Specifying offered chunks
The specification of chunk offerings follows a richer language for specifying aspects, including conjunction
&& and disjunction
||. The specification
(1 asp1 asp2) of the example is a shorthand notation for
(1 asp1 && asp2), which means that the chunk only is generated when both aspects are asked for simultaneously. When disjunction
|| is used, as in
(1 asp1 || asp2), only at least one of the aspects must be asked for. The syntax of such an 'offering' specification then is
variant | '(' variant aspectexpr? ')', where the absence of the aspect expression means 'any aspect'. An aspect expression follows the usual notation and priority rules as used in (say) Haskell.
3 Chunks
3.1 Overriding and alternatives
Chunks can be introduced, overridden completely or partially. Chunk introduction and overriding has been discussed, for example a chunk is introduced by:
%%[1.sometext
some text
%%]
and is overridden completely in variant 2 by:
%%[2 -1.sometext
some text
%%]
Completely overriding a chunk can be too much if only a part of a chunk has been modified. There are three ways to deal with this, using already introduced notation and notation for conditional nested chunks (for the third way). First, a large chunk can be split into smaller, so we have finer grain control for overriding:
%%[1
some part 1
%%]
%%[1.somepart2
some part 2
%%]
%%[1
some part 3
%%]
If we want to override
some part 2 we can now do this as usual:
%%[2 -1.somepart2
some part 2 new
%%]
However, the partitioned chunk now has a lot of chunk related clutter. A second solution is to name all parts, include those in a new overall chunk and override the latter:
%%[somepart1
some part 1
%%]
%%[somepart2
some part 2
%%]
%%[somepart3
some part 3
%%]
%%[1.all
%%@somepart1
%%@somepart2
%%@somepart3
%%]
Chunks without a variant number are never included automatically but only when referred to by name from another chunk which is included. We override by:
%%[somepart2new
some part 2 new
%%]
%%[2 -1.all
%%@somepart1
%%@somepart2new
%%@somepart3
%%]
Again, a lot of clutter, so usually the best solution is to inline the choice using conditional nested groups of chunks. The original text structure and ordering is then maintained, although we still require shuffle notation:
%%[1
some part 1
%%[[1
some part 2
%%][2
some part 2 new
%%]]
some part 3
%%]
A nested chunk is denoted by
%%... %%? instead of
%%[ ... %%]. Conditional nested groups of chunks are denoted by
%% ... %%?
A nested chunk always is for a particular variant; a name without a variant is not allowed.
We can also use this mechanism to specify defaults for aspects:
%%[1
some part 1
%%[[1
some part 2
%%][(2 asp)
some part 2 new asp
%%][2
some part 2 new
%%]]
some part 3
%%]
If for variant 2 aspect
asp is requested the text
some part 2 new asp is generated, otherwise the default
some part 2 new for variant 2. If more than one nested chunk can be chosen from,
shuffle chooses arbitrarily.
3.2 Metadata: type, wrapping, module + import/export, ..
A chunk may have data associated other than textual content. The use of this information depends on the context it is used in. For example, when asking for AG output (option
--ag), the type of a chunk influences how its content ends up in the generated output:
%%[1 hs
haskell
%%]
%%[1 ag
AG
%%]
The type
ag specifies that the chunk content is AG text,
hs specifies it is Haskell. However, when asking for Haskell output (option
--hs) this difference is ignored. The metadata defined for a chunk textually starts right after the
%%[ and ends at the end of the line. See the
syntax diagram for chunks below for the exact placement and values of metadata:
-
chunktype, as discussed above. Additionally haddock comment (wrapped in {-| ... -} can be included. However, when AG is generated its position in the output is not deterministic as the AG compiler also shuffles around with Haskell code.
-
chunkref, as discussed earlier, used to override a particular chunk or include it.
-
chunkwrap, additional wrapping of a chunk, currently only used for output latex. code wraps as a lhs2tex code block, safecode puts this in a parbox as well (used within beamer), verbatim wraps inside a verbatim environment, normal size or small, tt is similar to verbatim but will become obsolete.
-
module, adds a Haskell for type hs module definition when Haskell or AG is generated.
-
import and export, take import/export lists and generate according to Haskell or AG and type hs or ag.
Inside and entity or module name special syntax allows the use of key/value pairs. Key/value pairs are defined on the commandline by option
--def=key:value and the value is used by referring to the key by
%{key}. In EHC this is used for externally specifying the toplevel module name of the library constructed for a particular variant. Overlapping Haskell namespaces are thus avoided and libraries for different variants can be used simultaneously. Also, grouping of name elements is done by
{ ... }, guaranteeing whitespace free output generation for its content.
Syntax for chunk definitions:
<chunk> ::= '%%[' (<chunkvariantdef> | <chunknameddef>) <chunkcontent> '%%]'
<chunkvariantdef> ::= <variantoffer>
('.' <nm>)?
('-' (<chunkref> | '(' <chunkref>* ')'))?
<chunkoptions>
<module>? <imports>? <exports>?
<chunknameddef> ::= <nm>
<chunkcontent> ::= <textline>*
<variantoffer> ::= <variantnr> | '(' <variantnr> <aspectexpr>? ')'
<variantnr> ::= <int>
<aspectexpr> ::= <aspectand1> ('||' <aspectand1>)*
<aspectand1> ::= <aspectand2> ('&&' <aspectand2>)*
<aspectand2> ::= <aspect>+
<aspect> ::= <ident>
<chunkref> ::= (<fileref> '.')? (<variantnr> '.')? <nm>
<nm> ::= <ident> ('.' <ident>)*
<chunkoptions> ::= (<chunkwrap> | <chunktype>)*
<chunkwrap> ::= 'wrap' '=' ( 'code' | 'safecode'
| 'tt' | 'tttiny'
| 'verbatim' | 'verbatimsmall'
| 'beamerblockcode' <str1>
| 'boxcode' ('{' <frac> '}')?
)
<chunktype> ::= 'hs' | 'ag' | 'haddock' | 'plain'
<module> ::= 'module' <str2>
<imports> ::= 'import' '(' <entities> ')'
<exports> ::= 'export' '(' <entities> ')'
<entities> ::= <entity> (',' <entity>)*
<entity> ::= <str2>
<str2> ::= <str1> | <ident> | <int> | '{' <str2>+ '}' | '%' '{' <ident> '}"
<str1> ::= '"' <non-">* '"'
<frac> ::= <int> ('.' <int>)?
3.3 Content
Content of a chunk consists of a mixture of plain text and shuffle
interpreted parts. See the [[#LabeLGrammarChunkcontent][syntax diagram for
chunk content]] below. Each piece of content either is a line not
starting with
%%@ or
%%[[ or a reference to another chunk
or a nested conditional group. A plain line consists of characters. The
only escape to shuffle not starting at the beginning of a line consists
of
%%@{ ... %%} and
%%@[ ... %%]:
Syntax for chunk content:
<chunkcontent> ::= <content>*
<content> ::= <textcontent>
| '%%@' <chunkref> ('@' <variantreqm>)? <chunkoptions>
| '%%[[' <variantoffer> <chunkoptions> <chunkcontent>
('%%][' <variantoffer> <chunkoptions> <chunkcontent>)*
'%%]]'
<textcontent> ::= <char>*
| '%%@{' <str2> '%%}'
| '%%@[' <inlinetype> ':' <char>* '%%]'
<inlinetype > ::= 'file' | 'exec'
<variantreqm> ::= <variantnr> | '(' <variantnr> <aspect>? ')'
3.4 References
4 Output
4.1 Haskell
4.2 AG
4.3 LaTeX / Lhs2TeX
4.4 Plain
5 Makefile generation
Dependencies between AG files are explicitly encoded in makefiles. These dependencies can be
generated from the AG imports in a chunked AG file using
shuffle. We demonstrate this
by means of an example from the
ruler project.
There are typically two ways to compile an AG file: as a module with semantic functions, or
as a module with data type definitions. In order to compile the AG files, the makefile
infrastructure requires the following lists:
RULER3_AG_D_MAIN_SRC_AG | AG modules with data type definitions |
RULER3_AG_D_DPDS_SRC_AG | Dependencies of the above AG modules |
RULER3_AG_S_MAIN_SRC_AG | AG modules with semantic functions |
RULER3_AG_S_DPDS_SRC_AG | Dependencies of the above AG modules |
Aside from these lists, there needs to be a rule for each AG source file and its dependencies,
to the derived AG module:
RULER3_EXPR_EXPR_MAIN_SRC_AG := $(patsubst %, \
$(SRC_RULER3_PREFIX)%.cag, Expr/Expr)
RULER3_EXPR_EXPR_DPDS_SRC_AG := $(patsubst %, \
$(RULER3_BLD_PREFIX)%.ag, Expr/AbsSynAG)
$(patsubst $(SRC_RULER3_PREFIX)%.ag,$(RULER3_BLD_PREFIX)%.hs, \
$(RULER3_EXPR_EXPR_MAIN_SRC_AG)) : $(RULER3_EXPR_EXPR_DPDS_SRC_AG)
These lists and rules are generated by
shuffle on a file with paths to AG modules, using the
--dep option and several
parameters to choose names for these makefile variables. For example, the file
files-ag-d.dep contains the following
lines:
Expr/Expr.cag
Ty/Ty.cag
!AbsSyn/AbsSyn1.cag
The invocation of
shuffle:
shuffle files-ag-d.dep --dep \
--depnameprefix=RULER3_ \
--depsrcvar=SRC_RULER3_PREFIX \
--depdstvar=RULER3_BLD_PREFIX \
--depmainvar=RULER3_AG_D_MAIN_SRC_AG \
--depdpdsvar=RULER3_AG_D_DPDS_SRC_AG \
> files-ag-d-dep.mk
results in a makefile containing the required rules and the lists
RULER3_AG_D_MAIN_SRC_AG and
RULER3_AG_D_DPDS_SRC_AG.
Note that
shuffle --dep takes only and all chunk-imports tagged with an
ag-kind into consideration.
6 Shuffle commandline invocation
6.1 Commandline usage
As printed by
shuffle --help:
Usage shuffle [options] [file ([alias=]file)*|-]
options:
-a --ag generate code for ag, default=no
-h --hs generate code for haskell, default=no
-l --latex generate code for latex, default=no
--preamble[=yes|no] include preamble (marked by version=0), default=yes
--line[=yes|no] insert #LINE pragmas, default=no
-p --plain generate plain code, default=no
--text2text generate code for haskell, default=no
--index combined with latex, generate index entries, default=no
--gen=all|<nr>|(<nr> <aspect>*) (to be obsolete, renamed to --gen-reqm) generate for version, default=none
-g all|<nr>|(<nr> <aspect>*) --gen-reqm=all|<nr>|(<nr> <aspect>*) generate for version, default=none
--compiler=<compiler version> Version of the GHC compiler, i.e. 6.6
--hidedest=here|appx=<file> destination of text marked as 'hide', default=here
--order=<order-spec> (to be obsolete, renamed to --variant-order) variant order
--variant-order=<order-spec> variant order
-b <name> --base=<name> base name, default=derived from filename
--xref-except=<filename> file with list of strings not to be cross ref'd
--help output this help
--dep output dependencies
--depnameprefix[=<name>] Prefix of generated makefile vars.
--depsrcvar[=<name>] Source base-directory
--depdstvar[=<name>] Destination base-directory
--depmainvar[=<name>] Varname for the list of main files
--depdpdsvar[=<name>] Varname for the list of dependencies
--deporigdpdsvar[=<name>] Varname for the list of original dependencies
--depderivdpdsvar[=<name>] Varname for the list of derived dependencies
--depbase[=<dir>] Root directory for the dependency generation
--depign[=(<file> )*] Totally ignored dependencies
--depterm[=(<file> => <dep>+ ,)*] Dependency ignore list (or terminals)
--lhs2tex[=yes|no] wrap chunks in lhs2tex's code environment, default=yes
--agmodheader[=yes|no] generate AG MODULE headers instead of Haskell module headers
--def=key:value define key/value pair, alternate form: key=value
6.2 Commandline options
-
--gen-reqm=variant | '(' variant aspect* ')'
-
--plain
-
--ag
-
--hs
-
--lhs2tex=no|yes