Static-file templating engine.
Installation
The arcana compiler can be downloaded with git
and installed with cargo
.
# download a copy of the repository
git clone "https://github.com/frankiebaffa/arcana"
cd arcana
# install the arcc compiler
cargo install --path compiler
arcc -h
Glossary
alias: A reference to a value within the current context (i.e.
value.is.here
).
chain: A hyphen (-
) character following the closure of tags such as
if, for, and their respective else. Tells the parser to ignore whitespace until
the next block opening.
content: Characters included in the output of the parser.
context: A map of values.
pathlike: A literal path (i.e. "path/to/file.txt"
) or an alias to a
path in the current context (i.e. context.path.to.file
).
sealed: A context scope whose modifications will not propagate to a higher level.
stringlike: A string, number, or boolean in the current context.
Tags
Expression tags control the flow of the document and typically conform to the following pattern:
<EXP>{\s*<PATHORALIAS>\s*[|<MOD>[\s*<MODARGS>]*]*\s*}
Comment
#{This is a comment.}#
Comments are ignored by the parser. If a comment is followed by a trailing line-break, it will also be ignored. Comments are closed with a special endblock so they can span multiple lines and contain the templating syntax without prematurely closing.
#{ Uncomment in production
${alias}
}#
Ignore
!{This file is ignored.}!
Disregards anything following the tag. The inner content of this block is ignored, but is useful for placing the reason why the file is ignored. Ignores are closed with a special endblock so they can span multiple lines and contain the templating syntax without prematurely closing.
!{ File is not yet ready.
@{file in files}{
&{file.path}{
={$root}<{file.content}
}
}
}!
Extend-Template
+{pathlike}
A template to parse using the final context of the current file. The output
content of the current file will be set to the special alias $content
.
Source-File
.{pathlike}
A context file to include in the current context. Matching values will be overwritten.
Modifiers
As
.{pathlike|as obj}
The as
modifier can be used to specify an alias at which to place the values
sourced from the specified context file.
Consider the context file context.json
:
{
"name": "Jane Doe",
"age": 42
}
And the template file template.arcana
:
.{"context.json"|as person}
${person.name}: ${person.age}
When compiled, the template file would yield the result:
Jane Doe: 42
Include-File
&{pathlike}
A file to parse and include in the position of the tag. Any changes to the context while parsing the file are sealed.
An optional block can be included which will allow for modifications to the
sealed context prior to parsing the given file. Any output of this block will
also be included in the sealed context with the special $content
alias.
&{pathlike}{
={alias-1}{This is a property.}
={alias-2}{This is another.}
}
Modifiers
Raw
&{pathlike|raw}
Does not parse the content, only includes directly. Can be used in conjunction with the markdown modifier.
Markdown
&{pathlike|md}
Parses the output of the include as No-Flavor Markdown. Can be used in conjunction with the raw modifier.
If
%{alias}-
{${alias}}-
{Alias does not exist.}
Evaluates the output of the condition as true or false. If true, the first
trailing block is parsed. Else, the second trailing block is parsed. The default
condition of the if tag is exists. The initial if tag as well as the
true condition tag can be optionally trailed by a chain as shown above. The
condition can be preceeded by the not operator (!
) to negate the evaluated
condition.
Exists
%{alias exists}{${alias}}{Alias does not exist.}
Evaluates to true if the given alias exists in the current context.
Empty
%{!alias empty}{${alias}}{Alias was empty.}
Evaluates to true if the given alias has an empty value in the current context.
For-Each-Item
@{item in alias}{
${item.name}
}{
No items.
}
Loop through contents of an array in context. The inner context of the loop is sealed. The first trailing block will be parsed for every item found within the array. If there are no items, the second block will be parsed. The for-each-item tag can be trailed by a chain.
Loop Context
For loops initialize a special alias into the sealed context named $loop
.
This object's values are mapped to the following aliases.
$loop.index: The 0-indexed position of the current iteration.
$loop.position: The 1-indexed position of the current iteration.
$loop.length: The length of the array being iterated over.
$loop.max: The maximum index of the array begin iterated over.
$loop.first: Set iff the current value of $loop.index
is 0.
$loop.last: Set iff the current value of $loop.index
is $loop.max
.
Modifiers
Paths
Treat the values found within the array as paths to files.
@{dir in alias|paths}{
*{file in dir|ext "json"}{
.{file|as subobj}
${subobj.description}
}{
No files in directory.
}
}{
No directories in alias.
}
Reverse
Reverse the order of the array.
@{item in alias|reverse}-
{%{ !$loop.first }{
}${ item }}-
{No items.}
For-Each-File
*{file in pathlike}{
&{item}
}{
No files in directory.
}
Loop through the files found within a given directory. The inner context of the loop is sealed. The trailing blocks function identically to the for-each-item tag.
Loop Context
The same loop context is set for this tag as for for-each-item.
Modifiers
With-Extension
*{file in pathlike|ext "arcana"}{
&{file}{
={$loop}<{loop}
}
}
Only include files with the matching extension.
Reverse
*{file in pathlike|ext "arcana"|reverse}{
&{file}
}{
No files found in directory.
}
Reverse the order of the files.
Include-Content
${alias.to.stringlike}
Includes the stringlike value of the alias from the current context in the content.
Modifiers
Lower
${alias|lower}
Changes the content to lowercase.
Replace
${alias|replace "x" "y"}
Replaces instances of x
with y
.
Upper
${alias|upper}
Changes the content to uppercase.
Path
${ alias | path }
Handles the content as a path.
Filename
${ alias | path | filename }
Outputs only the filename of the path.
Trim
${alias|trim}
Removes whitespace from the start and end of the content.
Split
${alias|split 2 0}
Splits the content into 2
parts and uses the part at index 0
.
Consider the context file context.json
:
{
"name": "Jane Doe"
}
And the template file template.arcana
:
.{"context.json"}
${name|split 2 1}
The output of the parser would be:
Doe
Set-Item
={alias}{Here is the value}
Sets the value at alias within the current context to the content of the block. This can also be used with other items in the context.
={alias}{${item.name}}
Modifiers
Array
Initializes an array at alias if it is not an already an array and pushes the given value into it.
={alias|array}{First item}
This modifier can also be chained together using chains or inline.
={alias|array}{First item}{Second item}-
{Third item}{Fourth item}
Path
Sets alias to a pathlike built from the output of the block.
={alias|path}{path/to/file.txt}
Siphon-Item
={alias1}<{alias2}
Sets alias1 to the literal json value found at alias2. This is useful when
an entire object needs to be set at a different alias while including another
template. The special $root
alias can be used in the first position iff
alias2 is a map and all of the keys are wished to be moved to the root of the
current context.
The following example utilizes the siphon-item tag as well as the $root
alias. If the file album.arcana
is used both from another template as well as
within a standalone template, it may only reference the properties within
album
at their root alias, i.e. name
, instead of accessing them from a
higher scoped alias such as album.name
.
@{album in artist.albums}{
&{"album.arcana"}{
={$root}<{album}
}
}
Unset Item
/{alias}
Unsets the value at alias.
Modifiers
Pop
Pops the last value off of the array.
/{alias|pop}