Skip to content

Commit 8494897

Browse files
committed
updated readme
1 parent e67b3ea commit 8494897

File tree

2 files changed

+64
-28
lines changed

2 files changed

+64
-28
lines changed

README.md

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,33 @@
22

33
BPT - **B**ash **P**ure **T**emplate (Engine)
44

5+
<p align="center"><img src="res/bpt.svg"></img></div>
6+
57
**Features:**
68

7-
- **Pure:** The rendering process is pure, ensuring that the rendered result is fully predictable and consistent across multiple runs.
8-
- This is the core feature and also the purpose of this template engine. This feature makes `bpt` suitable for generating a large number of small configuration files without unexpected changes due to an environment variable change or an external file change when re-generating config files.
9-
- A fingerprinting function is provided to digest all the inputs, including files, variables, and the engine itself.
9+
- **Pure:** The rendering process resembles a [pure function][pure_function], ensuring that the rendered results are fully predictable and consistent across multiple runs given the same inputs. It also produces no side-effects other than the generated script or the render result.
10+
- This feature makes `bpt` suitable for generating a large number of small configuration files without unexpected changes due to an environment variable change or an external file change when re-generating config files.
11+
- A fingerprinting function is provided to digest all the inputs, including template files, variables, and the engine itself.
1012
- Functionalities are also provided to collect all the variables and includes used in a template.
11-
- **Minimal dependency:** `bpt` requires only minimal external programs in addition to bash itself. All the external programs required are within coreutils, which is most likely bundled with the Linux installation.
13+
- **Minimal dependency:** `bpt` requires only a minimal set of external programs in addition to bash. All the external programs required are in coreutils, which is most likely bundled with your Linux installation.
1214
- `bash >= 5`
13-
- `md5sum`, `sort`, `uniq`, and `cat`.
15+
- coreutils: `md5sum`, `sort`, `uniq`, `cat`, `mktemp`, ...
1416
- **Single bash script:** The `bpt.sh` script contains everything you need.
1517
- It can be called directly or used as a library by sourcing it in another bash script.
1618
- It works out of the box.
17-
- **A little bit of extra functionalities:** While a simple template engine that only replaces `{{var}}`s can also be easily made pure, the functionalities is somewhat limited. BPT provides a little bit extra that makes life easier.
19+
- **A little bit of extra functionalities:** While a simple template engine that only replaces `{{var}}`s can also be easily made pure, their functionalities are somewhat limited. BPT provides a little bit extra that makes life easier.
1820
- Delimiters can be replaced.
19-
- Branching, looping, and basic boolean operations are supported.
21+
- Branching, list iteration, and basic boolean operations are supported.
2022
- Recursive inclusion of templates is also supported.
2123
- Refer to [Writing Templates](#writing-templates) for more details.
2224

2325
**Deficiencies:**
2426

25-
- **Slow:** Although optimization has been made, a LALR(1) parser implemented in bash is still quite slow. It is not recommended to use `bpt` for large and complex templates such as an HTML templating engine.
27+
- **Slow:** Although optimizations have been made, a LALR(1) parser implemented in bash is still quite slow. It is not recommended to use `bpt` for large and complex templates.
2628
- The startup overhead is around 16ms.
2729
- Rendering a template consisting of 100 variables takes around 75ms.
2830
- Rendering a template consisting of 1000 variables takes around 900ms, with most of the time spent on reduction.
29-
- Refer to [#Benchmarking](#benchmarking) for more details.
31+
- Refer to [#Benchmarks](#benchmarks) for more details.
3032

3133
# Installation & Usages
3234

@@ -38,7 +40,7 @@ All you need is `bpt.sh`. Place it anywhere you'd like, then `chmod +x bpt.sh`.
3840
var1=a var2=b ./bpt.sh ge template.tpl # Render the template
3941
var1=a var2=b ./bpt.sh f template.tpl # Fingerprint the template
4042
./bpt.sh g template.tpl > out.sh # Generate reusable script
41-
./bpt.sh g -l '<<' -r '>>' ge template.tpl # Customize delimiters
43+
./bpt.sh g -l '<<' -r '>>' template.tpl # Customize delimiters
4244
./bpt.sh cv template.tpl # Collect variables
4345
./bpt.sh ci template.tpl # Collect includes
4446
./bpt.sh -h # Print help
@@ -54,15 +56,16 @@ mapfile -t vars <<<"$(bpt.main cv template.tpl)"
5456
mapfile -t includes <<<"$(bpt.main ci template.tpl)"
5557
script="$(bpt.main g template.tpl)"
5658
render_result="$(bpt.main ge template.tpl)"
59+
fingerprint="$(bpt.main f template.tpl)"
5760
```
5861

5962
# Writing Templates
6063

61-
BPT employs a straightforward grammar whereby any content beyond the top-level delimiters is treated as strings, whereas anything enclosed by delimiters is considered either a string or a keyword. Notably, any string located within these delimiters must be enclosed in single or double quotes.
64+
BPT employs a straightforward grammar whereby any content beyond the top-level delimiters is treated as strings, whereas anything enclosed by delimiters is considered either a string, an identifier, or a keyword. Notably, any string located within these delimiters must be enclosed in single or double quotes.
6265

6366
## Examples
6467

65-
### Basic Variable Replacement
68+
### Variable Replacement
6669

6770
```
6871
{{var}}
@@ -72,9 +75,17 @@ BPT employs a straightforward grammar whereby any content beyond the top-level d
7275
{{var and {{var2}}}} # Use {{var2}} if var is not empty
7376
```
7477

78+
Bash internal variables can also be used:
79+
80+
```
81+
{{RANDOM}}, {{BASH_VERSION}}, {{HOSTNAME}}, {{PWD}}, ...
82+
```
83+
84+
**Note:** It's obvious that using non-deterministic variables such as `{{RANDOM}}` or `{{SRANDOM}}` results in impure fingerprinting/rendering processes. `$RANDOM` can produce varying results in two subsequent evaluations. It is thus recommended to use `rand=$RANDOM ./bpt ge ...` and use `{{rand}}` in templates to let the `fingerprint` command correctly capture the non-determinism of `$RANDOM`.
85+
7586
### Include Another Template
7687

77-
All include paths are relative to where the script is called from (`$PWD`).
88+
All include paths are relative to where the script is called from (`$PWD`). Includes are processed recursively.
7889

7990
```
8091
{{include: "another.tpl"}}
@@ -103,13 +114,20 @@ All include paths are relative to where the script is called from (`$PWD`).
103114
{{if "abc" == "def": "yes"}}
104115
{{if "abc" != "def": "no"}}
105116
{{if ! "abc" == "def": "yes"}}
117+
{{if "abc" =~ "a.*": "yes"}}
106118
107119
# Combined comparison
108120
{{if "a" or "": "yes"}}
109121
{{if ("1" -lt "2" or "") and (("a" < "b") or "abc"): "yes"}}
122+
123+
# Shorthands
124+
{{""}} # 'false'
125+
{{"a"}} # 'true'
126+
{{"a": "yes"}} # 'yes'
127+
{{{{var}}: "yes": "no"}} # 'yes' if {{var}} is not empty, otherwise 'no'
110128
```
111129

112-
### Looping
130+
### Iterate a List
113131

114132
```
115133
{{for var in "a" "b" "c": {{var}}","}}
@@ -118,13 +136,23 @@ All include paths are relative to where the script is called from (`$PWD`).
118136

119137
### Builtin Functions
120138

121-
Currently we only support `seq`, `len` and `quote`. The `seq` builtin is a wrapper of the `seq` executable in coreutils. The other two are implemented in pure bash.
139+
Currently we only support `seq`, `len`, `quote`, `cat` and `split`. The `seq` builtin is a wrapper of the `seq` executable in coreutils. The others are implemented in pure bash.
140+
141+
```
142+
{{len: "abc"}} # 3
143+
{{seq: "3"}} # 1 2 3
144+
{{seq: "1" "3" "10"}} # 1 4 7 10
145+
```
122146

123147
```
124-
{{len: "abc"}} # 3
125-
{{seq: "3"}} # 1 2 3
126-
{{seq: "1" "3" "10"}}' # 1 4 7 10
127-
{{quote: {{seq: "3"}}}} # 123
148+
{{quote: "2 3" "3" }} # 2 33
149+
{{for i in {{quote: "2 3" "3" }}: {{i}}","}} # 2 33,
150+
151+
{{cat: "2 3" "3" }} # 2 33
152+
{{for i in {{cat: "2 3" "3" }}: {{i}}","}} # 2,33,
153+
154+
{{split: "2 3" "3" }} # 2 3 3
155+
{{for i in {{split: "2 3" "3" }}: {{i}}","}} # 2,3,3,
128156
```
129157

130158
### Mixture of the above
@@ -148,7 +176,8 @@ obj1=Pen obj2=Apple obj3=Pineapple ./bpt.sh ge <<-EOF
148176
}}
149177
150178
EOF
151-
179+
```
180+
```
152181
I have a Pen, I have a Apple,
153182
Uh! Apple-Pen,
154183
I have a Pen, I have Pineapple,
@@ -166,8 +195,11 @@ For reference, the complete BNF of bpt is as follows:
166195
```
167196
DOC -> DOC STMT
168197
| .
169-
STMT -> IF | FORIN | INCLUDE | BUILTIN | VAR | STR .
170-
IF -> ld if BOOLS cl DOC ELIF ELSE rd .
198+
STMT -> IF | FORIN | INCLUDE | BUILTIN | QUOTE | VAR | STR .
199+
IF -> ld if BOOLS cl DOC ELIF ELSE rd
200+
| ld BOOLS cl DOC cl DOC rd
201+
| ld BOOLS cl DOC rd
202+
| ld BOOLS rd .
171203
ELIF -> ELIF elif BOOLS cl DOC
172204
| .
173205
ELSE -> else cl DOC
@@ -188,20 +220,22 @@ BOOL -> ARGS BOP ARGS
188220
FORIN -> ld for ID in ARGS cl DOC rd .
189221
INCLUDE -> ld include cl STR rd .
190222
BUILTIN -> ld ID cl ARGS rd .
223+
QUOTE -> ld quote cl ARGS rd .
191224
ARGS -> ARGS STMT
192225
| STMT .
193226
VAR -> ld ID rd
194227
| ld ID or VAR rd
195228
| ld ID or STR rd
196229
| ld ID and VAR rd
197230
| ld ID and STR rd .
198-
BOP -> ne | eq | gt | lt | ge | le | strgt | strlt | streq | strne .
231+
BOP -> ne | eq | gt | lt | ge | le
232+
| strne | streq | strgt | strlt | strcm .
199233
UOP -> ex .
200234
ID -> id .
201235
STR -> str .
202236
```
203237

204-
And token mappings:
238+
Token mappings:
205239

206240
```
207241
ld -> left delimiter, default {{
@@ -213,15 +247,13 @@ id -> [[:alpha:]_][[:alnum:]_]*
213247
str -> Everything outside the toplevel ld...rd or inside "..." or '...' in ld...rd
214248
```
215249

216-
# Benchmarking
250+
# Benchmarks
217251

218252
Comparisons were made among some pure-bash template engines that work out of the box in two different settings:
219253

220254
- Rendering a simple plain text "aaa": This measures the startup overhead of the engine.
221255
- Rendering 1000 variables `${var}`/`{{var}}`/`<%var%>` whose content is set to `a`: This measures the variable render performance.
222256

223-
We have set the plain bash processing/variable replacement as the baseline. Each test is run 12 times, with the first 2 times as warmups. You can find more details in the benchmark.sh file. If you want to run the benchmarks yourself, you'll need to install [hyperfine][hyperfine].
224-
225257
The plain bash processing/variable replacement was set as the baseline. Each test was run 12 times, with the first 2 times as warmups. More details can be found in the [benchmark.sh](benchmark/benchmark.sh) file. If you want to run the benchmarks yourself, [hyperfine][hyperfine] needs to be installed.
226258

227259
The following results were obtained with Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz:
@@ -248,7 +280,7 @@ The following results were obtained with Intel(R) Core(TM) i9-9900K CPU @ 3.60GH
248280
| `var=a ./mo 1000vars.tpl` | 3200.0 ± 92.6 | 3106.5 | 3405.4 | 1885.37 ± 1740.78 |
249281
| `var=a ./bpt.sh ge 1000vars.tpl` | 895.1 ± 14.8 | 886.6 | 935.6 | 527.36 ± 486.76 |
250282

251-
Engines that do complete parsing ([mo][mo], [bash-tpl][bash-tpl] and [bpt][bpt]) are slower by orders of magitude than engines that only do pattern matching and replacement (plain bash, [shtpl][shtpl] and [renderest][renderest]). A simple profiling of bpt shows that most of its time are spent on parsing (compared to lexing and evaluating the output).
283+
Engines that do complete parsing ([mo][mo], [bash-tpl][bash-tpl] and [bpt][bpt]) are slower by orders of magitude than engines that only do pattern matching and replacement (plain bash, [shtpl][shtpl] and [renderest][renderest]). A simple profiling of bpt shows that most of its time are spent on the parsing part (time spent on lexing and evaluating the output is relatively negligible).
252284

253285
# Contributing
254286

@@ -267,6 +299,7 @@ Adding functionalities shall not conflict with or break existing features.
267299
MIT. See the [LICENSE](LICENSE) file.
268300

269301
<!-- References --->
302+
[pure_function]:https://en.wikipedia.org/wiki/Pure_function
270303
[bash_unit]:https://github.com/pgrange/bash_unit
271304
[grammophone]:https://github.com/mdaines/grammophone
272305
[hyperfine]:https://github.com/sharkdp/hyperfine

0 commit comments

Comments
 (0)