Browse Source

Initial commit

master v0.1.0
Jason T. Lenz 3 years ago
commit
3902da7abc
  1. 2
      .gitignore
  2. 24
      LICENSE
  3. 156
      Makefile
  4. 286
      README.md
  5. 132
      doc/pgot.1
  6. 114
      doc/pgot.1.scd
  7. 698
      doc/pgot.5
  8. 625
      doc/pgot.5.scd
  9. 8
      go.mod
  10. 4
      go.sum
  11. 40
      lib/frontMatter.go
  12. 23
      lib/gotFuncs.go
  13. 265
      lib/pgot.go
  14. 119
      main.go
  15. 54
      template/Makefile.got
  16. 278
      template/README.md.got
  17. 1
      tests/invalid-frontmatter-malformed/tCmd
  18. 1
      tests/invalid-frontmatter-malformed/tExit
  19. 1
      tests/invalid-frontmatter-malformed/tStderr
  20. 6
      tests/invalid-frontmatter-malformed/tStdin
  21. 0
      tests/invalid-frontmatter-malformed/tStdout
  22. 1
      tests/invalid-frontmatter-missing/tCmd
  23. 1
      tests/invalid-frontmatter-missing/tExit
  24. 1
      tests/invalid-frontmatter-missing/tStderr
  25. 2
      tests/invalid-frontmatter-missing/tStdin
  26. 0
      tests/invalid-frontmatter-missing/tStdout
  27. 1
      tests/invalid-include-infinite-loop/tCmd
  28. 1
      tests/invalid-include-infinite-loop/tExit
  29. 5
      tests/invalid-include-infinite-loop/tInput1.got
  30. 1
      tests/invalid-include-infinite-loop/tStderrRegex
  31. 10
      tests/invalid-include-infinite-loop/tStdin
  32. 0
      tests/invalid-include-infinite-loop/tStdout
  33. 1
      tests/invalid-include-no-array/tCmd
  34. 1
      tests/invalid-include-no-array/tExit
  35. 1
      tests/invalid-include-no-array/tStderr
  36. 10
      tests/invalid-include-no-array/tStdin
  37. 0
      tests/invalid-include-no-array/tStdout
  38. 7
      tests/invalid-include-relative-path-missing/subdir/subdir2/tInput2.got
  39. 4
      tests/invalid-include-relative-path-missing/subdir/subdir3/tInput3.got
  40. 7
      tests/invalid-include-relative-path-missing/subdir/tInput1.got
  41. 1
      tests/invalid-include-relative-path-missing/tCmd
  42. 1
      tests/invalid-include-relative-path-missing/tExit
  43. 9
      tests/invalid-include-relative-path-missing/tInput.got
  44. 1
      tests/invalid-include-relative-path-missing/tStderr
  45. 0
      tests/invalid-include-relative-path-missing/tStdout
  46. 14
      tests/pgot_test.go
  47. 1
      tests/valid-builtin-lnp-label/tCmd
  48. 1
      tests/valid-builtin-lnp-label/tExit
  49. 0
      tests/valid-builtin-lnp-label/tStderr
  50. 3
      tests/valid-builtin-lnp-label/tStdin
  51. 1
      tests/valid-builtin-lnp-label/tStdout
  52. 1
      tests/valid-builtin-lnp-nil/tCmd
  53. 1
      tests/valid-builtin-lnp-nil/tExit
  54. 0
      tests/valid-builtin-lnp-nil/tStderr
  55. 3
      tests/valid-builtin-lnp-nil/tStdin
  56. 1
      tests/valid-builtin-lnp-nil/tStdout
  57. 1
      tests/valid-example-advanced/tCmd
  58. 1
      tests/valid-example-advanced/tExit
  59. 23
      tests/valid-example-advanced/tInput.got
  60. 23
      tests/valid-example-advanced/tOutput.check
  61. 0
      tests/valid-example-advanced/tStderr
  62. 0
      tests/valid-example-advanced/tStdout
  63. 1
      tests/valid-example-relevant/tCmd
  64. 1
      tests/valid-example-relevant/tExit
  65. 4
      tests/valid-example-relevant/tInput.got
  66. 1
      tests/valid-example-relevant/tOutput.check
  67. 0
      tests/valid-example-relevant/tStderr
  68. 0
      tests/valid-example-relevant/tStdout
  69. 1
      tests/valid-example-useless/tCmd
  70. 1
      tests/valid-example-useless/tExit
  71. 2
      tests/valid-example-useless/tInput.got
  72. 0
      tests/valid-example-useless/tOutput.check
  73. 0
      tests/valid-example-useless/tStderr
  74. 0
      tests/valid-example-useless/tStdout
  75. 1
      tests/valid-frontmatter-empty-whitespace/tCmd
  76. 1
      tests/valid-frontmatter-empty-whitespace/tExit
  77. 0
      tests/valid-frontmatter-empty-whitespace/tStderr
  78. 5
      tests/valid-frontmatter-empty-whitespace/tStdin
  79. 2
      tests/valid-frontmatter-empty-whitespace/tStdout
  80. 1
      tests/valid-frontmatter-empty/tCmd
  81. 1
      tests/valid-frontmatter-empty/tExit
  82. 0
      tests/valid-frontmatter-empty/tStderr
  83. 4
      tests/valid-frontmatter-empty/tStdin
  84. 2
      tests/valid-frontmatter-empty/tStdout
  85. 1
      tests/valid-include-multiple-levels/tCmd
  86. 1
      tests/valid-include-multiple-levels/tExit
  87. 6
      tests/valid-include-multiple-levels/tInput1.got
  88. 4
      tests/valid-include-multiple-levels/tInput2.got
  89. 0
      tests/valid-include-multiple-levels/tStderr
  90. 10
      tests/valid-include-multiple-levels/tStdin
  91. 6
      tests/valid-include-multiple-levels/tStdout
  92. 1
      tests/valid-include-using-namespace/tCmd
  93. 1
      tests/valid-include-using-namespace/tExit
  94. 4
      tests/valid-include-using-namespace/tInput1.got
  95. 0
      tests/valid-include-using-namespace/tStderr
  96. 10
      tests/valid-include-using-namespace/tStdin
  97. 5
      tests/valid-include-using-namespace/tStdout
  98. 4
      tests/valid-include-using-path-from-command-line/subdir1/tInput1.got
  99. 4
      tests/valid-include-using-path-from-command-line/subdir2/subdir3/tInput3.got
  100. 4
      tests/valid-include-using-path-from-command-line/subdir2/tInput2.got

2
.gitignore

@ -0,0 +1,2 @@
pgot
*.result

24
LICENSE

@ -0,0 +1,24 @@
Copyright (c) 2020 Jason T. Lenz. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

156
Makefile

@ -0,0 +1,156 @@
.POSIX:
PNAME = pgot
RTEMPLATE ?= ../repo-template
all: goUtil doc
doc: docMain docMan
clean: cleanGoUtil cleanCheck
cleanDoc: cleanDocMain cleanDocMan
install: installGoUtil installMan
uninstall: uninstallGoUtil uninstallMan
.DEFAULT_GOAL := all
.PHONY: all doc clean cleanDoc install uninstall
#---Helper Macros to Remove Files---
RMDIR_IF_EMPTY := sh -c '\
if test -d $$0 && ! ls -1qA $$0 | grep -q . ; then \
rmdir $$0; \
fi'
RM ?= rm -f
#---Generate Golang Utility---
PREFIX ?= /usr/local
_INSTDIR = $(DESTDIR)$(PREFIX)
BINDIR ?= $(_INSTDIR)/bin
GO ?= go
GOFLAGS ?=
VERSION != git describe --first-parent 2> /dev/null
GOSRC != find . -name '*.go'
goUtil: $(PNAME)
$(PNAME): $(GOSRC)
$(GO) build $(GOFLAGS) \
-ldflags "-X main.Version=$(VERSION)" \
-o $@
cleanGoUtil:
$(RM) $(PNAME)
installGoUtil: $(PNAME)
strip $(PNAME)
mkdir -m755 -p $(BINDIR)
install -m755 $(PNAME) $(BINDIR)/$(PNAME)
uninstallGoUtil:
$(RM) $(BINDIR)/$(PNAME)
$(RMDIR_IF_EMPTY) $(BINDIR)
.PHONY: goUtil cleanGoUtil installGoUtil uninstallGoUtil
#---Generate Man Page(s)---
.SUFFIXES:
.SUFFIXES: .1 .5 .1.scd .5.scd
PREFIX ?= /usr/local
_INSTDIR = $(DESTDIR)$(PREFIX)
MANDIR ?= $(_INSTDIR)/share/man
DOCMAN := doc/$(PNAME).1 doc/$(PNAME).5
.1.scd.1:
scdoc < $< > $@
.5.scd.5:
scdoc < $< > $@
docMan: $(DOCMAN)
cleanDocMan:
$(RM) $(DOCMAN)
installMan: $(DOCMAN)
mkdir -m755 -p $(MANDIR)/man1 $(MANDIR)/man5
install -m644 doc/$(PNAME).1 $(MANDIR)/man1/$(PNAME).1
install -m644 doc/$(PNAME).5 $(MANDIR)/man5/$(PNAME).5
uninstallMan:
$(RM) $(MANDIR)/man1/$(PNAME).1
$(RM) $(MANDIR)/man5/$(PNAME).5
$(RMDIR_IF_EMPTY) $(MANDIR)/man1
$(RMDIR_IF_EMPTY) $(MANDIR)/man5
$(RMDIR_IF_EMPTY) $(MANDIR)
.PHONY: installMan uninstallMan
#---Test/Check Section---
TESTDIR = tests
check: $(PNAME)
cd $(TESTDIR) && go test -v
cleanCheck:
find $(TESTDIR) -name '*.result' -delete
.PHONY: check cleanCheck
#---Generate Main Documents---
DOCMAIN := README.md LICENSE
README.md: template/README.md.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
LICENSE: $(RTEMPLATE)/LICENSE.src/BSD-2-clause.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
docMain: $(DOCMAIN)
cleanDocMain:
$(RM) $(DOCMAIN)
.PHONY: docMain, cleanDocMain
#---Generate Makefile---
Makefile: template/Makefile.got
pgot -i ":$(RTEMPLATE)" -o $@ $<
mkFile: Makefile
regenMkFile:
pgot -i ":$(RTEMPLATE)" -o Makefile template/Makefile.got
.PHONY: mkFile regenMkFile
#---Lint Helper Target---
lint:
@find . -path ./.git -prune -or \
-type f -and -not -name 'Makefile' \
-exec grep -Hn '<no value>' '{}' ';'
#---TODO Helper Target---
todo:
@find . -path ./.git -prune -or \
-type f -and -not -name 'Makefile' \
-exec grep -Hn TODO '{}' ';'
# vim:set noet tw=80:

286
README.md

@ -0,0 +1,286 @@
# pgot
**pgot** (process golang template) is a lightweight command line [template
processing](https://en.wikipedia.org/wiki/Template_processor) utility. It
directly leverages the [golang template
package](https://golang.org/pkg/text/template) from the standard library. pgot
processes files that contain a combination of frontmatter followed by a
template. The frontmatter consists of
[JSON](https://en.wikipedia.org/wiki/JSON) formatted data, and the template that
follows is free form text containing
[actions](https://golang.org/pkg/text/template/#hdr-Actions). By convention,
files to be processed by pgot are referred to as "got" (golang template) files
and have a ".got" filename extension although this is not a requirement.
The pgot utility implements a "pgotInclude" keyword that can be defined in
frontmatter to include (aka import) other got files. Each of these included
files can also include other got files if desired. The number of nested
includes is limited only by memory and the number of open files allowed by the
operating system.
Both the frontmatter and the template from an included got file can be leveraged
by the originating got file. The imported template is accessed via a [template
action](https://golang.org/pkg/text/template/#hdr-Actions) using the name of the
file minus its extension. By default, imported frontmatter is merged into the
global namespace (aka "."), however pgotInclude also has the option of importing
into a separate namespace if desired to avoid data naming collisions. Although
pgot is small in size these features along with the comprehensive actions
available in the standard template library enable a large range of applications
from simple to large scale complex text file hierarchies.
The range of uses is quite large, but a few examples include:
* A simple README, LICENSE, or CONTRIBUTING template with standard sections that
an author can leverage across various git repositories (the pgot source code
[repository](https://git.lenzplace.org/lenzj/pgot) is an example).
* A static website using imported pgot templates to standardize website
appearance, headers, footers, etc. (<https://blog.lenzplace.org> is an example).
* A mail merge script to generate tailored messages for each recipient.
* A large book written in markdown or latex with metadata in frontmatter for
authors, chapters, references, contributors, keywords, etc.
## Command line synopsis
```text
Usage: pgot [OPTION]... [FILE]...
Read a got (golang template) file and send transformed text to output.
Options:
-d string
string of json frontmatter to include
-i paths
colon separated list of paths to search with pgotInclude
-o file
output file path (default "-")
```
If no files are specified on the command line, then pgot reads from sdtin.
Similarly if the -o option is not used, the output is sent to stdout (aka "-").
If multiple files are specified on the command line the last file listed is the
primary or root file with previous files being included in a way similar to
how the "pgotInclude" keyword works in frontmatter.
The -d option can be used to insert frontmatter from the command line. This
frontmatter is merged into the global (aka ".") namespace and takes precedence
or overwrites any frontmatter read in from the main got file or included files.
The path(s) specified with the -i option can include absolute or relative paths.
An empty path in the -i option (the default) indicates that the local path
relative to the respective got file should be searched. A few examples below
shed further light on this:
```text
pgot -i "" myfile.got
This is the same as the default for the -i option. For any relative files
specified using pgotInclude, the base dir of the calling got file will be
where the relative path starts.
pgot -i ":/home/veronica/pgot-inc" myfile.got
The folder of the calling got file will be searched first (nothing before
the first ":") and if the file is not found there it will then search the
"/home/veronica/pgot-inc" folder.
pgot -i "/home/larry/inc1::/home/larry/inc2" myfile.got
The inc1 folder will be searched first, followed by the folder the calling
got file is in, followed by the inc2 folder.
```
## "Got" file format
A got file must include a frontmatter JSON section bounded by three semicolons
each on their own line before and after the JSON data. Anything after this
frontmatter section is considered to be the "template" which is processed by the
golang template package. The full documentation of the template actions provided
by the golang library is available [here](https://golang.org/pkg/text/template).
Note that it is acceptable to have an empty frontmatter section or template
section or both.
### Got file examples
The following examples also exist in the "tests" sub folder within this
repository. A minimal (although admittedly useless) got file consists of the
following:
```text
;;;
;;;
```
A slightly more relevant got file:
```text
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{.name}}
```
And a more advanced example leveraged from
[here](https://golang.org/pkg/text/template/#example_Template):
```text
;;;
{
"guests" : [
["Aunt Mildred", "bone china tea set", true],
["Uncle John", "moleskin pants", false],
["Cousin Rodney", "", false]
]
}
;;;
{{range .guests -}}
Dear {{index . 0}},
{{if index . 2}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with index . 1 -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
---
{{end -}}
```
### Frontmatter key word
Any valid JSON structure can be used in the frontmatter section. There is
however one keyword that is reserved when used in frontmatter.
**pgotInclude**
```text
"pgotInclude" : [ "file1.got", "file2.got", "file3.got" ]
"pgotInclude" : [ { "f":"file1.got", "n":"name1" },
{ "f":"file2.got", "n":"name2" },
{ "f":"file3.got", "n":"name3" } ]
```
The pgotInclude keyword is used to include (aka import) other pgot files and can
be used in one of two variations as shown above. Note that three files are
showdn in the above variations, however any number of files can be included
(from one to the limits of memory and the filesystem).
The first variation above imports each got file in the order defined in the
array. The frontmatter is merged directly into the top level "." namespace. If
two imported got files contain the same data element name, the last file
included takes precedence. In other words, file3 would take precedence over
file2, and file1. Lastly, any frontmatter defined in the originating got file
(the file called directly by pgot) is included last and takes precedence
(overwrites) any included data structures that have the same element name.
The second variation can be used to import frontmatter into the specified
namespace which can be useful to avoid data collisions or overwriting. For
example if file1 defines an element "color" : "Purple", and file2 defines
"color" : "Green", they can both be accessed separately via
{{.name1.color}}, and {{.name2.color}}.
It's important to note that the included got filenames may be specified with
relative or absolute paths. For relative paths the the included got file is
relative to the location of the originating got file.
Note that future keywords may be created which start with "pgot", so it is
advisable to avoid naming any elements in frontmatter starting with "pgot".
## Custom pgot actions
In addition to the standard [template
actions](https://golang.org/pkg/text/template/#hdr-Actions), there is currently
one custom template action defined by pgot (below). This is included more as a
simple example rather than a critical feature. For those interested in
implementing custom template actions the
[gotFuncs.go](/lenzj/pgot/src/branch/master/lib/gotFuncs.go) source
code demonstrates how custom template actions can be added.
```text
{{lnp "label" "url"}}
```
lnp (link new page) generates the specified url as an html link which opens a
new page when the user selects the link. If label is nil (aka "") then the url
is displayed as the clickable link, otherwise the label itself is displayed.
## Compiling from source
### Dependencies
* Go compiler (v1.12 or later).
* Go package [chunkio](https://git.lenzplace.org/lenzj/chunkio)
* Go package [testcli](https://git.lenzplace.org/lenzj/testcli) to run tests.
* [Scdoc](https://git.sr.ht/~sircmpwn/scdoc/) utility to generate the man page.
Only needed if changes to man page sources are made.
* [pgot](https://git.lenzplace.org/lenzj/pgot) (this utility) to process files in the templates sub
folder. Only needed if changes to README.md, LICENCE, Makefile etc. are
needed.
### Installing
```text
$ make
# make install
```
## Running the tests
```text
$ make check
```
## Contributing
If you have a bugfix, update, issue or feature enhancement the best way to reach
me is by following the instructions in the link below. Thank you!
<https://blog.lenzplace.org/about/contact.html>
## Versioning
I follow the [SemVer](http://semver.org/) strategy for versioning. The latest
version is listed in the [releases](/lenzj/pgot/releases) section.
## License
This project is licensed under a BSD two clause license - see the
[LICENSE](LICENSE) file for details.
## FAQ
**Q**: What causes the following error message: "pgot : invalid character '}'
looking for beginning of object key string"?
**A**: This is an error message from the golang JSON library. The frontmatter
is likely not valid structured [JSON](https://json.org/). Make sure you don't
have something like the following. The last comma in the "copyright" row should
be deleted.
```text
;;;
{
"author" : "Jose",
"date" : "March 6th, 2019",
"copyright" : "2019",
}
;;;
```
**Q**: I keep getting '\<no value\>' in parts of my output?
**A**: This is what the golang template engine outputs when a
[pipeline](https://golang.org/pkg/text/template/#hdr-Actions) is undefined. It
can sometimes be handy when debugging golang templates to include
{{.}} somewhere in the template. This displays __all__ the elements
in the global pipeline defined at time of template execution.
## Other similar projects
* <https://github.com/hairyhenderson/gomplate>
* <https://github.com/gliderlabs/sigil>
<!-- vim:set ts=4 sw=4 et tw=80: -->

132
doc/pgot.1

@ -0,0 +1,132 @@
.\" Generated by scdoc 1.9.7
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "pgot" "1" "2020-04-26"
.P
.SH NAME
.P
pgot- a command line template processing utility
.P
.SH SYNOPSIS
.P
\fIpgot\fR [\fIoption\fR] [\fIfile\fR] \fI...\fR
.P
.SH OPTIONS
.P
\fB-d\fR \fIstring\fR
.RS 4
A string of json frontmatter to include
.P
.RE
\fB-i\fR \fIpaths\fR
.RS 4
A colon separated list of paths to search with gotInclude
.P
.RE
\fB-o\fR \fIfile\fR
.RS 4
Output file path (default "-")
.P
.RE
.SH DESCRIPTION
.P
\fBpgot\fR (process golang template) is a command line template processing utility.
It directly leverages the powerful golang template package. \fBpgot\fR processes
files containing frontmatter followed by a template. The frontmatter consists
of JSON formatted data, and the template that follows is free form text
containing template actions. By convention, files to be processed by pgot are
referred to as "got" (golang template) files and have a ".got" filename
extension although this is not a requirement.
.P
The pgot utility implements a "pgotInclude" keyword that can be defined in
frontmatter to include (aka import) other got files. Each of these included
files can also include other got files if desired. The number of nested
includes is limited only by memory and the number of open files allowed by the
operating system (a pretty large number).
.P
Both the frontmatter and the template from the included got file can be
leveraged by the originating got file. This enables pgot to be used both in
simple text processing applications as well as complex large scale text
processing. As one example, the author of pgot used it to create a static blog
website with uniform html page headers, footers, and other features while
avoiding repetitive data entry (aka following the DRY principle).
.P
A got file must include a frontmatter JSON section bounded by three semicolons
each on their own line before and after the JSON data. It is acceptable to
have an empty frontmatter section or template section or both.
.P
.SH EXAMPLE
.P
A minimal (although admittedly useless) got file consists of the following:
.P
.nf
.RS 4
;;;
;;;
.fi
.RE
.P
A slightly more relevant got file:
.P
.nf
.RS 4
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{\&.name}}
.fi
.RE
.P
And a more advanced example leveraged from the golang template website:
.P
.nf
.RS 4
;;;
{
"guests" : [
["Aunt Mildred", "bone china tea set", true],
["Uncle John", "moleskin pants", false],
["Cousin Rodney", "", false]
]
}
;;;
{{range \&.guests -}}
Dear {{index \&. 0}},
{{if index \&. 2}}
It was a pleasure to see you at the wedding\&.
{{- else}}
It is a shame you couldn't make it to the wedding\&.
{{- end}}
{{with index \&. 1 -}}
Thank you for the lovely {{\&.}}\&.
{{end}}
Best wishes,
Josie
---
{{end -}}
.fi
.RE
.P
.SH BUGS
.P
Bug reporting instructions:
.br
<https://blog.lenzplace.org/about/contact.html>
.P
.SH SEE ALSO
.P
\fBpgot\fR(5)
.P
Website for pgot:
.br
<https://git.lenzplace.org/lenzj/pgot>
.P
Other useful references:
.br
<https://golang.org/pkg/text/template>
<https://en.wikipedia.org/wiki/Template_processor>
<https://en.wikipedia.org/wiki/JSON>
.P

114
doc/pgot.1.scd

@ -0,0 +1,114 @@
pgot(1)
; This is a scdoc source file for generating a man page
; See https://git.sr.ht/~sircmpwn/scdoc
# NAME
pgot- a command line template processing utility
# SYNOPSIS
_pgot_ [_option_] [_file_] _..._
# OPTIONS
*-d* _string_
A string of json frontmatter to include
*-i* _paths_
A colon separated list of paths to search with gotInclude
*-o* _file_
Output file path (default "-")
# DESCRIPTION
*pgot* (process golang template) is a command line template processing utility.
It directly leverages the powerful golang template package. *pgot* processes
files containing frontmatter followed by a template. The frontmatter consists
of JSON formatted data, and the template that follows is free form text
containing template actions. By convention, files to be processed by pgot are
referred to as "got" (golang template) files and have a ".got" filename
extension although this is not a requirement.
The pgot utility implements a "pgotInclude" keyword that can be defined in
frontmatter to include (aka import) other got files. Each of these included
files can also include other got files if desired. The number of nested
includes is limited only by memory and the number of open files allowed by the
operating system (a pretty large number).
Both the frontmatter and the template from the included got file can be
leveraged by the originating got file. This enables pgot to be used both in
simple text processing applications as well as complex large scale text
processing. As one example, the author of pgot used it to create a static blog
website with uniform html page headers, footers, and other features while
avoiding repetitive data entry (aka following the DRY principle).
A got file must include a frontmatter JSON section bounded by three semicolons
each on their own line before and after the JSON data. It is acceptable to
have an empty frontmatter section or template section or both.
# EXAMPLE
A minimal (although admittedly useless) got file consists of the following:
```
;;;
;;;
```
A slightly more relevant got file:
```
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{.name}}
```
And a more advanced example leveraged from the golang template website:
```
;;;
{
"guests" : [
["Aunt Mildred", "bone china tea set", true],
["Uncle John", "moleskin pants", false],
["Cousin Rodney", "", false]
]
}
;;;
{{range .guests -}}
Dear {{index . 0}},
{{if index . 2}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with index . 1 -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
---
{{end -}}
```
# BUGS
Bug reporting instructions:++
<https://blog.lenzplace.org/about/contact.html>
# SEE ALSO
*pgot*(5)
Website for pgot:++
<https://git.lenzplace.org/lenzj/pgot>
Other useful references:++
<https://golang.org/pkg/text/template>
<https://en.wikipedia.org/wiki/Template_processor>
<https://en.wikipedia.org/wiki/JSON>
; vim:set noet tw=80:

698
doc/pgot.5

@ -0,0 +1,698 @@
.\" Generated by scdoc 1.9.7
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "pgot" "5" "2020-04-26"
.P
.SH NAME
.P
pgot - structure of got (golang template) files used by pgot.
.P
.SH DESCRIPTION
.P
By convention, files to be processed by pgot are referred to as "got" (golang
template) files and have a ".got" filename extension although this is not a
requirement.
.P
All got files contain frontmatter followed by a template. The frontmatter
contains formatted data, and the template that follows is free form text
containing actions defined by the golang template package.
.P
The pgot utility implements a "pgotInclude" keyword that can be defined in
frontmatter to include (aka import) other got files. Each of these included
files can also include other got files if desired. The number of nested
includes is limited only by memory and the number of open files allowed by the
operating system (a pretty large number).
.P
Both the frontmatter and the template from the included got file can be
leveraged by the originating got file. This enables pgot to be used both in
simple text processing applications as well as complex large scale text
processing.
.P
.SH FRONTMATTER SYNTAX
.P
Frontmatter in got files is defined by a JSON data structure. The JSON data
structure is bounded before and after by three semicolons and a newline and must
exist at the beginning of the got file. An example is shown below:
.P
.nf
.RS 4
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{\&.name}}\&.
.fi
.RE
.P
In the example above, the string "Hello world, my name is {{.name}}" is the
template portion of the file and the lines prior make up the JSON frontmatter.
.P
Note that if and when other data structure languages are included in the golang
standard library in the future they may be added to pgot frontmatter syntax as
optional methods with their own bounded signature. (I.E. YAML bounded with
"---" or TOML with "+
+")
.P
.SS Frontmatter keywords
.P
\fBpgotInclude\fR
.P
.nf
.RS 4
"pgotInclude" : [ "file1\&.got", "file2\&.got", "file3\&.got" ]
"pgotInclude" : [ { "f":"file1\&.got", "n":"name1" },
{ "f":"file2\&.got", "n":"name2" },
{ "f":"file3\&.got", "n":"name3" } ]
.fi
.RE
.P
The pgotInclude keyword is used to include (aka import) other pgot files and can
be used in one of two variations as shown above. Note that three files are
showdn in the above variations, however any number of files can be included
(from one to the limits of memory and the filesystem).
.P
The first variation above imports each got file in the order defined in the
array. The frontmatter is merged directly into the top level "." namespace. If
two imported got files contain the same data element name, the last file
included takes precedence. In other words, file3 would take precedence over
file2, and file1. Lastly, any frontmatter defined in the originating got file
(the file called directly by pgot) is included last and takes precedence
(overwrites) any included data structures that have the same element name.
.P
The second variation can be used to import frontmatter into the specified
namespace which can be useful to avoid data collisions or overwriting. For
example if file1 defines an element "color" : "Purple", and file2 defines
"color" : "Green", they can both be accessed separately via {{.name1.color}},
and {{.name2.color}}.
.P
It's important to note that the included got filenames may be specified with
relative or absolute paths. For relative paths the the included got file is
relative to the location of the originating got file.
.P
Note that future keywords may be created which start with "pgot", so it is
advisable to avoid naming any elements in frontmatter starting with "pgot".
.P
.SH TEMPLATE SYNTAX
.P
Templates are executed by applying them to the data structure from the
associated frontmatter. Annotations in the template refer to elements of the
data structure (typically a field of a struct or a key in a map) to control
execution and derive values to be displayed. Execution of the template walks
the structure and sets the cursor, represented by a period '.' and called "dot",
to the value at the current location in the structure as execution proceeds.
.P
The input text for a template is UTF-8-encoded text in any format.
"Actions"--data evaluations or control structures--are delimited by "{{" and
"}}"; all text outside actions is copied to the output unchanged. Except for raw
strings, actions may not span newlines, although comments can.
.P
.SS Imported templates
.P
When a got file is included using the \fBpgotInclude\fR keyword in frontmatter, the
associated template is included also and its name is the same as the associated
filename minus its extension. This template can be accessed using one of the
two {{template ...}} methods mentioned in the \fBActions\fR section below. A short
example is as follows:
.P
file1.got
.nf
.RS 4
;;;
{ "date" : "2020-04-19" }
;;;
I am typing this on {{\&.date}}
.fi
.RE
.P
file2.got
.nf
.RS 4
;;;
{
"date" : "Sunday",
"pgotInclude" : [ "file1\&.got" ]
}
;;;
{{template "file1" \&.}}
.fi
.RE
.P
Output from "pgot file2.got"
.nf
.RS 4
I am typing this on Sunday
.fi
.RE
.P
.SS Custom pgot actions
.P
There is currently one custom template action defined by pgot (below). This is
included more as an example rather than a critical feature. For those
interested in implementing custom template actions the source code at
<https://git.lenzplace.org/lenzj/pgot/src/branch/master/lib/gotFuncs.go>
demonstrates this simple example.
.P
{{lnp label url}}
.RS 4
lnp (link new page) generates the specified html url in a format which
opens a new page when the user selects the link. If label is nil (aka
"") then the url is displayed as the clickable link, otherwise the label
itself is displayed.
.P
.RE
.SS Text and spaces
.P
By default, all text between actions is copied verbatim when the template is
executed. However, to aid in formatting template source code, if an action's
left delimiter (by default "{{") is followed immediately by a minus sign and
ASCII space character ("{{- "), all trailing white space is trimmed from the
immediately preceding text. Similarly, if the right delimiter ("}}") is preceded
by a space and minus sign (" -}}"), all leading white space is trimmed from the
immediately following text. In these trim markers, the ASCII space must be
present; "{{-3}}" parses as an action containing the number -3.
.P
For instance, when executing the template whose source is
.P
.RS 4
"{{23 -}} < {{- 45}}"
.P
.RE
the generated output would be
.P
.RS 4
"23<45"
.P
.RE
For this trimming, the definition of white space characters is the same as in
Go: space, horizontal tab, carriage return, and newline.
.P
.SS Actions
.P
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
data, defined in detail in the corresponding sections that follow.
.P
{{/* a comment */}}
.br
{{- /* a comment with white space trimmed from preceding and following text */ -}}
.RS 4
A comment; discarded. May contain newlines. Comments do not nest and
must start and end at the delimiters, as shown here.
.P
.RE
{{pipeline}}
.RS 4
The default textual representation (the same as would be printed by
fmt.Print) of the value of the pipeline is copied to the output.
.P
.RE
{{if pipeline}} T1 {{end}}
.RS 4
If the value of the pipeline is empty, no output is generated;
otherwise, T1 is executed. The empty values are false, 0, any nil
pointer or interface value, and any array, slice, map, or string of
length zero. Dot is unaffected.
.P
.RE
{{if pipeline}} T1 {{else}} T0 {{end}}
.RS 4
If the value of the pipeline is empty, T0 is executed; otherwise, T1 is
executed. Dot is unaffected.
.P
.RE
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
.RS 4
To simplify the appearance of if-else chains, the else action of an if
may include another if directly; the effect is exactly the same as
writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
.P
.RE
{{range pipeline}} T1 {{end}}
.RS 4
The value of the pipeline must be an array, slice, map, or channel. If
the value of the pipeline has length zero, nothing is output; otherwise,
dot is set to the successive elements of the array, slice, or map and T1
is executed. If the value is a map and the keys are of basic type with
a defined order, the elements will be visited in sorted key order.
.P
.RE
{{range pipeline}} T1 {{else}} T0 {{end}}
.RS 4
The value of the pipeline must be an array, slice, map, or channel. If
the value of the pipeline has length zero, dot is unaffected and T0 is
executed; otherwise, dot is set to the successive elements of the array,
slice, or map and T1 is executed.
.P
.RE
{{template "name"}}
.RS 4
The template with the specified name is executed with nil data.
.P
.RE
{{template "name" pipeline}}
.RS 4
The template with the specified name is executed with dot set to the
value of the pipeline.
.P
.RE
{{block "name" pipeline}} T1 {{end}}
.RS 4
A block is shorthand for defining a template {{define "name"}} T1
{{end}} and then executing it in place {{template "name" pipeline}} The
typical use is to define a set of root templates that are then
customized by redefining the block templates within.
.P
.RE
{{with pipeline}} T1 {{end}}
.RS 4
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is executed.
.P
.RE
{{with pipeline}} T1 {{else}} T0 {{end}}
.RS 4
If the value of the pipeline is empty, dot is unaffected and T0 is
executed; otherwise, dot is set to the value of the pipeline and T1 is
executed.
.P
.RE
.SS Arguments
.P
An argument is a simple value, denoted by one of the following.
.P
.nf
.RS 4
- A boolean, string, character, integer, floating-point, imaginary
or complex constant in Go syntax\&. These behave like Go's untyped
constants\&. Note that, as in Go, whether a large integer constant
overflows when assigned or passed to a function can depend on whether
the host machine's ints are 32 or 64 bits\&.
- The keyword nil, representing an untyped Go nil\&.
- The character '\&.' (period):
\&.
The result is the value of dot\&.
- A variable name, which is a (possibly empty) alphanumeric string
preceded by a dollar sign, such as
$piOver2
or
$
The result is the value of the variable\&. Variables are described below\&.
- The name of a field of the data, which must be a struct, preceded
by a period, such as
\&.Field
The result is the value of the field\&. Field invocations may be chained:
\&.Field1\&.Field2
Fields can also be evaluated on variables, including chaining:
$x\&.Field1\&.Field2
- The name of a key of the data, which must be a map, preceded by a period, such
as
\&.Key
The result is the map element value indexed by the key\&. Key invocations may
be chained and combined with fields to any depth:
\&.Field1\&.Key1\&.Field2\&.Key2
Although the key must be an alphanumeric identifier, unlike with field names
they do not need to start with an upper case letter\&. Keys can also be
evaluated on variables, including chaining:
$x\&.key1\&.key2
- The name of a niladic method of the data, preceded by a period, such as
\&.Method
The result is the value of invoking the method with dot as the receiver,
dot\&.Method()\&. Such a method must have one return value (of any type) or two
return values, the second of which is an error\&. If it has two and the
returned error is non-nil, execution terminates and an error is returned to
the caller as the value of Execute\&. Method invocations may be chained and
combined with fields and keys to any depth:
\&.Field1\&.Key1\&.Method1\&.Field2\&.Key2\&.Method2
Methods can also be evaluated on variables, including chaining:
$x\&.Method1\&.Field
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun()\&. The return types and
values behave as in methods\&. Functions and function names are described below\&.
- A parenthesized instance of one the above, for grouping\&. The result may be
accessed by a field or map key invocation\&.
print (\&.F1 arg1) (\&.F2 arg2)
(\&.StructValuedMethod "arg")\&.Field
.fi
.RE
.P
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required. If an evaluation
yields a function value, such as a function-valued field of a struct, the
function is not invoked automatically, but it can be used as a truth value for
an if action and the like. To invoke it, use the call function, defined below.
.P
.SS Pipelines
.P
A pipeline is a possibly chained sequence of "commands". A command is a simple
value (argument) or a function or method call, possibly with multiple arguments:
.P
.nf
.RS 4
Argument
The result is the value of evaluating the argument\&.
\&.Method [Argument\&.\&.\&.]
The method can be alone or the last element of a chain but,
unlike methods in the middle of a chain, it can take arguments\&.
The result is the value of calling the method with the
arguments:
dot\&.Method(Argument1, etc\&.)
functionName [Argument\&.\&.\&.]
The result is the value of calling the function associated
with the name:
function(Argument1, etc\&.)
Functions and function names are described below\&.
.fi
.RE
.P
A pipeline may be "chained" by separating a sequence of commands with pipeline
characters '|'. In a chained pipeline, the result of each command is passed as
the last argument of the following command. The output of the final command in
the pipeline is the value of the pipeline.
.P
The output of a command will be either one value or two values, the second of
which has type error. If that second value is present and evaluates to non-nil,
execution terminates and the error is returned to the caller of Execute.
.P
.SS Variables
.P
A pipeline inside an action may initialize a variable to capture the result. The
initialization has syntax
.P
.nf
.RS 4
$variable := pipeline
.fi
.RE
.P
where $variable is the name of the variable. An action that declares a variable
produces no output.
.P
Variables previously declared can also be assigned, using the syntax
.P
.nf
.RS 4
$variable = pipeline
.fi
.RE
.P
If a "range" action initializes a variable, the variable is set to the
successive elements of the iteration. Also, a "range" may declare two variables,
separated by a comma:
.P
.nf
.RS 4
range $index, $element := pipeline
.fi
.RE
.P
in which case $index and $element are set to the successive values of the
array/slice index or map key and element, respectively. Note that if there is
only one variable, it is assigned the element; this is opposite to the
convention in Go range clauses.
.P
A variable's scope extends to the "end" action of the control structure ("if",
"with", or "range") in which it is declared, or to the end of the template if
there is no such control structure. A template invocation does not inherit
variables from the point of its invocation.
.P
When execution begins, $ is set to the data argument passed to Execute, that is,
to the starting value of dot.
.P
Here are some example one-line templates demonstrating pipelines and variables.
All produce the quoted word "output":
.P
.nf
.RS 4
{{""output""}}
A string constant\&.
{{`"output"`}}
A raw string constant\&.
{{printf "%q" "output"}}
A function call\&.
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
command\&.
{{printf "%q" (print "out" "put")}}
A parenthesized argument\&.
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call\&.
{{"output" | printf "%s" | printf "%q"}}
A longer chain\&.
{{with "output"}}{{printf "%q" \&.}}{{end}}
A with action using dot\&.
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
A with action that creates and uses a variable\&.
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
A with action that uses the variable in another action\&.
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
The same, but pipelined\&.
.fi
.RE
.P
.SS Functions
.P
During execution functions are found in two function maps: first in the
template, then in the global function map. By default, no functions are defined
in the template but the Funcs method can be used to add them.
.P
Predefined global functions are named as follows.
.P
.nf
.RS 4
and
Returns the boolean AND of its arguments by returning the
first empty argument or the last argument, that is,
"and x y" behaves as "if x then y else x"\&. All the
arguments are evaluated\&.
call
Returns the result of calling the first argument, which
must be a function, with the remaining arguments as parameters\&.
Thus "call \&.X\&.Y 1 2" is, in Go notation, dot\&.X\&.Y(1, 2) where
Y is a func-valued field, map entry, or the like\&.
The first argument must be the result of an evaluation
that yields a value of function type (as distinct from
a predefined function such as print)\&. The function must
return either one or two result values, the second of which
is of type error\&. If the arguments don't match the function
or the returned error value is non-nil, execution stops\&.
html
Returns the escaped HTML equivalent of the textual
representation of its arguments\&. This function is unavailable
in html/template, with a few exceptions\&.
index
Returns the result of indexing its first argument by the
following arguments\&. Thus "index x 1 2 3" is, in Go syntax,
x[1][2][3]\&. Each indexed item must be a map, slice, or array\&.
slice
slice returns the result of slicing its first argument by the
remaining arguments\&. Thus "slice x 1 2" is, in Go syntax, x[1:2],
while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
is x[1:2:3]\&. The first argument must be a string, slice, or array\&.
js
Returns the escaped JavaScript equivalent of the textual
representation of its arguments\&.
len
Returns the integer length of its argument\&.
not
Returns the boolean negation of its single argument\&.
or
Returns the boolean OR of its arguments by returning the
first non-empty argument or the last argument, that is,
"or x y" behaves as "if x then x else y"\&. All the
arguments are evaluated\&.
print
An alias for fmt\&.Sprint
printf
An alias for fmt\&.Sprintf
println
An alias for fmt\&.Sprintln
urlquery
Returns the escaped value of the textual representation of
its arguments in a form suitable for embedding in a URL query\&.
This function is unavailable in html/template, with a few
exceptions\&.
.fi
.RE
.P
The boolean functions take any zero value to be false and a non-zero value to be
true.
.P
There is also a set of binary comparison operators defined as functions:
.nf
.RS 4
eq
Returns the boolean truth of arg1 == arg2
ne
Returns the boolean truth of arg1 != arg2
lt
Returns the boolean truth of arg1 < arg2
le
Returns the boolean truth of arg1 <= arg2
gt
Returns the boolean truth of arg1 > arg2
ge
Returns the boolean truth of arg1 >= arg2
.fi
.RE
.P
For simpler multi-way equality tests, eq (only) accepts two or more arguments
and compares the second and subsequent to the first, returning in effect
.P
.nf
.RS 4
arg1==arg2 || arg1==arg3 || arg1==arg4 \&.\&.\&.
.fi
.RE
.P
(Unlike with || in Go, however, eq is a function call and all the arguments will
be evaluated.)
.P
The comparison functions work on any values whose type Go defines as comparable.
For basic types such as integers, the rules are relaxed: size and exact type are
ignored, so any integer value, signed or unsigned, may be compared with any
other integer value. (The arithmetic value is compared, not the bit pattern, so
all negative integers are less than all unsigned integers.) However, as usual,
one may not compare an int with a float32 and so on.
.P
.SS Associated templates
.P
Each template is named by a string specified when it is created. Also, each
template is associated with zero or more other templates that it may invoke by
name; such associations are transitive and form a name space of templates.
.P
A template may use a template invocation to instantiate another associated
template; see the explanation of the "template" action above. The name must be
that of a template associated with the template that contains the invocation.
.P
.SS Nested template definitions
.P
When parsing a template, another template may be defined and associated with the
template being parsed. Template definitions must appear at the top level of the
template, much like global variables in a Go program.
.P
The syntax of such definitions is to surround each template declaration with a
"define" and "end" action.
.P
The define action names the template being created by providing a string
constant. Here is a simple example:
.P
.nf
.RS 4
{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}
.fi
.RE
.P
This defines two templates, T1 and T2, and a third T3 that invokes the other two
when it is executed. Finally it invokes T3. If executed this template will
produce the text
.P
.nf
.RS 4
ONE TWO
.fi
.RE
.P
.SH EXAMPLE
.P
A minimal (although admittedly useless) got file consists of the following:
.P
.nf
.RS 4
;;;
;;;
.fi
.RE
.P
A slightly more relevant got file:
.P
.nf
.RS 4
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{\&.name}}
.fi
.RE
.P
And a more advanced example leveraged from the golang template website:
.P
.nf
.RS 4
;;;
{
"guests" : [
["Aunt Mildred", "bone china tea set", true],
["Uncle John", "moleskin pants", false],
["Cousin Rodney", "", false]
]
}
;;;
{{range \&.guests -}}
Dear {{index \&. 0}},
{{if index \&. 2}}
It was a pleasure to see you at the wedding\&.
{{- else}}
It is a shame you couldn't make it to the wedding\&.
{{- end}}
{{with index \&. 1 -}}
Thank you for the lovely {{\&.}}\&.
{{end}}
Best wishes,
Josie
---
{{end -}}
.fi
.RE
.P
.SH BUGS
.P
Bug reporting instructions:
.br
<https://blog.lenzplace.org/about/contact.html>
.P
.SH SEE ALSO
.P
\fBpgot\fR(1)
.P
Website for pgot:
.br
<https://git.lenzplace.org/lenzj/pgot>
.P
Other useful references:
.br
<https://golang.org/pkg/text/template>
<https://en.wikipedia.org/wiki/Template_processor>
<https://en.wikipedia.org/wiki/JSON>
.P

625
doc/pgot.5.scd

@ -0,0 +1,625 @@
pgot(5)
; This is a scdoc source file for generating a man page
; See https://git.sr.ht/~sircmpwn/scdoc
# NAME
pgot - structure of got (golang template) files used by pgot.
# DESCRIPTION
By convention, files to be processed by pgot are referred to as "got" (golang
template) files and have a ".got" filename extension although this is not a
requirement.
All got files contain frontmatter followed by a template. The frontmatter
contains formatted data, and the template that follows is free form text
containing actions defined by the golang template package.
The pgot utility implements a "pgotInclude" keyword that can be defined in
frontmatter to include (aka import) other got files. Each of these included
files can also include other got files if desired. The number of nested
includes is limited only by memory and the number of open files allowed by the
operating system (a pretty large number).
Both the frontmatter and the template from the included got file can be
leveraged by the originating got file. This enables pgot to be used both in
simple text processing applications as well as complex large scale text
processing.
# FRONTMATTER SYNTAX
Frontmatter in got files is defined by a JSON data structure. The JSON data
structure is bounded before and after by three semicolons and a newline and must
exist at the beginning of the got file. An example is shown below:
```
;;;
{ "name" : "Jason" }
;;;
Hello world, my name is {{.name}}.
```
In the example above, the string "Hello world, my name is {{.name}}" is the
template portion of the file and the lines prior make up the JSON frontmatter.
Note that if and when other data structure languages are included in the golang
standard library in the future they may be added to pgot frontmatter syntax as
optional methods with their own bounded signature. (I.E. YAML bounded with
"---" or TOML with "+++")
## Frontmatter keywords
*pgotInclude*
```
"pgotInclude" : [ "file1.got", "file2.got", "file3.got" ]
"pgotInclude" : [ { "f":"file1.got", "n":"name1" },
{ "f":"file2.got", "n":"name2" },
{ "f":"file3.got", "n":"name3" } ]
```
The pgotInclude keyword is used to include (aka import) other pgot files and can
be used in one of two variations as shown above. Note that three files are
showdn in the above variations, however any number of files can be included
(from one to the limits of memory and the filesystem).
The first variation above imports each got file in the order defined in the
array. The frontmatter is merged directly into the top level "." namespace. If
two imported got files contain the same data element name, the last file
included takes precedence. In other words, file3 would take precedence over
file2, and file1. Lastly, any frontmatter defined in the originating got file
(the file called directly by pgot) is included last and takes precedence
(overwrites) any included data structures that have the same element name.
The second variation can be used to import frontmatter into the specified
namespace which can be useful to avoid data collisions or overwriting. For
example if file1 defines an element "color" : "Purple", and file2 defines
"color" : "Green", they can both be accessed separately via {{.name1.color}},
and {{.name2.color}}.
It's important to note that the included got filenames may be specified with
relative or absolute paths. For relative paths the the included got file is
relative to the location of the originating got file.
Note that future keywords may be created which start with "pgot", so it is
advisable to avoid naming any elements in frontmatter starting with "pgot".
# TEMPLATE SYNTAX
; Note that much of the content in this section of the man page is derived from
; the golang template package documentation.
; See https://golang.org/pkg/text/template
Templates are executed by applying them to the data structure from the
associated frontmatter. Annotations in the template refer to elements of the
data structure (typically a field of a struct or a key in a map) to control
execution and derive values to be displayed. Execution of the template walks
the structure and sets the cursor, represented by a period '.' and called "dot",
to the value at the current location in the structure as execution proceeds.
The input text for a template is UTF-8-encoded text in any format.
"Actions"--data evaluations or control structures--are delimited by "{{" and
"}}"; all text outside actions is copied to the output unchanged. Except for raw
strings, actions may not span newlines, although comments can.
## Imported templates
When a got file is included using the *pgotInclude* keyword in frontmatter, the
associated template is included also and its name is the same as the associated
filename minus its extension. This template can be accessed using one of the
two {{template ...}} methods mentioned in the *Actions* section below. A short
example is as follows:
file1.got
```
;;;
{ "date" : "2020-04-19" }
;;;
I am typing this on {{.date}}
```
file2.got
```
;;;
{
"date" : "Sunday",
"pgotInclude" : [ "file1.got" ]
}
;;;
{{template "file1" .}}
```
Output from "pgot file2.got"
```
I am typing this on Sunday
```
## Custom pgot actions
There is currently one custom template action defined by pgot (below). This is
included more as an example rather than a critical feature. For those
interested in implementing custom template actions the source code at
<https://git.lenzplace.org/lenzj/pgot/src/branch/master/lib/gotFuncs.go>
demonstrates this simple example.
{{lnp label url}}
lnp (link new page) generates the specified html url in a format which
opens a new page when the user selects the link. If label is nil (aka
"") then the url is displayed as the clickable link, otherwise the label
itself is displayed.
## Text and spaces
By default, all text between actions is copied verbatim when the template is
executed. However, to aid in formatting template source code, if an action's
left delimiter (by default "{{") is followed immediately by a minus sign and
ASCII space character ("{{- "), all trailing white space is trimmed from the
immediately preceding text. Similarly, if the right delimiter ("}}") is preceded
by a space and minus sign (" -}}"), all leading white space is trimmed from the
immediately following text. In these trim markers, the ASCII space must be
present; "{{-3}}" parses as an action containing the number -3.
For instance, when executing the template whose source is
"{{23 -}} < {{- 45}}"
the generated output would be
"23<45"
For this trimming, the definition of white space characters is the same as in
Go: space, horizontal tab, carriage return, and newline.
## Actions
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
data, defined in detail in the corresponding sections that follow.
{{/\* a comment \*/}}++
{{- /\* a comment with white space trimmed from preceding and following text \*/ -}}
A comment; discarded. May contain newlines. Comments do not nest and
must start and end at the delimiters, as shown here.
{{pipeline}}
The default textual representation (the same as would be printed by
fmt.Print) of the value of the pipeline is copied to the output.
{{if pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, T1 is executed. The empty values are false, 0, any nil
pointer or interface value, and any array, slice, map, or string of
length zero. Dot is unaffected.
{{if pipeline}} T1 {{else}} T0 {{end}}
If the value of the pipeline is empty, T0 is executed; otherwise, T1 is
executed. Dot is unaffected.
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
To simplify the appearance of if-else chains, the else action of an if
may include another if directly; the effect is exactly the same as
writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}
The value of the pipeline must be an array, slice, map, or channel. If
the value of the pipeline has length zero, nothing is output; otherwise,
dot is set to the successive elements of the array, slice, or map and T1
is executed. If the value is a map and the keys are of basic type with
a defined order, the elements will be visited in sorted key order.
{{range pipeline}} T1 {{else}} T0 {{end}}
The value of the pipeline must be an array, slice, map, or channel. If
the value of the pipeline has length zero, dot is unaffected and T0 is
executed; otherwise, dot is set to the successive elements of the array,
slice, or map and T1 is executed.
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set to the
value of the pipeline.
{{block "name" pipeline}} T1 {{end}}
A block is shorthand for defining a template {{define "name"}} T1
{{end}} and then executing it in place {{template "name" pipeline}} The
typical use is to define a set of root templates that are then
customized by redefining the block templates within.
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is executed.
{{with pipeline}} T1 {{else}} T0 {{end}}
If the value of the pipeline is empty, dot is unaffected and T0 is
executed; otherwise, dot is set to the value of the pipeline and T1 is
executed.
## Arguments
An argument is a simple value, denoted by one of the following.
```
- A boolean, string, character, integer, floating-point, imaginary
or complex constant in Go syntax. These behave like Go's untyped
constants. Note that, as in Go, whether a large integer constant
overflows when assigned or passed to a function can depend on whether
the host machine's ints are 32 or 64 bits.
- The keyword nil, representing an untyped Go nil.
- The character '.' (period):
.
The result is the value of dot.
- A variable name, which is a (possibly empty) alphanumeric string
preceded by a dollar sign, such as
$piOver2
or
$
The result is the value of the variable. Variables are described below.
- The name of a field of the data, which must be a struct, preceded
by a period, such as
.Field
The result is the value of the field. Field invocations may be chained:
.Field1.Field2
Fields can also be evaluated on variables, including chaining:
$x.Field1.Field2
- The name of a key of the data, which must be a map, preceded by a period, such
as
.Key
The result is the map element value indexed by the key. Key invocations may
be chained and combined with fields to any depth:
.Field1.Key1.Field2.Key2
Although the key must be an alphanumeric identifier, unlike with field names
they do not need to start with an upper case letter. Keys can also be
evaluated on variables, including chaining:
$x.key1.key2
- The name of a niladic method of the data, preceded by a period, such as
.Method
The result is the value of invoking the method with dot as the receiver,
dot.Method(). Such a method must have one return value (of any type) or two
return values, the second of which is an error. If it has two and the
returned error is non-nil, execution terminates and an error is returned to
the caller as the value of Execute. Method invocations may be chained and
combined with fields and keys to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
Methods can also be evaluated on variables, including chaining:
$x.Method1.Field
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun(). The return types and
values behave as in methods. Functions and function names are described below.
- A parenthesized instance of one the above, for grouping. The result may be
accessed by a field or map key invocation.
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod "arg").Field
```
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required. If an evaluation
yields a function value, such as a function-valued field of a struct, the
function is not invoked automatically, but it can be used as a truth value for
an if action and the like. To invoke it, use the call function, defined below.
## Pipelines
A pipeline is a possibly chained sequence of "commands". A command is a simple
value (argument) or a function or method call, possibly with multiple arguments:
```
Argument
The result is the value of evaluating the argument.
.Method [Argument...]
The method can be alone or the last element of a chain but,
unlike methods in the middle of a chain, it can take arguments.
The result is the value of calling the method with the
arguments:
dot.Method(Argument1, etc.)
functionName [Argument...]
The result is the value of calling the function associated
with the name:
function(Argument1, etc.)
Functions and function names are described below.
```
A pipeline may be "chained" by separating a sequence of commands with pipeline
characters '|'. In a chained pipeline, the result of each command is passed as
the last argument of the following command. The output of the final command in
the pipeline is the value of the pipeline.
The output of a command will be either one value or two values, the second of
which has type error. If that second value is present and evaluates to non-nil,
execution terminates and the error is returned to the caller of Execute.
## Variables
A pipeline inside an action may initialize a variable to capture the result. The
initialization has syntax
```
$variable := pipeline
```
where $variable is the name of the variable. An action that declares a variable
produces no output.
Variables previously declared can also be assigned, using the syntax
```
$variable = pipeline
```
If a "range" action initializes a variable, the variable is set to the
successive elements of the iteration. Also, a "range" may declare two variables,
separated by a comma:
```
range $index, $element := pipeline
```
in which case $index and $element are set to the successive values of the
array/slice index or map key and element, respectively. Note that if there is
only one variable, it is assigned the element; this is opposite to the
convention in Go range clauses.
A variable's scope extends to the "end" action of the control structure ("if",
"with", or "range") in which it is declared, or to the end of the template if
there is no such control structure. A template invocation does not inherit
variables from the point of its invocation.
When execution begins, $ is set to the data argument passed to Execute, that is,
to the starting value of dot.
Here are some example one-line templates demonstrating pipelines and variables.
All produce the quoted word "output":
```
{{"\"output\""}}
A string constant.
{{`"output"`}}
A raw string constant.
{{printf "%q" "output"}}
A function call.
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
command.
{{printf "%q" (print "out" "put")}}
A parenthesized argument.
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
A longer chain.
{{with "output"}}{{printf "%q" .}}{{end}}
A with action using dot.
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
A with action that creates and uses a variable.
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
A with action that uses the variable in another action.
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
The same, but pipelined.
```