First commit

This commit is contained in:
ef3d0c3e 2024-04-10 12:42:24 +02:00
commit b076958893
18 changed files with 5185 additions and 0 deletions

View file

@ -0,0 +1,22 @@
use regex::Regex;
use std::fs;
fn main() {
let re = Regex::new(r"(?m)(^|\n)([#]+)(( |\t)+)(.+)$").unwrap();
//let src = fs::read_to_string("./readme.nml").unwrap();
let src = String::from("# Test\n## Second line!\n### Third");
let mut result = Vec::<String>::new();
for (line, [_, count, spacing, string]) in re.captures_iter(&src).map(|v| v.extract())
{
println!("`{line}`:\n{count}/{spacing}/{string}\n");
}
//test(&p);
/*
*/
//t1.join().unwrap();
//t2.join().unwrap();
}

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

3821
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "rust_learn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crossbeam-utils = "0.8.19"
files = "2.2.3"
regex = "1.10.3"

896
readme.nml Normal file
View file

@ -0,0 +1,896 @@
#:Inc resources/template.nml
#+Title NML -- The nice markup language!
#+TOC Table of content
%%(define (spoiler-text title content)
(string-append "{{{<details><summary>}}}" title "{{{</summary>}}}\n"
content
"{{{</details>}}}")
)
(define (make-anchor-link name)
(string-append "[" name "](#" (nml-html-get-anchor name) ")"))
%%
#+DefaultUnorderedBullet •
# Preamble
NML is a markup language designed to be featureful. It integrates features from other markup language, such as MarkDown and LaTeX. And it also features an extension language to extend the language or do computations.
It currently is in an unstable stage. You should expect bugs and incomplete feature (CXXABI, NML Object, and Highlight caching).
# Building
NML uses CMake as it's build system. To build NML, you must create a directory where the program will be compiled =build=.
NML requires =libguile= (dev lib) to be installed.
NML can be installed by moving it to your path. Note that this is not a requirement.
Here is a summary:
```sh
# Clone nml
git clone https://github.com/ef3d0c3e/nml
cd nml
mkdir build && cd build
# Build
cmake ..
make
# (Optional) Install nml
mv nml (to a directory in $PATH)
```
## Additional requirements
In order to enable LaTeX processing, you must install =latex2svg= (found in =nml/readme=) into your path.
# Syntax
## Definitions
NML allows defining internal variable that can be used inside a document, or by NML itself.
```c
#+VariableName Value
→ %VariableName% : "Value"
#+SplitDefinition ValueLine1\
ValueLine2
→ %SplitDefinition% : "ValueLine1ValueLine2"
#+MultiLine First\\
Sec\
ond\\
Third
→ %MultiLine% : "First\nSecond\nThird"
```
Calling a defined variable is done by putting the variable's name between two =\%='s^{{Called variables are parsed, so they can contain more NML code, or even new variable definitions}}.
*Note*: If a variable is already defined, defining a new variable with the same name will overwrite the first variable
```c
#+MyName Robert
#+MyName John
→ %MyName% : "John"```
**Path-aware definitions**
It may be useful to have definitions that correspond to the location of a file. However, when including a NML document from another directory, the relative location of this file will change.
You can create path-aware definitions by adding a ='='s after the variable's name.
```txt, Example use case
[File] My NML template file.nml
| #+Picture image.png
| #+PictureAware' image.png
[File] image.png
[Folder] My project
| [File] Project.nml
| | #:Inc ../My NML template file.nml
| |
| | %Picture% → image.png (wrong)
| | %PictureAware% → ../image.png (correct)
```
**Proxy definitions**
It is possible to create a definition based on other variables. While using a clever tricks by defining a variable that evaluates to another one, this technique also work in contextes where variables aren't evaluated! (ex: calling a variable from scheme)
```c, Proxy variable example
#+A My variable...
#+R& A :: References A
%R% :: → "My variable..."
#+A Changed!
%R% :: → "Changed!"
```
The syntax consists of adding a =&='s after the variable's name.
**Copy definitions**
It is also possible to create variable that will copy the content of another variable at a given moment.
This is what copy definitions are for!
```c, Copy defitinions example
#+A My variable...
#+R% A :: Copy of A
%R% :: → "My variable..."
#+A Changed!
%R% :: → "My variable..."
```
The syntax consists of adding a =\%='s after the variable's name.
## Raws & Inline raws
Sometime it is convinient to add code that the compiler will leave as-is so that the target output document can be interacted with without having to go through NML.
This feature can be abused in order to extend the language and create new syntax elements (see %(make-anchor-link "DefStyle")%, %(make-anchor-link "DefPresentation")% and %(make-anchor-link "DefProcess")%)
**Inline raws**
Inline raws can be used inside a paragraph, applied on text for instance.
The syntax goes as follow: =\{{{content}}}=
For instance, raws can be used to add colored text: {{{<a style="color:red">Red text</a>}}}.
This was done using the following: =\{{{<a style\="color:red">Red text</a>\}}}=
But using a bit of scheme, we can make it easier to use:
```c
%%(define (colored-text color text)
(string-append "{{{<a style=\"color:" color "\">}}}" text "{{{</a>}}}"))%%
```
%%(define (colored-text color text)
(string-append "{{{<a style=\"color:" color "\">}}}" text "{{{</a>}}}"))%%
Usage: =\%(colored-text "green" "Green text")\%= **→** %(colored-text "green" "Green text")%
**Regular Raws**
Regular raws act like inline raws except that they are not meant to be used inside a text paragraph.
## Comments
You can add comments to NML documents by putting =\::='s before your comment.
## Code fragment
It is possible to add code fragment to your documents. To make your life easier, code fragment support a variety of options such as importing code from a file or automatic highlighting.
Here is the syntax for a simple code fragment:
```c
\```language (write txt for none)
Your code here
\```
```
Additionaly, you can add a name for your code fragment, which will display as a title:
```c
\```c, My C Program
[...]
\```
```
**Code style**
In order to use code fragments, you must first have a code style. This is a file used by =source-highlight= to set colors of highlighted code.
Currently, this file is using [nml.style](nml.style) a color code that looks relatively well with NML's default colors.
To set which file to use for =source-highlight=, you have to modify the =%CodeStyle%= variable
**Including code from another file**
Instead of copy-pasting you code into code fragments and end up with long NML files, you can import text from a source file rather easily.
The syntax for importing code is =#:Inc path=.
Below is the result given by importing the [fizz.cpp](./resources/fizz.cpp) file:
```cpp, My C++ fizzbuzz
#:Inc ./resources/fizz.cpp
```
This is given by:
```c
\```cpp, My C++ fizzbuzz
\#:Inc ./resources/fizz.cpp
\```
```
Additionaly, you can import a selected number of lines from a file.
The syntax syntax is =#:Inc path, start, n=, where =start= is the starting line, and =n= is the number of lines to import.
```cpp, Lines 8 to 13 of fizz.cpp
#:Inc ./resources/fizz.cpp, 8, 6
```
This is given by:
```c
\```cpp, Lines 8 to 13 of fizz.cpp
\#:Inc ./resources/fizz.cpp, 8, 6
\```
```
## Includes
You can include other document's content with the include mechanic.
This can be used to include a template document that sets a special footer, style or anything you want to have in all of your documents.
Including a document works as if copy-pasting the content of the document inside the current document.
For instance, we can include the =[include.nml](resources/include.nml)= document inside this one:
#:Inc resources/include.nml
*Note*: Including another document will import (and overwrite) variables, figures, external references, custom styles, custom presentations and custom processes.
For instance, including document =include.nml= imported the =IncludedVar= variable:
*\%IncludedVar\%* **→** %IncludedVar%
The syntax is as following:
```c, (on an empty new line)
\#:Inc path
```
## Lists
**Unnumbered**
NML supports numbered and unnumbered lists out of the box, lists can also be nested indefinitely.
#+Bullet •
* This an unnumbered list
* It has another unnumbered list nested into it!\\
#+Bullet →
** Nested!
%(spoiler-text "NML Code" "```c
#+Bullet •
* This an unnumbered list
* It has another unnumbered list nested into it!\\\\
#+Bullet →
** Nested!
```")%
Lists' bullets can be customized, using the =Bullet= variable. It's also possible to set a custom style for the compiler using =BulletStyle=.
The default =Bullet= for unnumbered lists is =\*)=, which is ugly, because it's meant to be changed!
The =Bullet= variable resets at the end of every unordered list. Changing =DefaultUnorderedBullet= is a good way of ensuring a default unordered bullet.
**Numbered**
Likewise, lists can also be numerated:
- First element
- Second
-- Second A)
-- Second B)\\
#+Bullet •
-* Second ???
- Third
%(spoiler-text "NML Code" "```c
- First element
- Second
-- Second A)
-- Second B)\\\\
#+Bullet •
-* Second ???
- Third
```")%
Numbered lists' bullets can also be customized:
#+Bullet i/
- First element
- Second\\
#+Bullet A)\\
#+BulletStyle color:red
-- Second A)
-- Second B)\\
#+Bullet •
-* Second ???\\
#+BulletCounter 5
- Fifth
%(spoiler-text "NML code" "```c
#+Bullet i/
- First element
- Second\\\\
#+Bullet A)\\\\
#+BulletStyle color:red
-- Second A)
-- Second B)\\\\
#+Bullet •
-* Second ???\\\\
#+BulletCounter 5
- Fifth
```")%
The =BulletCounter= variable overwrite the current counter to have custom numbered indices lists.
In order to be valid, a =Bullet= variable for numbered lists must contain at least one of the following character:
- =1= For numerical numbering (1, 2, 3, ...)
- =a= For lowercase alphabetical numbering (a, b, c, ...), up to 26
- =A= For uppercase alphabetical numbering (A, B, C, ...), up to 26
- =i= For lowercase roman numbering (i, ii, iii, ...), up to 3999
- =I= For uppercase roman numbering (I, II, III, ...), up to 3999
- =v= For lowercase roman numbering, but =i='s are replaced by =•='s (•, ••, •••, ...), up to 3999
The default =Bullet= for numbered lists is =1.=, which produces bullets as shown above
Other characters in =Bullet= will not be touched and be added to the final bullet (e.g =<1i.>= gives =<1i.>=, =<2i.>=, =<3i.>=...)
The =Bullet= variable resets at the end of every ordered list. Changing =DefaultOrderedBullet= is a good way of ensuring a default ordered bullet.
*Note*: In future versions of NML, there will be a possibility to invoke a scheme procedure in order to have custom bullets.
## Sections
Sections basic elements in NML, they work as delimiters and have a name. Depending on the compiler and settings, a list of sections might be produced somewhere in the resulting document (such as a *table of content*) and might be numbered.
```c
# Sections
## SubSection
### SubSubSection
##* This subsection is not numbered
###** This SubSubSection is not numbered and will not appear in the TOC
```
**Numbering format**
Sections numbering is based on the current number of sections and previous sections.
By default, numbering separates all numbers with a =.='s. You can define a scheme procedure that will output a custom formatting.
Define =OrderedSectionFormatter= or =UnorderedSectionFormatter= to the name of a lisp procedure. Both procedures should take two argument, the first being the section's number and the second being the section stack.
```c
%%(define (ordered-formatter num sstack)
"<...>")
(define (unordered-formatter num sstack)
"<...>")%%
#+OrderedSectionFormatter ordered-formatter
#+UnorderedSectionFormatter unordered-formatter
```
**Specifity in HTML**
In HTML, sections have a link that is used to create direct link referring to a unique section
To change the content of this lick, modify the =SectionLink= variable, the default in HTML is =[link]=.
You can also set where this link will show by modifying the =SectionLinkPos= variable:
* Set it to =before= to have it shown before the section's name
* Set it to =after= to have it shown after the section's name
* Set it to =never= to disable it
*Note*: below are the settings used in this document:
```c
#+SectionLinkPos before
#+SectionLink #
```
## Text styles
You can add style to text to make **bold**, *italic*, __underline__ and =verbatim= text.
The syntax goes a follow:
* =\*\*Bold text\*\*= **→** **Bold text**
* =\*Italic text\*= **→** *Italic text*
* =\__Underline text\__= **→** __Underline text__
* =\=Verbatim text\== **→** =Verbatim text=
It is possible to create your own text styles, see the %(make-anchor-link "DefStyle")% section.
## Ruler
===
You can add a 'ruler' to a document that acts as a horizontal separator.
To create a ruler you have to put three (or more) =\=='s signs on an empty line.
```c
===
```
## Figures
Your documents will ofter need figures. NML supports three main types of figures: pictures, videos and audio^{{Of course, depending on the target format, some figures types may not be supported. For instance, videos and audio are not supported for PDF documents.}}.
NML tries to handle figures so that they can be interacted with or referenced in other paragraphs.
For instance, below is a picture:
![figure1](resources/fig1.jpg) Optional figure description
This picture is internally named =figure1=, meaning it can be referenced inside a paragraph using it's internal name: §{figure1}
```c, Defining a figure (on an empty new line)
![internal reference name](figure path.extension) Optional description
```
```c, Referencing a figure
§{internal reference name}
```
## Links
Similarly to figures, your document may contains links.
For instance:
* My [github](https://github.com/ef3d0c3e)
* My [e-mail](mailto:ef3d0c3e@pundalik.org)
```c, Links syntax
[Link text](protocol://link url)
```
## Annotations
You can add inline annotations to your documents, like so^{{This is an *annotation*}}.
#+Annotation My fizzbuzz solution
Or like this^{{===
My super cool fizzbuzz
```cpp, Source code
#:Inc ./resources/fizz.cpp
```
===}}.
#+Annotation [note]
The syntax for annotations is as following:
```c
^{{Annotation content
Can be split on multiple lines}}
```
By default annotations will appear with the =[note]= label.
You can change this by modifying the =Annotation= variable
%(spoiler-text "NML code" "```c
You can add inline annotations to your documents, like so^{{This is an *annotation*}}.
#+Annotation My fizzbuzz solution
Or like this^{{===
My super cool fizzbuzz
\\```cpp, Source code
\\#:Inc ./resources/fizz.cpp
\\```
===}}.
```")%
## External references
Like Wikipedia pages, documents can contain external references -- references to external resources.
For instance, here is an external reference to the Wikipedia article "Green terror"§[Green terror Wikipedia article][Wikipedia contributors](https://en.wikipedia.org/wiki/Green_terror).
The syntax is as following:
```c
§[Clickable reference description][Reference authors](protocol://reference hyperlink)
```
**Reference section**
External references will generate a 'section' called =Reference= that will contain every references of the document.
You can view a reference in this reference ection by clicking on the number generated.
* The name of the reference section is defined in the variable =ExternalRef=
* Using multiple times the same reference will create a single entry into the reference section (and they will also share the same number)
%(spoiler-text "NML code" "```c
§[Green terror Wikipedia article][Wikipedia contributors](https://en.wikipedia.org/wiki/Green_terror).
```")%
## Quotations
In NML document you can add quotations using the following syntax:
```c
>Quotation
>continued on this line
>[Quote's author]
```
>Quotation
>continued on this line
>[Quote's author]
*Note*: The quote's author part is optional and can be placed anywhere in the quote
## Presentation
Presentation refers to syntax elements that set how to display a paragraph (centering, boxed, ...)
**Centering**
To center things, you have to put them between two brackets (=\[[= and =]]=):
```c
[[Centered text!]]
```
Gives:
[[Centered text!]]
**Boxed**
To put a box aroung things, you have to put them between three brackets (=\[[[= and =]]]=):
```c
[[[Boxed text!]]]
```
Gives:
[[[Boxed text!]]]
**Left line**
To put a line on the left of things, you have to put them between these characters =\[[|= and =|]]=:
```c
[[|This text has a line on its left!|]]
```
Gives:
[[|This text has a line on its left!|]]
# LaTeX
:: Store and reset tex-related variables
#+TexPreambleSave% TexPreamble
#+TexPreamble
#+TexPrependSave% TexPrepend
#+TexPrepend
NML has built-in support for LaTeX. However, it requires that you install a modified [latex2svg](./latex2svg) to your =PATH= and that you install its requirements, more information on the program's [GitHub page](https://github.com/Moonbase59/latex2svg).
## Setup
Compiled LaTeX files are stored in a folder to speed-up document compiling. By default this folder is called =tex=, however you can change it's name using the =--tex-directory= argument when executing NML.
Also, the folder won't auto-generate, you will have to manually create it for the tex processing to be enabled.
In order to write inline math LaTeX in NML, you have to use the followin syntax: =\$LaTeX math code\$=
For instance: =\$1+1\=2\$= **→** $1+1=2$
As you can see, by default, LaTeX will appear black, which is not too visible on a dark background.
You can change the text color by defining the following variables:
```c
#+TexPreamble \
\usepackage{xcolor, amsfonts}\\
\definecolor{__color1}{HTML}{c5c5ce}
#+TexPrepend \color{__color1}
```
#+TexPreamble \
\usepackage{xcolor, amsfonts}\\
\definecolor{__color1}{HTML}{c5c5ce}
#+TexPrepend \color{__color1}
Now, LaTeX code will appear with a more visible color: $\sum_{k \in \mathbb N^*} \frac{1}{k^2} = \frac{\pi^2}{6}$
:: Revert tex variables to match the ones in template.nml
#+TexPreamble% TexPreambleSave
#+TexPrepend% TexPrependSave
Compiled LaTeX files are stored in vector graphic format (=svg=).
* The files with suffixes =_m= are for %(make-anchor-link "Inline math")%
* The files with suffixes =_l= are for %(make-anchor-link "Inline math line")%
* The files with suffixes =_n= are for %(make-anchor-link "Regular LaTeX")%
* The files with suffixes =_i= are for %(make-anchor-link "LaTeX include")%
## Inline math
Inline math is the default LaTeX mode for writing equations, it is the same as typing your code between two =\$='s in a regular LaTeX file.
The syntax is simply =\$LaTeX math code\$=.
For instance:
* =\$\sin \frac{\pi}{4} \= \frac{\sqrt 2}{2}\$= **→** $\sin \frac{\pi}{4} = \frac{\sqrt 2}{2}$
* =\$x^n (T\*R) \= \sum^{n}_{k\=0} \begin{pmatrix}n \\ k\end{pmatrix} (x^k T) \* (x^{n-k} R)\$= **→** $x^n (T*R) = \sum^{n}_{k=0} \begin{pmatrix}n \\ k\end{pmatrix} (x^k T) * (x^{n-k} R)$
While in certain cases, inline math may have trouble respecting line-height and alignment, it is very handy and easy to use.
## Inline math line
Inline math line is the same as inline math, except it is equivalent to typing LaTeX code between two =\$\$='s.
The syntax is simply =\$\$LaTeX math code\$\$=.
## Regular LaTeX
Regular LaTeX is the same as directly typing in LaTeX. With this you can create, for instance, LaTeX plots, graphes or tables directly in your NML document.
The syntax is =\$|Your LaTeX|\$=. Below is an example:
[[$|This is LaTeX typed directly into \textbf{NML}!|$]]
Combining with =#:Inc=, you can import LaTeX from other files:
[[
#:Inc ./resources/plot.tex
]]
## LaTeX related variables
LaTeX processing uses 3 different variables:
* =TexFontSize= (default: =12=) This sets the font size (in pt) of the generated LaTeX.
* =TexPreamble= This is a preamble used before the content
* =TexPrepend= This will be appended at the begining of the content of every LaTeX file.
* =TexAppend= This will be appended at the end of the content of every LaTeX file.
You should look into [template.nml](./template.nml), the file where the LaTeX settings for this document are.
# User-Defined Syntax
NML offers the possibility to create you own syntax with the help of scheme.
## DefStyle
DefStyle allows you to define new text style.
The syntax is:
```c, (on an empty new line)
#:DefStyle syntax-name regex
```
In order to define how this new style works, you will have to define two new scheme procedure.
Given that your new syntax is called =my-style=, you will have to define procedures =(my-style-begin)= and =(my-style-end)=.
They both will return a string and the *begin* procedure is called when your style begins, and the *end* procedure is called when your style ends.
Below is an example style, we define a strikethrough text style:
```c, Example strikethrough style
%%(define (strikethrough-begin)
"<s>")
(define (strikethrough-end)
"</s>")%%
#:DefStyle strikethrough ~
```
%%(define (strikethrough-begin)
"<s>")
(define (strikethrough-end)
"</s>")%%
#:DefStyle strikethrough ~
The result:
* \~Strikethrough text\~ **→** ~Strikethrough text~
**More complex styles**^{{This section contains informations not yet detailed in this readme. Such as the NML Object interface for Guile.}}
Additionaly, you can define a =(my-style-apply elements)= procedure. This procedures takes the content of the document on which the style is used and returns the new content after modifications.
This can be used to make some more complex styles:
```c, Alternating color custom text style
%%(define (alternating-begin)
"")
(define (alternating-end)
"")
(define (alternating-apply elems)
(let ((result '()))
(for-each (lambda (x)
(if (nmlo-is-text x)
(let ((str (nmlo-text-content x)))
(string-for-each-index (lambda (i)
(if (even? i)
(set! result (append result (list
(list 16 "<a style=\"color:orange\">")
(list 0 (string (string-ref str i)))
(list 16 "</a>"))))
(set! result (append result (list
(list 16 "<a style=\"color:red\">")
(list 0 (string (string-ref str i)))
(list 16 "</a>"))))
))
str))
(set! result (append result x)))
)
elems)
result))
%%
#:DefStyle alternating @
```
%%(define (alternating-begin)
"")
(define (alternating-end)
"")
(define (alternating-apply elems)
(let ((result '()))
(for-each (lambda (x)
(if (nmlo-is-text x)
(let ((str (nmlo-text-content x)))
(string-for-each-index (lambda (i)
(if (even? i)
(set! result (append result (list
(list 16 "<a style=\"color:orange\">")
(list 0 (string (string-ref str i)))
(list 16 "</a>"))))
(set! result (append result (list
(list 16 "<a style=\"color:red\">")
(list 0 (string (string-ref str i)))
(list 16 "</a>"))))
))
str))
(set! result (append result x)))
)
elems)
result))
%%
#:DefStyle alternating @
The result:
* =\@Alternating color text!\@= **→** @Alternating color text!@
# Scheme
NML uses the GNU Scheme language to extend documents.
You can define and call scheme procedure inside nml documents in order to extend NML.
## Inline scheme
You can use scheme as a handy tool for inline operations.
For instance you can use it to output the result of calling a procedure.
When using inline scheme, the SCM object returned by the procedure is converted as a string for display.
Example:
- =\%(- 10 (\* 2 2) (/ 60 (+ 3 4 4)))\%= gives %(- 10 (* 2 2) (/ 60 (+ 3 4 4)))%.
- =\%(string-append "Hello" ", " "world" "!")\%= gives %(string-append "Hello" ", " "world" "!")%
- =\%(exp (sqrt -1))\%= gives %(exp (sqrt -1))%
The syntax is simply =\%(scheme-expression)\%=
## Global scheme
It's also possible to write scheme code without having to worry about returning a SCM object.
This is usualy better for defining procedures or variables.
The syntax consists of putting the scheme code inside two =\%='s
For instance, we can define the factorial procedure as follow:
```lisp
%%(define (! n)
(if (= n 1)
1
(* n (! (- n 1)))))%%
```
%%(define (! n)
(if (= n 1)
1
(* n (! (- n 1)))))%%
And then we can call it using inline scheme: =\%(! 5)\%= **→** %(! 5)%.
## Custom procedures
NML defines new scheme procedures for convennence.
Also, in order to interact with NML, scheme has access to procedures and objects related to NML.
Below is a list of important procedures and objects:
**Variable related:**
* **(nml-var-defined *var-name*)** : Checks if a NML variable with name =var-name= is defined in current document.
* **(nml-var-get *var-name*)** : Gets content of NML variable with name =var-name=, returns =nil= if variable does not exist.
* **(nml-var-get-default *var-name* *default-value*)** : Gets content of NML variable with name =var-name=, returns =default-value= if variable does not exist.
* **(nml-var-define *var-name* *var-value*)** : Defines new NML variable with name =var-name= and content =var-value=. Procedure returns true on success. Both parameters must be strings, otherwise the procedure returns false.
**Document related:**
* **(nml-doc-parse *doc-path* (*doc-inherit* *doc-data*))** : Parses a NML document located at =doc-path=. Additionaly, another document and data may be passed to inherit certain attributes. \
Parsing will use of =nml-current-parser= as a parser. Will returns a NML document on success.
* **(nml-doc-compile *doc* *compiler-name* (*tex-path*))** : Compiles a NML document (=doc=) into a string, using compiler =compiler-name=.
* **nml-current-doc** Current document instance
* **nml-current-data** Current data instance
* **nml-current-parser** Current parser instance
**Utilities:**
* **(nml-num-roman *number* *roman-base*)**: Converts a number (=number=) to roman numeral, using =roman-base= as an alphabet. \
Exemple: =\%(nml-num-roman 14 '("M" "CM" "D" "CD" "C" "XC" "L" "XL" "X" "IX" "V" "IV" "I"))\%= **→** %(nml-num-roman 14 '("M" "CM" "D" "CD" "C" "XC" "L" "XL" "X" "IX" "V" "IV" "I"))%.
**Utilities for HTMLCompiler:**
* **(nml-html-get-anchor *name*)**: Gets anchor name of =name= as a string.
* **(nml-html-format *text*)**: Formats =text= to be HTML safe.
**Filesystem Utilities:**
* **(nml-fs-path *path-name*)**: Gets path representing =path-name= (relative to current working directory)
* **(nml-fs-exists *path*)**: Returns whether or not =path= exists
* **(nml-fs-is-file *path*)**: Returns whether or not =path= is a file
* **(nml-fs-is-dir *path*)**: Returns whether or not =path= is a directory
* **(nml-fs-filename *path*)**: Returns ending filename of =path= (=src/test.c= **→** =test.c=)
* **(nml-fs-fullname *path*)**: Returns name of absolute path of =path=.
* **(nml-fs-map *proc* *path*)**: Applies procedure =proc= to every elements of =path=
**String Utilities:**
* **(string-tail *string* *n*)**: Returns a new string containing the =n=-th last characters of =string=.
* **(string-ends-with *string* *suffix*)**: Returns whether or not =string= ends with =suffix=
* **(string-starts-with *string* *prefix*)**: Returns whether or not =string= starts with =prefix=
## Scheme example
**Compiling a NML document from another NML document**
```lisp
(define (write-file path content)
(if (and (nml-fs-exists path) (not (nml-fs-is-file path))) 'nil)
(call-with-output-file (nml-fs-fullname path)
(lambda (output-port)
(display content output-port))))
(define (compile-document compiler path)
(nml-doc-compile (nml-doc-parse path) compiler))
(define document (nml-doc-parse (nml-fs-path "document.nml")))
(define output (nml-fs-path "output.html"))
(write-file output (nml-doc-compile document "html"))
```
## The NML Object interface
NML offers it's own interface to allow scheme code to interact with nml data types.
Here's a list of syntax elements that have a scheme interface:
=Text=,
=StylePush=,
=StylePop=,
=Break=,
=Section=,
=ListBegin=,
=ListEnd=,
=ListEntry=,
=Ruler=,
=Figure=,
=Code=,
=Quote=,
=Reference=,
=Link=,
=Latex=,
=Raw=,
=RawInline=,
=ExternalRef=,
=Presentation=,
=Annotation=,
=CustomStylePush=,
=CustomStylePop=,
=CustomPresPush=,
=CustomPresPop=
All of these elements have a =make= method =nmlo-ELEMENT-make=
And also have setters and getters for each of their members: =nmlo-ELEMENT-FIELD-get= and =nmlo-ELEMENT-FIELD-set=.
# Compilers
## Text compiler
Text compiler is the compiler that compiles a parsed NML document to text, this is used for developping features and debugging.
## HTML compiler
HTML compiler is the compiler that compiles a parsed NML document to a HTML webpage.
**HTML Specific variables**
* =CSS= Location of a CSS file.
* =Title= Document's title
* =PageTitle= Web page title (default: =Title=)
* =Author= Document's author
* =Date= Date of document
* =TOC= Name of table of content (wille not appear unless set)
* =ExternalRef= Name of the external reference section
# Other resources
* [GNU Guile Reference Manual](https://www.gnu.org/software/guile/manual/)
* [Source code for this document](https://github.com/ef3d0c3e/NML/readme/readme.nml)

3
src/files.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod file;
pub mod cursor;
pub mod token;

28
src/files/cursor.rs Normal file
View file

@ -0,0 +1,28 @@
use super::file::File;
#[derive(Debug)]
pub struct Cursor<'a>
{
pub file: &'a File,
pub content: String,
pub position: usize,
}
impl<'a> Cursor<'a>
{
pub fn new(_file: &'a File) -> Result<Cursor<'a>, std::io::Error>
{
let _content = match std::fs::read_to_string(&_file.path)
{
Ok(content) => content,
Err(error) => return Err(error),
};
Ok(Cursor
{
file: _file,
content: _content,
position: 0usize,
})
}
}

17
src/files/file.rs Normal file
View file

@ -0,0 +1,17 @@
use std::path::Path;
#[derive(Debug)]
pub struct File
{
pub path: String,
}
impl File
{
pub fn new(_path: String) -> File
{
File {
path: _path,
}
}
}

30
src/files/token.rs Normal file
View file

@ -0,0 +1,30 @@
use super::file::File;
use super::cursor::Cursor;
pub struct Token<'a>
{
file: &'a File,
start: usize,
len: usize,
}
impl<'a> Token<'a>
{
pub fn new(_file: &'a File, _start: usize, _len: usize) -> Token<'a>
{
Token {
file: _file,
start: _start,
len: _len,
}
}
pub fn from(cursor: &'a Cursor, mat: regex::Match<'a>) -> Token<'a>
{
Token {
file: cursor.file,
start: cursor.position,
len: mat.len(),
}
}
}

110
src/main.rs Normal file
View file

@ -0,0 +1,110 @@
mod parser;
use self::parser::rule::SyntaxRule;
use self::parser::section::SectionRule;
mod files;
use self::files::file::File;
use self::files::cursor::Cursor;
mod syntax;
use syntax::element::Element;
use syntax::element::Text;
fn main() {
let file = File::new(String::from("./test.nml"));
let mut cursor = Cursor::new(&file).unwrap();
cursor.position = 5;
let rule_se = SectionRule::new();
let (token, res) = rule_se.on_match(&cursor).unwrap();
println!("{}", res.elements.len());
/*
let re_sections = regex::Regex::new(r"(?:^|\n)(#{1,})(\*|\+)((?:\t| ){0,})(.*)").unwrap();
//let mut validators = Vec::<Box<dyn GroupValidator>>::new();
let f = File::new(Box::new(std::path::Path::new("./test.nml")));
let content = std::fs::read_to_string(*f.path).unwrap();
let grammar = vec![re_sections];
let mut positions = [0usize; 1];
let mut i = 0;
while i < content.len()
{
// Update every positions
for k in 0..grammar.len()
{
let rule = &grammar[k];
let position = &mut positions[k];
if *position == std::usize::MAX { continue };
match rule.find_at(&content, i)
{
Some(mat) => *position = mat.start(),
None => *position = std::usize::MAX,
}
println!("{position}");
}
// Gets closest match
let mut next_position = std::usize::MAX;
let mut closest_match = std::usize::MAX;
for k in 0..grammar.len()
{
if positions[k] >= next_position { continue; }
next_position = positions[k];
closest_match = k;
}
println!("Unmatched: {}", &content[i..next_position]);
// No matches left
if closest_match == std::usize::MAX
{
println!("Done");
break;
}
// Extract matches from rule
i = next_position; // Set to begining of match
let mat = &grammar[closest_match].captures_at(&content, i).unwrap(); // Capture match
for m in 0..mat.len()
{
match mat.get(m)
{
Some(s) => {
println!("Group {m}: `{}`", s.as_str());
},
None => println!("Group {m}: None"),
}
}
i += mat.get(0).unwrap().len(); // Add match length
println!("Left={}", &content[i..]);
println!("pos={i}");
let mut s = String::new();
std::io::stdin().read_line(&mut s).expect("Did not enter a correct string");
}
*/
/*
validators.push(Box::new(StringValidator::new("Depth".to_string(), |_group| -> ValidationStatus {
ValidationStatus::Ok()
})));
validators.push(Box::new(StringValidator::new("Index Type".to_string(), |group| -> ValidationStatus {
match group
{
"" => ValidationStatus::Ok(),
"*" => ValidationStatus::Ok(),
_ => ValidationStatus::Error("")
}
ValidationStatus::Ok()
})));
*/
//let _sec_rule = SyntaxRule::new("Section".to_string(), r"(?m)(?:^|\n)(#{1,})(\\*|\\+)((?:\t| ){0,})(.*)", validators).unwrap();
}

3
src/parser.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod rule;
pub mod section;

51
src/parser/rule.rs Normal file
View file

@ -0,0 +1,51 @@
use regex::Captures;
use super::super::syntax::element::Element;
use super::super::files::cursor::Cursor;
use super::super::files::token::Token;
pub struct RuleResult
{
length: usize,
pub elements: Vec<Box<dyn Element>>,
}
impl RuleResult
{
pub fn new(_length: usize, elem: Box<dyn Element>) -> RuleResult
{
RuleResult
{
length: _length,
elements: vec![elem],
}
}
}
#[derive(Debug)]
pub struct RuleError<'a>
{
// where: token
cursor: &'a Cursor<'a>,
mat: Option<regex::Match<'a>>,
message: String,
}
impl<'a> RuleError<'a>
{
pub fn new(_cursor: &'a Cursor<'a>, _match: Option<regex::Match<'a>>, _message: String) -> RuleError<'a>
{
RuleError
{
cursor: _cursor,
mat: _match,
message: _message,
}
}
}
pub trait SyntaxRule
{
fn name(&self) -> &'static str;
fn next_match<'a>(&self, cursor: &'a Cursor) -> Option<usize>;
fn on_match<'a>(&self, cursor: &'a Cursor) -> Result<(Token<'a>, RuleResult), RuleError<'a>>;
}

151
src/parser/section.rs Normal file
View file

@ -0,0 +1,151 @@
use regex::Regex;
use super::rule::{RuleResult, RuleError, SyntaxRule};
use super::super::files::cursor::Cursor;
use super::super::files::token::Token;
use super::super::syntax::element::{Element, ReferenceableElement};
pub mod SectionKind
{
pub const NONE : u8 = 0x00;
pub const NO_TOC : u8 = 0x01;
pub const NO_NUMBER : u8 = 0x02;
}
pub struct Section
{
title: String,
reference: Option<String>,
section_kind: u8,
depth: usize,
}
impl Section
{
pub fn new<'h>(_title: String, _reference: Option<String>, kind: u8, _depth: usize) -> Section
{
Section {
title: _title,
reference: _reference,
section_kind: kind,
depth: _depth,
}
}
}
impl Element for Section
{
fn element_name(&self) -> &'static str { "Section" }
}
impl ReferenceableElement for Section
{
fn reference_name(&self) -> Option<&String> { self.reference.as_ref() }
}
// TODO: Single file for grammar + element, and add `Rule` suffix for rules
pub struct SectionRule
{
regex: Regex,
}
impl SectionRule
{
pub fn new() -> SectionRule
{
SectionRule
{
regex: regex::Regex::new(r"(?:^|\n)(#{1,})(\{.*\})?((?:\*|\+){0,})?((?:\t| ){0,})(.*)").unwrap()
}
}
}
impl SyntaxRule for SectionRule
{
fn name(&self) -> &'static str { "Section" }
fn next_match<'a>(&self, cursor: &'a Cursor) -> Option<usize>
{
match self.regex.find_at(&cursor.content, cursor.position)
{
Some(m) => Some(m.start()),
None => None
}
}
fn on_match<'a>(&self, cursor: &'a Cursor) -> Result<(Token<'a>, RuleResult), RuleError<'a>>
{
let m = self.regex.captures_at(&cursor.content, cursor.position).unwrap(); // Capture match
let section_depth = match m.get(1)
{
Some(depth) => {
if depth.len() > 6
{
return Err(RuleError::new(&cursor, m.get(1),
format!("Section depth must not be greater than 6, got `{}` (depth: {})", depth.as_str(), depth.len())))
}
depth.len()
}
_ => return Err(RuleError::new(&cursor, m.get(1), String::from("Empty section depth")))
};
// Spacing
match m.get(4)
{
Some(spacing) => {
if spacing.as_str().is_empty() || !spacing.as_str().chars().all(|c| c == ' ' || c == '\t')
{
return Err(RuleError::new(&cursor, m.get(4),
format!("Sections require spacing made of spaces or tab before the section's title, got: `{}`", spacing.as_str())))
}
}
_ => return Err(RuleError::new(&cursor, m.get(4),
String::from("Sections require spacing made of spaces or tab before the section's title")))
}
let section_refname = match m.get(2)
{
Some(reference) => {
// TODO: Validate reference name
// TODO: After parsing, check for duplicate references
Some(String::from(reference.as_str()))
}
_ => None
};
let section_kind = match m.get(3)
{
Some(kind) => {
match kind.as_str() {
"*+" => SectionKind::NO_NUMBER | SectionKind::NO_TOC,
"*" => SectionKind::NO_NUMBER,
"+" => SectionKind::NO_TOC,
"" => SectionKind::NONE,
_ => return Err(RuleError::new(&cursor, m.get(3),
format!("Section kind must be either `*` for unnumbered, `+` to hide from TOC or `*+`, got `{}`. Leave empty for normal sections", kind.as_str())))
}
}
_ => SectionKind::NONE,
};
let section_title = match m.get(5) {
Some(title) => match title.as_str() {
"" => return Err(RuleError::new(&cursor, m.get(5),
String::from("Sections require a non-empty title"))),
_ => String::from(title.as_str())
}
_ => return Err(RuleError::new(&cursor, m.get(5),
String::from("Sections require a non-empty title")))
};
let section = Box::new(Section::new(
section_title,
section_refname,
section_kind,
section_depth));
Ok((Token::from(cursor, m.get(0).unwrap()), RuleResult::new(m.get(0).unwrap().len(), section)))
}
}

1
src/syntax.rs Normal file
View file

@ -0,0 +1 @@
pub mod element;

4
src/syntax/document.rs Normal file
View file

@ -0,0 +1,4 @@
struct Document
{
}

30
src/syntax/element.rs Normal file
View file

@ -0,0 +1,30 @@
pub trait Element
{
fn element_name(&self) -> &'static str;
fn token(&'a self) -> Token<'a>
}
pub trait ReferenceableElement : Element
{
fn reference_name(&self) -> Option<&String>;
}
pub struct Text
{
content: String,
}
impl Text
{
pub fn new<'h>(_content: &'h str) -> Text
{
Text {
content: String::from(_content)
}
}
}
impl Element for Text
{
fn element_name(&self) -> &'static str { "Text" }
}

6
test.nml Normal file
View file

@ -0,0 +1,6 @@
Line1
##{refname}+
##*a* inc
######+ First section
second
##* Subsectiona