## Installation The _arcana compiler_ can be downloaded with `git` and installed with `cargo`. ```bash # 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 ``` See the [help](https://raw.githubusercontent.com/frankiebaffa/arcana/master/compiler/resources/help.txt) document for the usage of the compiler. ## 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_. ## Whitespace Control ```arcana This is an example \ of whitespace control. ``` Arcana has one mode of whitespace control, a single backslash ending a line. The parser will ignore the backslash and consume all following whitespace without dumping to _content_. The above example would compile to the following. ```arcana This is an example of whitespace control. ``` ## Tags Expression tags can control the flow of the document, spawn other parsers, and can write content to other files. ### Comment ```arcana #{This is a comment.}# ``` Comments are ignored by the parser. Comments are closed with a special endblock so they can span multiple lines and contain the templating syntax without prematurely closing. ```arcana #{ Uncomment in production ${alias} }# ``` ### Extend-Template ```arcana +{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 ```arcana .{pathlike} ``` A _context_ file to include in the current _context_. Matching values will be overwritten. #### Modifiers ##### As ```arcana .{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`: ```json { "name": "Jane Doe", "age": 42 } ``` And the _template_ file `template.arcana`: ```arcana .{"context.json"|as person} ${person.name}: ${person.age} ``` When compiled, the _template_ file would yield the result: ```txt Jane Doe: 42 ``` ### Include-File ```arcana &{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_. Consider the following file `link.arcana`. ```arcana ${$content} ``` It can be included in another template like so: ```arcana

Click &{"./link.arcana"}(\ ={}({ "href": "https://github.com", "cls": "a-link" })\ Here\ )

``` Or without the JSON literal block: ```arcana

Click &{"./link.arcana"}(\ ={href}("https://github.com")\ ={cls}("a-link")\ Here\ )

``` Either of these templates would compile to the following: ```arcana

Click Here

``` #### Modifiers ##### Raw ```arcana &{pathlike|raw} ``` Does not parse the content, only includes directly. Can be used in conjunction with the _markdown_ modifier. ##### Markdown ```arcana &{pathlike|md} ``` Parses the output of the include as [No-Flavor Markdown](http://frankiebaffa.com/software/nfm.html). Can be used in conjunction with the _raw_ modifier. ### If ```arcana %{alias}- (${alias})- (Alias is falsey.) ``` 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 _truthy_. 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 ```arcana %{alias exists}(${alias})(Alias does not exist.) ``` Evaluates to true if the given _alias_ exists in the current _context_. #### Empty ```arcana %{!alias empty}{${alias}}{Alias was empty.} ``` Evaluates to true if the given _alias_ has an empty value in the current _context_. #### Truthy ```arcana %{this.alias}(Alias was true.)(Alias was false.) ``` Evaluates to true if the given _alias_ is truthy. A defined string will always be true. A number will be true when greater than 0. An array will always be true. An object will be evaluated based on whether or not it contains any keys. Null will always evaluate to false. #### Comparisons ```arcana %{this.alias==that.alias}() %{this.alias>that.alias}() %{this.alias>=that.alias}() %{this.aliasthat.alias||$loop exists}() ``` Conditions can be chained together using `&&` for `and` and `||` for `or`. ### For-Each-Item ```arcana @{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. ```arcana @{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. ```arcana @{item in alias|reverse}- (%{ !$loop.first }( )${ item })- (No items.) ``` ### For-Each-File ```arcana *{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 ```arcana *{file in "./this/dir"|ext "json"}( .{file|as sub} *{sub-file in sub.files}( \(${$loop.position}\) ${sub-file|filename} ) ) ``` Only include files with the matching extension. ##### Reverse ```arcana *{file in pathlike|ext "arcana"|reverse}( &{file} )( No files found in directory. ) ``` Reverse the order of the files. ### Include-Content ```arcana ${alias.to.stringlike} ``` Includes the _stringlike_ value of the _alias_ from the current _context_ in the _content_. #### Modifiers ##### Lower ```arcana ${alias|lower} ``` Changes the _content_ to lowercase. ##### Replace ```arcana ${alias|replace "x" "y"} ``` Replaces instances of `x` with `y`. ##### Upper ```arcana ${alias|upper} ``` Changes the _content_ to uppercase. ##### Path ```arcana ${ alias | path } ``` Handles the _content_ as a path. ##### Filename ```arcana ${ alias | path | filename } ``` Outputs only the filename of the path. ##### Trim ```arcana ${alias|trim} ``` Removes whitespace from the start and end of the _content_. ##### Split ```arcana ${alias|split 2 0} ``` Splits the _content_ into `2` parts and uses the part at index `0`. Consider the _context_ file `context.json`: ```json { "name": "Jane Doe" } ``` And the _template_ file `template.arcana`: ```arcana .{"context.json"} ${name|split 2 1} ``` The output of the parser would be: ```txt Doe ``` ##### Json ```arcana ={item}("This string here.") ={ctx.item}(${item|json}) ``` Represents the content as raw json. ### Set-Item ```arcana ={alias}("Here is the value") ``` Sets the value at _alias_ within the current _context_ to the parsed JSON output of the block. The block can contain arcana syntax as long as the parsed output is valid JSON. ```arcana ={this-name}(${item.name|json}) ``` Any JSON type can be set using the _set-item_ block. ```arcana ={this-object}({ "key": "value", "items": [ "first", "second", "third" ], "a-number": 54.2 }) ``` The root context can also be written-to by avoiding the inclusion of an alias. ```arcana ={}({ "root-level-item": "Some value." }) ${root-level-item} ``` This comes in handy when an included file references an object from the root context and is accessed from within a loop or another context. Consider the following file `artist.arcana`: ```arcana # ${name} ${brief} @{album in albums}( &{"./album.arcana"}{ ={}(${album|json}) } ) ``` The file considers the `artist` object to be at the root level. So if we wanted to include the file from another context, say `artists.arcana`, we could reference the artist as a root level object: ```arcana @{artist in artists}( &{"./artist.arcana"|md}( ={}(${artist|json}) ) ) ``` ### Unset Item ```arcana /{alias} ``` Unsets the value at _alias_. ## File Operation Tags The following tags perform file operations and are intended for deployment purposes. If you're feeling a bit _nutty_, you can use them in your standard templates however you wish. ### Write-Content ```arcana ^{pathlike}(\ &{"this/file.arcana"}(={title}("A title here"))\ ) ``` Writes to _pathlike_ the _content_ of the block. ### Copy-Path ```arcana ~{source-pathlike destination-pathlike} ``` Copies the file at the source _pathlike_ to the file at the destination _pathlike_. ### Delete-Path ```arcana -{pathlike} ``` Deletes the file at _pathlike_. ### Example Using the file operation tags, you can write your own logging deployment files like the following. ```arcana #{ ./deploy.arcana }#\ ={font-file}("./in/font.ttf")\ ={font-dest}("./out/fonts/font.ttf")\ Copying font "${font-file|path}" to "${font-dest|path}"\ ~{font-file font-dest} Compiling templates...\ *{template in "./in/templates"|files|ext "arcana"}( ={template-dest}("./out/${$loop.entry.stem}.html")\ Compiling "${template|path}" to "${template-dest|path}"\ ^{template-dest}(&{template})\ )\ ``` Running the `arcc ./deploy.arcana` command would output the following while also properly deploying the files. ```txt Compiling font "./in/font.ttf" to "./out/fonts/font.ttf" Compiling templates... Compiling "./in/templates/first.arcana" to "./out/first.html" Compiling "./in/templates/second.arcana" to "./out/second.html" ```