Introduction
When writing documentation, there are generally two choices: good looking source, and good looking output. In spite of their ease-of-use, word-processors are typically unable to produce good-quality typographic output, with poor hyphenation and justification algorithms, lax placement of figures and only rudimentary support for global document styling. On the flip-side, high-quality typesetting programs can be unintuitive, have odd behaviours and use a notation which us unfriendly to the user. Emblem bridges the gap between these two realities.
When many existing programs were created, some particularly useful technologies either did not exist or were in their infancy. Emblem is designed with a more modern approach to the process of typesetting large documents, in particular, it is designed to be extensible, hackable and with support for global styling rules with CSS.
Although significant effort has been expended to make Emblem easy to use, it is expected that the user will have some familiarity with:
- Running programs from the command-line / shell
- Basic knowledge of CSS styling rules
What’s Emblem?
Emblem is an elegant, extensible typesetter with both a friendly interface and good output. It sports:
- A lightweight, uncluttered, unified syntax
- With Markdown syntactic sugar
- Extension support through a simple interface
- User-space extensions allow for the creation of arbitrary document elements
- Driver-extensions allow for output on any device
- Excellent typographic quality output
- Global document styling through either CSS, SCSS or SASS
Usage & Document Format
Suppose we have a simple document called hello.em
---
# Hello, world!
How are you today? Here's a poem I like.
.centre:
Murder Squad, Murder Squad,
Does whatever a murder squad does
Can they swing from a web?
No they can’t ... Murder Squad
Hope _you_ **like** `it`.
This can be compiled into a PDF (the default output driver) with the following command---
em hello
The command-line interface allows for a good amount of customisation of the typesetting run. For example, the above could be modified to apply a different base style, ‘my-article-style,’ outputting to HTML---
em hello -cmy-article-style -Thtml
Remarks
This documentation has sections for all kinds of Emblem users, from document- and extension-writers to Emblem-developers. No user is expected to require all of the information in these docs, hence they are split into separate sections which contain:
- Information on how Emblem works
- Information on how to use Emblem
- Descriptions of the directives available by default
- Information on the CLI, extension and core APIs
- Licensing information
How Emblem Works
In this document, we will discuss the how documents are processed, how documents are styled, extensions and input and output drivers.
At its most abstracted level, Emblem takes input of a document, typesets it and outputs it. It does this by parsing its input looking for directives, structuring-calls calls which sub-divide a document’s source, which can apply both styles to and perform computation upon document content. Emblem contains a standard library of directives to facilitate many common tasks encountered when writing. For example, to create a document with a table of contents and some headings, something akin to the following could be used.
.toc // Constructs the table of contents
.h1: Hello, world!
This is my first document.
If this is in a file called my-first-doc.em
, we can typeset it by executing the following.
em my-first-doc
As one might expect, this creates a table of contents followed by a level-1 header and then a some text.
The .h1
directive is a good example of what directives can do as it applies both the .h1
style (by default is an emboldened and enlarged form of main text) and computes the heading number.
This latter follows the convention of technical documents for headings and sub-headings, so for example the heading numbered 2.5.3 would be the third sub-sub-heading under the fifth sub-heading in the second section of a document.
The order in which elements are computed leads us to the first defining feature of Emblem.
The typesetting Loop
Emblem processes its input from start to finish and whilst this lends itself naturally to some concepts, it is somewhat at-odds with others. A notable example of this latter case is the creation of a table of contents. As is both customary and seen in the above document, the table of contents is placed before the body of the text where the headings which it will contain are defined. The problem is that when the table of contents is required, we cannot guarantee that headings have been processed, so Emblem would not be able to construct an accurate table of contents.
To alleviate this, Emblem operates a typesetting loop whereby it repeatedly typesets the document until either it has settled to a consistent state, or until a cut-off number of iterations is reached. As such, the overall operation of Emblem can be considered as below.
The typesetting loop gives the user the guarantee that---with sufficiently-many iterations---the document output by Emblem will fully typeset after a single execution of the ‘em’ binary. These docs also provide more information on how to control the typesetting loop elsewhere. For now, however, we turn to the question of document styling.
Styling documents with CSS
Writing documents inherently brings two concerns: the writing of good text to appeal to the mind, and the application of pleasant styles to appeal to the eye. Naturally, when writing, some modicum of consideration must be lent to each, but they are naturally at-odds. Given finite time, an author must decide how best to partition their efforts to please their audience. In the extreme cases, we have on the one hand the screen-writer who, knowing their document is but a means to the realisation of their work, will choose to write using mono-spaced typefaces, with minimal concern of layout, and on the other hand the graphic designer who pours endless hours into the presentation and adjustment of but a few important words.
By making use of Cascading Style Sheets (CSS), Emblem supports this writers’ dichotomy. CSS is an expressive and highly extensible method of applying visual information to structured documents and, through industrious use of its selection rules, an author can construct good abstractions for how their document will appear. This allows styling information to be automatically applied, thereby allowing the user to focus their attention entirely upon one of style and content, when the time comes.
A very useful example is in the insertion of indents at the start of paragraphs. In long-form text in the Latin alphabet, the modern style starts each paragraph on the line immediately below the last and, to highlight the boundary, adds an indent to the first line of each paragraph after the first. This allows the first paragraph to neatly sit upon the upper left reaches of its allotted space, the entire body of text to calmly flow uninterrupted, and for paragraphs to be clearly yet unobtrusively be delimited. This can be concisely encoded through the use of CSS’s selectors as below.
p { /* By default, paragraphs are not indented */
text-indent: none;
}
p + p { /* A paragraph which directly follows another requires an indent */
text-indent: 2em;
}
As a result of this short snippet, the author no longer needs to consider where paragraph indents are applied as it will be handled automatically.
The user is able to add their own styles by specifying some style sheet which can be used to override the defaults and thereby tailor the look of their document.
Extensions
Automation and extensibility are at the heart of Emblem, and in this section we’ll describe how it is possible to direct Emblem to do some of the author’s work for them.
When a call to a directive, d, is evaluated, Emblem first checks to see whether d is associated with an extension function.
If it is, then this function is executed and its result is taken to be a part of the document.
An example of this can be seen above concerning the .toc
directive, which constructs a table of contents from headings.
Extensions are simply Lua files and have two purposes in Emblem---the import of custom style sheets and the creation of new directive functions. These style sheets are considered to be ‘above’ the user’s one, thus allowing for defaults to be created and overridden as required. Conversely, directive functions have slightly more nuance. As Emblem processes a document, it maintains a Lua environment which contains a public table called ‘em.’ It is this table which is checked to determine whether a given directive name has an associated function to call.
Extensions can be added to the runtime by use of the -x
/--extension
option and arguments can be passed to these extensions through use of the -a
/--ext-arg
option.
For example, to include an extension ext
with parameter param
given a value arg
, the following can be used.
em -x ext -a ext.param=arg
To define a new directive function, an extension simply adds an entry into the ‘em’ table,1 alongside help text and the number of parameters it expects.
The entry also encodes some basic type information, so if a directive is called with the wrong number of arguments, a warning can be issued.
These entries also encode a help message which can be accessed through the .help
directive.
The following example shows information about the .echo
directive:
.help{echo}
Being constructed from Lua, extensions allow for arbitrary functionality when writing a document, however this is not the only place where extensions can operate.
Input and output drivers
The other place in Emblem where extensions are accepted is when the program takes input or writes output.
Until this point, we have only discussed the writing of documents in Emblem source files (*.em
) however in truth, Emblem can parse any format for which it has the appropriate input driver.
The same is true when writing output---the formats to which Emblem may output depend only the output drivers it knows.
These options can be accessed through the command-line api by using the -F
/--from
and -T
/--to
options.
The following will take input of html and output markdown.
em -Fhtml -Tmd mydoc.html
The above is somewhat redundant---we have specified that the input file is html
both explicitly with the -F
option, but also implicitly with the .html
file extension.
Therefore, Emblem also maintains a mapping of file-extensions to input languages and when no -F
option is specified, the input file extension is used to determine the parser to use.
The core Emblem parser is used by default.
In the case of input drivers, it is also possible to access this same functionality from within a document, thus allowing for a document’s source to comprise of multiple languages as the author requires.
This is done through use of the .include
directive.
.include{some-file} // Includes some-file.em
.include{some-file.em} // Same as the above
.include{some-other-file.html}{html} // Includes ‘some-other-file.html’ using the html language explicitly
.include{some-other-file.html} // Same as the above but Emblem implictly figures out that the input language is ‘html’
Information on how to write input drivers and output drivers can be found elsewhere in these docs.
The Emblem public table, ‘em,’ can be imported from std.base
Expressions and Conditions
In Emblem, an expression is a section of text which is parsed and evaluated as a mathematical statement. These can be used to concisely represent maths operations without using the arithmetic or logic directives. A condition is a special case of an expression, where if the expression is empty, false is returned instead of emitting a parse-error. These are commonly used in the flow-control directives.
It must be noted that expressions are only capable of handling numbers, so for checking textual equality for example, the .streq
directive must still be used.
The following operators are recognised in expressions with the numbers giving their precedence level with higher numbers indicating that the associated operators bind more tightly.
As the multiplication operator, *
, has a higher precedence than the binary subtraction operator, -
, we can see that the expression 1+2*3
is correctly evaluated to 7 instead of 9.
-
<==>
-- Dual implication==>
-- Single-implication (right)<==
-- Single-implication (left)
-
||
-- Disjunction (‘lazy-or’)
-
&&
-- Conjunction (‘lazy-and’)
-
<=>
-- Dual implication (higher precedence)=>
-- Single implication (right, higher precedence)
-
!=
-- Numeric inequality==
-- Numeric equality
-
<=
-- Inequality, less-than-or-equal, lazy<
-- Inequality, less-than, lazy>=
-- Inequality, greater-than-or-equal, lazy>
-- Inequality, less-than, lazy
-
~
-- Bitwise exclusive disjunction (‘xor’)|
-- Bitwise disjunction (‘strict-or’)
-
&
-- Bitwise conjunction (‘strict-and’)
-
+
-- Addition-
-- Subtraction
-
//
-- Integer division%
-- Modulo*
-- Multiplication/
-- Division
-
!
-- Logical negation-
-- Numeric negation~
-- Bitwise negation+
-- Unary plus
-
^
-- Exponentiation
Example -- Fizz-Buzz
The following is a solution in Emblem to the FizzBuzz common interview problem. This is by no means a model solution for Emblem is not a programming language---first and foremost it is a typesetter, it just happens to have a handy extension language which can be programmed in.
.for{!i <- 1}{1 <= !i <= 100}{!i <~ !i + 1}:
.if{!i % 15 == 0}:
.echo: FizzBuzz
::
.if{!i % 5 == 0}:
.echo: Buzz
::
.if{!i % 3 == 0}:
.echo: Fizz
::
.echo: !i
Document directives
In Emblem, the semantic structure of a document is partitioned by the use of directives. As the document is processed, they are used to enact not only styling changes but also extension functions.
- If an extension function exists a same-name entry in the Emblem public table (
em
to extension-writers), then it is executed - If there is a style with the same name as the directive, then that is applied.
All syntactic sugars (such as surrounding a word in underscores to make it italic) are simple translations to directive calls.
Example - Styling part of a sentence
The following lines will all yield the same result---an entire or part of a sentence emboldened by use of the .bf
directive.
.bf: This entire sentence is bold
This sentence is only has .bf{two bold} words.
Hello **rather obnoxious** world!
Example - Cross-referencing
In academic papers and technical documents, it is often necessary to refer the reader to different sections, often to inform them of the location if pertinent information. In Emblem, this is done through the label-anchor-reference system: headings and other document items set a label, a document-writer places an anchor at an important location which makes note of the label value there, and then calls a reference to that anchor which returns the required label value.
# If this is some section with some useful things in (behind the scenes it edits the current label value, for example this may be section 6.2.3)
And perhaps this is the location of that useful thing, then an anchor is set with .anchor{a-useful-thing} or the syntactic sugar @useful-thing.
# Some other document sections may then be written
Which contain other things to interest the reader.
When the time comes, and the reader’s attention must be brought back to what was before, the relevant label can be automatically determined when by calling .ref{a-useful-thing} or #a-useful-thing.
Document structure
When writing large documents, it is typically necessary to convey the structure of the piece to the reader to allow them to not only focus their attention where is most beneficial to them, but also to convey the significance of all parts of the text.
For this, it is customary to use a system of headings---declarations of upcoming content---and a table of contents to allow a reader to navigate to wherever they will.
An analogous structure may be befitting of an author---to avoid an overlarge monolithic source, Emblem provides directives and pragmas to allow the source of a document to be partitioned into many different files.
In this section, we will detail each of these concepts:
- Sectioning documents with headers
- Creating a table of contents
- Breaking document source into multiple files
Example - Multi-section document
The following is a possible outline of the sections of a technical report.
# Introduction
# Background
## Motivating Example
## Concepts
# Literature Review
# Design
## Specification
## Architecture
## UI
# Implementation
## Tools and Libraries
## User-Extensions
# Results and Discussion
# Further Analysis and Extensions
# Evaluation
# Conclusion
Example - Multi-file document
If the above example were written in a single file, it would be reasonable to assume that editing the document would slowly become more and more cumbersome. A more manageable structure may result from taking each heading with both subheadings and a significant amount of content beneath it, and place this in a directory beneath the project root.
.
|- report.em
|- introduction.em
|- background/
| |- background.em
| |- motivating-example.em
| |- concepts.em
|- literature-review.em
|- design/
| |- design.em
| |- specification.em
| |- architecture.em
| |- ui.em
|- implementation/
| |- implementation.em
| |- tools-and-libraries.em
|- results-and-discussion.em
|- further-analysis-and-extensions.em
|- evaluation.em
|- conclusion.em
As we are only including Emblem source files, we can make use of the :include
pragma to invoke the source of each document, hence the main file, report.em
---and also the main file in each of the sections in subdirectories will look something like the below.
As a part of the file inclusion process for Emblem source, if a file file.em
is not found, then file/file.em
is tried, hence the root does not need to encode exactly how its contained sections are laid out in the file system.
:include "introduction"
:include "background"
:include "literature-review"
:include "design"
:include "implementation"
:include "results-and-discussion"
:include "further-analysis-and-extensions"
:include "evaluation"
:include "conclusion"
Where possible, the user should try to use the :include
pragma instead of the .include
directive as the former is more efficient, skipping a translation step between extension space and the core, which contains the Emblem parser used by both methods.
.h1
, .h2
, .h3
, .h4
, .h5
, .h6
These directives create level 1--6 headers respectively, that is, markers to delimit the sections of a document to the reader.
These headers be numbered and will appear in the table of contents.
To create a header which does not appear in the =toc,= the starred variants may be used, that is .h1*
in place of .h1
etc.
The header directives take a single argument, the text of the header.
Given the frequency with which headers appear in documents, syntactic sugar is available---any line which starts with between one and six hashes and some space will have the remainder of the line taken as a header, just as in markdown.
Hence the line .h3: some text
is the same as ### some text
.
Similarly, .h3*: some other text
is the same as ###* some other text
.
Example -- Document structure
Sections can be used to break different parts of a document (or in this case presentation script) into smaller marked areas for consumption by an intended audience.
# Introduction
Hello and welcome to our presentation on the dangers of garden gnomes.
# Why Garden Gnomes are Dangerous
You might not know this but garden gnomes are dangerous.
## What a Garden Gnome can do
You never know what a gnome will do when your back is turned.
## What Have Garden Gnomes Been Observed Doing?
Nobody has been able to record any suspicious activity in relation to garden gnomes, but that's exactly what they'd want.
# Conclusion
Watch your back out there, kiddos.
Example -- Document sections (exhaustive document)
The following is an exhaustive list of available headers and their related syntactic sugars.
.h1: This is the largest header
.h2: This is a sub-header
.h3: This is a sub-sub-header
.h4: This header is even deeper
.h5: This is an extremely deep header
.h6: This header is so deep its utility is questionable but here it is
# This is the largest header
## This is a sub-header
### This is a sub-sub-header
#### This header is even deeper
##### This is an extremely deep header
###### This header is so deep its utility is questionable but here it is
.h1*: This appears the same as `.h1` but is un-numbered and absent from the TOC
.h2*: Same as the above but smaller
.h3*: Same as the above but smaller
.h4*: Same as the above but smaller
.h5*: Same as the above but smaller
.h6*: Same as the above but smaller
#* This appears the same as `.h1` but is un-numbered and absent from the TOC
##* Same as the above but smaller
###* Same as the above but smaller
####* Same as the above but smaller
#####* Same as the above but smaller
######* Same as the above but smaller
.include
, .include*
and :include
When writing large documents which consist of a large number of sections, it can be beneficial to partition the source into a number of smaller files. The result can be a document which is much easier to work upon than a single monolithic source file.
In Emblem, there are three methods by which the contents of another file can be included in a document:
- The
.include
directive, - The
.include*
directive and - The
:include
pragma (notice the ‘:’ prefix rather than ‘.’).
We will explain the basic usage of each, before discussing how paths and languages are handled.
Basic Usage
In basic use, the .include
directive takes input of a file name with either no extension or ending in ‘.em,’ tries to find that file, parses it as an Emblem document and then returns the result.
This result is also cached to avoid repeatedly re-parsing the same file.
The contents of the specified file is then pasted into the document in the current location.
Due to how paragraphs are recognised, if the call to .include
is the only thing in a paragraph, then the given file’s contents are scanned to discern paragraphs, however, if it is not the sole member of the paragraph, then its contents is added directly into the paragraph.
.include: some-file // Paragraphs in some-file.em are parsed as if were written directly in the source file
Some sentence.
.include: some-file // Content of some-file.em are pasted into the surrounding paragraph so likely appear as more sentences.
Some other sentence.
If the file being read is expected to change between typesetting iterations, the .include*
directive may be used, which does not cache its result.
This is hence less efficient for un-changing source files.
The .include*
directive shares all other properties of the .include
directive.
The :include
pragma differs from the .include
directive as it is handled at parse-time, beyond the extension environment and as such, the file-path is fixed.
The following will include ‘some-file.em’ like the above---
:include "some-file"
As this is handled separately from extensions, the file path given to :include
is constant---it cannot be changed dynamically by the user and is read exactly as seen in the source file.
Hence although the :include
pragma is restrictive, it provides a guarantee to external tools parsing Emblem source as to the structure of the document, without needing to implement evaluation mechanics.
File name handling
The sections in a large document naturally differ in size---the introduction and conclusion are likely dwarfed by the body of a report, the preamble and acknowledgements are likely dwarfed by these in turn, and even among the main passages of work, some will bring together content from many sources and some only few.
As such, it can be useful to partition a document’s source files into multiple folders, each helping group together relevant materials.
The file structure of a report may hence look something like this, with report.em
as the main file.
.
|- report.em
|- introduction.em
|- background/
| |- background.em
| |- motivating-example.em
| |- concepts.em
|- literature-review.em
|- design/
| |- design.em
| |- specification.em
| |- architecture.em
| |- ui.em
|- implementation/
| |- implementation.em
| |- tools-and-libraries.em
|- results-and-discussion.em
|- further-analysis-and-extensions.em
|- evaluation.em
|- conclusion.em
In Emblem, this file structure is embraced by the method by which Emblem searches for ‘.em’ files.
When given a file name either file.em
or just file
(no extension), Emblem first searches for that file in the current directory, and if this is not found, it searches for file/file.em
.
As such, we can include all of the required ‘.em’ files from the main one without needing to know how the files are laid out on disk, allowing for a more abstracted view.
The main file can hence be written as:
:include "introduction"
:include "background"
:include "literature-review"
:include "design"
:include "implementation"
:include "results-and-discussion"
:include "further-analysis-and-extensions"
:include "evaluation"
:include "conclusion"
Languages and input drivers
So far, we have only discussed the inclusion of Emblem source files, however, this is not the only format accepted by Emblem. By making use of input drivers either explicitly or implicitly, we can make use of document source written in other languages.
To explicitly make use of input drivers, we can make use of the optional second argument of the .include
and .include*
directives.
In this parameter, we can specify the language of the given source file, prompting Emblem to look for a parse function associated with that language.1
The result of calling this function on the input file is then returned.
.include{some-file.html}{html} // Asserts that some-file.html should be parsed as html
The implicit use of input drivers helps eliminate some redundancy in the above statement---when a file extension is given, Emblem uses a mapping of extensions to input languages before finding the parse function as before.
As such, the second parameter in the above .include
call can be removed, relying on Emblem to recognise the input language, hence leading to a cleaner source.
.include{some-file.html}
As extensions are involved in the processing of languages, we can see that the above logic only applies to the .include
and .include*
directives, and not the :include
pragma.
Recall that input drivers are just extensions which declare an association between an input language and a parse function.
.toc
The .toc
directive creates a table of contents by making a list of all of the headings of a document.
As this list is only complete at the end of a run, after the .toc
has been processed, a document with a table of contents always requires at least two iterations of the typesetting loop.
No arguments are taken.
.toc
If there are headings which an author does not wish to be seen in the contents table, starred variants of the heading directives should be used, that is, every appearance of .h1
or # ...
should be replaced with .h1*
or #* ...
respectively, for .h1
to .h6
, and # ...
to ###### ...
.
References
Any respectable document needs a way of referring to content both within and without, the former being used to soundly structure the arguments of a document, and the latter to invoke facts presented elsewhere.
In Emblem, these two modes of reference are handled by two systems:
- The anchor-and-reference system for internal links and
- The bibliography-and-citation system for external links.
The concepts presented in this section will likely be familiar to LaTeX users as much inspiration has been taken from the \label
, \ref
, \bibliography
and \cite
commands therein.
.anchor
and .ref
- Note differences between anchor, label and ref
- Note scoping due to label storage in variable
.bib
and .cite
To reference other works outside of a document, we can use citations which point to bibliography entries with information on how to find the exact source.
In Emblem, these are done using the .cite
and .bib
directives.
The .cite
directive takes input of a key, a unique string which provides a reference to a particular bibliography item.
The bibliography is both parsed and produced by the .bib
directive.
// TODO: talk about the optional argument!
.bib // Reads from the default location
.bib: file // Reads from some-file if it exists, otherwise, file.yml, file.yaml or file.json
Unless the optional bibliography file-name parameter is given, this directive reads searches for a file called ‘bib’.
carlson2011you:
title: "You probably think this paper's about you: Narcissists' perceptions of their personality and reputation."
author: Carlson, Erika N and Vazire, Simine and Oltmanns, Thomas F
journal: Journal of personality and social psychology
volume: 101
number: 1
pages: 185
year: 2011
publisher: American Psychological Association
greenfield2015asdf:
title: "ASDF: A new data format for astronomy"
author: Greenfield, P and Droettboom, M and Bray, E
journal: Astronomy and Computing
volume: 12
pages: 240--251
year: 2015
publisher: Elsevier
Flow control
Some directives can be used to manipulate the contents of the document in response to some conditions. Indeed, this subset of available directives are designed to act as a small Turing-complete programming language.
Over the following sections, we will explore directives for:
- Branching
.case
.if
- Looping
.foreach
- Handling directives
.call
.def
.exists
.undef
- Handling variables
.defined
.get-var
.set-var
.call
The .call
directive takes input of a directive name and some arguments to pass to it.
It returns a call to the specified directive with the arguments given.
For example, one could call .echo
with three arguments like so---
.call{echo}{Hello}{world}{how are you?}
Which is implicitly translated by the .call
to---
.echo{Hello}{world}{how are you?}
As the name of the directive is required, note that the .
which precedes directive calls is omitted, so .echo
above becomes just echo
.
Example - Specifying an error function
The following code could be used to change the severity of an error condition based on a user’s preference. That the user inputs a number is assumed for brevity.
!err_severity <- .readline
!err_func <- .case{!err_severity}{echo}{warn}{error}
...
.call{!err_func}: Something went wrong!
.case
The case directive is used to select one of a number of cases by index.
These cases are 1-indexed, so the first is referenced by index 1.
So for example if !index
is 2, the following will be evaluated to ‘World.’
.case{!index}:
Hello
::
World
::
How are you?
If the index supplied is higher than the number of cases present or is negative, the last case is returned as a fallback option, hence if above !index
is -1 or 12 for example, ‘How are you?’ will be returned.
The index given to .case
is parsed as an expression, hence it is possible to compute the index using mathematical operators.
Example -- Chinese calendar year name
The following could be used to translate from the year number in the Gregorian calendar to its Chinese calendar name, assuming the current year is stored in !year
.
.case{1 + !year % 12}{Rooster}{Dog}{Pig}{Rat}{Ox}{Tiger}{Rabbit}{Dragon}{Snake}{Horse}{Goat}{Monkey}
.def
This directive allows the user to define their own directives for later use.
It takes two inputs: a directive name and a directive body to be executed whenever the new directive is called.
The new directive is allowed to take parameters which can be referenced by values !n
in the loop body, so !1
evaluates to the first argument given, !2
the second and so on.
Note that as a directive name is taken, there is no .
preceding the new directive name when .def
is called.
If functions are nested, it may be useful to use multiple exclamation marks when accessing arguments from different scopes.
Example -- Checking input
The following could be used to perform type-checking upon a given value.
.def{bool}:
.if{! .streq{!1}{true} || .streq{!1}{false}}:
.error: Expected either ‘true’ or ‘false’ but got !1
!1
Then, later in the document the following could be written:
.bool{true}
.bool{false}
This would give certainty that boolean values have indeed been given.
The .bool
function may be useful if some values are stored in variables, or some other method which obscures the value from the source.
The .def
directive is provided to allow an author to extend the functionality of Emblem from within their document.
It is useful for simple tasks, however for more complicated ones, it may be easier to write an extension and make use of Moonscript or Lua’s abstractions instead.
.defined
The .defined
directive checks whether a given directive name corresponds to a known directive function and returns 1
or 0
accordingly.
.defined{echo} // Returns 1 as .echo exists
.undef{echo} // Undefines .echo
.defined{echo} // Returns 0 as .echo no longer exists.
.exists
The .exists
directive checks whether a given variable name exists in current scope or in a parent and then returns 1
or 0
accordingly.
.exists{x} // Returns 0 as !x does not exist
!x <- asdf
.exists{x} // Returns 1 as !x now exists.
.for
The .for
directive emulates the standard for-loop seen in many programming languages.
It takes input of:
- An initialiser -- a statement which is performed before iterations commence. It is customary to define an iteration variable here.
- A condition -- a condition which is evaluated before each iteration, and if it is found to be false,
.for
execution terminates - A mutator -- a statement which is executed at the end of each iteration. It is customary to mutate the iteration variable here.
- A loop body -- a section of emblem script to execute each iteration which is included in the for-loop’s return value
There are two scope considerations:
- The loop body is evaluated within its own scope
- The initialiser, condition and mutator are evaluated within a scope which surrounds the loop body.
This second point means that whereas the default assignment operator, <-
, can be used in the initialiser and mutator, for the same effect on the iteration variable, the finding-assignment operator, <--
, should be used to ensure that the change to the iteration variable is not lost at inner scope-closure.
The same is true of the expression-assignment and finding-expression-assignment operators, <~
and <~~
.
Example -- Iterating over even numbers
The following outputs the even numbers inclusively between one and one hundred by allowing an iteration variable, !i
to take values from 2 to 100 in increments of 2.
.for{!i <- 2}{1 <= !i <= 100}{!i <~ !i + 2}:
.echo: !i
.foreach
The .foreach
directive takes three inputs: an iteration variable name, a list of values and a loop body, and evaluates the loop body once for each value in the given list, assigning the iteration variable as it goes.
The list of values is parsed as a space-separated list.
As an example, the following will write the help text associated with each known directive to the console.
.foreach{d}{.known-directives}:
.echo: .help: !d
The .foreach
directive hence performs a bounded iteration---the number of times it loops is bounded by the size of its ‘values’ input.
Example -- Testing the truth values of a boolean function
This code will iterate over input values to the .expr
implication operator, =>
, and output the associated truth value.
.foreach{x}{false true}:
.foreach{y}{false true}:
.echo: !x !y .expr: !x => !y
.get-var
This directive takes input of a variable name and obtains its value in the current scope. If there is no such value with that name, nothing is returned.
The .get-var
directive has associated syntactic sugar, so the following two are equivalent.
.get-var{variable}
!variable
More exclamation points can be used to widen the scope searched, hence !!var
will obtain the value of ‘var’ in the scope above its current definition.
The .get-var
directive is the counter-part to .set-var
, which sets variables to given values.
.if
This directive allows for the selection of a single branch (or none) by use of a condition. This directive takes two forms:
.if{condition}{branch}
.if{condition}{branch 1}{branch 2}
The two-argument form operates as follows:
- If condition is true, branch is returned
- Otherwise nothing is returned
The three-argument form operates as follows:1
- If condition is true, branch 1 is returned
- Otherwise, branch 2 is returned
Example -- Greetings message
Assuming that the variable !is_morning
contains a value which represents whether the current time is morning, we could tailor the user’s greeting message to the current time.
.echo:
.if{!is_morning}:
Good morning!
::
Hello!
This form operates exactly the same as an if-else block common to many programming languages.
.set-var
The .set-var
directive is what the !var <- val
syntactic sugar is translated to at parse-time.
This directive takes the name of a variable and a value, and assigns that variable to the string representation of the given value at the point of call.
By syntactic sugar, the following two lines are equivalent and both set the value of variable hello
in the current scope to ‘world.’
.set-var{hello}{world}
!hello <- world
Note the initial exclamation mark required on the second line. This is required by the parser and is reflected in the same number of exclamation marks when referencing that variable.
If the variable name contains exclamation marks, the scope of the assignment is widened, so for example, to set the variable hello
in the parent scope and set that to ‘world,’ we can call
.set-var{\!hello}{world}
Note that as we want the literal variable name, the initial exclamation mark must be escaped to prevent variable recognition at parse-time and hence expansion.
Only the first exclamation mark requires an escape, so the literal !!asdf
can be escaped into \!!asdf
.
The above snippet is equivalent by syntactic sugar to the following.
!!hello <- world
More exclamation marks will widen the scope-search further.
The .set-var
directive has some variants for some specialised use-cases.
We combine .expr
with .set-var
with the .set-var-expr
directive, which evaluates the value parameter as an expression before performing assignment.
We also have .find-set-var
and .find-set-var-expr
, which are the same as .set-var
and .set-var-expr
, except that the search for an existing instance of the variable is always performed, even when the number of exclamation marks specified would not trigger it with the non-find assignment directives.
This cleans up the syntax slightly for the common operation of extracting a value from the scope of a loop for example---the following two are equivalent.
!!hello <- world
!hello <-- world
The variants of .set-var
each have their own syntactic sugar.
The following is a list of all sugars associated with assignment
!var <- value // Equivalent to .set-var{var}{value}
!var <~ exp // Equivalent to .set-var-expr{var}{exp} or !var <- .expr: exp
!var <-- value // Equivalent to .find-set-var{var}{value}
!var <~~ exp // Equivalent to .find-set-var-expr{var}{exp} or !var <-- .expr: exp
Example -- Recording a user’s name for later use
We could write a document like so which takes input of the user’s name and then re-uses that value multiple times. By storing the user’s response in a variable, we can avoid the need to re-ask them.
.echo: Please enter your name
!name <- .readline
Dear !name,
It has come to my attention that the name !name is shared by us both.
Please change yours.
Warmest regards,
!name
.undef
Calling .undef
with the name of a directive removes that directive from use.
If the given directive is not known, nothing is done.
Example -- Preventing system calls
The following example un-defines the system directive, .$
.
.undef{$}
Example -- Ruining user experience
The .undef
directive can be used to remove all directives from availability.
.foreach{d}{.known-directives}:
.undef{!d}
.while
The .while
directive takes two inputs: a condition and a loop body and does the following:
- Check to see if the condition is true
- If it is, evaluate the loop body and go to step 1
The results of each iteration are concatenated together beneath a Content node:
Example -- Sanitising user input
When using the .readline
directive which reads a single line from the standard input, some sanitisation may be needed on a user’s input.
The following will ask the user for an input until they enter something which isn’t empty
.echo: Please input a number
!x <- .readline
.while{.streq{}: !x}:
.warn: User didn't enter anything
!x <-- .readline
Logging
Some directives can be used to report the state of the document, either for general information (.echo
), for warning the user about a possibly problematic state (.warn
) or for halting execution at a catastrophic failure (.error
).
.echo
and .echo-on
These directives write text to the standard output.
If multiple text arguments are given, they are concatenated together with spaces before being echoed.
The .echo-on
directive treats its first input as a typesetting loop pass number and will only output on that pass.
Example -- Asking the user’s name
Using the .readline
directive a line of input can be read from stdin
, allowing the program to respond to the user.
.echo: Hello, what’s your name?
!name <- .readline
.echo: Oh hello, !name
Example -- Outputting the current pass
The following could be placed at the top of an input file to more explicitly show which typesetting pass is currently being evaluated.
.echo-on{1}: Hello this is the first pass
.echo-on{2}: This is the second pass
.echo-on{3}: This is the third one
.error
and .error-on
When something has gone terribly wrong, the user may wish to stop the program.
In Emblem, this can be done by use of the .error
directive, which takes input of a message and outputs it as an error from the current source location before halting the program.
If multiple arguments are given, all are concatenated together before output is given as usual.
If the error is only applicable to a certain typesetting pass, the .error-on
directive may be used.
This takes input of a number and message(s) as the .error
directive, but only outputs when the current typesetting iteration is equal to the number given.
For non-critical errors, that is, those for which execution can safely continue, consider using the .warn
or .warn-on
directives.
Example -- Sanitising user input
When using the .readline
directive which reads a single line from the standard input, some sanitisation may be needed on a user’s input.
The following will check to see that the user has indeed input something.
.echo: Please input a number
!x <- .readline
.if{.streq{}: !x}:
.error: User didn't enter anything
.warn
and .warn-on
When something has gone wrong but the problem isn’t fatal, a warning may be required to notify the user of what has happened.
This can be done using the .warn
directive, which takes input of a message and outputs it as a warning from the current source location.
If multiple arguments are given, all are concatenated together before output is given as usual.
The .warn
directive is subject to the warning-handling specified at the command-line, in particular the -E
flag turns warnings into errors.
If the warning is only applicable to a particular typesetting pass, the .warn-on
directive may be used, which takes input of a pass number to perform the output upon and the remainder of its arguments as .warn
.
Example -- Sanitising user input
When using the .readline
directive which reads a single line from the standard input, some sanitisation may be needed on a user’s input.
The following will ask the user for an input until they enter something which isn’t empty
.echo: Please input a number
!x <- .readline
.while{.streq{}: !x}:
.warn: User didn't enter anything
!x <-- .readline
Logic
To augment the capabilities of the flow control directives, it is possible to construct logical conditions from other statements.
In particular, directives are provided to:
These are discussed in this section.
.eq
, .numeq
, .streq
In Emblem, there are three modes of equality: structural, textual and numerical.
These correspond to the equality functions .eq
, .streq
and .numeq
respectively.
Although all compute equality, the differences between each should be noted.
Structural equality -- .eq
This definition of equality is the strongest, it looks at the document structure of two items and asks whether they are identical, that is, not only does it look at each literal fragment, but also checks whether these are arranged in memory in the same way.
It must be noted that results of directive calls are also checked, which can lead to some odd results.
Example -- Identical structures
The following examples all return true as they are identical
.eq{}{}
.eq{hello, world!}{hello, world!}
.eq:
.it:
Hello, world!
::
.it:
Hello, world!
Example -- Non-equal, apparently identical structures
However, all of the following are false as, although they may appear the same in output they differ in how they got there. Some of the following behaviour may seem strange, but comes as a consequence of the very insistent nature of this kind of equality.
!x <- a
!y <- b
.eq{!x}{!y} // This is false as different variables are referenced (‘x’ and ‘y’)
.eq: // This is false as when evaluated these two sections are given distinct numbers
# Hello, world!
::
# Hello, world!
.eq: // This is false due to the method by which trailing arguments are handled (a node to represent hold possibly multiple lines is necessarily inserted
.it{Hello, world!}
.it:
Hello, world!
Example -- Apparently non-equal, identical structures
As syntactic sugar is translated into directive calls when parsing is performed, all of the following are true.
.eq{_something emphasised_}{.it{something emphasised}}
.eq{[a-citation]}{.cite{a-citation}}
.eq{!x <- asdf}{.set-var{x}{asdf}}
Textual equality -- .streq
Whereas structural equality checks the internal representation of structures, textual or ‘string’ equality simply checks to see whether the literal text from two sources is the same, regardless of how they were constructed.
Any structurally-equal pair of values is necessarily textually-equal.
Example -- Non-identical structures with textual equality 1
The following all resolve to a true value at the point when the standard library has finished loading.
.streq{**something emphasised**}{_something emphasised_}
.streq{.tt{something}}{.bf{something}}
.streq{.it{two words}}{.it{two}{words}}
Example -- Apparently-equal structures without textual equality
The following evaluates to a false value for the same reason that it does not have structural-equality---when constructing a .h1
header, a section number is prepended
.streq:
// The text here is ‘1. Hello, world!’
# Hello, world!
::
// The text here is ‘2. Hello, world!’
# Hello, world!
Numerical equality -- .numeq
Whereas textual equality checks that all literals in the text are equal, numerical equality first attempts to translate the text into a number before doing the same. As a result, numerical equality is weaker than textual equality. The numbers considered are base-10.
It should be noted that the numerical equality function does not perform any operations such as addition or subtraction.
If these are required, they must be invoked explicitly through other directives whose results are passed as inputs to .numeq
.
More information on arithmetic directives is available elsewhere.
Example -- Equal numbers
Prepended zeros have no bearing on the value of a number and hence are ignored under numerical equality. All of the following evaluate to a true value.
.numeq{1234}{1234}
.numeq{1234}{000000000000000001234}
.expr
The .expr
directive takes input of an expression, parses and evaluates it.
As it uses the same parsing logic as the flow-control directives, it is possible to use .expr
for debugging.
Example -- A simple calculator
Using .expr
and a while loop, a simple calculator can be made.
The following will repeatedly read an expression to evaluate from the user, until they input nothing.
!resp <- .readline
.while{! .streq{}{!resp}}:
.echo: .expr: !resp
!resp <-- .readline
.gt
, .ge
, .lt
, .le
The inequality functions take input of numerical values and return a boolean value as one might expect them to in maths. At their heart, they take pairs of numbers and check whether they satisfy the relevant condition:
.gt
---Greater than.ge
---Greater than or equal.lt
---Less than.le
---Less than or equal
Example -- Checking pairs of numbers
The following are simple examples of inequalities
.gt{123}{321} // Evaluates to false
.ge{123}{321} // Evaluates to false
.lt{123}{321} // Evaluates to true
.le{123}{321} // Evaluates to true
.gt{456}{456} // Evaluates to false
.ge{456}{456} // Evaluates to true
.lt{456}{456} // Evaluates to false
.le{456}{456} // Evaluates to true
Example -- Checking more than two numbers
Sometimes, multiple numbers must be checked, and in this case maths often uses the following shorthand:
\( a \leq b \leq c \Longleftrightarrow a \leq b \mathrel{\&} b \leq c \)
The directives we discuss here also allow the same functionality---if more than two arguments are presented, from left to right, each consecutive pair is checked for inequality and a true value is only returned if all these conditions hold. It should be noted that computation can be halted early, so later arguments will not be evaluated if an earlier pair evaluates to a false value.
Boolean
Sometimes, it is necessary to combine several conditions into one, more complicated one.
It must be noted that each of these directives consider their input values in the same way as the flow-control directives:
- If the value is empty, it is false
- If the value is ‘0’, it is false
- If the value is ‘false’ (case-insensitive), it is false
- Any other value is true
In the following section, we describe an adequate set of operators with which any boolean expression may be represented:
.all
---conjunction, similar to the ‘and’ operator of many programming languages.any
---disjunction, similar to the ‘or’ operator of many programming languages.impl
---implication.not
---negation.xor
---exclusive disjunction
.all
The .all
directive interprets its inputs as conditions and iff none is false, it returns a true value.
It evaluates its arguments from left to right as required to resolve its value---if any are false, the rest of its arguments are not evaluated.
.all{true}{true}{true} // returns true
.all{true}{true}{false} // returns false
.all{false}{false} // returns false
.all // Vacuously returns true
.all{false}{.error{Something went wrong}} // This returns false and never calls .error
.any
The .any
directive interprets its inputs as conditions and iff at least one is true, it returns a true value.
It evaluates its arguments from left to right as required to resolve its value---if any are true, the rest of its arguments are not evaluated.
.any{true}{true}{true} // returns true
.any{true}{true}{false} // returns true
.any{false}{false} // returns false
.any // Vacuously returns false
.any{true}{.error{Something went wrong}} // This returns true and never calls .error
.impl
This directive computes boolean implication, that is, given two inputs whether one implies the other by the following truth table.
\( a \) | \( b \) | \( a \Rightarrow b \) |
---|---|---|
false | false | true |
false | true | false |
true | false | true |
true | true | true |
.not
This takes a single boolean input and negates it---
\( a \) | \( \neg b \) |
---|---|
false | true |
true | false |
.xor
This directive takes two conditions and outputs their exclusive disjunction, that is, it returns true iff they have different boolean values.
\( a \) | \( b \) | \( a \oplus b \) |
---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | false |
Arithmetic
Directives are provided to handle numbers. They do what one might expect, they take numbers, operate upon them and output a number. This section details these directives.
When parsing numbers, it is important to note some things. In particular, numbers:
- are in base-10
- cannot contain spaces
- can be negative
- can contain fractional parts (after the radical point)
- can be \( \pm \infty \)
- can be NaN (not-a-number)
.abs
Computes the absolute value or magnitude of a given number, that is, the unsigned distance between it and zero.
\( \mathtt{.abs}(x) = \vert{x}\vert = \begin{cases} x & \text{if}\ x \geq 0 \\ -x & \text{if}\ x < 0 \end{cases} \)
.add
Takes two numbers, adds them and returns the result.
.add{4}{25} // Returns 29
.div
Takes two numbers, returns the left divided by the right. There are some special cases:
- When only the right input is zero:
- Returns \( +\infty \) when the left input is positive
- Returns \( -\infty \) when the left input is negative
- When both inputs are zero:
- Returns
±NaN
- Returns
The .div
, .idiv
and .mod
functions are related by the following identity---
\[ \frac ab = a \mathbin{//} b + \frac{a \mathbin{\%}b} b \]
.idiv
Takes two numbers, returns the left integer-divided by the right, that is, division is performed and then the result is rounded down to the nearest integer (the quotient).
There are some special cases:
- When only the right input is zero:
- Returns \( +\infty \) when the left input is positive
- Returns \( -\infty \) when the left input is negative
- When both inputs are zero:
- Returns
±NaN
- Returns
The .div
, .idiv
and .mod
functions are related by the following identity---
\[ \frac ab = a \mathbin{//} b + \frac{a \mathbin{\%}b} b \]
.mod
Takes two numbers, and returns the remainder of the left when integer-divided by the right.
There are some special cases:
- When only the right input is zero:
- Returns \( +\infty \) when the left input is positive
- Returns \( -\infty \) when the left input is negative
- When both inputs are zero:
- Returns
±NaN
- Returns
The .div
, .idiv
and .mod
functions are related by the following identity---
\[ \frac ab = a \mathbin{//} b + \frac{a \mathbin{\%}b} b \]
.mul
Takes input of two numbers and returns their product.
.mul{4}{25} // Returns 100
.pow
Takes a base and an exponent and returns the base raised to the power of the exponent.
.pow{2}{3} // Returns 8
.pow{2}{-3} // Returns 0.125
.pow{3}{2} // Returns 9
.pow{-3}{2} // Returns 9
.sign
Computes a given number’s sign. Takes input of a number \(x\) and returns:
- 1 if \(x > 0\)
- 0 if \(x = 0\)
- -1 if \(x < 0\)
.sub
Takes two numbers, subtracts the second from the first and returns the result.
.sub{4}{25} // Returns -21
Miscellaneous Directives
The directives in this section didn’t neatly fit into any of the other sections.
.curr-version
When developing documents and iterating through multiple cycles of drafting and improvements, it is possible that multiple versions of the same document may be in close proximity, thus leading to some confusion. This can be avoided by adding some indication of the document’s version in the output.
The .curr-version
directive does this my making use of the between-run store.
Example -- Placing the version in the title
# Research, Re: Search and Re-Search (version .curr-version)
.help
When the user is unsure of how to use a directive, they can get some assistance by calling the .help
directive.
This takes input of a directive-name, and returns a some associated help-text.
It should be noted that the preceding .
from the directive name is omitted.
Example -- Getting the help of the help function
Even the .help
directive can be input into .help
.help: help // Returns ‘Show documentation for a given directive’ (or there-abouts)
Example -- Getting all help
The following will iterate through all known directives, outputting their help text to the command-line.
.foreach{d}{.known-directives}:
.echo: .help: !d
.known-directives
This function outputs a list of known directives. Currently, this list contains only directives for which a function has been defined, however in future this will be expanded to cover all directives which have associated styling information. This list is also sorted in alphabetical order for convenience.
Example -- Getting all help
The following will iterate through all known directives, outputting their help text to the command-line.
.foreach{d}{.known-directives}:
.echo: .help: !d
.vars
Variables in Emblem exist within a given scope.
The .vars
directive returns a list of these scopes and the variables which they contain at the time of calling.
This may be useful for debugging.
.readline
The .readline
directive reads a single line from standard input and returns it.
Example -- Interacting with a user
As stdin
is read, the .readline
directive can be used to interact with a user.
.echo: Ahoy there! What’s yer name?
!resp <- .readline
.echo: Ahoy there, !resp
.$
Emblem allows the user to run arbitrary shell commands with use of the .$
or ‘system’ directive.
As the command it takes is run externally, there are two problems:
- The command given to the system directive has no guarantee of portability
- This directive has all the security risks associated with random code execution so be careful when executing someone else’s source code. This can be alleviated by use of sandboxing.
The system directive is given the .$
name for visual similarity to a terminal when used.
Example -- Listing files in the current directory
Using the standard UNIX ls
program, we can see the contents of the current directory:
.$: ls
<!-- Hello, this is some text which may contain repeated words. -->
<!-- We wonder how long it is, but we we’d rather know now many repeated words the text might contain. -->
.env
The .env
directive provides a way to read external environment variables.
It takes input of an environment variable name and returns its value.
If the environment variable does not exist, the empty string is returned.
Example -- Getting the name of the user’s editor
On Linux, it is common to record the name of the user’s text editor in EDITOR
environment variable.
This can be accessed within an Emblem document as follows.
.env: EDITOR // May or may not return ‘vim’
API
Interactions with Emblem come in three categories, each of which is handled in this section.
- Document-writers from the command-line
- Extension-writers through the Moonscript/Lua API
- Developers of the core
Command-line API
Takes input of a markdown-like document, processes it and typesets it before passing the result to a driver for outputting in some format. Extensions can be used to include arbitrary functionality; device drivers are also extensions.
Examples
em -Tutf8 example.em
em hello
Positional arguments
The following arguments are written anywhere in the call to em
(after the program name itself).
file
File to typeset or -
to read from stdin.
- type: string
- default: ‘-’
Optional arguments
The following are used to modify the behaviour of Emblem, to suit a user’s preferences.
-a extension.param=value
, --ext-arg extension.param=value
Pass parameter param with value value to extension ext.
- type: List
-c style
, --class style
Set the output class.
- type: string
- default: ‘” DATA_DIR “/article’
-C
, --colourise-output
Specify output colourisation, -1 for none, 1 for always, 0 for auto-detect (default is 0).
- type: int
- default: 0
-E
, --fatal-warnings
Treat warnings as errors.
- type: flag
-F driver
, --from driver
Set the input driver, if none is specified, the emblem parser is used.
- type: string
- default: ‘’
-f family
, --font-family family
Specify a default font family.
- type: string
- default: ‘’
-h
, --help
Display this help message and exit.
- type: help
-M
, --max-iters
Max number of typesetting iterations allowed.
- type: int
- default: 4
-o stem
, --output-to stem
Name of the output file without file-extension. If left unspecified the input file (with its extension removed) is used, otherwise emdoc.
- type: string
- default: ‘’
-S size
, --font-size size
Specify the default font size.
- type: double
- default: 0
-s
, --sandbox-level
Restrict access to the system, 0 makes no restrictions, 1 restricts creation of new subprocesses, 2 restricts uses all level 1 restrictions and prevents file system access. Default is 1.
- type: int
- default: 1
-T driver
, --to driver
Set the output driver, default is html.
- type: string
- default: ‘html’
-t
, --tab-size
The assumed size of tabs when reading input files.
- type: int
- default: 4
-v verbosity
, --verbose verbosity
Set output verbosity, 3 outputs everything, 0 suppresses all messages including errors. Default is 1.
- type: int
- default: 1
-V
, --version
Display version and license information and exit.
- type: version
-x extension
, --extension extension
Specify an extension to load. When used multiple times, the specified extensions are loaded in the order given.
- type: List
Moonscript/Lua Extension API
Emblem is hackable, that is, arbitrary functionality may be added by its users. This is done in one of three ways:
- In code executed as the document goes through its typesetting cycle (‘extensions’)
- In code executed to convert a given input format to Emblem’s internal structures (‘input drivers’)
- In code executed to convert to a given output format (‘output drivers’)
In this section, we will explore the Emblem standard library, std.*
:
std.ast
std.base
std.bib
std.constants
std.events
std.func
std.hdr
std.in.*
std.lingo
std.log
std.out.*
std.ref
std.show
std.store
std.style
std.util
Typesetting-time extensions
Typesetting-time extensions, hereafter referred to simply as ‘extensions,’ are snippets of Lua code which are imported after the document has been parsed and are executed as it undergoes its typesetting run.
Emblem Public Table
Extensions can define functions to be accessed in the document by editing the Emblem Public Table stored in the em
variable.
For example, we may wish create a document which includes a note of how long it took to compile.
To do this, we must create a function which creates the desired string, and place it into some field in the em
table, say cpu_time_used
em.cpu_time_used = function()
return 'This document was typeset in ' .. os.clock() .. ' seconds of CPU time'
end
If this code is written in a file called cpu-time.lua
, it can be imported by adding the flag -fcpu-time
when em
is run (note the file extension is optional).
Now, when the directive .cpu_time_used
is seen in the document, the above code will be executed, it will be replaced with the message.
Evaluation Order
Emblem is lazy, that is, it tries to do no more work than is necessary to typeset a document. So for example, if a directive takes three inputs and just outputs the first one, emblem will not bother to evaluate the others. This is because by default, Emblem will only evaluate a node if it can guarantee that it will appear in the output.
Sometimes, however, it can be useful to force Emblem to use a different evaluation order, such as to inspect the results which would not otherwise appear directly in the output.
This can be done using the eval
function, which takes a node and evaluates it, or the eval_string
function which
Evaluation order is manipulated in the definition of the .if
directive, which looks something like the following:
local base = require('std.base')
base.em['if'] = function(c, b)
cs = base.eval_string(c)
if cs == '' or cs == '0' or string.lower(cs) == 'false' then
return nil
else
return b
end
end
Here, although input c
is never present in what is returned, by calling eval_string
upon it we can reason about it.
Events
To help extensions react to how the document is being processed, there are several events which are triggered.
These are triggered on objects which extend the Component
class defined in the std.base
module and are as follows.
on_start
, executed once after all extensions are loadedon_iter_start
, executed at the start of each iterationon_iter_end
, executed at the end of each iterationon_end
, executed once, after the final iteration but before output
There are a number of classes which may be imported from the std.std
module which provide frameworks for storing data whilst reacting to these events.
For example, the table of contents is a subclass of Component
which stores the names and numbers of headings as the document is processed, requesting another run if the table of contents at the end of the previous run is different to that at the end of the current (e.g. a page-number has been updated by some other change).
A re-iteration can be requested by calling the requires_reiter
function in Lua.
This will cause the typesetting loop to be run again, unless the (user-configurable) number of maximum iterations has been reached.
The number of the current iteration (starting from 1) is accessible through the em_iter
variable.
Input Drivers
Emblem can take input of any format for which it has an input driver.
When Emblem inputs a file through the .include
directive, a language can optionally be specified in the second parameter to determine the parser to use.
This language is used to look up the parser to use in the std.in.drivers.known_languages
table.
An input driver is simply a module which adds at least one parser function into this table.
Output Drivers
Emblem is capable of outputting to any format for which it has an output driver.
The binary itself contains some output drivers, but it is also possible to import ones from other sources as desired.
In analogy with input drivers, there exists a table, std.out.drivers.output_drivers
, which is used when looking up the language into which the document will be output.
An output driver is simply module which adds at least one outputting function into this table.
std.ast
Provides an interface for constructing Emblem document nodes.
- Author: Edward Jones
- Date: 2021-09-17
mkcall
Make a function which constructs a call to a given directive.
- Param
Name
: of the directive to call
Returns: A function which takes arguments and returns a call upon those arguments
mkcall = (name) -> (args) -> ...
std.base
Provides the base library for use with extensions.
- Author: Edward Jones
- Date: 2021-09-17
close_var_scope
Closes the most recently-opened variable scope.
close_var_scope = -> ...
copy_loc
Copy a source-code location.
Returns: A copy of the current source code location
copy_loc = -> ...
em_loc
Get the current location in the source code.
Returns: A pointer to the current source location
em_loc = -> ...
eval_string
Evaluates a node pointer and extracts the text contained in and below it.
- Param
d
: The userdata pointer to evaluate and extract from
Returns: A string which represents all text at and beneath d
eval_string = (d) -> ...
get_var
Gets the value of a given variable, if the variable name starts with n > 0 exclamation marks, then that many possible matches are skipped while searching from the innermost scope.
- Param
rn
: The raw variable name as a string or core pointer - Param
d
: An optional default value to return ifrn
does not exist
Returns: The value of variable rn
in the current scope otherwise d
get_var = (rn, d) -> ...
is_instance
Tests whether an object is an instance of a given class.
- Param
cls
: The class to test, may be a class name or a class itself - Param
obj
: The object to test
Returns: true
if obj
is an instance of a sub-class of cls
, otherwise false
is_instance = (cls, obj) -> ...
iter_num
Returns the number of the current iteration of the typesetting loop (starts at 1).
Returns: The number of times the typesetting loop has been started this run
iter_num = -> ...
node_string
Extracts the text beneath a given node.
- Param
n
: The node to convert into a string, must be a table
Returns: The text stored at and under the given node
node_string = (n) -> ...
open_var_scope
Opens a new variable scope.
open_var_scope = -> ...
set_var
Set a variable to a given value, if the variable name starts with n > 0 exclamation marks, then a search is performed to set the n-th variable with the same name in found whilst searching parent scopes.
- Param
n
: The name of the variable (string or code pointer) - Param
v
: The value to set (not changed by this operation) - Param
surrounding_scope
: If set to true, search is bumped up one scope (useful for the .set-var directive which would otherwise have the set value swallowed in its own scope)
set_var = (n, v, surrounding_scope=false, search=false) -> ...
set_var_string
Set a given variable to a given value as a string.
- Param
n
: Variable name as forset_var
- Param
v
: Value to evaluate then set to n - Param
w
: Scope widening paramerer as forset_var
set_var_string = (n, v, ...) -> ...
wrap_indices
Wrap the __get and __set methods into the __index and __newindex methods if available.
- Param
object
: (table) to wrap
Returns: nil
wrap_indices = => ...
std.bib
Provides bibliographies and citations.
- Author: Edward Jones
- Date: 2021-09-17
get_cite_style
Get the current citation style.
Returns: The current citation style
get_cite_style = -> ...
set_cite_style
Set the current citation style to a given style.
- Param
style
: The new current citation style
set_cite_style = (style) -> ...
std.edit
Provides functions to compute and interpret edit distance.
- Author: Edward Jones
- Date: 2021-09-24
closest
Find the closest string to a given one from a list of strings.
- Param
s
: A source string - Param
ts
: A list of strings
Returns: The t in ts
which is closest to s
closest = (s, ts) -> ...
edit_distance
Compute the edit distance between two strings.
- Param
u
: A string - Param
v
: Another string
Returns: The edit distance between u
and v
edit_distance = (u, v) -> ...
unknown_x_msg
Create a suggestion message when an incorrect value was supplied which should have been in a list of options.
- Param
x
: The name of the type ofv
- Param
v
: The incorrect value given - Param
vs
: The list of valid values of whichv
should have been a member
Returns: A user-friendly message which suggests the u
in vs
which is closest to v
if it is not too distant
unknown_x_msg = (x, v, vs) -> ...
std.events
Prpvides functionality for responding to typesetting events.
- Author: Edward Jones
- Date: 2021-09-17
std.expr
Provides a framework for evaluating expressions.
- Author: Edward Jones
- Date: 2021-09-25
expr
Takes a string (or document pointer), parses it and evaluates it as an expression.
- Param
str
: The text to parse and evaluate
Returns: An integer which was evaluated from the expression represented by str
or nil on failure
expr = (s) -> ...
std.func
Provides functional abstractions.
- Author: Edward Jones
- Date: 2021-09-17
co_to_list
Creates a list from the values yielded from a coroutine.
- Param
c
: Coroutine from which to extract values
Returns: A list of values returned from c
co_to_list = (c) -> ...
co_to_table
Constructs a table from a coroutine which yields {k,v} pairs.
- Param
c
: Coroutine from which to extract kv-pairs
Returns: A table t
such that for each {k,v}
yielded from c
, t\[k]
= v
co_to_table = (c) -> ...
do_nothing
A function which does nothing.
do_nothing = -> ...
filter
Creates a coroutine whihc filters the values of a coroutine by a predicate.
- Param
p
: A predicate - Param
es
: A coroutine which yields values to be filtered
Returns: A coroutine which yields the values of e
of es
which satisfy p(e)
in the order they are yielded from es
filter = (p, es) -> ...
filter_list
Returns a list of values of a given list which satisfy a predicate.
- Param
p
: A predicate - Param
es
: A list of values
Returns: A list of elements e
of es
which satisfy p(e)
filter_list = (p, es) -> ...
id
Identity function, takes a value and returns it.
- Param
x
: Value to return
Returns: x
id = (x) -> ...
int
Creates a coroutine which yields the integers.
Returns: a coroutine which yields the integers
int = -> ...
key_list
Construct a list of keys from a table.
- Param
t
: A table from which to extract keys
Returns: A list of keys in t
key_list = (t) -> ...
keys
Creates a coroutine which yields the keys of a table.
- Param
t
: A table from which to extract keys
Returns: A coroutine which yields the keys of t
keys = (t) -> ...
kv_pairs
Creates a coroutine which yields {k,v} pairs of a table.
- Param
t
: A table from which to extract key-value pairs
Returns: A coroutine which yields the kv-pairs of t
kv_pairs = (t) -> ...
map
Maps the values yielded by a coroutine with a given function.
- Param
f
: The mapping function - Param
es
: The values to map
Returns: a coroutine which yields values f(e)
for each e
yielded from es
map = (f, es) -> ...
nat
Creates a coroutine which yields the natural numbers.
Returns: A coroutine which yields the natural numbers
nat = -> ...
seq
Creates a coroutine which yields the values of a sequence.
- Param
first
: The first value of the sequence - Param
last
: The last value of the sequence - Param
step
: The difference between consecutive yielded values
Returns: A coroutine which yields values starting from first
, in increments of step
until last
is reached
seq = (first, last, step) -> ...
take
Takes values from a coroutine whilst a predicate holds.
- Param
p
: Predicate to check - Param
es
: From which to take values
Returns: A coroutine which yields values e
of es
for which p(e)
holds, until the first which does not (at which point the coroutine finishes
take = (p, es) -> ...
value_list
Construct a list of values in a table.
- Param
t
: A table from which to extract values
Returns: A list of the values in v
value_list = (t) -> ...
values
Creates a coroutine which yields the values of a table.
- Param
t
: A table from which to extract values
Returns: A coroutine which yields the values of t
values = (t) -> ...
whole
Creates a coroutine which yields whole numbers.
Returns: A coroutine which yields the whole numbers
whole = -> ...
std.hdr
Provides headings and tables of contents.
- Author: Edward Jones
- Date: 2021-09-17
std.in/
This module collection provides the machinery for defining and implementing the core set of input drivers.
Its most important file is std.in.drivers
, which contains the code to handle parsing inputs and which defines some useful classes for input drivers.
All other modules in this module collection are implementations of input drivers which are always available when running Emblem.
std.in.drivers
Manages input drivers, heeps associations for mapping input languages to parsers, and file extensions to input languages.
- Author: Edward Jones
- Date: 2021-09-17
std.lingo
Provides a rudamentary scripting language for use in documents.
- Author: Edward Jones
- Date: 2021-09-17
cond
Takes input of value and evaluates it as a condition.
- Param
c
: Lua value or core pointer to evaluate as a condition
Returns: false if c
is false
or is the empty string, ‘0’ or ‘false’ (case-insensitive), otherwise true
cond = (c) -> ...
toint
Converts a value to a condition integer (for a more compact representation.
- Param
b
: A value to check
Returns: 0 if b is 0, the empty string, false or nil, otherwise 1
toint = (b) -> ...
std.log
Provides logging functions.
- Author: Edward Jones
- Date: 2021-09-17
std.out/
This module collection provides the machinery for defining and implementing the core set of output drivers.
Its most important file is std.out.drivers
, which contains the code to translation to output formats and writing to files, and defines some useful classes for output drivers.
All other modules in this module collection are implementations of input drivers which are always available when running Emblem.
std.out.bb
Provides an output driver for BBCode.
- Author: Edward Jones
- Date: 2021-09-24
std.out.drivers
Provides a framework for handling and creating output drivers.
- Author: Edward Jones
- Date: 2021-09-24
get_output_driver
Gets the current output driver.
Returns: The current output driver
get_output_driver = -> ...
set_output_driver
Sets the current output driver.
- Param
dname
: The name of the new output driver
set_output_driver = (dname) -> ...
std.out.html
Provides an output driver for HTML.
- Author: Edward Jones
- Date: 2021-09-24
std.out.latex
Provides an output driver for LaTeX.
- Author: Edward Jones
- Date: 2021-09-24
std.out.md
Provides an output driver for markdown.
- Author: Edward Jones
- Date: 2021-09-24
std.ref
Implements the label-anchor-reference system for cross-referencing within documents.
- Author: Edward Jones
- Date: 2021-09-17
get_label
Get the label in the current context.
Returns: The value of the label in the current context
get_label = -> ...
set_label
Set the current label.
- Param
c
: The new label
set_label = (c) -> ...
std.show
Provides functions to show the values of lua-tables in more human-readible formats.
- Author: Edward Jones
- Date: 2021-09-17
show
Construct a string-representation of an object.
- Param
v
: Value for which to construct a string-representation
Returns: A string representation of v
show = (v) -> ...
showp
Construct a prettier string-representation of an object.
- Param
v
: Value for which to construct a string-representation
Returns: A string representation of v
which is slightly prettier than that of show
.
showp = (v) -> ...
std.store
Allows values to be stored between executions of Emblem.
- Author: Edward Jones
- Date: 2021-09-17
std.style
Provides wrappers for basic styling directives.
- Author: Edward Jones
- Date: 2021-09-17
std.util
Provides miscellaneous utility functions.
- Author: Edward Jones
- Date: 2021-09-17
argmax
Computes the argmax of a function over a set of values.
- Param
f
: The function to compute - Param
vs
: a list of values to test
Returns: The argmax of f
over vs
, that is the v in vs
which maximises f(v)
, also returns the value of f(v)
argmax = (f, vs) -> ...
argmin
Computes the argmin of a function over a set of values.
- Param
f
: The function to compute - Param
vs
: a list of values to test
Returns: The argmin of f
over vs
, that is the v in vs
which minimises f(v)
, also returns the value of f(v)
argmin = (f, vs) -> ...
bool_to_int
Converts a boolean value to an integer.
- Param
b
: The value to check
Returns: true if b
is considered true otherwise 0
bool_to_int = (b) -> ...
char_at
Returns a string representing the character at a given index.
- Param
i
: The index to obtain - Param
s
: The string from which to extract
Returns: A string which contains just the i
-th character of s
char_at = (i, s) -> ...
chars
Creates a coroutine which yields the characters of a string.
- Param
s
: The string from which to yield
Returns: A coroutine which yields strings of length 1 which represent the individual characters of s
chars = (s) -> ...
elem
Returns whether a value is an element of a list.
- Param
v
: A value to check membership - Param
vs
: A list of values to search
Returns: true if v
is a value if vs
, otherwise false
elem = (v, vs) -> ...
eq
Recursively computes the equality of two values.
- Param
a
: A value to check - Param
b
: A value to check
Returns: true if a
and b
are equal, otherwise false
eq = (a,b) -> ...
extend
Computes the result of extending a given list by other lists (pure).
- Param
xs
: A list to extend - Param
...
: Further lists
Returns: A list which is the concatenation of the lists passed, in the order passed.
extend = (xs, ...) -> ...
is_list
Returns whether a given value represents a list.
- Param
l
: The value to check
Returns: true if l
is a list, otherwise false
is_list = (l) -> ...
non_nil
Returns whether a value is not nil.
- Param
v
: Value to check
Returns: true if v
is not nil otherwise false
non_nil = (v) -> ...
on_iter_wrap
Wrap a given function so that it is only run on a particular typesetting iteration.
- Param
f
: The function to wrap
Returns: A function which takes a number n and the parameters to f which when called will only execute f if the current typesetting loop iteration is equal to n
on_iter_wrap = (f) -> (n, ...) -> ...
sorted
Sort a list and then return it (impure).
- Param
t
: A list to sort - Param
...
: Further parameters totable.sort
Returns: t
, having been sorted in place
sorted = (t, ...) -> ...
C API
Following the C-style of headers and body files, this section of the docs follows the same partition between module interfaces, which correspond to files *.h
in the source repo, and module bodies, which correspond to files *.c
.
It must be noted that some of the files in this section of the docs do not directly exist in the source as they are auto-generated.
Some examples of this are the lexer body and parser body, which is are generated from Flex and Bison sources respectively.
Module interfaces
This section details the interfaces exposed from each core module, that is, documentation present in files *.h
in the repository.
data/
Defines the generic data structures used to represent internal data.
array.h
Exposes functions for handling arrays, fixed-length composite data-structures, with elements accessible by index.
- Author: Edward Jones
- Date: 2021-09-17
dest_arr
Destroy an array object.
- Param
arr
: Pointer to the array to destroy - Param
ed
: Element destructor function or NULL
void dest_arr(Array* arr, func_sig(void, ed, (void*)));
dest_arr_iter
Destror an array iterator.
- Param
iter
: Pointer to the iterator to destroy
void dest_arr_iter(ArrayIter* iter);
get_arrv
Get array value at a given index.
- Param
ret
: Pointer to a Maybe type to populate with the result - Param
arr
: Pointer to the array containing the required value - Param
idx
: Index of the array element to find
void get_arrv(Maybe* ret, Array* arr, size_t idx);
iter_arr
Iterate once over an array.
- Param
v
: Pointer to the return value - Param
iter
: Pointer to the array iterator to use
Returns: True iff a value was successfully written to v
, false iff there were no elements left
bool iter_arr(void** v, ArrayIter* iter);
make_arr
Make an array object, a block of memory of finite length.
- Param
arr
: Pointer to the array to initialise. - Param
cnt
: Length of the array
Returns: True iff memory allocation was successful
bool make_arr(Array* arr, size_t cnt);
make_arr_iter
Make an array iterator.
- Param
iter
: Pointer to the iterator to make - Param
arr
: Pointer to the array to iterate over
void make_arr_iter(ArrayIter* iter, Array* arr);
set_arrv
Set the value of an array at a particular index.
- Param
arr
: Pointer to the array to modify - Param
idx
: Index of the value to change - Param
val
: Value to assign to arr[idx]
Returns: Returns true iff idx
is a valid index of arr
bool set_arrv(Array* arr, size_t idx, void* val);
cmp.h
Exposes comparator functions.
- Author: Edward Jones
- Date: 2021-09-17
CMP_SIG
Compare float.
- Param
v1
: A float to compare - Param
v2
: Another float to compare
CMP_SIG(float);
CMP_SIG
Compare strings.
- Param
v1
: A string to compare - Param
v2
: Another string to compare
CMP_SIG(str);
CMP_SIG
Compare ints.
- Param
v1
: An int to compare - Param
v2
: Another int to compare
CMP_SIG(int);
CMP_SIG
Compare size_ts.
- Param
v1
: A size_t to compare - Param
v2
: Another size_t to compare
CMP_SIG(size_t);
CMP_SIG
Compare doubles.
- Param
v1
: A double to compare - Param
v2
: Another double to compare
CMP_SIG(double);
CMP_SIG
The signature of a comparator.
- Param
name
: The name of the comparison functino
Returns: The signature of the comparison function for name
s
# define CMP_SIG(name) Cmp cmp_##name##s(void* v1, void* v2)
CMP_SIG
Compare characters.
- Param
v1
: A character to compare - Param
v2
: Another character to compare
CMP_SIG(char);
CMP_SIG
Compare void pointer. This compares the numerical value of the pointers, not their content!
- Param
v1
: A void pointer to compare - Param
v2
: Another void pointer to compare
CMP_SIG(ptr);
Cmp
Type of a function which compares two values.
- Param
v1
: A value to compare - Param
v2
: Another value to compare
Returns: The comparison result of v1
against v2
typedef Cmp(fun Comparator)(void* v1, void* v2);
streq
Check if two C-strings are equal (to improve readability with strcmp).
- Param
s
: Pointer to a string to check - Param
t
: Pointer to a string to check
Returns: true if the strings are equal, false otherwise
bool streq(char const* s, char const* t);
dest-free.h
Defines preprocessor rules for the creation of functions which destroy and then free memory of a given type.
- Author: Edward Jones
- Date: 2021-09-17
destructor.h
Provides type definition for destructors, functions responsible for finalising and freeing the memory of a given structure.
- Author: Edward Jones
- Date: 2021-09-17
void
Type of an object destructor.
- Param
o
: Pointer to an object to destroy
typedef void(fun Destructor)(void* o);
either.h
Exposes functions for processing Either objects, which can represent values of two types.
- Author: Edward Jones
- Date: 2021-09-17
dest_either
Destroy an either-type object.
- Param
e
: Pointer to the either object to destroy - Param
led
: Element destructor for the left - Param
red
: Element destructor for the right
void dest_either(Either* e, Destructor led, Destructor red);
fmap_either
Fmap over either-type object.
- Param
eo
: Output either type - Param
ei
: Input Either type - Param
f
: Function to apply to the contents ofei
to produceeo
void fmap_either(Either* eo, Either* ei, Fmap f);
make_either_left
Construct an either-type object with the left constructor.
- Param
e
: Pointer to the either object to initialise - Param
left_val
: Value to place into the left constructor
void make_either_left(Either* e, void* left_val);
make_either_right
Construct an either-type object with the right constructor.
- Param
e
: Pointer to the either object to initialise - Param
right_val
: Value to place into the right constructor
void make_either_right(Either* e, void* right_val);
succ_either
Return whether a given either-type object represents a successful result.
- Param
e
: Pointer to the either object to check
Returns: true if e uses the right constructor otherwise false
bool succ_either(Either* e);
fmap.h
Provides type definition for fmap functions, which allow computations on data within structures.
- Author: Edward Jones
- Date: 2021-09-17
Fmap
Type of a function which can be used in an fmap operation.
- Param
o
: The value outputted by the fmap - Param
i
: The unput value
typedef void (*Fmap)(void** o, void* i);
hash.h
Exposes hash functions for standard data types.
- Author: Edward Jones
- Date: 2021-09-17
HASH_SIG
Hash a string.
- Param
v
: A value to hash
Returns: The hash of v
HASH_SIG(str);
HASH_SIG
Hash a size_t.
- Param
v
: A value to hash
Returns: The hash of v
HASH_SIG(size_t);
HASH_SIG
Hash a void pointer. This operates on the numerical value of the pointer, and not its content!
- Param
v
: A value to hash
Returns: The hash of v
HASH_SIG(ptr);
HASH_SIG
Hash an int.
- Param
v
: A value to hash
Returns: The hash of v
HASH_SIG(int);
HASH_SIG
Signature of a hashing function.
- Param
name
: The name of the type to hash
Returns: A signature for a function which hashes name
s
#define HASH_SIG(name) Hash hash_##name(void* v)
HASH_SIG
Hash a character.
- Param
v
: A value to hash
Returns: The hash of v
HASH_SIG(char);
Hasher
Type of a function which takes an object and returns its hash.
- Param
v
: A value to hash
Returns: The hash of v
typedef Hash (*Hasher)(void* v);
list-array-conversions.h
Exposes conversion functiosn between arrays and lists.
- Author: Edward Jones
- Date: 2021-09-17
make_arr_from_list
Create an array from a list.
- Param
arr
: Array to write to - Param
l
: List ot read
void make_arr_from_list(Array* arr, List* l);
make_list_from_arr
Create a list from an array. List myst be freed.
- Param
l
: Pointer to the list to create - Param
arr
: Pointer to the array to copy
void make_list_from_arr(List* l, Array* arr);
list.h
Exposes functions to handle the list data structure.
- Author: Edward Jones
- Date: 2021-09-17
all_list
Return whether all elements of a list of booleans are true.
- Param
l
: List to check
Returns: true iff all elements of the list are true
bool all_list(List* l);
any_list
Return whether there is some element in a list of booleans which is true.
- Param
l
: List to check
Returns: true iff at at least one element of a list is true.
bool any_list(List* l);
append_list
Append a value to a list.
- Param
l
: Pointer to the list to affect - Param
v
: Pointer to the node to add
Returns: true iff successful
bool append_list(List* l, void* v);
append_list_node
Append a list node to a list.
- Param
l
: Pointer to the list to affect - Param
ln
: Pointer to the node to add
Returns: true iff successful
bool append_list_node(List* l, ListNode* ln);
cconcat_list
Concatenate a list in place, affecting exactly one list (takes time linear in the length of the second list).
- Param
r
: List to output - Param
l
: List to concatenate tor
void cconcat_list(List* r, List* l);
concat_list
Concatenate a pair of lists into another.
- Param
r
: The list outputted - Param
l1
: The first list to concatenate. - Param
l2
: The second list to concatenate.
void concat_list(List* r, List* l1, List* l2);
dest_list
Destroy a list. Does not affect list elements.
- Param
l
: Pointer to the list to destroy. - Param
freeNodes
: Iff not false, frees the memory used by the contained ListNodes - Param
ed
: Element destructor called on the data field of each ListNode or NULL
void dest_list(List* l, Destructor ed);
dest_list_iter
Destroy an iterator.
- Param
i
: Pointer to the iterator to destroy
void dest_list_iter(ListIter* i);
dest_list_node
Destroy a list node.
- Param
ln
: Pointer to the list node to destroy - Param
ed
: Element destructor to be called on the data field of the list node or NULL
void dest_list_node(ListNode* ln, Destructor ed);
dest_reversed_list_iter
Destroy a reversed iterator.
- Param
i
: Pointer to the iterator to destroy
void dest_reversed_list_iter(ReversedListIter* i);
iconcat_list
Concatenate a pair of lists in place. Takes constant time and the second list is no longer valid and should be.
- Param
l1
: List to concatenate (left) - Param
l2
: List to concatenate (right)
void iconcat_list(List* r, List* l);
in_list
Checks whether a given element is in a list (by reference-equality).
- Param
l
: Pointer to the list to check - Param
val
: Pointer to the value to test
Returns: true iff the value is in the list
bool in_list(List* l, void* val);
in_list_eq
Tests whether there exists an element in a given list which is equal under some function.
- Param
m
: Maybe container for the value found to be equal toval
undercmp
- Param
l
: Pointer to the list to check - Param
cmp
: Comparator function to check, val is placed into the first argument. - Param
val
: The value to test against
void in_list_eq(Maybe* m, List* l, Comparator cmp, void* val);
is_empty_list
Return whether a list is empty.
- Param
l
: List to check
Returns: false iff the list is not empty
bool is_empty_list(List* l);
iter_list
Move the iterator to the next element in the list.
- Param
v
: A location where the value at the current point in the list will be written - Param
i
: Pointer to the iterator to use
Returns: false if there are no more elements to iterate, true otherwise
bool iter_list(void** v, ListIter* i);
iter_list_nodes
Move the iterator to the next node in the list.
- Param
n
: A location where a pointer to the current node will be written - Param
i
: Pointer to the iterator to use
Returns: false if there are no more elements to iterate, true otherwise
bool iter_list_nodes(ListNode** n, ListIter* i);
iter_list_reversed
Move the iterator to the next element in the list (when iterated backwards).
- Param
val
: A location where the value at the current point in the list will be written - Param
i
: Pointer to the iterator to use
Returns: false if there are no more elements to iterate, true otherwise
bool iter_list_reversed(void** val, ReversedListIter* i);
make_list
Initialise a list.
- Param
l
: Pointer to the area of memory to initialise
void make_list(List* l);
make_list_iter
Initialise an iterator for a list.
- Param
i
: Pointer to the iterator to initialise - Param
l
: Pointer to the list which the iterator will run over
void make_list_iter(ListIter* i, List* l);
make_list_node
Initialise a list node.
- Param
ln
: Pointer to the memory to initialise - Param
data
: The data to store in the node
void make_list_node(ListNode* ln, void* data);
make_reversed_list_iter
Initialise a reverse-iterator for a list.
- Param
i
: Pointer to the iterator to initialise - Param
l
: Pointer to the list which the iterator will run over
void make_reversed_list_iter(ReversedListIter* i, List* l);
prepend_list
Add a value to the head of a list.
- Param
l
: pointer to the list to change - Param
v
: Pointer to the value to prepend
Returns: true iff successful
bool prepend_list(List* l, void* v);
prepend_list_node
Add a node to the front of a list.
- Param
l
: Pointer to the list to change - Param
ln
: Pointer to the node to add
Returns: true iff successfil
bool prepend_list_node(List* l, ListNode* ln);
remove_list_node
Remove a node from a list.
- Param
l
: Pointer to the list which containsln
- Param
ln
: Pointer to the node to remove
Returns: Returns true iff successful
bool remove_list_node(List* l, ListNode* ln);
set_sublist
Declare whether all nodes of a list are now entirely contained within another, preventing double-frees.
- Param
l
: List to modify - Param
is_sublist
: Whetherl
is a sublist of another list
void set_sublist(List* l, bool is_sublist);
locked.h
Exposes functions for data-structure locking, designed to reduce the possibility of race conditions.
- Author: Edward Jones
- Date: 2021-09-17
map.h
Exposes functions to handle the Map data structure.
- Author: Edward Jones
- Date: 2021-09-17
dest_map
Destroy a map.
- Param
map
: Pointer to the map to destroy - Param
ved
: Value destructor
void dest_map(Map* map, Destructor ved);
dest_map_iter
Destroy a map iterator.
- Param
iter
: Pointer to the iterator to destroy
void dest_map_iter(MapIter* iter);
get_map
Get a value from a map.
- Param
mo
: Return value populated with the value if present - Param
map
: Pointer to the map to retrieve the value from - Param
key
: Key to use
void get_map(Maybe* mo, Map* map, void* key);
iter_map
Iterate once over key-value pairs using a specified iterator.
- Param
p
: Pointer to the return pair - Param
iter
: Pointer to the iterator to use
Returns: true
if iteration was successful, otherwise false
as iteration has finished
bool iter_map(Pair** p, MapIter* iter);
iter_map_keys
Iterate once over the keys in a map using a given iterator.
- Param
k
: Pointer to the key to return - Param
iter
: Pointer to the iterator to use
Returns: true
iff iteration was successful, otherwise false
as iteration has finished
bool iter_map_keys(void** k, MapIter* iter);
iter_map_values
Iterate over the values in a map using a given iterator.
- Param
v
: Pointer to the value to return - Param
iter
: Pointer to the iterator to use
Returns: true
iff iteration was successful, otherwise false
as iteration has finished
bool iter_map_values(void** v, MapIter* iter);
make_map
Make a map.
- Param
map
: Pointer to the map to create - Param
hash
: Hash function to use on keys - Param
kcmp
: Comparator to use on keys - Param
ked
: Destructor to use on keys
Returns: true
iff sufficient memory could be allocated to the map, false
otherwise
bool make_map(Map* map, Hasher hash, Comparator kcmp, Destructor ked);
make_map_from_list
Make a map from a list of key-value Pairs.
- Param
map
: Pointer to the map to create - Param
list
: Pointer to the list of key-value Pairs - Param
hash
: Hash function to use on keys - Param
kcmp
: Comparison function to use on keys - Param
ked
: Destructor for the keys
Returns: true
iff enough memory could be allocated to create the map, false
otherwise
bool make_map_from_list(Map* map, List* list, Hasher hash, Comparator kcmp, Destructor ked);
make_map_iter
Make an iterator over the map.
- Param
iter
: Pointer to the iterator to make - Param
map
: Pointer to the map to iterate over
void make_map_iter(MapIter* iter, Map* map);
push_map
Push a value into a map.
- Param
oldval
: Pointer to a maybe type to be populated with the old value at a given key, if present - Param
m
: Pointer to the map to populate - Param
k
: Key to push - Param
v
: Value to push
Returns: true
iff sufficient memory was available during pushing and (if necessary) resizing, otherwise false
bool push_map(Maybe* oldval, Map* m, void* k, void* v);
maybe.h
Exposes functions to handle the maybe data-type, which can represent either a value or nothing.
- Author: Edward Jones
- Date: 2021-09-17
dest_maybe
Destroy a maybe-type object. Any stored data must be destroyed separately.
- Param
m
: Pointer to a meybe object to destroy - Param
ed
: Element destructor to be called on the just field if the constructor permits
void dest_maybe(Maybe* m, Destructor ed);
fmap_maybe
Apply a function to the stored data in the maybe and output a new maybe object with the new value.
- Param
mo
: Ouptut maybe object. Should be an uninitialised maybe-type pointer. - Param
mi
: Input maybe object which will havef
applied to it - Param
f
: Function to apply to any data insidemi
void fmap_maybe(Maybe* restrict mo, Maybe* restrict mi, Fmap f);
make_maybe_just
Construct a maybe-type object with the just constructor.
- Param
m
: Pointer to a location to initialise - Param
data
: Data to store in the just
void make_maybe_just(Maybe* m, void* data);
make_maybe_nothing
Construct a maybe-type object with the nothing constructor.
- Param
m
: Pointer to a location to initialise
void make_maybe_nothing(Maybe* m);
succ_maybe
Check whether a maybe-type object represents a success.
- Param
m
: Pointer to a maybe object
Returns: Returns true if the constructor of m
is Just, otherwise false
bool succ_maybe(Maybe* m) __attribute__((pure));
str.h
Exposes functions to hendle the string data-type.
- Author: Edward Jones
- Date: 2021-09-17
arr_to_str
Create a string from an array of characters.
- Param
str
: Pointer to the string to create - Param
arr
: Pointer to the array to read
void arr_to_str(Str* str, Array* arr);
copy_into_str
Copy the value of a string into another string starting at a given index. Only mutates if the entire string.
- Param
cont
: The container string to write into - Param
ins
: The insertion string to read from - Param
startIdx
: The index to start writing from
Returns: true
iff startIdx
is low enough to allow all of ins
to fit in cont
otherwise false
bool copy_into_str(Str* cont, Str* ins, size_t startIdx);
dest_free_sig
Destroy and free a string.
- Param
str
: String to destroy
dest_free_sig(str, Str);
dest_str
Destroy a string and free its memory if required.
- Param
str
: Pointer to the string to destroy
void dest_str(Str* str);
get_strc
Get the character at a specified index.
- Param
ret
: Pointer to the return value, creates a Maybe object of constructor JUST iff the index was valid. In this - Param
str
: Pointer to the string to search - Param
idx
: Index to get if valid
void get_strc(Maybe* ret, Str* str, size_t idx);
make_str
Make an empty string.
- Param
str
: Pointer to the String object to initialise
void make_str(Str* str);
make_strc
Make a string by copying another.
- Param
str
: Pointer to the string to make - Param
raw
: Pointer to the raw characters to copy
void make_strc(Str* str, char* raw);
make_strl
Make a string of specified length.
- Param
str
: Pointer to the string to initialise - Param
len
: Length of the string to create
Returns: True iff memory was successfully allocated
bool make_strl(Str* str, size_t len);
make_strr
Make a string by reference to a raw value, freeing the raw value when destroyed.
- Param
str
: Pointer to the string to make - Param
raw
: Pointer to the raw characters
void make_strr(Str* str, char* raw);
make_strv
Make a string by reference to a raw value.
- Param
str
: Pointer to the string to make - Param
raw
: Pointer to the raw characters
void make_strv(Str* str, char* raw);
set_strc
Set the value of a character in a string at a specified index.
- Param
str
: Pointer to the string to mutate - Param
idx
: Index of the mutation - Param
val
: Value to write
Returns: true
if idx
was valid else false
bool set_strc(Str* str, size_t idx, char val);
str_to_arr
Create an array (of character values) from a string.
- Param
arr
: Pointer to the array to make - Param
str
: Pointer to the string whose data will be copied
void str_to_arr(Array* arr, Str* str);
tuple.h
Provides definitions for tuples, fixed-length heterogeneous data-types.
- Author: Edward Jones
- Date: 2021-09-17
unit.h
Exposes functions to handle the Unit data type, which cannot represent any information.
- Author: Edward Jones
- Date: 2021-09-17
dest_unit
Destroy a unit.
- Param
unitp
: Pointer to a unit to destroy
void dest_unit(Unit* unitp);
make_unit
Make a unit.
- Param
unitp
: Pointer to the unit to make
void make_unit(Unit* unitp);
doc-struct/
This directory contains modules used to define and handle document structure.
ast.h
Exposes functions to handle Emblem document structures.
- Author: Edward Jones
- Date: 2021-09-17
discern-pars.h
Exposes functionality for automatically placing paragraph nodes in a document.
- Author: Edward Jones
- Date: 2021-09-17
location.h
Exposes functions to handle the Location structure for keeping track of places in the document source.
- Author: Edward Jones
- Date: 2021-09-17
drivers/
This directory contains files used for interacting with output drivers. It is responsibly for both defining a set of core output drivers, and also interfacing with those defined in extension-space.
driver-params.h
Exposes functionality to handle output drivers parameters.
- Author: Edward Jones
- Date: 2021-09-17
drivers.h
Exposes functions for handling output drivers.
- Author: Edward Jones
- Date: 2021-09-17
driver-util.h
This module lacks a documentation header.
write-out.h
This module lacks a documentation header.
ext/
This directory contains the code which manages and interacts with extension space. It allows the execution of arbitrary Lua code as the program executes and is the module primarily responsible for Emblem’s extensibility.
In the source code, the standard extension library is defined in the lib/
directory beneath this one.
debug.h
Exposes functions for debugging the Lua API stack.
- Author: Edward Jones
- Date: 2021-09-17
ext-env.h
Exposes functions for handling the Lua extension environment.
- Author: Edward Jones
- Date: 2021-09-17
ext-loader.h
Exposes the extension loader.
- Author: Edward Jones
- Date: 2021-09-17
ext-params.h
Exposes functions to handle extension-environment parameters.
- Author: Edward Jones
- Date: 2021-09-17
lua-ast-io.h
Exposes functions for translating between Lua tables and document trees.
- Author: Edward Jones
- Date: 2021-09-17
lua-constants.h
Exposes function for setting-up interface constants in the extension environment.
- Author: Edward Jones
- Date: 2021-09-17
lua-em-parser.h
Exposes interface between core Emblem parser and extension space.
- Author: Edward Jones
- Date: 2021-09-17
lua-events.h
Exposes functions for issuing typesetting events to extension-space.
- Author: Edward Jones
- Date: 2021-09-17
lua.c
Provides functions for executing evaluation-passes on document trees.
- Author: Edward Jones
- Date: 2021-09-17
lua-lib-load.h
Exposes function to load standard Lua libraries into extension-space.
- Author: Edward Jones
- Date: 2021-09-17
lua-pointers.h
This module lacks a documentation header.
style.h
Exposes function for importing stylesheets from extension-space.
- Author: Edward Jones
- Date: 2021-09-17
logs/
This directory holds code which is responsible for logging-output. It also provides an interface between its logging functions and extension-space.
ext-log.h
Exposes functions to make core logging functions available to extension-space.
- Author: Edward Jones
- Date: 2021-09-17
logs.h
Exposes logging functions.
- Author: Edward Jones
- Date: 2021-09-17
fini_logs
Finalise logging.
void fini_logs(void);
init_logs
Initialise logging.
- Param
args
: Parsed command-line arguments
void init_logs(Args* args);
log_debug
Write a debug message to stderr.
- Param
format
: debug message format (printf) - Param
...
: Possible printf arguments
void log_debug(const char* restrict format, ...) __attribute__((format(printf, 1, 2)));
log_err
Write an error stderr.
- Param
format
: Error format (printf) - Param
...
: Possible printf arguments
void log_err(const char* restrict format, ...) __attribute__((cold)) __attribute__((format(printf, 1, 2)));
log_err_at
Write an error stderr referencing a source location.
- Param
loc
: The source location to reference - Param
format
: Error format (printf) - Param
...
: Possible printf arguments
void log_err_at(Location* loc, const char* restrict format, ...) __attribute__((cold))
log_info
Write information to stderr.
- Param
format
: Information format (printf) - Param
...
: Possible printf arguments
void log_info(const char* restrict format, ...) __attribute__((format(printf, 1, 2)));
log_warn
Write a warning to stderr.
- Param
format
: Warning format (printf) - Param
...
: Possible printf arguments
int log_warn(const char* restrict format, ...) __attribute__((format(printf, 1, 2)));
log_warn_at
Write a warning to stderr referencing a source location.
- Param
loc
: The source location to reference - Param
format
: Warning format (printf) - Param
...
: Possible printf arguments
int log_warn_at(Location* loc, const char* restrict format, ...);
vlog_debug
Write a debug message to stderr, using a va_list of format-arguments.
- Param
format
: debug message format (printf) - Param
...
: Possible printf arguments
void vlog_debug(const char* restrict format, va_list va);
vlog_err
Write an error stderr, using a va_list of format-arguments.
- Param
format
: Error format (printf) - Param
...
: Possible printf arguments
void vlog_err(const char* restrict format, va_list va);
vlog_err_at
Write an error stderr referencing a source location, using a va_list of format-arguments.
- Param
loc
: The source location to reference - Param
format
: Error format (printf) - Param
...
: Possible printf arguments
void vlog_err_at(Location* loc, const char* restrict format, va_list va);
vlog_info
Write information to stderr, using a va_list of format-arguments.
- Param
format
: Information format (printf) - Param
...
: Possible printf arguments
void vlog_info(const char* restrict format, va_list va);
vlog_warn
Write a warning to stderr, using a va_list of format-arguments.
- Param
format
: Warning format (printf) - Param
...
: Possible printf arguments
int vlog_warn(const char* restrict format, va_list va);
vlog_warn_at
Write a warning to stderr referencing a source location, using a va_list of format-arguments.
- Param
loc
: The source location to reference - Param
format
: Warning format (printf) - Param
...
: Possible printf arguments
int vlog_warn_at(Location* loc, const char* restrict format, va_list va);
parser/
This directory is responsible for all parsing which occurs from within the core. It contains the Emblem parser and lexer specifications as well as code for input sanitisation and handling syntactic sugar.
parser.h
Exposes document parser at the topmost level.
- Author: Edward Jones
- Date: 2021-09-17
sanitise-word.h
Exposes word-sanitiser for the Emblem parser.
- Author: Edward Jones
- Date: 2021-09-17
sugar.h
Exposes functions to construct syntactic sugar calls.
- Author: Edward Jones
- Date: 2021-09-17
pp/
Holds useful definitions for the preprocessor.
assert.h
Preprocessor definitions for compile-time assertions.
- Author: Edward Jones
- Date: 2021-09-17
lambda.h
Provides functions and preprocessor definitions for lambdas (anonymous functions).
- Author: Edward Jones
- Date: 2021-09-17
func_sig
A named function signature.
- Param
r
: Return type of the lambda, cannot be void - Param
n
: Expression of the body of the lambda, must have non-void type - Param
ps
: Parameters of the lambda
Returns: The signature of a function-pointer of name n
which takes ps
and returns r
#define func_sig(r, n, ps) r(fun n) ps
func_type
A function type.
- Param
r
: Return type of the function signature - Param
ps
: Parameters of the function
Returns: The type of a function-pointer which takes ps
and returns r
#define func_type(r, ps) r(fun) ps
ilambda
Create an impure anonymous function.
- Param
r
: Return type of the lambda expression - Param
ps
: Paramters of the lambda expression - Param
b
: Body of the lambda expression, must be surrounded by curly braces
Returns: A pointer to an anonymous function with parameters ps
, return-type r
and body b
# define ilambda(r, ps, b) \
lambda
Create a pure anonymous function.
- Param
r
: Return type of the lambda, cannot be void - Param
ps
: Parameters of the lambda - Param
e
: Expression of the body of the lambda, must have non-void type
Returns: A pointer to an anonymous function which takes ps
and returns the value of e
of type r
# define lambda(r, ps, e) \
void
Lambda-friendly implementation of free
.
- Param
p
: Pointer to memory to free
extern void(fun freel)(void* p);
not_implemented.h
Preprocessor definitions for function-stubs to stymie compiler errors.
- Author: Edward Jones
- Date: 2021-09-17
NOT_IMPLEMENTED
Declare a function signature as not-implemented.
- Param
sig
: A signature to mark as unimplemented
Returns: An implementation of the signature which prints that the function is missing before exiting unsuccessfully
#define NOT_IMPLEMENTED(sig) \
unused.h
Provides preprocessor definitions to declare variables as unused to stymie compiler warnings.
- Author: Edward Jones
- Date: 2021-09-17
UNUSED
Declare a variable as unused.
- Param
x
: The name of the variable to declare unused
Returns: A statement without effect which makes the compiler believe that x
is used
#define UNUSED(x) x = x
style/
This module handles the resolution and usage of styling information. Specifically, it is responsible for inputting, preprocessing, reading and applying style-sheets.
css.h
Exposes functions to handle styling and stylesheets.
- Author: Edward Jones
- Date: 2021-09-17
css-params.h
Exposes functions to handle CSS-environment parameters.
- Author: Edward Jones
- Date: 2021-09-17
preprocess-css.h
Exposes functions to call a preprocessor on CSS documents.
- Author: Edward Jones
- Date: 2021-09-17
typesetter/
This module is responsible for handling the typesetter. It governs both the typesetting loop and the internal typesetting engine.
typesetter.h
Provides an interface to call the typesetting loop.
- Author: Edward Jones
- Date: 2021-09-17
Module internal docs
This section details the internal documentation written in each core module, that is, documentation present in files *.c
in in the repository.
data/
Defines the generic data structures used to represent internal data.
array.c
Implement fixed-length arrays structures.
- Author: Edward Jones
- Date: 2021-09-17
cmp.c
Implements comparator functions.
- Author: Edward Jones
- Date: 2021-09-17
CMP
Create a generic comparator function.
- Param
name
: Name of the type - Param
type
: Type to compare
Returns: A function which compares two type
s
# define CMP(name, type) Cmp cmp_##name##s(void* v1, void* v2) COMPARISON_BODY(type)
either.c
Provices an implementation of the Either data-type, which can encode values of two types.
- Author: Edward Jones
- Date: 2021-09-17
hash.c
Provides hash functions for standard data types.
- Author: Edward Jones
- Date: 2021-09-17
HASH_NUM
Construct a function which hashes a specified numeric type.
- Param
name
: The name of the type to hash - Param
type
: The type of the values to hash
Returns: A function which hashes type
s
#define HASH_NUM(name, type) \
list-array-conversions.c
Provides conversoin functions between arrays and lists.
- Author: Edward Jones
- Date: 2021-09-17
list.c
Implements the list data-structure, a deque.
- Author: Edward Jones
- Date: 2021-09-17
locked.c
Provides a locking structure for accessing data-structures, designed to prevent race conditions.
- Author: Edward Jones
- Date: 2021-09-17
map.c
Implements the map data structure.
- Author: Edward Jones
- Date: 2021-09-17
next_non_empty_bucket
Find the next non-empty bucket with an index strictly after a specified index.
- Param
map
: Map to search - Param
curr
: Strict index lower bound
Returns: true
iff a non-empty bucket at index > curr
was found, else false
static bool next_non_empty_bucket(Map* map, unsigned int* curr);
pkcmp
Key comparator function between a kv pair and a specific key.
- Param
kcmp
: Location of the key comparator function
Returns: A lambda function which compares its input v1
to the key of its input v2
#define pkcmp(kcmp) lambda(Cmp, (void* v1, void* v2), kcmp(v1, ((Pair*)v2)->p0))
maybe.c
Implements the Maybe data-structure, which can represent either a value or nothing.
- Author: Edward Jones
- Date: 2021-09-17
str.c
Implements the string data-structure.
- Author: Edward Jones
- Date: 2021-09-17
unit.c
Implements the Unit data type.
- Author: Edward Jones
- Date: 2021-09-17
doc-struct/
This directory contains modules used to define and handle document structure.
ast.c
Implements the document structure data types.
- Author: Edward Jones
- Date: 2021-09-17
discern-pars.c
Implements functions to automatically place paragraph nodes in a document.
- Author: Edward Jones
- Date: 2021-09-17
location.c
Implements the Location data-structure and useful functions for keeping track of locations in the document source.
- Author: Edward Jones
- Date: 2021-09-17
drivers/
This directory contains files used for interacting with output drivers. It is responsibly for both defining a set of core output drivers, and also interfacing with those defined in extension-space.
drivers.c
Handles the execution of output drivers both from the core and in extensions.
- Author: Edward Jones
- Date: 2021-09-17
write-out.c
This module lacks a documentation header.
em.c
Insertion point for the ‘em’ binary, invokes all functionality used for typeestting documents.
- Author: Edward Jones
- Date: 2021-09-17
main
Entry point.
- Param
argc
: Number of command-line arguments - Param
argv
: Command-line argument array
Returns: Program exit code
int main(int argc, char** argv)
ext/
This directory contains the code which manages and interacts with extension space. It allows the execution of arbitrary Lua code as the program executes and is the module primarily responsible for Emblem’s extensibility.
In the source code, the standard extension library is defined in the lib/
directory beneath this one.
debug.c
Implements basic functionality for debugging involving the Lua stack.
- Author: Edward Jones
- Date: 2021-09-17
ext-env.c
Implements the Lua extension environment, loading libraries, extensions and pointers.
- Author: Edward Jones
- Date: 2021-09-17
ext-loader.c
Implements the extension-loader.
- Author: Edward Jones
- Date: 2021-09-17
ext-params.c
Implements functions to handle extension-environment parameters.
- Author: Edward Jones
- Date: 2021-09-17
lua-ast-io.c
Provides functions for translating from Lua tables to docuemnt-trees and vice-versa.
- Author: Edward Jones
- Date: 2021-09-17
lua.c
Provides functions for executing evaluation-passes on document trees.
- Author: Edward Jones
- Date: 2021-09-17
lua-em-parser.c
Provides interface between core Emblem parser and extension-space.
- Author: Edward Jones
- Date: 2021-09-17
lua-events.c
Implements callers for typesetting events for extension-space.
- Author: Edward Jones
- Date: 2021-09-17
lua-pointers.c
This module lacks a documentation header.
style.c
Implements function for loading stylesheets from extension-space.
- Author: Edward Jones
- Date: 2021-09-17
logs/
This directory holds code which is responsible for logging-output. It also provides an interface between its logging functions and extension-space.
ext-log.c
Implements the C-side interface between extension-space and logging functions.
- Author: Edward Jones
- Date: 2021-09-17
logs.c
Implements logging functions.
- Author: Edward Jones
- Date: 2021-09-17
LOG_X_CALL
Construct a call to the logging function at verbosity lvl
, where v
is the start of the formatting.
- Param
lvl
: Verbosity level of the call - Param
v
: Name of the first format argument
Returns: A call to log_x with va_args handled
#define LOG_X_CALL(name, f) \
log_debug
Write a debug message to stderr.
- Param
format
: debug message format (printf) - Param
...
: Possible printf arguments
void log_debug(const char* restrict format, ...) { LOG_X_CALL(debug, format); }
log_err
Write an error stderr.
- Param
format
: Error format (printf) - Param
...
: Possible printf arguments
void log_err(const char* restrict format, ...) { LOG_X_CALL(err, format); }
log_info
Write information to stderr.
- Param
format
: Information format (printf) - Param
...
: Possible printf arguments
void log_info(const char* restrict format, ...) { LOG_X_CALL(info, format); }
log_warn
Write a warning to stderr.
- Param
format
: Warning format (printf) - Param
...
: Possible printf arguments
int log_warn(const char* restrict format, ...)
parser/
This directory is responsible for all parsing which occurs from within the core. It contains the Emblem parser and lexer specifications as well as code for input sanitisation and handling syntactic sugar.
parser.c
Implements the parser at the top-level (entire-document).
- Author: Edward Jones
- Date: 2021-09-17
sanitise-word.c
Implements the word-sanitiser function for the Emblem parser.
- Author: Edward Jones
- Date: 2021-09-17
sugar.c
Implements functions to construct syntactic sugar functions.
- Author: Edward Jones
- Date: 2021-09-17
pp/
Holds useful definitions for the preprocessor.
lambda.c
Implementations of free functions as lambdas.
- Author: Edward Jones
- Date: 2021-09-17
style/
This module handles the resolution and usage of styling information. Specifically, it is responsible for inputting, preprocessing, reading and applying style-sheets.
css.c
Manages resolution of styles and the handling of stylesheets.
- Author: Edward Jones
- Date: 2021-09-17
css-params.c
Implements functions to handle CSS-environment parameters.
- Author: Edward Jones
- Date: 2021-09-17
preprocess-css.c
Provides an implementation for a CSS preprocessor using SCSS/SASS.
- Author: Edward Jones
- Date: 2021-09-17
typesetter/
This module is responsible for handling the typesetter. It governs both the typesetting loop and the internal typesetting engine.
typesetter.c
Implements the typesetting loop.
- Author: Edward Jones
- Date: 2021-09-17
License and Author
This project is maintained by Ed Jones and is licensed under the GNU General Public License version 3.