風神「嵐の日」
git-svn-id: file:///srv/svn/repo/aya/trunk@89 cec141ff-132a-4243-88a5-ce187bd62f94
This commit is contained in:
parent
f0da6dc37f
commit
6d135bb68c
9
Makefile
9
Makefile
@ -1,11 +1,12 @@
|
||||
DESTDIR ?=
|
||||
GOFLAGS ?= -v -buildvcs=false -mod=vendor -buildmode=exe -ldflags "-w -X `go list`.Date=${DATE} -X `go list`.Vendor=${GOOS} -X `go list`.Version=${VERSION}"
|
||||
GO ?= go
|
||||
GOFLAGS ?= -v -buildvcs=false -buildmode=exe -ldflags "-w -X `${GO} list`.Date=${DATE} -X `${GO} list`.Vendor=${GOOS} -X `${GO} list`.Version=${VERSION}"
|
||||
PREFIX ?= /usr/local
|
||||
DATE ?= `date -u +%F`
|
||||
GOOS ?= `go env GOOS`
|
||||
VERSION ?= `git describe --tags`
|
||||
GOOS ?= `${GO} env GOOS`
|
||||
VERSION ?= 1.0F
|
||||
build:
|
||||
go build ${GOFLAGS} ./cmd/aya
|
||||
${GO} build ${GOFLAGS} ./cmd/aya
|
||||
clean:
|
||||
rm -f aya
|
||||
dist:
|
||||
|
24
README.md
24
README.md
@ -17,9 +17,9 @@ Named after [Aya Shameimaru](https://en.touhouwiki.net/wiki/Aya_Shameimaru) from
|
||||
|
||||
Build it manually provided you have Go (>=1.17) installed:
|
||||
|
||||
$ go install marisa.chaotic.ninja/aya/cmd/aya@latest (1)
|
||||
$ go install mahou-no-mori.yakumo.dev/aya/cmd/aya@latest (1)
|
||||
--- or ---
|
||||
$ git clone https://git.chaotic.ninja/yakumo_izuru/aya
|
||||
$ git clone https://git.yakumo.dev/yakumo.izuru/aya
|
||||
$ cd aya
|
||||
$ make
|
||||
# make install
|
||||
@ -84,7 +84,7 @@ for f in ./blog/*/*.md ; do
|
||||
url=`$AYA var $f url`
|
||||
title=`$AYA var $f title | tr A-Z a-z`
|
||||
descr=`$AYA var $f description`
|
||||
echo $timestamp "<item><title>$title</title><link>https://technicalmarisa.chaotic.ninja/blog/$url</link><description>$descr</description><pubDate>$(gdate --date @$timestamp -R)</pubDate><guid>http://technicalmarisa.chaotic.ninja/blog/$url</guid></item>"
|
||||
echo $timestamp "<item><title>$title</title><link>https://technicalmarisa.yakumo.dev/blog/$url</link><description>$descr</description><pubDate>$(gdate --date @$timestamp -R)</pubDate><guid>http://technicalmarisa.yakumo.dev/blog/$url</guid></item>"
|
||||
fi
|
||||
done | sort -r -n | cut -d' ' -f2- >> $AYA_OUTDIR/blog/rss.xml
|
||||
echo '</channel>' >> $AYA_OUTDIR/blog/rss.xml
|
||||
@ -104,24 +104,6 @@ Read `aya(1)`
|
||||
## License
|
||||
The software is distributed under the [MIT/X11](LICENSE) license.
|
||||
|
||||
## Sites using Aya!
|
||||
(I know, I made the majority of them, but they still count)
|
||||
|
||||
| Title | Author | Link |
|
||||
|------------------------|--------------------------------------------------|---------------------------------------|
|
||||
| Aya (project homepage) | Izuru Yakumo | https://aya.chaotic.ninja |
|
||||
| Chaotic Ninja | Izuru Yakumo, Mima-sama | https://chaotic.ninja |
|
||||
| Geidontei | Izuru Yakumo | https://geidontei.chaotic.ninja |
|
||||
| ChaoticIRC Network | Izuru Yakumo | https://im.chaotic.ninja |
|
||||
| Kanako | Izuru Yakumo | https://kanako.chaotic.ninja |
|
||||
| Kill-9 The Revival | Various authors | https://kill-9.chaotic.ninja |
|
||||
| PXIMG(7) | Izuru Yakumo | https://pximg.chaotic.ninja |
|
||||
| Shinmyoumaru | Mima-sama | https://shinmyoumaru.chaotic.ninja |
|
||||
| Suika | Izuru Yakumo | https://suika.chaotic.ninja |
|
||||
| TechnicalMarisa | Izuru Yakumo | https://technicalmarisa.chaotic.ninja |
|
||||
| Tengu Space | [DeviousTengu](https://fedi.tengu.space/devious) | https://tengu.space |
|
||||
| WindowMaker Shrine | Izuru Yakumo | https://themes.chaotic.ninja |
|
||||
|
||||
---
|
||||
|
||||
Ayaya~
|
||||
|
@ -1,4 +1,3 @@
|
||||
// $TheSupernovaDuo: marisa.chaotic.ninja/aya/cmd/aya, v1.0.1 2023-12-12 14:27:02+0000, yakumo_izuru Exp $
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -7,7 +6,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"marisa.chaotic.ninja/aya"
|
||||
"mahou-no-mori.yakumo.dev/aya"
|
||||
)
|
||||
|
||||
// Define global constants here
|
||||
@ -73,7 +72,7 @@ func main() {
|
||||
fmt.Println(strings.TrimSpace(s))
|
||||
}
|
||||
case "version":
|
||||
aya.PrintVersion()
|
||||
fmt.Println(aya.PrintVersion())
|
||||
os.Exit(0)
|
||||
case "watch":
|
||||
buildAll(true)
|
||||
|
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
||||
module marisa.chaotic.ninja/aya
|
||||
module mahou-no-mori.yakumo.dev/aya
|
||||
|
||||
go 1.17
|
||||
|
||||
|
3
serve.go
3
serve.go
@ -1,4 +1,5 @@
|
||||
// Taken from https://github.com/fogleman/serve and repurposed as a library
|
||||
/* The following code has been taken from https://github.com/fogleman/serve and it is used on this project as a library */
|
||||
/* Attribution goes to its original author */
|
||||
package aya
|
||||
|
||||
import (
|
||||
|
4
usage.go
4
usage.go
@ -7,8 +7,8 @@ import (
|
||||
// This function is called by the `aya help` subcommand
|
||||
func PrintUsage() {
|
||||
fmt.Printf("aya/%v\n", PrintFullVersion())
|
||||
fmt.Println("Homepage: https://aya.chaotic.ninja")
|
||||
fmt.Println("Repository: https://git.chaotic.ninja/usr/yakumo_izuru/aya")
|
||||
fmt.Println("Homepage: https://suzunaan.yakumo.dev/aya/")
|
||||
fmt.Println("Repository: https://svn.yakumo.dev/yakumo.izuru/aya")
|
||||
fmt.Println("==")
|
||||
fmt.Println("build [file] · (Re)build a site or a file in particular")
|
||||
fmt.Println("clean · Remove the generated .pub directory")
|
||||
|
7
vendor/github.com/eknkc/amber/.travis.yml
generated
vendored
7
vendor/github.com/eknkc/amber/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
9
vendor/github.com/eknkc/amber/LICENSE
generated
vendored
9
vendor/github.com/eknkc/amber/LICENSE
generated
vendored
@ -1,9 +0,0 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 Ekin Koc ekin@eknkc.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
442
vendor/github.com/eknkc/amber/README.md
generated
vendored
442
vendor/github.com/eknkc/amber/README.md
generated
vendored
@ -1,442 +0,0 @@
|
||||
# amber [](http://godoc.org/github.com/eknkc/amber) [](https://travis-ci.org/eknkc/amber)
|
||||
|
||||
## Notice
|
||||
> While Amber is perfectly fine and stable to use, I've been working on a direct Pug.js port for Go. It is somewhat hacky at the moment but take a look at [Pug.go](https://github.com/eknkc/pug) if you are looking for a [Pug.js](https://github.com/pugjs/pug) compatible Go template engine.
|
||||
|
||||
### Usage
|
||||
```go
|
||||
import "github.com/eknkc/amber"
|
||||
```
|
||||
|
||||
Amber is an elegant templating engine for Go Programming Language
|
||||
It is inspired from HAML and Jade
|
||||
|
||||
### Tags
|
||||
|
||||
A tag is simply a word:
|
||||
|
||||
html
|
||||
|
||||
is converted to
|
||||
|
||||
```html
|
||||
<html></html>
|
||||
```
|
||||
|
||||
It is possible to add ID and CLASS attributes to tags:
|
||||
|
||||
div#main
|
||||
span.time
|
||||
|
||||
are converted to
|
||||
|
||||
```html
|
||||
<div id="main"></div>
|
||||
<span class="time"></span>
|
||||
```
|
||||
|
||||
Any arbitrary attribute name / value pair can be added this way:
|
||||
|
||||
a[href="http://www.google.com"]
|
||||
|
||||
You can mix multiple attributes together
|
||||
|
||||
a#someid[href="/"][title="Main Page"].main.link Click Link
|
||||
|
||||
gets converted to
|
||||
|
||||
```html
|
||||
<a id="someid" class="main link" href="/" title="Main Page">Click Link</a>
|
||||
```
|
||||
|
||||
It is also possible to define these attributes within the block of a tag
|
||||
|
||||
a
|
||||
#someid
|
||||
[href="/"]
|
||||
[title="Main Page"]
|
||||
.main
|
||||
.link
|
||||
| Click Link
|
||||
|
||||
### Doctypes
|
||||
|
||||
To add a doctype, use `!!!` or `doctype` keywords:
|
||||
|
||||
!!! transitional
|
||||
// <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
or use `doctype`
|
||||
|
||||
doctype 5
|
||||
// <!DOCTYPE html>
|
||||
|
||||
Available options: `5`, `default`, `xml`, `transitional`, `strict`, `frameset`, `1.1`, `basic`, `mobile`
|
||||
|
||||
### Tag Content
|
||||
|
||||
For single line tag text, you can just append the text after tag name:
|
||||
|
||||
p Testing!
|
||||
|
||||
would yield
|
||||
|
||||
<p>Testing!</p>
|
||||
|
||||
For multi line tag text, or nested tags, use indentation:
|
||||
|
||||
html
|
||||
head
|
||||
title Page Title
|
||||
body
|
||||
div#content
|
||||
p
|
||||
| This is a long page content
|
||||
| These lines are all part of the parent p
|
||||
|
||||
a[href="/"] Go To Main Page
|
||||
|
||||
### Data
|
||||
|
||||
Input template data can be reached by key names directly. For example, assuming the template has been
|
||||
executed with following JSON data:
|
||||
|
||||
```json
|
||||
{
|
||||
"Name": "Ekin",
|
||||
"LastName": "Koc",
|
||||
"Repositories": [
|
||||
"amber",
|
||||
"dateformat"
|
||||
],
|
||||
"Avatar": "/images/ekin.jpg",
|
||||
"Friends": 17
|
||||
}
|
||||
```
|
||||
|
||||
It is possible to interpolate fields using `#{}`
|
||||
|
||||
p Welcome #{Name}!
|
||||
|
||||
would print
|
||||
|
||||
```html
|
||||
<p>Welcome Ekin!</p>
|
||||
```
|
||||
|
||||
Attributes can have field names as well
|
||||
|
||||
a[title=Name][href="/ekin.koc"]
|
||||
|
||||
would print
|
||||
|
||||
```html
|
||||
<a title="Ekin" href="/ekin.koc"></a>
|
||||
```
|
||||
|
||||
### Expressions
|
||||
|
||||
Amber can expand basic expressions. For example, it is possible to concatenate strings with + operator:
|
||||
|
||||
p Welcome #{Name + " " + LastName}
|
||||
|
||||
Arithmetic expressions are also supported:
|
||||
|
||||
p You need #{50 - Friends} more friends to reach 50!
|
||||
|
||||
Expressions can be used within attributes
|
||||
|
||||
img[alt=Name + " " + LastName][src=Avatar]
|
||||
|
||||
### Variables
|
||||
|
||||
It is possible to define dynamic variables within templates,
|
||||
all variables must start with a $ character and can be assigned as in the following example:
|
||||
|
||||
div
|
||||
$fullname = Name + " " + LastName
|
||||
p Welcome #{$fullname}
|
||||
|
||||
If you need to access the supplied data itself (i.e. the object containing Name, LastName etc fields.) you can use `$` variable
|
||||
|
||||
p $.Name
|
||||
|
||||
### Conditions
|
||||
|
||||
For conditional blocks, it is possible to use `if <expression>`
|
||||
|
||||
div
|
||||
if Friends > 10
|
||||
p You have more than 10 friends
|
||||
else if Friends > 5
|
||||
p You have more than 5 friends
|
||||
else
|
||||
p You need more friends
|
||||
|
||||
Again, it is possible to use arithmetic and boolean operators
|
||||
|
||||
div
|
||||
if Name == "Ekin" && LastName == "Koc"
|
||||
p Hey! I know you..
|
||||
|
||||
There is a special syntax for conditional attributes. Only block attributes can have conditions;
|
||||
|
||||
div
|
||||
.hasfriends ? Friends > 0
|
||||
|
||||
This would yield a div with `hasfriends` class only if the `Friends > 0` condition holds. It is
|
||||
perfectly fine to use the same method for other types of attributes:
|
||||
|
||||
div
|
||||
#foo ? Name == "Ekin"
|
||||
[bar=baz] ? len(Repositories) > 0
|
||||
|
||||
### Iterations
|
||||
|
||||
It is possible to iterate over arrays and maps using `each`:
|
||||
|
||||
each $repo in Repositories
|
||||
p #{$repo}
|
||||
|
||||
would print
|
||||
|
||||
p amber
|
||||
p dateformat
|
||||
|
||||
It is also possible to iterate over values and indexes at the same time
|
||||
|
||||
each $i, $repo in Repositories
|
||||
p
|
||||
.even ? $i % 2 == 0
|
||||
.odd ? $i % 2 == 1
|
||||
|
||||
### Mixins
|
||||
|
||||
Mixins (reusable template blocks that accept arguments) can be defined:
|
||||
|
||||
mixin surprise
|
||||
span Surprise!
|
||||
mixin link($href, $title, $text)
|
||||
a[href=$href][title=$title] #{$text}
|
||||
|
||||
and then called multiple times within a template (or even within another mixin definition):
|
||||
|
||||
div
|
||||
+surprise
|
||||
+surprise
|
||||
+link("http://google.com", "Google", "Check out Google")
|
||||
|
||||
Template data, variables, expressions, etc., can all be passed as arguments:
|
||||
|
||||
+link(GoogleUrl, $googleTitle, "Check out " + $googleTitle)
|
||||
|
||||
### Imports
|
||||
|
||||
A template can import other templates using `import`:
|
||||
|
||||
a.amber
|
||||
p this is template a
|
||||
|
||||
b.amber
|
||||
p this is template b
|
||||
|
||||
c.amber
|
||||
div
|
||||
import a
|
||||
import b
|
||||
|
||||
gets compiled to
|
||||
|
||||
div
|
||||
p this is template a
|
||||
p this is template b
|
||||
|
||||
### Inheritance
|
||||
|
||||
A template can inherit other templates. In order to inherit another template, an `extends` keyword should be used.
|
||||
Parent template can define several named blocks and child template can modify the blocks.
|
||||
|
||||
master.amber
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
block meta
|
||||
meta[name="description"][content="This is a great website"]
|
||||
|
||||
title
|
||||
block title
|
||||
| Default title
|
||||
body
|
||||
block content
|
||||
|
||||
subpage.amber
|
||||
extends master
|
||||
|
||||
block title
|
||||
| Some sub page!
|
||||
|
||||
block append meta
|
||||
// This will be added after the description meta tag. It is also possible
|
||||
// to prepend someting to an existing block
|
||||
meta[name="keywords"][content="foo bar"]
|
||||
|
||||
block content
|
||||
div#main
|
||||
p Some content here
|
||||
|
||||
### License
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 Ekin Koc <ekin@eknkc.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
var DefaultOptions = Options{true, false}
|
||||
var DefaultDirOptions = DirOptions{".amber", true}
|
||||
```
|
||||
|
||||
#### func Compile
|
||||
|
||||
```go
|
||||
func Compile(input string, options Options) (*template.Template, error)
|
||||
```
|
||||
Parses and compiles the supplied amber template string. Returns corresponding Go
|
||||
Template (html/templates) instance. Necessary runtime functions will be injected
|
||||
and the template will be ready to be executed.
|
||||
|
||||
#### func CompileFile
|
||||
|
||||
```go
|
||||
func CompileFile(filename string, options Options) (*template.Template, error)
|
||||
```
|
||||
Parses and compiles the contents of supplied filename. Returns corresponding Go
|
||||
Template (html/templates) instance. Necessary runtime functions will be injected
|
||||
and the template will be ready to be executed.
|
||||
|
||||
#### func CompileDir
|
||||
```go
|
||||
func CompileDir(dirname string, dopt DirOptions, opt Options) (map[string]*template.Template, error)
|
||||
```
|
||||
Parses and compiles the contents of a supplied directory name. Returns a mapping of template name (extension stripped) to corresponding Go Template (html/template) instance. Necessary runtime functions will be injected and the template will be ready to be executed.
|
||||
|
||||
If there are templates in subdirectories, its key in the map will be it's path relative to `dirname`. For example:
|
||||
```
|
||||
templates/
|
||||
|-- index.amber
|
||||
|-- layouts/
|
||||
|-- base.amber
|
||||
```
|
||||
```go
|
||||
templates, err := amber.CompileDir("templates/", amber.DefaultDirOptions, amber.DefaultOptions)
|
||||
templates["index"] // index.amber Go Template
|
||||
templates["layouts/base"] // base.amber Go Template
|
||||
```
|
||||
By default, the search will be recursive and will match only files ending in ".amber". If recursive is turned off, it will only search the top level of the directory. Specified extension must start with a period.
|
||||
|
||||
#### type Compiler
|
||||
|
||||
```go
|
||||
type Compiler struct {
|
||||
// Compiler options
|
||||
Options
|
||||
}
|
||||
```
|
||||
|
||||
Compiler is the main interface of Amber Template Engine. In order to use an
|
||||
Amber template, it is required to create a Compiler and compile an Amber source
|
||||
to native Go template.
|
||||
|
||||
compiler := amber.New()
|
||||
// Parse the input file
|
||||
err := compiler.ParseFile("./input.amber")
|
||||
if err == nil {
|
||||
// Compile input file to Go template
|
||||
tpl, err := compiler.Compile()
|
||||
if err == nil {
|
||||
// Check built in html/template documentation for further details
|
||||
tpl.Execute(os.Stdout, somedata)
|
||||
}
|
||||
}
|
||||
|
||||
#### func New
|
||||
|
||||
```go
|
||||
func New() *Compiler
|
||||
```
|
||||
Create and initialize a new Compiler
|
||||
|
||||
#### func (*Compiler) Compile
|
||||
|
||||
```go
|
||||
func (c *Compiler) Compile() (*template.Template, error)
|
||||
```
|
||||
Compile amber and create a Go Template (html/templates) instance. Necessary
|
||||
runtime functions will be injected and the template will be ready to be
|
||||
executed.
|
||||
|
||||
#### func (*Compiler) CompileString
|
||||
|
||||
```go
|
||||
func (c *Compiler) CompileString() (string, error)
|
||||
```
|
||||
Compile template and return the Go Template source You would not be using this
|
||||
unless debugging / checking the output. Please use Compile method to obtain a
|
||||
template instance directly.
|
||||
|
||||
#### func (*Compiler) CompileWriter
|
||||
|
||||
```go
|
||||
func (c *Compiler) CompileWriter(out io.Writer) (err error)
|
||||
```
|
||||
Compile amber and write the Go Template source into given io.Writer instance You
|
||||
would not be using this unless debugging / checking the output. Please use
|
||||
Compile method to obtain a template instance directly.
|
||||
|
||||
#### func (*Compiler) Parse
|
||||
|
||||
```go
|
||||
func (c *Compiler) Parse(input string) (err error)
|
||||
```
|
||||
Parse given raw amber template string.
|
||||
|
||||
#### func (*Compiler) ParseFile
|
||||
|
||||
```go
|
||||
func (c *Compiler) ParseFile(filename string) (err error)
|
||||
```
|
||||
Parse the amber template file in given path
|
||||
|
||||
#### type Options
|
||||
|
||||
```go
|
||||
type Options struct {
|
||||
// Setting if pretty printing is enabled.
|
||||
// Pretty printing ensures that the output html is properly indented and in human readable form.
|
||||
// If disabled, produced HTML is compact. This might be more suitable in production environments.
|
||||
// Defaukt: true
|
||||
PrettyPrint bool
|
||||
// Setting if line number emitting is enabled
|
||||
// In this form, Amber emits line number comments in the output template. It is usable in debugging environments.
|
||||
// Default: false
|
||||
LineNumbers bool
|
||||
}
|
||||
```
|
||||
|
||||
#### type DirOptions
|
||||
|
||||
```go
|
||||
// Used to provide options to directory compilation
|
||||
type DirOptions struct {
|
||||
// File extension to match for compilation
|
||||
Ext string
|
||||
// Whether or not to walk subdirectories
|
||||
Recursive bool
|
||||
}
|
||||
```
|
844
vendor/github.com/eknkc/amber/compiler.go
generated
vendored
844
vendor/github.com/eknkc/amber/compiler.go
generated
vendored
@ -1,844 +0,0 @@
|
||||
package amber
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
gp "go/parser"
|
||||
gt "go/token"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eknkc/amber/parser"
|
||||
)
|
||||
|
||||
var builtinFunctions = [...]string{
|
||||
"len",
|
||||
"print",
|
||||
"printf",
|
||||
"println",
|
||||
"urlquery",
|
||||
"js",
|
||||
"json",
|
||||
"index",
|
||||
"html",
|
||||
"unescaped",
|
||||
}
|
||||
|
||||
const (
|
||||
dollar = "__DOLLAR__"
|
||||
)
|
||||
|
||||
// Compiler is the main interface of Amber Template Engine.
|
||||
// In order to use an Amber template, it is required to create a Compiler and
|
||||
// compile an Amber source to native Go template.
|
||||
// compiler := amber.New()
|
||||
// // Parse the input file
|
||||
// err := compiler.ParseFile("./input.amber")
|
||||
// if err == nil {
|
||||
// // Compile input file to Go template
|
||||
// tpl, err := compiler.Compile()
|
||||
// if err == nil {
|
||||
// // Check built in html/template documentation for further details
|
||||
// tpl.Execute(os.Stdout, somedata)
|
||||
// }
|
||||
// }
|
||||
type Compiler struct {
|
||||
// Compiler options
|
||||
Options
|
||||
filename string
|
||||
node parser.Node
|
||||
indentLevel int
|
||||
newline bool
|
||||
buffer *bytes.Buffer
|
||||
tempvarIndex int
|
||||
mixins map[string]*parser.Mixin
|
||||
}
|
||||
|
||||
// New creates and initialize a new Compiler.
|
||||
func New() *Compiler {
|
||||
compiler := new(Compiler)
|
||||
compiler.filename = ""
|
||||
compiler.tempvarIndex = 0
|
||||
compiler.PrettyPrint = true
|
||||
compiler.Options = DefaultOptions
|
||||
compiler.mixins = make(map[string]*parser.Mixin)
|
||||
|
||||
return compiler
|
||||
}
|
||||
|
||||
// Options defines template output behavior.
|
||||
type Options struct {
|
||||
// Setting if pretty printing is enabled.
|
||||
// Pretty printing ensures that the output html is properly indented and in human readable form.
|
||||
// If disabled, produced HTML is compact. This might be more suitable in production environments.
|
||||
// Default: true
|
||||
PrettyPrint bool
|
||||
// Setting if line number emitting is enabled
|
||||
// In this form, Amber emits line number comments in the output template. It is usable in debugging environments.
|
||||
// Default: false
|
||||
LineNumbers bool
|
||||
// Setting the virtual filesystem to use
|
||||
// If set, will attempt to use a virtual filesystem provided instead of os.
|
||||
// Default: nil
|
||||
VirtualFilesystem http.FileSystem
|
||||
}
|
||||
|
||||
// DirOptions is used to provide options to directory compilation.
|
||||
type DirOptions struct {
|
||||
// File extension to match for compilation
|
||||
Ext string
|
||||
// Whether or not to walk subdirectories
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
// DefaultOptions sets pretty-printing to true and line numbering to false.
|
||||
var DefaultOptions = Options{true, false, nil}
|
||||
|
||||
// DefaultDirOptions sets expected file extension to ".amber" and recursive search for templates within a directory to true.
|
||||
var DefaultDirOptions = DirOptions{".amber", true}
|
||||
|
||||
// Compile parses and compiles the supplied amber template string. Returns corresponding Go Template (html/templates) instance.
|
||||
// Necessary runtime functions will be injected and the template will be ready to be executed.
|
||||
func Compile(input string, options Options) (*template.Template, error) {
|
||||
comp := New()
|
||||
comp.Options = options
|
||||
|
||||
err := comp.Parse(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return comp.Compile()
|
||||
}
|
||||
|
||||
// Compile parses and compiles the supplied amber template []byte.
|
||||
// Returns corresponding Go Template (html/templates) instance.
|
||||
// Necessary runtime functions will be injected and the template will be ready to be executed.
|
||||
func CompileData(input []byte, filename string, options Options) (*template.Template, error) {
|
||||
comp := New()
|
||||
comp.Options = options
|
||||
|
||||
err := comp.ParseData(input, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return comp.Compile()
|
||||
}
|
||||
|
||||
// MustCompile is the same as Compile, except the input is assumed error free. If else, panic.
|
||||
func MustCompile(input string, options Options) *template.Template {
|
||||
t, err := Compile(input, options)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// CompileFile parses and compiles the contents of supplied filename. Returns corresponding Go Template (html/templates) instance.
|
||||
// Necessary runtime functions will be injected and the template will be ready to be executed.
|
||||
func CompileFile(filename string, options Options) (*template.Template, error) {
|
||||
comp := New()
|
||||
comp.Options = options
|
||||
|
||||
err := comp.ParseFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return comp.Compile()
|
||||
}
|
||||
|
||||
// MustCompileFile is the same as CompileFile, except the input is assumed error free. If else, panic.
|
||||
func MustCompileFile(filename string, options Options) *template.Template {
|
||||
t, err := CompileFile(filename, options)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// CompileDir parses and compiles the contents of a supplied directory path, with options.
|
||||
// Returns a map of a template identifier (key) to a Go Template instance.
|
||||
// Ex: if the dirname="templates/" had a file "index.amber" the key would be "index"
|
||||
// If option for recursive is True, this parses every file of relevant extension
|
||||
// in all subdirectories. The key then is the path e.g: "layouts/layout"
|
||||
func CompileDir(dirname string, dopt DirOptions, opt Options) (map[string]*template.Template, error) {
|
||||
dir, err := os.Open(dirname)
|
||||
if err != nil && opt.VirtualFilesystem != nil {
|
||||
vdir, err := opt.VirtualFilesystem.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir = vdir.(*os.File)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compiled := make(map[string]*template.Template)
|
||||
for _, file := range files {
|
||||
// filename is for example "index.amber"
|
||||
filename := file.Name()
|
||||
fileext := filepath.Ext(filename)
|
||||
|
||||
// If recursive is true and there's a subdirectory, recurse
|
||||
if dopt.Recursive && file.IsDir() {
|
||||
dirpath := filepath.Join(dirname, filename)
|
||||
subcompiled, err := CompileDir(dirpath, dopt, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy templates from subdirectory into parent template mapping
|
||||
for k, v := range subcompiled {
|
||||
// Concat with parent directory name for unique paths
|
||||
key := filepath.Join(filename, k)
|
||||
compiled[key] = v
|
||||
}
|
||||
} else if fileext == dopt.Ext {
|
||||
// Otherwise compile the file and add to mapping
|
||||
fullpath := filepath.Join(dirname, filename)
|
||||
tmpl, err := CompileFile(fullpath, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Strip extension
|
||||
key := filename[0 : len(filename)-len(fileext)]
|
||||
compiled[key] = tmpl
|
||||
}
|
||||
}
|
||||
|
||||
return compiled, nil
|
||||
}
|
||||
|
||||
// MustCompileDir is the same as CompileDir, except input is assumed error free. If else, panic.
|
||||
func MustCompileDir(dirname string, dopt DirOptions, opt Options) map[string]*template.Template {
|
||||
m, err := CompileDir(dirname, dopt, opt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Parse given raw amber template string.
|
||||
func (c *Compiler) Parse(input string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}()
|
||||
|
||||
parser, err := parser.StringParser(input)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.node = parser.Parse()
|
||||
return
|
||||
}
|
||||
|
||||
// Parse given raw amber template bytes, and the filename that belongs with it
|
||||
func (c *Compiler) ParseData(input []byte, filename string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}()
|
||||
|
||||
parser, err := parser.ByteParser(input)
|
||||
parser.SetFilename(filename)
|
||||
if c.VirtualFilesystem != nil {
|
||||
parser.SetVirtualFilesystem(c.VirtualFilesystem)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.node = parser.Parse()
|
||||
return
|
||||
}
|
||||
|
||||
// ParseFile parses the amber template file in given path.
|
||||
func (c *Compiler) ParseFile(filename string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}()
|
||||
|
||||
p, err := parser.FileParser(filename)
|
||||
if err != nil && c.VirtualFilesystem != nil {
|
||||
p, err = parser.VirtualFileParser(filename, c.VirtualFilesystem)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.node = p.Parse()
|
||||
c.filename = filename
|
||||
return
|
||||
}
|
||||
|
||||
// Compile amber and create a Go Template (html/templates) instance.
|
||||
// Necessary runtime functions will be injected and the template will be ready to be executed.
|
||||
func (c *Compiler) Compile() (*template.Template, error) {
|
||||
return c.CompileWithName(filepath.Base(c.filename))
|
||||
}
|
||||
|
||||
// CompileWithName is the same as Compile, but allows to specify a name for the template.
|
||||
func (c *Compiler) CompileWithName(name string) (*template.Template, error) {
|
||||
return c.CompileWithTemplate(template.New(name))
|
||||
}
|
||||
|
||||
// CompileWithTemplate is the same as Compile but allows to specify a template.
|
||||
func (c *Compiler) CompileWithTemplate(t *template.Template) (*template.Template, error) {
|
||||
data, err := c.CompileString()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tpl, err := t.Funcs(FuncMap).Parse(data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tpl, nil
|
||||
}
|
||||
|
||||
// CompileWriter compiles amber and writes the Go Template source into given io.Writer instance.
|
||||
// You would not be using this unless debugging / checking the output. Please use Compile
|
||||
// method to obtain a template instance directly.
|
||||
func (c *Compiler) CompileWriter(out io.Writer) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}()
|
||||
|
||||
c.buffer = new(bytes.Buffer)
|
||||
c.visit(c.node)
|
||||
|
||||
if c.buffer.Len() > 0 {
|
||||
c.write("\n")
|
||||
}
|
||||
|
||||
_, err = c.buffer.WriteTo(out)
|
||||
return
|
||||
}
|
||||
|
||||
// CompileString compiles the template and returns the Go Template source.
|
||||
// You would not be using this unless debugging / checking the output. Please use Compile
|
||||
// method to obtain a template instance directly.
|
||||
func (c *Compiler) CompileString() (string, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err := c.CompileWriter(&buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := buf.String()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) visit(node parser.Node) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
|
||||
panic(r)
|
||||
}
|
||||
|
||||
pos := node.Pos()
|
||||
|
||||
if len(pos.Filename) > 0 {
|
||||
panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
|
||||
} else {
|
||||
panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
switch node.(type) {
|
||||
case *parser.Block:
|
||||
c.visitBlock(node.(*parser.Block))
|
||||
case *parser.Doctype:
|
||||
c.visitDoctype(node.(*parser.Doctype))
|
||||
case *parser.Comment:
|
||||
c.visitComment(node.(*parser.Comment))
|
||||
case *parser.Tag:
|
||||
c.visitTag(node.(*parser.Tag))
|
||||
case *parser.Text:
|
||||
c.visitText(node.(*parser.Text))
|
||||
case *parser.Condition:
|
||||
c.visitCondition(node.(*parser.Condition))
|
||||
case *parser.Each:
|
||||
c.visitEach(node.(*parser.Each))
|
||||
case *parser.Assignment:
|
||||
c.visitAssignment(node.(*parser.Assignment))
|
||||
case *parser.Mixin:
|
||||
c.visitMixin(node.(*parser.Mixin))
|
||||
case *parser.MixinCall:
|
||||
c.visitMixinCall(node.(*parser.MixinCall))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) write(value string) {
|
||||
c.buffer.WriteString(value)
|
||||
}
|
||||
|
||||
func (c *Compiler) indent(offset int, newline bool) {
|
||||
if !c.PrettyPrint {
|
||||
return
|
||||
}
|
||||
|
||||
if newline && c.buffer.Len() > 0 {
|
||||
c.write("\n")
|
||||
}
|
||||
|
||||
for i := 0; i < c.indentLevel+offset; i++ {
|
||||
c.write("\t")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) tempvar() string {
|
||||
c.tempvarIndex++
|
||||
return "$__amber_" + strconv.Itoa(c.tempvarIndex)
|
||||
}
|
||||
|
||||
func (c *Compiler) escape(input string) string {
|
||||
return strings.Replace(strings.Replace(input, `\`, `\\`, -1), `"`, `\"`, -1)
|
||||
}
|
||||
|
||||
func (c *Compiler) visitBlock(block *parser.Block) {
|
||||
for _, node := range block.Children {
|
||||
if _, ok := node.(*parser.Text); !block.CanInline() && ok {
|
||||
c.indent(0, true)
|
||||
}
|
||||
|
||||
c.visit(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) visitDoctype(doctype *parser.Doctype) {
|
||||
c.write(doctype.String())
|
||||
}
|
||||
|
||||
func (c *Compiler) visitComment(comment *parser.Comment) {
|
||||
if comment.Silent {
|
||||
return
|
||||
}
|
||||
|
||||
c.indent(0, false)
|
||||
|
||||
if comment.Block == nil {
|
||||
c.write(`{{unescaped "<!-- ` + c.escape(comment.Value) + ` -->"}}`)
|
||||
} else {
|
||||
c.write(`<!-- ` + comment.Value)
|
||||
c.visitBlock(comment.Block)
|
||||
c.write(` -->`)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) visitCondition(condition *parser.Condition) {
|
||||
c.write(`{{if ` + c.visitRawInterpolation(condition.Expression) + `}}`)
|
||||
c.visitBlock(condition.Positive)
|
||||
if condition.Negative != nil {
|
||||
c.write(`{{else}}`)
|
||||
c.visitBlock(condition.Negative)
|
||||
}
|
||||
c.write(`{{end}}`)
|
||||
}
|
||||
|
||||
func (c *Compiler) visitEach(each *parser.Each) {
|
||||
if each.Block == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(each.Y) == 0 {
|
||||
c.write(`{{range ` + each.X + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
|
||||
} else {
|
||||
c.write(`{{range ` + each.X + `, ` + each.Y + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
|
||||
}
|
||||
c.visitBlock(each.Block)
|
||||
c.write(`{{end}}`)
|
||||
}
|
||||
|
||||
func (c *Compiler) visitAssignment(assgn *parser.Assignment) {
|
||||
c.write(`{{` + assgn.X + ` := ` + c.visitRawInterpolation(assgn.Expression) + `}}`)
|
||||
}
|
||||
|
||||
func (c *Compiler) visitTag(tag *parser.Tag) {
|
||||
type attrib struct {
|
||||
name string
|
||||
value func() string
|
||||
condition string
|
||||
}
|
||||
|
||||
attribs := make(map[string]*attrib)
|
||||
|
||||
for _, item := range tag.Attributes {
|
||||
attritem := item
|
||||
attr := new(attrib)
|
||||
attr.name = item.Name
|
||||
|
||||
attr.value = func() string {
|
||||
if !attritem.IsRaw {
|
||||
return c.visitInterpolation(attritem.Value)
|
||||
} else if attritem.Value == "" {
|
||||
return ""
|
||||
} else {
|
||||
return attritem.Value
|
||||
}
|
||||
}
|
||||
|
||||
if len(attritem.Condition) != 0 {
|
||||
attr.condition = c.visitRawInterpolation(attritem.Condition)
|
||||
}
|
||||
|
||||
if attr.name == "class" && attribs["class"] != nil {
|
||||
prevclass := attribs["class"]
|
||||
prevvalue := prevclass.value
|
||||
|
||||
prevclass.value = func() string {
|
||||
aval := attr.value()
|
||||
|
||||
if len(attr.condition) > 0 {
|
||||
aval = `{{if ` + attr.condition + `}}` + aval + `{{end}}`
|
||||
}
|
||||
|
||||
if len(prevclass.condition) > 0 {
|
||||
return `{{if ` + prevclass.condition + `}}` + prevvalue() + `{{end}} ` + aval
|
||||
}
|
||||
|
||||
return prevvalue() + " " + aval
|
||||
}
|
||||
} else {
|
||||
attribs[attritem.Name] = attr
|
||||
}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(attribs))
|
||||
for key := range attribs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
c.indent(0, true)
|
||||
c.write("<" + tag.Name)
|
||||
|
||||
for _, name := range keys {
|
||||
value := attribs[name]
|
||||
|
||||
if len(value.condition) > 0 {
|
||||
c.write(`{{if ` + value.condition + `}}`)
|
||||
}
|
||||
|
||||
val := value.value()
|
||||
|
||||
if val == "" {
|
||||
c.write(` ` + name)
|
||||
} else {
|
||||
c.write(` ` + name + `="` + val + `"`)
|
||||
}
|
||||
|
||||
if len(value.condition) > 0 {
|
||||
c.write(`{{end}}`)
|
||||
}
|
||||
}
|
||||
|
||||
if tag.IsSelfClosing() {
|
||||
c.write(` />`)
|
||||
} else {
|
||||
c.write(`>`)
|
||||
|
||||
if tag.Block != nil {
|
||||
if !tag.Block.CanInline() {
|
||||
c.indentLevel++
|
||||
}
|
||||
|
||||
c.visitBlock(tag.Block)
|
||||
|
||||
if !tag.Block.CanInline() {
|
||||
c.indentLevel--
|
||||
c.indent(0, true)
|
||||
}
|
||||
}
|
||||
|
||||
c.write(`</` + tag.Name + `>`)
|
||||
}
|
||||
}
|
||||
|
||||
var textInterpolateRegexp = regexp.MustCompile(`#\{(.*?)\}`)
|
||||
var textEscapeRegexp = regexp.MustCompile(`\{\{(.*?)\}\}`)
|
||||
|
||||
func (c *Compiler) visitText(txt *parser.Text) {
|
||||
value := textEscapeRegexp.ReplaceAllStringFunc(txt.Value, func(value string) string {
|
||||
return `{{"{{"}}` + value[2:len(value)-2] + `{{"}}"}}`
|
||||
})
|
||||
|
||||
value = textInterpolateRegexp.ReplaceAllStringFunc(value, func(value string) string {
|
||||
return c.visitInterpolation(value[2 : len(value)-1])
|
||||
})
|
||||
|
||||
lines := strings.Split(value, "\n")
|
||||
for i := 0; i < len(lines); i++ {
|
||||
c.write(lines[i])
|
||||
|
||||
if i < len(lines)-1 {
|
||||
c.write("\n")
|
||||
c.indent(0, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) visitInterpolation(value string) string {
|
||||
return `{{` + c.visitRawInterpolation(value) + `}}`
|
||||
}
|
||||
|
||||
func (c *Compiler) visitRawInterpolation(value string) string {
|
||||
if value == "" {
|
||||
value = "\"\""
|
||||
}
|
||||
|
||||
value = strings.Replace(value, "$", dollar, -1)
|
||||
expr, err := gp.ParseExpr(value)
|
||||
if err != nil {
|
||||
panic("Unable to parse expression.")
|
||||
}
|
||||
value = strings.Replace(c.visitExpression(expr), dollar, "$", -1)
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *Compiler) visitExpression(outerexpr ast.Expr) string {
|
||||
stack := list.New()
|
||||
|
||||
pop := func() string {
|
||||
if stack.Front() == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
val := stack.Front().Value.(string)
|
||||
stack.Remove(stack.Front())
|
||||
return val
|
||||
}
|
||||
|
||||
var exec func(ast.Expr)
|
||||
|
||||
exec = func(expr ast.Expr) {
|
||||
switch expr := expr.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
{
|
||||
be := expr
|
||||
|
||||
exec(be.Y)
|
||||
exec(be.X)
|
||||
|
||||
negate := false
|
||||
name := c.tempvar()
|
||||
c.write(`{{` + name + ` := `)
|
||||
|
||||
switch be.Op {
|
||||
case gt.ADD:
|
||||
c.write("__amber_add ")
|
||||
case gt.SUB:
|
||||
c.write("__amber_sub ")
|
||||
case gt.MUL:
|
||||
c.write("__amber_mul ")
|
||||
case gt.QUO:
|
||||
c.write("__amber_quo ")
|
||||
case gt.REM:
|
||||
c.write("__amber_rem ")
|
||||
case gt.LAND:
|
||||
c.write("and ")
|
||||
case gt.LOR:
|
||||
c.write("or ")
|
||||
case gt.EQL:
|
||||
c.write("__amber_eql ")
|
||||
case gt.NEQ:
|
||||
c.write("__amber_eql ")
|
||||
negate = true
|
||||
case gt.LSS:
|
||||
c.write("__amber_lss ")
|
||||
case gt.GTR:
|
||||
c.write("__amber_gtr ")
|
||||
case gt.LEQ:
|
||||
c.write("__amber_gtr ")
|
||||
negate = true
|
||||
case gt.GEQ:
|
||||
c.write("__amber_lss ")
|
||||
negate = true
|
||||
default:
|
||||
panic("Unexpected operator!")
|
||||
}
|
||||
|
||||
c.write(pop() + ` ` + pop() + `}}`)
|
||||
|
||||
if !negate {
|
||||
stack.PushFront(name)
|
||||
} else {
|
||||
negname := c.tempvar()
|
||||
c.write(`{{` + negname + ` := not ` + name + `}}`)
|
||||
stack.PushFront(negname)
|
||||
}
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
{
|
||||
ue := expr
|
||||
|
||||
exec(ue.X)
|
||||
|
||||
name := c.tempvar()
|
||||
c.write(`{{` + name + ` := `)
|
||||
|
||||
switch ue.Op {
|
||||
case gt.SUB:
|
||||
c.write("__amber_minus ")
|
||||
case gt.ADD:
|
||||
c.write("__amber_plus ")
|
||||
case gt.NOT:
|
||||
c.write("not ")
|
||||
default:
|
||||
panic("Unexpected operator!")
|
||||
}
|
||||
|
||||
c.write(pop() + `}}`)
|
||||
stack.PushFront(name)
|
||||
}
|
||||
case *ast.ParenExpr:
|
||||
exec(expr.X)
|
||||
case *ast.BasicLit:
|
||||
stack.PushFront(strings.Replace(expr.Value, dollar, "$", -1))
|
||||
case *ast.Ident:
|
||||
name := expr.Name
|
||||
if len(name) >= len(dollar) && name[:len(dollar)] == dollar {
|
||||
if name == dollar {
|
||||
stack.PushFront(`.`)
|
||||
} else {
|
||||
stack.PushFront(`$` + expr.Name[len(dollar):])
|
||||
}
|
||||
} else {
|
||||
stack.PushFront(`.` + expr.Name)
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
se := expr
|
||||
exec(se.X)
|
||||
x := pop()
|
||||
|
||||
if x == "." {
|
||||
x = ""
|
||||
}
|
||||
|
||||
name := c.tempvar()
|
||||
c.write(`{{` + name + ` := ` + x + `.` + se.Sel.Name + `}}`)
|
||||
stack.PushFront(name)
|
||||
case *ast.CallExpr:
|
||||
ce := expr
|
||||
|
||||
for i := len(ce.Args) - 1; i >= 0; i-- {
|
||||
exec(ce.Args[i])
|
||||
}
|
||||
|
||||
name := c.tempvar()
|
||||
builtin := false
|
||||
|
||||
if ident, ok := ce.Fun.(*ast.Ident); ok {
|
||||
for _, fname := range builtinFunctions {
|
||||
if fname == ident.Name {
|
||||
builtin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for fname, _ := range FuncMap {
|
||||
if fname == ident.Name {
|
||||
builtin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if builtin {
|
||||
stack.PushFront(ce.Fun.(*ast.Ident).Name)
|
||||
c.write(`{{` + name + ` := ` + pop())
|
||||
} else if se, ok := ce.Fun.(*ast.SelectorExpr); ok {
|
||||
exec(se.X)
|
||||
x := pop()
|
||||
|
||||
if x == "." {
|
||||
x = ""
|
||||
}
|
||||
stack.PushFront(se.Sel.Name)
|
||||
c.write(`{{` + name + ` := ` + x + `.` + pop())
|
||||
} else {
|
||||
exec(ce.Fun)
|
||||
c.write(`{{` + name + ` := call ` + pop())
|
||||
}
|
||||
|
||||
for i := 0; i < len(ce.Args); i++ {
|
||||
c.write(` `)
|
||||
c.write(pop())
|
||||
}
|
||||
|
||||
c.write(`}}`)
|
||||
|
||||
stack.PushFront(name)
|
||||
default:
|
||||
panic("Unable to parse expression. Unsupported: " + reflect.TypeOf(expr).String())
|
||||
}
|
||||
}
|
||||
|
||||
exec(outerexpr)
|
||||
return pop()
|
||||
}
|
||||
|
||||
func (c *Compiler) visitMixin(mixin *parser.Mixin) {
|
||||
c.mixins[mixin.Name] = mixin
|
||||
}
|
||||
|
||||
func (c *Compiler) visitMixinCall(mixinCall *parser.MixinCall) {
|
||||
mixin := c.mixins[mixinCall.Name]
|
||||
|
||||
switch {
|
||||
case mixin == nil:
|
||||
panic(fmt.Sprintf("unknown mixin %q", mixinCall.Name))
|
||||
|
||||
case len(mixinCall.Args) < len(mixin.Args):
|
||||
panic(fmt.Sprintf(
|
||||
"not enough arguments in call to mixin %q (have: %d, want: %d)",
|
||||
mixinCall.Name,
|
||||
len(mixinCall.Args),
|
||||
len(mixin.Args),
|
||||
))
|
||||
case len(mixinCall.Args) > len(mixin.Args):
|
||||
panic(fmt.Sprintf(
|
||||
"too many arguments in call to mixin %q (have: %d, want: %d)",
|
||||
mixinCall.Name,
|
||||
len(mixinCall.Args),
|
||||
len(mixin.Args),
|
||||
))
|
||||
}
|
||||
|
||||
for i, arg := range mixin.Args {
|
||||
c.write(fmt.Sprintf(`{{%s := %s}}`, arg, c.visitRawInterpolation(mixinCall.Args[i])))
|
||||
}
|
||||
c.visitBlock(mixin.Block)
|
||||
}
|
257
vendor/github.com/eknkc/amber/doc.go
generated
vendored
257
vendor/github.com/eknkc/amber/doc.go
generated
vendored
@ -1,257 +0,0 @@
|
||||
/*
|
||||
Package amber is an elegant templating engine for Go Programming Language.
|
||||
It is inspired from HAML and Jade.
|
||||
|
||||
Tags
|
||||
|
||||
A tag is simply a word:
|
||||
|
||||
html
|
||||
|
||||
is converted to
|
||||
|
||||
<html></html>
|
||||
|
||||
It is possible to add ID and CLASS attributes to tags:
|
||||
|
||||
div#main
|
||||
span.time
|
||||
|
||||
are converted to
|
||||
|
||||
<div id="main"></div>
|
||||
<span class="time"></span>
|
||||
|
||||
Any arbitrary attribute name / value pair can be added this way:
|
||||
|
||||
a[href="http://www.google.com"]
|
||||
|
||||
You can mix multiple attributes together
|
||||
|
||||
a#someid[href="/"][title="Main Page"].main.link Click Link
|
||||
|
||||
gets converted to
|
||||
|
||||
<a id="someid" class="main link" href="/" title="Main Page">Click Link</a>
|
||||
|
||||
It is also possible to define these attributes within the block of a tag
|
||||
|
||||
a
|
||||
#someid
|
||||
[href="/"]
|
||||
[title="Main Page"]
|
||||
.main
|
||||
.link
|
||||
| Click Link
|
||||
|
||||
Doctypes
|
||||
|
||||
To add a doctype, use `!!!` or `doctype` keywords:
|
||||
|
||||
!!! transitional
|
||||
// <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
or use `doctype`
|
||||
|
||||
doctype 5
|
||||
// <!DOCTYPE html>
|
||||
|
||||
Available options: `5`, `default`, `xml`, `transitional`, `strict`, `frameset`, `1.1`, `basic`, `mobile`
|
||||
|
||||
Tag Content
|
||||
|
||||
For single line tag text, you can just append the text after tag name:
|
||||
|
||||
p Testing!
|
||||
|
||||
would yield
|
||||
|
||||
<p>Testing!</p>
|
||||
|
||||
For multi line tag text, or nested tags, use indentation:
|
||||
|
||||
html
|
||||
head
|
||||
title Page Title
|
||||
body
|
||||
div#content
|
||||
p
|
||||
| This is a long page content
|
||||
| These lines are all part of the parent p
|
||||
|
||||
a[href="/"] Go To Main Page
|
||||
|
||||
Data
|
||||
|
||||
Input template data can be reached by key names directly. For example, assuming the template has been
|
||||
executed with following JSON data:
|
||||
|
||||
{
|
||||
"Name": "Ekin",
|
||||
"LastName": "Koc",
|
||||
"Repositories": [
|
||||
"amber",
|
||||
"dateformat"
|
||||
],
|
||||
"Avatar": "/images/ekin.jpg",
|
||||
"Friends": 17
|
||||
}
|
||||
|
||||
It is possible to interpolate fields using `#{}`
|
||||
|
||||
p Welcome #{Name}!
|
||||
|
||||
would print
|
||||
|
||||
<p>Welcome Ekin!</p>
|
||||
|
||||
Attributes can have field names as well
|
||||
|
||||
a[title=Name][href="/ekin.koc"]
|
||||
|
||||
would print
|
||||
|
||||
<a title="Ekin" href="/ekin.koc"></a>
|
||||
|
||||
Expressions
|
||||
|
||||
Amber can expand basic expressions. For example, it is possible to concatenate strings with + operator:
|
||||
|
||||
p Welcome #{Name + " " + LastName}
|
||||
|
||||
Arithmetic expressions are also supported:
|
||||
|
||||
p You need #{50 - Friends} more friends to reach 50!
|
||||
|
||||
Expressions can be used within attributes
|
||||
|
||||
img[alt=Name + " " + LastName][src=Avatar]
|
||||
|
||||
Variables
|
||||
|
||||
It is possible to define dynamic variables within templates,
|
||||
all variables must start with a $ character and can be assigned as in the following example:
|
||||
|
||||
div
|
||||
$fullname = Name + " " + LastName
|
||||
p Welcome #{$fullname}
|
||||
|
||||
If you need to access the supplied data itself (i.e. the object containing Name, LastName etc fields.) you can use `$` variable
|
||||
|
||||
p $.Name
|
||||
|
||||
Conditions
|
||||
|
||||
For conditional blocks, it is possible to use `if <expression>`
|
||||
|
||||
div
|
||||
if Friends > 10
|
||||
p You have more than 10 friends
|
||||
else if Friends > 5
|
||||
p You have more than 5 friends
|
||||
else
|
||||
p You need more friends
|
||||
|
||||
Again, it is possible to use arithmetic and boolean operators
|
||||
|
||||
div
|
||||
if Name == "Ekin" && LastName == "Koc"
|
||||
p Hey! I know you..
|
||||
|
||||
There is a special syntax for conditional attributes. Only block attributes can have conditions;
|
||||
|
||||
div
|
||||
.hasfriends ? Friends > 0
|
||||
|
||||
This would yield a div with `hasfriends` class only if the `Friends > 0` condition holds. It is
|
||||
perfectly fine to use the same method for other types of attributes:
|
||||
|
||||
div
|
||||
#foo ? Name == "Ekin"
|
||||
[bar=baz] ? len(Repositories) > 0
|
||||
|
||||
Iterations
|
||||
|
||||
It is possible to iterate over arrays and maps using `each`:
|
||||
|
||||
each $repo in Repositories
|
||||
p #{$repo}
|
||||
|
||||
would print
|
||||
|
||||
p amber
|
||||
p dateformat
|
||||
|
||||
It is also possible to iterate over values and indexes at the same time
|
||||
|
||||
each $i, $repo in Repositories
|
||||
p
|
||||
.even ? $i % 2 == 0
|
||||
.odd ? $i % 2 == 1
|
||||
|
||||
Includes
|
||||
|
||||
A template can include other templates using `include`:
|
||||
|
||||
a.amber
|
||||
p this is template a
|
||||
|
||||
b.amber
|
||||
p this is template b
|
||||
|
||||
c.amber
|
||||
div
|
||||
include a
|
||||
include b
|
||||
|
||||
gets compiled to
|
||||
|
||||
div
|
||||
p this is template a
|
||||
p this is template b
|
||||
|
||||
Inheritance
|
||||
|
||||
A template can inherit other templates. In order to inherit another template, an `extends` keyword should be used.
|
||||
Parent template can define several named blocks and child template can modify the blocks.
|
||||
|
||||
master.amber
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
block meta
|
||||
meta[name="description"][content="This is a great website"]
|
||||
|
||||
title
|
||||
block title
|
||||
| Default title
|
||||
body
|
||||
block content
|
||||
|
||||
subpage.amber
|
||||
extends master
|
||||
|
||||
block title
|
||||
| Some sub page!
|
||||
|
||||
block append meta
|
||||
// This will be added after the description meta tag. It is also possible
|
||||
// to prepend something to an existing block
|
||||
meta[name="keywords"][content="foo bar"]
|
||||
|
||||
block content
|
||||
div#main
|
||||
p Some content here
|
||||
|
||||
License
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 Ekin Koc <ekin@eknkc.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package amber
|
285
vendor/github.com/eknkc/amber/parser/nodes.go
generated
vendored
285
vendor/github.com/eknkc/amber/parser/nodes.go
generated
vendored
@ -1,285 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var selfClosingTags = [...]string{
|
||||
"meta",
|
||||
"img",
|
||||
"link",
|
||||
"input",
|
||||
"source",
|
||||
"area",
|
||||
"base",
|
||||
"col",
|
||||
"br",
|
||||
"hr",
|
||||
}
|
||||
|
||||
var doctypes = map[string]string{
|
||||
"5": `<!DOCTYPE html>`,
|
||||
"default": `<!DOCTYPE html>`,
|
||||
"xml": `<?xml version="1.0" encoding="utf-8" ?>`,
|
||||
"transitional": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">`,
|
||||
"strict": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">`,
|
||||
"frameset": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">`,
|
||||
"1.1": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">`,
|
||||
"basic": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">`,
|
||||
"mobile": `<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">`,
|
||||
}
|
||||
|
||||
type Node interface {
|
||||
Pos() SourcePosition
|
||||
}
|
||||
|
||||
type SourcePosition struct {
|
||||
LineNum int
|
||||
ColNum int
|
||||
TokenLength int
|
||||
Filename string
|
||||
}
|
||||
|
||||
func (s *SourcePosition) Pos() SourcePosition {
|
||||
return *s
|
||||
}
|
||||
|
||||
type Doctype struct {
|
||||
SourcePosition
|
||||
Value string
|
||||
}
|
||||
|
||||
func newDoctype(value string) *Doctype {
|
||||
dt := new(Doctype)
|
||||
dt.Value = value
|
||||
return dt
|
||||
}
|
||||
|
||||
func (d *Doctype) String() string {
|
||||
if defined := doctypes[d.Value]; len(defined) != 0 {
|
||||
return defined
|
||||
}
|
||||
|
||||
return `<!DOCTYPE ` + d.Value + `>`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
SourcePosition
|
||||
Value string
|
||||
Block *Block
|
||||
Silent bool
|
||||
}
|
||||
|
||||
func newComment(value string) *Comment {
|
||||
dt := new(Comment)
|
||||
dt.Value = value
|
||||
dt.Block = nil
|
||||
dt.Silent = false
|
||||
return dt
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
SourcePosition
|
||||
Value string
|
||||
Raw bool
|
||||
}
|
||||
|
||||
func newText(value string, raw bool) *Text {
|
||||
dt := new(Text)
|
||||
dt.Value = value
|
||||
dt.Raw = raw
|
||||
return dt
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
SourcePosition
|
||||
Children []Node
|
||||
}
|
||||
|
||||
func newBlock() *Block {
|
||||
block := new(Block)
|
||||
block.Children = make([]Node, 0)
|
||||
return block
|
||||
}
|
||||
|
||||
func (b *Block) push(node Node) {
|
||||
b.Children = append(b.Children, node)
|
||||
}
|
||||
|
||||
func (b *Block) pushFront(node Node) {
|
||||
b.Children = append([]Node{node}, b.Children...)
|
||||
}
|
||||
|
||||
func (b *Block) CanInline() bool {
|
||||
if len(b.Children) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
allText := true
|
||||
|
||||
for _, child := range b.Children {
|
||||
if txt, ok := child.(*Text); !ok || txt.Raw {
|
||||
allText = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return allText
|
||||
}
|
||||
|
||||
const (
|
||||
NamedBlockDefault = iota
|
||||
NamedBlockAppend
|
||||
NamedBlockPrepend
|
||||
)
|
||||
|
||||
type NamedBlock struct {
|
||||
Block
|
||||
Name string
|
||||
Modifier int
|
||||
}
|
||||
|
||||
func newNamedBlock(name string) *NamedBlock {
|
||||
bb := new(NamedBlock)
|
||||
bb.Name = name
|
||||
bb.Block.Children = make([]Node, 0)
|
||||
bb.Modifier = NamedBlockDefault
|
||||
return bb
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
SourcePosition
|
||||
Name string
|
||||
Value string
|
||||
IsRaw bool
|
||||
Condition string
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
SourcePosition
|
||||
Block *Block
|
||||
Name string
|
||||
IsInterpolated bool
|
||||
Attributes []Attribute
|
||||
}
|
||||
|
||||
func newTag(name string) *Tag {
|
||||
tag := new(Tag)
|
||||
tag.Block = nil
|
||||
tag.Name = name
|
||||
tag.Attributes = make([]Attribute, 0)
|
||||
tag.IsInterpolated = false
|
||||
return tag
|
||||
|
||||
}
|
||||
|
||||
func (t *Tag) IsSelfClosing() bool {
|
||||
for _, tag := range selfClosingTags {
|
||||
if tag == t.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tag) IsRawText() bool {
|
||||
return t.Name == "style" || t.Name == "script"
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
SourcePosition
|
||||
Positive *Block
|
||||
Negative *Block
|
||||
Expression string
|
||||
}
|
||||
|
||||
func newCondition(exp string) *Condition {
|
||||
cond := new(Condition)
|
||||
cond.Expression = exp
|
||||
return cond
|
||||
}
|
||||
|
||||
type Each struct {
|
||||
SourcePosition
|
||||
X string
|
||||
Y string
|
||||
Expression string
|
||||
Block *Block
|
||||
}
|
||||
|
||||
func newEach(exp string) *Each {
|
||||
each := new(Each)
|
||||
each.Expression = exp
|
||||
return each
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
SourcePosition
|
||||
X string
|
||||
Expression string
|
||||
}
|
||||
|
||||
func newAssignment(x, expression string) *Assignment {
|
||||
assgn := new(Assignment)
|
||||
assgn.X = x
|
||||
assgn.Expression = expression
|
||||
return assgn
|
||||
}
|
||||
|
||||
type Mixin struct {
|
||||
SourcePosition
|
||||
Block *Block
|
||||
Name string
|
||||
Args []string
|
||||
}
|
||||
|
||||
func newMixin(name, args string) *Mixin {
|
||||
mixin := new(Mixin)
|
||||
mixin.Name = name
|
||||
|
||||
delExp := regexp.MustCompile(`,\s`)
|
||||
mixin.Args = delExp.Split(args, -1)
|
||||
|
||||
for i := 0; i < len(mixin.Args); i++ {
|
||||
mixin.Args[i] = strings.TrimSpace(mixin.Args[i])
|
||||
if mixin.Args[i] == "" {
|
||||
mixin.Args = append(mixin.Args[:i], mixin.Args[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
return mixin
|
||||
}
|
||||
|
||||
type MixinCall struct {
|
||||
SourcePosition
|
||||
Name string
|
||||
Args []string
|
||||
}
|
||||
|
||||
func newMixinCall(name, args string) *MixinCall {
|
||||
mixinCall := new(MixinCall)
|
||||
mixinCall.Name = name
|
||||
|
||||
if args != "" {
|
||||
const t = "%s"
|
||||
quoteExp := regexp.MustCompile(`"(.*?)"`)
|
||||
delExp := regexp.MustCompile(`,\s`)
|
||||
|
||||
quotes := quoteExp.FindAllString(args, -1)
|
||||
replaced := quoteExp.ReplaceAllString(args, t)
|
||||
mixinCall.Args = delExp.Split(replaced, -1)
|
||||
|
||||
qi := 0
|
||||
for i, arg := range mixinCall.Args {
|
||||
if arg == t {
|
||||
mixinCall.Args[i] = quotes[qi]
|
||||
qi++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mixinCall
|
||||
}
|
482
vendor/github.com/eknkc/amber/parser/parser.go
generated
vendored
482
vendor/github.com/eknkc/amber/parser/parser.go
generated
vendored
@ -1,482 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
scanner *scanner
|
||||
filename string
|
||||
fs http.FileSystem
|
||||
currenttoken *token
|
||||
namedBlocks map[string]*NamedBlock
|
||||
parent *Parser
|
||||
result *Block
|
||||
}
|
||||
|
||||
func newParser(rdr io.Reader) *Parser {
|
||||
p := new(Parser)
|
||||
p.scanner = newScanner(rdr)
|
||||
p.namedBlocks = make(map[string]*NamedBlock)
|
||||
return p
|
||||
}
|
||||
|
||||
func StringParser(input string) (*Parser, error) {
|
||||
return newParser(bytes.NewReader([]byte(input))), nil
|
||||
}
|
||||
|
||||
func ByteParser(input []byte) (*Parser, error) {
|
||||
return newParser(bytes.NewReader(input)), nil
|
||||
}
|
||||
|
||||
func (p *Parser) SetFilename(filename string) {
|
||||
p.filename = filename
|
||||
}
|
||||
|
||||
func (p *Parser) SetVirtualFilesystem(fs http.FileSystem) {
|
||||
p.fs = fs
|
||||
}
|
||||
|
||||
func FileParser(filename string) (*Parser, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := newParser(bytes.NewReader(data))
|
||||
parser.filename = filename
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func VirtualFileParser(filename string, fs http.FileSystem) (*Parser, error) {
|
||||
file, err := fs.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := newParser(bytes.NewReader(data))
|
||||
parser.filename = filename
|
||||
parser.fs = fs
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() *Block {
|
||||
if p.result != nil {
|
||||
return p.result
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
|
||||
panic(r)
|
||||
}
|
||||
|
||||
pos := p.pos()
|
||||
|
||||
if len(pos.Filename) > 0 {
|
||||
panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
|
||||
} else {
|
||||
panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
block := newBlock()
|
||||
p.advance()
|
||||
|
||||
for {
|
||||
if p.currenttoken == nil || p.currenttoken.Kind == tokEOF {
|
||||
break
|
||||
}
|
||||
|
||||
if p.currenttoken.Kind == tokBlank {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
|
||||
block.push(p.parse())
|
||||
}
|
||||
|
||||
if p.parent != nil {
|
||||
p.parent.Parse()
|
||||
|
||||
for _, prev := range p.parent.namedBlocks {
|
||||
ours := p.namedBlocks[prev.Name]
|
||||
|
||||
if ours == nil {
|
||||
// Put a copy of the named block into current context, so that sub-templates can use the block
|
||||
p.namedBlocks[prev.Name] = prev
|
||||
continue
|
||||
}
|
||||
|
||||
top := findTopmostParentWithNamedBlock(p, prev.Name)
|
||||
nb := top.namedBlocks[prev.Name]
|
||||
switch ours.Modifier {
|
||||
case NamedBlockAppend:
|
||||
for i := 0; i < len(ours.Children); i++ {
|
||||
nb.push(ours.Children[i])
|
||||
}
|
||||
case NamedBlockPrepend:
|
||||
for i := len(ours.Children) - 1; i >= 0; i-- {
|
||||
nb.pushFront(ours.Children[i])
|
||||
}
|
||||
default:
|
||||
nb.Children = ours.Children
|
||||
}
|
||||
}
|
||||
|
||||
block = p.parent.result
|
||||
}
|
||||
|
||||
p.result = block
|
||||
return block
|
||||
}
|
||||
|
||||
func (p *Parser) pos() SourcePosition {
|
||||
pos := p.scanner.Pos()
|
||||
pos.Filename = p.filename
|
||||
return pos
|
||||
}
|
||||
|
||||
func (p *Parser) parseRelativeFile(filename string) *Parser {
|
||||
if len(p.filename) == 0 {
|
||||
panic("Unable to import or extend " + filename + " in a non filesystem based parser.")
|
||||
}
|
||||
|
||||
filename = filepath.Join(filepath.Dir(p.filename), filename)
|
||||
|
||||
if strings.IndexRune(filepath.Base(filename), '.') < 0 {
|
||||
filename = filename + ".amber"
|
||||
}
|
||||
|
||||
parser, err := FileParser(filename)
|
||||
if err != nil && p.fs != nil {
|
||||
parser, err = VirtualFileParser(filename, p.fs)
|
||||
}
|
||||
if err != nil {
|
||||
panic("Unable to read " + filename + ", Error: " + string(err.Error()))
|
||||
}
|
||||
|
||||
return parser
|
||||
}
|
||||
|
||||
func (p *Parser) parse() Node {
|
||||
switch p.currenttoken.Kind {
|
||||
case tokDoctype:
|
||||
return p.parseDoctype()
|
||||
case tokComment:
|
||||
return p.parseComment()
|
||||
case tokText:
|
||||
return p.parseText()
|
||||
case tokIf:
|
||||
return p.parseIf()
|
||||
case tokEach:
|
||||
return p.parseEach()
|
||||
case tokImport:
|
||||
return p.parseImport()
|
||||
case tokTag:
|
||||
return p.parseTag()
|
||||
case tokAssignment:
|
||||
return p.parseAssignment()
|
||||
case tokNamedBlock:
|
||||
return p.parseNamedBlock()
|
||||
case tokExtends:
|
||||
return p.parseExtends()
|
||||
case tokIndent:
|
||||
return p.parseBlock(nil)
|
||||
case tokMixin:
|
||||
return p.parseMixin()
|
||||
case tokMixinCall:
|
||||
return p.parseMixinCall()
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Unexpected token: %d", p.currenttoken.Kind))
|
||||
}
|
||||
|
||||
func (p *Parser) expect(typ rune) *token {
|
||||
if p.currenttoken.Kind != typ {
|
||||
panic("Unexpected token!")
|
||||
}
|
||||
curtok := p.currenttoken
|
||||
p.advance()
|
||||
return curtok
|
||||
}
|
||||
|
||||
func (p *Parser) advance() {
|
||||
p.currenttoken = p.scanner.Next()
|
||||
}
|
||||
|
||||
func (p *Parser) parseExtends() *Block {
|
||||
if p.parent != nil {
|
||||
panic("Unable to extend multiple parent templates.")
|
||||
}
|
||||
|
||||
tok := p.expect(tokExtends)
|
||||
parser := p.parseRelativeFile(tok.Value)
|
||||
parser.Parse()
|
||||
p.parent = parser
|
||||
return newBlock()
|
||||
}
|
||||
|
||||
func (p *Parser) parseBlock(parent Node) *Block {
|
||||
p.expect(tokIndent)
|
||||
block := newBlock()
|
||||
block.SourcePosition = p.pos()
|
||||
|
||||
for {
|
||||
if p.currenttoken == nil || p.currenttoken.Kind == tokEOF || p.currenttoken.Kind == tokOutdent {
|
||||
break
|
||||
}
|
||||
|
||||
if p.currenttoken.Kind == tokBlank {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
|
||||
if p.currenttoken.Kind == tokId ||
|
||||
p.currenttoken.Kind == tokClassName ||
|
||||
p.currenttoken.Kind == tokAttribute {
|
||||
|
||||
if tag, ok := parent.(*Tag); ok {
|
||||
attr := p.expect(p.currenttoken.Kind)
|
||||
cond := attr.Data["Condition"]
|
||||
|
||||
switch attr.Kind {
|
||||
case tokId:
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", attr.Value, true, cond})
|
||||
case tokClassName:
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", attr.Value, true, cond})
|
||||
case tokAttribute:
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", cond})
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
panic("Conditional attributes must be placed immediately within a parent tag.")
|
||||
}
|
||||
}
|
||||
|
||||
block.push(p.parse())
|
||||
}
|
||||
|
||||
p.expect(tokOutdent)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (p *Parser) parseIf() *Condition {
|
||||
tok := p.expect(tokIf)
|
||||
cnd := newCondition(tok.Value)
|
||||
cnd.SourcePosition = p.pos()
|
||||
|
||||
readmore:
|
||||
switch p.currenttoken.Kind {
|
||||
case tokIndent:
|
||||
cnd.Positive = p.parseBlock(cnd)
|
||||
goto readmore
|
||||
case tokElse:
|
||||
p.expect(tokElse)
|
||||
if p.currenttoken.Kind == tokIf {
|
||||
cnd.Negative = newBlock()
|
||||
cnd.Negative.push(p.parseIf())
|
||||
} else if p.currenttoken.Kind == tokIndent {
|
||||
cnd.Negative = p.parseBlock(cnd)
|
||||
} else {
|
||||
panic("Unexpected token!")
|
||||
}
|
||||
goto readmore
|
||||
}
|
||||
|
||||
return cnd
|
||||
}
|
||||
|
||||
func (p *Parser) parseEach() *Each {
|
||||
tok := p.expect(tokEach)
|
||||
ech := newEach(tok.Value)
|
||||
ech.SourcePosition = p.pos()
|
||||
ech.X = tok.Data["X"]
|
||||
ech.Y = tok.Data["Y"]
|
||||
|
||||
if p.currenttoken.Kind == tokIndent {
|
||||
ech.Block = p.parseBlock(ech)
|
||||
}
|
||||
|
||||
return ech
|
||||
}
|
||||
|
||||
func (p *Parser) parseImport() *Block {
|
||||
tok := p.expect(tokImport)
|
||||
node := p.parseRelativeFile(tok.Value).Parse()
|
||||
node.SourcePosition = p.pos()
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Parser) parseNamedBlock() *Block {
|
||||
tok := p.expect(tokNamedBlock)
|
||||
|
||||
if p.namedBlocks[tok.Value] != nil {
|
||||
panic("Multiple definitions of named blocks are not permitted. Block " + tok.Value + " has been re defined.")
|
||||
}
|
||||
|
||||
block := newNamedBlock(tok.Value)
|
||||
block.SourcePosition = p.pos()
|
||||
|
||||
if tok.Data["Modifier"] == "append" {
|
||||
block.Modifier = NamedBlockAppend
|
||||
} else if tok.Data["Modifier"] == "prepend" {
|
||||
block.Modifier = NamedBlockPrepend
|
||||
}
|
||||
|
||||
if p.currenttoken.Kind == tokIndent {
|
||||
block.Block = *(p.parseBlock(nil))
|
||||
}
|
||||
|
||||
p.namedBlocks[block.Name] = block
|
||||
|
||||
if block.Modifier == NamedBlockDefault {
|
||||
return &block.Block
|
||||
}
|
||||
|
||||
return newBlock()
|
||||
}
|
||||
|
||||
func (p *Parser) parseDoctype() *Doctype {
|
||||
tok := p.expect(tokDoctype)
|
||||
node := newDoctype(tok.Value)
|
||||
node.SourcePosition = p.pos()
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Parser) parseComment() *Comment {
|
||||
tok := p.expect(tokComment)
|
||||
cmnt := newComment(tok.Value)
|
||||
cmnt.SourcePosition = p.pos()
|
||||
cmnt.Silent = tok.Data["Mode"] == "silent"
|
||||
|
||||
if p.currenttoken.Kind == tokIndent {
|
||||
cmnt.Block = p.parseBlock(cmnt)
|
||||
}
|
||||
|
||||
return cmnt
|
||||
}
|
||||
|
||||
func (p *Parser) parseText() *Text {
|
||||
tok := p.expect(tokText)
|
||||
node := newText(tok.Value, tok.Data["Mode"] == "raw")
|
||||
node.SourcePosition = p.pos()
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Parser) parseAssignment() *Assignment {
|
||||
tok := p.expect(tokAssignment)
|
||||
node := newAssignment(tok.Data["X"], tok.Value)
|
||||
node.SourcePosition = p.pos()
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Parser) parseTag() *Tag {
|
||||
tok := p.expect(tokTag)
|
||||
tag := newTag(tok.Value)
|
||||
tag.SourcePosition = p.pos()
|
||||
|
||||
ensureBlock := func() {
|
||||
if tag.Block == nil {
|
||||
tag.Block = newBlock()
|
||||
}
|
||||
}
|
||||
|
||||
readmore:
|
||||
switch p.currenttoken.Kind {
|
||||
case tokIndent:
|
||||
if tag.IsRawText() {
|
||||
p.scanner.readRaw = true
|
||||
}
|
||||
|
||||
block := p.parseBlock(tag)
|
||||
if tag.Block == nil {
|
||||
tag.Block = block
|
||||
} else {
|
||||
for _, c := range block.Children {
|
||||
tag.Block.push(c)
|
||||
}
|
||||
}
|
||||
case tokId:
|
||||
id := p.expect(tokId)
|
||||
if len(id.Data["Condition"]) > 0 {
|
||||
panic("Conditional attributes must be placed in a block within a tag.")
|
||||
}
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", id.Value, true, ""})
|
||||
goto readmore
|
||||
case tokClassName:
|
||||
cls := p.expect(tokClassName)
|
||||
if len(cls.Data["Condition"]) > 0 {
|
||||
panic("Conditional attributes must be placed in a block within a tag.")
|
||||
}
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", cls.Value, true, ""})
|
||||
goto readmore
|
||||
case tokAttribute:
|
||||
attr := p.expect(tokAttribute)
|
||||
if len(attr.Data["Condition"]) > 0 {
|
||||
panic("Conditional attributes must be placed in a block within a tag.")
|
||||
}
|
||||
tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", ""})
|
||||
goto readmore
|
||||
case tokText:
|
||||
if p.currenttoken.Data["Mode"] != "piped" {
|
||||
ensureBlock()
|
||||
tag.Block.pushFront(p.parseText())
|
||||
goto readmore
|
||||
}
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
func (p *Parser) parseMixin() *Mixin {
|
||||
tok := p.expect(tokMixin)
|
||||
mixin := newMixin(tok.Value, tok.Data["Args"])
|
||||
mixin.SourcePosition = p.pos()
|
||||
|
||||
if p.currenttoken.Kind == tokIndent {
|
||||
mixin.Block = p.parseBlock(mixin)
|
||||
}
|
||||
|
||||
return mixin
|
||||
}
|
||||
|
||||
func (p *Parser) parseMixinCall() *MixinCall {
|
||||
tok := p.expect(tokMixinCall)
|
||||
mixinCall := newMixinCall(tok.Value, tok.Data["Args"])
|
||||
mixinCall.SourcePosition = p.pos()
|
||||
return mixinCall
|
||||
}
|
||||
|
||||
func findTopmostParentWithNamedBlock(p *Parser, name string) *Parser {
|
||||
top := p
|
||||
|
||||
for {
|
||||
if top.namedBlocks[name] == nil {
|
||||
return nil
|
||||
}
|
||||
if top.parent == nil {
|
||||
return top
|
||||
}
|
||||
if top.parent.namedBlocks[name] != nil {
|
||||
top = top.parent
|
||||
} else {
|
||||
return top
|
||||
}
|
||||
}
|
||||
}
|
501
vendor/github.com/eknkc/amber/parser/scanner.go
generated
vendored
501
vendor/github.com/eknkc/amber/parser/scanner.go
generated
vendored
@ -1,501 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
tokEOF = -(iota + 1)
|
||||
tokDoctype
|
||||
tokComment
|
||||
tokIndent
|
||||
tokOutdent
|
||||
tokBlank
|
||||
tokId
|
||||
tokClassName
|
||||
tokTag
|
||||
tokText
|
||||
tokAttribute
|
||||
tokIf
|
||||
tokElse
|
||||
tokEach
|
||||
tokAssignment
|
||||
tokImport
|
||||
tokNamedBlock
|
||||
tokExtends
|
||||
tokMixin
|
||||
tokMixinCall
|
||||
)
|
||||
|
||||
const (
|
||||
scnNewLine = iota
|
||||
scnLine
|
||||
scnEOF
|
||||
)
|
||||
|
||||
type scanner struct {
|
||||
reader *bufio.Reader
|
||||
indentStack *list.List
|
||||
stash *list.List
|
||||
|
||||
state int32
|
||||
buffer string
|
||||
|
||||
line int
|
||||
col int
|
||||
lastTokenLine int
|
||||
lastTokenCol int
|
||||
lastTokenSize int
|
||||
|
||||
readRaw bool
|
||||
}
|
||||
|
||||
type token struct {
|
||||
Kind rune
|
||||
Value string
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
func newScanner(r io.Reader) *scanner {
|
||||
s := new(scanner)
|
||||
s.reader = bufio.NewReader(r)
|
||||
s.indentStack = list.New()
|
||||
s.stash = list.New()
|
||||
s.state = scnNewLine
|
||||
s.line = -1
|
||||
s.col = 0
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *scanner) Pos() SourcePosition {
|
||||
return SourcePosition{s.lastTokenLine + 1, s.lastTokenCol + 1, s.lastTokenSize, ""}
|
||||
}
|
||||
|
||||
// Returns next token found in buffer
|
||||
func (s *scanner) Next() *token {
|
||||
if s.readRaw {
|
||||
s.readRaw = false
|
||||
return s.NextRaw()
|
||||
}
|
||||
|
||||
s.ensureBuffer()
|
||||
|
||||
if stashed := s.stash.Front(); stashed != nil {
|
||||
tok := stashed.Value.(*token)
|
||||
s.stash.Remove(stashed)
|
||||
return tok
|
||||
}
|
||||
|
||||
switch s.state {
|
||||
case scnEOF:
|
||||
if outdent := s.indentStack.Back(); outdent != nil {
|
||||
s.indentStack.Remove(outdent)
|
||||
return &token{tokOutdent, "", nil}
|
||||
}
|
||||
|
||||
return &token{tokEOF, "", nil}
|
||||
case scnNewLine:
|
||||
s.state = scnLine
|
||||
|
||||
if tok := s.scanIndent(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
return s.Next()
|
||||
case scnLine:
|
||||
if tok := s.scanMixin(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanMixinCall(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanDoctype(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanCondition(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanEach(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanImport(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanExtends(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanBlock(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanAssignment(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanTag(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanId(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanClassName(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanAttribute(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanComment(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
|
||||
if tok := s.scanText(); tok != nil {
|
||||
return tok
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *scanner) NextRaw() *token {
|
||||
result := ""
|
||||
level := 0
|
||||
|
||||
for {
|
||||
s.ensureBuffer()
|
||||
|
||||
switch s.state {
|
||||
case scnEOF:
|
||||
return &token{tokText, result, map[string]string{"Mode": "raw"}}
|
||||
case scnNewLine:
|
||||
s.state = scnLine
|
||||
|
||||
if tok := s.scanIndent(); tok != nil {
|
||||
if tok.Kind == tokIndent {
|
||||
level++
|
||||
} else if tok.Kind == tokOutdent {
|
||||
level--
|
||||
} else {
|
||||
result = result + "\n"
|
||||
continue
|
||||
}
|
||||
|
||||
if level < 0 {
|
||||
s.stash.PushBack(&token{tokOutdent, "", nil})
|
||||
|
||||
if len(result) > 0 && result[len(result)-1] == '\n' {
|
||||
result = result[:len(result)-1]
|
||||
}
|
||||
|
||||
return &token{tokText, result, map[string]string{"Mode": "raw"}}
|
||||
}
|
||||
}
|
||||
case scnLine:
|
||||
if len(result) > 0 {
|
||||
result = result + "\n"
|
||||
}
|
||||
for i := 0; i < level; i++ {
|
||||
result += "\t"
|
||||
}
|
||||
result = result + s.buffer
|
||||
s.consume(len(s.buffer))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxIndent = regexp.MustCompile(`^(\s+)`)
|
||||
|
||||
func (s *scanner) scanIndent() *token {
|
||||
if len(s.buffer) == 0 {
|
||||
return &token{tokBlank, "", nil}
|
||||
}
|
||||
|
||||
var head *list.Element
|
||||
for head = s.indentStack.Front(); head != nil; head = head.Next() {
|
||||
value := head.Value.(*regexp.Regexp)
|
||||
|
||||
if match := value.FindString(s.buffer); len(match) != 0 {
|
||||
s.consume(len(match))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newIndent := rgxIndent.FindString(s.buffer)
|
||||
|
||||
if len(newIndent) != 0 && head == nil {
|
||||
s.indentStack.PushBack(regexp.MustCompile(regexp.QuoteMeta(newIndent)))
|
||||
s.consume(len(newIndent))
|
||||
return &token{tokIndent, newIndent, nil}
|
||||
}
|
||||
|
||||
if len(newIndent) == 0 && head != nil {
|
||||
for head != nil {
|
||||
next := head.Next()
|
||||
s.indentStack.Remove(head)
|
||||
if next == nil {
|
||||
return &token{tokOutdent, "", nil}
|
||||
} else {
|
||||
s.stash.PushBack(&token{tokOutdent, "", nil})
|
||||
}
|
||||
head = next
|
||||
}
|
||||
}
|
||||
|
||||
if len(newIndent) != 0 && head != nil {
|
||||
panic("Mismatching indentation. Please use a coherent indent schema.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxDoctype = regexp.MustCompile(`^(!!!|doctype)\s*(.*)`)
|
||||
|
||||
func (s *scanner) scanDoctype() *token {
|
||||
if sm := rgxDoctype.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
if len(sm[2]) == 0 {
|
||||
sm[2] = "html"
|
||||
}
|
||||
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokDoctype, sm[2], nil}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxIf = regexp.MustCompile(`^if\s+(.+)$`)
|
||||
var rgxElse = regexp.MustCompile(`^else\s*`)
|
||||
|
||||
func (s *scanner) scanCondition() *token {
|
||||
if sm := rgxIf.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokIf, sm[1], nil}
|
||||
}
|
||||
|
||||
if sm := rgxElse.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokElse, "", nil}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxEach = regexp.MustCompile(`^each\s+(\$[\w0-9\-_]*)(?:\s*,\s*(\$[\w0-9\-_]*))?\s+in\s+(.+)$`)
|
||||
|
||||
func (s *scanner) scanEach() *token {
|
||||
if sm := rgxEach.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokEach, sm[3], map[string]string{"X": sm[1], "Y": sm[2]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxAssignment = regexp.MustCompile(`^(\$[\w0-9\-_]*)?\s*=\s*(.+)$`)
|
||||
|
||||
func (s *scanner) scanAssignment() *token {
|
||||
if sm := rgxAssignment.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokAssignment, sm[2], map[string]string{"X": sm[1]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxComment = regexp.MustCompile(`^\/\/(-)?\s*(.*)$`)
|
||||
|
||||
func (s *scanner) scanComment() *token {
|
||||
if sm := rgxComment.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
mode := "embed"
|
||||
if len(sm[1]) != 0 {
|
||||
mode = "silent"
|
||||
}
|
||||
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokComment, sm[2], map[string]string{"Mode": mode}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxId = regexp.MustCompile(`^#([\w-]+)(?:\s*\?\s*(.*)$)?`)
|
||||
|
||||
func (s *scanner) scanId() *token {
|
||||
if sm := rgxId.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokId, sm[1], map[string]string{"Condition": sm[2]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxClassName = regexp.MustCompile(`^\.([\w-]+)(?:\s*\?\s*(.*)$)?`)
|
||||
|
||||
func (s *scanner) scanClassName() *token {
|
||||
if sm := rgxClassName.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokClassName, sm[1], map[string]string{"Condition": sm[2]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxAttribute = regexp.MustCompile(`^\[([\w\-:@\.]+)\s*(?:=\s*(\"([^\"\\]*)\"|([^\]]+)))?\](?:\s*\?\s*(.*)$)?`)
|
||||
|
||||
func (s *scanner) scanAttribute() *token {
|
||||
if sm := rgxAttribute.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
|
||||
if len(sm[3]) != 0 || sm[2] == "" {
|
||||
return &token{tokAttribute, sm[1], map[string]string{"Content": sm[3], "Mode": "raw", "Condition": sm[5]}}
|
||||
}
|
||||
|
||||
return &token{tokAttribute, sm[1], map[string]string{"Content": sm[4], "Mode": "expression", "Condition": sm[5]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxImport = regexp.MustCompile(`^import\s+([0-9a-zA-Z_\-\. \/]*)$`)
|
||||
|
||||
func (s *scanner) scanImport() *token {
|
||||
if sm := rgxImport.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokImport, sm[1], nil}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxExtends = regexp.MustCompile(`^extends\s+([0-9a-zA-Z_\-\. \/]*)$`)
|
||||
|
||||
func (s *scanner) scanExtends() *token {
|
||||
if sm := rgxExtends.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokExtends, sm[1], nil}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxBlock = regexp.MustCompile(`^block\s+(?:(append|prepend)\s+)?([0-9a-zA-Z_\-\. \/]*)$`)
|
||||
|
||||
func (s *scanner) scanBlock() *token {
|
||||
if sm := rgxBlock.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokNamedBlock, sm[2], map[string]string{"Modifier": sm[1]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxTag = regexp.MustCompile(`^(\w[-:\w]*)`)
|
||||
|
||||
func (s *scanner) scanTag() *token {
|
||||
if sm := rgxTag.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokTag, sm[1], nil}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxMixin = regexp.MustCompile(`^mixin ([a-zA-Z_-]+\w*)(\(((\$\w*(,\s)?)*)\))?$`)
|
||||
|
||||
func (s *scanner) scanMixin() *token {
|
||||
if sm := rgxMixin.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokMixin, sm[1], map[string]string{"Args": sm[3]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxMixinCall = regexp.MustCompile(`^\+([A-Za-z_-]+\w*)(\((.+(,\s)?)*\))?$`)
|
||||
|
||||
func (s *scanner) scanMixinCall() *token {
|
||||
if sm := rgxMixinCall.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
return &token{tokMixinCall, sm[1], map[string]string{"Args": sm[3]}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rgxText = regexp.MustCompile(`^(\|)? ?(.*)$`)
|
||||
|
||||
func (s *scanner) scanText() *token {
|
||||
if sm := rgxText.FindStringSubmatch(s.buffer); len(sm) != 0 {
|
||||
s.consume(len(sm[0]))
|
||||
|
||||
mode := "inline"
|
||||
if sm[1] == "|" {
|
||||
mode = "piped"
|
||||
}
|
||||
|
||||
return &token{tokText, sm[2], map[string]string{"Mode": mode}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Moves position forward, and removes beginning of s.buffer (len bytes)
|
||||
func (s *scanner) consume(runes int) {
|
||||
if len(s.buffer) < runes {
|
||||
panic(fmt.Sprintf("Unable to consume %d runes from buffer.", runes))
|
||||
}
|
||||
|
||||
s.lastTokenLine = s.line
|
||||
s.lastTokenCol = s.col
|
||||
s.lastTokenSize = runes
|
||||
|
||||
s.buffer = s.buffer[runes:]
|
||||
s.col += runes
|
||||
}
|
||||
|
||||
// Reads string into s.buffer
|
||||
func (s *scanner) ensureBuffer() {
|
||||
if len(s.buffer) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := s.reader.ReadString('\n')
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
} else if err != nil && len(buf) == 0 {
|
||||
s.state = scnEOF
|
||||
} else {
|
||||
// endline "LF only" or "\n" use Unix, Linux, modern MacOS X, FreeBSD, BeOS, RISC OS
|
||||
if buf[len(buf)-1] == '\n' {
|
||||
buf = buf[:len(buf)-1]
|
||||
}
|
||||
// endline "CR+LF" or "\r\n" use internet protocols, DEC RT-11, Windows, CP/M, MS-DOS, OS/2, Symbian OS
|
||||
if len(buf) > 0 && buf[len(buf)-1] == '\r' {
|
||||
buf = buf[:len(buf)-1]
|
||||
}
|
||||
|
||||
s.state = scnNewLine
|
||||
s.buffer = buf
|
||||
s.line += 1
|
||||
s.col = 0
|
||||
}
|
||||
}
|
287
vendor/github.com/eknkc/amber/runtime.go
generated
vendored
287
vendor/github.com/eknkc/amber/runtime.go
generated
vendored
@ -1,287 +0,0 @@
|
||||
package amber
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var FuncMap = template.FuncMap{
|
||||
"__amber_add": runtime_add,
|
||||
"__amber_sub": runtime_sub,
|
||||
"__amber_mul": runtime_mul,
|
||||
"__amber_quo": runtime_quo,
|
||||
"__amber_rem": runtime_rem,
|
||||
"__amber_minus": runtime_minus,
|
||||
"__amber_plus": runtime_plus,
|
||||
"__amber_eql": runtime_eql,
|
||||
"__amber_gtr": runtime_gtr,
|
||||
"__amber_lss": runtime_lss,
|
||||
|
||||
"json": runtime_json,
|
||||
"unescaped": runtime_unescaped,
|
||||
}
|
||||
|
||||
func runtime_add(x, y interface{}) interface{} {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() + vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) + vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%d%s", vx.Int(), vy.String())
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() + float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() + vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%f%s", vx.Float(), vy.String())
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return fmt.Sprintf("%s%d", vx.String(), vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return fmt.Sprintf("%s%f", vx.String(), vy.Float())
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%s%s", vx.String(), vy.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_sub(x, y interface{}) interface{} {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() - vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) - vy.Float()
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() - float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() - vy.Float()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_mul(x, y interface{}) interface{} {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() * vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) * vy.Float()
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() * float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() * vy.Float()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_quo(x, y interface{}) interface{} {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() / vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) / vy.Float()
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() / float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() / vy.Float()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_rem(x, y interface{}) interface{} {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() % vy.Int()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_minus(x interface{}) interface{} {
|
||||
vx := reflect.ValueOf(x)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return -vx.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return -vx.Float()
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_plus(x interface{}) interface{} {
|
||||
vx := reflect.ValueOf(x)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return +vx.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return +vx.Float()
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
func runtime_eql(x, y interface{}) bool {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() == vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) == vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%d", vx.Int()) == vy.String()
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() == float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() == vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%f", vx.Float()) == vy.String()
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.String() == fmt.Sprintf("%d", vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.String() == fmt.Sprintf("%f", vy.Float())
|
||||
case reflect.String:
|
||||
return vx.String() == fmt.Sprintf("%s", vy.String())
|
||||
}
|
||||
}
|
||||
case reflect.Bool:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Bool() && vy.Int() != 0
|
||||
case reflect.Bool:
|
||||
return vx.Bool() == vy.Bool()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func runtime_lss(x, y interface{}) bool {
|
||||
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
|
||||
switch vx.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Int() < vy.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float64(vx.Int()) < vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%d", vx.Int()) < vy.String()
|
||||
}
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.Float() < float64(vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.Float() < vy.Float()
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("%f", vx.Float()) < vy.String()
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
switch vy.Kind() {
|
||||
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
|
||||
return vx.String() < fmt.Sprintf("%d", vy.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return vx.String() < fmt.Sprintf("%f", vy.Float())
|
||||
case reflect.String:
|
||||
return vx.String() < vy.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func runtime_gtr(x, y interface{}) bool {
|
||||
return !runtime_lss(x, y) && !runtime_eql(x, y)
|
||||
}
|
||||
|
||||
func runtime_json(x interface{}) (res string, err error) {
|
||||
bres, err := json.Marshal(x)
|
||||
res = string(bres)
|
||||
return
|
||||
}
|
||||
|
||||
func runtime_unescaped(x string) interface{} {
|
||||
return template.HTML(x)
|
||||
}
|
8
vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
8
vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
||||
*.out
|
||||
*.swp
|
||||
*.8
|
||||
*.6
|
||||
_obj
|
||||
_test*
|
||||
markdown
|
||||
tags
|
17
vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
17
vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
@ -1,17 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- tip
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go tool vet .
|
||||
- go test -v ./...
|
29
vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
29
vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
@ -1,29 +0,0 @@
|
||||
Blackfriday is distributed under the Simplified BSD License:
|
||||
|
||||
> Copyright © 2011 Russ Ross
|
||||
> All rights reserved.
|
||||
>
|
||||
> Redistribution and use in source and binary forms, with or without
|
||||
> modification, are permitted provided that the following conditions
|
||||
> are met:
|
||||
>
|
||||
> 1. Redistributions of source code must retain the above copyright
|
||||
> notice, this list of conditions and the following disclaimer.
|
||||
>
|
||||
> 2. 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 HOLDER 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.
|
335
vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
335
vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
@ -1,335 +0,0 @@
|
||||
Blackfriday
|
||||
[![Build Status][BuildV2SVG]][BuildV2URL]
|
||||
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
|
||||
===========
|
||||
|
||||
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
||||
is paranoid about its input (so you can safely feed it user-supplied
|
||||
data), it is fast, it supports common extensions (tables, smart
|
||||
punctuation substitutions, etc.), and it is safe for all utf-8
|
||||
(unicode) input.
|
||||
|
||||
HTML output is currently supported, along with Smartypants
|
||||
extensions.
|
||||
|
||||
It started as a translation from C of [Sundown][3].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Blackfriday is compatible with modern Go releases in module mode.
|
||||
With Go installed:
|
||||
|
||||
go get github.com/russross/blackfriday/v2
|
||||
|
||||
will resolve and add the package to the current development module,
|
||||
then build and install it. Alternatively, you can achieve the same
|
||||
if you import it in a package:
|
||||
|
||||
import "github.com/russross/blackfriday/v2"
|
||||
|
||||
and `go get` without parameters.
|
||||
|
||||
Legacy GOPATH mode is unsupported.
|
||||
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
||||
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
||||
documentation is available at
|
||||
https://pkg.go.dev/github.com/russross/blackfriday/v2.
|
||||
|
||||
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
|
||||
|
||||
Version 2 offers a number of improvements over v1:
|
||||
|
||||
* Cleaned up API
|
||||
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
|
||||
the document
|
||||
* Latest bug fixes
|
||||
* Flexibility to easily add your own rendering extensions
|
||||
|
||||
Potential drawbacks:
|
||||
|
||||
* Our benchmarks show v2 to be slightly slower than v1. Currently in the
|
||||
ballpark of around 15%.
|
||||
* API breakage. If you can't afford modifying your code to adhere to the new API
|
||||
and don't care too much about the new features, v2 is probably not for you.
|
||||
* Several bug fixes are trailing behind and still need to be forward-ported to
|
||||
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
||||
tracking.
|
||||
|
||||
If you are still interested in the legacy `v1`, you can import it from
|
||||
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
|
||||
here: https://pkg.go.dev/github.com/russross/blackfriday.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
For the most sensible markdown processing, it is as simple as getting your input
|
||||
into a byte slice and calling:
|
||||
|
||||
```go
|
||||
output := blackfriday.Run(input)
|
||||
```
|
||||
|
||||
Your input will be parsed and the output rendered with a set of most popular
|
||||
extensions enabled. If you want the most basic feature set, corresponding with
|
||||
the bare Markdown specification, use:
|
||||
|
||||
```go
|
||||
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
|
||||
```
|
||||
|
||||
### Sanitize untrusted content
|
||||
|
||||
Blackfriday itself does nothing to protect against malicious content. If you are
|
||||
dealing with user-supplied markdown, we recommend running Blackfriday's output
|
||||
through HTML sanitizer such as [Bluemonday][5].
|
||||
|
||||
Here's an example of simple usage of Blackfriday together with Bluemonday:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// ...
|
||||
unsafe := blackfriday.Run(input)
|
||||
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
|
||||
```
|
||||
|
||||
### Custom options
|
||||
|
||||
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
||||
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
||||
|
||||
### `blackfriday-tool`
|
||||
|
||||
You can also check out `blackfriday-tool` for a more complete example
|
||||
of how to use it. Download and install it using:
|
||||
|
||||
go get github.com/russross/blackfriday-tool
|
||||
|
||||
This is a simple command-line tool that allows you to process a
|
||||
markdown file using a standalone program. You can also browse the
|
||||
source directly on github if you are just looking for some example
|
||||
code:
|
||||
|
||||
* <https://github.com/russross/blackfriday-tool>
|
||||
|
||||
Note that if you have not already done so, installing
|
||||
`blackfriday-tool` will be sufficient to download and install
|
||||
blackfriday in addition to the tool itself. The tool binary will be
|
||||
installed in `$GOPATH/bin`. This is a statically-linked binary that
|
||||
can be copied to wherever you need it without worrying about
|
||||
dependencies and library versions.
|
||||
|
||||
### Sanitized anchor names
|
||||
|
||||
Blackfriday includes an algorithm for creating sanitized anchor names
|
||||
corresponding to a given input text. This algorithm is used to create
|
||||
anchors for headings when `AutoHeadingIDs` extension is enabled. The
|
||||
algorithm has a specification, so that other packages can create
|
||||
compatible anchor names and links to those anchors.
|
||||
|
||||
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
|
||||
|
||||
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
|
||||
create compatible links to the anchor names generated by blackfriday.
|
||||
This algorithm is also implemented in a small standalone package at
|
||||
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
|
||||
that want a small package and don't need full functionality of blackfriday.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
All features of Sundown are supported, including:
|
||||
|
||||
* **Compatibility**. The Markdown v1.0.3 test suite passes with
|
||||
the `--tidy` option. Without `--tidy`, the differences are
|
||||
mostly in whitespace and entity escaping, where blackfriday is
|
||||
more consistent and cleaner.
|
||||
|
||||
* **Common extensions**, including table support, fenced code
|
||||
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
|
||||
|
||||
* **Safety**. Blackfriday is paranoid when parsing, making it safe
|
||||
to feed untrusted user input without fear of bad things
|
||||
happening. The test suite stress tests this and there are no
|
||||
known inputs that make it crash. If you find one, please let me
|
||||
know and send me the input that does it.
|
||||
|
||||
NOTE: "safety" in this context means *runtime safety only*. In order to
|
||||
protect yourself against JavaScript injection in untrusted content, see
|
||||
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
|
||||
|
||||
* **Fast processing**. It is fast enough to render on-demand in
|
||||
most web applications without having to cache the output.
|
||||
|
||||
* **Thread safety**. You can run multiple parsers in different
|
||||
goroutines without ill effect. There is no dependence on global
|
||||
shared state.
|
||||
|
||||
* **Minimal dependencies**. Blackfriday only depends on standard
|
||||
library packages in Go. The source code is pretty
|
||||
self-contained, so it is easy to add to any project, including
|
||||
Google App Engine projects.
|
||||
|
||||
* **Standards compliant**. Output successfully validates using the
|
||||
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
|
||||
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
In addition to the standard markdown syntax, this package
|
||||
implements the following extensions:
|
||||
|
||||
* **Intra-word emphasis supression**. The `_` character is
|
||||
commonly used inside words when discussing code, so having
|
||||
markdown interpret it as an emphasis command is usually the
|
||||
wrong thing. Blackfriday lets you treat all emphasis markers as
|
||||
normal characters when they occur inside a word.
|
||||
|
||||
* **Tables**. Tables can be created by drawing them in the input
|
||||
using a simple syntax:
|
||||
|
||||
```
|
||||
Name | Age
|
||||
--------|------
|
||||
Bob | 27
|
||||
Alice | 23
|
||||
```
|
||||
|
||||
* **Fenced code blocks**. In addition to the normal 4-space
|
||||
indentation to mark code blocks, you can explicitly mark them
|
||||
and supply a language (to make syntax highlighting simple). Just
|
||||
mark it like this:
|
||||
|
||||
```go
|
||||
func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
You can use 3 or more backticks to mark the beginning of the
|
||||
block, and the same number to mark the end of the block.
|
||||
|
||||
To preserve classes of fenced code blocks while using the bluemonday
|
||||
HTML sanitizer, use the following policy:
|
||||
|
||||
```go
|
||||
p := bluemonday.UGCPolicy()
|
||||
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
|
||||
html := p.SanitizeBytes(unsafe)
|
||||
```
|
||||
|
||||
* **Definition lists**. A simple definition list is made of a single-line
|
||||
term followed by a colon and the definition for that term.
|
||||
|
||||
Cat
|
||||
: Fluffy animal everyone likes
|
||||
|
||||
Internet
|
||||
: Vector of transmission for pictures of cats
|
||||
|
||||
Terms must be separated from the previous definition by a blank line.
|
||||
|
||||
* **Footnotes**. A marker in the text that will become a superscript number;
|
||||
a footnote definition that will be placed in a list of footnotes at the
|
||||
end of the document. A footnote looks like this:
|
||||
|
||||
This is a footnote.[^1]
|
||||
|
||||
[^1]: the footnote text.
|
||||
|
||||
* **Autolinking**. Blackfriday can find URLs that have not been
|
||||
explicitly marked as links and turn them into links.
|
||||
|
||||
* **Strikethrough**. Use two tildes (`~~`) to mark text that
|
||||
should be crossed out.
|
||||
|
||||
* **Hard line breaks**. With this extension enabled newlines in the input
|
||||
translate into line breaks in the output. This extension is off by default.
|
||||
|
||||
* **Smart quotes**. Smartypants-style punctuation substitution is
|
||||
supported, turning normal double- and single-quote marks into
|
||||
curly quotes, etc.
|
||||
|
||||
* **LaTeX-style dash parsing** is an additional option, where `--`
|
||||
is translated into `–`, and `---` is translated into
|
||||
`—`. This differs from most smartypants processors, which
|
||||
turn a single hyphen into an ndash and a double hyphen into an
|
||||
mdash.
|
||||
|
||||
* **Smart fractions**, where anything that looks like a fraction
|
||||
is translated into suitable HTML (instead of just a few special
|
||||
cases like most smartypant processors). For example, `4/5`
|
||||
becomes `<sup>4</sup>⁄<sub>5</sub>`, which renders as
|
||||
<sup>4</sup>⁄<sub>5</sub>.
|
||||
|
||||
|
||||
Other renderers
|
||||
---------------
|
||||
|
||||
Blackfriday is structured to allow alternative rendering engines. Here
|
||||
are a few of note:
|
||||
|
||||
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
|
||||
provides a GitHub Flavored Markdown renderer with fenced code block
|
||||
highlighting, clickable heading anchor links.
|
||||
|
||||
It's not customizable, and its goal is to produce HTML output
|
||||
equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
|
||||
except the rendering is performed locally.
|
||||
|
||||
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||
but for markdown.
|
||||
|
||||
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
|
||||
renders output as LaTeX.
|
||||
|
||||
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
|
||||
integration with the [Chroma](https://github.com/alecthomas/chroma) code
|
||||
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
|
||||
provides a drop-in renderer ready to use with Blackfriday, as well as
|
||||
options and means for further customization.
|
||||
|
||||
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
||||
|
||||
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
* More unit testing
|
||||
* Improve Unicode support. It does not understand all Unicode
|
||||
rules (about what constitutes a letter, a punctuation symbol,
|
||||
etc.), so it may fail to detect word boundaries correctly in
|
||||
some instances. It is safe on all UTF-8 input.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)
|
||||
|
||||
|
||||
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
||||
[2]: https://golang.org/ "Go Language"
|
||||
[3]: https://github.com/vmg/sundown "Sundown"
|
||||
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
|
||||
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
||||
|
||||
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
|
||||
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
|
||||
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
|
||||
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
|
1612
vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
1612
vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
File diff suppressed because it is too large
Load Diff
46
vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
46
vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
@ -1,46 +0,0 @@
|
||||
// Package blackfriday is a markdown processor.
|
||||
//
|
||||
// It translates plain text with simple formatting rules into an AST, which can
|
||||
// then be further processed to HTML (provided by Blackfriday itself) or other
|
||||
// formats (provided by the community).
|
||||
//
|
||||
// The simplest way to invoke Blackfriday is to call the Run function. It will
|
||||
// take a text input and produce a text output in HTML (or other format).
|
||||
//
|
||||
// A slightly more sophisticated way to use Blackfriday is to create a Markdown
|
||||
// processor and to call Parse, which returns a syntax tree for the input
|
||||
// document. You can leverage Blackfriday's parsing for content extraction from
|
||||
// markdown documents. You can assign a custom renderer and set various options
|
||||
// to the Markdown processor.
|
||||
//
|
||||
// If you're interested in calling Blackfriday from command line, see
|
||||
// https://github.com/russross/blackfriday-tool.
|
||||
//
|
||||
// Sanitized Anchor Names
|
||||
//
|
||||
// Blackfriday includes an algorithm for creating sanitized anchor names
|
||||
// corresponding to a given input text. This algorithm is used to create
|
||||
// anchors for headings when AutoHeadingIDs extension is enabled. The
|
||||
// algorithm is specified below, so that other packages can create
|
||||
// compatible anchor names and links to those anchors.
|
||||
//
|
||||
// The algorithm iterates over the input text, interpreted as UTF-8,
|
||||
// one Unicode code point (rune) at a time. All runes that are letters (category L)
|
||||
// or numbers (category N) are considered valid characters. They are mapped to
|
||||
// lower case, and included in the output. All other runes are considered
|
||||
// invalid characters. Invalid characters that precede the first valid character,
|
||||
// as well as invalid character that follow the last valid character
|
||||
// are dropped completely. All other sequences of invalid characters
|
||||
// between two valid characters are replaced with a single dash character '-'.
|
||||
//
|
||||
// SanitizedAnchorName exposes this functionality, and can be used to
|
||||
// create compatible links to the anchor names generated by blackfriday.
|
||||
// This algorithm is also implemented in a small standalone package at
|
||||
// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
|
||||
// that want a small package and don't need full functionality of blackfriday.
|
||||
package blackfriday
|
||||
|
||||
// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
|
||||
// github.com/shurcooL/sanitized_anchor_name.
|
||||
// Otherwise, users of sanitized_anchor_name will get anchor names
|
||||
// that are incompatible with those generated by blackfriday.
|
2236
vendor/github.com/russross/blackfriday/v2/entities.go
generated
vendored
2236
vendor/github.com/russross/blackfriday/v2/entities.go
generated
vendored
File diff suppressed because it is too large
Load Diff
70
vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
70
vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
@ -1,70 +0,0 @@
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"html"
|
||||
"io"
|
||||
)
|
||||
|
||||
var htmlEscaper = [256][]byte{
|
||||
'&': []byte("&"),
|
||||
'<': []byte("<"),
|
||||
'>': []byte(">"),
|
||||
'"': []byte("""),
|
||||
}
|
||||
|
||||
func escapeHTML(w io.Writer, s []byte) {
|
||||
escapeEntities(w, s, false)
|
||||
}
|
||||
|
||||
func escapeAllHTML(w io.Writer, s []byte) {
|
||||
escapeEntities(w, s, true)
|
||||
}
|
||||
|
||||
func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) {
|
||||
var start, end int
|
||||
for end < len(s) {
|
||||
escSeq := htmlEscaper[s[end]]
|
||||
if escSeq != nil {
|
||||
isEntity, entityEnd := nodeIsEntity(s, end)
|
||||
if isEntity && !escapeValidEntities {
|
||||
w.Write(s[start : entityEnd+1])
|
||||
start = entityEnd + 1
|
||||
} else {
|
||||
w.Write(s[start:end])
|
||||
w.Write(escSeq)
|
||||
start = end + 1
|
||||
}
|
||||
}
|
||||
end++
|
||||
}
|
||||
if start < len(s) && end <= len(s) {
|
||||
w.Write(s[start:end])
|
||||
}
|
||||
}
|
||||
|
||||
func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) {
|
||||
isEntity = false
|
||||
endEntityPos = end + 1
|
||||
|
||||
if s[end] == '&' {
|
||||
for endEntityPos < len(s) {
|
||||
if s[endEntityPos] == ';' {
|
||||
if entities[string(s[end:endEntityPos+1])] {
|
||||
isEntity = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' {
|
||||
break
|
||||
}
|
||||
endEntityPos++
|
||||
}
|
||||
}
|
||||
|
||||
return isEntity, endEntityPos
|
||||
}
|
||||
|
||||
func escLink(w io.Writer, text []byte) {
|
||||
unesc := html.UnescapeString(string(text))
|
||||
escapeHTML(w, []byte(unesc))
|
||||
}
|
952
vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
952
vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
@ -1,952 +0,0 @@
|
||||
//
|
||||
// Blackfriday Markdown Processor
|
||||
// Available at http://github.com/russross/blackfriday
|
||||
//
|
||||
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||
// Distributed under the Simplified BSD License.
|
||||
// See README.md for details.
|
||||
//
|
||||
|
||||
//
|
||||
//
|
||||
// HTML rendering backend
|
||||
//
|
||||
//
|
||||
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTMLFlags control optional behavior of HTML renderer.
|
||||
type HTMLFlags int
|
||||
|
||||
// HTML renderer configuration options.
|
||||
const (
|
||||
HTMLFlagsNone HTMLFlags = 0
|
||||
SkipHTML HTMLFlags = 1 << iota // Skip preformatted HTML blocks
|
||||
SkipImages // Skip embedded images
|
||||
SkipLinks // Skip all links
|
||||
Safelink // Only link to trusted protocols
|
||||
NofollowLinks // Only link with rel="nofollow"
|
||||
NoreferrerLinks // Only link with rel="noreferrer"
|
||||
NoopenerLinks // Only link with rel="noopener"
|
||||
HrefTargetBlank // Add a blank target
|
||||
CompletePage // Generate a complete HTML page
|
||||
UseXHTML // Generate XHTML output instead of HTML
|
||||
FootnoteReturnLinks // Generate a link at the end of a footnote to return to the source
|
||||
Smartypants // Enable smart punctuation substitutions
|
||||
SmartypantsFractions // Enable smart fractions (with Smartypants)
|
||||
SmartypantsDashes // Enable smart dashes (with Smartypants)
|
||||
SmartypantsLatexDashes // Enable LaTeX-style dashes (with Smartypants)
|
||||
SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering
|
||||
SmartypantsQuotesNBSP // Enable « French guillemets » (with Smartypants)
|
||||
TOC // Generate a table of contents
|
||||
)
|
||||
|
||||
var (
|
||||
htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag)
|
||||
)
|
||||
|
||||
const (
|
||||
htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" +
|
||||
processingInstruction + "|" + declaration + "|" + cdata + ")"
|
||||
closeTag = "</" + tagName + "\\s*[>]"
|
||||
openTag = "<" + tagName + attribute + "*" + "\\s*/?>"
|
||||
attribute = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)"
|
||||
attributeValue = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")"
|
||||
attributeValueSpec = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")"
|
||||
attributeName = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
|
||||
cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
|
||||
declaration = "<![A-Z]+" + "\\s+[^>]*>"
|
||||
doubleQuotedValue = "\"[^\"]*\""
|
||||
htmlComment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
|
||||
processingInstruction = "[<][?].*?[?][>]"
|
||||
singleQuotedValue = "'[^']*'"
|
||||
tagName = "[A-Za-z][A-Za-z0-9-]*"
|
||||
unquotedValue = "[^\"'=<>`\\x00-\\x20]+"
|
||||
)
|
||||
|
||||
// HTMLRendererParameters is a collection of supplementary parameters tweaking
|
||||
// the behavior of various parts of HTML renderer.
|
||||
type HTMLRendererParameters struct {
|
||||
// Prepend this text to each relative URL.
|
||||
AbsolutePrefix string
|
||||
// Add this text to each footnote anchor, to ensure uniqueness.
|
||||
FootnoteAnchorPrefix string
|
||||
// Show this text inside the <a> tag for a footnote return link, if the
|
||||
// HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string
|
||||
// <sup>[return]</sup> is used.
|
||||
FootnoteReturnLinkContents string
|
||||
// If set, add this text to the front of each Heading ID, to ensure
|
||||
// uniqueness.
|
||||
HeadingIDPrefix string
|
||||
// If set, add this text to the back of each Heading ID, to ensure uniqueness.
|
||||
HeadingIDSuffix string
|
||||
// Increase heading levels: if the offset is 1, <h1> becomes <h2> etc.
|
||||
// Negative offset is also valid.
|
||||
// Resulting levels are clipped between 1 and 6.
|
||||
HeadingLevelOffset int
|
||||
|
||||
Title string // Document title (used if CompletePage is set)
|
||||
CSS string // Optional CSS file URL (used if CompletePage is set)
|
||||
Icon string // Optional icon file URL (used if CompletePage is set)
|
||||
|
||||
Flags HTMLFlags // Flags allow customizing this renderer's behavior
|
||||
}
|
||||
|
||||
// HTMLRenderer is a type that implements the Renderer interface for HTML output.
|
||||
//
|
||||
// Do not create this directly, instead use the NewHTMLRenderer function.
|
||||
type HTMLRenderer struct {
|
||||
HTMLRendererParameters
|
||||
|
||||
closeTag string // how to end singleton tags: either " />" or ">"
|
||||
|
||||
// Track heading IDs to prevent ID collision in a single generation.
|
||||
headingIDs map[string]int
|
||||
|
||||
lastOutputLen int
|
||||
disableTags int
|
||||
|
||||
sr *SPRenderer
|
||||
}
|
||||
|
||||
const (
|
||||
xhtmlClose = " />"
|
||||
htmlClose = ">"
|
||||
)
|
||||
|
||||
// NewHTMLRenderer creates and configures an HTMLRenderer object, which
|
||||
// satisfies the Renderer interface.
|
||||
func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||
// configure the rendering engine
|
||||
closeTag := htmlClose
|
||||
if params.Flags&UseXHTML != 0 {
|
||||
closeTag = xhtmlClose
|
||||
}
|
||||
|
||||
if params.FootnoteReturnLinkContents == "" {
|
||||
// U+FE0E is VARIATION SELECTOR-15.
|
||||
// It suppresses automatic emoji presentation of the preceding
|
||||
// U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS.
|
||||
params.FootnoteReturnLinkContents = "<span aria-label='Return'>↩\ufe0e</span>"
|
||||
}
|
||||
|
||||
return &HTMLRenderer{
|
||||
HTMLRendererParameters: params,
|
||||
|
||||
closeTag: closeTag,
|
||||
headingIDs: make(map[string]int),
|
||||
|
||||
sr: NewSmartypantsRenderer(params.Flags),
|
||||
}
|
||||
}
|
||||
|
||||
func isHTMLTag(tag []byte, tagname string) bool {
|
||||
found, _ := findHTMLTagPos(tag, tagname)
|
||||
return found
|
||||
}
|
||||
|
||||
// Look for a character, but ignore it when it's in any kind of quotes, it
|
||||
// might be JavaScript
|
||||
func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
|
||||
inSingleQuote := false
|
||||
inDoubleQuote := false
|
||||
inGraveQuote := false
|
||||
i := start
|
||||
for i < len(html) {
|
||||
switch {
|
||||
case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
|
||||
return i
|
||||
case html[i] == '\'':
|
||||
inSingleQuote = !inSingleQuote
|
||||
case html[i] == '"':
|
||||
inDoubleQuote = !inDoubleQuote
|
||||
case html[i] == '`':
|
||||
inGraveQuote = !inGraveQuote
|
||||
}
|
||||
i++
|
||||
}
|
||||
return start
|
||||
}
|
||||
|
||||
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
|
||||
i := 0
|
||||
if i < len(tag) && tag[0] != '<' {
|
||||
return false, -1
|
||||
}
|
||||
i++
|
||||
i = skipSpace(tag, i)
|
||||
|
||||
if i < len(tag) && tag[i] == '/' {
|
||||
i++
|
||||
}
|
||||
|
||||
i = skipSpace(tag, i)
|
||||
j := 0
|
||||
for ; i < len(tag); i, j = i+1, j+1 {
|
||||
if j >= len(tagname) {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.ToLower(string(tag[i]))[0] != tagname[j] {
|
||||
return false, -1
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(tag) {
|
||||
return false, -1
|
||||
}
|
||||
|
||||
rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>')
|
||||
if rightAngle >= i {
|
||||
return true, rightAngle
|
||||
}
|
||||
|
||||
return false, -1
|
||||
}
|
||||
|
||||
func skipSpace(tag []byte, i int) int {
|
||||
for i < len(tag) && isspace(tag[i]) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func isRelativeLink(link []byte) (yes bool) {
|
||||
// a tag begin with '#'
|
||||
if link[0] == '#' {
|
||||
return true
|
||||
}
|
||||
|
||||
// link begin with '/' but not '//', the second maybe a protocol relative link
|
||||
if len(link) >= 2 && link[0] == '/' && link[1] != '/' {
|
||||
return true
|
||||
}
|
||||
|
||||
// only the root '/'
|
||||
if len(link) == 1 && link[0] == '/' {
|
||||
return true
|
||||
}
|
||||
|
||||
// current directory : begin with "./"
|
||||
if bytes.HasPrefix(link, []byte("./")) {
|
||||
return true
|
||||
}
|
||||
|
||||
// parent directory : begin with "../"
|
||||
if bytes.HasPrefix(link, []byte("../")) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string {
|
||||
for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] {
|
||||
tmp := fmt.Sprintf("%s-%d", id, count+1)
|
||||
|
||||
if _, tmpFound := r.headingIDs[tmp]; !tmpFound {
|
||||
r.headingIDs[id] = count + 1
|
||||
id = tmp
|
||||
} else {
|
||||
id = id + "-1"
|
||||
}
|
||||
}
|
||||
|
||||
if _, found := r.headingIDs[id]; !found {
|
||||
r.headingIDs[id] = 0
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte {
|
||||
if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' {
|
||||
newDest := r.AbsolutePrefix
|
||||
if link[0] != '/' {
|
||||
newDest += "/"
|
||||
}
|
||||
newDest += string(link)
|
||||
return []byte(newDest)
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string {
|
||||
if isRelativeLink(link) {
|
||||
return attrs
|
||||
}
|
||||
val := []string{}
|
||||
if flags&NofollowLinks != 0 {
|
||||
val = append(val, "nofollow")
|
||||
}
|
||||
if flags&NoreferrerLinks != 0 {
|
||||
val = append(val, "noreferrer")
|
||||
}
|
||||
if flags&NoopenerLinks != 0 {
|
||||
val = append(val, "noopener")
|
||||
}
|
||||
if flags&HrefTargetBlank != 0 {
|
||||
attrs = append(attrs, "target=\"_blank\"")
|
||||
}
|
||||
if len(val) == 0 {
|
||||
return attrs
|
||||
}
|
||||
attr := fmt.Sprintf("rel=%q", strings.Join(val, " "))
|
||||
return append(attrs, attr)
|
||||
}
|
||||
|
||||
func isMailto(link []byte) bool {
|
||||
return bytes.HasPrefix(link, []byte("mailto:"))
|
||||
}
|
||||
|
||||
func needSkipLink(flags HTMLFlags, dest []byte) bool {
|
||||
if flags&SkipLinks != 0 {
|
||||
return true
|
||||
}
|
||||
return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest)
|
||||
}
|
||||
|
||||
func isSmartypantable(node *Node) bool {
|
||||
pt := node.Parent.Type
|
||||
return pt != Link && pt != CodeBlock && pt != Code
|
||||
}
|
||||
|
||||
func appendLanguageAttr(attrs []string, info []byte) []string {
|
||||
if len(info) == 0 {
|
||||
return attrs
|
||||
}
|
||||
endOfLang := bytes.IndexAny(info, "\t ")
|
||||
if endOfLang < 0 {
|
||||
endOfLang = len(info)
|
||||
}
|
||||
return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang]))
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) {
|
||||
w.Write(name)
|
||||
if len(attrs) > 0 {
|
||||
w.Write(spaceBytes)
|
||||
w.Write([]byte(strings.Join(attrs, " ")))
|
||||
}
|
||||
w.Write(gtBytes)
|
||||
r.lastOutputLen = 1
|
||||
}
|
||||
|
||||
func footnoteRef(prefix string, node *Node) []byte {
|
||||
urlFrag := prefix + string(slugify(node.Destination))
|
||||
anchor := fmt.Sprintf(`<a href="#fn:%s">%d</a>`, urlFrag, node.NoteID)
|
||||
return []byte(fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, urlFrag, anchor))
|
||||
}
|
||||
|
||||
func footnoteItem(prefix string, slug []byte) []byte {
|
||||
return []byte(fmt.Sprintf(`<li id="fn:%s%s">`, prefix, slug))
|
||||
}
|
||||
|
||||
func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte {
|
||||
const format = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
|
||||
return []byte(fmt.Sprintf(format, prefix, slug, returnLink))
|
||||
}
|
||||
|
||||
func itemOpenCR(node *Node) bool {
|
||||
if node.Prev == nil {
|
||||
return false
|
||||
}
|
||||
ld := node.Parent.ListData
|
||||
return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0
|
||||
}
|
||||
|
||||
func skipParagraphTags(node *Node) bool {
|
||||
grandparent := node.Parent.Parent
|
||||
if grandparent == nil || grandparent.Type != List {
|
||||
return false
|
||||
}
|
||||
tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0
|
||||
return grandparent.Type == List && tightOrTerm
|
||||
}
|
||||
|
||||
func cellAlignment(align CellAlignFlags) string {
|
||||
switch align {
|
||||
case TableAlignmentLeft:
|
||||
return "left"
|
||||
case TableAlignmentRight:
|
||||
return "right"
|
||||
case TableAlignmentCenter:
|
||||
return "center"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) out(w io.Writer, text []byte) {
|
||||
if r.disableTags > 0 {
|
||||
w.Write(htmlTagRe.ReplaceAll(text, []byte{}))
|
||||
} else {
|
||||
w.Write(text)
|
||||
}
|
||||
r.lastOutputLen = len(text)
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) cr(w io.Writer) {
|
||||
if r.lastOutputLen > 0 {
|
||||
r.out(w, nlBytes)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
nlBytes = []byte{'\n'}
|
||||
gtBytes = []byte{'>'}
|
||||
spaceBytes = []byte{' '}
|
||||
)
|
||||
|
||||
var (
|
||||
brTag = []byte("<br>")
|
||||
brXHTMLTag = []byte("<br />")
|
||||
emTag = []byte("<em>")
|
||||
emCloseTag = []byte("</em>")
|
||||
strongTag = []byte("<strong>")
|
||||
strongCloseTag = []byte("</strong>")
|
||||
delTag = []byte("<del>")
|
||||
delCloseTag = []byte("</del>")
|
||||
ttTag = []byte("<tt>")
|
||||
ttCloseTag = []byte("</tt>")
|
||||
aTag = []byte("<a")
|
||||
aCloseTag = []byte("</a>")
|
||||
preTag = []byte("<pre>")
|
||||
preCloseTag = []byte("</pre>")
|
||||
codeTag = []byte("<code>")
|
||||
codeCloseTag = []byte("</code>")
|
||||
pTag = []byte("<p>")
|
||||
pCloseTag = []byte("</p>")
|
||||
blockquoteTag = []byte("<blockquote>")
|
||||
blockquoteCloseTag = []byte("</blockquote>")
|
||||
hrTag = []byte("<hr>")
|
||||
hrXHTMLTag = []byte("<hr />")
|
||||
ulTag = []byte("<ul>")
|
||||
ulCloseTag = []byte("</ul>")
|
||||
olTag = []byte("<ol>")
|
||||
olCloseTag = []byte("</ol>")
|
||||
dlTag = []byte("<dl>")
|
||||
dlCloseTag = []byte("</dl>")
|
||||
liTag = []byte("<li>")
|
||||
liCloseTag = []byte("</li>")
|
||||
ddTag = []byte("<dd>")
|
||||
ddCloseTag = []byte("</dd>")
|
||||
dtTag = []byte("<dt>")
|
||||
dtCloseTag = []byte("</dt>")
|
||||
tableTag = []byte("<table>")
|
||||
tableCloseTag = []byte("</table>")
|
||||
tdTag = []byte("<td")
|
||||
tdCloseTag = []byte("</td>")
|
||||
thTag = []byte("<th")
|
||||
thCloseTag = []byte("</th>")
|
||||
theadTag = []byte("<thead>")
|
||||
theadCloseTag = []byte("</thead>")
|
||||
tbodyTag = []byte("<tbody>")
|
||||
tbodyCloseTag = []byte("</tbody>")
|
||||
trTag = []byte("<tr>")
|
||||
trCloseTag = []byte("</tr>")
|
||||
h1Tag = []byte("<h1")
|
||||
h1CloseTag = []byte("</h1>")
|
||||
h2Tag = []byte("<h2")
|
||||
h2CloseTag = []byte("</h2>")
|
||||
h3Tag = []byte("<h3")
|
||||
h3CloseTag = []byte("</h3>")
|
||||
h4Tag = []byte("<h4")
|
||||
h4CloseTag = []byte("</h4>")
|
||||
h5Tag = []byte("<h5")
|
||||
h5CloseTag = []byte("</h5>")
|
||||
h6Tag = []byte("<h6")
|
||||
h6CloseTag = []byte("</h6>")
|
||||
|
||||
footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")
|
||||
footnotesCloseDivBytes = []byte("\n</div>\n")
|
||||
)
|
||||
|
||||
func headingTagsFromLevel(level int) ([]byte, []byte) {
|
||||
if level <= 1 {
|
||||
return h1Tag, h1CloseTag
|
||||
}
|
||||
switch level {
|
||||
case 2:
|
||||
return h2Tag, h2CloseTag
|
||||
case 3:
|
||||
return h3Tag, h3CloseTag
|
||||
case 4:
|
||||
return h4Tag, h4CloseTag
|
||||
case 5:
|
||||
return h5Tag, h5CloseTag
|
||||
}
|
||||
return h6Tag, h6CloseTag
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
||||
if r.Flags&UseXHTML == 0 {
|
||||
r.out(w, hrTag)
|
||||
} else {
|
||||
r.out(w, hrXHTMLTag)
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNode is a default renderer of a single node of a syntax tree. For
|
||||
// block nodes it will be called twice: first time with entering=true, second
|
||||
// time with entering=false, so that it could know when it's working on an open
|
||||
// tag and when on close. It writes the result to w.
|
||||
//
|
||||
// The return value is a way to tell the calling walker to adjust its walk
|
||||
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||
// The typical behavior is to return GoToNext, which asks for the usual
|
||||
// traversal to the next node.
|
||||
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
||||
attrs := []string{}
|
||||
switch node.Type {
|
||||
case Text:
|
||||
if r.Flags&Smartypants != 0 {
|
||||
var tmp bytes.Buffer
|
||||
escapeHTML(&tmp, node.Literal)
|
||||
r.sr.Process(w, tmp.Bytes())
|
||||
} else {
|
||||
if node.Parent.Type == Link {
|
||||
escLink(w, node.Literal)
|
||||
} else {
|
||||
escapeHTML(w, node.Literal)
|
||||
}
|
||||
}
|
||||
case Softbreak:
|
||||
r.cr(w)
|
||||
// TODO: make it configurable via out(renderer.softbreak)
|
||||
case Hardbreak:
|
||||
if r.Flags&UseXHTML == 0 {
|
||||
r.out(w, brTag)
|
||||
} else {
|
||||
r.out(w, brXHTMLTag)
|
||||
}
|
||||
r.cr(w)
|
||||
case Emph:
|
||||
if entering {
|
||||
r.out(w, emTag)
|
||||
} else {
|
||||
r.out(w, emCloseTag)
|
||||
}
|
||||
case Strong:
|
||||
if entering {
|
||||
r.out(w, strongTag)
|
||||
} else {
|
||||
r.out(w, strongCloseTag)
|
||||
}
|
||||
case Del:
|
||||
if entering {
|
||||
r.out(w, delTag)
|
||||
} else {
|
||||
r.out(w, delCloseTag)
|
||||
}
|
||||
case HTMLSpan:
|
||||
if r.Flags&SkipHTML != 0 {
|
||||
break
|
||||
}
|
||||
r.out(w, node.Literal)
|
||||
case Link:
|
||||
// mark it but don't link it if it is not a safe link: no smartypants
|
||||
dest := node.LinkData.Destination
|
||||
if needSkipLink(r.Flags, dest) {
|
||||
if entering {
|
||||
r.out(w, ttTag)
|
||||
} else {
|
||||
r.out(w, ttCloseTag)
|
||||
}
|
||||
} else {
|
||||
if entering {
|
||||
dest = r.addAbsPrefix(dest)
|
||||
var hrefBuf bytes.Buffer
|
||||
hrefBuf.WriteString("href=\"")
|
||||
escLink(&hrefBuf, dest)
|
||||
hrefBuf.WriteByte('"')
|
||||
attrs = append(attrs, hrefBuf.String())
|
||||
if node.NoteID != 0 {
|
||||
r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node))
|
||||
break
|
||||
}
|
||||
attrs = appendLinkAttrs(attrs, r.Flags, dest)
|
||||
if len(node.LinkData.Title) > 0 {
|
||||
var titleBuff bytes.Buffer
|
||||
titleBuff.WriteString("title=\"")
|
||||
escapeHTML(&titleBuff, node.LinkData.Title)
|
||||
titleBuff.WriteByte('"')
|
||||
attrs = append(attrs, titleBuff.String())
|
||||
}
|
||||
r.tag(w, aTag, attrs)
|
||||
} else {
|
||||
if node.NoteID != 0 {
|
||||
break
|
||||
}
|
||||
r.out(w, aCloseTag)
|
||||
}
|
||||
}
|
||||
case Image:
|
||||
if r.Flags&SkipImages != 0 {
|
||||
return SkipChildren
|
||||
}
|
||||
if entering {
|
||||
dest := node.LinkData.Destination
|
||||
dest = r.addAbsPrefix(dest)
|
||||
if r.disableTags == 0 {
|
||||
//if options.safe && potentiallyUnsafe(dest) {
|
||||
//out(w, `<img src="" alt="`)
|
||||
//} else {
|
||||
r.out(w, []byte(`<img src="`))
|
||||
escLink(w, dest)
|
||||
r.out(w, []byte(`" alt="`))
|
||||
//}
|
||||
}
|
||||
r.disableTags++
|
||||
} else {
|
||||
r.disableTags--
|
||||
if r.disableTags == 0 {
|
||||
if node.LinkData.Title != nil {
|
||||
r.out(w, []byte(`" title="`))
|
||||
escapeHTML(w, node.LinkData.Title)
|
||||
}
|
||||
r.out(w, []byte(`" />`))
|
||||
}
|
||||
}
|
||||
case Code:
|
||||
r.out(w, codeTag)
|
||||
escapeAllHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
case Document:
|
||||
break
|
||||
case Paragraph:
|
||||
if skipParagraphTags(node) {
|
||||
break
|
||||
}
|
||||
if entering {
|
||||
// TODO: untangle this clusterfuck about when the newlines need
|
||||
// to be added and when not.
|
||||
if node.Prev != nil {
|
||||
switch node.Prev.Type {
|
||||
case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule:
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
if node.Parent.Type == BlockQuote && node.Prev == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
r.out(w, pTag)
|
||||
} else {
|
||||
r.out(w, pCloseTag)
|
||||
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
case BlockQuote:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, blockquoteTag)
|
||||
} else {
|
||||
r.out(w, blockquoteCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case HTMLBlock:
|
||||
if r.Flags&SkipHTML != 0 {
|
||||
break
|
||||
}
|
||||
r.cr(w)
|
||||
r.out(w, node.Literal)
|
||||
r.cr(w)
|
||||
case Heading:
|
||||
headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level
|
||||
openTag, closeTag := headingTagsFromLevel(headingLevel)
|
||||
if entering {
|
||||
if node.IsTitleblock {
|
||||
attrs = append(attrs, `class="title"`)
|
||||
}
|
||||
if node.HeadingID != "" {
|
||||
id := r.ensureUniqueHeadingID(node.HeadingID)
|
||||
if r.HeadingIDPrefix != "" {
|
||||
id = r.HeadingIDPrefix + id
|
||||
}
|
||||
if r.HeadingIDSuffix != "" {
|
||||
id = id + r.HeadingIDSuffix
|
||||
}
|
||||
attrs = append(attrs, fmt.Sprintf(`id="%s"`, id))
|
||||
}
|
||||
r.cr(w)
|
||||
r.tag(w, openTag, attrs)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
case HorizontalRule:
|
||||
r.cr(w)
|
||||
r.outHRTag(w)
|
||||
r.cr(w)
|
||||
case List:
|
||||
openTag := ulTag
|
||||
closeTag := ulCloseTag
|
||||
if node.ListFlags&ListTypeOrdered != 0 {
|
||||
openTag = olTag
|
||||
closeTag = olCloseTag
|
||||
}
|
||||
if node.ListFlags&ListTypeDefinition != 0 {
|
||||
openTag = dlTag
|
||||
closeTag = dlCloseTag
|
||||
}
|
||||
if entering {
|
||||
if node.IsFootnotesList {
|
||||
r.out(w, footnotesDivBytes)
|
||||
r.outHRTag(w)
|
||||
r.cr(w)
|
||||
}
|
||||
r.cr(w)
|
||||
if node.Parent.Type == Item && node.Parent.Parent.Tight {
|
||||
r.cr(w)
|
||||
}
|
||||
r.tag(w, openTag[:len(openTag)-1], attrs)
|
||||
r.cr(w)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
//cr(w)
|
||||
//if node.parent.Type != Item {
|
||||
// cr(w)
|
||||
//}
|
||||
if node.Parent.Type == Item && node.Next != nil {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.Parent.Type == Document || node.Parent.Type == BlockQuote {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.IsFootnotesList {
|
||||
r.out(w, footnotesCloseDivBytes)
|
||||
}
|
||||
}
|
||||
case Item:
|
||||
openTag := liTag
|
||||
closeTag := liCloseTag
|
||||
if node.ListFlags&ListTypeDefinition != 0 {
|
||||
openTag = ddTag
|
||||
closeTag = ddCloseTag
|
||||
}
|
||||
if node.ListFlags&ListTypeTerm != 0 {
|
||||
openTag = dtTag
|
||||
closeTag = dtCloseTag
|
||||
}
|
||||
if entering {
|
||||
if itemOpenCR(node) {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.ListData.RefLink != nil {
|
||||
slug := slugify(node.ListData.RefLink)
|
||||
r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug))
|
||||
break
|
||||
}
|
||||
r.out(w, openTag)
|
||||
} else {
|
||||
if node.ListData.RefLink != nil {
|
||||
slug := slugify(node.ListData.RefLink)
|
||||
if r.Flags&FootnoteReturnLinks != 0 {
|
||||
r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug))
|
||||
}
|
||||
}
|
||||
r.out(w, closeTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case CodeBlock:
|
||||
attrs = appendLanguageAttr(attrs, node.Info)
|
||||
r.cr(w)
|
||||
r.out(w, preTag)
|
||||
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
||||
escapeAllHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
r.out(w, preCloseTag)
|
||||
if node.Parent.Type != Item {
|
||||
r.cr(w)
|
||||
}
|
||||
case Table:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, tableTag)
|
||||
} else {
|
||||
r.out(w, tableCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableCell:
|
||||
openTag := tdTag
|
||||
closeTag := tdCloseTag
|
||||
if node.IsHeader {
|
||||
openTag = thTag
|
||||
closeTag = thCloseTag
|
||||
}
|
||||
if entering {
|
||||
align := cellAlignment(node.Align)
|
||||
if align != "" {
|
||||
attrs = append(attrs, fmt.Sprintf(`align="%s"`, align))
|
||||
}
|
||||
if node.Prev == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
r.tag(w, openTag, attrs)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableHead:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, theadTag)
|
||||
} else {
|
||||
r.out(w, theadCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableBody:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, tbodyTag)
|
||||
// XXX: this is to adhere to a rather silly test. Should fix test.
|
||||
if node.FirstChild == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
} else {
|
||||
r.out(w, tbodyCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableRow:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, trTag)
|
||||
} else {
|
||||
r.out(w, trCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
default:
|
||||
panic("Unknown node type " + node.Type.String())
|
||||
}
|
||||
return GoToNext
|
||||
}
|
||||
|
||||
// RenderHeader writes HTML document preamble and TOC if requested.
|
||||
func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) {
|
||||
r.writeDocumentHeader(w)
|
||||
if r.Flags&TOC != 0 {
|
||||
r.writeTOC(w, ast)
|
||||
}
|
||||
}
|
||||
|
||||
// RenderFooter writes HTML document footer.
|
||||
func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) {
|
||||
if r.Flags&CompletePage == 0 {
|
||||
return
|
||||
}
|
||||
io.WriteString(w, "\n</body>\n</html>\n")
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) {
|
||||
if r.Flags&CompletePage == 0 {
|
||||
return
|
||||
}
|
||||
ending := ""
|
||||
if r.Flags&UseXHTML != 0 {
|
||||
io.WriteString(w, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
|
||||
io.WriteString(w, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
|
||||
io.WriteString(w, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
|
||||
ending = " /"
|
||||
} else {
|
||||
io.WriteString(w, "<!DOCTYPE html>\n")
|
||||
io.WriteString(w, "<html>\n")
|
||||
}
|
||||
io.WriteString(w, "<head>\n")
|
||||
io.WriteString(w, " <title>")
|
||||
if r.Flags&Smartypants != 0 {
|
||||
r.sr.Process(w, []byte(r.Title))
|
||||
} else {
|
||||
escapeHTML(w, []byte(r.Title))
|
||||
}
|
||||
io.WriteString(w, "</title>\n")
|
||||
io.WriteString(w, " <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
||||
io.WriteString(w, Version)
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
io.WriteString(w, " <meta charset=\"utf-8\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
if r.CSS != "" {
|
||||
io.WriteString(w, " <link rel=\"stylesheet\" type=\"text/css\" href=\"")
|
||||
escapeHTML(w, []byte(r.CSS))
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
}
|
||||
if r.Icon != "" {
|
||||
io.WriteString(w, " <link rel=\"icon\" type=\"image/x-icon\" href=\"")
|
||||
escapeHTML(w, []byte(r.Icon))
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
}
|
||||
io.WriteString(w, "</head>\n")
|
||||
io.WriteString(w, "<body>\n\n")
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
inHeading := false
|
||||
tocLevel := 0
|
||||
headingCount := 0
|
||||
|
||||
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
if node.Type == Heading && !node.HeadingData.IsTitleblock {
|
||||
inHeading = entering
|
||||
if entering {
|
||||
node.HeadingID = fmt.Sprintf("toc_%d", headingCount)
|
||||
if node.Level == tocLevel {
|
||||
buf.WriteString("</li>\n\n<li>")
|
||||
} else if node.Level < tocLevel {
|
||||
for node.Level < tocLevel {
|
||||
tocLevel--
|
||||
buf.WriteString("</li>\n</ul>")
|
||||
}
|
||||
buf.WriteString("</li>\n\n<li>")
|
||||
} else {
|
||||
for node.Level > tocLevel {
|
||||
tocLevel++
|
||||
buf.WriteString("\n<ul>\n<li>")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount)
|
||||
headingCount++
|
||||
} else {
|
||||
buf.WriteString("</a>")
|
||||
}
|
||||
return GoToNext
|
||||
}
|
||||
|
||||
if inHeading {
|
||||
return r.RenderNode(&buf, node, entering)
|
||||
}
|
||||
|
||||
return GoToNext
|
||||
})
|
||||
|
||||
for ; tocLevel > 0; tocLevel-- {
|
||||
buf.WriteString("</li>\n</ul>")
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
io.WriteString(w, "<nav>\n")
|
||||
w.Write(buf.Bytes())
|
||||
io.WriteString(w, "\n\n</nav>\n")
|
||||
}
|
||||
r.lastOutputLen = buf.Len()
|
||||
}
|
1228
vendor/github.com/russross/blackfriday/v2/inline.go
generated
vendored
1228
vendor/github.com/russross/blackfriday/v2/inline.go
generated
vendored
File diff suppressed because it is too large
Load Diff
950
vendor/github.com/russross/blackfriday/v2/markdown.go
generated
vendored
950
vendor/github.com/russross/blackfriday/v2/markdown.go
generated
vendored
@ -1,950 +0,0 @@
|
||||
// Blackfriday Markdown Processor
|
||||
// Available at http://github.com/russross/blackfriday
|
||||
//
|
||||
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||
// Distributed under the Simplified BSD License.
|
||||
// See README.md for details.
|
||||
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
//
|
||||
// Markdown parsing and processing
|
||||
//
|
||||
|
||||
// Version string of the package. Appears in the rendered document when
|
||||
// CompletePage flag is on.
|
||||
const Version = "2.0"
|
||||
|
||||
// Extensions is a bitwise or'ed collection of enabled Blackfriday's
|
||||
// extensions.
|
||||
type Extensions int
|
||||
|
||||
// These are the supported markdown parsing extensions.
|
||||
// OR these values together to select multiple extensions.
|
||||
const (
|
||||
NoExtensions Extensions = 0
|
||||
NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words
|
||||
Tables // Render tables
|
||||
FencedCode // Render fenced code blocks
|
||||
Autolink // Detect embedded URLs that are not explicitly marked
|
||||
Strikethrough // Strikethrough text using ~~test~~
|
||||
LaxHTMLBlocks // Loosen up HTML block parsing rules
|
||||
SpaceHeadings // Be strict about prefix heading rules
|
||||
HardLineBreak // Translate newlines into line breaks
|
||||
TabSizeEight // Expand tabs to eight spaces instead of four
|
||||
Footnotes // Pandoc-style footnotes
|
||||
NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block
|
||||
HeadingIDs // specify heading IDs with {#id}
|
||||
Titleblock // Titleblock ala pandoc
|
||||
AutoHeadingIDs // Create the heading ID from the text
|
||||
BackslashLineBreak // Translate trailing backslashes into line breaks
|
||||
DefinitionLists // Render definition lists
|
||||
|
||||
CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants |
|
||||
SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
|
||||
|
||||
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
|
||||
Autolink | Strikethrough | SpaceHeadings | HeadingIDs |
|
||||
BackslashLineBreak | DefinitionLists
|
||||
)
|
||||
|
||||
// ListType contains bitwise or'ed flags for list and list item objects.
|
||||
type ListType int
|
||||
|
||||
// These are the possible flag values for the ListItem renderer.
|
||||
// Multiple flag values may be ORed together.
|
||||
// These are mostly of interest if you are writing a new output format.
|
||||
const (
|
||||
ListTypeOrdered ListType = 1 << iota
|
||||
ListTypeDefinition
|
||||
ListTypeTerm
|
||||
|
||||
ListItemContainsBlock
|
||||
ListItemBeginningOfList // TODO: figure out if this is of any use now
|
||||
ListItemEndOfList
|
||||
)
|
||||
|
||||
// CellAlignFlags holds a type of alignment in a table cell.
|
||||
type CellAlignFlags int
|
||||
|
||||
// These are the possible flag values for the table cell renderer.
|
||||
// Only a single one of these values will be used; they are not ORed together.
|
||||
// These are mostly of interest if you are writing a new output format.
|
||||
const (
|
||||
TableAlignmentLeft CellAlignFlags = 1 << iota
|
||||
TableAlignmentRight
|
||||
TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight)
|
||||
)
|
||||
|
||||
// The size of a tab stop.
|
||||
const (
|
||||
TabSizeDefault = 4
|
||||
TabSizeDouble = 8
|
||||
)
|
||||
|
||||
// blockTags is a set of tags that are recognized as HTML block tags.
|
||||
// Any of these can be included in markdown text without special escaping.
|
||||
var blockTags = map[string]struct{}{
|
||||
"blockquote": {},
|
||||
"del": {},
|
||||
"div": {},
|
||||
"dl": {},
|
||||
"fieldset": {},
|
||||
"form": {},
|
||||
"h1": {},
|
||||
"h2": {},
|
||||
"h3": {},
|
||||
"h4": {},
|
||||
"h5": {},
|
||||
"h6": {},
|
||||
"iframe": {},
|
||||
"ins": {},
|
||||
"math": {},
|
||||
"noscript": {},
|
||||
"ol": {},
|
||||
"pre": {},
|
||||
"p": {},
|
||||
"script": {},
|
||||
"style": {},
|
||||
"table": {},
|
||||
"ul": {},
|
||||
|
||||
// HTML5
|
||||
"address": {},
|
||||
"article": {},
|
||||
"aside": {},
|
||||
"canvas": {},
|
||||
"figcaption": {},
|
||||
"figure": {},
|
||||
"footer": {},
|
||||
"header": {},
|
||||
"hgroup": {},
|
||||
"main": {},
|
||||
"nav": {},
|
||||
"output": {},
|
||||
"progress": {},
|
||||
"section": {},
|
||||
"video": {},
|
||||
}
|
||||
|
||||
// Renderer is the rendering interface. This is mostly of interest if you are
|
||||
// implementing a new rendering format.
|
||||
//
|
||||
// Only an HTML implementation is provided in this repository, see the README
|
||||
// for external implementations.
|
||||
type Renderer interface {
|
||||
// RenderNode is the main rendering method. It will be called once for
|
||||
// every leaf node and twice for every non-leaf node (first with
|
||||
// entering=true, then with entering=false). The method should write its
|
||||
// rendition of the node to the supplied writer w.
|
||||
RenderNode(w io.Writer, node *Node, entering bool) WalkStatus
|
||||
|
||||
// RenderHeader is a method that allows the renderer to produce some
|
||||
// content preceding the main body of the output document. The header is
|
||||
// understood in the broad sense here. For example, the default HTML
|
||||
// renderer will write not only the HTML document preamble, but also the
|
||||
// table of contents if it was requested.
|
||||
//
|
||||
// The method will be passed an entire document tree, in case a particular
|
||||
// implementation needs to inspect it to produce output.
|
||||
//
|
||||
// The output should be written to the supplied writer w. If your
|
||||
// implementation has no header to write, supply an empty implementation.
|
||||
RenderHeader(w io.Writer, ast *Node)
|
||||
|
||||
// RenderFooter is a symmetric counterpart of RenderHeader.
|
||||
RenderFooter(w io.Writer, ast *Node)
|
||||
}
|
||||
|
||||
// Callback functions for inline parsing. One such function is defined
|
||||
// for each character that triggers a response when parsing inline data.
|
||||
type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node)
|
||||
|
||||
// Markdown is a type that holds extensions and the runtime state used by
|
||||
// Parse, and the renderer. You can not use it directly, construct it with New.
|
||||
type Markdown struct {
|
||||
renderer Renderer
|
||||
referenceOverride ReferenceOverrideFunc
|
||||
refs map[string]*reference
|
||||
inlineCallback [256]inlineParser
|
||||
extensions Extensions
|
||||
nesting int
|
||||
maxNesting int
|
||||
insideLink bool
|
||||
|
||||
// Footnotes need to be ordered as well as available to quickly check for
|
||||
// presence. If a ref is also a footnote, it's stored both in refs and here
|
||||
// in notes. Slice is nil if footnotes not enabled.
|
||||
notes []*reference
|
||||
|
||||
doc *Node
|
||||
tip *Node // = doc
|
||||
oldTip *Node
|
||||
lastMatchedContainer *Node // = doc
|
||||
allClosed bool
|
||||
}
|
||||
|
||||
func (p *Markdown) getRef(refid string) (ref *reference, found bool) {
|
||||
if p.referenceOverride != nil {
|
||||
r, overridden := p.referenceOverride(refid)
|
||||
if overridden {
|
||||
if r == nil {
|
||||
return nil, false
|
||||
}
|
||||
return &reference{
|
||||
link: []byte(r.Link),
|
||||
title: []byte(r.Title),
|
||||
noteID: 0,
|
||||
hasBlock: false,
|
||||
text: []byte(r.Text)}, true
|
||||
}
|
||||
}
|
||||
// refs are case insensitive
|
||||
ref, found = p.refs[strings.ToLower(refid)]
|
||||
return ref, found
|
||||
}
|
||||
|
||||
func (p *Markdown) finalize(block *Node) {
|
||||
above := block.Parent
|
||||
block.open = false
|
||||
p.tip = above
|
||||
}
|
||||
|
||||
func (p *Markdown) addChild(node NodeType, offset uint32) *Node {
|
||||
return p.addExistingChild(NewNode(node), offset)
|
||||
}
|
||||
|
||||
func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node {
|
||||
for !p.tip.canContain(node.Type) {
|
||||
p.finalize(p.tip)
|
||||
}
|
||||
p.tip.AppendChild(node)
|
||||
p.tip = node
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Markdown) closeUnmatchedBlocks() {
|
||||
if !p.allClosed {
|
||||
for p.oldTip != p.lastMatchedContainer {
|
||||
parent := p.oldTip.Parent
|
||||
p.finalize(p.oldTip)
|
||||
p.oldTip = parent
|
||||
}
|
||||
p.allClosed = true
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Public interface
|
||||
//
|
||||
//
|
||||
|
||||
// Reference represents the details of a link.
|
||||
// See the documentation in Options for more details on use-case.
|
||||
type Reference struct {
|
||||
// Link is usually the URL the reference points to.
|
||||
Link string
|
||||
// Title is the alternate text describing the link in more detail.
|
||||
Title string
|
||||
// Text is the optional text to override the ref with if the syntax used was
|
||||
// [refid][]
|
||||
Text string
|
||||
}
|
||||
|
||||
// ReferenceOverrideFunc is expected to be called with a reference string and
|
||||
// return either a valid Reference type that the reference string maps to or
|
||||
// nil. If overridden is false, the default reference logic will be executed.
|
||||
// See the documentation in Options for more details on use-case.
|
||||
type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool)
|
||||
|
||||
// New constructs a Markdown processor. You can use the same With* functions as
|
||||
// for Run() to customize parser's behavior and the renderer.
|
||||
func New(opts ...Option) *Markdown {
|
||||
var p Markdown
|
||||
for _, opt := range opts {
|
||||
opt(&p)
|
||||
}
|
||||
p.refs = make(map[string]*reference)
|
||||
p.maxNesting = 16
|
||||
p.insideLink = false
|
||||
docNode := NewNode(Document)
|
||||
p.doc = docNode
|
||||
p.tip = docNode
|
||||
p.oldTip = docNode
|
||||
p.lastMatchedContainer = docNode
|
||||
p.allClosed = true
|
||||
// register inline parsers
|
||||
p.inlineCallback[' '] = maybeLineBreak
|
||||
p.inlineCallback['*'] = emphasis
|
||||
p.inlineCallback['_'] = emphasis
|
||||
if p.extensions&Strikethrough != 0 {
|
||||
p.inlineCallback['~'] = emphasis
|
||||
}
|
||||
p.inlineCallback['`'] = codeSpan
|
||||
p.inlineCallback['\n'] = lineBreak
|
||||
p.inlineCallback['['] = link
|
||||
p.inlineCallback['<'] = leftAngle
|
||||
p.inlineCallback['\\'] = escape
|
||||
p.inlineCallback['&'] = entity
|
||||
p.inlineCallback['!'] = maybeImage
|
||||
p.inlineCallback['^'] = maybeInlineFootnote
|
||||
if p.extensions&Autolink != 0 {
|
||||
p.inlineCallback['h'] = maybeAutoLink
|
||||
p.inlineCallback['m'] = maybeAutoLink
|
||||
p.inlineCallback['f'] = maybeAutoLink
|
||||
p.inlineCallback['H'] = maybeAutoLink
|
||||
p.inlineCallback['M'] = maybeAutoLink
|
||||
p.inlineCallback['F'] = maybeAutoLink
|
||||
}
|
||||
if p.extensions&Footnotes != 0 {
|
||||
p.notes = make([]*reference, 0)
|
||||
}
|
||||
return &p
|
||||
}
|
||||
|
||||
// Option customizes the Markdown processor's default behavior.
|
||||
type Option func(*Markdown)
|
||||
|
||||
// WithRenderer allows you to override the default renderer.
|
||||
func WithRenderer(r Renderer) Option {
|
||||
return func(p *Markdown) {
|
||||
p.renderer = r
|
||||
}
|
||||
}
|
||||
|
||||
// WithExtensions allows you to pick some of the many extensions provided by
|
||||
// Blackfriday. You can bitwise OR them.
|
||||
func WithExtensions(e Extensions) Option {
|
||||
return func(p *Markdown) {
|
||||
p.extensions = e
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoExtensions turns off all extensions and custom behavior.
|
||||
func WithNoExtensions() Option {
|
||||
return func(p *Markdown) {
|
||||
p.extensions = NoExtensions
|
||||
p.renderer = NewHTMLRenderer(HTMLRendererParameters{
|
||||
Flags: HTMLFlagsNone,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithRefOverride sets an optional function callback that is called every
|
||||
// time a reference is resolved.
|
||||
//
|
||||
// In Markdown, the link reference syntax can be made to resolve a link to
|
||||
// a reference instead of an inline URL, in one of the following ways:
|
||||
//
|
||||
// * [link text][refid]
|
||||
// * [refid][]
|
||||
//
|
||||
// Usually, the refid is defined at the bottom of the Markdown document. If
|
||||
// this override function is provided, the refid is passed to the override
|
||||
// function first, before consulting the defined refids at the bottom. If
|
||||
// the override function indicates an override did not occur, the refids at
|
||||
// the bottom will be used to fill in the link details.
|
||||
func WithRefOverride(o ReferenceOverrideFunc) Option {
|
||||
return func(p *Markdown) {
|
||||
p.referenceOverride = o
|
||||
}
|
||||
}
|
||||
|
||||
// Run is the main entry point to Blackfriday. It parses and renders a
|
||||
// block of markdown-encoded text.
|
||||
//
|
||||
// The simplest invocation of Run takes one argument, input:
|
||||
// output := Run(input)
|
||||
// This will parse the input with CommonExtensions enabled and render it with
|
||||
// the default HTMLRenderer (with CommonHTMLFlags).
|
||||
//
|
||||
// Variadic arguments opts can customize the default behavior. Since Markdown
|
||||
// type does not contain exported fields, you can not use it directly. Instead,
|
||||
// use the With* functions. For example, this will call the most basic
|
||||
// functionality, with no extensions:
|
||||
// output := Run(input, WithNoExtensions())
|
||||
//
|
||||
// You can use any number of With* arguments, even contradicting ones. They
|
||||
// will be applied in order of appearance and the latter will override the
|
||||
// former:
|
||||
// output := Run(input, WithNoExtensions(), WithExtensions(exts),
|
||||
// WithRenderer(yourRenderer))
|
||||
func Run(input []byte, opts ...Option) []byte {
|
||||
r := NewHTMLRenderer(HTMLRendererParameters{
|
||||
Flags: CommonHTMLFlags,
|
||||
})
|
||||
optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)}
|
||||
optList = append(optList, opts...)
|
||||
parser := New(optList...)
|
||||
ast := parser.Parse(input)
|
||||
var buf bytes.Buffer
|
||||
parser.renderer.RenderHeader(&buf, ast)
|
||||
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
return parser.renderer.RenderNode(&buf, node, entering)
|
||||
})
|
||||
parser.renderer.RenderFooter(&buf, ast)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Parse is an entry point to the parsing part of Blackfriday. It takes an
|
||||
// input markdown document and produces a syntax tree for its contents. This
|
||||
// tree can then be rendered with a default or custom renderer, or
|
||||
// analyzed/transformed by the caller to whatever non-standard needs they have.
|
||||
// The return value is the root node of the syntax tree.
|
||||
func (p *Markdown) Parse(input []byte) *Node {
|
||||
p.block(input)
|
||||
// Walk the tree and finish up some of unfinished blocks
|
||||
for p.tip != nil {
|
||||
p.finalize(p.tip)
|
||||
}
|
||||
// Walk the tree again and process inline markdown in each block
|
||||
p.doc.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell {
|
||||
p.inline(node, node.content)
|
||||
node.content = nil
|
||||
}
|
||||
return GoToNext
|
||||
})
|
||||
p.parseRefsToAST()
|
||||
return p.doc
|
||||
}
|
||||
|
||||
func (p *Markdown) parseRefsToAST() {
|
||||
if p.extensions&Footnotes == 0 || len(p.notes) == 0 {
|
||||
return
|
||||
}
|
||||
p.tip = p.doc
|
||||
block := p.addBlock(List, nil)
|
||||
block.IsFootnotesList = true
|
||||
block.ListFlags = ListTypeOrdered
|
||||
flags := ListItemBeginningOfList
|
||||
// Note: this loop is intentionally explicit, not range-form. This is
|
||||
// because the body of the loop will append nested footnotes to p.notes and
|
||||
// we need to process those late additions. Range form would only walk over
|
||||
// the fixed initial set.
|
||||
for i := 0; i < len(p.notes); i++ {
|
||||
ref := p.notes[i]
|
||||
p.addExistingChild(ref.footnote, 0)
|
||||
block := ref.footnote
|
||||
block.ListFlags = flags | ListTypeOrdered
|
||||
block.RefLink = ref.link
|
||||
if ref.hasBlock {
|
||||
flags |= ListItemContainsBlock
|
||||
p.block(ref.title)
|
||||
} else {
|
||||
p.inline(block, ref.title)
|
||||
}
|
||||
flags &^= ListItemBeginningOfList | ListItemContainsBlock
|
||||
}
|
||||
above := block.Parent
|
||||
finalizeList(block)
|
||||
p.tip = above
|
||||
block.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
if node.Type == Paragraph || node.Type == Heading {
|
||||
p.inline(node, node.content)
|
||||
node.content = nil
|
||||
}
|
||||
return GoToNext
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Link references
|
||||
//
|
||||
// This section implements support for references that (usually) appear
|
||||
// as footnotes in a document, and can be referenced anywhere in the document.
|
||||
// The basic format is:
|
||||
//
|
||||
// [1]: http://www.google.com/ "Google"
|
||||
// [2]: http://www.github.com/ "Github"
|
||||
//
|
||||
// Anywhere in the document, the reference can be linked by referring to its
|
||||
// label, i.e., 1 and 2 in this example, as in:
|
||||
//
|
||||
// This library is hosted on [Github][2], a git hosting site.
|
||||
//
|
||||
// Actual footnotes as specified in Pandoc and supported by some other Markdown
|
||||
// libraries such as php-markdown are also taken care of. They look like this:
|
||||
//
|
||||
// This sentence needs a bit of further explanation.[^note]
|
||||
//
|
||||
// [^note]: This is the explanation.
|
||||
//
|
||||
// Footnotes should be placed at the end of the document in an ordered list.
|
||||
// Finally, there are inline footnotes such as:
|
||||
//
|
||||
// Inline footnotes^[Also supported.] provide a quick inline explanation,
|
||||
// but are rendered at the bottom of the document.
|
||||
//
|
||||
|
||||
// reference holds all information necessary for a reference-style links or
|
||||
// footnotes.
|
||||
//
|
||||
// Consider this markdown with reference-style links:
|
||||
//
|
||||
// [link][ref]
|
||||
//
|
||||
// [ref]: /url/ "tooltip title"
|
||||
//
|
||||
// It will be ultimately converted to this HTML:
|
||||
//
|
||||
// <p><a href=\"/url/\" title=\"title\">link</a></p>
|
||||
//
|
||||
// And a reference structure will be populated as follows:
|
||||
//
|
||||
// p.refs["ref"] = &reference{
|
||||
// link: "/url/",
|
||||
// title: "tooltip title",
|
||||
// }
|
||||
//
|
||||
// Alternatively, reference can contain information about a footnote. Consider
|
||||
// this markdown:
|
||||
//
|
||||
// Text needing a footnote.[^a]
|
||||
//
|
||||
// [^a]: This is the note
|
||||
//
|
||||
// A reference structure will be populated as follows:
|
||||
//
|
||||
// p.refs["a"] = &reference{
|
||||
// link: "a",
|
||||
// title: "This is the note",
|
||||
// noteID: <some positive int>,
|
||||
// }
|
||||
//
|
||||
// TODO: As you can see, it begs for splitting into two dedicated structures
|
||||
// for refs and for footnotes.
|
||||
type reference struct {
|
||||
link []byte
|
||||
title []byte
|
||||
noteID int // 0 if not a footnote ref
|
||||
hasBlock bool
|
||||
footnote *Node // a link to the Item node within a list of footnotes
|
||||
|
||||
text []byte // only gets populated by refOverride feature with Reference.Text
|
||||
}
|
||||
|
||||
func (r *reference) String() string {
|
||||
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
|
||||
r.link, r.title, r.text, r.noteID, r.hasBlock)
|
||||
}
|
||||
|
||||
// Check whether or not data starts with a reference link.
|
||||
// If so, it is parsed and stored in the list of references
|
||||
// (in the render struct).
|
||||
// Returns the number of bytes to skip to move past it,
|
||||
// or zero if the first line is not a reference.
|
||||
func isReference(p *Markdown, data []byte, tabSize int) int {
|
||||
// up to 3 optional leading spaces
|
||||
if len(data) < 4 {
|
||||
return 0
|
||||
}
|
||||
i := 0
|
||||
for i < 3 && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
|
||||
noteID := 0
|
||||
|
||||
// id part: anything but a newline between brackets
|
||||
if data[i] != '[' {
|
||||
return 0
|
||||
}
|
||||
i++
|
||||
if p.extensions&Footnotes != 0 {
|
||||
if i < len(data) && data[i] == '^' {
|
||||
// we can set it to anything here because the proper noteIds will
|
||||
// be assigned later during the second pass. It just has to be != 0
|
||||
noteID = 1
|
||||
i++
|
||||
}
|
||||
}
|
||||
idOffset := i
|
||||
for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) || data[i] != ']' {
|
||||
return 0
|
||||
}
|
||||
idEnd := i
|
||||
// footnotes can have empty ID, like this: [^], but a reference can not be
|
||||
// empty like this: []. Break early if it's not a footnote and there's no ID
|
||||
if noteID == 0 && idOffset == idEnd {
|
||||
return 0
|
||||
}
|
||||
// spacer: colon (space | tab)* newline? (space | tab)*
|
||||
i++
|
||||
if i >= len(data) || data[i] != ':' {
|
||||
return 0
|
||||
}
|
||||
i++
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i < len(data) && (data[i] == '\n' || data[i] == '\r') {
|
||||
i++
|
||||
if i < len(data) && data[i] == '\n' && data[i-1] == '\r' {
|
||||
i++
|
||||
}
|
||||
}
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var (
|
||||
linkOffset, linkEnd int
|
||||
titleOffset, titleEnd int
|
||||
lineEnd int
|
||||
raw []byte
|
||||
hasBlock bool
|
||||
)
|
||||
|
||||
if p.extensions&Footnotes != 0 && noteID != 0 {
|
||||
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
|
||||
lineEnd = linkEnd
|
||||
} else {
|
||||
linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i)
|
||||
}
|
||||
if lineEnd == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// a valid ref has been found
|
||||
|
||||
ref := &reference{
|
||||
noteID: noteID,
|
||||
hasBlock: hasBlock,
|
||||
}
|
||||
|
||||
if noteID > 0 {
|
||||
// reusing the link field for the id since footnotes don't have links
|
||||
ref.link = data[idOffset:idEnd]
|
||||
// if footnote, it's not really a title, it's the contained text
|
||||
ref.title = raw
|
||||
} else {
|
||||
ref.link = data[linkOffset:linkEnd]
|
||||
ref.title = data[titleOffset:titleEnd]
|
||||
}
|
||||
|
||||
// id matches are case-insensitive
|
||||
id := string(bytes.ToLower(data[idOffset:idEnd]))
|
||||
|
||||
p.refs[id] = ref
|
||||
|
||||
return lineEnd
|
||||
}
|
||||
|
||||
func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
|
||||
// link: whitespace-free sequence, optionally between angle brackets
|
||||
if data[i] == '<' {
|
||||
i++
|
||||
}
|
||||
linkOffset = i
|
||||
for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' {
|
||||
i++
|
||||
}
|
||||
linkEnd = i
|
||||
if data[linkOffset] == '<' && data[linkEnd-1] == '>' {
|
||||
linkOffset++
|
||||
linkEnd--
|
||||
}
|
||||
|
||||
// optional spacer: (space | tab)* (newline | '\'' | '"' | '(' )
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' {
|
||||
return
|
||||
}
|
||||
|
||||
// compute end-of-line
|
||||
if i >= len(data) || data[i] == '\r' || data[i] == '\n' {
|
||||
lineEnd = i
|
||||
}
|
||||
if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' {
|
||||
lineEnd++
|
||||
}
|
||||
|
||||
// optional (space|tab)* spacer after a newline
|
||||
if lineEnd > 0 {
|
||||
i = lineEnd + 1
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// optional title: any non-newline sequence enclosed in '"() alone on its line
|
||||
if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') {
|
||||
i++
|
||||
titleOffset = i
|
||||
|
||||
// look for EOL
|
||||
for i < len(data) && data[i] != '\n' && data[i] != '\r' {
|
||||
i++
|
||||
}
|
||||
if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' {
|
||||
titleEnd = i + 1
|
||||
} else {
|
||||
titleEnd = i
|
||||
}
|
||||
|
||||
// step back
|
||||
i--
|
||||
for i > titleOffset && (data[i] == ' ' || data[i] == '\t') {
|
||||
i--
|
||||
}
|
||||
if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') {
|
||||
lineEnd = titleEnd
|
||||
titleEnd = i
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The first bit of this logic is the same as Parser.listItem, but the rest
|
||||
// is much simpler. This function simply finds the entire block and shifts it
|
||||
// over by one tab if it is indeed a block (just returns the line if it's not).
|
||||
// blockEnd is the end of the section in the input buffer, and contents is the
|
||||
// extracted text that was shifted over one tab. It will need to be rendered at
|
||||
// the end of the document.
|
||||
func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
|
||||
if i == 0 || len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// skip leading whitespace on first line
|
||||
for i < len(data) && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
|
||||
blockStart = i
|
||||
|
||||
// find the end of the line
|
||||
blockEnd = i
|
||||
for i < len(data) && data[i-1] != '\n' {
|
||||
i++
|
||||
}
|
||||
|
||||
// get working buffer
|
||||
var raw bytes.Buffer
|
||||
|
||||
// put the first line into the working buffer
|
||||
raw.Write(data[blockEnd:i])
|
||||
blockEnd = i
|
||||
|
||||
// process the following lines
|
||||
containsBlankLine := false
|
||||
|
||||
gatherLines:
|
||||
for blockEnd < len(data) {
|
||||
i++
|
||||
|
||||
// find the end of this line
|
||||
for i < len(data) && data[i-1] != '\n' {
|
||||
i++
|
||||
}
|
||||
|
||||
// if it is an empty line, guess that it is part of this item
|
||||
// and move on to the next line
|
||||
if p.isEmpty(data[blockEnd:i]) > 0 {
|
||||
containsBlankLine = true
|
||||
blockEnd = i
|
||||
continue
|
||||
}
|
||||
|
||||
n := 0
|
||||
if n = isIndented(data[blockEnd:i], indentSize); n == 0 {
|
||||
// this is the end of the block.
|
||||
// we don't want to include this last line in the index.
|
||||
break gatherLines
|
||||
}
|
||||
|
||||
// if there were blank lines before this one, insert a new one now
|
||||
if containsBlankLine {
|
||||
raw.WriteByte('\n')
|
||||
containsBlankLine = false
|
||||
}
|
||||
|
||||
// get rid of that first tab, write to buffer
|
||||
raw.Write(data[blockEnd+n : i])
|
||||
hasBlock = true
|
||||
|
||||
blockEnd = i
|
||||
}
|
||||
|
||||
if data[blockEnd-1] != '\n' {
|
||||
raw.WriteByte('\n')
|
||||
}
|
||||
|
||||
contents = raw.Bytes()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Miscellaneous helper functions
|
||||
//
|
||||
//
|
||||
|
||||
// Test if a character is a punctuation symbol.
|
||||
// Taken from a private function in regexp in the stdlib.
|
||||
func ispunct(c byte) bool {
|
||||
for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Test if a character is a whitespace character.
|
||||
func isspace(c byte) bool {
|
||||
return ishorizontalspace(c) || isverticalspace(c)
|
||||
}
|
||||
|
||||
// Test if a character is a horizontal whitespace character.
|
||||
func ishorizontalspace(c byte) bool {
|
||||
return c == ' ' || c == '\t'
|
||||
}
|
||||
|
||||
// Test if a character is a vertical character.
|
||||
func isverticalspace(c byte) bool {
|
||||
return c == '\n' || c == '\r' || c == '\f' || c == '\v'
|
||||
}
|
||||
|
||||
// Test if a character is letter.
|
||||
func isletter(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
}
|
||||
|
||||
// Test if a character is a letter or a digit.
|
||||
// TODO: check when this is looking for ASCII alnum and when it should use unicode
|
||||
func isalnum(c byte) bool {
|
||||
return (c >= '0' && c <= '9') || isletter(c)
|
||||
}
|
||||
|
||||
// Replace tab characters with spaces, aligning to the next TAB_SIZE column.
|
||||
// always ends output with a newline
|
||||
func expandTabs(out *bytes.Buffer, line []byte, tabSize int) {
|
||||
// first, check for common cases: no tabs, or only tabs at beginning of line
|
||||
i, prefix := 0, 0
|
||||
slowcase := false
|
||||
for i = 0; i < len(line); i++ {
|
||||
if line[i] == '\t' {
|
||||
if prefix == i {
|
||||
prefix++
|
||||
} else {
|
||||
slowcase = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no need to decode runes if all tabs are at the beginning of the line
|
||||
if !slowcase {
|
||||
for i = 0; i < prefix*tabSize; i++ {
|
||||
out.WriteByte(' ')
|
||||
}
|
||||
out.Write(line[prefix:])
|
||||
return
|
||||
}
|
||||
|
||||
// the slow case: we need to count runes to figure out how
|
||||
// many spaces to insert for each tab
|
||||
column := 0
|
||||
i = 0
|
||||
for i < len(line) {
|
||||
start := i
|
||||
for i < len(line) && line[i] != '\t' {
|
||||
_, size := utf8.DecodeRune(line[i:])
|
||||
i += size
|
||||
column++
|
||||
}
|
||||
|
||||
if i > start {
|
||||
out.Write(line[start:i])
|
||||
}
|
||||
|
||||
if i >= len(line) {
|
||||
break
|
||||
}
|
||||
|
||||
for {
|
||||
out.WriteByte(' ')
|
||||
column++
|
||||
if column%tabSize == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Find if a line counts as indented or not.
|
||||
// Returns number of characters the indent is (0 = not indented).
|
||||
func isIndented(data []byte, indentSize int) int {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
if data[0] == '\t' {
|
||||
return 1
|
||||
}
|
||||
if len(data) < indentSize {
|
||||
return 0
|
||||
}
|
||||
for i := 0; i < indentSize; i++ {
|
||||
if data[i] != ' ' {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return indentSize
|
||||
}
|
||||
|
||||
// Create a url-safe slug for fragments
|
||||
func slugify(in []byte) []byte {
|
||||
if len(in) == 0 {
|
||||
return in
|
||||
}
|
||||
out := make([]byte, 0, len(in))
|
||||
sym := false
|
||||
|
||||
for _, ch := range in {
|
||||
if isalnum(ch) {
|
||||
sym = false
|
||||
out = append(out, ch)
|
||||
} else if sym {
|
||||
continue
|
||||
} else {
|
||||
out = append(out, '-')
|
||||
sym = true
|
||||
}
|
||||
}
|
||||
var a, b int
|
||||
var ch byte
|
||||
for a, ch = range out {
|
||||
if ch != '-' {
|
||||
break
|
||||
}
|
||||
}
|
||||
for b = len(out) - 1; b > 0; b-- {
|
||||
if out[b] != '-' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out[a : b+1]
|
||||
}
|
360
vendor/github.com/russross/blackfriday/v2/node.go
generated
vendored
360
vendor/github.com/russross/blackfriday/v2/node.go
generated
vendored
@ -1,360 +0,0 @@
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NodeType specifies a type of a single node of a syntax tree. Usually one
|
||||
// node (and its type) corresponds to a single markdown feature, e.g. emphasis
|
||||
// or code block.
|
||||
type NodeType int
|
||||
|
||||
// Constants for identifying different types of nodes. See NodeType.
|
||||
const (
|
||||
Document NodeType = iota
|
||||
BlockQuote
|
||||
List
|
||||
Item
|
||||
Paragraph
|
||||
Heading
|
||||
HorizontalRule
|
||||
Emph
|
||||
Strong
|
||||
Del
|
||||
Link
|
||||
Image
|
||||
Text
|
||||
HTMLBlock
|
||||
CodeBlock
|
||||
Softbreak
|
||||
Hardbreak
|
||||
Code
|
||||
HTMLSpan
|
||||
Table
|
||||
TableCell
|
||||
TableHead
|
||||
TableBody
|
||||
TableRow
|
||||
)
|
||||
|
||||
var nodeTypeNames = []string{
|
||||
Document: "Document",
|
||||
BlockQuote: "BlockQuote",
|
||||
List: "List",
|
||||
Item: "Item",
|
||||
Paragraph: "Paragraph",
|
||||
Heading: "Heading",
|
||||
HorizontalRule: "HorizontalRule",
|
||||
Emph: "Emph",
|
||||
Strong: "Strong",
|
||||
Del: "Del",
|
||||
Link: "Link",
|
||||
Image: "Image",
|
||||
Text: "Text",
|
||||
HTMLBlock: "HTMLBlock",
|
||||
CodeBlock: "CodeBlock",
|
||||
Softbreak: "Softbreak",
|
||||
Hardbreak: "Hardbreak",
|
||||
Code: "Code",
|
||||
HTMLSpan: "HTMLSpan",
|
||||
Table: "Table",
|
||||
TableCell: "TableCell",
|
||||
TableHead: "TableHead",
|
||||
TableBody: "TableBody",
|
||||
TableRow: "TableRow",
|
||||
}
|
||||
|
||||
func (t NodeType) String() string {
|
||||
return nodeTypeNames[t]
|
||||
}
|
||||
|
||||
// ListData contains fields relevant to a List and Item node type.
|
||||
type ListData struct {
|
||||
ListFlags ListType
|
||||
Tight bool // Skip <p>s around list item data if true
|
||||
BulletChar byte // '*', '+' or '-' in bullet lists
|
||||
Delimiter byte // '.' or ')' after the number in ordered lists
|
||||
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||||
IsFootnotesList bool // This is a list of footnotes
|
||||
}
|
||||
|
||||
// LinkData contains fields relevant to a Link node type.
|
||||
type LinkData struct {
|
||||
Destination []byte // Destination is what goes into a href
|
||||
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||||
NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote
|
||||
Footnote *Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil.
|
||||
}
|
||||
|
||||
// CodeBlockData contains fields relevant to a CodeBlock node type.
|
||||
type CodeBlockData struct {
|
||||
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||||
Info []byte // This holds the info string
|
||||
FenceChar byte
|
||||
FenceLength int
|
||||
FenceOffset int
|
||||
}
|
||||
|
||||
// TableCellData contains fields relevant to a TableCell node type.
|
||||
type TableCellData struct {
|
||||
IsHeader bool // This tells if it's under the header row
|
||||
Align CellAlignFlags // This holds the value for align attribute
|
||||
}
|
||||
|
||||
// HeadingData contains fields relevant to a Heading node type.
|
||||
type HeadingData struct {
|
||||
Level int // This holds the heading level number
|
||||
HeadingID string // This might hold heading ID, if present
|
||||
IsTitleblock bool // Specifies whether it's a title block
|
||||
}
|
||||
|
||||
// Node is a single element in the abstract syntax tree of the parsed document.
|
||||
// It holds connections to the structurally neighboring nodes and, for certain
|
||||
// types of nodes, additional information that might be needed when rendering.
|
||||
type Node struct {
|
||||
Type NodeType // Determines the type of the node
|
||||
Parent *Node // Points to the parent
|
||||
FirstChild *Node // Points to the first child, if any
|
||||
LastChild *Node // Points to the last child, if any
|
||||
Prev *Node // Previous sibling; nil if it's the first child
|
||||
Next *Node // Next sibling; nil if it's the last child
|
||||
|
||||
Literal []byte // Text contents of the leaf nodes
|
||||
|
||||
HeadingData // Populated if Type is Heading
|
||||
ListData // Populated if Type is List
|
||||
CodeBlockData // Populated if Type is CodeBlock
|
||||
LinkData // Populated if Type is Link
|
||||
TableCellData // Populated if Type is TableCell
|
||||
|
||||
content []byte // Markdown content of the block nodes
|
||||
open bool // Specifies an open block node that has not been finished to process yet
|
||||
}
|
||||
|
||||
// NewNode allocates a node of a specified type.
|
||||
func NewNode(typ NodeType) *Node {
|
||||
return &Node{
|
||||
Type: typ,
|
||||
open: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) String() string {
|
||||
ellipsis := ""
|
||||
snippet := n.Literal
|
||||
if len(snippet) > 16 {
|
||||
snippet = snippet[:16]
|
||||
ellipsis = "..."
|
||||
}
|
||||
return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis)
|
||||
}
|
||||
|
||||
// Unlink removes node 'n' from the tree.
|
||||
// It panics if the node is nil.
|
||||
func (n *Node) Unlink() {
|
||||
if n.Prev != nil {
|
||||
n.Prev.Next = n.Next
|
||||
} else if n.Parent != nil {
|
||||
n.Parent.FirstChild = n.Next
|
||||
}
|
||||
if n.Next != nil {
|
||||
n.Next.Prev = n.Prev
|
||||
} else if n.Parent != nil {
|
||||
n.Parent.LastChild = n.Prev
|
||||
}
|
||||
n.Parent = nil
|
||||
n.Next = nil
|
||||
n.Prev = nil
|
||||
}
|
||||
|
||||
// AppendChild adds a node 'child' as a child of 'n'.
|
||||
// It panics if either node is nil.
|
||||
func (n *Node) AppendChild(child *Node) {
|
||||
child.Unlink()
|
||||
child.Parent = n
|
||||
if n.LastChild != nil {
|
||||
n.LastChild.Next = child
|
||||
child.Prev = n.LastChild
|
||||
n.LastChild = child
|
||||
} else {
|
||||
n.FirstChild = child
|
||||
n.LastChild = child
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts 'sibling' immediately before 'n'.
|
||||
// It panics if either node is nil.
|
||||
func (n *Node) InsertBefore(sibling *Node) {
|
||||
sibling.Unlink()
|
||||
sibling.Prev = n.Prev
|
||||
if sibling.Prev != nil {
|
||||
sibling.Prev.Next = sibling
|
||||
}
|
||||
sibling.Next = n
|
||||
n.Prev = sibling
|
||||
sibling.Parent = n.Parent
|
||||
if sibling.Prev == nil {
|
||||
sibling.Parent.FirstChild = sibling
|
||||
}
|
||||
}
|
||||
|
||||
// IsContainer returns true if 'n' can contain children.
|
||||
func (n *Node) IsContainer() bool {
|
||||
switch n.Type {
|
||||
case Document:
|
||||
fallthrough
|
||||
case BlockQuote:
|
||||
fallthrough
|
||||
case List:
|
||||
fallthrough
|
||||
case Item:
|
||||
fallthrough
|
||||
case Paragraph:
|
||||
fallthrough
|
||||
case Heading:
|
||||
fallthrough
|
||||
case Emph:
|
||||
fallthrough
|
||||
case Strong:
|
||||
fallthrough
|
||||
case Del:
|
||||
fallthrough
|
||||
case Link:
|
||||
fallthrough
|
||||
case Image:
|
||||
fallthrough
|
||||
case Table:
|
||||
fallthrough
|
||||
case TableHead:
|
||||
fallthrough
|
||||
case TableBody:
|
||||
fallthrough
|
||||
case TableRow:
|
||||
fallthrough
|
||||
case TableCell:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeaf returns true if 'n' is a leaf node.
|
||||
func (n *Node) IsLeaf() bool {
|
||||
return !n.IsContainer()
|
||||
}
|
||||
|
||||
func (n *Node) canContain(t NodeType) bool {
|
||||
if n.Type == List {
|
||||
return t == Item
|
||||
}
|
||||
if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
|
||||
return t != Item
|
||||
}
|
||||
if n.Type == Table {
|
||||
return t == TableHead || t == TableBody
|
||||
}
|
||||
if n.Type == TableHead || n.Type == TableBody {
|
||||
return t == TableRow
|
||||
}
|
||||
if n.Type == TableRow {
|
||||
return t == TableCell
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WalkStatus allows NodeVisitor to have some control over the tree traversal.
|
||||
// It is returned from NodeVisitor and different values allow Node.Walk to
|
||||
// decide which node to go to next.
|
||||
type WalkStatus int
|
||||
|
||||
const (
|
||||
// GoToNext is the default traversal of every node.
|
||||
GoToNext WalkStatus = iota
|
||||
// SkipChildren tells walker to skip all children of current node.
|
||||
SkipChildren
|
||||
// Terminate tells walker to terminate the traversal.
|
||||
Terminate
|
||||
)
|
||||
|
||||
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||||
// Called twice for every node: once with entering=true when the branch is
|
||||
// first visited, then with entering=false after all the children are done.
|
||||
type NodeVisitor func(node *Node, entering bool) WalkStatus
|
||||
|
||||
// Walk is a convenience method that instantiates a walker and starts a
|
||||
// traversal of subtree rooted at n.
|
||||
func (n *Node) Walk(visitor NodeVisitor) {
|
||||
w := newNodeWalker(n)
|
||||
for w.current != nil {
|
||||
status := visitor(w.current, w.entering)
|
||||
switch status {
|
||||
case GoToNext:
|
||||
w.next()
|
||||
case SkipChildren:
|
||||
w.entering = false
|
||||
w.next()
|
||||
case Terminate:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type nodeWalker struct {
|
||||
current *Node
|
||||
root *Node
|
||||
entering bool
|
||||
}
|
||||
|
||||
func newNodeWalker(root *Node) *nodeWalker {
|
||||
return &nodeWalker{
|
||||
current: root,
|
||||
root: root,
|
||||
entering: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (nw *nodeWalker) next() {
|
||||
if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
|
||||
nw.current = nil
|
||||
return
|
||||
}
|
||||
if nw.entering && nw.current.IsContainer() {
|
||||
if nw.current.FirstChild != nil {
|
||||
nw.current = nw.current.FirstChild
|
||||
nw.entering = true
|
||||
} else {
|
||||
nw.entering = false
|
||||
}
|
||||
} else if nw.current.Next == nil {
|
||||
nw.current = nw.current.Parent
|
||||
nw.entering = false
|
||||
} else {
|
||||
nw.current = nw.current.Next
|
||||
nw.entering = true
|
||||
}
|
||||
}
|
||||
|
||||
func dump(ast *Node) {
|
||||
fmt.Println(dumpString(ast))
|
||||
}
|
||||
|
||||
func dumpR(ast *Node, depth int) string {
|
||||
if ast == nil {
|
||||
return ""
|
||||
}
|
||||
indent := bytes.Repeat([]byte("\t"), depth)
|
||||
content := ast.Literal
|
||||
if content == nil {
|
||||
content = ast.content
|
||||
}
|
||||
result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
|
||||
for n := ast.FirstChild; n != nil; n = n.Next {
|
||||
result += dumpR(n, depth+1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func dumpString(ast *Node) string {
|
||||
return dumpR(ast, 0)
|
||||
}
|
457
vendor/github.com/russross/blackfriday/v2/smartypants.go
generated
vendored
457
vendor/github.com/russross/blackfriday/v2/smartypants.go
generated
vendored
@ -1,457 +0,0 @@
|
||||
//
|
||||
// Blackfriday Markdown Processor
|
||||
// Available at http://github.com/russross/blackfriday
|
||||
//
|
||||
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||
// Distributed under the Simplified BSD License.
|
||||
// See README.md for details.
|
||||
//
|
||||
|
||||
//
|
||||
//
|
||||
// SmartyPants rendering
|
||||
//
|
||||
//
|
||||
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// SPRenderer is a struct containing state of a Smartypants renderer.
|
||||
type SPRenderer struct {
|
||||
inSingleQuote bool
|
||||
inDoubleQuote bool
|
||||
callbacks [256]smartCallback
|
||||
}
|
||||
|
||||
func wordBoundary(c byte) bool {
|
||||
return c == 0 || isspace(c) || ispunct(c)
|
||||
}
|
||||
|
||||
func tolower(c byte) byte {
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c - 'A' + 'a'
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func isdigit(c byte) bool {
|
||||
return c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote byte, isOpen *bool, addNBSP bool) bool {
|
||||
// edge of the buffer is likely to be a tag that we don't get to see,
|
||||
// so we treat it like text sometimes
|
||||
|
||||
// enumerate all sixteen possibilities for (previousChar, nextChar)
|
||||
// each can be one of {0, space, punct, other}
|
||||
switch {
|
||||
case previousChar == 0 && nextChar == 0:
|
||||
// context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case isspace(previousChar) && nextChar == 0:
|
||||
// [ "] might be [ "<code>foo...]
|
||||
*isOpen = true
|
||||
case ispunct(previousChar) && nextChar == 0:
|
||||
// [!"] hmm... could be [Run!"] or [("<code>...]
|
||||
*isOpen = false
|
||||
case /* isnormal(previousChar) && */ nextChar == 0:
|
||||
// [a"] is probably a close
|
||||
*isOpen = false
|
||||
case previousChar == 0 && isspace(nextChar):
|
||||
// [" ] might be [...foo</code>" ]
|
||||
*isOpen = false
|
||||
case isspace(previousChar) && isspace(nextChar):
|
||||
// [ " ] context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case ispunct(previousChar) && isspace(nextChar):
|
||||
// [!" ] is probably a close
|
||||
*isOpen = false
|
||||
case /* isnormal(previousChar) && */ isspace(nextChar):
|
||||
// [a" ] this is one of the easy cases
|
||||
*isOpen = false
|
||||
case previousChar == 0 && ispunct(nextChar):
|
||||
// ["!] hmm... could be ["$1.95] or [</code>"!...]
|
||||
*isOpen = false
|
||||
case isspace(previousChar) && ispunct(nextChar):
|
||||
// [ "!] looks more like [ "$1.95]
|
||||
*isOpen = true
|
||||
case ispunct(previousChar) && ispunct(nextChar):
|
||||
// [!"!] context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case /* isnormal(previousChar) && */ ispunct(nextChar):
|
||||
// [a"!] is probably a close
|
||||
*isOpen = false
|
||||
case previousChar == 0 /* && isnormal(nextChar) */ :
|
||||
// ["a] is probably an open
|
||||
*isOpen = true
|
||||
case isspace(previousChar) /* && isnormal(nextChar) */ :
|
||||
// [ "a] this is one of the easy cases
|
||||
*isOpen = true
|
||||
case ispunct(previousChar) /* && isnormal(nextChar) */ :
|
||||
// [!"a] is probably an open
|
||||
*isOpen = true
|
||||
default:
|
||||
// [a'b] maybe a contraction?
|
||||
*isOpen = false
|
||||
}
|
||||
|
||||
// Note that with the limited lookahead, this non-breaking
|
||||
// space will also be appended to single double quotes.
|
||||
if addNBSP && !*isOpen {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
|
||||
out.WriteByte('&')
|
||||
if *isOpen {
|
||||
out.WriteByte('l')
|
||||
} else {
|
||||
out.WriteByte('r')
|
||||
}
|
||||
out.WriteByte(quote)
|
||||
out.WriteString("quo;")
|
||||
|
||||
if addNBSP && *isOpen {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 {
|
||||
t1 := tolower(text[1])
|
||||
|
||||
if t1 == '\'' {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if (t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (len(text) < 3 || wordBoundary(text[2])) {
|
||||
out.WriteString("’")
|
||||
return 0
|
||||
}
|
||||
|
||||
if len(text) >= 3 {
|
||||
t2 := tolower(text[2])
|
||||
|
||||
if ((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) &&
|
||||
(len(text) < 4 || wordBoundary(text[3])) {
|
||||
out.WriteString("’")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextChar := byte(0)
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote, false) {
|
||||
return 0
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 {
|
||||
t1 := tolower(text[1])
|
||||
t2 := tolower(text[2])
|
||||
|
||||
if t1 == 'c' && t2 == ')' {
|
||||
out.WriteString("©")
|
||||
return 2
|
||||
}
|
||||
|
||||
if t1 == 'r' && t2 == ')' {
|
||||
out.WriteString("®")
|
||||
return 2
|
||||
}
|
||||
|
||||
if len(text) >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')' {
|
||||
out.WriteString("™")
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 {
|
||||
if text[1] == '-' {
|
||||
out.WriteString("—")
|
||||
return 1
|
||||
}
|
||||
|
||||
if wordBoundary(previousChar) && wordBoundary(text[1]) {
|
||||
out.WriteString("–")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 && text[1] == '-' && text[2] == '-' {
|
||||
out.WriteString("—")
|
||||
return 2
|
||||
}
|
||||
if len(text) >= 2 && text[1] == '-' {
|
||||
out.WriteString("–")
|
||||
return 1
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte, addNBSP bool) int {
|
||||
if bytes.HasPrefix(text, []byte(""")) {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 7 {
|
||||
nextChar = text[6]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, addNBSP) {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(text, []byte("�")) {
|
||||
return 3
|
||||
}
|
||||
|
||||
out.WriteByte('&')
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAmp(angledQuotes, addNBSP bool) func(*bytes.Buffer, byte, []byte) int {
|
||||
var quote byte = 'd'
|
||||
if angledQuotes {
|
||||
quote = 'a'
|
||||
}
|
||||
|
||||
return func(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartAmpVariant(out, previousChar, text, quote, addNBSP)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 && text[1] == '.' && text[2] == '.' {
|
||||
out.WriteString("…")
|
||||
return 2
|
||||
}
|
||||
|
||||
if len(text) >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.' {
|
||||
out.WriteString("…")
|
||||
return 4
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 && text[1] == '`' {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||
// is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b
|
||||
// note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8)
|
||||
// and avoid changing dates like 1/23/2005 into fractions.
|
||||
numEnd := 0
|
||||
for len(text) > numEnd && isdigit(text[numEnd]) {
|
||||
numEnd++
|
||||
}
|
||||
if numEnd == 0 {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
denStart := numEnd + 1
|
||||
if len(text) > numEnd+3 && text[numEnd] == 0xe2 && text[numEnd+1] == 0x81 && text[numEnd+2] == 0x84 {
|
||||
denStart = numEnd + 3
|
||||
} else if len(text) < numEnd+2 || text[numEnd] != '/' {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
denEnd := denStart
|
||||
for len(text) > denEnd && isdigit(text[denEnd]) {
|
||||
denEnd++
|
||||
}
|
||||
if denEnd == denStart {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
if len(text) == denEnd || wordBoundary(text[denEnd]) && text[denEnd] != '/' {
|
||||
out.WriteString("<sup>")
|
||||
out.Write(text[:numEnd])
|
||||
out.WriteString("</sup>⁄<sub>")
|
||||
out.Write(text[denStart:denEnd])
|
||||
out.WriteString("</sub>")
|
||||
return denEnd - 1
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||
if text[0] == '1' && text[1] == '/' && text[2] == '2' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' {
|
||||
out.WriteString("½")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if text[0] == '1' && text[1] == '/' && text[2] == '4' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h') {
|
||||
out.WriteString("¼")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if text[0] == '3' && text[1] == '/' && text[2] == '4' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's') {
|
||||
out.WriteString("¾")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
||||
nextChar := byte(0)
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, false) {
|
||||
out.WriteString(""")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartDoubleQuoteVariant(out, previousChar, text, 'a')
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
i := 0
|
||||
|
||||
for i < len(text) && text[i] != '>' {
|
||||
i++
|
||||
}
|
||||
|
||||
out.Write(text[:i+1])
|
||||
return i
|
||||
}
|
||||
|
||||
type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int
|
||||
|
||||
// NewSmartypantsRenderer constructs a Smartypants renderer object.
|
||||
func NewSmartypantsRenderer(flags HTMLFlags) *SPRenderer {
|
||||
var (
|
||||
r SPRenderer
|
||||
|
||||
smartAmpAngled = r.smartAmp(true, false)
|
||||
smartAmpAngledNBSP = r.smartAmp(true, true)
|
||||
smartAmpRegular = r.smartAmp(false, false)
|
||||
smartAmpRegularNBSP = r.smartAmp(false, true)
|
||||
|
||||
addNBSP = flags&SmartypantsQuotesNBSP != 0
|
||||
)
|
||||
|
||||
if flags&SmartypantsAngledQuotes == 0 {
|
||||
r.callbacks['"'] = r.smartDoubleQuote
|
||||
if !addNBSP {
|
||||
r.callbacks['&'] = smartAmpRegular
|
||||
} else {
|
||||
r.callbacks['&'] = smartAmpRegularNBSP
|
||||
}
|
||||
} else {
|
||||
r.callbacks['"'] = r.smartAngledDoubleQuote
|
||||
if !addNBSP {
|
||||
r.callbacks['&'] = smartAmpAngled
|
||||
} else {
|
||||
r.callbacks['&'] = smartAmpAngledNBSP
|
||||
}
|
||||
}
|
||||
r.callbacks['\''] = r.smartSingleQuote
|
||||
r.callbacks['('] = r.smartParens
|
||||
if flags&SmartypantsDashes != 0 {
|
||||
if flags&SmartypantsLatexDashes == 0 {
|
||||
r.callbacks['-'] = r.smartDash
|
||||
} else {
|
||||
r.callbacks['-'] = r.smartDashLatex
|
||||
}
|
||||
}
|
||||
r.callbacks['.'] = r.smartPeriod
|
||||
if flags&SmartypantsFractions == 0 {
|
||||
r.callbacks['1'] = r.smartNumber
|
||||
r.callbacks['3'] = r.smartNumber
|
||||
} else {
|
||||
for ch := '1'; ch <= '9'; ch++ {
|
||||
r.callbacks[ch] = r.smartNumberGeneric
|
||||
}
|
||||
}
|
||||
r.callbacks['<'] = r.smartLeftAngle
|
||||
r.callbacks['`'] = r.smartBacktick
|
||||
return &r
|
||||
}
|
||||
|
||||
// Process is the entry point of the Smartypants renderer.
|
||||
func (r *SPRenderer) Process(w io.Writer, text []byte) {
|
||||
mark := 0
|
||||
for i := 0; i < len(text); i++ {
|
||||
if action := r.callbacks[text[i]]; action != nil {
|
||||
if i > mark {
|
||||
w.Write(text[mark:i])
|
||||
}
|
||||
previousChar := byte(0)
|
||||
if i > 0 {
|
||||
previousChar = text[i-1]
|
||||
}
|
||||
var tmp bytes.Buffer
|
||||
i += action(&tmp, previousChar, text[i:])
|
||||
w.Write(tmp.Bytes())
|
||||
mark = i + 1
|
||||
}
|
||||
}
|
||||
if mark < len(text) {
|
||||
w.Write(text[mark:])
|
||||
}
|
||||
}
|
27
vendor/github.com/yosssi/gcss/.gitignore
generated
vendored
27
vendor/github.com/yosssi/gcss/.gitignore
generated
vendored
@ -1,27 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
test/*.css
|
||||
cmd/gcss/test/*.css
|
||||
test/e2e/actual/*.css
|
21
vendor/github.com/yosssi/gcss/LICENSE
generated
vendored
21
vendor/github.com/yosssi/gcss/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Keiji Yoshida
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
vendor/github.com/yosssi/gcss/Makefile
generated
vendored
2
vendor/github.com/yosssi/gcss/Makefile
generated
vendored
@ -1,2 +0,0 @@
|
||||
t:
|
||||
@go test -cover -race ./...
|
110
vendor/github.com/yosssi/gcss/README.md
generated
vendored
110
vendor/github.com/yosssi/gcss/README.md
generated
vendored
@ -1,110 +0,0 @@
|
||||
# GCSS - Pure Go CSS Preprocessor
|
||||
|
||||
[](https://app.wercker.com/project/bykey/4857161fd705e6c43df492e6a33ce87f)
|
||||
[](https://ci.appveyor.com/project/yosssi/gcss/branch/master)
|
||||
[](https://coveralls.io/r/yosssi/gcss?branch=master)
|
||||
[](http://godoc.org/github.com/yosssi/gcss)
|
||||
[](https://gitter.im/yosssi/gcss?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
## Overview
|
||||
|
||||
GCSS is a pure Go CSS preprocessor. This is inspired by [Sass](http://sass-lang.com/) and [Stylus](http://learnboost.github.io/stylus/).
|
||||
|
||||
## Syntax
|
||||
|
||||
### Variables
|
||||
|
||||
```scss
|
||||
$base-font: Helvetica, sans-serif
|
||||
$main-color: blue
|
||||
|
||||
body
|
||||
font: 100% $base-font
|
||||
color: $main-color
|
||||
```
|
||||
|
||||
### Nesting
|
||||
|
||||
```scss
|
||||
nav
|
||||
ul
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
a
|
||||
color: blue
|
||||
&:hover
|
||||
color: red
|
||||
```
|
||||
|
||||
### Mixins
|
||||
|
||||
```scss
|
||||
$border-radius($radius)
|
||||
-webkit-border-radius: $radius
|
||||
-moz-border-radius: $radius
|
||||
-ms-border-radius: $radius
|
||||
border-radius: $radius
|
||||
|
||||
.box
|
||||
$border-radius(10px)
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/yosssi/gcss/...
|
||||
```
|
||||
|
||||
## Compile from the Command-Line
|
||||
|
||||
```sh
|
||||
$ gcss /path/to/gcss/file
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```sh
|
||||
$ cat /path/to/gcss/file | gcss > /path/to/css/file
|
||||
```
|
||||
|
||||
## Compile from Go programs
|
||||
|
||||
You can compile a GCSS file from Go programs by invoking the `gcss.CompileFile` function.
|
||||
|
||||
```go
|
||||
cssPath, err := gcss.CompileFile("path_to_gcss_file")
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, cssPath)
|
||||
```
|
||||
|
||||
You can invoke the `gcss.Compile` function instead of the `gcss.CompileFile` function. The `gcss.Compile` function takes `io.Writer` and `io.Reader` as a parameter, compiles the GCSS data which is read from the `io.Reader` and writes the result CSS data to the `io.Writer`. Please see the [GoDoc](http://godoc.org/github.com/yosssi/gcss) for the details.
|
||||
|
||||
```go
|
||||
f, err := os.Open("path_to_gcss_file")
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := gcss.Compile(os.Stdout, f)
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
* [GoDoc](http://godoc.org/github.com/yosssi/gcss)
|
||||
|
||||
## Syntax Highlightings
|
||||
|
||||
* [vim-gcss](https://github.com/yosssi/vim-gcss) - Vim syntax highlighting for GCSS
|
59
vendor/github.com/yosssi/gcss/at_rule.go
generated
vendored
59
vendor/github.com/yosssi/gcss/at_rule.go
generated
vendored
@ -1,59 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// atRule represents an at-rule of CSS.
|
||||
type atRule struct {
|
||||
elementBase
|
||||
}
|
||||
|
||||
// WriteTo writes the at-rule to the writer.
|
||||
func (ar *atRule) WriteTo(w io.Writer) (int64, error) {
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
bf.WriteString(strings.TrimSpace(ar.ln.s))
|
||||
|
||||
if len(ar.sels) == 0 && len(ar.decs) == 0 && !ar.hasMixinDecs() && !ar.hasMixinSels() {
|
||||
bf.WriteString(semicolon)
|
||||
n, err := w.Write(bf.Bytes())
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
bf.WriteString(openBrace)
|
||||
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
ar.writeDecsTo(bf, nil)
|
||||
|
||||
for _, sel := range ar.sels {
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
sel.WriteTo(bf)
|
||||
}
|
||||
|
||||
// Write the mixin's selectors.
|
||||
for _, mi := range ar.mixins {
|
||||
sels, prms := mi.selsParams()
|
||||
|
||||
for _, sl := range sels {
|
||||
sl.parent = ar
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
sl.writeTo(bf, prms)
|
||||
}
|
||||
}
|
||||
|
||||
bf.WriteString(closeBrace)
|
||||
|
||||
n, err := w.Write(bf.Bytes())
|
||||
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// newAtRule creates and returns a at-rule.
|
||||
func newAtRule(ln *line, parent element) *atRule {
|
||||
return &atRule{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
}
|
||||
}
|
20
vendor/github.com/yosssi/gcss/comment.go
generated
vendored
20
vendor/github.com/yosssi/gcss/comment.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import "io"
|
||||
|
||||
// comment represents a comment of CSS.
|
||||
type comment struct {
|
||||
elementBase
|
||||
}
|
||||
|
||||
// WriteTo does nothing.
|
||||
func (c *comment) WriteTo(w io.Writer) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// newComment creates and returns a comment.
|
||||
func newComment(ln *line, parent element) *comment {
|
||||
return &comment{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
}
|
||||
}
|
130
vendor/github.com/yosssi/gcss/compile.go
generated
vendored
130
vendor/github.com/yosssi/gcss/compile.go
generated
vendored
@ -1,130 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// extensions
|
||||
const (
|
||||
extCSS = ".css"
|
||||
extGCSS = ".gcss"
|
||||
)
|
||||
|
||||
// cssFilePath converts path's extenstion into a CSS file extension.
|
||||
var cssFilePath = func(path string) string {
|
||||
return convertExt(path, extCSS)
|
||||
}
|
||||
|
||||
// Compile compiles GCSS data which is read from src and
|
||||
// Writes the result CSS data to the dst.
|
||||
func Compile(dst io.Writer, src io.Reader) (int, error) {
|
||||
data, err := ioutil.ReadAll(src)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
bc, berrc := compileBytes(data)
|
||||
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
BufWriteLoop:
|
||||
for {
|
||||
select {
|
||||
case b, ok := <-bc:
|
||||
if !ok {
|
||||
break BufWriteLoop
|
||||
}
|
||||
|
||||
bf.Write(b)
|
||||
case err := <-berrc:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return dst.Write(bf.Bytes())
|
||||
}
|
||||
|
||||
// CompileFile parses the GCSS file specified by the path parameter,
|
||||
// generates a CSS file and returns the path of the generated CSS file
|
||||
// and an error when it occurs.
|
||||
func CompileFile(path string) (string, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cssPath := cssFilePath(path)
|
||||
|
||||
bc, berrc := compileBytes(data)
|
||||
|
||||
done, werrc := write(cssPath, bc, berrc)
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case err := <-werrc:
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cssPath, nil
|
||||
}
|
||||
|
||||
// compileBytes parses the GCSS byte array passed as the s parameter,
|
||||
// generates a CSS byte array and returns the two channels: the first
|
||||
// one returns the CSS byte array and the last one returns an error
|
||||
// when it occurs.
|
||||
func compileBytes(b []byte) (<-chan []byte, <-chan error) {
|
||||
lines := strings.Split(formatLF(string(b)), lf)
|
||||
|
||||
bc := make(chan []byte, len(lines))
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
ctx := newContext()
|
||||
|
||||
elemc, pErrc := parse(lines)
|
||||
|
||||
for {
|
||||
select {
|
||||
case elem, ok := <-elemc:
|
||||
if !ok {
|
||||
close(bc)
|
||||
return
|
||||
}
|
||||
|
||||
elem.SetContext(ctx)
|
||||
|
||||
switch v := elem.(type) {
|
||||
case *mixinDeclaration:
|
||||
ctx.mixins[v.name] = v
|
||||
case *variable:
|
||||
ctx.vars[v.name] = v
|
||||
case *atRule, *declaration, *selector:
|
||||
bf := new(bytes.Buffer)
|
||||
elem.WriteTo(bf)
|
||||
bc <- bf.Bytes()
|
||||
}
|
||||
case err := <-pErrc:
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return bc, errc
|
||||
}
|
||||
|
||||
// Path converts path's extenstion into a GCSS file extension.
|
||||
func Path(path string) string {
|
||||
return convertExt(path, extGCSS)
|
||||
}
|
||||
|
||||
// convertExt converts path's extension into ext.
|
||||
func convertExt(path string, ext string) string {
|
||||
return strings.TrimSuffix(path, filepath.Ext(path)) + ext
|
||||
}
|
15
vendor/github.com/yosssi/gcss/context.go
generated
vendored
15
vendor/github.com/yosssi/gcss/context.go
generated
vendored
@ -1,15 +0,0 @@
|
||||
package gcss
|
||||
|
||||
// context represents a context of the parsing process.
|
||||
type context struct {
|
||||
vars map[string]*variable
|
||||
mixins map[string]*mixinDeclaration
|
||||
}
|
||||
|
||||
// newContext creates and returns a context.
|
||||
func newContext() *context {
|
||||
return &context{
|
||||
vars: make(map[string]*variable),
|
||||
mixins: make(map[string]*mixinDeclaration),
|
||||
}
|
||||
}
|
109
vendor/github.com/yosssi/gcss/declaration.go
generated
vendored
109
vendor/github.com/yosssi/gcss/declaration.go
generated
vendored
@ -1,109 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// declaration represents a declaration of CSS.
|
||||
type declaration struct {
|
||||
elementBase
|
||||
property string
|
||||
value string
|
||||
}
|
||||
|
||||
// WriteTo writes the declaration to the writer.
|
||||
func (dec *declaration) WriteTo(w io.Writer) (int64, error) {
|
||||
return dec.writeTo(w, nil)
|
||||
}
|
||||
|
||||
// writeTo writes the declaration to the writer.
|
||||
func (dec *declaration) writeTo(w io.Writer, params map[string]string) (int64, error) {
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
bf.WriteString(dec.property)
|
||||
bf.WriteString(colon)
|
||||
|
||||
for i, v := range strings.Split(dec.value, space) {
|
||||
if i > 0 {
|
||||
bf.WriteString(space)
|
||||
}
|
||||
|
||||
for j, w := range strings.Split(v, comma) {
|
||||
if j > 0 {
|
||||
bf.WriteString(comma)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(w, dollarMark) {
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
dec.writeParamTo(bf, strings.TrimPrefix(w, dollarMark), params)
|
||||
} else {
|
||||
bf.WriteString(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bf.WriteString(semicolon)
|
||||
|
||||
n, err := w.Write(bf.Bytes())
|
||||
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// writeParam writes the param to the writer.
|
||||
func (dec *declaration) writeParamTo(w io.Writer, name string, params map[string]string) (int64, error) {
|
||||
if s, ok := params[name]; ok {
|
||||
if strings.HasPrefix(s, dollarMark) {
|
||||
if v, ok := dec.Context().vars[strings.TrimPrefix(s, dollarMark)]; ok {
|
||||
return v.WriteTo(w)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
n, err := w.Write([]byte(s))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
if v, ok := dec.Context().vars[name]; ok {
|
||||
return v.WriteTo(w)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// declarationPV extracts a declaration property and value
|
||||
// from the line.
|
||||
func declarationPV(ln *line) (string, string, error) {
|
||||
pv := strings.SplitN(strings.TrimSpace(ln.s), space, 2)
|
||||
|
||||
if len(pv) < 2 {
|
||||
return "", "", fmt.Errorf("declaration's property and value should be divided by a space [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(pv[0], colon) {
|
||||
return "", "", fmt.Errorf("property should end with a colon [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(pv[0], colon), pv[1], nil
|
||||
}
|
||||
|
||||
// newDeclaration creates and returns a declaration.
|
||||
func newDeclaration(ln *line, parent element) (*declaration, error) {
|
||||
property, value, err := declarationPV(ln)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(value, semicolon) {
|
||||
return nil, fmt.Errorf("declaration must not end with %q [line: %d]", semicolon, ln.no)
|
||||
}
|
||||
|
||||
return &declaration{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
property: property,
|
||||
value: value,
|
||||
}, nil
|
||||
}
|
2
vendor/github.com/yosssi/gcss/doc.go
generated
vendored
2
vendor/github.com/yosssi/gcss/doc.go
generated
vendored
@ -1,2 +0,0 @@
|
||||
// Package gcss provides the CSS preprocessor.
|
||||
package gcss
|
41
vendor/github.com/yosssi/gcss/element.go
generated
vendored
41
vendor/github.com/yosssi/gcss/element.go
generated
vendored
@ -1,41 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import "io"
|
||||
|
||||
// element represents an element of GCSS source codes.
|
||||
type element interface {
|
||||
io.WriterTo
|
||||
AppendChild(child element)
|
||||
Base() *elementBase
|
||||
SetContext(*context)
|
||||
Context() *context
|
||||
}
|
||||
|
||||
// newElement creates and returns an element.
|
||||
func newElement(ln *line, parent element) (element, error) {
|
||||
var e element
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case ln.isComment():
|
||||
e = newComment(ln, parent)
|
||||
case ln.isAtRule():
|
||||
e = newAtRule(ln, parent)
|
||||
case ln.isMixinDeclaration():
|
||||
// error can be ignored becuase the line is checked beforehand
|
||||
// by calling `ln.isMixinDeclaration()`.
|
||||
e, _ = newMixinDeclaration(ln, parent)
|
||||
case ln.isMixinInvocation():
|
||||
// error can be ignored becuase the line is checked beforehand
|
||||
// by calling `ln.isMixinInvocation()`.
|
||||
e, _ = newMixinInvocation(ln, parent)
|
||||
case ln.isVariable():
|
||||
e, err = newVariable(ln, parent)
|
||||
case ln.isDeclaration():
|
||||
e, err = newDeclaration(ln, parent)
|
||||
default:
|
||||
e, err = newSelector(ln, parent)
|
||||
}
|
||||
|
||||
return e, err
|
||||
}
|
104
vendor/github.com/yosssi/gcss/element_base.go
generated
vendored
104
vendor/github.com/yosssi/gcss/element_base.go
generated
vendored
@ -1,104 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// elementBase holds the common fields of an element.
|
||||
type elementBase struct {
|
||||
ln *line
|
||||
parent element
|
||||
sels []*selector
|
||||
decs []*declaration
|
||||
mixins []*mixinInvocation
|
||||
ctx *context
|
||||
}
|
||||
|
||||
// AppendChild appends a child element to the element.
|
||||
func (eBase *elementBase) AppendChild(child element) {
|
||||
switch c := child.(type) {
|
||||
case *mixinInvocation:
|
||||
eBase.mixins = append(eBase.mixins, c)
|
||||
case *declaration:
|
||||
eBase.decs = append(eBase.decs, c)
|
||||
case *selector:
|
||||
eBase.sels = append(eBase.sels, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Base returns the element base.
|
||||
func (eBase *elementBase) Base() *elementBase {
|
||||
return eBase
|
||||
}
|
||||
|
||||
// SetContext sets the context to the element.
|
||||
func (eBase *elementBase) SetContext(ctx *context) {
|
||||
eBase.ctx = ctx
|
||||
}
|
||||
|
||||
// Context returns the top element's context.
|
||||
func (eBase *elementBase) Context() *context {
|
||||
if eBase.parent != nil {
|
||||
return eBase.parent.Context()
|
||||
}
|
||||
|
||||
return eBase.ctx
|
||||
}
|
||||
|
||||
// hasMixinDecs returns true if the element has a mixin
|
||||
// which has declarations.
|
||||
func (eBase *elementBase) hasMixinDecs() bool {
|
||||
for _, mi := range eBase.mixins {
|
||||
if decs, _ := mi.decsParams(); len(decs) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// hasMixinSels returns true if the element has a mixin
|
||||
// which has selectors.
|
||||
func (eBase *elementBase) hasMixinSels() bool {
|
||||
for _, mi := range eBase.mixins {
|
||||
if sels, _ := mi.selsParams(); len(sels) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// writeDecsTo writes the element's declarations to w.
|
||||
func (eBase *elementBase) writeDecsTo(w io.Writer, params map[string]string) (int64, error) {
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
// Write the declarations.
|
||||
for _, dec := range eBase.decs {
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
dec.writeTo(bf, params)
|
||||
}
|
||||
|
||||
// Write the mixin's declarations.
|
||||
for _, mi := range eBase.mixins {
|
||||
decs, prms := mi.decsParams()
|
||||
|
||||
for _, dec := range decs {
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
dec.writeTo(bf, prms)
|
||||
}
|
||||
}
|
||||
|
||||
n, err := w.Write(bf.Bytes())
|
||||
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// newElementBase creates and returns an element base.
|
||||
func newElementBase(ln *line, parent element) elementBase {
|
||||
return elementBase{
|
||||
ln: ln,
|
||||
parent: parent,
|
||||
}
|
||||
}
|
114
vendor/github.com/yosssi/gcss/line.go
generated
vendored
114
vendor/github.com/yosssi/gcss/line.go
generated
vendored
@ -1,114 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const unicodeSpace = 32
|
||||
|
||||
const indentTop = 0
|
||||
|
||||
// line represents a line of codes.
|
||||
type line struct {
|
||||
no int
|
||||
s string
|
||||
indent int
|
||||
}
|
||||
|
||||
// isEmpty returns true if the line's s is zero value.
|
||||
func (ln *line) isEmpty() bool {
|
||||
return strings.TrimSpace(ln.s) == ""
|
||||
}
|
||||
|
||||
// isTopIndent returns true if the line's indent is the top level.
|
||||
func (ln *line) isTopIndent() bool {
|
||||
return ln.indent == indentTop
|
||||
}
|
||||
|
||||
// childOf returns true if the line is a child of the parent.
|
||||
func (ln *line) childOf(parent element) (bool, error) {
|
||||
var ok bool
|
||||
var err error
|
||||
|
||||
switch pIndent := parent.Base().ln.indent; {
|
||||
case ln.indent == pIndent+1:
|
||||
ok = true
|
||||
case ln.indent > pIndent+1:
|
||||
err = fmt.Errorf("indent is invalid [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
return ok, err
|
||||
}
|
||||
|
||||
// isDeclaration returns true if the line is a declaration.
|
||||
func (ln *line) isDeclaration() bool {
|
||||
_, _, err := declarationPV(ln)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// isAtRule returns true if the line is an at-rule.
|
||||
func (ln *line) isAtRule() bool {
|
||||
return strings.HasPrefix(strings.TrimSpace(ln.s), atMark)
|
||||
}
|
||||
|
||||
// isVariable returns true if the line is a variable.
|
||||
func (ln *line) isVariable() bool {
|
||||
if !ln.isTopIndent() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, _, err := variableNV(ln)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// isMixinDeclaration returns true if the line is a mixin declaration.
|
||||
func (ln *line) isMixinDeclaration() bool {
|
||||
if !ln.isTopIndent() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, _, err := mixinNP(ln, true)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// isMixinInvocation returns true if the line is a mixin invocation.
|
||||
func (ln *line) isMixinInvocation() bool {
|
||||
if ln.isTopIndent() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, _, err := mixinNP(ln, false)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// isComment returns true if the line is a comment.
|
||||
func (ln *line) isComment() bool {
|
||||
return strings.HasPrefix(strings.TrimSpace(ln.s), doubleSlash)
|
||||
}
|
||||
|
||||
// newLine creates and returns a line.
|
||||
func newLine(no int, s string) *line {
|
||||
return &line{
|
||||
no: no,
|
||||
s: s,
|
||||
indent: indent(s),
|
||||
}
|
||||
}
|
||||
|
||||
// indent returns the string's indent.
|
||||
func indent(s string) int {
|
||||
var i int
|
||||
|
||||
for _, b := range s {
|
||||
if b != unicodeSpace {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return i / 2
|
||||
}
|
85
vendor/github.com/yosssi/gcss/mixin_declaration.go
generated
vendored
85
vendor/github.com/yosssi/gcss/mixin_declaration.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// mixinDeclaration represents a mixin declaration.
|
||||
type mixinDeclaration struct {
|
||||
elementBase
|
||||
name string
|
||||
paramNames []string
|
||||
}
|
||||
|
||||
// WriteTo writes the selector to the writer.
|
||||
func (md *mixinDeclaration) WriteTo(w io.Writer) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// mixinNP extracts a mixin name and parameters from the line.
|
||||
func mixinNP(ln *line, isDeclaration bool) (string, []string, error) {
|
||||
s := strings.TrimSpace(ln.s)
|
||||
|
||||
if !strings.HasPrefix(s, dollarMark) {
|
||||
return "", nil, fmt.Errorf("mixin must start with %q [line: %d]", dollarMark, ln.no)
|
||||
}
|
||||
|
||||
s = strings.TrimPrefix(s, dollarMark)
|
||||
|
||||
np := strings.Split(s, openParenthesis)
|
||||
|
||||
if len(np) != 2 {
|
||||
return "", nil, fmt.Errorf("mixin's format is invalid [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
paramsS := strings.TrimSpace(np[1])
|
||||
|
||||
if !strings.HasSuffix(paramsS, closeParenthesis) {
|
||||
return "", nil, fmt.Errorf("mixin must end with %q [line: %d]", closeParenthesis, ln.no)
|
||||
}
|
||||
|
||||
paramsS = strings.TrimSuffix(paramsS, closeParenthesis)
|
||||
|
||||
if strings.Index(paramsS, closeParenthesis) != -1 {
|
||||
return "", nil, fmt.Errorf("mixin's format is invalid [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
var params []string
|
||||
|
||||
if paramsS != "" {
|
||||
params = strings.Split(paramsS, comma)
|
||||
}
|
||||
|
||||
for i, p := range params {
|
||||
p = strings.TrimSpace(p)
|
||||
|
||||
if isDeclaration {
|
||||
if !strings.HasPrefix(p, dollarMark) {
|
||||
return "", nil, fmt.Errorf("mixin's parameter must start with %q [line: %d]", dollarMark, ln.no)
|
||||
}
|
||||
|
||||
p = strings.TrimPrefix(p, dollarMark)
|
||||
}
|
||||
|
||||
params[i] = p
|
||||
}
|
||||
|
||||
return np[0], params, nil
|
||||
}
|
||||
|
||||
// newMixinDeclaration creates and returns a mixin declaration.
|
||||
func newMixinDeclaration(ln *line, parent element) (*mixinDeclaration, error) {
|
||||
name, paramNames, err := mixinNP(ln, true)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mixinDeclaration{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
name: name,
|
||||
paramNames: paramNames,
|
||||
}, nil
|
||||
}
|
72
vendor/github.com/yosssi/gcss/mixin_invocation.go
generated
vendored
72
vendor/github.com/yosssi/gcss/mixin_invocation.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import "io"
|
||||
|
||||
// mixinInvocation represents a mixin invocation.
|
||||
type mixinInvocation struct {
|
||||
elementBase
|
||||
name string
|
||||
paramValues []string
|
||||
}
|
||||
|
||||
// WriteTo writes the selector to the writer.
|
||||
func (mi *mixinInvocation) WriteTo(w io.Writer) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// decsParams returns the mixin's declarations and params.
|
||||
func (mi *mixinInvocation) decsParams() ([]*declaration, map[string]string) {
|
||||
md, ok := mi.Context().mixins[mi.name]
|
||||
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
|
||||
l := len(mi.paramValues)
|
||||
|
||||
for i, name := range md.paramNames {
|
||||
if i < l {
|
||||
params[name] = mi.paramValues[i]
|
||||
}
|
||||
}
|
||||
|
||||
return md.decs, params
|
||||
}
|
||||
|
||||
// selsParams returns the mixin's selectors and params.
|
||||
func (mi *mixinInvocation) selsParams() ([]*selector, map[string]string) {
|
||||
md, ok := mi.Context().mixins[mi.name]
|
||||
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
|
||||
l := len(mi.paramValues)
|
||||
|
||||
for i, name := range md.paramNames {
|
||||
if i < l {
|
||||
params[name] = mi.paramValues[i]
|
||||
}
|
||||
}
|
||||
|
||||
return md.sels, params
|
||||
}
|
||||
|
||||
// newMixinInvocation creates and returns a mixin invocation.
|
||||
func newMixinInvocation(ln *line, parent element) (*mixinInvocation, error) {
|
||||
name, paramValues, err := mixinNP(ln, false)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mixinInvocation{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
name: name,
|
||||
paramValues: paramValues,
|
||||
}, nil
|
||||
}
|
115
vendor/github.com/yosssi/gcss/parse.go
generated
vendored
115
vendor/github.com/yosssi/gcss/parse.go
generated
vendored
@ -1,115 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import "strings"
|
||||
|
||||
// Special characters
|
||||
const (
|
||||
cr = "\r"
|
||||
lf = "\n"
|
||||
crlf = "\r\n"
|
||||
space = " "
|
||||
colon = ":"
|
||||
comma = ","
|
||||
openBrace = "{"
|
||||
closeBrace = "}"
|
||||
semicolon = ";"
|
||||
ampersand = "&"
|
||||
atMark = "@"
|
||||
dollarMark = "$"
|
||||
openParenthesis = "("
|
||||
closeParenthesis = ")"
|
||||
slash = "/"
|
||||
doubleSlash = slash + slash
|
||||
)
|
||||
|
||||
// parse parses the string, generates the elements
|
||||
// and returns the two channels: the first one returns
|
||||
// the generated elements and the last one returns
|
||||
// an error when it occurs.
|
||||
func parse(lines []string) (<-chan element, <-chan error) {
|
||||
elemc := make(chan element, len(lines))
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
i := 0
|
||||
l := len(lines)
|
||||
|
||||
for i < l {
|
||||
// Fetch a line.
|
||||
ln := newLine(i+1, lines[i])
|
||||
i++
|
||||
|
||||
// Ignore the empty line.
|
||||
if ln.isEmpty() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ln.isTopIndent() {
|
||||
elem, err := newElement(ln, nil)
|
||||
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err := appendChildren(elem, lines, &i, l); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
elemc <- elem
|
||||
}
|
||||
}
|
||||
|
||||
close(elemc)
|
||||
}()
|
||||
|
||||
return elemc, errc
|
||||
}
|
||||
|
||||
// appendChildren parses the lines and appends the child elements
|
||||
// to the parent element.
|
||||
func appendChildren(parent element, lines []string, i *int, l int) error {
|
||||
for *i < l {
|
||||
// Fetch a line.
|
||||
ln := newLine(*i+1, lines[*i])
|
||||
|
||||
// Ignore the empty line.
|
||||
if ln.isEmpty() {
|
||||
*i++
|
||||
return nil
|
||||
}
|
||||
|
||||
ok, err := ln.childOf(parent)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
child, err := newElement(ln, parent)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parent.AppendChild(child)
|
||||
|
||||
*i++
|
||||
|
||||
if err := appendChildren(child, lines, i, l); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatLF replaces the line feed codes with LF and
|
||||
// returns the result string.
|
||||
func formatLF(s string) string {
|
||||
return strings.Replace(strings.Replace(s, crlf, lf, -1), cr, lf, -1)
|
||||
}
|
110
vendor/github.com/yosssi/gcss/selector.go
generated
vendored
110
vendor/github.com/yosssi/gcss/selector.go
generated
vendored
@ -1,110 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// selector represents a selector of CSS.
|
||||
type selector struct {
|
||||
elementBase
|
||||
name string
|
||||
}
|
||||
|
||||
// WriteTo writes the selector to the writer.
|
||||
func (sel *selector) WriteTo(w io.Writer) (int64, error) {
|
||||
return sel.writeTo(w, nil)
|
||||
}
|
||||
|
||||
// writeTo writes the selector to the writer.
|
||||
func (sel *selector) writeTo(w io.Writer, params map[string]string) (int64, error) {
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
// Write the declarations.
|
||||
if len(sel.decs) > 0 || sel.hasMixinDecs() {
|
||||
bf.WriteString(sel.names())
|
||||
bf.WriteString(openBrace)
|
||||
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
sel.writeDecsTo(bf, params)
|
||||
|
||||
bf.WriteString(closeBrace)
|
||||
}
|
||||
|
||||
// Write the child selectors.
|
||||
for _, childSel := range sel.sels {
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
childSel.writeTo(bf, params)
|
||||
}
|
||||
|
||||
// Write the mixin's selectors.
|
||||
for _, mi := range sel.mixins {
|
||||
sels, prms := mi.selsParams()
|
||||
|
||||
for _, sl := range sels {
|
||||
sl.parent = sel
|
||||
// Writing to the bytes.Buffer never returns an error.
|
||||
sl.writeTo(bf, prms)
|
||||
}
|
||||
}
|
||||
|
||||
n, err := w.Write(bf.Bytes())
|
||||
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// names returns the selector names.
|
||||
func (sel *selector) names() string {
|
||||
bf := new(bytes.Buffer)
|
||||
|
||||
switch parent := sel.parent.(type) {
|
||||
case nil, *atRule:
|
||||
for _, name := range strings.Split(sel.name, comma) {
|
||||
if bf.Len() > 0 {
|
||||
bf.WriteString(comma)
|
||||
}
|
||||
|
||||
bf.WriteString(strings.TrimSpace(name))
|
||||
}
|
||||
case *selector:
|
||||
for _, parentS := range strings.Split(parent.names(), comma) {
|
||||
for _, s := range strings.Split(sel.name, comma) {
|
||||
if bf.Len() > 0 {
|
||||
bf.WriteString(comma)
|
||||
}
|
||||
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
if strings.Index(s, ampersand) != -1 {
|
||||
bf.WriteString(strings.Replace(s, ampersand, parentS, -1))
|
||||
} else {
|
||||
bf.WriteString(parentS)
|
||||
bf.WriteString(space)
|
||||
bf.WriteString(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bf.String()
|
||||
}
|
||||
|
||||
// newSelector creates and returns a selector.
|
||||
func newSelector(ln *line, parent element) (*selector, error) {
|
||||
name := strings.TrimSpace(ln.s)
|
||||
|
||||
if strings.HasSuffix(name, openBrace) {
|
||||
return nil, fmt.Errorf("selector must not end with %q [line: %d]", openBrace, ln.no)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(name, closeBrace) {
|
||||
return nil, fmt.Errorf("selector must not end with %q [line: %d]", closeBrace, ln.no)
|
||||
}
|
||||
|
||||
return &selector{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
62
vendor/github.com/yosssi/gcss/variable.go
generated
vendored
62
vendor/github.com/yosssi/gcss/variable.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// variable represents a GCSS variable.
|
||||
type variable struct {
|
||||
elementBase
|
||||
name string
|
||||
value string
|
||||
}
|
||||
|
||||
// WriteTo writes the variable to the writer.
|
||||
func (v *variable) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write([]byte(v.value))
|
||||
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// variableNV extracts a variable name and value
|
||||
// from the line.
|
||||
func variableNV(ln *line) (string, string, error) {
|
||||
s := strings.TrimSpace(ln.s)
|
||||
|
||||
if !strings.HasPrefix(s, dollarMark) {
|
||||
return "", "", fmt.Errorf("variable must start with %q [line: %d]", dollarMark, ln.no)
|
||||
}
|
||||
|
||||
nv := strings.SplitN(s, space, 2)
|
||||
|
||||
if len(nv) < 2 {
|
||||
return "", "", fmt.Errorf("variable's name and value should be divided by a space [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(nv[0], colon) {
|
||||
return "", "", fmt.Errorf("variable's name should end with a colon [line: %d]", ln.no)
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(strings.TrimPrefix(nv[0], dollarMark), colon), nv[1], nil
|
||||
}
|
||||
|
||||
// newVariable creates and returns a variable.
|
||||
func newVariable(ln *line, parent element) (*variable, error) {
|
||||
name, value, err := variableNV(ln)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(value, semicolon) {
|
||||
return nil, fmt.Errorf("variable must not end with %q", semicolon)
|
||||
}
|
||||
|
||||
return &variable{
|
||||
elementBase: newElementBase(ln, parent),
|
||||
name: name,
|
||||
value: value,
|
||||
}, nil
|
||||
}
|
4
vendor/github.com/yosssi/gcss/version.go
generated
vendored
4
vendor/github.com/yosssi/gcss/version.go
generated
vendored
@ -1,4 +0,0 @@
|
||||
package gcss
|
||||
|
||||
// Version is the version of GCSS.
|
||||
const Version = "GCSS 0.1.0"
|
39
vendor/github.com/yosssi/gcss/wercker.yml
generated
vendored
39
vendor/github.com/yosssi/gcss/wercker.yml
generated
vendored
@ -1,39 +0,0 @@
|
||||
box: yosssi/golang-latest@1.0.7
|
||||
# Build definition
|
||||
build:
|
||||
# The steps that will be executed on build
|
||||
steps:
|
||||
# Sets the go workspace and places you package
|
||||
# at the right place in the workspace tree
|
||||
- setup-go-workspace
|
||||
|
||||
# Gets the dependencies
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
cd $WERCKER_SOURCE_DIR
|
||||
go version
|
||||
go get -t ./...
|
||||
|
||||
# Build the project
|
||||
- script:
|
||||
name: go build
|
||||
code: |
|
||||
go build ./...
|
||||
|
||||
# Test the project
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -cover -race ./...
|
||||
|
||||
# Invoke goveralls
|
||||
- script:
|
||||
name: goveralls
|
||||
code: |
|
||||
go get github.com/axw/gocov/gocov
|
||||
go get github.com/mattn/goveralls
|
||||
echo "mode: count" > all.cov
|
||||
packages=(. cmd/gcss)
|
||||
for package in ${packages[@]}; do go test --covermode=count -coverprofile=$package.cov ./$package; sed -e "1d" $package.cov >> all.cov; done
|
||||
GIT_BRANCH=$WERCKER_GIT_BRANCH goveralls -coverprofile=all.cov -service=wercker.com -repotoken $COVERALLS_REPO_TOKEN
|
62
vendor/github.com/yosssi/gcss/write.go
generated
vendored
62
vendor/github.com/yosssi/gcss/write.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
package gcss
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// writeFlusher is the interface that groups the basic Write and Flush methods.
|
||||
type writeFlusher interface {
|
||||
io.Writer
|
||||
Flush() error
|
||||
}
|
||||
|
||||
var newBufWriter = func(w io.Writer) writeFlusher {
|
||||
return bufio.NewWriter(w)
|
||||
}
|
||||
|
||||
// write writes the input byte data to the CSS file.
|
||||
func write(path string, bc <-chan []byte, berrc <-chan error) (<-chan struct{}, <-chan error) {
|
||||
done := make(chan struct{})
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
f, err := os.Create(path)
|
||||
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
w := newBufWriter(f)
|
||||
|
||||
for {
|
||||
select {
|
||||
case b, ok := <-bc:
|
||||
if !ok {
|
||||
if err := w.Flush(); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
done <- struct{}{}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := w.Write(b); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
case err := <-berrc:
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return done, errc
|
||||
}
|
50
vendor/gopkg.in/yaml.v3/LICENSE
generated
vendored
50
vendor/gopkg.in/yaml.v3/LICENSE
generated
vendored
@ -1,50 +0,0 @@
|
||||
|
||||
This project is covered by two different licenses: MIT and Apache.
|
||||
|
||||
#### MIT License ####
|
||||
|
||||
The following files were ported to Go from C files of libyaml, and thus
|
||||
are still covered by their original MIT license, with the additional
|
||||
copyright staring in 2011 when the project was ported over:
|
||||
|
||||
apic.go emitterc.go parserc.go readerc.go scannerc.go
|
||||
writerc.go yamlh.go yamlprivateh.go
|
||||
|
||||
Copyright (c) 2006-2010 Kirill Simonov
|
||||
Copyright (c) 2006-2011 Kirill Simonov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### Apache License ###
|
||||
|
||||
All the remaining project files are covered by the Apache license:
|
||||
|
||||
Copyright (c) 2011-2019 Canonical Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
13
vendor/gopkg.in/yaml.v3/NOTICE
generated
vendored
13
vendor/gopkg.in/yaml.v3/NOTICE
generated
vendored
@ -1,13 +0,0 @@
|
||||
Copyright 2011-2016 Canonical Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
150
vendor/gopkg.in/yaml.v3/README.md
generated
vendored
150
vendor/gopkg.in/yaml.v3/README.md
generated
vendored
@ -1,150 +0,0 @@
|
||||
# YAML support for the Go language
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The yaml package enables Go programs to comfortably encode and decode YAML
|
||||
values. It was developed within [Canonical](https://www.canonical.com) as
|
||||
part of the [juju](https://juju.ubuntu.com) project, and is based on a
|
||||
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
|
||||
C library to parse and generate YAML data quickly and reliably.
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
The yaml package supports most of YAML 1.2, but preserves some behavior
|
||||
from 1.1 for backwards compatibility.
|
||||
|
||||
Specifically, as of v3 of the yaml package:
|
||||
|
||||
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being
|
||||
decoded into a typed bool value. Otherwise they behave as a string. Booleans
|
||||
in YAML 1.2 are _true/false_ only.
|
||||
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_
|
||||
as specified in YAML 1.2, because most parsers still use the old format.
|
||||
Octals in the _0o777_ format are supported though, so new files work.
|
||||
- Does not support base-60 floats. These are gone from YAML 1.2, and were
|
||||
actually never supported by this package as it's clearly a poor choice.
|
||||
|
||||
and offers backwards
|
||||
compatibility with YAML 1.1 in some cases.
|
||||
1.2, including support for
|
||||
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
|
||||
implemented, and base-60 floats from YAML 1.1 are purposefully not
|
||||
supported since they're a poor design and are gone in YAML 1.2.
|
||||
|
||||
Installation and usage
|
||||
----------------------
|
||||
|
||||
The import path for the package is *gopkg.in/yaml.v3*.
|
||||
|
||||
To install it, run:
|
||||
|
||||
go get gopkg.in/yaml.v3
|
||||
|
||||
API documentation
|
||||
-----------------
|
||||
|
||||
If opened in a browser, the import path itself leads to the API documentation:
|
||||
|
||||
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3)
|
||||
|
||||
API stability
|
||||
-------------
|
||||
|
||||
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in).
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
|
||||
Please see the LICENSE file for details.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var data = `
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d: [3, 4]
|
||||
`
|
||||
|
||||
// Note: struct fields must be public in order for unmarshal to
|
||||
// correctly populate the data.
|
||||
type T struct {
|
||||
A string
|
||||
B struct {
|
||||
RenamedC int `yaml:"c"`
|
||||
D []int `yaml:",flow"`
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := T{}
|
||||
|
||||
err := yaml.Unmarshal([]byte(data), &t)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- t:\n%v\n\n", t)
|
||||
|
||||
d, err := yaml.Marshal(&t)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- t dump:\n%s\n\n", string(d))
|
||||
|
||||
m := make(map[interface{}]interface{})
|
||||
|
||||
err = yaml.Unmarshal([]byte(data), &m)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- m:\n%v\n\n", m)
|
||||
|
||||
d, err = yaml.Marshal(&m)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- m dump:\n%s\n\n", string(d))
|
||||
}
|
||||
```
|
||||
|
||||
This example will generate the following output:
|
||||
|
||||
```
|
||||
--- t:
|
||||
{Easy! {2 [3 4]}}
|
||||
|
||||
--- t dump:
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d: [3, 4]
|
||||
|
||||
|
||||
--- m:
|
||||
map[a:Easy! b:map[c:2 d:[3 4]]]
|
||||
|
||||
--- m dump:
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d:
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
|
747
vendor/gopkg.in/yaml.v3/apic.go
generated
vendored
747
vendor/gopkg.in/yaml.v3/apic.go
generated
vendored
@ -1,747 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
// Copyright (c) 2006-2010 Kirill Simonov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
|
||||
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
|
||||
|
||||
// Check if we can move the queue at the beginning of the buffer.
|
||||
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
|
||||
if parser.tokens_head != len(parser.tokens) {
|
||||
copy(parser.tokens, parser.tokens[parser.tokens_head:])
|
||||
}
|
||||
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
|
||||
parser.tokens_head = 0
|
||||
}
|
||||
parser.tokens = append(parser.tokens, *token)
|
||||
if pos < 0 {
|
||||
return
|
||||
}
|
||||
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
|
||||
parser.tokens[parser.tokens_head+pos] = *token
|
||||
}
|
||||
|
||||
// Create a new parser object.
|
||||
func yaml_parser_initialize(parser *yaml_parser_t) bool {
|
||||
*parser = yaml_parser_t{
|
||||
raw_buffer: make([]byte, 0, input_raw_buffer_size),
|
||||
buffer: make([]byte, 0, input_buffer_size),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy a parser object.
|
||||
func yaml_parser_delete(parser *yaml_parser_t) {
|
||||
*parser = yaml_parser_t{}
|
||||
}
|
||||
|
||||
// String read handler.
|
||||
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
if parser.input_pos == len(parser.input) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(buffer, parser.input[parser.input_pos:])
|
||||
parser.input_pos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Reader read handler.
|
||||
func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
return parser.input_reader.Read(buffer)
|
||||
}
|
||||
|
||||
// Set a string input.
|
||||
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_string_read_handler
|
||||
parser.input = input
|
||||
parser.input_pos = 0
|
||||
}
|
||||
|
||||
// Set a file input.
|
||||
func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_reader_read_handler
|
||||
parser.input_reader = r
|
||||
}
|
||||
|
||||
// Set the source encoding.
|
||||
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
||||
if parser.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the encoding only once")
|
||||
}
|
||||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
// Create a new emitter object.
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{
|
||||
buffer: make([]byte, output_buffer_size),
|
||||
raw_buffer: make([]byte, 0, output_raw_buffer_size),
|
||||
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||
best_width: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an emitter object.
|
||||
func yaml_emitter_delete(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{}
|
||||
}
|
||||
|
||||
// String write handler.
|
||||
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// yaml_writer_write_handler uses emitter.output_writer to write the
|
||||
// emitted text.
|
||||
func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
_, err := emitter.output_writer.Write(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a string output.
|
||||
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_string_write_handler
|
||||
emitter.output_buffer = output_buffer
|
||||
}
|
||||
|
||||
// Set a file output.
|
||||
func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_writer_write_handler
|
||||
emitter.output_writer = w
|
||||
}
|
||||
|
||||
// Set the output encoding.
|
||||
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
|
||||
if emitter.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the output encoding only once")
|
||||
}
|
||||
emitter.encoding = encoding
|
||||
}
|
||||
|
||||
// Set the canonical output style.
|
||||
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
|
||||
emitter.canonical = canonical
|
||||
}
|
||||
|
||||
// Set the indentation increment.
|
||||
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
|
||||
if indent < 2 || indent > 9 {
|
||||
indent = 2
|
||||
}
|
||||
emitter.best_indent = indent
|
||||
}
|
||||
|
||||
// Set the preferred line width.
|
||||
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
|
||||
if width < 0 {
|
||||
width = -1
|
||||
}
|
||||
emitter.best_width = width
|
||||
}
|
||||
|
||||
// Set if unescaped non-ASCII characters are allowed.
|
||||
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
|
||||
emitter.unicode = unicode
|
||||
}
|
||||
|
||||
// Set the preferred line break character.
|
||||
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
|
||||
emitter.line_break = line_break
|
||||
}
|
||||
|
||||
///*
|
||||
// * Destroy a token object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_token_delete(yaml_token_t *token)
|
||||
//{
|
||||
// assert(token); // Non-NULL token object expected.
|
||||
//
|
||||
// switch (token.type)
|
||||
// {
|
||||
// case YAML_TAG_DIRECTIVE_TOKEN:
|
||||
// yaml_free(token.data.tag_directive.handle);
|
||||
// yaml_free(token.data.tag_directive.prefix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ALIAS_TOKEN:
|
||||
// yaml_free(token.data.alias.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ANCHOR_TOKEN:
|
||||
// yaml_free(token.data.anchor.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_TAG_TOKEN:
|
||||
// yaml_free(token.data.tag.handle);
|
||||
// yaml_free(token.data.tag.suffix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_SCALAR_TOKEN:
|
||||
// yaml_free(token.data.scalar.value);
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// memset(token, 0, sizeof(yaml_token_t));
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Check if a string is a valid UTF-8 sequence.
|
||||
// *
|
||||
// * Check 'reader.c' for more details on UTF-8 encoding.
|
||||
// */
|
||||
//
|
||||
//static int
|
||||
//yaml_check_utf8(yaml_char_t *start, size_t length)
|
||||
//{
|
||||
// yaml_char_t *end = start+length;
|
||||
// yaml_char_t *pointer = start;
|
||||
//
|
||||
// while (pointer < end) {
|
||||
// unsigned char octet;
|
||||
// unsigned int width;
|
||||
// unsigned int value;
|
||||
// size_t k;
|
||||
//
|
||||
// octet = pointer[0];
|
||||
// width = (octet & 0x80) == 0x00 ? 1 :
|
||||
// (octet & 0xE0) == 0xC0 ? 2 :
|
||||
// (octet & 0xF0) == 0xE0 ? 3 :
|
||||
// (octet & 0xF8) == 0xF0 ? 4 : 0;
|
||||
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
|
||||
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
|
||||
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
|
||||
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
|
||||
// if (!width) return 0;
|
||||
// if (pointer+width > end) return 0;
|
||||
// for (k = 1; k < width; k ++) {
|
||||
// octet = pointer[k];
|
||||
// if ((octet & 0xC0) != 0x80) return 0;
|
||||
// value = (value << 6) + (octet & 0x3F);
|
||||
// }
|
||||
// if (!((width == 1) ||
|
||||
// (width == 2 && value >= 0x80) ||
|
||||
// (width == 3 && value >= 0x800) ||
|
||||
// (width == 4 && value >= 0x10000))) return 0;
|
||||
//
|
||||
// pointer += width;
|
||||
// }
|
||||
//
|
||||
// return 1;
|
||||
//}
|
||||
//
|
||||
|
||||
// Create STREAM-START.
|
||||
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_START_EVENT,
|
||||
encoding: encoding,
|
||||
}
|
||||
}
|
||||
|
||||
// Create STREAM-END.
|
||||
func yaml_stream_end_event_initialize(event *yaml_event_t) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_END_EVENT,
|
||||
}
|
||||
}
|
||||
|
||||
// Create DOCUMENT-START.
|
||||
func yaml_document_start_event_initialize(
|
||||
event *yaml_event_t,
|
||||
version_directive *yaml_version_directive_t,
|
||||
tag_directives []yaml_tag_directive_t,
|
||||
implicit bool,
|
||||
) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_START_EVENT,
|
||||
version_directive: version_directive,
|
||||
tag_directives: tag_directives,
|
||||
implicit: implicit,
|
||||
}
|
||||
}
|
||||
|
||||
// Create DOCUMENT-END.
|
||||
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_END_EVENT,
|
||||
implicit: implicit,
|
||||
}
|
||||
}
|
||||
|
||||
// Create ALIAS.
|
||||
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_ALIAS_EVENT,
|
||||
anchor: anchor,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SCALAR.
|
||||
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SCALAR_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
value: value,
|
||||
implicit: plain_implicit,
|
||||
quoted_implicit: quoted_implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-START.
|
||||
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-END.
|
||||
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create MAPPING-START.
|
||||
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
}
|
||||
|
||||
// Create MAPPING-END.
|
||||
func yaml_mapping_end_event_initialize(event *yaml_event_t) {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_END_EVENT,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an event object.
|
||||
func yaml_event_delete(event *yaml_event_t) {
|
||||
*event = yaml_event_t{}
|
||||
}
|
||||
|
||||
///*
|
||||
// * Create a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_initialize(document *yaml_document_t,
|
||||
// version_directive *yaml_version_directive_t,
|
||||
// tag_directives_start *yaml_tag_directive_t,
|
||||
// tag_directives_end *yaml_tag_directive_t,
|
||||
// start_implicit int, end_implicit int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// struct {
|
||||
// start *yaml_node_t
|
||||
// end *yaml_node_t
|
||||
// top *yaml_node_t
|
||||
// } nodes = { NULL, NULL, NULL }
|
||||
// version_directive_copy *yaml_version_directive_t = NULL
|
||||
// struct {
|
||||
// start *yaml_tag_directive_t
|
||||
// end *yaml_tag_directive_t
|
||||
// top *yaml_tag_directive_t
|
||||
// } tag_directives_copy = { NULL, NULL, NULL }
|
||||
// value yaml_tag_directive_t = { NULL, NULL }
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert((tag_directives_start && tag_directives_end) ||
|
||||
// (tag_directives_start == tag_directives_end))
|
||||
// // Valid tag directives are expected.
|
||||
//
|
||||
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// if (version_directive) {
|
||||
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
|
||||
// if (!version_directive_copy) goto error
|
||||
// version_directive_copy.major = version_directive.major
|
||||
// version_directive_copy.minor = version_directive.minor
|
||||
// }
|
||||
//
|
||||
// if (tag_directives_start != tag_directives_end) {
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
|
||||
// goto error
|
||||
// for (tag_directive = tag_directives_start
|
||||
// tag_directive != tag_directives_end; tag_directive ++) {
|
||||
// assert(tag_directive.handle)
|
||||
// assert(tag_directive.prefix)
|
||||
// if (!yaml_check_utf8(tag_directive.handle,
|
||||
// strlen((char *)tag_directive.handle)))
|
||||
// goto error
|
||||
// if (!yaml_check_utf8(tag_directive.prefix,
|
||||
// strlen((char *)tag_directive.prefix)))
|
||||
// goto error
|
||||
// value.handle = yaml_strdup(tag_directive.handle)
|
||||
// value.prefix = yaml_strdup(tag_directive.prefix)
|
||||
// if (!value.handle || !value.prefix) goto error
|
||||
// if (!PUSH(&context, tag_directives_copy, value))
|
||||
// goto error
|
||||
// value.handle = NULL
|
||||
// value.prefix = NULL
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
|
||||
// tag_directives_copy.start, tag_directives_copy.top,
|
||||
// start_implicit, end_implicit, mark, mark)
|
||||
//
|
||||
// return 1
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, nodes)
|
||||
// yaml_free(version_directive_copy)
|
||||
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
|
||||
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
// }
|
||||
// STACK_DEL(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Destroy a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_document_delete(document *yaml_document_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
//
|
||||
// context.error = YAML_NO_ERROR // Eliminate a compiler warning.
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// while (!STACK_EMPTY(&context, document.nodes)) {
|
||||
// node yaml_node_t = POP(&context, document.nodes)
|
||||
// yaml_free(node.tag)
|
||||
// switch (node.type) {
|
||||
// case YAML_SCALAR_NODE:
|
||||
// yaml_free(node.data.scalar.value)
|
||||
// break
|
||||
// case YAML_SEQUENCE_NODE:
|
||||
// STACK_DEL(&context, node.data.sequence.items)
|
||||
// break
|
||||
// case YAML_MAPPING_NODE:
|
||||
// STACK_DEL(&context, node.data.mapping.pairs)
|
||||
// break
|
||||
// default:
|
||||
// assert(0) // Should not happen.
|
||||
// }
|
||||
// }
|
||||
// STACK_DEL(&context, document.nodes)
|
||||
//
|
||||
// yaml_free(document.version_directive)
|
||||
// for (tag_directive = document.tag_directives.start
|
||||
// tag_directive != document.tag_directives.end
|
||||
// tag_directive++) {
|
||||
// yaml_free(tag_directive.handle)
|
||||
// yaml_free(tag_directive.prefix)
|
||||
// }
|
||||
// yaml_free(document.tag_directives.start)
|
||||
//
|
||||
// memset(document, 0, sizeof(yaml_document_t))
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get a document node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_node(document *yaml_document_t, index int)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
|
||||
// return document.nodes.start + index - 1
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get the root object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_root_node(document *yaml_document_t)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (document.nodes.top != document.nodes.start) {
|
||||
// return document.nodes.start
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a scalar node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_scalar(document *yaml_document_t,
|
||||
// tag *yaml_char_t, value *yaml_char_t, length int,
|
||||
// style yaml_scalar_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// value_copy *yaml_char_t = NULL
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert(value) // Non-NULL value is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (length < 0) {
|
||||
// length = strlen((char *)value)
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(value, length)) goto error
|
||||
// value_copy = yaml_malloc(length+1)
|
||||
// if (!value_copy) goto error
|
||||
// memcpy(value_copy, value, length)
|
||||
// value_copy[length] = '\0'
|
||||
//
|
||||
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// yaml_free(tag_copy)
|
||||
// yaml_free(value_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a sequence node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_sequence(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_sequence_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_item_t
|
||||
// end *yaml_node_item_t
|
||||
// top *yaml_node_item_t
|
||||
// } items = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, items)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a mapping node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_mapping(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_mapping_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_pair_t
|
||||
// end *yaml_node_pair_t
|
||||
// top *yaml_node_pair_t
|
||||
// } pairs = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, pairs)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append an item to a sequence node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_sequence_item(document *yaml_document_t,
|
||||
// sequence int, item int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(sequence > 0
|
||||
// && document.nodes.start + sequence <= document.nodes.top)
|
||||
// // Valid sequence id is required.
|
||||
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
|
||||
// // A sequence node is required.
|
||||
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
|
||||
// // Valid item id is required.
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[sequence-1].data.sequence.items, item))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append a pair of a key and a value to a mapping node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_mapping_pair(document *yaml_document_t,
|
||||
// mapping int, key int, value int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// pair yaml_node_pair_t
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(mapping > 0
|
||||
// && document.nodes.start + mapping <= document.nodes.top)
|
||||
// // Valid mapping id is required.
|
||||
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
|
||||
// // A mapping node is required.
|
||||
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
|
||||
// // Valid key id is required.
|
||||
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
|
||||
// // Valid value id is required.
|
||||
//
|
||||
// pair.key = key
|
||||
// pair.value = value
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
//
|
1000
vendor/gopkg.in/yaml.v3/decode.go
generated
vendored
1000
vendor/gopkg.in/yaml.v3/decode.go
generated
vendored
File diff suppressed because it is too large
Load Diff
2020
vendor/gopkg.in/yaml.v3/emitterc.go
generated
vendored
2020
vendor/gopkg.in/yaml.v3/emitterc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
577
vendor/gopkg.in/yaml.v3/encode.go
generated
vendored
577
vendor/gopkg.in/yaml.v3/encode.go
generated
vendored
@ -1,577 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type encoder struct {
|
||||
emitter yaml_emitter_t
|
||||
event yaml_event_t
|
||||
out []byte
|
||||
flow bool
|
||||
indent int
|
||||
doneInit bool
|
||||
}
|
||||
|
||||
func newEncoder() *encoder {
|
||||
e := &encoder{}
|
||||
yaml_emitter_initialize(&e.emitter)
|
||||
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
||||
yaml_emitter_set_unicode(&e.emitter, true)
|
||||
return e
|
||||
}
|
||||
|
||||
func newEncoderWithWriter(w io.Writer) *encoder {
|
||||
e := &encoder{}
|
||||
yaml_emitter_initialize(&e.emitter)
|
||||
yaml_emitter_set_output_writer(&e.emitter, w)
|
||||
yaml_emitter_set_unicode(&e.emitter, true)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *encoder) init() {
|
||||
if e.doneInit {
|
||||
return
|
||||
}
|
||||
if e.indent == 0 {
|
||||
e.indent = 4
|
||||
}
|
||||
e.emitter.best_indent = e.indent
|
||||
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
|
||||
e.emit()
|
||||
e.doneInit = true
|
||||
}
|
||||
|
||||
func (e *encoder) finish() {
|
||||
e.emitter.open_ended = false
|
||||
yaml_stream_end_event_initialize(&e.event)
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) destroy() {
|
||||
yaml_emitter_delete(&e.emitter)
|
||||
}
|
||||
|
||||
func (e *encoder) emit() {
|
||||
// This will internally delete the e.event value.
|
||||
e.must(yaml_emitter_emit(&e.emitter, &e.event))
|
||||
}
|
||||
|
||||
func (e *encoder) must(ok bool) {
|
||||
if !ok {
|
||||
msg := e.emitter.problem
|
||||
if msg == "" {
|
||||
msg = "unknown problem generating YAML content"
|
||||
}
|
||||
failf("%s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
|
||||
e.init()
|
||||
var node *Node
|
||||
if in.IsValid() {
|
||||
node, _ = in.Interface().(*Node)
|
||||
}
|
||||
if node != nil && node.Kind == DocumentNode {
|
||||
e.nodev(in)
|
||||
} else {
|
||||
yaml_document_start_event_initialize(&e.event, nil, nil, true)
|
||||
e.emit()
|
||||
e.marshal(tag, in)
|
||||
yaml_document_end_event_initialize(&e.event, true)
|
||||
e.emit()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) marshal(tag string, in reflect.Value) {
|
||||
tag = shortTag(tag)
|
||||
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
iface := in.Interface()
|
||||
switch value := iface.(type) {
|
||||
case *Node:
|
||||
e.nodev(in)
|
||||
return
|
||||
case Node:
|
||||
if !in.CanAddr() {
|
||||
var n = reflect.New(in.Type()).Elem()
|
||||
n.Set(in)
|
||||
in = n
|
||||
}
|
||||
e.nodev(in.Addr())
|
||||
return
|
||||
case time.Time:
|
||||
e.timev(tag, in)
|
||||
return
|
||||
case *time.Time:
|
||||
e.timev(tag, in.Elem())
|
||||
return
|
||||
case time.Duration:
|
||||
e.stringv(tag, reflect.ValueOf(value.String()))
|
||||
return
|
||||
case Marshaler:
|
||||
v, err := value.MarshalYAML()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
if v == nil {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
e.marshal(tag, reflect.ValueOf(v))
|
||||
return
|
||||
case encoding.TextMarshaler:
|
||||
text, err := value.MarshalText()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
in = reflect.ValueOf(string(text))
|
||||
case nil:
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
switch in.Kind() {
|
||||
case reflect.Interface:
|
||||
e.marshal(tag, in.Elem())
|
||||
case reflect.Map:
|
||||
e.mapv(tag, in)
|
||||
case reflect.Ptr:
|
||||
e.marshal(tag, in.Elem())
|
||||
case reflect.Struct:
|
||||
e.structv(tag, in)
|
||||
case reflect.Slice, reflect.Array:
|
||||
e.slicev(tag, in)
|
||||
case reflect.String:
|
||||
e.stringv(tag, in)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
e.intv(tag, in)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
e.uintv(tag, in)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
e.floatv(tag, in)
|
||||
case reflect.Bool:
|
||||
e.boolv(tag, in)
|
||||
default:
|
||||
panic("cannot marshal type: " + in.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) mapv(tag string, in reflect.Value) {
|
||||
e.mappingv(tag, func() {
|
||||
keys := keyList(in.MapKeys())
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
e.marshal("", k)
|
||||
e.marshal("", in.MapIndex(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
|
||||
for _, num := range index {
|
||||
for {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
v = v.Field(num)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (e *encoder) structv(tag string, in reflect.Value) {
|
||||
sinfo, err := getStructInfo(in.Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e.mappingv(tag, func() {
|
||||
for _, info := range sinfo.FieldsList {
|
||||
var value reflect.Value
|
||||
if info.Inline == nil {
|
||||
value = in.Field(info.Num)
|
||||
} else {
|
||||
value = e.fieldByIndex(in, info.Inline)
|
||||
if !value.IsValid() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if info.OmitEmpty && isZero(value) {
|
||||
continue
|
||||
}
|
||||
e.marshal("", reflect.ValueOf(info.Key))
|
||||
e.flow = info.Flow
|
||||
e.marshal("", value)
|
||||
}
|
||||
if sinfo.InlineMap >= 0 {
|
||||
m := in.Field(sinfo.InlineMap)
|
||||
if m.Len() > 0 {
|
||||
e.flow = false
|
||||
keys := keyList(m.MapKeys())
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
if _, found := sinfo.FieldsMap[k.String()]; found {
|
||||
panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
|
||||
}
|
||||
e.marshal("", k)
|
||||
e.flow = false
|
||||
e.marshal("", m.MapIndex(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) mappingv(tag string, f func()) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_MAPPING_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_MAPPING_STYLE
|
||||
}
|
||||
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
|
||||
e.emit()
|
||||
f()
|
||||
yaml_mapping_end_event_initialize(&e.event)
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) slicev(tag string, in reflect.Value) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_SEQUENCE_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_SEQUENCE_STYLE
|
||||
}
|
||||
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
||||
e.emit()
|
||||
n := in.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
e.marshal("", in.Index(i))
|
||||
}
|
||||
e.must(yaml_sequence_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
|
||||
//
|
||||
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
|
||||
// in YAML 1.2 and by this package, but these should be marshalled quoted for
|
||||
// the time being for compatibility with other parsers.
|
||||
func isBase60Float(s string) (result bool) {
|
||||
// Fast path.
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
c := s[0]
|
||||
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
|
||||
return false
|
||||
}
|
||||
// Do the full match.
|
||||
return base60float.MatchString(s)
|
||||
}
|
||||
|
||||
// From http://yaml.org/type/float.html, except the regular expression there
|
||||
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
|
||||
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
|
||||
|
||||
// isOldBool returns whether s is bool notation as defined in YAML 1.1.
|
||||
//
|
||||
// We continue to force strings that YAML 1.1 would interpret as booleans to be
|
||||
// rendered as quotes strings so that the marshalled output valid for YAML 1.1
|
||||
// parsing.
|
||||
func isOldBool(s string) (result bool) {
|
||||
switch s {
|
||||
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
|
||||
"n", "N", "no", "No", "NO", "off", "Off", "OFF":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) stringv(tag string, in reflect.Value) {
|
||||
var style yaml_scalar_style_t
|
||||
s := in.String()
|
||||
canUsePlain := true
|
||||
switch {
|
||||
case !utf8.ValidString(s):
|
||||
if tag == binaryTag {
|
||||
failf("explicitly tagged !!binary data must be base64-encoded")
|
||||
}
|
||||
if tag != "" {
|
||||
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
|
||||
}
|
||||
// It can't be encoded directly as YAML so use a binary tag
|
||||
// and encode it as base64.
|
||||
tag = binaryTag
|
||||
s = encodeBase64(s)
|
||||
case tag == "":
|
||||
// Check to see if it would resolve to a specific
|
||||
// tag when encoded unquoted. If it doesn't,
|
||||
// there's no need to quote it.
|
||||
rtag, _ := resolve("", s)
|
||||
canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
|
||||
}
|
||||
// Note: it's possible for user code to emit invalid YAML
|
||||
// if they explicitly specify a tag and a string containing
|
||||
// text that's incompatible with that tag.
|
||||
switch {
|
||||
case strings.Contains(s, "\n"):
|
||||
if e.flow {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
} else {
|
||||
style = yaml_LITERAL_SCALAR_STYLE
|
||||
}
|
||||
case canUsePlain:
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
default:
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) boolv(tag string, in reflect.Value) {
|
||||
var s string
|
||||
if in.Bool() {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) intv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatInt(in.Int(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) uintv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatUint(in.Uint(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) timev(tag string, in reflect.Value) {
|
||||
t := in.Interface().(time.Time)
|
||||
s := t.Format(time.RFC3339Nano)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) floatv(tag string, in reflect.Value) {
|
||||
// Issue #352: When formatting, use the precision of the underlying value
|
||||
precision := 64
|
||||
if in.Kind() == reflect.Float32 {
|
||||
precision = 32
|
||||
}
|
||||
|
||||
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
|
||||
switch s {
|
||||
case "+Inf":
|
||||
s = ".inf"
|
||||
case "-Inf":
|
||||
s = "-.inf"
|
||||
case "NaN":
|
||||
s = ".nan"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) nilv() {
|
||||
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
|
||||
// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
|
||||
implicit := tag == ""
|
||||
if !implicit {
|
||||
tag = longTag(tag)
|
||||
}
|
||||
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
||||
e.event.head_comment = head
|
||||
e.event.line_comment = line
|
||||
e.event.foot_comment = foot
|
||||
e.event.tail_comment = tail
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) nodev(in reflect.Value) {
|
||||
e.node(in.Interface().(*Node), "")
|
||||
}
|
||||
|
||||
func (e *encoder) node(node *Node, tail string) {
|
||||
// Zero nodes behave as nil.
|
||||
if node.Kind == 0 && node.IsZero() {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
|
||||
// If the tag was not explicitly requested, and dropping it won't change the
|
||||
// implicit tag of the value, don't include it in the presentation.
|
||||
var tag = node.Tag
|
||||
var stag = shortTag(tag)
|
||||
var forceQuoting bool
|
||||
if tag != "" && node.Style&TaggedStyle == 0 {
|
||||
if node.Kind == ScalarNode {
|
||||
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
|
||||
tag = ""
|
||||
} else {
|
||||
rtag, _ := resolve("", node.Value)
|
||||
if rtag == stag {
|
||||
tag = ""
|
||||
} else if stag == strTag {
|
||||
tag = ""
|
||||
forceQuoting = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var rtag string
|
||||
switch node.Kind {
|
||||
case MappingNode:
|
||||
rtag = mapTag
|
||||
case SequenceNode:
|
||||
rtag = seqTag
|
||||
}
|
||||
if rtag == stag {
|
||||
tag = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case DocumentNode:
|
||||
yaml_document_start_event_initialize(&e.event, nil, nil, true)
|
||||
e.event.head_comment = []byte(node.HeadComment)
|
||||
e.emit()
|
||||
for _, node := range node.Content {
|
||||
e.node(node, "")
|
||||
}
|
||||
yaml_document_end_event_initialize(&e.event, true)
|
||||
e.event.foot_comment = []byte(node.FootComment)
|
||||
e.emit()
|
||||
|
||||
case SequenceNode:
|
||||
style := yaml_BLOCK_SEQUENCE_STYLE
|
||||
if node.Style&FlowStyle != 0 {
|
||||
style = yaml_FLOW_SEQUENCE_STYLE
|
||||
}
|
||||
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
|
||||
e.event.head_comment = []byte(node.HeadComment)
|
||||
e.emit()
|
||||
for _, node := range node.Content {
|
||||
e.node(node, "")
|
||||
}
|
||||
e.must(yaml_sequence_end_event_initialize(&e.event))
|
||||
e.event.line_comment = []byte(node.LineComment)
|
||||
e.event.foot_comment = []byte(node.FootComment)
|
||||
e.emit()
|
||||
|
||||
case MappingNode:
|
||||
style := yaml_BLOCK_MAPPING_STYLE
|
||||
if node.Style&FlowStyle != 0 {
|
||||
style = yaml_FLOW_MAPPING_STYLE
|
||||
}
|
||||
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
|
||||
e.event.tail_comment = []byte(tail)
|
||||
e.event.head_comment = []byte(node.HeadComment)
|
||||
e.emit()
|
||||
|
||||
// The tail logic below moves the foot comment of prior keys to the following key,
|
||||
// since the value for each key may be a nested structure and the foot needs to be
|
||||
// processed only the entirety of the value is streamed. The last tail is processed
|
||||
// with the mapping end event.
|
||||
var tail string
|
||||
for i := 0; i+1 < len(node.Content); i += 2 {
|
||||
k := node.Content[i]
|
||||
foot := k.FootComment
|
||||
if foot != "" {
|
||||
kopy := *k
|
||||
kopy.FootComment = ""
|
||||
k = &kopy
|
||||
}
|
||||
e.node(k, tail)
|
||||
tail = foot
|
||||
|
||||
v := node.Content[i+1]
|
||||
e.node(v, "")
|
||||
}
|
||||
|
||||
yaml_mapping_end_event_initialize(&e.event)
|
||||
e.event.tail_comment = []byte(tail)
|
||||
e.event.line_comment = []byte(node.LineComment)
|
||||
e.event.foot_comment = []byte(node.FootComment)
|
||||
e.emit()
|
||||
|
||||
case AliasNode:
|
||||
yaml_alias_event_initialize(&e.event, []byte(node.Value))
|
||||
e.event.head_comment = []byte(node.HeadComment)
|
||||
e.event.line_comment = []byte(node.LineComment)
|
||||
e.event.foot_comment = []byte(node.FootComment)
|
||||
e.emit()
|
||||
|
||||
case ScalarNode:
|
||||
value := node.Value
|
||||
if !utf8.ValidString(value) {
|
||||
if stag == binaryTag {
|
||||
failf("explicitly tagged !!binary data must be base64-encoded")
|
||||
}
|
||||
if stag != "" {
|
||||
failf("cannot marshal invalid UTF-8 data as %s", stag)
|
||||
}
|
||||
// It can't be encoded directly as YAML so use a binary tag
|
||||
// and encode it as base64.
|
||||
tag = binaryTag
|
||||
value = encodeBase64(value)
|
||||
}
|
||||
|
||||
style := yaml_PLAIN_SCALAR_STYLE
|
||||
switch {
|
||||
case node.Style&DoubleQuotedStyle != 0:
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
case node.Style&SingleQuotedStyle != 0:
|
||||
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
|
||||
case node.Style&LiteralStyle != 0:
|
||||
style = yaml_LITERAL_SCALAR_STYLE
|
||||
case node.Style&FoldedStyle != 0:
|
||||
style = yaml_FOLDED_SCALAR_STYLE
|
||||
case strings.Contains(value, "\n"):
|
||||
style = yaml_LITERAL_SCALAR_STYLE
|
||||
case forceQuoting:
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
|
||||
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
|
||||
default:
|
||||
failf("cannot encode node with unknown kind %d", node.Kind)
|
||||
}
|
||||
}
|
1258
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
1258
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
434
vendor/gopkg.in/yaml.v3/readerc.go
generated
vendored
434
vendor/gopkg.in/yaml.v3/readerc.go
generated
vendored
@ -1,434 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
// Copyright (c) 2006-2010 Kirill Simonov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Set the reader error and return 0.
|
||||
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
|
||||
parser.error = yaml_READER_ERROR
|
||||
parser.problem = problem
|
||||
parser.problem_offset = offset
|
||||
parser.problem_value = value
|
||||
return false
|
||||
}
|
||||
|
||||
// Byte order marks.
|
||||
const (
|
||||
bom_UTF8 = "\xef\xbb\xbf"
|
||||
bom_UTF16LE = "\xff\xfe"
|
||||
bom_UTF16BE = "\xfe\xff"
|
||||
)
|
||||
|
||||
// Determine the input stream encoding by checking the BOM symbol. If no BOM is
|
||||
// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
|
||||
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
|
||||
// Ensure that we had enough bytes in the raw buffer.
|
||||
for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the encoding.
|
||||
buf := parser.raw_buffer
|
||||
pos := parser.raw_buffer_pos
|
||||
avail := len(buf) - pos
|
||||
if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
|
||||
parser.encoding = yaml_UTF16LE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
|
||||
parser.encoding = yaml_UTF16BE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
parser.raw_buffer_pos += 3
|
||||
parser.offset += 3
|
||||
} else {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Update the raw buffer.
|
||||
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
|
||||
size_read := 0
|
||||
|
||||
// Return if the raw buffer is full.
|
||||
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Return on EOF.
|
||||
if parser.eof {
|
||||
return true
|
||||
}
|
||||
|
||||
// Move the remaining bytes in the raw buffer to the beginning.
|
||||
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
|
||||
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
|
||||
}
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
|
||||
parser.raw_buffer_pos = 0
|
||||
|
||||
// Call the read handler to fill the buffer.
|
||||
size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
|
||||
if err == io.EOF {
|
||||
parser.eof = true
|
||||
} else if err != nil {
|
||||
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure that the buffer contains at least `length` characters.
|
||||
// Return true on success, false on failure.
|
||||
//
|
||||
// The length is supposed to be significantly less that the buffer size.
|
||||
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
|
||||
if parser.read_handler == nil {
|
||||
panic("read handler must be set")
|
||||
}
|
||||
|
||||
// [Go] This function was changed to guarantee the requested length size at EOF.
|
||||
// The fact we need to do this is pretty awful, but the description above implies
|
||||
// for that to be the case, and there are tests
|
||||
|
||||
// If the EOF flag is set and the raw buffer is empty, do nothing.
|
||||
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
// [Go] ACTUALLY! Read the documentation of this function above.
|
||||
// This is just broken. To return true, we need to have the
|
||||
// given length in the buffer. Not doing that means every single
|
||||
// check that calls this function to make sure the buffer has a
|
||||
// given length is Go) panicking; or C) accessing invalid memory.
|
||||
//return true
|
||||
}
|
||||
|
||||
// Return if the buffer contains enough characters.
|
||||
if parser.unread >= length {
|
||||
return true
|
||||
}
|
||||
|
||||
// Determine the input encoding if it is not known yet.
|
||||
if parser.encoding == yaml_ANY_ENCODING {
|
||||
if !yaml_parser_determine_encoding(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Move the unread characters to the beginning of the buffer.
|
||||
buffer_len := len(parser.buffer)
|
||||
if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
|
||||
copy(parser.buffer, parser.buffer[parser.buffer_pos:])
|
||||
buffer_len -= parser.buffer_pos
|
||||
parser.buffer_pos = 0
|
||||
} else if parser.buffer_pos == buffer_len {
|
||||
buffer_len = 0
|
||||
parser.buffer_pos = 0
|
||||
}
|
||||
|
||||
// Open the whole buffer for writing, and cut it before returning.
|
||||
parser.buffer = parser.buffer[:cap(parser.buffer)]
|
||||
|
||||
// Fill the buffer until it has enough characters.
|
||||
first := true
|
||||
for parser.unread < length {
|
||||
|
||||
// Fill the raw buffer if necessary.
|
||||
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return false
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
// Decode the raw buffer.
|
||||
inner:
|
||||
for parser.raw_buffer_pos != len(parser.raw_buffer) {
|
||||
var value rune
|
||||
var width int
|
||||
|
||||
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
|
||||
|
||||
// Decode the next character.
|
||||
switch parser.encoding {
|
||||
case yaml_UTF8_ENCODING:
|
||||
// Decode a UTF-8 character. Check RFC 3629
|
||||
// (http://www.ietf.org/rfc/rfc3629.txt) for more details.
|
||||
//
|
||||
// The following table (taken from the RFC) is used for
|
||||
// decoding.
|
||||
//
|
||||
// Char. number range | UTF-8 octet sequence
|
||||
// (hexadecimal) | (binary)
|
||||
// --------------------+------------------------------------
|
||||
// 0000 0000-0000 007F | 0xxxxxxx
|
||||
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
//
|
||||
// Additionally, the characters in the range 0xD800-0xDFFF
|
||||
// are prohibited as they are reserved for use with UTF-16
|
||||
// surrogate pairs.
|
||||
|
||||
// Determine the length of the UTF-8 sequence.
|
||||
octet := parser.raw_buffer[parser.raw_buffer_pos]
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
width = 1
|
||||
case octet&0xE0 == 0xC0:
|
||||
width = 2
|
||||
case octet&0xF0 == 0xE0:
|
||||
width = 3
|
||||
case octet&0xF8 == 0xF0:
|
||||
width = 4
|
||||
default:
|
||||
// The leading octet is invalid.
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid leading UTF-8 octet",
|
||||
parser.offset, int(octet))
|
||||
}
|
||||
|
||||
// Check if the raw buffer contains an incomplete character.
|
||||
if width > raw_unread {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-8 octet sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Decode the leading octet.
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
value = rune(octet & 0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
value = rune(octet & 0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
value = rune(octet & 0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
value = rune(octet & 0x07)
|
||||
default:
|
||||
value = 0
|
||||
}
|
||||
|
||||
// Check and decode the trailing octets.
|
||||
for k := 1; k < width; k++ {
|
||||
octet = parser.raw_buffer[parser.raw_buffer_pos+k]
|
||||
|
||||
// Check if the octet is valid.
|
||||
if (octet & 0xC0) != 0x80 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid trailing UTF-8 octet",
|
||||
parser.offset+k, int(octet))
|
||||
}
|
||||
|
||||
// Decode the octet.
|
||||
value = (value << 6) + rune(octet&0x3F)
|
||||
}
|
||||
|
||||
// Check the length of the sequence against the value.
|
||||
switch {
|
||||
case width == 1:
|
||||
case width == 2 && value >= 0x80:
|
||||
case width == 3 && value >= 0x800:
|
||||
case width == 4 && value >= 0x10000:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid length of a UTF-8 sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
|
||||
// Check the range of the value.
|
||||
if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid Unicode character",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
|
||||
var low, high int
|
||||
if parser.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
low, high = 1, 0
|
||||
}
|
||||
|
||||
// The UTF-16 encoding is not as simple as one might
|
||||
// naively think. Check RFC 2781
|
||||
// (http://www.ietf.org/rfc/rfc2781.txt).
|
||||
//
|
||||
// Normally, two subsequent bytes describe a Unicode
|
||||
// character. However a special technique (called a
|
||||
// surrogate pair) is used for specifying character
|
||||
// values larger than 0xFFFF.
|
||||
//
|
||||
// A surrogate pair consists of two pseudo-characters:
|
||||
// high surrogate area (0xD800-0xDBFF)
|
||||
// low surrogate area (0xDC00-0xDFFF)
|
||||
//
|
||||
// The following formulas are used for decoding
|
||||
// and encoding characters using surrogate pairs:
|
||||
//
|
||||
// U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
|
||||
// U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
|
||||
// W1 = 110110yyyyyyyyyy
|
||||
// W2 = 110111xxxxxxxxxx
|
||||
//
|
||||
// where U is the character value, W1 is the high surrogate
|
||||
// area, W2 is the low surrogate area.
|
||||
|
||||
// Check for incomplete UTF-16 character.
|
||||
if raw_unread < 2 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 character",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the character.
|
||||
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
|
||||
|
||||
// Check for unexpected low surrogate area.
|
||||
if value&0xFC00 == 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"unexpected low surrogate area",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Check for a high surrogate area.
|
||||
if value&0xFC00 == 0xD800 {
|
||||
width = 4
|
||||
|
||||
// Check for incomplete surrogate pair.
|
||||
if raw_unread < 4 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 surrogate pair",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the next character.
|
||||
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
|
||||
|
||||
// Check for a low surrogate area.
|
||||
if value2&0xFC00 != 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"expected low surrogate area",
|
||||
parser.offset+2, int(value2))
|
||||
}
|
||||
|
||||
// Generate the value of the surrogate pair.
|
||||
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
|
||||
} else {
|
||||
width = 2
|
||||
}
|
||||
|
||||
default:
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
// Check if the character is in the allowed range:
|
||||
// #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
|
||||
// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
|
||||
// | [#x10000-#x10FFFF] (32 bit)
|
||||
switch {
|
||||
case value == 0x09:
|
||||
case value == 0x0A:
|
||||
case value == 0x0D:
|
||||
case value >= 0x20 && value <= 0x7E:
|
||||
case value == 0x85:
|
||||
case value >= 0xA0 && value <= 0xD7FF:
|
||||
case value >= 0xE000 && value <= 0xFFFD:
|
||||
case value >= 0x10000 && value <= 0x10FFFF:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"control characters are not allowed",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Move the raw pointers.
|
||||
parser.raw_buffer_pos += width
|
||||
parser.offset += width
|
||||
|
||||
// Finally put the character into the buffer.
|
||||
if value <= 0x7F {
|
||||
// 0000 0000-0000 007F . 0xxxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(value)
|
||||
buffer_len += 1
|
||||
} else if value <= 0x7FF {
|
||||
// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 2
|
||||
} else if value <= 0xFFFF {
|
||||
// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 3
|
||||
} else {
|
||||
// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 4
|
||||
}
|
||||
|
||||
parser.unread++
|
||||
}
|
||||
|
||||
// On EOF, put NUL into the buffer and return.
|
||||
if parser.eof {
|
||||
parser.buffer[buffer_len] = 0
|
||||
buffer_len++
|
||||
parser.unread++
|
||||
break
|
||||
}
|
||||
}
|
||||
// [Go] Read the documentation of this function above. To return true,
|
||||
// we need to have the given length in the buffer. Not doing that means
|
||||
// every single check that calls this function to make sure the buffer
|
||||
// has a given length is Go) panicking; or C) accessing invalid memory.
|
||||
// This happens here due to the EOF above breaking early.
|
||||
for buffer_len < length {
|
||||
parser.buffer[buffer_len] = 0
|
||||
buffer_len++
|
||||
}
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return true
|
||||
}
|
326
vendor/gopkg.in/yaml.v3/resolve.go
generated
vendored
326
vendor/gopkg.in/yaml.v3/resolve.go
generated
vendored
@ -1,326 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type resolveMapItem struct {
|
||||
value interface{}
|
||||
tag string
|
||||
}
|
||||
|
||||
var resolveTable = make([]byte, 256)
|
||||
var resolveMap = make(map[string]resolveMapItem)
|
||||
|
||||
func init() {
|
||||
t := resolveTable
|
||||
t[int('+')] = 'S' // Sign
|
||||
t[int('-')] = 'S'
|
||||
for _, c := range "0123456789" {
|
||||
t[int(c)] = 'D' // Digit
|
||||
}
|
||||
for _, c := range "yYnNtTfFoO~" {
|
||||
t[int(c)] = 'M' // In map
|
||||
}
|
||||
t[int('.')] = '.' // Float (potentially in map)
|
||||
|
||||
var resolveMapList = []struct {
|
||||
v interface{}
|
||||
tag string
|
||||
l []string
|
||||
}{
|
||||
{true, boolTag, []string{"true", "True", "TRUE"}},
|
||||
{false, boolTag, []string{"false", "False", "FALSE"}},
|
||||
{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
|
||||
{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
|
||||
{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
|
||||
{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
|
||||
{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
|
||||
{"<<", mergeTag, []string{"<<"}},
|
||||
}
|
||||
|
||||
m := resolveMap
|
||||
for _, item := range resolveMapList {
|
||||
for _, s := range item.l {
|
||||
m[s] = resolveMapItem{item.v, item.tag}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
nullTag = "!!null"
|
||||
boolTag = "!!bool"
|
||||
strTag = "!!str"
|
||||
intTag = "!!int"
|
||||
floatTag = "!!float"
|
||||
timestampTag = "!!timestamp"
|
||||
seqTag = "!!seq"
|
||||
mapTag = "!!map"
|
||||
binaryTag = "!!binary"
|
||||
mergeTag = "!!merge"
|
||||
)
|
||||
|
||||
var longTags = make(map[string]string)
|
||||
var shortTags = make(map[string]string)
|
||||
|
||||
func init() {
|
||||
for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
|
||||
ltag := longTag(stag)
|
||||
longTags[stag] = ltag
|
||||
shortTags[ltag] = stag
|
||||
}
|
||||
}
|
||||
|
||||
const longTagPrefix = "tag:yaml.org,2002:"
|
||||
|
||||
func shortTag(tag string) string {
|
||||
if strings.HasPrefix(tag, longTagPrefix) {
|
||||
if stag, ok := shortTags[tag]; ok {
|
||||
return stag
|
||||
}
|
||||
return "!!" + tag[len(longTagPrefix):]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func longTag(tag string) string {
|
||||
if strings.HasPrefix(tag, "!!") {
|
||||
if ltag, ok := longTags[tag]; ok {
|
||||
return ltag
|
||||
}
|
||||
return longTagPrefix + tag[2:]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func resolvableTag(tag string) bool {
|
||||
switch tag {
|
||||
case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
|
||||
|
||||
func resolve(tag string, in string) (rtag string, out interface{}) {
|
||||
tag = shortTag(tag)
|
||||
if !resolvableTag(tag) {
|
||||
return tag, in
|
||||
}
|
||||
|
||||
defer func() {
|
||||
switch tag {
|
||||
case "", rtag, strTag, binaryTag:
|
||||
return
|
||||
case floatTag:
|
||||
if rtag == intTag {
|
||||
switch v := out.(type) {
|
||||
case int64:
|
||||
rtag = floatTag
|
||||
out = float64(v)
|
||||
return
|
||||
case int:
|
||||
rtag = floatTag
|
||||
out = float64(v)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
|
||||
}()
|
||||
|
||||
// Any data is accepted as a !!str or !!binary.
|
||||
// Otherwise, the prefix is enough of a hint about what it might be.
|
||||
hint := byte('N')
|
||||
if in != "" {
|
||||
hint = resolveTable[in[0]]
|
||||
}
|
||||
if hint != 0 && tag != strTag && tag != binaryTag {
|
||||
// Handle things we can lookup in a map.
|
||||
if item, ok := resolveMap[in]; ok {
|
||||
return item.tag, item.value
|
||||
}
|
||||
|
||||
// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
|
||||
// are purposefully unsupported here. They're still quoted on
|
||||
// the way out for compatibility with other parser, though.
|
||||
|
||||
switch hint {
|
||||
case 'M':
|
||||
// We've already checked the map above.
|
||||
|
||||
case '.':
|
||||
// Not in the map, so maybe a normal float.
|
||||
floatv, err := strconv.ParseFloat(in, 64)
|
||||
if err == nil {
|
||||
return floatTag, floatv
|
||||
}
|
||||
|
||||
case 'D', 'S':
|
||||
// Int, float, or timestamp.
|
||||
// Only try values as a timestamp if the value is unquoted or there's an explicit
|
||||
// !!timestamp tag.
|
||||
if tag == "" || tag == timestampTag {
|
||||
t, ok := parseTimestamp(in)
|
||||
if ok {
|
||||
return timestampTag, t
|
||||
}
|
||||
}
|
||||
|
||||
plain := strings.Replace(in, "_", "", -1)
|
||||
intv, err := strconv.ParseInt(plain, 0, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return intTag, int(intv)
|
||||
} else {
|
||||
return intTag, intv
|
||||
}
|
||||
}
|
||||
uintv, err := strconv.ParseUint(plain, 0, 64)
|
||||
if err == nil {
|
||||
return intTag, uintv
|
||||
}
|
||||
if yamlStyleFloat.MatchString(plain) {
|
||||
floatv, err := strconv.ParseFloat(plain, 64)
|
||||
if err == nil {
|
||||
return floatTag, floatv
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(plain, "0b") {
|
||||
intv, err := strconv.ParseInt(plain[2:], 2, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return intTag, int(intv)
|
||||
} else {
|
||||
return intTag, intv
|
||||
}
|
||||
}
|
||||
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
|
||||
if err == nil {
|
||||
return intTag, uintv
|
||||
}
|
||||
} else if strings.HasPrefix(plain, "-0b") {
|
||||
intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
|
||||
if err == nil {
|
||||
if true || intv == int64(int(intv)) {
|
||||
return intTag, int(intv)
|
||||
} else {
|
||||
return intTag, intv
|
||||
}
|
||||
}
|
||||
}
|
||||
// Octals as introduced in version 1.2 of the spec.
|
||||
// Octals from the 1.1 spec, spelled as 0777, are still
|
||||
// decoded by default in v3 as well for compatibility.
|
||||
// May be dropped in v4 depending on how usage evolves.
|
||||
if strings.HasPrefix(plain, "0o") {
|
||||
intv, err := strconv.ParseInt(plain[2:], 8, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return intTag, int(intv)
|
||||
} else {
|
||||
return intTag, intv
|
||||
}
|
||||
}
|
||||
uintv, err := strconv.ParseUint(plain[2:], 8, 64)
|
||||
if err == nil {
|
||||
return intTag, uintv
|
||||
}
|
||||
} else if strings.HasPrefix(plain, "-0o") {
|
||||
intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
|
||||
if err == nil {
|
||||
if true || intv == int64(int(intv)) {
|
||||
return intTag, int(intv)
|
||||
} else {
|
||||
return intTag, intv
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
|
||||
}
|
||||
}
|
||||
return strTag, in
|
||||
}
|
||||
|
||||
// encodeBase64 encodes s as base64 that is broken up into multiple lines
|
||||
// as appropriate for the resulting length.
|
||||
func encodeBase64(s string) string {
|
||||
const lineLen = 70
|
||||
encLen := base64.StdEncoding.EncodedLen(len(s))
|
||||
lines := encLen/lineLen + 1
|
||||
buf := make([]byte, encLen*2+lines)
|
||||
in := buf[0:encLen]
|
||||
out := buf[encLen:]
|
||||
base64.StdEncoding.Encode(in, []byte(s))
|
||||
k := 0
|
||||
for i := 0; i < len(in); i += lineLen {
|
||||
j := i + lineLen
|
||||
if j > len(in) {
|
||||
j = len(in)
|
||||
}
|
||||
k += copy(out[k:], in[i:j])
|
||||
if lines > 1 {
|
||||
out[k] = '\n'
|
||||
k++
|
||||
}
|
||||
}
|
||||
return string(out[:k])
|
||||
}
|
||||
|
||||
// This is a subset of the formats allowed by the regular expression
|
||||
// defined at http://yaml.org/type/timestamp.html.
|
||||
var allowedTimestampFormats = []string{
|
||||
"2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields.
|
||||
"2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t".
|
||||
"2006-1-2 15:4:5.999999999", // space separated with no time zone
|
||||
"2006-1-2", // date only
|
||||
// Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5"
|
||||
// from the set of examples.
|
||||
}
|
||||
|
||||
// parseTimestamp parses s as a timestamp string and
|
||||
// returns the timestamp and reports whether it succeeded.
|
||||
// Timestamp formats are defined at http://yaml.org/type/timestamp.html
|
||||
func parseTimestamp(s string) (time.Time, bool) {
|
||||
// TODO write code to check all the formats supported by
|
||||
// http://yaml.org/type/timestamp.html instead of using time.Parse.
|
||||
|
||||
// Quick check: all date formats start with YYYY-.
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if c := s[i]; c < '0' || c > '9' {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i != 4 || i == len(s) || s[i] != '-' {
|
||||
return time.Time{}, false
|
||||
}
|
||||
for _, format := range allowedTimestampFormats {
|
||||
if t, err := time.Parse(format, s); err == nil {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
3038
vendor/gopkg.in/yaml.v3/scannerc.go
generated
vendored
3038
vendor/gopkg.in/yaml.v3/scannerc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
134
vendor/gopkg.in/yaml.v3/sorter.go
generated
vendored
134
vendor/gopkg.in/yaml.v3/sorter.go
generated
vendored
@ -1,134 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type keyList []reflect.Value
|
||||
|
||||
func (l keyList) Len() int { return len(l) }
|
||||
func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l keyList) Less(i, j int) bool {
|
||||
a := l[i]
|
||||
b := l[j]
|
||||
ak := a.Kind()
|
||||
bk := b.Kind()
|
||||
for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
|
||||
a = a.Elem()
|
||||
ak = a.Kind()
|
||||
}
|
||||
for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
|
||||
b = b.Elem()
|
||||
bk = b.Kind()
|
||||
}
|
||||
af, aok := keyFloat(a)
|
||||
bf, bok := keyFloat(b)
|
||||
if aok && bok {
|
||||
if af != bf {
|
||||
return af < bf
|
||||
}
|
||||
if ak != bk {
|
||||
return ak < bk
|
||||
}
|
||||
return numLess(a, b)
|
||||
}
|
||||
if ak != reflect.String || bk != reflect.String {
|
||||
return ak < bk
|
||||
}
|
||||
ar, br := []rune(a.String()), []rune(b.String())
|
||||
digits := false
|
||||
for i := 0; i < len(ar) && i < len(br); i++ {
|
||||
if ar[i] == br[i] {
|
||||
digits = unicode.IsDigit(ar[i])
|
||||
continue
|
||||
}
|
||||
al := unicode.IsLetter(ar[i])
|
||||
bl := unicode.IsLetter(br[i])
|
||||
if al && bl {
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
if al || bl {
|
||||
if digits {
|
||||
return al
|
||||
} else {
|
||||
return bl
|
||||
}
|
||||
}
|
||||
var ai, bi int
|
||||
var an, bn int64
|
||||
if ar[i] == '0' || br[i] == '0' {
|
||||
for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
|
||||
if ar[j] != '0' {
|
||||
an = 1
|
||||
bn = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
|
||||
an = an*10 + int64(ar[ai]-'0')
|
||||
}
|
||||
for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
|
||||
bn = bn*10 + int64(br[bi]-'0')
|
||||
}
|
||||
if an != bn {
|
||||
return an < bn
|
||||
}
|
||||
if ai != bi {
|
||||
return ai < bi
|
||||
}
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
return len(ar) < len(br)
|
||||
}
|
||||
|
||||
// keyFloat returns a float value for v if it is a number/bool
|
||||
// and whether it is a number/bool or not.
|
||||
func keyFloat(v reflect.Value) (f float64, ok bool) {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return float64(v.Uint()), true
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return 1, true
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// numLess returns whether a < b.
|
||||
// a and b must necessarily have the same kind.
|
||||
func numLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
}
|
||||
panic("not a number")
|
||||
}
|
48
vendor/gopkg.in/yaml.v3/writerc.go
generated
vendored
48
vendor/gopkg.in/yaml.v3/writerc.go
generated
vendored
@ -1,48 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
// Copyright (c) 2006-2010 Kirill Simonov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaml
|
||||
|
||||
// Set the writer error and return false.
|
||||
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
|
||||
emitter.error = yaml_WRITER_ERROR
|
||||
emitter.problem = problem
|
||||
return false
|
||||
}
|
||||
|
||||
// Flush the output buffer.
|
||||
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
|
||||
if emitter.write_handler == nil {
|
||||
panic("write handler not set")
|
||||
}
|
||||
|
||||
// Check if the buffer is empty.
|
||||
if emitter.buffer_pos == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
return true
|
||||
}
|
698
vendor/gopkg.in/yaml.v3/yaml.go
generated
vendored
698
vendor/gopkg.in/yaml.v3/yaml.go
generated
vendored
@ -1,698 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package yaml implements YAML support for the Go language.
|
||||
//
|
||||
// Source code and other details for the project are available at GitHub:
|
||||
//
|
||||
// https://github.com/go-yaml/yaml
|
||||
//
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// The Unmarshaler interface may be implemented by types to customize their
|
||||
// behavior when being unmarshaled from a YAML document.
|
||||
type Unmarshaler interface {
|
||||
UnmarshalYAML(value *Node) error
|
||||
}
|
||||
|
||||
type obsoleteUnmarshaler interface {
|
||||
UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
}
|
||||
|
||||
// The Marshaler interface may be implemented by types to customize their
|
||||
// behavior when being marshaled into a YAML document. The returned value
|
||||
// is marshaled in place of the original value implementing Marshaler.
|
||||
//
|
||||
// If an error is returned by MarshalYAML, the marshaling procedure stops
|
||||
// and returns with the provided error.
|
||||
type Marshaler interface {
|
||||
MarshalYAML() (interface{}, error)
|
||||
}
|
||||
|
||||
// Unmarshal decodes the first document found within the in byte slice
|
||||
// and assigns decoded values into the out value.
|
||||
//
|
||||
// Maps and pointers (to a struct, string, int, etc) are accepted as out
|
||||
// values. If an internal pointer within a struct is not initialized,
|
||||
// the yaml package will initialize it if necessary for unmarshalling
|
||||
// the provided data. The out parameter must not be nil.
|
||||
//
|
||||
// The type of the decoded values should be compatible with the respective
|
||||
// values in out. If one or more values cannot be decoded due to a type
|
||||
// mismatches, decoding continues partially until the end of the YAML
|
||||
// content, and a *yaml.TypeError is returned with details for all
|
||||
// missed values.
|
||||
//
|
||||
// Struct fields are only unmarshalled if they are exported (have an
|
||||
// upper case first letter), and are unmarshalled using the field name
|
||||
// lowercased as the default key. Custom keys may be defined via the
|
||||
// "yaml" name in the field tag: the content preceding the first comma
|
||||
// is used as the key, and the following comma-separated options are
|
||||
// used to tweak the marshalling process (see Marshal).
|
||||
// Conflicting names result in a runtime error.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int `yaml:"a,omitempty"`
|
||||
// B int
|
||||
// }
|
||||
// var t T
|
||||
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
||||
//
|
||||
// See the documentation of Marshal for the format of tags and a list of
|
||||
// supported tag options.
|
||||
//
|
||||
func Unmarshal(in []byte, out interface{}) (err error) {
|
||||
return unmarshal(in, out, false)
|
||||
}
|
||||
|
||||
// A Decoder reads and decodes YAML values from an input stream.
|
||||
type Decoder struct {
|
||||
parser *parser
|
||||
knownFields bool
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
//
|
||||
// The decoder introduces its own buffering and may read
|
||||
// data from r beyond the YAML values requested.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
parser: newParserFromReader(r),
|
||||
}
|
||||
}
|
||||
|
||||
// KnownFields ensures that the keys in decoded mappings to
|
||||
// exist as fields in the struct being decoded into.
|
||||
func (dec *Decoder) KnownFields(enable bool) {
|
||||
dec.knownFields = enable
|
||||
}
|
||||
|
||||
// Decode reads the next YAML-encoded value from its input
|
||||
// and stores it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the
|
||||
// conversion of YAML into a Go value.
|
||||
func (dec *Decoder) Decode(v interface{}) (err error) {
|
||||
d := newDecoder()
|
||||
d.knownFields = dec.knownFields
|
||||
defer handleErr(&err)
|
||||
node := dec.parser.parse()
|
||||
if node == nil {
|
||||
return io.EOF
|
||||
}
|
||||
out := reflect.ValueOf(v)
|
||||
if out.Kind() == reflect.Ptr && !out.IsNil() {
|
||||
out = out.Elem()
|
||||
}
|
||||
d.unmarshal(node, out)
|
||||
if len(d.terrors) > 0 {
|
||||
return &TypeError{d.terrors}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode decodes the node and stores its data into the value pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about the
|
||||
// conversion of YAML into a Go value.
|
||||
func (n *Node) Decode(v interface{}) (err error) {
|
||||
d := newDecoder()
|
||||
defer handleErr(&err)
|
||||
out := reflect.ValueOf(v)
|
||||
if out.Kind() == reflect.Ptr && !out.IsNil() {
|
||||
out = out.Elem()
|
||||
}
|
||||
d.unmarshal(n, out)
|
||||
if len(d.terrors) > 0 {
|
||||
return &TypeError{d.terrors}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
||||
defer handleErr(&err)
|
||||
d := newDecoder()
|
||||
p := newParser(in)
|
||||
defer p.destroy()
|
||||
node := p.parse()
|
||||
if node != nil {
|
||||
v := reflect.ValueOf(out)
|
||||
if v.Kind() == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
d.unmarshal(node, v)
|
||||
}
|
||||
if len(d.terrors) > 0 {
|
||||
return &TypeError{d.terrors}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal serializes the value provided into a YAML document. The structure
|
||||
// of the generated document will reflect the structure of the value itself.
|
||||
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
|
||||
//
|
||||
// Struct fields are only marshalled if they are exported (have an upper case
|
||||
// first letter), and are marshalled using the field name lowercased as the
|
||||
// default key. Custom keys may be defined via the "yaml" name in the field
|
||||
// tag: the content preceding the first comma is used as the key, and the
|
||||
// following comma-separated options are used to tweak the marshalling process.
|
||||
// Conflicting names result in a runtime error.
|
||||
//
|
||||
// The field tag format accepted is:
|
||||
//
|
||||
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
|
||||
//
|
||||
// The following flags are currently supported:
|
||||
//
|
||||
// omitempty Only include the field if it's not set to the zero
|
||||
// value for the type or to empty slices or maps.
|
||||
// Zero valued structs will be omitted if all their public
|
||||
// fields are zero, unless they implement an IsZero
|
||||
// method (see the IsZeroer interface type), in which
|
||||
// case the field will be excluded if IsZero returns true.
|
||||
//
|
||||
// flow Marshal using a flow style (useful for structs,
|
||||
// sequences and maps).
|
||||
//
|
||||
// inline Inline the field, which must be a struct or a map,
|
||||
// causing all of its fields or keys to be processed as if
|
||||
// they were part of the outer struct. For maps, keys must
|
||||
// not conflict with the yaml keys of other struct fields.
|
||||
//
|
||||
// In addition, if the key is "-", the field is ignored.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int `yaml:"a,omitempty"`
|
||||
// B int
|
||||
// }
|
||||
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
||||
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
||||
//
|
||||
func Marshal(in interface{}) (out []byte, err error) {
|
||||
defer handleErr(&err)
|
||||
e := newEncoder()
|
||||
defer e.destroy()
|
||||
e.marshalDoc("", reflect.ValueOf(in))
|
||||
e.finish()
|
||||
out = e.out
|
||||
return
|
||||
}
|
||||
|
||||
// An Encoder writes YAML values to an output stream.
|
||||
type Encoder struct {
|
||||
encoder *encoder
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
// The Encoder should be closed after use to flush all data
|
||||
// to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{
|
||||
encoder: newEncoderWithWriter(w),
|
||||
}
|
||||
}
|
||||
|
||||
// Encode writes the YAML encoding of v to the stream.
|
||||
// If multiple items are encoded to the stream, the
|
||||
// second and subsequent document will be preceded
|
||||
// with a "---" document separator, but the first will not.
|
||||
//
|
||||
// See the documentation for Marshal for details about the conversion of Go
|
||||
// values to YAML.
|
||||
func (e *Encoder) Encode(v interface{}) (err error) {
|
||||
defer handleErr(&err)
|
||||
e.encoder.marshalDoc("", reflect.ValueOf(v))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes value v and stores its representation in n.
|
||||
//
|
||||
// See the documentation for Marshal for details about the
|
||||
// conversion of Go values into YAML.
|
||||
func (n *Node) Encode(v interface{}) (err error) {
|
||||
defer handleErr(&err)
|
||||
e := newEncoder()
|
||||
defer e.destroy()
|
||||
e.marshalDoc("", reflect.ValueOf(v))
|
||||
e.finish()
|
||||
p := newParser(e.out)
|
||||
p.textless = true
|
||||
defer p.destroy()
|
||||
doc := p.parse()
|
||||
*n = *doc.Content[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetIndent changes the used indentation used when encoding.
|
||||
func (e *Encoder) SetIndent(spaces int) {
|
||||
if spaces < 0 {
|
||||
panic("yaml: cannot indent to a negative number of spaces")
|
||||
}
|
||||
e.encoder.indent = spaces
|
||||
}
|
||||
|
||||
// Close closes the encoder by writing any remaining data.
|
||||
// It does not write a stream terminating string "...".
|
||||
func (e *Encoder) Close() (err error) {
|
||||
defer handleErr(&err)
|
||||
e.encoder.finish()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleErr(err *error) {
|
||||
if v := recover(); v != nil {
|
||||
if e, ok := v.(yamlError); ok {
|
||||
*err = e.err
|
||||
} else {
|
||||
panic(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type yamlError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func fail(err error) {
|
||||
panic(yamlError{err})
|
||||
}
|
||||
|
||||
func failf(format string, args ...interface{}) {
|
||||
panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
|
||||
}
|
||||
|
||||
// A TypeError is returned by Unmarshal when one or more fields in
|
||||
// the YAML document cannot be properly decoded into the requested
|
||||
// types. When this error is returned, the value is still
|
||||
// unmarshaled partially.
|
||||
type TypeError struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
func (e *TypeError) Error() string {
|
||||
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
|
||||
}
|
||||
|
||||
type Kind uint32
|
||||
|
||||
const (
|
||||
DocumentNode Kind = 1 << iota
|
||||
SequenceNode
|
||||
MappingNode
|
||||
ScalarNode
|
||||
AliasNode
|
||||
)
|
||||
|
||||
type Style uint32
|
||||
|
||||
const (
|
||||
TaggedStyle Style = 1 << iota
|
||||
DoubleQuotedStyle
|
||||
SingleQuotedStyle
|
||||
LiteralStyle
|
||||
FoldedStyle
|
||||
FlowStyle
|
||||
)
|
||||
|
||||
// Node represents an element in the YAML document hierarchy. While documents
|
||||
// are typically encoded and decoded into higher level types, such as structs
|
||||
// and maps, Node is an intermediate representation that allows detailed
|
||||
// control over the content being decoded or encoded.
|
||||
//
|
||||
// It's worth noting that although Node offers access into details such as
|
||||
// line numbers, colums, and comments, the content when re-encoded will not
|
||||
// have its original textual representation preserved. An effort is made to
|
||||
// render the data plesantly, and to preserve comments near the data they
|
||||
// describe, though.
|
||||
//
|
||||
// Values that make use of the Node type interact with the yaml package in the
|
||||
// same way any other type would do, by encoding and decoding yaml data
|
||||
// directly or indirectly into them.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// var person struct {
|
||||
// Name string
|
||||
// Address yaml.Node
|
||||
// }
|
||||
// err := yaml.Unmarshal(data, &person)
|
||||
//
|
||||
// Or by itself:
|
||||
//
|
||||
// var person Node
|
||||
// err := yaml.Unmarshal(data, &person)
|
||||
//
|
||||
type Node struct {
|
||||
// Kind defines whether the node is a document, a mapping, a sequence,
|
||||
// a scalar value, or an alias to another node. The specific data type of
|
||||
// scalar nodes may be obtained via the ShortTag and LongTag methods.
|
||||
Kind Kind
|
||||
|
||||
// Style allows customizing the apperance of the node in the tree.
|
||||
Style Style
|
||||
|
||||
// Tag holds the YAML tag defining the data type for the value.
|
||||
// When decoding, this field will always be set to the resolved tag,
|
||||
// even when it wasn't explicitly provided in the YAML content.
|
||||
// When encoding, if this field is unset the value type will be
|
||||
// implied from the node properties, and if it is set, it will only
|
||||
// be serialized into the representation if TaggedStyle is used or
|
||||
// the implicit tag diverges from the provided one.
|
||||
Tag string
|
||||
|
||||
// Value holds the unescaped and unquoted represenation of the value.
|
||||
Value string
|
||||
|
||||
// Anchor holds the anchor name for this node, which allows aliases to point to it.
|
||||
Anchor string
|
||||
|
||||
// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
|
||||
Alias *Node
|
||||
|
||||
// Content holds contained nodes for documents, mappings, and sequences.
|
||||
Content []*Node
|
||||
|
||||
// HeadComment holds any comments in the lines preceding the node and
|
||||
// not separated by an empty line.
|
||||
HeadComment string
|
||||
|
||||
// LineComment holds any comments at the end of the line where the node is in.
|
||||
LineComment string
|
||||
|
||||
// FootComment holds any comments following the node and before empty lines.
|
||||
FootComment string
|
||||
|
||||
// Line and Column hold the node position in the decoded YAML text.
|
||||
// These fields are not respected when encoding the node.
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
// IsZero returns whether the node has all of its fields unset.
|
||||
func (n *Node) IsZero() bool {
|
||||
return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
|
||||
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
|
||||
}
|
||||
|
||||
|
||||
// LongTag returns the long form of the tag that indicates the data type for
|
||||
// the node. If the Tag field isn't explicitly defined, one will be computed
|
||||
// based on the node properties.
|
||||
func (n *Node) LongTag() string {
|
||||
return longTag(n.ShortTag())
|
||||
}
|
||||
|
||||
// ShortTag returns the short form of the YAML tag that indicates data type for
|
||||
// the node. If the Tag field isn't explicitly defined, one will be computed
|
||||
// based on the node properties.
|
||||
func (n *Node) ShortTag() string {
|
||||
if n.indicatedString() {
|
||||
return strTag
|
||||
}
|
||||
if n.Tag == "" || n.Tag == "!" {
|
||||
switch n.Kind {
|
||||
case MappingNode:
|
||||
return mapTag
|
||||
case SequenceNode:
|
||||
return seqTag
|
||||
case AliasNode:
|
||||
if n.Alias != nil {
|
||||
return n.Alias.ShortTag()
|
||||
}
|
||||
case ScalarNode:
|
||||
tag, _ := resolve("", n.Value)
|
||||
return tag
|
||||
case 0:
|
||||
// Special case to make the zero value convenient.
|
||||
if n.IsZero() {
|
||||
return nullTag
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return shortTag(n.Tag)
|
||||
}
|
||||
|
||||
func (n *Node) indicatedString() bool {
|
||||
return n.Kind == ScalarNode &&
|
||||
(shortTag(n.Tag) == strTag ||
|
||||
(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
|
||||
}
|
||||
|
||||
// SetString is a convenience function that sets the node to a string value
|
||||
// and defines its style in a pleasant way depending on its content.
|
||||
func (n *Node) SetString(s string) {
|
||||
n.Kind = ScalarNode
|
||||
if utf8.ValidString(s) {
|
||||
n.Value = s
|
||||
n.Tag = strTag
|
||||
} else {
|
||||
n.Value = encodeBase64(s)
|
||||
n.Tag = binaryTag
|
||||
}
|
||||
if strings.Contains(n.Value, "\n") {
|
||||
n.Style = LiteralStyle
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Maintain a mapping of keys to structure field indexes
|
||||
|
||||
// The code in this section was copied from mgo/bson.
|
||||
|
||||
// structInfo holds details for the serialization of fields of
|
||||
// a given struct.
|
||||
type structInfo struct {
|
||||
FieldsMap map[string]fieldInfo
|
||||
FieldsList []fieldInfo
|
||||
|
||||
// InlineMap is the number of the field in the struct that
|
||||
// contains an ,inline map, or -1 if there's none.
|
||||
InlineMap int
|
||||
|
||||
// InlineUnmarshalers holds indexes to inlined fields that
|
||||
// contain unmarshaler values.
|
||||
InlineUnmarshalers [][]int
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
Key string
|
||||
Num int
|
||||
OmitEmpty bool
|
||||
Flow bool
|
||||
// Id holds the unique field identifier, so we can cheaply
|
||||
// check for field duplicates without maintaining an extra map.
|
||||
Id int
|
||||
|
||||
// Inline holds the field index if the field is part of an inlined struct.
|
||||
Inline []int
|
||||
}
|
||||
|
||||
var structMap = make(map[reflect.Type]*structInfo)
|
||||
var fieldMapMutex sync.RWMutex
|
||||
var unmarshalerType reflect.Type
|
||||
|
||||
func init() {
|
||||
var v Unmarshaler
|
||||
unmarshalerType = reflect.ValueOf(&v).Elem().Type()
|
||||
}
|
||||
|
||||
func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||
fieldMapMutex.RLock()
|
||||
sinfo, found := structMap[st]
|
||||
fieldMapMutex.RUnlock()
|
||||
if found {
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
n := st.NumField()
|
||||
fieldsMap := make(map[string]fieldInfo)
|
||||
fieldsList := make([]fieldInfo, 0, n)
|
||||
inlineMap := -1
|
||||
inlineUnmarshalers := [][]int(nil)
|
||||
for i := 0; i != n; i++ {
|
||||
field := st.Field(i)
|
||||
if field.PkgPath != "" && !field.Anonymous {
|
||||
continue // Private field
|
||||
}
|
||||
|
||||
info := fieldInfo{Num: i}
|
||||
|
||||
tag := field.Tag.Get("yaml")
|
||||
if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
|
||||
tag = string(field.Tag)
|
||||
}
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
inline := false
|
||||
fields := strings.Split(tag, ",")
|
||||
if len(fields) > 1 {
|
||||
for _, flag := range fields[1:] {
|
||||
switch flag {
|
||||
case "omitempty":
|
||||
info.OmitEmpty = true
|
||||
case "flow":
|
||||
info.Flow = true
|
||||
case "inline":
|
||||
inline = true
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
|
||||
}
|
||||
}
|
||||
tag = fields[0]
|
||||
}
|
||||
|
||||
if inline {
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Map:
|
||||
if inlineMap >= 0 {
|
||||
return nil, errors.New("multiple ,inline maps in struct " + st.String())
|
||||
}
|
||||
if field.Type.Key() != reflect.TypeOf("") {
|
||||
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
|
||||
}
|
||||
inlineMap = info.Num
|
||||
case reflect.Struct, reflect.Ptr:
|
||||
ftype := field.Type
|
||||
for ftype.Kind() == reflect.Ptr {
|
||||
ftype = ftype.Elem()
|
||||
}
|
||||
if ftype.Kind() != reflect.Struct {
|
||||
return nil, errors.New("option ,inline may only be used on a struct or map field")
|
||||
}
|
||||
if reflect.PtrTo(ftype).Implements(unmarshalerType) {
|
||||
inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
|
||||
} else {
|
||||
sinfo, err := getStructInfo(ftype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, index := range sinfo.InlineUnmarshalers {
|
||||
inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
|
||||
}
|
||||
for _, finfo := range sinfo.FieldsList {
|
||||
if _, found := fieldsMap[finfo.Key]; found {
|
||||
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
if finfo.Inline == nil {
|
||||
finfo.Inline = []int{i, finfo.Num}
|
||||
} else {
|
||||
finfo.Inline = append([]int{i}, finfo.Inline...)
|
||||
}
|
||||
finfo.Id = len(fieldsList)
|
||||
fieldsMap[finfo.Key] = finfo
|
||||
fieldsList = append(fieldsList, finfo)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("option ,inline may only be used on a struct or map field")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag != "" {
|
||||
info.Key = tag
|
||||
} else {
|
||||
info.Key = strings.ToLower(field.Name)
|
||||
}
|
||||
|
||||
if _, found = fieldsMap[info.Key]; found {
|
||||
msg := "duplicated key '" + info.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
info.Id = len(fieldsList)
|
||||
fieldsList = append(fieldsList, info)
|
||||
fieldsMap[info.Key] = info
|
||||
}
|
||||
|
||||
sinfo = &structInfo{
|
||||
FieldsMap: fieldsMap,
|
||||
FieldsList: fieldsList,
|
||||
InlineMap: inlineMap,
|
||||
InlineUnmarshalers: inlineUnmarshalers,
|
||||
}
|
||||
|
||||
fieldMapMutex.Lock()
|
||||
structMap[st] = sinfo
|
||||
fieldMapMutex.Unlock()
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
// IsZeroer is used to check whether an object is zero to
|
||||
// determine whether it should be omitted when marshaling
|
||||
// with the omitempty flag. One notable implementation
|
||||
// is time.Time.
|
||||
type IsZeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
kind := v.Kind()
|
||||
if z, ok := v.Interface().(IsZeroer); ok {
|
||||
if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
|
||||
return true
|
||||
}
|
||||
return z.IsZero()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return len(v.String()) == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
case reflect.Slice:
|
||||
return v.Len() == 0
|
||||
case reflect.Map:
|
||||
return v.Len() == 0
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Struct:
|
||||
vt := v.Type()
|
||||
for i := v.NumField() - 1; i >= 0; i-- {
|
||||
if vt.Field(i).PkgPath != "" {
|
||||
continue // Private field
|
||||
}
|
||||
if !isZero(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
807
vendor/gopkg.in/yaml.v3/yamlh.go
generated
vendored
807
vendor/gopkg.in/yaml.v3/yamlh.go
generated
vendored
@ -1,807 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
// Copyright (c) 2006-2010 Kirill Simonov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// The version directive data.
|
||||
type yaml_version_directive_t struct {
|
||||
major int8 // The major version number.
|
||||
minor int8 // The minor version number.
|
||||
}
|
||||
|
||||
// The tag directive data.
|
||||
type yaml_tag_directive_t struct {
|
||||
handle []byte // The tag handle.
|
||||
prefix []byte // The tag prefix.
|
||||
}
|
||||
|
||||
type yaml_encoding_t int
|
||||
|
||||
// The stream encoding.
|
||||
const (
|
||||
// Let the parser choose the encoding.
|
||||
yaml_ANY_ENCODING yaml_encoding_t = iota
|
||||
|
||||
yaml_UTF8_ENCODING // The default UTF-8 encoding.
|
||||
yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM.
|
||||
yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM.
|
||||
)
|
||||
|
||||
type yaml_break_t int
|
||||
|
||||
// Line break types.
|
||||
const (
|
||||
// Let the parser choose the break type.
|
||||
yaml_ANY_BREAK yaml_break_t = iota
|
||||
|
||||
yaml_CR_BREAK // Use CR for line breaks (Mac style).
|
||||
yaml_LN_BREAK // Use LN for line breaks (Unix style).
|
||||
yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style).
|
||||
)
|
||||
|
||||
type yaml_error_type_t int
|
||||
|
||||
// Many bad things could happen with the parser and emitter.
|
||||
const (
|
||||
// No error is produced.
|
||||
yaml_NO_ERROR yaml_error_type_t = iota
|
||||
|
||||
yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory.
|
||||
yaml_READER_ERROR // Cannot read or decode the input stream.
|
||||
yaml_SCANNER_ERROR // Cannot scan the input stream.
|
||||
yaml_PARSER_ERROR // Cannot parse the input stream.
|
||||
yaml_COMPOSER_ERROR // Cannot compose a YAML document.
|
||||
yaml_WRITER_ERROR // Cannot write to the output stream.
|
||||
yaml_EMITTER_ERROR // Cannot emit a YAML stream.
|
||||
)
|
||||
|
||||
// The pointer position.
|
||||
type yaml_mark_t struct {
|
||||
index int // The position index.
|
||||
line int // The position line.
|
||||
column int // The position column.
|
||||
}
|
||||
|
||||
// Node Styles
|
||||
|
||||
type yaml_style_t int8
|
||||
|
||||
type yaml_scalar_style_t yaml_style_t
|
||||
|
||||
// Scalar styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0
|
||||
|
||||
yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style.
|
||||
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
|
||||
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
|
||||
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
|
||||
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
|
||||
)
|
||||
|
||||
type yaml_sequence_style_t yaml_style_t
|
||||
|
||||
// Sequence styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
|
||||
|
||||
yaml_BLOCK_SEQUENCE_STYLE // The block sequence style.
|
||||
yaml_FLOW_SEQUENCE_STYLE // The flow sequence style.
|
||||
)
|
||||
|
||||
type yaml_mapping_style_t yaml_style_t
|
||||
|
||||
// Mapping styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
|
||||
|
||||
yaml_BLOCK_MAPPING_STYLE // The block mapping style.
|
||||
yaml_FLOW_MAPPING_STYLE // The flow mapping style.
|
||||
)
|
||||
|
||||
// Tokens
|
||||
|
||||
type yaml_token_type_t int
|
||||
|
||||
// Token types.
|
||||
const (
|
||||
// An empty token.
|
||||
yaml_NO_TOKEN yaml_token_type_t = iota
|
||||
|
||||
yaml_STREAM_START_TOKEN // A STREAM-START token.
|
||||
yaml_STREAM_END_TOKEN // A STREAM-END token.
|
||||
|
||||
yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token.
|
||||
yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token.
|
||||
yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token.
|
||||
yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token.
|
||||
|
||||
yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token.
|
||||
yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token.
|
||||
yaml_BLOCK_END_TOKEN // A BLOCK-END token.
|
||||
|
||||
yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token.
|
||||
yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token.
|
||||
yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token.
|
||||
yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token.
|
||||
|
||||
yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token.
|
||||
yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token.
|
||||
yaml_KEY_TOKEN // A KEY token.
|
||||
yaml_VALUE_TOKEN // A VALUE token.
|
||||
|
||||
yaml_ALIAS_TOKEN // An ALIAS token.
|
||||
yaml_ANCHOR_TOKEN // An ANCHOR token.
|
||||
yaml_TAG_TOKEN // A TAG token.
|
||||
yaml_SCALAR_TOKEN // A SCALAR token.
|
||||
)
|
||||
|
||||
func (tt yaml_token_type_t) String() string {
|
||||
switch tt {
|
||||
case yaml_NO_TOKEN:
|
||||
return "yaml_NO_TOKEN"
|
||||
case yaml_STREAM_START_TOKEN:
|
||||
return "yaml_STREAM_START_TOKEN"
|
||||
case yaml_STREAM_END_TOKEN:
|
||||
return "yaml_STREAM_END_TOKEN"
|
||||
case yaml_VERSION_DIRECTIVE_TOKEN:
|
||||
return "yaml_VERSION_DIRECTIVE_TOKEN"
|
||||
case yaml_TAG_DIRECTIVE_TOKEN:
|
||||
return "yaml_TAG_DIRECTIVE_TOKEN"
|
||||
case yaml_DOCUMENT_START_TOKEN:
|
||||
return "yaml_DOCUMENT_START_TOKEN"
|
||||
case yaml_DOCUMENT_END_TOKEN:
|
||||
return "yaml_DOCUMENT_END_TOKEN"
|
||||
case yaml_BLOCK_SEQUENCE_START_TOKEN:
|
||||
return "yaml_BLOCK_SEQUENCE_START_TOKEN"
|
||||
case yaml_BLOCK_MAPPING_START_TOKEN:
|
||||
return "yaml_BLOCK_MAPPING_START_TOKEN"
|
||||
case yaml_BLOCK_END_TOKEN:
|
||||
return "yaml_BLOCK_END_TOKEN"
|
||||
case yaml_FLOW_SEQUENCE_START_TOKEN:
|
||||
return "yaml_FLOW_SEQUENCE_START_TOKEN"
|
||||
case yaml_FLOW_SEQUENCE_END_TOKEN:
|
||||
return "yaml_FLOW_SEQUENCE_END_TOKEN"
|
||||
case yaml_FLOW_MAPPING_START_TOKEN:
|
||||
return "yaml_FLOW_MAPPING_START_TOKEN"
|
||||
case yaml_FLOW_MAPPING_END_TOKEN:
|
||||
return "yaml_FLOW_MAPPING_END_TOKEN"
|
||||
case yaml_BLOCK_ENTRY_TOKEN:
|
||||
return "yaml_BLOCK_ENTRY_TOKEN"
|
||||
case yaml_FLOW_ENTRY_TOKEN:
|
||||
return "yaml_FLOW_ENTRY_TOKEN"
|
||||
case yaml_KEY_TOKEN:
|
||||
return "yaml_KEY_TOKEN"
|
||||
case yaml_VALUE_TOKEN:
|
||||
return "yaml_VALUE_TOKEN"
|
||||
case yaml_ALIAS_TOKEN:
|
||||
return "yaml_ALIAS_TOKEN"
|
||||
case yaml_ANCHOR_TOKEN:
|
||||
return "yaml_ANCHOR_TOKEN"
|
||||
case yaml_TAG_TOKEN:
|
||||
return "yaml_TAG_TOKEN"
|
||||
case yaml_SCALAR_TOKEN:
|
||||
return "yaml_SCALAR_TOKEN"
|
||||
}
|
||||
return "<unknown token>"
|
||||
}
|
||||
|
||||
// The token structure.
|
||||
type yaml_token_t struct {
|
||||
// The token type.
|
||||
typ yaml_token_type_t
|
||||
|
||||
// The start/end of the token.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
|
||||
// The stream encoding (for yaml_STREAM_START_TOKEN).
|
||||
encoding yaml_encoding_t
|
||||
|
||||
// The alias/anchor/scalar value or tag/tag directive handle
|
||||
// (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN).
|
||||
value []byte
|
||||
|
||||
// The tag suffix (for yaml_TAG_TOKEN).
|
||||
suffix []byte
|
||||
|
||||
// The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN).
|
||||
prefix []byte
|
||||
|
||||
// The scalar style (for yaml_SCALAR_TOKEN).
|
||||
style yaml_scalar_style_t
|
||||
|
||||
// The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN).
|
||||
major, minor int8
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
type yaml_event_type_t int8
|
||||
|
||||
// Event types.
|
||||
const (
|
||||
// An empty event.
|
||||
yaml_NO_EVENT yaml_event_type_t = iota
|
||||
|
||||
yaml_STREAM_START_EVENT // A STREAM-START event.
|
||||
yaml_STREAM_END_EVENT // A STREAM-END event.
|
||||
yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event.
|
||||
yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event.
|
||||
yaml_ALIAS_EVENT // An ALIAS event.
|
||||
yaml_SCALAR_EVENT // A SCALAR event.
|
||||
yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event.
|
||||
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
|
||||
yaml_MAPPING_START_EVENT // A MAPPING-START event.
|
||||
yaml_MAPPING_END_EVENT // A MAPPING-END event.
|
||||
yaml_TAIL_COMMENT_EVENT
|
||||
)
|
||||
|
||||
var eventStrings = []string{
|
||||
yaml_NO_EVENT: "none",
|
||||
yaml_STREAM_START_EVENT: "stream start",
|
||||
yaml_STREAM_END_EVENT: "stream end",
|
||||
yaml_DOCUMENT_START_EVENT: "document start",
|
||||
yaml_DOCUMENT_END_EVENT: "document end",
|
||||
yaml_ALIAS_EVENT: "alias",
|
||||
yaml_SCALAR_EVENT: "scalar",
|
||||
yaml_SEQUENCE_START_EVENT: "sequence start",
|
||||
yaml_SEQUENCE_END_EVENT: "sequence end",
|
||||
yaml_MAPPING_START_EVENT: "mapping start",
|
||||
yaml_MAPPING_END_EVENT: "mapping end",
|
||||
yaml_TAIL_COMMENT_EVENT: "tail comment",
|
||||
}
|
||||
|
||||
func (e yaml_event_type_t) String() string {
|
||||
if e < 0 || int(e) >= len(eventStrings) {
|
||||
return fmt.Sprintf("unknown event %d", e)
|
||||
}
|
||||
return eventStrings[e]
|
||||
}
|
||||
|
||||
// The event structure.
|
||||
type yaml_event_t struct {
|
||||
|
||||
// The event type.
|
||||
typ yaml_event_type_t
|
||||
|
||||
// The start and end of the event.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
|
||||
// The document encoding (for yaml_STREAM_START_EVENT).
|
||||
encoding yaml_encoding_t
|
||||
|
||||
// The version directive (for yaml_DOCUMENT_START_EVENT).
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
|
||||
tag_directives []yaml_tag_directive_t
|
||||
|
||||
// The comments
|
||||
head_comment []byte
|
||||
line_comment []byte
|
||||
foot_comment []byte
|
||||
tail_comment []byte
|
||||
|
||||
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
|
||||
anchor []byte
|
||||
|
||||
// The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
|
||||
tag []byte
|
||||
|
||||
// The scalar value (for yaml_SCALAR_EVENT).
|
||||
value []byte
|
||||
|
||||
// Is the document start/end indicator implicit, or the tag optional?
|
||||
// (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT).
|
||||
implicit bool
|
||||
|
||||
// Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT).
|
||||
quoted_implicit bool
|
||||
|
||||
// The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
|
||||
style yaml_style_t
|
||||
}
|
||||
|
||||
func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) }
|
||||
func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) }
|
||||
func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) }
|
||||
|
||||
// Nodes
|
||||
|
||||
const (
|
||||
yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null.
|
||||
yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false.
|
||||
yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values.
|
||||
yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values.
|
||||
yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values.
|
||||
yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values.
|
||||
|
||||
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
|
||||
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
|
||||
|
||||
// Not in original libyaml.
|
||||
yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
|
||||
yaml_MERGE_TAG = "tag:yaml.org,2002:merge"
|
||||
|
||||
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
|
||||
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
|
||||
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
|
||||
)
|
||||
|
||||
type yaml_node_type_t int
|
||||
|
||||
// Node types.
|
||||
const (
|
||||
// An empty node.
|
||||
yaml_NO_NODE yaml_node_type_t = iota
|
||||
|
||||
yaml_SCALAR_NODE // A scalar node.
|
||||
yaml_SEQUENCE_NODE // A sequence node.
|
||||
yaml_MAPPING_NODE // A mapping node.
|
||||
)
|
||||
|
||||
// An element of a sequence node.
|
||||
type yaml_node_item_t int
|
||||
|
||||
// An element of a mapping node.
|
||||
type yaml_node_pair_t struct {
|
||||
key int // The key of the element.
|
||||
value int // The value of the element.
|
||||
}
|
||||
|
||||
// The node structure.
|
||||
type yaml_node_t struct {
|
||||
typ yaml_node_type_t // The node type.
|
||||
tag []byte // The node tag.
|
||||
|
||||
// The node data.
|
||||
|
||||
// The scalar parameters (for yaml_SCALAR_NODE).
|
||||
scalar struct {
|
||||
value []byte // The scalar value.
|
||||
length int // The length of the scalar value.
|
||||
style yaml_scalar_style_t // The scalar style.
|
||||
}
|
||||
|
||||
// The sequence parameters (for YAML_SEQUENCE_NODE).
|
||||
sequence struct {
|
||||
items_data []yaml_node_item_t // The stack of sequence items.
|
||||
style yaml_sequence_style_t // The sequence style.
|
||||
}
|
||||
|
||||
// The mapping parameters (for yaml_MAPPING_NODE).
|
||||
mapping struct {
|
||||
pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value).
|
||||
pairs_start *yaml_node_pair_t // The beginning of the stack.
|
||||
pairs_end *yaml_node_pair_t // The end of the stack.
|
||||
pairs_top *yaml_node_pair_t // The top of the stack.
|
||||
style yaml_mapping_style_t // The mapping style.
|
||||
}
|
||||
|
||||
start_mark yaml_mark_t // The beginning of the node.
|
||||
end_mark yaml_mark_t // The end of the node.
|
||||
|
||||
}
|
||||
|
||||
// The document structure.
|
||||
type yaml_document_t struct {
|
||||
|
||||
// The document nodes.
|
||||
nodes []yaml_node_t
|
||||
|
||||
// The version directive.
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
// The list of tag directives.
|
||||
tag_directives_data []yaml_tag_directive_t
|
||||
tag_directives_start int // The beginning of the tag directives list.
|
||||
tag_directives_end int // The end of the tag directives list.
|
||||
|
||||
start_implicit int // Is the document start indicator implicit?
|
||||
end_implicit int // Is the document end indicator implicit?
|
||||
|
||||
// The start/end of the document.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
}
|
||||
|
||||
// The prototype of a read handler.
|
||||
//
|
||||
// The read handler is called when the parser needs to read more bytes from the
|
||||
// source. The handler should write not more than size bytes to the buffer.
|
||||
// The number of written bytes should be set to the size_read variable.
|
||||
//
|
||||
// [in,out] data A pointer to an application data specified by
|
||||
// yaml_parser_set_input().
|
||||
// [out] buffer The buffer to write the data from the source.
|
||||
// [in] size The size of the buffer.
|
||||
// [out] size_read The actual number of bytes read from the source.
|
||||
//
|
||||
// On success, the handler should return 1. If the handler failed,
|
||||
// the returned value should be 0. On EOF, the handler should set the
|
||||
// size_read to 0 and return 1.
|
||||
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
|
||||
|
||||
// This structure holds information about a potential simple key.
|
||||
type yaml_simple_key_t struct {
|
||||
possible bool // Is a simple key possible?
|
||||
required bool // Is a simple key required?
|
||||
token_number int // The number of the token.
|
||||
mark yaml_mark_t // The position mark.
|
||||
}
|
||||
|
||||
// The states of the parser.
|
||||
type yaml_parser_state_t int
|
||||
|
||||
const (
|
||||
yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
|
||||
|
||||
yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document.
|
||||
yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START.
|
||||
yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document.
|
||||
yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END.
|
||||
yaml_PARSE_BLOCK_NODE_STATE // Expect a block node.
|
||||
yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence.
|
||||
yaml_PARSE_FLOW_NODE_STATE // Expect a flow node.
|
||||
yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence.
|
||||
yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence.
|
||||
yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence.
|
||||
yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
|
||||
yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key.
|
||||
yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value.
|
||||
yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry.
|
||||
yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping.
|
||||
yaml_PARSE_END_STATE // Expect nothing.
|
||||
)
|
||||
|
||||
func (ps yaml_parser_state_t) String() string {
|
||||
switch ps {
|
||||
case yaml_PARSE_STREAM_START_STATE:
|
||||
return "yaml_PARSE_STREAM_START_STATE"
|
||||
case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
|
||||
return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE"
|
||||
case yaml_PARSE_DOCUMENT_START_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_START_STATE"
|
||||
case yaml_PARSE_DOCUMENT_CONTENT_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_CONTENT_STATE"
|
||||
case yaml_PARSE_DOCUMENT_END_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_END_STATE"
|
||||
case yaml_PARSE_BLOCK_NODE_STATE:
|
||||
return "yaml_PARSE_BLOCK_NODE_STATE"
|
||||
case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
|
||||
return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE"
|
||||
case yaml_PARSE_FLOW_NODE_STATE:
|
||||
return "yaml_PARSE_FLOW_NODE_STATE"
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE"
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE"
|
||||
case yaml_PARSE_END_STATE:
|
||||
return "yaml_PARSE_END_STATE"
|
||||
}
|
||||
return "<unknown parser state>"
|
||||
}
|
||||
|
||||
// This structure holds aliases data.
|
||||
type yaml_alias_data_t struct {
|
||||
anchor []byte // The anchor.
|
||||
index int // The node id.
|
||||
mark yaml_mark_t // The anchor mark.
|
||||
}
|
||||
|
||||
// The parser structure.
|
||||
//
|
||||
// All members are internal. Manage the structure using the
|
||||
// yaml_parser_ family of functions.
|
||||
type yaml_parser_t struct {
|
||||
|
||||
// Error handling
|
||||
|
||||
error yaml_error_type_t // Error type.
|
||||
|
||||
problem string // Error description.
|
||||
|
||||
// The byte about which the problem occurred.
|
||||
problem_offset int
|
||||
problem_value int
|
||||
problem_mark yaml_mark_t
|
||||
|
||||
// The error context.
|
||||
context string
|
||||
context_mark yaml_mark_t
|
||||
|
||||
// Reader stuff
|
||||
|
||||
read_handler yaml_read_handler_t // Read handler.
|
||||
|
||||
input_reader io.Reader // File input data.
|
||||
input []byte // String input data.
|
||||
input_pos int
|
||||
|
||||
eof bool // EOF flag
|
||||
|
||||
buffer []byte // The working buffer.
|
||||
buffer_pos int // The current position of the buffer.
|
||||
|
||||
unread int // The number of unread characters in the buffer.
|
||||
|
||||
newlines int // The number of line breaks since last non-break/non-blank character
|
||||
|
||||
raw_buffer []byte // The raw buffer.
|
||||
raw_buffer_pos int // The current position of the buffer.
|
||||
|
||||
encoding yaml_encoding_t // The input encoding.
|
||||
|
||||
offset int // The offset of the current position (in bytes).
|
||||
mark yaml_mark_t // The mark of the current position.
|
||||
|
||||
// Comments
|
||||
|
||||
head_comment []byte // The current head comments
|
||||
line_comment []byte // The current line comments
|
||||
foot_comment []byte // The current foot comments
|
||||
tail_comment []byte // Foot comment that happens at the end of a block.
|
||||
stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc)
|
||||
|
||||
comments []yaml_comment_t // The folded comments for all parsed tokens
|
||||
comments_head int
|
||||
|
||||
// Scanner stuff
|
||||
|
||||
stream_start_produced bool // Have we started to scan the input stream?
|
||||
stream_end_produced bool // Have we reached the end of the input stream?
|
||||
|
||||
flow_level int // The number of unclosed '[' and '{' indicators.
|
||||
|
||||
tokens []yaml_token_t // The tokens queue.
|
||||
tokens_head int // The head of the tokens queue.
|
||||
tokens_parsed int // The number of tokens fetched from the queue.
|
||||
token_available bool // Does the tokens queue contain a token ready for dequeueing.
|
||||
|
||||
indent int // The current indentation level.
|
||||
indents []int // The indentation levels stack.
|
||||
|
||||
simple_key_allowed bool // May a simple key occur at the current position?
|
||||
simple_keys []yaml_simple_key_t // The stack of simple keys.
|
||||
simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number
|
||||
|
||||
// Parser stuff
|
||||
|
||||
state yaml_parser_state_t // The current parser state.
|
||||
states []yaml_parser_state_t // The parser states stack.
|
||||
marks []yaml_mark_t // The stack of marks.
|
||||
tag_directives []yaml_tag_directive_t // The list of TAG directives.
|
||||
|
||||
// Dumper stuff
|
||||
|
||||
aliases []yaml_alias_data_t // The alias data.
|
||||
|
||||
document *yaml_document_t // The currently parsed document.
|
||||
}
|
||||
|
||||
type yaml_comment_t struct {
|
||||
|
||||
scan_mark yaml_mark_t // Position where scanning for comments started
|
||||
token_mark yaml_mark_t // Position after which tokens will be associated with this comment
|
||||
start_mark yaml_mark_t // Position of '#' comment mark
|
||||
end_mark yaml_mark_t // Position where comment terminated
|
||||
|
||||
head []byte
|
||||
line []byte
|
||||
foot []byte
|
||||
}
|
||||
|
||||
// Emitter Definitions
|
||||
|
||||
// The prototype of a write handler.
|
||||
//
|
||||
// The write handler is called when the emitter needs to flush the accumulated
|
||||
// characters to the output. The handler should write @a size bytes of the
|
||||
// @a buffer to the output.
|
||||
//
|
||||
// @param[in,out] data A pointer to an application data specified by
|
||||
// yaml_emitter_set_output().
|
||||
// @param[in] buffer The buffer with bytes to be written.
|
||||
// @param[in] size The size of the buffer.
|
||||
//
|
||||
// @returns On success, the handler should return @c 1. If the handler failed,
|
||||
// the returned value should be @c 0.
|
||||
//
|
||||
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
|
||||
|
||||
type yaml_emitter_state_t int
|
||||
|
||||
// The emitter states.
|
||||
const (
|
||||
// Expect STREAM-START.
|
||||
yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
|
||||
|
||||
yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END.
|
||||
yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END.
|
||||
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
|
||||
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
|
||||
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
|
||||
yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out
|
||||
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
|
||||
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out
|
||||
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
|
||||
yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence.
|
||||
yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence.
|
||||
yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping.
|
||||
yaml_EMIT_END_STATE // Expect nothing.
|
||||
)
|
||||
|
||||
// The emitter structure.
|
||||
//
|
||||
// All members are internal. Manage the structure using the @c yaml_emitter_
|
||||
// family of functions.
|
||||
type yaml_emitter_t struct {
|
||||
|
||||
// Error handling
|
||||
|
||||
error yaml_error_type_t // Error type.
|
||||
problem string // Error description.
|
||||
|
||||
// Writer stuff
|
||||
|
||||
write_handler yaml_write_handler_t // Write handler.
|
||||
|
||||
output_buffer *[]byte // String output data.
|
||||
output_writer io.Writer // File output data.
|
||||
|
||||
buffer []byte // The working buffer.
|
||||
buffer_pos int // The current position of the buffer.
|
||||
|
||||
raw_buffer []byte // The raw buffer.
|
||||
raw_buffer_pos int // The current position of the buffer.
|
||||
|
||||
encoding yaml_encoding_t // The stream encoding.
|
||||
|
||||
// Emitter stuff
|
||||
|
||||
canonical bool // If the output is in the canonical style?
|
||||
best_indent int // The number of indentation spaces.
|
||||
best_width int // The preferred width of the output lines.
|
||||
unicode bool // Allow unescaped non-ASCII characters?
|
||||
line_break yaml_break_t // The preferred line break.
|
||||
|
||||
state yaml_emitter_state_t // The current emitter state.
|
||||
states []yaml_emitter_state_t // The stack of states.
|
||||
|
||||
events []yaml_event_t // The event queue.
|
||||
events_head int // The head of the event queue.
|
||||
|
||||
indents []int // The stack of indentation levels.
|
||||
|
||||
tag_directives []yaml_tag_directive_t // The list of tag directives.
|
||||
|
||||
indent int // The current indentation level.
|
||||
|
||||
flow_level int // The current flow level.
|
||||
|
||||
root_context bool // Is it the document root context?
|
||||
sequence_context bool // Is it a sequence context?
|
||||
mapping_context bool // Is it a mapping context?
|
||||
simple_key_context bool // Is it a simple mapping key context?
|
||||
|
||||
line int // The current line.
|
||||
column int // The current column.
|
||||
whitespace bool // If the last character was a whitespace?
|
||||
indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
|
||||
open_ended bool // If an explicit document end is required?
|
||||
|
||||
space_above bool // Is there's an empty line above?
|
||||
foot_indent int // The indent used to write the foot comment above, or -1 if none.
|
||||
|
||||
// Anchor analysis.
|
||||
anchor_data struct {
|
||||
anchor []byte // The anchor value.
|
||||
alias bool // Is it an alias?
|
||||
}
|
||||
|
||||
// Tag analysis.
|
||||
tag_data struct {
|
||||
handle []byte // The tag handle.
|
||||
suffix []byte // The tag suffix.
|
||||
}
|
||||
|
||||
// Scalar analysis.
|
||||
scalar_data struct {
|
||||
value []byte // The scalar value.
|
||||
multiline bool // Does the scalar contain line breaks?
|
||||
flow_plain_allowed bool // Can the scalar be expessed in the flow plain style?
|
||||
block_plain_allowed bool // Can the scalar be expressed in the block plain style?
|
||||
single_quoted_allowed bool // Can the scalar be expressed in the single quoted style?
|
||||
block_allowed bool // Can the scalar be expressed in the literal or folded styles?
|
||||
style yaml_scalar_style_t // The output style.
|
||||
}
|
||||
|
||||
// Comments
|
||||
head_comment []byte
|
||||
line_comment []byte
|
||||
foot_comment []byte
|
||||
tail_comment []byte
|
||||
|
||||
key_line_comment []byte
|
||||
|
||||
// Dumper stuff
|
||||
|
||||
opened bool // If the stream was already opened?
|
||||
closed bool // If the stream was already closed?
|
||||
|
||||
// The information associated with the document nodes.
|
||||
anchors *struct {
|
||||
references int // The number of references.
|
||||
anchor int // The anchor id.
|
||||
serialized bool // If the node has been emitted?
|
||||
}
|
||||
|
||||
last_anchor_id int // The last assigned anchor id.
|
||||
|
||||
document *yaml_document_t // The currently emitted document.
|
||||
}
|
198
vendor/gopkg.in/yaml.v3/yamlprivateh.go
generated
vendored
198
vendor/gopkg.in/yaml.v3/yamlprivateh.go
generated
vendored
@ -1,198 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2011-2019 Canonical Ltd
|
||||
// Copyright (c) 2006-2010 Kirill Simonov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaml
|
||||
|
||||
const (
|
||||
// The size of the input raw buffer.
|
||||
input_raw_buffer_size = 512
|
||||
|
||||
// The size of the input buffer.
|
||||
// It should be possible to decode the whole raw buffer.
|
||||
input_buffer_size = input_raw_buffer_size * 3
|
||||
|
||||
// The size of the output buffer.
|
||||
output_buffer_size = 128
|
||||
|
||||
// The size of the output raw buffer.
|
||||
// It should be possible to encode the whole output buffer.
|
||||
output_raw_buffer_size = (output_buffer_size*2 + 2)
|
||||
|
||||
// The size of other stacks and queues.
|
||||
initial_stack_size = 16
|
||||
initial_queue_size = 16
|
||||
initial_string_size = 16
|
||||
)
|
||||
|
||||
// Check if the character at the specified position is an alphabetical
|
||||
// character, a digit, '_', or '-'.
|
||||
func is_alpha(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a digit.
|
||||
func is_digit(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9'
|
||||
}
|
||||
|
||||
// Get the value of a digit.
|
||||
func as_digit(b []byte, i int) int {
|
||||
return int(b[i]) - '0'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a hex-digit.
|
||||
func is_hex(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f'
|
||||
}
|
||||
|
||||
// Get the value of a hex-digit.
|
||||
func as_hex(b []byte, i int) int {
|
||||
bi := b[i]
|
||||
if bi >= 'A' && bi <= 'F' {
|
||||
return int(bi) - 'A' + 10
|
||||
}
|
||||
if bi >= 'a' && bi <= 'f' {
|
||||
return int(bi) - 'a' + 10
|
||||
}
|
||||
return int(bi) - '0'
|
||||
}
|
||||
|
||||
// Check if the character is ASCII.
|
||||
func is_ascii(b []byte, i int) bool {
|
||||
return b[i] <= 0x7F
|
||||
}
|
||||
|
||||
// Check if the character at the start of the buffer can be printed unescaped.
|
||||
func is_printable(b []byte, i int) bool {
|
||||
return ((b[i] == 0x0A) || // . == #x0A
|
||||
(b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E
|
||||
(b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF
|
||||
(b[i] > 0xC2 && b[i] < 0xED) ||
|
||||
(b[i] == 0xED && b[i+1] < 0xA0) ||
|
||||
(b[i] == 0xEE) ||
|
||||
(b[i] == 0xEF && // #xE000 <= . <= #xFFFD
|
||||
!(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF
|
||||
!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is NUL.
|
||||
func is_z(b []byte, i int) bool {
|
||||
return b[i] == 0x00
|
||||
}
|
||||
|
||||
// Check if the beginning of the buffer is a BOM.
|
||||
func is_bom(b []byte, i int) bool {
|
||||
return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is space.
|
||||
func is_space(b []byte, i int) bool {
|
||||
return b[i] == ' '
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is tab.
|
||||
func is_tab(b []byte, i int) bool {
|
||||
return b[i] == '\t'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is blank (space or tab).
|
||||
func is_blank(b []byte, i int) bool {
|
||||
//return is_space(b, i) || is_tab(b, i)
|
||||
return b[i] == ' ' || b[i] == '\t'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a line break.
|
||||
func is_break(b []byte, i int) bool {
|
||||
return (b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029)
|
||||
}
|
||||
|
||||
func is_crlf(b []byte, i int) bool {
|
||||
return b[i] == '\r' && b[i+1] == '\n'
|
||||
}
|
||||
|
||||
// Check if the character is a line break or NUL.
|
||||
func is_breakz(b []byte, i int) bool {
|
||||
//return is_break(b, i) || is_z(b, i)
|
||||
return (
|
||||
// is_break:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
// is_z:
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Check if the character is a line break, space, or NUL.
|
||||
func is_spacez(b []byte, i int) bool {
|
||||
//return is_space(b, i) || is_breakz(b, i)
|
||||
return (
|
||||
// is_space:
|
||||
b[i] == ' ' ||
|
||||
// is_breakz:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Check if the character is a line break, space, tab, or NUL.
|
||||
func is_blankz(b []byte, i int) bool {
|
||||
//return is_blank(b, i) || is_breakz(b, i)
|
||||
return (
|
||||
// is_blank:
|
||||
b[i] == ' ' || b[i] == '\t' ||
|
||||
// is_breakz:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Determine the width of the character.
|
||||
func width(b byte) int {
|
||||
// Don't replace these by a switch without first
|
||||
// confirming that it is being inlined.
|
||||
if b&0x80 == 0x00 {
|
||||
return 1
|
||||
}
|
||||
if b&0xE0 == 0xC0 {
|
||||
return 2
|
||||
}
|
||||
if b&0xF0 == 0xE0 {
|
||||
return 3
|
||||
}
|
||||
if b&0xF8 == 0xF0 {
|
||||
return 4
|
||||
}
|
||||
return 0
|
||||
|
||||
}
|
13
vendor/modules.txt
vendored
13
vendor/modules.txt
vendored
@ -1,13 +0,0 @@
|
||||
# github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385
|
||||
## explicit
|
||||
github.com/eknkc/amber
|
||||
github.com/eknkc/amber/parser
|
||||
# github.com/russross/blackfriday/v2 v2.1.0
|
||||
## explicit
|
||||
github.com/russross/blackfriday/v2
|
||||
# github.com/yosssi/gcss v0.1.0
|
||||
## explicit
|
||||
github.com/yosssi/gcss
|
||||
# gopkg.in/yaml.v3 v3.0.1
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
Loading…
x
Reference in New Issue
Block a user