First commit
This commit is contained in:
commit
b076958893
18 changed files with 5185 additions and 0 deletions
|
@ -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();
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
3821
Cargo.lock
generated
Normal file
3821
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
Normal file
11
Cargo.toml
Normal 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
896
readme.nml
Normal 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
3
src/files.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod file;
|
||||||
|
pub mod cursor;
|
||||||
|
pub mod token;
|
28
src/files/cursor.rs
Normal file
28
src/files/cursor.rs
Normal 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
17
src/files/file.rs
Normal 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
30
src/files/token.rs
Normal 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
110
src/main.rs
Normal 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
3
src/parser.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod rule;
|
||||||
|
pub mod section;
|
||||||
|
|
51
src/parser/rule.rs
Normal file
51
src/parser/rule.rs
Normal 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
151
src/parser/section.rs
Normal 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
1
src/syntax.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod element;
|
4
src/syntax/document.rs
Normal file
4
src/syntax/document.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
struct Document
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
30
src/syntax/element.rs
Normal file
30
src/syntax/element.rs
Normal 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
6
test.nml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Line1
|
||||||
|
##{refname}+
|
||||||
|
##*a* inc
|
||||||
|
######+ First section
|
||||||
|
second
|
||||||
|
##* Subsectiona
|
Loading…
Reference in a new issue