1 | Dollphie
|
2 | ========
|
3 |
|
4 | Dollphie is a structured document language inspired by
|
5 | [Scribble](http://docs.racket-lang.org/scribble/), and mainly designed for
|
6 | documenting source code. It's also a retake on my previous attempt at
|
7 | [a similar language for annotating source code](http://robotlolita.me/2013/02/23/unfancy-documentation.html),
|
8 | but this is more light-weight, more general, and more expressive.
|
9 |
|
10 |
|
11 | ## 0. Overview
|
12 |
|
13 | Dollphie is a Lisp-like language with lightweight syntax for writing large
|
14 | amount of text, and some syntactical sugar for inline formatting. Unlike
|
15 | languages like LaTeX, Dollphie has a consistent grammar, and is not tied to a
|
16 | particular output format.
|
17 |
|
18 | A documentation for a module could be written in Dollphie as follows:
|
19 |
|
20 | # module: Operators
|
21 |
|
22 | Provides operators as first-class functions.
|
23 |
|
24 | -- Core operators --------------------------------------------------
|
25 |
|
26 | ### function: add
|
27 |
|
28 | Returns the sum of two *numbers*.
|
29 |
|
30 | @type{Number -> Number -> Number}
|
31 | @code('js) ::
|
32 | function add(a){ return function(b) {
|
33 | return a + b
|
34 | }}
|
35 |
|
36 | -- Exports ---------------------------------------------------------
|
37 |
|
38 | @code('js){{{
|
39 | module.exports = add
|
40 | }}}
|
41 |
|
42 | Once we desugar the document, we get the following Dollphie code:
|
43 |
|
44 | @module("Operators" [
|
45 | @text("Provides operators as first-class functions.")
|
46 | @section("Core operators" [
|
47 | @function("add" [
|
48 | @text("Returns the sum of two " @bold("numbers."))
|
49 | @type(block: "Number -> Number -> Number")
|
50 | @code('js block: "function add(a){ return function(b) {
|
51 | return a + b
|
52 | }}")])])
|
53 | @section("Exports" [
|
54 | @code('js block: "module.exports = add")])])
|
55 |
|
56 |
|
57 | ## 1. Formal syntax
|
58 |
|
59 | Dollphie is a fairly simple Lisp dialect:
|
60 |
|
61 | ```hs
|
62 | eol = "\n" | "\r"
|
63 | space = EOL | " " | "\t"
|
64 |
|
65 | digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
66 | digits = digit+
|
67 | number = digits ("." digits)?
|
68 |
|
69 | escapedChar = "\\" char
|
70 | stringChar = escapedChar | not("\"")
|
71 | string = "\"" stringChar* "\""
|
72 |
|
73 | reserved = "define" | "let" | "if" | "fun" | "quote"
|
74 | special = "{" | "}" | "(" | ")" | "[" | "]" | ":" | "'" | "\"" | "@"
|
75 | nameStart = not(special | digit | space)
|
76 | nameRest = not(special | space)
|
77 | name = nameStart nameRest* [unless reserved]
|
78 |
|
79 | bool = "true" | "false"
|
80 | nil = "nil"
|
81 | vector = "[" value* "]"
|
82 | symbol = "'" name
|
83 | value = number | string | name | symbol | vector | nil
|
84 |
|
85 | call = "@" name callArgs? callBlock?
|
86 | callArgs = "(" callExpression* ")"
|
87 | callExpression = name [no space here] ":" expression
|
88 | | expression
|
89 | callBlock = simpleBlock | rawBlock | indentBlock
|
90 | simpleBlock = "{" ["\\}" or anything but "}"] "}"
|
91 | rawBlock = "{{{" [anything but "}}}"] "}"
|
92 | indentBlock = "::" INDENT [anything] DEDENT
|
93 |
|
94 | def = "@define" "(" name args expression* ")"
|
95 | args = "[" name* "]"
|
96 |
|
97 | let = "@let" "(" letVector expression* ")"
|
98 | letVector = "[" (name [no space here] ":" value)+ "]"
|
99 |
|
100 | if = "@if" "(" expression "then:" expression "else:" expression ")"
|
101 |
|
102 | fun = "@fun" "(" args expression* ")"
|
103 |
|
104 | quote = "@raw" "(" expression ")"
|
105 |
|
106 | expression = let | if | fun | def | quote | call | value
|
107 | document = expression*
|
108 | ```
|
109 |
|
110 | The sugared dialect has the following grammar:
|
111 |
|
112 | ```hs
|
113 | heading = section | declaration
|
114 | block = paragraph | expression
|
115 |
|
116 | section = '-'+ title '-'*
|
117 | title = not('-')*
|
118 | declaration = '#'+ name [no space here] ':' qualifiedName '#'*
|
119 |
|
120 | qualifiedName = name ('.' name)*
|
121 |
|
122 | hardLine = '|' text+ EOL
|
123 | softLine = text+ EOL
|
124 | blankLine = ws EOL
|
125 | paragraph = (hardLine | softLine)+ blankLine
|
126 |
|
127 | strong = '\\*' | '*' text+ '*'
|
128 | italic = '\\/' | '/' text+ '/'
|
129 | formatting = strong | italic
|
130 |
|
131 | text = expression | formatting | word
|
132 | word = [anything but whitespace]
|
133 | ```
|