I am writing a processor for the Citation Style Language (CSL) in JavaScript.
Before I explain what my problem is, I must give some context first:
CSL is an XML specification for describing how to generate citations and bibliographies in a particular style (e.g. Harvard, APA, etc.)
Let's say I wanted to create my own citation style, e.g. the "Stack Exchange" style
Given the following XML pseudo code (an XML complying with the Citation Style Language specification would look a bit different but hopefully you get the idea.)
<citation>
<author>
<lastname suffix=", "/>
<firstname first-letter-only="true" suffix="."/>
</author>
<year prefix=" (" suffix="). "/>
<title suffix=". "/>
<source prefix="Retrieved from "/>
</citation>
And the following data:
{ "author_lastname": "Doe",
"author_firstname": "John",
"title": "My paper title",
"year": "2009",
"source": "http://example.com" }
Then I should expect the following citation:
Doe, J. (2019). My paper title. Retrieved from http://example.com
I use XSLT to generate a JavaScript module out of an XML document. I then use this module as any regular Node.js modules to produce citations.
$ xsltproc generate-module.xsl stackexchange.xml >stackexchange.js
Then in Node.js:
var citation = require('./stackexchange.js');
citation({ "author_lastname": "Doe",
"author_firstname": "John",
"title": "My paper title",
"year": "2009",
"source": "http://example.com" });
//=> "Doe, J. (2019). My paper title. Retrieved from http://example.com"
In a nutshell, each node in a XML document is converted into a function that receives as its parameters:
- its attributes (if any)
- its children (as functions; if any)
- the data it needs to process
Let's imagine the following contrived example:
<a>
<b>
<c>
<d value="foo"/>
</c>
</b>
</a>
The generated module would something like that:
// dummy-style.js
module.exports = () => a(b(c(d("foo"))));
Where:
d
will simply return itsvalue
attributec
will append the string'c'
b
will append the string'b'
a
will append the string'a'
Example of usage:
var citation = require('./dummy-style.js');
citation();
//=> "foocba"
Now here's the challenge that I am facing!
The specification could say things like: « if d
produces the string 'foo' then b
must not append the string 'b' to its output »
This is where I'm struggling as I need to somehow keep track of which functions have been called and where they've been called. It's becoming clear to me that my functions cannot simply return strings; they must be wrapped with some sort of metadata.
It feels like I need to inspect my functions call stack, inspect that stack and apply some other rules before I can replay it. But I really don't know how to approach this.
Questions
- Am I facing an issue that is common when designing "compilers" or "processors"?
- Are there any design principles that I'm lacking?
- How would you tackle this issue?