literate-programming-lib
所属分类:工具库
开发工具:JavaScript
文件大小:0KB
下载次数:0
上传日期:2022-03-26 01:24:47
上 传 者:
sh-1993
说明: 识字编程的库组件,
(The library component of literate-programming,)
文件列表:
.npmignore (67, 2020-01-14)
.travis.yml (50, 2020-01-14)
LICENSE (1078, 2020-01-14)
TODO.md (540, 2020-01-14)
compose_.md (1007, 2020-01-14)
index.js (170992, 2020-01-14)
lprc.js (162, 2020-01-14)
no-augments.md (1836, 2020-01-14)
package-lock.json (21098, 2020-01-14)
package.json (931, 2020-01-14)
project.md (61235, 2020-01-14)
setup.md (3969, 2020-01-14)
src/ (0, 2020-01-14)
src/commands.md (88580, 2020-01-14)
src/commonmark.md (7397, 2020-01-14)
src/debugging.md (4421, 2020-01-14)
src/directives.md (72055, 2020-01-14)
src/events.md (11076, 2020-01-14)
src/logs.md (8953, 2020-01-14)
src/matrix.md (9037, 2020-01-14)
src/requires.md (4274, 2020-01-14)
src/stitching.md (63828, 2020-01-14)
src/subcommands.md (16504, 2020-01-14)
src/tests.md (13958, 2020-01-14)
test.js (9133, 2020-01-14)
tests/ (0, 2020-01-14)
tests/anon.md (454, 2020-01-14)
tests/arrayify.md (493, 2020-01-14)
tests/assert.md (268, 2020-01-14)
tests/async.md (947, 2020-01-14)
tests/asynceval.md (427, 2020-01-14)
tests/backslash.md (548, 2020-01-14)
tests/blockoff.md (306, 2020-01-14)
tests/capitalizations.md (357, 2020-01-14)
tests/caps.md (204, 2020-01-14)
tests/cd.md (980, 2020-01-14)
tests/codeblocks.md (711, 2020-01-14)
tests/commands.md (238, 2020-01-14)
... ...
# literate-programming-lib [![Build Status](https://travis-ci.org/jostylr/literate-programming-lib.png)](https://travis-ci.org/jostylr/literate-programming-lib)
Write your code anywhere and in any order with as much explanation as you
like. literate-programming will weave it all together to produce your project.
This is a modificaiton of and an implementation of
[Knuth's Literate Programming](http://www-cs-faculty.stanford.edu/~uno/lp.html)
technique. It is
perhaps most in line with [noweb](http://tex.loria.fr/litte/ieee.pdf).
It uses markdown as the basic document format with the code to be weaved
together being markdown code blocks. GitHub flavored code fences can also be used
to demarcate code blocks. In particular, [commonmark](http://commonmark.org/)
is the spec that the parsing of the markdown is used. Anything considered code
by it will be considered code by literate programming.
This processing does not care what language(s) your are programming in. But it
may skew towards more useful for the web stack.
This is the core library that is used as a module. See
[-cli](https://github.com/jostylr/literate-programming-cli) for the command
line client. The [full](https://github.com/jostylr/literate-programming)
version has a variety of useful standard
plugins ("batteries included").
## Installation
This requires [node.js](http://nodejs.org) and [npm](https://npmjs.org/) to be
installed. See [nvm](https://github.com/creationix/nvm) for a recommend
installation of node; it allows one to toggle between different versions. This
has been tested on node.js .10, .12, and io.js. It is basic javascript and
should work pretty much on any javascript engine.
Then issue the command:
npm install literate-programming-lib
Since this is the library module, typically you use the client version install
and do not install the lib directly. If you are hacking with modules, then you
already know that you will want this in the package.json file.
## Using as a module
You can use `Folder = require('literate-programming-lib');` to get
a constructor that will create what I think of as a folder.
The folder will handle all the documents and scopes and etc.
To actually use this library (as opposed to the command line client),
you need to establish how it fetches documents and tell
it how to save documents. An example is below. If you just want to compile
some documents, use the command line client and ignore this. Just saying the
following is not pretty. At least, not yet!
The thing to keep in mind is
that this library is structured around events
using my [event-when](https://github.com/jostylr/event-when) library. The
variable gcd is the event emitter (dispatcher if you will).
var fs = require('fs');
var Folder = require('literate-programming-lib');
var folder = new Folder();
var gcd = folder.gcd;
var colon = folder.colon;
gcd.on("need document", function (rawname) {
var safename = colon.escape(rawname);
fs.readfile(rawname, {encoding:'utf8'}, function (err, text) {
if (err) {
gcd.emit("error:file not found:" + safename);
} else {
folder.newdoc(safename, text);
}
});
});
gcd.on("file ready", function(text, evObj) {
var filename = evObj.pieces[0];
fs.writefile(filename, text);
});
gcd.emit("need document:first.md");
This last line should start the whole chain of compilation with first.md being read in
and then any of its files being called, etc., and then any files to save will
get saved.
The reason the lib does not have this natively is that I separated it out
specifically to avoid requiring file system access. Instead you can use any kind of
function that provides text, or whatever. It should be fine to also use
`folder.newdoc` directly on each bit of text as needed; everything will
patiently wait until the right stuff is ready. I think.
Note that live code can be run from a literate program as well. So be
careful!
## Example
Let's give a quick example of what a sample text might look like.
# Welcome
So you want to make a literate program? Let's have a program that outputs
all numbers between 1 to 10.
Let's save it in file count.js
[count.js](#Structure "save:")
## Structure
We have some intial setup. Then we will generate the array of numbers. We
end with outputting the numbers.
var numarr = [], start=1, end = 11, step = 1;
_"Loop"
_"Output"
## Output
At this point, we have the array of numbers. Now we can join them with a
comma and output that to the console.
console.log("The numbers are: ", numarr.join(", ") );
## Loop
Set the loop up and push the numbers onto it.
var i;
for (i = start; i < end; i += step) {
numarr.push(i);
}
A full example of a literate program is lp.md in this repository. It compiles
to this library.
## Document syntax
A literate program is a markdown document with some special conventions.
The basic idea is that each header line (regardless of level, either atx # or
seText underline ) demarcates a full block. Code blocks within a full block
are the bits that are woven together.
### Code Block
Each code block can contain whatever kind of code, but there is a primary special
syntax.
`_"Block name"` This tells the compiler to compile the block with "Block
name" and then replace the `_"Block name"` with that code.
Note the the allowed quotes are double, single, and backtick. Matching types
are expected. And yes, it is useful to have three different types.
The full syntax is something of the form
`_"scope name::block name:minor block name | cmd arg 1, arg 2 | cmd2 |cmd3 ..."`
where the scope name allows us to refer to other documents (or artificial
common scopes) and the commands run the output of one to the input of the
other, also taking in arguments which could they themselves be block
substitutions.
Note that one can also backslash escape the underscore. To have multiple
escapes (to allow for multiple compiling), one can use `\#_"` where the number
gets decremented by one on each compile and, when it is compiled with a 0 there,
the sub finally gets run.
A block of the form `_":first"` would look for a minor block, i.e., a block
that has been created by a switch directive. See next section.
One can also visually hide parts of the document, without it being hidden to
the compiler, by using html comments. If the start of a line is `` before doing the markdown
compiling.
### Directive
A directive is a command that interacts with external input/output. Just about
every literate program has at least one save directive that will save some
compiled block to a file.
The syntax for the save directive is
[file.ext](#name-the-heading "save: encoding | pipe commands")
where
* `file.ext` is the name of the file to save to
* `name-the-heading` is the heading of the block whose compiled version is being saved.
Spaces in the heading get converted to dashes for id linking purposes. Colons can be used
to reference other scopes and/or minor blocks. In particular, `#:jack` will
refernce the `jack` minor in the current heading block where the save
directive is located.
* `save:` is there to say this is the directive to save a file
* `encoding` is any valid encoding of
[iconv-lite](https://github.com/ashtuchkin/iconv-lite/wiki/Supported-Encodings).
This is relevant more in the command line module, but is here as the save
directive is here.
* `pipe commands` optional commands to process the text before saving. See
next section.
For other directives, what the various parts mean depends, but it is always
[some](#stuff "dir: whatever")
where the `dir` should be replaced with a directive name. If dir is absent,
but the colon is there, then this demarcates a minor block start.
### Pipes
One can also use pipes to pipe the compiled text through a command to do
something to it. For example, `_"Some JS code | jshint"` will take the code
in block `some JS code` and pipe it into the jshint command which can be a
thin wrapper for the jshint module and report errors to the console.
That command would then return the text in an untouched fashion. We can also use
pipe commands to modify the text.
Commands can be used in block substitutions, minor block directive switches, and
other directives that are setup to use them such as the save and out directive:
`[code.js](#some-js-code "save: | jstidy)` will tidy up the code
before storing it in the file `code.js`.
If you want your own directive to process pipes, see the [save directive](https://github.com/jostylr/literate-programming-lib/blob/master/lp.md#save) in
lp.md. Pay particular attention to the "process" and "deal with start" minor
blocks. The functionality of pipe parsing is in the `doc.pipeParsing` command,
but there are events that need to be respected in the setup.
Commands take arguments separated by commas and commands end with pipes or the
block naming quote. One can also use a named code block as an argument, using
any of the quote marks (same or different as surround block name). To
escape commas, quotes, pipes, underscores, spaces (spaces get trimmed from the
beginning and ending of an argument), newlines, one can use a backslash, which
also escapes itself. Note that the commonmark parser will escape all
backslash-punctuation combinations outside of code blocks. So you may need a
double backslash in directive command pipings.
You can also use `\n` to pu ta newline in line or `\u...` where the ... is a
unicode codepoint per JavaScript spec implemented by [string.fromcodepoint](https://github.com/mathiasbynens/String.fromCodePoint).
### Minor Block
Finally, you can use distinct code blocks within a full block. If you simply
have multiple code blocks with none of the switching syntax below, then they
will get concatenated into a single code block.
You can also switch to have what I call minor blocks within a main heading. This is mainly
used for small bits that are just pushed out of the way for convenience. A
full heading change is more appropriate for something that merits separate attention.
To create a minor block, one can either use a link of the form `[code name]()` or
`[code name](#whatever ":|cmd ...")` Note this is a bit of a break from
earlier versions in which a link on its own line would create a minor block. Now it is
purely on the form and not on placement.
Example: Let's say in heading block `### Loopy` we have `[outer loop]()`
Then it will create a code block that can be referenced by
`_"Loopy:outer loop"`.
Note: If the switch syntax is `[](#... ":|...")` then this just transforms
whatever is point to in href using the pipe commands. That is, it is not a
switch, but fills in a gap for main blocks not having pipe switch syntax. The
key is the empty link text.
#### Templating
One use of minor blocks is as a templating mechanism.
## Top
After the first compile, the numbers will be decremented, but the blocks
will not be evaluated.
\1_":first"
\2_":second"
\1_":final"
This is now a template. We could use it as
[jack](# "store:| compile basic ")
[happy.txt](#jack "save:| compile great")
[sad.txt](# "save:| compile basic | compile grumpy")
# Basic
[first]()
Greetings and Salutations
[final]()
Sincerely,
Jack
# Great
[second]()
You are great.
# Grumpy
[second]()
You are grumpy.
# Middle
[second]()
You are okay.
## Another
\_":first"
\_"$2:second"
\_":final"
[middle.txt](# "save:| sub $2, middle | compile basic")
This would produce the files:
happy.txt
Greetings and Salutations
You are great.
Sincerely,
Jack
sad.txt
Greetings and Salutations
You are grumpy.
Sincerely,
Jack
middle.txt
Greetings and Salutations
You are okay.
Sincerely,
Jack
Note that you need to be careful about feeding in the escaped commands into
other parsers. For example, I was using Pugs to generate HTML structure and
then using this templating to inject content (using markdown). Well, Pugs
escapes quotes and this was causing troubles. So I used backticks to delimit
the block name instead of quotes and it worked fine. Be flexible.
## Nifty parts of writing literate programming
* You can have your code in any order you wish.
* You can separate out flow control from the processing. For example,
if (condition) {
_"Truth"
} else {
_"Beauty"
}
The above lets you write the if/else statement with its logic and put the
code in the code blocks `truth` and `beauty`. This can help keep one's
code to within a single screenful per notion.
* You can write code in the currently live document that has no effect, put in
ideas in the future, etc.
* You can "paste" multiple blocks of code using the same block name. This is
like DRY, but the code does get repeated for the computer. You can also
substitute in various values in the substitution process so that code
blocks that are almost the same but with different names can come from the
same root structure.
* You can put distracting data checks/sanitation/transformations into another
block and focus on the algorithm without the use of functions (which can be
distracting).
* You can process the blocks in any fashion you want. So for example, to
create a JSON object, one could use a simpler setup appropriate for the
particular data and then transform it into JSON. It's all good.
* This brings DSL and grunt power, written in the same place as your code. It
is really about coding up an entire project.
* Getting the length of functions right is difficult. Too short functions,
and boilerplate and redirection becomes quite the bother. Too long, and it
is hard to understand what all a function is doing. Too long and we lose
composability. Too short, the chain of composing them becomes too long.
Literate programming can help somewhat in that we can have longer functions
and still have it understood. We could also potentially use the litpro
blocks again allowing for some composability though that that should be
rare. I think the rule of thumb is that if breaking it up seems good from a
usability stance, do it. If breaking it up is more about keeping a function
to a readable length, use litpro blocks. Another advantage of using litpro
blocks is that we get the benefit of small parts when coding, but when
debugging, we can see a much larger flow of code all at once in the compiled
version.
I also like to use it to compile an entire project from a single file, pulling
in other literate program files as needed. That is, one can have a
command-and-control literate program file and a bunch of separate files for
separate concerns. But note that you need not split the project into any
pre-defined ways. For example, if designing a web interface, you can organize
the files by widgets, mixing in HTML, CSS, and JS in a single file whose
purpose is clear. Then the central file can pull it all in to a single web
page (or many) as well as save the CSS and JS to their own files as per the
reommendation, lessing the CSS, tanspiling ES6, linting, and minifying all as
desired. Or you could just write each output file separate in its own litpro
document.
It's all good. You decide the order and grouping. The structure of your litpro
documents is up to you and is **independent** of the needed structures of the
output.
## Directives vs commands vs subcommand
Directives affect the flow of the literate program itself, such as defining
commands, saving output, or directly storing values. Commands transform
incoming text or other input. Subcommands create useful arguments to commands.
Directives can be thought of as procedures, commands as methods on the input,
and subcommands as functions. And indeed, directives do not compose in the
sense of returning a value. Commands are written like the chain syntax, with
what is on the left being evaluated first. Subcommands are written with
typical function syntax, with what is on the right being evaluated first.
## Built in directives
There are a variety of directives that come built in.
* **Save** `[filename](#start "save:options|commands")` Save the text from
start into file filename. The options can be used in different ways, but in
the command client it is an encoding string for saving the file; the default
encoding is utf8.
* **Store** `[name|value](#start "store:value|...")` If the value is present, then
it is sent through the pipes. If there is no value, then the `#start`
location is used for the value and that gets piped. The name is used to
store the value. You can also use the pipe syntax in the linkname part for
the value instead. This dominates over the start or option value. A little
bit easer for the reader to see in rendered form.
* **Log** Same as store, except instead of storing it in the doc, it logs it
to console. Same exact syntax.
* **Transform** `[des|name](#start "transform:|...)` or `[des|name](#start
":|...")`. This takes the value that start points to and transforms it
using the pipe commands. Note one can store the transformed values by
placing the variable name after a pipe in the link text. The description of
link text has no role. For the syntax with no transform, it can be link text
that starts with a pipe or it can be completely empty. Note that if it is
empty, then it does not appear and is completely obscure to the reader.
* **Load** `[alias](url "load:options")` This loads the file, found at the url
(file name probably) and stores it in the alias scope as well as under the
url name. We recommend using a short alias and not relying on the filename
path since the alias is what will be used repeatedly to reference the blocks
in the loaded file. Options are open, but for the command line client it is
the encoding string with default utf8. Note there are no pipes since there
is no block to act on it.
* **Cd** `[path](#ignore "cd: load/save")` This creates the ability to change
directories for either loading or saving. This is relative to the default
directory. `[](# "cd: load")` (or save) will clear the path; it is always
good to do that when done. Ideally, this would be a tightly grouped of files
(listing like a directory) with the initial change right before the list and
the changing back after the list.
* **Define** `[commandName](#start "define: async/sync/raw/defaults|cmd")`
This allows one to define commands in a lit pro document. Very handy. Order
is irrelevant; anything requiring a command will wait for it to be defined.
This is convenient, but also a bit more of a bother for debugging. Anyway,
the start is where we find the text for the body of the command. The post
colon, pre pipe area expects one of three options which is explained below
in plugins.You can also pipe your command definition through pipe commands
before finally installing the function as a live function. Lo ... ...
近期下载者:
相关文件:
收藏者: