[WIP] Initial commit

This commit is contained in:
eleksir
2025-10-26 23:34:15 +03:00
commit 521ac9abc0
75 changed files with 25289 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/01-LoadJsonFile
/*.exe

21
LICENSE.TXT Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2025 Sergei Fedosoff, <eleksir@gmail.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.

122
Makefile Normal file
View File

@@ -0,0 +1,122 @@
#!/usr/bin/env gmake -f
GOOPTS=CGO_ENABLED=0
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -buildvcs=false
MYNAME=doll
BINARY=${MYNAME}
UNIX_BINARY=${MYNAME}
WINDOWS_BINARY=${MYNAME}.exe
RMCMD=rm -rf
# На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe
# не нужен
ifeq ($(OS),Windows_NT)
ifdef GOOS
ifeq ($(GOOS),windows)
BINARY=${WINDOWS_BINARY}
else # not ifeq ($(GOOS),windows)
BINARY=${MYNAME}
endif # ifeq ($(GOOS),windows)
else # not ifdef GOOS
BINARY=${WINDOWS_BINARY}
endif # ifdef GOOS
ifeq ($(SHELL), sh.exe)
RMCMD=DEL /Q /F
endif
endif
# Явно определяем символ новой строки, чтобы избежать неоднозначности на windows
define IFS
endef
all: clean build
build:
ifeq ($(OS),Windows_NT)
# Looks like on windows gnu make explicitly set SHELL to sh.exe, if it was not set.
ifeq ($(SHELL), sh.exe)
# # Vanilla cmd.exe / powershell.
SET "CGO_ENABLED=0"
go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
else ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # git-bash
CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
else # not ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # Some other shell.
# # TODO: handle it.
$(info "-- Dunno how to handle this shell: ${SHELL}")
endif # ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
else # not ($(OS),Windows_NT)
CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
endif # ifeq ($(OS),Windows_NT)
clean:
ifeq ($(OS),Windows_NT)
ifeq ($(SHELL),sh.exe)
# # Vanilla cmd.exe / powershell.
if exist 01-LoadJsonFile.exe ${RMCMD} 01-LoadJsonFile.exe
if exist 01-LoadJsonFile ${RMCMD} 01-LoadJsonFile
if exist 02-TestLuaScript.exe ${RMCMD} 02-TestLuaScript.exe
if exist 02-TestLuaScript ${RMCMD} 02-TestLuaScript
else # not ifeq ($(SHELL),sh.exe)
${RMCMD} ./01-LoadJsonFile.exe
${RMCMD} ./01-LoadJsonFile
${RMCMD} ./02-TestLuaScript.exe
${RMCMD} ./02-TestLuaScript
endif # ifeq ($(SHELL),sh.exe)
else # not ifeq ($(OS),Windows_NT)
${RMCMD} ./01-LoadJsonFile
${RMCMD} ./02-TestLuaScript
endif
upgrade:
go get -u ./...
go mod tidy
go mod vendor
01-LoadJsonFile: clean
ifeq ($(OS),Windows_NT)
# Looks like on windows gnu make explicitly set SHELL to sh.exe, if it was not set.
ifeq ($(SHELL), sh.exe)
# # Vanilla cmd.exe / powershell.
SET "CGO_ENABLED=0"
go build ${BUILDOPTS} -o 01-LoadJsonFile.exe ./cmd/01-LoadJsonFile
else ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # git-bash
CGO_ENABLED=0 go build ${BUILDOPTS} -o 01-LoadJsonFile.exe ./cmd/01-LoadJsonFile
else # not ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # Some other shell.
# # TODO: handle it.
$(info "-- Dunno how to handle this shell: ${SHELL}")
endif # ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
else # not ($(OS),Windows_NT)
CGO_ENABLED=0 go build ${BUILDOPTS} -o 01-LoadJsonFile ./cmd/01-LoadJsonFile
endif # ifeq ($(OS),Windows_NT)
02-TestLuaScript: clean
ifeq ($(OS),Windows_NT)
# Looks like on windows gnu make explicitly set SHELL to sh.exe, if it was not set.
ifeq ($(SHELL), sh.exe)
# # Vanilla cmd.exe / powershell.
SET "CGO_ENABLED=0"
go build ${BUILDOPTS} -o 02-TestLuaScript.exe ./cmd/02-TestLuaScript
else ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # git-bash
CGO_ENABLED=0 go build ${BUILDOPTS} -o 02-TestLuaScript.exe ./cmd/02-TestLuaScript
else # not ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
# # Some other shell.
# # TODO: handle it.
$(info "-- Dunno how to handle this shell: ${SHELL}")
endif # ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
else # not ($(OS),Windows_NT)
CGO_ENABLED=0 go build ${BUILDOPTS} -o 02-TestLuaScript ./cmd/02-TestLuaScript
endif # ifeq ($(OS),Windows_NT)
# vim: set ft=make noet ai ts=4 sw=4 sts=4:

83
README.md Normal file
View File

@@ -0,0 +1,83 @@
# What it should become?
Utility service like puppetlabs puppet, but a bit simplified. It will consist of 2 parts - server and agent. Each agent
will be able to fetch only configs thaat intened only for it's use. On server-side there will be jsons that describing
separate hosts, host-groups and services and all of these files will be under control of some vcs like hg or git.
## When it will be ready to see and touch?
There is no due or deadline it will de ready to test or use. All depends on speed of researching abilities of lua and
other go modules. And spare time ofcorse. It is some sort of pet-project. If there is any use of this project it will
be great!
## Short story - why this project began?
The thing is that puppetlabs puppet become fat-ass monster that is relatively hard to maintain. Its development process
move relatively in fast pace newer versions are sometimes incompatible with older ones it is hotably hard to keep heap
of already working manifests, templates and configs up to date with newer versions of puppet.
Version 2.7 of puppet was relatively easy to maintain but awful to install on newer systems. Matz's Ruby Interpreter is
unable to deliver acceptable level of performance hogging cpu fast. So i have to run JRuby which using JVM which is
famous for its memory wasting. It is faster than MRI, but compiling newer JVM on older linuxes is real pain in the ass.
In ruby eco-system exist Ruby-On-Rails framework that is all-in-one solution for web apps in ruby ecosystem. But
puppetlabs puppet does not use it. So it is relatively hard to force puppet server work in proper scalable and stable
fashion. If you want to package puppet server, you have to build ruby itself, and vendor tons of modules of specific
versions and also put there puppel-server itself.
It is clear that with each new version of puppet its appetites grew in exploding manner and as of today it is became fat
beast.
So to speak, it was version 2.7 with it's own bust, that relavely well working, but it is more than 10 years old and
long ago obsoleted bu puppetlabs. It was designed to run on later versions of ruby-1.8. With some minor quirks it is
possible to make it work on ruby-1.9. But to make it work on more recent versions of ruby puppet-2.7 should be heavily
reworked. And generally puppet suffres fron performance issues because it was written entirely on ruby.
Speaking of puppet agent it is resource hog too.
Some time ago i've got an idea to puppet me home servers but in a more pleasurable manner. And I looked at what is on
"market" exist on this toppic. It was same players: puppet, chef, ansible, salstack, cfengine.
And all of these solutions have thei own weak spots. And one of the most painful is portability. For example ansible
itself claims tha it is agentless. But in reality it uses naked python on client-side, they blew somewhat-compatible
client-code on python3 stdin and it executes it. Agent is ephemeral but in order to run commands it requires real
python3 interpreter installation, so they just lie about agentlessity of puppet to sell it to sylly ones.
Nevertheless, in my opinion puppet is most suitable solution because it not only able to setup services but also
designed to support cosistent configuration and on configuration change it restores changes back. And well puppeted
servers can preserve its servers configuration. Agents regulary run and poll puppet server for configs state and compare
it with local state situation. All configs transformed to their original state and revices restarted to maintain
original configuration.
Thats why i've com to idea of making my own service same in spirit as puppet, but less complex. Its goal will be to
distribute config changes and maintain state of puppeted machines.
puppet in a nutshell, but on a diet.
## What is working mechanics?
The request made fron client to server and payload of request describes some facts abou itself like what os it running,
what its hostname, networking interfaces, addresses, package manager etc - all general info that can be required for
configuration. As respose server sends long json that describes what configs with which data should be placed on
filesystem and which services should be restarted.
## What about dashboard-а?
I am not able to make very beutiful dashboards. The idea is to cheat and make simple to use API to give the idea of
state of servers and supply some additional metrics and data. This api and be joined with monitoring system (like zabbix,
icnga, etc) to make nice dashboards and perform alerts and notifications. It is more convinient way of representing
status data in my opinion.
## Why lua was taken as scripting language?
There is more than one implementation of lua language on go. Including github.com/yuin/gopher-lua which seems to be
interesting. It gives ability to make own modules in go. At least it is possible to make own implementation of json
and http(s) modules that can be useful in lua templaing. Also it is possible to make lua module that can put variables
from go namespace to lua's.
## So, how progress on this puppet-thing implementation?
All progress in this repo.
Right now it all about r'n'd of current libraries and crutch all of this to make workint pieces of code for lua
templating engine.

86
README.ru.md Normal file
View File

@@ -0,0 +1,86 @@
# Чем это должно стать?
Утилитарным сервисом, работающим по принципу puppet-а, в упрощённом виде. Есть сервер, есть клиенты. Каждый клиент может
забрать с сервера только свои конфиги. На сервере описаны конфиги для отдельных хостов, групп серверов и сервисов,
которые находятся, напрмер, под управлением системы контроля версий, типа hg или git.
## А когда уже можно будет это всё заценить?
Сроков нет - проект никем и ничем не спонсируется. В зависимости от того, насколько прорывным будет исследование
возможностей lua и прочих модулей go. Это такой пет-проджект, для интереса. ЕСли от этого будет и польза это будет
потрясающе.
## Краткая история, зачем оно появилось?
Дело в том, что паппет быстро стал огромным монстром, поддерживать который довольно сложно, а учитывая, что он
сравнительно быстро развивается и часто более новые версии несовместимы с более старыми, приходится сильно напрягаться
при поддержке теплейтов и манифестов для управляемых серверов.
Старая версия, например 2.7, была ещё относительно простой в обслуживании, но просто ужасной в плане запуска серверной
части. С ростом количества серверов, c-шный руби начинал нещадно тормозить, выжирая процессор. Приходилось брать всякие
извращения типа jruby. Который был конечно быстрее, но поскольку это java то сервер становился более прожорливым к
памяти, да и java-приложения обладают ограниченной портабельностью, особенно теперь, когда версии jvm каждый год теряют
в обратной совместимости, а собирать-портировать новые версии jvm под старые системы - то ещё удовольствие.
Далее, в рубях всё довольно грустно с запуском веб-приложений, несмотря на то, что руби имеет в своём распоряжении
rails. Паппет-сервер написан не как rails-приложение и из этого растут трудности с со сборкой-запуском этого приложения.
Для того, чтобы собрать работающий *правильно* паппет-сервер, надо собирать как руби, так и кучу его модулей и в эту
систему вписывать дистрибутив паппета.
Понятное дело, что с каждой новой версией аппетиты паппета растут как на дрожжах и на текущий момент это довольно жирный
монстр.
Условно говоря, последняя более-менее приемлемо работающая версия была 2.7, с кучей своих багов, однако этой версии уже
больше 10 лет, она была написана ещё для рубей 1.8, кое-как заводилась на 1.9. Да и вцелом у неё были ощутимые проблемы
с производительностью, что не удивительно, ведь это приложение было написано полностью на рубях.
Да и клиент паппета тоже не то чтобы скромный сервис в своих аппетитах по памяти и по процессору.
Какое-то время назад у меня возникла идея "опаппетить" свои домашние сервера, на сей раз в более приятной манере, я
решил присмотреться, что у нас есть на "рынке" систем управления конфигурациями. А там всё те же участники:
puppet, chef, ansible, saltstack, cfengine.
И у всех этих утилит есть или были свои болячки. И одна из самых неприятных - портабельность. То есть даже ansible,
который пытаются продавать как "безагентный" на самом деле вполне себе агентный, просто агент у него эфемерный - это
питон, которому на ввод вдувают довольно портабельный код.
Но тем не менее, одним из самых удачных на мой взгляд является именно паппет, потому что он не только сетапит хосты, но
и поддерживает конфиги в определённом состоянии. То есть агентская часть запускается на регулярной основе и если в
отслеживаемых конфигах обнаруживаются изменения, то они меняются на место, а ответственные сервисы перезапускаются.
И вот, отсюда у меня родилась идея, а почему бы не написать свой инструмент такого же плана, но только более примитивный.
Его задачей будет регулярная проверка отслеживаемых конфигов и восстановление состояния.
По сути, тот же паппет, но на диете.
## Какова механика в общих чертах?
С клиента на сервер поступает запрос, в этом запросе клиент описывает что у него за система, как его
зовут и прочие такие "факты" общего характера. Ему в ответ возвращается огромный json, в котором будет описано, какие
конфиги куда положить, какие сервисы надо рестартануть.
Из нюансов, агент должен уметь показывать diff того, что поменялось.
## Что насчёт dashboard-а?
Я не силён в красивые dashboard-ы. Идея заключается в том, чтобы схитрить и предоставить простенькое http-api для
выяснения состояний управляемых серверов и прочих таких вещей, чтобы можно было взять систему мониторинга и запросто
прикрутить к ней все интересные метрики, составить там свой dashboard, собирать какую-то стату и так далее - всё красиво
и даже есть "стандартная" система оповещений. :)
## Почему взят lua в качестве скриптового языка?
Всё просто - на go существует несколько реализаций этого яп. В том числе github.com/yuin/gopher-lua мне показалась
интересной в плане того, что там есть возможность создавать свои модули для lua на go. Как минимум, можно будет создать
свой клиент для http(s) и json, которые могут понадобится для написания темплейтов. А также можно будет создать свой
модуль, позволяющий подгружать переменные из гошного адресного пространства в lua-шное.
## Ну и как успехи в реализации этого "альтернативного паппета"?
Все успехи в этом репозитории.
Пока что всё сводится к исследованию того, что могут современные библиотеки и обвешивании всего этого костылями для
получения кусков работающего кода, который уже можно использовать как компоненты для целевой системы.
На текущий момент, изучаем возможности интерпретатора lua, что по сути должно стать в итоге тем яп, на котором будет
написана скриптовая часть темплейтов.

View File

@@ -0,0 +1,21 @@
package main
import (
"doll/internal/doll"
"doll/internal/log"
"os"
"github.com/davecgh/go-spew/spew"
)
func main() {
log.Init("info", os.Stderr)
log.Info("loading config.json")
data := doll.LoadJsonFile("test_data/config.json")
log.Info("data fron config.json is")
spew.Dump(data)
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -0,0 +1,34 @@
package main
import (
"doll/internal/log"
"os"
lua "github.com/yuin/gopher-lua"
)
func main() {
log.Init("info", os.Stderr)
L := lua.NewState()
defer L.Close()
// Load the Lua script file.
log.Info("Loading ./test_data/01-test.lua")
luaCode, err := os.ReadFile("./test_data/01-test.lua")
if err != nil {
log.Errorf("Error reading Lua file: %s", err)
os.Exit(1)
}
if err := L.DoString(string(luaCode)); err != nil {
log.Errorf("Error evaluating Lua code: %s", err)
os.Exit(1)
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

9
go.mod Normal file
View File

@@ -0,0 +1,9 @@
module doll
go 1.25.3
require (
github.com/davecgh/go-spew v1.1.1
github.com/hjson/hjson-go/v4 v4.5.0
github.com/yuin/gopher-lua v1.1.1
)

6
go.sum Normal file
View File

@@ -0,0 +1,6 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hjson/hjson-go/v4 v4.5.0 h1:ZHLiZ+HaGqPOtEe8T6qY8QHnoEsAeBv8wqxniQAp+CY=
github.com/hjson/hjson-go/v4 v4.5.0/go.mod h1:4zx6c7Y0vWcm8IRyVoQJUHAPJLXLvbG6X8nk1RLigSo=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=

10
internal/doll/globals.go Normal file
View File

@@ -0,0 +1,10 @@
package doll
// Instance global variable that contains all variables defined in jsons describing hosts, host groups and services.
var Instance map[string]any
// Facts global variable that contains all data that host provides us with.
// TODO: think about struct format for this data. As log as it is machine-written encoding/json must be used.
var Facts map[string]any
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

30
internal/doll/jsonfile.go Normal file
View File

@@ -0,0 +1,30 @@
package doll
import (
"doll/internal/log"
"encoding/json"
"os"
)
// LoadJsonFile loads data from json to Instance global variable map.
func LoadJsonFile(file string) map[string]any {
var (
err error
data []byte
)
data, err = os.ReadFile(file)
if err != nil {
log.Fatalf("Unable to load %s: %s", file, err)
}
err = json.Unmarshal([]byte(data), &Instance)
if err != nil {
log.Fatalf("Unable to marshal JSON data from %s: %s", file, err)
}
return Instance
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

3
internal/doll/types.go Normal file
View File

@@ -0,0 +1,3 @@
package doll
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

176
internal/log/log.go Normal file
View File

@@ -0,0 +1,176 @@
package log
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"log/slog"
"os"
"time"
)
// Handler exposes slog handler struct.
var Handler slog.Handler
// Ctx log context. For now it is just sits here and do nothing.
var Ctx = context.Background()
// Writer go-writer eported for capturing other types of logs, even for std log go facility.
var Writer io.Writer
// reader is required for Writer to work properly. It is connected via io.Pipe() to Writer.
var reader io.Reader
// Init setup logger stuff.
// level can be error, warn, info, debug if something other supplied info level selected.
// fileDescriptor should be opened before supplying it to Init().
func Init(level string, fileDescriptor *os.File) {
var loglevel slog.Level
// no panic, no trace.
switch level {
case "debug":
loglevel = slog.LevelDebug
case "info":
loglevel = slog.LevelInfo
case "warn":
loglevel = slog.LevelWarn
case "error":
loglevel = slog.LevelError
default:
loglevel = slog.LevelInfo
}
opts := &slog.HandlerOptions{ //nolint: exhaustruct
// Use the ReplaceAttr function on the handler options
// to be able to replace any single attribute in the log output
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { //nolint: revive
// check that we are handling the time key
if a.Key != slog.TimeKey {
return a
}
t := a.Value.Time()
// change the value from a time.Time to a String
// where the string has the correct time format.
a.Value = slog.StringValue(t.Format(time.DateTime))
return a
},
Level: loglevel,
}
Handler = slog.NewTextHandler(fileDescriptor, opts)
slog.SetDefault(
slog.New(Handler),
)
reader, Writer = io.Pipe()
go readIoPipe()
}
// readIoPipe reads pipe and writes it to slog.
func readIoPipe() {
scanner := bufio.NewScanner(reader)
for {
for scanner.Scan() {
line := scanner.Text() // Get the current line as a string
slog.Debug(line)
}
if err := scanner.Err(); err != nil {
// io.EOF is expected when done reading, e.g. on exit.
if errors.Is(err, io.EOF) {
return
}
// something weird happen, we should it log to guess later what it was.
Errorf("Error during scanning log writer pipe: %v\n", err)
}
}
}
// GetLevel returns current loglevel.
func GetLevel() string {
switch {
case Handler.Enabled(Ctx, slog.LevelDebug):
return "debug"
case Handler.Enabled(Ctx, slog.LevelInfo):
return "info"
case Handler.Enabled(Ctx, slog.LevelWarn):
return "warn"
case Handler.Enabled(Ctx, slog.LevelError):
return "error"
default:
return "unknown"
}
}
// Fatal passes message directly to slog.Error() and exits with error code 1.
func Fatal(message string) {
slog.Error(message)
os.Exit(1)
}
// Fatalf logs message on error log level and exits with error code 1. It allows to supply arguments in printf() style .
func Fatalf(format string, a ...any) {
slog.Error(fmt.Sprintf(format, a...))
os.Exit(1)
}
// Error passes message directly to slog.Error().
func Error(message string) {
slog.Error(message)
}
// Errorf logs message on error log level and allows to supply arguments in printf() style.
func Errorf(format string, a ...any) {
slog.Error(fmt.Sprintf(format, a...))
}
// Warn passes message directly to slog.Warn().
func Warn(message string) {
slog.Warn(message)
}
// Warnf logs message on warn log level and allows to supply arguments in printf() style.
func Warnf(format string, a ...any) {
slog.Warn(fmt.Sprintf(format, a...))
}
// Info passes message directly to slog.Info().
func Info(message string) {
slog.Info(message)
}
// Infof logs message on info log level and allows to supply arguments in printf() style.
func Infof(format string, a ...any) {
slog.Info(fmt.Sprintf(format, a...))
}
// Debug passes message directly to slog.Debug().
func Debug(message string) {
slog.Debug(message)
}
// Debugf logs message on debug log level and allows to supply arguments in printf() style.
func Debugf(format string, a ...any) {
slog.Debug(fmt.Sprintf(format, a...))
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -0,0 +1,100 @@
package serverSonfig
import (
"encoding/json"
"fmt"
"os"
"doll/internal/log"
hjson "github.com/hjson/hjson-go/v4"
)
// MyConfig config structure prototype.
type MyConfig struct {
Loglevel string `json:"loglevel,omitempty"`
Logfile string `json:"logfile,omitempty"`
}
var (
// Config contains application running config data.
Config MyConfig
)
// ReadConfig reads and validates config file. Setups defaults where possible if values are not provided.
func ReadConfig(location string) error { //nolint:gocognit,gocyclo
var (
err error
configLoaded bool
)
fileInfo, err := os.Stat(location)
if err != nil {
return fmt.Errorf("unable to read %s: %w", location, err)
}
// Config file seems to be too long for valid config.
if fileInfo.Size() > 65535 {
return fmt.Errorf("config file %s is too long for config, skipping", location)
}
buf, err := os.ReadFile(location)
// Config file is unreadable for some reasons.
if err != nil {
return fmt.Errorf("Skip reading config file %s: %s", location, err)
}
// According to docs hjson claims to be able to pase bad json, but parses it to go map.
// It is more interesting to get strict as result, so we parse data to go map, then make json out of this map and
// then parse this json to struct. It is not the most effective and speedy solution but it works and this action
// been performed not so often.
var (
sampleConfig MyConfig
tmp map[string]interface{}
)
err = hjson.Unmarshal(buf, &tmp)
// Unable to parse json to map, that is unfortunate.
if err != nil {
return fmt.Errorf("Skip parsing config file %s: %s", location, err)
}
tmpJSON, err := json.Marshal(tmp)
// Unable to parse map to json, well, we can nothing to with that.
if err != nil {
return fmt.Errorf("Skip parsing config file %s: %s", location, err)
}
// Unable to parse json to struct, just bail it here.
if err := json.Unmarshal(tmpJSON, &sampleConfig); err != nil {
return fmt.Errorf("Skip parsing config file %s: %s", location, err)
}
// Time to validate config values here.
if sampleConfig.Loglevel == "" {
sampleConfig.Loglevel = "info"
log.Info("loglevel not defined in config, using info")
}
// sampleConfig.Log = "" if not set
// TODO: validate the rest values fron config.
Config = sampleConfig
configLoaded = true
log.Infof("Using %s as config file", location)
if !configLoaded {
return fmt.Errorf("config was not loaded!") //nolint:goerr113,revive,stylecheck
}
return err //nolint:wrapcheck
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

3
test_data/01-test.lua Normal file
View File

@@ -0,0 +1,3 @@
print ("Hello World!")
-- vim: set ft=go noet ai ts=4 sw=4 sts=4:

4
test_data/config.json Normal file
View File

@@ -0,0 +1,4 @@
{
"loglevel": "info",
"logfile": "/var/log/messages"
}

15
vendor/github.com/davecgh/go-spew/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

145
vendor/github.com/davecgh/go-spew/spew/bypass.go generated vendored Normal file
View File

@@ -0,0 +1,145 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
import (
"reflect"
"unsafe"
)
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = false
// ptrSize is the size of a pointer on the current arch.
ptrSize = unsafe.Sizeof((*byte)(nil))
)
type flag uintptr
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data. It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}

38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe !go1.4
package spew
import "reflect"
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = true
)
// unsafeReflectValue typically converts the passed reflect.Value into a one
// that bypasses the typical safety restrictions preventing access to
// unaddressable and unexported data. However, doing this relies on access to
// the unsafe package. This is a stub version which simply returns the passed
// reflect.Value when the unsafe package is not available.
func unsafeReflectValue(v reflect.Value) reflect.Value {
return v
}

341
vendor/github.com/davecgh/go-spew/spew/common.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
panicBytes = []byte("(PANIC=")
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface {})")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
asteriskBytes = []byte("*")
colonBytes = []byte(":")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
spaceBytes = []byte(" ")
pointerChainBytes = []byte("->")
nilAngleBytes = []byte("<nil>")
maxNewlineBytes = []byte("<max depth reached>\n")
maxShortBytes = []byte("<max>")
circularBytes = []byte("<already shown>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
precisionBytes = []byte(".")
openAngleBytes = []byte("<")
closeAngleBytes = []byte(">")
openMapBytes = []byte("map[")
closeMapBytes = []byte("]")
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// catchPanic handles any panics that might occur during the handleMethods
// calls.
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes)
fmt.Fprintf(w, "%v", err)
w.Write(closeParenBytes)
}
}
// handleMethods attempts to call the Error and String methods on the underlying
// type the passed reflect.Value represents and outputes the result to Writer w.
//
// It handles panics in any called methods by catching and displaying the error
// as the formatted value.
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe, when it's available,
// to bypass these restrictions since this package does not mutate the
// values.
if !v.CanInterface() {
if UnsafeDisabled {
return false
}
v = unsafeReflectValue(v)
}
// Choose whether or not to do error and Stringer interface lookups against
// the base type or a pointer to the base type depending on settings.
// Technically calling one of these methods with a pointer receiver can
// mutate the value, however, types which choose to satisify an error or
// Stringer interface with a pointer receiver should not be mutating their
// state inside these interface methods.
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
v = unsafeReflectValue(v)
}
if v.CanAddr() {
v = v.Addr()
}
// Is it an error or Stringer?
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.Error()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.Error()))
return true
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.String()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.String()))
return true
}
return false
}
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilAngleBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
values []reflect.Value
strings []string // either nil or same len and values
cs *ConfigState
}
// newValuesSorter initializes a valuesSorter instance, which holds a set of
// surrogate keys on which the data should be sorted. It uses flags in
// ConfigState to decide if and how to populate those surrogate keys.
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
vs := &valuesSorter{values: values, cs: cs}
if canSortSimply(vs.values[0].Kind()) {
return vs
}
if !cs.DisableMethods {
vs.strings = make([]string, len(values))
for i := range vs.values {
b := bytes.Buffer{}
if !handleMethods(cs, &b, vs.values[i]) {
vs.strings = nil
break
}
vs.strings[i] = b.String()
}
}
if vs.strings == nil && cs.SpewKeys {
vs.strings = make([]string, len(values))
for i := range vs.values {
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
}
}
return vs
}
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
// directly, or whether it should be considered for sorting by surrogate keys
// (if the ConfigState allows it).
func canSortSimply(kind reflect.Kind) bool {
// This switch parallels valueSortLess, except for the default case.
switch kind {
case reflect.Bool:
return true
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.String:
return true
case reflect.Uintptr:
return true
case reflect.Array:
return true
}
return false
}
// Len returns the number of values in the slice. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Len() int {
return len(s.values)
}
// Swap swaps the values at the passed indices. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
if s.strings != nil {
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
}
}
// valueSortLess returns whether the first value should sort before the second
// value. It is used by valueSorter.Less as part of the sort.Interface
// implementation.
func valueSortLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Bool:
return !a.Bool() && b.Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return a.Int() < b.Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
case reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Array:
// Compare the contents of both arrays.
l := a.Len()
for i := 0; i < l; i++ {
av := a.Index(i)
bv := b.Index(i)
if av.Interface() == bv.Interface() {
continue
}
return valueSortLess(av, bv)
}
}
return a.String() < b.String()
}
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
if s.strings == nil {
return valueSortLess(s.values[i], s.values[j])
}
return s.strings[i] < s.strings[j]
}
// sortValues is a sort function that handles both native types and any type that
// can be converted to error or Stringer. Other inputs are sorted according to
// their Value.String() value to ensure display stability.
func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
sort.Sort(newValuesSorter(values, cs))
}

306
vendor/github.com/davecgh/go-spew/spew/config.go generated vendored Normal file
View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"os"
)
// ConfigState houses the configuration options used by spew to format and
// display values. There is a global instance, Config, that is used to control
// all top-level Formatter and Dump functionality. Each ConfigState instance
// provides methods equivalent to the top-level functions.
//
// The zero value for ConfigState provides no indentation. You would typically
// want to set it to a space or a tab.
//
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
// with default settings. See the documentation of NewDefaultConfig for default
// values.
type ConfigState struct {
// Indent specifies the string to use for each indentation level. The
// global config instance that all top-level functions use set this to a
// single space by default. If you would like more indentation, you might
// set this to a tab with "\t" or perhaps two spaces with " ".
Indent string
// MaxDepth controls the maximum number of levels to descend into nested
// data structures. The default, 0, means there is no limit.
//
// NOTE: Circular data structures are properly detected, so it is not
// necessary to set this value unless you specifically want to limit deeply
// nested data structures.
MaxDepth int
// DisableMethods specifies whether or not error and Stringer interfaces are
// invoked for types that implement them.
DisableMethods bool
// DisablePointerMethods specifies whether or not to check for and invoke
// error and Stringer interfaces on types which only accept a pointer
// receiver when the current type is not a pointer.
//
// NOTE: This might be an unsafe action since calling one of these methods
// with a pointer receiver could technically mutate the value, however,
// in practice, types which choose to satisify an error or Stringer
// interface with a pointer receiver should not be mutating their state
// inside these interface methods. As a result, this option relies on
// access to the unsafe package, so it will not have any effect when
// running in environments without access to the unsafe package such as
// Google App Engine or with the "safe" build tag specified.
DisablePointerMethods bool
// DisablePointerAddresses specifies whether to disable the printing of
// pointer addresses. This is useful when diffing data structures in tests.
DisablePointerAddresses bool
// DisableCapacities specifies whether to disable the printing of capacities
// for arrays, slices, maps and channels. This is useful when diffing
// data structures in tests.
DisableCapacities bool
// ContinueOnMethod specifies whether or not recursion should continue once
// a custom error or Stringer interface is invoked. The default, false,
// means it will print the results of invoking the custom error or Stringer
// interface and return immediately instead of continuing to recurse into
// the internals of the data type.
//
// NOTE: This flag does not have any effect if method invocation is disabled
// via the DisableMethods or DisablePointerMethods options.
ContinueOnMethod bool
// SortKeys specifies map keys should be sorted before being printed. Use
// this to have a more deterministic, diffable output. Note that only
// native types (bool, int, uint, floats, uintptr and string) and types
// that support the error or Stringer interfaces (if methods are
// enabled) are supported, with other types sorted according to the
// reflect.Value.String() output which guarantees display stability.
SortKeys bool
// SpewKeys specifies that, as a last resort attempt, map keys should
// be spewed to strings and sorted by those strings. This is only
// considered if SortKeys is true.
SpewKeys bool
}
// Config is the active configuration of the top-level functions.
// The configuration can be changed by modifying the contents of spew.Config.
var Config = ConfigState{Indent: " "}
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the formatted string as a value that satisfies error. See NewFormatter
// for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, c.convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, c.convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, c.convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a Formatter interface returned by c.NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, c.convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
return fmt.Print(c.convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, c.convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
return fmt.Println(c.convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprint(a ...interface{}) string {
return fmt.Sprint(c.convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, c.convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a Formatter interface returned by c.NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintln(a ...interface{}) string {
return fmt.Sprintln(c.convertArgs(a)...)
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
c.Printf, c.Println, or c.Printf.
*/
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(c, v)
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
fdump(c, w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by modifying the public members
of c. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func (c *ConfigState) Dump(a ...interface{}) {
fdump(c, os.Stdout, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func (c *ConfigState) Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(c, &buf, a...)
return buf.String()
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a spew Formatter interface using
// the ConfigState associated with s.
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = newFormatter(c, arg)
}
return formatters
}
// NewDefaultConfig returns a ConfigState with the following default settings.
//
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
func NewDefaultConfig() *ConfigState {
return &ConfigState{Indent: " "}
}

211
vendor/github.com/davecgh/go-spew/spew/doc.go generated vendored Normal file
View File

@@ -0,0 +1,211 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
* SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
spew.Dump(myVar1, myVar2, ...)
You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer. For example, to dump to standard error:
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) (len=1) {
(string) (len=3) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
functions have syntax you are most likely already familiar with:
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Println(myVar, myVar2)
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
See the Index for the full list convenience functions.
Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
See the Printf example for details on the setup of variables being shown
here.
Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output. Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew

509
vendor/github.com/davecgh/go-spew/spew/dump.go generated vendored Normal file
View File

@@ -0,0 +1,509 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
// uint8Type is a reflect.Type representing a uint8. It is used to
// convert cgo types to uint8 slices for hexdumping.
uint8Type = reflect.TypeOf(uint8(0))
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}
// indent performs indentation according to the depth level and cs.Indent
// option.
func (d *dumpState) indent() {
if d.ignoreNextIndent {
d.ignoreNextIndent = false
return
}
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
}
// unpackValue returns values inside of non-nil interfaces when possible.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
// dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
}
}
// Keep list of all dereferenced pointers to show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by dereferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
cycleFound = true
indirects--
break
}
d.pointers[addr] = d.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type information.
d.w.Write(openParenBytes)
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
d.w.Write([]byte(ve.Type().String()))
d.w.Write(closeParenBytes)
// Display pointer information.
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
d.w.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
d.w.Write(pointerChainBytes)
}
printHexPtr(d.w, addr)
}
d.w.Write(closeParenBytes)
}
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound:
d.w.Write(circularBytes)
default:
d.ignoreNextType = true
d.dump(ve)
}
d.w.Write(closeParenBytes)
}
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
// reflection) arrays and slices are dumped in hexdump -C fashion.
func (d *dumpState) dumpSlice(v reflect.Value) {
// Determine whether this type should be hex dumped or not. Also,
// for types which should be hexdumped, try to use the underlying data
// first, then fall back to trying to convert them to a uint8 slice.
var buf []uint8
doConvert := false
doHexDump := false
numEntries := v.Len()
if numEntries > 0 {
vt := v.Index(0).Type()
vts := vt.String()
switch {
// C types that need to be converted.
case cCharRE.MatchString(vts):
fallthrough
case cUnsignedCharRE.MatchString(vts):
fallthrough
case cUint8tCharRE.MatchString(vts):
doConvert = true
// Try to use existing uint8 slices and fall back to converting
// and copying if that fails.
case vt.Kind() == reflect.Uint8:
// We need an addressable interface to convert the type
// to a byte slice. However, the reflect package won't
// give us an interface on certain things like
// unexported struct fields in order to enforce
// visibility rules. We use unsafe, when available, to
// bypass these restrictions since this package does not
// mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
if !UnsafeDisabled {
vs = vs.Slice(0, numEntries)
// Use the existing uint8 slice if it can be
// type asserted.
iface := vs.Interface()
if slice, ok := iface.([]uint8); ok {
buf = slice
doHexDump = true
break
}
}
// The underlying data needs to be converted if it can't
// be type asserted to a uint8 slice.
doConvert = true
}
// Copy and convert the underlying type if needed.
if doConvert && vt.ConvertibleTo(uint8Type) {
// Convert and copy each element into a uint8 byte
// slice.
buf = make([]uint8, numEntries)
for i := 0; i < numEntries; i++ {
vv := v.Index(i)
buf[i] = uint8(vv.Convert(uint8Type).Uint())
}
doHexDump = true
}
}
// Hexdump the entire slice as needed.
if doHexDump {
indent := strings.Repeat(d.cs.Indent, d.depth)
str := indent + hex.Dump(buf)
str = strings.Replace(str, "\n", "\n"+indent, -1)
str = strings.TrimRight(str, d.cs.Indent)
d.w.Write([]byte(str))
return
}
// Recursively call dump for each item.
for i := 0; i < numEntries; i++ {
d.dump(d.unpackValue(v.Index(i)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
// dump is the main workhorse for dumping a value. It uses the passed reflect
// value to figure out what kind of object we are dealing with and formats it
// appropriately. It is a recursive function, however circular data structures
// are detected and handled properly.
func (d *dumpState) dump(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
d.indent()
d.dumpPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(v.Type().String()))
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
d.ignoreNextType = false
// Display length and capacity if the built-in len and cap functions
// work with the value's kind and the len/cap itself is non-zero.
valueLen, valueCap := 0, 0
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
valueLen, valueCap = v.Len(), v.Cap()
case reflect.Map, reflect.String:
valueLen = v.Len()
}
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
d.w.Write(openParenBytes)
if valueLen != 0 {
d.w.Write(lenEqualsBytes)
printInt(d.w, int64(valueLen), 10)
}
if !d.cs.DisableCapacities && valueCap != 0 {
if valueLen != 0 {
d.w.Write(spaceBytes)
}
d.w.Write(capEqualsBytes)
printInt(d.w, int64(valueCap), 10)
}
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
// Call Stringer/error interfaces if they exist and the handle methods flag
// is enabled
if !d.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(d.cs, d.w, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(d.w, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(d.w, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(d.w, v.Uint(), 10)
case reflect.Float32:
printFloat(d.w, v.Float(), 32)
case reflect.Float64:
printFloat(d.w, v.Float(), 64)
case reflect.Complex64:
printComplex(d.w, v.Complex(), 32)
case reflect.Complex128:
printComplex(d.w, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
d.dumpSlice(v)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.String:
d.w.Write([]byte(strconv.Quote(v.String())))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
d.w.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
numEntries := v.Len()
keys := v.MapKeys()
if d.cs.SortKeys {
sortValues(keys, d.cs)
}
for i, key := range keys {
d.dump(d.unpackValue(key))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.MapIndex(key)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Struct:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
vt := v.Type()
numFields := v.NumField()
for i := 0; i < numFields; i++ {
d.indent()
vtf := vt.Field(i)
d.w.Write([]byte(vtf.Name))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.Field(i)))
if i < (numFields - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new
// types are added.
default:
if v.CanInterface() {
fmt.Fprintf(d.w, "%v", v.Interface())
} else {
fmt.Fprintf(d.w, "%v", v.String())
}
}
}
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
w.Write(spaceBytes)
w.Write(nilAngleBytes)
w.Write(newlineBytes)
continue
}
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes)
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
fdump(&Config, w, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(&Config, &buf, a...)
return buf.String()
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by an exported package global,
spew.Config. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func Dump(a ...interface{}) {
fdump(&Config, os.Stdout, a...)
}

419
vendor/github.com/davecgh/go-spew/spew/format.go generated vendored Normal file
View File

@@ -0,0 +1,419 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
)
// supportedFlags is a list of all the character flags supported by fmt package.
const supportedFlags = "0-+# "
// formatState implements the fmt.Formatter interface and contains information
// about the state of a formatting operation. The NewFormatter function can
// be used to get a new Formatter which can be used directly as arguments
// in standard fmt package printing calls.
type formatState struct {
value interface{}
fs fmt.State
depth int
pointers map[uintptr]int
ignoreNextType bool
cs *ConfigState
}
// buildDefaultFormat recreates the original format string without precision
// and width information to pass in to fmt.Sprintf in the case of an
// unrecognized type. Unless new types are added to the language, this
// function won't ever be called.
func (f *formatState) buildDefaultFormat() (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
buf.WriteRune('v')
format = buf.String()
return format
}
// constructOrigFormat recreates the original format string including precision
// and width information to pass along to the standard fmt package. This allows
// automatic deferral of all format strings this package doesn't support.
func (f *formatState) constructOrigFormat(verb rune) (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
if width, ok := f.fs.Width(); ok {
buf.WriteString(strconv.Itoa(width))
}
if precision, ok := f.fs.Precision(); ok {
buf.Write(precisionBytes)
buf.WriteString(strconv.Itoa(precision))
}
buf.WriteRune(verb)
format = buf.String()
return format
}
// unpackValue returns values inside of non-nil interfaces when possible and
// ensures that types for values which have been unpacked from an interface
// are displayed when the show types flag is also set.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
f.ignoreNextType = false
if !v.IsNil() {
v = v.Elem()
}
}
return v
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
func (f *formatState) formatPtr(v reflect.Value) {
// Display nil if top level pointer is nil.
showTypes := f.fs.Flag('#')
if v.IsNil() && (!showTypes || f.ignoreNextType) {
f.fs.Write(nilAngleBytes)
return
}
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range f.pointers {
if depth >= f.depth {
delete(f.pointers, k)
}
}
// Keep list of all dereferenced pointers to possibly show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by derferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
cycleFound = true
indirects--
break
}
f.pointers[addr] = f.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type or indirection level depending on flags.
if showTypes && !f.ignoreNextType {
f.fs.Write(openParenBytes)
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
f.fs.Write([]byte(ve.Type().String()))
f.fs.Write(closeParenBytes)
} else {
if nilFound || cycleFound {
indirects += strings.Count(ve.Type().String(), "*")
}
f.fs.Write(openAngleBytes)
f.fs.Write([]byte(strings.Repeat("*", indirects)))
f.fs.Write(closeAngleBytes)
}
// Display pointer information depending on flags.
if f.fs.Flag('+') && (len(pointerChain) > 0) {
f.fs.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
f.fs.Write(pointerChainBytes)
}
printHexPtr(f.fs, addr)
}
f.fs.Write(closeParenBytes)
}
// Display dereferenced value.
switch {
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound:
f.fs.Write(circularShortBytes)
default:
f.ignoreNextType = true
f.format(ve)
}
}
// format is the main workhorse for providing the Formatter interface. It
// uses the passed reflect value to figure out what kind of object we are
// dealing with and formats it appropriately. It is a recursive function,
// however circular data structures are detected and handled properly.
func (f *formatState) format(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
f.fs.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
f.formatPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !f.ignoreNextType && f.fs.Flag('#') {
f.fs.Write(openParenBytes)
f.fs.Write([]byte(v.Type().String()))
f.fs.Write(closeParenBytes)
}
f.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled.
if !f.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.cs, f.fs, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(f.fs, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(f.fs, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(f.fs, v.Uint(), 10)
case reflect.Float32:
printFloat(f.fs, v.Float(), 32)
case reflect.Float64:
printFloat(f.fs, v.Float(), 64)
case reflect.Complex64:
printComplex(f.fs, v.Complex(), 32)
case reflect.Complex128:
printComplex(f.fs, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
f.fs.Write(openBracketBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(v.Index(i)))
}
}
f.depth--
f.fs.Write(closeBracketBytes)
case reflect.String:
f.fs.Write([]byte(v.String()))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
f.fs.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
f.fs.Write(openMapBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
keys := v.MapKeys()
if f.cs.SortKeys {
sortValues(keys, f.cs)
}
for i, key := range keys {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(key))
f.fs.Write(colonBytes)
f.ignoreNextType = true
f.format(f.unpackValue(v.MapIndex(key)))
}
}
f.depth--
f.fs.Write(closeMapBytes)
case reflect.Struct:
numFields := v.NumField()
f.fs.Write(openBraceBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
vt := v.Type()
for i := 0; i < numFields; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
vtf := vt.Field(i)
if f.fs.Flag('+') || f.fs.Flag('#') {
f.fs.Write([]byte(vtf.Name))
f.fs.Write(colonBytes)
}
f.format(f.unpackValue(v.Field(i)))
}
}
f.depth--
f.fs.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(f.fs, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(f.fs, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it if any get added.
default:
format := f.buildDefaultFormat()
if v.CanInterface() {
fmt.Fprintf(f.fs, format, v.Interface())
} else {
fmt.Fprintf(f.fs, format, v.String())
}
}
}
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
// details.
func (f *formatState) Format(fs fmt.State, verb rune) {
f.fs = fs
// Use standard formatting for verbs that are not v.
if verb != 'v' {
format := f.constructOrigFormat(verb)
fmt.Fprintf(fs, format, f.value)
return
}
if f.value == nil {
if fs.Flag('#') {
fs.Write(interfaceBytes)
}
fs.Write(nilAngleBytes)
return
}
f.format(reflect.ValueOf(f.value))
}
// newFormatter is a helper function to consolidate the logic from the various
// public methods which take varying config states.
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: cs}
fs.pointers = make(map[uintptr]int)
return fs
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
Printf, Println, or Fprintf.
*/
func NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(&Config, v)
}

148
vendor/github.com/davecgh/go-spew/spew/spew.go generated vendored Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"fmt"
"io"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the formatted string as a value that satisfies error. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a default Formatter interface returned by NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
func Print(a ...interface{}) (n int, err error) {
return fmt.Print(convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
func Println(a ...interface{}) (n int, err error) {
return fmt.Println(convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprint(a ...interface{}) string {
return fmt.Sprint(convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintln(a ...interface{}) string {
return fmt.Sprintln(convertArgs(a)...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a default spew Formatter interface.
func convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = NewFormatter(arg)
}
return formatters
}

0
vendor/github.com/hjson/hjson-go/v4/.gitignore generated vendored Normal file
View File

21
vendor/github.com/hjson/hjson-go/v4/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016, 2017 Christian Zangl
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.

377
vendor/github.com/hjson/hjson-go/v4/README.md generated vendored Normal file
View File

@@ -0,0 +1,377 @@
# hjson-go
[![Build Status](https://github.com/hjson/hjson-go/workflows/test/badge.svg)](https://github.com/hjson/hjson-go/actions)
[![Go Pkg](https://img.shields.io/github/release/hjson/hjson-go.svg?style=flat-square&label=go-pkg)](https://github.com/hjson/hjson-go/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/hjson/hjson-go?style=flat-square)](https://goreportcard.com/report/github.com/hjson/hjson-go)
[![coverage](https://img.shields.io/badge/coverage-ok-brightgreen.svg?style=flat-square)](https://gocover.io/github.com/hjson/hjson-go/)
[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/hjson/hjson-go/v4)
![Hjson Intro](https://hjson.github.io/hjson1.gif)
```
{
# specify rate in requests/second (because comments are helpful!)
rate: 1000
// prefer c-style comments?
/* feeling old fashioned? */
# did you notice that rate doesn't need quotes?
hey: look ma, no quotes for strings either!
# best of all
notice: []
anything: ?
# yes, commas are optional!
}
```
The Go implementation of Hjson is based on [hjson-js](https://github.com/hjson/hjson-js). For other platforms see [hjson.github.io](https://hjson.github.io).
More documentation can be found at https://pkg.go.dev/github.com/hjson/hjson-go/v4
# Install
Instructions for installing a pre-built **hjson-cli** tool can be found at https://hjson.github.io/users-bin.html
If you instead want to build locally, make sure you have a working Go environment. See the [install instructions](https://golang.org/doc/install.html).
- In order to use Hjson from your own Go source code, just add an import line like the one here below. Before building your project, run `go mod tidy` in order to download the Hjson source files. The suffix `/v4` is required in the import path, unless you specifically want to use an older major version.
```go
import "github.com/hjson/hjson-go/v4"
```
- If you instead want to use the **hjson-cli** command line tool, run the command here below in your terminal. The executable will be installed into your `go/bin` folder, make sure that folder is included in your `PATH` environment variable.
```bash
go install github.com/hjson/hjson-go/v4/hjson-cli@latest
```
# Usage as command line tool
```
usage: hjson-cli [OPTIONS] [INPUT]
hjson can be used to convert JSON from/to Hjson.
hjson will read the given JSON/Hjson input file or read from stdin.
Options:
-bracesSameLine
Print braces on the same line.
-c Output as JSON.
-h Show this screen.
-indentBy string
The indent string. (default " ")
-j Output as formatted JSON.
-omitRootBraces
Omit braces at the root.
-preserveKeyOrder
Preserve key order in objects/maps.
-quoteAlways
Always quote string values.
-v
Show version.
```
Sample:
- run `hjson-cli test.json > test.hjson` to convert to Hjson
- run `hjson-cli -j test.hjson > test.json` to convert to JSON
# Usage as a GO library
```go
package main
import (
"github.com/hjson/hjson-go/v4"
"fmt"
)
func main() {
// Now let's look at decoding Hjson data into Go
// values.
sampleText := []byte(`
{
# specify rate in requests/second
rate: 1000
array:
[
foo
bar
]
}`)
// We need to provide a variable where Hjson
// can put the decoded data.
var dat map[string]interface{}
// Decode with default options and check for errors.
if err := hjson.Unmarshal(sampleText, &dat); err != nil {
panic(err)
}
// short for:
// options := hjson.DefaultDecoderOptions()
// err := hjson.UnmarshalWithOptions(sampleText, &dat, options)
fmt.Println(dat)
// In order to use the values in the decoded map,
// we'll need to cast them to their appropriate type.
rate := dat["rate"].(float64)
fmt.Println(rate)
array := dat["array"].([]interface{})
str1 := array[0].(string)
fmt.Println(str1)
// To encode to Hjson with default options:
sampleMap := map[string]int{"apple": 5, "lettuce": 7}
hjson, _ := hjson.Marshal(sampleMap)
// short for:
// options := hjson.DefaultOptions()
// hjson, _ := hjson.MarshalWithOptions(sampleMap, options)
fmt.Println(string(hjson))
}
```
## Unmarshal to Go structs
If you prefer, you can also unmarshal to Go structs (including structs implementing the json.Unmarshaler interface or the encoding.TextUnmarshaler interface). The Go JSON package is used for this, so the same rules apply. Specifically for the "json" key in struct field tags. For more details about this type of unmarshalling, see the [documentation for json.Unmarshal()](https://pkg.go.dev/encoding/json#Unmarshal).
```go
package main
import (
"github.com/hjson/hjson-go/v4"
"fmt"
)
type Sample struct {
Rate int
Array []string
}
type SampleAlias struct {
Rett int `json:"rate"`
Ashtray []string `json:"array"`
}
func main() {
sampleText := []byte(`
{
# specify rate in requests/second
rate: 1000
array:
[
foo
bar
]
}`)
// unmarshal
var sample Sample
hjson.Unmarshal(sampleText, &sample)
fmt.Println(sample.Rate)
fmt.Println(sample.Array)
// unmarshal using json tags on struct fields
var sampleAlias SampleAlias
hjson.Unmarshal(sampleText, &sampleAlias)
fmt.Println(sampleAlias.Rett)
fmt.Println(sampleAlias.Ashtray)
}
```
## Comments on struct fields
By using key `comment` in struct field tags you can specify comments to be written on one or more lines preceding the struct field in the Hjson output. Another way to output comments is to use *hjson.Node* structs, more on than later.
```go
package main
import (
"github.com/hjson/hjson-go/v4"
"fmt"
)
type foo struct {
A string `json:"x" comment:"First comment"`
B int32 `comment:"Second comment\nLook ma, new lines"`
C string
D int32
}
func main() {
a := foo{A: "hi!", B: 3, C: "some text", D: 5}
buf, err := hjson.Marshal(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(buf))
}
```
Output:
```
{
# First comment
x: hi!
# Second comment
# Look ma, new lines
B: 3
C: some text
D: 5
}
```
## Read and write comments
The only way to read comments from Hjson input is to use a destination variable of type *hjson.Node* or *&ast;hjson.Node*. The *hjson.Node* must be the root destination, it won't work if you create a field of type *hjson.Node* in some other struct and use that struct as destination. An *hjson.Node* struct is simply a wrapper for a value and comments stored in an *hjson.Comments* struct. It also has several convenience functions, for example *AtIndex()* or *SetKey()* that can be used when you know that the node contains a value of type `[]interface{}` or *&ast;hjson.OrderedMap*. All of the elements in `[]interface{}` or *&ast;hjson.OrderedMap* will be of type *&ast;hjson.Node* in trees created by *hjson.Unmarshal*, but the *hjson.Node* convenience functions unpack the actual values from them.
When *hjson.Node* or *&ast;hjson.Node* is used as destination for Hjson unmarshal the output will be a tree of *&ast;hjson.Node* where all of the values contained in tree nodes will be of these types:
* `nil` (no type)
* `float64` &nbsp;&nbsp;(if *UseJSONNumber* == `false`)
* *json.Number* &nbsp;&nbsp;(if *UseJSONNumber* == `true`)
* `string`
* `bool`
* `[]interface{}`
* *&ast;hjson.OrderedMap*
These are just the types used by Hjson unmarshal and the convenience functions, you are free to assign any type of values to nodes in your own code.
The comments will contain all whitespace chars too (including line feeds) so that an Hjson document can be read and written without altering the layout. This can be disabled by setting the decoding option *WhitespaceAsComments* to `false`.
```go
package main
import (
"fmt"
"github.com/hjson/hjson-go/v4"
)
func main() {
// Now let's look at decoding Hjson data into hjson.Node.
sampleText := []byte(`
{
# specify rate in requests/second
rate: 1000
array:
[
foo
bar
]
}`)
var node hjson.Node
if err := hjson.Unmarshal(sampleText, &node); err != nil {
panic(err)
}
node.NK("array").Cm.Before = ` # please specify an array
`
if _, _, err := node.NKC("subMap").SetKey("subVal", 1); err != nil {
panic(err)
}
outBytes, err := hjson.Marshal(node)
if err != nil {
panic(err)
}
fmt.Println(string(outBytes))
}
```
Output:
```
{
# specify rate in requests/second
rate: 1000
# please specify an array
array:
[
foo
bar
]
subMap: {
subVal: 1
}
}
```
## Type ambiguity
Hjson allows quoteless strings. But if a value is a valid number, boolean or `null` then it will be unmarshalled into that type instead of a string when unmarshalling into `interface{}`. This can lead to unintended consequences if the creator of an Hjson file meant to write a string but didn't think of that the quoteless string they wrote also was a valid number.
The ambiguity can be avoided by using typed destinations when unmarshalling. A string destination will receive a string even if the quoteless string also was a valid number, boolean or `null`. Example:
```go
package main
import (
"github.com/hjson/hjson-go/v4"
"fmt"
)
type foo struct {
A string
}
func main() {
var dest foo
err := hjson.Unmarshal([]byte(`a: 3`), &dest)
if err != nil {
fmt.Println(err)
}
fmt.Println(dest)
}
```
Output:
```
{3}
```
String pointer destinations can be set to `nil` by writing `null` in an Hjson file. The same goes for a pointer destination of any type that implements `UnmarshalText()`.
## ElemTyper interface
If a destination type implements hjson.ElemTyper, Unmarshal() will call ElemType() on the destination when unmarshalling an array or an object, to see if any array element or leaf node should be of type string even if it can be treated as a number, boolean or null. This is most useful if the destination also implements the json.Unmarshaler interface, because then there is no other way for Unmarshal() to know the type of the elements on the destination. If a destination implements ElemTyper all of its elements must be of the same type.
Example implementation for a generic ordered map:
```go
func (o *OrderedMap[T]) ElemType() reflect.Type {
return reflect.TypeOf((*T)(nil)).Elem()
}
```
## Max nesting depth
In order to avoid stack overflow all unmarshal-functions return an error if the Hjson input contains a tree more than 10000 levels deep. This is the same limit as in the Go standard library package `encoding/json`.
# API
[![godoc](https://godoc.org/github.com/hjson/hjson-go/v4?status.svg)](https://godoc.org/github.com/hjson/hjson-go/v4)
# History
[see releases](https://github.com/hjson/hjson-go/releases)

59
vendor/github.com/hjson/hjson-go/v4/build_release.sh generated vendored Normal file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
cd `dirname $0`
ROOT=$PWD
BINARIES=$ROOT/binaries
if [ -d "$BINARIES" ]; then rm -rf $BINARIES; fi
mkdir $BINARIES
function build() {
export GOOS=$1
export GOARCH=$2
echo build $GOOS $GOARCH
VERSION=`cd $ROOT && git describe --tags`
OUT=${BINARIES}/hjson_${VERSION}_${GOOS}_${GOARCH}
mkdir $OUT
cd $OUT
go build -ldflags "-w -s -X main.Version=${VERSION}" github.com/hjson/hjson-go/v4/hjson-cli
if [[ $3 == "zip" ]]; then
mv $OUT/hjson-cli.exe $OUT/hjson.exe
zip -j ${OUT}.zip $OUT/*
else
mv $OUT/hjson-cli $OUT/hjson
tar -czf ${OUT}.tar.gz -C $OUT .
fi
rm -r $OUT
}
build android arm64
build darwin amd64
build darwin arm64
build dragonfly amd64
build freebsd 386
build freebsd amd64
build freebsd arm
build linux 386
build linux amd64
build linux arm
build linux arm64
build linux mips64
build linux mips64le
build linux ppc64
build linux ppc64le
build netbsd 386
build netbsd amd64
build netbsd arm
build openbsd 386
build openbsd amd64
build openbsd arm
build plan9 386
build plan9 amd64
build solaris amd64
build windows 386 zip
build windows amd64 zip
build windows arm zip
build windows arm64 zip

1057
vendor/github.com/hjson/hjson-go/v4/decode.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

709
vendor/github.com/hjson/hjson-go/v4/encode.go generated vendored Normal file
View File

@@ -0,0 +1,709 @@
package hjson
import (
"bytes"
"encoding"
"encoding/json"
"errors"
"fmt"
"math"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"unicode/utf8"
)
// EncoderOptions defines options for encoding to Hjson.
type EncoderOptions struct {
// End of line, should be either \n or \r\n
Eol string
// Place braces on the same line
BracesSameLine bool
// Emit braces for the root object
EmitRootBraces bool
// Always place string in quotes
QuoteAlways bool
// Place string in quotes if it could otherwise be a number, boolean or null
QuoteAmbiguousStrings bool
// Indent string
IndentBy string
// Base indentation string
BaseIndentation string
// Write comments, if any are found in hjson.Node structs or as tags on
// other structs.
Comments bool
}
// DefaultOptions returns the default encoding options.
// Eol = "\n"
// BracesSameLine = true
// EmitRootBraces = true
// QuoteAlways = false
// QuoteAmbiguousStrings = true
// IndentBy = " "
// BaseIndentation = ""
// Comments = true
func DefaultOptions() EncoderOptions {
return EncoderOptions{
Eol: "\n",
BracesSameLine: true,
EmitRootBraces: true,
QuoteAlways: false,
QuoteAmbiguousStrings: true,
IndentBy: " ",
BaseIndentation: "",
Comments: true,
}
}
// Start looking for circular references below this depth.
const depthLimit = 1024
type hjsonEncoder struct {
bytes.Buffer // output
EncoderOptions
indent int
pDepth uint
parents map[uintptr]struct{} // Starts to be filled after pDepth has reached depthLimit
structTypeCache map[reflect.Type][]structFieldInfo
}
var JSONNumberType = reflect.TypeOf(json.Number(""))
var needsEscape, needsQuotes, needsEscapeML, startsWithKeyword, needsEscapeName *regexp.Regexp
func init() {
var commonRange = `\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}`
// needsEscape tests if the string can be written without escapes
needsEscape = regexp.MustCompile(`[\\\"\x00-\x1f` + commonRange + `]`)
// needsQuotes tests if the string can be written as a quoteless string (includes needsEscape but without \\ and \")
needsQuotes = regexp.MustCompile(`^\s|^"|^'|^#|^/\*|^//|^\{|^\}|^\[|^\]|^:|^,|\s$|[\x00-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}]`)
// needsEscapeML tests if the string can be written as a multiline string (like needsEscape but without \n, \\, \", \t)
needsEscapeML = regexp.MustCompile(`'''|^[\s]+$|[\x00-\x08\x0b-\x1f` + commonRange + `]`)
// starts with a keyword and optionally is followed by a comment
startsWithKeyword = regexp.MustCompile(`^(true|false|null)\s*((,|\]|\}|#|//|/\*).*)?$`)
needsEscapeName = regexp.MustCompile(`[,\{\[\}\]\s:#"']|//|/\*`)
}
var meta = map[byte][]byte{
// table of character substitutions
'\b': []byte("\\b"),
'\t': []byte("\\t"),
'\n': []byte("\\n"),
'\f': []byte("\\f"),
'\r': []byte("\\r"),
'"': []byte("\\\""),
'\\': []byte("\\\\"),
}
func (e *hjsonEncoder) quoteReplace(text string) string {
return string(needsEscape.ReplaceAllFunc([]byte(text), func(a []byte) []byte {
c := meta[a[0]]
if c != nil {
return c
}
r, _ := utf8.DecodeRune(a)
return []byte(fmt.Sprintf("\\u%04x", r))
}))
}
func (e *hjsonEncoder) quoteForComment(cmStr string) bool {
chars := []rune(cmStr)
for _, r := range chars {
switch r {
case '\r', '\n':
return false
case '/', '#':
return true
}
}
return false
}
func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool,
keyComment string, hasCommentAfter bool) {
// Check if we can insert this string without quotes
// see hjson syntax (must not parse as true, false, null or number)
if len(value) == 0 {
e.WriteString(separator + `""`)
} else if e.QuoteAlways ||
hasCommentAfter ||
needsQuotes.MatchString(value) ||
(e.QuoteAmbiguousStrings && (startsWithNumber([]byte(value)) ||
startsWithKeyword.MatchString(value))) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we first check if the string can be expressed in multiline
// format or we must replace the offending characters with safe escape
// sequences.
if !needsEscape.MatchString(value) {
e.WriteString(separator + `"` + value + `"`)
} else if !needsEscapeML.MatchString(value) && !isRootObject {
e.mlString(value, separator, keyComment)
} else {
e.WriteString(separator + `"` + e.quoteReplace(value) + `"`)
}
} else {
// return without quotes
e.WriteString(separator + value)
}
}
func (e *hjsonEncoder) mlString(value string, separator string, keyComment string) {
a := strings.Split(value, "\n")
if len(a) == 1 {
// The string contains only a single line. We still use the multiline
// format as it avoids escaping the \ character (e.g. when used in a
// regex).
e.WriteString(separator + "'''")
e.WriteString(a[0])
} else {
if !strings.Contains(keyComment, "\n") {
e.writeIndent(e.indent + 1)
}
e.WriteString("'''")
for _, v := range a {
indent := e.indent + 1
if len(v) == 0 {
indent = 0
}
e.writeIndent(indent)
e.WriteString(v)
}
e.writeIndent(e.indent + 1)
}
e.WriteString("'''")
}
func (e *hjsonEncoder) quoteName(name string) string {
if len(name) == 0 {
return `""`
}
// Check if we can insert this name without quotes
if needsEscapeName.MatchString(name) || needsEscape.MatchString(name) {
return `"` + e.quoteReplace(name) + `"`
}
// without quotes
return name
}
func (e *hjsonEncoder) bracesIndent(isObjElement, isEmpty bool, cm Comments,
separator string) {
if !isObjElement || cm.Key == "" {
if isObjElement && !e.BracesSameLine && (!isEmpty || cm.InsideFirst != "") {
e.writeIndent(e.indent)
} else {
e.WriteString(separator)
}
}
}
type sortAlpha []reflect.Value
func (s sortAlpha) Len() int {
return len(s)
}
func (s sortAlpha) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortAlpha) Less(i, j int) bool {
return fmt.Sprintf("%v", s[i]) < fmt.Sprintf("%v", s[j])
}
func (e *hjsonEncoder) writeIndentNoEOL(indent int) {
e.WriteString(e.BaseIndentation)
for i := 0; i < indent; i++ {
e.WriteString(e.IndentBy)
}
}
func (e *hjsonEncoder) writeIndent(indent int) {
e.WriteString(e.Eol)
e.writeIndentNoEOL(indent)
}
func (e *hjsonEncoder) useMarshalerJSON(
value reflect.Value,
noIndent bool,
separator string,
isRootObject,
isObjElement bool,
) error {
b, err := value.Interface().(json.Marshaler).MarshalJSON()
if err != nil {
return err
}
decOpt := DefaultDecoderOptions()
decOpt.UseJSONNumber = true
var dummyDest interface{}
jsonRoot, err := orderedUnmarshal(b, &dummyDest, decOpt, false, false)
if err != nil {
return err
}
// Output Hjson with our current options, instead of JSON.
return e.str(reflect.ValueOf(jsonRoot), noIndent, separator, isRootObject,
isObjElement, Comments{})
}
var marshalerJSON = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
var marshalerText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
func (e *hjsonEncoder) unpackNode(value reflect.Value, cm Comments) (reflect.Value, Comments) {
if value.IsValid() {
if node, ok := value.Interface().(Node); ok {
value = reflect.ValueOf(node.Value)
if e.Comments {
cm = node.Cm
}
} else if pNode, ok := value.Interface().(*Node); ok {
value = reflect.ValueOf(pNode.Value)
if e.Comments {
cm = pNode.Cm
}
}
}
return value, cm
}
// This function can often be called from within itself, so do not output
// anything from the upper half of it.
func (e *hjsonEncoder) str(
value reflect.Value,
noIndent bool,
separator string,
isRootObject,
isObjElement bool,
cm Comments,
) error {
// Produce a string from value.
// Unpack *Node, possibly overwrite cm.
value, cm = e.unpackNode(value, cm)
if cm.Key != "" {
separator = ""
}
kind := value.Kind()
switch kind {
case reflect.Ptr, reflect.Slice, reflect.Map:
if e.pDepth++; e.pDepth > depthLimit {
if e.parents == nil {
e.parents = map[uintptr]struct{}{}
}
p := value.Pointer()
if _, ok := e.parents[p]; ok {
return errors.New("Circular reference found, pointer of type " + value.Type().String())
}
e.parents[p] = struct{}{}
defer delete(e.parents, p)
}
defer func() { e.pDepth-- }()
}
if !value.IsValid() {
e.WriteString(separator)
e.WriteString("null")
return nil
}
if kind == reflect.Interface || kind == reflect.Ptr {
if value.IsNil() {
e.WriteString(separator)
e.WriteString("null")
return nil
}
return e.str(value.Elem(), noIndent, separator, isRootObject, isObjElement, cm)
}
// Our internal orderedMap implements marshalerJSON. We must therefore place
// this check before checking marshalerJSON. Calling orderedMap.MarshalJSON()
// from this function would cause an infinite loop.
if om, ok := value.Interface().(OrderedMap); ok {
var fis []fieldInfo
for _, key := range om.Keys {
fis = append(fis, fieldInfo{
field: reflect.ValueOf(om.Map[key]),
name: key,
})
}
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
}
if value.Type().Implements(marshalerJSON) {
return e.useMarshalerJSON(value, noIndent, separator, isRootObject, isObjElement)
}
if value.Type().Implements(marshalerText) {
b, err := value.Interface().(encoding.TextMarshaler).MarshalText()
if err != nil {
return err
}
return e.str(reflect.ValueOf(string(b)), noIndent, separator, isRootObject,
isObjElement, cm)
}
switch kind {
case reflect.String:
if value.Type() == JSONNumberType {
n := value.String()
if n == "" {
n = "0"
}
// without quotes
e.WriteString(separator + n)
} else {
e.quote(value.String(), separator, isRootObject, cm.Key,
e.quoteForComment(cm.After))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.WriteString(separator)
e.WriteString(strconv.FormatInt(value.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Uintptr:
e.WriteString(separator)
e.WriteString(strconv.FormatUint(value.Uint(), 10))
case reflect.Float32, reflect.Float64:
// JSON numbers must be finite. Encode non-finite numbers as null.
e.WriteString(separator)
number := value.Float()
if math.IsInf(number, 0) || math.IsNaN(number) {
e.WriteString("null")
} else if number == -0 {
e.WriteString("0")
} else {
// find shortest representation ('G' does not work)
val := strconv.FormatFloat(number, 'f', -1, 64)
exp := strconv.FormatFloat(number, 'E', -1, 64)
if len(exp) < len(val) {
val = strings.ToLower(exp)
}
e.WriteString(val)
}
case reflect.Bool:
e.WriteString(separator)
if value.Bool() {
e.WriteString("true")
} else {
e.WriteString("false")
}
case reflect.Slice, reflect.Array:
e.bracesIndent(isObjElement, value.Len() == 0, cm, separator)
e.WriteString("[" + cm.InsideFirst)
if value.Len() == 0 {
if cm.InsideFirst != "" || cm.InsideLast != "" {
e.WriteString(e.Eol)
if cm.InsideLast == "" {
e.writeIndentNoEOL(e.indent)
}
}
e.WriteString(cm.InsideLast + "]")
return nil
}
indent1 := e.indent
e.indent++
// Join all of the element texts together, separated with newlines
for i := 0; i < value.Len(); i++ {
elem, elemCm := e.unpackNode(value.Index(i), Comments{})
if elemCm.Before == "" && elemCm.Key == "" {
e.writeIndent(e.indent)
} else {
e.WriteString(e.Eol + elemCm.Before + elemCm.Key)
}
if err := e.str(elem, true, "", false, false, elemCm); err != nil {
return err
}
e.WriteString(elemCm.After)
}
if cm.InsideLast != "" {
e.WriteString(e.Eol + cm.InsideLast)
} else {
e.writeIndent(indent1)
}
e.WriteString("]")
e.indent = indent1
case reflect.Map:
var fis []fieldInfo
useMarshalText := value.Type().Key().Implements(marshalerText)
keys := value.MapKeys()
sort.Sort(sortAlpha(keys))
for _, key := range keys {
var name string
if useMarshalText {
keyBytes, err := key.Interface().(encoding.TextMarshaler).MarshalText()
if err != nil {
return err
}
name = string(keyBytes)
} else {
name = fmt.Sprintf("%v", key)
}
fis = append(fis, fieldInfo{
field: value.MapIndex(key),
name: name,
})
}
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
case reflect.Struct:
// Struct field info is identical for all instances of the same type.
// Only the values on the fields can be different.
t := value.Type()
sfis, ok := e.structTypeCache[t]
if !ok {
sfis = getStructFieldInfoSlice(t)
e.structTypeCache[t] = sfis
}
// Collect fields first, too see if any should be shown (considering
// "omitEmpty").
var fis []fieldInfo
FieldLoop:
for _, sfi := range sfis {
// The field might be found on the root struct or in embedded structs.
fv := value
for _, i := range sfi.indexPath {
if fv.Kind() == reflect.Ptr {
if fv.IsNil() {
continue FieldLoop
}
fv = fv.Elem()
}
fv = fv.Field(i)
}
if sfi.omitEmpty && isEmptyValue(fv) {
continue
}
fi := fieldInfo{
field: fv,
name: sfi.name,
}
if e.Comments {
fi.comment = sfi.comment
}
fis = append(fis, fi)
}
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
default:
return errors.New("Unsupported type " + value.Type().String())
}
return nil
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
default:
return false
}
}
func investigateComment(txt string) (
endsInsideComment,
endsWithLineFeed bool,
) {
var prev rune
for _, rn := range txt {
switch rn {
case '\n':
endsInsideComment = false
case '#':
endsInsideComment = true
case '/':
if prev == '/' {
endsInsideComment = true
}
}
endsWithLineFeed = (rn == '\n')
prev = rn
}
return
}
// Marshal returns the Hjson encoding of v using
// default options.
//
// See MarshalWithOptions.
func Marshal(v interface{}) ([]byte, error) {
return MarshalWithOptions(v, DefaultOptions())
}
// MarshalWithOptions returns the Hjson encoding of v.
//
// The value v is traversed recursively.
//
// Boolean values are written as true or false.
//
// Floating point, integer, and json.Number values are written as numbers (with
// decimals only if needed, using . as decimals separator).
//
// String values encode as Hjson strings (quoteless, multiline or
// JSON).
//
// Array and slice values encode as arrays, surrounded by []. Unlike
// json.Marshal, hjson.Marshal will encode a nil-slice as [] instead of null.
//
// Map values encode as objects, surrounded by {}. The map's key type must be
// possible to print to a string using fmt.Sprintf("%v", key), or implement
// encoding.TextMarshaler. The map keys are sorted alphabetically and
// used as object keys. Unlike json.Marshal, hjson.Marshal will encode a
// nil-map as {} instead of null.
//
// Struct values also encode as objects, surrounded by {}. Only the exported
// fields are encoded to Hjson. The fields will appear in the same order as in
// the struct.
//
// The encoding of each struct field can be customized by the format string
// stored under the "json" key in the struct field's tag.
// The format string gives the name of the field, possibly followed by a comma
// and "omitempty". The name may be empty in order to specify "omitempty"
// without overriding the default field name.
//
// The "omitempty" option specifies that the field should be omitted
// from the encoding if the field has an empty value, defined as
// false, 0, a nil pointer, a nil interface value, and any empty array,
// slice, map, or string.
//
// As a special case, if the field tag is "-", the field is always omitted.
// Note that a field with name "-" can still be generated using the tag "-,".
//
// Comments can be set on struct fields using the "comment" key in the struct
// field's tag. The comment will be written on the line before the field key,
// prefixed with #. Or possible several lines prefixed by #, if there are line
// breaks (\n) in the comment text.
//
// If both the "json" and the "comment" tag keys are used on a struct field
// they should be separated by whitespace.
//
// Examples of struct field tags and their meanings:
//
// // Field appears in Hjson as key "myName".
// Field int `json:"myName"`
//
// // Field appears in Hjson as key "myName" and the field is omitted from
// // the object if its value is empty, as defined above.
// Field int `json:"myName,omitempty"`
//
// // Field appears in Hjson as key "Field" (the default), but the field is
// // skipped if empty. Note the leading comma.
// Field int `json:",omitempty"`
//
// // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in Hjson as key "-".
// Field int `json:"-,"`
//
// // Field appears in Hjson preceded by a line just containing `# A comment.`
// Field int `comment:"A comment."`
//
// // Field appears in Hjson as key "myName" preceded by a line just
// // containing `# A comment.`
// Field int `json:"myName" comment:"A comment."`
//
// Anonymous struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
// as described in the next paragraph.
// An anonymous struct field with a name given in its JSON tag is treated as
// having that name, rather than being anonymous.
// An anonymous struct field of interface type is treated the same as having
// that type as its name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for JSON when
// deciding which field to marshal or unmarshal. If there are
// multiple fields at the same level, and that level is the least
// nested (and would therefore be the nesting level selected by the
// usual Go rules), the following extra rules apply:
//
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
//
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
//
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON value.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON value.
//
// If an encountered value implements the json.Marshaler interface then the
// function MarshalJSON() is called on it. The JSON is then converted to Hjson
// using the current indentation and options given in the call to json.Marshal().
//
// If an encountered value implements the encoding.TextMarshaler interface
// but not the json.Marshaler interface, then the function MarshalText() is
// called on it to get a text.
//
// Channel, complex, and function values cannot be encoded in Hjson, will
// result in an error.
//
// Hjson cannot represent cyclic data structures and Marshal does not handle
// them. Passing cyclic structures to Marshal will result in an error.
func MarshalWithOptions(v interface{}, options EncoderOptions) ([]byte, error) {
e := &hjsonEncoder{
indent: 0,
EncoderOptions: options,
structTypeCache: map[reflect.Type][]structFieldInfo{},
}
value := reflect.ValueOf(v)
_, cm := e.unpackNode(value, Comments{})
e.WriteString(cm.Before + cm.Key)
err := e.str(value, true, e.BaseIndentation, true, false, cm)
if err != nil {
return nil, err
}
e.WriteString(cm.After)
return e.Bytes(), nil
}

415
vendor/github.com/hjson/hjson-go/v4/node.go generated vendored Normal file
View File

@@ -0,0 +1,415 @@
package hjson
import (
"encoding/json"
"fmt"
"reflect"
)
type Comments struct {
// Comment/whitespace on line(s) before the value, and before the value on
// the same line. If not empty, is expected to end with a line feed +
// indentation for the value.
Before string
// Comment/whitespace between the key and this value, if this value is an
// element in a map/object.
Key string
// Comment/whitespace after (but still on the same line as) the leading
// bracket ({ or [) for this value, if this value is a slice/array or
// map/object. Is not expected to contain any line feed.
InsideFirst string
// Comment/whitespace from the beginning of the first line after all child
// values belonging to this value, until the closing bracket (} or ]), if
// this value is a slice/array or map/object. If not empty, is expected to
// end with a line feed + indentation for the closing bracket.
InsideLast string
// Comment/whitespace after (but still on the same line as) the value. Is not
// expected to contain any line feed. hjson.Unmarshal() will try to assign
// comments/whitespace from lines between values to `Before` on the value
// after those lines, or to `InsideLast` on the slice/map if the lines
// containing comments appear after the last element inside a slice/map.
After string
}
// Node must be used as destination for Unmarshal() or UnmarshalWithOptions()
// whenever comments should be read from the input. The struct is simply a
// wrapper for the actual values and a helper struct containing any comments.
// The Value in the destination Node will be overwritten in the call to
// Unmarshal() or UnmarshalWithOptions(), i.e. node trees are not merged.
// After the unmarshal, Node.Value will contain any of these types:
//
// nil (no type)
// float64 (if UseJSONNumber == false)
// json.Number (if UseJSONNumber == true)
// string
// bool
// []interface{}
// *hjson.OrderedMap
//
// All elements in an []interface{} or *hjson.OrderedMap will be of the type
// *hjson.Node, so that they can contain comments.
//
// This example shows unmarshalling input with comments, changing the value on
// a single key (the input is assumed to have an object/map as root) and then
// marshalling the node tree again, including comments and with preserved key
// order in the object/map.
//
// var node hjson.Node
// err := hjson.Unmarshal(input, &node)
// if err != nil {
// return err
// }
// _, err = node.SetKey("setting1", 3)
// if err != nil {
// return err
// }
// output, err := hjson.Marshal(node)
// if err != nil {
// return err
// }
type Node struct {
Value interface{}
Cm Comments
}
// Len returns the length of the value wrapped by this Node, if the value is of
// type *hjson.OrderedMap, []interface{} or string. Otherwise 0 is returned.
func (c *Node) Len() int {
if c == nil {
return 0
}
switch cont := c.Value.(type) {
case *OrderedMap:
return cont.Len()
case []interface{}:
return len(cont)
case string:
return len(cont)
}
return 0
}
// AtIndex returns the key (if any) and value (unwrapped from its Node) found
// at the specified index, if this Node contains a value of type
// *hjson.OrderedMap or []interface{}. Returns an error for unexpected types.
// Panics if index < 0 or index >= Len().
func (c *Node) AtIndex(index int) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key = cont.Keys[index]
elem = cont.Map[key]
case []interface{}:
elem = cont[index]
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
node, ok := elem.(*Node)
if !ok {
return "", nil, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
}
return key, node.Value, nil
}
// AtKey returns the value (unwrapped from its Node) found for the specified
// key, if this Node contains a value of type *hjson.OrderedMap. An error is
// returned for unexpected types. The second returned value is true if the key
// was found, false otherwise.
func (c *Node) AtKey(key string) (interface{}, bool, error) {
if c == nil {
return nil, false, nil
}
om, ok := c.Value.(*OrderedMap)
if !ok {
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
elem, ok := om.Map[key]
if !ok {
return nil, false, nil
}
node, ok := elem.(*Node)
if !ok {
return nil, false, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
}
return node.Value, true, nil
}
// Append adds the input value to the end of the []interface{} wrapped by this
// Node. If this Node contains nil without a type, an empty []interface{} is
// first created. If this Node contains a value of any other type, an error is
// returned.
func (c *Node) Append(value interface{}) error {
if c == nil {
return fmt.Errorf("Node is nil")
}
var arr []interface{}
if c.Value == nil {
arr = []interface{}{}
} else {
var ok bool
arr, ok = c.Value.([]interface{})
if !ok {
return fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
}
c.Value = append(arr, &Node{Value: value})
return nil
}
// Insert inserts a new key/value pair at the specified index, if this Node
// contains a value of type *hjson.OrderedMap or []interface{}. Returns an error
// for unexpected types. Panics if index < 0 or index > c.Len(). If the key
// already exists in the OrderedMap, the new value is set but the position of
// the key is not changed. Otherwise the value to insert is wrapped in a new
// Node. If this Node contains []interface{}, the key is ignored. Returns the
// old value and true if the key already exists in the/ OrderedMap, nil and
// false otherwise.
func (c *Node) Insert(index int, key string, value interface{}) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
var oldVal interface{}
var found bool
switch cont := c.Value.(type) {
case *OrderedMap:
oldVal, found := cont.Map[key]
if found {
if node, ok := oldVal.(*Node); ok {
oldVal = node.Value
node.Value = value
} else {
cont.Map[key] = &Node{Value: value}
}
} else {
oldVal, found = cont.Insert(index, key, &Node{Value: value})
}
case []interface{}:
value = &Node{Value: value}
if index == len(cont) {
c.Value = append(cont, value)
} else {
cont = append(cont[:index+1], cont[index:]...)
cont[index] = value
c.Value = cont
}
default:
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
return oldVal, found, nil
}
// SetIndex assigns the specified value to the child Node found at the specified
// index, if this Node contains a value of type *hjson.OrderedMap or
// []interface{}. Returns an error for unexpected types. Returns the key (if
// any) and value previously found at the specified index. Panics if index < 0
// or index >= Len().
func (c *Node) SetIndex(index int, value interface{}) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key = cont.Keys[index]
elem = cont.Map[key]
case []interface{}:
elem = cont[index]
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
var oldVal interface{}
node, ok := elem.(*Node)
if ok {
oldVal = node.Value
node.Value = value
} else {
oldVal = elem
switch cont := c.Value.(type) {
case *OrderedMap:
cont.Map[key] = &Node{Value: value}
case []interface{}:
cont[index] = &Node{Value: value}
}
}
return key, oldVal, nil
}
// SetKey assigns the specified value to the child Node identified by the
// specified key, if this Node contains a value of the type *hjson.OrderedMap.
// If this Node contains nil without a type, an empty *hjson.OrderedMap is
// first created. If this Node contains a value of any other type an error is
// returned. If the key cannot be found in the OrderedMap, a new Node is
// created, wrapping the specified value, and appended to the end of the
// OrderedMap. Returns the old value and true if the key already existed in
// the OrderedMap, nil and false otherwise.
func (c *Node) SetKey(key string, value interface{}) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
var om *OrderedMap
if c.Value == nil {
om = NewOrderedMap()
c.Value = om
} else {
var ok bool
om, ok = c.Value.(*OrderedMap)
if !ok {
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
}
var oldVal interface{}
elem, ok := om.Map[key]
if ok {
var node *Node
node, ok = elem.(*Node)
if ok {
oldVal = node.Value
node.Value = value
}
}
foundKey := true
if !ok {
oldVal, foundKey = om.Set(key, &Node{Value: value})
}
return oldVal, foundKey, nil
}
// DeleteIndex deletes the value or key/value pair found at the specified index,
// if this Node contains a value of type *hjson.OrderedMap or []interface{}.
// Returns an error for unexpected types. Panics if index < 0 or
// index >= c.Len(). Returns the deleted key (if any) and value.
func (c *Node) DeleteIndex(index int) (string, interface{}, error) {
if c == nil {
return "", nil, fmt.Errorf("Node is nil")
}
var key string
var value interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
key, value = cont.DeleteIndex(index)
case []interface{}:
value = cont[index]
cont = append(cont[:index], cont[index+1:]...)
c.Value = cont
default:
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
if node, ok := value.(*Node); ok {
value = node.Value
}
return key, value, nil
}
// DeleteKey deletes the key/value pair with the specified key, if found and if
// this Node contains a value of type *hjson.OrderedMap. Returns an error for
// unexpected types. Returns the deleted value and true if the key was found,
// nil and false otherwise.
func (c *Node) DeleteKey(key string) (interface{}, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("Node is nil")
}
if om, ok := c.Value.(*OrderedMap); ok {
oldValue, found := om.DeleteKey(key)
if node, ok := oldValue.(*Node); ok {
oldValue = node.Value
}
return oldValue, found, nil
}
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
}
// NI is an acronym formed from "get Node pointer by Index". Returns the *Node
// element found at the specified index, if this Node contains a value of type
// *hjson.OrderedMap or []interface{}. Returns nil otherwise. Panics if
// index < 0 or index >= Len(). Does not create or alter any value.
func (c *Node) NI(index int) *Node {
if c == nil {
return nil
}
var elem interface{}
switch cont := c.Value.(type) {
case *OrderedMap:
elem = cont.AtIndex(index)
case []interface{}:
elem = cont[index]
default:
return nil
}
if node, ok := elem.(*Node); ok {
return node
}
return nil
}
// NK is an acronym formed from "get Node pointer by Key". Returns the *Node
// element found for the specified key, if this Node contains a value of type
// *hjson.OrderedMap. Returns nil otherwise. Does not create or alter anything.
func (c *Node) NK(key string) *Node {
if c == nil {
return nil
}
om, ok := c.Value.(*OrderedMap)
if !ok {
return nil
}
if elem, ok := om.Map[key]; ok {
if node, ok := elem.(*Node); ok {
return node
}
}
return nil
}
// NKC is an acronym formed from "get Node pointer by Key, Create if not found".
// Returns the *Node element found for the specified key, if this Node contains
// a value of type *hjson.OrderedMap. If this Node contains nil without a type,
// an empty *hjson.OrderedMap is first created. If this Node contains a value of
// any other type or if the element idendified by the specified key is not of
// type *Node, an error is returned. If the key cannot be found in the
// OrderedMap, a new Node is created for the specified key. Example usage:
//
// var node hjson.Node
// node.NKC("rootKey1").NKC("subKey1").SetKey("valKey1", "my value")
func (c *Node) NKC(key string) *Node {
if c == nil {
return nil
}
var om *OrderedMap
if c.Value == nil {
om = NewOrderedMap()
c.Value = om
} else {
var ok bool
om, ok = c.Value.(*OrderedMap)
if !ok {
return nil
}
}
if elem, ok := om.Map[key]; ok {
if node, ok := elem.(*Node); ok {
return node
}
} else {
node := &Node{}
om.Set(key, node)
return node
}
return nil
}
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
// hjson.Node trees to be used as input for json.Marshal().
func (c Node) MarshalJSON() ([]byte, error) {
return json.Marshal(c.Value)
}
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
// enabling hjson.Node to be used as destination for json.Unmarshal().
func (c *Node) UnmarshalJSON(b []byte) error {
return Unmarshal(b, c)
}

167
vendor/github.com/hjson/hjson-go/v4/orderedmap.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
package hjson
import (
"bytes"
"encoding/json"
)
// OrderedMap wraps a map and a slice containing all of the keys from the map,
// so that the order of the keys can be specified. The Keys slice can be sorted
// or rearranged like any other slice, but do not add or remove keys manually
// on it. Use OrderedMap.Insert(), OrderedMap.Set(), OrderedMap.DeleteIndex() or
// OrderedMap.DeleteKey() instead.
//
// Example of how to iterate through the elements of an OrderedMap in order:
//
// for _, key := range om.Keys {
// fmt.Printf("%v\n", om.Map[key])
// }
//
// Always use the functions Insert() or Set() instead of setting values
// directly on OrderedMap.Map, because any new keys must also be added to
// OrderedMap.Keys. Otherwise those keys will be ignored when iterating through
// the elements of the OrderedMap in order, as for example happens in the
// function hjson.Marshal().
type OrderedMap struct {
Keys []string
Map map[string]interface{}
}
// KeyValue is only used as input to NewOrderedMapFromSlice().
type KeyValue struct {
Key string
Value interface{}
}
// NewOrderedMap returns a pointer to a new OrderedMap. An OrderedMap should
// always be passed by reference, never by value. If an OrderedMap is passed
// by value then appending new keys won't affect all of the copies of the
// OrderedMap.
func NewOrderedMap() *OrderedMap {
return &OrderedMap{
Keys: nil,
Map: map[string]interface{}{},
}
}
// NewOrderedMapFromSlice is like NewOrderedMap but with initial values.
// Example:
//
// om := NewOrderedMapFromSlice([]KeyValue{
// {"B", "first"},
// {"A", "second"},
// })
func NewOrderedMapFromSlice(args []KeyValue) *OrderedMap {
c := NewOrderedMap()
for _, elem := range args {
c.Set(elem.Key, elem.Value)
}
return c
}
// Len returns the number of values contained in the OrderedMap.
func (c *OrderedMap) Len() int {
return len(c.Keys)
}
// AtIndex returns the value found at the specified index. Panics if
// index < 0 or index >= c.Len().
func (c *OrderedMap) AtIndex(index int) interface{} {
return c.Map[c.Keys[index]]
}
// AtKey returns the value found for the specified key, and true if the value
// was found. Returns nil and false if the value was not found.
func (c *OrderedMap) AtKey(key string) (interface{}, bool) {
ret, ok := c.Map[key]
return ret, ok
}
// Insert inserts a new key/value pair at the specified index. Panics if
// index < 0 or index > c.Len(). If the key already exists in the OrderedMap,
// the new value is set but the position of the key is not changed. Returns
// the old value and true if the key already exists in the OrderedMap, nil and
// false otherwise.
func (c *OrderedMap) Insert(index int, key string, value interface{}) (interface{}, bool) {
oldValue, exists := c.Map[key]
c.Map[key] = value
if exists {
return oldValue, true
}
if index == len(c.Keys) {
c.Keys = append(c.Keys, key)
} else {
c.Keys = append(c.Keys[:index+1], c.Keys[index:]...)
c.Keys[index] = key
}
return nil, false
}
// Set sets the specified value for the specified key. If the key does not
// already exist in the OrderedMap it is appended to the end of the OrderedMap.
// If the key already exists in the OrderedMap, the new value is set but the
// position of the key is not changed. Returns the old value and true if the
// key already exists in the OrderedMap, nil and false otherwise.
func (c *OrderedMap) Set(key string, value interface{}) (interface{}, bool) {
return c.Insert(len(c.Keys), key, value)
}
// DeleteIndex deletes the key/value pair found at the specified index.
// Returns the deleted key and value. Panics if index < 0 or index >= c.Len().
func (c *OrderedMap) DeleteIndex(index int) (string, interface{}) {
key := c.Keys[index]
value := c.Map[key]
delete(c.Map, key)
c.Keys = append(c.Keys[:index], c.Keys[index+1:]...)
return key, value
}
// DeleteKey deletes the key/value pair with the specified key, if found.
// Returns the deleted value and true if the key was found, nil and false
// otherwise.
func (c *OrderedMap) DeleteKey(key string) (interface{}, bool) {
for index, ck := range c.Keys {
if ck == key {
_, value := c.DeleteIndex(index)
return value, true
}
}
return nil, false
}
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
// hjson.OrderedMap to be used as input for json.Marshal().
func (c *OrderedMap) MarshalJSON() ([]byte, error) {
var b bytes.Buffer
b.WriteString("{")
for index, key := range c.Keys {
if index > 0 {
b.WriteString(",")
}
jbuf, err := json.Marshal(key)
if err != nil {
return nil, err
}
b.Write(jbuf)
b.WriteString(":")
jbuf, err = json.Marshal(c.Map[key])
if err != nil {
return nil, err
}
b.Write(jbuf)
}
b.WriteString("}")
return b.Bytes(), nil
}
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
// enabling hjson.OrderedMap to be used as destination for json.Unmarshal().
func (c *OrderedMap) UnmarshalJSON(b []byte) error {
c.Keys = nil
c.Map = map[string]interface{}{}
return Unmarshal(b, c)
}

116
vendor/github.com/hjson/hjson-go/v4/parseNumber.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
package hjson
import (
"encoding/json"
"errors"
"math"
"strconv"
)
type parseNumber struct {
data []byte
at int // The index of the current character
ch byte // The current character
}
func (p *parseNumber) next() bool {
// get the next character.
len := len(p.data)
if p.at < len {
p.ch = p.data[p.at]
p.at++
return true
}
if p.at == len {
p.at++
p.ch = 0
}
return false
}
func (p *parseNumber) peek(offs int) byte {
pos := p.at + offs
if pos >= 0 && pos < len(p.data) {
return p.data[pos]
}
return 0
}
func startsWithNumber(text []byte) bool {
if _, err := tryParseNumber(text, true, false); err == nil {
return true
}
return false
}
func tryParseNumber(text []byte, stopAtNext, useJSONNumber bool) (interface{}, error) {
// Parse a number value.
p := parseNumber{
data: text,
at: 0,
ch: ' ',
}
leadingZeros := 0
testLeading := true
p.next()
if p.ch == '-' {
p.next()
}
for p.ch >= '0' && p.ch <= '9' {
if testLeading {
if p.ch == '0' {
leadingZeros++
} else {
testLeading = false
}
}
p.next()
}
if testLeading {
leadingZeros--
} // single 0 is allowed
if p.ch == '.' {
for p.next() && p.ch >= '0' && p.ch <= '9' {
}
}
if p.ch == 'e' || p.ch == 'E' {
p.next()
if p.ch == '-' || p.ch == '+' {
p.next()
}
for p.ch >= '0' && p.ch <= '9' {
p.next()
}
}
end := p.at
// skip white/to (newline)
for p.ch > 0 && p.ch <= ' ' {
p.next()
}
if stopAtNext {
// end scan if we find a punctuator character like ,}] or a comment
if p.ch == ',' || p.ch == '}' || p.ch == ']' ||
p.ch == '#' || p.ch == '/' && (p.peek(0) == '/' || p.peek(0) == '*') {
p.ch = 0
}
}
if p.ch > 0 || leadingZeros != 0 {
return 0, errors.New("Invalid number")
}
if useJSONNumber {
return json.Number(string(p.data[0 : end-1])), nil
}
number, err := strconv.ParseFloat(string(p.data[0:end-1]), 64)
if err != nil {
return 0, err
}
if math.IsInf(number, 0) || math.IsNaN(number) {
return 0, errors.New("Invalid number")
}
return number, nil
}

346
vendor/github.com/hjson/hjson-go/v4/structs.go generated vendored Normal file
View File

@@ -0,0 +1,346 @@
package hjson
import (
"fmt"
"reflect"
"sort"
"strings"
)
type fieldInfo struct {
field reflect.Value
name string
comment string
}
type structFieldInfo struct {
name string
tagged bool
comment string
omitEmpty bool
indexPath []int
}
// Use lower key name as key. Values are arrays in case some fields only differ
// by upper/lower case.
type structFieldMap map[string][]structFieldInfo
func (s structFieldMap) insert(sfi structFieldInfo) {
key := strings.ToLower(sfi.name)
s[key] = append(s[key], sfi)
}
func (s structFieldMap) getField(name string) (structFieldInfo, bool) {
key := strings.ToLower(name)
if arr, ok := s[key]; ok {
for _, elem := range arr {
if elem.name == name {
return elem, true
}
}
return arr[0], true
}
return structFieldInfo{}, false
}
// dominantField looks through the fields, all of which are known to
// have the same name, to find the single field that dominates the
// others using Go's embedding rules, modified by the presence of
// JSON tags. If there are multiple top-level fields, the boolean
// will be false: This condition is an error in Go and we skip all
// the fields.
func dominantField(fields []structFieldInfo) (structFieldInfo, bool) {
// The fields are sorted in increasing index-length order, then by presence of tag.
// That means that the first field is the dominant one. We need only check
// for error cases: two fields at top level, either both tagged or neither tagged.
if len(fields) > 1 && len(fields[0].indexPath) == len(fields[1].indexPath) && fields[0].tagged == fields[1].tagged {
return structFieldInfo{}, false
}
return fields[0], true
}
// byIndex sorts by index sequence.
type byIndex []structFieldInfo
func (x byIndex) Len() int { return len(x) }
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byIndex) Less(i, j int) bool {
for k, xik := range x[i].indexPath {
if k >= len(x[j].indexPath) {
return false
}
if xik != x[j].indexPath[k] {
return xik < x[j].indexPath[k]
}
}
return len(x[i].indexPath) < len(x[j].indexPath)
}
func getStructFieldInfo(rootType reflect.Type) []structFieldInfo {
type structInfo struct {
typ reflect.Type
indexPath []int
}
var sfis []structFieldInfo
structsToInvestigate := []structInfo{structInfo{typ: rootType}}
// Struct types already visited at an earlier depth.
visited := map[reflect.Type]bool{}
// Count the number of specific struct types on a specific depth.
typeDepthCount := map[reflect.Type]int{}
for len(structsToInvestigate) > 0 {
curStructs := structsToInvestigate
structsToInvestigate = []structInfo{}
curTDC := typeDepthCount
typeDepthCount = map[reflect.Type]int{}
for _, curStruct := range curStructs {
if visited[curStruct.typ] {
// The struct type has already appeared on an earlier depth. Fields on
// an earlier depth always have precedence over fields with identical
// name on a later depth, so no point in investigating this type again.
continue
}
visited[curStruct.typ] = true
for i := 0; i < curStruct.typ.NumField(); i++ {
sf := curStruct.typ.Field(i)
if sf.Anonymous {
t := sf.Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// If the field is not exported and not a struct.
if sf.PkgPath != "" && t.Kind() != reflect.Struct {
// Ignore embedded fields of unexported non-struct types.
continue
}
// Do not ignore embedded fields of unexported struct types
// since they may have exported fields.
} else if sf.PkgPath != "" {
// Ignore unexported non-embedded fields.
continue
}
jsonTag := sf.Tag.Get("json")
if jsonTag == "-" {
continue
}
sfi := structFieldInfo{
name: sf.Name,
comment: sf.Tag.Get("comment"),
}
splits := strings.Split(jsonTag, ",")
if splits[0] != "" {
sfi.name = splits[0]
sfi.tagged = true
}
if len(splits) > 1 {
for _, opt := range splits[1:] {
if opt == "omitempty" {
sfi.omitEmpty = true
}
}
}
sfi.indexPath = make([]int, len(curStruct.indexPath)+1)
copy(sfi.indexPath, curStruct.indexPath)
sfi.indexPath[len(curStruct.indexPath)] = i
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
// Follow pointer.
ft = ft.Elem()
}
// If the current field should be included.
if sfi.tagged || !sf.Anonymous || ft.Kind() != reflect.Struct {
sfis = append(sfis, sfi)
if curTDC[curStruct.typ] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
sfis = append(sfis, sfi)
}
continue
}
// Record new anonymous struct to explore in next round.
typeDepthCount[ft]++
if typeDepthCount[ft] == 1 {
structsToInvestigate = append(structsToInvestigate, structInfo{
typ: ft,
indexPath: sfi.indexPath,
})
}
}
}
}
sort.Slice(sfis, func(i, j int) bool {
// sort field by name, breaking ties with depth, then
// breaking ties with "name came from json tag", then
// breaking ties with index sequence.
if sfis[i].name != sfis[j].name {
return sfis[i].name < sfis[j].name
}
if len(sfis[i].indexPath) != len(sfis[j].indexPath) {
return len(sfis[i].indexPath) < len(sfis[j].indexPath)
}
if sfis[i].tagged != sfis[j].tagged {
return sfis[i].tagged
}
return byIndex(sfis).Less(i, j)
})
// Delete all fields that are hidden by the Go rules for embedded fields,
// except that fields with JSON tags are promoted.
// The fields are sorted in primary order of name, secondary order
// of field index length. Loop over names; for each name, delete
// hidden fields by choosing the one dominant field that survives.
out := sfis[:0]
for advance, i := 0, 0; i < len(sfis); i += advance {
// One iteration per name.
// Find the sequence of sfis with the name of this first field.
sfi := sfis[i]
name := sfi.name
for advance = 1; i+advance < len(sfis); advance++ {
fj := sfis[i+advance]
if fj.name != name {
break
}
}
if advance == 1 { // Only one field with this name
out = append(out, sfi)
continue
}
dominant, ok := dominantField(sfis[i : i+advance])
if ok {
out = append(out, dominant)
}
}
return out
}
func getStructFieldInfoSlice(rootType reflect.Type) []structFieldInfo {
sfis := getStructFieldInfo(rootType)
sort.Sort(byIndex(sfis))
return sfis
}
func getStructFieldInfoMap(rootType reflect.Type) structFieldMap {
sfis := getStructFieldInfo(rootType)
out := structFieldMap{}
for _, elem := range sfis {
out.insert(elem)
}
return out
}
func (e *hjsonEncoder) writeFields(
fis []fieldInfo,
noIndent bool,
separator string,
isRootObject bool,
isObjElement bool,
cm Comments,
) error {
indent1 := e.indent
if !isRootObject || e.EmitRootBraces || len(fis) == 0 {
e.bracesIndent(isObjElement, len(fis) == 0, cm, separator)
e.WriteString("{" + cm.InsideFirst)
if len(fis) == 0 {
if cm.InsideFirst != "" || cm.InsideLast != "" {
e.WriteString(e.Eol)
}
e.WriteString(cm.InsideLast)
if cm.InsideLast != "" {
endsInsideComment, endsWithLineFeed := investigateComment(cm.InsideLast)
if endsInsideComment {
e.writeIndent(e.indent)
}
if endsWithLineFeed {
e.writeIndentNoEOL(e.indent)
}
} else if cm.InsideFirst != "" {
e.writeIndentNoEOL(e.indent)
}
e.WriteString("}")
return nil
}
e.indent++
} else {
e.WriteString(cm.InsideFirst)
}
// Join all of the member texts together, separated with newlines
var elemCm Comments
for i, fi := range fis {
var elem reflect.Value
elem, elemCm = e.unpackNode(fi.field, elemCm)
if i > 0 || !isRootObject || e.EmitRootBraces {
e.WriteString(e.Eol)
}
if len(fi.comment) > 0 {
for _, line := range strings.Split(fi.comment, e.Eol) {
e.writeIndentNoEOL(e.indent)
e.WriteString(fmt.Sprintf("# %s\n", line))
}
}
if elemCm.Before == "" {
e.writeIndentNoEOL(e.indent)
} else {
e.WriteString(elemCm.Before)
}
e.WriteString(e.quoteName(fi.name))
e.WriteString(":")
e.WriteString(elemCm.Key)
if err := e.str(elem, false, " ", false, true, elemCm); err != nil {
return err
}
if len(fi.comment) > 0 && i < len(fis)-1 {
e.WriteString(e.Eol)
}
e.WriteString(elemCm.After)
}
if cm.InsideLast != "" {
e.WriteString(e.Eol + cm.InsideLast)
}
if !isRootObject || e.EmitRootBraces {
if cm.InsideLast == "" {
e.writeIndent(indent1)
} else {
endsInsideComment, endsWithLineFeed := investigateComment(cm.InsideLast)
if endsInsideComment {
e.writeIndent(indent1)
} else if endsWithLineFeed {
e.writeIndentNoEOL(indent1)
}
}
e.WriteString("}")
}
e.indent = indent1
return nil
}

1
vendor/github.com/yuin/gopher-lua/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
.idea

21
vendor/github.com/yuin/gopher-lua/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Yusuke Inuzuka
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.

10
vendor/github.com/yuin/gopher-lua/Makefile generated vendored Normal file
View File

@@ -0,0 +1,10 @@
.PHONY: build test glua
build:
./_tools/go-inline *.go && go fmt . && go build
glua: *.go pm/*.go cmd/glua/glua.go
./_tools/go-inline *.go && go fmt . && go build cmd/glua/glua.go
test:
./_tools/go-inline *.go && go fmt . && go test

890
vendor/github.com/yuin/gopher-lua/README.rst generated vendored Normal file
View File

@@ -0,0 +1,890 @@
===============================================================================
GopherLua: VM and compiler for Lua in Go.
===============================================================================
.. image:: https://pkg.go.dev/badge/github.com/yuin/gopher-lua.svg
:target: https://pkg.go.dev/github.com/yuin/gopher-lua
.. image:: https://github.com/yuin/gopher-lua/workflows/test/badge.svg?branch=master&event=push
:target: https://github.com/yuin/gopher-lua/actions?query=workflow:test
.. image:: https://coveralls.io/repos/github/yuin/gopher-lua/badge.svg?branch=master
:target: https://coveralls.io/github/yuin/gopher-lua
.. image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/yuin/gopher-lua
:target: https://gitter.im/yuin/gopher-lua?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
GopherLua is a Lua5.1(+ `goto` statement in Lua5.2) VM and compiler written in Go. GopherLua has a same goal
with Lua: **Be a scripting language with extensible semantics** . It provides
Go APIs that allow you to easily embed a scripting language to your Go host
programs.
.. contents::
:depth: 1
----------------------------------------------------------------
Design principle
----------------------------------------------------------------
- Be a scripting language with extensible semantics.
- User-friendly Go API
- The stack based API like the one used in the original Lua
implementation will cause a performance improvements in GopherLua
(It will reduce memory allocations and concrete type <-> interface conversions).
GopherLua API is **not** the stack based API.
GopherLua give preference to the user-friendliness over the performance.
----------------------------------------------------------------
How about performance?
----------------------------------------------------------------
GopherLua is not fast but not too slow, I think.
GopherLua has almost equivalent ( or little bit better ) performance as Python3 on micro benchmarks.
There are some benchmarks on the `wiki page <https://github.com/yuin/gopher-lua/wiki/Benchmarks>`_ .
----------------------------------------------------------------
Installation
----------------------------------------------------------------
.. code-block:: bash
go get github.com/yuin/gopher-lua
GopherLua supports >= Go1.9.
----------------------------------------------------------------
Usage
----------------------------------------------------------------
GopherLua APIs perform in much the same way as Lua, **but the stack is used only
for passing arguments and receiving returned values.**
GopherLua supports channel operations. See **"Goroutines"** section.
Import a package.
.. code-block:: go
import (
"github.com/yuin/gopher-lua"
)
Run scripts in the VM.
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoString(`print("hello")`); err != nil {
panic(err)
}
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoFile("hello.lua"); err != nil {
panic(err)
}
Refer to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ and `Go doc <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
Note that elements that are not commented in `Go doc <http://godoc.org/github.com/yuin/gopher-lua>`_ equivalent to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ , except GopherLua uses objects instead of Lua stack indices.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Data model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All data in a GopherLua program is an ``LValue`` . ``LValue`` is an interface
type that has following methods.
- ``String() string``
- ``Type() LValueType``
Objects implement an LValue interface are
================ ========================= ================== =======================
Type name Go type Type() value Constants
================ ========================= ================== =======================
``LNilType`` (constants) ``LTNil`` ``LNil``
``LBool`` (constants) ``LTBool`` ``LTrue``, ``LFalse``
``LNumber`` float64 ``LTNumber`` ``-``
``LString`` string ``LTString`` ``-``
``LFunction`` struct pointer ``LTFunction`` ``-``
``LUserData`` struct pointer ``LTUserData`` ``-``
``LState`` struct pointer ``LTThread`` ``-``
``LTable`` struct pointer ``LTTable`` ``-``
``LChannel`` chan LValue ``LTChannel`` ``-``
================ ========================= ================== =======================
You can test an object type in Go way(type assertion) or using a ``Type()`` value.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if str, ok := lv.(lua.LString); ok {
// lv is LString
fmt.Println(string(str))
}
if lv.Type() != lua.LTString {
panic("string required.")
}
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if tbl, ok := lv.(*lua.LTable); ok {
// lv is LTable
fmt.Println(L.ObjLen(tbl))
}
Note that ``LBool`` , ``LNumber`` , ``LString`` is not a pointer.
To test ``LNilType`` and ``LBool``, You **must** use pre-defined constants.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if lv == lua.LTrue { // correct
}
if bl, ok := lv.(lua.LBool); ok && bool(bl) { // wrong
}
In Lua, both ``nil`` and ``false`` make a condition false. ``LVIsFalse`` and ``LVAsBool`` implement this specification.
.. code-block:: go
lv := L.Get(-1) // get the value at the top of the stack
if lua.LVIsFalse(lv) { // lv is nil or false
}
if lua.LVAsBool(lv) { // lv is neither nil nor false
}
Objects that based on go structs(``LFunction``. ``LUserData``, ``LTable``)
have some public methods and fields. You can use these methods and fields for
performance and debugging, but there are some limitations.
- Metatable does not work.
- No error handlings.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Callstack & Registry size
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The size of an ``LState``'s callstack controls the maximum call depth for Lua functions within a script (Go function calls do not count).
The registry of an ``LState`` implements stack storage for calling functions (both Lua and Go functions) and also for temporary variables in expressions. Its storage requirements will increase with callstack usage and also with code complexity.
Both the registry and the callstack can be set to either a fixed size or to auto size.
When you have a large number of ``LStates`` instantiated in a process, it's worth taking the time to tune the registry and callstack options.
+++++++++
Registry
+++++++++
The registry can have an initial size, a maximum size and a step size configured on a per ``LState`` basis. This will allow the registry to grow as needed. It will not shrink again after growing.
.. code-block:: go
L := lua.NewState(lua.Options{
RegistrySize: 1024 * 20, // this is the initial size of the registry
RegistryMaxSize: 1024 * 80, // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto grow
RegistryGrowStep: 32, // this is how much to step up the registry by each time it runs out of space. The default is `32`.
})
defer L.Close()
A registry which is too small for a given script will ultimately result in a panic. A registry which is too big will waste memory (which can be significant if many ``LStates`` are instantiated).
Auto growing registries incur a small performance hit at the point they are resized but will not otherwise affect performance.
+++++++++
Callstack
+++++++++
The callstack can operate in two different modes, fixed or auto size.
A fixed size callstack has the highest performance and has a fixed memory overhead.
An auto sizing callstack will allocate and release callstack pages on demand which will ensure the minimum amount of memory is in use at any time. The downside is it will incur a small performance impact every time a new page of callframes is allocated.
By default an ``LState`` will allocate and free callstack frames in pages of 8, so the allocation overhead is not incurred on every function call. It is very likely that the performance impact of an auto resizing callstack will be negligible for most use cases.
.. code-block:: go
L := lua.NewState(lua.Options{
CallStackSize: 120, // this is the maximum callstack size of this LState
MinimizeStackMemory: true, // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`.
})
defer L.Close()
++++++++++++++++
Option defaults
++++++++++++++++
The above examples show how to customize the callstack and registry size on a per ``LState`` basis. You can also adjust some defaults for when options are not specified by altering the values of ``lua.RegistrySize``, ``lua.RegistryGrowStep`` and ``lua.CallStackSize``.
An ``LState`` object that has been created by ``*LState#NewThread()`` inherits the callstack & registry size from the parent ``LState`` object.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Miscellaneous lua.NewState options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Options.SkipOpenLibs bool(default false)**
- By default, GopherLua opens all built-in libraries when new LState is created.
- You can skip this behaviour by setting this to ``true`` .
- Using the various `OpenXXX(L *LState) int` functions you can open only those libraries that you require, for an example see below.
- **Options.IncludeGoStackTrace bool(default false)**
- By default, GopherLua does not show Go stack traces when panics occur.
- You can get Go stack traces by setting this to ``true`` .
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Refer to `Lua Reference Manual <http://www.lua.org/manual/5.1/>`_ and `Go doc(LState methods) <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
+++++++++++++++++++++++++++++++++++++++++
Calling Go from Lua
+++++++++++++++++++++++++++++++++++++++++
.. code-block:: go
func Double(L *lua.LState) int {
lv := L.ToInt(1) /* get argument */
L.Push(lua.LNumber(lv * 2)) /* push result */
return 1 /* number of results */
}
func main() {
L := lua.NewState()
defer L.Close()
L.SetGlobal("double", L.NewFunction(Double)) /* Original lua_setglobal uses stack... */
}
.. code-block:: lua
print(double(20)) -- > "40"
Any function registered with GopherLua is a ``lua.LGFunction``, defined in ``value.go``
.. code-block:: go
type LGFunction func(*LState) int
Working with coroutines.
.. code-block:: go
co, _ := L.NewThread() /* create a new thread */
fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */
for {
st, err, values := L.Resume(co, fn)
if st == lua.ResumeError {
fmt.Println("yield break(error)")
fmt.Println(err.Error())
break
}
for i, lv := range values {
fmt.Printf("%v : %v\n", i, lv)
}
if st == lua.ResumeOK {
fmt.Println("yield break(ok)")
break
}
}
+++++++++++++++++++++++++++++++++++++++++
Opening a subset of builtin modules
+++++++++++++++++++++++++++++++++++++++++
The following demonstrates how to open a subset of the built-in modules in Lua, say for example to avoid enabling modules with access to local files or system calls.
main.go
.. code-block:: go
func main() {
L := lua.NewState(lua.Options{SkipOpenLibs: true})
defer L.Close()
for _, pair := range []struct {
n string
f lua.LGFunction
}{
{lua.LoadLibName, lua.OpenPackage}, // Must be first
{lua.BaseLibName, lua.OpenBase},
{lua.TabLibName, lua.OpenTable},
} {
if err := L.CallByParam(lua.P{
Fn: L.NewFunction(pair.f),
NRet: 0,
Protect: true,
}, lua.LString(pair.n)); err != nil {
panic(err)
}
}
if err := L.DoFile("main.lua"); err != nil {
panic(err)
}
}
+++++++++++++++++++++++++++++++++++++++++
Creating a module by Go
+++++++++++++++++++++++++++++++++++++++++
mymodule.go
.. code-block:: go
package mymodule
import (
"github.com/yuin/gopher-lua"
)
func Loader(L *lua.LState) int {
// register functions to the table
mod := L.SetFuncs(L.NewTable(), exports)
// register other stuff
L.SetField(mod, "name", lua.LString("value"))
// returns the module
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"myfunc": myfunc,
}
func myfunc(L *lua.LState) int {
return 0
}
mymain.go
.. code-block:: go
package main
import (
"./mymodule"
"github.com/yuin/gopher-lua"
)
func main() {
L := lua.NewState()
defer L.Close()
L.PreloadModule("mymodule", mymodule.Loader)
if err := L.DoFile("main.lua"); err != nil {
panic(err)
}
}
main.lua
.. code-block:: lua
local m = require("mymodule")
m.myfunc()
print(m.name)
+++++++++++++++++++++++++++++++++++++++++
Calling Lua from Go
+++++++++++++++++++++++++++++++++++++++++
.. code-block:: go
L := lua.NewState()
defer L.Close()
if err := L.DoFile("double.lua"); err != nil {
panic(err)
}
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("double"),
NRet: 1,
Protect: true,
}, lua.LNumber(10)); err != nil {
panic(err)
}
ret := L.Get(-1) // returned value
L.Pop(1) // remove received value
If ``Protect`` is false, GopherLua will panic instead of returning an ``error`` value.
+++++++++++++++++++++++++++++++++++++++++
User-Defined types
+++++++++++++++++++++++++++++++++++++++++
You can extend GopherLua with new types written in Go.
``LUserData`` is provided for this purpose.
.. code-block:: go
type Person struct {
Name string
}
const luaPersonTypeName = "person"
// Registers my person type to given L.
func registerPersonType(L *lua.LState) {
mt := L.NewTypeMetatable(luaPersonTypeName)
L.SetGlobal("person", mt)
// static attributes
L.SetField(mt, "new", L.NewFunction(newPerson))
// methods
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), personMethods))
}
// Constructor
func newPerson(L *lua.LState) int {
person := &Person{L.CheckString(1)}
ud := L.NewUserData()
ud.Value = person
L.SetMetatable(ud, L.GetTypeMetatable(luaPersonTypeName))
L.Push(ud)
return 1
}
// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkPerson(L *lua.LState) *Person {
ud := L.CheckUserData(1)
if v, ok := ud.Value.(*Person); ok {
return v
}
L.ArgError(1, "person expected")
return nil
}
var personMethods = map[string]lua.LGFunction{
"name": personGetSetName,
}
// Getter and setter for the Person#Name
func personGetSetName(L *lua.LState) int {
p := checkPerson(L)
if L.GetTop() == 2 {
p.Name = L.CheckString(2)
return 0
}
L.Push(lua.LString(p.Name))
return 1
}
func main() {
L := lua.NewState()
defer L.Close()
registerPersonType(L)
if err := L.DoString(`
p = person.new("Steeve")
print(p:name()) -- "Steeve"
p:name("Alice")
print(p:name()) -- "Alice"
`); err != nil {
panic(err)
}
}
+++++++++++++++++++++++++++++++++++++++++
Terminating a running LState
+++++++++++++++++++++++++++++++++++++++++
GopherLua supports the `Go Concurrency Patterns: Context <https://blog.golang.org/context>`_ .
.. code-block:: go
L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// set the context to our LState
L.SetContext(ctx)
err := L.DoString(`
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do end
end
sleep(3)
`)
// err.Error() contains "context deadline exceeded"
With coroutines
.. code-block:: go
L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithCancel(context.Background())
L.SetContext(ctx)
defer cancel()
L.DoString(`
function coro()
local i = 0
while true do
coroutine.yield(i)
i = i+1
end
return i
end
`)
co, cocancel := L.NewThread()
defer cocancel()
fn := L.GetGlobal("coro").(*LFunction)
_, err, values := L.Resume(co, fn) // err is nil
cancel() // cancel the parent context
_, err, values = L.Resume(co, fn) // err is NOT nil : child context was canceled
**Note that using a context causes performance degradation.**
.. code-block::
time ./glua-with-context.exe fib.lua
9227465
0.01s user 0.11s system 1% cpu 7.505 total
time ./glua-without-context.exe fib.lua
9227465
0.01s user 0.01s system 0% cpu 5.306 total
+++++++++++++++++++++++++++++++++++++++++
Sharing Lua byte code between LStates
+++++++++++++++++++++++++++++++++++++++++
Calling ``DoFile`` will load a Lua script, compile it to byte code and run the byte code in a ``LState``.
If you have multiple ``LStates`` which are all required to run the same script, you can share the byte code between them,
which will save on memory.
Sharing byte code is safe as it is read only and cannot be altered by lua scripts.
.. code-block:: go
// CompileLua reads the passed lua file from disk and compiles it.
func CompileLua(filePath string) (*lua.FunctionProto, error) {
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
return nil, err
}
reader := bufio.NewReader(file)
chunk, err := parse.Parse(reader, filePath)
if err != nil {
return nil, err
}
proto, err := lua.Compile(chunk, filePath)
if err != nil {
return nil, err
}
return proto, nil
}
// DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
// to calling DoFile on the LState with the original source file.
func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {
lfunc := L.NewFunctionFromProto(proto)
L.Push(lfunc)
return L.PCall(0, lua.MultRet, nil)
}
// Example shows how to share the compiled byte code from a lua script between multiple VMs.
func Example() {
codeToShare := CompileLua("mylua.lua")
a := lua.NewState()
b := lua.NewState()
c := lua.NewState()
DoCompiledFile(a, codeToShare)
DoCompiledFile(b, codeToShare)
DoCompiledFile(c, codeToShare)
}
+++++++++++++++++++++++++++++++++++++++++
Goroutines
+++++++++++++++++++++++++++++++++++++++++
The ``LState`` is not goroutine-safe. It is recommended to use one LState per goroutine and communicate between goroutines by using channels.
Channels are represented by ``channel`` objects in GopherLua. And a ``channel`` table provides functions for performing channel operations.
Some objects can not be sent over channels due to having non-goroutine-safe objects inside itself.
- a thread(state)
- a function
- an userdata
- a table with a metatable
You **must not** send these objects from Go APIs to channels.
.. code-block:: go
func receiver(ch, quit chan lua.LValue) {
L := lua.NewState()
defer L.Close()
L.SetGlobal("ch", lua.LChannel(ch))
L.SetGlobal("quit", lua.LChannel(quit))
if err := L.DoString(`
local exit = false
while not exit do
channel.select(
{"|<-", ch, function(ok, v)
if not ok then
print("channel closed")
exit = true
else
print("received:", v)
end
end},
{"|<-", quit, function(ok, v)
print("quit")
exit = true
end}
)
end
`); err != nil {
panic(err)
}
}
func sender(ch, quit chan lua.LValue) {
L := lua.NewState()
defer L.Close()
L.SetGlobal("ch", lua.LChannel(ch))
L.SetGlobal("quit", lua.LChannel(quit))
if err := L.DoString(`
ch:send("1")
ch:send("2")
`); err != nil {
panic(err)
}
ch <- lua.LString("3")
quit <- lua.LTrue
}
func main() {
ch := make(chan lua.LValue)
quit := make(chan lua.LValue)
go receiver(ch, quit)
go sender(ch, quit)
time.Sleep(3 * time.Second)
}
'''''''''''''''
Go API
'''''''''''''''
``ToChannel``, ``CheckChannel``, ``OptChannel`` are available.
Refer to `Go doc(LState methods) <http://godoc.org/github.com/yuin/gopher-lua>`_ for further information.
'''''''''''''''
Lua API
'''''''''''''''
- **channel.make([buf:int]) -> ch:channel**
- Create new channel that has a buffer size of ``buf``. By default, ``buf`` is 0.
- **channel.select(case:table [, case:table, case:table ...]) -> {index:int, recv:any, ok}**
- Same as the ``select`` statement in Go. It returns the index of the chosen case and, if that
case was a receive operation, the value received and a boolean indicating whether the channel has been closed.
- ``case`` is a table that outlined below.
- receiving: `{"|<-", ch:channel [, handler:func(ok, data:any)]}`
- sending: `{"<-|", ch:channel, data:any [, handler:func(data:any)]}`
- default: `{"default" [, handler:func()]}`
``channel.select`` examples:
.. code-block:: lua
local idx, recv, ok = channel.select(
{"|<-", ch1},
{"|<-", ch2}
)
if not ok then
print("closed")
elseif idx == 1 then -- received from ch1
print(recv)
elseif idx == 2 then -- received from ch2
print(recv)
end
.. code-block:: lua
channel.select(
{"|<-", ch1, function(ok, data)
print(ok, data)
end},
{"<-|", ch2, "value", function(data)
print(data)
end},
{"default", function()
print("default action")
end}
)
- **channel:send(data:any)**
- Send ``data`` over the channel.
- **channel:receive() -> ok:bool, data:any**
- Receive some data over the channel.
- **channel:close()**
- Close the channel.
''''''''''''''''''''''''''''''
The LState pool pattern
''''''''''''''''''''''''''''''
To create per-thread LState instances, You can use the ``sync.Pool`` like mechanism.
.. code-block:: go
type lStatePool struct {
m sync.Mutex
saved []*lua.LState
}
func (pl *lStatePool) Get() *lua.LState {
pl.m.Lock()
defer pl.m.Unlock()
n := len(pl.saved)
if n == 0 {
return pl.New()
}
x := pl.saved[n-1]
pl.saved = pl.saved[0 : n-1]
return x
}
func (pl *lStatePool) New() *lua.LState {
L := lua.NewState()
// setting the L up here.
// load scripts, set global variables, share channels, etc...
return L
}
func (pl *lStatePool) Put(L *lua.LState) {
pl.m.Lock()
defer pl.m.Unlock()
pl.saved = append(pl.saved, L)
}
func (pl *lStatePool) Shutdown() {
for _, L := range pl.saved {
L.Close()
}
}
// Global LState pool
var luaPool = &lStatePool{
saved: make([]*lua.LState, 0, 4),
}
Now, you can get per-thread LState objects from the ``luaPool`` .
.. code-block:: go
func MyWorker() {
L := luaPool.Get()
defer luaPool.Put(L)
/* your code here */
}
func main() {
defer luaPool.Shutdown()
go MyWorker()
go MyWorker()
/* etc... */
}
----------------------------------------------------------------
Differences between Lua and GopherLua
----------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Goroutines
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- GopherLua supports channel operations.
- GopherLua has a type named ``channel``.
- The ``channel`` table provides functions for performing channel operations.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unsupported functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``string.dump``
- ``os.setlocale``
- ``lua_Debug.namewhat``
- ``package.loadlib``
- debug hooks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Miscellaneous notes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``collectgarbage`` does not take any arguments and runs the garbage collector for the entire Go program.
- ``file:setvbuf`` does not support a line buffering.
- Daylight saving time is not supported.
- GopherLua has a function to set an environment variable : ``os.setenv(name, value)``
- GopherLua support ``goto`` and ``::label::`` statement in Lua5.2.
- `goto` is a keyword and not a valid variable name.
----------------------------------------------------------------
Standalone interpreter
----------------------------------------------------------------
Lua has an interpreter called ``lua`` . GopherLua has an interpreter called ``glua`` .
.. code-block:: bash
go get github.com/yuin/gopher-lua/cmd/glua
``glua`` has same options as ``lua`` .
----------------------------------------------------------------
How to Contribute
----------------------------------------------------------------
See `Guidlines for contributors <https://github.com/yuin/gopher-lua/tree/master/.github/CONTRIBUTING.md>`_ .
----------------------------------------------------------------
Libraries for GopherLua
----------------------------------------------------------------
- `gopher-luar <https://github.com/layeh/gopher-luar>`_ : Simplifies data passing to and from gopher-lua
- `gluamapper <https://github.com/yuin/gluamapper>`_ : Mapping a Lua table to a Go struct
- `gluare <https://github.com/yuin/gluare>`_ : Regular expressions for gopher-lua
- `gluahttp <https://github.com/cjoudrey/gluahttp>`_ : HTTP request module for gopher-lua
- `gopher-json <https://github.com/layeh/gopher-json>`_ : A simple JSON encoder/decoder for gopher-lua
- `gluayaml <https://github.com/kohkimakimoto/gluayaml>`_ : Yaml parser for gopher-lua
- `glua-lfs <https://github.com/layeh/gopher-lfs>`_ : Partially implements the luafilesystem module for gopher-lua
- `gluaurl <https://github.com/cjoudrey/gluaurl>`_ : A url parser/builder module for gopher-lua
- `gluahttpscrape <https://github.com/felipejfc/gluahttpscrape>`_ : A simple HTML scraper module for gopher-lua
- `gluaxmlpath <https://github.com/ailncode/gluaxmlpath>`_ : An xmlpath module for gopher-lua
- `gmoonscript <https://github.com/rucuriousyet/gmoonscript>`_ : Moonscript Compiler for the Gopher Lua VM
- `loguago <https://github.com/rucuriousyet/loguago>`_ : Zerolog wrapper for Gopher-Lua
- `gluacrypto <https://github.com/tengattack/gluacrypto>`_ : A native Go implementation of crypto library for the GopherLua VM.
- `gluasql <https://github.com/tengattack/gluasql>`_ : A native Go implementation of SQL client for the GopherLua VM.
- `purr <https://github.com/leyafo/purr>`_ : A http mock testing tool.
- `vadv/gopher-lua-libs <https://github.com/vadv/gopher-lua-libs>`_ : Some usefull libraries for GopherLua VM.
- `gluaperiphery <https://github.com/BixData/gluaperiphery>`_ : A periphery library for the GopherLua VM (GPIO, SPI, I2C, MMIO, and Serial peripheral I/O for Linux).
- `glua-async <https://github.com/CuberL/glua-async>`_ : An async/await implement for gopher-lua.
- `gopherlua-debugger <https://github.com/edolphin-ydf/gopherlua-debugger>`_ : A debugger for gopher-lua
- `gluamahonia <https://github.com/super1207/gluamahonia>`_ : An encoding converter for gopher-lua
----------------------------------------------------------------
Donation
----------------------------------------------------------------
BTC: 1NEDSyUmo4SMTDP83JJQSWi1MvQUGGNMZB
----------------------------------------------------------------
License
----------------------------------------------------------------
MIT
----------------------------------------------------------------
Author
----------------------------------------------------------------
Yusuke Inuzuka

2093
vendor/github.com/yuin/gopher-lua/_state.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

1049
vendor/github.com/yuin/gopher-lua/_vm.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

79
vendor/github.com/yuin/gopher-lua/alloc.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package lua
import (
"reflect"
"unsafe"
)
// iface is an internal representation of the go-interface.
type iface struct {
itab unsafe.Pointer
word unsafe.Pointer
}
const preloadLimit LNumber = 128
var _fv float64
var _uv uintptr
var preloads [int(preloadLimit)]LValue
func init() {
for i := 0; i < int(preloadLimit); i++ {
preloads[i] = LNumber(i)
}
}
// allocator is a fast bulk memory allocator for the LValue.
type allocator struct {
size int
fptrs []float64
fheader *reflect.SliceHeader
scratchValue LValue
scratchValueP *iface
}
func newAllocator(size int) *allocator {
al := &allocator{
size: size,
fptrs: make([]float64, 0, size),
fheader: nil,
}
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
al.scratchValue = LNumber(0)
al.scratchValueP = (*iface)(unsafe.Pointer(&al.scratchValue))
return al
}
// LNumber2I takes a number value and returns an interface LValue representing the same number.
// Converting an LNumber to a LValue naively, by doing:
// `var val LValue = myLNumber`
// will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory
// overhead of these allocs by allocating blocks of floats instead.
// The downside of this is that all of the floats on a given block have to become eligible for gc before the block
// as a whole can be gc-ed.
func (al *allocator) LNumber2I(v LNumber) LValue {
// first check for shared preloaded numbers
if v >= 0 && v < preloadLimit && float64(v) == float64(int64(v)) {
return preloads[int(v)]
}
// check if we need a new alloc page
if cap(al.fptrs) == len(al.fptrs) {
al.fptrs = make([]float64, 0, al.size)
al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs))
}
// alloc a new float, and store our value into it
al.fptrs = append(al.fptrs, float64(v))
fptr := &al.fptrs[len(al.fptrs)-1]
// hack our scratch LValue to point to our allocated value
// this scratch lvalue is copied when this function returns meaning the scratch value can be reused
// on the next call
al.scratchValueP.word = unsafe.Pointer(fptr)
return al.scratchValue
}

29
vendor/github.com/yuin/gopher-lua/ast/ast.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package ast
type PositionHolder interface {
Line() int
SetLine(int)
LastLine() int
SetLastLine(int)
}
type Node struct {
line int
lastline int
}
func (self *Node) Line() int {
return self.line
}
func (self *Node) SetLine(line int) {
self.line = line
}
func (self *Node) LastLine() int {
return self.lastline
}
func (self *Node) SetLastLine(line int) {
self.lastline = line
}

138
vendor/github.com/yuin/gopher-lua/ast/expr.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
package ast
type Expr interface {
PositionHolder
exprMarker()
}
type ExprBase struct {
Node
}
func (expr *ExprBase) exprMarker() {}
/* ConstExprs {{{ */
type ConstExpr interface {
Expr
constExprMarker()
}
type ConstExprBase struct {
ExprBase
}
func (expr *ConstExprBase) constExprMarker() {}
type TrueExpr struct {
ConstExprBase
}
type FalseExpr struct {
ConstExprBase
}
type NilExpr struct {
ConstExprBase
}
type NumberExpr struct {
ConstExprBase
Value string
}
type StringExpr struct {
ConstExprBase
Value string
}
/* ConstExprs }}} */
type Comma3Expr struct {
ExprBase
AdjustRet bool
}
type IdentExpr struct {
ExprBase
Value string
}
type AttrGetExpr struct {
ExprBase
Object Expr
Key Expr
}
type TableExpr struct {
ExprBase
Fields []*Field
}
type FuncCallExpr struct {
ExprBase
Func Expr
Receiver Expr
Method string
Args []Expr
AdjustRet bool
}
type LogicalOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type RelationalOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type StringConcatOpExpr struct {
ExprBase
Lhs Expr
Rhs Expr
}
type ArithmeticOpExpr struct {
ExprBase
Operator string
Lhs Expr
Rhs Expr
}
type UnaryMinusOpExpr struct {
ExprBase
Expr Expr
}
type UnaryNotOpExpr struct {
ExprBase
Expr Expr
}
type UnaryLenOpExpr struct {
ExprBase
Expr Expr
}
type FunctionExpr struct {
ExprBase
ParList *ParList
Stmts []Stmt
}

17
vendor/github.com/yuin/gopher-lua/ast/misc.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package ast
type Field struct {
Key Expr
Value Expr
}
type ParList struct {
HasVargs bool
Names []string
}
type FuncName struct {
Func Expr
Receiver Expr
Method string
}

107
vendor/github.com/yuin/gopher-lua/ast/stmt.go generated vendored Normal file
View File

@@ -0,0 +1,107 @@
package ast
type Stmt interface {
PositionHolder
stmtMarker()
}
type StmtBase struct {
Node
}
func (stmt *StmtBase) stmtMarker() {}
type AssignStmt struct {
StmtBase
Lhs []Expr
Rhs []Expr
}
type LocalAssignStmt struct {
StmtBase
Names []string
Exprs []Expr
}
type FuncCallStmt struct {
StmtBase
Expr Expr
}
type DoBlockStmt struct {
StmtBase
Stmts []Stmt
}
type WhileStmt struct {
StmtBase
Condition Expr
Stmts []Stmt
}
type RepeatStmt struct {
StmtBase
Condition Expr
Stmts []Stmt
}
type IfStmt struct {
StmtBase
Condition Expr
Then []Stmt
Else []Stmt
}
type NumberForStmt struct {
StmtBase
Name string
Init Expr
Limit Expr
Step Expr
Stmts []Stmt
}
type GenericForStmt struct {
StmtBase
Names []string
Exprs []Expr
Stmts []Stmt
}
type FuncDefStmt struct {
StmtBase
Name *FuncName
Func *FunctionExpr
}
type ReturnStmt struct {
StmtBase
Exprs []Expr
}
type BreakStmt struct {
StmtBase
}
type LabelStmt struct {
StmtBase
Name string
}
type GotoStmt struct {
StmtBase
Label string
}

22
vendor/github.com/yuin/gopher-lua/ast/token.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
package ast
import (
"fmt"
)
type Position struct {
Source string
Line int
Column int
}
type Token struct {
Type int
Name string
Str string
Pos Position
}
func (self *Token) String() string {
return fmt.Sprintf("<type:%v, str:%v>", self.Name, self.Str)
}

465
vendor/github.com/yuin/gopher-lua/auxlib.go generated vendored Normal file
View File

@@ -0,0 +1,465 @@
package lua
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
/* checkType {{{ */
func (ls *LState) CheckAny(n int) LValue {
if n > ls.GetTop() {
ls.ArgError(n, "value expected")
}
return ls.Get(n)
}
func (ls *LState) CheckInt(n int) int {
v := ls.Get(n)
if intv, ok := v.(LNumber); ok {
return int(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckInt64(n int) int64 {
v := ls.Get(n)
if intv, ok := v.(LNumber); ok {
return int64(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckNumber(n int) LNumber {
v := ls.Get(n)
if lv, ok := v.(LNumber); ok {
return lv
}
if lv, ok := v.(LString); ok {
if num, err := parseNumber(string(lv)); err == nil {
return num
}
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) CheckString(n int) string {
v := ls.Get(n)
if lv, ok := v.(LString); ok {
return string(lv)
} else if LVCanConvToString(v) {
return ls.ToString(n)
}
ls.TypeError(n, LTString)
return ""
}
func (ls *LState) CheckBool(n int) bool {
v := ls.Get(n)
if lv, ok := v.(LBool); ok {
return bool(lv)
}
ls.TypeError(n, LTBool)
return false
}
func (ls *LState) CheckTable(n int) *LTable {
v := ls.Get(n)
if lv, ok := v.(*LTable); ok {
return lv
}
ls.TypeError(n, LTTable)
return nil
}
func (ls *LState) CheckFunction(n int) *LFunction {
v := ls.Get(n)
if lv, ok := v.(*LFunction); ok {
return lv
}
ls.TypeError(n, LTFunction)
return nil
}
func (ls *LState) CheckUserData(n int) *LUserData {
v := ls.Get(n)
if lv, ok := v.(*LUserData); ok {
return lv
}
ls.TypeError(n, LTUserData)
return nil
}
func (ls *LState) CheckThread(n int) *LState {
v := ls.Get(n)
if lv, ok := v.(*LState); ok {
return lv
}
ls.TypeError(n, LTThread)
return nil
}
func (ls *LState) CheckType(n int, typ LValueType) {
v := ls.Get(n)
if v.Type() != typ {
ls.TypeError(n, typ)
}
}
func (ls *LState) CheckTypes(n int, typs ...LValueType) {
vt := ls.Get(n).Type()
for _, typ := range typs {
if vt == typ {
return
}
}
buf := []string{}
for _, typ := range typs {
buf = append(buf, typ.String())
}
ls.ArgError(n, strings.Join(buf, " or ")+" expected, got "+ls.Get(n).Type().String())
}
func (ls *LState) CheckOption(n int, options []string) int {
str := ls.CheckString(n)
for i, v := range options {
if v == str {
return i
}
}
ls.ArgError(n, fmt.Sprintf("invalid option: %s (must be one of %s)", str, strings.Join(options, ",")))
return 0
}
/* }}} */
/* optType {{{ */
func (ls *LState) OptInt(n int, d int) int {
v := ls.Get(n)
if v == LNil {
return d
}
if intv, ok := v.(LNumber); ok {
return int(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptInt64(n int, d int64) int64 {
v := ls.Get(n)
if v == LNil {
return d
}
if intv, ok := v.(LNumber); ok {
return int64(intv)
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptNumber(n int, d LNumber) LNumber {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LNumber); ok {
return lv
}
ls.TypeError(n, LTNumber)
return 0
}
func (ls *LState) OptString(n int, d string) string {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LString); ok {
return string(lv)
}
ls.TypeError(n, LTString)
return ""
}
func (ls *LState) OptBool(n int, d bool) bool {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(LBool); ok {
return bool(lv)
}
ls.TypeError(n, LTBool)
return false
}
func (ls *LState) OptTable(n int, d *LTable) *LTable {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LTable); ok {
return lv
}
ls.TypeError(n, LTTable)
return nil
}
func (ls *LState) OptFunction(n int, d *LFunction) *LFunction {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LFunction); ok {
return lv
}
ls.TypeError(n, LTFunction)
return nil
}
func (ls *LState) OptUserData(n int, d *LUserData) *LUserData {
v := ls.Get(n)
if v == LNil {
return d
}
if lv, ok := v.(*LUserData); ok {
return lv
}
ls.TypeError(n, LTUserData)
return nil
}
/* }}} */
/* error operations {{{ */
func (ls *LState) ArgError(n int, message string) {
ls.RaiseError("bad argument #%v to %v (%v)", n, ls.rawFrameFuncName(ls.currentFrame), message)
}
func (ls *LState) TypeError(n int, typ LValueType) {
ls.RaiseError("bad argument #%v to %v (%v expected, got %v)", n, ls.rawFrameFuncName(ls.currentFrame), typ.String(), ls.Get(n).Type().String())
}
/* }}} */
/* debug operations {{{ */
func (ls *LState) Where(level int) string {
return ls.where(level, false)
}
/* }}} */
/* table operations {{{ */
func (ls *LState) FindTable(obj *LTable, n string, size int) LValue {
names := strings.Split(n, ".")
curobj := obj
for _, name := range names {
if curobj.Type() != LTTable {
return LNil
}
nextobj := ls.RawGet(curobj, LString(name))
if nextobj == LNil {
tb := ls.CreateTable(0, size)
ls.RawSet(curobj, LString(name), tb)
curobj = tb
} else if nextobj.Type() != LTTable {
return LNil
} else {
curobj = nextobj.(*LTable)
}
}
return curobj
}
/* }}} */
/* register operations {{{ */
func (ls *LState) RegisterModule(name string, funcs map[string]LGFunction) LValue {
tb := ls.FindTable(ls.Get(RegistryIndex).(*LTable), "_LOADED", 1)
mod := ls.GetField(tb, name)
if mod.Type() != LTTable {
newmod := ls.FindTable(ls.Get(GlobalsIndex).(*LTable), name, len(funcs))
if newmodtb, ok := newmod.(*LTable); !ok {
ls.RaiseError("name conflict for module(%v)", name)
} else {
for fname, fn := range funcs {
newmodtb.RawSetString(fname, ls.NewFunction(fn))
}
ls.SetField(tb, name, newmodtb)
return newmodtb
}
}
return mod
}
func (ls *LState) SetFuncs(tb *LTable, funcs map[string]LGFunction, upvalues ...LValue) *LTable {
for fname, fn := range funcs {
tb.RawSetString(fname, ls.NewClosure(fn, upvalues...))
}
return tb
}
/* }}} */
/* metatable operations {{{ */
func (ls *LState) NewTypeMetatable(typ string) *LTable {
regtable := ls.Get(RegistryIndex)
mt := ls.GetField(regtable, typ)
if tb, ok := mt.(*LTable); ok {
return tb
}
mtnew := ls.NewTable()
ls.SetField(regtable, typ, mtnew)
return mtnew
}
func (ls *LState) GetMetaField(obj LValue, event string) LValue {
return ls.metaOp1(obj, event)
}
func (ls *LState) GetTypeMetatable(typ string) LValue {
return ls.GetField(ls.Get(RegistryIndex), typ)
}
func (ls *LState) CallMeta(obj LValue, event string) LValue {
op := ls.metaOp1(obj, event)
if op.Type() == LTFunction {
ls.reg.Push(op)
ls.reg.Push(obj)
ls.Call(1, 1)
return ls.reg.Pop()
}
return LNil
}
/* }}} */
/* load and function call operations {{{ */
func (ls *LState) LoadFile(path string) (*LFunction, error) {
var file *os.File
var err error
if len(path) == 0 {
file = os.Stdin
} else {
file, err = os.Open(path)
defer file.Close()
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
reader := bufio.NewReader(file)
// get the first character.
c, err := reader.ReadByte()
if err != nil && err != io.EOF {
return nil, newApiErrorE(ApiErrorFile, err)
}
if c == byte('#') {
// Unix exec. file?
// skip first line
_, err, _ = readBufioLine(reader)
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
if err != io.EOF {
// if the file is not empty,
// unread the first character of the file or newline character(readBufioLine's last byte).
err = reader.UnreadByte()
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
}
return ls.Load(reader, path)
}
func (ls *LState) LoadString(source string) (*LFunction, error) {
return ls.Load(strings.NewReader(source), "<string>")
}
func (ls *LState) DoFile(path string) error {
if fn, err := ls.LoadFile(path); err != nil {
return err
} else {
ls.Push(fn)
return ls.PCall(0, MultRet, nil)
}
}
func (ls *LState) DoString(source string) error {
if fn, err := ls.LoadString(source); err != nil {
return err
} else {
ls.Push(fn)
return ls.PCall(0, MultRet, nil)
}
}
/* }}} */
/* GopherLua original APIs {{{ */
// ToStringMeta returns string representation of given LValue.
// This method calls the `__tostring` meta method if defined.
func (ls *LState) ToStringMeta(lv LValue) LValue {
if fn, ok := ls.metaOp1(lv, "__tostring").(*LFunction); ok {
ls.Push(fn)
ls.Push(lv)
ls.Call(1, 1)
return ls.reg.Pop()
} else {
return LString(lv.String())
}
}
// Set a module loader to the package.preload table.
func (ls *LState) PreloadModule(name string, loader LGFunction) {
preload := ls.GetField(ls.GetField(ls.Get(EnvironIndex), "package"), "preload")
if _, ok := preload.(*LTable); !ok {
ls.RaiseError("package.preload must be a table")
}
ls.SetField(preload, name, ls.NewFunction(loader))
}
// Checks whether the given index is an LChannel and returns this channel.
func (ls *LState) CheckChannel(n int) chan LValue {
v := ls.Get(n)
if ch, ok := v.(LChannel); ok {
return (chan LValue)(ch)
}
ls.TypeError(n, LTChannel)
return nil
}
// If the given index is a LChannel, returns this channel. If this argument is absent or is nil, returns ch. Otherwise, raises an error.
func (ls *LState) OptChannel(n int, ch chan LValue) chan LValue {
v := ls.Get(n)
if v == LNil {
return ch
}
if ch, ok := v.(LChannel); ok {
return (chan LValue)(ch)
}
ls.TypeError(n, LTChannel)
return nil
}
/* }}} */
//

597
vendor/github.com/yuin/gopher-lua/baselib.go generated vendored Normal file
View File

@@ -0,0 +1,597 @@
package lua
import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
)
/* basic functions {{{ */
func OpenBase(L *LState) int {
global := L.Get(GlobalsIndex).(*LTable)
L.SetGlobal("_G", global)
L.SetGlobal("_VERSION", LString(LuaVersion))
L.SetGlobal("_GOPHER_LUA_VERSION", LString(PackageName+" "+PackageVersion))
basemod := L.RegisterModule("_G", baseFuncs)
global.RawSetString("ipairs", L.NewClosure(baseIpairs, L.NewFunction(ipairsaux)))
global.RawSetString("pairs", L.NewClosure(basePairs, L.NewFunction(pairsaux)))
L.Push(basemod)
return 1
}
var baseFuncs = map[string]LGFunction{
"assert": baseAssert,
"collectgarbage": baseCollectGarbage,
"dofile": baseDoFile,
"error": baseError,
"getfenv": baseGetFEnv,
"getmetatable": baseGetMetatable,
"load": baseLoad,
"loadfile": baseLoadFile,
"loadstring": baseLoadString,
"next": baseNext,
"pcall": basePCall,
"print": basePrint,
"rawequal": baseRawEqual,
"rawget": baseRawGet,
"rawset": baseRawSet,
"select": baseSelect,
"_printregs": base_PrintRegs,
"setfenv": baseSetFEnv,
"setmetatable": baseSetMetatable,
"tonumber": baseToNumber,
"tostring": baseToString,
"type": baseType,
"unpack": baseUnpack,
"xpcall": baseXPCall,
// loadlib
"module": loModule,
"require": loRequire,
// hidden features
"newproxy": baseNewProxy,
}
func baseAssert(L *LState) int {
if !L.ToBool(1) {
L.RaiseError(L.OptString(2, "assertion failed!"))
return 0
}
return L.GetTop()
}
func baseCollectGarbage(L *LState) int {
runtime.GC()
return 0
}
func baseDoFile(L *LState) int {
src := L.ToString(1)
top := L.GetTop()
fn, err := L.LoadFile(src)
if err != nil {
L.Push(LString(err.Error()))
L.Panic(L)
}
L.Push(fn)
L.Call(0, MultRet)
return L.GetTop() - top
}
func baseError(L *LState) int {
obj := L.CheckAny(1)
level := L.OptInt(2, 1)
L.Error(obj, level)
return 0
}
func baseGetFEnv(L *LState) int {
var value LValue
if L.GetTop() == 0 {
value = LNumber(1)
} else {
value = L.Get(1)
}
if fn, ok := value.(*LFunction); ok {
if !fn.IsG {
L.Push(fn.Env)
} else {
L.Push(L.G.Global)
}
return 1
}
if number, ok := value.(LNumber); ok {
level := int(float64(number))
if level <= 0 {
L.Push(L.Env)
} else {
cf := L.currentFrame
for i := 0; i < level && cf != nil; i++ {
cf = cf.Parent
}
if cf == nil || cf.Fn.IsG {
L.Push(L.G.Global)
} else {
L.Push(cf.Fn.Env)
}
}
return 1
}
L.Push(L.G.Global)
return 1
}
func baseGetMetatable(L *LState) int {
L.Push(L.GetMetatable(L.CheckAny(1)))
return 1
}
func ipairsaux(L *LState) int {
tb := L.CheckTable(1)
i := L.CheckInt(2)
i++
v := tb.RawGetInt(i)
if v == LNil {
return 0
} else {
L.Pop(1)
L.Push(LNumber(i))
L.Push(LNumber(i))
L.Push(v)
return 2
}
}
func baseIpairs(L *LState) int {
tb := L.CheckTable(1)
L.Push(L.Get(UpvalueIndex(1)))
L.Push(tb)
L.Push(LNumber(0))
return 3
}
func loadaux(L *LState, reader io.Reader, chunkname string) int {
if fn, err := L.Load(reader, chunkname); err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(fn)
return 1
}
}
func baseLoad(L *LState) int {
fn := L.CheckFunction(1)
chunkname := L.OptString(2, "?")
top := L.GetTop()
buf := []string{}
for {
L.SetTop(top)
L.Push(fn)
L.Call(0, 1)
ret := L.reg.Pop()
if ret == LNil {
break
} else if LVCanConvToString(ret) {
str := ret.String()
if len(str) > 0 {
buf = append(buf, string(str))
} else {
break
}
} else {
L.Push(LNil)
L.Push(LString("reader function must return a string"))
return 2
}
}
return loadaux(L, strings.NewReader(strings.Join(buf, "")), chunkname)
}
func baseLoadFile(L *LState) int {
var reader io.Reader
var chunkname string
var err error
if L.GetTop() < 1 {
reader = os.Stdin
chunkname = "<stdin>"
} else {
chunkname = L.CheckString(1)
reader, err = os.Open(chunkname)
if err != nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("can not open file: %v", chunkname)))
return 2
}
defer reader.(*os.File).Close()
}
return loadaux(L, reader, chunkname)
}
func baseLoadString(L *LState) int {
return loadaux(L, strings.NewReader(L.CheckString(1)), L.OptString(2, "<string>"))
}
func baseNext(L *LState) int {
tb := L.CheckTable(1)
index := LNil
if L.GetTop() >= 2 {
index = L.Get(2)
}
key, value := tb.Next(index)
if key == LNil {
L.Push(LNil)
return 1
}
L.Push(key)
L.Push(value)
return 2
}
func pairsaux(L *LState) int {
tb := L.CheckTable(1)
key, value := tb.Next(L.Get(2))
if key == LNil {
return 0
} else {
L.Pop(1)
L.Push(key)
L.Push(key)
L.Push(value)
return 2
}
}
func basePairs(L *LState) int {
tb := L.CheckTable(1)
L.Push(L.Get(UpvalueIndex(1)))
L.Push(tb)
L.Push(LNil)
return 3
}
func basePCall(L *LState) int {
L.CheckAny(1)
v := L.Get(1)
if v.Type() != LTFunction && L.GetMetaField(v, "__call").Type() != LTFunction {
L.Push(LFalse)
L.Push(LString("attempt to call a " + v.Type().String() + " value"))
return 2
}
nargs := L.GetTop() - 1
if err := L.PCall(nargs, MultRet, nil); err != nil {
L.Push(LFalse)
if aerr, ok := err.(*ApiError); ok {
L.Push(aerr.Object)
} else {
L.Push(LString(err.Error()))
}
return 2
} else {
L.Insert(LTrue, 1)
return L.GetTop()
}
}
func basePrint(L *LState) int {
top := L.GetTop()
for i := 1; i <= top; i++ {
fmt.Print(L.ToStringMeta(L.Get(i)).String())
if i != top {
fmt.Print("\t")
}
}
fmt.Println("")
return 0
}
func base_PrintRegs(L *LState) int {
L.printReg()
return 0
}
func baseRawEqual(L *LState) int {
if L.CheckAny(1) == L.CheckAny(2) {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
return 1
}
func baseRawGet(L *LState) int {
L.Push(L.RawGet(L.CheckTable(1), L.CheckAny(2)))
return 1
}
func baseRawSet(L *LState) int {
L.RawSet(L.CheckTable(1), L.CheckAny(2), L.CheckAny(3))
return 0
}
func baseSelect(L *LState) int {
L.CheckTypes(1, LTNumber, LTString)
switch lv := L.Get(1).(type) {
case LNumber:
idx := int(lv)
num := L.GetTop()
if idx < 0 {
idx = num + idx
} else if idx > num {
idx = num
}
if 1 > idx {
L.ArgError(1, "index out of range")
}
return num - idx
case LString:
if string(lv) != "#" {
L.ArgError(1, "invalid string '"+string(lv)+"'")
}
L.Push(LNumber(L.GetTop() - 1))
return 1
}
return 0
}
func baseSetFEnv(L *LState) int {
var value LValue
if L.GetTop() == 0 {
value = LNumber(1)
} else {
value = L.Get(1)
}
env := L.CheckTable(2)
if fn, ok := value.(*LFunction); ok {
if fn.IsG {
L.RaiseError("cannot change the environment of given object")
} else {
fn.Env = env
L.Push(fn)
return 1
}
}
if number, ok := value.(LNumber); ok {
level := int(float64(number))
if level <= 0 {
L.Env = env
return 0
}
cf := L.currentFrame
for i := 0; i < level && cf != nil; i++ {
cf = cf.Parent
}
if cf == nil || cf.Fn.IsG {
L.RaiseError("cannot change the environment of given object")
} else {
cf.Fn.Env = env
L.Push(cf.Fn)
return 1
}
}
L.RaiseError("cannot change the environment of given object")
return 0
}
func baseSetMetatable(L *LState) int {
L.CheckTypes(2, LTNil, LTTable)
obj := L.Get(1)
if obj == LNil {
L.RaiseError("cannot set metatable to a nil object.")
}
mt := L.Get(2)
if m := L.metatable(obj, true); m != LNil {
if tb, ok := m.(*LTable); ok && tb.RawGetString("__metatable") != LNil {
L.RaiseError("cannot change a protected metatable")
}
}
L.SetMetatable(obj, mt)
L.SetTop(1)
return 1
}
func baseToNumber(L *LState) int {
base := L.OptInt(2, 10)
noBase := L.Get(2) == LNil
switch lv := L.CheckAny(1).(type) {
case LNumber:
L.Push(lv)
case LString:
str := strings.Trim(string(lv), " \n\t")
if strings.Index(str, ".") > -1 {
if v, err := strconv.ParseFloat(str, LNumberBit); err != nil {
L.Push(LNil)
} else {
L.Push(LNumber(v))
}
} else {
if noBase && strings.HasPrefix(strings.ToLower(str), "0x") {
base, str = 16, str[2:] // Hex number
}
if v, err := strconv.ParseInt(str, base, LNumberBit); err != nil {
L.Push(LNil)
} else {
L.Push(LNumber(v))
}
}
default:
L.Push(LNil)
}
return 1
}
func baseToString(L *LState) int {
v1 := L.CheckAny(1)
L.Push(L.ToStringMeta(v1))
return 1
}
func baseType(L *LState) int {
L.Push(LString(L.CheckAny(1).Type().String()))
return 1
}
func baseUnpack(L *LState) int {
tb := L.CheckTable(1)
start := L.OptInt(2, 1)
end := L.OptInt(3, tb.Len())
for i := start; i <= end; i++ {
L.Push(tb.RawGetInt(i))
}
ret := end - start + 1
if ret < 0 {
return 0
}
return ret
}
func baseXPCall(L *LState) int {
fn := L.CheckFunction(1)
errfunc := L.CheckFunction(2)
top := L.GetTop()
L.Push(fn)
if err := L.PCall(0, MultRet, errfunc); err != nil {
L.Push(LFalse)
if aerr, ok := err.(*ApiError); ok {
L.Push(aerr.Object)
} else {
L.Push(LString(err.Error()))
}
return 2
} else {
L.Insert(LTrue, top+1)
return L.GetTop() - top
}
}
/* }}} */
/* load lib {{{ */
func loModule(L *LState) int {
name := L.CheckString(1)
loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
tb := L.GetField(loaded, name)
if _, ok := tb.(*LTable); !ok {
tb = L.FindTable(L.Get(GlobalsIndex).(*LTable), name, 1)
if tb == LNil {
L.RaiseError("name conflict for module: %v", name)
}
L.SetField(loaded, name, tb)
}
if L.GetField(tb, "_NAME") == LNil {
L.SetField(tb, "_M", tb)
L.SetField(tb, "_NAME", LString(name))
names := strings.Split(name, ".")
pname := ""
if len(names) > 1 {
pname = strings.Join(names[:len(names)-1], ".") + "."
}
L.SetField(tb, "_PACKAGE", LString(pname))
}
caller := L.currentFrame.Parent
if caller == nil {
L.RaiseError("no calling stack.")
} else if caller.Fn.IsG {
L.RaiseError("module() can not be called from GFunctions.")
}
L.SetFEnv(caller.Fn, tb)
top := L.GetTop()
for i := 2; i <= top; i++ {
L.Push(L.Get(i))
L.Push(tb)
L.Call(1, 0)
}
L.Push(tb)
return 1
}
var loopdetection = &LUserData{}
func loRequire(L *LState) int {
name := L.CheckString(1)
loaded := L.GetField(L.Get(RegistryIndex), "_LOADED")
lv := L.GetField(loaded, name)
if LVAsBool(lv) {
if lv == loopdetection {
L.RaiseError("loop or previous error loading module: %s", name)
}
L.Push(lv)
return 1
}
loaders, ok := L.GetField(L.Get(RegistryIndex), "_LOADERS").(*LTable)
if !ok {
L.RaiseError("package.loaders must be a table")
}
messages := []string{}
var modasfunc LValue
for i := 1; ; i++ {
loader := L.RawGetInt(loaders, i)
if loader == LNil {
L.RaiseError("module %s not found:\n\t%s, ", name, strings.Join(messages, "\n\t"))
}
L.Push(loader)
L.Push(LString(name))
L.Call(1, 1)
ret := L.reg.Pop()
switch retv := ret.(type) {
case *LFunction:
modasfunc = retv
goto loopbreak
case LString:
messages = append(messages, string(retv))
}
}
loopbreak:
L.SetField(loaded, name, loopdetection)
L.Push(modasfunc)
L.Push(LString(name))
L.Call(1, 1)
ret := L.reg.Pop()
modv := L.GetField(loaded, name)
if ret != LNil && modv == loopdetection {
L.SetField(loaded, name, ret)
L.Push(ret)
} else if modv == loopdetection {
L.SetField(loaded, name, LTrue)
L.Push(LTrue)
} else {
L.Push(modv)
}
return 1
}
/* }}} */
/* hidden features {{{ */
func baseNewProxy(L *LState) int {
ud := L.NewUserData()
L.SetTop(1)
if L.Get(1) == LTrue {
L.SetMetatable(ud, L.NewTable())
} else if d, ok := L.Get(1).(*LUserData); ok {
L.SetMetatable(ud, L.GetMetatable(d))
}
L.Push(ud)
return 1
}
/* }}} */
//

184
vendor/github.com/yuin/gopher-lua/channellib.go generated vendored Normal file
View File

@@ -0,0 +1,184 @@
package lua
import (
"reflect"
)
func checkChannel(L *LState, idx int) reflect.Value {
ch := L.CheckChannel(idx)
return reflect.ValueOf(ch)
}
func checkGoroutineSafe(L *LState, idx int) LValue {
v := L.CheckAny(2)
if !isGoroutineSafe(v) {
L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable")
}
return v
}
func OpenChannel(L *LState) int {
var mod LValue
//_, ok := L.G.builtinMts[int(LTChannel)]
// if !ok {
mod = L.RegisterModule(ChannelLibName, channelFuncs)
mt := L.SetFuncs(L.NewTable(), channelMethods)
mt.RawSetString("__index", mt)
L.G.builtinMts[int(LTChannel)] = mt
// }
L.Push(mod)
return 1
}
var channelFuncs = map[string]LGFunction{
"make": channelMake,
"select": channelSelect,
}
func channelMake(L *LState) int {
buffer := L.OptInt(1, 0)
L.Push(LChannel(make(chan LValue, buffer)))
return 1
}
func channelSelect(L *LState) int {
//TODO check case table size
cases := make([]reflect.SelectCase, L.GetTop())
top := L.GetTop()
for i := 0; i < top; i++ {
cas := reflect.SelectCase{
Dir: reflect.SelectSend,
Chan: reflect.ValueOf(nil),
Send: reflect.ValueOf(nil),
}
tbl := L.CheckTable(i + 1)
dir, ok1 := tbl.RawGetInt(1).(LString)
if !ok1 {
L.ArgError(i+1, "invalid select case")
}
switch string(dir) {
case "<-|":
ch, ok := tbl.RawGetInt(2).(LChannel)
if !ok {
L.ArgError(i+1, "invalid select case")
}
cas.Chan = reflect.ValueOf((chan LValue)(ch))
v := tbl.RawGetInt(3)
if !isGoroutineSafe(v) {
L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable")
}
cas.Send = reflect.ValueOf(v)
case "|<-":
ch, ok := tbl.RawGetInt(2).(LChannel)
if !ok {
L.ArgError(i+1, "invalid select case")
}
cas.Chan = reflect.ValueOf((chan LValue)(ch))
cas.Dir = reflect.SelectRecv
case "default":
cas.Dir = reflect.SelectDefault
default:
L.ArgError(i+1, "invalid channel direction:"+string(dir))
}
cases[i] = cas
}
if L.ctx != nil {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(L.ctx.Done()),
Send: reflect.ValueOf(nil),
})
}
pos, recv, rok := reflect.Select(cases)
if L.ctx != nil && pos == L.GetTop() {
return 0
}
lv := LNil
if recv.Kind() != 0 {
lv, _ = recv.Interface().(LValue)
if lv == nil {
lv = LNil
}
}
tbl := L.Get(pos + 1).(*LTable)
last := tbl.RawGetInt(tbl.Len())
if last.Type() == LTFunction {
L.Push(last)
switch cases[pos].Dir {
case reflect.SelectRecv:
if rok {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
L.Push(lv)
L.Call(2, 0)
case reflect.SelectSend:
L.Push(tbl.RawGetInt(3))
L.Call(1, 0)
case reflect.SelectDefault:
L.Call(0, 0)
}
}
L.Push(LNumber(pos + 1))
L.Push(lv)
if rok {
L.Push(LTrue)
} else {
L.Push(LFalse)
}
return 3
}
var channelMethods = map[string]LGFunction{
"receive": channelReceive,
"send": channelSend,
"close": channelClose,
}
func channelReceive(L *LState) int {
rch := checkChannel(L, 1)
var v reflect.Value
var ok bool
if L.ctx != nil {
cases := []reflect.SelectCase{{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(L.ctx.Done()),
Send: reflect.ValueOf(nil),
}, {
Dir: reflect.SelectRecv,
Chan: rch,
Send: reflect.ValueOf(nil),
}}
_, v, ok = reflect.Select(cases)
} else {
v, ok = rch.Recv()
}
if ok {
L.Push(LTrue)
L.Push(v.Interface().(LValue))
} else {
L.Push(LFalse)
L.Push(LNil)
}
return 2
}
func channelSend(L *LState) int {
rch := checkChannel(L, 1)
v := checkGoroutineSafe(L, 2)
rch.Send(reflect.ValueOf(v))
return 0
}
func channelClose(L *LState) int {
rch := checkChannel(L, 1)
rch.Close()
return 0
}
//

1869
vendor/github.com/yuin/gopher-lua/compile.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

43
vendor/github.com/yuin/gopher-lua/config.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
package lua
import (
"os"
)
var CompatVarArg = true
var FieldsPerFlush = 50
var RegistrySize = 256 * 20
var RegistryGrowStep = 32
var CallStackSize = 256
var MaxTableGetLoop = 100
var MaxArrayIndex = 67108864
type LNumber float64
const LNumberBit = 64
const LNumberScanFormat = "%f"
const LuaVersion = "Lua 5.1"
var LuaPath = "LUA_PATH"
var LuaLDir string
var LuaPathDefault string
var LuaOS string
var LuaDirSep string
var LuaPathSep = ";"
var LuaPathMark = "?"
var LuaExecDir = "!"
var LuaIgMark = "-"
func init() {
if os.PathSeparator == '/' { // unix-like
LuaOS = "unix"
LuaLDir = "/usr/local/share/lua/5.1"
LuaDirSep = "/"
LuaPathDefault = "./?.lua;" + LuaLDir + "/?.lua;" + LuaLDir + "/?/init.lua"
} else { // windows
LuaOS = "windows"
LuaLDir = "!\\lua"
LuaDirSep = "\\"
LuaPathDefault = ".\\?.lua;" + LuaLDir + "\\?.lua;" + LuaLDir + "\\?\\init.lua"
}
}

112
vendor/github.com/yuin/gopher-lua/coroutinelib.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
package lua
func OpenCoroutine(L *LState) int {
// TODO: Tie module name to contents of linit.go?
mod := L.RegisterModule(CoroutineLibName, coFuncs)
L.Push(mod)
return 1
}
var coFuncs = map[string]LGFunction{
"create": coCreate,
"yield": coYield,
"resume": coResume,
"running": coRunning,
"status": coStatus,
"wrap": coWrap,
}
func coCreate(L *LState) int {
fn := L.CheckFunction(1)
newthread, _ := L.NewThread()
base := 0
newthread.stack.Push(callFrame{
Fn: fn,
Pc: 0,
Base: base,
LocalBase: base + 1,
ReturnBase: base,
NArgs: 0,
NRet: MultRet,
Parent: nil,
TailCall: 0,
})
L.Push(newthread)
return 1
}
func coYield(L *LState) int {
return -1
}
func coResume(L *LState) int {
th := L.CheckThread(1)
if L.G.CurrentThread == th {
msg := "can not resume a running thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
if th.Dead {
msg := "can not resume a dead thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
th.Parent = L
L.G.CurrentThread = th
if !th.isStarted() {
cf := th.stack.Last()
th.currentFrame = cf
th.SetTop(0)
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
cf.NArgs = nargs
th.initCallFrame(cf)
th.Panic = panicWithoutTraceback
} else {
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
}
top := L.GetTop()
threadRun(th)
return L.GetTop() - top
}
func coRunning(L *LState) int {
if L.G.MainThread == L {
L.Push(LNil)
return 1
}
L.Push(L.G.CurrentThread)
return 1
}
func coStatus(L *LState) int {
L.Push(LString(L.Status(L.CheckThread(1))))
return 1
}
func wrapaux(L *LState) int {
L.Insert(L.ToThread(UpvalueIndex(1)), 1)
return coResume(L)
}
func coWrap(L *LState) int {
coCreate(L)
L.CheckThread(L.GetTop()).wrapped = true
v := L.Get(L.GetTop())
L.Pop(1)
L.Push(L.NewClosure(wrapaux, v))
return 1
}
//

173
vendor/github.com/yuin/gopher-lua/debuglib.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package lua
import (
"fmt"
"strings"
)
func OpenDebug(L *LState) int {
dbgmod := L.RegisterModule(DebugLibName, debugFuncs)
L.Push(dbgmod)
return 1
}
var debugFuncs = map[string]LGFunction{
"getfenv": debugGetFEnv,
"getinfo": debugGetInfo,
"getlocal": debugGetLocal,
"getmetatable": debugGetMetatable,
"getupvalue": debugGetUpvalue,
"setfenv": debugSetFEnv,
"setlocal": debugSetLocal,
"setmetatable": debugSetMetatable,
"setupvalue": debugSetUpvalue,
"traceback": debugTraceback,
}
func debugGetFEnv(L *LState) int {
L.Push(L.GetFEnv(L.CheckAny(1)))
return 1
}
func debugGetInfo(L *LState) int {
L.CheckTypes(1, LTFunction, LTNumber)
arg1 := L.Get(1)
what := L.OptString(2, "Slunf")
var dbg *Debug
var fn LValue
var err error
var ok bool
switch lv := arg1.(type) {
case *LFunction:
dbg = &Debug{}
fn, err = L.GetInfo(">"+what, dbg, lv)
case LNumber:
dbg, ok = L.GetStack(int(lv))
if !ok {
L.Push(LNil)
return 1
}
fn, err = L.GetInfo(what, dbg, LNil)
}
if err != nil {
L.Push(LNil)
return 1
}
tbl := L.NewTable()
if len(dbg.Name) > 0 {
tbl.RawSetString("name", LString(dbg.Name))
} else {
tbl.RawSetString("name", LNil)
}
tbl.RawSetString("what", LString(dbg.What))
tbl.RawSetString("source", LString(dbg.Source))
tbl.RawSetString("currentline", LNumber(dbg.CurrentLine))
tbl.RawSetString("nups", LNumber(dbg.NUpvalues))
tbl.RawSetString("linedefined", LNumber(dbg.LineDefined))
tbl.RawSetString("lastlinedefined", LNumber(dbg.LastLineDefined))
tbl.RawSetString("func", fn)
L.Push(tbl)
return 1
}
func debugGetLocal(L *LState) int {
level := L.CheckInt(1)
idx := L.CheckInt(2)
dbg, ok := L.GetStack(level)
if !ok {
L.ArgError(1, "level out of range")
}
name, value := L.GetLocal(dbg, idx)
if len(name) > 0 {
L.Push(LString(name))
L.Push(value)
return 2
}
L.Push(LNil)
return 1
}
func debugGetMetatable(L *LState) int {
L.Push(L.GetMetatable(L.CheckAny(1)))
return 1
}
func debugGetUpvalue(L *LState) int {
fn := L.CheckFunction(1)
idx := L.CheckInt(2)
name, value := L.GetUpvalue(fn, idx)
if len(name) > 0 {
L.Push(LString(name))
L.Push(value)
return 2
}
L.Push(LNil)
return 1
}
func debugSetFEnv(L *LState) int {
L.SetFEnv(L.CheckAny(1), L.CheckAny(2))
return 0
}
func debugSetLocal(L *LState) int {
level := L.CheckInt(1)
idx := L.CheckInt(2)
value := L.CheckAny(3)
dbg, ok := L.GetStack(level)
if !ok {
L.ArgError(1, "level out of range")
}
name := L.SetLocal(dbg, idx, value)
if len(name) > 0 {
L.Push(LString(name))
} else {
L.Push(LNil)
}
return 1
}
func debugSetMetatable(L *LState) int {
L.CheckTypes(2, LTNil, LTTable)
obj := L.Get(1)
mt := L.Get(2)
L.SetMetatable(obj, mt)
L.SetTop(1)
return 1
}
func debugSetUpvalue(L *LState) int {
fn := L.CheckFunction(1)
idx := L.CheckInt(2)
value := L.CheckAny(3)
name := L.SetUpvalue(fn, idx, value)
if len(name) > 0 {
L.Push(LString(name))
} else {
L.Push(LNil)
}
return 1
}
func debugTraceback(L *LState) int {
msg := ""
level := L.OptInt(2, 1)
ls := L
if L.GetTop() > 0 {
if s, ok := L.Get(1).(LString); ok {
msg = string(s)
}
if l, ok := L.Get(1).(*LState); ok {
ls = l
msg = ""
}
}
traceback := strings.TrimSpace(ls.stackTrace(level))
if len(msg) > 0 {
traceback = fmt.Sprintf("%s\n%s", msg, traceback)
}
L.Push(LString(traceback))
return 1
}

193
vendor/github.com/yuin/gopher-lua/function.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
package lua
import (
"fmt"
"strings"
)
const (
VarArgHasArg uint8 = 1
VarArgIsVarArg uint8 = 2
VarArgNeedsArg uint8 = 4
)
type DbgLocalInfo struct {
Name string
StartPc int
EndPc int
}
type DbgCall struct {
Name string
Pc int
}
type FunctionProto struct {
SourceName string
LineDefined int
LastLineDefined int
NumUpvalues uint8
NumParameters uint8
IsVarArg uint8
NumUsedRegisters uint8
Code []uint32
Constants []LValue
FunctionPrototypes []*FunctionProto
DbgSourcePositions []int
DbgLocals []*DbgLocalInfo
DbgCalls []DbgCall
DbgUpvalues []string
stringConstants []string
}
/* Upvalue {{{ */
type Upvalue struct {
next *Upvalue
reg *registry
index int
value LValue
closed bool
}
func (uv *Upvalue) Value() LValue {
//if uv.IsClosed() {
if uv.closed || uv.reg == nil {
return uv.value
}
//return uv.reg.Get(uv.index)
return uv.reg.array[uv.index]
}
func (uv *Upvalue) SetValue(value LValue) {
if uv.IsClosed() {
uv.value = value
} else {
uv.reg.Set(uv.index, value)
}
}
func (uv *Upvalue) Close() {
value := uv.Value()
uv.closed = true
uv.value = value
}
func (uv *Upvalue) IsClosed() bool {
return uv.closed || uv.reg == nil
}
func UpvalueIndex(i int) int {
return GlobalsIndex - i
}
/* }}} */
/* FunctionProto {{{ */
func newFunctionProto(name string) *FunctionProto {
return &FunctionProto{
SourceName: name,
LineDefined: 0,
LastLineDefined: 0,
NumUpvalues: 0,
NumParameters: 0,
IsVarArg: 0,
NumUsedRegisters: 2,
Code: make([]uint32, 0, 128),
Constants: make([]LValue, 0, 32),
FunctionPrototypes: make([]*FunctionProto, 0, 16),
DbgSourcePositions: make([]int, 0, 128),
DbgLocals: make([]*DbgLocalInfo, 0, 16),
DbgCalls: make([]DbgCall, 0, 128),
DbgUpvalues: make([]string, 0, 16),
stringConstants: make([]string, 0, 32),
}
}
func (fp *FunctionProto) String() string {
return fp.str(1, 0)
}
func (fp *FunctionProto) str(level int, count int) string {
indent := strings.Repeat(" ", level-1)
buf := []string{}
buf = append(buf, fmt.Sprintf("%v; function [%v] definition (level %v)\n",
indent, count, level))
buf = append(buf, fmt.Sprintf("%v; %v upvalues, %v params, %v stacks\n",
indent, fp.NumUpvalues, fp.NumParameters, fp.NumUsedRegisters))
for reg, linfo := range fp.DbgLocals {
buf = append(buf, fmt.Sprintf("%v.local %v ; %v\n", indent, linfo.Name, reg))
}
for reg, upvalue := range fp.DbgUpvalues {
buf = append(buf, fmt.Sprintf("%v.upvalue %v ; %v\n", indent, upvalue, reg))
}
for reg, conzt := range fp.Constants {
buf = append(buf, fmt.Sprintf("%v.const %v ; %v\n", indent, conzt.String(), reg))
}
buf = append(buf, "\n")
protono := 0
for no, code := range fp.Code {
inst := opGetOpCode(code)
if inst == OP_CLOSURE {
buf = append(buf, "\n")
buf = append(buf, fp.FunctionPrototypes[protono].str(level+1, protono))
buf = append(buf, "\n")
protono++
}
buf = append(buf, fmt.Sprintf("%v[%03d] %v (line:%v)\n",
indent, no+1, opToString(code), fp.DbgSourcePositions[no]))
}
buf = append(buf, fmt.Sprintf("%v; end of function\n", indent))
return strings.Join(buf, "")
}
/* }}} */
/* LFunction {{{ */
func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: false,
Env: env,
Proto: proto,
GFunction: nil,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: true,
Env: env,
Proto: nil,
GFunction: gfunc,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func (fn *LFunction) LocalName(regno, pc int) (string, bool) {
if fn.IsG {
return "", false
}
p := fn.Proto
for i := 0; i < len(p.DbgLocals) && p.DbgLocals[i].StartPc < pc; i++ {
if pc < p.DbgLocals[i].EndPc {
regno--
if regno == 0 {
return p.DbgLocals[i].Name, true
}
}
}
return "", false
}
/* }}} */

749
vendor/github.com/yuin/gopher-lua/iolib.go generated vendored Normal file
View File

@@ -0,0 +1,749 @@
package lua
import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"syscall"
)
var ioFuncs = map[string]LGFunction{
"close": ioClose,
"flush": ioFlush,
"lines": ioLines,
"input": ioInput,
"output": ioOutput,
"open": ioOpenFile,
"popen": ioPopen,
"read": ioRead,
"type": ioType,
"tmpfile": ioTmpFile,
"write": ioWrite,
}
const lFileClass = "FILE*"
type lFile struct {
fp *os.File
pp *exec.Cmd
writer io.Writer
reader *bufio.Reader
stdout io.ReadCloser
closed bool
}
type lFileType int
const (
lFileFile lFileType = iota
lFileProcess
)
const fileDefOutIndex = 1
const fileDefInIndex = 2
const fileDefaultWriteBuffer = 4096
const fileDefaultReadBuffer = 4096
func checkFile(L *LState) *lFile {
ud := L.CheckUserData(1)
if file, ok := ud.Value.(*lFile); ok {
return file
}
L.ArgError(1, "file expected")
return nil
}
func errorIfFileIsClosed(L *LState, file *lFile) {
if file.closed {
L.ArgError(1, "file is closed")
}
}
func newFile(L *LState, file *os.File, path string, flag int, perm os.FileMode, writable, readable bool) (*LUserData, error) {
ud := L.NewUserData()
var err error
if file == nil {
file, err = os.OpenFile(path, flag, perm)
if err != nil {
return nil, err
}
}
lfile := &lFile{fp: file, pp: nil, writer: nil, reader: nil, stdout: nil, closed: false}
ud.Value = lfile
if writable {
lfile.writer = file
}
if readable {
lfile.reader = bufio.NewReaderSize(file, fileDefaultReadBuffer)
}
L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
return ud, nil
}
func newProcess(L *LState, cmd string, writable, readable bool) (*LUserData, error) {
ud := L.NewUserData()
c, args := popenArgs(cmd)
pp := exec.Command(c, args...)
lfile := &lFile{fp: nil, pp: pp, writer: nil, reader: nil, stdout: nil, closed: false}
ud.Value = lfile
var err error
if writable {
lfile.writer, err = pp.StdinPipe()
}
if readable {
lfile.stdout, err = pp.StdoutPipe()
lfile.reader = bufio.NewReaderSize(lfile.stdout, fileDefaultReadBuffer)
}
if err != nil {
return nil, err
}
err = pp.Start()
if err != nil {
return nil, err
}
L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
return ud, nil
}
func (file *lFile) Type() lFileType {
if file.fp == nil {
return lFileProcess
}
return lFileFile
}
func (file *lFile) Name() string {
switch file.Type() {
case lFileFile:
return fmt.Sprintf("file %s", file.fp.Name())
case lFileProcess:
return fmt.Sprintf("process %s", file.pp.Path)
}
return ""
}
func (file *lFile) AbandonReadBuffer() error {
if file.Type() == lFileFile && file.reader != nil {
_, err := file.fp.Seek(-int64(file.reader.Buffered()), 1)
if err != nil {
return err
}
file.reader = bufio.NewReaderSize(file.fp, fileDefaultReadBuffer)
}
return nil
}
func fileDefOut(L *LState) *LUserData {
return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefOutIndex).(*LUserData)
}
func fileDefIn(L *LState) *LUserData {
return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefInIndex).(*LUserData)
}
func fileIsWritable(L *LState, file *lFile) int {
if file.writer == nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("%s is opened for only reading.", file.Name())))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
return 0
}
func fileIsReadable(L *LState, file *lFile) int {
if file.reader == nil {
L.Push(LNil)
L.Push(LString(fmt.Sprintf("%s is opened for only writing.", file.Name())))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
return 0
}
var stdFiles = []struct {
name string
file *os.File
writable bool
readable bool
}{
{"stdout", os.Stdout, true, false},
{"stdin", os.Stdin, false, true},
{"stderr", os.Stderr, true, false},
}
func OpenIo(L *LState) int {
mod := L.RegisterModule(IoLibName, map[string]LGFunction{}).(*LTable)
mt := L.NewTypeMetatable(lFileClass)
mt.RawSetString("__index", mt)
L.SetFuncs(mt, fileMethods)
mt.RawSetString("lines", L.NewClosure(fileLines, L.NewFunction(fileLinesIter)))
for _, finfo := range stdFiles {
file, _ := newFile(L, finfo.file, "", 0, os.FileMode(0), finfo.writable, finfo.readable)
mod.RawSetString(finfo.name, file)
}
uv := L.CreateTable(2, 0)
uv.RawSetInt(fileDefOutIndex, mod.RawGetString("stdout"))
uv.RawSetInt(fileDefInIndex, mod.RawGetString("stdin"))
for name, fn := range ioFuncs {
mod.RawSetString(name, L.NewClosure(fn, uv))
}
mod.RawSetString("lines", L.NewClosure(ioLines, uv, L.NewClosure(ioLinesIter, uv)))
// Modifications are being made in-place rather than returned?
L.Push(mod)
return 1
}
var fileMethods = map[string]LGFunction{
"__tostring": fileToString,
"write": fileWrite,
"close": fileClose,
"flush": fileFlush,
"lines": fileLines,
"read": fileRead,
"seek": fileSeek,
"setvbuf": fileSetVBuf,
}
func fileToString(L *LState) int {
file := checkFile(L)
if file.Type() == lFileFile {
if file.closed {
L.Push(LString("file (closed)"))
} else {
L.Push(LString("file"))
}
} else {
if file.closed {
L.Push(LString("process (closed)"))
} else {
L.Push(LString("process"))
}
}
return 1
}
func fileWriteAux(L *LState, file *lFile, idx int) int {
if n := fileIsWritable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
top := L.GetTop()
out := file.writer
var err error
for i := idx; i <= top; i++ {
L.CheckTypes(i, LTNumber, LTString)
s := LVAsString(L.Get(i))
if _, err = out.Write(unsafeFastStringToReadOnlyBytes(s)); err != nil {
goto errreturn
}
}
file.AbandonReadBuffer()
L.Push(LTrue)
return 1
errreturn:
file.AbandonReadBuffer()
L.Push(LNil)
L.Push(LString(err.Error()))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
func fileCloseAux(L *LState, file *lFile) int {
file.closed = true
var err error
if file.writer != nil {
if bwriter, ok := file.writer.(*bufio.Writer); ok {
if err = bwriter.Flush(); err != nil {
goto errreturn
}
}
}
file.AbandonReadBuffer()
switch file.Type() {
case lFileFile:
if err = file.fp.Close(); err != nil {
goto errreturn
}
L.Push(LTrue)
return 1
case lFileProcess:
if file.stdout != nil {
file.stdout.Close() // ignore errors
}
err = file.pp.Wait()
var exitStatus int // Initialised to zero value = 0
if err != nil {
if e2, ok := err.(*exec.ExitError); ok {
if s, ok := e2.Sys().(syscall.WaitStatus); ok {
exitStatus = s.ExitStatus()
} else {
err = errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
}
}
} else {
exitStatus = 0
}
L.Push(LNumber(exitStatus))
return 1
}
errreturn:
L.RaiseError(err.Error())
return 0
}
func fileFlushAux(L *LState, file *lFile) int {
if n := fileIsWritable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
if bwriter, ok := file.writer.(*bufio.Writer); ok {
if err := bwriter.Flush(); err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
}
L.Push(LTrue)
return 1
}
func fileReadAux(L *LState, file *lFile, idx int) int {
if n := fileIsReadable(L, file); n != 0 {
return n
}
errorIfFileIsClosed(L, file)
if L.GetTop() == idx-1 {
L.Push(LString("*l"))
}
var err error
top := L.GetTop()
for i := idx; i <= top; i++ {
switch lv := L.Get(i).(type) {
case LNumber:
size := int64(lv)
if size == 0 {
_, err = file.reader.ReadByte()
if err == io.EOF {
L.Push(LNil)
goto normalreturn
}
file.reader.UnreadByte()
}
var buf []byte
var iseof bool
buf, err, iseof = readBufioSize(file.reader, size)
if iseof {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
case LString:
options := L.CheckString(i)
if len(options) > 0 && options[0] != '*' {
L.ArgError(2, "invalid options:"+options)
}
for _, opt := range options[1:] {
switch opt {
case 'n':
var v LNumber
_, err = fmt.Fscanf(file.reader, LNumberScanFormat, &v)
if err == io.EOF {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(v)
case 'a':
var buf []byte
buf, err = ioutil.ReadAll(file.reader)
if err == io.EOF {
L.Push(emptyLString)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
case 'l':
var buf []byte
var iseof bool
buf, err, iseof = readBufioLine(file.reader)
if iseof {
L.Push(LNil)
goto normalreturn
}
if err != nil {
goto errreturn
}
L.Push(LString(string(buf)))
default:
L.ArgError(2, "invalid options:"+string(opt))
}
}
}
}
normalreturn:
return L.GetTop() - top
errreturn:
L.RaiseError(err.Error())
//L.Push(LNil)
//L.Push(LString(err.Error()))
return 2
}
var fileSeekOptions = []string{"set", "cur", "end"}
func fileSeek(L *LState) int {
file := checkFile(L)
if file.Type() != lFileFile {
L.Push(LNil)
L.Push(LString("can not seek a process."))
return 2
}
top := L.GetTop()
if top == 1 {
L.Push(LString("cur"))
L.Push(LNumber(0))
} else if top == 2 {
L.Push(LNumber(0))
}
var pos int64
var err error
err = file.AbandonReadBuffer()
if err != nil {
goto errreturn
}
pos, err = file.fp.Seek(L.CheckInt64(3), L.CheckOption(2, fileSeekOptions))
if err != nil {
goto errreturn
}
L.Push(LNumber(pos))
return 1
errreturn:
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
func fileWrite(L *LState) int {
return fileWriteAux(L, checkFile(L), 2)
}
func fileClose(L *LState) int {
return fileCloseAux(L, checkFile(L))
}
func fileFlush(L *LState) int {
return fileFlushAux(L, checkFile(L))
}
func fileLinesIter(L *LState) int {
var file *lFile
if ud, ok := L.Get(1).(*LUserData); ok {
file = ud.Value.(*lFile)
} else {
file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
}
buf, _, err := file.reader.ReadLine()
if err != nil {
if err == io.EOF {
L.Push(LNil)
return 1
}
L.RaiseError(err.Error())
}
L.Push(LString(string(buf)))
return 1
}
func fileLines(L *LState) int {
file := checkFile(L)
ud := L.CheckUserData(1)
if n := fileIsReadable(L, file); n != 0 {
return 0
}
L.Push(L.NewClosure(fileLinesIter, L.Get(UpvalueIndex(1)), ud))
return 1
}
func fileRead(L *LState) int {
return fileReadAux(L, checkFile(L), 2)
}
var filebufOptions = []string{"no", "full"}
func fileSetVBuf(L *LState) int {
var err error
var writer io.Writer
file := checkFile(L)
if n := fileIsWritable(L, file); n != 0 {
return n
}
switch filebufOptions[L.CheckOption(2, filebufOptions)] {
case "no":
switch file.Type() {
case lFileFile:
file.writer = file.fp
case lFileProcess:
file.writer, err = file.pp.StdinPipe()
if err != nil {
goto errreturn
}
}
case "full", "line": // TODO line buffer not supported
bufsize := L.OptInt(3, fileDefaultWriteBuffer)
switch file.Type() {
case lFileFile:
file.writer = bufio.NewWriterSize(file.fp, bufsize)
case lFileProcess:
writer, err = file.pp.StdinPipe()
if err != nil {
goto errreturn
}
file.writer = bufio.NewWriterSize(writer, bufsize)
}
}
L.Push(LTrue)
return 1
errreturn:
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
func ioInput(L *LState) int {
if L.GetTop() == 0 {
L.Push(fileDefIn(L))
return 1
}
switch lv := L.Get(1).(type) {
case LString:
file, err := newFile(L, nil, string(lv), os.O_RDONLY, 0600, false, true)
if err != nil {
L.RaiseError(err.Error())
}
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, file)
L.Push(file)
return 1
case *LUserData:
if _, ok := lv.Value.(*lFile); ok {
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, lv)
L.Push(lv)
return 1
}
}
L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
return 0
}
func ioClose(L *LState) int {
if L.GetTop() == 0 {
return fileCloseAux(L, fileDefOut(L).Value.(*lFile))
}
return fileClose(L)
}
func ioFlush(L *LState) int {
return fileFlushAux(L, fileDefOut(L).Value.(*lFile))
}
func ioLinesIter(L *LState) int {
var file *lFile
toclose := false
if ud, ok := L.Get(1).(*LUserData); ok {
file = ud.Value.(*lFile)
} else {
file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
toclose = true
}
buf, _, err := file.reader.ReadLine()
if err != nil {
if err == io.EOF {
if toclose {
fileCloseAux(L, file)
}
L.Push(LNil)
return 1
}
L.RaiseError(err.Error())
}
L.Push(LString(string(buf)))
return 1
}
func ioLines(L *LState) int {
if L.GetTop() == 0 {
L.Push(L.Get(UpvalueIndex(2)))
L.Push(fileDefIn(L))
return 2
}
path := L.CheckString(1)
ud, err := newFile(L, nil, path, os.O_RDONLY, os.FileMode(0600), false, true)
if err != nil {
return 0
}
L.Push(L.NewClosure(ioLinesIter, L.Get(UpvalueIndex(1)), ud))
return 1
}
var ioOpenOpions = []string{"r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "w+", "wb+", "a+", "ab+"}
func ioOpenFile(L *LState) int {
path := L.CheckString(1)
if L.GetTop() == 1 {
L.Push(LString("r"))
}
mode := os.O_RDONLY
perm := 0600
writable := true
readable := true
switch ioOpenOpions[L.CheckOption(2, ioOpenOpions)] {
case "r", "rb":
mode = os.O_RDONLY
writable = false
case "w", "wb":
mode = os.O_WRONLY | os.O_TRUNC | os.O_CREATE
readable = false
case "a", "ab":
mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
case "r+", "rb+":
mode = os.O_RDWR
case "w+", "wb+":
mode = os.O_RDWR | os.O_TRUNC | os.O_CREATE
case "a+", "ab+":
mode = os.O_APPEND | os.O_RDWR | os.O_CREATE
}
file, err := newFile(L, nil, path, mode, os.FileMode(perm), writable, readable)
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
return 3
}
L.Push(file)
return 1
}
var ioPopenOptions = []string{"r", "w"}
func ioPopen(L *LState) int {
cmd := L.CheckString(1)
if L.GetTop() == 1 {
L.Push(LString("r"))
} else if L.GetTop() > 1 && (L.Get(2)).Type() == LTNil {
L.SetTop(1)
L.Push(LString("r"))
}
var file *LUserData
var err error
switch ioPopenOptions[L.CheckOption(2, ioPopenOptions)] {
case "r":
file, err = newProcess(L, cmd, false, true)
case "w":
file, err = newProcess(L, cmd, true, false)
}
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
L.Push(file)
return 1
}
func ioRead(L *LState) int {
return fileReadAux(L, fileDefIn(L).Value.(*lFile), 1)
}
func ioType(L *LState) int {
ud, udok := L.Get(1).(*LUserData)
if !udok {
L.Push(LNil)
return 1
}
file, ok := ud.Value.(*lFile)
if !ok {
L.Push(LNil)
return 1
}
if file.closed {
L.Push(LString("closed file"))
return 1
}
L.Push(LString("file"))
return 1
}
func ioTmpFile(L *LState) int {
file, err := ioutil.TempFile("", "")
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
}
L.G.tempFiles = append(L.G.tempFiles, file)
ud, _ := newFile(L, file, "", 0, os.FileMode(0), true, true)
L.Push(ud)
return 1
}
func ioOutput(L *LState) int {
if L.GetTop() == 0 {
L.Push(fileDefOut(L))
return 1
}
switch lv := L.Get(1).(type) {
case LString:
file, err := newFile(L, nil, string(lv), os.O_WRONLY|os.O_CREATE, 0600, true, false)
if err != nil {
L.RaiseError(err.Error())
}
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, file)
L.Push(file)
return 1
case *LUserData:
if _, ok := lv.Value.(*lFile); ok {
L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, lv)
L.Push(lv)
return 1
}
}
L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
return 0
}
func ioWrite(L *LState) int {
return fileWriteAux(L, fileDefOut(L).Value.(*lFile), 1)
}
//

54
vendor/github.com/yuin/gopher-lua/linit.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
package lua
const (
// BaseLibName is here for consistency; the base functions have no namespace/library.
BaseLibName = ""
// LoadLibName is here for consistency; the loading system has no namespace/library.
LoadLibName = "package"
// TabLibName is the name of the table Library.
TabLibName = "table"
// IoLibName is the name of the io Library.
IoLibName = "io"
// OsLibName is the name of the os Library.
OsLibName = "os"
// StringLibName is the name of the string Library.
StringLibName = "string"
// MathLibName is the name of the math Library.
MathLibName = "math"
// DebugLibName is the name of the debug Library.
DebugLibName = "debug"
// ChannelLibName is the name of the channel Library.
ChannelLibName = "channel"
// CoroutineLibName is the name of the coroutine Library.
CoroutineLibName = "coroutine"
)
type luaLib struct {
libName string
libFunc LGFunction
}
var luaLibs = []luaLib{
luaLib{LoadLibName, OpenPackage},
luaLib{BaseLibName, OpenBase},
luaLib{TabLibName, OpenTable},
luaLib{IoLibName, OpenIo},
luaLib{OsLibName, OpenOs},
luaLib{StringLibName, OpenString},
luaLib{MathLibName, OpenMath},
luaLib{DebugLibName, OpenDebug},
luaLib{ChannelLibName, OpenChannel},
luaLib{CoroutineLibName, OpenCoroutine},
}
// OpenLibs loads the built-in libraries. It is equivalent to running OpenLoad,
// then OpenBase, then iterating over the other OpenXXX functions in any order.
func (ls *LState) OpenLibs() {
// NB: Map iteration order in Go is deliberately randomised, so must open Load/Base
// prior to iterating.
for _, lib := range luaLibs {
ls.Push(ls.NewFunction(lib.libFunc))
ls.Push(LString(lib.libName))
ls.Call(1, 0)
}
}

128
vendor/github.com/yuin/gopher-lua/loadlib.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
package lua
import (
"fmt"
"os"
"path/filepath"
"strings"
)
/* load lib {{{ */
var loLoaders = []LGFunction{loLoaderPreload, loLoaderLua}
func loGetPath(env string, defpath string) string {
path := os.Getenv(env)
if len(path) == 0 {
path = defpath
}
path = strings.Replace(path, ";;", ";"+defpath+";", -1)
if os.PathSeparator != '/' {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(err)
}
path = strings.Replace(path, "!", dir, -1)
}
return path
}
func loFindFile(L *LState, name, pname string) (string, string) {
name = strings.Replace(name, ".", string(os.PathSeparator), -1)
lv := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), pname)
path, ok := lv.(LString)
if !ok {
L.RaiseError("package.%s must be a string", pname)
}
messages := []string{}
for _, pattern := range strings.Split(string(path), ";") {
luapath := strings.Replace(pattern, "?", name, -1)
if _, err := os.Stat(luapath); err == nil {
return luapath, ""
} else {
messages = append(messages, err.Error())
}
}
return "", strings.Join(messages, "\n\t")
}
func OpenPackage(L *LState) int {
packagemod := L.RegisterModule(LoadLibName, loFuncs)
L.SetField(packagemod, "preload", L.NewTable())
loaders := L.CreateTable(len(loLoaders), 0)
for i, loader := range loLoaders {
L.RawSetInt(loaders, i+1, L.NewFunction(loader))
}
L.SetField(packagemod, "loaders", loaders)
L.SetField(L.Get(RegistryIndex), "_LOADERS", loaders)
loaded := L.NewTable()
L.SetField(packagemod, "loaded", loaded)
L.SetField(L.Get(RegistryIndex), "_LOADED", loaded)
L.SetField(packagemod, "path", LString(loGetPath(LuaPath, LuaPathDefault)))
L.SetField(packagemod, "cpath", emptyLString)
L.SetField(packagemod, "config", LString(LuaDirSep+"\n"+LuaPathSep+
"\n"+LuaPathMark+"\n"+LuaExecDir+"\n"+LuaIgMark+"\n"))
L.Push(packagemod)
return 1
}
var loFuncs = map[string]LGFunction{
"loadlib": loLoadLib,
"seeall": loSeeAll,
}
func loLoaderPreload(L *LState) int {
name := L.CheckString(1)
preload := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), "preload")
if _, ok := preload.(*LTable); !ok {
L.RaiseError("package.preload must be a table")
}
lv := L.GetField(preload, name)
if lv == LNil {
L.Push(LString(fmt.Sprintf("no field package.preload['%s']", name)))
return 1
}
L.Push(lv)
return 1
}
func loLoaderLua(L *LState) int {
name := L.CheckString(1)
path, msg := loFindFile(L, name, "path")
if len(path) == 0 {
L.Push(LString(msg))
return 1
}
fn, err1 := L.LoadFile(path)
if err1 != nil {
L.RaiseError(err1.Error())
}
L.Push(fn)
return 1
}
func loLoadLib(L *LState) int {
L.RaiseError("loadlib is not supported")
return 0
}
func loSeeAll(L *LState) int {
mod := L.CheckTable(1)
mt := L.GetMetatable(mod)
if mt == LNil {
mt = L.CreateTable(0, 1)
L.SetMetatable(mod, mt)
}
L.SetField(mt, "__index", L.Get(GlobalsIndex))
return 0
}
/* }}} */
//

231
vendor/github.com/yuin/gopher-lua/mathlib.go generated vendored Normal file
View File

@@ -0,0 +1,231 @@
package lua
import (
"math"
"math/rand"
)
func OpenMath(L *LState) int {
mod := L.RegisterModule(MathLibName, mathFuncs).(*LTable)
mod.RawSetString("pi", LNumber(math.Pi))
mod.RawSetString("huge", LNumber(math.MaxFloat64))
L.Push(mod)
return 1
}
var mathFuncs = map[string]LGFunction{
"abs": mathAbs,
"acos": mathAcos,
"asin": mathAsin,
"atan": mathAtan,
"atan2": mathAtan2,
"ceil": mathCeil,
"cos": mathCos,
"cosh": mathCosh,
"deg": mathDeg,
"exp": mathExp,
"floor": mathFloor,
"fmod": mathFmod,
"frexp": mathFrexp,
"ldexp": mathLdexp,
"log": mathLog,
"log10": mathLog10,
"max": mathMax,
"min": mathMin,
"mod": mathMod,
"modf": mathModf,
"pow": mathPow,
"rad": mathRad,
"random": mathRandom,
"randomseed": mathRandomseed,
"sin": mathSin,
"sinh": mathSinh,
"sqrt": mathSqrt,
"tan": mathTan,
"tanh": mathTanh,
}
func mathAbs(L *LState) int {
L.Push(LNumber(math.Abs(float64(L.CheckNumber(1)))))
return 1
}
func mathAcos(L *LState) int {
L.Push(LNumber(math.Acos(float64(L.CheckNumber(1)))))
return 1
}
func mathAsin(L *LState) int {
L.Push(LNumber(math.Asin(float64(L.CheckNumber(1)))))
return 1
}
func mathAtan(L *LState) int {
L.Push(LNumber(math.Atan(float64(L.CheckNumber(1)))))
return 1
}
func mathAtan2(L *LState) int {
L.Push(LNumber(math.Atan2(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathCeil(L *LState) int {
L.Push(LNumber(math.Ceil(float64(L.CheckNumber(1)))))
return 1
}
func mathCos(L *LState) int {
L.Push(LNumber(math.Cos(float64(L.CheckNumber(1)))))
return 1
}
func mathCosh(L *LState) int {
L.Push(LNumber(math.Cosh(float64(L.CheckNumber(1)))))
return 1
}
func mathDeg(L *LState) int {
L.Push(LNumber(float64(L.CheckNumber(1)) * 180 / math.Pi))
return 1
}
func mathExp(L *LState) int {
L.Push(LNumber(math.Exp(float64(L.CheckNumber(1)))))
return 1
}
func mathFloor(L *LState) int {
L.Push(LNumber(math.Floor(float64(L.CheckNumber(1)))))
return 1
}
func mathFmod(L *LState) int {
L.Push(LNumber(math.Mod(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathFrexp(L *LState) int {
v1, v2 := math.Frexp(float64(L.CheckNumber(1)))
L.Push(LNumber(v1))
L.Push(LNumber(v2))
return 2
}
func mathLdexp(L *LState) int {
L.Push(LNumber(math.Ldexp(float64(L.CheckNumber(1)), L.CheckInt(2))))
return 1
}
func mathLog(L *LState) int {
L.Push(LNumber(math.Log(float64(L.CheckNumber(1)))))
return 1
}
func mathLog10(L *LState) int {
L.Push(LNumber(math.Log10(float64(L.CheckNumber(1)))))
return 1
}
func mathMax(L *LState) int {
if L.GetTop() == 0 {
L.RaiseError("wrong number of arguments")
}
max := L.CheckNumber(1)
top := L.GetTop()
for i := 2; i <= top; i++ {
v := L.CheckNumber(i)
if v > max {
max = v
}
}
L.Push(max)
return 1
}
func mathMin(L *LState) int {
if L.GetTop() == 0 {
L.RaiseError("wrong number of arguments")
}
min := L.CheckNumber(1)
top := L.GetTop()
for i := 2; i <= top; i++ {
v := L.CheckNumber(i)
if v < min {
min = v
}
}
L.Push(min)
return 1
}
func mathMod(L *LState) int {
lhs := L.CheckNumber(1)
rhs := L.CheckNumber(2)
L.Push(luaModulo(lhs, rhs))
return 1
}
func mathModf(L *LState) int {
v1, v2 := math.Modf(float64(L.CheckNumber(1)))
L.Push(LNumber(v1))
L.Push(LNumber(v2))
return 2
}
func mathPow(L *LState) int {
L.Push(LNumber(math.Pow(float64(L.CheckNumber(1)), float64(L.CheckNumber(2)))))
return 1
}
func mathRad(L *LState) int {
L.Push(LNumber(float64(L.CheckNumber(1)) * math.Pi / 180))
return 1
}
func mathRandom(L *LState) int {
switch L.GetTop() {
case 0:
L.Push(LNumber(rand.Float64()))
case 1:
n := L.CheckInt(1)
L.Push(LNumber(rand.Intn(n) + 1))
default:
min := L.CheckInt(1)
max := L.CheckInt(2) + 1
L.Push(LNumber(rand.Intn(max-min) + min))
}
return 1
}
func mathRandomseed(L *LState) int {
rand.Seed(L.CheckInt64(1))
return 0
}
func mathSin(L *LState) int {
L.Push(LNumber(math.Sin(float64(L.CheckNumber(1)))))
return 1
}
func mathSinh(L *LState) int {
L.Push(LNumber(math.Sinh(float64(L.CheckNumber(1)))))
return 1
}
func mathSqrt(L *LState) int {
L.Push(LNumber(math.Sqrt(float64(L.CheckNumber(1)))))
return 1
}
func mathTan(L *LState) int {
L.Push(LNumber(math.Tan(float64(L.CheckNumber(1)))))
return 1
}
func mathTanh(L *LState) int {
L.Push(LNumber(math.Tanh(float64(L.CheckNumber(1)))))
return 1
}
//

371
vendor/github.com/yuin/gopher-lua/opcode.go generated vendored Normal file
View File

@@ -0,0 +1,371 @@
package lua
import (
"fmt"
)
/*
gopherlua uses Lua 5.1.4's opcodes.
Lua 5.1.4 opcodes layout:
instruction = 32bit(fixed length)
+---------------------------------------------+
|0-5(6bits)|6-13(8bit)|14-22(9bit)|23-31(9bit)|
|==========+==========+===========+===========|
| opcode | A | C | B |
|----------+----------+-----------+-----------|
| opcode | A | Bx(unsigned) |
|----------+----------+-----------+-----------|
| opcode | A | sBx(signed) |
+---------------------------------------------+
*/
const opInvalidInstruction = ^uint32(0)
const opSizeCode = 6
const opSizeA = 8
const opSizeB = 9
const opSizeC = 9
const opSizeBx = 18
const opSizesBx = 18
const opMaxArgsA = (1 << opSizeA) - 1
const opMaxArgsB = (1 << opSizeB) - 1
const opMaxArgsC = (1 << opSizeC) - 1
const opMaxArgBx = (1 << opSizeBx) - 1
const opMaxArgSbx = opMaxArgBx >> 1
const (
OP_MOVE int = iota /* A B R(A) := R(B) */
OP_MOVEN /* A B R(A) := R(B); followed by R(C) MOVE ops */
OP_LOADK /* A Bx R(A) := Kst(Bx) */
OP_LOADBOOL /* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL /* A B R(A) := ... := R(B) := nil */
OP_GETUPVAL /* A B R(A) := UpValue[B] */
OP_GETGLOBAL /* A Bx R(A) := Gbl[Kst(Bx)] */
OP_GETTABLE /* A B C R(A) := R(B)[RK(C)] */
OP_GETTABLEKS /* A B C R(A) := R(B)[RK(C)] ; RK(C) is constant string */
OP_SETGLOBAL /* A Bx Gbl[Kst(Bx)] := R(A) */
OP_SETUPVAL /* A B UpValue[B] := R(A) */
OP_SETTABLE /* A B C R(A)[RK(B)] := RK(C) */
OP_SETTABLEKS /* A B C R(A)[RK(B)] := RK(C) ; RK(B) is constant string */
OP_NEWTABLE /* A B C R(A) := {} (size = BC) */
OP_SELF /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD /* A B C R(A) := RK(B) + RK(C) */
OP_SUB /* A B C R(A) := RK(B) - RK(C) */
OP_MUL /* A B C R(A) := RK(B) * RK(C) */
OP_DIV /* A B C R(A) := RK(B) / RK(C) */
OP_MOD /* A B C R(A) := RK(B) % RK(C) */
OP_POW /* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM /* A B R(A) := -R(B) */
OP_NOT /* A B R(A) := not R(B) */
OP_LEN /* A B R(A) := length of R(B) */
OP_CONCAT /* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP /* sBx pc+=sBx */
OP_EQ /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST /* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL /* A B C R(A) ... R(A+C-2) := R(A)(R(A+1) ... R(A+B-1)) */
OP_TAILCALL /* A B C return R(A)(R(A+1) ... R(A+B-1)) */
OP_RETURN /* A B return R(A) ... R(A+B-2) (see note) */
OP_FORLOOP /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP /* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORLOOP /* A C R(A+3) ... R(A+3+C) := R(A)(R(A+1) R(A+2));
if R(A+3) ~= nil then { pc++; R(A+2)=R(A+3); } */
OP_SETLIST /* A B C R(A)[(C-1)*FPF+i] := R(A+i) 1 <= i <= B */
OP_CLOSE /* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE /* A Bx R(A) := closure(KPROTO[Bx] R(A) ... R(A+n)) */
OP_VARARG /* A B R(A) R(A+1) ... R(A+B-1) = vararg */
OP_NOP /* NOP */
)
const opCodeMax = OP_NOP
type opArgMode int
const (
opArgModeN opArgMode = iota
opArgModeU
opArgModeR
opArgModeK
)
type opType int
const (
opTypeABC = iota
opTypeABx
opTypeASbx
)
type opProp struct {
Name string
IsTest bool
SetRegA bool
ModeArgB opArgMode
ModeArgC opArgMode
Type opType
}
var opProps = []opProp{
opProp{"MOVE", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"MOVEN", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"LOADK", false, true, opArgModeK, opArgModeN, opTypeABx},
opProp{"LOADBOOL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"LOADNIL", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"GETUPVAL", false, true, opArgModeU, opArgModeN, opTypeABC},
opProp{"GETGLOBAL", false, true, opArgModeK, opArgModeN, opTypeABx},
opProp{"GETTABLE", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"GETTABLEKS", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"SETGLOBAL", false, false, opArgModeK, opArgModeN, opTypeABx},
opProp{"SETUPVAL", false, false, opArgModeU, opArgModeN, opTypeABC},
opProp{"SETTABLE", false, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"SETTABLEKS", false, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"NEWTABLE", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"SELF", false, true, opArgModeR, opArgModeK, opTypeABC},
opProp{"ADD", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"SUB", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"MUL", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"DIV", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"MOD", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"POW", false, true, opArgModeK, opArgModeK, opTypeABC},
opProp{"UNM", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"NOT", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"LEN", false, true, opArgModeR, opArgModeN, opTypeABC},
opProp{"CONCAT", false, true, opArgModeR, opArgModeR, opTypeABC},
opProp{"JMP", false, false, opArgModeR, opArgModeN, opTypeASbx},
opProp{"EQ", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"LT", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"LE", true, false, opArgModeK, opArgModeK, opTypeABC},
opProp{"TEST", true, true, opArgModeR, opArgModeU, opTypeABC},
opProp{"TESTSET", true, true, opArgModeR, opArgModeU, opTypeABC},
opProp{"CALL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"TAILCALL", false, true, opArgModeU, opArgModeU, opTypeABC},
opProp{"RETURN", false, false, opArgModeU, opArgModeN, opTypeABC},
opProp{"FORLOOP", false, true, opArgModeR, opArgModeN, opTypeASbx},
opProp{"FORPREP", false, true, opArgModeR, opArgModeN, opTypeASbx},
opProp{"TFORLOOP", true, false, opArgModeN, opArgModeU, opTypeABC},
opProp{"SETLIST", false, false, opArgModeU, opArgModeU, opTypeABC},
opProp{"CLOSE", false, false, opArgModeN, opArgModeN, opTypeABC},
opProp{"CLOSURE", false, true, opArgModeU, opArgModeN, opTypeABx},
opProp{"VARARG", false, true, opArgModeU, opArgModeN, opTypeABC},
opProp{"NOP", false, false, opArgModeR, opArgModeN, opTypeASbx},
}
func opGetOpCode(inst uint32) int {
return int(inst >> 26)
}
func opSetOpCode(inst *uint32, opcode int) {
*inst = (*inst & 0x3ffffff) | uint32(opcode<<26)
}
func opGetArgA(inst uint32) int {
return int(inst>>18) & 0xff
}
func opSetArgA(inst *uint32, arg int) {
*inst = (*inst & 0xfc03ffff) | uint32((arg&0xff)<<18)
}
func opGetArgB(inst uint32) int {
return int(inst & 0x1ff)
}
func opSetArgB(inst *uint32, arg int) {
*inst = (*inst & 0xfffffe00) | uint32(arg&0x1ff)
}
func opGetArgC(inst uint32) int {
return int(inst>>9) & 0x1ff
}
func opSetArgC(inst *uint32, arg int) {
*inst = (*inst & 0xfffc01ff) | uint32((arg&0x1ff)<<9)
}
func opGetArgBx(inst uint32) int {
return int(inst & 0x3ffff)
}
func opSetArgBx(inst *uint32, arg int) {
*inst = (*inst & 0xfffc0000) | uint32(arg&0x3ffff)
}
func opGetArgSbx(inst uint32) int {
return opGetArgBx(inst) - opMaxArgSbx
}
func opSetArgSbx(inst *uint32, arg int) {
opSetArgBx(inst, arg+opMaxArgSbx)
}
func opCreateABC(op int, a int, b int, c int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgB(&inst, b)
opSetArgC(&inst, c)
return inst
}
func opCreateABx(op int, a int, bx int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgBx(&inst, bx)
return inst
}
func opCreateASbx(op int, a int, sbx int) uint32 {
var inst uint32 = 0
opSetOpCode(&inst, op)
opSetArgA(&inst, a)
opSetArgSbx(&inst, sbx)
return inst
}
const opBitRk = 1 << (opSizeB - 1)
const opMaxIndexRk = opBitRk - 1
func opIsK(value int) bool {
return bool((value & opBitRk) != 0)
}
func opIndexK(value int) int {
return value & ^opBitRk
}
func opRkAsk(value int) int {
return value | opBitRk
}
func opToString(inst uint32) string {
op := opGetOpCode(inst)
if op > opCodeMax {
return ""
}
prop := &(opProps[op])
arga := opGetArgA(inst)
argb := opGetArgB(inst)
argc := opGetArgC(inst)
argbx := opGetArgBx(inst)
argsbx := opGetArgSbx(inst)
buf := ""
switch prop.Type {
case opTypeABC:
buf = fmt.Sprintf("%s | %d, %d, %d", prop.Name, arga, argb, argc)
case opTypeABx:
buf = fmt.Sprintf("%s | %d, %d", prop.Name, arga, argbx)
case opTypeASbx:
buf = fmt.Sprintf("%s | %d, %d", prop.Name, arga, argsbx)
}
switch op {
case OP_MOVE:
buf += fmt.Sprintf("; R(%v) := R(%v)", arga, argb)
case OP_MOVEN:
buf += fmt.Sprintf("; R(%v) := R(%v); followed by %v MOVE ops", arga, argb, argc)
case OP_LOADK:
buf += fmt.Sprintf("; R(%v) := Kst(%v)", arga, argbx)
case OP_LOADBOOL:
buf += fmt.Sprintf("; R(%v) := (Bool)%v; if (%v) pc++", arga, argb, argc)
case OP_LOADNIL:
buf += fmt.Sprintf("; R(%v) := ... := R(%v) := nil", arga, argb)
case OP_GETUPVAL:
buf += fmt.Sprintf("; R(%v) := UpValue[%v]", arga, argb)
case OP_GETGLOBAL:
buf += fmt.Sprintf("; R(%v) := Gbl[Kst(%v)]", arga, argbx)
case OP_GETTABLE:
buf += fmt.Sprintf("; R(%v) := R(%v)[RK(%v)]", arga, argb, argc)
case OP_GETTABLEKS:
buf += fmt.Sprintf("; R(%v) := R(%v)[RK(%v)] ; RK(%v) is constant string", arga, argb, argc, argc)
case OP_SETGLOBAL:
buf += fmt.Sprintf("; Gbl[Kst(%v)] := R(%v)", argbx, arga)
case OP_SETUPVAL:
buf += fmt.Sprintf("; UpValue[%v] := R(%v)", argb, arga)
case OP_SETTABLE:
buf += fmt.Sprintf("; R(%v)[RK(%v)] := RK(%v)", arga, argb, argc)
case OP_SETTABLEKS:
buf += fmt.Sprintf("; R(%v)[RK(%v)] := RK(%v) ; RK(%v) is constant string", arga, argb, argc, argb)
case OP_NEWTABLE:
buf += fmt.Sprintf("; R(%v) := {} (size = BC)", arga)
case OP_SELF:
buf += fmt.Sprintf("; R(%v+1) := R(%v); R(%v) := R(%v)[RK(%v)]", arga, argb, arga, argb, argc)
case OP_ADD:
buf += fmt.Sprintf("; R(%v) := RK(%v) + RK(%v)", arga, argb, argc)
case OP_SUB:
buf += fmt.Sprintf("; R(%v) := RK(%v) - RK(%v)", arga, argb, argc)
case OP_MUL:
buf += fmt.Sprintf("; R(%v) := RK(%v) * RK(%v)", arga, argb, argc)
case OP_DIV:
buf += fmt.Sprintf("; R(%v) := RK(%v) / RK(%v)", arga, argb, argc)
case OP_MOD:
buf += fmt.Sprintf("; R(%v) := RK(%v) %% RK(%v)", arga, argb, argc)
case OP_POW:
buf += fmt.Sprintf("; R(%v) := RK(%v) ^ RK(%v)", arga, argb, argc)
case OP_UNM:
buf += fmt.Sprintf("; R(%v) := -R(%v)", arga, argb)
case OP_NOT:
buf += fmt.Sprintf("; R(%v) := not R(%v)", arga, argb)
case OP_LEN:
buf += fmt.Sprintf("; R(%v) := length of R(%v)", arga, argb)
case OP_CONCAT:
buf += fmt.Sprintf("; R(%v) := R(%v).. ... ..R(%v)", arga, argb, argc)
case OP_JMP:
buf += fmt.Sprintf("; pc+=%v", argsbx)
case OP_EQ:
buf += fmt.Sprintf("; if ((RK(%v) == RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_LT:
buf += fmt.Sprintf("; if ((RK(%v) < RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_LE:
buf += fmt.Sprintf("; if ((RK(%v) <= RK(%v)) ~= %v) then pc++", argb, argc, arga)
case OP_TEST:
buf += fmt.Sprintf("; if not (R(%v) <=> %v) then pc++", arga, argc)
case OP_TESTSET:
buf += fmt.Sprintf("; if (R(%v) <=> %v) then R(%v) := R(%v) else pc++", argb, argc, arga, argb)
case OP_CALL:
buf += fmt.Sprintf("; R(%v) ... R(%v+%v-2) := R(%v)(R(%v+1) ... R(%v+%v-1))", arga, arga, argc, arga, arga, arga, argb)
case OP_TAILCALL:
buf += fmt.Sprintf("; return R(%v)(R(%v+1) ... R(%v+%v-1))", arga, arga, arga, argb)
case OP_RETURN:
buf += fmt.Sprintf("; return R(%v) ... R(%v+%v-2)", arga, arga, argb)
case OP_FORLOOP:
buf += fmt.Sprintf("; R(%v)+=R(%v+2); if R(%v) <?= R(%v+1) then { pc+=%v; R(%v+3)=R(%v) }", arga, arga, arga, arga, argsbx, arga, arga)
case OP_FORPREP:
buf += fmt.Sprintf("; R(%v)-=R(%v+2); pc+=%v", arga, arga, argsbx)
case OP_TFORLOOP:
buf += fmt.Sprintf("; R(%v+3) ... R(%v+3+%v) := R(%v)(R(%v+1) R(%v+2)); if R(%v+3) ~= nil then { pc++; R(%v+2)=R(%v+3); }", arga, arga, argc, arga, arga, arga, arga, arga, arga)
case OP_SETLIST:
buf += fmt.Sprintf("; R(%v)[(%v-1)*FPF+i] := R(%v+i) 1 <= i <= %v", arga, argc, arga, argb)
case OP_CLOSE:
buf += fmt.Sprintf("; close all variables in the stack up to (>=) R(%v)", arga)
case OP_CLOSURE:
buf += fmt.Sprintf("; R(%v) := closure(KPROTO[%v] R(%v) ... R(%v+n))", arga, argbx, arga, arga)
case OP_VARARG:
buf += fmt.Sprintf("; R(%v) R(%v+1) ... R(%v+%v-1) = vararg", arga, arga, arga, argb)
case OP_NOP:
/* nothing to do */
}
return buf
}

236
vendor/github.com/yuin/gopher-lua/oslib.go generated vendored Normal file
View File

@@ -0,0 +1,236 @@
package lua
import (
"io/ioutil"
"os"
"strings"
"time"
)
var startedAt time.Time
func init() {
startedAt = time.Now()
}
func getIntField(L *LState, tb *LTable, key string, v int) int {
ret := tb.RawGetString(key)
switch lv := ret.(type) {
case LNumber:
return int(lv)
case LString:
slv := string(lv)
slv = strings.TrimLeft(slv, " ")
if strings.HasPrefix(slv, "0") && !strings.HasPrefix(slv, "0x") && !strings.HasPrefix(slv, "0X") {
// Standard lua interpreter only support decimal and hexadecimal
slv = strings.TrimLeft(slv, "0")
if slv == "" {
return 0
}
}
if num, err := parseNumber(slv); err == nil {
return int(num)
}
default:
return v
}
return v
}
func getBoolField(L *LState, tb *LTable, key string, v bool) bool {
ret := tb.RawGetString(key)
if lb, ok := ret.(LBool); ok {
return bool(lb)
}
return v
}
func OpenOs(L *LState) int {
osmod := L.RegisterModule(OsLibName, osFuncs)
L.Push(osmod)
return 1
}
var osFuncs = map[string]LGFunction{
"clock": osClock,
"difftime": osDiffTime,
"execute": osExecute,
"exit": osExit,
"date": osDate,
"getenv": osGetEnv,
"remove": osRemove,
"rename": osRename,
"setenv": osSetEnv,
"setlocale": osSetLocale,
"time": osTime,
"tmpname": osTmpname,
}
func osClock(L *LState) int {
L.Push(LNumber(float64(time.Now().Sub(startedAt)) / float64(time.Second)))
return 1
}
func osDiffTime(L *LState) int {
L.Push(LNumber(L.CheckInt64(1) - L.CheckInt64(2)))
return 1
}
func osExecute(L *LState) int {
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
cmd, args := popenArgs(L.CheckString(1))
args = append([]string{cmd}, args...)
process, err := os.StartProcess(cmd, args, &procAttr)
if err != nil {
L.Push(LNumber(1))
return 1
}
ps, err := process.Wait()
if err != nil || !ps.Success() {
L.Push(LNumber(1))
return 1
}
L.Push(LNumber(0))
return 1
}
func osExit(L *LState) int {
L.Close()
os.Exit(L.OptInt(1, 0))
return 1
}
func osDate(L *LState) int {
t := time.Now()
isUTC := false
cfmt := "%c"
if L.GetTop() >= 1 {
cfmt = L.CheckString(1)
if strings.HasPrefix(cfmt, "!") {
cfmt = strings.TrimLeft(cfmt, "!")
isUTC = true
}
if L.GetTop() >= 2 {
t = time.Unix(L.CheckInt64(2), 0)
}
if isUTC {
t = t.UTC()
}
if strings.HasPrefix(cfmt, "*t") {
ret := L.NewTable()
ret.RawSetString("year", LNumber(t.Year()))
ret.RawSetString("month", LNumber(t.Month()))
ret.RawSetString("day", LNumber(t.Day()))
ret.RawSetString("hour", LNumber(t.Hour()))
ret.RawSetString("min", LNumber(t.Minute()))
ret.RawSetString("sec", LNumber(t.Second()))
ret.RawSetString("wday", LNumber(t.Weekday()+1))
// TODO yday & dst
ret.RawSetString("yday", LNumber(0))
ret.RawSetString("isdst", LFalse)
L.Push(ret)
return 1
}
}
L.Push(LString(strftime(t, cfmt)))
return 1
}
func osGetEnv(L *LState) int {
v := os.Getenv(L.CheckString(1))
if len(v) == 0 {
L.Push(LNil)
} else {
L.Push(LString(v))
}
return 1
}
func osRemove(L *LState) int {
err := os.Remove(L.CheckString(1))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osRename(L *LState) int {
err := os.Rename(L.CheckString(1), L.CheckString(2))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osSetLocale(L *LState) int {
// setlocale is not supported
L.Push(LFalse)
return 1
}
func osSetEnv(L *LState) int {
err := os.Setenv(L.CheckString(1), L.CheckString(2))
if err != nil {
L.Push(LNil)
L.Push(LString(err.Error()))
return 2
} else {
L.Push(LTrue)
return 1
}
}
func osTime(L *LState) int {
if L.GetTop() == 0 {
L.Push(LNumber(time.Now().Unix()))
} else {
lv := L.CheckAny(1)
if lv == LNil {
L.Push(LNumber(time.Now().Unix()))
} else {
tbl, ok := lv.(*LTable)
if !ok {
L.TypeError(1, LTTable)
}
sec := getIntField(L, tbl, "sec", 0)
min := getIntField(L, tbl, "min", 0)
hour := getIntField(L, tbl, "hour", 12)
day := getIntField(L, tbl, "day", -1)
month := getIntField(L, tbl, "month", -1)
year := getIntField(L, tbl, "year", -1)
isdst := getBoolField(L, tbl, "isdst", false)
t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local)
// TODO dst
if false {
print(isdst)
}
L.Push(LNumber(t.Unix()))
}
}
return 1
}
func osTmpname(L *LState) int {
file, err := ioutil.TempFile("", "")
if err != nil {
L.RaiseError("unable to generate a unique filename")
}
file.Close()
os.Remove(file.Name()) // ignore errors
L.Push(LString(file.Name()))
return 1
}
//

7
vendor/github.com/yuin/gopher-lua/package.go generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// GopherLua: VM and compiler for Lua in Go
package lua
const PackageName = "GopherLua"
const PackageVersion = "0.1"
const PackageAuthors = "Yusuke Inuzuka"
const PackageCopyRight = PackageName + " " + PackageVersion + " Copyright (C) 2015 -2017 " + PackageAuthors

7
vendor/github.com/yuin/gopher-lua/parse/Makefile generated vendored Normal file
View File

@@ -0,0 +1,7 @@
all : parser.go
parser.go : parser.go.y
goyacc -o $@ parser.go.y; [ -f y.output ] && ( rm -f y.output )
clean:
rm -f parser.go

549
vendor/github.com/yuin/gopher-lua/parse/lexer.go generated vendored Normal file
View File

@@ -0,0 +1,549 @@
package parse
import (
"bufio"
"bytes"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"github.com/yuin/gopher-lua/ast"
)
const EOF = -1
const whitespace1 = 1<<'\t' | 1<<' '
const whitespace2 = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '
type Error struct {
Pos ast.Position
Message string
Token string
}
func (e *Error) Error() string {
pos := e.Pos
if pos.Line == EOF {
return fmt.Sprintf("%v at EOF: %s\n", pos.Source, e.Message)
} else {
return fmt.Sprintf("%v line:%d(column:%d) near '%v': %s\n", pos.Source, pos.Line, pos.Column, e.Token, e.Message)
}
}
func writeChar(buf *bytes.Buffer, c int) { buf.WriteByte(byte(c)) }
func isDecimal(ch int) bool { return '0' <= ch && ch <= '9' }
func isIdent(ch int, pos int) bool {
return ch == '_' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z' || isDecimal(ch) && pos > 0
}
func isDigit(ch int) bool {
return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F'
}
type Scanner struct {
Pos ast.Position
reader *bufio.Reader
}
func NewScanner(reader io.Reader, source string) *Scanner {
return &Scanner{
Pos: ast.Position{
Source: source,
Line: 1,
Column: 0,
},
reader: bufio.NewReaderSize(reader, 4096),
}
}
func (sc *Scanner) Error(tok string, msg string) *Error { return &Error{sc.Pos, msg, tok} }
func (sc *Scanner) TokenError(tok ast.Token, msg string) *Error { return &Error{tok.Pos, msg, tok.Str} }
func (sc *Scanner) readNext() int {
ch, err := sc.reader.ReadByte()
if err == io.EOF {
return EOF
}
return int(ch)
}
func (sc *Scanner) Newline(ch int) {
if ch < 0 {
return
}
sc.Pos.Line += 1
sc.Pos.Column = 0
next := sc.Peek()
if ch == '\n' && next == '\r' || ch == '\r' && next == '\n' {
sc.reader.ReadByte()
}
}
func (sc *Scanner) Next() int {
ch := sc.readNext()
switch ch {
case '\n', '\r':
sc.Newline(ch)
ch = int('\n')
case EOF:
sc.Pos.Line = EOF
sc.Pos.Column = 0
default:
sc.Pos.Column++
}
return ch
}
func (sc *Scanner) Peek() int {
ch := sc.readNext()
if ch != EOF {
sc.reader.UnreadByte()
}
return ch
}
func (sc *Scanner) skipWhiteSpace(whitespace int64) int {
ch := sc.Next()
for ; whitespace&(1<<uint(ch)) != 0; ch = sc.Next() {
}
return ch
}
func (sc *Scanner) skipComments(ch int) error {
// multiline comment
if sc.Peek() == '[' {
ch = sc.Next()
if sc.Peek() == '[' || sc.Peek() == '=' {
var buf bytes.Buffer
if err := sc.scanMultilineString(sc.Next(), &buf); err != nil {
return sc.Error(buf.String(), "invalid multiline comment")
}
return nil
}
}
for {
if ch == '\n' || ch == '\r' || ch < 0 {
break
}
ch = sc.Next()
}
return nil
}
func (sc *Scanner) scanIdent(ch int, buf *bytes.Buffer) error {
writeChar(buf, ch)
for isIdent(sc.Peek(), 1) {
writeChar(buf, sc.Next())
}
return nil
}
func (sc *Scanner) scanDecimal(ch int, buf *bytes.Buffer) error {
writeChar(buf, ch)
for isDecimal(sc.Peek()) {
writeChar(buf, sc.Next())
}
return nil
}
func (sc *Scanner) scanNumber(ch int, buf *bytes.Buffer) error {
if ch == '0' { // octal
if sc.Peek() == 'x' || sc.Peek() == 'X' {
writeChar(buf, ch)
writeChar(buf, sc.Next())
hasvalue := false
for isDigit(sc.Peek()) {
writeChar(buf, sc.Next())
hasvalue = true
}
if !hasvalue {
return sc.Error(buf.String(), "illegal hexadecimal number")
}
return nil
} else if sc.Peek() != '.' && isDecimal(sc.Peek()) {
ch = sc.Next()
}
}
sc.scanDecimal(ch, buf)
if sc.Peek() == '.' {
sc.scanDecimal(sc.Next(), buf)
}
if ch = sc.Peek(); ch == 'e' || ch == 'E' {
writeChar(buf, sc.Next())
if ch = sc.Peek(); ch == '-' || ch == '+' {
writeChar(buf, sc.Next())
}
sc.scanDecimal(sc.Next(), buf)
}
return nil
}
func (sc *Scanner) scanString(quote int, buf *bytes.Buffer) error {
ch := sc.Next()
for ch != quote {
if ch == '\n' || ch == '\r' || ch < 0 {
return sc.Error(buf.String(), "unterminated string")
}
if ch == '\\' {
if err := sc.scanEscape(ch, buf); err != nil {
return err
}
} else {
writeChar(buf, ch)
}
ch = sc.Next()
}
return nil
}
func (sc *Scanner) scanEscape(ch int, buf *bytes.Buffer) error {
ch = sc.Next()
switch ch {
case 'a':
buf.WriteByte('\a')
case 'b':
buf.WriteByte('\b')
case 'f':
buf.WriteByte('\f')
case 'n':
buf.WriteByte('\n')
case 'r':
buf.WriteByte('\r')
case 't':
buf.WriteByte('\t')
case 'v':
buf.WriteByte('\v')
case '\\':
buf.WriteByte('\\')
case '"':
buf.WriteByte('"')
case '\'':
buf.WriteByte('\'')
case '\n':
buf.WriteByte('\n')
case '\r':
buf.WriteByte('\n')
sc.Newline('\r')
default:
if '0' <= ch && ch <= '9' {
bytes := []byte{byte(ch)}
for i := 0; i < 2 && isDecimal(sc.Peek()); i++ {
bytes = append(bytes, byte(sc.Next()))
}
val, _ := strconv.ParseInt(string(bytes), 10, 32)
writeChar(buf, int(val))
} else {
writeChar(buf, ch)
}
}
return nil
}
func (sc *Scanner) countSep(ch int) (int, int) {
count := 0
for ; ch == '='; count = count + 1 {
ch = sc.Next()
}
return count, ch
}
func (sc *Scanner) scanMultilineString(ch int, buf *bytes.Buffer) error {
var count1, count2 int
count1, ch = sc.countSep(ch)
if ch != '[' {
return sc.Error(string(rune(ch)), "invalid multiline string")
}
ch = sc.Next()
if ch == '\n' || ch == '\r' {
ch = sc.Next()
}
for {
if ch < 0 {
return sc.Error(buf.String(), "unterminated multiline string")
} else if ch == ']' {
count2, ch = sc.countSep(sc.Next())
if count1 == count2 && ch == ']' {
goto finally
}
buf.WriteByte(']')
buf.WriteString(strings.Repeat("=", count2))
continue
}
writeChar(buf, ch)
ch = sc.Next()
}
finally:
return nil
}
var reservedWords = map[string]int{
"and": TAnd, "break": TBreak, "do": TDo, "else": TElse, "elseif": TElseIf,
"end": TEnd, "false": TFalse, "for": TFor, "function": TFunction,
"if": TIf, "in": TIn, "local": TLocal, "nil": TNil, "not": TNot, "or": TOr,
"return": TReturn, "repeat": TRepeat, "then": TThen, "true": TTrue,
"until": TUntil, "while": TWhile, "goto": TGoto}
func (sc *Scanner) Scan(lexer *Lexer) (ast.Token, error) {
redo:
var err error
tok := ast.Token{}
newline := false
ch := sc.skipWhiteSpace(whitespace1)
if ch == '\n' || ch == '\r' {
newline = true
ch = sc.skipWhiteSpace(whitespace2)
}
if ch == '(' && lexer.PrevTokenType == ')' {
lexer.PNewLine = newline
} else {
lexer.PNewLine = false
}
var _buf bytes.Buffer
buf := &_buf
tok.Pos = sc.Pos
switch {
case isIdent(ch, 0):
tok.Type = TIdent
err = sc.scanIdent(ch, buf)
tok.Str = buf.String()
if err != nil {
goto finally
}
if typ, ok := reservedWords[tok.Str]; ok {
tok.Type = typ
}
case isDecimal(ch):
tok.Type = TNumber
err = sc.scanNumber(ch, buf)
tok.Str = buf.String()
default:
switch ch {
case EOF:
tok.Type = EOF
case '-':
if sc.Peek() == '-' {
err = sc.skipComments(sc.Next())
if err != nil {
goto finally
}
goto redo
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '"', '\'':
tok.Type = TString
err = sc.scanString(ch, buf)
tok.Str = buf.String()
case '[':
if c := sc.Peek(); c == '[' || c == '=' {
tok.Type = TString
err = sc.scanMultilineString(sc.Next(), buf)
tok.Str = buf.String()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '=':
if sc.Peek() == '=' {
tok.Type = TEqeq
tok.Str = "=="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '~':
if sc.Peek() == '=' {
tok.Type = TNeq
tok.Str = "~="
sc.Next()
} else {
err = sc.Error("~", "Invalid '~' token")
}
case '<':
if sc.Peek() == '=' {
tok.Type = TLte
tok.Str = "<="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '>':
if sc.Peek() == '=' {
tok.Type = TGte
tok.Str = ">="
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '.':
ch2 := sc.Peek()
switch {
case isDecimal(ch2):
tok.Type = TNumber
err = sc.scanNumber(ch, buf)
tok.Str = buf.String()
case ch2 == '.':
writeChar(buf, ch)
writeChar(buf, sc.Next())
if sc.Peek() == '.' {
writeChar(buf, sc.Next())
tok.Type = T3Comma
} else {
tok.Type = T2Comma
}
default:
tok.Type = '.'
}
tok.Str = buf.String()
case ':':
if sc.Peek() == ':' {
tok.Type = T2Colon
tok.Str = "::"
sc.Next()
} else {
tok.Type = ch
tok.Str = string(rune(ch))
}
case '+', '*', '/', '%', '^', '#', '(', ')', '{', '}', ']', ';', ',':
tok.Type = ch
tok.Str = string(rune(ch))
default:
writeChar(buf, ch)
err = sc.Error(buf.String(), "Invalid token")
goto finally
}
}
finally:
tok.Name = TokenName(int(tok.Type))
return tok, err
}
// yacc interface {{{
type Lexer struct {
scanner *Scanner
Stmts []ast.Stmt
PNewLine bool
Token ast.Token
PrevTokenType int
}
func (lx *Lexer) Lex(lval *yySymType) int {
lx.PrevTokenType = lx.Token.Type
tok, err := lx.scanner.Scan(lx)
if err != nil {
panic(err)
}
if tok.Type < 0 {
return 0
}
lval.token = tok
lx.Token = tok
return int(tok.Type)
}
func (lx *Lexer) Error(message string) {
panic(lx.scanner.Error(lx.Token.Str, message))
}
func (lx *Lexer) TokenError(tok ast.Token, message string) {
panic(lx.scanner.TokenError(tok, message))
}
func Parse(reader io.Reader, name string) (chunk []ast.Stmt, err error) {
lexer := &Lexer{NewScanner(reader, name), nil, false, ast.Token{Str: ""}, TNil}
chunk = nil
defer func() {
if e := recover(); e != nil {
err, _ = e.(error)
}
}()
yyParse(lexer)
chunk = lexer.Stmts
return
}
// }}}
// Dump {{{
func isInlineDumpNode(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Struct, reflect.Slice, reflect.Interface, reflect.Ptr:
return false
default:
return true
}
}
func dump(node interface{}, level int, s string) string {
rt := reflect.TypeOf(node)
if fmt.Sprint(rt) == "<nil>" {
return strings.Repeat(s, level) + "<nil>"
}
rv := reflect.ValueOf(node)
buf := []string{}
switch rt.Kind() {
case reflect.Slice:
if rv.Len() == 0 {
return strings.Repeat(s, level) + "<empty>"
}
for i := 0; i < rv.Len(); i++ {
buf = append(buf, dump(rv.Index(i).Interface(), level, s))
}
case reflect.Ptr:
vt := rv.Elem()
tt := rt.Elem()
indicies := []int{}
for i := 0; i < tt.NumField(); i++ {
if strings.Index(tt.Field(i).Name, "Base") > -1 {
continue
}
indicies = append(indicies, i)
}
switch {
case len(indicies) == 0:
return strings.Repeat(s, level) + "<empty>"
case len(indicies) == 1 && isInlineDumpNode(vt.Field(indicies[0])):
for _, i := range indicies {
buf = append(buf, strings.Repeat(s, level)+"- Node$"+tt.Name()+": "+dump(vt.Field(i).Interface(), 0, s))
}
default:
buf = append(buf, strings.Repeat(s, level)+"- Node$"+tt.Name())
for _, i := range indicies {
if isInlineDumpNode(vt.Field(i)) {
inf := dump(vt.Field(i).Interface(), 0, s)
buf = append(buf, strings.Repeat(s, level+1)+tt.Field(i).Name+": "+inf)
} else {
buf = append(buf, strings.Repeat(s, level+1)+tt.Field(i).Name+": ")
buf = append(buf, dump(vt.Field(i).Interface(), level+2, s))
}
}
}
default:
buf = append(buf, strings.Repeat(s, level)+fmt.Sprint(node))
}
return strings.Join(buf, "\n")
}
func Dump(chunk []ast.Stmt) string {
return dump(chunk, 0, " ")
}
// }}

1385
vendor/github.com/yuin/gopher-lua/parse/parser.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

535
vendor/github.com/yuin/gopher-lua/parse/parser.go.y generated vendored Normal file
View File

@@ -0,0 +1,535 @@
%{
package parse
import (
"github.com/yuin/gopher-lua/ast"
)
%}
%type<stmts> chunk
%type<stmts> chunk1
%type<stmts> block
%type<stmt> stat
%type<stmts> elseifs
%type<stmt> laststat
%type<funcname> funcname
%type<funcname> funcname1
%type<exprlist> varlist
%type<expr> var
%type<namelist> namelist
%type<exprlist> exprlist
%type<expr> expr
%type<expr> string
%type<expr> prefixexp
%type<expr> functioncall
%type<expr> afunctioncall
%type<exprlist> args
%type<expr> function
%type<funcexpr> funcbody
%type<parlist> parlist
%type<expr> tableconstructor
%type<fieldlist> fieldlist
%type<field> field
%type<fieldsep> fieldsep
%union {
token ast.Token
stmts []ast.Stmt
stmt ast.Stmt
funcname *ast.FuncName
funcexpr *ast.FunctionExpr
exprlist []ast.Expr
expr ast.Expr
fieldlist []*ast.Field
field *ast.Field
fieldsep string
namelist []string
parlist *ast.ParList
}
/* Reserved words */
%token<token> TAnd TBreak TDo TElse TElseIf TEnd TFalse TFor TFunction TIf TIn TLocal TNil TNot TOr TReturn TRepeat TThen TTrue TUntil TWhile TGoto
/* Literals */
%token<token> TEqeq TNeq TLte TGte T2Comma T3Comma T2Colon TIdent TNumber TString '{' '('
/* Operators */
%left TOr
%left TAnd
%left '>' '<' TGte TLte TEqeq TNeq
%right T2Comma
%left '+' '-'
%left '*' '/' '%'
%right UNARY /* not # -(unary) */
%right '^'
%%
chunk:
chunk1 {
$$ = $1
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
} |
chunk1 laststat {
$$ = append($1, $2)
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
} |
chunk1 laststat ';' {
$$ = append($1, $2)
if l, ok := yylex.(*Lexer); ok {
l.Stmts = $$
}
}
chunk1:
{
$$ = []ast.Stmt{}
} |
chunk1 stat {
$$ = append($1, $2)
} |
chunk1 ';' {
$$ = $1
}
block:
chunk {
$$ = $1
}
stat:
varlist '=' exprlist {
$$ = &ast.AssignStmt{Lhs: $1, Rhs: $3}
$$.SetLine($1[0].Line())
} |
/* 'stat = functioncal' causes a reduce/reduce conflict */
prefixexp {
if _, ok := $1.(*ast.FuncCallExpr); !ok {
yylex.(*Lexer).Error("parse error")
} else {
$$ = &ast.FuncCallStmt{Expr: $1}
$$.SetLine($1.Line())
}
} |
TDo block TEnd {
$$ = &ast.DoBlockStmt{Stmts: $2}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($3.Pos.Line)
} |
TWhile expr TDo block TEnd {
$$ = &ast.WhileStmt{Condition: $2, Stmts: $4}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($5.Pos.Line)
} |
TRepeat block TUntil expr {
$$ = &ast.RepeatStmt{Condition: $4, Stmts: $2}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.Line())
} |
TIf expr TThen block elseifs TEnd {
$$ = &ast.IfStmt{Condition: $2, Then: $4}
cur := $$
for _, elseif := range $5 {
cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
cur = elseif
}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($6.Pos.Line)
} |
TIf expr TThen block elseifs TElse block TEnd {
$$ = &ast.IfStmt{Condition: $2, Then: $4}
cur := $$
for _, elseif := range $5 {
cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
cur = elseif
}
cur.(*ast.IfStmt).Else = $7
$$.SetLine($1.Pos.Line)
$$.SetLastLine($8.Pos.Line)
} |
TFor TIdent '=' expr ',' expr TDo block TEnd {
$$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Stmts: $8}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($9.Pos.Line)
} |
TFor TIdent '=' expr ',' expr ',' expr TDo block TEnd {
$$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Step:$8, Stmts: $10}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($11.Pos.Line)
} |
TFor namelist TIn exprlist TDo block TEnd {
$$ = &ast.GenericForStmt{Names:$2, Exprs:$4, Stmts: $6}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($7.Pos.Line)
} |
TFunction funcname funcbody {
$$ = &ast.FuncDefStmt{Name: $2, Func: $3}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($3.LastLine())
} |
TLocal TFunction TIdent funcbody {
$$ = &ast.LocalAssignStmt{Names:[]string{$3.Str}, Exprs: []ast.Expr{$4}}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.LastLine())
} |
TLocal namelist '=' exprlist {
$$ = &ast.LocalAssignStmt{Names: $2, Exprs:$4}
$$.SetLine($1.Pos.Line)
} |
TLocal namelist {
$$ = &ast.LocalAssignStmt{Names: $2, Exprs:[]ast.Expr{}}
$$.SetLine($1.Pos.Line)
} |
T2Colon TIdent T2Colon {
$$ = &ast.LabelStmt{Name: $2.Str}
$$.SetLine($1.Pos.Line)
} |
TGoto TIdent {
$$ = &ast.GotoStmt{Label: $2.Str}
$$.SetLine($1.Pos.Line)
}
elseifs:
{
$$ = []ast.Stmt{}
} |
elseifs TElseIf expr TThen block {
$$ = append($1, &ast.IfStmt{Condition: $3, Then: $5})
$$[len($$)-1].SetLine($2.Pos.Line)
}
laststat:
TReturn {
$$ = &ast.ReturnStmt{Exprs:nil}
$$.SetLine($1.Pos.Line)
} |
TReturn exprlist {
$$ = &ast.ReturnStmt{Exprs:$2}
$$.SetLine($1.Pos.Line)
} |
TBreak {
$$ = &ast.BreakStmt{}
$$.SetLine($1.Pos.Line)
}
funcname:
funcname1 {
$$ = $1
} |
funcname1 ':' TIdent {
$$ = &ast.FuncName{Func:nil, Receiver:$1.Func, Method: $3.Str}
}
funcname1:
TIdent {
$$ = &ast.FuncName{Func: &ast.IdentExpr{Value:$1.Str}}
$$.Func.SetLine($1.Pos.Line)
} |
funcname1 '.' TIdent {
key:= &ast.StringExpr{Value:$3.Str}
key.SetLine($3.Pos.Line)
fn := &ast.AttrGetExpr{Object: $1.Func, Key: key}
fn.SetLine($3.Pos.Line)
$$ = &ast.FuncName{Func: fn}
}
varlist:
var {
$$ = []ast.Expr{$1}
} |
varlist ',' var {
$$ = append($1, $3)
}
var:
TIdent {
$$ = &ast.IdentExpr{Value:$1.Str}
$$.SetLine($1.Pos.Line)
} |
prefixexp '[' expr ']' {
$$ = &ast.AttrGetExpr{Object: $1, Key: $3}
$$.SetLine($1.Line())
} |
prefixexp '.' TIdent {
key := &ast.StringExpr{Value:$3.Str}
key.SetLine($3.Pos.Line)
$$ = &ast.AttrGetExpr{Object: $1, Key: key}
$$.SetLine($1.Line())
}
namelist:
TIdent {
$$ = []string{$1.Str}
} |
namelist ',' TIdent {
$$ = append($1, $3.Str)
}
exprlist:
expr {
$$ = []ast.Expr{$1}
} |
exprlist ',' expr {
$$ = append($1, $3)
}
expr:
TNil {
$$ = &ast.NilExpr{}
$$.SetLine($1.Pos.Line)
} |
TFalse {
$$ = &ast.FalseExpr{}
$$.SetLine($1.Pos.Line)
} |
TTrue {
$$ = &ast.TrueExpr{}
$$.SetLine($1.Pos.Line)
} |
TNumber {
$$ = &ast.NumberExpr{Value: $1.Str}
$$.SetLine($1.Pos.Line)
} |
T3Comma {
$$ = &ast.Comma3Expr{}
$$.SetLine($1.Pos.Line)
} |
function {
$$ = $1
} |
prefixexp {
$$ = $1
} |
string {
$$ = $1
} |
tableconstructor {
$$ = $1
} |
expr TOr expr {
$$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "or", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TAnd expr {
$$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "and", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '>' expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '<' expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TGte expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TLte expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TEqeq expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "==", Rhs: $3}
$$.SetLine($1.Line())
} |
expr TNeq expr {
$$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "~=", Rhs: $3}
$$.SetLine($1.Line())
} |
expr T2Comma expr {
$$ = &ast.StringConcatOpExpr{Lhs: $1, Rhs: $3}
$$.SetLine($1.Line())
} |
expr '+' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "+", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '-' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "-", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '*' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "*", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '/' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "/", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '%' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "%", Rhs: $3}
$$.SetLine($1.Line())
} |
expr '^' expr {
$$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "^", Rhs: $3}
$$.SetLine($1.Line())
} |
'-' expr %prec UNARY {
$$ = &ast.UnaryMinusOpExpr{Expr: $2}
$$.SetLine($2.Line())
} |
TNot expr %prec UNARY {
$$ = &ast.UnaryNotOpExpr{Expr: $2}
$$.SetLine($2.Line())
} |
'#' expr %prec UNARY {
$$ = &ast.UnaryLenOpExpr{Expr: $2}
$$.SetLine($2.Line())
}
string:
TString {
$$ = &ast.StringExpr{Value: $1.Str}
$$.SetLine($1.Pos.Line)
}
prefixexp:
var {
$$ = $1
} |
afunctioncall {
$$ = $1
} |
functioncall {
$$ = $1
} |
'(' expr ')' {
if ex, ok := $2.(*ast.Comma3Expr); ok {
ex.AdjustRet = true
}
$$ = $2
$$.SetLine($1.Pos.Line)
}
afunctioncall:
'(' functioncall ')' {
$2.(*ast.FuncCallExpr).AdjustRet = true
$$ = $2
}
functioncall:
prefixexp args {
$$ = &ast.FuncCallExpr{Func: $1, Args: $2}
$$.SetLine($1.Line())
} |
prefixexp ':' TIdent args {
$$ = &ast.FuncCallExpr{Method: $3.Str, Receiver: $1, Args: $4}
$$.SetLine($1.Line())
}
args:
'(' ')' {
if yylex.(*Lexer).PNewLine {
yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
}
$$ = []ast.Expr{}
} |
'(' exprlist ')' {
if yylex.(*Lexer).PNewLine {
yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
}
$$ = $2
} |
tableconstructor {
$$ = []ast.Expr{$1}
} |
string {
$$ = []ast.Expr{$1}
}
function:
TFunction funcbody {
$$ = &ast.FunctionExpr{ParList:$2.ParList, Stmts: $2.Stmts}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($2.LastLine())
}
funcbody:
'(' parlist ')' block TEnd {
$$ = &ast.FunctionExpr{ParList: $2, Stmts: $4}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($5.Pos.Line)
} |
'(' ')' block TEnd {
$$ = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: $3}
$$.SetLine($1.Pos.Line)
$$.SetLastLine($4.Pos.Line)
}
parlist:
T3Comma {
$$ = &ast.ParList{HasVargs: true, Names: []string{}}
} |
namelist {
$$ = &ast.ParList{HasVargs: false, Names: []string{}}
$$.Names = append($$.Names, $1...)
} |
namelist ',' T3Comma {
$$ = &ast.ParList{HasVargs: true, Names: []string{}}
$$.Names = append($$.Names, $1...)
}
tableconstructor:
'{' '}' {
$$ = &ast.TableExpr{Fields: []*ast.Field{}}
$$.SetLine($1.Pos.Line)
} |
'{' fieldlist '}' {
$$ = &ast.TableExpr{Fields: $2}
$$.SetLine($1.Pos.Line)
}
fieldlist:
field {
$$ = []*ast.Field{$1}
} |
fieldlist fieldsep field {
$$ = append($1, $3)
} |
fieldlist fieldsep {
$$ = $1
}
field:
TIdent '=' expr {
$$ = &ast.Field{Key: &ast.StringExpr{Value:$1.Str}, Value: $3}
$$.Key.SetLine($1.Pos.Line)
} |
'[' expr ']' '=' expr {
$$ = &ast.Field{Key: $2, Value: $5}
} |
expr {
$$ = &ast.Field{Value: $1}
}
fieldsep:
',' {
$$ = ","
} |
';' {
$$ = ";"
}
%%
func TokenName(c int) string {
if c >= TAnd && c-TAnd < len(yyToknames) {
if yyToknames[c-TAnd] != "" {
return yyToknames[c-TAnd]
}
}
return string([]byte{byte(c)})
}

638
vendor/github.com/yuin/gopher-lua/pm/pm.go generated vendored Normal file
View File

@@ -0,0 +1,638 @@
// Lua pattern match functions for Go
package pm
import (
"fmt"
)
const EOS = -1
const _UNKNOWN = -2
/* Error {{{ */
type Error struct {
Pos int
Message string
}
func newError(pos int, message string, args ...interface{}) *Error {
if len(args) == 0 {
return &Error{pos, message}
}
return &Error{pos, fmt.Sprintf(message, args...)}
}
func (e *Error) Error() string {
switch e.Pos {
case EOS:
return fmt.Sprintf("%s at EOS", e.Message)
case _UNKNOWN:
return fmt.Sprintf("%s", e.Message)
default:
return fmt.Sprintf("%s at %d", e.Message, e.Pos)
}
}
/* }}} */
/* MatchData {{{ */
type MatchData struct {
// captured positions
// layout
// xxxx xxxx xxxx xxx0 : caputured positions
// xxxx xxxx xxxx xxx1 : position captured positions
captures []uint32
}
func newMatchState() *MatchData { return &MatchData{[]uint32{}} }
func (st *MatchData) addPosCapture(s, pos int) {
for s+1 >= len(st.captures) {
st.captures = append(st.captures, 0)
}
st.captures[s] = (uint32(pos) << 1) | 1
st.captures[s+1] = (uint32(pos) << 1) | 1
}
func (st *MatchData) setCapture(s, pos int) uint32 {
for s >= len(st.captures) {
st.captures = append(st.captures, 0)
}
v := st.captures[s]
st.captures[s] = (uint32(pos) << 1)
return v
}
func (st *MatchData) restoreCapture(s int, pos uint32) { st.captures[s] = pos }
func (st *MatchData) CaptureLength() int { return len(st.captures) }
func (st *MatchData) IsPosCapture(idx int) bool { return (st.captures[idx] & 1) == 1 }
func (st *MatchData) Capture(idx int) int { return int(st.captures[idx] >> 1) }
/* }}} */
/* scanner {{{ */
type scannerState struct {
Pos int
started bool
}
type scanner struct {
src []byte
State scannerState
saved scannerState
}
func newScanner(src []byte) *scanner {
return &scanner{
src: src,
State: scannerState{
Pos: 0,
started: false,
},
saved: scannerState{},
}
}
func (sc *scanner) Length() int { return len(sc.src) }
func (sc *scanner) Next() int {
if !sc.State.started {
sc.State.started = true
if len(sc.src) == 0 {
sc.State.Pos = EOS
}
} else {
sc.State.Pos = sc.NextPos()
}
if sc.State.Pos == EOS {
return EOS
}
return int(sc.src[sc.State.Pos])
}
func (sc *scanner) CurrentPos() int {
return sc.State.Pos
}
func (sc *scanner) NextPos() int {
if sc.State.Pos == EOS || sc.State.Pos >= len(sc.src)-1 {
return EOS
}
if !sc.State.started {
return 0
} else {
return sc.State.Pos + 1
}
}
func (sc *scanner) Peek() int {
cureof := sc.State.Pos == EOS
ch := sc.Next()
if !cureof {
if sc.State.Pos == EOS {
sc.State.Pos = len(sc.src) - 1
} else {
sc.State.Pos--
if sc.State.Pos < 0 {
sc.State.Pos = 0
sc.State.started = false
}
}
}
return ch
}
func (sc *scanner) Save() { sc.saved = sc.State }
func (sc *scanner) Restore() { sc.State = sc.saved }
/* }}} */
/* bytecode {{{ */
type opCode int
const (
opChar opCode = iota
opMatch
opTailMatch
opJmp
opSplit
opSave
opPSave
opBrace
opNumber
)
type inst struct {
OpCode opCode
Class class
Operand1 int
Operand2 int
}
/* }}} */
/* classes {{{ */
type class interface {
Matches(ch int) bool
}
type dotClass struct{}
func (pn *dotClass) Matches(ch int) bool { return true }
type charClass struct {
Ch int
}
func (pn *charClass) Matches(ch int) bool { return pn.Ch == ch }
type singleClass struct {
Class int
}
func (pn *singleClass) Matches(ch int) bool {
ret := false
switch pn.Class {
case 'a', 'A':
ret = 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z'
case 'c', 'C':
ret = (0x00 <= ch && ch <= 0x1F) || ch == 0x7F
case 'd', 'D':
ret = '0' <= ch && ch <= '9'
case 'l', 'L':
ret = 'a' <= ch && ch <= 'z'
case 'p', 'P':
ret = (0x21 <= ch && ch <= 0x2f) || (0x3a <= ch && ch <= 0x40) || (0x5b <= ch && ch <= 0x60) || (0x7b <= ch && ch <= 0x7e)
case 's', 'S':
switch ch {
case ' ', '\f', '\n', '\r', '\t', '\v':
ret = true
}
case 'u', 'U':
ret = 'A' <= ch && ch <= 'Z'
case 'w', 'W':
ret = '0' <= ch && ch <= '9' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z'
case 'x', 'X':
ret = '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F'
case 'z', 'Z':
ret = ch == 0
default:
return ch == pn.Class
}
if 'A' <= pn.Class && pn.Class <= 'Z' {
return !ret
}
return ret
}
type setClass struct {
IsNot bool
Classes []class
}
func (pn *setClass) Matches(ch int) bool {
for _, class := range pn.Classes {
if class.Matches(ch) {
return !pn.IsNot
}
}
return pn.IsNot
}
type rangeClass struct {
Begin class
End class
}
func (pn *rangeClass) Matches(ch int) bool {
switch begin := pn.Begin.(type) {
case *charClass:
end, ok := pn.End.(*charClass)
if !ok {
return false
}
return begin.Ch <= ch && ch <= end.Ch
}
return false
}
// }}}
// patterns {{{
type pattern interface{}
type singlePattern struct {
Class class
}
type seqPattern struct {
MustHead bool
MustTail bool
Patterns []pattern
}
type repeatPattern struct {
Type int
Class class
}
type posCapPattern struct{}
type capPattern struct {
Pattern pattern
}
type numberPattern struct {
N int
}
type bracePattern struct {
Begin int
End int
}
// }}}
/* parse {{{ */
func parseClass(sc *scanner, allowset bool) class {
ch := sc.Next()
switch ch {
case '%':
return &singleClass{sc.Next()}
case '.':
if allowset {
return &dotClass{}
}
return &charClass{ch}
case '[':
if allowset {
return parseClassSet(sc)
}
return &charClass{ch}
//case '^' '$', '(', ')', ']', '*', '+', '-', '?':
// panic(newError(sc.CurrentPos(), "invalid %c", ch))
case EOS:
panic(newError(sc.CurrentPos(), "unexpected EOS"))
default:
return &charClass{ch}
}
}
func parseClassSet(sc *scanner) class {
set := &setClass{false, []class{}}
if sc.Peek() == '^' {
set.IsNot = true
sc.Next()
}
isrange := false
for {
ch := sc.Peek()
switch ch {
// case '[':
// panic(newError(sc.CurrentPos(), "'[' can not be nested"))
case EOS:
panic(newError(sc.CurrentPos(), "unexpected EOS"))
case ']':
if len(set.Classes) > 0 {
sc.Next()
goto exit
}
fallthrough
case '-':
if len(set.Classes) > 0 {
sc.Next()
isrange = true
continue
}
fallthrough
default:
set.Classes = append(set.Classes, parseClass(sc, false))
}
if isrange {
begin := set.Classes[len(set.Classes)-2]
end := set.Classes[len(set.Classes)-1]
set.Classes = set.Classes[0 : len(set.Classes)-2]
set.Classes = append(set.Classes, &rangeClass{begin, end})
isrange = false
}
}
exit:
if isrange {
set.Classes = append(set.Classes, &charClass{'-'})
}
return set
}
func parsePattern(sc *scanner, toplevel bool) *seqPattern {
pat := &seqPattern{}
if toplevel {
if sc.Peek() == '^' {
sc.Next()
pat.MustHead = true
}
}
for {
ch := sc.Peek()
switch ch {
case '%':
sc.Save()
sc.Next()
switch sc.Peek() {
case '0':
panic(newError(sc.CurrentPos(), "invalid capture index"))
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
pat.Patterns = append(pat.Patterns, &numberPattern{sc.Next() - 48})
case 'b':
sc.Next()
pat.Patterns = append(pat.Patterns, &bracePattern{sc.Next(), sc.Next()})
default:
sc.Restore()
pat.Patterns = append(pat.Patterns, &singlePattern{parseClass(sc, true)})
}
case '.', '[', ']':
pat.Patterns = append(pat.Patterns, &singlePattern{parseClass(sc, true)})
//case ']':
// panic(newError(sc.CurrentPos(), "invalid ']'"))
case ')':
if toplevel {
panic(newError(sc.CurrentPos(), "invalid ')'"))
}
return pat
case '(':
sc.Next()
if sc.Peek() == ')' {
sc.Next()
pat.Patterns = append(pat.Patterns, &posCapPattern{})
} else {
ret := &capPattern{parsePattern(sc, false)}
if sc.Peek() != ')' {
panic(newError(sc.CurrentPos(), "unfinished capture"))
}
sc.Next()
pat.Patterns = append(pat.Patterns, ret)
}
case '*', '+', '-', '?':
sc.Next()
if len(pat.Patterns) > 0 {
spat, ok := pat.Patterns[len(pat.Patterns)-1].(*singlePattern)
if ok {
pat.Patterns = pat.Patterns[0 : len(pat.Patterns)-1]
pat.Patterns = append(pat.Patterns, &repeatPattern{ch, spat.Class})
continue
}
}
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
case '$':
if toplevel && (sc.NextPos() == sc.Length()-1 || sc.NextPos() == EOS) {
pat.MustTail = true
} else {
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
}
sc.Next()
case EOS:
sc.Next()
goto exit
default:
sc.Next()
pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
}
}
exit:
return pat
}
type iptr struct {
insts []inst
capture int
}
func compilePattern(p pattern, ps ...*iptr) []inst {
var ptr *iptr
toplevel := false
if len(ps) == 0 {
toplevel = true
ptr = &iptr{[]inst{inst{opSave, nil, 0, -1}}, 2}
} else {
ptr = ps[0]
}
switch pat := p.(type) {
case *singlePattern:
ptr.insts = append(ptr.insts, inst{opChar, pat.Class, -1, -1})
case *seqPattern:
for _, cp := range pat.Patterns {
compilePattern(cp, ptr)
}
case *repeatPattern:
idx := len(ptr.insts)
switch pat.Type {
case '*':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 1, idx + 3},
inst{opChar, pat.Class, -1, -1},
inst{opJmp, nil, idx, -1})
case '+':
ptr.insts = append(ptr.insts,
inst{opChar, pat.Class, -1, -1},
inst{opSplit, nil, idx, idx + 2})
case '-':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 3, idx + 1},
inst{opChar, pat.Class, -1, -1},
inst{opJmp, nil, idx, -1})
case '?':
ptr.insts = append(ptr.insts,
inst{opSplit, nil, idx + 1, idx + 2},
inst{opChar, pat.Class, -1, -1})
}
case *posCapPattern:
ptr.insts = append(ptr.insts, inst{opPSave, nil, ptr.capture, -1})
ptr.capture += 2
case *capPattern:
c0, c1 := ptr.capture, ptr.capture+1
ptr.capture += 2
ptr.insts = append(ptr.insts, inst{opSave, nil, c0, -1})
compilePattern(pat.Pattern, ptr)
ptr.insts = append(ptr.insts, inst{opSave, nil, c1, -1})
case *bracePattern:
ptr.insts = append(ptr.insts, inst{opBrace, nil, pat.Begin, pat.End})
case *numberPattern:
ptr.insts = append(ptr.insts, inst{opNumber, nil, pat.N, -1})
}
if toplevel {
if p.(*seqPattern).MustTail {
ptr.insts = append(ptr.insts, inst{opSave, nil, 1, -1}, inst{opTailMatch, nil, -1, -1})
}
ptr.insts = append(ptr.insts, inst{opSave, nil, 1, -1}, inst{opMatch, nil, -1, -1})
}
return ptr.insts
}
/* }}} parse */
/* VM {{{ */
// Simple recursive virtual machine based on the
// "Regular Expression Matching: the Virtual Machine Approach" (https://swtch.com/~rsc/regexp/regexp2.html)
func recursiveVM(src []byte, insts []inst, pc, sp int, ms ...*MatchData) (bool, int, *MatchData) {
var m *MatchData
if len(ms) == 0 {
m = newMatchState()
} else {
m = ms[0]
}
redo:
inst := insts[pc]
switch inst.OpCode {
case opChar:
if sp >= len(src) || !inst.Class.Matches(int(src[sp])) {
return false, sp, m
}
pc++
sp++
goto redo
case opMatch:
return true, sp, m
case opTailMatch:
return sp >= len(src), sp, m
case opJmp:
pc = inst.Operand1
goto redo
case opSplit:
if ok, nsp, _ := recursiveVM(src, insts, inst.Operand1, sp, m); ok {
return true, nsp, m
}
pc = inst.Operand2
goto redo
case opSave:
s := m.setCapture(inst.Operand1, sp)
if ok, nsp, _ := recursiveVM(src, insts, pc+1, sp, m); ok {
return true, nsp, m
}
m.restoreCapture(inst.Operand1, s)
return false, sp, m
case opPSave:
m.addPosCapture(inst.Operand1, sp+1)
pc++
goto redo
case opBrace:
if sp >= len(src) || int(src[sp]) != inst.Operand1 {
return false, sp, m
}
count := 1
for sp = sp + 1; sp < len(src); sp++ {
if int(src[sp]) == inst.Operand2 {
count--
}
if count == 0 {
pc++
sp++
goto redo
}
if int(src[sp]) == inst.Operand1 {
count++
}
}
return false, sp, m
case opNumber:
idx := inst.Operand1 * 2
if idx >= m.CaptureLength()-1 {
panic(newError(_UNKNOWN, "invalid capture index"))
}
capture := src[m.Capture(idx):m.Capture(idx+1)]
for i := 0; i < len(capture); i++ {
if i+sp >= len(src) || capture[i] != src[i+sp] {
return false, sp, m
}
}
pc++
sp += len(capture)
goto redo
}
panic("should not reach here")
}
/* }}} */
/* API {{{ */
func Find(p string, src []byte, offset, limit int) (matches []*MatchData, err error) {
defer func() {
if v := recover(); v != nil {
if perr, ok := v.(*Error); ok {
err = perr
} else {
panic(v)
}
}
}()
pat := parsePattern(newScanner([]byte(p)), true)
insts := compilePattern(pat)
matches = []*MatchData{}
for sp := offset; sp <= len(src); {
ok, nsp, ms := recursiveVM(src, insts, 0, sp)
sp++
if ok {
if sp < nsp {
sp = nsp
}
matches = append(matches, ms)
}
if len(matches) == limit || pat.MustHead {
break
}
}
return
}
/* }}} */

2306
vendor/github.com/yuin/gopher-lua/state.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

448
vendor/github.com/yuin/gopher-lua/stringlib.go generated vendored Normal file
View File

@@ -0,0 +1,448 @@
package lua
import (
"fmt"
"strings"
"github.com/yuin/gopher-lua/pm"
)
const emptyLString LString = LString("")
func OpenString(L *LState) int {
var mod *LTable
//_, ok := L.G.builtinMts[int(LTString)]
//if !ok {
mod = L.RegisterModule(StringLibName, strFuncs).(*LTable)
gmatch := L.NewClosure(strGmatch, L.NewFunction(strGmatchIter))
mod.RawSetString("gmatch", gmatch)
mod.RawSetString("gfind", gmatch)
mod.RawSetString("__index", mod)
L.G.builtinMts[int(LTString)] = mod
//}
L.Push(mod)
return 1
}
var strFuncs = map[string]LGFunction{
"byte": strByte,
"char": strChar,
"dump": strDump,
"find": strFind,
"format": strFormat,
"gsub": strGsub,
"len": strLen,
"lower": strLower,
"match": strMatch,
"rep": strRep,
"reverse": strReverse,
"sub": strSub,
"upper": strUpper,
}
func strByte(L *LState) int {
str := L.CheckString(1)
start := L.OptInt(2, 1) - 1
end := L.OptInt(3, -1)
l := len(str)
if start < 0 {
start = l + start + 1
}
if end < 0 {
end = l + end + 1
}
if L.GetTop() == 2 {
if start < 0 || start >= l {
return 0
}
L.Push(LNumber(str[start]))
return 1
}
start = intMax(start, 0)
end = intMin(end, l)
if end < 0 || end <= start || start >= l {
return 0
}
for i := start; i < end; i++ {
L.Push(LNumber(str[i]))
}
return end - start
}
func strChar(L *LState) int {
top := L.GetTop()
bytes := make([]byte, L.GetTop())
for i := 1; i <= top; i++ {
bytes[i-1] = uint8(L.CheckInt(i))
}
L.Push(LString(string(bytes)))
return 1
}
func strDump(L *LState) int {
L.RaiseError("GopherLua does not support the string.dump")
return 0
}
func strFind(L *LState) int {
str := L.CheckString(1)
pattern := L.CheckString(2)
if len(pattern) == 0 {
L.Push(LNumber(1))
L.Push(LNumber(0))
return 2
}
init := luaIndex2StringIndex(str, L.OptInt(3, 1), true)
plain := false
if L.GetTop() == 4 {
plain = LVAsBool(L.Get(4))
}
if plain {
pos := strings.Index(str[init:], pattern)
if pos < 0 {
L.Push(LNil)
return 1
}
L.Push(LNumber(init+pos) + 1)
L.Push(LNumber(init + pos + len(pattern)))
return 2
}
mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), init, 1)
if err != nil {
L.RaiseError(err.Error())
}
if len(mds) == 0 {
L.Push(LNil)
return 1
}
md := mds[0]
L.Push(LNumber(md.Capture(0) + 1))
L.Push(LNumber(md.Capture(1)))
for i := 2; i < md.CaptureLength(); i += 2 {
if md.IsPosCapture(i) {
L.Push(LNumber(md.Capture(i)))
} else {
L.Push(LString(str[md.Capture(i):md.Capture(i+1)]))
}
}
return md.CaptureLength()/2 + 1
}
func strFormat(L *LState) int {
str := L.CheckString(1)
args := make([]interface{}, L.GetTop()-1)
top := L.GetTop()
for i := 2; i <= top; i++ {
args[i-2] = L.Get(i)
}
npat := strings.Count(str, "%") - strings.Count(str, "%%")
L.Push(LString(fmt.Sprintf(str, args[:intMin(npat, len(args))]...)))
return 1
}
func strGsub(L *LState) int {
str := L.CheckString(1)
pat := L.CheckString(2)
L.CheckTypes(3, LTString, LTTable, LTFunction)
repl := L.CheckAny(3)
limit := L.OptInt(4, -1)
mds, err := pm.Find(pat, unsafeFastStringToReadOnlyBytes(str), 0, limit)
if err != nil {
L.RaiseError(err.Error())
}
if len(mds) == 0 {
L.SetTop(1)
L.Push(LNumber(0))
return 2
}
switch lv := repl.(type) {
case LString:
L.Push(LString(strGsubStr(L, str, string(lv), mds)))
case *LTable:
L.Push(LString(strGsubTable(L, str, lv, mds)))
case *LFunction:
L.Push(LString(strGsubFunc(L, str, lv, mds)))
}
L.Push(LNumber(len(mds)))
return 2
}
type replaceInfo struct {
Indicies []int
String string
}
func checkCaptureIndex(L *LState, m *pm.MatchData, idx int) {
if idx <= 2 {
return
}
if idx >= m.CaptureLength() {
L.RaiseError("invalid capture index")
}
}
func capturedString(L *LState, m *pm.MatchData, str string, idx int) string {
checkCaptureIndex(L, m, idx)
if idx >= m.CaptureLength() && idx == 2 {
idx = 0
}
if m.IsPosCapture(idx) {
return fmt.Sprint(m.Capture(idx))
} else {
return str[m.Capture(idx):m.Capture(idx+1)]
}
}
func strGsubDoReplace(str string, info []replaceInfo) string {
offset := 0
buf := []byte(str)
for _, replace := range info {
oldlen := len(buf)
b1 := append([]byte(""), buf[0:offset+replace.Indicies[0]]...)
b2 := []byte("")
index2 := offset + replace.Indicies[1]
if index2 <= len(buf) {
b2 = append(b2, buf[index2:len(buf)]...)
}
buf = append(b1, replace.String...)
buf = append(buf, b2...)
offset += len(buf) - oldlen
}
return string(buf)
}
func strGsubStr(L *LState, str string, repl string, matches []*pm.MatchData) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
start, end := match.Capture(0), match.Capture(1)
sc := newFlagScanner('%', "", "", repl)
for c, eos := sc.Next(); !eos; c, eos = sc.Next() {
if !sc.ChangeFlag {
if sc.HasFlag {
if c >= '0' && c <= '9' {
sc.AppendString(capturedString(L, match, str, 2*(int(c)-48)))
} else {
sc.AppendChar('%')
sc.AppendChar(c)
}
sc.HasFlag = false
} else {
sc.AppendChar(c)
}
}
}
infoList = append(infoList, replaceInfo{[]int{start, end}, sc.String()})
}
return strGsubDoReplace(str, infoList)
}
func strGsubTable(L *LState, str string, repl *LTable, matches []*pm.MatchData) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
idx := 0
if match.CaptureLength() > 2 { // has captures
idx = 2
}
var value LValue
if match.IsPosCapture(idx) {
value = L.GetTable(repl, LNumber(match.Capture(idx)))
} else {
value = L.GetField(repl, str[match.Capture(idx):match.Capture(idx+1)])
}
if !LVIsFalse(value) {
infoList = append(infoList, replaceInfo{[]int{match.Capture(0), match.Capture(1)}, LVAsString(value)})
}
}
return strGsubDoReplace(str, infoList)
}
func strGsubFunc(L *LState, str string, repl *LFunction, matches []*pm.MatchData) string {
infoList := make([]replaceInfo, 0, len(matches))
for _, match := range matches {
start, end := match.Capture(0), match.Capture(1)
L.Push(repl)
nargs := 0
if match.CaptureLength() > 2 { // has captures
for i := 2; i < match.CaptureLength(); i += 2 {
if match.IsPosCapture(i) {
L.Push(LNumber(match.Capture(i)))
} else {
L.Push(LString(capturedString(L, match, str, i)))
}
nargs++
}
} else {
L.Push(LString(capturedString(L, match, str, 0)))
nargs++
}
L.Call(nargs, 1)
ret := L.reg.Pop()
if !LVIsFalse(ret) {
infoList = append(infoList, replaceInfo{[]int{start, end}, LVAsString(ret)})
}
}
return strGsubDoReplace(str, infoList)
}
type strMatchData struct {
str string
pos int
matches []*pm.MatchData
}
func strGmatchIter(L *LState) int {
md := L.CheckUserData(1).Value.(*strMatchData)
str := md.str
matches := md.matches
idx := md.pos
md.pos += 1
if idx == len(matches) {
return 0
}
L.Push(L.Get(1))
match := matches[idx]
if match.CaptureLength() == 2 {
L.Push(LString(str[match.Capture(0):match.Capture(1)]))
return 1
}
for i := 2; i < match.CaptureLength(); i += 2 {
if match.IsPosCapture(i) {
L.Push(LNumber(match.Capture(i)))
} else {
L.Push(LString(str[match.Capture(i):match.Capture(i+1)]))
}
}
return match.CaptureLength()/2 - 1
}
func strGmatch(L *LState) int {
str := L.CheckString(1)
pattern := L.CheckString(2)
mds, err := pm.Find(pattern, []byte(str), 0, -1)
if err != nil {
L.RaiseError(err.Error())
}
L.Push(L.Get(UpvalueIndex(1)))
ud := L.NewUserData()
ud.Value = &strMatchData{str, 0, mds}
L.Push(ud)
return 2
}
func strLen(L *LState) int {
str := L.CheckString(1)
L.Push(LNumber(len(str)))
return 1
}
func strLower(L *LState) int {
str := L.CheckString(1)
L.Push(LString(strings.ToLower(str)))
return 1
}
func strMatch(L *LState) int {
str := L.CheckString(1)
pattern := L.CheckString(2)
offset := L.OptInt(3, 1)
l := len(str)
if offset < 0 {
offset = l + offset + 1
}
offset--
if offset < 0 {
offset = 0
}
mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), offset, 1)
if err != nil {
L.RaiseError(err.Error())
}
if len(mds) == 0 {
L.Push(LNil)
return 0
}
md := mds[0]
nsubs := md.CaptureLength() / 2
switch nsubs {
case 1:
L.Push(LString(str[md.Capture(0):md.Capture(1)]))
return 1
default:
for i := 2; i < md.CaptureLength(); i += 2 {
if md.IsPosCapture(i) {
L.Push(LNumber(md.Capture(i)))
} else {
L.Push(LString(str[md.Capture(i):md.Capture(i+1)]))
}
}
return nsubs - 1
}
}
func strRep(L *LState) int {
str := L.CheckString(1)
n := L.CheckInt(2)
if n < 0 {
L.Push(emptyLString)
} else {
L.Push(LString(strings.Repeat(str, n)))
}
return 1
}
func strReverse(L *LState) int {
str := L.CheckString(1)
bts := []byte(str)
out := make([]byte, len(bts))
for i, j := 0, len(bts)-1; j >= 0; i, j = i+1, j-1 {
out[i] = bts[j]
}
L.Push(LString(string(out)))
return 1
}
func strSub(L *LState) int {
str := L.CheckString(1)
start := luaIndex2StringIndex(str, L.CheckInt(2), true)
end := luaIndex2StringIndex(str, L.OptInt(3, -1), false)
l := len(str)
if start >= l || end < start {
L.Push(emptyLString)
} else {
L.Push(LString(str[start:end]))
}
return 1
}
func strUpper(L *LState) int {
str := L.CheckString(1)
L.Push(LString(strings.ToUpper(str)))
return 1
}
func luaIndex2StringIndex(str string, i int, start bool) int {
if start && i != 0 {
i -= 1
}
l := len(str)
if i < 0 {
i = l + i + 1
}
i = intMax(0, i)
if !start && i > l {
i = l
}
return i
}
//

387
vendor/github.com/yuin/gopher-lua/table.go generated vendored Normal file
View File

@@ -0,0 +1,387 @@
package lua
const defaultArrayCap = 32
const defaultHashCap = 32
type lValueArraySorter struct {
L *LState
Fn *LFunction
Values []LValue
}
func (lv lValueArraySorter) Len() int {
return len(lv.Values)
}
func (lv lValueArraySorter) Swap(i, j int) {
lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i]
}
func (lv lValueArraySorter) Less(i, j int) bool {
if lv.Fn != nil {
lv.L.Push(lv.Fn)
lv.L.Push(lv.Values[i])
lv.L.Push(lv.Values[j])
lv.L.Call(2, 1)
return LVAsBool(lv.L.reg.Pop())
}
return lessThan(lv.L, lv.Values[i], lv.Values[j])
}
func newLTable(acap int, hcap int) *LTable {
if acap < 0 {
acap = 0
}
if hcap < 0 {
hcap = 0
}
tb := &LTable{}
tb.Metatable = LNil
if acap != 0 {
tb.array = make([]LValue, 0, acap)
}
if hcap != 0 {
tb.strdict = make(map[string]LValue, hcap)
}
return tb
}
// Len returns length of this LTable without using __len.
func (tb *LTable) Len() int {
if tb.array == nil {
return 0
}
var prev LValue = LNil
for i := len(tb.array) - 1; i >= 0; i-- {
v := tb.array[i]
if prev == LNil && v != LNil {
return i + 1
}
prev = v
}
return 0
}
// Append appends a given LValue to this LTable.
func (tb *LTable) Append(value LValue) {
if value == LNil {
return
}
if tb.array == nil {
tb.array = make([]LValue, 0, defaultArrayCap)
}
if len(tb.array) == 0 || tb.array[len(tb.array)-1] != LNil {
tb.array = append(tb.array, value)
} else {
i := len(tb.array) - 2
for ; i >= 0; i-- {
if tb.array[i] != LNil {
break
}
}
tb.array[i+1] = value
}
}
// Insert inserts a given LValue at position `i` in this table.
func (tb *LTable) Insert(i int, value LValue) {
if tb.array == nil {
tb.array = make([]LValue, 0, defaultArrayCap)
}
if i > len(tb.array) {
tb.RawSetInt(i, value)
return
}
if i <= 0 {
tb.RawSet(LNumber(i), value)
return
}
i -= 1
tb.array = append(tb.array, LNil)
copy(tb.array[i+1:], tb.array[i:])
tb.array[i] = value
}
// MaxN returns a maximum number key that nil value does not exist before it.
func (tb *LTable) MaxN() int {
if tb.array == nil {
return 0
}
for i := len(tb.array) - 1; i >= 0; i-- {
if tb.array[i] != LNil {
return i + 1
}
}
return 0
}
// Remove removes from this table the element at a given position.
func (tb *LTable) Remove(pos int) LValue {
if tb.array == nil {
return LNil
}
larray := len(tb.array)
if larray == 0 {
return LNil
}
i := pos - 1
oldval := LNil
switch {
case i >= larray:
// nothing to do
case i == larray-1 || i < 0:
oldval = tb.array[larray-1]
tb.array = tb.array[:larray-1]
default:
oldval = tb.array[i]
copy(tb.array[i:], tb.array[i+1:])
tb.array[larray-1] = nil
tb.array = tb.array[:larray-1]
}
return oldval
}
// RawSet sets a given LValue to a given index without the __newindex metamethod.
// It is recommended to use `RawSetString` or `RawSetInt` for performance
// if you already know the given LValue is a string or number.
func (tb *LTable) RawSet(key LValue, value LValue) {
switch v := key.(type) {
case LNumber:
if isArrayKey(v) {
if tb.array == nil {
tb.array = make([]LValue, 0, defaultArrayCap)
}
index := int(v) - 1
alen := len(tb.array)
switch {
case index == alen:
tb.array = append(tb.array, value)
case index > alen:
for i := 0; i < (index - alen); i++ {
tb.array = append(tb.array, LNil)
}
tb.array = append(tb.array, value)
case index < alen:
tb.array[index] = value
}
return
}
case LString:
tb.RawSetString(string(v), value)
return
}
tb.RawSetH(key, value)
}
// RawSetInt sets a given LValue at a position `key` without the __newindex metamethod.
func (tb *LTable) RawSetInt(key int, value LValue) {
if key < 1 || key >= MaxArrayIndex {
tb.RawSetH(LNumber(key), value)
return
}
if tb.array == nil {
tb.array = make([]LValue, 0, 32)
}
index := key - 1
alen := len(tb.array)
switch {
case index == alen:
tb.array = append(tb.array, value)
case index > alen:
for i := 0; i < (index - alen); i++ {
tb.array = append(tb.array, LNil)
}
tb.array = append(tb.array, value)
case index < alen:
tb.array[index] = value
}
}
// RawSetString sets a given LValue to a given string index without the __newindex metamethod.
func (tb *LTable) RawSetString(key string, value LValue) {
if tb.strdict == nil {
tb.strdict = make(map[string]LValue, defaultHashCap)
}
if tb.keys == nil {
tb.keys = []LValue{}
tb.k2i = map[LValue]int{}
}
if value == LNil {
// TODO tb.keys and tb.k2i should also be removed
delete(tb.strdict, key)
} else {
tb.strdict[key] = value
lkey := LString(key)
if _, ok := tb.k2i[lkey]; !ok {
tb.k2i[lkey] = len(tb.keys)
tb.keys = append(tb.keys, lkey)
}
}
}
// RawSetH sets a given LValue to a given index without the __newindex metamethod.
func (tb *LTable) RawSetH(key LValue, value LValue) {
if s, ok := key.(LString); ok {
tb.RawSetString(string(s), value)
return
}
if tb.dict == nil {
tb.dict = make(map[LValue]LValue, len(tb.strdict))
}
if tb.keys == nil {
tb.keys = []LValue{}
tb.k2i = map[LValue]int{}
}
if value == LNil {
// TODO tb.keys and tb.k2i should also be removed
delete(tb.dict, key)
} else {
tb.dict[key] = value
if _, ok := tb.k2i[key]; !ok {
tb.k2i[key] = len(tb.keys)
tb.keys = append(tb.keys, key)
}
}
}
// RawGet returns an LValue associated with a given key without __index metamethod.
func (tb *LTable) RawGet(key LValue) LValue {
switch v := key.(type) {
case LNumber:
if isArrayKey(v) {
if tb.array == nil {
return LNil
}
index := int(v) - 1
if index >= len(tb.array) {
return LNil
}
return tb.array[index]
}
case LString:
if tb.strdict == nil {
return LNil
}
if ret, ok := tb.strdict[string(v)]; ok {
return ret
}
return LNil
}
if tb.dict == nil {
return LNil
}
if v, ok := tb.dict[key]; ok {
return v
}
return LNil
}
// RawGetInt returns an LValue at position `key` without __index metamethod.
func (tb *LTable) RawGetInt(key int) LValue {
if tb.array == nil {
return LNil
}
index := int(key) - 1
if index >= len(tb.array) || index < 0 {
return LNil
}
return tb.array[index]
}
// RawGet returns an LValue associated with a given key without __index metamethod.
func (tb *LTable) RawGetH(key LValue) LValue {
if s, sok := key.(LString); sok {
if tb.strdict == nil {
return LNil
}
if v, vok := tb.strdict[string(s)]; vok {
return v
}
return LNil
}
if tb.dict == nil {
return LNil
}
if v, ok := tb.dict[key]; ok {
return v
}
return LNil
}
// RawGetString returns an LValue associated with a given key without __index metamethod.
func (tb *LTable) RawGetString(key string) LValue {
if tb.strdict == nil {
return LNil
}
if v, vok := tb.strdict[string(key)]; vok {
return v
}
return LNil
}
// ForEach iterates over this table of elements, yielding each in turn to a given function.
func (tb *LTable) ForEach(cb func(LValue, LValue)) {
if tb.array != nil {
for i, v := range tb.array {
if v != LNil {
cb(LNumber(i+1), v)
}
}
}
if tb.strdict != nil {
for k, v := range tb.strdict {
if v != LNil {
cb(LString(k), v)
}
}
}
if tb.dict != nil {
for k, v := range tb.dict {
if v != LNil {
cb(k, v)
}
}
}
}
// This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ).
func (tb *LTable) Next(key LValue) (LValue, LValue) {
init := false
if key == LNil {
key = LNumber(0)
init = true
}
if init || key != LNumber(0) {
if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 && kv < LNumber(MaxArrayIndex) {
index := int(kv)
if tb.array != nil {
for ; index < len(tb.array); index++ {
if v := tb.array[index]; v != LNil {
return LNumber(index + 1), v
}
}
}
if tb.array == nil || index == len(tb.array) {
if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) {
return LNil, LNil
}
key = tb.keys[0]
if v := tb.RawGetH(key); v != LNil {
return key, v
}
}
}
}
for i := tb.k2i[key] + 1; i < len(tb.keys); i++ {
key := tb.keys[i]
if v := tb.RawGetH(key); v != LNil {
return key, v
}
}
return LNil, LNil
}

100
vendor/github.com/yuin/gopher-lua/tablelib.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
package lua
import (
"sort"
)
func OpenTable(L *LState) int {
tabmod := L.RegisterModule(TabLibName, tableFuncs)
L.Push(tabmod)
return 1
}
var tableFuncs = map[string]LGFunction{
"getn": tableGetN,
"concat": tableConcat,
"insert": tableInsert,
"maxn": tableMaxN,
"remove": tableRemove,
"sort": tableSort,
}
func tableSort(L *LState) int {
tbl := L.CheckTable(1)
sorter := lValueArraySorter{L, nil, tbl.array}
if L.GetTop() != 1 {
sorter.Fn = L.CheckFunction(2)
}
sort.Sort(sorter)
return 0
}
func tableGetN(L *LState) int {
L.Push(LNumber(L.CheckTable(1).Len()))
return 1
}
func tableMaxN(L *LState) int {
L.Push(LNumber(L.CheckTable(1).MaxN()))
return 1
}
func tableRemove(L *LState) int {
tbl := L.CheckTable(1)
if L.GetTop() == 1 {
L.Push(tbl.Remove(-1))
} else {
L.Push(tbl.Remove(L.CheckInt(2)))
}
return 1
}
func tableConcat(L *LState) int {
tbl := L.CheckTable(1)
sep := LString(L.OptString(2, ""))
i := L.OptInt(3, 1)
j := L.OptInt(4, tbl.Len())
if L.GetTop() == 3 {
if i > tbl.Len() || i < 1 {
L.Push(emptyLString)
return 1
}
}
i = intMax(intMin(i, tbl.Len()), 1)
j = intMin(intMin(j, tbl.Len()), tbl.Len())
if i > j {
L.Push(emptyLString)
return 1
}
//TODO should flushing?
retbottom := L.GetTop()
for ; i <= j; i++ {
v := tbl.RawGetInt(i)
if !LVCanConvToString(v) {
L.RaiseError("invalid value (%s) at index %d in table for concat", v.Type().String(), i)
}
L.Push(v)
if i != j {
L.Push(sep)
}
}
L.Push(stringConcat(L, L.GetTop()-retbottom, L.reg.Top()-1))
return 1
}
func tableInsert(L *LState) int {
tbl := L.CheckTable(1)
nargs := L.GetTop()
if nargs == 1 {
L.RaiseError("wrong number of arguments")
}
if L.GetTop() == 2 {
tbl.Append(L.Get(2))
return 0
}
tbl.Insert(int(L.CheckInt(2)), L.CheckAny(3))
return 0
}
//

265
vendor/github.com/yuin/gopher-lua/utils.go generated vendored Normal file
View File

@@ -0,0 +1,265 @@
package lua
import (
"bufio"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
)
func intMin(a, b int) int {
if a < b {
return a
} else {
return b
}
}
func intMax(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func defaultFormat(v interface{}, f fmt.State, c rune) {
buf := make([]string, 0, 10)
buf = append(buf, "%")
for i := 0; i < 128; i++ {
if f.Flag(i) {
buf = append(buf, string(rune(i)))
}
}
if w, ok := f.Width(); ok {
buf = append(buf, strconv.Itoa(w))
}
if p, ok := f.Precision(); ok {
buf = append(buf, "."+strconv.Itoa(p))
}
buf = append(buf, string(c))
format := strings.Join(buf, "")
fmt.Fprintf(f, format, v)
}
type flagScanner struct {
flag byte
start string
end string
buf []byte
str string
Length int
Pos int
HasFlag bool
ChangeFlag bool
}
func newFlagScanner(flag byte, start, end, str string) *flagScanner {
return &flagScanner{flag, start, end, make([]byte, 0, len(str)), str, len(str), 0, false, false}
}
func (fs *flagScanner) AppendString(str string) { fs.buf = append(fs.buf, str...) }
func (fs *flagScanner) AppendChar(ch byte) { fs.buf = append(fs.buf, ch) }
func (fs *flagScanner) String() string { return string(fs.buf) }
func (fs *flagScanner) Next() (byte, bool) {
c := byte('\000')
fs.ChangeFlag = false
if fs.Pos == fs.Length {
if fs.HasFlag {
fs.AppendString(fs.end)
}
return c, true
} else {
c = fs.str[fs.Pos]
if c == fs.flag {
if fs.Pos < (fs.Length-1) && fs.str[fs.Pos+1] == fs.flag {
fs.HasFlag = false
fs.AppendChar(fs.flag)
fs.Pos += 2
return fs.Next()
} else if fs.Pos != fs.Length-1 {
if fs.HasFlag {
fs.AppendString(fs.end)
}
fs.AppendString(fs.start)
fs.ChangeFlag = true
fs.HasFlag = true
}
}
}
fs.Pos++
return c, false
}
var cDateFlagToGo = map[byte]string{
'a': "mon", 'A': "Monday", 'b': "Jan", 'B': "January", 'c': "02 Jan 06 15:04 MST", 'd': "02",
'F': "2006-01-02", 'H': "15", 'I': "03", 'm': "01", 'M': "04", 'p': "PM", 'P': "pm", 'S': "05",
'x': "15/04/05", 'X': "15:04:05", 'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"}
func strftime(t time.Time, cfmt string) string {
sc := newFlagScanner('%', "", "", cfmt)
for c, eos := sc.Next(); !eos; c, eos = sc.Next() {
if !sc.ChangeFlag {
if sc.HasFlag {
if v, ok := cDateFlagToGo[c]; ok {
sc.AppendString(t.Format(v))
} else {
switch c {
case 'w':
sc.AppendString(fmt.Sprint(int(t.Weekday())))
default:
sc.AppendChar('%')
sc.AppendChar(c)
}
}
sc.HasFlag = false
} else {
sc.AppendChar(c)
}
}
}
return sc.String()
}
func isInteger(v LNumber) bool {
return float64(v) == float64(int64(v))
//_, frac := math.Modf(float64(v))
//return frac == 0.0
}
func isArrayKey(v LNumber) bool {
return isInteger(v) && v < LNumber(int((^uint(0))>>1)) && v > LNumber(0) && v < LNumber(MaxArrayIndex)
}
func parseNumber(number string) (LNumber, error) {
var value LNumber
number = strings.Trim(number, " \t\n")
if v, err := strconv.ParseInt(number, 0, LNumberBit); err != nil {
if v2, err2 := strconv.ParseFloat(number, LNumberBit); err2 != nil {
return LNumber(0), err2
} else {
value = LNumber(v2)
}
} else {
value = LNumber(v)
}
return value, nil
}
func popenArgs(arg string) (string, []string) {
cmd := "/bin/sh"
args := []string{"-c"}
if LuaOS == "windows" {
cmd = "C:\\Windows\\system32\\cmd.exe"
args = []string{"/c"}
}
args = append(args, arg)
return cmd, args
}
func isGoroutineSafe(lv LValue) bool {
switch v := lv.(type) {
case *LFunction, *LUserData, *LState:
return false
case *LTable:
return v.Metatable == LNil
default:
return true
}
}
func readBufioSize(reader *bufio.Reader, size int64) ([]byte, error, bool) {
result := []byte{}
read := int64(0)
var err error
var n int
for read != size {
buf := make([]byte, size-read)
n, err = reader.Read(buf)
if err != nil {
break
}
read += int64(n)
result = append(result, buf[:n]...)
}
e := err
if e != nil && e == io.EOF {
e = nil
}
return result, e, len(result) == 0 && err == io.EOF
}
func readBufioLine(reader *bufio.Reader) ([]byte, error, bool) {
result := []byte{}
var buf []byte
var err error
var isprefix bool = true
for isprefix {
buf, isprefix, err = reader.ReadLine()
if err != nil {
break
}
result = append(result, buf...)
}
e := err
if e != nil && e == io.EOF {
e = nil
}
return result, e, len(result) == 0 && err == io.EOF
}
func int2Fb(val int) int {
e := 0
x := val
for x >= 16 {
x = (x + 1) >> 1
e++
}
if x < 8 {
return x
}
return ((e + 1) << 3) | (x - 8)
}
func strCmp(s1, s2 string) int {
len1 := len(s1)
len2 := len(s2)
for i := 0; ; i++ {
c1 := -1
if i < len1 {
c1 = int(s1[i])
}
c2 := -1
if i != len2 {
c2 = int(s2[i])
}
switch {
case c1 < c2:
return -1
case c1 > c2:
return +1
case c1 < 0:
return 0
}
}
}
func unsafeFastStringToReadOnlyBytes(s string) (bs []byte) {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
bh.Data = sh.Data
bh.Cap = sh.Len
bh.Len = sh.Len
return
}

215
vendor/github.com/yuin/gopher-lua/value.go generated vendored Normal file
View File

@@ -0,0 +1,215 @@
package lua
import (
"context"
"fmt"
"os"
)
type LValueType int
const (
LTNil LValueType = iota
LTBool
LTNumber
LTString
LTFunction
LTUserData
LTThread
LTTable
LTChannel
)
var lValueNames = [9]string{"nil", "boolean", "number", "string", "function", "userdata", "thread", "table", "channel"}
func (vt LValueType) String() string {
return lValueNames[int(vt)]
}
type LValue interface {
String() string
Type() LValueType
}
// LVIsFalse returns true if a given LValue is a nil or false otherwise false.
func LVIsFalse(v LValue) bool { return v == LNil || v == LFalse }
// LVIsFalse returns false if a given LValue is a nil or false otherwise true.
func LVAsBool(v LValue) bool { return v != LNil && v != LFalse }
// LVAsString returns string representation of a given LValue
// if the LValue is a string or number, otherwise an empty string.
func LVAsString(v LValue) string {
switch sn := v.(type) {
case LString, LNumber:
return sn.String()
default:
return ""
}
}
// LVCanConvToString returns true if a given LValue is a string or number
// otherwise false.
func LVCanConvToString(v LValue) bool {
switch v.(type) {
case LString, LNumber:
return true
default:
return false
}
}
// LVAsNumber tries to convert a given LValue to a number.
func LVAsNumber(v LValue) LNumber {
switch lv := v.(type) {
case LNumber:
return lv
case LString:
if num, err := parseNumber(string(lv)); err == nil {
return num
}
}
return LNumber(0)
}
type LNilType struct{}
func (nl *LNilType) String() string { return "nil" }
func (nl *LNilType) Type() LValueType { return LTNil }
var LNil = LValue(&LNilType{})
type LBool bool
func (bl LBool) String() string {
if bool(bl) {
return "true"
}
return "false"
}
func (bl LBool) Type() LValueType { return LTBool }
var LTrue = LBool(true)
var LFalse = LBool(false)
type LString string
func (st LString) String() string { return string(st) }
func (st LString) Type() LValueType { return LTString }
// fmt.Formatter interface
func (st LString) Format(f fmt.State, c rune) {
switch c {
case 'd', 'i':
if nm, err := parseNumber(string(st)); err != nil {
defaultFormat(nm, f, 'd')
} else {
defaultFormat(string(st), f, 's')
}
default:
defaultFormat(string(st), f, c)
}
}
func (nm LNumber) String() string {
if isInteger(nm) {
return fmt.Sprint(int64(nm))
}
return fmt.Sprint(float64(nm))
}
func (nm LNumber) Type() LValueType { return LTNumber }
// fmt.Formatter interface
func (nm LNumber) Format(f fmt.State, c rune) {
switch c {
case 'q', 's':
defaultFormat(nm.String(), f, c)
case 'b', 'c', 'd', 'o', 'x', 'X', 'U':
defaultFormat(int64(nm), f, c)
case 'e', 'E', 'f', 'F', 'g', 'G':
defaultFormat(float64(nm), f, c)
case 'i':
defaultFormat(int64(nm), f, 'd')
default:
if isInteger(nm) {
defaultFormat(int64(nm), f, c)
} else {
defaultFormat(float64(nm), f, c)
}
}
}
type LTable struct {
Metatable LValue
array []LValue
dict map[LValue]LValue
strdict map[string]LValue
keys []LValue
k2i map[LValue]int
}
func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) }
func (tb *LTable) Type() LValueType { return LTTable }
type LFunction struct {
IsG bool
Env *LTable
Proto *FunctionProto
GFunction LGFunction
Upvalues []*Upvalue
}
type LGFunction func(*LState) int
func (fn *LFunction) String() string { return fmt.Sprintf("function: %p", fn) }
func (fn *LFunction) Type() LValueType { return LTFunction }
type Global struct {
MainThread *LState
CurrentThread *LState
Registry *LTable
Global *LTable
builtinMts map[int]LValue
tempFiles []*os.File
gccount int32
}
type LState struct {
G *Global
Parent *LState
Env *LTable
Panic func(*LState)
Dead bool
Options Options
stop int32
reg *registry
stack callFrameStack
alloc *allocator
currentFrame *callFrame
wrapped bool
uvcache *Upvalue
hasErrorFunc bool
mainLoop func(*LState, *callFrame)
ctx context.Context
ctxCancelFn context.CancelFunc
}
func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) }
func (ls *LState) Type() LValueType { return LTThread }
type LUserData struct {
Value interface{}
Env *LTable
Metatable LValue
}
func (ud *LUserData) String() string { return fmt.Sprintf("userdata: %p", ud) }
func (ud *LUserData) Type() LValueType { return LTUserData }
type LChannel chan LValue
func (ch LChannel) String() string { return fmt.Sprintf("channel: %p", ch) }
func (ch LChannel) Type() LValueType { return LTChannel }

2465
vendor/github.com/yuin/gopher-lua/vm.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

12
vendor/modules.txt vendored Normal file
View File

@@ -0,0 +1,12 @@
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/hjson/hjson-go/v4 v4.5.0
## explicit; go 1.12
github.com/hjson/hjson-go/v4
# github.com/yuin/gopher-lua v1.1.1
## explicit; go 1.17
github.com/yuin/gopher-lua
github.com/yuin/gopher-lua/ast
github.com/yuin/gopher-lua/parse
github.com/yuin/gopher-lua/pm