mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 04:51:05 +01:00
added
This commit is contained in:
23
vendor/github.com/glycerine/blake2b/README
generated
vendored
Normal file
23
vendor/github.com/glycerine/blake2b/README
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Go implementation of BLAKE2b collision-resistant cryptographic hash function
|
||||
created by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and
|
||||
Christian Winnerlein (https://blake2.net).
|
||||
|
||||
INSTALLATION
|
||||
|
||||
$ go get github.com/dchest/blake2b
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
See http://godoc.org/github.com/dchest/blake2b
|
||||
|
||||
|
||||
PUBLIC DOMAIN DEDICATION
|
||||
|
||||
Written in 2012 by Dmitry Chestnykh.
|
||||
|
||||
To the extent possible under law, the author have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
299
vendor/github.com/glycerine/blake2b/blake2b.go
generated
vendored
Normal file
299
vendor/github.com/glycerine/blake2b/blake2b.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// Written in 2012 by Dmitry Chestnykh.
|
||||
//
|
||||
// To the extent possible under law, the author have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
// Package blake2b implements BLAKE2b cryptographic hash function.
|
||||
package blake2b
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
const (
|
||||
BlockSize = 128 // block size of algorithm
|
||||
Size = 64 // maximum digest size
|
||||
SaltSize = 16 // maximum salt size
|
||||
PersonSize = 16 // maximum personalization string size
|
||||
KeySize = 64 // maximum size of key
|
||||
)
|
||||
|
||||
type digest struct {
|
||||
h [8]uint64 // current chain value
|
||||
t [2]uint64 // message bytes counter
|
||||
f [2]uint64 // finalization flags
|
||||
x [BlockSize]byte // buffer for data not yet compressed
|
||||
nx int // number of bytes in buffer
|
||||
|
||||
ih [8]uint64 // initial chain value (after config)
|
||||
paddedKey [BlockSize]byte // copy of key, padded with zeros
|
||||
isKeyed bool // indicates whether hash was keyed
|
||||
size uint8 // digest size in bytes
|
||||
isLastNode bool // indicates processing of the last node in tree hashing
|
||||
}
|
||||
|
||||
// Initialization values.
|
||||
var iv = [8]uint64{
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1, 0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||
}
|
||||
|
||||
// Config is used to configure hash function parameters and keying.
|
||||
// All parameters are optional.
|
||||
type Config struct {
|
||||
Size uint8 // digest size (if zero, default size of 64 bytes is used)
|
||||
Key []byte // key for prefix-MAC
|
||||
Salt []byte // salt (if < 16 bytes, padded with zeros)
|
||||
Person []byte // personalization (if < 16 bytes, padded with zeros)
|
||||
Tree *Tree // parameters for tree hashing
|
||||
}
|
||||
|
||||
// Tree represents parameters for tree hashing.
|
||||
type Tree struct {
|
||||
Fanout uint8 // fanout
|
||||
MaxDepth uint8 // maximal depth
|
||||
LeafSize uint32 // leaf maximal byte length (0 for unlimited)
|
||||
NodeOffset uint64 // node offset (0 for first, leftmost or leaf)
|
||||
NodeDepth uint8 // node depth (0 for leaves)
|
||||
InnerHashSize uint8 // inner hash byte length
|
||||
IsLastNode bool // indicates processing of the last node of layer
|
||||
}
|
||||
|
||||
var (
|
||||
defaultConfig = &Config{Size: Size}
|
||||
config256 = &Config{Size: 32}
|
||||
)
|
||||
|
||||
func verifyConfig(c *Config) error {
|
||||
if c.Size > Size {
|
||||
return errors.New("digest size is too large")
|
||||
}
|
||||
if len(c.Key) > KeySize {
|
||||
return errors.New("key is too large")
|
||||
}
|
||||
if len(c.Salt) > SaltSize {
|
||||
// Smaller salt is okay: it will be padded with zeros.
|
||||
return errors.New("salt is too large")
|
||||
}
|
||||
if len(c.Person) > PersonSize {
|
||||
// Smaller personalization is okay: it will be padded with zeros.
|
||||
return errors.New("personalization is too large")
|
||||
}
|
||||
if c.Tree != nil {
|
||||
if c.Tree.Fanout == 1 {
|
||||
return errors.New("fanout of 1 is not allowed in tree mode")
|
||||
}
|
||||
if c.Tree.MaxDepth < 2 {
|
||||
return errors.New("incorrect tree depth")
|
||||
}
|
||||
if c.Tree.InnerHashSize < 1 || c.Tree.InnerHashSize > Size {
|
||||
return errors.New("incorrect tree inner hash size")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash configured with the given Config.
|
||||
// Config can be nil, in which case the default one is used, calculating 64-byte digest.
|
||||
// Returns non-nil error if Config contains invalid parameters.
|
||||
func New(c *Config) (hash.Hash, error) {
|
||||
if c == nil {
|
||||
c = defaultConfig
|
||||
} else {
|
||||
if c.Size == 0 {
|
||||
// Set default size if it's zero.
|
||||
c.Size = Size
|
||||
}
|
||||
if err := verifyConfig(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d := new(digest)
|
||||
d.initialize(c)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// initialize initializes digest with the given
|
||||
// config, which must be non-nil and verified.
|
||||
func (d *digest) initialize(c *Config) {
|
||||
// Create parameter block.
|
||||
var p [BlockSize]byte
|
||||
p[0] = c.Size
|
||||
p[1] = uint8(len(c.Key))
|
||||
if c.Salt != nil {
|
||||
copy(p[32:], c.Salt)
|
||||
}
|
||||
if c.Person != nil {
|
||||
copy(p[48:], c.Person)
|
||||
}
|
||||
if c.Tree != nil {
|
||||
p[2] = c.Tree.Fanout
|
||||
p[3] = c.Tree.MaxDepth
|
||||
binary.LittleEndian.PutUint32(p[4:], c.Tree.LeafSize)
|
||||
binary.LittleEndian.PutUint64(p[8:], c.Tree.NodeOffset)
|
||||
p[16] = c.Tree.NodeDepth
|
||||
p[17] = c.Tree.InnerHashSize
|
||||
} else {
|
||||
p[2] = 1
|
||||
p[3] = 1
|
||||
}
|
||||
// Initialize.
|
||||
d.size = c.Size
|
||||
for i := 0; i < 8; i++ {
|
||||
d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(p[i*8:])
|
||||
}
|
||||
if c.Tree != nil && c.Tree.IsLastNode {
|
||||
d.isLastNode = true
|
||||
}
|
||||
// Process key.
|
||||
if c.Key != nil {
|
||||
copy(d.paddedKey[:], c.Key)
|
||||
d.Write(d.paddedKey[:])
|
||||
d.isKeyed = true
|
||||
}
|
||||
// Save a copy of initialized state.
|
||||
copy(d.ih[:], d.h[:])
|
||||
}
|
||||
|
||||
// New512 returns a new hash.Hash computing the BLAKE2b 64-byte checksum.
|
||||
func New512() hash.Hash {
|
||||
d := new(digest)
|
||||
d.initialize(defaultConfig)
|
||||
return d
|
||||
}
|
||||
|
||||
// New256 returns a new hash.Hash computing the BLAKE2b 32-byte checksum.
|
||||
func New256() hash.Hash {
|
||||
d := new(digest)
|
||||
d.initialize(config256)
|
||||
return d
|
||||
}
|
||||
|
||||
// NewMAC returns a new hash.Hash computing BLAKE2b prefix-
|
||||
// Message Authentication Code of the given size in bytes
|
||||
// (up to 64) with the given key (up to 64 bytes in length).
|
||||
func NewMAC(outBytes uint8, key []byte) hash.Hash {
|
||||
d, err := New(&Config{Size: outBytes, Key: key})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Reset resets the state of digest to the initial state
|
||||
// after configuration and keying.
|
||||
func (d *digest) Reset() {
|
||||
copy(d.h[:], d.ih[:])
|
||||
d.t[0] = 0
|
||||
d.t[1] = 0
|
||||
d.f[0] = 0
|
||||
d.f[1] = 0
|
||||
d.nx = 0
|
||||
if d.isKeyed {
|
||||
d.Write(d.paddedKey[:])
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the digest size in bytes.
|
||||
func (d *digest) Size() int { return int(d.size) }
|
||||
|
||||
// BlockSize returns the algorithm block size in bytes.
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
nn = len(p)
|
||||
left := BlockSize - d.nx
|
||||
if len(p) > left {
|
||||
// Process buffer.
|
||||
copy(d.x[d.nx:], p[:left])
|
||||
p = p[left:]
|
||||
blocks(d, d.x[:])
|
||||
d.nx = 0
|
||||
}
|
||||
// Process full blocks except for the last one.
|
||||
if len(p) > BlockSize {
|
||||
n := len(p) &^ (BlockSize - 1)
|
||||
if n == len(p) {
|
||||
n -= BlockSize
|
||||
}
|
||||
blocks(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
// Fill buffer.
|
||||
d.nx += copy(d.x[d.nx:], p)
|
||||
return
|
||||
}
|
||||
|
||||
// Sum returns the calculated checksum.
|
||||
func (d0 *digest) Sum(in []byte) []byte {
|
||||
// Make a copy of d0 so that caller can keep writing and summing.
|
||||
d := *d0
|
||||
hash := d.checkSum()
|
||||
return append(in, hash[:d.size]...)
|
||||
}
|
||||
|
||||
func (d *digest) checkSum() [Size]byte {
|
||||
// Do not create unnecessary copies of the key.
|
||||
if d.isKeyed {
|
||||
for i := 0; i < len(d.paddedKey); i++ {
|
||||
d.paddedKey[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
dec := BlockSize - uint64(d.nx)
|
||||
if d.t[0] < dec {
|
||||
d.t[1]--
|
||||
}
|
||||
d.t[0] -= dec
|
||||
|
||||
// Pad buffer with zeros.
|
||||
for i := d.nx; i < len(d.x); i++ {
|
||||
d.x[i] = 0
|
||||
}
|
||||
// Set last block flag.
|
||||
d.f[0] = 0xffffffffffffffff
|
||||
if d.isLastNode {
|
||||
d.f[1] = 0xffffffffffffffff
|
||||
}
|
||||
// Compress last block.
|
||||
blocks(d, d.x[:])
|
||||
|
||||
var out [Size]byte
|
||||
j := 0
|
||||
for _, s := range d.h[:(d.size-1)/8+1] {
|
||||
out[j+0] = byte(s >> 0)
|
||||
out[j+1] = byte(s >> 8)
|
||||
out[j+2] = byte(s >> 16)
|
||||
out[j+3] = byte(s >> 24)
|
||||
out[j+4] = byte(s >> 32)
|
||||
out[j+5] = byte(s >> 40)
|
||||
out[j+6] = byte(s >> 48)
|
||||
out[j+7] = byte(s >> 56)
|
||||
j += 8
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Sum512 returns a 64-byte BLAKE2b hash of data.
|
||||
func Sum512(data []byte) [64]byte {
|
||||
var d digest
|
||||
d.initialize(defaultConfig)
|
||||
d.Write(data)
|
||||
return d.checkSum()
|
||||
}
|
||||
|
||||
// Sum256 returns a 32-byte BLAKE2b hash of data.
|
||||
func Sum256(data []byte) (out [32]byte) {
|
||||
var d digest
|
||||
d.initialize(config256)
|
||||
d.Write(data)
|
||||
sum := d.checkSum()
|
||||
copy(out[:], sum[:32])
|
||||
return
|
||||
}
|
||||
1420
vendor/github.com/glycerine/blake2b/block.go
generated
vendored
Normal file
1420
vendor/github.com/glycerine/blake2b/block.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
25
vendor/github.com/glycerine/greenpack/LICENSE
generated
vendored
Normal file
25
vendor/github.com/glycerine/greenpack/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
MIT License
|
||||
|
||||
Portions Copyright (c) 2016 Jason E. Aten
|
||||
Portions Copyright (c) 2014 Philip Hofer
|
||||
Portions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated
|
||||
|
||||
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.
|
||||
24
vendor/github.com/glycerine/greenpack/msgp/advise_linux.go
generated
vendored
Normal file
24
vendor/github.com/glycerine/greenpack/msgp/advise_linux.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build linux,!appengine
|
||||
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func adviseRead(mem []byte) {
|
||||
syscall.Madvise(mem, syscall.MADV_SEQUENTIAL|syscall.MADV_WILLNEED)
|
||||
}
|
||||
|
||||
func adviseWrite(mem []byte) {
|
||||
syscall.Madvise(mem, syscall.MADV_SEQUENTIAL)
|
||||
}
|
||||
|
||||
func fallocate(f *os.File, sz int64) error {
|
||||
err := syscall.Fallocate(int(f.Fd()), 0, 0, sz)
|
||||
if err == syscall.ENOTSUP {
|
||||
return f.Truncate(sz)
|
||||
}
|
||||
return err
|
||||
}
|
||||
17
vendor/github.com/glycerine/greenpack/msgp/advise_other.go
generated
vendored
Normal file
17
vendor/github.com/glycerine/greenpack/msgp/advise_other.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// +build !linux appengine
|
||||
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// TODO: darwin, BSD support
|
||||
|
||||
func adviseRead(mem []byte) {}
|
||||
|
||||
func adviseWrite(mem []byte) {}
|
||||
|
||||
func fallocate(f *os.File, sz int64) error {
|
||||
return f.Truncate(sz)
|
||||
}
|
||||
15
vendor/github.com/glycerine/greenpack/msgp/appengine.go
generated
vendored
Normal file
15
vendor/github.com/glycerine/greenpack/msgp/appengine.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build appengine
|
||||
|
||||
package msgp
|
||||
|
||||
// let's just assume appengine
|
||||
// uses 64-bit hardware...
|
||||
const smallint = false
|
||||
|
||||
func UnsafeString(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func UnsafeBytes(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
39
vendor/github.com/glycerine/greenpack/msgp/circular.go
generated
vendored
Normal file
39
vendor/github.com/glycerine/greenpack/msgp/circular.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package msgp
|
||||
|
||||
type timer interface {
|
||||
StartTimer()
|
||||
StopTimer()
|
||||
}
|
||||
|
||||
// EndlessReader is an io.Reader
|
||||
// that loops over the same data
|
||||
// endlessly. It is used for benchmarking.
|
||||
type EndlessReader struct {
|
||||
tb timer
|
||||
data []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
// NewEndlessReader returns a new endless reader
|
||||
func NewEndlessReader(b []byte, tb timer) *EndlessReader {
|
||||
return &EndlessReader{tb: tb, data: b, offset: 0}
|
||||
}
|
||||
|
||||
// Read implements io.Reader. In practice, it
|
||||
// always returns (len(p), nil), although it
|
||||
// fills the supplied slice while the benchmark
|
||||
// timer is stopped.
|
||||
func (c *EndlessReader) Read(p []byte) (int, error) {
|
||||
c.tb.StopTimer()
|
||||
var n int
|
||||
l := len(p)
|
||||
m := len(c.data)
|
||||
for n < l {
|
||||
nn := copy(p[n:], c.data[c.offset:])
|
||||
n += nn
|
||||
c.offset += nn
|
||||
c.offset %= m
|
||||
}
|
||||
c.tb.StartTimer()
|
||||
return n, nil
|
||||
}
|
||||
37
vendor/github.com/glycerine/greenpack/msgp/clue.go
generated
vendored
Normal file
37
vendor/github.com/glycerine/greenpack/msgp/clue.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Clue2Field(name string, clue string, zid int64) string {
|
||||
if zid >= 0 {
|
||||
return fmt.Sprintf("%s_zid%02d_%s", name, zid, clue)
|
||||
}
|
||||
// handle the missing zid, and don't write -1 as the zid.
|
||||
return fmt.Sprintf("%s__%s", name, clue)
|
||||
}
|
||||
|
||||
func Field2Clue(fieldname string) (name string, clue string, zid int64, err error) {
|
||||
parts := strings.Split(fieldname, "_")
|
||||
n := len(parts)
|
||||
if n < 3 {
|
||||
err = fmt.Errorf("too few underscore (expect at least two) in fieldname '%s'", fieldname)
|
||||
return
|
||||
}
|
||||
clue = parts[n-1]
|
||||
if strings.HasPrefix(parts[n-2], "zid") {
|
||||
tmp, err2 := strconv.Atoi(parts[n-2][3:])
|
||||
if err2 == nil {
|
||||
zid = int64(tmp)
|
||||
} else {
|
||||
err = fmt.Errorf("problem parsing out _zid field in fieldname '%s': '%v'", fieldname, err2)
|
||||
return
|
||||
}
|
||||
}
|
||||
used := len(parts[n-1]) + len(parts[n-2]) + 2
|
||||
name = fieldname[:len(fieldname)-used]
|
||||
return
|
||||
}
|
||||
146
vendor/github.com/glycerine/greenpack/msgp/dedup.go
generated
vendored
Normal file
146
vendor/github.com/glycerine/greenpack/msgp/dedup.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var _ = fmt.Printf
|
||||
|
||||
// Methods for deduplicating repeated occurances of the same pointer.
|
||||
//
|
||||
// When writing, we track the sequence of pointers written.
|
||||
// When we see a duplicate pointer, we write the special
|
||||
// extension "duplicate" value along with the pointer's
|
||||
// occurance order in the serialization.
|
||||
//
|
||||
// As we read back, we keep a count that increments for every
|
||||
// pointer we read, and we save a map from the count to the pointer.
|
||||
// When we encounter a value that is the special value indicating reuse, then
|
||||
// we refer back to the pointer k (k being found within the special extension value)
|
||||
// and we plug in the k-th pointer instead.
|
||||
|
||||
// writer then reader methods
|
||||
|
||||
// ===============================
|
||||
// ===============================
|
||||
// Writer methods
|
||||
// ===============================
|
||||
// ===============================
|
||||
|
||||
func (mw *Writer) DedupReset() {
|
||||
mw.ptrWrit = make(map[interface{}]int)
|
||||
mw.ptrCountNext = 0
|
||||
}
|
||||
|
||||
// diagnostic
|
||||
func (mw *Writer) DedupPointerCount() int {
|
||||
return len(mw.ptrWrit)
|
||||
}
|
||||
|
||||
// upon writing each pointer, first check if it is a duplicate;
|
||||
// i.e. appears more than once, pointing to the same object.
|
||||
func (mw *Writer) DedupWriteIsDup(v interface{}) (res bool, err error) {
|
||||
defer func() {
|
||||
// This recover allows test 911 (_generated/gen_test.go:67) to run green.
|
||||
// It turns indexing by []byte msgp.Raw into a no-op. Which it
|
||||
// should be.
|
||||
if recover() != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
if v == nil || reflect.ValueOf(v).IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
k, dup := mw.ptrWrit[v]
|
||||
if !dup {
|
||||
mw.ptrWrit[v] = mw.ptrCountNext
|
||||
//fmt.Printf("\n\n $$$ NOT dup write %p -> k=%v / %#v\n\n", v, mw.ptrCountNext, v)
|
||||
mw.ptrCountNext++
|
||||
return false, nil
|
||||
} else {
|
||||
//fmt.Printf("\n\n $$$ DUP write %p -> k=%v / %#v\n\n", v, k, v)
|
||||
}
|
||||
return true, mw.DedupWriteExt(k)
|
||||
}
|
||||
|
||||
// write DedupExtension with k integer count
|
||||
// of the pointer that is duplicated here. k is
|
||||
// runtime appearance order.
|
||||
func (mw *Writer) DedupWriteExt(k int) error {
|
||||
var by [8]byte
|
||||
kby := AppendInt(by[:0], k)
|
||||
ext := RawExtension{
|
||||
Data: kby,
|
||||
Type: DedupExtension,
|
||||
}
|
||||
return mw.WriteExtension(&ext)
|
||||
}
|
||||
|
||||
// =============================
|
||||
// =============================
|
||||
// Reader side
|
||||
// =============================
|
||||
// =============================
|
||||
|
||||
func (m *Reader) DedupReadExt() (int, error) {
|
||||
ext := RawExtension{
|
||||
Type: DedupExtension,
|
||||
}
|
||||
err := m.ReadExtension(&ext)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
var nbs NilBitsStack
|
||||
k, _, err := nbs.ReadIntBytes(ext.Data)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func (r *Reader) DedupReset() {
|
||||
r.dedupPointers = r.dedupPointers[:0]
|
||||
}
|
||||
|
||||
func (r *Reader) DedupPtr(k int) interface{} {
|
||||
if k >= 0 && k < len(r.dedupPointers) {
|
||||
return r.dedupPointers[k]
|
||||
}
|
||||
panic(fmt.Sprintf("Reader.DedupPtr requested for k=%v but that was out of range! (avail=%v)", k, len(r.dedupPointers)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Reader) DedupIndexEachPtr(ptr interface{}) {
|
||||
//fmt.Printf("\n DedupIndexEachPtr called for ptr=%p/%T/val='%#v'\n", ptr, ptr, ptr)
|
||||
if ptr == nil {
|
||||
return
|
||||
}
|
||||
va := reflect.ValueOf(ptr)
|
||||
if va.IsNil() {
|
||||
return
|
||||
}
|
||||
m.dedupPointers = append(m.dedupPointers, ptr)
|
||||
//fmt.Printf("\n\n *** Reader.DedupIndexEachPtr stored ptr '%#v', as sequence k=%v\n\n", ptr, len(m.dedupPointers)-1)
|
||||
}
|
||||
|
||||
func (m *Reader) DedupReadIsDup(field, typeTarget string) (iface interface{}, res bool) {
|
||||
//fmt.Printf("\n+++ Reader.DedupReadIsDup(field:'%s', type:'%s') starting.\n", field, typeTarget)
|
||||
//defer func() {
|
||||
// fmt.Printf("\n^^^ Reader.DedupReadIsDup() returning res=%v\n", res)
|
||||
//}()
|
||||
typ, err := m.peekExtensionType()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if typ != DedupExtension {
|
||||
return nil, false
|
||||
}
|
||||
k, err := m.DedupReadExt()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
ptr := m.DedupPtr(k)
|
||||
//fmt.Printf("\n m.DedupReadIsDup() substituting, ptr= %p b/c read k=%v\n", ptr, k)
|
||||
return ptr, true
|
||||
}
|
||||
142
vendor/github.com/glycerine/greenpack/msgp/defs.go
generated
vendored
Normal file
142
vendor/github.com/glycerine/greenpack/msgp/defs.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// This package is the support library for the greenpack code generator (http://github.com/glycerine/greenpack).
|
||||
//
|
||||
// This package defines the utilites used by the greenpack code generator for encoding and decoding MessagePack
|
||||
// from []byte and io.Reader/io.Writer types. Much of this package is devoted to helping the greenpack code
|
||||
// generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces.
|
||||
//
|
||||
// This package defines four "families" of functions:
|
||||
// - AppendXxxx() appends an object to a []byte in MessagePack encoding.
|
||||
// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes.
|
||||
// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type.
|
||||
// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type.
|
||||
//
|
||||
// Once a type has satisfied the `Encodable` and `Decodable` interfaces,
|
||||
// it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using
|
||||
// msgp.Encode(io.Writer, msgp.Encodable)
|
||||
// and
|
||||
// msgp.Decode(io.Reader, msgp.Decodable)
|
||||
//
|
||||
// There are also methods for converting MessagePack to JSON without
|
||||
// an explicit de-serialization step.
|
||||
//
|
||||
// For additional tips, tricks, and gotchas, please visit
|
||||
// the wiki at http://github.com/glycerine/greenpack
|
||||
package msgp
|
||||
|
||||
const last4 = 0x0f
|
||||
const first4 = 0xf0
|
||||
const last5 = 0x1f
|
||||
const first3 = 0xe0
|
||||
const last7 = 0x7f
|
||||
|
||||
func isfixint(b byte) bool {
|
||||
return b>>7 == 0
|
||||
}
|
||||
|
||||
func isnfixint(b byte) bool {
|
||||
return b&first3 == mnfixint
|
||||
}
|
||||
|
||||
func isfixmap(b byte) bool {
|
||||
return b&first4 == mfixmap
|
||||
}
|
||||
|
||||
func isfixarray(b byte) bool {
|
||||
return b&first4 == mfixarray
|
||||
}
|
||||
|
||||
func isfixstr(b byte) bool {
|
||||
return b&first3 == mfixstr
|
||||
}
|
||||
|
||||
func wfixint(u uint8) byte {
|
||||
return u & last7
|
||||
}
|
||||
|
||||
func rfixint(b byte) uint8 {
|
||||
return b
|
||||
}
|
||||
|
||||
func wnfixint(i int8) byte {
|
||||
return byte(i) | mnfixint
|
||||
}
|
||||
|
||||
func rnfixint(b byte) int8 {
|
||||
return int8(b)
|
||||
}
|
||||
|
||||
func rfixmap(b byte) uint8 {
|
||||
return b & last4
|
||||
}
|
||||
|
||||
func wfixmap(u uint8) byte {
|
||||
return mfixmap | (u & last4)
|
||||
}
|
||||
|
||||
func rfixstr(b byte) uint8 {
|
||||
return b & last5
|
||||
}
|
||||
|
||||
func wfixstr(u uint8) byte {
|
||||
return (u & last5) | mfixstr
|
||||
}
|
||||
|
||||
func rfixarray(b byte) uint8 {
|
||||
return (b & last4)
|
||||
}
|
||||
|
||||
func wfixarray(u uint8) byte {
|
||||
return (u & last4) | mfixarray
|
||||
}
|
||||
|
||||
// These are all the byte
|
||||
// prefixes defined by the
|
||||
// msgpack standard
|
||||
const (
|
||||
// 0XXXXXXX
|
||||
mfixint uint8 = 0x00
|
||||
|
||||
// 111XXXXX
|
||||
mnfixint uint8 = 0xe0
|
||||
|
||||
// 1000XXXX
|
||||
mfixmap uint8 = 0x80
|
||||
|
||||
// 1001XXXX
|
||||
mfixarray uint8 = 0x90
|
||||
|
||||
// 101XXXXX
|
||||
mfixstr uint8 = 0xa0
|
||||
|
||||
mnil uint8 = 0xc0
|
||||
mfalse uint8 = 0xc2
|
||||
mtrue uint8 = 0xc3
|
||||
mbin8 uint8 = 0xc4
|
||||
mbin16 uint8 = 0xc5
|
||||
mbin32 uint8 = 0xc6
|
||||
mext8 uint8 = 0xc7
|
||||
mext16 uint8 = 0xc8
|
||||
mext32 uint8 = 0xc9
|
||||
mfloat32 uint8 = 0xca
|
||||
mfloat64 uint8 = 0xcb
|
||||
muint8 uint8 = 0xcc
|
||||
muint16 uint8 = 0xcd
|
||||
muint32 uint8 = 0xce
|
||||
muint64 uint8 = 0xcf
|
||||
mint8 uint8 = 0xd0
|
||||
mint16 uint8 = 0xd1
|
||||
mint32 uint8 = 0xd2
|
||||
mint64 uint8 = 0xd3
|
||||
mfixext1 uint8 = 0xd4
|
||||
mfixext2 uint8 = 0xd5
|
||||
mfixext4 uint8 = 0xd6
|
||||
mfixext8 uint8 = 0xd7
|
||||
mfixext16 uint8 = 0xd8
|
||||
mstr8 uint8 = 0xd9
|
||||
mstr16 uint8 = 0xda
|
||||
mstr32 uint8 = 0xdb
|
||||
marray16 uint8 = 0xdc
|
||||
marray32 uint8 = 0xdd
|
||||
mmap16 uint8 = 0xde
|
||||
mmap32 uint8 = 0xdf
|
||||
)
|
||||
245
vendor/github.com/glycerine/greenpack/msgp/edit.go
generated
vendored
Normal file
245
vendor/github.com/glycerine/greenpack/msgp/edit.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Locate returns a []byte pointing to the field
|
||||
// in a messagepack map with the provided key. (The returned []byte
|
||||
// points to a sub-slice of 'raw'; Locate does no allocations.) If the
|
||||
// key doesn't exist in the map, a zero-length []byte will be returned.
|
||||
func Locate(key string, raw []byte) []byte {
|
||||
s, n := locate(raw, key)
|
||||
return raw[s:n]
|
||||
}
|
||||
|
||||
// Replace takes a key ("key") in a messagepack map ("raw")
|
||||
// and replaces its value with the one provided and returns
|
||||
// the new []byte. The returned []byte may point to the same
|
||||
// memory as "raw". Replace makes no effort to evaluate the validity
|
||||
// of the contents of 'val'. It may use up to the full capacity of 'raw.'
|
||||
// Replace returns 'nil' if the field doesn't exist or if the object in 'raw'
|
||||
// is not a map.
|
||||
func Replace(key string, raw []byte, val []byte) []byte {
|
||||
start, end := locate(raw, key)
|
||||
if start == end {
|
||||
return nil
|
||||
}
|
||||
return replace(raw, start, end, val, true)
|
||||
}
|
||||
|
||||
// CopyReplace works similarly to Replace except that the returned
|
||||
// byte slice does not point to the same memory as 'raw'. CopyReplace
|
||||
// returns 'nil' if the field doesn't exist or 'raw' isn't a map.
|
||||
func CopyReplace(key string, raw []byte, val []byte) []byte {
|
||||
start, end := locate(raw, key)
|
||||
if start == end {
|
||||
return nil
|
||||
}
|
||||
return replace(raw, start, end, val, false)
|
||||
}
|
||||
|
||||
// Remove removes a key-value pair from 'raw'. It returns
|
||||
// 'raw' unchanged if the key didn't exist.
|
||||
func Remove(key string, raw []byte) []byte {
|
||||
start, end := locateKV(raw, key)
|
||||
if start == end {
|
||||
return raw
|
||||
}
|
||||
raw = raw[:start+copy(raw[start:], raw[end:])]
|
||||
return resizeMap(raw, -1)
|
||||
}
|
||||
|
||||
// HasKey returns whether the map in 'raw' has
|
||||
// a field with key 'key'
|
||||
func HasKey(key string, raw []byte) bool {
|
||||
var nbs *NilBitsStack
|
||||
sz, bts, err := nbs.ReadMapHeaderBytes(raw)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var field []byte
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
field, bts, err = nbs.ReadStringZC(bts)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if UnsafeString(field) == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func replace(raw []byte, start int, end int, val []byte, inplace bool) []byte {
|
||||
ll := end - start // length of segment to replace
|
||||
lv := len(val)
|
||||
|
||||
if inplace {
|
||||
extra := lv - ll
|
||||
|
||||
// fastest case: we're doing
|
||||
// a 1:1 replacement
|
||||
if extra == 0 {
|
||||
copy(raw[start:], val)
|
||||
return raw
|
||||
|
||||
} else if extra < 0 {
|
||||
// 'val' smaller than replaced value
|
||||
// copy in place and shift back
|
||||
|
||||
x := copy(raw[start:], val)
|
||||
y := copy(raw[start+x:], raw[end:])
|
||||
return raw[:start+x+y]
|
||||
|
||||
} else if extra < cap(raw)-len(raw) {
|
||||
// 'val' less than (cap-len) extra bytes
|
||||
// copy in place and shift forward
|
||||
raw = raw[0 : len(raw)+extra]
|
||||
// shift end forward
|
||||
copy(raw[end+extra:], raw[end:])
|
||||
copy(raw[start:], val)
|
||||
return raw
|
||||
}
|
||||
}
|
||||
|
||||
// we have to allocate new space
|
||||
out := make([]byte, len(raw)+len(val)-ll)
|
||||
x := copy(out, raw[:start])
|
||||
y := copy(out[x:], val)
|
||||
copy(out[x+y:], raw[end:])
|
||||
return out
|
||||
}
|
||||
|
||||
// locate does a naive O(n) search for the map key; returns start, end
|
||||
// (returns 0,0 on error)
|
||||
func locate(raw []byte, key string) (start int, end int) {
|
||||
var (
|
||||
sz uint32
|
||||
bts []byte
|
||||
field []byte
|
||||
err error
|
||||
)
|
||||
var nbs *NilBitsStack
|
||||
sz, bts, err = nbs.ReadMapHeaderBytes(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// loop and locate field
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
field, bts, err = nbs.ReadStringZC(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
if UnsafeString(field) == key {
|
||||
// start location
|
||||
l := len(raw)
|
||||
start = l - len(bts)
|
||||
bts, err = Skip(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
end = l - len(bts)
|
||||
return
|
||||
}
|
||||
bts, err = Skip(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// locate key AND value
|
||||
func locateKV(raw []byte, key string) (start int, end int) {
|
||||
var nbs *NilBitsStack
|
||||
|
||||
var (
|
||||
sz uint32
|
||||
bts []byte
|
||||
field []byte
|
||||
err error
|
||||
)
|
||||
sz, bts, err = nbs.ReadMapHeaderBytes(raw)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
tmp := len(bts)
|
||||
field, bts, err = nbs.ReadStringZC(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
if UnsafeString(field) == key {
|
||||
start = len(raw) - tmp
|
||||
bts, err = Skip(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
end = len(raw) - len(bts)
|
||||
return
|
||||
}
|
||||
bts, err = Skip(bts)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// delta is delta on map size
|
||||
func resizeMap(raw []byte, delta int64) []byte {
|
||||
var sz int64
|
||||
switch raw[0] {
|
||||
case mmap16:
|
||||
sz = int64(big.Uint16(raw[1:]))
|
||||
if sz+delta <= math.MaxUint16 {
|
||||
big.PutUint16(raw[1:], uint16(sz+delta))
|
||||
return raw
|
||||
}
|
||||
if cap(raw)-len(raw) >= 2 {
|
||||
raw = raw[0 : len(raw)+2]
|
||||
copy(raw[5:], raw[3:])
|
||||
big.PutUint32(raw[1:], uint32(sz+delta))
|
||||
return raw
|
||||
}
|
||||
n := make([]byte, 0, len(raw)+5)
|
||||
n = AppendMapHeader(n, uint32(sz+delta))
|
||||
return append(n, raw[3:]...)
|
||||
|
||||
case mmap32:
|
||||
sz = int64(big.Uint32(raw[1:]))
|
||||
big.PutUint32(raw[1:], uint32(sz+delta))
|
||||
return raw
|
||||
|
||||
default:
|
||||
sz = int64(rfixmap(raw[0]))
|
||||
if sz+delta < 16 {
|
||||
raw[0] = wfixmap(uint8(sz + delta))
|
||||
return raw
|
||||
} else if sz+delta <= math.MaxUint16 {
|
||||
if cap(raw)-len(raw) >= 2 {
|
||||
raw = raw[0 : len(raw)+2]
|
||||
copy(raw[3:], raw[1:])
|
||||
raw[0] = mmap16
|
||||
big.PutUint16(raw[1:], uint16(sz+delta))
|
||||
return raw
|
||||
}
|
||||
n := make([]byte, 0, len(raw)+5)
|
||||
n = AppendMapHeader(n, uint32(sz+delta))
|
||||
return append(n, raw[1:]...)
|
||||
}
|
||||
if cap(raw)-len(raw) >= 4 {
|
||||
raw = raw[0 : len(raw)+4]
|
||||
copy(raw[5:], raw[1:])
|
||||
raw[0] = mmap32
|
||||
big.PutUint32(raw[1:], uint32(sz+delta))
|
||||
return raw
|
||||
}
|
||||
n := make([]byte, 0, len(raw)+5)
|
||||
n = AppendMapHeader(n, uint32(sz+delta))
|
||||
return append(n, raw[1:]...)
|
||||
}
|
||||
}
|
||||
99
vendor/github.com/glycerine/greenpack/msgp/elsize.go
generated
vendored
Normal file
99
vendor/github.com/glycerine/greenpack/msgp/elsize.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package msgp
|
||||
|
||||
// size of every object on the wire,
|
||||
// plus type information. gives us
|
||||
// constant-time type information
|
||||
// for traversing composite objects.
|
||||
//
|
||||
var sizes = [256]bytespec{
|
||||
mnil: {size: 1, extra: constsize, typ: NilType},
|
||||
mfalse: {size: 1, extra: constsize, typ: BoolType},
|
||||
mtrue: {size: 1, extra: constsize, typ: BoolType},
|
||||
mbin8: {size: 2, extra: extra8, typ: BinType},
|
||||
mbin16: {size: 3, extra: extra16, typ: BinType},
|
||||
mbin32: {size: 5, extra: extra32, typ: BinType},
|
||||
mext8: {size: 3, extra: extra8, typ: ExtensionType},
|
||||
mext16: {size: 4, extra: extra16, typ: ExtensionType},
|
||||
mext32: {size: 6, extra: extra32, typ: ExtensionType},
|
||||
mfloat32: {size: 5, extra: constsize, typ: Float32Type},
|
||||
mfloat64: {size: 9, extra: constsize, typ: Float64Type},
|
||||
muint8: {size: 2, extra: constsize, typ: UintType},
|
||||
muint16: {size: 3, extra: constsize, typ: UintType},
|
||||
muint32: {size: 5, extra: constsize, typ: UintType},
|
||||
muint64: {size: 9, extra: constsize, typ: UintType},
|
||||
mint8: {size: 2, extra: constsize, typ: IntType},
|
||||
mint16: {size: 3, extra: constsize, typ: IntType},
|
||||
mint32: {size: 5, extra: constsize, typ: IntType},
|
||||
mint64: {size: 9, extra: constsize, typ: IntType},
|
||||
mfixext1: {size: 3, extra: constsize, typ: ExtensionType},
|
||||
mfixext2: {size: 4, extra: constsize, typ: ExtensionType},
|
||||
mfixext4: {size: 6, extra: constsize, typ: ExtensionType},
|
||||
mfixext8: {size: 10, extra: constsize, typ: ExtensionType},
|
||||
mfixext16: {size: 18, extra: constsize, typ: ExtensionType},
|
||||
mstr8: {size: 2, extra: extra8, typ: StrType},
|
||||
mstr16: {size: 3, extra: extra16, typ: StrType},
|
||||
mstr32: {size: 5, extra: extra32, typ: StrType},
|
||||
marray16: {size: 3, extra: array16v, typ: ArrayType},
|
||||
marray32: {size: 5, extra: array32v, typ: ArrayType},
|
||||
mmap16: {size: 3, extra: map16v, typ: MapType},
|
||||
mmap32: {size: 5, extra: map32v, typ: MapType},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// set up fixed fields
|
||||
|
||||
// fixint
|
||||
for i := mfixint; i < 0x80; i++ {
|
||||
sizes[i] = bytespec{size: 1, extra: constsize, typ: IntType}
|
||||
}
|
||||
|
||||
// nfixint
|
||||
for i := uint16(mnfixint); i < 0x100; i++ {
|
||||
sizes[uint8(i)] = bytespec{size: 1, extra: constsize, typ: IntType}
|
||||
}
|
||||
|
||||
// fixstr gets constsize,
|
||||
// since the prefix yields the size
|
||||
for i := mfixstr; i < 0xc0; i++ {
|
||||
sizes[i] = bytespec{size: 1 + rfixstr(i), extra: constsize, typ: StrType}
|
||||
}
|
||||
|
||||
// fixmap
|
||||
for i := mfixmap; i < 0x90; i++ {
|
||||
sizes[i] = bytespec{size: 1, extra: varmode(2 * rfixmap(i)), typ: MapType}
|
||||
}
|
||||
|
||||
// fixarray
|
||||
for i := mfixarray; i < 0xa0; i++ {
|
||||
sizes[i] = bytespec{size: 1, extra: varmode(rfixarray(i)), typ: ArrayType}
|
||||
}
|
||||
}
|
||||
|
||||
// a valid bytespsec has
|
||||
// non-zero 'size' and
|
||||
// non-zero 'typ'
|
||||
type bytespec struct {
|
||||
size uint8 // prefix size information
|
||||
extra varmode // extra size information
|
||||
typ Type // type
|
||||
_ byte // makes bytespec 4 bytes (yes, this matters)
|
||||
}
|
||||
|
||||
// size mode
|
||||
// if positive, # elements for composites
|
||||
type varmode int8
|
||||
|
||||
const (
|
||||
constsize varmode = 0 // constant size (size bytes + uint8(varmode) objects)
|
||||
extra8 = -1 // has uint8(p[1]) extra bytes
|
||||
extra16 = -2 // has be16(p[1:]) extra bytes
|
||||
extra32 = -3 // has be32(p[1:]) extra bytes
|
||||
map16v = -4 // use map16
|
||||
map32v = -5 // use map32
|
||||
array16v = -6 // use array16
|
||||
array32v = -7 // use array32
|
||||
)
|
||||
|
||||
func getType(v byte) Type {
|
||||
return sizes[v].typ
|
||||
}
|
||||
142
vendor/github.com/glycerine/greenpack/msgp/errors.go
generated
vendored
Normal file
142
vendor/github.com/glycerine/greenpack/msgp/errors.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrShortBytes is returned when the
|
||||
// slice being decoded is too short to
|
||||
// contain the contents of the message
|
||||
ErrShortBytes error = errShort{}
|
||||
|
||||
// this error is only returned
|
||||
// if we reach code that should
|
||||
// be unreachable
|
||||
fatal error = errFatal{}
|
||||
)
|
||||
|
||||
// Error is the interface satisfied
|
||||
// by all of the errors that originate
|
||||
// from this package.
|
||||
type Error interface {
|
||||
error
|
||||
|
||||
// Resumable returns whether
|
||||
// or not the error means that
|
||||
// the stream of data is malformed
|
||||
// and the information is unrecoverable.
|
||||
Resumable() bool
|
||||
}
|
||||
|
||||
type errShort struct{}
|
||||
|
||||
func (e errShort) Error() string { return "msgp: too few bytes left to read object" }
|
||||
func (e errShort) Resumable() bool { return false }
|
||||
|
||||
type errFatal struct{}
|
||||
|
||||
func (f errFatal) Error() string { return "msgp: fatal decoding error (unreachable code)" }
|
||||
func (f errFatal) Resumable() bool { return false }
|
||||
|
||||
// ArrayError is an error returned
|
||||
// when decoding a fix-sized array
|
||||
// of the wrong size
|
||||
type ArrayError struct {
|
||||
Wanted uint32
|
||||
Got uint32
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (a ArrayError) Error() string {
|
||||
return fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got)
|
||||
}
|
||||
|
||||
// Resumable is always 'true' for ArrayErrors
|
||||
func (a ArrayError) Resumable() bool { return true }
|
||||
|
||||
// IntOverflow is returned when a call
|
||||
// would downcast an integer to a type
|
||||
// with too few bits to hold its value.
|
||||
type IntOverflow struct {
|
||||
Value int64 // the value of the integer
|
||||
FailedBitsize int // the bit size that the int64 could not fit into
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (i IntOverflow) Error() string {
|
||||
return fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize)
|
||||
}
|
||||
|
||||
// Resumable is always 'true' for overflows
|
||||
func (i IntOverflow) Resumable() bool { return true }
|
||||
|
||||
// UintOverflow is returned when a call
|
||||
// would downcast an unsigned integer to a type
|
||||
// with too few bits to hold its value
|
||||
type UintOverflow struct {
|
||||
Value uint64 // value of the uint
|
||||
FailedBitsize int // the bit size that couldn't fit the value
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (u UintOverflow) Error() string {
|
||||
return fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize)
|
||||
}
|
||||
|
||||
// Resumable is always 'true' for overflows
|
||||
func (u UintOverflow) Resumable() bool { return true }
|
||||
|
||||
// A TypeError is returned when a particular
|
||||
// decoding method is unsuitable for decoding
|
||||
// a particular MessagePack value.
|
||||
type TypeError struct {
|
||||
Method Type // Type expected by method
|
||||
Encoded Type // Type actually encoded
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (t TypeError) Error() string {
|
||||
return fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method)
|
||||
}
|
||||
|
||||
// Resumable returns 'true' for TypeErrors
|
||||
func (t TypeError) Resumable() bool { return true }
|
||||
|
||||
// returns either InvalidPrefixError or
|
||||
// TypeError depending on whether or not
|
||||
// the prefix is recognized
|
||||
func badPrefix(want Type, lead byte) error {
|
||||
t := sizes[lead].typ
|
||||
if t == InvalidType {
|
||||
return InvalidPrefixError(lead)
|
||||
}
|
||||
return TypeError{Method: want, Encoded: t}
|
||||
}
|
||||
|
||||
// InvalidPrefixError is returned when a bad encoding
|
||||
// uses a prefix that is not recognized in the MessagePack standard.
|
||||
// This kind of error is unrecoverable.
|
||||
type InvalidPrefixError byte
|
||||
|
||||
// Error implements the error interface
|
||||
func (i InvalidPrefixError) Error() string {
|
||||
return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i))
|
||||
}
|
||||
|
||||
// Resumable returns 'false' for InvalidPrefixErrors
|
||||
func (i InvalidPrefixError) Resumable() bool { return false }
|
||||
|
||||
// ErrUnsupportedType is returned
|
||||
// when a bad argument is supplied
|
||||
// to a function that takes `interface{}`.
|
||||
type ErrUnsupportedType struct {
|
||||
T reflect.Type
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (e *ErrUnsupportedType) Error() string { return fmt.Sprintf("msgp: type %q not supported(4)", e.T) }
|
||||
|
||||
// Resumable returns 'true' for ErrUnsupportedType
|
||||
func (e *ErrUnsupportedType) Resumable() bool { return true }
|
||||
559
vendor/github.com/glycerine/greenpack/msgp/extension.go
generated
vendored
Normal file
559
vendor/github.com/glycerine/greenpack/msgp/extension.go
generated
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
// Complex64Extension is the extension number used for complex64
|
||||
Complex64Extension = 3
|
||||
|
||||
// Complex128Extension is the extension number used for complex128
|
||||
Complex128Extension = 4
|
||||
|
||||
// TimeExtension is the extension number used for time.Time
|
||||
TimeExtension = 5
|
||||
|
||||
// DedupExtension allows us to avoid cycles and avoid excess
|
||||
// space consumption for graphs that are not strictly trees.
|
||||
DedupExtension = 6
|
||||
|
||||
// DurationExtension is used for time.Duration
|
||||
DurationExtension = 7
|
||||
)
|
||||
|
||||
// our extensions live here
|
||||
var extensionReg = make(map[int8]func() Extension)
|
||||
|
||||
// RegisterExtension registers extensions so that they
|
||||
// can be initialized and returned by methods that
|
||||
// decode `interface{}` values. This should only
|
||||
// be called during initialization. f() should return
|
||||
// a newly-initialized zero value of the extension. Keep in
|
||||
// mind that extensions 3, 4, and 5 are reserved for
|
||||
// complex64, complex128, and time.Time, respectively,
|
||||
// and that MessagePack reserves extension types from -127 to -1.
|
||||
//
|
||||
// For example, if you wanted to register a user-defined struct:
|
||||
//
|
||||
// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} })
|
||||
//
|
||||
// RegisterExtension will panic if you call it multiple times
|
||||
// with the same 'typ' argument, or if you use a reserved
|
||||
// type (3, 4, 5, 6, 7).
|
||||
func RegisterExtension(typ int8, f func() Extension) {
|
||||
switch typ {
|
||||
case Complex64Extension, Complex128Extension, TimeExtension, DedupExtension, DurationExtension:
|
||||
panic(fmt.Sprint("msgp: forbidden extension type:", typ))
|
||||
}
|
||||
if _, ok := extensionReg[typ]; ok {
|
||||
panic(fmt.Sprint("msgp: RegisterExtension() called with typ", typ, "more than once"))
|
||||
}
|
||||
extensionReg[typ] = f
|
||||
}
|
||||
|
||||
// ExtensionTypeError is an error type returned
|
||||
// when there is a mis-match between an extension type
|
||||
// and the type encoded on the wire
|
||||
type ExtensionTypeError struct {
|
||||
Got int8
|
||||
Want int8
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e ExtensionTypeError) Error() string {
|
||||
return fmt.Sprintf("msgp: error decoding extension: wanted type %d; got type %d", e.Want, e.Got)
|
||||
}
|
||||
|
||||
// Resumable returns 'true' for ExtensionTypeErrors
|
||||
func (e ExtensionTypeError) Resumable() bool { return true }
|
||||
|
||||
func errExt(got int8, wanted int8) error {
|
||||
return ExtensionTypeError{Got: got, Want: wanted}
|
||||
}
|
||||
|
||||
// Extension is the interface fulfilled
|
||||
// by types that want to define their
|
||||
// own binary encoding.
|
||||
type Extension interface {
|
||||
// ExtensionType should return
|
||||
// a int8 that identifies the concrete
|
||||
// type of the extension. (Types <0 are
|
||||
// officially reserved by the MessagePack
|
||||
// specifications.)
|
||||
ExtensionType() int8
|
||||
|
||||
// Len should return the length
|
||||
// of the data to be encoded
|
||||
Len() int
|
||||
|
||||
// MarshalBinaryTo should copy
|
||||
// the data into the supplied slice,
|
||||
// assuming that the slice has length Len()
|
||||
MarshalBinaryTo([]byte) error
|
||||
|
||||
UnmarshalBinary([]byte) error
|
||||
}
|
||||
|
||||
// RawExtension implements the Extension interface
|
||||
type RawExtension struct {
|
||||
Data []byte
|
||||
Type int8
|
||||
}
|
||||
|
||||
// ExtensionType implements Extension.ExtensionType, and returns r.Type
|
||||
func (r *RawExtension) ExtensionType() int8 { return r.Type }
|
||||
|
||||
// Len implements Extension.Len, and returns len(r.Data)
|
||||
func (r *RawExtension) Len() int { return len(r.Data) }
|
||||
|
||||
// MarshalBinaryTo implements Extension.MarshalBinaryTo,
|
||||
// and returns a copy of r.Data
|
||||
func (r *RawExtension) MarshalBinaryTo(d []byte) error {
|
||||
copy(d, r.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements Extension.UnmarshalBinary,
|
||||
// and sets r.Data to the contents of the provided slice
|
||||
func (r *RawExtension) UnmarshalBinary(b []byte) error {
|
||||
if cap(r.Data) >= len(b) {
|
||||
r.Data = r.Data[0:len(b)]
|
||||
} else {
|
||||
r.Data = make([]byte, len(b))
|
||||
}
|
||||
copy(r.Data, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteExtension writes an extension type to the writer
|
||||
func (mw *Writer) WriteExtension(e Extension) error {
|
||||
l := e.Len()
|
||||
var err error
|
||||
switch l {
|
||||
case 0:
|
||||
o, err := mw.require(3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext8
|
||||
mw.buf[o+1] = 0
|
||||
mw.buf[o+2] = byte(e.ExtensionType())
|
||||
case 1:
|
||||
o, err := mw.require(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext1
|
||||
mw.buf[o+1] = byte(e.ExtensionType())
|
||||
case 2:
|
||||
o, err := mw.require(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext2
|
||||
mw.buf[o+1] = byte(e.ExtensionType())
|
||||
case 4:
|
||||
o, err := mw.require(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext4
|
||||
mw.buf[o+1] = byte(e.ExtensionType())
|
||||
case 8:
|
||||
o, err := mw.require(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext8
|
||||
mw.buf[o+1] = byte(e.ExtensionType())
|
||||
case 16:
|
||||
o, err := mw.require(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext16
|
||||
mw.buf[o+1] = byte(e.ExtensionType())
|
||||
default:
|
||||
switch {
|
||||
case l < math.MaxUint8:
|
||||
o, err := mw.require(3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext8
|
||||
mw.buf[o+1] = byte(uint8(l))
|
||||
mw.buf[o+2] = byte(e.ExtensionType())
|
||||
case l < math.MaxUint16:
|
||||
o, err := mw.require(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext16
|
||||
big.PutUint16(mw.buf[o+1:], uint16(l))
|
||||
mw.buf[o+3] = byte(e.ExtensionType())
|
||||
default:
|
||||
o, err := mw.require(6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext32
|
||||
big.PutUint32(mw.buf[o+1:], uint32(l))
|
||||
mw.buf[o+5] = byte(e.ExtensionType())
|
||||
}
|
||||
}
|
||||
// we can only write directly to the
|
||||
// buffer if we're sure that it
|
||||
// fits the object
|
||||
if l <= mw.bufsize() {
|
||||
o, err := mw.require(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.MarshalBinaryTo(mw.buf[o:])
|
||||
}
|
||||
// here we create a new buffer
|
||||
// just large enough for the body
|
||||
// and save it as the write buffer
|
||||
err = mw.flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := make([]byte, l)
|
||||
err = e.MarshalBinaryTo(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf = buf
|
||||
mw.wloc = l
|
||||
return nil
|
||||
}
|
||||
|
||||
// peek at the extension type, assuming the next
|
||||
// kind to be read is Extension
|
||||
func (m *Reader) peekExtensionType() (int8, error) {
|
||||
p, err := m.R.Peek(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
spec := sizes[p[0]]
|
||||
if spec.typ != ExtensionType {
|
||||
return 0, badPrefix(ExtensionType, p[0])
|
||||
}
|
||||
if spec.extra == constsize {
|
||||
return int8(p[1]), nil
|
||||
}
|
||||
size := spec.size
|
||||
p, err = m.R.Peek(int(size))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int8(p[size-1]), nil
|
||||
}
|
||||
|
||||
// peekExtension peeks at the extension encoding type
|
||||
// (must guarantee at least 1 byte in 'b')
|
||||
func peekExtension(b []byte) (int8, error) {
|
||||
spec := sizes[b[0]]
|
||||
size := spec.size
|
||||
if spec.typ != ExtensionType {
|
||||
return 0, badPrefix(ExtensionType, b[0])
|
||||
}
|
||||
if len(b) < int(size) {
|
||||
return 0, ErrShortBytes
|
||||
}
|
||||
// for fixed extensions,
|
||||
// the type information is in
|
||||
// the second byte
|
||||
if spec.extra == constsize {
|
||||
return int8(b[1]), nil
|
||||
}
|
||||
// otherwise, it's in the last
|
||||
// part of the prefix
|
||||
return int8(b[size-1]), nil
|
||||
}
|
||||
|
||||
// ReadExtension reads the next object from the reader
|
||||
// as an extension. ReadExtension will fail if the next
|
||||
// object in the stream is not an extension, or if
|
||||
// e.Type() is not the same as the wire type.
|
||||
func (m *Reader) ReadExtension(e Extension) (err error) {
|
||||
var p []byte
|
||||
p, err = m.R.Peek(2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lead := p[0]
|
||||
var read int
|
||||
var off int
|
||||
switch lead {
|
||||
case mfixext1:
|
||||
if int8(p[1]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[1]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
p, err = m.R.Peek(3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[2:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(3)
|
||||
}
|
||||
return
|
||||
|
||||
case mfixext2:
|
||||
if int8(p[1]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[1]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
p, err = m.R.Peek(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[2:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(4)
|
||||
}
|
||||
return
|
||||
|
||||
case mfixext4:
|
||||
if int8(p[1]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[1]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
p, err = m.R.Peek(6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[2:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(6)
|
||||
}
|
||||
return
|
||||
|
||||
case mfixext8:
|
||||
if int8(p[1]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[1]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
p, err = m.R.Peek(10)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[2:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(10)
|
||||
}
|
||||
return
|
||||
|
||||
case mfixext16:
|
||||
if int8(p[1]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[1]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
p, err = m.R.Peek(18)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[2:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(18)
|
||||
}
|
||||
return
|
||||
|
||||
case mext8:
|
||||
p, err = m.R.Peek(3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if int8(p[2]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[2]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
read = int(uint8(p[1]))
|
||||
off = 3
|
||||
|
||||
case mext16:
|
||||
p, err = m.R.Peek(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if int8(p[3]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[3]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
read = int(big.Uint16(p[1:]))
|
||||
off = 4
|
||||
|
||||
case mext32:
|
||||
p, err = m.R.Peek(6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if int8(p[5]) != e.ExtensionType() {
|
||||
err = errExt(int8(p[5]), e.ExtensionType())
|
||||
return
|
||||
}
|
||||
read = int(big.Uint32(p[1:]))
|
||||
off = 6
|
||||
|
||||
default:
|
||||
err = badPrefix(ExtensionType, lead)
|
||||
return
|
||||
}
|
||||
|
||||
p, err = m.R.Peek(read + off)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = e.UnmarshalBinary(p[off:])
|
||||
if err == nil {
|
||||
_, err = m.R.Skip(read + off)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AppendExtension appends a MessagePack extension to the provided slice
|
||||
func AppendExtension(b []byte, e Extension) ([]byte, error) {
|
||||
l := e.Len()
|
||||
var o []byte
|
||||
var n int
|
||||
switch l {
|
||||
case 0:
|
||||
o, n = ensure(b, 3)
|
||||
o[n] = mext8
|
||||
o[n+1] = 0
|
||||
o[n+2] = byte(e.ExtensionType())
|
||||
return o[:n+3], nil
|
||||
case 1:
|
||||
o, n = ensure(b, 3)
|
||||
o[n] = mfixext1
|
||||
o[n+1] = byte(e.ExtensionType())
|
||||
n += 2
|
||||
case 2:
|
||||
o, n = ensure(b, 4)
|
||||
o[n] = mfixext2
|
||||
o[n+1] = byte(e.ExtensionType())
|
||||
n += 2
|
||||
case 4:
|
||||
o, n = ensure(b, 6)
|
||||
o[n] = mfixext4
|
||||
o[n+1] = byte(e.ExtensionType())
|
||||
n += 2
|
||||
case 8:
|
||||
o, n = ensure(b, 10)
|
||||
o[n] = mfixext8
|
||||
o[n+1] = byte(e.ExtensionType())
|
||||
n += 2
|
||||
case 16:
|
||||
o, n = ensure(b, 18)
|
||||
o[n] = mfixext16
|
||||
o[n+1] = byte(e.ExtensionType())
|
||||
n += 2
|
||||
}
|
||||
switch {
|
||||
case l < math.MaxUint8:
|
||||
o, n = ensure(b, l+3)
|
||||
o[n] = mext8
|
||||
o[n+1] = byte(uint8(l))
|
||||
o[n+2] = byte(e.ExtensionType())
|
||||
n += 3
|
||||
case l < math.MaxUint16:
|
||||
o, n = ensure(b, l+4)
|
||||
o[n] = mext16
|
||||
big.PutUint16(o[n+1:], uint16(l))
|
||||
o[n+3] = byte(e.ExtensionType())
|
||||
n += 4
|
||||
default:
|
||||
o, n = ensure(b, l+6)
|
||||
o[n] = mext32
|
||||
big.PutUint32(o[n+1:], uint32(l))
|
||||
o[n+5] = byte(e.ExtensionType())
|
||||
n += 6
|
||||
}
|
||||
return o, e.MarshalBinaryTo(o[n:])
|
||||
}
|
||||
|
||||
// ReadExtensionBytes reads an extension from 'b' into 'e'
|
||||
// and returns any remaining bytes.
|
||||
// Possible errors:
|
||||
// - ErrShortBytes ('b' not long enough)
|
||||
// - ExtensionTypeErorr{} (wire type not the same as e.Type())
|
||||
// - TypeErorr{} (next object not an extension)
|
||||
// - InvalidPrefixError
|
||||
// - An umarshal error returned from e.UnmarshalBinary
|
||||
func (nbs *NilBitsStack) ReadExtensionBytes(b []byte, e Extension) ([]byte, error) {
|
||||
if nbs != nil && nbs.AlwaysNil {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
l := len(b)
|
||||
if l < 3 {
|
||||
return b, ErrShortBytes
|
||||
}
|
||||
lead := b[0]
|
||||
var (
|
||||
sz int // size of 'data'
|
||||
off int // offset of 'data'
|
||||
typ int8
|
||||
)
|
||||
switch lead {
|
||||
case mfixext1:
|
||||
typ = int8(b[1])
|
||||
sz = 1
|
||||
off = 2
|
||||
case mfixext2:
|
||||
typ = int8(b[1])
|
||||
sz = 2
|
||||
off = 2
|
||||
case mfixext4:
|
||||
typ = int8(b[1])
|
||||
sz = 4
|
||||
off = 2
|
||||
case mfixext8:
|
||||
typ = int8(b[1])
|
||||
sz = 8
|
||||
off = 2
|
||||
case mfixext16:
|
||||
typ = int8(b[1])
|
||||
sz = 16
|
||||
off = 2
|
||||
case mext8:
|
||||
sz = int(uint8(b[1]))
|
||||
typ = int8(b[2])
|
||||
off = 3
|
||||
if sz == 0 {
|
||||
return b[3:], e.UnmarshalBinary(b[3:3])
|
||||
}
|
||||
case mext16:
|
||||
if l < 4 {
|
||||
return b, ErrShortBytes
|
||||
}
|
||||
sz = int(big.Uint16(b[1:]))
|
||||
typ = int8(b[3])
|
||||
off = 4
|
||||
case mext32:
|
||||
if l < 6 {
|
||||
return b, ErrShortBytes
|
||||
}
|
||||
sz = int(big.Uint32(b[1:]))
|
||||
typ = int8(b[5])
|
||||
off = 6
|
||||
default:
|
||||
return b, badPrefix(ExtensionType, lead)
|
||||
}
|
||||
|
||||
if typ != e.ExtensionType() {
|
||||
return b, errExt(typ, e.ExtensionType())
|
||||
}
|
||||
|
||||
// the data of the extension starts
|
||||
// at 'off' and is 'sz' bytes long
|
||||
if len(b[off:]) < sz {
|
||||
return b, ErrShortBytes
|
||||
}
|
||||
tot := off + sz
|
||||
return b[tot:], e.UnmarshalBinary(b[off:tot])
|
||||
}
|
||||
91
vendor/github.com/glycerine/greenpack/msgp/file.go
generated
vendored
Normal file
91
vendor/github.com/glycerine/greenpack/msgp/file.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// +build linux,!appengine darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ReadFile reads a file into 'dst' using
|
||||
// a read-only memory mapping. Consequently,
|
||||
// the file must be mmap-able, and the
|
||||
// Unmarshaler should never write to
|
||||
// the source memory. (Methods generated
|
||||
// by the msgp tool obey that constraint, but
|
||||
// user-defined implementations may not.)
|
||||
//
|
||||
// Reading and writing through file mappings
|
||||
// is only efficient for large files; small
|
||||
// files are best read and written using
|
||||
// the ordinary streaming interfaces.
|
||||
//
|
||||
func ReadFile(dst Unmarshaler, file *os.File) error {
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := syscall.Mmap(int(file.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adviseRead(data)
|
||||
_, err = dst.UnmarshalMsg(data)
|
||||
uerr := syscall.Munmap(data)
|
||||
if err == nil {
|
||||
err = uerr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalSizer is the combination
|
||||
// of the Marshaler and Sizer
|
||||
// interfaces.
|
||||
type MarshalSizer interface {
|
||||
Marshaler
|
||||
Sizer
|
||||
}
|
||||
|
||||
// WriteFile writes a file from 'src' using
|
||||
// memory mapping. It overwrites the entire
|
||||
// contents of the previous file.
|
||||
// The mapping size is calculated
|
||||
// using the `Msgsize()` method
|
||||
// of 'src', so it must produce a result
|
||||
// equal to or greater than the actual encoded
|
||||
// size of the object. Otherwise,
|
||||
// a fault (SIGBUS) will occur.
|
||||
//
|
||||
// Reading and writing through file mappings
|
||||
// is only efficient for large files; small
|
||||
// files are best read and written using
|
||||
// the ordinary streaming interfaces.
|
||||
//
|
||||
// NOTE: The performance of this call
|
||||
// is highly OS- and filesystem-dependent.
|
||||
// Users should take care to test that this
|
||||
// performs as expected in a production environment.
|
||||
// (Linux users should run a kernel and filesystem
|
||||
// that support fallocate(2) for the best results.)
|
||||
func WriteFile(src MarshalSizer, file *os.File) error {
|
||||
sz := src.Msgsize()
|
||||
err := fallocate(file, int64(sz))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := syscall.Mmap(int(file.Fd()), 0, sz, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adviseWrite(data)
|
||||
chunk := data[:0]
|
||||
chunk, err = src.MarshalMsg(chunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uerr := syscall.Munmap(data)
|
||||
if uerr != nil {
|
||||
return uerr
|
||||
}
|
||||
return file.Truncate(int64(len(chunk)))
|
||||
}
|
||||
47
vendor/github.com/glycerine/greenpack/msgp/file_port.go
generated
vendored
Normal file
47
vendor/github.com/glycerine/greenpack/msgp/file_port.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// +build windows appengine
|
||||
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// MarshalSizer is the combination
|
||||
// of the Marshaler and Sizer
|
||||
// interfaces.
|
||||
type MarshalSizer interface {
|
||||
Marshaler
|
||||
Sizer
|
||||
}
|
||||
|
||||
func ReadFile(dst Unmarshaler, file *os.File) error {
|
||||
if u, ok := dst.(Decodable); ok {
|
||||
return u.DecodeMsg(NewReader(file))
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dst.UnmarshalMsg(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func WriteFile(src MarshalSizer, file *os.File) error {
|
||||
if e, ok := src.(Encodable); ok {
|
||||
w := NewWriter(file)
|
||||
err := e.EncodeMsg(w)
|
||||
if err == nil {
|
||||
err = w.Flush()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
raw, err := src.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.Write(raw)
|
||||
return err
|
||||
}
|
||||
174
vendor/github.com/glycerine/greenpack/msgp/integers.go
generated
vendored
Normal file
174
vendor/github.com/glycerine/greenpack/msgp/integers.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package msgp
|
||||
|
||||
/* ----------------------------------
|
||||
integer encoding utilities
|
||||
(inline-able)
|
||||
|
||||
TODO(tinylib): there are faster,
|
||||
albeit non-portable solutions
|
||||
to the code below. implement
|
||||
byteswap?
|
||||
---------------------------------- */
|
||||
|
||||
func putMint64(b []byte, i int64) {
|
||||
b[0] = mint64
|
||||
b[1] = byte(i >> 56)
|
||||
b[2] = byte(i >> 48)
|
||||
b[3] = byte(i >> 40)
|
||||
b[4] = byte(i >> 32)
|
||||
b[5] = byte(i >> 24)
|
||||
b[6] = byte(i >> 16)
|
||||
b[7] = byte(i >> 8)
|
||||
b[8] = byte(i)
|
||||
}
|
||||
|
||||
func getMint64(b []byte) int64 {
|
||||
return (int64(b[1]) << 56) | (int64(b[2]) << 48) |
|
||||
(int64(b[3]) << 40) | (int64(b[4]) << 32) |
|
||||
(int64(b[5]) << 24) | (int64(b[6]) << 16) |
|
||||
(int64(b[7]) << 8) | (int64(b[8]))
|
||||
}
|
||||
|
||||
func putMint32(b []byte, i int32) {
|
||||
b[0] = mint32
|
||||
b[1] = byte(i >> 24)
|
||||
b[2] = byte(i >> 16)
|
||||
b[3] = byte(i >> 8)
|
||||
b[4] = byte(i)
|
||||
}
|
||||
|
||||
func getMint32(b []byte) int32 {
|
||||
return (int32(b[1]) << 24) | (int32(b[2]) << 16) | (int32(b[3]) << 8) | (int32(b[4]))
|
||||
}
|
||||
|
||||
func putMint16(b []byte, i int16) {
|
||||
b[0] = mint16
|
||||
b[1] = byte(i >> 8)
|
||||
b[2] = byte(i)
|
||||
}
|
||||
|
||||
func getMint16(b []byte) (i int16) {
|
||||
return (int16(b[1]) << 8) | int16(b[2])
|
||||
}
|
||||
|
||||
func putMint8(b []byte, i int8) {
|
||||
b[0] = mint8
|
||||
b[1] = byte(i)
|
||||
}
|
||||
|
||||
func getMint8(b []byte) (i int8) {
|
||||
return int8(b[1])
|
||||
}
|
||||
|
||||
func putMuint64(b []byte, u uint64) {
|
||||
b[0] = muint64
|
||||
b[1] = byte(u >> 56)
|
||||
b[2] = byte(u >> 48)
|
||||
b[3] = byte(u >> 40)
|
||||
b[4] = byte(u >> 32)
|
||||
b[5] = byte(u >> 24)
|
||||
b[6] = byte(u >> 16)
|
||||
b[7] = byte(u >> 8)
|
||||
b[8] = byte(u)
|
||||
}
|
||||
|
||||
func getMuint64(b []byte) uint64 {
|
||||
return (uint64(b[1]) << 56) | (uint64(b[2]) << 48) |
|
||||
(uint64(b[3]) << 40) | (uint64(b[4]) << 32) |
|
||||
(uint64(b[5]) << 24) | (uint64(b[6]) << 16) |
|
||||
(uint64(b[7]) << 8) | (uint64(b[8]))
|
||||
}
|
||||
|
||||
func putMuint32(b []byte, u uint32) {
|
||||
b[0] = muint32
|
||||
b[1] = byte(u >> 24)
|
||||
b[2] = byte(u >> 16)
|
||||
b[3] = byte(u >> 8)
|
||||
b[4] = byte(u)
|
||||
}
|
||||
|
||||
func getMuint32(b []byte) uint32 {
|
||||
return (uint32(b[1]) << 24) | (uint32(b[2]) << 16) | (uint32(b[3]) << 8) | (uint32(b[4]))
|
||||
}
|
||||
|
||||
func putMuint16(b []byte, u uint16) {
|
||||
b[0] = muint16
|
||||
b[1] = byte(u >> 8)
|
||||
b[2] = byte(u)
|
||||
}
|
||||
|
||||
func getMuint16(b []byte) uint16 {
|
||||
return (uint16(b[1]) << 8) | uint16(b[2])
|
||||
}
|
||||
|
||||
func putMuint8(b []byte, u uint8) {
|
||||
b[0] = muint8
|
||||
b[1] = byte(u)
|
||||
}
|
||||
|
||||
func getMuint8(b []byte) uint8 {
|
||||
return uint8(b[1])
|
||||
}
|
||||
|
||||
func getUnix(b []byte) (sec int64, nsec int32) {
|
||||
sec = (int64(b[0]) << 56) | (int64(b[1]) << 48) |
|
||||
(int64(b[2]) << 40) | (int64(b[3]) << 32) |
|
||||
(int64(b[4]) << 24) | (int64(b[5]) << 16) |
|
||||
(int64(b[6]) << 8) | (int64(b[7]))
|
||||
|
||||
nsec = (int32(b[8]) << 24) | (int32(b[9]) << 16) | (int32(b[10]) << 8) | (int32(b[11]))
|
||||
return
|
||||
}
|
||||
|
||||
func putUnix(b []byte, sec int64, nsec int32) {
|
||||
b[0] = byte(sec >> 56)
|
||||
b[1] = byte(sec >> 48)
|
||||
b[2] = byte(sec >> 40)
|
||||
b[3] = byte(sec >> 32)
|
||||
b[4] = byte(sec >> 24)
|
||||
b[5] = byte(sec >> 16)
|
||||
b[6] = byte(sec >> 8)
|
||||
b[7] = byte(sec)
|
||||
b[8] = byte(nsec >> 24)
|
||||
b[9] = byte(nsec >> 16)
|
||||
b[10] = byte(nsec >> 8)
|
||||
b[11] = byte(nsec)
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
prefix utilities
|
||||
----------------------------- */
|
||||
|
||||
// write prefix and uint8
|
||||
func prefixu8(b []byte, pre byte, sz uint8) {
|
||||
b[0] = pre
|
||||
b[1] = byte(sz)
|
||||
}
|
||||
|
||||
// write prefix and big-endian uint16
|
||||
func prefixu16(b []byte, pre byte, sz uint16) {
|
||||
b[0] = pre
|
||||
b[1] = byte(sz >> 8)
|
||||
b[2] = byte(sz)
|
||||
}
|
||||
|
||||
// write prefix and big-endian uint32
|
||||
func prefixu32(b []byte, pre byte, sz uint32) {
|
||||
b[0] = pre
|
||||
b[1] = byte(sz >> 24)
|
||||
b[2] = byte(sz >> 16)
|
||||
b[3] = byte(sz >> 8)
|
||||
b[4] = byte(sz)
|
||||
}
|
||||
|
||||
func prefixu64(b []byte, pre byte, sz uint64) {
|
||||
b[0] = pre
|
||||
b[1] = byte(sz >> 56)
|
||||
b[2] = byte(sz >> 48)
|
||||
b[3] = byte(sz >> 40)
|
||||
b[4] = byte(sz >> 32)
|
||||
b[5] = byte(sz >> 24)
|
||||
b[6] = byte(sz >> 16)
|
||||
b[7] = byte(sz >> 8)
|
||||
b[8] = byte(sz)
|
||||
}
|
||||
555
vendor/github.com/glycerine/greenpack/msgp/json.go
generated
vendored
Normal file
555
vendor/github.com/glycerine/greenpack/msgp/json.go
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
null = []byte("null")
|
||||
hex = []byte("0123456789abcdef")
|
||||
)
|
||||
|
||||
var defuns [_maxtype]func(jsWriter, *Reader) (int, error)
|
||||
|
||||
// note: there is an initialization loop if
|
||||
// this isn't set up during init()
|
||||
func init() {
|
||||
// since none of these functions are inline-able,
|
||||
// there is not much of a penalty to the indirect
|
||||
// call. however, this is best expressed as a jump-table...
|
||||
defuns = [_maxtype]func(jsWriter, *Reader) (int, error){
|
||||
StrType: rwString,
|
||||
BinType: rwBytes,
|
||||
MapType: rwMap,
|
||||
ArrayType: rwArray,
|
||||
Float64Type: rwFloat64,
|
||||
Float32Type: rwFloat32,
|
||||
BoolType: rwBool,
|
||||
IntType: rwInt,
|
||||
UintType: rwUint,
|
||||
NilType: rwNil,
|
||||
ExtensionType: rwExtension,
|
||||
Complex64Type: rwExtension,
|
||||
Complex128Type: rwExtension,
|
||||
TimeType: rwTime,
|
||||
DurationType: rwDuration,
|
||||
}
|
||||
}
|
||||
|
||||
// this is the interface
|
||||
// used to write json
|
||||
type jsWriter interface {
|
||||
io.Writer
|
||||
io.ByteWriter
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
// CopyToJSON reads MessagePack from 'src' and copies it
|
||||
// as JSON to 'dst' until EOF.
|
||||
func CopyToJSON(dst io.Writer, src io.Reader) (n int64, err error) {
|
||||
r := NewReader(src)
|
||||
n, err = r.WriteToJSON(dst)
|
||||
freeR(r)
|
||||
return
|
||||
}
|
||||
|
||||
// WriteToJSON translates MessagePack from 'r' and writes it as
|
||||
// JSON to 'w' until the underlying reader returns io.EOF. It returns
|
||||
// the number of bytes written, and an error if it stopped before EOF.
|
||||
func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
|
||||
var j jsWriter
|
||||
var bf *bufio.Writer
|
||||
if jsw, ok := w.(jsWriter); ok {
|
||||
j = jsw
|
||||
} else {
|
||||
bf = bufio.NewWriter(w)
|
||||
j = bf
|
||||
}
|
||||
var nn int
|
||||
for err == nil {
|
||||
nn, err = rwNext(j, r)
|
||||
n += int64(nn)
|
||||
}
|
||||
if err != io.EOF {
|
||||
if bf != nil {
|
||||
bf.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
if bf != nil {
|
||||
err = bf.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func rwNext(w jsWriter, src *Reader) (int, error) {
|
||||
t, err := src.NextType()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return defuns[t](w, src)
|
||||
}
|
||||
|
||||
func rwMap(dst jsWriter, src *Reader) (n int, err error) {
|
||||
var comma bool
|
||||
var sz uint32
|
||||
var field []byte
|
||||
|
||||
sz, err = src.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sz == 0 {
|
||||
return dst.WriteString("{}")
|
||||
}
|
||||
|
||||
err = dst.WriteByte('{')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
var nn int
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
if comma {
|
||||
err = dst.WriteByte(',')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
field, err = src.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nn, err = rwquoted(dst, field)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = dst.WriteByte(':')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
nn, err = rwNext(dst, src)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !comma {
|
||||
comma = true
|
||||
}
|
||||
}
|
||||
|
||||
err = dst.WriteByte('}')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
return
|
||||
}
|
||||
|
||||
func rwArray(dst jsWriter, src *Reader) (n int, err error) {
|
||||
err = dst.WriteByte('[')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var sz uint32
|
||||
var nn int
|
||||
sz, err = src.ReadArrayHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
comma := false
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
if comma {
|
||||
err = dst.WriteByte(',')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
nn, err = rwNext(dst, src)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
comma = true
|
||||
}
|
||||
|
||||
err = dst.WriteByte(']')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
return
|
||||
}
|
||||
|
||||
func rwNil(dst jsWriter, src *Reader) (int, error) {
|
||||
err := src.ReadNil()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dst.Write(null)
|
||||
}
|
||||
|
||||
func rwFloat32(dst jsWriter, src *Reader) (int, error) {
|
||||
f, err := src.ReadFloat32()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 64)
|
||||
return dst.Write(src.scratch)
|
||||
}
|
||||
|
||||
func rwFloat64(dst jsWriter, src *Reader) (int, error) {
|
||||
f, err := src.ReadFloat64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 32)
|
||||
return dst.Write(src.scratch)
|
||||
}
|
||||
|
||||
func rwInt(dst jsWriter, src *Reader) (int, error) {
|
||||
i, err := src.ReadInt64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src.scratch = strconv.AppendInt(src.scratch[:0], i, 10)
|
||||
return dst.Write(src.scratch)
|
||||
}
|
||||
|
||||
func rwUint(dst jsWriter, src *Reader) (int, error) {
|
||||
u, err := src.ReadUint64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src.scratch = strconv.AppendUint(src.scratch[:0], u, 10)
|
||||
return dst.Write(src.scratch)
|
||||
}
|
||||
|
||||
func rwBool(dst jsWriter, src *Reader) (int, error) {
|
||||
b, err := src.ReadBool()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if b {
|
||||
return dst.WriteString("true")
|
||||
}
|
||||
return dst.WriteString("false")
|
||||
}
|
||||
|
||||
func rwTime(dst jsWriter, src *Reader) (int, error) {
|
||||
t, err := src.ReadTime()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bts, err := t.MarshalJSON()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dst.Write(bts)
|
||||
}
|
||||
|
||||
func rwDuration(dst jsWriter, src *Reader) (int, error) {
|
||||
dur, err := src.ReadDuration()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bts, err := durationMarshalJSON(dur)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dst.Write(bts)
|
||||
}
|
||||
|
||||
func rwExtension(dst jsWriter, src *Reader) (n int, err error) {
|
||||
et, err := src.peekExtensionType()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// registered extensions can override
|
||||
// the JSON encoding
|
||||
if j, ok := extensionReg[et]; ok {
|
||||
var bts []byte
|
||||
e := j()
|
||||
err = src.ReadExtension(e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
bts, err = json.Marshal(e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return dst.Write(bts)
|
||||
}
|
||||
|
||||
e := RawExtension{}
|
||||
e.Type = et
|
||||
err = src.ReadExtension(&e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var nn int
|
||||
err = dst.WriteByte('{')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
|
||||
nn, err = dst.WriteString(`"type:"`)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
src.scratch = strconv.AppendInt(src.scratch[0:0], int64(e.Type), 10)
|
||||
nn, err = dst.Write(src.scratch)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nn, err = dst.WriteString(`,"data":"`)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
enc := base64.NewEncoder(base64.StdEncoding, dst)
|
||||
|
||||
nn, err = enc.Write(e.Data)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = enc.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nn, err = dst.WriteString(`"}`)
|
||||
n += nn
|
||||
return
|
||||
}
|
||||
|
||||
func rwString(dst jsWriter, src *Reader) (n int, err error) {
|
||||
var p []byte
|
||||
p, err = src.R.Peek(1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lead := p[0]
|
||||
var read int
|
||||
|
||||
if isfixstr(lead) {
|
||||
read = int(rfixstr(lead))
|
||||
src.R.Skip(1)
|
||||
goto write
|
||||
}
|
||||
|
||||
switch lead {
|
||||
case mstr8:
|
||||
p, err = src.R.Next(2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
read = int(uint8(p[1]))
|
||||
case mstr16:
|
||||
p, err = src.R.Next(3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
read = int(big.Uint16(p[1:]))
|
||||
case mstr32:
|
||||
p, err = src.R.Next(5)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
read = int(big.Uint32(p[1:]))
|
||||
default:
|
||||
err = badPrefix(StrType, lead)
|
||||
return
|
||||
}
|
||||
write:
|
||||
p, err = src.R.Next(read)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n, err = rwquoted(dst, p)
|
||||
return
|
||||
}
|
||||
|
||||
func rwBytes(dst jsWriter, src *Reader) (n int, err error) {
|
||||
var nn int
|
||||
err = dst.WriteByte('"')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
src.scratch, err = src.ReadBytes(src.scratch[:0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
enc := base64.NewEncoder(base64.StdEncoding, dst)
|
||||
nn, err = enc.Write(src.scratch)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = enc.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dst.WriteByte('"')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
return
|
||||
}
|
||||
|
||||
// Below (c) The Go Authors, 2009-2014
|
||||
// Subject to the BSD-style license found at http://golang.org
|
||||
//
|
||||
// see: encoding/json/encode.go:(*encodeState).stringbytes()
|
||||
func rwquoted(dst jsWriter, s []byte) (n int, err error) {
|
||||
var nn int
|
||||
err = dst.WriteByte('"')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
nn, err = dst.Write(s[start:i])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
err = dst.WriteByte('\\')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
err = dst.WriteByte(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
case '\n':
|
||||
err = dst.WriteByte('\\')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
err = dst.WriteByte('n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
case '\r':
|
||||
err = dst.WriteByte('\\')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
err = dst.WriteByte('r')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
default:
|
||||
nn, err = dst.WriteString(`\u00`)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dst.WriteByte(hex[b>>4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
err = dst.WriteByte(hex[b&0xF])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRune(s[i:])
|
||||
if c == utf8.RuneError && size == 1 {
|
||||
if start < i {
|
||||
nn, err = dst.Write(s[start:i])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nn, err = dst.WriteString(`\ufffd`)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
}
|
||||
if c == '\u2028' || c == '\u2029' {
|
||||
if start < i {
|
||||
nn, err = dst.Write(s[start:i])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nn, err = dst.WriteString(`\u202`)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dst.WriteByte(hex[c&0xF])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
nn, err = dst.Write(s[start:])
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = dst.WriteByte('"')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
return
|
||||
}
|
||||
416
vendor/github.com/glycerine/greenpack/msgp/json_bytes.go
generated
vendored
Normal file
416
vendor/github.com/glycerine/greenpack/msgp/json_bytes.go
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error)
|
||||
|
||||
func init() {
|
||||
|
||||
// NOTE(pmh): this is best expressed as a jump table,
|
||||
// but gc doesn't do that yet. revisit post-go1.5.
|
||||
unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){
|
||||
StrType: rwStringBytes,
|
||||
BinType: rwBytesBytes,
|
||||
MapType: rwMapBytes,
|
||||
ArrayType: rwArrayBytes,
|
||||
Float64Type: rwFloat64Bytes,
|
||||
Float32Type: rwFloat32Bytes,
|
||||
BoolType: rwBoolBytes,
|
||||
IntType: rwIntBytes,
|
||||
UintType: rwUintBytes,
|
||||
NilType: rwNullBytes,
|
||||
ExtensionType: rwExtensionBytes,
|
||||
Complex64Type: rwExtensionBytes,
|
||||
Complex128Type: rwExtensionBytes,
|
||||
TimeType: rwTimeBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalAsJSON takes raw messagepack and writes
|
||||
// it as JSON to 'w'. If an error is returned, the
|
||||
// bytes not translated will also be returned. If
|
||||
// no errors are encountered, the length of the returned
|
||||
// slice will be zero.
|
||||
func UnmarshalAsJSON(w io.Writer, msg []byte) ([]byte, error) {
|
||||
var (
|
||||
scratch []byte
|
||||
cast bool
|
||||
dst jsWriter
|
||||
err error
|
||||
)
|
||||
if jsw, ok := w.(jsWriter); ok {
|
||||
dst = jsw
|
||||
cast = true
|
||||
} else {
|
||||
dst = bufio.NewWriterSize(w, 512)
|
||||
}
|
||||
for len(msg) > 0 && err == nil {
|
||||
msg, scratch, err = writeNext(dst, msg, scratch)
|
||||
}
|
||||
if !cast && err == nil {
|
||||
err = dst.(*bufio.Writer).Flush()
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func writeNext(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
if len(msg) < 1 {
|
||||
return msg, scratch, ErrShortBytes
|
||||
}
|
||||
t := getType(msg[0])
|
||||
if t == InvalidType {
|
||||
return msg, scratch, InvalidPrefixError(msg[0])
|
||||
}
|
||||
if t == ExtensionType {
|
||||
et, err := peekExtension(msg)
|
||||
if err != nil {
|
||||
return nil, scratch, err
|
||||
}
|
||||
if et == TimeExtension {
|
||||
t = TimeType
|
||||
} else if et == DurationExtension {
|
||||
t = DurationType
|
||||
}
|
||||
|
||||
}
|
||||
return unfuns[t](w, msg, scratch)
|
||||
}
|
||||
|
||||
func rwArrayBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
sz, msg, err := nbs.ReadArrayHeaderBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
err = w.WriteByte('[')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
if i != 0 {
|
||||
err = w.WriteByte(',')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
}
|
||||
msg, scratch, err = writeNext(w, msg, scratch)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
}
|
||||
err = w.WriteByte(']')
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwMapBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
sz, msg, err := nbs.ReadMapHeaderBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
err = w.WriteByte('{')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
if i != 0 {
|
||||
err = w.WriteByte(',')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
}
|
||||
msg, scratch, err = rwMapKeyBytes(w, msg, scratch)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
err = w.WriteByte(':')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
msg, scratch, err = writeNext(w, msg, scratch)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
}
|
||||
err = w.WriteByte('}')
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwMapKeyBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
msg, scratch, err := rwStringBytes(w, msg, scratch)
|
||||
if err != nil {
|
||||
if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType {
|
||||
return rwBytesBytes(w, msg, scratch)
|
||||
}
|
||||
}
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwStringBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
str, msg, err := nbs.ReadStringZC(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = rwquoted(w, str)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwBytesBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
bts, msg, err := nbs.ReadBytesZC(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
l := base64.StdEncoding.EncodedLen(len(bts))
|
||||
if cap(scratch) >= l {
|
||||
scratch = scratch[0:l]
|
||||
} else {
|
||||
scratch = make([]byte, l)
|
||||
}
|
||||
base64.StdEncoding.Encode(scratch, bts)
|
||||
err = w.WriteByte('"')
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.Write(scratch)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
err = w.WriteByte('"')
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwNullBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
msg, err := nbs.ReadNilBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.Write(null)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwBoolBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
b, msg, err := nbs.ReadBoolBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
if b {
|
||||
_, err = w.WriteString("true")
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.WriteString("false")
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwIntBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
i, msg, err := nbs.ReadInt64Bytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendInt(scratch[0:0], i, 10)
|
||||
_, err = w.Write(scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
u, msg, err := nbs.ReadUint64Bytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendUint(scratch[0:0], u, 10)
|
||||
_, err = w.Write(scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
var f float64
|
||||
var err error
|
||||
var sz int
|
||||
if f64 {
|
||||
sz = 64
|
||||
f, msg, err = nbs.ReadFloat64Bytes(msg)
|
||||
} else {
|
||||
sz = 32
|
||||
var v float32
|
||||
v, msg, err = nbs.ReadFloat32Bytes(msg)
|
||||
f = float64(v)
|
||||
}
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz)
|
||||
_, err = w.Write(scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
var f float32
|
||||
var err error
|
||||
f, msg, err = nbs.ReadFloat32Bytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendFloat(scratch[:0], float64(f), 'f', -1, 32)
|
||||
_, err = w.Write(scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwFloat64Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
var f float64
|
||||
var err error
|
||||
f, msg, err = nbs.ReadFloat64Bytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)
|
||||
_, err = w.Write(scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwTimeBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
var t time.Time
|
||||
var err error
|
||||
t, msg, err = nbs.ReadTimeBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
bts, err := t.MarshalJSON()
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.Write(bts)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func rwExtensionBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
var err error
|
||||
var et int8
|
||||
et, err = peekExtension(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
// if it's time.Time
|
||||
if et == TimeExtension {
|
||||
var tm time.Time
|
||||
tm, msg, err = nbs.ReadTimeBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
bts, err := tm.MarshalJSON()
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.Write(bts)
|
||||
return msg, scratch, err
|
||||
|
||||
} else if et == DurationExtension {
|
||||
var dur time.Duration
|
||||
dur, msg, err = nbs.ReadDurationBytes(msg)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
_, err := w.WriteString(`{"time.Duration":`)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch = strconv.AppendInt(scratch[0:0], int64(dur), 10)
|
||||
_, err = w.Write(scratch)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.WriteString(`}`)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
// if the extension is registered,
|
||||
// use its canonical JSON form
|
||||
if f, ok := extensionReg[et]; ok {
|
||||
e := f()
|
||||
msg, err = nbs.ReadExtensionBytes(msg, e)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
bts, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
_, err = w.Write(bts)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
// otherwise, write `{"type": <num>, "data": "<base64data>"}`
|
||||
r := RawExtension{}
|
||||
r.Type = et
|
||||
msg, err = nbs.ReadExtensionBytes(msg, &r)
|
||||
if err != nil {
|
||||
return msg, scratch, err
|
||||
}
|
||||
scratch, err = writeExt(w, r, scratch)
|
||||
return msg, scratch, err
|
||||
}
|
||||
|
||||
func durationMarshalJSON(dur time.Duration) ([]byte, error) {
|
||||
|
||||
var w bytes.Buffer
|
||||
_, err := w.WriteString(`{"time.Duration":`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scratch := make([]byte, 32)
|
||||
scratch = strconv.AppendInt(scratch[0:0], int64(dur), 10)
|
||||
_, err = w.Write(scratch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = w.WriteString(`}`)
|
||||
return w.Bytes(), err
|
||||
}
|
||||
|
||||
func writeExt(w jsWriter, r RawExtension, scratch []byte) ([]byte, error) {
|
||||
_, err := w.WriteString(`{"type":`)
|
||||
if err != nil {
|
||||
return scratch, err
|
||||
}
|
||||
scratch = strconv.AppendInt(scratch[0:0], int64(r.Type), 10)
|
||||
_, err = w.Write(scratch)
|
||||
if err != nil {
|
||||
return scratch, err
|
||||
}
|
||||
_, err = w.WriteString(`,"data":"`)
|
||||
if err != nil {
|
||||
return scratch, err
|
||||
}
|
||||
l := base64.StdEncoding.EncodedLen(len(r.Data))
|
||||
if cap(scratch) >= l {
|
||||
scratch = scratch[0:l]
|
||||
} else {
|
||||
scratch = make([]byte, l)
|
||||
}
|
||||
base64.StdEncoding.Encode(scratch, r.Data)
|
||||
_, err = w.Write(scratch)
|
||||
if err != nil {
|
||||
return scratch, err
|
||||
}
|
||||
_, err = w.WriteString(`"}`)
|
||||
return scratch, err
|
||||
}
|
||||
126
vendor/github.com/glycerine/greenpack/msgp/nilbits.go
generated
vendored
Normal file
126
vendor/github.com/glycerine/greenpack/msgp/nilbits.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//"runtime/debug"
|
||||
)
|
||||
|
||||
const MaxNestedStructPointerDepth = 8
|
||||
|
||||
// NilBitsStack is a helper for Unmarshal
|
||||
// methods to track where we are when
|
||||
// deserializing from empty/nil/missing
|
||||
// fields.
|
||||
type NilBitsStack struct {
|
||||
// simulate getting nils on the wire
|
||||
AlwaysNil bool
|
||||
LifoAlwaysNil int // this lifo always had false in it, so change it to a count.
|
||||
LifoBts [MaxNestedStructPointerDepth][]byte // caps our static tree depth at 8, but avoids all memory allocation during the decode hotpath.
|
||||
|
||||
// UnsafeZeroCopy will make strings point to the
|
||||
// original msgpack buffers. This is unsafe because
|
||||
// if the buffer changes the string will change/be
|
||||
// invalid/not protected against re-use. But for
|
||||
// processing and disposing of single messages, one-at-at-time,
|
||||
// without re-using any part of a message (or making a copy of strings explicitly with copy()
|
||||
// if you must) then we can avoid all allocations for strings.
|
||||
UnsafeZeroCopy bool
|
||||
}
|
||||
|
||||
func (r *NilBitsStack) Init(cfg *RuntimeConfig) {
|
||||
if cfg != nil {
|
||||
r.UnsafeZeroCopy = cfg.UnsafeZeroCopy
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NilBitsStack) IsNil(bts []byte) bool {
|
||||
if r.AlwaysNil {
|
||||
return true
|
||||
}
|
||||
if len(bts) != 0 && bts[0] == mnil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// OnlyNilSlice is a slice that contains
|
||||
// only the msgpack nil (0xc0) bytes.
|
||||
var OnlyNilSlice = []byte{mnil}
|
||||
|
||||
// AlwaysNilString returns a string representation
|
||||
// of the internal state of the NilBitsStack for
|
||||
// debugging purposes.
|
||||
func (r *NilBitsStack) AlwaysNilString() string {
|
||||
|
||||
s := "bottom: "
|
||||
for i := 0; i > r.LifoAlwaysNil; i++ {
|
||||
s += "T"
|
||||
}
|
||||
/* for _, v := range r.LifoAlwaysNil {
|
||||
if v {
|
||||
s += "T"
|
||||
} else {
|
||||
s += "f"
|
||||
}
|
||||
}
|
||||
*/
|
||||
return s
|
||||
}
|
||||
|
||||
// PushAlwaysNil will set r.AlwaysNil to true
|
||||
// and store bts on the internal stack.
|
||||
func (r *NilBitsStack) PushAlwaysNil(bts []byte) []byte {
|
||||
//fmt.Printf("PushAlwaysNil(), pre we are '%v'. Called from: stack is '%v'\n", r.AlwaysNilString(), string(debug.Stack()))
|
||||
// save current state
|
||||
|
||||
if r.LifoAlwaysNil == MaxNestedStructPointerDepth {
|
||||
panic(fmt.Sprintf("we hit our maximum nested struct-inside-struct depth! you must recompile msgp with github.com/glycerine/greenpack/msgp/nilbits.go: MaxNestedStructPointerDepth set to greater than the current value of %v, and then regenerate your msgp Unmarshaling code.", MaxNestedStructPointerDepth))
|
||||
}
|
||||
|
||||
r.LifoBts[r.LifoAlwaysNil] = bts
|
||||
r.LifoAlwaysNil++
|
||||
|
||||
// set reader r to always return nils
|
||||
r.AlwaysNil = true
|
||||
|
||||
return OnlyNilSlice
|
||||
}
|
||||
|
||||
// PopAlwaysNil pops the last []byte off the internal
|
||||
// stack and returns it. If the stack is empty
|
||||
// we panic.
|
||||
func (r *NilBitsStack) PopAlwaysNil() (bts []byte) {
|
||||
//fmt.Printf("my NilBitsTack.PopAlwaysNil() called!! ... stack is '%v'\n", string(debug.Stack()))
|
||||
// defer func() {
|
||||
// fmt.Printf("len of bts returned by PopAlwaysNil: %v, and debug string: '%v'\n",
|
||||
// len(bts), r.AlwaysNilString())
|
||||
// }()
|
||||
n := r.LifoAlwaysNil
|
||||
if n == 0 {
|
||||
panic("PopAlwaysNil called on empty lifo")
|
||||
}
|
||||
if n < 0 {
|
||||
panic("PopAlwaysNil called on illegal-less-thab-empty lifo")
|
||||
}
|
||||
bts = r.LifoBts[n-1]
|
||||
|
||||
r.LifoAlwaysNil--
|
||||
if r.LifoAlwaysNil == 0 {
|
||||
r.AlwaysNil = false
|
||||
}
|
||||
|
||||
return bts
|
||||
}
|
||||
|
||||
func ShowFound(found []bool) string {
|
||||
s := "["
|
||||
for i := range found {
|
||||
if found[i] {
|
||||
s += "1,"
|
||||
} else {
|
||||
s += "0,"
|
||||
}
|
||||
}
|
||||
s += "]"
|
||||
return s
|
||||
}
|
||||
268
vendor/github.com/glycerine/greenpack/msgp/number.go
generated
vendored
Normal file
268
vendor/github.com/glycerine/greenpack/msgp/number.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// The portable parts of the Number implementation
|
||||
|
||||
// Number can be
|
||||
// an int64, uint64, float32,
|
||||
// or float64 internally.
|
||||
// It can decode itself
|
||||
// from any of the native
|
||||
// messagepack number types.
|
||||
// The zero-value of Number
|
||||
// is Int(0). Using the equality
|
||||
// operator with Number compares
|
||||
// both the type and the value
|
||||
// of the number.
|
||||
type Number struct {
|
||||
// internally, this
|
||||
// is just a tagged union.
|
||||
// the raw bits of the number
|
||||
// are stored the same way regardless.
|
||||
bits uint64
|
||||
typ Type
|
||||
}
|
||||
|
||||
// AsInt sets the number to an int64.
|
||||
func (n *Number) AsInt(i int64) {
|
||||
|
||||
// we always store int(0)
|
||||
// as {0, InvalidType} in
|
||||
// order to preserve
|
||||
// the behavior of the == operator
|
||||
if i == 0 {
|
||||
n.typ = InvalidType
|
||||
n.bits = 0
|
||||
return
|
||||
}
|
||||
|
||||
n.typ = IntType
|
||||
n.bits = uint64(i)
|
||||
}
|
||||
|
||||
// AsUint sets the number to a uint64.
|
||||
func (n *Number) AsUint(u uint64) {
|
||||
n.typ = UintType
|
||||
n.bits = u
|
||||
}
|
||||
|
||||
// AsFloat32 sets the value of the number
|
||||
// to a float32.
|
||||
func (n *Number) AsFloat32(f float32) {
|
||||
n.typ = Float32Type
|
||||
n.bits = uint64(math.Float32bits(f))
|
||||
}
|
||||
|
||||
// AsFloat64 sets the value of the
|
||||
// number to a float64.
|
||||
func (n *Number) AsFloat64(f float64) {
|
||||
n.typ = Float64Type
|
||||
n.bits = math.Float64bits(f)
|
||||
}
|
||||
|
||||
// Int casts the number as an int64, and
|
||||
// returns whether or not that was the
|
||||
// underlying type.
|
||||
func (n *Number) Int() (int64, bool) {
|
||||
return int64(n.bits), n.typ == IntType || n.typ == InvalidType
|
||||
}
|
||||
|
||||
// Uint casts the number as a uint64, and returns
|
||||
// whether or not that was the underlying type.
|
||||
func (n *Number) Uint() (uint64, bool) {
|
||||
return n.bits, n.typ == UintType
|
||||
}
|
||||
|
||||
// Float casts the number to a float64, and
|
||||
// returns whether or not that was the underlying
|
||||
// type (either a float64 or a float32).
|
||||
func (n *Number) Float() (float64, bool) {
|
||||
switch n.typ {
|
||||
case Float32Type:
|
||||
return float64(math.Float32frombits(uint32(n.bits))), true
|
||||
case Float64Type:
|
||||
return math.Float64frombits(n.bits), true
|
||||
default:
|
||||
return 0.0, false
|
||||
}
|
||||
}
|
||||
|
||||
// Type will return one of:
|
||||
// Float64Type, Float32Type, UintType, or IntType.
|
||||
func (n *Number) Type() Type {
|
||||
if n.typ == InvalidType {
|
||||
return IntType
|
||||
}
|
||||
return n.typ
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (n *Number) DecodeMsg(r *Reader) error {
|
||||
typ, err := r.NextType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch typ {
|
||||
case Float32Type:
|
||||
f, err := r.ReadFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.AsFloat32(f)
|
||||
return nil
|
||||
case Float64Type:
|
||||
f, err := r.ReadFloat64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.AsFloat64(f)
|
||||
return nil
|
||||
case IntType:
|
||||
i, err := r.ReadInt64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.AsInt(i)
|
||||
return nil
|
||||
case UintType:
|
||||
u, err := r.ReadUint64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.AsUint(u)
|
||||
return nil
|
||||
default:
|
||||
return TypeError{Encoded: typ, Method: IntType}
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (n *Number) UnmarshalMsg(b []byte) ([]byte, error) {
|
||||
var nbs *NilBitsStack
|
||||
typ := NextType(b)
|
||||
switch typ {
|
||||
case IntType:
|
||||
i, o, err := nbs.ReadInt64Bytes(b)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
n.AsInt(i)
|
||||
return o, nil
|
||||
case UintType:
|
||||
u, o, err := nbs.ReadUint64Bytes(b)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
n.AsUint(u)
|
||||
return o, nil
|
||||
case Float64Type:
|
||||
f, o, err := nbs.ReadFloat64Bytes(b)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
n.AsFloat64(f)
|
||||
return o, nil
|
||||
case Float32Type:
|
||||
f, o, err := nbs.ReadFloat32Bytes(b)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
n.AsFloat32(f)
|
||||
return o, nil
|
||||
default:
|
||||
return b, TypeError{Method: IntType, Encoded: typ}
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (n *Number) MarshalMsg(b []byte) ([]byte, error) {
|
||||
switch n.typ {
|
||||
case IntType:
|
||||
return AppendInt64(b, int64(n.bits)), nil
|
||||
case UintType:
|
||||
return AppendUint64(b, uint64(n.bits)), nil
|
||||
case Float64Type:
|
||||
return AppendFloat64(b, math.Float64frombits(n.bits)), nil
|
||||
case Float32Type:
|
||||
return AppendFloat32(b, math.Float32frombits(uint32(n.bits))), nil
|
||||
default:
|
||||
return AppendInt64(b, 0), nil
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (n *Number) EncodeMsg(w *Writer) error {
|
||||
switch n.typ {
|
||||
case IntType:
|
||||
return w.WriteInt64(int64(n.bits))
|
||||
case UintType:
|
||||
return w.WriteUint64(n.bits)
|
||||
case Float64Type:
|
||||
return w.WriteFloat64(math.Float64frombits(n.bits))
|
||||
case Float32Type:
|
||||
return w.WriteFloat32(math.Float32frombits(uint32(n.bits)))
|
||||
default:
|
||||
return w.WriteInt64(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Msgsize implements msgp.Sizer
|
||||
func (n *Number) Msgsize() int {
|
||||
switch n.typ {
|
||||
case Float32Type:
|
||||
return Float32Size
|
||||
case Float64Type:
|
||||
return Float64Size
|
||||
case IntType:
|
||||
return Int64Size
|
||||
case UintType:
|
||||
return Uint64Size
|
||||
default:
|
||||
return 1 // fixint(0)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (n *Number) MarshalJSON() ([]byte, error) {
|
||||
t := n.Type()
|
||||
if t == InvalidType {
|
||||
return []byte{'0'}, nil
|
||||
}
|
||||
out := make([]byte, 0, 32)
|
||||
switch t {
|
||||
case Float32Type, Float64Type:
|
||||
f, _ := n.Float()
|
||||
return strconv.AppendFloat(out, f, 'f', -1, 64), nil
|
||||
case IntType:
|
||||
i, _ := n.Int()
|
||||
return strconv.AppendInt(out, i, 10), nil
|
||||
case UintType:
|
||||
u, _ := n.Uint()
|
||||
return strconv.AppendUint(out, u, 10), nil
|
||||
default:
|
||||
panic("(*Number).typ is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (n *Number) String() string {
|
||||
switch n.typ {
|
||||
case InvalidType:
|
||||
return "0"
|
||||
case Float32Type, Float64Type:
|
||||
f, _ := n.Float()
|
||||
return strconv.FormatFloat(f, 'f', -1, 64)
|
||||
case IntType:
|
||||
i, _ := n.Int()
|
||||
return strconv.FormatInt(i, 10)
|
||||
case UintType:
|
||||
u, _ := n.Uint()
|
||||
return strconv.FormatUint(u, 10)
|
||||
default:
|
||||
panic("(*Number).typ is invalid")
|
||||
}
|
||||
}
|
||||
1597
vendor/github.com/glycerine/greenpack/msgp/read.go
generated
vendored
Normal file
1597
vendor/github.com/glycerine/greenpack/msgp/read.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1483
vendor/github.com/glycerine/greenpack/msgp/read_bytes.go
generated
vendored
Normal file
1483
vendor/github.com/glycerine/greenpack/msgp/read_bytes.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
vendor/github.com/glycerine/greenpack/msgp/runtimecfg.go
generated
vendored
Normal file
13
vendor/github.com/glycerine/greenpack/msgp/runtimecfg.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package msgp
|
||||
|
||||
type RuntimeConfig struct {
|
||||
|
||||
// UnsafeZeroCopy will make strings point to the
|
||||
// original msgpack buffers. This is unsafe because
|
||||
// if the buffer changes the string will change/be
|
||||
// invalid/not protected against re-use. But for
|
||||
// processing and disposing of single messages, one-at-at-time,
|
||||
// without re-using any part of a message (or making a copy of strings explicitly with copy()
|
||||
// if you must) then we can avoid all allocations for strings.
|
||||
UnsafeZeroCopy bool
|
||||
}
|
||||
39
vendor/github.com/glycerine/greenpack/msgp/size.go
generated
vendored
Normal file
39
vendor/github.com/glycerine/greenpack/msgp/size.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package msgp
|
||||
|
||||
// The sizes provided
|
||||
// are the worst-case
|
||||
// encoded sizes for
|
||||
// each type. For variable-
|
||||
// length types ([]byte, string),
|
||||
// the total encoded size is
|
||||
// the prefix size plus the
|
||||
// length of the object.
|
||||
const (
|
||||
Int64Size = 9
|
||||
IntSize = Int64Size
|
||||
UintSize = Int64Size
|
||||
Int8Size = 2
|
||||
Int16Size = 3
|
||||
Int32Size = 5
|
||||
Uint8Size = 2
|
||||
ByteSize = Uint8Size
|
||||
Uint16Size = 3
|
||||
Uint32Size = 5
|
||||
Uint64Size = Int64Size
|
||||
Float64Size = 9
|
||||
Float32Size = 5
|
||||
Complex64Size = 10
|
||||
Complex128Size = 18
|
||||
|
||||
TimeSize = 15
|
||||
DurationSize = 12
|
||||
BoolSize = 1
|
||||
NilSize = 1
|
||||
|
||||
MapHeaderSize = 5
|
||||
ArrayHeaderSize = 5
|
||||
|
||||
BytesPrefixSize = 5
|
||||
StringPrefixSize = 5
|
||||
ExtensionPrefixSize = 6
|
||||
)
|
||||
40
vendor/github.com/glycerine/greenpack/msgp/unsafe.go
generated
vendored
Normal file
40
vendor/github.com/glycerine/greenpack/msgp/unsafe.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// +build !appengine
|
||||
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NOTE:
|
||||
// all of the definition in this file
|
||||
// should be repeated in appengine.go,
|
||||
// but without using unsafe
|
||||
|
||||
const (
|
||||
// spec says int and uint are always
|
||||
// the same size, but that int/uint
|
||||
// size may not be machine word size
|
||||
smallint = unsafe.Sizeof(int(0)) == 4
|
||||
)
|
||||
|
||||
// UnsafeString returns the byte slice as a volatile string
|
||||
// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR.
|
||||
// THIS IS EVIL CODE.
|
||||
// YOU HAVE BEEN WARNED.
|
||||
func UnsafeString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: uintptr(unsafe.Pointer(&b[0])), Len: len(b)}))
|
||||
}
|
||||
|
||||
// UnsafeBytes returns the string as a byte slice
|
||||
// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR.
|
||||
// THIS IS EVIL CODE.
|
||||
// YOU HAVE BEEN WARNED.
|
||||
func UnsafeBytes(s string) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Len: len(s),
|
||||
Cap: len(s),
|
||||
Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
|
||||
}))
|
||||
}
|
||||
869
vendor/github.com/glycerine/greenpack/msgp/write.go
generated
vendored
Normal file
869
vendor/github.com/glycerine/greenpack/msgp/write.go
generated
vendored
Normal file
@@ -0,0 +1,869 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sizer is an interface implemented
|
||||
// by types that can estimate their
|
||||
// size when MessagePack encoded.
|
||||
// This interface is optional, but
|
||||
// encoding/marshaling implementations
|
||||
// may use this as a way to pre-allocate
|
||||
// memory for serialization.
|
||||
type Sizer interface {
|
||||
Msgsize() int
|
||||
}
|
||||
|
||||
var (
|
||||
// Nowhere is an io.Writer to nowhere
|
||||
Nowhere io.Writer = nwhere{}
|
||||
|
||||
btsType = reflect.TypeOf(([]byte)(nil))
|
||||
writerPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Writer{
|
||||
buf: make([]byte, 2048),
|
||||
ptrWrit: make(map[interface{}]int),
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func popWriter(w io.Writer) *Writer {
|
||||
wr := writerPool.Get().(*Writer)
|
||||
wr.Reset(w)
|
||||
wr.DedupReset()
|
||||
return wr
|
||||
}
|
||||
|
||||
func pushWriter(wr *Writer) {
|
||||
wr.w = nil
|
||||
wr.wloc = 0
|
||||
writerPool.Put(wr)
|
||||
}
|
||||
|
||||
// freeW frees a writer for use
|
||||
// by other processes. It is not necessary
|
||||
// to call freeW on a writer. However, maintaining
|
||||
// a reference to a *Writer after calling freeW on
|
||||
// it will cause undefined behavior.
|
||||
func freeW(w *Writer) { pushWriter(w) }
|
||||
|
||||
// Require ensures that cap(old)-len(old) >= extra.
|
||||
func Require(old []byte, extra int) []byte {
|
||||
l := len(old)
|
||||
c := cap(old)
|
||||
r := l + extra
|
||||
if c >= r {
|
||||
return old
|
||||
} else if l == 0 {
|
||||
return make([]byte, 0, extra)
|
||||
}
|
||||
// the new size is the greater
|
||||
// of double the old capacity
|
||||
// and the sum of the old length
|
||||
// and the number of new bytes
|
||||
// necessary.
|
||||
c <<= 1
|
||||
if c < r {
|
||||
c = r
|
||||
}
|
||||
n := make([]byte, l, c)
|
||||
copy(n, old)
|
||||
return n
|
||||
}
|
||||
|
||||
// nowhere writer
|
||||
type nwhere struct{}
|
||||
|
||||
func (n nwhere) Write(p []byte) (int, error) { return len(p), nil }
|
||||
|
||||
// Marshaler is the interface implemented
|
||||
// by types that know how to marshal themselves
|
||||
// as MessagePack. MarshalMsg appends the marshalled
|
||||
// form of the object to the provided
|
||||
// byte slice, returning the extended
|
||||
// slice and any errors encountered.
|
||||
type Marshaler interface {
|
||||
MarshalMsg([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// Encodable is the interface implemented
|
||||
// by types that know how to write themselves
|
||||
// as MessagePack using a *msgp.Writer.
|
||||
type Encodable interface {
|
||||
EncodeMsg(*Writer) error
|
||||
}
|
||||
|
||||
// Writer is a buffered writer
|
||||
// that can be used to write
|
||||
// MessagePack objects to an io.Writer.
|
||||
// You must call *Writer.Flush() in order
|
||||
// to flush all of the buffered data
|
||||
// to the underlying writer.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
buf []byte
|
||||
wloc int
|
||||
|
||||
ptrWrit map[interface{}]int
|
||||
ptrCountNext int
|
||||
}
|
||||
|
||||
// NewWriter returns a new *Writer.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
if wr, ok := w.(*Writer); ok {
|
||||
return wr
|
||||
}
|
||||
return popWriter(w)
|
||||
}
|
||||
|
||||
// NewWriterSize returns a writer with a custom buffer size.
|
||||
func NewWriterSize(w io.Writer, sz int) *Writer {
|
||||
// we must be able to require() 18
|
||||
// contiguous bytes, so that is the
|
||||
// practical minimum buffer size
|
||||
if sz < 18 {
|
||||
sz = 18
|
||||
}
|
||||
|
||||
return &Writer{
|
||||
w: w,
|
||||
buf: make([]byte, sz),
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes an Encodable to an io.Writer.
|
||||
func Encode(w io.Writer, e Encodable) error {
|
||||
wr := NewWriter(w)
|
||||
err := e.EncodeMsg(wr)
|
||||
if err == nil {
|
||||
err = wr.Flush()
|
||||
}
|
||||
freeW(wr)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mw *Writer) flush() error {
|
||||
if mw.wloc == 0 {
|
||||
return nil
|
||||
}
|
||||
n, err := mw.w.Write(mw.buf[:mw.wloc])
|
||||
if err != nil {
|
||||
if n > 0 {
|
||||
mw.wloc = copy(mw.buf, mw.buf[n:mw.wloc])
|
||||
}
|
||||
return err
|
||||
}
|
||||
mw.wloc = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush flushes all of the buffered
|
||||
// data to the underlying writer.
|
||||
func (mw *Writer) Flush() error { return mw.flush() }
|
||||
|
||||
// Buffered returns the number bytes in the write buffer
|
||||
func (mw *Writer) Buffered() int { return len(mw.buf) - mw.wloc }
|
||||
|
||||
func (mw *Writer) avail() int { return len(mw.buf) - mw.wloc }
|
||||
|
||||
func (mw *Writer) bufsize() int { return len(mw.buf) }
|
||||
|
||||
// NOTE: this should only be called with
|
||||
// a number that is guaranteed to be less than
|
||||
// len(mw.buf). typically, it is called with a constant.
|
||||
//
|
||||
// NOTE: this is a hot code path
|
||||
func (mw *Writer) require(n int) (int, error) {
|
||||
c := len(mw.buf)
|
||||
wl := mw.wloc
|
||||
if c-wl < n {
|
||||
if err := mw.flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
wl = mw.wloc
|
||||
}
|
||||
mw.wloc += n
|
||||
return wl, nil
|
||||
}
|
||||
|
||||
func (mw *Writer) Append(b ...byte) error {
|
||||
if mw.avail() < len(b) {
|
||||
err := mw.flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mw.wloc += copy(mw.buf[mw.wloc:], b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// push one byte onto the buffer
|
||||
//
|
||||
// NOTE: this is a hot code path
|
||||
func (mw *Writer) push(b byte) error {
|
||||
if mw.wloc == len(mw.buf) {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mw.buf[mw.wloc] = b
|
||||
mw.wloc++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *Writer) prefix8(b byte, u uint8) error {
|
||||
const need = 2
|
||||
if len(mw.buf)-mw.wloc < need {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prefixu8(mw.buf[mw.wloc:], b, u)
|
||||
mw.wloc += need
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *Writer) prefix16(b byte, u uint16) error {
|
||||
const need = 3
|
||||
if len(mw.buf)-mw.wloc < need {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prefixu16(mw.buf[mw.wloc:], b, u)
|
||||
mw.wloc += need
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *Writer) prefix32(b byte, u uint32) error {
|
||||
const need = 5
|
||||
if len(mw.buf)-mw.wloc < need {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prefixu32(mw.buf[mw.wloc:], b, u)
|
||||
mw.wloc += need
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *Writer) prefix64(b byte, u uint64) error {
|
||||
const need = 9
|
||||
if len(mw.buf)-mw.wloc < need {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prefixu64(mw.buf[mw.wloc:], b, u)
|
||||
mw.wloc += need
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements io.Writer, and writes
|
||||
// data directly to the buffer.
|
||||
func (mw *Writer) Write(p []byte) (int, error) {
|
||||
l := len(p)
|
||||
if mw.avail() < l {
|
||||
if err := mw.flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if l > len(mw.buf) {
|
||||
return mw.w.Write(p)
|
||||
}
|
||||
}
|
||||
mw.wloc += copy(mw.buf[mw.wloc:], p)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// implements io.WriteString
|
||||
func (mw *Writer) writeString(s string) error {
|
||||
l := len(s)
|
||||
if mw.avail() < l {
|
||||
if err := mw.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if l > len(mw.buf) {
|
||||
_, err := io.WriteString(mw.w, s)
|
||||
return err
|
||||
}
|
||||
}
|
||||
mw.wloc += copy(mw.buf[mw.wloc:], s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset changes the underlying writer used by the Writer
|
||||
func (mw *Writer) Reset(w io.Writer) {
|
||||
mw.buf = mw.buf[:cap(mw.buf)]
|
||||
mw.w = w
|
||||
mw.wloc = 0
|
||||
}
|
||||
|
||||
// WriteMapHeader writes a map header of the given
|
||||
// size to the writer
|
||||
func (mw *Writer) WriteMapHeader(sz uint32) error {
|
||||
switch {
|
||||
case sz <= 15:
|
||||
return mw.push(wfixmap(uint8(sz)))
|
||||
case sz <= math.MaxUint16:
|
||||
return mw.prefix16(mmap16, uint16(sz))
|
||||
default:
|
||||
return mw.prefix32(mmap32, sz)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteArrayHeader writes an array header of the
|
||||
// given size to the writer
|
||||
func (mw *Writer) WriteArrayHeader(sz uint32) error {
|
||||
switch {
|
||||
case sz <= 15:
|
||||
return mw.push(wfixarray(uint8(sz)))
|
||||
case sz <= math.MaxUint16:
|
||||
return mw.prefix16(marray16, uint16(sz))
|
||||
default:
|
||||
return mw.prefix32(marray32, sz)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteNil writes a nil byte to the buffer
|
||||
func (mw *Writer) WriteNil() error {
|
||||
return mw.push(mnil)
|
||||
}
|
||||
|
||||
// WriteFloat64 writes a float64 to the writer
|
||||
func (mw *Writer) WriteFloat64(f float64) error {
|
||||
return mw.prefix64(mfloat64, math.Float64bits(f))
|
||||
}
|
||||
|
||||
// WriteFloat32 writes a float32 to the writer
|
||||
func (mw *Writer) WriteFloat32(f float32) error {
|
||||
return mw.prefix32(mfloat32, math.Float32bits(f))
|
||||
}
|
||||
|
||||
// WriteInt64 writes an int64 to the writer
|
||||
func (mw *Writer) WriteInt64(i int64) error {
|
||||
if i >= 0 {
|
||||
switch {
|
||||
case i <= math.MaxInt8:
|
||||
return mw.push(wfixint(uint8(i)))
|
||||
case i <= math.MaxInt16:
|
||||
return mw.prefix16(mint16, uint16(i))
|
||||
case i <= math.MaxInt32:
|
||||
return mw.prefix32(mint32, uint32(i))
|
||||
default:
|
||||
return mw.prefix64(mint64, uint64(i))
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case i >= -32:
|
||||
return mw.push(wnfixint(int8(i)))
|
||||
case i >= math.MinInt8:
|
||||
return mw.prefix8(mint8, uint8(i))
|
||||
case i >= math.MinInt16:
|
||||
return mw.prefix16(mint16, uint16(i))
|
||||
case i >= math.MinInt32:
|
||||
return mw.prefix32(mint32, uint32(i))
|
||||
default:
|
||||
return mw.prefix64(mint64, uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// WriteInt8 writes an int8 to the writer
|
||||
func (mw *Writer) WriteInt8(i int8) error { return mw.WriteInt64(int64(i)) }
|
||||
|
||||
// WriteInt16 writes an int16 to the writer
|
||||
func (mw *Writer) WriteInt16(i int16) error { return mw.WriteInt64(int64(i)) }
|
||||
|
||||
// WriteInt32 writes an int32 to the writer
|
||||
func (mw *Writer) WriteInt32(i int32) error { return mw.WriteInt64(int64(i)) }
|
||||
|
||||
// WriteInt writes an int to the writer
|
||||
func (mw *Writer) WriteInt(i int) error { return mw.WriteInt64(int64(i)) }
|
||||
|
||||
// WriteUint64 writes a uint64 to the writer
|
||||
func (mw *Writer) WriteUint64(u uint64) error {
|
||||
switch {
|
||||
case u <= (1<<7)-1:
|
||||
return mw.push(wfixint(uint8(u)))
|
||||
case u <= math.MaxUint8:
|
||||
return mw.prefix8(muint8, uint8(u))
|
||||
case u <= math.MaxUint16:
|
||||
return mw.prefix16(muint16, uint16(u))
|
||||
case u <= math.MaxUint32:
|
||||
return mw.prefix32(muint32, uint32(u))
|
||||
default:
|
||||
return mw.prefix64(muint64, u)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteByte is analogous to WriteUint8
|
||||
func (mw *Writer) WriteByte(u byte) error { return mw.WriteUint8(uint8(u)) }
|
||||
|
||||
// WriteUint8 writes a uint8 to the writer
|
||||
func (mw *Writer) WriteUint8(u uint8) error { return mw.WriteUint64(uint64(u)) }
|
||||
|
||||
// WriteUint16 writes a uint16 to the writer
|
||||
func (mw *Writer) WriteUint16(u uint16) error { return mw.WriteUint64(uint64(u)) }
|
||||
|
||||
// WriteUint32 writes a uint32 to the writer
|
||||
func (mw *Writer) WriteUint32(u uint32) error { return mw.WriteUint64(uint64(u)) }
|
||||
|
||||
// WriteUint writes a uint to the writer
|
||||
func (mw *Writer) WriteUint(u uint) error { return mw.WriteUint64(uint64(u)) }
|
||||
|
||||
// WriteBytes writes binary as 'bin' to the writer
|
||||
func (mw *Writer) WriteBytes(b []byte) error {
|
||||
sz := uint32(len(b))
|
||||
var err error
|
||||
switch {
|
||||
case sz <= math.MaxUint8:
|
||||
err = mw.prefix8(mbin8, uint8(sz))
|
||||
case sz <= math.MaxUint16:
|
||||
err = mw.prefix16(mbin16, uint16(sz))
|
||||
default:
|
||||
err = mw.prefix32(mbin32, sz)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = mw.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteBytesHeader writes just the size header
|
||||
// of a MessagePack 'bin' object. The user is responsible
|
||||
// for then writing 'sz' more bytes into the stream.
|
||||
func (mw *Writer) WriteBytesHeader(sz uint32) error {
|
||||
switch {
|
||||
case sz <= math.MaxUint8:
|
||||
return mw.prefix8(mbin8, uint8(sz))
|
||||
case sz <= math.MaxUint16:
|
||||
return mw.prefix16(mbin16, uint16(sz))
|
||||
default:
|
||||
return mw.prefix32(mbin32, sz)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteBool writes a bool to the writer
|
||||
func (mw *Writer) WriteBool(b bool) error {
|
||||
if b {
|
||||
return mw.push(mtrue)
|
||||
}
|
||||
return mw.push(mfalse)
|
||||
}
|
||||
|
||||
// WriteString writes a messagepack string to the writer.
|
||||
// (This is NOT an implementation of io.StringWriter)
|
||||
func (mw *Writer) WriteString(s string) error {
|
||||
sz := uint32(len(s))
|
||||
var err error
|
||||
switch {
|
||||
case sz <= 31:
|
||||
err = mw.push(wfixstr(uint8(sz)))
|
||||
case sz <= math.MaxUint8:
|
||||
err = mw.prefix8(mstr8, uint8(sz))
|
||||
case sz <= math.MaxUint16:
|
||||
err = mw.prefix16(mstr16, uint16(sz))
|
||||
default:
|
||||
err = mw.prefix32(mstr32, sz)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mw.writeString(s)
|
||||
}
|
||||
|
||||
// WriteStringHeader writes just the string size
|
||||
// header of a MessagePack 'str' object. The user
|
||||
// is responsible for writing 'sz' more valid UTF-8
|
||||
// bytes to the stream.
|
||||
func (mw *Writer) WriteStringHeader(sz uint32) error {
|
||||
switch {
|
||||
case sz <= 31:
|
||||
return mw.push(wfixstr(uint8(sz)))
|
||||
case sz <= math.MaxUint8:
|
||||
return mw.prefix8(mstr8, uint8(sz))
|
||||
case sz <= math.MaxUint16:
|
||||
return mw.prefix16(mstr16, uint16(sz))
|
||||
default:
|
||||
return mw.prefix32(mstr32, sz)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteStringFromBytes writes a 'str' object
|
||||
// from a []byte.
|
||||
func (mw *Writer) WriteStringFromBytes(str []byte) error {
|
||||
sz := uint32(len(str))
|
||||
var err error
|
||||
switch {
|
||||
case sz <= 31:
|
||||
err = mw.push(wfixstr(uint8(sz)))
|
||||
case sz <= math.MaxUint8:
|
||||
err = mw.prefix8(mstr8, uint8(sz))
|
||||
case sz <= math.MaxUint16:
|
||||
err = mw.prefix16(mstr16, uint16(sz))
|
||||
default:
|
||||
err = mw.prefix32(mstr32, sz)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = mw.Write(str)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteComplex64 writes a complex64 to the writer
|
||||
func (mw *Writer) WriteComplex64(f complex64) error {
|
||||
o, err := mw.require(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext8
|
||||
mw.buf[o+1] = Complex64Extension
|
||||
big.PutUint32(mw.buf[o+2:], math.Float32bits(real(f)))
|
||||
big.PutUint32(mw.buf[o+6:], math.Float32bits(imag(f)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteComplex128 writes a complex128 to the writer
|
||||
func (mw *Writer) WriteComplex128(f complex128) error {
|
||||
o, err := mw.require(18)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mfixext16
|
||||
mw.buf[o+1] = Complex128Extension
|
||||
big.PutUint64(mw.buf[o+2:], math.Float64bits(real(f)))
|
||||
big.PutUint64(mw.buf[o+10:], math.Float64bits(imag(f)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMapStrStr writes a map[string]string to the writer
|
||||
func (mw *Writer) WriteMapStrStr(mp map[string]string) (err error) {
|
||||
err = mw.WriteMapHeader(uint32(len(mp)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for key, val := range mp {
|
||||
err = mw.WriteString(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = mw.WriteString(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMapStrIntf writes a map[string]interface to the writer
|
||||
func (mw *Writer) WriteMapStrIntf(mp map[string]interface{}) (err error) {
|
||||
err = mw.WriteMapHeader(uint32(len(mp)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for key, val := range mp {
|
||||
err = mw.WriteString(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = mw.WriteIntf(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTime writes a time.Time object to the wire.
|
||||
//
|
||||
// Time is encoded as Unix time, which means that
|
||||
// location (time zone) data is removed from the object.
|
||||
// The encoded object itself is 12 bytes: 8 bytes for
|
||||
// a big-endian 64-bit integer denoting seconds
|
||||
// elapsed since "zero" Unix time, followed by 4 bytes
|
||||
// for a big-endian 32-bit signed integer denoting
|
||||
// the nanosecond offset of the time. This encoding
|
||||
// is intended to ease portability across languages.
|
||||
// (Note that this is *not* the standard time.Time
|
||||
// binary encoding, because its implementation relies
|
||||
// heavily on the internal representation used by the
|
||||
// time package.)
|
||||
func (mw *Writer) WriteTime(t time.Time) error {
|
||||
t = t.UTC().Truncate(0) // strip out monotone clock
|
||||
o, err := mw.require(15)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext8
|
||||
mw.buf[o+1] = 12
|
||||
mw.buf[o+2] = TimeExtension
|
||||
putUnix(mw.buf[o+3:], t.Unix(), int32(t.Nanosecond()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteDuration writes a time.Duration object to the wire.
|
||||
//
|
||||
// Duration is encoded as int64.
|
||||
func (mw *Writer) WriteDuration(dur time.Duration) error {
|
||||
o, err := mw.require(12)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mw.buf[o] = mext8
|
||||
mw.buf[o+1] = 9
|
||||
mw.buf[o+2] = DurationExtension
|
||||
putMint64(mw.buf[o+3:], int64(dur))
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteIntf writes the concrete type of 'v'.
|
||||
// WriteIntf will error if 'v' is not one of the following:
|
||||
// - A bool, float, string, []byte, int, uint, or complex
|
||||
// - A map of supported types (with string keys)
|
||||
// - An array or slice of supported types
|
||||
// - A pointer to a supported type
|
||||
// - A type that satisfies the msgp.Encodable interface
|
||||
// - A type that satisfies the msgp.Extension interface
|
||||
func (mw *Writer) WriteIntf(v interface{}) error {
|
||||
if v == nil {
|
||||
return mw.WriteNil()
|
||||
}
|
||||
switch v := v.(type) {
|
||||
|
||||
// preferred interfaces
|
||||
|
||||
case Encodable:
|
||||
return v.EncodeMsg(mw)
|
||||
case Extension:
|
||||
return mw.WriteExtension(v)
|
||||
|
||||
// concrete types
|
||||
|
||||
case bool:
|
||||
return mw.WriteBool(v)
|
||||
case float32:
|
||||
return mw.WriteFloat32(v)
|
||||
case float64:
|
||||
return mw.WriteFloat64(v)
|
||||
case complex64:
|
||||
return mw.WriteComplex64(v)
|
||||
case complex128:
|
||||
return mw.WriteComplex128(v)
|
||||
case uint8:
|
||||
return mw.WriteUint8(v)
|
||||
case uint16:
|
||||
return mw.WriteUint16(v)
|
||||
case uint32:
|
||||
return mw.WriteUint32(v)
|
||||
case uint64:
|
||||
return mw.WriteUint64(v)
|
||||
case uint:
|
||||
return mw.WriteUint(v)
|
||||
case int8:
|
||||
return mw.WriteInt8(v)
|
||||
case int16:
|
||||
return mw.WriteInt16(v)
|
||||
case int32:
|
||||
return mw.WriteInt32(v)
|
||||
case int64:
|
||||
return mw.WriteInt64(v)
|
||||
case int:
|
||||
return mw.WriteInt(v)
|
||||
case string:
|
||||
return mw.WriteString(v)
|
||||
case []byte:
|
||||
return mw.WriteBytes(v)
|
||||
case map[string]string:
|
||||
return mw.WriteMapStrStr(v)
|
||||
case map[string]interface{}:
|
||||
return mw.WriteMapStrIntf(v)
|
||||
case time.Time:
|
||||
return mw.WriteTime(v)
|
||||
case time.Duration:
|
||||
return mw.WriteDuration(v)
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(v)
|
||||
if !isSupported(val.Kind()) || !val.IsValid() {
|
||||
return fmt.Errorf("msgp: type %s not supported(1)", val)
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
if val.IsNil() {
|
||||
return mw.WriteNil()
|
||||
}
|
||||
return mw.WriteIntf(val.Elem().Interface())
|
||||
case reflect.Slice:
|
||||
return mw.writeSlice(val)
|
||||
case reflect.Map:
|
||||
return mw.writeMap(val)
|
||||
}
|
||||
return &ErrUnsupportedType{val.Type()}
|
||||
}
|
||||
|
||||
func (mw *Writer) writeMap(v reflect.Value) (err error) {
|
||||
if v.Elem().Kind() != reflect.String {
|
||||
return errors.New("msgp: map keys must be strings")
|
||||
}
|
||||
ks := v.MapKeys()
|
||||
err = mw.WriteMapHeader(uint32(len(ks)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, key := range ks {
|
||||
val := v.MapIndex(key)
|
||||
err = mw.WriteString(key.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = mw.WriteIntf(val.Interface())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (mw *Writer) writeSlice(v reflect.Value) (err error) {
|
||||
// is []byte
|
||||
if v.Type().ConvertibleTo(btsType) {
|
||||
return mw.WriteBytes(v.Bytes())
|
||||
}
|
||||
|
||||
sz := uint32(v.Len())
|
||||
err = mw.WriteArrayHeader(sz)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := uint32(0); i < sz; i++ {
|
||||
err = mw.WriteIntf(v.Index(int(i)).Interface())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (mw *Writer) writeStruct(v reflect.Value) error {
|
||||
if enc, ok := v.Interface().(Encodable); ok {
|
||||
return enc.EncodeMsg(mw)
|
||||
}
|
||||
return fmt.Errorf("msgp: unsupported type: %s", v.Type())
|
||||
}
|
||||
|
||||
func (mw *Writer) writeVal(v reflect.Value) error {
|
||||
if !isSupported(v.Kind()) {
|
||||
return fmt.Errorf("msgp: msgp/enc: type %q not supported(2)", v.Type())
|
||||
}
|
||||
|
||||
// shortcut for nil values
|
||||
if v.IsNil() {
|
||||
return mw.WriteNil()
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return mw.WriteBool(v.Bool())
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return mw.WriteFloat64(v.Float())
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return mw.WriteComplex128(v.Complex())
|
||||
|
||||
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
|
||||
return mw.WriteInt64(v.Int())
|
||||
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
mw.WriteNil()
|
||||
}
|
||||
return mw.writeVal(v.Elem())
|
||||
|
||||
case reflect.Map:
|
||||
return mw.writeMap(v)
|
||||
|
||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:
|
||||
return mw.WriteUint64(v.Uint())
|
||||
|
||||
case reflect.String:
|
||||
return mw.WriteString(v.String())
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
return mw.writeSlice(v)
|
||||
|
||||
case reflect.Struct:
|
||||
return mw.writeStruct(v)
|
||||
|
||||
}
|
||||
return fmt.Errorf("msgp: msgp/enc: type %q not supported(3)", v.Type())
|
||||
}
|
||||
|
||||
// is the reflect.Kind encodable?
|
||||
func isSupported(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// GuessSize guesses the size of the underlying
|
||||
// value of 'i'. If the underlying value is not
|
||||
// a simple builtin (or []byte), GuessSize defaults
|
||||
// to 512.
|
||||
func GuessSize(i interface{}) int {
|
||||
if i == nil {
|
||||
return NilSize
|
||||
}
|
||||
|
||||
switch i := i.(type) {
|
||||
case Sizer:
|
||||
return i.Msgsize()
|
||||
case Extension:
|
||||
return ExtensionPrefixSize + i.Len()
|
||||
case float64:
|
||||
return Float64Size
|
||||
case float32:
|
||||
return Float32Size
|
||||
case uint8, uint16, uint32, uint64, uint:
|
||||
return UintSize
|
||||
case int8, int16, int32, int64, int:
|
||||
return IntSize
|
||||
case []byte:
|
||||
return BytesPrefixSize + len(i)
|
||||
case string:
|
||||
return StringPrefixSize + len(i)
|
||||
case complex64:
|
||||
return Complex64Size
|
||||
case complex128:
|
||||
return Complex128Size
|
||||
case bool:
|
||||
return BoolSize
|
||||
case map[string]interface{}:
|
||||
s := MapHeaderSize
|
||||
for key, val := range i {
|
||||
s += StringPrefixSize + len(key) + GuessSize(val)
|
||||
}
|
||||
return s
|
||||
case map[string]string:
|
||||
s := MapHeaderSize
|
||||
for key, val := range i {
|
||||
s += 2*StringPrefixSize + len(key) + len(val)
|
||||
}
|
||||
return s
|
||||
default:
|
||||
return 512
|
||||
}
|
||||
}
|
||||
464
vendor/github.com/glycerine/greenpack/msgp/write_bytes.go
generated
vendored
Normal file
464
vendor/github.com/glycerine/greenpack/msgp/write_bytes.go
generated
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
package msgp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ensure 'sz' extra bytes in 'b' btw len(b) and cap(b)
|
||||
func ensure(b []byte, sz int) ([]byte, int) {
|
||||
l := len(b)
|
||||
c := cap(b)
|
||||
if c-l < sz {
|
||||
o := make([]byte, (2*c)+sz) // exponential growth
|
||||
n := copy(o, b)
|
||||
return o[:n+sz], n
|
||||
}
|
||||
return b[:l+sz], l
|
||||
}
|
||||
|
||||
// AppendMapHeader appends a map header with the
|
||||
// given size to the slice
|
||||
func AppendMapHeader(b []byte, sz uint32) []byte {
|
||||
switch {
|
||||
case sz <= 15:
|
||||
return append(b, wfixmap(uint8(sz)))
|
||||
|
||||
case sz <= math.MaxUint16:
|
||||
o, n := ensure(b, 3)
|
||||
prefixu16(o[n:], mmap16, uint16(sz))
|
||||
return o
|
||||
|
||||
default:
|
||||
o, n := ensure(b, 5)
|
||||
prefixu32(o[n:], mmap32, sz)
|
||||
return o
|
||||
}
|
||||
}
|
||||
|
||||
// AppendArrayHeader appends an array header with
|
||||
// the given size to the slice
|
||||
func AppendArrayHeader(b []byte, sz uint32) []byte {
|
||||
switch {
|
||||
case sz <= 15:
|
||||
return append(b, wfixarray(uint8(sz)))
|
||||
|
||||
case sz <= math.MaxUint16:
|
||||
o, n := ensure(b, 3)
|
||||
prefixu16(o[n:], marray16, uint16(sz))
|
||||
return o
|
||||
|
||||
default:
|
||||
o, n := ensure(b, 5)
|
||||
prefixu32(o[n:], marray32, sz)
|
||||
return o
|
||||
}
|
||||
}
|
||||
|
||||
// AppendNil appends a 'nil' byte to the slice
|
||||
func AppendNil(b []byte) []byte { return append(b, mnil) }
|
||||
|
||||
// AppendFloat64 appends a float64 to the slice
|
||||
func AppendFloat64(b []byte, f float64) []byte {
|
||||
o, n := ensure(b, Float64Size)
|
||||
prefixu64(o[n:], mfloat64, math.Float64bits(f))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendFloat32 appends a float32 to the slice
|
||||
func AppendFloat32(b []byte, f float32) []byte {
|
||||
o, n := ensure(b, Float32Size)
|
||||
prefixu32(o[n:], mfloat32, math.Float32bits(f))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendInt64 appends an int64 to the slice
|
||||
func AppendInt64(b []byte, i int64) []byte {
|
||||
if i >= 0 {
|
||||
switch {
|
||||
case i <= math.MaxInt8:
|
||||
return append(b, wfixint(uint8(i)))
|
||||
case i <= math.MaxInt16:
|
||||
o, n := ensure(b, 3)
|
||||
putMint16(o[n:], int16(i))
|
||||
return o
|
||||
case i <= math.MaxInt32:
|
||||
o, n := ensure(b, 5)
|
||||
putMint32(o[n:], int32(i))
|
||||
return o
|
||||
default:
|
||||
o, n := ensure(b, 9)
|
||||
putMint64(o[n:], i)
|
||||
return o
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case i >= -32:
|
||||
return append(b, wnfixint(int8(i)))
|
||||
case i >= math.MinInt8:
|
||||
o, n := ensure(b, 2)
|
||||
putMint8(o[n:], int8(i))
|
||||
return o
|
||||
case i >= math.MinInt16:
|
||||
o, n := ensure(b, 3)
|
||||
putMint16(o[n:], int16(i))
|
||||
return o
|
||||
case i >= math.MinInt32:
|
||||
o, n := ensure(b, 5)
|
||||
putMint32(o[n:], int32(i))
|
||||
return o
|
||||
default:
|
||||
o, n := ensure(b, 9)
|
||||
putMint64(o[n:], i)
|
||||
return o
|
||||
}
|
||||
}
|
||||
|
||||
// AppendInt appends an int to the slice
|
||||
func AppendInt(b []byte, i int) []byte { return AppendInt64(b, int64(i)) }
|
||||
|
||||
// AppendInt8 appends an int8 to the slice
|
||||
func AppendInt8(b []byte, i int8) []byte { return AppendInt64(b, int64(i)) }
|
||||
|
||||
// AppendInt16 appends an int16 to the slice
|
||||
func AppendInt16(b []byte, i int16) []byte { return AppendInt64(b, int64(i)) }
|
||||
|
||||
// AppendInt32 appends an int32 to the slice
|
||||
func AppendInt32(b []byte, i int32) []byte { return AppendInt64(b, int64(i)) }
|
||||
|
||||
// AppendUint64 appends a uint64 to the slice
|
||||
func AppendUint64(b []byte, u uint64) []byte {
|
||||
switch {
|
||||
case u <= (1<<7)-1:
|
||||
return append(b, wfixint(uint8(u)))
|
||||
|
||||
case u <= math.MaxUint8:
|
||||
o, n := ensure(b, 2)
|
||||
putMuint8(o[n:], uint8(u))
|
||||
return o
|
||||
|
||||
case u <= math.MaxUint16:
|
||||
o, n := ensure(b, 3)
|
||||
putMuint16(o[n:], uint16(u))
|
||||
return o
|
||||
|
||||
case u <= math.MaxUint32:
|
||||
o, n := ensure(b, 5)
|
||||
putMuint32(o[n:], uint32(u))
|
||||
return o
|
||||
|
||||
default:
|
||||
o, n := ensure(b, 9)
|
||||
putMuint64(o[n:], u)
|
||||
return o
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// AppendUint appends a uint to the slice
|
||||
func AppendUint(b []byte, u uint) []byte { return AppendUint64(b, uint64(u)) }
|
||||
|
||||
// AppendUint8 appends a uint8 to the slice
|
||||
func AppendUint8(b []byte, u uint8) []byte { return AppendUint64(b, uint64(u)) }
|
||||
|
||||
// AppendByte is analogous to AppendUint8
|
||||
func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, uint8(u)) }
|
||||
|
||||
// AppendUint16 appends a uint16 to the slice
|
||||
func AppendUint16(b []byte, u uint16) []byte { return AppendUint64(b, uint64(u)) }
|
||||
|
||||
// AppendUint32 appends a uint32 to the slice
|
||||
func AppendUint32(b []byte, u uint32) []byte { return AppendUint64(b, uint64(u)) }
|
||||
|
||||
// AppendBytes appends bytes to the slice as MessagePack 'bin' data
|
||||
func AppendBytes(b []byte, bts []byte) []byte {
|
||||
sz := len(bts)
|
||||
var o []byte
|
||||
var n int
|
||||
switch {
|
||||
case sz <= math.MaxUint8:
|
||||
o, n = ensure(b, 2+sz)
|
||||
prefixu8(o[n:], mbin8, uint8(sz))
|
||||
n += 2
|
||||
case sz <= math.MaxUint16:
|
||||
o, n = ensure(b, 3+sz)
|
||||
prefixu16(o[n:], mbin16, uint16(sz))
|
||||
n += 3
|
||||
default:
|
||||
o, n = ensure(b, 5+sz)
|
||||
prefixu32(o[n:], mbin32, uint32(sz))
|
||||
n += 5
|
||||
}
|
||||
return o[:n+copy(o[n:], bts)]
|
||||
}
|
||||
|
||||
// AppendBool appends a bool to the slice
|
||||
func AppendBool(b []byte, t bool) []byte {
|
||||
if t {
|
||||
return append(b, mtrue)
|
||||
}
|
||||
return append(b, mfalse)
|
||||
}
|
||||
|
||||
// AppendString appends a string as a MessagePack 'str' to the slice
|
||||
func AppendString(b []byte, s string) []byte {
|
||||
sz := len(s)
|
||||
var n int
|
||||
var o []byte
|
||||
switch {
|
||||
case sz <= 31:
|
||||
o, n = ensure(b, 1+sz)
|
||||
o[n] = wfixstr(uint8(sz))
|
||||
n++
|
||||
case sz <= math.MaxUint8:
|
||||
o, n = ensure(b, 2+sz)
|
||||
prefixu8(o[n:], mstr8, uint8(sz))
|
||||
n += 2
|
||||
case sz <= math.MaxUint16:
|
||||
o, n = ensure(b, 3+sz)
|
||||
prefixu16(o[n:], mstr16, uint16(sz))
|
||||
n += 3
|
||||
default:
|
||||
o, n = ensure(b, 5+sz)
|
||||
prefixu32(o[n:], mstr32, uint32(sz))
|
||||
n += 5
|
||||
}
|
||||
return o[:n+copy(o[n:], s)]
|
||||
}
|
||||
|
||||
// AppendStringFromBytes appends a []byte
|
||||
// as a MessagePack 'str' to the slice 'b.'
|
||||
func AppendStringFromBytes(b []byte, str []byte) []byte {
|
||||
sz := len(str)
|
||||
var n int
|
||||
var o []byte
|
||||
switch {
|
||||
case sz <= 31:
|
||||
o, n = ensure(b, 1+sz)
|
||||
o[n] = wfixstr(uint8(sz))
|
||||
n++
|
||||
case sz <= math.MaxUint8:
|
||||
o, n = ensure(b, 2+sz)
|
||||
prefixu8(o[n:], mstr8, uint8(sz))
|
||||
n += 2
|
||||
case sz <= math.MaxUint16:
|
||||
o, n = ensure(b, 3+sz)
|
||||
prefixu16(o[n:], mstr16, uint16(sz))
|
||||
n += 3
|
||||
default:
|
||||
o, n = ensure(b, 5+sz)
|
||||
prefixu32(o[n:], mstr32, uint32(sz))
|
||||
n += 5
|
||||
}
|
||||
return o[:n+copy(o[n:], str)]
|
||||
}
|
||||
|
||||
// AppendComplex64 appends a complex64 to the slice as a MessagePack extension
|
||||
func AppendComplex64(b []byte, c complex64) []byte {
|
||||
o, n := ensure(b, Complex64Size)
|
||||
o[n] = mfixext8
|
||||
o[n+1] = Complex64Extension
|
||||
big.PutUint32(o[n+2:], math.Float32bits(real(c)))
|
||||
big.PutUint32(o[n+6:], math.Float32bits(imag(c)))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendComplex128 appends a complex128 to the slice as a MessagePack extension
|
||||
func AppendComplex128(b []byte, c complex128) []byte {
|
||||
o, n := ensure(b, Complex128Size)
|
||||
o[n] = mfixext16
|
||||
o[n+1] = Complex128Extension
|
||||
big.PutUint64(o[n+2:], math.Float64bits(real(c)))
|
||||
big.PutUint64(o[n+10:], math.Float64bits(imag(c)))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendTime appends a time.Time to the slice as a MessagePack extension
|
||||
func AppendTime(b []byte, t time.Time) []byte {
|
||||
o, n := ensure(b, TimeSize)
|
||||
t = t.UTC()
|
||||
o[n] = mext8
|
||||
o[n+1] = 12
|
||||
o[n+2] = TimeExtension
|
||||
putUnix(o[n+3:], t.Unix(), int32(t.Nanosecond()))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendDuration appends a time.Duration to the slice as a MessagePack extension
|
||||
func AppendDuration(b []byte, dur time.Duration) []byte {
|
||||
o, n := ensure(b, DurationSize)
|
||||
o[n] = mext8
|
||||
o[n+1] = 9
|
||||
o[n+2] = DurationExtension
|
||||
putMint64(o[n+3:], int64(dur))
|
||||
return o
|
||||
}
|
||||
|
||||
// AppendMapStrStr appends a map[string]string to the slice
|
||||
// as a MessagePack map with 'str'-type keys and values
|
||||
func AppendMapStrStr(b []byte, m map[string]string) []byte {
|
||||
sz := uint32(len(m))
|
||||
b = AppendMapHeader(b, sz)
|
||||
for key, val := range m {
|
||||
b = AppendString(b, key)
|
||||
b = AppendString(b, val)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendMapStrIntf appends a map[string]interface{} to the slice
|
||||
// as a MessagePack map with 'str'-type keys.
|
||||
func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) {
|
||||
sz := uint32(len(m))
|
||||
b = AppendMapHeader(b, sz)
|
||||
var err error
|
||||
for key, val := range m {
|
||||
b = AppendString(b, key)
|
||||
b, err = AppendIntf(b, val)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// AppendMapStrSomething appends a map[string]* to the slice
|
||||
// as a MessagePack map with 'str'-type keys. * must be
|
||||
// serializable by AppendIntf().
|
||||
func AppendMapStrSomething(b []byte, m reflect.Value) ([]byte, error) {
|
||||
|
||||
keys := m.MapKeys()
|
||||
sz := uint32(len(keys))
|
||||
if sz == 0 {
|
||||
b = AppendMapHeader(b, sz)
|
||||
return b, nil
|
||||
}
|
||||
var err error
|
||||
for i, key := range keys {
|
||||
if i == 0 {
|
||||
if key.Type().Kind() != reflect.String {
|
||||
return b, &ErrUnsupportedType{T: m.Type()}
|
||||
}
|
||||
// lazy because we try hard not to write
|
||||
// half a value to b and then error out.
|
||||
b = AppendMapHeader(b, sz)
|
||||
}
|
||||
|
||||
b = AppendString(b, key.String())
|
||||
val := m.MapIndex(key)
|
||||
b, err = AppendIntf(b, val.Interface())
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// AppendIntf appends the concrete type of 'i' to the
|
||||
// provided []byte. 'i' must be one of the following:
|
||||
// - 'nil'
|
||||
// - A bool, float, string, []byte, int, uint, or complex
|
||||
// - A map[string]interface{} or map[string]string
|
||||
// - A []T, where T is another supported type
|
||||
// - A *T, where T is another supported type
|
||||
// - A type that satisfieds the msgp.Marshaler interface
|
||||
// - A type that satisfies the msgp.Extension interface
|
||||
func AppendIntf(b []byte, i interface{}) ([]byte, error) {
|
||||
if i == nil {
|
||||
return AppendNil(b), nil
|
||||
}
|
||||
|
||||
// all the concrete types
|
||||
// for which we have methods
|
||||
switch i := i.(type) {
|
||||
case Marshaler:
|
||||
return i.MarshalMsg(b)
|
||||
case Extension:
|
||||
return AppendExtension(b, i)
|
||||
case bool:
|
||||
return AppendBool(b, i), nil
|
||||
case float32:
|
||||
return AppendFloat32(b, i), nil
|
||||
case float64:
|
||||
return AppendFloat64(b, i), nil
|
||||
case complex64:
|
||||
return AppendComplex64(b, i), nil
|
||||
case complex128:
|
||||
return AppendComplex128(b, i), nil
|
||||
case string:
|
||||
return AppendString(b, i), nil
|
||||
case []byte:
|
||||
return AppendBytes(b, i), nil
|
||||
case int8:
|
||||
return AppendInt8(b, i), nil
|
||||
case int16:
|
||||
return AppendInt16(b, i), nil
|
||||
case int32:
|
||||
return AppendInt32(b, i), nil
|
||||
case int64:
|
||||
return AppendInt64(b, i), nil
|
||||
case int:
|
||||
return AppendInt64(b, int64(i)), nil
|
||||
case uint:
|
||||
return AppendUint64(b, uint64(i)), nil
|
||||
case uint8:
|
||||
return AppendUint8(b, i), nil
|
||||
case uint16:
|
||||
return AppendUint16(b, i), nil
|
||||
case uint32:
|
||||
return AppendUint32(b, i), nil
|
||||
case uint64:
|
||||
return AppendUint64(b, i), nil
|
||||
case time.Time:
|
||||
return AppendTime(b, i), nil
|
||||
case time.Duration:
|
||||
return AppendDuration(b, i), nil
|
||||
case map[string]interface{}:
|
||||
return AppendMapStrIntf(b, i)
|
||||
case map[string]string:
|
||||
return AppendMapStrStr(b, i), nil
|
||||
case []interface{}:
|
||||
b = AppendArrayHeader(b, uint32(len(i)))
|
||||
var err error
|
||||
for _, k := range i {
|
||||
b, err = AppendIntf(b, k)
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
v := reflect.ValueOf(i)
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
l := v.Len()
|
||||
b = AppendArrayHeader(b, uint32(l))
|
||||
for i := 0; i < l; i++ {
|
||||
b, err = AppendIntf(b, v.Index(i).Interface())
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
return AppendNil(b), err
|
||||
}
|
||||
b, err = AppendIntf(b, v.Elem().Interface())
|
||||
return b, err
|
||||
|
||||
case reflect.Map:
|
||||
return AppendMapStrSomething(b, v)
|
||||
default:
|
||||
return b, &ErrUnsupportedType{T: v.Type()}
|
||||
}
|
||||
}
|
||||
|
||||
// AppendNegativeOneAndString is a helper for runtime struct id
|
||||
func AppendNegativeOneAndStringAsBytes(b []byte, str []byte) []byte {
|
||||
o := AppendInt64(b, -1)
|
||||
return AppendStringFromBytes(o, str)
|
||||
}
|
||||
21
vendor/github.com/glycerine/liner/COPYING
generated
vendored
Normal file
21
vendor/github.com/glycerine/liner/COPYING
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright © 2012 Peter Harris
|
||||
|
||||
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 (including the next
|
||||
paragraph) 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.
|
||||
|
||||
100
vendor/github.com/glycerine/liner/README.md
generated
vendored
Normal file
100
vendor/github.com/glycerine/liner/README.md
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
Liner
|
||||
=====
|
||||
|
||||
Liner is a command line editor with history. It was inspired by linenoise;
|
||||
everything Unix-like is a VT100 (or is trying very hard to be). If your
|
||||
terminal is not pretending to be a VT100, change it. Liner also support
|
||||
Windows.
|
||||
|
||||
Liner is released under the X11 license (which is similar to the new BSD
|
||||
license).
|
||||
|
||||
Line Editing
|
||||
------------
|
||||
|
||||
The following line editing commands are supported on platforms and terminals
|
||||
that Liner supports:
|
||||
|
||||
Keystroke | Action
|
||||
--------- | ------
|
||||
Ctrl-A, Home | Move cursor to beginning of line
|
||||
Ctrl-E, End | Move cursor to end of line
|
||||
Ctrl-B, Left | Move cursor one character left
|
||||
Ctrl-F, Right| Move cursor one character right
|
||||
Ctrl-Left, Alt-B | Move cursor to previous word
|
||||
Ctrl-Right, Alt-F | Move cursor to next word
|
||||
Ctrl-D, Del | (if line is *not* empty) Delete character under cursor
|
||||
Ctrl-D | (if line *is* empty) End of File - usually quits application
|
||||
Ctrl-C | Reset input (create new empty prompt)
|
||||
Ctrl-L | Clear screen (line is unmodified)
|
||||
Ctrl-T | Transpose previous character with current character
|
||||
Ctrl-H, BackSpace | Delete character before cursor
|
||||
Ctrl-W | Delete word leading up to cursor
|
||||
Ctrl-K | Delete from cursor to end of line
|
||||
Ctrl-U | Delete from start of line to cursor
|
||||
Ctrl-P, Up | Previous match from history
|
||||
Ctrl-N, Down | Next match from history
|
||||
Ctrl-R | Reverse Search history (Ctrl-S forward, Ctrl-G cancel)
|
||||
Ctrl-Y | Paste from Yank buffer (Alt-Y to paste next yank instead)
|
||||
Tab | Next completion
|
||||
Shift-Tab | (after Tab) Previous completion
|
||||
|
||||
Getting started
|
||||
-----------------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/peterh/liner"
|
||||
)
|
||||
|
||||
var (
|
||||
history_fn = filepath.Join(os.TempDir(), ".liner_example_history")
|
||||
names = []string{"john", "james", "mary", "nancy"}
|
||||
)
|
||||
|
||||
func main() {
|
||||
line := liner.NewLiner()
|
||||
defer line.Close()
|
||||
|
||||
line.SetCtrlCAborts(true)
|
||||
|
||||
line.SetCompleter(func(line string) (c []string) {
|
||||
for _, n := range names {
|
||||
if strings.HasPrefix(n, strings.ToLower(line)) {
|
||||
c = append(c, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
if f, err := os.Open(history_fn); err == nil {
|
||||
line.ReadHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
|
||||
if name, err := line.Prompt("What is your name? "); err == nil {
|
||||
log.Print("Got: ", name)
|
||||
line.AppendHistory(name)
|
||||
} else if err == liner.ErrPromptAborted {
|
||||
log.Print("Aborted")
|
||||
} else {
|
||||
log.Print("Error reading line: ", err)
|
||||
}
|
||||
|
||||
if f, err := os.Create(history_fn); err != nil {
|
||||
log.Print("Error writing history file: ", err)
|
||||
} else {
|
||||
line.WriteHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For documentation, see http://godoc.org/github.com/peterh/liner
|
||||
39
vendor/github.com/glycerine/liner/bsdinput.go
generated
vendored
Normal file
39
vendor/github.com/glycerine/liner/bsdinput.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// +build openbsd freebsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
)
|
||||
|
||||
const (
|
||||
// Input flags
|
||||
inpck = 0x010
|
||||
istrip = 0x020
|
||||
icrnl = 0x100
|
||||
ixon = 0x200
|
||||
|
||||
// Output flags
|
||||
opost = 0x1
|
||||
|
||||
// Control flags
|
||||
cs8 = 0x300
|
||||
|
||||
// Local flags
|
||||
isig = 0x080
|
||||
icanon = 0x100
|
||||
iexten = 0x400
|
||||
)
|
||||
|
||||
type termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed int32
|
||||
Ospeed int32
|
||||
}
|
||||
226
vendor/github.com/glycerine/liner/common.go
generated
vendored
Normal file
226
vendor/github.com/glycerine/liner/common.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
Package liner implements a simple command line editor, inspired by linenoise
|
||||
(https://github.com/antirez/linenoise/). This package supports WIN32 in
|
||||
addition to the xterm codes supported by everything else.
|
||||
*/
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/ring"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type commonState struct {
|
||||
terminalSupported bool
|
||||
outputRedirected bool
|
||||
inputRedirected bool
|
||||
history []string
|
||||
historyMutex sync.RWMutex
|
||||
completer WordCompleter
|
||||
columns int
|
||||
killRing *ring.Ring
|
||||
ctrlCAborts bool
|
||||
r *bufio.Reader
|
||||
tabStyle TabStyle
|
||||
multiLineMode bool
|
||||
cursorRows int
|
||||
maxRows int
|
||||
}
|
||||
|
||||
// TabStyle is used to select how tab completions are displayed.
|
||||
type TabStyle int
|
||||
|
||||
// Two tab styles are currently available:
|
||||
//
|
||||
// TabCircular cycles through each completion item and displays it directly on
|
||||
// the prompt
|
||||
//
|
||||
// TabPrints prints the list of completion items to the screen after a second
|
||||
// tab key is pressed. This behaves similar to GNU readline and BASH (which
|
||||
// uses readline)
|
||||
const (
|
||||
TabCircular TabStyle = iota
|
||||
TabPrints
|
||||
)
|
||||
|
||||
// ErrPromptAborted is returned from Prompt or PasswordPrompt when the user presses Ctrl-C
|
||||
// if SetCtrlCAborts(true) has been called on the State
|
||||
var ErrPromptAborted = errors.New("prompt aborted")
|
||||
|
||||
// ErrNotTerminalOutput is returned from Prompt or PasswordPrompt if the
|
||||
// platform is normally supported, but stdout has been redirected
|
||||
var ErrNotTerminalOutput = errors.New("standard output is not a terminal")
|
||||
|
||||
// Max elements to save on the killring
|
||||
const KillRingMax = 60
|
||||
|
||||
// HistoryLimit is the maximum number of entries saved in the scrollback history.
|
||||
const HistoryLimit = 1000
|
||||
|
||||
// ReadHistory reads scrollback history from r. Returns the number of lines
|
||||
// read, and any read error (except io.EOF).
|
||||
func (s *State) ReadHistory(r io.Reader) (num int, err error) {
|
||||
s.historyMutex.Lock()
|
||||
defer s.historyMutex.Unlock()
|
||||
|
||||
in := bufio.NewReader(r)
|
||||
num = 0
|
||||
for {
|
||||
line, part, err := in.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
if part {
|
||||
return num, fmt.Errorf("line %d is too long", num+1)
|
||||
}
|
||||
if !utf8.Valid(line) {
|
||||
return num, fmt.Errorf("invalid string at line %d", num+1)
|
||||
}
|
||||
num++
|
||||
s.history = append(s.history, string(line))
|
||||
if len(s.history) > HistoryLimit {
|
||||
s.history = s.history[1:]
|
||||
}
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// WriteHistory writes scrollback history to w. Returns the number of lines
|
||||
// successfully written, and any write error.
|
||||
//
|
||||
// Unlike the rest of liner's API, WriteHistory is safe to call
|
||||
// from another goroutine while Prompt is in progress.
|
||||
// This exception is to facilitate the saving of the history buffer
|
||||
// during an unexpected exit (for example, due to Ctrl-C being invoked)
|
||||
func (s *State) WriteHistory(w io.Writer) (num int, err error) {
|
||||
s.historyMutex.RLock()
|
||||
defer s.historyMutex.RUnlock()
|
||||
|
||||
for _, item := range s.history {
|
||||
_, err := fmt.Fprintln(w, item)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
num++
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// AppendHistory appends an entry to the scrollback history. AppendHistory
|
||||
// should be called iff Prompt returns a valid command.
|
||||
func (s *State) AppendHistory(item string) {
|
||||
s.historyMutex.Lock()
|
||||
defer s.historyMutex.Unlock()
|
||||
|
||||
if len(s.history) > 0 {
|
||||
if item == s.history[len(s.history)-1] {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.history = append(s.history, item)
|
||||
if len(s.history) > HistoryLimit {
|
||||
s.history = s.history[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the history lines starting with prefix
|
||||
func (s *State) getHistoryByPrefix(prefix string) (ph []string) {
|
||||
for _, h := range s.history {
|
||||
if strings.HasPrefix(h, prefix) {
|
||||
ph = append(ph, h)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the history lines matching the inteligent search
|
||||
func (s *State) getHistoryByPattern(pattern string) (ph []string, pos []int) {
|
||||
if pattern == "" {
|
||||
return
|
||||
}
|
||||
for _, h := range s.history {
|
||||
if i := strings.Index(h, pattern); i >= 0 {
|
||||
ph = append(ph, h)
|
||||
pos = append(pos, i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Completer takes the currently edited line content at the left of the cursor
|
||||
// and returns a list of completion candidates.
|
||||
// If the line is "Hello, wo!!!" and the cursor is before the first '!', "Hello, wo" is passed
|
||||
// to the completer which may return {"Hello, world", "Hello, Word"} to have "Hello, world!!!".
|
||||
type Completer func(line string) []string
|
||||
|
||||
// WordCompleter takes the currently edited line with the cursor position and
|
||||
// returns the completion candidates for the partial word to be completed.
|
||||
// If the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, wo!!!", 9) is passed
|
||||
// to the completer which may returns ("Hello, ", {"world", "Word"}, "!!!") to have "Hello, world!!!".
|
||||
type WordCompleter func(line string, pos int) (head string, completions []string, tail string)
|
||||
|
||||
// SetCompleter sets the completion function that Liner will call to
|
||||
// fetch completion candidates when the user presses tab.
|
||||
func (s *State) SetCompleter(f Completer) {
|
||||
if f == nil {
|
||||
s.completer = nil
|
||||
return
|
||||
}
|
||||
s.completer = func(line string, pos int) (string, []string, string) {
|
||||
return "", f(string([]rune(line)[:pos])), string([]rune(line)[pos:])
|
||||
}
|
||||
}
|
||||
|
||||
// SetWordCompleter sets the completion function that Liner will call to
|
||||
// fetch completion candidates when the user presses tab.
|
||||
func (s *State) SetWordCompleter(f WordCompleter) {
|
||||
s.completer = f
|
||||
}
|
||||
|
||||
// SetTabCompletionStyle sets the behvavior when the Tab key is pressed
|
||||
// for auto-completion. TabCircular is the default behavior and cycles
|
||||
// through the list of candidates at the prompt. TabPrints will print
|
||||
// the available completion candidates to the screen similar to BASH
|
||||
// and GNU Readline
|
||||
func (s *State) SetTabCompletionStyle(tabStyle TabStyle) {
|
||||
s.tabStyle = tabStyle
|
||||
}
|
||||
|
||||
// ModeApplier is the interface that wraps a representation of the terminal
|
||||
// mode. ApplyMode sets the terminal to this mode.
|
||||
type ModeApplier interface {
|
||||
ApplyMode() error
|
||||
}
|
||||
|
||||
// SetCtrlCAborts sets whether Prompt on a supported terminal will return an
|
||||
// ErrPromptAborted when Ctrl-C is pressed. The default is false (will not
|
||||
// return when Ctrl-C is pressed). Unsupported terminals typically raise SIGINT
|
||||
// (and Prompt does not return) regardless of the value passed to SetCtrlCAborts.
|
||||
func (s *State) SetCtrlCAborts(aborts bool) {
|
||||
s.ctrlCAborts = aborts
|
||||
}
|
||||
|
||||
// SetMultiLineMode sets whether line is auto-wrapped. The default is false (single line).
|
||||
func (s *State) SetMultiLineMode(mlmode bool) {
|
||||
s.multiLineMode = mlmode
|
||||
}
|
||||
|
||||
func (s *State) promptUnsupported(p string) (string, error) {
|
||||
if !s.inputRedirected || !s.terminalSupported {
|
||||
fmt.Print(p)
|
||||
}
|
||||
linebuf, _, err := s.r.ReadLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(linebuf), nil
|
||||
}
|
||||
57
vendor/github.com/glycerine/liner/fallbackinput.go
generated
vendored
Normal file
57
vendor/github.com/glycerine/liner/fallbackinput.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// State represents an open terminal
|
||||
type State struct {
|
||||
commonState
|
||||
}
|
||||
|
||||
// Prompt displays p, and then waits for user input. Prompt does not support
|
||||
// line editing on this operating system.
|
||||
func (s *State) Prompt(p string) (string, error) {
|
||||
return s.promptUnsupported(p)
|
||||
}
|
||||
|
||||
// PasswordPrompt is not supported in this OS.
|
||||
func (s *State) PasswordPrompt(p string) (string, error) {
|
||||
return "", errors.New("liner: function not supported in this terminal")
|
||||
}
|
||||
|
||||
// NewLiner initializes a new *State
|
||||
//
|
||||
// Note that this operating system uses a fallback mode without line
|
||||
// editing. Patches welcome.
|
||||
func NewLiner() *State {
|
||||
var s State
|
||||
s.r = bufio.NewReader(os.Stdin)
|
||||
return &s
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode
|
||||
func (s *State) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalSupported returns false because line editing is not
|
||||
// supported on this platform.
|
||||
func TerminalSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type noopMode struct{}
|
||||
|
||||
func (n noopMode) ApplyMode() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalMode returns a noop InputModeSetter on this platform.
|
||||
func TerminalMode() (ModeApplier, error) {
|
||||
return noopMode{}, nil
|
||||
}
|
||||
368
vendor/github.com/glycerine/liner/input.go
generated
vendored
Normal file
368
vendor/github.com/glycerine/liner/input.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// +build linux darwin openbsd freebsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type nexter struct {
|
||||
r rune
|
||||
err error
|
||||
}
|
||||
|
||||
// State represents an open terminal
|
||||
type State struct {
|
||||
commonState
|
||||
origMode termios
|
||||
defaultMode termios
|
||||
next <-chan nexter
|
||||
winch chan os.Signal
|
||||
pending []rune
|
||||
useCHA bool
|
||||
}
|
||||
|
||||
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
|
||||
// restore the terminal to its previous state, call State.Close().
|
||||
//
|
||||
// Note if you are still using Go 1.0: NewLiner handles SIGWINCH, so it will
|
||||
// leak a channel every time you call it. Therefore, it is recommened that you
|
||||
// upgrade to a newer release of Go, or ensure that NewLiner is only called
|
||||
// once.
|
||||
func NewLiner() *State {
|
||||
var s State
|
||||
s.r = bufio.NewReader(os.Stdin)
|
||||
|
||||
s.terminalSupported = TerminalSupported()
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.origMode = *m.(*termios)
|
||||
} else {
|
||||
s.inputRedirected = true
|
||||
}
|
||||
if _, err := getMode(syscall.Stdout); err != 0 {
|
||||
s.outputRedirected = true
|
||||
}
|
||||
if s.inputRedirected && s.outputRedirected {
|
||||
s.terminalSupported = false
|
||||
}
|
||||
if s.terminalSupported && !s.inputRedirected && !s.outputRedirected {
|
||||
mode := s.origMode
|
||||
mode.Iflag &^= icrnl | inpck | istrip | ixon
|
||||
mode.Cflag |= cs8
|
||||
mode.Lflag &^= syscall.ECHO | icanon | iexten
|
||||
mode.ApplyMode()
|
||||
|
||||
winch := make(chan os.Signal, 1)
|
||||
signal.Notify(winch, syscall.SIGWINCH)
|
||||
s.winch = winch
|
||||
|
||||
s.checkOutput()
|
||||
}
|
||||
|
||||
if !s.outputRedirected {
|
||||
s.getColumns()
|
||||
s.outputRedirected = s.columns <= 0
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
var errTimedOut = errors.New("timeout")
|
||||
|
||||
func (s *State) startPrompt() {
|
||||
if s.terminalSupported {
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.defaultMode = *m.(*termios)
|
||||
mode := s.defaultMode
|
||||
mode.Lflag &^= isig
|
||||
mode.ApplyMode()
|
||||
}
|
||||
}
|
||||
s.restartPrompt()
|
||||
}
|
||||
|
||||
func (s *State) restartPrompt() {
|
||||
next := make(chan nexter)
|
||||
go func() {
|
||||
for {
|
||||
var n nexter
|
||||
n.r, _, n.err = s.r.ReadRune()
|
||||
next <- n
|
||||
// Shut down nexter loop when an end condition has been reached
|
||||
if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD {
|
||||
close(next)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
s.next = next
|
||||
}
|
||||
|
||||
func (s *State) stopPrompt() {
|
||||
if s.terminalSupported {
|
||||
s.defaultMode.ApplyMode()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
|
||||
select {
|
||||
case thing, ok := <-s.next:
|
||||
if !ok {
|
||||
return 0, errors.New("liner: internal error")
|
||||
}
|
||||
if thing.err != nil {
|
||||
return 0, thing.err
|
||||
}
|
||||
s.pending = append(s.pending, thing.r)
|
||||
return thing.r, nil
|
||||
case <-timeout:
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, errTimedOut
|
||||
}
|
||||
// not reached
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *State) readNext() (interface{}, error) {
|
||||
if len(s.pending) > 0 {
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
var r rune
|
||||
select {
|
||||
case thing, ok := <-s.next:
|
||||
if !ok {
|
||||
return 0, errors.New("liner: internal error")
|
||||
}
|
||||
if thing.err != nil {
|
||||
return nil, thing.err
|
||||
}
|
||||
r = thing.r
|
||||
case <-s.winch:
|
||||
s.getColumns()
|
||||
return winch, nil
|
||||
}
|
||||
if r != esc {
|
||||
return r, nil
|
||||
}
|
||||
s.pending = append(s.pending, r)
|
||||
|
||||
// Wait at most 50 ms for the rest of the escape sequence
|
||||
// If nothing else arrives, it was an actual press of the esc key
|
||||
timeout := time.After(50 * time.Millisecond)
|
||||
flag, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return flag, nil
|
||||
}
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
switch flag {
|
||||
case '[':
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return unknown, err
|
||||
}
|
||||
switch code {
|
||||
case 'A':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return up, nil
|
||||
case 'B':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return down, nil
|
||||
case 'C':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return right, nil
|
||||
case 'D':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return left, nil
|
||||
case 'F':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return end, nil
|
||||
case 'H':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return home, nil
|
||||
case 'Z':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return shiftTab, nil
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num := []rune{code}
|
||||
for {
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch code {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num = append(num, code)
|
||||
case ';':
|
||||
// Modifier code to follow
|
||||
// This only supports Ctrl-left and Ctrl-right for now
|
||||
x, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
if x != 1 {
|
||||
// Can't be left or right
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
num = num[:0]
|
||||
for {
|
||||
code, err = s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch code {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num = append(num, code)
|
||||
case 'C', 'D':
|
||||
// right, left
|
||||
mod, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
if mod != 5 {
|
||||
// Not bare Ctrl
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
if code == 'C' {
|
||||
return wordRight, nil
|
||||
}
|
||||
return wordLeft, nil
|
||||
default:
|
||||
// Not left or right
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
}
|
||||
case '~':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
x, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
switch x {
|
||||
case 2:
|
||||
return insert, nil
|
||||
case 3:
|
||||
return del, nil
|
||||
case 5:
|
||||
return pageUp, nil
|
||||
case 6:
|
||||
return pageDown, nil
|
||||
case 7:
|
||||
return home, nil
|
||||
case 8:
|
||||
return end, nil
|
||||
case 15:
|
||||
return f5, nil
|
||||
case 17:
|
||||
return f6, nil
|
||||
case 18:
|
||||
return f7, nil
|
||||
case 19:
|
||||
return f8, nil
|
||||
case 20:
|
||||
return f9, nil
|
||||
case 21:
|
||||
return f10, nil
|
||||
case 23:
|
||||
return f11, nil
|
||||
case 24:
|
||||
return f12, nil
|
||||
default:
|
||||
return unknown, nil
|
||||
}
|
||||
default:
|
||||
// unrecognized escape code
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'O':
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
switch code {
|
||||
case 'c':
|
||||
return wordRight, nil
|
||||
case 'd':
|
||||
return wordLeft, nil
|
||||
case 'H':
|
||||
return home, nil
|
||||
case 'F':
|
||||
return end, nil
|
||||
case 'P':
|
||||
return f1, nil
|
||||
case 'Q':
|
||||
return f2, nil
|
||||
case 'R':
|
||||
return f3, nil
|
||||
case 'S':
|
||||
return f4, nil
|
||||
default:
|
||||
return unknown, nil
|
||||
}
|
||||
case 'b':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altB, nil
|
||||
case 'f':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altF, nil
|
||||
case 'y':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altY, nil
|
||||
default:
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// not reached
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode
|
||||
func (s *State) Close() error {
|
||||
stopSignal(s.winch)
|
||||
if !s.inputRedirected {
|
||||
s.origMode.ApplyMode()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalSupported returns true if the current terminal supports
|
||||
// line editing features, and false if liner will use the 'dumb'
|
||||
// fallback for input.
|
||||
// Note that TerminalSupported does not check all factors that may
|
||||
// cause liner to not fully support the terminal (such as stdin redirection)
|
||||
func TerminalSupported() bool {
|
||||
bad := map[string]bool{"": true, "dumb": true, "cons25": true}
|
||||
return !bad[strings.ToLower(os.Getenv("TERM"))]
|
||||
}
|
||||
39
vendor/github.com/glycerine/liner/input_darwin.go
generated
vendored
Normal file
39
vendor/github.com/glycerine/liner/input_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// +build darwin
|
||||
|
||||
package liner
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
)
|
||||
|
||||
const (
|
||||
// Input flags
|
||||
inpck = 0x010
|
||||
istrip = 0x020
|
||||
icrnl = 0x100
|
||||
ixon = 0x200
|
||||
|
||||
// Output flags
|
||||
opost = 0x1
|
||||
|
||||
// Control flags
|
||||
cs8 = 0x300
|
||||
|
||||
// Local flags
|
||||
isig = 0x080
|
||||
icanon = 0x100
|
||||
iexten = 0x400
|
||||
)
|
||||
|
||||
type termios struct {
|
||||
Iflag uintptr
|
||||
Oflag uintptr
|
||||
Cflag uintptr
|
||||
Lflag uintptr
|
||||
Cc [20]byte
|
||||
Ispeed uintptr
|
||||
Ospeed uintptr
|
||||
}
|
||||
26
vendor/github.com/glycerine/liner/input_linux.go
generated
vendored
Normal file
26
vendor/github.com/glycerine/liner/input_linux.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// +build linux
|
||||
|
||||
package liner
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TCGETS
|
||||
setTermios = syscall.TCSETS
|
||||
)
|
||||
|
||||
const (
|
||||
icrnl = syscall.ICRNL
|
||||
inpck = syscall.INPCK
|
||||
istrip = syscall.ISTRIP
|
||||
ixon = syscall.IXON
|
||||
opost = syscall.OPOST
|
||||
cs8 = syscall.CS8
|
||||
isig = syscall.ISIG
|
||||
icanon = syscall.ICANON
|
||||
iexten = syscall.IEXTEN
|
||||
)
|
||||
|
||||
type termios struct {
|
||||
syscall.Termios
|
||||
}
|
||||
321
vendor/github.com/glycerine/liner/input_windows.go
generated
vendored
Normal file
321
vendor/github.com/glycerine/liner/input_windows.go
generated
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||
procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
)
|
||||
|
||||
// These names are from the Win32 api, so they use underscores (contrary to
|
||||
// what golint suggests)
|
||||
const (
|
||||
std_input_handle = uint32(-10 & 0xFFFFFFFF)
|
||||
std_output_handle = uint32(-11 & 0xFFFFFFFF)
|
||||
std_error_handle = uint32(-12 & 0xFFFFFFFF)
|
||||
invalid_handle_value = ^uintptr(0)
|
||||
)
|
||||
|
||||
type inputMode uint32
|
||||
|
||||
// State represents an open terminal
|
||||
type State struct {
|
||||
commonState
|
||||
handle syscall.Handle
|
||||
hOut syscall.Handle
|
||||
origMode inputMode
|
||||
defaultMode inputMode
|
||||
key interface{}
|
||||
repeat uint16
|
||||
}
|
||||
|
||||
const (
|
||||
enableEchoInput = 0x4
|
||||
enableInsertMode = 0x20
|
||||
enableLineInput = 0x2
|
||||
enableMouseInput = 0x10
|
||||
enableProcessedInput = 0x1
|
||||
enableQuickEditMode = 0x40
|
||||
enableWindowInput = 0x8
|
||||
)
|
||||
|
||||
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
|
||||
// restore the terminal to its previous state, call State.Close().
|
||||
func NewLiner() *State {
|
||||
var s State
|
||||
hIn, _, _ := procGetStdHandle.Call(uintptr(std_input_handle))
|
||||
s.handle = syscall.Handle(hIn)
|
||||
hOut, _, _ := procGetStdHandle.Call(uintptr(std_output_handle))
|
||||
s.hOut = syscall.Handle(hOut)
|
||||
|
||||
s.terminalSupported = true
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.origMode = m.(inputMode)
|
||||
mode := s.origMode
|
||||
mode &^= enableEchoInput
|
||||
mode &^= enableInsertMode
|
||||
mode &^= enableLineInput
|
||||
mode &^= enableMouseInput
|
||||
mode |= enableWindowInput
|
||||
mode.ApplyMode()
|
||||
} else {
|
||||
s.inputRedirected = true
|
||||
s.r = bufio.NewReader(os.Stdin)
|
||||
}
|
||||
|
||||
s.getColumns()
|
||||
s.outputRedirected = s.columns <= 0
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
// These names are from the Win32 api, so they use underscores (contrary to
|
||||
// what golint suggests)
|
||||
const (
|
||||
focus_event = 0x0010
|
||||
key_event = 0x0001
|
||||
menu_event = 0x0008
|
||||
mouse_event = 0x0002
|
||||
window_buffer_size_event = 0x0004
|
||||
)
|
||||
|
||||
type input_record struct {
|
||||
eventType uint16
|
||||
pad uint16
|
||||
blob [16]byte
|
||||
}
|
||||
|
||||
type key_event_record struct {
|
||||
KeyDown int32
|
||||
RepeatCount uint16
|
||||
VirtualKeyCode uint16
|
||||
VirtualScanCode uint16
|
||||
Char int16
|
||||
ControlKeyState uint32
|
||||
}
|
||||
|
||||
// These names are from the Win32 api, so they use underscores (contrary to
|
||||
// what golint suggests)
|
||||
const (
|
||||
vk_tab = 0x09
|
||||
vk_prior = 0x21
|
||||
vk_next = 0x22
|
||||
vk_end = 0x23
|
||||
vk_home = 0x24
|
||||
vk_left = 0x25
|
||||
vk_up = 0x26
|
||||
vk_right = 0x27
|
||||
vk_down = 0x28
|
||||
vk_insert = 0x2d
|
||||
vk_delete = 0x2e
|
||||
vk_f1 = 0x70
|
||||
vk_f2 = 0x71
|
||||
vk_f3 = 0x72
|
||||
vk_f4 = 0x73
|
||||
vk_f5 = 0x74
|
||||
vk_f6 = 0x75
|
||||
vk_f7 = 0x76
|
||||
vk_f8 = 0x77
|
||||
vk_f9 = 0x78
|
||||
vk_f10 = 0x79
|
||||
vk_f11 = 0x7a
|
||||
vk_f12 = 0x7b
|
||||
bKey = 0x42
|
||||
fKey = 0x46
|
||||
yKey = 0x59
|
||||
)
|
||||
|
||||
const (
|
||||
shiftPressed = 0x0010
|
||||
leftAltPressed = 0x0002
|
||||
leftCtrlPressed = 0x0008
|
||||
rightAltPressed = 0x0001
|
||||
rightCtrlPressed = 0x0004
|
||||
|
||||
modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed
|
||||
)
|
||||
|
||||
func (s *State) readNext() (interface{}, error) {
|
||||
if s.repeat > 0 {
|
||||
s.repeat--
|
||||
return s.key, nil
|
||||
}
|
||||
|
||||
var input input_record
|
||||
pbuf := uintptr(unsafe.Pointer(&input))
|
||||
var rv uint32
|
||||
prv := uintptr(unsafe.Pointer(&rv))
|
||||
|
||||
for {
|
||||
ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv)
|
||||
|
||||
if ok == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if input.eventType == window_buffer_size_event {
|
||||
xy := (*coord)(unsafe.Pointer(&input.blob[0]))
|
||||
s.columns = int(xy.x)
|
||||
return winch, nil
|
||||
}
|
||||
if input.eventType != key_event {
|
||||
continue
|
||||
}
|
||||
ke := (*key_event_record)(unsafe.Pointer(&input.blob[0]))
|
||||
if ke.KeyDown == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed {
|
||||
s.key = shiftTab
|
||||
} else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
|
||||
ke.ControlKeyState&modKeys == rightAltPressed) {
|
||||
s.key = altB
|
||||
} else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
|
||||
ke.ControlKeyState&modKeys == rightAltPressed) {
|
||||
s.key = altF
|
||||
} else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
|
||||
ke.ControlKeyState&modKeys == rightAltPressed) {
|
||||
s.key = altY
|
||||
} else if ke.Char > 0 {
|
||||
s.key = rune(ke.Char)
|
||||
} else {
|
||||
switch ke.VirtualKeyCode {
|
||||
case vk_prior:
|
||||
s.key = pageUp
|
||||
case vk_next:
|
||||
s.key = pageDown
|
||||
case vk_end:
|
||||
s.key = end
|
||||
case vk_home:
|
||||
s.key = home
|
||||
case vk_left:
|
||||
s.key = left
|
||||
if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
|
||||
if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
|
||||
s.key = wordLeft
|
||||
}
|
||||
}
|
||||
case vk_right:
|
||||
s.key = right
|
||||
if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
|
||||
if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
|
||||
s.key = wordRight
|
||||
}
|
||||
}
|
||||
case vk_up:
|
||||
s.key = up
|
||||
case vk_down:
|
||||
s.key = down
|
||||
case vk_insert:
|
||||
s.key = insert
|
||||
case vk_delete:
|
||||
s.key = del
|
||||
case vk_f1:
|
||||
s.key = f1
|
||||
case vk_f2:
|
||||
s.key = f2
|
||||
case vk_f3:
|
||||
s.key = f3
|
||||
case vk_f4:
|
||||
s.key = f4
|
||||
case vk_f5:
|
||||
s.key = f5
|
||||
case vk_f6:
|
||||
s.key = f6
|
||||
case vk_f7:
|
||||
s.key = f7
|
||||
case vk_f8:
|
||||
s.key = f8
|
||||
case vk_f9:
|
||||
s.key = f9
|
||||
case vk_f10:
|
||||
s.key = f10
|
||||
case vk_f11:
|
||||
s.key = f11
|
||||
case vk_f12:
|
||||
s.key = f12
|
||||
default:
|
||||
// Eat modifier keys
|
||||
// TODO: return Action(Unknown) if the key isn't a
|
||||
// modifier.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ke.RepeatCount > 1 {
|
||||
s.repeat = ke.RepeatCount - 1
|
||||
}
|
||||
return s.key, nil
|
||||
}
|
||||
return unknown, nil
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode
|
||||
func (s *State) Close() error {
|
||||
s.origMode.ApplyMode()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *State) startPrompt() {
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.defaultMode = m.(inputMode)
|
||||
mode := s.defaultMode
|
||||
mode &^= enableProcessedInput
|
||||
mode.ApplyMode()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) restartPrompt() {
|
||||
}
|
||||
|
||||
func (s *State) stopPrompt() {
|
||||
s.defaultMode.ApplyMode()
|
||||
}
|
||||
|
||||
// TerminalSupported returns true because line editing is always
|
||||
// supported on Windows.
|
||||
func TerminalSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (mode inputMode) ApplyMode() error {
|
||||
hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
|
||||
if hIn == invalid_handle_value || hIn == 0 {
|
||||
return err
|
||||
}
|
||||
ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode))
|
||||
if ok != 0 {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TerminalMode returns the current terminal input mode as an InputModeSetter.
|
||||
//
|
||||
// This function is provided for convenience, and should
|
||||
// not be necessary for most users of liner.
|
||||
func TerminalMode() (ModeApplier, error) {
|
||||
var mode inputMode
|
||||
hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
|
||||
if hIn == invalid_handle_value || hIn == 0 {
|
||||
return nil, err
|
||||
}
|
||||
ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode)))
|
||||
if ok != 0 {
|
||||
err = nil
|
||||
}
|
||||
return mode, err
|
||||
}
|
||||
1007
vendor/github.com/glycerine/liner/line.go
generated
vendored
Normal file
1007
vendor/github.com/glycerine/liner/line.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
75
vendor/github.com/glycerine/liner/output.go
generated
vendored
Normal file
75
vendor/github.com/glycerine/liner/output.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// +build linux darwin openbsd freebsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (s *State) cursorPos(x int) {
|
||||
if s.useCHA {
|
||||
// 'G' is "Cursor Character Absolute (CHA)"
|
||||
fmt.Printf("\x1b[%dG", x+1)
|
||||
} else {
|
||||
// 'C' is "Cursor Forward (CUF)"
|
||||
fmt.Print("\r")
|
||||
if x > 0 {
|
||||
fmt.Printf("\x1b[%dC", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) eraseLine() {
|
||||
fmt.Print("\x1b[0K")
|
||||
}
|
||||
|
||||
func (s *State) eraseScreen() {
|
||||
fmt.Print("\x1b[H\x1b[2J")
|
||||
}
|
||||
|
||||
func (s *State) moveUp(lines int) {
|
||||
fmt.Printf("\x1b[%dA", lines)
|
||||
}
|
||||
|
||||
func (s *State) moveDown(lines int) {
|
||||
fmt.Printf("\x1b[%dB", lines)
|
||||
}
|
||||
|
||||
func (s *State) emitNewLine() {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
|
||||
type winSize struct {
|
||||
row, col uint16
|
||||
xpixel, ypixel uint16
|
||||
}
|
||||
|
||||
func (s *State) getColumns() {
|
||||
var ws winSize
|
||||
ok, _, _ := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdout),
|
||||
syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws)))
|
||||
if ok < 0 {
|
||||
s.columns = 80
|
||||
}
|
||||
s.columns = int(ws.col)
|
||||
}
|
||||
|
||||
func (s *State) checkOutput() {
|
||||
// xterm is known to support CHA
|
||||
if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") {
|
||||
s.useCHA = true
|
||||
return
|
||||
}
|
||||
|
||||
// The test for functional ANSI CHA is unreliable (eg the Windows
|
||||
// telnet command does not support reading the cursor position with
|
||||
// an ANSI DSR request, despite setting TERM=ansi)
|
||||
|
||||
// Assume CHA isn't supported (which should be safe, although it
|
||||
// does result in occasional visible cursor jitter)
|
||||
s.useCHA = false
|
||||
}
|
||||
72
vendor/github.com/glycerine/liner/output_windows.go
generated
vendored
Normal file
72
vendor/github.com/glycerine/liner/output_windows.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package liner
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type coord struct {
|
||||
x, y int16
|
||||
}
|
||||
type smallRect struct {
|
||||
left, top, right, bottom int16
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
dwSize coord
|
||||
dwCursorPosition coord
|
||||
wAttributes int16
|
||||
srWindow smallRect
|
||||
dwMaximumWindowSize coord
|
||||
}
|
||||
|
||||
func (s *State) cursorPos(x int) {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(s.hOut),
|
||||
uintptr(int(x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16))
|
||||
}
|
||||
|
||||
func (s *State) eraseLine() {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
var numWritten uint32
|
||||
procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '),
|
||||
uintptr(sbi.dwSize.x-sbi.dwCursorPosition.x),
|
||||
uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16),
|
||||
uintptr(unsafe.Pointer(&numWritten)))
|
||||
}
|
||||
|
||||
func (s *State) eraseScreen() {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
var numWritten uint32
|
||||
procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '),
|
||||
uintptr(sbi.dwSize.x)*uintptr(sbi.dwSize.y),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&numWritten)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(s.hOut), 0)
|
||||
}
|
||||
|
||||
func (s *State) moveUp(lines int) {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(s.hOut),
|
||||
uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)-lines)<<16))
|
||||
}
|
||||
|
||||
func (s *State) moveDown(lines int) {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(s.hOut),
|
||||
uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)+lines)<<16))
|
||||
}
|
||||
|
||||
func (s *State) emitNewLine() {
|
||||
// windows doesn't need to omit a new line
|
||||
}
|
||||
|
||||
func (s *State) getColumns() {
|
||||
var sbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
|
||||
s.columns = int(sbi.dwSize.x)
|
||||
}
|
||||
12
vendor/github.com/glycerine/liner/signal.go
generated
vendored
Normal file
12
vendor/github.com/glycerine/liner/signal.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build go1.1,!windows
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func stopSignal(c chan<- os.Signal) {
|
||||
signal.Stop(c)
|
||||
}
|
||||
11
vendor/github.com/glycerine/liner/signal_legacy.go
generated
vendored
Normal file
11
vendor/github.com/glycerine/liner/signal_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build !go1.1,!windows
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func stopSignal(c chan<- os.Signal) {
|
||||
// signal.Stop does not exist before Go 1.1
|
||||
}
|
||||
37
vendor/github.com/glycerine/liner/unixmode.go
generated
vendored
Normal file
37
vendor/github.com/glycerine/liner/unixmode.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// +build linux darwin freebsd openbsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (mode *termios) ApplyMode() error {
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), setTermios, uintptr(unsafe.Pointer(mode)))
|
||||
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalMode returns the current terminal input mode as an InputModeSetter.
|
||||
//
|
||||
// This function is provided for convenience, and should
|
||||
// not be necessary for most users of liner.
|
||||
func TerminalMode() (ModeApplier, error) {
|
||||
mode, errno := getMode(syscall.Stdin)
|
||||
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
func getMode(handle int) (*termios, syscall.Errno) {
|
||||
var mode termios
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(handle), getTermios, uintptr(unsafe.Pointer(&mode)))
|
||||
|
||||
return &mode, errno
|
||||
}
|
||||
79
vendor/github.com/glycerine/liner/width.go
generated
vendored
Normal file
79
vendor/github.com/glycerine/liner/width.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package liner
|
||||
|
||||
import "unicode"
|
||||
|
||||
// These character classes are mostly zero width (when combined).
|
||||
// A few might not be, depending on the user's font. Fixing this
|
||||
// is non-trivial, given that some terminals don't support
|
||||
// ANSI DSR/CPR
|
||||
var zeroWidth = []*unicode.RangeTable{
|
||||
unicode.Mn,
|
||||
unicode.Me,
|
||||
unicode.Cc,
|
||||
unicode.Cf,
|
||||
}
|
||||
|
||||
var doubleWidth = []*unicode.RangeTable{
|
||||
unicode.Han,
|
||||
unicode.Hangul,
|
||||
unicode.Hiragana,
|
||||
unicode.Katakana,
|
||||
}
|
||||
|
||||
// countGlyphs considers zero-width characters to be zero glyphs wide,
|
||||
// and members of Chinese, Japanese, and Korean scripts to be 2 glyphs wide.
|
||||
func countGlyphs(s []rune) int {
|
||||
n := 0
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case unicode.IsOneOf(zeroWidth, r):
|
||||
case unicode.IsOneOf(doubleWidth, r):
|
||||
n += 2
|
||||
default:
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func countMultiLineGlyphs(s []rune, columns int, start int) int {
|
||||
n := start
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case unicode.IsOneOf(zeroWidth, r):
|
||||
case unicode.IsOneOf(doubleWidth, r):
|
||||
n += 2
|
||||
// no room for a 2-glyphs-wide char in the ending
|
||||
// so skip a column and display it at the beginning
|
||||
if n%columns == 1 {
|
||||
n++
|
||||
}
|
||||
default:
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func getPrefixGlyphs(s []rune, num int) []rune {
|
||||
p := 0
|
||||
for n := 0; n < num && p < len(s); p++ {
|
||||
if !unicode.IsOneOf(zeroWidth, s[p]) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
for p < len(s) && unicode.IsOneOf(zeroWidth, s[p]) {
|
||||
p++
|
||||
}
|
||||
return s[:p]
|
||||
}
|
||||
|
||||
func getSuffixGlyphs(s []rune, num int) []rune {
|
||||
p := len(s)
|
||||
for n := 0; n < num && p > 0; p-- {
|
||||
if !unicode.IsOneOf(zeroWidth, s[p-1]) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return s[p:]
|
||||
}
|
||||
22
vendor/github.com/glycerine/zygomys/LICENSE
generated
vendored
Normal file
22
vendor/github.com/glycerine/zygomys/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2016, The zygomys authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
21
vendor/github.com/glycerine/zygomys/zygo/address.go
generated
vendored
Normal file
21
vendor/github.com/glycerine/zygomys/zygo/address.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package zygo
|
||||
|
||||
type Address struct {
|
||||
function *SexpFunction
|
||||
position int
|
||||
}
|
||||
|
||||
func (a Address) IsStackElem() {}
|
||||
|
||||
func (stack *Stack) PushAddr(function *SexpFunction, pc int) {
|
||||
stack.Push(Address{function, pc})
|
||||
}
|
||||
|
||||
func (stack *Stack) PopAddr() (*SexpFunction, int, error) {
|
||||
elem, err := stack.Pop()
|
||||
if err != nil {
|
||||
return MissingFunction, 0, err
|
||||
}
|
||||
addr := elem.(Address)
|
||||
return addr.function, addr.position, nil
|
||||
}
|
||||
221
vendor/github.com/glycerine/zygomys/zygo/arrayutils.go
generated
vendored
Normal file
221
vendor/github.com/glycerine/zygomys/zygo/arrayutils.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
package zygo
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MapArray(env *Zlisp, fun *SexpFunction, arr *SexpArray) (Sexp, error) {
|
||||
result := make([]Sexp, len(arr.Val))
|
||||
var err error
|
||||
|
||||
var firstTyp *RegisteredType
|
||||
for i := range arr.Val {
|
||||
result[i], err = env.Apply(fun, arr.Val[i:i+1])
|
||||
if err != nil {
|
||||
return &SexpArray{Val: result, Typ: firstTyp, Env: env}, err
|
||||
}
|
||||
if firstTyp == nil {
|
||||
firstTyp = result[i].Type()
|
||||
}
|
||||
}
|
||||
|
||||
return &SexpArray{Val: result, Typ: firstTyp, Env: env}, nil
|
||||
}
|
||||
|
||||
func ConcatArray(arr *SexpArray, rest []Sexp) (Sexp, error) {
|
||||
if arr == nil {
|
||||
return SexpNull, fmt.Errorf("ConcatArray called with nil arr")
|
||||
}
|
||||
var res SexpArray
|
||||
res.Val = arr.Val
|
||||
for i, x := range rest {
|
||||
switch t := x.(type) {
|
||||
case *SexpArray:
|
||||
res.Val = append(res.Val, t.Val...)
|
||||
default:
|
||||
return &res, fmt.Errorf("ConcatArray error: %d-th argument "+
|
||||
"(0-based) is not an array", i)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// (arrayidx ar [0 1])
|
||||
func ArrayIndexFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("in ArrayIndexFunction, args = '%#v'", args)
|
||||
narg := len(args)
|
||||
if narg != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var err error
|
||||
args, err = env.ResolveDotSym(args)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
var ar *SexpArray
|
||||
switch ar2 := args[0].(type) {
|
||||
case *SexpArraySelector:
|
||||
x, err := ar2.RHS(env)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
switch xArr := x.(type) {
|
||||
case *SexpArray:
|
||||
ar = xArr
|
||||
case *SexpHash:
|
||||
return HashIndexFunction(env, name, []Sexp{xArr, args[1]})
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: ar as arrayidx, but that did not resolve to an array, instead '%s'/type %T", x.SexpString(nil), x)
|
||||
}
|
||||
case *SexpArray:
|
||||
ar = ar2
|
||||
case *SexpHash:
|
||||
return HashIndexFunction(env, name, args)
|
||||
case *SexpHashSelector:
|
||||
Q("ArrayIndexFunction sees args[0] is a hashSelector")
|
||||
return HashIndexFunction(env, name, args)
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: ar was not an array, instead '%s'/type %T",
|
||||
args[0].SexpString(nil), args[0])
|
||||
}
|
||||
|
||||
var idx *SexpArray
|
||||
switch idx2 := args[1].(type) {
|
||||
case *SexpArray:
|
||||
idx = idx2
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index was not an array, instead '%s'/type %T",
|
||||
args[1].SexpString(nil), args[1])
|
||||
}
|
||||
|
||||
ret := SexpArraySelector{
|
||||
Select: idx,
|
||||
Container: ar,
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
// IndexBy subsets one array (possibly multidimensional) by another.
|
||||
// e.g. if arr is [a b c] and idx is [0], we'll return a.
|
||||
func (arr *SexpArray) IndexBy(idx *SexpArray) (Sexp, error) {
|
||||
nIdx := len(idx.Val)
|
||||
nTarget := arr.NumDim()
|
||||
|
||||
if nIdx > nTarget {
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index requested %d dimensions, only have %d",
|
||||
nIdx, nTarget)
|
||||
}
|
||||
|
||||
if len(idx.Val) == 0 {
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: no index supplied")
|
||||
}
|
||||
if len(idx.Val) != 1 {
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: we only support a single index value atm")
|
||||
}
|
||||
|
||||
i := 0
|
||||
myInt, isInt := idx.Val[i].(*SexpInt)
|
||||
if !isInt {
|
||||
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index with non-integer '%v'",
|
||||
idx.Val[i].SexpString(nil))
|
||||
}
|
||||
k := myInt.Val
|
||||
pos := k % int64(len(arr.Val))
|
||||
if k < 0 {
|
||||
mk := -k
|
||||
mod := mk % int64(len(arr.Val))
|
||||
pos = int64(len(arr.Val)) - mod
|
||||
}
|
||||
//Q("return pos %v", pos)
|
||||
return arr.Val[pos], nil
|
||||
}
|
||||
|
||||
func (arr *SexpArray) NumDim() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// SexpSelector: select a subset of an array:
|
||||
// can be multidimensional index/slice
|
||||
// and hence know its container and its position(s),
|
||||
// and thus be able to read and write that position as
|
||||
// need be.
|
||||
type SexpArraySelector struct {
|
||||
Select *SexpArray
|
||||
Container *SexpArray
|
||||
}
|
||||
|
||||
func (si *SexpArraySelector) SexpString(ps *PrintState) string {
|
||||
Q("in SexpArraySelector.SexpString(), si.Container.Env = %p", si.Container.Env)
|
||||
rhs, err := si.RHS(si.Container.Env)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("(arraySelector %v %v)", si.Container.SexpString(ps), si.Select.SexpString(ps))
|
||||
}
|
||||
|
||||
Q("in SexpArraySelector.SexpString(), rhs = %v", rhs.SexpString(ps))
|
||||
Q("in SexpArraySelector.SexpString(), si.Container = %v", si.Container.SexpString(ps))
|
||||
Q("in SexpArraySelector.SexpString(), si.Select = %v", si.Select.SexpString(ps))
|
||||
|
||||
return fmt.Sprintf("%v /*(arraySelector %v %v)*/", rhs.SexpString(ps), si.Container.SexpString(ps), si.Select.SexpString(ps))
|
||||
}
|
||||
|
||||
// Type returns the type of the value.
|
||||
func (si *SexpArraySelector) Type() *RegisteredType {
|
||||
return GoStructRegistry.Lookup("arraySelector")
|
||||
}
|
||||
|
||||
// RHS applies the selector to the contain and returns
|
||||
// the value obtained.
|
||||
func (x *SexpArraySelector) RHS(env *Zlisp) (Sexp, error) {
|
||||
if len(x.Select.Val) != 1 {
|
||||
return SexpNull, fmt.Errorf("SexpArraySelector: only " +
|
||||
"size 1 selectors implemented")
|
||||
}
|
||||
var i int64
|
||||
switch asInt := x.Select.Val[0].(type) {
|
||||
case *SexpInt:
|
||||
i = asInt.Val
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("SexpArraySelector: int "+
|
||||
"selector required; we saw %T", x.Select.Val[0])
|
||||
}
|
||||
if i < 0 {
|
||||
return SexpNull, fmt.Errorf("SexpArraySelector: negative "+
|
||||
"indexes not supported; we saw %v", i)
|
||||
}
|
||||
if i >= int64(len(x.Container.Val)) {
|
||||
return SexpNull, fmt.Errorf("SexpArraySelector: index "+
|
||||
"%v is out-of-bounds; length is %v", i, len(x.Container.Val))
|
||||
}
|
||||
ret := x.Container.Val[i]
|
||||
Q("arraySelector returning ret = %#v", ret)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Selector stores indexing information that isn't
|
||||
// yet materialized for getting or setting.
|
||||
//
|
||||
type Selector interface {
|
||||
// RHS (right-hand-side) is used to dereference
|
||||
// the pointer-like Selector, yielding a value suitable for the
|
||||
// right-hand-side of an assignment statement.
|
||||
//
|
||||
RHS(env *Zlisp) (Sexp, error)
|
||||
|
||||
// AssignToSelection sets the selection to rhs
|
||||
// The selected elements are the left-hand-side of the
|
||||
// assignment *lhs = rhs
|
||||
AssignToSelection(env *Zlisp, rhs Sexp) error
|
||||
}
|
||||
|
||||
func (x *SexpArraySelector) AssignToSelection(env *Zlisp, rhs Sexp) error {
|
||||
_, err := x.RHS(x.Container.Env) // check for errors
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
x.Container.Val[x.Select.Val[0].(*SexpInt).Val] = rhs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewSexpArray(arr []Sexp) *SexpArray {
|
||||
return &SexpArray{Val: arr, Env: env}
|
||||
}
|
||||
1
vendor/github.com/glycerine/zygomys/zygo/basetypes.go
generated
vendored
Normal file
1
vendor/github.com/glycerine/zygomys/zygo/basetypes.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package zygo
|
||||
26
vendor/github.com/glycerine/zygomys/zygo/blake2.go
generated
vendored
Normal file
26
vendor/github.com/glycerine/zygomys/zygo/blake2.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/glycerine/blake2b"
|
||||
)
|
||||
|
||||
// Blake2bUint64 returns an 8 byte BLAKE2b cryptographic
|
||||
// hash of the raw.
|
||||
//
|
||||
// we're using the pure go: https://github.com/dchest/blake2b
|
||||
//
|
||||
// but the C-wrapped refence may be helpful as well --
|
||||
//
|
||||
// reference: https://godoc.org/github.com/codahale/blake2
|
||||
// reference: https://blake2.net/
|
||||
// reference: https://tools.ietf.org/html/rfc7693
|
||||
//
|
||||
func Blake2bUint64(raw []byte) uint64 {
|
||||
cfg := &blake2b.Config{Size: 8}
|
||||
h, err := blake2b.New(cfg)
|
||||
panicOn(err)
|
||||
h.Write(raw)
|
||||
by := h.Sum(nil)
|
||||
return binary.LittleEndian.Uint64(by[:8])
|
||||
}
|
||||
128
vendor/github.com/glycerine/zygomys/zygo/bsave.go
generated
vendored
Normal file
128
vendor/github.com/glycerine/zygomys/zygo/bsave.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/glycerine/greenpack/msgp"
|
||||
)
|
||||
|
||||
// (bsave value path) writes value as greenpack to file.
|
||||
//
|
||||
// (greenpack value) writes value as greenpack to SexpRaw in memory.
|
||||
//
|
||||
// bsave converts to binary with (togo) then saves the binary to file.
|
||||
func WriteShadowGreenpackToFileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
narg := len(args)
|
||||
if narg < 1 || narg > 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
// check arg[0]
|
||||
var asHash *SexpHash
|
||||
switch x := args[0].(type) {
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("%s error: top value must be a hash or defmap; we see '%T'", name, args[0])
|
||||
case *SexpHash:
|
||||
// okay, good
|
||||
asHash = x
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "bsave":
|
||||
if narg != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
case "greenpack":
|
||||
if narg != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
_, err := toGreenpackHelper(env, asHash, &buf, "memory")
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpRaw{Val: buf.Bytes()}, nil
|
||||
}
|
||||
|
||||
// check arg[1]
|
||||
var fn string
|
||||
switch fna := args[1].(type) {
|
||||
case *SexpStr:
|
||||
fn = fna.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("error: %s requires a string (SexpStr) path to write to as the second argument. we got type %T / value = %v", name, args[1], args[1])
|
||||
}
|
||||
|
||||
// don't overwrite existing file
|
||||
if FileExists(fn) {
|
||||
return SexpNull, fmt.Errorf("error: %s refusing to write to existing file '%s'",
|
||||
name, fn)
|
||||
}
|
||||
|
||||
f, err := os.Create(fn)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error: %s sees error trying to create file '%s': '%v'", name, fn, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = toGreenpackHelper(env, asHash, f, fn)
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
func toGreenpackHelper(env *Zlisp, asHash *SexpHash, f io.Writer, fn string) (Sexp, error) {
|
||||
|
||||
// create shadow structs
|
||||
_, err := ToGoFunction(env, "togo", []Sexp{asHash})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("ToGo call sees error: '%v'", err)
|
||||
}
|
||||
|
||||
if asHash.GoShadowStruct == nil {
|
||||
return SexpNull, fmt.Errorf("GoShadowStruct was nil, on attempt to write to '%s'", fn)
|
||||
}
|
||||
|
||||
enc, ok := interface{}(asHash.GoShadowStruct).(msgp.Encodable)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("error: GoShadowStruct was not greenpack Encodable -- run `go generate` or add greenpack to the source file for type '%T'. on attempt to save to '%s'", asHash.GoShadowStruct, fn)
|
||||
}
|
||||
w := msgp.NewWriter(f)
|
||||
err = msgp.Encode(w, enc)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error: greenpack encoding to file '%s' of type '%T' sees error '%v'", fn, asHash.GoShadowStruct, err)
|
||||
}
|
||||
err = w.Flush()
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
func ReadGreenpackFromFileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
narg := len(args)
|
||||
|
||||
if narg != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var fn string
|
||||
switch fna := args[0].(type) {
|
||||
case *SexpStr:
|
||||
fn = fna.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("%s requires a string path to read. we got type %T / value = %v", name, args[0], args[0])
|
||||
}
|
||||
|
||||
if !FileExists(string(fn)) {
|
||||
return SexpNull, fmt.Errorf("file '%s' does not exist", fn)
|
||||
}
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
defer f.Close()
|
||||
by, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return MsgpackToSexp(by, env)
|
||||
}
|
||||
879
vendor/github.com/glycerine/zygomys/zygo/builders.go
generated
vendored
Normal file
879
vendor/github.com/glycerine/zygomys/zygo/builders.go
generated
vendored
Normal file
@@ -0,0 +1,879 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// package.go: declare package, structs, function types
|
||||
|
||||
// A builder is a special kind of function. Like
|
||||
// a macro it receives the un-evaluated tree
|
||||
// of symbols from its caller. A builder
|
||||
// can therefore be used to build new types
|
||||
// and declarations new functions/methods.
|
||||
//
|
||||
// Like a function, a builder is called at
|
||||
// run/evaluation time, not at definition time.
|
||||
//
|
||||
// Since it receives an un-evaluated tree of
|
||||
// symbols, a builder must manually evaluate
|
||||
// any arguments it wishes to find bindings for.
|
||||
//
|
||||
// The primary use here is to be able to define
|
||||
// packages, structs, interfaces, functions,
|
||||
// methods, and type aliases.
|
||||
//
|
||||
func (env *Zlisp) ImportPackageBuilder() {
|
||||
env.AddBuilder("infixExpand", InfixBuilder)
|
||||
env.AddBuilder("infix", InfixBuilder)
|
||||
env.AddBuilder(":", ColonAccessBuilder)
|
||||
env.AddBuilder("sys", SystemBuilder)
|
||||
env.AddBuilder("struct", StructBuilder)
|
||||
env.AddBuilder("func", FuncBuilder)
|
||||
env.AddBuilder("method", FuncBuilder)
|
||||
env.AddBuilder("interface", InterfaceBuilder)
|
||||
//env.AddBuilder("package", PackageBuilder)
|
||||
//env.AddBuilder("import", ImportBuilder)
|
||||
env.AddBuilder("var", VarBuilder)
|
||||
env.AddBuilder("expectError", ExpectErrorBuilder)
|
||||
env.AddBuilder("comma", CommaBuilder)
|
||||
// env.AddBuilder("&", AddressOfBuilder)
|
||||
|
||||
env.AddBuilder("import", ImportPackageBuilder)
|
||||
|
||||
env.AddFunction("sliceOf", SliceOfFunction)
|
||||
env.AddFunction("ptr", PointerToFunction)
|
||||
}
|
||||
|
||||
var sxSliceOf *SexpFunction = MakeUserFunction("sliceOf", SliceOfFunction)
|
||||
var sxArrayOf *SexpFunction = MakeUserFunction("arrayOf", ArrayOfFunction)
|
||||
|
||||
type SexpUserVarDefn struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type RecordDefn struct {
|
||||
Name string
|
||||
Fields []*SexpField
|
||||
FieldType map[string]*RegisteredType
|
||||
}
|
||||
|
||||
func NewRecordDefn() *RecordDefn {
|
||||
return &RecordDefn{
|
||||
FieldType: make(map[string]*RegisteredType),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RecordDefn) SetName(name string) {
|
||||
r.Name = name
|
||||
}
|
||||
func (r *RecordDefn) SetFields(flds []*SexpField) {
|
||||
r.Fields = flds
|
||||
for _, f := range flds {
|
||||
g := (*SexpHash)(f)
|
||||
rt, err := g.HashGet(nil, f.KeyOrder[0])
|
||||
panicOn(err)
|
||||
r.FieldType[g.KeyOrder[0].(*SexpSymbol).name] = rt.(*RegisteredType)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *RecordDefn) Type() *RegisteredType {
|
||||
rt := GoStructRegistry.Registry[p.Name]
|
||||
//Q("RecordDefn) Type() sees rt = %v", rt)
|
||||
return rt
|
||||
}
|
||||
|
||||
// pretty print a struct
|
||||
func (p *RecordDefn) SexpString(ps *PrintState) string {
|
||||
Q("RecordDefn::SexpString() called!")
|
||||
if len(p.Fields) == 0 {
|
||||
return fmt.Sprintf("(struct %s)", p.Name)
|
||||
}
|
||||
s := fmt.Sprintf("(struct %s [\n", p.Name)
|
||||
|
||||
w := make([][]int, len(p.Fields))
|
||||
maxnfield := 0
|
||||
for i, f := range p.Fields {
|
||||
w[i] = f.FieldWidths()
|
||||
Q("w[i=%v] = %v", i, w[i])
|
||||
maxnfield = maxi(maxnfield, len(w[i]))
|
||||
}
|
||||
|
||||
biggestCol := make([]int, maxnfield)
|
||||
Q("\n")
|
||||
for j := 0; j < maxnfield; j++ {
|
||||
for i := range p.Fields {
|
||||
Q("i= %v, j=%v, len(w[i])=%v check=%v", i, j, len(w[i]), len(w[i]) < j)
|
||||
if j < len(w[i]) {
|
||||
biggestCol[j] = maxi(biggestCol[j], w[i][j]+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
Q("RecordDefn::SexpString(): maxnfield = %v, out of %v", maxnfield, len(p.Fields))
|
||||
Q("RecordDefn::SexpString(): biggestCol = %#v", biggestCol)
|
||||
|
||||
// computing padding
|
||||
// x
|
||||
// xx xx
|
||||
// xxxxxxx x
|
||||
// xxx x x x
|
||||
//
|
||||
// becomes
|
||||
//
|
||||
// x
|
||||
// xx xx
|
||||
// xxxxxxx
|
||||
// xxx x x x
|
||||
Q("pad = %#v", biggestCol)
|
||||
for _, f := range p.Fields {
|
||||
s += " " + f.AlignString(biggestCol) + "\n"
|
||||
}
|
||||
s += " ])\n"
|
||||
return s
|
||||
}
|
||||
|
||||
func maxi(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
type SexpField SexpHash
|
||||
|
||||
func (r SexpField) Type() *RegisteredType {
|
||||
return r.GoStructFactory
|
||||
}
|
||||
|
||||
// compute key and value widths to assist alignment
|
||||
func (f *SexpField) FieldWidths() []int {
|
||||
hash := (*SexpHash)(f)
|
||||
wide := []int{}
|
||||
for _, key := range hash.KeyOrder {
|
||||
val, err := hash.HashGet(nil, key)
|
||||
str := ""
|
||||
if err == nil {
|
||||
switch s := key.(type) {
|
||||
case *SexpStr:
|
||||
str += s.S + ":"
|
||||
case *SexpSymbol:
|
||||
str += s.name + ":"
|
||||
default:
|
||||
str += key.SexpString(nil) + ":"
|
||||
}
|
||||
wide = append(wide, len(str))
|
||||
wide = append(wide, len(val.SexpString(nil))+1)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return wide
|
||||
}
|
||||
|
||||
func (f *SexpField) AlignString(pad []int) string {
|
||||
hash := (*SexpHash)(f)
|
||||
str := " (" + hash.TypeName + " "
|
||||
spc := " "
|
||||
for i, key := range hash.KeyOrder {
|
||||
val, err := hash.HashGet(nil, key)
|
||||
r := ""
|
||||
if err == nil {
|
||||
switch s := key.(type) {
|
||||
case *SexpStr:
|
||||
r += s.S + ":"
|
||||
case *SexpSymbol:
|
||||
r += s.name + ":"
|
||||
default:
|
||||
r += key.SexpString(nil) + ":"
|
||||
}
|
||||
xtra := pad[i*2] - len(r)
|
||||
if xtra < 0 {
|
||||
panic(fmt.Sprintf("xtra = %d, pad[i=%v]=%v, len(r)=%v (r=%v)", xtra, i, pad[i], len(r), r))
|
||||
}
|
||||
leftpad := strings.Repeat(" ", xtra)
|
||||
vs := val.SexpString(nil)
|
||||
rightpad := strings.Repeat(" ", pad[(i*2)+1]-len(vs))
|
||||
if i == 0 {
|
||||
spc = " "
|
||||
} else {
|
||||
spc = ""
|
||||
}
|
||||
r = leftpad + r + spc + vs + rightpad
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
str += r
|
||||
}
|
||||
if len(hash.Map) > 0 {
|
||||
return str[:len(str)-1] + ")"
|
||||
}
|
||||
return str + ")"
|
||||
}
|
||||
|
||||
func (f *SexpField) SexpString(ps *PrintState) string {
|
||||
hash := (*SexpHash)(f)
|
||||
str := " (" + hash.TypeName + " "
|
||||
|
||||
for i, key := range hash.KeyOrder {
|
||||
val, err := hash.HashGet(nil, key)
|
||||
if err == nil {
|
||||
switch s := key.(type) {
|
||||
case *SexpStr:
|
||||
str += s.S + ":"
|
||||
case *SexpSymbol:
|
||||
str += s.name + ":"
|
||||
default:
|
||||
str += key.SexpString(nil) + ":"
|
||||
}
|
||||
if i > 0 {
|
||||
str += val.SexpString(nil) + " "
|
||||
} else {
|
||||
str += val.SexpString(nil) + " "
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if len(hash.Map) > 0 {
|
||||
return str[:len(str)-1] + ")"
|
||||
}
|
||||
return str + ")"
|
||||
}
|
||||
|
||||
func StructBuilder(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
n := len(args)
|
||||
if n < 1 {
|
||||
return SexpNull, fmt.Errorf("struct name is missing. use: " +
|
||||
"(struct struct-name ...)\n")
|
||||
}
|
||||
|
||||
Q("in struct builder, name = '%s', args = ", name)
|
||||
for i := range args {
|
||||
Q("args[%v] = '%s' of type %T", i, args[i].SexpString(nil), args[i])
|
||||
}
|
||||
var symN *SexpSymbol
|
||||
switch b := args[0].(type) {
|
||||
case *SexpSymbol:
|
||||
symN = b
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(b)
|
||||
if isQuo {
|
||||
symN = sy.(*SexpSymbol)
|
||||
} else {
|
||||
return SexpNull, fmt.Errorf("bad struct name: symbol required")
|
||||
}
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad struct name: symbol required")
|
||||
}
|
||||
|
||||
Q("good: have struct name '%v'", symN)
|
||||
|
||||
env.datastack.PushExpr(SexpNull)
|
||||
structName := symN.name
|
||||
|
||||
{
|
||||
// begin enable recursion -- add ourselves to the env early, then
|
||||
// update later, so that structs can refer to themselves.
|
||||
udsR := NewRecordDefn()
|
||||
udsR.SetName(structName)
|
||||
rtR := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return udsR, nil
|
||||
})
|
||||
rtR.UserStructDefn = udsR
|
||||
rtR.DisplayAs = structName
|
||||
GoStructRegistry.RegisterUserdef(rtR, false, structName)
|
||||
|
||||
// overwrite any existing definition, deliberately ignore any error,
|
||||
// as there may not be a prior definition present at all.
|
||||
env.linearstack.DeleteSymbolFromTopOfStackScope(symN)
|
||||
|
||||
err := env.LexicalBindSymbol(symN, rtR)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("struct builder could not bind symbol '%s': '%v'",
|
||||
structName, err)
|
||||
}
|
||||
// end enable recursion
|
||||
}
|
||||
var xar []Sexp
|
||||
var flat []*SexpField
|
||||
if n > 2 {
|
||||
return SexpNull, fmt.Errorf("bad struct declaration: more than two arguments." +
|
||||
"prototype is (struct name [(field ...)*] )")
|
||||
}
|
||||
if n == 2 {
|
||||
Q("in case n == 2")
|
||||
switch ar := args[1].(type) {
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad struct declaration '%v': second argument "+
|
||||
"must be a slice of fields."+
|
||||
" prototype is (struct name [(field ...)*] )", structName)
|
||||
case *SexpArray:
|
||||
arr := ar.Val
|
||||
if len(arr) == 0 {
|
||||
// allow this
|
||||
} else {
|
||||
// dup to avoid messing with the stack on eval:
|
||||
//dup := env.Duplicate()
|
||||
for i, ele := range arr {
|
||||
Q("about to eval i=%v", i)
|
||||
//ev, err := dup.EvalExpressions([]Sexp{ele})
|
||||
ev, err := EvalFunction(env, "evalStructBuilder", []Sexp{ele})
|
||||
Q("done with eval i=%v. ev=%v", i, ev.SexpString(nil))
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
|
||||
"field at array entry %v; error was '%v'", structName, i, err)
|
||||
}
|
||||
Q("checking for isHash at i=%v", i)
|
||||
asHash, isHash := ev.(*SexpField)
|
||||
if !isHash {
|
||||
Q("was not hash, instead was %T", ev)
|
||||
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
|
||||
"field array at entry %v; a (field ...) is required. Instead saw '%T'/with value = '%v'",
|
||||
structName, i, ev, ev.SexpString(nil))
|
||||
}
|
||||
Q("good eval i=%v, ev=%#v / %v", i, ev, ev.SexpString(nil))
|
||||
ko := asHash.KeyOrder
|
||||
if len(ko) == 0 {
|
||||
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
|
||||
"field array at entry %v; field had no name",
|
||||
structName, i)
|
||||
}
|
||||
Q("ko = '%#v'", ko)
|
||||
first := ko[0]
|
||||
Q("first = '%#v'", first)
|
||||
xar = append(xar, first)
|
||||
xar = append(xar, ev)
|
||||
flat = append(flat, ev.(*SexpField))
|
||||
}
|
||||
Q("no err from EvalExpressions, got xar = '%#v'", xar)
|
||||
}
|
||||
}
|
||||
} // end n == 2
|
||||
|
||||
uds := NewRecordDefn()
|
||||
uds.SetName(structName)
|
||||
uds.SetFields(flat)
|
||||
Q("good: made typeDefnHash: '%s'", uds.SexpString(nil))
|
||||
rt := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return uds, nil
|
||||
})
|
||||
rt.UserStructDefn = uds
|
||||
rt.DisplayAs = structName
|
||||
GoStructRegistry.RegisterUserdef(rt, false, structName)
|
||||
Q("good: registered new userdefined struct '%s'", structName)
|
||||
|
||||
// replace our recursive-reference-enabling symbol with the real one.
|
||||
err := env.linearstack.DeleteSymbolFromTopOfStackScope(symN)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("internal error: should have already had symbol '%s' "+
|
||||
"bound, but DeleteSymbolFromTopOfStackScope returned error: '%v'",
|
||||
symN.name, err)
|
||||
}
|
||||
err = env.LexicalBindSymbol(symN, rt)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("late: struct builder could not bind symbol '%s': '%v'",
|
||||
structName, err)
|
||||
}
|
||||
Q("good: bound symbol '%s' to RegisteredType '%s'", symN.SexpString(nil), rt.SexpString(nil))
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
func InterfaceBuilder(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
nargs := len(args)
|
||||
switch {
|
||||
case nargs < 1:
|
||||
return SexpNull, fmt.Errorf("interface name is missing. use: " +
|
||||
"(interface interface-name [...])\n")
|
||||
case nargs == 1:
|
||||
return SexpNull, fmt.Errorf("interface array of methods missing. use: " +
|
||||
"(interface interface-name [...])\n")
|
||||
case nargs > 2:
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
// P("in interface builder, past arg check")
|
||||
var iname string
|
||||
var symN *SexpSymbol
|
||||
switch sy := args[0].(type) {
|
||||
case *SexpSymbol:
|
||||
symN = sy
|
||||
iname = sy.name
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("interface name must be a symbol; we got %T", args[0])
|
||||
}
|
||||
|
||||
// sanity check the name
|
||||
builtin, builtTyp := env.IsBuiltinSym(symN)
|
||||
if builtin {
|
||||
return SexpNull,
|
||||
fmt.Errorf("already have %s '%s', refusing to overwrite with interface",
|
||||
builtTyp, symN.name)
|
||||
}
|
||||
|
||||
if env.HasMacro(symN) {
|
||||
return SexpNull, fmt.Errorf("Already have macro named '%s': refusing"+
|
||||
" to define interface of same name.", symN.name)
|
||||
}
|
||||
// end sanity check the name
|
||||
|
||||
var arrMeth *SexpArray
|
||||
switch ar := args[1].(type) {
|
||||
case *SexpArray:
|
||||
arrMeth = ar
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("interface method vector expected after name; we got %T", args[1])
|
||||
}
|
||||
|
||||
// P("in interface builder, args = ")
|
||||
// for i := range args {
|
||||
// P("args[%v] = '%s'", i, args[i].SexpString(nil))
|
||||
// }
|
||||
|
||||
methods := make([]*SexpFunction, 0)
|
||||
methodSlice := arrMeth.Val
|
||||
if len(methodSlice) > 0 {
|
||||
//dup := env.Duplicate()
|
||||
for i := range methodSlice {
|
||||
//ev, err := dup.EvalExpressions([]Sexp{methodSlice[i]})
|
||||
ev, err := EvalFunction(env, "evalInterface", []Sexp{methodSlice[i]})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error parsing the %v-th method in interface definition: '%v'", i, err)
|
||||
}
|
||||
me, gotFunc := ev.(*SexpFunction)
|
||||
if !gotFunc {
|
||||
return SexpNull,
|
||||
fmt.Errorf("error parsing the %v-th method in interface: was not function but rather %T",
|
||||
i, ev)
|
||||
}
|
||||
methods = append(methods, me)
|
||||
}
|
||||
}
|
||||
|
||||
decl := &SexpInterfaceDecl{
|
||||
name: iname,
|
||||
methods: methods,
|
||||
}
|
||||
return decl, nil
|
||||
}
|
||||
|
||||
func SliceOfFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
if len(args) != 1 {
|
||||
return SexpNull, fmt.Errorf("argument x to (%s x) is missing. use: "+
|
||||
"(%s a-regtype)\n", name, name)
|
||||
}
|
||||
|
||||
Q("in SliceOfFunction")
|
||||
|
||||
var rt *RegisteredType
|
||||
switch arg := args[0].(type) {
|
||||
case *RegisteredType:
|
||||
rt = arg
|
||||
case *SexpHash:
|
||||
rt = arg.GoStructFactory
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("argument tx in (%s x) was not regtype, "+
|
||||
"instead type %T displaying as '%v' ",
|
||||
name, arg, arg.SexpString(nil))
|
||||
}
|
||||
|
||||
Q("sliceOf arg = '%s' with type %T", args[0].SexpString(nil), args[0])
|
||||
|
||||
sliceRt := GoStructRegistry.GetOrCreateSliceType(rt)
|
||||
Q("in SliceOfFunction: returning sliceRt = '%#v'", sliceRt)
|
||||
return sliceRt, nil
|
||||
}
|
||||
|
||||
func PointerToFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
if len(args) != 1 {
|
||||
return SexpNull, fmt.Errorf("argument to pointer-to is missing. use: "+
|
||||
"(%s a-regtype)\n", name)
|
||||
}
|
||||
|
||||
//P("in PointerToFunction(): args[0] = '%#v'", args[0])
|
||||
|
||||
var rt *RegisteredType
|
||||
switch arg := args[0].(type) {
|
||||
case *RegisteredType:
|
||||
rt = arg
|
||||
case *SexpHash:
|
||||
rt = arg.GoStructFactory
|
||||
case *SexpPointer:
|
||||
// dereference operation, rather than type declaration
|
||||
//P("dereference operation on *SexpPointer detected, returning target")
|
||||
if arg == nil || arg.Target == nil {
|
||||
return SexpNull, fmt.Errorf("illegal to dereference nil pointer")
|
||||
}
|
||||
return arg.Target, nil
|
||||
case *SexpReflect:
|
||||
Q("dereference operation on SexpReflect detected")
|
||||
// TODO what goes here?
|
||||
return SexpNull, fmt.Errorf("illegal to dereference nil pointer")
|
||||
case *SexpSymbol:
|
||||
if arg.isDot {
|
||||
// (* h.a) dereferencing a dot symbol
|
||||
resolved, err := dotGetSetHelper(env, arg.name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resolved, nil
|
||||
} else {
|
||||
panic("TODO: what goes here, for (* sym) where sym is a regular symbol")
|
||||
}
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("argument x in (%s x) was not regtype or SexpPointer, "+
|
||||
"instead type %T displaying as '%v' ",
|
||||
name, arg, arg.SexpString(nil))
|
||||
}
|
||||
|
||||
Q("pointer-to arg = '%s' with type %T", args[0].SexpString(nil), args[0])
|
||||
|
||||
ptrRt := GoStructRegistry.GetOrCreatePointerType(rt)
|
||||
return ptrRt, nil
|
||||
}
|
||||
|
||||
func StructConstructorFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("in struct ctor, name = '%s', args = %#v", name, args)
|
||||
return MakeHash(args, name, env)
|
||||
}
|
||||
|
||||
func BaseTypeConstructorFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("in base type ctor, args = '%#v'", args)
|
||||
if len(args) < 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
Q("in base ctor, name = '%s', args = %#v", name, args)
|
||||
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
func baseConstruct(env *Zlisp, f *RegisteredType, nargs int) (Sexp, error) {
|
||||
if nargs > 1 {
|
||||
return SexpNull, fmt.Errorf("%d is too many arguments for a base type constructor", nargs)
|
||||
}
|
||||
|
||||
v, err := f.Factory(env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
Q("see call to baseConstruct, v = %v/type=%T", v, v)
|
||||
if nargs == 0 {
|
||||
switch v.(type) {
|
||||
case *int, *uint8, *uint16, *uint32, *uint64, *int8, *int16, *int32, *int64:
|
||||
return &SexpInt{}, nil
|
||||
case *float32, *float64:
|
||||
return &SexpFloat{}, nil
|
||||
case *string:
|
||||
return &SexpStr{S: ""}, nil
|
||||
case *bool:
|
||||
return &SexpBool{Val: false}, nil
|
||||
case *time.Time:
|
||||
return &SexpTime{}, nil
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("unhandled no-arg case in baseConstruct, v has type=%T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// get our one argument
|
||||
args, err := env.datastack.PopExpressions(1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
arg := args[0]
|
||||
|
||||
switch v.(type) {
|
||||
case *int, *uint8, *uint16, *uint32, *uint64, *int8, *int16, *int32, *int64:
|
||||
myint, ok := arg.(*SexpInt)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("cannot convert %T to int", arg)
|
||||
}
|
||||
return myint, nil
|
||||
case *float32, *float64:
|
||||
myfloat, ok := arg.(*SexpFloat)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("cannot convert %T to float", arg)
|
||||
}
|
||||
return myfloat, nil
|
||||
case *string:
|
||||
mystring, ok := arg.(*SexpStr)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("cannot convert %T to string", arg)
|
||||
}
|
||||
return mystring, nil
|
||||
case *bool:
|
||||
mybool, ok := arg.(*SexpBool)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("cannot convert %T to bool", arg)
|
||||
}
|
||||
return mybool, nil
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("unhandled case in baseConstruct, arg = %#v/type=%T", arg, arg)
|
||||
}
|
||||
//return SexpNull, fmt.Errorf("unhandled no-arg case in baseConstruct, v has type=%T", v)
|
||||
}
|
||||
|
||||
// generate fixed size array
|
||||
func ArrayOfFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
if len(args) != 2 {
|
||||
return SexpNull, fmt.Errorf("insufficient arguments to ([size] regtype) array constructor. use: " +
|
||||
"([size...] a-regtype)\n")
|
||||
}
|
||||
sz := 0
|
||||
Q("args = %#v in ArrayOfFunction", args)
|
||||
switch ar := args[1].(type) {
|
||||
case *SexpArray:
|
||||
if len(ar.Val) == 0 {
|
||||
return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)")
|
||||
}
|
||||
asInt, isInt := ar.Val[0].(*SexpInt)
|
||||
if !isInt {
|
||||
return SexpNull, fmt.Errorf("size must be an int (not %T) in array constructor; e.g. ([size ...] regtype)", ar.Val[0])
|
||||
}
|
||||
sz = int(asInt.Val)
|
||||
// TODO: implement multiple dimensional arrays (matrixes etc).
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)")
|
||||
}
|
||||
|
||||
var rt *RegisteredType
|
||||
switch arg := args[0].(type) {
|
||||
case *RegisteredType:
|
||||
rt = arg
|
||||
case *SexpHash:
|
||||
rt = arg.GoStructFactory
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("argument tx in (%s x) was not regtype, "+
|
||||
"instead type %T displaying as '%v' ",
|
||||
name, arg, arg.SexpString(nil))
|
||||
}
|
||||
|
||||
//Q("arrayOf arg = '%s' with type %T", args[0].SexpString(nil), args[0])
|
||||
|
||||
derivedType := reflect.ArrayOf(sz, rt.TypeCache)
|
||||
arrayRt := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return reflect.New(derivedType), nil
|
||||
})
|
||||
arrayRt.DisplayAs = fmt.Sprintf("(%s %s)", name, rt.DisplayAs)
|
||||
arrayName := "arrayOf" + rt.RegisteredName
|
||||
GoStructRegistry.RegisterUserdef(arrayRt, false, arrayName)
|
||||
return arrayRt, nil
|
||||
}
|
||||
|
||||
func VarBuilder(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
n := len(args)
|
||||
if n != 2 {
|
||||
return SexpNull, fmt.Errorf("var name/type missing. use: " +
|
||||
"(var name regtype)\n")
|
||||
}
|
||||
|
||||
Q("in var builder, name = '%s', args = ", name)
|
||||
for i := range args {
|
||||
Q("args[%v] = '%s' of type %T", i, args[i].SexpString(nil), args[i])
|
||||
}
|
||||
var symN *SexpSymbol
|
||||
switch b := args[0].(type) {
|
||||
case *SexpSymbol:
|
||||
symN = b
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(b)
|
||||
if isQuo {
|
||||
symN = sy.(*SexpSymbol)
|
||||
} else {
|
||||
return SexpNull, fmt.Errorf("bad var name: symbol required")
|
||||
}
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad var name: symbol required")
|
||||
}
|
||||
Q("good: have var name '%v'", symN)
|
||||
|
||||
//dup := env.Duplicate()
|
||||
Q("about to eval args[1]=%v", args[1])
|
||||
//ev, err := dup.EvalExpressions(args[1:2])
|
||||
ev, err := EvalFunction(env, "evalVar", args[1:2])
|
||||
Q("done with eval, ev=%v / type %T", ev.SexpString(nil), ev)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("bad var declaration, problem with type '%v': %v", args[1].SexpString(nil), err)
|
||||
}
|
||||
|
||||
var rt *RegisteredType
|
||||
switch myrt := ev.(type) {
|
||||
case *RegisteredType:
|
||||
rt = myrt
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad var declaration, type '%v' is unknown", rt.SexpString(nil))
|
||||
}
|
||||
|
||||
val, err := rt.Factory(env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("var declaration error: could not make type '%s': %v",
|
||||
rt.SexpString(nil), err)
|
||||
}
|
||||
var valSexp Sexp
|
||||
Q("val is of type %T", val)
|
||||
switch v := val.(type) {
|
||||
case Sexp:
|
||||
valSexp = v
|
||||
case reflect.Value:
|
||||
Q("v is of type %T", v.Interface())
|
||||
switch rd := v.Interface().(type) {
|
||||
case ***RecordDefn:
|
||||
Q("we have RecordDefn rd = %#v", *rd)
|
||||
}
|
||||
valSexp = &SexpReflect{Val: reflect.ValueOf(v)}
|
||||
default:
|
||||
valSexp = &SexpReflect{Val: reflect.ValueOf(v)}
|
||||
}
|
||||
|
||||
Q("var decl: valSexp is '%v'", valSexp.SexpString(nil))
|
||||
err = env.LexicalBindSymbol(symN, valSexp)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("var declaration error: could not bind symbol '%s': %v",
|
||||
symN.name, err)
|
||||
}
|
||||
Q("good: var decl bound symbol '%s' to '%s' of type '%s'", symN.SexpString(nil), valSexp.SexpString(nil), rt.SexpString(nil))
|
||||
|
||||
env.datastack.PushExpr(valSexp)
|
||||
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
func ExpectErrorBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
narg := len(args)
|
||||
if narg != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
dup := env.Duplicate()
|
||||
es, err := dup.EvalExpressions(args[0:1])
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error evaluating the error string to expect: %s", err)
|
||||
}
|
||||
|
||||
var expectedError *SexpStr
|
||||
switch e := es.(type) {
|
||||
case *SexpStr:
|
||||
expectedError = e
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("first arg to expectError must be the string of the error to expect")
|
||||
}
|
||||
Q("expectedError = %v", expectedError)
|
||||
ev, err := dup.EvalExpressions(args[1:2])
|
||||
Q("done with eval, ev=%v / type %T. err = %v", ev.SexpString(nil), ev, err)
|
||||
if err != nil {
|
||||
if err.Error() == expectedError.S {
|
||||
return SexpNull, nil
|
||||
}
|
||||
return SexpNull, fmt.Errorf("expectError expected '%s' but saw '%s'", expectedError.S, err)
|
||||
}
|
||||
|
||||
if expectedError.S == "" {
|
||||
return SexpNull, nil
|
||||
}
|
||||
return SexpNull, fmt.Errorf("expectError expected '%s' but got no error", expectedError.S)
|
||||
}
|
||||
|
||||
func ColonAccessBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) < 1 || len(args) > 3 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
////Q("ColonAccessBuilder, args = %#v", args)
|
||||
name = "hget"
|
||||
|
||||
//dup := env.Duplicate()
|
||||
//collec, err := dup.EvalExpressions(args[1:2])
|
||||
collec, err := EvalFunction(env, "evalColonAccess", args[1:2])
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
swapped := args
|
||||
swapped[1] = swapped[0]
|
||||
swapped[0] = collec
|
||||
|
||||
if len(args) == 3 {
|
||||
// have default, needs eval too
|
||||
//defaul, err := dup.EvalExpressions(args[2:3])
|
||||
defaul, err := EvalFunction(env, "evalColonDefault", args[2:3])
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
swapped[2] = defaul
|
||||
}
|
||||
|
||||
switch sx := collec.(type) {
|
||||
case *SexpHash:
|
||||
return HashAccessFunction(env, name, swapped)
|
||||
case *SexpArray:
|
||||
return ArrayAccessFunction(env, name, swapped)
|
||||
case *SexpArraySelector:
|
||||
Q("*SexpSelector seen in : operator form.")
|
||||
return sx.RHS(env)
|
||||
}
|
||||
return SexpNull, fmt.Errorf("second argument to ':' function must be hash or array")
|
||||
}
|
||||
|
||||
// CommaBuilder turns expressions on the LHS and RHS like {a,b,c = 1,2,3}
|
||||
// into arrays (set [a b c] [1 2 3])
|
||||
func CommaBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
|
||||
n := len(args)
|
||||
if n < 1 {
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
res := make([]Sexp, 0)
|
||||
for i := range args {
|
||||
commaHelper(args[i], &res)
|
||||
}
|
||||
return &SexpArray{Val: res}, nil
|
||||
}
|
||||
|
||||
func commaHelper(a Sexp, res *[]Sexp) {
|
||||
//Q("top of commaHelper with a = '%s'", a.SexpString(nil))
|
||||
switch x := a.(type) {
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(x)
|
||||
if isQuo {
|
||||
symN := sy.(*SexpSymbol)
|
||||
//Q("have quoted symbol symN=%v", symN.SexpString(nil))
|
||||
*res = append(*res, symN)
|
||||
return
|
||||
}
|
||||
|
||||
ar, err := ListToArray(x)
|
||||
if err != nil || len(ar) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
switch sym := ar[0].(type) {
|
||||
case *SexpSymbol:
|
||||
if sym.name == "comma" {
|
||||
//Q("have recursive comma")
|
||||
over := ar[1:]
|
||||
for i := range over {
|
||||
commaHelper(over[i], res)
|
||||
}
|
||||
} else {
|
||||
//Q("have symbol sym = '%v'", sym.SexpString(nil))
|
||||
*res = append(*res, a)
|
||||
}
|
||||
default:
|
||||
*res = append(*res, a)
|
||||
}
|
||||
default:
|
||||
*res = append(*res, a)
|
||||
}
|
||||
}
|
||||
209
vendor/github.com/glycerine/zygomys/zygo/callgo.go
generated
vendored
Normal file
209
vendor/github.com/glycerine/zygomys/zygo/callgo.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// call Go methods
|
||||
|
||||
// Using reflection, invoke a Go method on a struct or interface.
|
||||
// args[0] is a hash with an an attached GoStruct
|
||||
// args[1] is a hash representing a method call on that struct.
|
||||
// The returned Sexp is a hash that represents the result of that call.
|
||||
func CallGoMethodFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("_method user func running!\n")
|
||||
|
||||
// protect against bad calls/bad reflection
|
||||
var wasPanic bool
|
||||
var recovered interface{}
|
||||
tr := make([]byte, 16384)
|
||||
trace := &tr
|
||||
sx, err := func() (Sexp, error) {
|
||||
defer func() {
|
||||
recovered = recover()
|
||||
if recovered != nil {
|
||||
wasPanic = true
|
||||
nbyte := runtime.Stack(*trace, false)
|
||||
*trace = (*trace)[:nbyte]
|
||||
}
|
||||
}()
|
||||
|
||||
narg := len(args)
|
||||
if narg < 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
obj, isHash := args[0].(*SexpHash)
|
||||
if !isHash {
|
||||
return SexpNull, fmt.Errorf("_method error: first argument must be a hash or defmap (a record) with an attached GoObject")
|
||||
}
|
||||
|
||||
var methodname string
|
||||
switch m := args[1].(type) {
|
||||
case *SexpSymbol:
|
||||
methodname = m.name
|
||||
case *SexpStr:
|
||||
methodname = m.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("_method error: second argument must be a method name in symbol or string form (got %T)", args[1])
|
||||
}
|
||||
|
||||
// get the method list, verify the method exists and get its type
|
||||
if obj.NumMethod == -1 {
|
||||
err := obj.SetMethodList(env)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("could not get method list for object: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var method reflect.Method
|
||||
found := false
|
||||
for _, me := range obj.GoMethods {
|
||||
if me.Name == methodname {
|
||||
method = me
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return SexpNull, fmt.Errorf("no such method '%s' on %s. choices are: %s",
|
||||
methodname, obj.TypeName,
|
||||
(obj.GoMethSx).SexpString(nil))
|
||||
}
|
||||
// INVAR: var method holds our call target
|
||||
|
||||
// try always expecting this to be already done... test crashes
|
||||
//P("in CallGoMethod '%s' obj.GoShadowStructVa = '%#v'", methodname, obj.GoShadowStructVa)
|
||||
if obj.GoShadowStructVa.Kind() == reflect.Invalid {
|
||||
// ready the struct... but only because there isn't already a shadow struct there!!
|
||||
if !obj.ShadowSet {
|
||||
_, err := ToGoFunction(env, "togo", []Sexp{obj})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error converting object to Go struct: '%s'", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
inputVa := []reflect.Value{(obj.GoShadowStructVa)}
|
||||
|
||||
// prep args.
|
||||
needed := method.Type.NumIn() - 1 // one for the receiver
|
||||
avail := narg - 2
|
||||
if needed != avail {
|
||||
// TODO: support varargs eventually
|
||||
return SexpNull, fmt.Errorf("method %s needs %d arguments, but we have %d", method.Name, needed, avail)
|
||||
}
|
||||
|
||||
var va reflect.Value
|
||||
for i := 2; i < narg; i++ {
|
||||
typ := method.Type.In(i - 1)
|
||||
pdepth := PointerDepth(typ)
|
||||
// we only handle 0 and 1 for now
|
||||
Q("pdepth = %v\n", pdepth)
|
||||
switch pdepth {
|
||||
case 0:
|
||||
va = reflect.New(typ)
|
||||
case 1:
|
||||
// handle the common single pointer to struct case
|
||||
va = reflect.New(typ.Elem())
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("error converting %d-th argument to "+
|
||||
"Go: we don't handle double pointers", i-2)
|
||||
}
|
||||
Q("converting to go '%#v' into -> %#v\n", args[i], va.Interface())
|
||||
iface, err := SexpToGoStructs(args[i], va.Interface(), env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error converting %d-th "+
|
||||
"argument to Go: '%s'", i-2, err)
|
||||
}
|
||||
switch pdepth {
|
||||
case 0:
|
||||
inputVa = append(inputVa, reflect.ValueOf(iface).Elem())
|
||||
case 1:
|
||||
inputVa = append(inputVa, reflect.ValueOf(iface))
|
||||
}
|
||||
Q("\n allocated new %T/val=%#v /i=%#v\n", va, va, va.Interface())
|
||||
}
|
||||
|
||||
//P("_method: about to .Call by reflection!\n")
|
||||
|
||||
out := method.Func.Call(inputVa)
|
||||
|
||||
var iout []interface{}
|
||||
for _, o := range out {
|
||||
iout = append(iout, o.Interface())
|
||||
}
|
||||
Q("done with _method call, iout = %#v\n", iout)
|
||||
Q("done with _method call, iout[0] = %#v\n", iout[0])
|
||||
|
||||
nout := len(out)
|
||||
r := make([]Sexp, 0)
|
||||
for i := 0; i < nout; i++ {
|
||||
f := out[i].Interface()
|
||||
switch e := f.(type) {
|
||||
case nil:
|
||||
r = append(r, SexpNull)
|
||||
case int64:
|
||||
r = append(r, &SexpInt{Val: e})
|
||||
case int:
|
||||
r = append(r, &SexpInt{Val: int64(e)})
|
||||
case error:
|
||||
r = append(r, &SexpError{e})
|
||||
case string:
|
||||
r = append(r, &SexpStr{S: e})
|
||||
case float64:
|
||||
r = append(r, &SexpFloat{Val: e})
|
||||
case []byte:
|
||||
r = append(r, &SexpRaw{Val: e})
|
||||
case rune:
|
||||
r = append(r, &SexpChar{Val: e})
|
||||
default:
|
||||
// go through the type registry
|
||||
found := false
|
||||
for hashName, factory := range GoStructRegistry.Registry {
|
||||
st, err := factory.Factory(env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("MakeHash '%s' problem on Factory call: %s",
|
||||
hashName, err)
|
||||
}
|
||||
Q("got st from Factory, checking if types match")
|
||||
if reflect.ValueOf(st).Type() == out[i].Type() {
|
||||
Q("types match")
|
||||
retHash, err := MakeHash([]Sexp{}, factory.RegisteredName, env)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("MakeHash '%s' problem: %s",
|
||||
hashName, err)
|
||||
}
|
||||
|
||||
Q("filling from shadow")
|
||||
err = retHash.FillHashFromShadow(env, f)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
r = append(r, retHash)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r = append(r, &SexpReflect{Val: out[i]})
|
||||
}
|
||||
}
|
||||
}
|
||||
return env.NewSexpArray(r), nil
|
||||
}()
|
||||
if wasPanic {
|
||||
return SexpNull, fmt.Errorf("\n recovered from panic "+
|
||||
"during CallGo. panic on = '%v'\n"+
|
||||
"stack trace:\n%s\n", recovered, string(*trace))
|
||||
}
|
||||
return sx, err
|
||||
}
|
||||
|
||||
// detect if inteface is holding anything
|
||||
func NilOrHoldsNil(iface interface{}) bool {
|
||||
if iface == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.ValueOf(iface).IsNil()
|
||||
}
|
||||
54
vendor/github.com/glycerine/zygomys/zygo/cfg.go
generated
vendored
Normal file
54
vendor/github.com/glycerine/zygomys/zygo/cfg.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
// configure a glisp repl
|
||||
type ZlispConfig struct {
|
||||
CpuProfile string
|
||||
MemProfile string
|
||||
ExitOnFailure bool
|
||||
CountFuncCalls bool
|
||||
Flags *flag.FlagSet
|
||||
ExtensionsVersion string
|
||||
Command string
|
||||
Sandboxed bool
|
||||
Quiet bool
|
||||
Trace bool
|
||||
LoadDemoStructs bool
|
||||
AfterScriptDontExit bool
|
||||
|
||||
// liner bombs under emacs, avoid it with this flag.
|
||||
NoLiner bool
|
||||
Prompt string // default "zygo> "
|
||||
|
||||
}
|
||||
|
||||
func NewZlispConfig(cmdname string) *ZlispConfig {
|
||||
return &ZlispConfig{
|
||||
Flags: flag.NewFlagSet(cmdname, flag.ExitOnError),
|
||||
}
|
||||
}
|
||||
|
||||
// call DefineFlags before myflags.Parse()
|
||||
func (c *ZlispConfig) DefineFlags() {
|
||||
c.Flags.StringVar(&c.CpuProfile, "cpuprofile", "", "write cpu profile to file")
|
||||
c.Flags.StringVar(&c.MemProfile, "memprofile", "", "write mem profile to file")
|
||||
c.Flags.BoolVar(&c.ExitOnFailure, "exitonfail", false, "exit on failure instead of starting repl")
|
||||
c.Flags.BoolVar(&c.CountFuncCalls, "countcalls", false, "count how many times each function is run")
|
||||
c.Flags.StringVar(&c.Command, "c", "", "expressions to evaluate")
|
||||
c.Flags.BoolVar(&c.AfterScriptDontExit, "i", false, "after running the command line script, remain interactive rather than exiting")
|
||||
c.Flags.BoolVar(&c.Sandboxed, "sandbox", false, "run sandboxed; disallow system/external interaction functions")
|
||||
c.Flags.BoolVar(&c.Quiet, "quiet", false, "start repl without printing the version/mode/help banner")
|
||||
c.Flags.BoolVar(&c.Trace, "trace", false, "trace execution (warning: very verbose and slow)")
|
||||
c.Flags.BoolVar(&c.LoadDemoStructs, "demo", false, "load the demo structs: Event, Snoopy, Hornet, Weather and friends.")
|
||||
}
|
||||
|
||||
// call c.ValidateConfig() after myflags.Parse()
|
||||
func (c *ZlispConfig) ValidateConfig() error {
|
||||
if c.Prompt == "" {
|
||||
c.Prompt = "zygo> "
|
||||
}
|
||||
return nil
|
||||
}
|
||||
70
vendor/github.com/glycerine/zygomys/zygo/channels.go
generated
vendored
Normal file
70
vendor/github.com/glycerine/zygomys/zygo/channels.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type SexpChannel struct {
|
||||
Val chan Sexp
|
||||
Typ *RegisteredType
|
||||
}
|
||||
|
||||
func (ch *SexpChannel) SexpString(ps *PrintState) string {
|
||||
return "[chan]"
|
||||
}
|
||||
|
||||
func (ch *SexpChannel) Type() *RegisteredType {
|
||||
return ch.Typ // TODO what should this be?
|
||||
}
|
||||
|
||||
func MakeChanFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
if len(args) > 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
size := 0
|
||||
if len(args) == 1 {
|
||||
switch t := args[0].(type) {
|
||||
case *SexpInt:
|
||||
size = int(t.Val)
|
||||
default:
|
||||
return SexpNull, errors.New(
|
||||
fmt.Sprintf("argument to %s must be int", name))
|
||||
}
|
||||
}
|
||||
|
||||
return &SexpChannel{Val: make(chan Sexp, size)}, nil
|
||||
}
|
||||
|
||||
func ChanTxFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
if len(args) < 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var channel chan Sexp
|
||||
switch t := args[0].(type) {
|
||||
case *SexpChannel:
|
||||
channel = chan Sexp(t.Val)
|
||||
default:
|
||||
return SexpNull, errors.New(
|
||||
fmt.Sprintf("argument 0 of %s must be channel", name))
|
||||
}
|
||||
|
||||
if name == "send" {
|
||||
if len(args) != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
channel <- args[1]
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
return <-channel, nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportChannels() {
|
||||
env.AddFunction("makeChan", MakeChanFunction)
|
||||
env.AddFunction("send", ChanTxFunction)
|
||||
env.AddFunction("<!", ChanTxFunction)
|
||||
}
|
||||
93
vendor/github.com/glycerine/zygomys/zygo/check.go
generated
vendored
Normal file
93
vendor/github.com/glycerine/zygomys/zygo/check.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// FunctionCallNameTypeCheck type checks a function call.
|
||||
func (env *Zlisp) FunctionCallNameTypeCheck(f *SexpFunction, nargs *int) error {
|
||||
if f.inputTypes != nil {
|
||||
Q("FunctionCallNameTypeCheck sees inputTypes: '%v'", f.inputTypes.SexpString(nil))
|
||||
} else {
|
||||
return nil // no type checking requested
|
||||
}
|
||||
if f.varargs {
|
||||
return nil // name/type checking for vargarg not currently implemented.
|
||||
}
|
||||
|
||||
// our call arguments prepared -- will be pushed to the datastack
|
||||
finalArgs := make([]Sexp, f.inputTypes.NumKeys)
|
||||
|
||||
// pop everything off the stack, will push finalArgs later
|
||||
exprs, err := env.datastack.PopExpressions(*nargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// map the named submitted args, for fast lookup by name
|
||||
submittedByName := make(map[string]Sexp)
|
||||
|
||||
// prep submittedByName
|
||||
for i := 0; i < *nargs; i++ {
|
||||
switch sym := exprs[i].(type) {
|
||||
case *SexpSymbol:
|
||||
if sym.colonTail {
|
||||
Q("in env.CallFunction, have symbol.colonTail: exprs[%v]='%#v'", i, sym)
|
||||
typ, err := f.inputTypes.HashGet(env, sym)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s takes no argument '%s'", f.name, sym.name)
|
||||
}
|
||||
if i == (*nargs)-1 {
|
||||
return fmt.Errorf("named parameter '%s' not followed by value", sym.name)
|
||||
}
|
||||
val := exprs[i+1]
|
||||
i++
|
||||
_, already := submittedByName[sym.name]
|
||||
if already {
|
||||
return fmt.Errorf("duplicate named parameter '%s'", sym.name)
|
||||
}
|
||||
|
||||
submittedByName[sym.name] = val
|
||||
valtyp := val.Type()
|
||||
if typ != nil && typ != valtyp {
|
||||
return fmt.Errorf("type mismatch for parameter '%s': expected '%s', got '%s'",
|
||||
sym.name, typ.SexpString(nil), valtyp.SexpString(nil))
|
||||
}
|
||||
} else {
|
||||
Q("in env.CallFunction, exprs[%v]='%v'/type=%T", i, exprs[i].SexpString(nil), exprs[i])
|
||||
}
|
||||
default:
|
||||
Q("in env.CallFunction, exprs[%v]='%v'/type=%T", i, exprs[i].SexpString(nil), exprs[i])
|
||||
}
|
||||
}
|
||||
|
||||
// simplify name matching for now with this rule: all by name, or none.
|
||||
haveByName := len(submittedByName)
|
||||
if haveByName > 0 {
|
||||
if haveByName != f.inputTypes.NumKeys {
|
||||
return fmt.Errorf("named arguments count %v != expected %v", haveByName, f.inputTypes.NumKeys)
|
||||
}
|
||||
|
||||
// prep finalArgs in the order dictated
|
||||
for i, key := range f.inputTypes.KeyOrder {
|
||||
switch sy := key.(type) {
|
||||
case *SexpSymbol:
|
||||
// search for sy.name in our submittedByName args
|
||||
a, found := submittedByName[sy.name]
|
||||
if found {
|
||||
Q("%s call: matching %v-th argument named '%s': passing value '%s'",
|
||||
f.name, i, sy.name, a.SexpString(nil))
|
||||
finalArgs[i] = a
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported argument-name type %T", key)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// not using named parameters, restore the arguments to the stack as they were.
|
||||
finalArgs = exprs
|
||||
}
|
||||
*nargs = len(finalArgs)
|
||||
return env.datastack.PushExpressions(finalArgs)
|
||||
}
|
||||
45
vendor/github.com/glycerine/zygomys/zygo/closing.go
generated
vendored
Normal file
45
vendor/github.com/glycerine/zygomys/zygo/closing.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package zygo
|
||||
|
||||
// where we store our closure-supporing stack pointers
|
||||
type Closing struct {
|
||||
Stack *Stack
|
||||
Name string
|
||||
env *Zlisp
|
||||
}
|
||||
|
||||
func NewClosing(name string, env *Zlisp) *Closing {
|
||||
stk := env.linearstack.Clone()
|
||||
// be super strict: only store up to our
|
||||
// enclosing function definition, because after
|
||||
// that, the definition time of that function
|
||||
// should be what we use.
|
||||
|
||||
return &Closing{
|
||||
Stack: stk,
|
||||
Name: name,
|
||||
env: env}
|
||||
}
|
||||
|
||||
func NewEmptyClosing(name string, env *Zlisp) *Closing {
|
||||
return &Closing{
|
||||
Stack: env.NewStack(0),
|
||||
Name: name,
|
||||
env: env}
|
||||
}
|
||||
|
||||
func (c *Closing) IsStackElem() {}
|
||||
|
||||
func (c *Closing) LookupSymbolUntilFunction(sym *SexpSymbol, setVal *Sexp, maximumFuncToSearch int, checkCaptures bool) (Sexp, error, *Scope) {
|
||||
return c.Stack.LookupSymbolUntilFunction(sym, setVal, maximumFuncToSearch, checkCaptures)
|
||||
}
|
||||
func (c *Closing) LookupSymbol(sym *SexpSymbol, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
return c.Stack.LookupSymbol(sym, setVal)
|
||||
}
|
||||
|
||||
func (c *Closing) Show(env *Zlisp, ps *PrintState, label string) (string, error) {
|
||||
return c.Stack.Show(env, ps, label)
|
||||
}
|
||||
|
||||
func (c *Closing) TopScope() *Scope {
|
||||
return c.Stack.GetTop().(*Scope)
|
||||
}
|
||||
105
vendor/github.com/glycerine/zygomys/zygo/comment.go
generated
vendored
Normal file
105
vendor/github.com/glycerine/zygomys/zygo/comment.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package zygo
|
||||
|
||||
type SexpComment struct {
|
||||
Comment string
|
||||
Block bool
|
||||
}
|
||||
|
||||
func (p *SexpComment) SexpString(ps *PrintState) string {
|
||||
return p.Comment
|
||||
}
|
||||
|
||||
func (p *SexpComment) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["comment"]
|
||||
}
|
||||
|
||||
// Filters return true to keep, false to drop.
|
||||
type Filter func(x Sexp) bool
|
||||
|
||||
func RemoveCommentsFilter(x Sexp) bool {
|
||||
switch x.(type) {
|
||||
case *SexpComment:
|
||||
//P("RemoveCommentsFilter called on x= %T/val=%v. return false", x, x)
|
||||
return false
|
||||
default:
|
||||
//P("RemoveCommentsFilter called on x= %T/val=%v. return true", x, x)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// detect SexpEnd values and return false on them to filter them out.
|
||||
func RemoveEndsFilter(x Sexp) bool {
|
||||
switch n := x.(type) {
|
||||
case *SexpSentinel:
|
||||
if n.Val == SexpEnd.Val {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// detect SexpComma values and return false on them to filter them out.
|
||||
func RemoveCommasFilter(x Sexp) bool {
|
||||
switch x.(type) {
|
||||
case *SexpComma:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (env *Zlisp) FilterAny(x Sexp, f Filter) (filtered Sexp, keep bool) {
|
||||
switch ele := x.(type) {
|
||||
case *SexpArray:
|
||||
res := &SexpArray{Val: env.FilterArray(ele.Val, f), Typ: ele.Typ, IsFuncDeclTypeArray: ele.IsFuncDeclTypeArray, Env: env}
|
||||
return res, true
|
||||
case *SexpPair:
|
||||
return env.FilterList(ele, f), true
|
||||
case *SexpHash:
|
||||
return env.FilterHash(ele, f), true
|
||||
default:
|
||||
keep = f(x)
|
||||
if keep {
|
||||
return x, true
|
||||
}
|
||||
return SexpNull, false
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) FilterArray(x []Sexp, f Filter) []Sexp {
|
||||
//P("FilterArray: before: %d in size", len(x))
|
||||
//for i := range x {
|
||||
//P("x[i=%d] = %v", i, x[i].SexpString())
|
||||
//}
|
||||
res := []Sexp{}
|
||||
for i := range x {
|
||||
filtered, keep := env.FilterAny(x[i], f)
|
||||
if keep {
|
||||
res = append(res, filtered)
|
||||
}
|
||||
}
|
||||
//P("FilterArray: after: %d in size", len(res))
|
||||
//for i := range res {
|
||||
//P("x[i=%d] = %v", i, res[i].SexpString())
|
||||
//}
|
||||
return res
|
||||
}
|
||||
|
||||
func (env *Zlisp) FilterHash(h *SexpHash, f Filter) *SexpHash {
|
||||
// should not actually need this, since hashes
|
||||
// don't yet exist in parsed symbols. (they are
|
||||
// still lists).
|
||||
//P("in FilterHash")
|
||||
return h
|
||||
}
|
||||
|
||||
func (env *Zlisp) FilterList(h *SexpPair, f Filter) Sexp {
|
||||
//P("in FilterList")
|
||||
arr, err := ListToArray(h)
|
||||
res := []Sexp{}
|
||||
if err == NotAList {
|
||||
// don't filter pair lists
|
||||
return h
|
||||
}
|
||||
res = env.FilterArray(arr, f)
|
||||
return MakeList(res)
|
||||
}
|
||||
323
vendor/github.com/glycerine/zygomys/zygo/comparisons.go
generated
vendored
Normal file
323
vendor/github.com/glycerine/zygomys/zygo/comparisons.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func IsNaNFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var err error
|
||||
a := args[0]
|
||||
if sel, isSel := a.(Selector); isSel {
|
||||
a, err = sel.RHS(env)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
|
||||
switch at := a.(type) {
|
||||
case *SexpFloat:
|
||||
if math.IsNaN(at.Val) {
|
||||
return &SexpBool{Val: true}, nil
|
||||
}
|
||||
}
|
||||
return &SexpBool{Val: false}, nil
|
||||
}
|
||||
|
||||
func signumFloat(f float64) int {
|
||||
if f > 0 {
|
||||
return 1
|
||||
}
|
||||
if f < 0 {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func signumInt(i int64) int {
|
||||
if i > 0 {
|
||||
return 1
|
||||
}
|
||||
if i < 0 {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareFloat(f *SexpFloat, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpInt:
|
||||
if math.IsNaN(f.Val) {
|
||||
return 2, nil
|
||||
}
|
||||
return signumFloat(f.Val - float64(e.Val)), nil
|
||||
case *SexpFloat:
|
||||
nanCount := 0
|
||||
if math.IsNaN(f.Val) {
|
||||
nanCount++
|
||||
}
|
||||
if math.IsNaN(e.Val) {
|
||||
nanCount++
|
||||
}
|
||||
if nanCount > 0 {
|
||||
return 1 + nanCount, nil
|
||||
}
|
||||
return signumFloat(f.Val - e.Val), nil
|
||||
case *SexpChar:
|
||||
if math.IsNaN(f.Val) {
|
||||
return 2, nil
|
||||
}
|
||||
return signumFloat(f.Val - float64(e.Val)), nil
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 91: cannot compare %T to %T", f, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func compareInt(i *SexpInt, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpInt:
|
||||
return signumInt(i.Val - e.Val), nil
|
||||
case *SexpFloat:
|
||||
return signumFloat(float64(i.Val) - e.Val), nil
|
||||
case *SexpChar:
|
||||
return signumInt(i.Val - int64(e.Val)), nil
|
||||
case *SexpReflect:
|
||||
r := reflect.Value(e.Val)
|
||||
ifa := r.Interface()
|
||||
switch z := ifa.(type) {
|
||||
case *int64:
|
||||
return signumInt(i.Val - *z), nil
|
||||
}
|
||||
P("compareInt(): ifa = %v/%T", ifa, ifa)
|
||||
P("compareInt(): r.Elem() = %v/%T", r.Elem(), r.Elem())
|
||||
P("compareInt(): r.Elem().Interface() = %v/%T", r.Elem().Interface(), r.Elem().Interface())
|
||||
P("compareInt(): r.Elem().Type() = %v/%T", r.Elem().Type(), r.Elem().Type())
|
||||
P("compareInt(): r.Elem().Type().Name() = %v/%T", r.Elem().Type().Name(), r.Elem().Type().Name())
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 92: cannot compare %T to %T", i, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func compareChar(c *SexpChar, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpInt:
|
||||
return signumInt(int64(c.Val) - e.Val), nil
|
||||
case *SexpFloat:
|
||||
return signumFloat(float64(c.Val) - e.Val), nil
|
||||
case *SexpChar:
|
||||
return signumInt(int64(c.Val) - int64(e.Val)), nil
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 93: cannot compare %T to %T", c, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func compareString(s *SexpStr, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpStr:
|
||||
return bytes.Compare([]byte(s.S), []byte(e.S)), nil
|
||||
case *SexpReflect:
|
||||
r := reflect.Value(e.Val)
|
||||
ifa := r.Interface()
|
||||
switch z := ifa.(type) {
|
||||
case *string:
|
||||
return bytes.Compare([]byte(s.S), []byte(*z)), nil
|
||||
}
|
||||
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 94: cannot compare %T to %T", s, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func (env *Zlisp) compareSymbol(sym *SexpSymbol, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpSymbol:
|
||||
return signumInt(int64(sym.number - e.number)), nil
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 95: cannot compare %T to %T", sym, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func (env *Zlisp) comparePair(a *SexpPair, b Sexp) (int, error) {
|
||||
var bp *SexpPair
|
||||
switch t := b.(type) {
|
||||
case *SexpPair:
|
||||
bp = t
|
||||
default:
|
||||
errmsg := fmt.Sprintf("err 96: cannot compare %T to %T", a, b)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
res, err := env.Compare(a.Head, bp.Head)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if res != 0 {
|
||||
return res, nil
|
||||
}
|
||||
return env.Compare(a.Tail, bp.Tail)
|
||||
}
|
||||
|
||||
func (env *Zlisp) compareArray(a *SexpArray, b Sexp) (int, error) {
|
||||
var ba *SexpArray
|
||||
switch t := b.(type) {
|
||||
case *SexpArray:
|
||||
ba = t
|
||||
default:
|
||||
errmsg := fmt.Sprintf("err 97: cannot compare %T to %T", a, b)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
var length int
|
||||
if len(a.Val) < len(ba.Val) {
|
||||
length = len(a.Val)
|
||||
} else {
|
||||
length = len(ba.Val)
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
res, err := env.Compare(a.Val[i], ba.Val[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if res != 0 {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return signumInt(int64(len(a.Val) - len(ba.Val))), nil
|
||||
}
|
||||
|
||||
func compareBool(a *SexpBool, b Sexp) (int, error) {
|
||||
var bb *SexpBool
|
||||
switch bt := b.(type) {
|
||||
case *SexpBool:
|
||||
bb = bt
|
||||
default:
|
||||
errmsg := fmt.Sprintf("err 98: cannot compare %T to %T", a, b)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
// true > false
|
||||
if a.Val && bb.Val {
|
||||
return 0, nil
|
||||
}
|
||||
if a.Val {
|
||||
return 1, nil
|
||||
}
|
||||
if bb.Val {
|
||||
return -1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func comparePointers(a *SexpPointer, bs Sexp) (int, error) {
|
||||
var b *SexpPointer
|
||||
switch bt := bs.(type) {
|
||||
case *SexpPointer:
|
||||
b = bt
|
||||
default:
|
||||
return 0, fmt.Errorf("err 99: cannot compare %T to %T", a, bs)
|
||||
}
|
||||
|
||||
if a.Target == b.Target {
|
||||
return 0, nil
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) Compare(a Sexp, b Sexp) (int, error) {
|
||||
|
||||
var err error
|
||||
if sel, isSel := a.(Selector); isSel {
|
||||
a, err = sel.RHS(env)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if sel, isSel := b.(Selector); isSel {
|
||||
b, err = sel.RHS(env)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
switch at := a.(type) {
|
||||
case *SexpInt:
|
||||
return compareInt(at, b)
|
||||
case *SexpUint64:
|
||||
return compareUint64(at, b)
|
||||
case *SexpChar:
|
||||
return compareChar(at, b)
|
||||
case *SexpFloat:
|
||||
return compareFloat(at, b)
|
||||
case *SexpBool:
|
||||
return compareBool(at, b)
|
||||
case *SexpStr:
|
||||
return compareString(at, b)
|
||||
case *SexpSymbol:
|
||||
return env.compareSymbol(at, b)
|
||||
case *SexpPair:
|
||||
return env.comparePair(at, b)
|
||||
case *SexpArray:
|
||||
return env.compareArray(at, b)
|
||||
case *SexpHash:
|
||||
return compareHash(at, b)
|
||||
case *RegisteredType:
|
||||
return compareRegisteredTypes(at, b)
|
||||
case *SexpPointer:
|
||||
return comparePointers(at, b)
|
||||
case *SexpSentinel:
|
||||
if at == SexpNull && b == SexpNull {
|
||||
return 0, nil
|
||||
} else {
|
||||
return -1, nil
|
||||
}
|
||||
case *SexpTime:
|
||||
switch bt := b.(type) {
|
||||
case *SexpTime:
|
||||
if bt.Tm.Unix() == at.Tm.Unix() {
|
||||
return 0, nil
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
case *SexpReflect:
|
||||
r := reflect.Value(at.Val)
|
||||
ifa := r.Interface()
|
||||
//P("Compare(): ifa = %v/%t", ifa, ifa)
|
||||
//P("Compare(): r.Elem() = %v/%T", r.Elem(), r.Elem())
|
||||
switch z := ifa.(type) {
|
||||
case *int64:
|
||||
return compareInt(&SexpInt{Val: *z}, b)
|
||||
case *string:
|
||||
return compareString(&SexpStr{S: *z}, b)
|
||||
}
|
||||
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 100: cannot compare %T to %T", a, b)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
// only compare uint64 to uint64
|
||||
func compareUint64(i *SexpUint64, expr Sexp) (int, error) {
|
||||
switch e := expr.(type) {
|
||||
case *SexpUint64:
|
||||
return signumUint64(i.Val - e.Val), nil
|
||||
}
|
||||
errmsg := fmt.Sprintf("err 101: cannot compare %T to %T", i, expr)
|
||||
return 0, errors.New(errmsg)
|
||||
}
|
||||
|
||||
func signumUint64(i uint64) int {
|
||||
if i > 0 {
|
||||
return 1
|
||||
}
|
||||
if i < 0 {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
46
vendor/github.com/glycerine/zygomys/zygo/coroutines.go
generated
vendored
Normal file
46
vendor/github.com/glycerine/zygomys/zygo/coroutines.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type SexpGoroutine struct {
|
||||
env *Zlisp
|
||||
}
|
||||
|
||||
func (goro *SexpGoroutine) SexpString(ps *PrintState) string {
|
||||
return "[coroutine]"
|
||||
}
|
||||
func (goro *SexpGoroutine) Type() *RegisteredType {
|
||||
return nil // TODO what goes here
|
||||
}
|
||||
|
||||
func StartGoroutineFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
switch t := args[0].(type) {
|
||||
case *SexpGoroutine:
|
||||
go t.env.Run()
|
||||
default:
|
||||
return SexpNull, errors.New("not a goroutine")
|
||||
}
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
func CreateGoroutineMacro(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
goroenv := env.Duplicate()
|
||||
err := goroenv.LoadExpressions(args)
|
||||
if err != nil {
|
||||
return SexpNull, nil
|
||||
}
|
||||
goro := &SexpGoroutine{goroenv}
|
||||
|
||||
// (apply StartGoroutineFunction [goro])
|
||||
return MakeList([]Sexp{env.MakeSymbol("apply"),
|
||||
MakeUserFunction("__start", StartGoroutineFunction),
|
||||
&SexpArray{Val: []Sexp{goro}, Env: env}}), nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportGoroutines() {
|
||||
env.AddMacro("go", CreateGoroutineMacro)
|
||||
}
|
||||
75
vendor/github.com/glycerine/zygomys/zygo/datastack.go
generated
vendored
Normal file
75
vendor/github.com/glycerine/zygomys/zygo/datastack.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DataStackElem struct {
|
||||
expr Sexp
|
||||
}
|
||||
|
||||
func (d DataStackElem) IsStackElem() {}
|
||||
|
||||
func (stack *Stack) PushExpr(expr Sexp) {
|
||||
stack.Push(DataStackElem{expr})
|
||||
}
|
||||
|
||||
func (stack *Stack) PushExpressions(expr []Sexp) error {
|
||||
for _, x := range expr {
|
||||
stack.Push(DataStackElem{x})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *Stack) PopExpr() (Sexp, error) {
|
||||
elem, err := stack.Pop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return elem.(DataStackElem).expr, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) GetExpressions(n int) ([]Sexp, error) {
|
||||
stack_start := stack.tos - n + 1
|
||||
if stack_start < 0 {
|
||||
return nil, errors.New("not enough items on stack")
|
||||
}
|
||||
arr := make([]Sexp, n)
|
||||
for i := 0; i < n; i++ {
|
||||
arr[i] = stack.elements[stack_start+i].(DataStackElem).expr
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) PopExpressions(n int) ([]Sexp, error) {
|
||||
origSz := stack.Size()
|
||||
expressions, err := stack.GetExpressions(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack.TruncateToSize(origSz - n)
|
||||
return expressions, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) GetExpr(n int) (Sexp, error) {
|
||||
elem, err := stack.Get(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return elem.(DataStackElem).expr, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) PrintStack() {
|
||||
for i := 0; i <= stack.tos; i++ {
|
||||
expr := stack.elements[i].(DataStackElem).expr
|
||||
fmt.Println("\t" + expr.SexpString(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func (stack *Stack) PrintScopeStack() {
|
||||
for i := 0; i <= stack.tos; i++ {
|
||||
scop := stack.elements[i].(*Scope)
|
||||
scop.Show(stack.env, NewPrintStateWithIndent(4), "")
|
||||
}
|
||||
}
|
||||
149
vendor/github.com/glycerine/zygomys/zygo/demo_go_structs.go
generated
vendored
Normal file
149
vendor/github.com/glycerine/zygomys/zygo/demo_go_structs.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate msgp
|
||||
|
||||
//msgp:ignore Plane Wings Snoopy Hornet Hellcat SetOfPlanes
|
||||
|
||||
// the pointer wasn't getting followed.
|
||||
type NestOuter struct {
|
||||
Inner *NestInner `msg:"inner" json:"inner" zid:"0"`
|
||||
}
|
||||
|
||||
type NestInner struct {
|
||||
Hello string `msg:"hello" json:"hello" zid:"0"`
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Id int `json:"id" msg:"id"`
|
||||
User Person `json:"user" msg:"user"`
|
||||
Flight string `json:"flight" msg:"flight"`
|
||||
Pilot []string `json:"pilot" msg:"pilot"`
|
||||
Cancelled bool `json:"cancelled" msg:"cancelled"`
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
First string `json:"first" msg:"first"`
|
||||
Last string `json:"last" msg:"last"`
|
||||
}
|
||||
|
||||
func (ev *Event) DisplayEvent(from string) {
|
||||
fmt.Printf("%s %#v", from, ev)
|
||||
}
|
||||
|
||||
type Wings struct {
|
||||
SpanCm int
|
||||
}
|
||||
|
||||
type SetOfPlanes struct {
|
||||
Flyers []Flyer `json:"flyers" msg:"flyers"`
|
||||
}
|
||||
|
||||
// the interface Flyer confounds the msgp msgpack code generator,
|
||||
// so put the msgp:ignore Plane above
|
||||
type Plane struct {
|
||||
Wings
|
||||
|
||||
ID int `json:"id" msg:"id"`
|
||||
Speed int `json:"speed" msg:"speed"`
|
||||
Chld Flyer `json:"chld" msg:"chld"`
|
||||
Friends []Flyer `json:"friends"`
|
||||
}
|
||||
|
||||
type Snoopy struct {
|
||||
Plane `json:"plane" msg:"plane"`
|
||||
Cry string `json:"cry" msg:"cry"`
|
||||
Pack []int `json:"pack"`
|
||||
Carrying []Flyer `json:"carrying"`
|
||||
}
|
||||
|
||||
type Hornet struct {
|
||||
Plane `json:"plane" msg:"plane"`
|
||||
Mass float64
|
||||
Nickname string
|
||||
}
|
||||
|
||||
type Hellcat struct {
|
||||
Plane `json:"plane" msg:"plane"`
|
||||
}
|
||||
|
||||
func (p *Snoopy) Fly(w *Weather) (s string, err error) {
|
||||
w.Type = "VERY " + w.Type // side-effect, for demo purposes
|
||||
s = fmt.Sprintf("Snoopy sees weather '%s', cries '%s'", w.Type, p.Cry)
|
||||
fmt.Println(s)
|
||||
for _, flyer := range p.Friends {
|
||||
flyer.Fly(w)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Snoopy) GetCry() string {
|
||||
return p.Cry
|
||||
}
|
||||
|
||||
func (p *Snoopy) EchoWeather(w *Weather) *Weather {
|
||||
return w
|
||||
}
|
||||
|
||||
func (p *Snoopy) Sideeffect() {
|
||||
fmt.Printf("Sideeffect() called! p = %p\n", p)
|
||||
}
|
||||
|
||||
func (b *Hornet) Fly(w *Weather) (s string, err error) {
|
||||
fmt.Printf("Hornet.Fly() called. I see weather %v\n", w.Type)
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Hellcat) Fly(w *Weather) (s string, err error) {
|
||||
fmt.Printf("Hellcat.Fly() called. I see weather %v\n", w.Type)
|
||||
return
|
||||
}
|
||||
|
||||
type Flyer interface {
|
||||
Fly(w *Weather) (s string, err error)
|
||||
}
|
||||
|
||||
type Weather struct {
|
||||
Time time.Time `json:"time" msg:"time"`
|
||||
Size int64 `json:"size" msg:"size"`
|
||||
Type string `json:"type" msg:"type"`
|
||||
Details []byte `json:"details" msg:"details"`
|
||||
}
|
||||
|
||||
func (w *Weather) IsSunny() bool {
|
||||
return w.Type == "sunny"
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportDemoData() {
|
||||
|
||||
env.AddFunction("nestouter", DemoNestInnerOuterFunction)
|
||||
env.AddFunction("nestinner", DemoNestInnerOuterFunction)
|
||||
|
||||
rt := &RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &NestOuter{}, nil
|
||||
}}
|
||||
GoStructRegistry.RegisterUserdef(rt, true, "nestouter", "NestOuter")
|
||||
|
||||
rt = &RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &NestInner{}, nil
|
||||
}}
|
||||
GoStructRegistry.RegisterUserdef(rt, true, "nestinner", "NestInner")
|
||||
|
||||
}
|
||||
|
||||
// constructor
|
||||
func DemoNestInnerOuterFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
|
||||
n := len(args)
|
||||
switch n {
|
||||
case 0:
|
||||
return SexpNull, WrongNargs
|
||||
default:
|
||||
// many parameters, treat as key:value pairs in the hash/record.
|
||||
return ConstructorFunction(env, "msgmap", append([]Sexp{&SexpStr{S: name}}, MakeList(args)))
|
||||
}
|
||||
}
|
||||
845
vendor/github.com/glycerine/zygomys/zygo/demo_go_structs_gen.go
generated
vendored
Normal file
845
vendor/github.com/glycerine/zygomys/zygo/demo_go_structs_gen.go
generated
vendored
Normal file
@@ -0,0 +1,845 @@
|
||||
package zygo
|
||||
|
||||
// NOTE: THIS FILE WAS PRODUCED BY THE
|
||||
// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp)
|
||||
// DO NOT EDIT
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Event) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "id":
|
||||
z.Id, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "user":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "first":
|
||||
z.User.First, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "last":
|
||||
z.User.Last, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case "flight":
|
||||
z.Flight, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "pilot":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cap(z.Pilot) >= int(zb0003) {
|
||||
z.Pilot = (z.Pilot)[:zb0003]
|
||||
} else {
|
||||
z.Pilot = make([]string, zb0003)
|
||||
}
|
||||
for za0001 := range z.Pilot {
|
||||
z.Pilot[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
case "cancelled":
|
||||
z.Cancelled, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Event) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 5
|
||||
// write "id"
|
||||
err = en.Append(0x85, 0xa2, 0x69, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "user"
|
||||
// map header, size 2
|
||||
// write "first"
|
||||
err = en.Append(0xa4, 0x75, 0x73, 0x65, 0x72, 0x82, 0xa5, 0x66, 0x69, 0x72, 0x73, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.User.First)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "last"
|
||||
err = en.Append(0xa4, 0x6c, 0x61, 0x73, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.User.Last)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "flight"
|
||||
err = en.Append(0xa6, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Flight)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "pilot"
|
||||
err = en.Append(0xa5, 0x70, 0x69, 0x6c, 0x6f, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(len(z.Pilot)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for za0001 := range z.Pilot {
|
||||
err = en.WriteString(z.Pilot[za0001])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "cancelled"
|
||||
err = en.Append(0xa9, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Cancelled)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Event) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 5
|
||||
// string "id"
|
||||
o = append(o, 0x85, 0xa2, 0x69, 0x64)
|
||||
o = msgp.AppendInt(o, z.Id)
|
||||
// string "user"
|
||||
// map header, size 2
|
||||
// string "first"
|
||||
o = append(o, 0xa4, 0x75, 0x73, 0x65, 0x72, 0x82, 0xa5, 0x66, 0x69, 0x72, 0x73, 0x74)
|
||||
o = msgp.AppendString(o, z.User.First)
|
||||
// string "last"
|
||||
o = append(o, 0xa4, 0x6c, 0x61, 0x73, 0x74)
|
||||
o = msgp.AppendString(o, z.User.Last)
|
||||
// string "flight"
|
||||
o = append(o, 0xa6, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74)
|
||||
o = msgp.AppendString(o, z.Flight)
|
||||
// string "pilot"
|
||||
o = append(o, 0xa5, 0x70, 0x69, 0x6c, 0x6f, 0x74)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(z.Pilot)))
|
||||
for za0001 := range z.Pilot {
|
||||
o = msgp.AppendString(o, z.Pilot[za0001])
|
||||
}
|
||||
// string "cancelled"
|
||||
o = append(o, 0xa9, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64)
|
||||
o = msgp.AppendBool(o, z.Cancelled)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Event) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "id":
|
||||
z.Id, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "user":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "first":
|
||||
z.User.First, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "last":
|
||||
z.User.Last, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case "flight":
|
||||
z.Flight, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "pilot":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cap(z.Pilot) >= int(zb0003) {
|
||||
z.Pilot = (z.Pilot)[:zb0003]
|
||||
} else {
|
||||
z.Pilot = make([]string, zb0003)
|
||||
}
|
||||
for za0001 := range z.Pilot {
|
||||
z.Pilot[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
case "cancelled":
|
||||
z.Cancelled, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Event) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.IntSize + 5 + 1 + 6 + msgp.StringPrefixSize + len(z.User.First) + 5 + msgp.StringPrefixSize + len(z.User.Last) + 7 + msgp.StringPrefixSize + len(z.Flight) + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Pilot {
|
||||
s += msgp.StringPrefixSize + len(z.Pilot[za0001])
|
||||
}
|
||||
s += 10 + msgp.BoolSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *NestInner) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "hello":
|
||||
z.Hello, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z NestInner) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 1
|
||||
// write "hello"
|
||||
err = en.Append(0x81, 0xa5, 0x68, 0x65, 0x6c, 0x6c, 0x6f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Hello)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z NestInner) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 1
|
||||
// string "hello"
|
||||
o = append(o, 0x81, 0xa5, 0x68, 0x65, 0x6c, 0x6c, 0x6f)
|
||||
o = msgp.AppendString(o, z.Hello)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *NestInner) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "hello":
|
||||
z.Hello, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z NestInner) Msgsize() (s int) {
|
||||
s = 1 + 6 + msgp.StringPrefixSize + len(z.Hello)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *NestOuter) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "inner":
|
||||
if dc.IsNil() {
|
||||
err = dc.ReadNil()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
z.Inner = nil
|
||||
} else {
|
||||
if z.Inner == nil {
|
||||
z.Inner = new(NestInner)
|
||||
}
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "hello":
|
||||
z.Inner.Hello, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *NestOuter) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 1
|
||||
// write "inner"
|
||||
err = en.Append(0x81, 0xa5, 0x69, 0x6e, 0x6e, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if z.Inner == nil {
|
||||
err = en.WriteNil()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// map header, size 1
|
||||
// write "hello"
|
||||
err = en.Append(0x81, 0xa5, 0x68, 0x65, 0x6c, 0x6c, 0x6f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Inner.Hello)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *NestOuter) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 1
|
||||
// string "inner"
|
||||
o = append(o, 0x81, 0xa5, 0x69, 0x6e, 0x6e, 0x65, 0x72)
|
||||
if z.Inner == nil {
|
||||
o = msgp.AppendNil(o)
|
||||
} else {
|
||||
// map header, size 1
|
||||
// string "hello"
|
||||
o = append(o, 0x81, 0xa5, 0x68, 0x65, 0x6c, 0x6c, 0x6f)
|
||||
o = msgp.AppendString(o, z.Inner.Hello)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *NestOuter) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "inner":
|
||||
if msgp.IsNil(bts) {
|
||||
bts, err = msgp.ReadNilBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
z.Inner = nil
|
||||
} else {
|
||||
if z.Inner == nil {
|
||||
z.Inner = new(NestInner)
|
||||
}
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "hello":
|
||||
z.Inner.Hello, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *NestOuter) Msgsize() (s int) {
|
||||
s = 1 + 6
|
||||
if z.Inner == nil {
|
||||
s += msgp.NilSize
|
||||
} else {
|
||||
s += 1 + 6 + msgp.StringPrefixSize + len(z.Inner.Hello)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Person) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "first":
|
||||
z.First, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "last":
|
||||
z.Last, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z Person) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "first"
|
||||
err = en.Append(0x82, 0xa5, 0x66, 0x69, 0x72, 0x73, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.First)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "last"
|
||||
err = en.Append(0xa4, 0x6c, 0x61, 0x73, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Last)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z Person) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "first"
|
||||
o = append(o, 0x82, 0xa5, 0x66, 0x69, 0x72, 0x73, 0x74)
|
||||
o = msgp.AppendString(o, z.First)
|
||||
// string "last"
|
||||
o = append(o, 0xa4, 0x6c, 0x61, 0x73, 0x74)
|
||||
o = msgp.AppendString(o, z.Last)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Person) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "first":
|
||||
z.First, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "last":
|
||||
z.Last, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z Person) Msgsize() (s int) {
|
||||
s = 1 + 6 + msgp.StringPrefixSize + len(z.First) + 5 + msgp.StringPrefixSize + len(z.Last)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Weather) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "time":
|
||||
z.Time, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "size":
|
||||
z.Size, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "type":
|
||||
z.Type, err = dc.ReadString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "details":
|
||||
z.Details, err = dc.ReadBytes(z.Details)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Weather) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 4
|
||||
// write "time"
|
||||
err = en.Append(0x84, 0xa4, 0x74, 0x69, 0x6d, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.Time)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "size"
|
||||
err = en.Append(0xa4, 0x73, 0x69, 0x7a, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.Size)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "type"
|
||||
err = en.Append(0xa4, 0x74, 0x79, 0x70, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// write "details"
|
||||
err = en.Append(0xa7, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBytes(z.Details)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Weather) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 4
|
||||
// string "time"
|
||||
o = append(o, 0x84, 0xa4, 0x74, 0x69, 0x6d, 0x65)
|
||||
o = msgp.AppendTime(o, z.Time)
|
||||
// string "size"
|
||||
o = append(o, 0xa4, 0x73, 0x69, 0x7a, 0x65)
|
||||
o = msgp.AppendInt64(o, z.Size)
|
||||
// string "type"
|
||||
o = append(o, 0xa4, 0x74, 0x79, 0x70, 0x65)
|
||||
o = msgp.AppendString(o, z.Type)
|
||||
// string "details"
|
||||
o = append(o, 0xa7, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73)
|
||||
o = msgp.AppendBytes(o, z.Details)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Weather) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "time":
|
||||
z.Time, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "size":
|
||||
z.Size, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "type":
|
||||
z.Type, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "details":
|
||||
z.Details, bts, err = msgp.ReadBytesBytes(bts, z.Details)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Weather) Msgsize() (s int) {
|
||||
s = 1 + 5 + msgp.TimeSize + 5 + msgp.Int64Size + 5 + msgp.StringPrefixSize + len(z.Type) + 8 + msgp.BytesPrefixSize + len(z.Details)
|
||||
return
|
||||
}
|
||||
8
vendor/github.com/glycerine/zygomys/zygo/doc.go
generated
vendored
Normal file
8
vendor/github.com/glycerine/zygomys/zygo/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
This project does not use godoc. Instead there is extensive
|
||||
and detailed description of the language features maintained
|
||||
on the wiki. See the following link.
|
||||
|
||||
https://github.com/glycerine/zygomys/wiki
|
||||
*/
|
||||
package zygo
|
||||
859
vendor/github.com/glycerine/zygomys/zygo/environment.go
generated
vendored
Normal file
859
vendor/github.com/glycerine/zygomys/zygo/environment.go
generated
vendored
Normal file
@@ -0,0 +1,859 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type PreHook func(*Zlisp, string, []Sexp)
|
||||
type PostHook func(*Zlisp, string, Sexp)
|
||||
|
||||
type Zlisp struct {
|
||||
parser *Parser
|
||||
datastack *Stack
|
||||
addrstack *Stack
|
||||
|
||||
// linearstack: push on scope enter, pop on scope exit. runtime dynamic.
|
||||
linearstack *Stack
|
||||
|
||||
// loopstack: let break and continue find the nearest enclosing loop.
|
||||
loopstack *Stack
|
||||
|
||||
symtable map[string]int
|
||||
revsymtable map[int]string
|
||||
builtins map[int]*SexpFunction
|
||||
reserved map[int]bool
|
||||
macros map[int]*SexpFunction
|
||||
curfunc *SexpFunction
|
||||
mainfunc *SexpFunction
|
||||
pc int
|
||||
nextsymbol int
|
||||
before []PreHook
|
||||
after []PostHook
|
||||
|
||||
debugExec bool
|
||||
debugSymbolNotFound bool
|
||||
|
||||
showGlobalScope bool
|
||||
baseTypeCtor *SexpFunction
|
||||
|
||||
infixOps map[string]*InfixOp
|
||||
Pretty bool
|
||||
|
||||
booter Booter
|
||||
|
||||
// API use, since infix is already default at repl
|
||||
WrapLoadExpressionsInInfix bool
|
||||
}
|
||||
|
||||
// allow clients to establish a callback to
|
||||
// happen after reinflating a Go struct. These
|
||||
// structs need to be "booted" to be ready to go.
|
||||
func (env *Zlisp) SetBooter(b Booter) {
|
||||
env.booter = b
|
||||
}
|
||||
|
||||
// Booter provides for registering a callback
|
||||
// for any new Go struct created by the ToGoFunction (togo).
|
||||
type Booter func(s interface{})
|
||||
|
||||
const CallStackSize = 25
|
||||
const ScopeStackSize = 50
|
||||
const DataStackSize = 100
|
||||
const StackStackSize = 5
|
||||
const LoopStackSize = 5
|
||||
|
||||
var ReservedWords = []string{"byte", "defbuild", "builder", "field", "and", "or", "cond", "quote", "def", "mdef", "fn", "defn", "begin", "let", "letseq", "assert", "defmac", "macexpand", "syntaxQuote", "include", "for", "set", "break", "continue", "newScope", "_ls", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "complex64", "complex128", "bool", "string", "any", "break", "case", "chan", "const", "continue", "default", "else", "defer", "fallthrough", "for", "func", "go", "goto", "if", "import", "interface", "map", "package", "range", "return", "select", "struct", "switch", "type", "var", "append", "cap", "close", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover", "null", "nil", "-", "+", "--", "++", "-=", "+=", ":=", "=", ">", "<", ">=", "<=", "send", "NaN", "nan"}
|
||||
|
||||
func NewZlisp() *Zlisp {
|
||||
return NewZlispWithFuncs(AllBuiltinFunctions())
|
||||
}
|
||||
|
||||
func (env *Zlisp) Stop() error {
|
||||
return env.parser.Stop()
|
||||
}
|
||||
|
||||
// NewZlispSandbox returns a new *Zlisp instance that does not allow the
|
||||
// user to get to the outside world
|
||||
func NewZlispSandbox() *Zlisp {
|
||||
return NewZlispWithFuncs(SandboxSafeFunctions())
|
||||
}
|
||||
|
||||
// NewZlispWithFuncs returns a new *Zlisp instance with access to only the given builtin functions
|
||||
func NewZlispWithFuncs(funcs map[string]ZlispUserFunction) *Zlisp {
|
||||
env := new(Zlisp)
|
||||
env.baseTypeCtor = MakeUserFunction("__basetype_ctor", BaseTypeConstructorFunction)
|
||||
env.parser = env.NewParser()
|
||||
env.parser.Start()
|
||||
env.datastack = env.NewStack(DataStackSize)
|
||||
env.linearstack = env.NewStack(ScopeStackSize)
|
||||
|
||||
glob := env.NewNamedScope("global")
|
||||
glob.IsGlobal = true
|
||||
env.linearstack.Push(glob)
|
||||
env.addrstack = env.NewStack(CallStackSize)
|
||||
env.loopstack = env.NewStack(LoopStackSize)
|
||||
env.builtins = make(map[int]*SexpFunction)
|
||||
env.reserved = make(map[int]bool)
|
||||
env.macros = make(map[int]*SexpFunction)
|
||||
env.symtable = make(map[string]int)
|
||||
env.revsymtable = make(map[int]string)
|
||||
env.nextsymbol = 1
|
||||
env.before = []PreHook{}
|
||||
env.after = []PostHook{}
|
||||
env.infixOps = make(map[string]*InfixOp)
|
||||
env.AddGlobal("null", SexpNull)
|
||||
env.AddGlobal("nil", SexpNull)
|
||||
|
||||
for key, function := range funcs {
|
||||
sym := env.MakeSymbol(key)
|
||||
env.builtins[sym.number] = MakeUserFunction(key, function)
|
||||
env.AddFunction(key, function)
|
||||
}
|
||||
|
||||
for _, word := range ReservedWords {
|
||||
sym := env.MakeSymbol(word)
|
||||
env.reserved[sym.number] = true
|
||||
}
|
||||
|
||||
env.mainfunc = env.MakeFunction("__main", 0, false,
|
||||
make([]Instruction, 0), nil)
|
||||
env.curfunc = env.mainfunc
|
||||
env.pc = 0
|
||||
env.debugSymbolNotFound = false
|
||||
//env.debugSymbolNotFound = true
|
||||
//env.debugExec = true
|
||||
env.InitInfixOps()
|
||||
|
||||
return env
|
||||
|
||||
}
|
||||
|
||||
func (env *Zlisp) Clone() *Zlisp {
|
||||
dupenv := new(Zlisp)
|
||||
dupenv.parser = env.parser
|
||||
dupenv.baseTypeCtor = env.baseTypeCtor
|
||||
dupenv.datastack = env.datastack.Clone()
|
||||
dupenv.linearstack = env.linearstack.Clone()
|
||||
dupenv.addrstack = env.addrstack.Clone()
|
||||
|
||||
dupenv.builtins = env.builtins
|
||||
dupenv.reserved = env.reserved
|
||||
dupenv.macros = env.macros
|
||||
dupenv.symtable = env.symtable
|
||||
dupenv.revsymtable = env.revsymtable
|
||||
dupenv.nextsymbol = env.nextsymbol
|
||||
dupenv.before = env.before
|
||||
dupenv.after = env.after
|
||||
dupenv.infixOps = env.infixOps
|
||||
dupenv.linearstack.Push(env.linearstack.elements[0])
|
||||
|
||||
dupenv.mainfunc = env.MakeFunction("__main", 0, false,
|
||||
make([]Instruction, 0), nil)
|
||||
dupenv.curfunc = dupenv.mainfunc
|
||||
dupenv.pc = 0
|
||||
dupenv.debugExec = env.debugExec
|
||||
dupenv.debugSymbolNotFound = env.debugSymbolNotFound
|
||||
dupenv.showGlobalScope = env.showGlobalScope
|
||||
return dupenv
|
||||
}
|
||||
|
||||
func (env *Zlisp) Duplicate() *Zlisp {
|
||||
dupenv := new(Zlisp)
|
||||
dupenv.parser = env.parser
|
||||
dupenv.baseTypeCtor = env.baseTypeCtor
|
||||
dupenv.datastack = dupenv.NewStack(DataStackSize)
|
||||
dupenv.linearstack = dupenv.NewStack(ScopeStackSize)
|
||||
dupenv.addrstack = dupenv.NewStack(CallStackSize)
|
||||
dupenv.builtins = env.builtins
|
||||
dupenv.reserved = env.reserved
|
||||
dupenv.macros = env.macros
|
||||
dupenv.symtable = env.symtable
|
||||
dupenv.revsymtable = env.revsymtable
|
||||
dupenv.nextsymbol = env.nextsymbol
|
||||
dupenv.before = env.before
|
||||
dupenv.after = env.after
|
||||
dupenv.infixOps = env.infixOps
|
||||
|
||||
dupenv.linearstack.Push(env.linearstack.elements[0])
|
||||
|
||||
dupenv.mainfunc = env.MakeFunction("__main", 0, false,
|
||||
make([]Instruction, 0), nil)
|
||||
dupenv.curfunc = dupenv.mainfunc
|
||||
dupenv.pc = 0
|
||||
dupenv.debugExec = env.debugExec
|
||||
dupenv.debugSymbolNotFound = env.debugSymbolNotFound
|
||||
dupenv.showGlobalScope = env.showGlobalScope
|
||||
|
||||
return dupenv
|
||||
}
|
||||
|
||||
func (env *Zlisp) MakeDotSymbol(name string) *SexpSymbol {
|
||||
x := env.MakeSymbol(name)
|
||||
x.isDot = true
|
||||
return x
|
||||
}
|
||||
|
||||
func (env *Zlisp) DetectSigils(sym *SexpSymbol) {
|
||||
if sym == nil {
|
||||
return
|
||||
}
|
||||
if len(sym.name) == 0 {
|
||||
return
|
||||
}
|
||||
switch sym.name[0] {
|
||||
case '$':
|
||||
sym.isSigil = true
|
||||
sym.sigil = "$"
|
||||
case '#':
|
||||
sym.isSigil = true
|
||||
sym.sigil = "#"
|
||||
case '?':
|
||||
sym.isSigil = true
|
||||
sym.sigil = "?"
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) DumpSymTable() {
|
||||
for kk, vv := range env.symtable {
|
||||
fmt.Printf("symtable entry: kk: '%v' -> '%v'\n", kk, vv)
|
||||
}
|
||||
}
|
||||
func (env *Zlisp) MakeSymbol(name string) *SexpSymbol {
|
||||
if env == nil {
|
||||
panic("internal problem: env.MakeSymbol called with nil env")
|
||||
}
|
||||
symnum, ok := env.symtable[name]
|
||||
if ok {
|
||||
symbol := &SexpSymbol{name: name, number: symnum}
|
||||
env.DetectSigils(symbol)
|
||||
return symbol
|
||||
}
|
||||
symbol := &SexpSymbol{name: name, number: env.nextsymbol}
|
||||
env.symtable[name] = symbol.number
|
||||
env.revsymtable[symbol.number] = name
|
||||
|
||||
env.nextsymbol++
|
||||
env.DetectSigils(symbol)
|
||||
return symbol
|
||||
}
|
||||
|
||||
func (env *Zlisp) GenSymbol(prefix string) *SexpSymbol {
|
||||
symname := prefix + strconv.Itoa(env.nextsymbol)
|
||||
return env.MakeSymbol(symname)
|
||||
}
|
||||
|
||||
func (env *Zlisp) CurrentFunctionSize() int {
|
||||
if env.curfunc.user {
|
||||
return 0
|
||||
}
|
||||
return len(env.curfunc.fun)
|
||||
}
|
||||
|
||||
func (env *Zlisp) wrangleOptargs(fnargs, nargs int) error {
|
||||
if nargs < fnargs {
|
||||
return errors.New(
|
||||
fmt.Sprintf("Expected >%d arguments, got %d",
|
||||
fnargs, nargs))
|
||||
}
|
||||
if nargs > fnargs {
|
||||
optargs, err := env.datastack.PopExpressions(nargs - fnargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.datastack.PushExpr(MakeList(optargs))
|
||||
} else {
|
||||
env.datastack.PushExpr(SexpNull)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) CallFunction(function *SexpFunction, nargs int) error {
|
||||
for _, prehook := range env.before {
|
||||
expressions, err := env.datastack.GetExpressions(nargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prehook(env, function.name, expressions)
|
||||
}
|
||||
|
||||
// do name and type checking
|
||||
err := env.FunctionCallNameTypeCheck(function, &nargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if function.varargs {
|
||||
err := env.wrangleOptargs(function.nargs, nargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if nargs != function.nargs {
|
||||
return errors.New(
|
||||
fmt.Sprintf("%s expected %d arguments, got %d",
|
||||
function.name, function.nargs, nargs))
|
||||
}
|
||||
|
||||
if env.linearstack.IsEmpty() {
|
||||
panic("where's the global scope?")
|
||||
}
|
||||
|
||||
env.addrstack.PushAddr(env.curfunc, env.pc+1)
|
||||
|
||||
//P("DEBUG linearstack with this next:")
|
||||
//env.showStackHelper(env.linearstack, "linearstack")
|
||||
|
||||
// this effectely *is* the call, because it sets the
|
||||
// next instructions to happen once we exit.
|
||||
env.curfunc = function
|
||||
env.pc = 0
|
||||
|
||||
//Q("\n CallFunction starting with stack:\n")
|
||||
//env.ShowStackStackAndScopeStack()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ReturnFromFunction() error {
|
||||
for _, posthook := range env.after {
|
||||
retval, err := env.datastack.GetExpr(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
posthook(env, env.curfunc.name, retval)
|
||||
}
|
||||
var err error
|
||||
env.curfunc, env.pc, err = env.addrstack.PopAddr()
|
||||
return err
|
||||
}
|
||||
|
||||
func (env *Zlisp) CallUserFunction(
|
||||
function *SexpFunction, name string, nargs int) (nargReturned int, err error) {
|
||||
Q("CallUserFunction calling name '%s' with nargs=%v", name, nargs)
|
||||
for _, prehook := range env.before {
|
||||
expressions, err := env.datastack.GetExpressions(nargs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
prehook(env, function.name, expressions)
|
||||
}
|
||||
|
||||
args, err := env.datastack.PopExpressions(nargs)
|
||||
if err != nil {
|
||||
return 0, errors.New(
|
||||
fmt.Sprintf("Error calling '%s': %v", name, err))
|
||||
}
|
||||
|
||||
env.addrstack.PushAddr(env.curfunc, env.pc+1)
|
||||
env.curfunc = function
|
||||
env.pc = -1
|
||||
|
||||
//P("DEBUG linearstack with this next, just before calling function.userfun:")
|
||||
//env.showStackHelper(env.linearstack, "linearstack")
|
||||
|
||||
// protect against bad calls/bad reflection in usercalls
|
||||
var wasPanic bool
|
||||
var recovered interface{}
|
||||
tr := make([]byte, 16384)
|
||||
trace := &tr
|
||||
res, err := func() (Sexp, error) {
|
||||
defer func() {
|
||||
recovered = recover()
|
||||
if recovered != nil {
|
||||
wasPanic = true
|
||||
nbyte := runtime.Stack(*trace, false)
|
||||
*trace = (*trace)[:nbyte]
|
||||
}
|
||||
}()
|
||||
|
||||
// the point we were getting to, before the panic protection:
|
||||
return function.userfun(env, name, args)
|
||||
}()
|
||||
|
||||
//P("DEBUG linearstack with this next, just *after* calling function.userfun:")
|
||||
//env.showStackHelper(env.linearstack, "linearstack")
|
||||
|
||||
if wasPanic {
|
||||
err = fmt.Errorf("CallUserFunction caught panic during call of "+
|
||||
"'%s': '%v'\n stack trace:\n%v\n",
|
||||
name, recovered, string(*trace))
|
||||
}
|
||||
if err != nil {
|
||||
return 0, errors.New(
|
||||
fmt.Sprintf("Error calling '%s': %v", name, err))
|
||||
}
|
||||
|
||||
env.datastack.PushExpr(res)
|
||||
|
||||
for _, posthook := range env.after {
|
||||
posthook(env, name, res)
|
||||
}
|
||||
|
||||
env.curfunc, env.pc, _ = env.addrstack.PopAddr()
|
||||
return len(args), nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) LoadExpressions(xs []Sexp) error {
|
||||
|
||||
expressions := xs
|
||||
if env.WrapLoadExpressionsInInfix {
|
||||
infixSym := env.MakeSymbol("infix")
|
||||
expressions = []Sexp{MakeList([]Sexp{infixSym, &SexpArray{Val: xs, Env: env}})}
|
||||
}
|
||||
|
||||
//P("expressions before RemoveCommentsFilter: '%s'", (&SexpArray{Val: expressions, Env: env}).SexpString(0))
|
||||
expressions = env.FilterArray(expressions, RemoveCommentsFilter)
|
||||
|
||||
//P("expressions after RemoveCommentsFilter: '%s'", (&SexpArray{Val: expressions, Env: env}).SexpString(0))
|
||||
expressions = env.FilterArray(expressions, RemoveEndsFilter)
|
||||
|
||||
gen := NewGenerator(env)
|
||||
if !env.ReachedEnd() {
|
||||
gen.AddInstruction(PopInstr(0))
|
||||
}
|
||||
err := gen.GenerateBegin(expressions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env.mainfunc.fun = append(env.mainfunc.fun, gen.instructions...)
|
||||
env.curfunc = env.mainfunc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ParseFile(file string) ([]Sexp, error) {
|
||||
in, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var exp []Sexp
|
||||
|
||||
env.parser.Reset()
|
||||
env.parser.NewInput(bufio.NewReader(in))
|
||||
exp, err = env.parser.ParseTokens()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error on line %d: %v\n", env.parser.lexer.Linenum(), err)
|
||||
}
|
||||
|
||||
in.Close()
|
||||
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) LoadStream(stream io.RuneScanner) error {
|
||||
env.parser.ResetAddNewInput(stream)
|
||||
expressions, err := env.parser.ParseTokens()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error on line %d: %v\n", env.parser.lexer.Linenum(), err)
|
||||
}
|
||||
return env.LoadExpressions(expressions)
|
||||
}
|
||||
|
||||
func (env *Zlisp) EvalString(str string) (Sexp, error) {
|
||||
err := env.LoadString(str)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
VPrintf("\n EvalString: LoadString() done, now to Run():\n")
|
||||
return env.Run()
|
||||
}
|
||||
|
||||
// for most things now (except the main repl), prefer EvalFunction() instead of EvalExpressions.
|
||||
func (env *Zlisp) EvalExpressions(xs []Sexp) (Sexp, error) {
|
||||
//P("inside EvalExpressions with env %p: xs[0] = %s", env, xs[0].SexpString(0))
|
||||
err := env.LoadExpressions(xs)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return env.Run()
|
||||
}
|
||||
|
||||
func (env *Zlisp) LoadFile(file io.Reader) error {
|
||||
return env.LoadStream(bufio.NewReader(file))
|
||||
}
|
||||
|
||||
func (env *Zlisp) LoadString(str string) error {
|
||||
return env.LoadStream(bytes.NewBuffer([]byte(str)))
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddFunction(name string, function ZlispUserFunction) {
|
||||
env.AddGlobal(name, MakeUserFunction(name, function))
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddBuilder(name string, function ZlispUserFunction) {
|
||||
env.AddGlobal(name, MakeBuilderFunction(name, function))
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddGlobal(name string, obj Sexp) {
|
||||
sym := env.MakeSymbol(name)
|
||||
env.linearstack.elements[0].(*Scope).Map[sym.number] = obj
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddMacro(name string, function ZlispUserFunction) {
|
||||
sym := env.MakeSymbol(name)
|
||||
env.macros[sym.number] = MakeUserFunction(name, function)
|
||||
}
|
||||
|
||||
func (env *Zlisp) HasMacro(sym *SexpSymbol) bool {
|
||||
_, found := env.macros[sym.number]
|
||||
return found
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportEval() {
|
||||
env.AddFunction("eval", EvalFunction)
|
||||
}
|
||||
|
||||
func (env *Zlisp) DumpFunctionByName(name string) error {
|
||||
obj, found := env.FindObject(name)
|
||||
if !found {
|
||||
return errors.New(fmt.Sprintf("%q not found", name))
|
||||
}
|
||||
|
||||
var fun ZlispFunction
|
||||
switch t := obj.(type) {
|
||||
case *SexpFunction:
|
||||
if !t.user {
|
||||
fun = t.fun
|
||||
} else {
|
||||
return errors.New("not a glisp function")
|
||||
}
|
||||
default:
|
||||
return errors.New("dump by name error: not a function")
|
||||
}
|
||||
DumpFunction(fun, -1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// if pc is -1, don't show it.
|
||||
func DumpFunction(fun ZlispFunction, pc int) {
|
||||
blank := " "
|
||||
extra := blank
|
||||
for i, instr := range fun {
|
||||
if i == pc {
|
||||
extra = " PC-> "
|
||||
} else {
|
||||
extra = blank
|
||||
}
|
||||
fmt.Printf("%s %d: %s\n", extra, i, instr.InstrString())
|
||||
}
|
||||
if pc == len(fun) {
|
||||
fmt.Printf(" PC just past end at %d -----\n\n", pc)
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) DumpEnvironment() {
|
||||
fmt.Printf("PC: %d\n", env.pc)
|
||||
fmt.Println("Instructions:")
|
||||
if !env.curfunc.user {
|
||||
DumpFunction(env.curfunc.fun, env.pc)
|
||||
}
|
||||
fmt.Printf("DataStack (%p): (length %d)\n", env.datastack, env.datastack.Size())
|
||||
env.datastack.PrintStack()
|
||||
fmt.Printf("Linear stack: (length %d)\n", env.linearstack.Size())
|
||||
//env.linearstack.PrintScopeStack()
|
||||
// instead of the above, try:
|
||||
env.showStackHelper(env.linearstack, "linearstack")
|
||||
}
|
||||
|
||||
func (env *Zlisp) ReachedEnd() bool {
|
||||
return env.pc == env.CurrentFunctionSize()
|
||||
}
|
||||
|
||||
func (env *Zlisp) GetStackTrace(err error) string {
|
||||
str := fmt.Sprintf("error in %s:%d: %v\n",
|
||||
env.curfunc.name, env.pc, err)
|
||||
for !env.addrstack.IsEmpty() {
|
||||
fun, pos, _ := env.addrstack.PopAddr()
|
||||
str += fmt.Sprintf("in %s:%d\n", fun.name, pos)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (env *Zlisp) Clear() {
|
||||
env.datastack.tos = -1
|
||||
env.linearstack.tos = 0
|
||||
env.addrstack.tos = -1
|
||||
|
||||
env.mainfunc = env.MakeFunction("__main", 0, false,
|
||||
make([]Instruction, 0), nil)
|
||||
env.curfunc = env.mainfunc
|
||||
env.pc = 0
|
||||
}
|
||||
|
||||
func (env *Zlisp) FindObject(name string) (Sexp, bool) {
|
||||
sym := env.MakeSymbol(name)
|
||||
obj, err, _ := env.linearstack.LookupSymbol(sym, nil)
|
||||
if err != nil {
|
||||
return SexpNull, false
|
||||
}
|
||||
return obj, true
|
||||
}
|
||||
|
||||
func (env *Zlisp) Apply(fun *SexpFunction, args []Sexp) (Sexp, error) {
|
||||
VPrintf("\n\n debug Apply not working on user funcs: fun = '%#v' and args = '%#v'\n\n", fun, args)
|
||||
if fun.user {
|
||||
return fun.userfun(env, fun.name, args)
|
||||
}
|
||||
|
||||
env.pc = -2
|
||||
for _, expr := range args {
|
||||
env.datastack.PushExpr(expr)
|
||||
}
|
||||
|
||||
//VPrintf("\nApply Calling '%s'\n", fun.SexpString())
|
||||
err := env.CallFunction(fun, len(args))
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
return env.Run()
|
||||
}
|
||||
|
||||
func (env *Zlisp) Run() (Sexp, error) {
|
||||
|
||||
for env.pc != -1 && !env.ReachedEnd() {
|
||||
instr := env.curfunc.fun[env.pc]
|
||||
if env.debugExec {
|
||||
fmt.Printf("\n ====== in '%s', about to run: '%v'\n",
|
||||
env.curfunc.name, instr.InstrString())
|
||||
env.DumpEnvironment()
|
||||
fmt.Printf("\n ====== in '%s', now running the above.\n",
|
||||
env.curfunc.name)
|
||||
}
|
||||
err := instr.Execute(env)
|
||||
if err == StackUnderFlowErr {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if env.debugExec {
|
||||
fmt.Printf("\n ****** in '%s', after running, stack is: \n",
|
||||
env.curfunc.name)
|
||||
env.DumpEnvironment()
|
||||
fmt.Printf("\n ****** \n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if env.datastack.IsEmpty() {
|
||||
// this does fire.
|
||||
//P("debug: *** detected empty datastack, adding a null")
|
||||
env.datastack.PushExpr(SexpNull)
|
||||
}
|
||||
|
||||
return env.datastack.PopExpr()
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddPreHook(fun PreHook) {
|
||||
env.before = append(env.before, fun)
|
||||
}
|
||||
|
||||
func (env *Zlisp) AddPostHook(fun PostHook) {
|
||||
env.after = append(env.after, fun)
|
||||
}
|
||||
|
||||
// scan the instruction stream to locate loop start
|
||||
func (env *Zlisp) FindLoop(target *Loop) (int, error) {
|
||||
if env.curfunc.user {
|
||||
panic(fmt.Errorf("impossible in user-defined-function to find a loop '%s'", target.stmtname.name))
|
||||
}
|
||||
instruc := env.curfunc.fun
|
||||
for i := range instruc {
|
||||
switch loop := instruc[i].(type) {
|
||||
case LoopStartInstr:
|
||||
if loop.loop == target {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1, fmt.Errorf("could not find loop target '%s'", target.stmtname.name)
|
||||
}
|
||||
|
||||
func (env *Zlisp) showStackHelper(stack *Stack, name string) {
|
||||
note := ""
|
||||
n := stack.Top()
|
||||
if n < 0 {
|
||||
note = "(empty)"
|
||||
}
|
||||
fmt.Printf(" ======== env(%p).%s is %v deep: %s\n", env, name, n+1, note)
|
||||
s := ""
|
||||
for i := 0; i <= n; i++ {
|
||||
ele, err := stack.Get(n - i)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("env.%s access error on %v: %v",
|
||||
name, i, err))
|
||||
}
|
||||
label := fmt.Sprintf("%s %v", name, i)
|
||||
switch x := ele.(type) {
|
||||
case *Stack:
|
||||
s, _ = x.Show(env, nil, label)
|
||||
|
||||
case *Scope:
|
||||
s, _ = x.Show(env, nil, label)
|
||||
case Scope:
|
||||
s, _ = x.Show(env, nil, label)
|
||||
default:
|
||||
panic(fmt.Errorf("unrecognized element on %s: %T/val=%v",
|
||||
name, x, x))
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) dumpParentChain(curfunc *SexpFunction) {
|
||||
|
||||
cur := curfunc
|
||||
par := cur.parent
|
||||
for par != nil {
|
||||
fmt.Printf(" parent chain: cur:%v -> parent:%v\n", cur.name, par.name)
|
||||
fmt.Printf(" cur.closures = %s", ClosureToString(cur, env))
|
||||
cur = par
|
||||
par = par.parent
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) ShowStackStackAndScopeStack() error {
|
||||
env.showStackHelper(env.linearstack, "linearstack")
|
||||
fmt.Println(" --- done with env.linearstack, now here is env.curfunc --- ")
|
||||
fmt.Println(ClosureToString(env.curfunc, env))
|
||||
fmt.Println(" --- done with env.curfunc closure, now here is parent chain: --- ")
|
||||
env.dumpParentChain(env.curfunc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ShowGlobalStack() error {
|
||||
prev := env.showGlobalScope
|
||||
env.showGlobalScope = true
|
||||
err := env.ShowStackStackAndScopeStack()
|
||||
env.showGlobalScope = prev
|
||||
return err
|
||||
}
|
||||
|
||||
func (env *Zlisp) LexicalLookupSymbol(sym *SexpSymbol, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
|
||||
// DotSymbols always evaluate to themselves
|
||||
if sym.isDot || sym.isSigil || sym.colonTail {
|
||||
return sym, nil, nil
|
||||
}
|
||||
|
||||
//P("LexicalLookupSymbol('%s', with setVal: %v)\n", sym.name, setVal)
|
||||
|
||||
const maxFuncToScan = 1 // 1 or otherwise tests/{closure.zy, dynprob.zy, dynscope.zy} will fail.
|
||||
exp, err, scope := env.linearstack.LookupSymbolUntilFunction(sym, setVal, maxFuncToScan, false)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LexicalLookupSymbol('%s') found on env.linearstack(1, false) in scope '%s'\n", sym.name, scope.Name)
|
||||
return exp, err, scope
|
||||
case SymNotFound:
|
||||
break
|
||||
}
|
||||
|
||||
// check the parent function lexical captured scopes, if parent available.
|
||||
if env.curfunc.parent != nil {
|
||||
//P("checking non-nil parent...")
|
||||
//exp, err, whichScope := env.curfunc.parent.ClosingLookupSymbol(sym, setVal)
|
||||
exp, err, whichScope := env.curfunc.LookupSymbolInParentChainOfClosures(sym, setVal, env)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LookupSymbolUntilFunction('%s') found in curfunc.parent.ClosingLookupSymbol() scope '%s'\n", sym.name, whichScope.Name)
|
||||
return exp, err, whichScope
|
||||
default:
|
||||
//P("not found looking via env.curfunc.parent.ClosingLookupSymbol(sym='%s')", sym.name)
|
||||
//env.ShowStackStackAndScopeStack()
|
||||
}
|
||||
} else {
|
||||
|
||||
//fmt.Printf(" *** env.curfunc has closure of: %s\n", ClosureToString(env.curfunc, env))
|
||||
//exp, err, scope = env.curfunc.ClosingLookupSymbol(sym, setVal)
|
||||
exp, err, scope = env.curfunc.ClosingLookupSymbolUntilFunc(sym, setVal, 1, false)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LexicalLookupSymbol('%s') found in env.curfunc.ClosingLookupSymbolUnfilFunc(1, false) in scope '%s'\n", sym.name, scope.Name)
|
||||
return exp, err, scope
|
||||
}
|
||||
}
|
||||
|
||||
// with checkCaptures true, as tests/package.zy needs this.
|
||||
exp, err, scope = env.linearstack.LookupSymbolUntilFunction(sym, setVal, 2, true)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LexicalLookupSymbol('%s') found in env.linearstack.LookupSymbolUtilFunction(2, true) in parent runtime scope '%s'\n", sym.name, scope.Name)
|
||||
return exp, err, scope
|
||||
case SymNotFound:
|
||||
break
|
||||
}
|
||||
|
||||
return SexpNull, fmt.Errorf("symbol `%s` not found", sym.name), nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) LexicalBindSymbol(sym *SexpSymbol, expr Sexp) error {
|
||||
return env.linearstack.BindSymbol(sym, expr)
|
||||
}
|
||||
|
||||
// _closdump : show the closed over env attached to an *SexpFunction
|
||||
func DumpClosureEnvFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
switch f := args[0].(type) {
|
||||
case *SexpFunction:
|
||||
s := ClosureToString(f, env)
|
||||
return &SexpStr{S: s}, nil
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("_closdump needs an *SexpFunction to inspect")
|
||||
}
|
||||
}
|
||||
|
||||
func ClosureToString(f *SexpFunction, env *Zlisp) string {
|
||||
s, err := f.ShowClosing(env, NewPrintState(),
|
||||
fmt.Sprintf("closedOverScopes of '%s'", f.name))
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (env *Zlisp) IsBuiltinSym(sym *SexpSymbol) (builtin bool, typ string) {
|
||||
|
||||
_, isBuiltin := env.builtins[sym.number]
|
||||
if isBuiltin {
|
||||
return true, "built-in function"
|
||||
}
|
||||
_, isBuiltin = env.macros[sym.number]
|
||||
if isBuiltin {
|
||||
return true, "macro"
|
||||
}
|
||||
_, isReserved := env.reserved[sym.number]
|
||||
if isReserved {
|
||||
return true, "reserved word"
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (env *Zlisp) ResolveDotSym(arg []Sexp) ([]Sexp, error) {
|
||||
r := []Sexp{}
|
||||
for i := range arg {
|
||||
switch sym := arg[i].(type) {
|
||||
case *SexpSymbol:
|
||||
resolved, err := dotGetSetHelper(env, sym.name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, resolved)
|
||||
default:
|
||||
r = append(r, arg[i])
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
27
vendor/github.com/glycerine/zygomys/zygo/exists.go
generated
vendored
Normal file
27
vendor/github.com/glycerine/zygomys/zygo/exists.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func FileExists(name string) bool {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func DirExists(name string) bool {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
694
vendor/github.com/glycerine/zygomys/zygo/expressions.go
generated
vendored
Normal file
694
vendor/github.com/glycerine/zygomys/zygo/expressions.go
generated
vendored
Normal file
@@ -0,0 +1,694 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// all Sexp are typed, and have a zero value corresponding to
|
||||
// the type of the Sexp.
|
||||
|
||||
// Sexp is the central interface for all
|
||||
// S-expressions (Symbol expressions ala lisp).
|
||||
type Sexp interface {
|
||||
// SexpString: produce a string from our value.
|
||||
// Single-line strings can ignore indent.
|
||||
// Only multiline strings should follow every
|
||||
// newline with at least indent worth of spaces.
|
||||
SexpString(ps *PrintState) string
|
||||
|
||||
// Type returns the type of the value.
|
||||
Type() *RegisteredType
|
||||
}
|
||||
|
||||
type SexpPair struct {
|
||||
Head Sexp
|
||||
Tail Sexp
|
||||
}
|
||||
|
||||
type SexpPointer struct {
|
||||
ReflectTarget reflect.Value
|
||||
Target Sexp
|
||||
PointedToType *RegisteredType
|
||||
MyType *RegisteredType
|
||||
}
|
||||
|
||||
func NewSexpPointer(pointedTo Sexp) *SexpPointer {
|
||||
pointedToType := pointedTo.Type()
|
||||
|
||||
var reftarg reflect.Value
|
||||
|
||||
Q("NewSexpPointer sees pointedTo of '%#v'", pointedTo)
|
||||
switch e := pointedTo.(type) {
|
||||
case *SexpReflect:
|
||||
Q("SexpReflect.Val = '%#v'", e.Val)
|
||||
reftarg = e.Val
|
||||
default:
|
||||
reftarg = reflect.ValueOf(pointedTo)
|
||||
}
|
||||
|
||||
ptrRt := GoStructRegistry.GetOrCreatePointerType(pointedToType)
|
||||
Q("pointer type is ptrRt = '%#v'", ptrRt)
|
||||
p := &SexpPointer{
|
||||
ReflectTarget: reftarg,
|
||||
Target: pointedTo,
|
||||
PointedToType: pointedToType,
|
||||
MyType: ptrRt,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *SexpPointer) SexpString(ps *PrintState) string {
|
||||
return fmt.Sprintf("%p", &p.Target)
|
||||
//return fmt.Sprintf("(* %v) %p", p.PointedToType.RegisteredName, p.Target)
|
||||
}
|
||||
|
||||
func (p *SexpPointer) Type() *RegisteredType {
|
||||
return p.MyType
|
||||
}
|
||||
|
||||
type SexpInt struct {
|
||||
Val int64
|
||||
Typ *RegisteredType
|
||||
}
|
||||
type SexpUint64 struct {
|
||||
Val uint64
|
||||
Typ *RegisteredType
|
||||
}
|
||||
type SexpBool struct {
|
||||
Val bool
|
||||
Typ *RegisteredType
|
||||
}
|
||||
type SexpFloat struct {
|
||||
Val float64
|
||||
Typ *RegisteredType
|
||||
Scientific bool
|
||||
}
|
||||
type SexpChar struct {
|
||||
Val rune
|
||||
Typ *RegisteredType
|
||||
}
|
||||
type SexpStr struct {
|
||||
S string
|
||||
backtick bool
|
||||
Typ *RegisteredType
|
||||
}
|
||||
|
||||
func (r SexpStr) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["string"]
|
||||
}
|
||||
|
||||
func (r *SexpInt) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["int64"]
|
||||
}
|
||||
|
||||
func (r *SexpUint64) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["uint64"]
|
||||
}
|
||||
|
||||
func (r *SexpFloat) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["float64"]
|
||||
}
|
||||
|
||||
func (r *SexpBool) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["bool"]
|
||||
}
|
||||
|
||||
func (r *SexpChar) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["int32"]
|
||||
}
|
||||
|
||||
func (r *RegisteredType) Type() *RegisteredType {
|
||||
return r
|
||||
}
|
||||
|
||||
type SexpRaw struct {
|
||||
Val []byte
|
||||
Typ *RegisteredType
|
||||
}
|
||||
|
||||
func (r *SexpRaw) Type() *RegisteredType {
|
||||
return r.Typ
|
||||
}
|
||||
|
||||
type SexpReflect struct {
|
||||
Val reflect.Value
|
||||
}
|
||||
|
||||
func (r *SexpReflect) Type() *RegisteredType {
|
||||
k := reflectName(reflect.Value(r.Val))
|
||||
Q("SexpReflect.Type() looking up type named '%s'", k)
|
||||
ty, ok := GoStructRegistry.Registry[k]
|
||||
if !ok {
|
||||
Q("SexpReflect.Type(): type named '%s' not found", k)
|
||||
return nil
|
||||
}
|
||||
Q("SexpReflect.Type(): type named '%s' found as regtype '%v'", k, ty.SexpString(nil))
|
||||
return ty
|
||||
}
|
||||
|
||||
type SexpError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (r *SexpError) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["error"]
|
||||
}
|
||||
|
||||
func (r *SexpSentinel) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
type SexpClosureEnv Scope
|
||||
|
||||
func (r *SexpClosureEnv) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func (c *SexpClosureEnv) SexpString(ps *PrintState) string {
|
||||
scop := (*Scope)(c)
|
||||
s, err := scop.Show(scop.env, ps, "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type SexpSentinel struct {
|
||||
Val int
|
||||
}
|
||||
|
||||
// these are values now so that they also have addresses.
|
||||
var SexpNull = &SexpSentinel{Val: 0}
|
||||
var SexpEnd = &SexpSentinel{Val: 1}
|
||||
var SexpMarker = &SexpSentinel{Val: 2}
|
||||
|
||||
type SexpSemicolon struct{}
|
||||
type SexpComma struct{}
|
||||
|
||||
func (r *SexpSemicolon) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func (s *SexpSemicolon) SexpString(ps *PrintState) string {
|
||||
return ";"
|
||||
}
|
||||
|
||||
func (r *SexpComma) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func (s *SexpComma) SexpString(ps *PrintState) string {
|
||||
return ","
|
||||
}
|
||||
|
||||
func (sent *SexpSentinel) SexpString(ps *PrintState) string {
|
||||
if sent == SexpNull {
|
||||
return "nil"
|
||||
}
|
||||
if sent == SexpEnd {
|
||||
return "End"
|
||||
}
|
||||
if sent == SexpMarker {
|
||||
return "Marker"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func Cons(a Sexp, b Sexp) *SexpPair {
|
||||
return &SexpPair{a, b}
|
||||
}
|
||||
|
||||
func (pair *SexpPair) SexpString(ps *PrintState) string {
|
||||
str := "("
|
||||
|
||||
for {
|
||||
switch pair.Tail.(type) {
|
||||
case *SexpPair:
|
||||
str += pair.Head.SexpString(ps) + " "
|
||||
pair = pair.Tail.(*SexpPair)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
str += pair.Head.SexpString(ps)
|
||||
|
||||
if pair.Tail == SexpNull {
|
||||
str += ")"
|
||||
} else {
|
||||
str += " \\ " + pair.Tail.SexpString(ps) + ")"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
func (r *SexpPair) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
type SexpArray struct {
|
||||
Val []Sexp
|
||||
|
||||
Typ *RegisteredType
|
||||
|
||||
IsFuncDeclTypeArray bool
|
||||
Infix bool
|
||||
|
||||
Env *Zlisp
|
||||
}
|
||||
|
||||
func (r *SexpArray) Type() *RegisteredType {
|
||||
if r.Typ == nil {
|
||||
if len(r.Val) > 0 {
|
||||
// take type from first element
|
||||
ty := r.Val[0].Type()
|
||||
if ty != nil {
|
||||
r.Typ = GoStructRegistry.GetOrCreateSliceType(ty)
|
||||
}
|
||||
} else {
|
||||
// empty array
|
||||
r.Typ = GoStructRegistry.Lookup("[]")
|
||||
//P("lookup [] returned type %#v", r.Typ)
|
||||
}
|
||||
}
|
||||
return r.Typ
|
||||
}
|
||||
|
||||
func (arr *SexpArray) SexpString(ps *PrintState) string {
|
||||
indInner := ""
|
||||
indent := ps.GetIndent()
|
||||
innerPs := ps.AddIndent(4) // generates a fresh new PrintState
|
||||
inner := indent + 4
|
||||
//prettyEnd := ""
|
||||
pretty := false
|
||||
if arr != nil && arr.Env != nil && arr.Env.Pretty {
|
||||
pretty = true
|
||||
//prettyEnd = "\n"
|
||||
indInner = strings.Repeat(" ", inner)
|
||||
ps = innerPs
|
||||
}
|
||||
|
||||
opn := "["
|
||||
cls := "]"
|
||||
if arr.Infix {
|
||||
opn = "{"
|
||||
cls = "}"
|
||||
}
|
||||
if pretty {
|
||||
opn += "\n"
|
||||
indInner = strings.Repeat(" ", inner)
|
||||
}
|
||||
|
||||
if len(arr.Val) == 0 {
|
||||
return opn + cls
|
||||
}
|
||||
ta := arr.IsFuncDeclTypeArray
|
||||
str := opn
|
||||
for i, sexp := range arr.Val {
|
||||
str += indInner + sexp.SexpString(ps)
|
||||
if ta && i%2 == 0 {
|
||||
str += ":"
|
||||
} else {
|
||||
str += " "
|
||||
}
|
||||
}
|
||||
m := len(str)
|
||||
str = str[:m-1] + indInner + cls
|
||||
return str
|
||||
}
|
||||
|
||||
func (e *SexpError) SexpString(ps *PrintState) string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
type EmbedPath struct {
|
||||
ChildName string
|
||||
ChildFieldNum int
|
||||
}
|
||||
|
||||
func GetEmbedPath(e []EmbedPath) string {
|
||||
r := ""
|
||||
last := len(e) - 1
|
||||
for i, s := range e {
|
||||
r += s.ChildName
|
||||
if i < last {
|
||||
r += ":"
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type HashFieldDet struct {
|
||||
FieldNum int
|
||||
FieldType reflect.Type
|
||||
StructField reflect.StructField
|
||||
FieldName string
|
||||
FieldJsonTag string
|
||||
EmbedPath []EmbedPath // we are embedded if len(EmbedPath) > 0
|
||||
}
|
||||
type SexpHash struct {
|
||||
TypeName string
|
||||
Map map[int][]*SexpPair
|
||||
KeyOrder []Sexp
|
||||
GoStructFactory *RegisteredType
|
||||
NumKeys int
|
||||
GoMethods []reflect.Method
|
||||
GoFields []reflect.StructField
|
||||
GoMethSx SexpArray
|
||||
GoFieldSx SexpArray
|
||||
GoType reflect.Type
|
||||
NumMethod int
|
||||
GoShadowStruct interface{}
|
||||
GoShadowStructVa reflect.Value
|
||||
ShadowSet bool
|
||||
|
||||
// json tag name -> pointers to example values, as factories for SexpToGoStructs()
|
||||
JsonTagMap map[string]*HashFieldDet
|
||||
DetOrder []*HashFieldDet
|
||||
|
||||
// for using these as a scoping model
|
||||
DefnEnv *SexpHash
|
||||
SuperClass *SexpHash
|
||||
ZMain SexpFunction
|
||||
ZMethods map[string]*SexpFunction
|
||||
Env *Zlisp
|
||||
}
|
||||
|
||||
var MethodNotFound = fmt.Errorf("method not found")
|
||||
|
||||
func (h *SexpHash) RunZmethod(method string, args []Sexp) (Sexp, error) {
|
||||
f, ok := (h.ZMethods)[method]
|
||||
if !ok {
|
||||
return SexpNull, MethodNotFound
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("not done calling %s", f.name))
|
||||
//return SexpNull, nil
|
||||
}
|
||||
|
||||
func CallZMethodOnRecordFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
narg := len(args)
|
||||
if narg < 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var hash *SexpHash
|
||||
switch h := args[0].(type) {
|
||||
case *SexpHash:
|
||||
hash = h
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("can only _call on a record")
|
||||
}
|
||||
|
||||
method := ""
|
||||
switch s := args[1].(type) {
|
||||
case *SexpSymbol:
|
||||
method = s.name
|
||||
case *SexpStr:
|
||||
method = s.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("can only _call with a " +
|
||||
"symbol or string as the method name. example: (_call record method:)")
|
||||
}
|
||||
|
||||
return hash.RunZmethod(method, args[2:])
|
||||
}
|
||||
|
||||
func (h *SexpHash) SetMain(p *SexpFunction) {
|
||||
h.BindSymbol(h.Env.MakeSymbol(".main"), p)
|
||||
}
|
||||
|
||||
func (h *SexpHash) SetDefnEnv(p *SexpHash) {
|
||||
h.DefnEnv = p
|
||||
h.BindSymbol(h.Env.MakeSymbol(".parent"), p)
|
||||
}
|
||||
|
||||
func (h *SexpHash) Lookup(env *Zlisp, key Sexp) (expr Sexp, err error) {
|
||||
return h.HashGet(env, key)
|
||||
}
|
||||
|
||||
func (h *SexpHash) BindSymbol(key *SexpSymbol, val Sexp) error {
|
||||
return h.HashSet(key, val)
|
||||
}
|
||||
|
||||
func (h *SexpHash) SetGoStructFactory(factory *RegisteredType) {
|
||||
h.GoStructFactory = factory
|
||||
}
|
||||
|
||||
var SexpIntSize = 64
|
||||
var SexpFloatSize = 64
|
||||
|
||||
func (r *SexpReflect) SexpString(ps *PrintState) string {
|
||||
Q("in SexpReflect.SexpString(indent); top; type = %T", r)
|
||||
if reflect.Value(r.Val).Type().Kind() == reflect.Ptr {
|
||||
iface := reflect.Value(r.Val).Interface()
|
||||
switch iface.(type) {
|
||||
case *string:
|
||||
return fmt.Sprintf("`%v`", reflect.Value(r.Val).Elem().Interface())
|
||||
default:
|
||||
return fmt.Sprintf("%v", reflect.Value(r.Val).Elem().Interface())
|
||||
}
|
||||
}
|
||||
iface := reflect.Value(r.Val).Interface()
|
||||
Q("in SexpReflect.SexpString(indent); type = %T", iface)
|
||||
switch iface.(type) {
|
||||
default:
|
||||
return fmt.Sprintf("%v", iface)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SexpBool) SexpString(ps *PrintState) string {
|
||||
if bool(b.Val) {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (i *SexpInt) SexpString(ps *PrintState) string {
|
||||
return strconv.Itoa(int(i.Val))
|
||||
}
|
||||
|
||||
func (i *SexpUint64) SexpString(ps *PrintState) string {
|
||||
return strconv.FormatUint(i.Val, 10) + "ULL"
|
||||
}
|
||||
|
||||
func (f *SexpFloat) SexpString(ps *PrintState) string {
|
||||
if f.Scientific {
|
||||
return strconv.FormatFloat(f.Val, 'e', -1, SexpFloatSize)
|
||||
}
|
||||
return strconv.FormatFloat(f.Val, 'f', -1, SexpFloatSize)
|
||||
}
|
||||
|
||||
func (c *SexpChar) SexpString(ps *PrintState) string {
|
||||
return strconv.QuoteRune(c.Val)
|
||||
}
|
||||
|
||||
func (s *SexpStr) SexpString(ps *PrintState) string {
|
||||
if s.backtick {
|
||||
return "`" + s.S + "`"
|
||||
}
|
||||
return strconv.Quote(string(s.S))
|
||||
}
|
||||
|
||||
func (r *SexpRaw) SexpString(ps *PrintState) string {
|
||||
return fmt.Sprintf("%#v", []byte(r.Val))
|
||||
}
|
||||
|
||||
type SexpSymbol struct {
|
||||
name string
|
||||
number int
|
||||
isDot bool
|
||||
isSigil bool
|
||||
colonTail bool
|
||||
sigil string
|
||||
}
|
||||
|
||||
func (sym *SexpSymbol) RHS(env *Zlisp) (Sexp, error) {
|
||||
if sym.isDot && env != nil {
|
||||
return dotGetSetHelper(env, sym.name, nil)
|
||||
}
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
func (sym *SexpSymbol) AssignToSelection(env *Zlisp, rhs Sexp) error {
|
||||
if sym.isDot && env != nil {
|
||||
_, err := dotGetSetHelper(env, sym.name, &rhs)
|
||||
return err
|
||||
}
|
||||
panic("not implemented yet")
|
||||
}
|
||||
|
||||
func (sym *SexpSymbol) SexpString(ps *PrintState) string {
|
||||
if sym.colonTail {
|
||||
// return sym.name + ":"
|
||||
}
|
||||
return sym.name
|
||||
}
|
||||
|
||||
func (r *SexpSymbol) Type() *RegisteredType {
|
||||
return GoStructRegistry.Registry["symbol"]
|
||||
}
|
||||
|
||||
func (sym SexpSymbol) Name() string {
|
||||
return sym.name
|
||||
}
|
||||
|
||||
func (sym SexpSymbol) Number() int {
|
||||
return sym.number
|
||||
}
|
||||
|
||||
// SexpInterfaceDecl
|
||||
type SexpInterfaceDecl struct {
|
||||
name string
|
||||
methods []*SexpFunction
|
||||
}
|
||||
|
||||
func (r *SexpInterfaceDecl) SexpString(ps *PrintState) string {
|
||||
indent := ps.GetIndent()
|
||||
space := strings.Repeat(" ", indent)
|
||||
space4 := strings.Repeat(" ", indent+4)
|
||||
s := space + "(interface " + r.name + " ["
|
||||
if len(r.methods) > 0 {
|
||||
s += "\n"
|
||||
}
|
||||
for i := range r.methods {
|
||||
s += space4 + r.methods[i].SexpString(ps.AddIndent(4)) + "\n"
|
||||
}
|
||||
s += space + "])"
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *SexpInterfaceDecl) Type() *RegisteredType {
|
||||
// todo: how to register/what to register?
|
||||
return GoStructRegistry.Registry[r.name]
|
||||
}
|
||||
|
||||
// SexpFunction
|
||||
type SexpFunction struct {
|
||||
name string
|
||||
user bool
|
||||
nargs int
|
||||
varargs bool
|
||||
fun ZlispFunction
|
||||
userfun ZlispUserFunction
|
||||
orig Sexp
|
||||
closingOverScopes *Closing
|
||||
parent *SexpFunction
|
||||
isBuilder bool // see defbuild; builders are builtins that receive un-evaluated expressions
|
||||
inputTypes *SexpHash
|
||||
returnTypes *SexpHash
|
||||
hasBody bool // could just be declaration in an interface, without a body
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) Type() *RegisteredType {
|
||||
return nil // TODO what goes here
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) Copy() *SexpFunction {
|
||||
cp := *sf
|
||||
return &cp
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) SetClosing(clos *Closing) {
|
||||
ps4 := NewPrintStateWithIndent(4)
|
||||
pre, err := sf.ShowClosing(clos.env, ps4, "prev")
|
||||
_ = pre
|
||||
panicOn(err)
|
||||
newnew, err := sf.ShowClosing(clos.env, ps4, "newnew")
|
||||
_ = newnew
|
||||
panicOn(err)
|
||||
//P("99999 for sfun = %p, in sfun.SetClosing(), prev value is %p = '%s'\n",
|
||||
// sf, sf.closingOverScopes, pre)
|
||||
//P("88888 in sfun.SetClosing(), new value is %p = '%s'\n", clos, newnew)
|
||||
sf.closingOverScopes = clos
|
||||
//P("in SetClosing() for '%s'/%p: my stack is: '%s'", sf.name, sf, clos.Stack.SexpString(nil))
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) ShowClosing(env *Zlisp, ps *PrintState, label string) (string, error) {
|
||||
if sf.closingOverScopes == nil {
|
||||
return sf.name + " has no captured scopes.", nil
|
||||
}
|
||||
return sf.closingOverScopes.Show(env, ps, label)
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) ClosingLookupSymbolUntilFunction(sym *SexpSymbol) (Sexp, error, *Scope) {
|
||||
if sf.closingOverScopes != nil {
|
||||
return sf.closingOverScopes.LookupSymbolUntilFunction(sym, nil, 1, false)
|
||||
}
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) ClosingLookupSymbol(sym *SexpSymbol, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
if sf.closingOverScopes != nil {
|
||||
return sf.closingOverScopes.LookupSymbol(sym, setVal)
|
||||
}
|
||||
//P("sf.closingOverScopes was nil, no captured scopes. sf = '%v'", sf.SexpString(nil))
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
// chase parent pointers up the chain and check each of their immediate closures.
|
||||
func (sf *SexpFunction) LookupSymbolInParentChainOfClosures(sym *SexpSymbol, setVal *Sexp, env *Zlisp) (Sexp, error, *Scope) {
|
||||
|
||||
cur := sf
|
||||
par := sf.parent
|
||||
for par != nil {
|
||||
//fmt.Printf(" parent chain: cur:%v -> parent:%v\n", cur.name, par.name)
|
||||
//fmt.Printf(" cur.closures = %s", ClosureToString(cur, env))
|
||||
|
||||
exp, err, scope := cur.ClosingLookupSymbolUntilFunc(sym, setVal, 1, false)
|
||||
if err == nil {
|
||||
//P("LookupSymbolInParentChainOfClosures(sym='%s') found in scope '%s'\n", sym.name, scope.Name)
|
||||
return exp, err, scope
|
||||
}
|
||||
|
||||
cur = par
|
||||
par = par.parent
|
||||
}
|
||||
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) ClosingLookupSymbolUntilFunc(sym *SexpSymbol, setVal *Sexp, maximumFuncToSearch int, checkCaptures bool) (Sexp, error, *Scope) {
|
||||
if sf.closingOverScopes != nil {
|
||||
return sf.closingOverScopes.LookupSymbolUntilFunction(sym, setVal, maximumFuncToSearch, checkCaptures)
|
||||
}
|
||||
//P("sf.closingOverScopes was nil, no captured scopes. sf = '%v'", sf.SexpString(nil))
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
func (sf *SexpFunction) SexpString(ps *PrintState) string {
|
||||
if sf.orig == nil {
|
||||
return "fn [" + sf.name + "]"
|
||||
}
|
||||
return sf.orig.SexpString(ps)
|
||||
}
|
||||
|
||||
func IsTruthy(expr Sexp) bool {
|
||||
switch e := expr.(type) {
|
||||
case *SexpBool:
|
||||
return e.Val
|
||||
case *SexpInt:
|
||||
return e.Val != 0
|
||||
case *SexpUint64:
|
||||
return e.Val != 0
|
||||
case *SexpChar:
|
||||
return e.Val != 0
|
||||
case *SexpSentinel:
|
||||
return e != SexpNull
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type SexpStackmark struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (r *SexpStackmark) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func (mark *SexpStackmark) SexpString(ps *PrintState) string {
|
||||
return "stackmark " + mark.sym.name
|
||||
}
|
||||
295
vendor/github.com/glycerine/zygomys/zygo/func.go
generated
vendored
Normal file
295
vendor/github.com/glycerine/zygomys/zygo/func.go
generated
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func FuncBuilder(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
useName := name
|
||||
isMethod := false
|
||||
if name == "method" {
|
||||
isMethod = true
|
||||
useName = "method [p: (* StructName)]"
|
||||
}
|
||||
|
||||
use := "use: (" + useName + " funcName [inputs:type ...] [returns:type ...])"
|
||||
|
||||
n := len(args)
|
||||
if n < 1 {
|
||||
return SexpNull, fmt.Errorf("missing arguments. %s", use)
|
||||
}
|
||||
|
||||
inputsLoc := 1
|
||||
returnsLoc := 2
|
||||
bodyLoc := 3
|
||||
isAnon := false
|
||||
|
||||
var symN *SexpSymbol
|
||||
switch b := args[0].(type) {
|
||||
case *SexpSymbol:
|
||||
symN = b
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(b)
|
||||
if isQuo {
|
||||
symN = sy.(*SexpSymbol)
|
||||
} else {
|
||||
return SexpNull, fmt.Errorf("bad func name: symbol required")
|
||||
}
|
||||
|
||||
case *SexpArray:
|
||||
if isMethod {
|
||||
ok := false
|
||||
symN, ok = args[1].(*SexpSymbol)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("bad method name: symbol required after receiver array")
|
||||
}
|
||||
inputsLoc++
|
||||
returnsLoc++
|
||||
bodyLoc++
|
||||
} else {
|
||||
// anonymous function
|
||||
symN = env.GenSymbol("__anon")
|
||||
isAnon = true
|
||||
inputsLoc--
|
||||
returnsLoc--
|
||||
bodyLoc--
|
||||
}
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad func name: symbol required")
|
||||
}
|
||||
Q("good: have func name '%v'", symN.name)
|
||||
funcName := symN.name
|
||||
|
||||
builtin, builtTyp := env.IsBuiltinSym(symN)
|
||||
if builtin {
|
||||
return SexpNull,
|
||||
fmt.Errorf("already have %s '%s', refusing to overwrite with defn",
|
||||
builtTyp, symN.name)
|
||||
}
|
||||
|
||||
if env.HasMacro(symN) {
|
||||
return SexpNull, fmt.Errorf("Already have macro named '%s': refusing"+
|
||||
" to define function of same name.", symN.name)
|
||||
}
|
||||
|
||||
if n < inputsLoc+1 {
|
||||
return SexpNull, fmt.Errorf("func [inputs] array is missing. %s", use)
|
||||
}
|
||||
|
||||
if n < returnsLoc+1 {
|
||||
return SexpNull, fmt.Errorf("func [returns] array is missing. %s", use)
|
||||
}
|
||||
|
||||
var inputs *SexpArray
|
||||
switch ar := args[inputsLoc].(type) {
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad func declaration '%v': "+
|
||||
"expected array of input declarations after the name. %s", funcName, use)
|
||||
case *SexpArray:
|
||||
inputs = ar
|
||||
inputs.IsFuncDeclTypeArray = true
|
||||
}
|
||||
|
||||
var returns *SexpArray
|
||||
switch ar := args[returnsLoc].(type) {
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("bad func declaration '%v': third argument "+
|
||||
"must be a array of return declarations. %s", funcName, use)
|
||||
case *SexpArray:
|
||||
returns = ar
|
||||
returns.IsFuncDeclTypeArray = true
|
||||
}
|
||||
|
||||
body := args[bodyLoc:]
|
||||
|
||||
Q("in func builder, args = ")
|
||||
for i := range args {
|
||||
Q("args[%v] = '%s'", i, args[i].SexpString(nil))
|
||||
}
|
||||
Q("in func builder, isAnon = %v", isAnon)
|
||||
Q("in func builder, inputs = %v", inputs.SexpString(nil))
|
||||
Q("in func builder, returns = %v", returns.SexpString(nil))
|
||||
Q("in func builder, body = %v", (&SexpArray{Val: body, Env: env}).SexpString(nil))
|
||||
|
||||
inHash, err := GetFuncArgArray(inputs, env, "inputs")
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("inputs array parsing error: %v", err)
|
||||
}
|
||||
Q("inHash = '%v'", inHash.SexpString(nil))
|
||||
|
||||
retHash, err := GetFuncArgArray(returns, env, "returns")
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("returns array parsing error: %v", err)
|
||||
}
|
||||
Q("retHash = '%v'", retHash.SexpString(nil))
|
||||
|
||||
env.datastack.PushExpr(SexpNull)
|
||||
|
||||
Q("FuncBuilder() about to call buildSexpFun")
|
||||
|
||||
// ===================================
|
||||
// ===================================
|
||||
//
|
||||
// from buildSexpFun, adapted
|
||||
//
|
||||
// todo: type checking the inputs and handling the returns as well
|
||||
//
|
||||
// ===================================
|
||||
// ===================================
|
||||
|
||||
// sfun, err := buildSexpFun(env, symN.name, funcargs, body, orig)
|
||||
|
||||
//orig := &SexpArray{Val: args, Env: env}
|
||||
origa := []Sexp{env.MakeSymbol("func")}
|
||||
origa = append(origa, args...)
|
||||
orig := MakeList(origa)
|
||||
|
||||
funcargs := inHash.KeyOrder
|
||||
|
||||
gen := NewGenerator(env)
|
||||
gen.Tail = true
|
||||
|
||||
gen.funcname = funcName
|
||||
|
||||
afsHelper := &AddFuncScopeHelper{}
|
||||
gen.AddInstruction(AddFuncScopeInstr{Name: "runtime " + gen.funcname, Helper: afsHelper})
|
||||
|
||||
argsyms := make([]*SexpSymbol, len(funcargs))
|
||||
|
||||
// copy
|
||||
for i := range funcargs {
|
||||
argsyms[i] = funcargs[i].(*SexpSymbol)
|
||||
}
|
||||
|
||||
varargs := false
|
||||
nargs := len(funcargs)
|
||||
|
||||
if len(argsyms) >= 2 && argsyms[len(argsyms)-2].name == "&" {
|
||||
argsyms[len(argsyms)-2] = argsyms[len(argsyms)-1]
|
||||
argsyms = argsyms[0 : len(argsyms)-1]
|
||||
varargs = true
|
||||
nargs = len(argsyms) - 1
|
||||
}
|
||||
|
||||
VPrintf("\n in buildSexpFun(): DumpFunction just before %v args go onto stack\n",
|
||||
len(argsyms))
|
||||
if Working {
|
||||
DumpFunction(ZlispFunction(gen.instructions), -1)
|
||||
}
|
||||
for i := len(argsyms) - 1; i >= 0; i-- {
|
||||
gen.AddInstruction(PopStackPutEnvInstr{argsyms[i]})
|
||||
}
|
||||
err = gen.GenerateBegin(body)
|
||||
if err != nil {
|
||||
return MissingFunction, err
|
||||
}
|
||||
|
||||
// minimal sanity check that we return the number of arguments
|
||||
// on the stack that are declared
|
||||
if len(body) == 0 {
|
||||
for range retHash.KeyOrder {
|
||||
gen.AddInstruction(PushInstr{expr: SexpNull})
|
||||
}
|
||||
}
|
||||
|
||||
gen.AddInstruction(RemoveScopeInstr{})
|
||||
gen.AddInstruction(ReturnInstr{nil}) // nil is the error returned
|
||||
|
||||
newfunc := ZlispFunction(gen.instructions)
|
||||
sfun := gen.env.MakeFunction(gen.funcname, nargs,
|
||||
varargs, newfunc, orig)
|
||||
sfun.inputTypes = inHash
|
||||
sfun.returnTypes = retHash
|
||||
|
||||
// tell the function scope where their function is, to
|
||||
// provide access to the captured-closure scopes at runtime.
|
||||
afsHelper.MyFunction = sfun
|
||||
|
||||
clos := CreateClosureInstr{sfun}
|
||||
notePc := env.pc
|
||||
clos.Execute(env)
|
||||
|
||||
invok, err := env.datastack.PopExpr()
|
||||
panicOn(err) // we just pushed in the clos.Execute(), so this should always be err == nil
|
||||
env.pc = notePc
|
||||
err = env.LexicalBindSymbol(symN, invok)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("internal error: could not bind symN:'%s' into env: %v", symN.name, err)
|
||||
}
|
||||
|
||||
if len(body) > 0 {
|
||||
invok.(*SexpFunction).hasBody = true
|
||||
}
|
||||
return invok, nil
|
||||
}
|
||||
|
||||
func GetFuncArgArray(arr *SexpArray, env *Zlisp, where string) (*SexpHash, error) {
|
||||
ar := arr.Val
|
||||
n := len(ar)
|
||||
hash, err := MakeHash([]Sexp{}, "hash", env)
|
||||
panicOn(err)
|
||||
if n == 0 {
|
||||
return hash, nil
|
||||
}
|
||||
if n%2 != 0 {
|
||||
return nil, fmt.Errorf("func definintion's %s array must have an even number of elements (each name:type pair counts as two)", where)
|
||||
}
|
||||
|
||||
for i := 0; i < n; i += 2 {
|
||||
name := ar[i]
|
||||
typ := ar[i+1]
|
||||
|
||||
//P("name = %#v", name)
|
||||
//P("typ = %#v", typ)
|
||||
|
||||
var symN *SexpSymbol
|
||||
switch b := name.(type) {
|
||||
case *SexpSymbol:
|
||||
symN = b
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(b)
|
||||
if isQuo {
|
||||
symN = sy.(*SexpSymbol)
|
||||
} else {
|
||||
return nil, fmt.Errorf("bad formal parameter name: symbol required in %s array, not a symbol: '%s'",
|
||||
where, b.SexpString(nil))
|
||||
}
|
||||
}
|
||||
|
||||
var symTyp *SexpSymbol
|
||||
switch b := typ.(type) {
|
||||
case *SexpSymbol:
|
||||
symTyp = b
|
||||
case *SexpPair:
|
||||
sy, isQuo := isQuotedSymbol(b)
|
||||
if isQuo {
|
||||
symTyp = sy.(*SexpSymbol)
|
||||
} else {
|
||||
return nil, fmt.Errorf("bad formal parameter type: type required in %s array, but found '%s'",
|
||||
where, b.SexpString(nil))
|
||||
}
|
||||
}
|
||||
|
||||
//P("here is env.ShowGlobalStack():")
|
||||
//env.ShowGlobalStack()
|
||||
|
||||
//P("symN = '%s'", symN.SexpString(nil))
|
||||
//P("symTyp = '%s'", symTyp.SexpString(nil))
|
||||
|
||||
r, err, _ := env.LexicalLookupSymbol(symTyp, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not identify type %s: %v", symTyp.SexpString(nil), err)
|
||||
}
|
||||
switch rt := r.(type) {
|
||||
case *RegisteredType:
|
||||
// good, store it
|
||||
hash.HashSet(symN, rt)
|
||||
default:
|
||||
return nil, fmt.Errorf("'%s' is not a known type", symTyp.SexpString(nil))
|
||||
}
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
1787
vendor/github.com/glycerine/zygomys/zygo/functions.go
generated
vendored
Normal file
1787
vendor/github.com/glycerine/zygomys/zygo/functions.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1498
vendor/github.com/glycerine/zygomys/zygo/generator.go
generated
vendored
Normal file
1498
vendor/github.com/glycerine/zygomys/zygo/generator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendor/github.com/glycerine/zygomys/zygo/gitcommit.go
generated
vendored
Normal file
2
vendor/github.com/glycerine/zygomys/zygo/gitcommit.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
package zygo
|
||||
func init() { GITLASTTAG = "v5.1.1"; GITLASTCOMMIT = "acef8bb25d1cad8aebed3cedb59b481795ac9fa1" }
|
||||
59
vendor/github.com/glycerine/zygomys/zygo/gob.go
generated
vendored
Normal file
59
vendor/github.com/glycerine/zygomys/zygo/gob.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func GobEncodeFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
h, isHash := args[0].(*SexpHash)
|
||||
if !isHash {
|
||||
return SexpNull, fmt.Errorf("gob argument must be a hash or defmap")
|
||||
}
|
||||
|
||||
// fill the go shadow struct
|
||||
_, err := ToGoFunction(env, "togo", []Sexp{h})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error converting object to Go struct: '%s'", err)
|
||||
}
|
||||
|
||||
// serialize to gob
|
||||
var gobBytes bytes.Buffer
|
||||
|
||||
enc := gob.NewEncoder(&gobBytes)
|
||||
err = enc.Encode(h.GoShadowStruct)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("gob encode error: '%s'", err)
|
||||
}
|
||||
|
||||
return &SexpRaw{Val: gobBytes.Bytes()}, nil
|
||||
}
|
||||
|
||||
func GobDecodeFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
raw, isRaw := args[0].(*SexpRaw)
|
||||
if !isRaw {
|
||||
return SexpNull, fmt.Errorf("ungob argument must be raw []byte")
|
||||
}
|
||||
|
||||
rawBuf := bytes.NewBuffer(raw.Val)
|
||||
dec := gob.NewDecoder(rawBuf)
|
||||
var iface interface{}
|
||||
err := dec.Decode(iface)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("gob decode error: '%s'", err)
|
||||
}
|
||||
|
||||
// TODO convert to hash
|
||||
panic("not done yet!")
|
||||
|
||||
//return SexpNull, nil
|
||||
}
|
||||
472
vendor/github.com/glycerine/zygomys/zygo/gotypereg.go
generated
vendored
Normal file
472
vendor/github.com/glycerine/zygomys/zygo/gotypereg.go
generated
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The Go Type Registry
|
||||
// ====================
|
||||
//
|
||||
// simply decide upon a name, and add a maker
|
||||
// function for that returns a pointer to your struct.
|
||||
// The simply add to the init() function below.
|
||||
//
|
||||
// The env parameter to your MakeGoStructFunc()
|
||||
// function is there is case you want to initialize
|
||||
// your struct differently depending on the content
|
||||
// of its context, but this is not commonly needed.
|
||||
// Also, the factory method *must* support the
|
||||
// env parameter being nil and still return a
|
||||
// sensible, usable value. The factory will be called
|
||||
// with env = nil during init() time.
|
||||
//
|
||||
// The repl will automatically do a (defmap record)
|
||||
// for each record defined in the registry. e.g.
|
||||
// for snoopy, hornet, hellcat, etc.
|
||||
//
|
||||
var GoStructRegistry GoStructRegistryType
|
||||
|
||||
// the registry type
|
||||
type GoStructRegistryType struct {
|
||||
// comprehensive
|
||||
Registry map[string]*RegisteredType
|
||||
|
||||
// only init-time builtins
|
||||
Builtin map[string]*RegisteredType
|
||||
|
||||
// later, user-defined types
|
||||
Userdef map[string]*RegisteredType
|
||||
}
|
||||
|
||||
// consistently ordered list of all registered types (created at init time).
|
||||
var ListRegisteredTypes = []string{}
|
||||
|
||||
func (r *GoStructRegistryType) RegisterBuiltin(name string, e *RegisteredType) {
|
||||
r.register(name, e, false)
|
||||
e.IsUser = false
|
||||
}
|
||||
|
||||
func (r *GoStructRegistryType) RegisterPointer(pointedToName string, pointedToType *RegisteredType) *RegisteredType {
|
||||
newRT := &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
p, err := pointedToType.Factory(env, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}}
|
||||
r.register(fmt.Sprintf("(* %s)", pointedToName), newRT, false)
|
||||
newRT.IsPointer = true
|
||||
return newRT
|
||||
}
|
||||
|
||||
func (r *GoStructRegistryType) register(name string, e *RegisteredType, isUser bool) {
|
||||
if !e.initDone {
|
||||
e.Init()
|
||||
}
|
||||
e.RegisteredName = name
|
||||
e.Aliases[name] = true
|
||||
e.Aliases[e.ReflectName] = true
|
||||
|
||||
_, found := r.Registry[name]
|
||||
if !found {
|
||||
ListRegisteredTypes = append(ListRegisteredTypes, name)
|
||||
}
|
||||
_, found2 := r.Registry[e.ReflectName]
|
||||
if !found2 {
|
||||
ListRegisteredTypes = append(ListRegisteredTypes, e.ReflectName)
|
||||
}
|
||||
|
||||
if isUser {
|
||||
r.Userdef[name] = e
|
||||
} else {
|
||||
r.Builtin[name] = e
|
||||
}
|
||||
r.Registry[name] = e
|
||||
r.Registry[e.ReflectName] = e
|
||||
}
|
||||
|
||||
func (e *RegisteredType) Init() {
|
||||
e.Aliases = make(map[string]bool)
|
||||
val, err := e.Factory(nil, nil)
|
||||
panicOn(err)
|
||||
if val != nil {
|
||||
e.ValueCache = reflect.ValueOf(val)
|
||||
e.TypeCache = e.ValueCache.Type()
|
||||
e.PointerName = fmt.Sprintf("%T", val)
|
||||
e.ReflectName = e.PointerName[1:] // todo: make this conditional on whether PointerName starts with '*'.
|
||||
e.DisplayAs = e.ReflectName
|
||||
}
|
||||
e.initDone = true
|
||||
}
|
||||
|
||||
func reflectName(val reflect.Value) string {
|
||||
pointerName := fmt.Sprintf("%T", val.Interface())
|
||||
reflectName := pointerName[1:]
|
||||
return reflectName
|
||||
}
|
||||
func ifaceName(val interface{}) string {
|
||||
pointerName := fmt.Sprintf("%T", val)
|
||||
reflectName := pointerName[1:]
|
||||
return reflectName
|
||||
}
|
||||
|
||||
func (r *GoStructRegistryType) RegisterUserdef(
|
||||
e *RegisteredType,
|
||||
hasShadowStruct bool,
|
||||
names ...string) {
|
||||
|
||||
for i, name := range names {
|
||||
e0 := e
|
||||
if i > 0 {
|
||||
// make a copy of the RegisteredType for each name, so all names are kept.
|
||||
// Otherwise we overwrite the DisplayAs below.
|
||||
rt := *e
|
||||
e0 = &rt
|
||||
}
|
||||
r.register(name, e0, true)
|
||||
e0.IsUser = true
|
||||
e0.hasShadowStruct = hasShadowStruct
|
||||
|
||||
e0.Constructor = MakeUserFunction("__struct_"+name, StructConstructorFunction)
|
||||
if e0.DisplayAs == "" {
|
||||
e0.DisplayAs = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GoStructRegistryType) Lookup(name string) *RegisteredType {
|
||||
return r.Registry[name]
|
||||
}
|
||||
|
||||
// the type of all maker functions
|
||||
|
||||
type MakeGoStructFunc func(env *Zlisp, h *SexpHash) (interface{}, error)
|
||||
|
||||
var NullRT *RegisteredType
|
||||
var PairRT *RegisteredType
|
||||
var Int64RT *RegisteredType
|
||||
var BoolRT *RegisteredType
|
||||
var RuneRT *RegisteredType
|
||||
var Float64RT *RegisteredType
|
||||
var RawRT *RegisteredType
|
||||
var ReflectRT *RegisteredType
|
||||
var ErrorRT *RegisteredType
|
||||
var SentinelRT *RegisteredType
|
||||
var ClosureRT *RegisteredType
|
||||
var ArraySelectorRT *RegisteredType
|
||||
|
||||
type RegisteredType struct {
|
||||
initDone bool
|
||||
hasShadowStruct bool
|
||||
|
||||
Constructor *SexpFunction
|
||||
RegisteredName string
|
||||
Factory MakeGoStructFunc
|
||||
GenDefMap bool
|
||||
ValueCache reflect.Value
|
||||
TypeCache reflect.Type
|
||||
PointerName string
|
||||
ReflectName string
|
||||
IsUser bool
|
||||
Aliases map[string]bool
|
||||
DisplayAs string
|
||||
UserStructDefn *RecordDefn
|
||||
IsPointer bool
|
||||
}
|
||||
|
||||
func (p *RegisteredType) TypeCheckRecord(hash *SexpHash) error {
|
||||
Q("in RegisteredType.TypeCheckRecord(hash = '%v')", hash.SexpString(nil))
|
||||
if hash.TypeName == "field" {
|
||||
Q("in RegisteredType.TypeCheckRecord, TypeName == field, skipping.")
|
||||
return nil
|
||||
}
|
||||
if p.UserStructDefn != nil {
|
||||
Q("in RegisteredType.TypeCheckRecord, type checking against '%#v'", p.UserStructDefn)
|
||||
|
||||
var err error
|
||||
for _, key := range hash.KeyOrder {
|
||||
obs, _ := hash.HashGet(nil, key)
|
||||
err = hash.TypeCheckField(key, obs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *RegisteredType) SexpString(ps *PrintState) string {
|
||||
if p == nil {
|
||||
return "nil RegisteredType"
|
||||
}
|
||||
if p.UserStructDefn != nil {
|
||||
return p.UserStructDefn.SexpString(ps)
|
||||
}
|
||||
return p.DisplayAs
|
||||
}
|
||||
|
||||
func (p *RegisteredType) ShortName() string {
|
||||
if p.UserStructDefn != nil {
|
||||
return p.UserStructDefn.Name
|
||||
}
|
||||
return p.DisplayAs
|
||||
}
|
||||
|
||||
func NewRegisteredType(f MakeGoStructFunc) *RegisteredType {
|
||||
rt := &RegisteredType{Factory: f}
|
||||
rt.Init()
|
||||
return rt
|
||||
}
|
||||
|
||||
// builtin known Go Structs
|
||||
// NB these are used to test the functionality of the
|
||||
// Go integration.
|
||||
//
|
||||
func init() {
|
||||
GoStructRegistry = GoStructRegistryType{
|
||||
Registry: make(map[string]*RegisteredType),
|
||||
Builtin: make(map[string]*RegisteredType),
|
||||
Userdef: make(map[string]*RegisteredType),
|
||||
}
|
||||
|
||||
gsr := &GoStructRegistry
|
||||
|
||||
// add go builtin types
|
||||
// ====================
|
||||
|
||||
// empty array
|
||||
gsr.RegisterBuiltin("[]", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &SexpArray{}, nil
|
||||
}})
|
||||
|
||||
// scope, as used by the package operation
|
||||
gsr.RegisterBuiltin("packageScope", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
pkg := env.NewScope()
|
||||
pkg.Name = "prototype"
|
||||
pkg.IsPackage = true
|
||||
return pkg, nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("packageScopeStack", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
pkg := env.NewStack(0)
|
||||
pkg.Name = "prototypePackageScopeStack"
|
||||
pkg.IsPackage = true
|
||||
return pkg, nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("arraySelector", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &SexpArraySelector{}, nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("hashSelector", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &SexpHashSelector{}, nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("comment",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return SexpNull, nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("byte",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(byte), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("uint8",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(byte), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("int",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("uint16",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(uint16), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("uint32",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(uint32), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("uint64",
|
||||
&RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(uint64), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("int8", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int8), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("int16", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int16), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("int32", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int32), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("rune", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int32), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("int64", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(int64), nil
|
||||
}})
|
||||
gsr.RegisterBuiltin("float32", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(float32), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("float64", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(float64), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("complex64", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(complex64), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("complex128", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(complex128), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("bool", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(bool), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("string", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(string), nil
|
||||
}})
|
||||
|
||||
gsr.RegisterBuiltin("time.Time", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(time.Time), nil
|
||||
}})
|
||||
|
||||
// add Sexp types
|
||||
|
||||
gsr.RegisterBuiltin("symbol", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &SexpSymbol{}, nil
|
||||
}})
|
||||
|
||||
/* either:
|
||||
|
||||
gsr.RegisterBuiltin("time.Time", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return new(time.Time), nil
|
||||
}})
|
||||
*/
|
||||
|
||||
// PairRT *RegisteredType
|
||||
// RawRT *RegisteredType
|
||||
// ReflectRT *RegisteredType
|
||||
// ErrorRT *RegisteredType
|
||||
gsr.RegisterBuiltin("error", &RegisteredType{GenDefMap: false, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
var err error
|
||||
return &err, nil
|
||||
}})
|
||||
|
||||
// SentinelRT *RegisteredType
|
||||
// ClosureRT *RegisteredType
|
||||
}
|
||||
|
||||
func TypeListFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
narg := len(args)
|
||||
if narg != 0 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
r := ListRegisteredTypes
|
||||
s := make([]Sexp, len(r))
|
||||
for i := range r {
|
||||
s[i] = &SexpStr{S: r[i]}
|
||||
}
|
||||
return env.NewSexpArray(s), nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportBaseTypes() {
|
||||
for _, e := range GoStructRegistry.Builtin {
|
||||
env.AddGlobal(e.RegisteredName, e)
|
||||
}
|
||||
|
||||
for _, e := range GoStructRegistry.Userdef {
|
||||
env.AddGlobal(e.RegisteredName, e)
|
||||
}
|
||||
}
|
||||
|
||||
func compareRegisteredTypes(a *RegisteredType, bs Sexp) (int, error) {
|
||||
|
||||
var b *RegisteredType
|
||||
switch bt := bs.(type) {
|
||||
case *RegisteredType:
|
||||
b = bt
|
||||
default:
|
||||
return 0, fmt.Errorf("cannot compare %T to %T", a, bs)
|
||||
}
|
||||
|
||||
if a == b {
|
||||
// equal for sure
|
||||
return 0, nil
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (gsr *GoStructRegistryType) GetOrCreatePointerType(pointedToType *RegisteredType) *RegisteredType {
|
||||
Q("pointedToType = %#v", pointedToType)
|
||||
ptrName := "*" + pointedToType.RegisteredName
|
||||
ptrRt := gsr.Lookup(ptrName)
|
||||
if ptrRt != nil {
|
||||
Q("type named '%v' already registered, reusing the pointer type", ptrName)
|
||||
} else {
|
||||
Q("registering new pointer type '%v'", ptrName)
|
||||
derivedType := reflect.PtrTo(pointedToType.TypeCache)
|
||||
ptrRt = NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return reflect.New(derivedType), nil
|
||||
})
|
||||
ptrRt.DisplayAs = fmt.Sprintf("(* %s)", pointedToType.DisplayAs)
|
||||
ptrRt.RegisteredName = ptrName
|
||||
gsr.RegisterUserdef(ptrRt, false, ptrName)
|
||||
}
|
||||
return ptrRt
|
||||
}
|
||||
|
||||
func (gsr *GoStructRegistryType) GetOrCreateSliceType(rt *RegisteredType) *RegisteredType {
|
||||
//sliceName := "sliceOf" + rt.RegisteredName
|
||||
sliceName := "[]" + rt.RegisteredName
|
||||
sliceRt := gsr.Lookup(sliceName)
|
||||
if sliceRt != nil {
|
||||
Q("type named '%v' already registered, re-using the type", sliceName)
|
||||
} else {
|
||||
Q("registering new slice type '%v'", sliceName)
|
||||
derivedType := reflect.SliceOf(rt.TypeCache)
|
||||
sliceRt = NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return reflect.MakeSlice(derivedType, 0, 0), nil
|
||||
})
|
||||
sliceRt.DisplayAs = fmt.Sprintf("(%s)", sliceName)
|
||||
sliceRt.RegisteredName = sliceName
|
||||
gsr.RegisterUserdef(sliceRt, false, sliceName)
|
||||
}
|
||||
return sliceRt
|
||||
}
|
||||
|
||||
func RegisterDemoStructs() {
|
||||
|
||||
gsr := &GoStructRegistry
|
||||
|
||||
// demo and user defined structs
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Event{}, nil
|
||||
}}, true, "eventdemo")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Person{}, nil
|
||||
}}, true, "persondemo")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Snoopy{}, nil
|
||||
}}, true, "snoopy")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Hornet{}, nil
|
||||
}}, true, "hornet")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Hellcat{}, nil
|
||||
}}, true, "hellcat")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Weather{}, nil
|
||||
}}, true, "weather")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &Plane{}, nil
|
||||
}}, true, "plane")
|
||||
gsr.RegisterUserdef(&RegisteredType{GenDefMap: true, Factory: func(env *Zlisp, h *SexpHash) (interface{}, error) {
|
||||
return &SetOfPlanes{}, nil
|
||||
}}, true, "setOfPlanes")
|
||||
}
|
||||
1155
vendor/github.com/glycerine/zygomys/zygo/hashutils.go
generated
vendored
Normal file
1155
vendor/github.com/glycerine/zygomys/zygo/hashutils.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
vendor/github.com/glycerine/zygomys/zygo/import.go
generated
vendored
Normal file
67
vendor/github.com/glycerine/zygomys/zygo/import.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// import a package, analagous to Golang.
|
||||
func ImportPackageBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
//P("starting ImportPackageBuilder")
|
||||
n := len(args)
|
||||
if n != 1 && n != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var path Sexp
|
||||
var alias string
|
||||
|
||||
switch n {
|
||||
case 1:
|
||||
path = args[0]
|
||||
case 2:
|
||||
path = args[1]
|
||||
//P("import debug: alias position at args[0] is '%#v'", args[0])
|
||||
switch sy := args[0].(type) {
|
||||
case *SexpSymbol:
|
||||
//P("import debug: alias is symbol, ok: '%v'", sy.name)
|
||||
alias = sy.name
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("import error: alias was not a symbol name")
|
||||
}
|
||||
}
|
||||
|
||||
var pth string
|
||||
switch x := path.(type) {
|
||||
case *SexpStr:
|
||||
pth = x.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("import error: path argument must be string")
|
||||
}
|
||||
if !FileExists(pth) {
|
||||
return SexpNull, fmt.Errorf("import error: path '%s' does not exist", pth)
|
||||
}
|
||||
|
||||
pkg, err := SourceFileFunction(env, "source", []Sexp{path})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("import error: attempt to import path '%s' resulted in: '%s'", pth, err)
|
||||
}
|
||||
//P("pkg = '%#v'", pkg)
|
||||
|
||||
asPkg, isPkg := pkg.(*Stack)
|
||||
if !isPkg || !asPkg.IsPackage {
|
||||
return SexpNull, fmt.Errorf("import error: attempt to import path '%s' resulted value that was not a package, but rather '%T'", pth, pkg)
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
alias = asPkg.PackageName
|
||||
}
|
||||
//P("using alias = '%s'", alias)
|
||||
|
||||
// now set alias in the current env
|
||||
err = env.LexicalBindSymbol(env.MakeSymbol(alias), asPkg)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
return pkg, nil
|
||||
}
|
||||
1160
vendor/github.com/glycerine/zygomys/zygo/jsonmsgp.go
generated
vendored
Normal file
1160
vendor/github.com/glycerine/zygomys/zygo/jsonmsgp.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
860
vendor/github.com/glycerine/zygomys/zygo/lexer.go
generated
vendored
Normal file
860
vendor/github.com/glycerine/zygomys/zygo/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,860 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type TokenType int
|
||||
|
||||
const (
|
||||
TokenTypeEmpty TokenType = iota
|
||||
TokenLParen
|
||||
TokenRParen
|
||||
TokenLSquare
|
||||
TokenRSquare
|
||||
TokenLCurly
|
||||
TokenRCurly
|
||||
TokenDot
|
||||
TokenQuote
|
||||
TokenBacktick
|
||||
TokenTilde
|
||||
TokenTildeAt
|
||||
TokenSymbol
|
||||
TokenBool
|
||||
TokenDecimal
|
||||
TokenHex
|
||||
TokenOct
|
||||
TokenBinary
|
||||
TokenFloat
|
||||
TokenChar
|
||||
TokenString
|
||||
TokenCaret
|
||||
TokenColonOperator
|
||||
TokenThreadingOperator
|
||||
TokenBackslash
|
||||
TokenDollar
|
||||
TokenDotSymbol
|
||||
TokenFreshAssign
|
||||
TokenBeginBacktickString
|
||||
TokenBacktickString
|
||||
TokenComment
|
||||
TokenBeginBlockComment
|
||||
TokenEndBlockComment
|
||||
TokenSemicolon
|
||||
TokenSymbolColon
|
||||
TokenComma
|
||||
TokenUint64
|
||||
TokenEnd
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
typ TokenType
|
||||
str string
|
||||
}
|
||||
|
||||
var EndTk = Token{typ: TokenEnd}
|
||||
|
||||
func (t Token) String() string {
|
||||
switch t.typ {
|
||||
case TokenLParen:
|
||||
return "("
|
||||
case TokenRParen:
|
||||
return ")"
|
||||
case TokenLSquare:
|
||||
return "["
|
||||
case TokenRSquare:
|
||||
return "]"
|
||||
case TokenLCurly:
|
||||
return "{"
|
||||
case TokenRCurly:
|
||||
return "}"
|
||||
case TokenDot:
|
||||
return t.str
|
||||
case TokenQuote:
|
||||
return "'"
|
||||
case TokenBacktick:
|
||||
return "`"
|
||||
case TokenCaret:
|
||||
return "^"
|
||||
case TokenTilde:
|
||||
return "~"
|
||||
case TokenTildeAt:
|
||||
return "~@"
|
||||
case TokenHex:
|
||||
return "0x" + t.str
|
||||
case TokenOct:
|
||||
return "0o" + t.str
|
||||
case TokenBinary:
|
||||
return "0b" + t.str
|
||||
case TokenChar:
|
||||
return strconv.Quote(t.str)
|
||||
case TokenColonOperator:
|
||||
return ":"
|
||||
case TokenThreadingOperator:
|
||||
return "->"
|
||||
case TokenBackslash:
|
||||
return "\\"
|
||||
case TokenDollar:
|
||||
return "$"
|
||||
}
|
||||
return t.str
|
||||
}
|
||||
|
||||
type LexerState int
|
||||
|
||||
const (
|
||||
LexerNormal LexerState = iota
|
||||
LexerCommentLine //
|
||||
LexerStrLit //
|
||||
LexerStrEscaped //
|
||||
LexerUnquote //
|
||||
LexerBacktickString //
|
||||
LexerFreshAssignOrColon
|
||||
LexerFirstFwdSlash // could be start of // comment or /*
|
||||
LexerCommentBlock
|
||||
LexerCommentBlockAsterisk // could be end of block comment */
|
||||
LexerBuiltinOperator
|
||||
LexerRuneLit
|
||||
LexerRuneEscaped
|
||||
)
|
||||
|
||||
type Lexer struct {
|
||||
parser *Parser
|
||||
state LexerState
|
||||
prevrune rune
|
||||
tokens []Token
|
||||
buffer *bytes.Buffer
|
||||
|
||||
prevToken Token
|
||||
prevPrevToken Token
|
||||
stream io.RuneScanner
|
||||
next []io.RuneScanner
|
||||
linenum int
|
||||
|
||||
priori int
|
||||
priorRune [20]rune
|
||||
}
|
||||
|
||||
func (lexer *Lexer) AppendToken(tok Token) {
|
||||
lexer.tokens = append(lexer.tokens, tok)
|
||||
lexer.prevPrevToken = lexer.prevToken
|
||||
lexer.prevToken = tok
|
||||
}
|
||||
|
||||
func (lexer *Lexer) PrependToken(tok Token) {
|
||||
lexer.tokens = append([]Token{tok}, lexer.tokens...)
|
||||
}
|
||||
|
||||
//helper
|
||||
func (lexer *Lexer) twoback() rune {
|
||||
pen := lexer.priori - 2
|
||||
if pen < 0 {
|
||||
pen = len(lexer.priorRune) + pen
|
||||
}
|
||||
pr := lexer.priorRune[pen]
|
||||
return pr
|
||||
}
|
||||
|
||||
func NewLexer(p *Parser) *Lexer {
|
||||
return &Lexer{
|
||||
parser: p,
|
||||
tokens: make([]Token, 0, 10),
|
||||
buffer: new(bytes.Buffer),
|
||||
state: LexerNormal,
|
||||
linenum: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (lexer *Lexer) Linenum() int {
|
||||
return lexer.linenum
|
||||
}
|
||||
|
||||
func (lex *Lexer) Reset() {
|
||||
lex.stream = nil
|
||||
lex.tokens = lex.tokens[:0]
|
||||
lex.state = LexerNormal
|
||||
lex.linenum = 1
|
||||
lex.buffer.Reset()
|
||||
}
|
||||
|
||||
func (lex *Lexer) EmptyToken() Token {
|
||||
return Token{}
|
||||
}
|
||||
|
||||
func (lex *Lexer) Token(typ TokenType, str string) Token {
|
||||
t := Token{
|
||||
typ: typ,
|
||||
str: str,
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var (
|
||||
BoolRegex = regexp.MustCompile("^(true|false)$")
|
||||
Uint64Regex = regexp.MustCompile("^(0x|0o)?[0-9a-fA-F]+ULL$")
|
||||
DecimalRegex = regexp.MustCompile("^-?[0-9]+$")
|
||||
HexRegex = regexp.MustCompile("^0x[0-9a-fA-F]+$")
|
||||
OctRegex = regexp.MustCompile("^0o[0-7]+$")
|
||||
BinaryRegex = regexp.MustCompile("^0b[01]+$")
|
||||
|
||||
// SymbolRegex = regexp.MustCompile("^[^'#]+$")
|
||||
// (Sigil) symbols can begin with #, $, ?, but
|
||||
// sigils cannot appear later in any symbol.
|
||||
// Symbols cannot contain whitespace nor `~`, `@`, `(`, `)`, `[`, `]`,
|
||||
// `{`, `}`, `'`, `#`, `^`, `\`, `|`, `%`, `"`, `;`. They can optionally
|
||||
// end in `:`.
|
||||
// Nor, obviously, can symbols contain backticks, "`".
|
||||
// Symbols cannot start with a number. DotSymbols cannot have a number
|
||||
// as the first character after '.'
|
||||
SymbolRegex = regexp.MustCompile(`^[#$?]?[^#$?':;\\~@\[\]{}\^|"()%0-9,&][^'#:;\\~@\[\]{}\^|"()%,&*\-]*[:]?$`)
|
||||
// dot symbol examples: `.`, `.a`, `.a.b`, `.a.b.c`
|
||||
// dot symbol non-examples: `.a.`, `..`
|
||||
DotSymbolRegex = regexp.MustCompile(`^[.]$|^([.][^'#:;\\~@\[\]{}\^|"()%.0-9,][^'#:;\\~@\[\]{}\^|"()%.,*+\-]*)+$|^[^'#:;\\~@\[\]{}\^|"()%.0-9,][^'#:;\\~@\[\]{}\^|"()%.,*+\-]*([.][^'#:;\\~@\[\]{}\^|"()%.0-9,][^'#:;\\~@\[\]{}\^|"()%.,*+\-]*)+$`)
|
||||
DotPartsRegex = regexp.MustCompile(`[.]?[^'#:;\\~@\[\]{}\^|"()%.0-9,][^'#:;\\~@\[\]{}\^|"()%.,]*`)
|
||||
CharRegex = regexp.MustCompile("^'(\\\\?.|\n)'$")
|
||||
FloatRegex = regexp.MustCompile("^-?([0-9]+\\.[0-9]*)$|-?(\\.[0-9]+)$|-?([0-9]+(\\.[0-9]*)?[eE]([-+]?[0-9]+))$")
|
||||
ComplexRegex = regexp.MustCompile("^-?([0-9]+\\.[0-9]*)i?$|-?(\\.[0-9]+)i?$|-?([0-9]+(\\.[0-9]*)?[eE](-?[0-9]+))i?$")
|
||||
BuiltinOpRegex = regexp.MustCompile(`^(\+\+|\-\-|\+=|\-=|=|==|:=|\+|\-|\*|<|>|<=|>=|<-|->|\*=|/=|\*\*|!|!=|<!)$`)
|
||||
)
|
||||
|
||||
func StringToRunes(str string) []rune {
|
||||
b := []byte(str)
|
||||
runes := make([]rune, 0)
|
||||
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
runes = append(runes, r)
|
||||
b = b[size:]
|
||||
}
|
||||
return runes
|
||||
}
|
||||
|
||||
func EscapeChar(char rune) (rune, error) {
|
||||
switch char {
|
||||
case 'n':
|
||||
return '\n', nil
|
||||
case 'r':
|
||||
return '\r', nil
|
||||
case 'a':
|
||||
return '\a', nil
|
||||
case 't':
|
||||
return '\t', nil
|
||||
case '\\':
|
||||
return '\\', nil
|
||||
case '"':
|
||||
return '"', nil
|
||||
case '\'':
|
||||
return '\'', nil
|
||||
case '#':
|
||||
return '#', nil
|
||||
}
|
||||
return ' ', errors.New("invalid escape sequence")
|
||||
}
|
||||
|
||||
func DecodeChar(atom string) (string, error) {
|
||||
runes := StringToRunes(atom)
|
||||
n := len(runes)
|
||||
runes = runes[:n-1]
|
||||
runes = runes[1:]
|
||||
if len(runes) == 2 {
|
||||
char, err := EscapeChar(runes[1])
|
||||
return string(char), err
|
||||
}
|
||||
|
||||
if len(runes) == 1 {
|
||||
return string(runes[0]), nil
|
||||
}
|
||||
return "", errors.New("not a char literal")
|
||||
}
|
||||
|
||||
func (x *Lexer) DecodeAtom(atom string) (tk Token, err error) {
|
||||
|
||||
endColon := false
|
||||
n := len(atom)
|
||||
if atom[n-1] == ':' {
|
||||
endColon = true
|
||||
atom = atom[:n-1] // remove the colon
|
||||
}
|
||||
if atom == "&" {
|
||||
return x.Token(TokenSymbol, "&"), nil
|
||||
}
|
||||
if atom == "\\" {
|
||||
return x.Token(TokenBackslash, ""), nil
|
||||
}
|
||||
if BoolRegex.MatchString(atom) {
|
||||
return x.Token(TokenBool, atom), nil
|
||||
}
|
||||
if Uint64Regex.MatchString(atom) {
|
||||
return x.Token(TokenUint64, atom), nil
|
||||
}
|
||||
if DecimalRegex.MatchString(atom) {
|
||||
return x.Token(TokenDecimal, atom), nil
|
||||
}
|
||||
if HexRegex.MatchString(atom) {
|
||||
return x.Token(TokenHex, atom[2:]), nil
|
||||
}
|
||||
if OctRegex.MatchString(atom) {
|
||||
return x.Token(TokenOct, atom[2:]), nil
|
||||
}
|
||||
if BinaryRegex.MatchString(atom) {
|
||||
return x.Token(TokenBinary, atom[2:]), nil
|
||||
}
|
||||
if FloatRegex.MatchString(atom) {
|
||||
return x.Token(TokenFloat, atom), nil
|
||||
}
|
||||
if atom == "NaN" || atom == "nan" {
|
||||
return x.Token(TokenFloat, "NaN"), nil
|
||||
}
|
||||
if DotSymbolRegex.MatchString(atom) {
|
||||
//Q("matched DotSymbolRegex '%v'", atom)
|
||||
return x.Token(TokenDotSymbol, atom), nil
|
||||
}
|
||||
if BuiltinOpRegex.MatchString(atom) {
|
||||
return x.Token(TokenSymbol, atom), nil
|
||||
}
|
||||
if atom == ":" {
|
||||
return x.Token(TokenSymbol, atom), nil
|
||||
} else if SymbolRegex.MatchString(atom) {
|
||||
////Q("matched symbol regex, atom='%v'", atom)
|
||||
if endColon {
|
||||
////Q("matched symbol regex with colon, atom[:n-1]='%v'", atom[:n-1])
|
||||
return x.Token(TokenSymbolColon, atom[:n-1]), nil
|
||||
}
|
||||
return x.Token(TokenSymbol, atom), nil
|
||||
}
|
||||
if CharRegex.MatchString(atom) {
|
||||
char, err := DecodeChar(atom)
|
||||
if err != nil {
|
||||
return x.EmptyToken(), err
|
||||
}
|
||||
return x.Token(TokenChar, char), nil
|
||||
}
|
||||
|
||||
if endColon {
|
||||
return x.Token(TokenColonOperator, ":"), nil
|
||||
}
|
||||
|
||||
return x.EmptyToken(), fmt.Errorf("Unrecognized atom: '%s'", atom)
|
||||
}
|
||||
|
||||
func (lexer *Lexer) dumpBuffer() error {
|
||||
n := lexer.buffer.Len()
|
||||
if n <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tok, err := lexer.DecodeAtom(lexer.buffer.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.buffer.Reset()
|
||||
lexer.AppendToken(tok)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// with block comments, we've got to tell
|
||||
// the parser about them, so it can recognize
|
||||
// when another line is needed to finish a
|
||||
// block comment.
|
||||
func (lexer *Lexer) dumpComment() {
|
||||
str := lexer.buffer.String()
|
||||
lexer.buffer.Reset()
|
||||
lexer.AppendToken(lexer.Token(TokenComment, str))
|
||||
}
|
||||
|
||||
func (lexer *Lexer) dumpString() {
|
||||
str := lexer.buffer.String()
|
||||
lexer.buffer.Reset()
|
||||
lexer.AppendToken(lexer.Token(TokenString, str))
|
||||
}
|
||||
|
||||
func (lexer *Lexer) dumpBacktickString() {
|
||||
str := lexer.buffer.String()
|
||||
lexer.buffer.Reset()
|
||||
lexer.AppendToken(lexer.Token(TokenBacktickString, str))
|
||||
}
|
||||
|
||||
func (x *Lexer) DecodeBrace(brace rune) Token {
|
||||
switch brace {
|
||||
case '(':
|
||||
return x.Token(TokenLParen, "")
|
||||
case ')':
|
||||
return x.Token(TokenRParen, "")
|
||||
case '[':
|
||||
return x.Token(TokenLSquare, "")
|
||||
case ']':
|
||||
return x.Token(TokenRSquare, "")
|
||||
case '{':
|
||||
return x.Token(TokenLCurly, "")
|
||||
case '}':
|
||||
return x.Token(TokenRCurly, "")
|
||||
}
|
||||
return EndTk
|
||||
}
|
||||
|
||||
func (lexer *Lexer) LexNextRune(r rune) error {
|
||||
|
||||
// a little look-back ring. To help with scientific
|
||||
// notation recognition
|
||||
lexer.priorRune[lexer.priori] = r
|
||||
lexer.priori = (lexer.priori + 1) % len(lexer.priorRune)
|
||||
|
||||
top:
|
||||
switch lexer.state {
|
||||
|
||||
case LexerCommentBlock:
|
||||
//Q("lexer.state = LexerCommentBlock")
|
||||
if r == '\n' {
|
||||
_, err := lexer.buffer.WriteRune('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.dumpComment()
|
||||
// stay in LexerCommentBlock
|
||||
return nil
|
||||
}
|
||||
if r == '*' {
|
||||
lexer.state = LexerCommentBlockAsterisk
|
||||
return nil
|
||||
}
|
||||
case LexerCommentBlockAsterisk:
|
||||
//Q("lexer.state = LexerCommentBlockAsterisk")
|
||||
if r == '/' {
|
||||
_, err := lexer.buffer.WriteString("*/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.dumpComment()
|
||||
lexer.AppendToken(lexer.Token(TokenEndBlockComment, ""))
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
}
|
||||
_, err := lexer.buffer.WriteRune('*')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.state = LexerCommentBlock
|
||||
goto writeRuneToBuffer
|
||||
|
||||
case LexerFirstFwdSlash:
|
||||
//Q("lexer.state = LexerFirstFwdSlash")
|
||||
if r == '/' {
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.state = LexerCommentLine
|
||||
_, err = lexer.buffer.WriteString("//")
|
||||
return err
|
||||
}
|
||||
if r == '*' {
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = lexer.buffer.WriteString("/*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.state = LexerCommentBlock
|
||||
lexer.AppendToken(lexer.Token(TokenBeginBlockComment, ""))
|
||||
return nil
|
||||
}
|
||||
lexer.state = LexerBuiltinOperator
|
||||
lexer.prevrune = '/'
|
||||
err := lexer.dumpBuffer() // don't mix with token before the /
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
goto top // process the unknown rune r
|
||||
|
||||
case LexerCommentLine:
|
||||
//Q("lexer.state = LexerCommentLine")
|
||||
if r == '\n' {
|
||||
//Q("lexer.state = LexerCommentLine sees end of line comment: '%s', going to LexerNormal", string(lexer.buffer.Bytes()))
|
||||
lexer.dumpComment()
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
}
|
||||
|
||||
case LexerBacktickString:
|
||||
if r == '`' {
|
||||
lexer.dumpBacktickString()
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
}
|
||||
lexer.buffer.WriteRune(r)
|
||||
return nil
|
||||
|
||||
case LexerStrLit:
|
||||
if r == '\\' {
|
||||
lexer.state = LexerStrEscaped
|
||||
return nil
|
||||
}
|
||||
if r == '"' {
|
||||
lexer.dumpString()
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
}
|
||||
lexer.buffer.WriteRune(r)
|
||||
return nil
|
||||
|
||||
case LexerStrEscaped:
|
||||
char, err := EscapeChar(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.buffer.WriteRune(char)
|
||||
lexer.state = LexerStrLit
|
||||
return nil
|
||||
|
||||
case LexerRuneLit:
|
||||
if r == '\\' {
|
||||
lexer.state = LexerRuneEscaped
|
||||
return nil
|
||||
}
|
||||
if r == '\'' {
|
||||
// closing single quote
|
||||
lexer.buffer.WriteRune(r)
|
||||
lexer.dumpBuffer()
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
}
|
||||
lexer.buffer.WriteRune(r)
|
||||
return nil
|
||||
|
||||
case LexerRuneEscaped:
|
||||
char, err := EscapeChar(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.buffer.WriteRune(char)
|
||||
lexer.state = LexerRuneLit
|
||||
return nil
|
||||
|
||||
case LexerUnquote:
|
||||
if r == '@' {
|
||||
lexer.AppendToken(lexer.Token(TokenTildeAt, ""))
|
||||
} else {
|
||||
lexer.AppendToken(lexer.Token(TokenTilde, ""))
|
||||
lexer.buffer.WriteRune(r)
|
||||
}
|
||||
lexer.state = LexerNormal
|
||||
return nil
|
||||
case LexerFreshAssignOrColon:
|
||||
lexer.state = LexerNormal
|
||||
|
||||
// there was a ':' followed by either '=' or something other than '=',
|
||||
|
||||
// so proceed to process the normal ':' actions.
|
||||
if lexer.buffer.Len() == 0 {
|
||||
if r == '=' {
|
||||
lexer.AppendToken(lexer.Token(TokenFreshAssign, ":="))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if r == '=' {
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenFreshAssign, ":="))
|
||||
return nil
|
||||
} else {
|
||||
// but still allow ':' to be a token terminator at the end of a word.
|
||||
_, err := lexer.buffer.WriteRune(':')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
goto top // process the unknown rune r in Normal mode
|
||||
}
|
||||
|
||||
case LexerBuiltinOperator:
|
||||
//Q("in LexerBuiltinOperator")
|
||||
lexer.state = LexerNormal
|
||||
// three cases: negative number, one rune operator, two rune operator
|
||||
first := string(lexer.prevrune)
|
||||
atom := fmt.Sprintf("%c%c", lexer.prevrune, r)
|
||||
//Q("in LexerBuiltinOperator, first='%s', atom='%s'", first, atom)
|
||||
// are we a negative number -1 or -.1 rather than ->, --, -= operator?
|
||||
if lexer.prevrune == '-' {
|
||||
if FloatRegex.MatchString(atom) || DecimalRegex.MatchString(atom) {
|
||||
//Q("'%s' is the beginning of a negative number", atom)
|
||||
_, err := lexer.buffer.WriteString(atom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
//Q("atom was not matched by FloatRegex: '%s'", atom)
|
||||
}
|
||||
}
|
||||
|
||||
if BuiltinOpRegex.MatchString(atom) {
|
||||
//Q("2 rune atom in builtin op '%s', first='%s'", atom, first)
|
||||
// 2 rune op
|
||||
lexer.AppendToken(lexer.Token(TokenSymbol, atom))
|
||||
return nil
|
||||
}
|
||||
//Q("1 rune atom in builtin op '%s', first='%s'", atom, first)
|
||||
lexer.AppendToken(lexer.Token(TokenSymbol, first))
|
||||
goto top // still have to parse r in normal
|
||||
|
||||
case LexerNormal:
|
||||
switch r {
|
||||
case '+':
|
||||
fallthrough
|
||||
case '-':
|
||||
// 1e-1, 1E+1, 1e1 are allowed, scientific notation for floats.
|
||||
pr := lexer.twoback()
|
||||
if pr == 'e' || pr == 'E' {
|
||||
// scientific notation number?
|
||||
s := lexer.buffer.String()
|
||||
ns := len(s)
|
||||
if ns > 1 {
|
||||
sWithoutE := s[:ns-1]
|
||||
if DecimalRegex.MatchString(sWithoutE) ||
|
||||
FloatRegex.MatchString(sWithoutE) {
|
||||
goto writeRuneToBuffer
|
||||
}
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
case '*':
|
||||
fallthrough
|
||||
case '<':
|
||||
fallthrough
|
||||
case '>':
|
||||
fallthrough
|
||||
case '=':
|
||||
fallthrough
|
||||
case '!':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.state = LexerBuiltinOperator
|
||||
lexer.prevrune = r
|
||||
return nil
|
||||
|
||||
case '/':
|
||||
lexer.state = LexerFirstFwdSlash
|
||||
return nil
|
||||
|
||||
case '`':
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected backtick")
|
||||
}
|
||||
lexer.state = LexerBacktickString
|
||||
lexer.AppendToken(lexer.Token(TokenBeginBacktickString, ""))
|
||||
return nil
|
||||
|
||||
case '"':
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected quote")
|
||||
}
|
||||
lexer.state = LexerStrLit
|
||||
return nil
|
||||
|
||||
case '\'':
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected single quote")
|
||||
}
|
||||
lexer.buffer.WriteRune(r)
|
||||
lexer.state = LexerRuneLit
|
||||
return nil
|
||||
|
||||
case ';':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenSemicolon, ";"))
|
||||
return nil
|
||||
|
||||
case ',':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenComma, ","))
|
||||
return nil
|
||||
|
||||
// colon terminates a keyword symbol, e.g. in `mykey: "myvalue"`;
|
||||
// mykey is the symbol.
|
||||
// Exception: unless it is the := operator for fresh assigment.
|
||||
case ':':
|
||||
lexer.state = LexerFreshAssignOrColon
|
||||
// won't know if it is ':' alone or ':=' for sure
|
||||
// until we get the next rune
|
||||
return nil
|
||||
|
||||
// likewise &
|
||||
case '&':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenSymbol, "&"))
|
||||
return nil
|
||||
|
||||
case '%': // replaces ' as our quote shorthand
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected % quote")
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenQuote, ""))
|
||||
return nil
|
||||
|
||||
// caret '^' replaces backtick '`' as the start of a macro template, so
|
||||
// we can use `` as in Go for verbatim strings (strings with newlines, etc).
|
||||
case '^':
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected ^ caret")
|
||||
}
|
||||
lexer.AppendToken(lexer.Token(TokenCaret, ""))
|
||||
return nil
|
||||
|
||||
case '~':
|
||||
if lexer.buffer.Len() > 0 {
|
||||
return errors.New("Unexpected tilde")
|
||||
}
|
||||
lexer.state = LexerUnquote
|
||||
return nil
|
||||
|
||||
case '(':
|
||||
fallthrough
|
||||
case ')':
|
||||
fallthrough
|
||||
case '[':
|
||||
fallthrough
|
||||
case ']':
|
||||
fallthrough
|
||||
case '{':
|
||||
fallthrough
|
||||
case '}':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lexer.AppendToken(lexer.DecodeBrace(r))
|
||||
return nil
|
||||
case '\n':
|
||||
lexer.linenum++
|
||||
fallthrough
|
||||
case ' ':
|
||||
fallthrough
|
||||
case '\t':
|
||||
fallthrough
|
||||
case '\r':
|
||||
err := lexer.dumpBuffer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} // end switch r in LexerNormal state
|
||||
|
||||
} // end switch lexer.state
|
||||
|
||||
writeRuneToBuffer:
|
||||
_, err := lexer.buffer.WriteRune(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) PeekNextToken() (tok Token, err error) {
|
||||
/*
|
||||
Q("\n in PeekNextToken()\n")
|
||||
defer func() {
|
||||
Q("\n done with PeekNextToken() -> returning tok='%v', err=%v. tok='%#v'. tok==EndTk? %v\n",
|
||||
tok, err, tok, tok == EndTk)
|
||||
}()
|
||||
*/
|
||||
if lexer.stream == nil {
|
||||
if !lexer.PromoteNextStream() {
|
||||
return EndTk, nil
|
||||
}
|
||||
}
|
||||
|
||||
for len(lexer.tokens) == 0 {
|
||||
r, _, err := lexer.stream.ReadRune()
|
||||
if err != nil {
|
||||
if lexer.PromoteNextStream() {
|
||||
continue
|
||||
} else {
|
||||
return EndTk, nil
|
||||
}
|
||||
}
|
||||
|
||||
err = lexer.LexNextRune(r)
|
||||
if err != nil {
|
||||
return EndTk, err
|
||||
}
|
||||
}
|
||||
|
||||
tok = lexer.tokens[0]
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) GetNextToken() (tok Token, err error) {
|
||||
/*
|
||||
Q("\n in GetNextToken()\n")
|
||||
defer func() {
|
||||
Q("\n done with GetNextToken() -> returning tok='%v', err=%v. lexer.buffer.String()='%s'\n",
|
||||
tok, err, lexer.buffer.String())
|
||||
}()
|
||||
*/
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil || tok.typ == TokenEnd {
|
||||
return EndTk, err
|
||||
}
|
||||
lexer.tokens = lexer.tokens[1:]
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func (lex *Lexer) PromoteNextStream() (ok bool) {
|
||||
/*
|
||||
Q("entering PromoteNextStream()!\n")
|
||||
defer func() {
|
||||
Q("done with PromoteNextStream, promoted=%v\n", ok)
|
||||
}()
|
||||
*/
|
||||
if len(lex.next) == 0 {
|
||||
return false
|
||||
}
|
||||
//Q("Promoting next stream!\n")
|
||||
lex.stream = lex.next[0]
|
||||
lex.next = lex.next[1:]
|
||||
return true
|
||||
}
|
||||
|
||||
func (lex *Lexer) AddNextStream(s io.RuneScanner) {
|
||||
// in case we still have input available,
|
||||
// save new stuff for later
|
||||
lex.next = append(lex.next, s)
|
||||
|
||||
if lex.stream == nil {
|
||||
lex.PromoteNextStream()
|
||||
} else {
|
||||
_, _, err := lex.stream.ReadRune()
|
||||
if err == nil {
|
||||
lex.stream.UnreadRune()
|
||||
// still have input available
|
||||
return
|
||||
} else {
|
||||
lex.PromoteNextStream()
|
||||
}
|
||||
}
|
||||
}
|
||||
134
vendor/github.com/glycerine/zygomys/zygo/liner.go
generated
vendored
Normal file
134
vendor/github.com/glycerine/zygomys/zygo/liner.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/glycerine/liner"
|
||||
)
|
||||
|
||||
// filled at init time based on BuiltinFunctions
|
||||
var completion_keywords = []string{`(`}
|
||||
|
||||
var math_funcs = []string{`* `, `** `, `+ `, `- `, `-> `, `/ `, `< `, `<= `, `== `, `> `, `>= `, `\ `}
|
||||
|
||||
func init() {
|
||||
// fill in our auto-complete keywords
|
||||
sortme := []*SymtabE{}
|
||||
for f, _ := range AllBuiltinFunctions() {
|
||||
sortme = append(sortme, &SymtabE{Key: f})
|
||||
}
|
||||
sort.Sort(SymtabSorter(sortme))
|
||||
for i := range sortme {
|
||||
completion_keywords = append(completion_keywords, "("+sortme[i].Key)
|
||||
}
|
||||
|
||||
for i := range math_funcs {
|
||||
completion_keywords = append(completion_keywords, "("+math_funcs[i])
|
||||
}
|
||||
}
|
||||
|
||||
type Prompter struct {
|
||||
prompt string
|
||||
prompter *liner.State
|
||||
origMode liner.ModeApplier
|
||||
rawMode liner.ModeApplier
|
||||
}
|
||||
|
||||
// complete phrases that start with '('
|
||||
func MyWordCompleter(line string, pos int) (head string, c []string, tail string) {
|
||||
|
||||
beg := []rune(line[:pos])
|
||||
end := line[pos:]
|
||||
Q("\nline = '%s' pos=%v\n", line, pos)
|
||||
Q("\nbeg = '%v'\nend = '%s'\n", string(beg), end)
|
||||
// find most recent paren in beg
|
||||
n := len(beg)
|
||||
last := n - 1
|
||||
var i int
|
||||
var p int = -1
|
||||
outer:
|
||||
for i = last; i >= 0; i-- {
|
||||
Q("\nbeg[i=%v] is '%v'\n", i, string(beg[i]))
|
||||
switch beg[i] {
|
||||
case ' ':
|
||||
break outer
|
||||
case '(':
|
||||
p = i
|
||||
Q("\n found paren at p = %v\n", i)
|
||||
break outer
|
||||
}
|
||||
}
|
||||
Q("p=%d\n", p)
|
||||
prefix := string(beg)
|
||||
extendme := ""
|
||||
if p == 0 {
|
||||
prefix = ""
|
||||
extendme = string(beg)
|
||||
} else if p > 0 {
|
||||
prefix = string(beg[:p])
|
||||
extendme = string(beg[p:])
|
||||
}
|
||||
Q("prefix = '%s'\nextendme = '%s'\n", prefix, extendme)
|
||||
|
||||
for _, n := range completion_keywords {
|
||||
if strings.HasPrefix(n, strings.ToLower(extendme)) {
|
||||
Q("n='%s' has prefix = '%s'\n", n, extendme)
|
||||
c = append(c, n)
|
||||
}
|
||||
}
|
||||
|
||||
return prefix, c, end
|
||||
}
|
||||
|
||||
func NewPrompter(prompt string) *Prompter {
|
||||
origMode, err := liner.TerminalMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
p := &Prompter{
|
||||
prompt: prompt,
|
||||
prompter: liner.NewLiner(),
|
||||
origMode: origMode,
|
||||
}
|
||||
|
||||
rawMode, err := liner.TerminalMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.rawMode = rawMode
|
||||
|
||||
p.prompter.SetCtrlCAborts(false)
|
||||
p.prompter.SetWordCompleter(liner.WordCompleter(MyWordCompleter))
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Prompter) Close() {
|
||||
defer p.prompter.Close()
|
||||
}
|
||||
|
||||
func (p *Prompter) Getline(prompt *string) (line string, err error) {
|
||||
applyErr := p.rawMode.ApplyMode()
|
||||
if applyErr != nil {
|
||||
panic(applyErr)
|
||||
}
|
||||
defer func() {
|
||||
applyErr := p.origMode.ApplyMode()
|
||||
if applyErr != nil {
|
||||
panic(applyErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if prompt == nil {
|
||||
line, err = p.prompter.Prompt(p.prompt)
|
||||
} else {
|
||||
line, err = p.prompter.Prompt(*prompt)
|
||||
}
|
||||
if err == nil {
|
||||
p.prompter.AppendHistory(line)
|
||||
return line, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
119
vendor/github.com/glycerine/zygomys/zygo/listutils.go
generated
vendored
Normal file
119
vendor/github.com/glycerine/zygomys/zygo/listutils.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var NotAList = errors.New("not a list")
|
||||
|
||||
func ListToArray(expr Sexp) ([]Sexp, error) {
|
||||
if !IsList(expr) {
|
||||
return nil, NotAList
|
||||
}
|
||||
arr := make([]Sexp, 0)
|
||||
|
||||
for expr != SexpNull {
|
||||
list := expr.(*SexpPair)
|
||||
arr = append(arr, list.Head)
|
||||
expr = list.Tail
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func MakeList(expressions []Sexp) Sexp {
|
||||
if len(expressions) == 0 {
|
||||
return SexpNull
|
||||
}
|
||||
|
||||
return Cons(expressions[0], MakeList(expressions[1:]))
|
||||
}
|
||||
|
||||
func MapList(env *Zlisp, fun *SexpFunction, expr Sexp) (Sexp, error) {
|
||||
if expr == SexpNull {
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
var list = &SexpPair{}
|
||||
switch e := expr.(type) {
|
||||
case *SexpPair:
|
||||
list.Head = e.Head
|
||||
list.Tail = e.Tail
|
||||
default:
|
||||
return SexpNull, NotAList
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
list.Head, err = env.Apply(fun, []Sexp{list.Head})
|
||||
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
list.Tail, err = MapList(env, fun, list.Tail)
|
||||
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// O(n^2) for n total nodes in all lists. So this is
|
||||
// not super efficient. We have to
|
||||
// find the tail of each list in turn by
|
||||
// linear search. Avoid lists if possible in favor
|
||||
// of arrays.
|
||||
func ConcatLists(a *SexpPair, bs []Sexp) (Sexp, error) {
|
||||
result := a
|
||||
for _, b := range bs {
|
||||
res, err := ConcatTwoLists(result, b)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
x, ok := res.(*SexpPair)
|
||||
if !ok {
|
||||
return SexpNull, NotAList
|
||||
}
|
||||
result = x
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ConcatTwoLists(a *SexpPair, b Sexp) (Sexp, error) {
|
||||
if !IsList(b) {
|
||||
return SexpNull, NotAList
|
||||
}
|
||||
|
||||
if a.Tail == SexpNull {
|
||||
return Cons(a.Head, b), nil
|
||||
}
|
||||
|
||||
switch t := a.Tail.(type) {
|
||||
case *SexpPair:
|
||||
newtail, err := ConcatTwoLists(t, b)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return Cons(a.Head, newtail), nil
|
||||
}
|
||||
|
||||
return SexpNull, NotAList
|
||||
}
|
||||
|
||||
func ListLen(expr Sexp) (int, error) {
|
||||
sz := 0
|
||||
var list *SexpPair
|
||||
ok := false
|
||||
for expr != SexpNull {
|
||||
list, ok = expr.(*SexpPair)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("ListLen() called on non-list")
|
||||
}
|
||||
sz++
|
||||
expr = list.Tail
|
||||
}
|
||||
return sz, nil
|
||||
}
|
||||
1
vendor/github.com/glycerine/zygomys/zygo/makego.go
generated
vendored
Normal file
1
vendor/github.com/glycerine/zygomys/zygo/makego.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package zygo
|
||||
43
vendor/github.com/glycerine/zygomys/zygo/msgpackmap.go
generated
vendored
Normal file
43
vendor/github.com/glycerine/zygomys/zygo/msgpackmap.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (env *Zlisp) ImportMsgpackMap() {
|
||||
env.AddMacro("msgpack-map", MsgpackMapMacro)
|
||||
env.AddFunction("declare-msgpack-map", DeclareMsgpackMapFunction)
|
||||
}
|
||||
|
||||
// declare a new record type
|
||||
func MsgpackMapMacro(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
if len(args) < 1 {
|
||||
return SexpNull, fmt.Errorf("struct-name is missing. use: " +
|
||||
"(msgpack-map struct-name)\n")
|
||||
}
|
||||
|
||||
return MakeList([]Sexp{
|
||||
env.MakeSymbol("def"),
|
||||
args[0],
|
||||
MakeList([]Sexp{
|
||||
env.MakeSymbol("quote"),
|
||||
env.MakeSymbol("msgmap"),
|
||||
&SexpStr{S: args[0].(*SexpSymbol).name},
|
||||
}),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func DeclareMsgpackMapFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
switch t := args[0].(type) {
|
||||
case *SexpStr:
|
||||
return t, nil
|
||||
}
|
||||
return SexpNull, errors.New("argument must be string: the name of the new msgpack-map constructor function to create")
|
||||
}
|
||||
246
vendor/github.com/glycerine/zygomys/zygo/numerictower.go
generated
vendored
Normal file
246
vendor/github.com/glycerine/zygomys/zygo/numerictower.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
type IntegerOp int
|
||||
|
||||
const (
|
||||
ShiftLeft IntegerOp = iota
|
||||
ShiftRightArith
|
||||
ShiftRightLog
|
||||
Modulo
|
||||
BitAnd
|
||||
BitOr
|
||||
BitXor
|
||||
)
|
||||
|
||||
var WrongType error = errors.New("operands have invalid type")
|
||||
|
||||
func IntegerDo(op IntegerOp, a, b Sexp) (Sexp, error) {
|
||||
var ia *SexpInt
|
||||
var ib *SexpInt
|
||||
|
||||
switch i := a.(type) {
|
||||
case *SexpInt:
|
||||
ia = i
|
||||
case *SexpUint64:
|
||||
return UintegerDo(op, i, b)
|
||||
case *SexpChar:
|
||||
ia = &SexpInt{Val: int64(i.Val)}
|
||||
default:
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
|
||||
switch i := b.(type) {
|
||||
case *SexpInt:
|
||||
ib = i
|
||||
case *SexpUint64:
|
||||
return UintegerDo(op, &SexpUint64{Val: uint64(ia.Val)}, b)
|
||||
case *SexpChar:
|
||||
ib = &SexpInt{Val: int64(i.Val)}
|
||||
default:
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
|
||||
switch op {
|
||||
case ShiftLeft:
|
||||
return &SexpInt{Val: ia.Val << uint(ib.Val)}, nil
|
||||
case ShiftRightArith:
|
||||
return &SexpInt{Val: ia.Val >> uint(ib.Val)}, nil
|
||||
case ShiftRightLog:
|
||||
return &SexpInt{Val: int64(uint(ia.Val) >> uint(ib.Val))}, nil
|
||||
case Modulo:
|
||||
return &SexpInt{Val: ia.Val % ib.Val}, nil
|
||||
case BitAnd:
|
||||
return &SexpInt{Val: ia.Val & ib.Val}, nil
|
||||
case BitOr:
|
||||
return &SexpInt{Val: ia.Val | ib.Val}, nil
|
||||
case BitXor:
|
||||
return &SexpInt{Val: ia.Val ^ ib.Val}, nil
|
||||
}
|
||||
return SexpNull, errors.New("unrecognized shift operation")
|
||||
}
|
||||
|
||||
func UintegerDo(op IntegerOp, ia *SexpUint64, b Sexp) (Sexp, error) {
|
||||
var ib *SexpUint64
|
||||
|
||||
switch i := b.(type) {
|
||||
case *SexpUint64:
|
||||
ib = i
|
||||
case *SexpInt:
|
||||
ib = &SexpUint64{Val: uint64(i.Val)}
|
||||
case *SexpChar:
|
||||
ib = &SexpUint64{Val: uint64(i.Val)}
|
||||
default:
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
|
||||
switch op {
|
||||
case ShiftLeft:
|
||||
return &SexpUint64{Val: ia.Val << ib.Val}, nil
|
||||
case ShiftRightArith:
|
||||
return &SexpUint64{Val: ia.Val >> ib.Val}, nil
|
||||
case ShiftRightLog:
|
||||
return &SexpUint64{Val: ia.Val >> ib.Val}, nil
|
||||
case Modulo:
|
||||
return &SexpUint64{Val: ia.Val % ib.Val}, nil
|
||||
case BitAnd:
|
||||
return &SexpUint64{Val: ia.Val & ib.Val}, nil
|
||||
case BitOr:
|
||||
return &SexpUint64{Val: ia.Val | ib.Val}, nil
|
||||
case BitXor:
|
||||
return &SexpUint64{Val: ia.Val ^ ib.Val}, nil
|
||||
}
|
||||
return SexpNull, errors.New("unrecognized shift operation")
|
||||
}
|
||||
|
||||
type NumericOp int
|
||||
|
||||
const (
|
||||
Add NumericOp = iota
|
||||
Sub
|
||||
Mult
|
||||
Div
|
||||
Pow
|
||||
)
|
||||
|
||||
func NumericFloatDo(op NumericOp, a, b *SexpFloat) Sexp {
|
||||
switch op {
|
||||
case Add:
|
||||
return &SexpFloat{Val: a.Val + b.Val}
|
||||
case Sub:
|
||||
return &SexpFloat{Val: a.Val - b.Val}
|
||||
case Mult:
|
||||
return &SexpFloat{Val: a.Val * b.Val}
|
||||
case Div:
|
||||
return &SexpFloat{Val: a.Val / b.Val}
|
||||
case Pow:
|
||||
return &SexpFloat{Val: math.Pow(float64(a.Val), float64(b.Val))}
|
||||
}
|
||||
return SexpNull
|
||||
}
|
||||
|
||||
func NumericIntDo(op NumericOp, a, b *SexpInt) Sexp {
|
||||
switch op {
|
||||
case Add:
|
||||
return &SexpInt{Val: a.Val + b.Val}
|
||||
case Sub:
|
||||
return &SexpInt{Val: a.Val - b.Val}
|
||||
case Mult:
|
||||
return &SexpInt{Val: a.Val * b.Val}
|
||||
case Div:
|
||||
if a.Val%b.Val == 0 {
|
||||
return &SexpInt{Val: a.Val / b.Val}
|
||||
} else {
|
||||
return &SexpFloat{Val: float64(a.Val) / float64(b.Val)}
|
||||
}
|
||||
case Pow:
|
||||
return &SexpInt{Val: int64(math.Pow(float64(a.Val), float64(b.Val)))}
|
||||
}
|
||||
return SexpNull
|
||||
}
|
||||
|
||||
func NumericUint64Do(op NumericOp, a, b *SexpUint64) Sexp {
|
||||
switch op {
|
||||
case Add:
|
||||
return &SexpUint64{Val: a.Val + b.Val}
|
||||
case Sub:
|
||||
return &SexpUint64{Val: a.Val - b.Val}
|
||||
case Mult:
|
||||
return &SexpUint64{Val: a.Val * b.Val}
|
||||
case Div:
|
||||
if a.Val%b.Val == 0 {
|
||||
return &SexpUint64{Val: a.Val / b.Val}
|
||||
} else {
|
||||
return &SexpFloat{Val: float64(a.Val) / float64(b.Val)}
|
||||
}
|
||||
case Pow:
|
||||
return &SexpUint64{Val: uint64(math.Pow(float64(a.Val), float64(b.Val)))}
|
||||
}
|
||||
return SexpNull
|
||||
}
|
||||
|
||||
func NumericMatchFloat(op NumericOp, a *SexpFloat, b Sexp) (Sexp, error) {
|
||||
var fb *SexpFloat
|
||||
switch tb := b.(type) {
|
||||
case *SexpFloat:
|
||||
fb = tb
|
||||
case *SexpInt:
|
||||
fb = &SexpFloat{Val: float64(tb.Val)}
|
||||
case *SexpUint64:
|
||||
fb = &SexpFloat{Val: float64(tb.Val)}
|
||||
case *SexpChar:
|
||||
fb = &SexpFloat{Val: float64(tb.Val)}
|
||||
default:
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
return NumericFloatDo(op, a, fb), nil
|
||||
}
|
||||
|
||||
func NumericMatchInt(op NumericOp, a *SexpInt, b Sexp) (Sexp, error) {
|
||||
switch tb := b.(type) {
|
||||
case *SexpFloat:
|
||||
return NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb), nil
|
||||
case *SexpInt:
|
||||
return NumericIntDo(op, a, tb), nil
|
||||
case *SexpUint64:
|
||||
return NumericUint64Do(op, &SexpUint64{Val: uint64(a.Val)}, tb), nil
|
||||
case *SexpChar:
|
||||
return NumericIntDo(op, a, &SexpInt{Val: int64(tb.Val)}), nil
|
||||
}
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
|
||||
func NumericMatchUint64(op NumericOp, a *SexpUint64, b Sexp) (Sexp, error) {
|
||||
switch tb := b.(type) {
|
||||
case *SexpFloat:
|
||||
return NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb), nil
|
||||
case *SexpInt:
|
||||
return NumericUint64Do(op, a, &SexpUint64{Val: uint64(tb.Val)}), nil
|
||||
case *SexpUint64:
|
||||
return NumericUint64Do(op, a, tb), nil
|
||||
case *SexpChar:
|
||||
return NumericUint64Do(op, a, &SexpUint64{Val: uint64(tb.Val)}), nil
|
||||
}
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
|
||||
func NumericMatchChar(op NumericOp, a *SexpChar, b Sexp) (Sexp, error) {
|
||||
var res Sexp
|
||||
switch tb := b.(type) {
|
||||
case *SexpFloat:
|
||||
res = NumericFloatDo(op, &SexpFloat{Val: float64(a.Val)}, tb)
|
||||
case *SexpInt:
|
||||
res = NumericIntDo(op, &SexpInt{Val: int64(a.Val)}, tb)
|
||||
case *SexpUint64:
|
||||
return NumericUint64Do(op, &SexpUint64{Val: uint64(a.Val)}, tb), nil
|
||||
case *SexpChar:
|
||||
res = NumericIntDo(op, &SexpInt{Val: int64(a.Val)}, &SexpInt{Val: int64(tb.Val)})
|
||||
default:
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
switch tres := res.(type) {
|
||||
case *SexpFloat:
|
||||
return tres, nil
|
||||
case *SexpInt:
|
||||
return &SexpChar{Val: rune(tres.Val)}, nil
|
||||
}
|
||||
return SexpNull, errors.New("unexpected result")
|
||||
}
|
||||
|
||||
func NumericDo(op NumericOp, a, b Sexp) (Sexp, error) {
|
||||
switch ta := a.(type) {
|
||||
case *SexpFloat:
|
||||
return NumericMatchFloat(op, ta, b)
|
||||
case *SexpInt:
|
||||
return NumericMatchInt(op, ta, b)
|
||||
case *SexpUint64:
|
||||
return NumericMatchUint64(op, ta, b)
|
||||
case *SexpChar:
|
||||
return NumericMatchChar(op, ta, b)
|
||||
}
|
||||
return SexpNull, WrongType
|
||||
}
|
||||
7
vendor/github.com/glycerine/zygomys/zygo/panicon.go
generated
vendored
Normal file
7
vendor/github.com/glycerine/zygomys/zygo/panicon.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package zygo
|
||||
|
||||
func panicOn(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
650
vendor/github.com/glycerine/zygomys/zygo/parser.go
generated
vendored
Normal file
650
vendor/github.com/glycerine/zygomys/zygo/parser.go
generated
vendored
Normal file
@@ -0,0 +1,650 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var NaN float64
|
||||
|
||||
func init() {
|
||||
NaN = math.NaN()
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
lexer *Lexer
|
||||
env *Zlisp
|
||||
|
||||
Done chan bool
|
||||
reqStop chan bool
|
||||
AddInput chan io.RuneScanner
|
||||
ReqReset chan io.RuneScanner
|
||||
ParsedOutput chan []ParserReply
|
||||
|
||||
mut sync.Mutex
|
||||
stopped bool
|
||||
sendMe []ParserReply
|
||||
FlagSendNeedInput bool
|
||||
|
||||
inBacktick bool
|
||||
}
|
||||
|
||||
type ParserReply struct {
|
||||
Expr []Sexp
|
||||
Err error
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewParser() *Parser {
|
||||
p := &Parser{
|
||||
env: env,
|
||||
Done: make(chan bool),
|
||||
reqStop: make(chan bool),
|
||||
ReqReset: make(chan io.RuneScanner),
|
||||
AddInput: make(chan io.RuneScanner),
|
||||
ParsedOutput: make(chan []ParserReply),
|
||||
sendMe: make([]ParserReply, 0, 1),
|
||||
}
|
||||
p.lexer = NewLexer(p)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parser) Stop() error {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock()
|
||||
if p.stopped {
|
||||
return nil
|
||||
}
|
||||
p.stopped = true
|
||||
close(p.reqStop)
|
||||
<-p.Done
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts launches a background goroutine that runs an
|
||||
// infinite parsing loop.
|
||||
func (p *Parser) Start() {
|
||||
go func() {
|
||||
defer close(p.Done)
|
||||
expressions := make([]Sexp, 0, SliceDefaultCap)
|
||||
|
||||
// maybe we already have input, be optimistic!
|
||||
// no need to call p.GetMoreInput() before staring
|
||||
// our loop.
|
||||
|
||||
for {
|
||||
expr, err := p.ParseExpression(0)
|
||||
if err != nil || expr == SexpEnd {
|
||||
if err == ParserHaltRequested {
|
||||
return
|
||||
}
|
||||
err = p.GetMoreInput(expressions, err)
|
||||
if err == ParserHaltRequested {
|
||||
return
|
||||
}
|
||||
// GetMoreInput will have delivered what we gave them. Reset since we
|
||||
// don't own that memory any more.
|
||||
expressions = make([]Sexp, 0, SliceDefaultCap)
|
||||
} else {
|
||||
// INVAR: err == nil && expr is not SexpEnd
|
||||
expressions = append(expressions, expr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var ParserHaltRequested = fmt.Errorf("parser halt requested")
|
||||
var ResetRequested = fmt.Errorf("parser reset requested")
|
||||
|
||||
var ErrMoreInputNeeded = fmt.Errorf("parser needs more input")
|
||||
|
||||
// This function should *return* when it has more input
|
||||
// for the parser/lexer, which will call it when they get wedged.
|
||||
//
|
||||
// Listeners on p.ParsedOutput should know the Convention: sending
|
||||
// a length 0 []ParserReply on p.ParsedOutput channel means: we need more
|
||||
// input! They should send some in on p.AddInput channel; or request
|
||||
// a reset and simultaneously give us new input with p.ReqReset channel.
|
||||
func (p *Parser) GetMoreInput(deliverThese []Sexp, errorToReport error) error {
|
||||
|
||||
if len(deliverThese) == 0 && errorToReport == nil {
|
||||
p.FlagSendNeedInput = true
|
||||
} else {
|
||||
p.sendMe = append(p.sendMe,
|
||||
ParserReply{
|
||||
Expr: deliverThese,
|
||||
Err: errorToReport,
|
||||
})
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.reqStop:
|
||||
return ParserHaltRequested
|
||||
case input := <-p.AddInput:
|
||||
p.lexer.AddNextStream(input)
|
||||
p.FlagSendNeedInput = false
|
||||
return nil
|
||||
case input := <-p.ReqReset:
|
||||
p.lexer.Reset()
|
||||
p.lexer.AddNextStream(input)
|
||||
p.FlagSendNeedInput = false
|
||||
return ResetRequested
|
||||
case p.HaveStuffToSend() <- p.sendMe:
|
||||
p.sendMe = make([]ParserReply, 0, 1)
|
||||
p.FlagSendNeedInput = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) HaveStuffToSend() chan []ParserReply {
|
||||
if len(p.sendMe) > 0 || p.FlagSendNeedInput {
|
||||
return p.ParsedOutput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) Reset() {
|
||||
select {
|
||||
case p.ReqReset <- nil:
|
||||
case <-p.reqStop:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) NewInput(s io.RuneScanner) {
|
||||
select {
|
||||
case p.AddInput <- s:
|
||||
case <-p.reqStop:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) ResetAddNewInput(s io.RuneScanner) {
|
||||
select {
|
||||
case p.ReqReset <- s:
|
||||
case <-p.reqStop:
|
||||
}
|
||||
}
|
||||
|
||||
var UnexpectedEnd error = errors.New("Unexpected end of input")
|
||||
|
||||
const SliceDefaultCap = 10
|
||||
|
||||
func (parser *Parser) ParseList(depth int) (sx Sexp, err error) {
|
||||
lexer := parser.lexer
|
||||
var tok Token
|
||||
|
||||
tokFilled:
|
||||
for {
|
||||
tok, err = lexer.PeekNextToken()
|
||||
//Q("\n ParseList(depth=%d) got lexer.PeekNextToken() -> tok='%v' err='%v'\n", depth, tok, err)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if tok.typ != TokenEnd {
|
||||
break tokFilled
|
||||
}
|
||||
// instead of returning UnexpectedEnd, we:
|
||||
err = parser.GetMoreInput(nil, ErrMoreInputNeeded)
|
||||
//Q("\n ParseList(depth=%d) got back from parser.GetMoreInput(): '%v'\n", depth, err)
|
||||
switch err {
|
||||
case ParserHaltRequested:
|
||||
return SexpNull, err
|
||||
case ResetRequested:
|
||||
return SexpEnd, err
|
||||
}
|
||||
// have to still fill tok, so
|
||||
// loop to the top to PeekNextToken
|
||||
}
|
||||
|
||||
if tok.typ == TokenRParen {
|
||||
_, _ = lexer.GetNextToken()
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
var start = &SexpPair{}
|
||||
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
start.Head = expr
|
||||
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
// backslash '\' replaces dot '.' in zygo
|
||||
if tok.typ == TokenBackslash {
|
||||
// eat up the backslash
|
||||
_, _ = lexer.GetNextToken()
|
||||
expr, err = parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
// eat up the end paren
|
||||
tok, err = lexer.GetNextToken()
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
// make sure it was actually an end paren
|
||||
if tok.typ != TokenRParen {
|
||||
return SexpNull, errors.New("extra value in dotted pair")
|
||||
}
|
||||
start.Tail = expr
|
||||
return start, nil
|
||||
}
|
||||
|
||||
expr, err = parser.ParseList(depth + 1)
|
||||
if err != nil {
|
||||
return start, err
|
||||
}
|
||||
start.Tail = expr
|
||||
|
||||
return start, nil
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseArray(depth int) (Sexp, error) {
|
||||
lexer := parser.lexer
|
||||
arr := make([]Sexp, 0, SliceDefaultCap)
|
||||
|
||||
var tok Token
|
||||
var err error
|
||||
for {
|
||||
getTok:
|
||||
for {
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil {
|
||||
return SexpEnd, err
|
||||
}
|
||||
|
||||
if tok.typ == TokenComma {
|
||||
// pop off the ,
|
||||
_, _ = lexer.GetNextToken()
|
||||
continue getTok
|
||||
}
|
||||
|
||||
if tok.typ != TokenEnd {
|
||||
break getTok
|
||||
} else {
|
||||
//instead of return SexpEnd, UnexpectedEnd
|
||||
// we ask for more, and then loop
|
||||
err = parser.GetMoreInput(nil, ErrMoreInputNeeded)
|
||||
switch err {
|
||||
case ParserHaltRequested:
|
||||
return SexpNull, err
|
||||
case ResetRequested:
|
||||
return SexpEnd, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tok.typ == TokenRSquare {
|
||||
// pop off the ]
|
||||
_, _ = lexer.GetNextToken()
|
||||
break
|
||||
}
|
||||
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
arr = append(arr, expr)
|
||||
}
|
||||
|
||||
return &SexpArray{Val: arr, Env: parser.env}, nil
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseExpression(depth int) (res Sexp, err error) {
|
||||
defer func() {
|
||||
if res != nil {
|
||||
//Q("returning from ParseExpression at depth=%v with res='%s'\n", depth, res.SexpString(nil))
|
||||
} else {
|
||||
//Q("returning from ParseExpression at depth=%v, res = nil", depth)
|
||||
}
|
||||
}()
|
||||
|
||||
lexer := parser.lexer
|
||||
env := parser.env
|
||||
|
||||
//getAnother:
|
||||
tok, err := lexer.GetNextToken()
|
||||
if err != nil {
|
||||
return SexpEnd, err
|
||||
}
|
||||
|
||||
switch tok.typ {
|
||||
case TokenLParen:
|
||||
exp, err := parser.ParseList(depth + 1)
|
||||
return exp, err
|
||||
case TokenLSquare:
|
||||
exp, err := parser.ParseArray(depth + 1)
|
||||
return exp, err
|
||||
case TokenLCurly:
|
||||
exp, err := parser.ParseInfix(depth + 1)
|
||||
return exp, err
|
||||
case TokenQuote:
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return MakeList([]Sexp{env.MakeSymbol("quote"), expr}), nil
|
||||
case TokenCaret:
|
||||
// '^' is now our syntax-quote symbol, not TokenBacktick, to allow go-style `string literals`.
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return MakeList([]Sexp{env.MakeSymbol("syntaxQuote"), expr}), nil
|
||||
case TokenTilde:
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return MakeList([]Sexp{env.MakeSymbol("unquote"), expr}), nil
|
||||
case TokenTildeAt:
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return MakeList([]Sexp{env.MakeSymbol("unquote-splicing"), expr}), nil
|
||||
case TokenFreshAssign:
|
||||
return env.MakeSymbol(tok.str), nil
|
||||
case TokenColonOperator:
|
||||
return env.MakeSymbol(tok.str), nil
|
||||
case TokenDollar:
|
||||
return env.MakeSymbol(tok.str), nil
|
||||
case TokenBool:
|
||||
return &SexpBool{Val: tok.str == "true"}, nil
|
||||
case TokenUint64:
|
||||
// truncate off the "ULL" suffix
|
||||
inp := tok.str[:len(tok.str)-3]
|
||||
|
||||
// handle hex 0x and octacl 0o
|
||||
n := len(inp)
|
||||
base := 10
|
||||
if n > 2 {
|
||||
switch inp[:2] {
|
||||
case "0o":
|
||||
base = 8
|
||||
inp = inp[2:]
|
||||
case "0x":
|
||||
base = 16
|
||||
inp = inp[2:]
|
||||
}
|
||||
}
|
||||
u, err := strconv.ParseUint(inp, base, 64)
|
||||
//fmt.Printf("debug: parsed inp='%s' into u=%v\n", inp, u)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpUint64{Val: u}, nil
|
||||
case TokenDecimal:
|
||||
i, err := strconv.ParseInt(tok.str, 10, SexpIntSize)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpInt{Val: i}, nil
|
||||
case TokenHex:
|
||||
i, err := strconv.ParseInt(tok.str, 16, SexpIntSize)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpInt{Val: i}, nil
|
||||
case TokenOct:
|
||||
i, err := strconv.ParseInt(tok.str, 8, SexpIntSize)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpInt{Val: i}, nil
|
||||
case TokenBinary:
|
||||
i, err := strconv.ParseInt(tok.str, 2, SexpIntSize)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpInt{Val: i}, nil
|
||||
case TokenChar:
|
||||
return &SexpChar{Val: rune(tok.str[0])}, nil
|
||||
case TokenString:
|
||||
return &SexpStr{S: tok.str}, nil
|
||||
case TokenBeginBacktickString:
|
||||
parser.inBacktick = true
|
||||
return parser.ParseBacktickString(&tok)
|
||||
case TokenBacktickString:
|
||||
parser.inBacktick = false
|
||||
return &SexpStr{S: tok.str, backtick: true}, nil
|
||||
case TokenFloat:
|
||||
var f float64
|
||||
if tok.str == "NaN" {
|
||||
f = NaN
|
||||
} else {
|
||||
f, err = strconv.ParseFloat(tok.str, SexpFloatSize)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
r := &SexpFloat{Val: f}
|
||||
if strings.Contains(tok.str, "e") || strings.Contains(tok.str, "E") {
|
||||
r.Scientific = true
|
||||
}
|
||||
return r, nil
|
||||
|
||||
case TokenEnd:
|
||||
return SexpEnd, nil
|
||||
case TokenSymbol:
|
||||
return env.MakeSymbol(tok.str), nil
|
||||
case TokenSymbolColon:
|
||||
sym := env.MakeSymbol(tok.str)
|
||||
sym.colonTail = true
|
||||
return sym, nil
|
||||
case TokenDot:
|
||||
sym := env.MakeSymbol(tok.str)
|
||||
sym.isDot = true
|
||||
return sym, nil
|
||||
case TokenDotSymbol:
|
||||
sym := env.MakeSymbol(tok.str)
|
||||
sym.isDot = true
|
||||
return sym, nil
|
||||
case TokenComment:
|
||||
//Q("parser making SexpComment from '%s'", tok.str)
|
||||
return &SexpComment{Comment: tok.str}, nil
|
||||
// parser skips comments
|
||||
//goto getAnother
|
||||
case TokenBeginBlockComment:
|
||||
// parser skips comments
|
||||
return parser.ParseBlockComment(&tok)
|
||||
//parser.ParseBlockComment(&tok)
|
||||
//goto getAnother
|
||||
case TokenComma:
|
||||
return &SexpComma{}, nil
|
||||
case TokenSemicolon:
|
||||
return &SexpSemicolon{}, nil
|
||||
}
|
||||
return SexpNull, fmt.Errorf("Invalid syntax, don't know what to do with %v '%v'", tok.typ, tok)
|
||||
}
|
||||
|
||||
// ParseTokens is the main service the Parser provides.
|
||||
// Currently returns first error encountered, ignoring
|
||||
// any expressions after that.
|
||||
func (p *Parser) ParseTokens() ([]Sexp, error) {
|
||||
select {
|
||||
case out := <-p.ParsedOutput:
|
||||
Q("ParseTokens got p.ParsedOutput out: '%#v'", out)
|
||||
r := make([]Sexp, 0)
|
||||
for _, k := range out {
|
||||
r = append(r, k.Expr...)
|
||||
Q("\n ParseTokens k.Expr = '%v'\n\n", (&SexpArray{Val: k.Expr, Env: p.env}).SexpString(nil))
|
||||
if k.Err != nil {
|
||||
return r, k.Err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
case <-p.reqStop:
|
||||
return nil, ErrShuttingDown
|
||||
}
|
||||
}
|
||||
|
||||
var ErrShuttingDown error = fmt.Errorf("lexer shutting down")
|
||||
|
||||
func (parser *Parser) ParseBlockComment(start *Token) (sx Sexp, err error) {
|
||||
defer func() {
|
||||
if sx != nil {
|
||||
//Q("returning from ParseBlockComment with sx ='%v', err='%v'",
|
||||
// sx.SexpString(), err)
|
||||
}
|
||||
}()
|
||||
lexer := parser.lexer
|
||||
var tok Token
|
||||
var block = &SexpComment{Block: true, Comment: start.str}
|
||||
|
||||
for {
|
||||
tokFilled:
|
||||
for {
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if tok.typ != TokenEnd {
|
||||
break tokFilled
|
||||
}
|
||||
err = parser.GetMoreInput(nil, ErrMoreInputNeeded)
|
||||
switch err {
|
||||
case ParserHaltRequested:
|
||||
return SexpNull, err
|
||||
case ResetRequested:
|
||||
return SexpEnd, err
|
||||
}
|
||||
// have to still fill tok, so
|
||||
// loop to the top to PeekNextToken
|
||||
}
|
||||
|
||||
// consume it
|
||||
|
||||
//cons, err := lexer.GetNextToken()
|
||||
_, err := lexer.GetNextToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//Q("parse block comment is consuming '%v'", cons)
|
||||
|
||||
switch tok.typ {
|
||||
case TokenEndBlockComment:
|
||||
block.Comment += tok.str
|
||||
return block, nil
|
||||
case TokenComment:
|
||||
block.Comment += tok.str
|
||||
default:
|
||||
panic("internal error: inside a block comment, we should only see TokenComment and TokenEndBlockComment tokens")
|
||||
}
|
||||
}
|
||||
//return block, nil
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseBacktickString(start *Token) (sx Sexp, err error) {
|
||||
defer func() {
|
||||
if sx != nil {
|
||||
//Q("returning from ParseBlockComment with sx ='%v', err='%v'",
|
||||
// sx.SexpString(), err)
|
||||
}
|
||||
}()
|
||||
lexer := parser.lexer
|
||||
var tok Token
|
||||
|
||||
for {
|
||||
tokFilled:
|
||||
for {
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if tok.typ != TokenEnd {
|
||||
break tokFilled
|
||||
}
|
||||
err = parser.GetMoreInput(nil, ErrMoreInputNeeded)
|
||||
switch err {
|
||||
case ParserHaltRequested:
|
||||
return SexpNull, err
|
||||
case ResetRequested:
|
||||
return SexpEnd, err
|
||||
}
|
||||
// have to still fill tok, so
|
||||
// loop to the top to PeekNextToken
|
||||
}
|
||||
|
||||
// consume it
|
||||
|
||||
//cons, err := lexer.GetNextToken()
|
||||
_, err := lexer.GetNextToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//P("parse backtick string is consuming '%v'", cons)
|
||||
|
||||
switch tok.typ {
|
||||
case TokenBacktickString:
|
||||
return &SexpStr{S: tok.str, backtick: true}, nil
|
||||
default:
|
||||
panic("internal error: inside a backtick string, we should only see TokenBacktickString token")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseInfix(depth int) (Sexp, error) {
|
||||
lexer := parser.lexer
|
||||
arr := make([]Sexp, 0, SliceDefaultCap)
|
||||
var err error
|
||||
var tok Token
|
||||
for {
|
||||
getTok:
|
||||
for {
|
||||
tok, err = lexer.PeekNextToken()
|
||||
if err != nil {
|
||||
return SexpEnd, err
|
||||
}
|
||||
|
||||
if tok.typ != TokenEnd {
|
||||
break getTok
|
||||
} else {
|
||||
//instead of return SexpEnd, UnexpectedEnd
|
||||
// we ask for more, and then loop
|
||||
err = parser.GetMoreInput(nil, ErrMoreInputNeeded)
|
||||
switch err {
|
||||
case ParserHaltRequested:
|
||||
return SexpNull, err
|
||||
case ResetRequested:
|
||||
return SexpEnd, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tok.typ == TokenRCurly {
|
||||
// pop off the }
|
||||
_, _ = lexer.GetNextToken()
|
||||
break
|
||||
}
|
||||
|
||||
Q("debug: ParseInfix(depth=%v) calling ParseExpression", depth)
|
||||
expr, err := parser.ParseExpression(depth + 1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
Q("debug2: ParseInfix(depth=%v) appending expr = '%v'", depth, expr.SexpString(nil))
|
||||
|
||||
arr = append(arr, expr)
|
||||
}
|
||||
|
||||
var list SexpPair
|
||||
list.Head = parser.env.MakeSymbol("infix")
|
||||
list.Tail = SexpNull
|
||||
if len(arr) > 0 {
|
||||
list.Tail = Cons(&SexpArray{Val: arr, Infix: true, Env: parser.env}, SexpNull)
|
||||
}
|
||||
return &list, nil
|
||||
//return &SexpArray{Val: arr, Infix: true, Env: env}, nil
|
||||
}
|
||||
670
vendor/github.com/glycerine/zygomys/zygo/pratt.go
generated
vendored
Normal file
670
vendor/github.com/glycerine/zygomys/zygo/pratt.go
generated
vendored
Normal file
@@ -0,0 +1,670 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Pratt parsing. see http://javascript.crockford.com/tdop/tdop.html
|
||||
// Also nice writeup: http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
|
||||
// precedence levels (smaller == lower priority,
|
||||
// so smaller => goes towards top of tree)
|
||||
//
|
||||
// Borrowing from the tdop.html precedence list mostly:
|
||||
//
|
||||
// 0 non-binding operators like ;
|
||||
// 10 assignment operators like = :=
|
||||
// 20 ?
|
||||
// 30 or and
|
||||
// 40 relational operators like ==
|
||||
// 50 + -
|
||||
// 60 * /
|
||||
// 65 **
|
||||
// 70 unary operators like 'not'
|
||||
// 80 . [ (
|
||||
//
|
||||
|
||||
// InfixOp lets us attach led (MunchLeft) and nud (MunchRight)
|
||||
// Pratt parsing methods, along with a binding power, to a symbol.
|
||||
type InfixOp struct {
|
||||
Sym *SexpSymbol
|
||||
Bp int // binding power, aka precedence level.
|
||||
MunchRight RightMuncher // aka nud
|
||||
MunchLeft LeftMuncher // aka led
|
||||
MunchStmt StmtMuncher // aka std. Used only at the beginning of a statement.
|
||||
IsAssign bool
|
||||
}
|
||||
|
||||
// Infix creates a new infix operator
|
||||
func (env *Zlisp) Infix(op string, bp int) *InfixOp {
|
||||
oper := env.MakeSymbol(op)
|
||||
iop := &InfixOp{
|
||||
Sym: oper,
|
||||
Bp: bp,
|
||||
MunchLeft: func(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
right, err := pr.Expression(env, bp)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
list := MakeList([]Sexp{
|
||||
oper, left, right,
|
||||
})
|
||||
|
||||
Q("in Infix(), MunchLeft() call, pr.NextToken = %v. list returned = '%v'",
|
||||
pr.NextToken.SexpString(nil), list.SexpString(nil))
|
||||
return list, nil
|
||||
},
|
||||
}
|
||||
env.infixOps[op] = iop
|
||||
return iop
|
||||
}
|
||||
|
||||
func (env *Zlisp) InfixF(op string, bp int, f func(env *Zlisp, op string, bp int) *InfixOp) *InfixOp {
|
||||
return f(env, op, bp)
|
||||
}
|
||||
|
||||
// Infix creates a new (right-associative) short-circuiting
|
||||
// infix operator, used for `and` and `or` in infix processing.
|
||||
func (env *Zlisp) Infixr(op string, bp int) *InfixOp {
|
||||
oper := env.MakeSymbol(op)
|
||||
iop := &InfixOp{
|
||||
Sym: oper,
|
||||
Bp: bp,
|
||||
MunchLeft: func(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
right, err := pr.Expression(env, bp-1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
list := MakeList([]Sexp{
|
||||
oper, left, right,
|
||||
})
|
||||
return list, nil
|
||||
},
|
||||
}
|
||||
env.infixOps[op] = iop
|
||||
return iop
|
||||
}
|
||||
|
||||
// Prefix creates a new prefix operator, like `not`, for
|
||||
// infix processing.
|
||||
func (env *Zlisp) Prefix(op string, bp int) *InfixOp {
|
||||
oper := env.MakeSymbol(op)
|
||||
iop := &InfixOp{
|
||||
Sym: oper,
|
||||
Bp: bp,
|
||||
MunchRight: func(env *Zlisp, pr *Pratt) (Sexp, error) {
|
||||
right, err := pr.Expression(env, bp)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
list := MakeList([]Sexp{
|
||||
oper, right,
|
||||
})
|
||||
return list, nil
|
||||
},
|
||||
}
|
||||
env.infixOps[op] = iop
|
||||
return iop
|
||||
}
|
||||
|
||||
// Assignment creates a new assignment operator for infix
|
||||
// processing.
|
||||
func (env *Zlisp) Assignment(op string, bp int) *InfixOp {
|
||||
oper := env.MakeSymbol(op)
|
||||
operSet := env.MakeSymbol("set")
|
||||
iop := &InfixOp{
|
||||
Sym: oper,
|
||||
Bp: bp,
|
||||
MunchLeft: func(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
// TODO: check that left is okay as an LVALUE.
|
||||
|
||||
right, err := pr.Expression(env, bp-1)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if op == "=" || op == ":=" {
|
||||
oper = operSet
|
||||
}
|
||||
|
||||
list := MakeList([]Sexp{
|
||||
oper, left, right,
|
||||
})
|
||||
Q("assignment returning list: '%v'", list.SexpString(nil))
|
||||
return list, nil
|
||||
},
|
||||
IsAssign: true,
|
||||
}
|
||||
env.infixOps[op] = iop
|
||||
return iop
|
||||
}
|
||||
|
||||
// PostfixAssign creates a new postfix assignment operator for infix
|
||||
// processing.
|
||||
func (env *Zlisp) PostfixAssign(op string, bp int) *InfixOp {
|
||||
oper := env.MakeSymbol(op)
|
||||
iop := &InfixOp{
|
||||
Sym: oper,
|
||||
Bp: bp,
|
||||
MunchLeft: func(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
// TODO: check that left is okay as an LVALUE
|
||||
list := MakeList([]Sexp{
|
||||
oper, left,
|
||||
})
|
||||
Q("postfix assignment returning list: '%v'", list.SexpString(nil))
|
||||
return list, nil
|
||||
},
|
||||
}
|
||||
env.infixOps[op] = iop
|
||||
return iop
|
||||
}
|
||||
|
||||
func arrayOpMunchLeft(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
oper := env.MakeSymbol("arrayidx")
|
||||
Q("pr.NextToken = '%v', left = %#v", pr.NextToken.SexpString(nil), left)
|
||||
if len(pr.CnodeStack) > 0 {
|
||||
Q("pr.CnodeStack[0] = '%v'", pr.CnodeStack[0])
|
||||
}
|
||||
|
||||
right := pr.NextToken
|
||||
Q("right = %#v", right)
|
||||
list := MakeList([]Sexp{
|
||||
oper, left, pr.CnodeStack[0],
|
||||
})
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func dotOpMunchLeft(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error) {
|
||||
//Q("dotOp MunchLeft, left = '%v'. NextToken='%v'. pr.CnodeStack[0]='%v'", left.SexpString(nil), pr.NextToken.SexpString(nil), pr.CnodeStack[0].SexpString(nil))
|
||||
list := MakeList([]Sexp{
|
||||
env.MakeSymbol("hashidx"), left, pr.CnodeStack[0],
|
||||
})
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func starOpMunchRight(env *Zlisp, pr *Pratt) (Sexp, error) {
|
||||
right, err := pr.Expression(env, 70)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
list := MakeList([]Sexp{
|
||||
env.MakeSymbol("*"), right,
|
||||
})
|
||||
return list, nil
|
||||
}
|
||||
|
||||
var arrayOp *InfixOp
|
||||
|
||||
// InitInfixOps establishes the env.infixOps definitions
|
||||
// required for infix parsing using the Pratt parser.
|
||||
func (env *Zlisp) InitInfixOps() {
|
||||
env.Infix("+", 50)
|
||||
env.Infix("-", 50)
|
||||
|
||||
star := env.Infix("*", 60)
|
||||
star.MunchRight = starOpMunchRight
|
||||
|
||||
env.Infix("/", 60)
|
||||
env.Infix("mod", 60)
|
||||
env.Infixr("**", 65)
|
||||
env.Infixr("and", 30)
|
||||
env.Infixr("or", 30)
|
||||
env.Prefix("not", 70)
|
||||
env.Assignment("=", 10)
|
||||
env.Assignment(":=", 10)
|
||||
env.Assignment("+=", 10)
|
||||
env.Assignment("-=", 10)
|
||||
env.PostfixAssign("++", 10)
|
||||
env.PostfixAssign("--", 10)
|
||||
|
||||
env.Infix("==", 40)
|
||||
env.Infix("!=", 40)
|
||||
env.Infix(">", 40)
|
||||
env.Infix(">=", 40)
|
||||
env.Infix("<", 40)
|
||||
env.Infix("<=", 40)
|
||||
|
||||
// set the global arrayOp
|
||||
arrayOp = &InfixOp{
|
||||
Bp: 80,
|
||||
MunchLeft: arrayOpMunchLeft,
|
||||
}
|
||||
|
||||
dotOp := env.Infix(".", 80)
|
||||
dotOp.MunchLeft = dotOpMunchLeft
|
||||
|
||||
ifOp := env.Prefix("if", 5)
|
||||
//Q("ifOp = %#v", ifOp.SexpString(nil))
|
||||
|
||||
ifOp.MunchRight = func(env *Zlisp, pr *Pratt) (Sexp, error) {
|
||||
Q("ifOp.MunchRight(): NextToken='%v'. pr.CnodeStack[0]='%v'", pr.NextToken.SexpString(nil), pr.CnodeStack[0].SexpString(nil))
|
||||
right, err := pr.Expression(env, 5)
|
||||
Q("ifOp.MunchRight: back from Expression-1st-call, err = %#v, right = '%v'", err, right.SexpString(nil))
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
Q("in ifOpMunchRight, got from p.Expression(env, 0) the right = '%v', err = %#v, pr.CnodeStack[0] = %#v, ifOp.Sym = '%v'", right.SexpString(nil), err, pr.CnodeStack[0], ifOp.Sym.SexpString(nil))
|
||||
|
||||
thenExpr, err := pr.Expression(env, 0)
|
||||
Q("ifOp.MunchRight: back from Expression-2nd-call, err = %#v, thenExpr = '%v'", err, thenExpr.SexpString(nil))
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
Q("ifOp.MunchRight(), after Expression-2nd-call: . NextToken='%v'. pr.CnodeStack[0]='%v'", pr.NextToken.SexpString(nil), pr.CnodeStack[0].SexpString(nil))
|
||||
var elseExpr Sexp = SexpNull
|
||||
switch sym := pr.NextToken.(type) {
|
||||
case *SexpSymbol:
|
||||
if sym.name == "else" {
|
||||
Q("detected else, advancing past it")
|
||||
pr.Advance()
|
||||
elseExpr, err = pr.Expression(env, 0)
|
||||
Q("ifOp.MunchRight: back from Expression-3rd-call, err = %#v, elseExpr = '%v'", err, elseExpr.SexpString(nil))
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list := MakeList([]Sexp{
|
||||
env.MakeSymbol("cond"), right, thenExpr, elseExpr,
|
||||
})
|
||||
return list, nil
|
||||
}
|
||||
|
||||
env.Infix("comma", 15)
|
||||
}
|
||||
|
||||
type RightMuncher func(env *Zlisp, pr *Pratt) (Sexp, error)
|
||||
type LeftMuncher func(env *Zlisp, pr *Pratt, left Sexp) (Sexp, error)
|
||||
type StmtMuncher func(env *Zlisp, pr *Pratt) (Sexp, error)
|
||||
|
||||
func InfixBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("InfixBuilder top, name='%s', len(args)==%v ", name, len(args))
|
||||
if name != "infixExpand" && len(args) != 1 {
|
||||
// let {} mean nil
|
||||
return SexpNull, nil
|
||||
}
|
||||
var arr *SexpArray
|
||||
Q("InfixBuilder after top, args[0] has type ='%T' ", args[0])
|
||||
switch v := args[0].(type) {
|
||||
case *SexpArray:
|
||||
arr = v
|
||||
case *SexpPair:
|
||||
if name == "infixExpand" {
|
||||
_, isSent := v.Tail.(*SexpSentinel)
|
||||
if isSent {
|
||||
// expansion of {} is nil
|
||||
return SexpNull, nil
|
||||
}
|
||||
pair, isPair := v.Tail.(*SexpPair)
|
||||
if !isPair {
|
||||
return SexpNull, fmt.Errorf("infixExpand expects (infix []) as its argument; instead we saw '%T' [err 3]", v.Tail)
|
||||
}
|
||||
switch ar2 := pair.Head.(type) {
|
||||
case *SexpArray:
|
||||
Q("infixExpand, doing recursive call to InfixBuilder, ar2 = '%v'", ar2.SexpString(nil))
|
||||
return InfixBuilder(env, name, []Sexp{ar2})
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("infixExpand expects (infix []) as its argument; instead we saw '%T'", v.Tail)
|
||||
}
|
||||
}
|
||||
return SexpNull, fmt.Errorf("InfixBuilder must receive an SexpArray")
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("InfixBuilder must receive an SexpArray")
|
||||
}
|
||||
Q("InfixBuilder, name='%s', arr = ", name)
|
||||
for i := range arr.Val {
|
||||
Q("arr[%v] = '%v', of type %T", i, arr.Val[i].SexpString(nil), arr.Val[i])
|
||||
}
|
||||
pr := NewPratt(arr.Val)
|
||||
xs := []Sexp{}
|
||||
|
||||
if name == "infixExpand" {
|
||||
xs = append(xs, env.MakeSymbol("quote"))
|
||||
}
|
||||
|
||||
for {
|
||||
x, err := pr.Expression(env, 0)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
if x == nil {
|
||||
Q("x was nil")
|
||||
} else {
|
||||
Q("x back is not nil and is of type %T/val = '%v', err = %v", x, x.SexpString(nil), err)
|
||||
}
|
||||
_, isSemi := x.(*SexpSemicolon)
|
||||
if !isSemi {
|
||||
xs = append(xs, x)
|
||||
}
|
||||
Q("end of infix builder loop, pr.NextToken = '%v'", pr.NextToken.SexpString(nil))
|
||||
if pr.IsEOF() {
|
||||
break
|
||||
}
|
||||
|
||||
_, nextIsSemi := pr.NextToken.(*SexpSemicolon)
|
||||
if nextIsSemi {
|
||||
pr.Advance() // skip over the semicolon
|
||||
}
|
||||
}
|
||||
Q("infix builder loop done, here are my expressions:")
|
||||
for i, ele := range xs {
|
||||
Q("xs[%v] = %v", i, ele.SexpString(nil))
|
||||
}
|
||||
|
||||
if name == "infixExpand" {
|
||||
ret := MakeList(xs)
|
||||
Q("infixExpand: returning ret = '%v'", ret.SexpString(nil))
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
ev, err := EvalFunction(env, "infixEval", xs)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
type Pratt struct {
|
||||
NextToken Sexp
|
||||
CnodeStack []Sexp
|
||||
AccumTree Sexp
|
||||
|
||||
Pos int
|
||||
Stream []Sexp
|
||||
}
|
||||
|
||||
func NewPratt(stream []Sexp) *Pratt {
|
||||
p := &Pratt{
|
||||
NextToken: SexpNull,
|
||||
AccumTree: SexpNull,
|
||||
CnodeStack: make([]Sexp, 0),
|
||||
Stream: stream,
|
||||
}
|
||||
if len(stream) > 0 {
|
||||
p.NextToken = stream[0]
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Expression():
|
||||
//
|
||||
// From Douglas Crockford's article on Pratt parsing:
|
||||
// "Top Down Operator Precedence"
|
||||
// http://javascript.crockford.com/tdop/tdop.html
|
||||
//
|
||||
// The heart of Pratt's technique is the expression
|
||||
// function. It takes a right binding power that
|
||||
// controls how aggressively it binds to tokens on its right.
|
||||
// expression calls the nud method of the token.
|
||||
//
|
||||
// The nud is used to process literals, variables,
|
||||
// and prefix operators.
|
||||
//
|
||||
// Then as long as the right binding
|
||||
// power is less than the left binding power of the next
|
||||
// token, the led method is invoked on the following
|
||||
// token. The led is used to process infix and
|
||||
// suffix operators. This process can be recursive
|
||||
// because the nud and led
|
||||
// methods can call expression.
|
||||
//
|
||||
// In pseudo Java script:
|
||||
//
|
||||
// var expression = function (rbp) {
|
||||
// var left;
|
||||
// var t = token;
|
||||
// advance();
|
||||
// left = t.nud();
|
||||
// while (rbp < token.lbp) {
|
||||
// t = token;
|
||||
// advance();
|
||||
// left = t.led(left);
|
||||
// }
|
||||
// return left;
|
||||
// }
|
||||
//
|
||||
// jea: Below is a working expression() parsing routine. Reproduces the
|
||||
// original Pratt and Crockford formulation.
|
||||
//
|
||||
// AccumTree holds the accumulated parse tree at any point in time.
|
||||
// "The parse Tree Up to this point, by consuming the tokens
|
||||
// to the left" would be a better-but-too-long name.
|
||||
//
|
||||
// and AccumTree is the stuff to the left of the
|
||||
// current operator in the parse stream.
|
||||
//
|
||||
// data flows from NextToken -> cnode -> (possibly on the stack of t
|
||||
// recursive MunchLeft calls) -> into the AccumTree tree.
|
||||
//
|
||||
// better names: _left -> AccumTree (to be returned)
|
||||
// t -> cnode; as it is the current token's qtree
|
||||
// node to be processed. Once we grab this
|
||||
// we always advance() past it
|
||||
// before processing it, so that
|
||||
// NextToken contains the
|
||||
// following token.
|
||||
//
|
||||
//
|
||||
// meaning of rbp parameter: if you hit a token with
|
||||
// a NextToken.Lbp < rbp, then don't bother calling MunchLeft,
|
||||
// stop and return what you have.
|
||||
//
|
||||
// better explanation: rbp = a lower bound on descendant nodes
|
||||
// precedence level, so we can
|
||||
// guarantee the precedence-hill-climbing property (small precedence
|
||||
// at the top) in the resulting parse tree.
|
||||
//
|
||||
|
||||
func (p *Pratt) Expression(env *Zlisp, rbp int) (ret Sexp, err error) {
|
||||
defer func() {
|
||||
if ret == nil {
|
||||
Q("Expression is returning Sexp ret = nil")
|
||||
} else {
|
||||
Q("Expression is returning Sexp ret = '%v'", ret.SexpString(nil))
|
||||
}
|
||||
}()
|
||||
|
||||
cnode := p.NextToken
|
||||
if cnode != nil {
|
||||
Q("top of Expression, rbp = %v, cnode = '%v'", rbp, cnode.SexpString(nil))
|
||||
} else {
|
||||
Q("top of Expression, rbp = %v, cnode is nil", rbp)
|
||||
}
|
||||
if p.IsEOF() {
|
||||
Q("Expression sees IsEOF, returning cnode = %v", cnode.SexpString(nil))
|
||||
return cnode, nil
|
||||
}
|
||||
p.CnodeStack = append([]Sexp{p.NextToken}, p.CnodeStack...)
|
||||
//p.ShowCnodeStack()
|
||||
|
||||
p.Advance()
|
||||
|
||||
var curOp *InfixOp
|
||||
switch x := cnode.(type) {
|
||||
case *SexpSymbol:
|
||||
op, found := env.infixOps[x.name]
|
||||
if found {
|
||||
Q("Expression lookup of op.Sym=%v/op='%#v' succeeded", op.Sym.SexpString(nil), op)
|
||||
curOp = op
|
||||
} else {
|
||||
Q("Expression lookup of x.name == '%v' failed", x.name)
|
||||
}
|
||||
case *SexpArray:
|
||||
Q("in pratt parsing, got array x = '%v'", x.SexpString(nil))
|
||||
}
|
||||
|
||||
if curOp != nil && curOp.MunchRight != nil {
|
||||
// munch_right() of atoms returns this/itself, in which
|
||||
// case: p.AccumTree = t; is the result.
|
||||
Q("about to MunchRight on cnode = %v", cnode.SexpString(nil))
|
||||
p.AccumTree, err = curOp.MunchRight(env, p)
|
||||
if err != nil {
|
||||
Q("Expression(%v) MunchRight saw err = %v", rbp, err)
|
||||
return SexpNull, err
|
||||
}
|
||||
Q("after MunchRight on cnode = %v, p.AccumTree = '%v'",
|
||||
cnode.SexpString(nil), p.AccumTree.SexpString(nil))
|
||||
} else {
|
||||
// do this, or have the default MunchRight return itself.
|
||||
p.AccumTree = cnode
|
||||
}
|
||||
|
||||
for !p.IsEOF() {
|
||||
nextLbp, err := env.LeftBindingPower(p.NextToken)
|
||||
if err != nil {
|
||||
Q("env.LeftBindingPower('%s') saw err = %v",
|
||||
p.NextToken.SexpString(nil), err)
|
||||
return SexpNull, err
|
||||
}
|
||||
Q("nextLbp = %v, and rbp = %v, so rpb >= nextLbp == %v", nextLbp, rbp, rbp >= nextLbp)
|
||||
if rbp >= nextLbp {
|
||||
Q("found rbp >= nextLbp so breaking out of left-binding loop")
|
||||
break
|
||||
}
|
||||
|
||||
cnode = p.NextToken
|
||||
curOp = nil
|
||||
switch x := cnode.(type) {
|
||||
case *SexpSymbol:
|
||||
op, found := env.infixOps[x.name]
|
||||
if found {
|
||||
Q("assigning curOp <- cnode '%s'", x.name)
|
||||
curOp = op
|
||||
} else {
|
||||
if x.isDot {
|
||||
curOp = env.infixOps["."]
|
||||
Q("assigning curOp <- dotInfixOp; then curOp = %#v", curOp)
|
||||
}
|
||||
}
|
||||
case *SexpArray:
|
||||
Q("assigning curOp <- arrayOp")
|
||||
curOp = arrayOp
|
||||
case *SexpComma:
|
||||
curOp = env.infixOps["comma"]
|
||||
Q("assigning curOp <- infixOps[`comma`]; then curOp = %#v", curOp)
|
||||
case *SexpPair:
|
||||
// sexp-call, treat like function call with rbp 80
|
||||
Q("Expression sees an SexpPair")
|
||||
// leaving curOp nil seems to work just fine here.
|
||||
default:
|
||||
panic(fmt.Errorf("how to handle cnode type = %#v", cnode))
|
||||
}
|
||||
Q("curOp = %#v", curOp)
|
||||
|
||||
p.CnodeStack[0] = p.NextToken
|
||||
//_cnode_stack.front() = NextToken;
|
||||
|
||||
Q("in MunchLeft loop, before Advance, p.NextToken = %v",
|
||||
p.NextToken.SexpString(nil))
|
||||
p.Advance()
|
||||
if p.Pos < len(p.Stream) {
|
||||
Q("in MunchLeft loop, after Advance, p.NextToken = %v",
|
||||
p.NextToken.SexpString(nil))
|
||||
}
|
||||
|
||||
// if cnode->munch_left() returns this/itself, then
|
||||
// the net effect is: p.AccumTree = cnode;
|
||||
if curOp != nil && curOp.MunchLeft != nil {
|
||||
Q("about to MunchLeft, cnode = %v, p.AccumTree = %v", cnode.SexpString(nil), p.AccumTree.SexpString(nil))
|
||||
p.AccumTree, err = curOp.MunchLeft(env, p, p.AccumTree)
|
||||
if err != nil {
|
||||
Q("curOp.MunchLeft saw err = %v", err)
|
||||
return SexpNull, err
|
||||
}
|
||||
} else {
|
||||
Q("curOp has not MunchLeft, setting AccumTree <- cnode. here cnode = %v", cnode.SexpString(nil))
|
||||
// do this, or have the default MunchLeft return itself.
|
||||
p.AccumTree = cnode
|
||||
}
|
||||
|
||||
} // end for !p.IsEOF()
|
||||
|
||||
p.CnodeStack = p.CnodeStack[1:]
|
||||
//_cnode_stack.pop_front()
|
||||
Q("at end of Expression(%v), returning p.AccumTree=%v, err=nil", rbp, p.AccumTree.SexpString(nil))
|
||||
return p.AccumTree, nil
|
||||
}
|
||||
|
||||
// Advance sets p.NextToken
|
||||
func (p *Pratt) Advance() error {
|
||||
p.Pos++
|
||||
if p.Pos >= len(p.Stream) {
|
||||
return io.EOF
|
||||
}
|
||||
p.NextToken = p.Stream[p.Pos]
|
||||
Q("end of Advance, p.NextToken = '%v'", p.NextToken.SexpString(nil))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pratt) IsEOF() bool {
|
||||
if p.Pos >= len(p.Stream) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (env *Zlisp) LeftBindingPower(sx Sexp) (int, error) {
|
||||
Q("LeftBindingPower: sx is '%v'", sx.SexpString(nil))
|
||||
switch x := sx.(type) {
|
||||
case *SexpInt:
|
||||
return 0, nil
|
||||
case *SexpBool:
|
||||
return 0, nil
|
||||
case *SexpStr:
|
||||
return 0, nil
|
||||
case *SexpSymbol:
|
||||
op, found := env.infixOps[x.name]
|
||||
if x.name == "if" {
|
||||
// we don't want if to be doing any binding to the left,
|
||||
// so we enforce that it has zero left-binding power. It
|
||||
// gets a right-binding power of 5 since it is a prefix operator.
|
||||
Q("LeftBindingPower: found if, return 0 left-binding-power")
|
||||
return 0, nil
|
||||
}
|
||||
if found {
|
||||
Q("LeftBindingPower: found op '%#v', returning op.Bp = %v", op, op.Bp)
|
||||
return op.Bp, nil
|
||||
}
|
||||
if x.isDot {
|
||||
Q("LeftBindingPower: dot symbol '%v', "+
|
||||
"giving it binding-power 80", x.name)
|
||||
return 80, nil
|
||||
}
|
||||
Q("LeftBindingPower: no entry in env.infixOps for operation '%s'",
|
||||
x.name)
|
||||
return 0, nil
|
||||
case *SexpArray:
|
||||
return 80, nil
|
||||
case *SexpComma:
|
||||
return 15, nil
|
||||
case *SexpSemicolon:
|
||||
return 0, nil
|
||||
case *SexpComment:
|
||||
return 0, nil
|
||||
case *SexpPair:
|
||||
if x.Head != nil {
|
||||
switch sym := x.Head.(type) {
|
||||
case *SexpSymbol:
|
||||
if sym.name == "infix" {
|
||||
Q("detected infix!!! -- setting binding power to 0")
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("LeftBindingPower: unhandled sx :%#v", sx)
|
||||
}
|
||||
|
||||
func (p *Pratt) ShowCnodeStack() {
|
||||
if len(p.CnodeStack) == 0 {
|
||||
fmt.Println("CnodeStack is: empty")
|
||||
return
|
||||
}
|
||||
fmt.Println("CnodeStack is:")
|
||||
for i := range p.CnodeStack {
|
||||
fmt.Printf("CnodeStack[%v] = %v\n", i, p.CnodeStack[i].SexpString(nil))
|
||||
}
|
||||
}
|
||||
100
vendor/github.com/glycerine/zygomys/zygo/printstate.go
generated
vendored
Normal file
100
vendor/github.com/glycerine/zygomys/zygo/printstate.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// begin supporting SexpString structs
|
||||
|
||||
// PrintState threads the state of display through SexpString() and Show() calls,
|
||||
// to give pretty-printing indentation and to avoid infinite looping on
|
||||
// cyclic data structures.
|
||||
type PrintState struct {
|
||||
Indent int
|
||||
Seen Seen
|
||||
}
|
||||
|
||||
func (ps *PrintState) SetSeen(x interface{}, name string) {
|
||||
if ps == nil {
|
||||
panic("can't SetSeen on a nil PrintState")
|
||||
}
|
||||
ps.Seen[x] = struct{}{}
|
||||
}
|
||||
|
||||
func (ps *PrintState) GetSeen(x interface{}) bool {
|
||||
if ps == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := ps.Seen[x]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (ps *PrintState) GetIndent() int {
|
||||
if ps == nil {
|
||||
return 0
|
||||
}
|
||||
return ps.Indent
|
||||
}
|
||||
|
||||
func (ps *PrintState) AddIndent(addme int) *PrintState {
|
||||
if ps == nil {
|
||||
return &PrintState{
|
||||
Indent: addme,
|
||||
Seen: NewSeen(),
|
||||
}
|
||||
}
|
||||
return &PrintState{
|
||||
Indent: ps.Indent + addme,
|
||||
Seen: ps.Seen,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrintState() *PrintState {
|
||||
return &PrintState{
|
||||
Seen: NewSeen(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrintStateWithIndent(indent int) *PrintState {
|
||||
return &PrintState{
|
||||
Indent: indent,
|
||||
Seen: NewSeen(),
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PrintState) Clear() {
|
||||
ps.Indent = 0
|
||||
ps.Seen = NewSeen()
|
||||
}
|
||||
|
||||
func (ps *PrintState) Dump() {
|
||||
fmt.Printf("ps Dump: ")
|
||||
if ps == nil {
|
||||
fmt.Printf("nil\n")
|
||||
return
|
||||
}
|
||||
for k, v := range ps.Seen {
|
||||
fmt.Printf("ps Dump: %p -- %v\n", k, v)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
// Seen tracks if a value has already been displayed, to
|
||||
// detect and avoid cycles.
|
||||
//
|
||||
/* Q: How to do garbage-collection safe graph traversal in a graph of Go objects?
|
||||
|
||||
A: "Instead of converting the pointer to a uintptr, just store the pointer
|
||||
itself in a map[interface{}]bool. If you encounter the same pointer
|
||||
again, you will get the same map entry. The GC must guarantee that
|
||||
using pointers as map keys will work even if the pointers move."
|
||||
|
||||
- Ian Lance Taylor on golang-nuts (2016 June 20).
|
||||
*/
|
||||
type Seen map[interface{}]struct{}
|
||||
|
||||
func NewSeen() Seen {
|
||||
return Seen(make(map[interface{}]struct{}))
|
||||
}
|
||||
|
||||
// end supporting SexpString structs
|
||||
59
vendor/github.com/glycerine/zygomys/zygo/ptrcheck.go
generated
vendored
Normal file
59
vendor/github.com/glycerine/zygomys/zygo/ptrcheck.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// true if target is type *T where T
|
||||
// is a struct/string/int/other-non-pointer type.
|
||||
func IsExactlySinglePointer(target interface{}) bool {
|
||||
// va, isVa := target.(reflect.Value)
|
||||
// if isVa {
|
||||
// return IsExactlySinglePointerType(va.Type())
|
||||
// }
|
||||
typ := reflect.ValueOf(target).Type()
|
||||
return IsExactlySinglePointerType(typ)
|
||||
}
|
||||
func IsExactlySinglePointerType(typ reflect.Type) bool {
|
||||
kind := typ.Kind()
|
||||
if kind != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
typ2 := typ.Elem()
|
||||
kind2 := typ2.Kind()
|
||||
if kind2 == reflect.Ptr {
|
||||
return false // two level pointer
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// true if target is of type **T where T is
|
||||
// a struct/string/int/other-non-pointer type.
|
||||
func IsExactlyDoublePointer(target interface{}) bool {
|
||||
typ := reflect.ValueOf(target).Type()
|
||||
kind := typ.Kind()
|
||||
if kind != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
typ2 := typ.Elem()
|
||||
kind2 := typ2.Kind()
|
||||
if kind2 != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
if typ2.Elem().Kind() == reflect.Ptr {
|
||||
return false // triple level pointer, not double.
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func PointerDepth(typ reflect.Type) int {
|
||||
return pointerDepthHelper(typ, 0)
|
||||
}
|
||||
|
||||
func pointerDepthHelper(typ reflect.Type, accum int) int {
|
||||
kind := typ.Kind()
|
||||
if kind != reflect.Ptr {
|
||||
return accum
|
||||
}
|
||||
return pointerDepthHelper(typ.Elem(), accum+1)
|
||||
}
|
||||
17
vendor/github.com/glycerine/zygomys/zygo/random.go
generated
vendored
Normal file
17
vendor/github.com/glycerine/zygomys/zygo/random.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var defaultRand = rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
||||
func RandomFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
return &SexpFloat{Val: defaultRand.Float64()}, nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportRandom() {
|
||||
env.AddFunction("random", RandomFunction)
|
||||
}
|
||||
33
vendor/github.com/glycerine/zygomys/zygo/rawutils.go
generated
vendored
Normal file
33
vendor/github.com/glycerine/zygomys/zygo/rawutils.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func MakeRaw(args []Sexp) (*SexpRaw, error) {
|
||||
raw := make([]byte, 0)
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch e := args[i].(type) {
|
||||
case *SexpStr:
|
||||
a := []byte(e.S)
|
||||
raw = append(raw, a...)
|
||||
default:
|
||||
return &SexpRaw{},
|
||||
fmt.Errorf("raw takes only string arguments. We see %T: '%v'", e, e)
|
||||
}
|
||||
}
|
||||
return &SexpRaw{Val: raw}, nil
|
||||
}
|
||||
|
||||
func RawToStringFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
switch t := args[0].(type) {
|
||||
case *SexpRaw:
|
||||
return &SexpStr{S: string(t.Val)}, nil
|
||||
}
|
||||
return SexpNull, errors.New("argument must be raw")
|
||||
}
|
||||
100
vendor/github.com/glycerine/zygomys/zygo/regexp.go
generated
vendored
Normal file
100
vendor/github.com/glycerine/zygomys/zygo/regexp.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type SexpRegexp regexp.Regexp
|
||||
|
||||
func (re *SexpRegexp) SexpString(ps *PrintState) string {
|
||||
r := (*regexp.Regexp)(re)
|
||||
return fmt.Sprintf(`(regexpCompile "%v")`, r.String())
|
||||
}
|
||||
|
||||
func (r *SexpRegexp) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func regexpFindIndex(env *Zlisp,
|
||||
needle *regexp.Regexp, haystack string) (Sexp, error) {
|
||||
|
||||
loc := needle.FindStringIndex(haystack)
|
||||
|
||||
arr := make([]Sexp, len(loc))
|
||||
for i := range arr {
|
||||
arr[i] = Sexp(&SexpInt{Val: int64(loc[i])})
|
||||
}
|
||||
|
||||
return &SexpArray{Val: arr, Env: env}, nil
|
||||
}
|
||||
|
||||
func RegexpFind(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
if len(args) != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var haystack string
|
||||
switch t := args[1].(type) {
|
||||
case *SexpStr:
|
||||
haystack = t.S
|
||||
default:
|
||||
return SexpNull,
|
||||
errors.New(fmt.Sprintf("2nd argument of %v should be a string", name))
|
||||
}
|
||||
|
||||
var needle *regexp.Regexp
|
||||
switch t := args[0].(type) {
|
||||
case *SexpRegexp:
|
||||
needle = (*regexp.Regexp)(t)
|
||||
default:
|
||||
return SexpNull,
|
||||
errors.New(fmt.Sprintf("1st argument of %v should be a compiled regular expression", name))
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "regexpFind":
|
||||
str := needle.FindString(haystack)
|
||||
return &SexpStr{S: str}, nil
|
||||
case "regexpFindIndex":
|
||||
return regexpFindIndex(env, needle, haystack)
|
||||
case "regexpMatch":
|
||||
matches := needle.MatchString(haystack)
|
||||
return &SexpBool{Val: matches}, nil
|
||||
}
|
||||
|
||||
return SexpNull, errors.New("unknown function")
|
||||
}
|
||||
|
||||
func RegexpCompile(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
if len(args) < 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var re string
|
||||
switch t := args[0].(type) {
|
||||
case *SexpStr:
|
||||
re = t.S
|
||||
default:
|
||||
return SexpNull,
|
||||
errors.New("argument of regexpCompile should be a string")
|
||||
}
|
||||
|
||||
r, err := regexp.Compile(re)
|
||||
|
||||
if err != nil {
|
||||
return SexpNull, errors.New(
|
||||
fmt.Sprintf("error during regexpCompile: '%v'", err))
|
||||
}
|
||||
|
||||
return Sexp((*SexpRegexp)(r)), nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportRegex() {
|
||||
env.AddFunction("regexpCompile", RegexpCompile)
|
||||
env.AddFunction("regexpFindIndex", RegexpFind)
|
||||
env.AddFunction("regexpFind", RegexpFind)
|
||||
env.AddFunction("regexpMatch", RegexpFind)
|
||||
}
|
||||
515
vendor/github.com/glycerine/zygomys/zygo/repl.go
generated
vendored
Normal file
515
vendor/github.com/glycerine/zygomys/zygo/repl.go
generated
vendored
Normal file
@@ -0,0 +1,515 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var precounts map[string]int
|
||||
var postcounts map[string]int
|
||||
|
||||
func CountPreHook(env *Zlisp, name string, args []Sexp) {
|
||||
precounts[name] += 1
|
||||
}
|
||||
|
||||
func CountPostHook(env *Zlisp, name string, retval Sexp) {
|
||||
postcounts[name] += 1
|
||||
}
|
||||
|
||||
func getLine(reader *bufio.Reader) (string, error) {
|
||||
line := make([]byte, 0)
|
||||
for {
|
||||
linepart, hasMore, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
line = append(line, linepart...)
|
||||
if !hasMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(line), nil
|
||||
}
|
||||
|
||||
// NB at the moment this doesn't track comment and strings state,
|
||||
// so it will fail if unbalanced '(' are found in either.
|
||||
func isBalanced(str string) bool {
|
||||
parens := 0
|
||||
squares := 0
|
||||
|
||||
for _, c := range str {
|
||||
switch c {
|
||||
case '(':
|
||||
parens++
|
||||
case ')':
|
||||
parens--
|
||||
case '[':
|
||||
squares++
|
||||
case ']':
|
||||
squares--
|
||||
}
|
||||
}
|
||||
|
||||
return parens == 0 && squares == 0
|
||||
}
|
||||
|
||||
var continuationPrompt = "... "
|
||||
|
||||
func (pr *Prompter) getExpressionOrig(reader *bufio.Reader) (readin string, err error) {
|
||||
line, err := getLine(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for !isBalanced(line) {
|
||||
fmt.Printf(continuationPrompt)
|
||||
nextline, err := getLine(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
line += "\n" + nextline
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
|
||||
// liner reads Stdin only. If noLiner, then we read from reader.
|
||||
func (pr *Prompter) getExpressionWithLiner(env *Zlisp, reader *bufio.Reader, noLiner bool) (readin string, xs []Sexp, err error) {
|
||||
|
||||
var line, nextline string
|
||||
|
||||
if noLiner {
|
||||
fmt.Printf(pr.prompt)
|
||||
line, err = getLine(reader)
|
||||
} else {
|
||||
line, err = pr.Getline(nil)
|
||||
}
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
err = UnexpectedEnd
|
||||
var x []Sexp
|
||||
|
||||
// test parse, but don't load or generate bytecode
|
||||
env.parser.ResetAddNewInput(bytes.NewBuffer([]byte(line + "\n")))
|
||||
x, err = env.parser.ParseTokens()
|
||||
//P("\n after ResetAddNewInput, err = %v. x = '%s'\n", err, SexpArray(x).SexpString())
|
||||
|
||||
if len(x) > 0 {
|
||||
xs = append(xs, x...)
|
||||
}
|
||||
|
||||
for err == ErrMoreInputNeeded || err == UnexpectedEnd || err == ResetRequested {
|
||||
if noLiner {
|
||||
fmt.Printf(continuationPrompt)
|
||||
nextline, err = getLine(reader)
|
||||
} else {
|
||||
nextline, err = pr.Getline(&continuationPrompt)
|
||||
}
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
env.parser.NewInput(bytes.NewBuffer([]byte(nextline + "\n")))
|
||||
x, err = env.parser.ParseTokens()
|
||||
if len(x) > 0 {
|
||||
for i := range x {
|
||||
if x[i] == SexpEnd {
|
||||
P("found an SexpEnd token, omitting it")
|
||||
continue
|
||||
}
|
||||
xs = append(xs, x[i])
|
||||
}
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
line += "\n" + nextline
|
||||
Q("no problem parsing line '%s' into '%s', proceeding...\n", line, (&SexpArray{Val: x, Env: env}).SexpString(nil))
|
||||
return line, xs, nil
|
||||
case ResetRequested:
|
||||
continue
|
||||
case ErrMoreInputNeeded:
|
||||
continue
|
||||
default:
|
||||
return "", nil, fmt.Errorf("Error on line %d: %v\n", env.parser.lexer.Linenum(), err)
|
||||
}
|
||||
}
|
||||
return line, xs, nil
|
||||
}
|
||||
|
||||
func processDumpCommand(env *Zlisp, args []string) {
|
||||
if len(args) == 0 {
|
||||
env.DumpEnvironment()
|
||||
} else {
|
||||
err := env.DumpFunctionByName(args[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Repl(env *Zlisp, cfg *ZlispConfig) {
|
||||
var reader *bufio.Reader
|
||||
if cfg.NoLiner {
|
||||
// reader is used if one wishes to drop the liner library.
|
||||
// Useful for not full terminal env, like under test.
|
||||
reader = bufio.NewReader(os.Stdin)
|
||||
}
|
||||
|
||||
if cfg.Trace {
|
||||
// debug tracing
|
||||
env.debugExec = true
|
||||
}
|
||||
|
||||
if !cfg.Quiet {
|
||||
if cfg.Sandboxed {
|
||||
fmt.Printf("zygo [sandbox mode] version %s\n", Version())
|
||||
} else {
|
||||
fmt.Printf("zygo version %s\n", Version())
|
||||
}
|
||||
fmt.Printf("press tab (repeatedly) to get completion suggestions. Shift-tab goes back. Ctrl-d to exit.\n")
|
||||
}
|
||||
var pr *Prompter // can be nil if noLiner
|
||||
if !cfg.NoLiner {
|
||||
pr = NewPrompter(cfg.Prompt)
|
||||
defer pr.Close()
|
||||
} else {
|
||||
pr = &Prompter{prompt: cfg.Prompt}
|
||||
}
|
||||
infixSym := env.MakeSymbol("infix")
|
||||
|
||||
for {
|
||||
line, exprsInput, err := pr.getExpressionWithLiner(env, reader, cfg.NoLiner)
|
||||
//Q("\n exprsInput(len=%d) = '%v'\n line = '%s'\n", len(exprsInput), (&SexpArray{Val: exprsInput}).SexpString(nil), line)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if err == io.EOF {
|
||||
os.Exit(0)
|
||||
}
|
||||
env.Clear()
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(strings.Trim(line, " "), " ")
|
||||
//parts := strings.Split(line, " ")
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
first := strings.Trim(parts[0], " ")
|
||||
|
||||
if first == ".quit" {
|
||||
break
|
||||
}
|
||||
|
||||
if first == ".cd" {
|
||||
if len(parts) < 2 {
|
||||
fmt.Printf("provide directory path to change to.\n")
|
||||
continue
|
||||
}
|
||||
err := os.Chdir(parts[1])
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
continue
|
||||
}
|
||||
pwd, err := os.Getwd()
|
||||
if err == nil {
|
||||
fmt.Printf("cur dir: %s\n", pwd)
|
||||
} else {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// allow & at the repl to take the address of an expression
|
||||
if len(first) > 0 && first[0] == '&' {
|
||||
//P("saw & at repl, first='%v', parts='%#v'. exprsInput = '%#v'", first, parts, exprsInput)
|
||||
exprsInput = []Sexp{MakeList(exprsInput)}
|
||||
}
|
||||
|
||||
// allow * at the repl to dereference a pointer and print
|
||||
if len(first) > 0 && first[0] == '*' {
|
||||
//P("saw * at repl, first='%v', parts='%#v'. exprsInput = '%#v'", first, parts, exprsInput)
|
||||
exprsInput = []Sexp{MakeList(exprsInput)}
|
||||
}
|
||||
|
||||
if first == ".dump" {
|
||||
processDumpCommand(env, parts[1:])
|
||||
continue
|
||||
}
|
||||
|
||||
if first == ".gls" {
|
||||
fmt.Printf("\nScopes:\n")
|
||||
prev := env.showGlobalScope
|
||||
env.showGlobalScope = true
|
||||
err = env.ShowStackStackAndScopeStack()
|
||||
env.showGlobalScope = prev
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if first == ".ls" {
|
||||
err := env.ShowStackStackAndScopeStack()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if first == ".verb" {
|
||||
Verbose = !Verbose
|
||||
fmt.Printf("verbose: %v.\n", Verbose)
|
||||
continue
|
||||
}
|
||||
|
||||
if first == ".debug" {
|
||||
env.debugExec = true
|
||||
fmt.Printf("instruction debugging on.\n")
|
||||
continue
|
||||
}
|
||||
|
||||
if first == ".undebug" {
|
||||
env.debugExec = false
|
||||
fmt.Printf("instruction debugging off.\n")
|
||||
continue
|
||||
}
|
||||
|
||||
var expr Sexp
|
||||
n := len(exprsInput)
|
||||
if n > 0 {
|
||||
infixWrappedSexp := MakeList([]Sexp{infixSym, &SexpArray{Val: exprsInput, Env: env}})
|
||||
expr, err = env.EvalExpressions([]Sexp{infixWrappedSexp})
|
||||
} else {
|
||||
line = env.ReplLineInfixWrap(line)
|
||||
expr, err = env.EvalString(line + " ") // print standalone variables
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
case NoExpressionsFound:
|
||||
env.Clear()
|
||||
continue
|
||||
default:
|
||||
fmt.Print(env.GetStackTrace(err))
|
||||
env.Clear()
|
||||
continue
|
||||
}
|
||||
|
||||
if expr != SexpNull {
|
||||
// try to print strings more elegantly!
|
||||
switch e := expr.(type) {
|
||||
case *SexpStr:
|
||||
if e.backtick {
|
||||
fmt.Printf("`%s`\n", e.S)
|
||||
} else {
|
||||
fmt.Printf("%s\n", strconv.Quote(e.S))
|
||||
}
|
||||
default:
|
||||
switch sym := expr.(type) {
|
||||
case Selector:
|
||||
Q("repl calling RHS() on Selector")
|
||||
rhs, err := sym.RHS(env)
|
||||
if err != nil {
|
||||
Q("repl problem in call to RHS() on SexpSelector: '%v'", err)
|
||||
fmt.Print(env.GetStackTrace(err))
|
||||
env.Clear()
|
||||
continue
|
||||
} else {
|
||||
Q("got back rhs of type %T", rhs)
|
||||
fmt.Println(rhs.SexpString(nil))
|
||||
continue
|
||||
}
|
||||
case *SexpSymbol:
|
||||
if sym.isDot {
|
||||
resolved, err := dotGetSetHelper(env, sym.name, nil)
|
||||
if err != nil {
|
||||
fmt.Print(env.GetStackTrace(err))
|
||||
env.Clear()
|
||||
continue
|
||||
}
|
||||
fmt.Println(resolved.SexpString(nil))
|
||||
continue
|
||||
}
|
||||
}
|
||||
fmt.Println(expr.SexpString(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runScript(env *Zlisp, fname string, cfg *ZlispConfig) {
|
||||
file, err := os.Open(fname)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = env.LoadFile(file)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if cfg.ExitOnFailure {
|
||||
os.Exit(-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_, err = env.Run()
|
||||
if cfg.CountFuncCalls {
|
||||
fmt.Println("Pre:")
|
||||
for name, count := range precounts {
|
||||
fmt.Printf("\t%s: %d\n", name, count)
|
||||
}
|
||||
fmt.Println("Post:")
|
||||
for name, count := range postcounts {
|
||||
fmt.Printf("\t%s: %d\n", name, count)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Print(env.GetStackTrace(err))
|
||||
if cfg.ExitOnFailure {
|
||||
os.Exit(-1)
|
||||
}
|
||||
Repl(env, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) StandardSetup() {
|
||||
env.ImportBaseTypes()
|
||||
env.ImportEval()
|
||||
env.ImportTime()
|
||||
env.ImportPackageBuilder()
|
||||
env.ImportMsgpackMap()
|
||||
|
||||
defmap := `(defmac defmap [name] ^(defn ~name [& rest] (msgmap (quote ~name) rest)))`
|
||||
_, err := env.EvalString(defmap)
|
||||
panicOn(err)
|
||||
|
||||
// colonOp := `(defmac : [key hmap & def] ^(hget ~hmap (quote ~key) ~@def))`
|
||||
// _, err = env.EvalString(colonOp)
|
||||
// panicOn(err)
|
||||
|
||||
rangeMacro := `(defmac range [key value myhash & body]
|
||||
^(let [n (len ~myhash)]
|
||||
(for [(def i 0) (< i n) (def i (+ i 1))]
|
||||
(begin
|
||||
(mdef (quote ~key) (quote ~value) (hpair ~myhash i))
|
||||
~@body))))`
|
||||
_, err = env.EvalString(rangeMacro)
|
||||
panicOn(err)
|
||||
|
||||
reqMacro := `(defmac req [a] ^(source (sym2str (quote ~a))))`
|
||||
_, err = env.EvalString(reqMacro)
|
||||
panicOn(err)
|
||||
|
||||
incrMacro := `(defmac ++ [a] ^(set ~a (+ ~a 1)))`
|
||||
_, err = env.EvalString(incrMacro)
|
||||
panicOn(err)
|
||||
|
||||
incrEqMacro := `(defmac += [a b] ^(set ~a (+ ~a ~b)))`
|
||||
_, err = env.EvalString(incrEqMacro)
|
||||
panicOn(err)
|
||||
|
||||
decrMacro := `(defmac -- [a] ^(set ~a (- ~a 1)))`
|
||||
_, err = env.EvalString(decrMacro)
|
||||
panicOn(err)
|
||||
|
||||
decrEqMacro := `(defmac -= [a b] ^(set ~a (- ~a ~b)))`
|
||||
_, err = env.EvalString(decrEqMacro)
|
||||
panicOn(err)
|
||||
|
||||
env.ImportChannels()
|
||||
env.ImportGoroutines()
|
||||
env.ImportRegex()
|
||||
env.ImportRandom()
|
||||
|
||||
gob.Register(SexpHash{})
|
||||
gob.Register(SexpArray{})
|
||||
}
|
||||
|
||||
// like main() for a standalone repl, now in library
|
||||
func ReplMain(cfg *ZlispConfig) {
|
||||
var env *Zlisp
|
||||
if cfg.LoadDemoStructs {
|
||||
RegisterDemoStructs()
|
||||
}
|
||||
if cfg.Sandboxed {
|
||||
env = NewZlispSandbox()
|
||||
} else {
|
||||
env = NewZlisp()
|
||||
}
|
||||
env.StandardSetup()
|
||||
if cfg.LoadDemoStructs {
|
||||
// avoid data conflicts by only loading these in demo mode.
|
||||
env.ImportDemoData()
|
||||
}
|
||||
|
||||
if cfg.CpuProfile != "" {
|
||||
f, err := os.Create(cfg.CpuProfile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
err = pprof.StartCPUProfile(f)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
precounts = make(map[string]int)
|
||||
postcounts = make(map[string]int)
|
||||
|
||||
if cfg.CountFuncCalls {
|
||||
env.AddPreHook(CountPreHook)
|
||||
env.AddPostHook(CountPostHook)
|
||||
}
|
||||
|
||||
if cfg.Command != "" {
|
||||
_, err := env.EvalString(cfg.Command)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
runRepl := true
|
||||
args := cfg.Flags.Args()
|
||||
if len(args) > 0 {
|
||||
runRepl = false
|
||||
runScript(env, args[0], cfg)
|
||||
if cfg.AfterScriptDontExit {
|
||||
runRepl = true
|
||||
}
|
||||
}
|
||||
if runRepl {
|
||||
Repl(env, cfg)
|
||||
}
|
||||
|
||||
if cfg.MemProfile != "" {
|
||||
f, err := os.Create(cfg.MemProfile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = pprof.Lookup("heap").WriteTo(f, 1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) ReplLineInfixWrap(line string) string {
|
||||
return "{" + line + "}"
|
||||
}
|
||||
339
vendor/github.com/glycerine/zygomys/zygo/scopes.go
generated
vendored
Normal file
339
vendor/github.com/glycerine/zygomys/zygo/scopes.go
generated
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Scopes map names to values. Scope nesting avoids variable name collisions and
|
||||
// allows namespace maintainance. Most scopes (inside loops, inside functions)
|
||||
// are implicitly created. Packages are scopes that the user can manipulate
|
||||
// explicitly.
|
||||
type Scope struct {
|
||||
Map map[int]Sexp
|
||||
IsGlobal bool
|
||||
Name string
|
||||
PackageName string
|
||||
Parent *Scope
|
||||
IsFunction bool // if true, read-only.
|
||||
MyFunction *SexpFunction // so we can query captured closure scopes.
|
||||
IsPackage bool
|
||||
env *Zlisp
|
||||
}
|
||||
|
||||
// SexpString satisfies the Sexp interface, producing a string presentation of the value.
|
||||
func (s *Scope) SexpString(ps *PrintState) string {
|
||||
var label string
|
||||
head := ""
|
||||
if s.IsPackage {
|
||||
head = "(package " + s.PackageName
|
||||
} else {
|
||||
label = "scope " + s.Name
|
||||
if s.IsGlobal {
|
||||
label += " (global)"
|
||||
}
|
||||
}
|
||||
|
||||
str, err := s.Show(s.env, ps, s.Name)
|
||||
if err != nil {
|
||||
return "(" + label + ")"
|
||||
}
|
||||
|
||||
return head + " " + str + " )"
|
||||
}
|
||||
|
||||
// Type() satisfies the Sexp interface, returning the type of the value.
|
||||
func (s *Scope) Type() *RegisteredType {
|
||||
return GoStructRegistry.Lookup("packageScope")
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewScope() *Scope {
|
||||
return &Scope{
|
||||
Map: make(map[int]Sexp),
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewNamedScope(name string) *Scope {
|
||||
return &Scope{
|
||||
Map: make(map[int]Sexp),
|
||||
Name: name,
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scope) CloneScope() *Scope {
|
||||
n := s.env.NewScope()
|
||||
for k, v := range s.Map {
|
||||
n.Map[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (s Scope) IsStackElem() {}
|
||||
|
||||
func (stack *Stack) PushScope() {
|
||||
s := stack.env.NewScope()
|
||||
if stack.Size() > 0 {
|
||||
s.Parent = stack.GetTop().(*Scope)
|
||||
}
|
||||
stack.Push(s)
|
||||
}
|
||||
|
||||
func (stack *Stack) PopScope() error {
|
||||
_, err := stack.Pop()
|
||||
return err
|
||||
}
|
||||
|
||||
// dynamic scoping lookup. See env.LexicalLookupSymbol() for the lexically
|
||||
// scoped equivalent.
|
||||
// If setVal is not nil, and if we find the symbol, we set it in the scope
|
||||
// where it was found. This is equivalent to scope.UpdateSymbolInScope.
|
||||
//
|
||||
func (stack *Stack) lookupSymbol(sym *SexpSymbol, minFrame int, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
if !stack.IsEmpty() {
|
||||
for i := 0; i <= stack.tos-minFrame; i++ {
|
||||
//P("lookupSymbol checking stack %v of %v", i, (stack.tos-minFrame)+1)
|
||||
elem, err := stack.Get(i)
|
||||
if err != nil {
|
||||
//P("lookupSymbol bailing (early?) at i=%v on err='%v'", i, err)
|
||||
return SexpNull, err, nil
|
||||
}
|
||||
switch scope := elem.(type) {
|
||||
case (*Scope):
|
||||
expr, ok := scope.Map[sym.number]
|
||||
if ok {
|
||||
//P("lookupSymbol at stack scope# i=%v, we found sym '%s' with value '%s'", i, sym.name, expr.SexpString(0))
|
||||
if setVal != nil {
|
||||
scope.Map[sym.number] = *setVal
|
||||
}
|
||||
return expr, nil, scope
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//P("lookupSymbol finished stack scan without finding it")
|
||||
if stack.env != nil && stack.env.debugSymbolNotFound {
|
||||
stack.env.ShowStackStackAndScopeStack()
|
||||
}
|
||||
return SexpNull, fmt.Errorf("alas, symbol `%s` not found", sym.name), nil
|
||||
}
|
||||
|
||||
func (stack *Stack) LookupSymbol(sym *SexpSymbol, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
return stack.lookupSymbol(sym, 0, setVal)
|
||||
}
|
||||
|
||||
// LookupSymbolNonGlobal - closures use this to only find symbols below the global scope, to avoid copying globals it'll always be-able to ref
|
||||
func (stack *Stack) LookupSymbolNonGlobal(sym *SexpSymbol) (Sexp, error, *Scope) {
|
||||
return stack.lookupSymbol(sym, 1, nil)
|
||||
}
|
||||
|
||||
var SymNotFound = errors.New("symbol not found")
|
||||
|
||||
// lookup symbols, but don't go beyond a function boundary -- a user-defined
|
||||
// function boundary that is. We certainly have to go up beyond
|
||||
// all built-in operators like '+' and '-', '*' and '/'.
|
||||
func (stack *Stack) LookupSymbolUntilFunction(sym *SexpSymbol, setVal *Sexp, maximumFuncToSearch int, checkCaptures bool) (Sexp, error, *Scope) {
|
||||
|
||||
funcCount := 0
|
||||
if !stack.IsEmpty() {
|
||||
doneSearching:
|
||||
for i := 0; i <= stack.tos; i++ {
|
||||
elem, err := stack.Get(i)
|
||||
if err != nil {
|
||||
return SexpNull, err, nil
|
||||
}
|
||||
switch scope := elem.(type) {
|
||||
case (*Scope):
|
||||
VPrintf(" ...looking up in scope '%s'\n", scope.Name)
|
||||
expr, ok := scope.Map[sym.number]
|
||||
if ok {
|
||||
if setVal != nil {
|
||||
scope.UpdateSymbolInScope(sym, *setVal)
|
||||
}
|
||||
return expr, nil, scope
|
||||
}
|
||||
if scope.IsFunction {
|
||||
funcCount++
|
||||
//P(" ...scope '%s' was a function, halting up search and checking captured closures\n", scope.Name)
|
||||
|
||||
if checkCaptures {
|
||||
// check the captured closure scope stack
|
||||
|
||||
exp, err, whichScope := scope.MyFunction.ClosingLookupSymbol(sym, setVal)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LookupSymbolUntilFunction('%s') found in scope '%s'\n", sym.name, whichScope.Name)
|
||||
return exp, err, whichScope
|
||||
}
|
||||
}
|
||||
|
||||
// no luck inside the captured closure scopes.
|
||||
if funcCount >= maximumFuncToSearch {
|
||||
break doneSearching
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if stack != nil && stack.env != nil && stack.env.debugSymbolNotFound {
|
||||
fmt.Printf("debugSymbolNotFound is true, here are scopes:\n")
|
||||
stack.env.ShowStackStackAndScopeStack()
|
||||
}
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) BindSymbol(sym *SexpSymbol, expr Sexp) error {
|
||||
if stack.IsEmpty() {
|
||||
panic("empty stack!!")
|
||||
}
|
||||
cur, already := stack.elements[stack.tos].(*Scope).Map[sym.number]
|
||||
if already {
|
||||
Q("BindSymbol already sees symbol %v, currently bound to '%v'", sym.name, cur.SexpString(nil))
|
||||
|
||||
lhsTy := cur.Type()
|
||||
rhsTy := expr.Type()
|
||||
if lhsTy == nil {
|
||||
// for backcompat with closure.zy, just do the binding for now if the LHS isn't typed.
|
||||
//return fmt.Errorf("left-hand-side had nil type")
|
||||
// TODO: fix this? or require removal of previous symbol binding to avoid type errors?
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
if rhsTy == nil {
|
||||
// meh, we need to be able to assign nil to stuff without freaking out,
|
||||
// so force type match
|
||||
rhsTy = lhsTy
|
||||
|
||||
//return fmt.Errorf("right-hand-side had nil type back from Type() call; val = '%s'/%T", expr.SexpString(nil), expr)
|
||||
}
|
||||
|
||||
// both sides have type
|
||||
Q("BindSymbol: both sides have type. rhs=%v, lhs=%v", rhsTy.SexpString(nil), lhsTy.SexpString(nil))
|
||||
|
||||
if lhsTy == rhsTy {
|
||||
Q("BindSymbol: YES types match exactly. Good.")
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
if rhsTy.UserStructDefn != nil && rhsTy.UserStructDefn != lhsTy.UserStructDefn {
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
}
|
||||
|
||||
if lhsTy.UserStructDefn != nil && lhsTy.UserStructDefn != rhsTy.UserStructDefn {
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
}
|
||||
|
||||
// TODO: problem with this implementation is that it may narrow the possible
|
||||
// types assignments to this variable. To fix we'll need to keep around the
|
||||
// type of the symbol in the symbol table, separately from the value currently
|
||||
// bound to it.
|
||||
if lhsTy.TypeCache != nil && rhsTy.TypeCache != nil {
|
||||
if rhsTy.TypeCache.AssignableTo(lhsTy.TypeCache) {
|
||||
Q("BindSymbol: YES: rhsTy.TypeCache (%v) is AssigntableTo(lhsTy.TypeCache) (%v). Good.", rhsTy.TypeCache, lhsTy.TypeCache)
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Q("BindSymbol: at end, defaulting to deny")
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
} else {
|
||||
Q("BindSymbol: new symbol %v", sym.name)
|
||||
}
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *Stack) DeleteSymbolFromTopOfStackScope(sym *SexpSymbol) error {
|
||||
if stack.IsEmpty() {
|
||||
panic("empty stack!!")
|
||||
//return errors.New("no scope available")
|
||||
}
|
||||
_, present := stack.elements[stack.tos].(*Scope).Map[sym.number]
|
||||
if !present {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
delete(stack.elements[stack.tos].(*Scope).Map, sym.number)
|
||||
return nil
|
||||
}
|
||||
|
||||
// used to implement (set v 10)
|
||||
func (scope *Scope) UpdateSymbolInScope(sym *SexpSymbol, expr Sexp) error {
|
||||
|
||||
_, found := scope.Map[sym.number]
|
||||
if !found {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
scope.Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (scope *Scope) DeleteSymbolInScope(sym *SexpSymbol) error {
|
||||
|
||||
_, found := scope.Map[sym.number]
|
||||
if !found {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
delete(scope.Map, sym.number)
|
||||
return nil
|
||||
}
|
||||
|
||||
type SymtabE struct {
|
||||
Key string
|
||||
Val string
|
||||
}
|
||||
|
||||
type SymtabSorter []*SymtabE
|
||||
|
||||
func (a SymtabSorter) Len() int { return len(a) }
|
||||
func (a SymtabSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a SymtabSorter) Less(i, j int) bool { return a[i].Key < a[j].Key }
|
||||
|
||||
func (scop *Scope) Show(env *Zlisp, ps *PrintState, label string) (s string, err error) {
|
||||
//P("scop %p Show() starting, PackageName: '%s' IsGlobal: %v", scop, scop.PackageName, scop.IsGlobal)
|
||||
if ps == nil {
|
||||
ps = NewPrintState()
|
||||
}
|
||||
if ps.GetSeen(scop) {
|
||||
// This check is critical to prevent infinite looping in a cycle.
|
||||
// Scopes like global are referenced by every package, and
|
||||
// nested scopes refer to their paranets, so nesting
|
||||
// two packages will loop forever without this check.
|
||||
|
||||
// debug version: return fmt.Sprintf("already-saw Scope %p with scop.PackageName='%s'\n", scop, scop.PackageName), nil
|
||||
return "", nil
|
||||
} else {
|
||||
ps.SetSeen(scop, "Scope")
|
||||
}
|
||||
indent := ps.GetIndent()
|
||||
rep := strings.Repeat(" ", indent)
|
||||
rep4 := strings.Repeat(" ", indent+4)
|
||||
s += fmt.Sprintf("%s %s %s (%p)\n", rep, label, scop.Name, scop)
|
||||
if scop.IsGlobal && !env.showGlobalScope {
|
||||
s += fmt.Sprintf("%s (global scope - omitting content for brevity)\n", rep4)
|
||||
return
|
||||
}
|
||||
if len(scop.Map) == 0 {
|
||||
s += fmt.Sprintf("%s empty-scope: no symbols\n", rep4)
|
||||
return
|
||||
}
|
||||
sortme := []*SymtabE{}
|
||||
for symbolNumber, val := range scop.Map {
|
||||
symbolName := env.revsymtable[symbolNumber]
|
||||
sortme = append(sortme, &SymtabE{Key: symbolName, Val: val.SexpString(ps)})
|
||||
}
|
||||
sort.Sort(SymtabSorter(sortme))
|
||||
for i := range sortme {
|
||||
s += fmt.Sprintf("%s %s -> %s\n", rep4,
|
||||
sortme[i].Key, sortme[i].Val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Showable interface {
|
||||
Show(env *Zlisp, ps *PrintState, label string) (string, error)
|
||||
}
|
||||
169
vendor/github.com/glycerine/zygomys/zygo/slurp.go
generated
vendored
Normal file
169
vendor/github.com/glycerine/zygomys/zygo/slurp.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// read new-line delimited text from a file into an array (slurpf "path-to-file")
|
||||
func SlurpfileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var fn string
|
||||
switch fna := args[0].(type) {
|
||||
case *SexpStr:
|
||||
fn = fna.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("slurp requires a string path to read. we got type %T / value = %v", args[0], args[0])
|
||||
}
|
||||
|
||||
if !FileExists(string(fn)) {
|
||||
return SexpNull, fmt.Errorf("file '%s' does not exist", fn)
|
||||
}
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
a := make([]Sexp, 0)
|
||||
|
||||
bufIn := bufio.NewReader(f)
|
||||
lineNum := int64(1)
|
||||
for {
|
||||
lastline, err := bufIn.ReadBytes('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
return SexpNull, err
|
||||
}
|
||||
n := len(lastline)
|
||||
if err == io.EOF && n == 0 {
|
||||
break
|
||||
}
|
||||
if n > 0 {
|
||||
if lastline[n-1] == '\n' {
|
||||
a = append(a, &SexpStr{S: string(lastline[:n-1])})
|
||||
} else {
|
||||
a = append(a, &SexpStr{S: string(lastline)})
|
||||
}
|
||||
lineNum += 1
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
VPrintf("read %d lines\n", lineNum)
|
||||
return env.NewSexpArray(a), nil
|
||||
}
|
||||
|
||||
// (writef <content> path); (write path) is the macro version.
|
||||
// (owritef <content> path): write an array of strings out to the named file,
|
||||
// overwriting it in the process. (owrite) is the macro version.
|
||||
// save is the same as write.
|
||||
func WriteToFileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var fn string
|
||||
switch fna := args[1].(type) {
|
||||
case *SexpStr:
|
||||
fn = fna.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("owrite requires a string (SexpStr) path to write to as the second argument. we got type %T / value = %v", args[1], args[1])
|
||||
}
|
||||
|
||||
if name == "write" || name == "writef" || name == "save" {
|
||||
// don't overwrite existing file
|
||||
if FileExists(fn) {
|
||||
return SexpNull, fmt.Errorf("refusing to write to existing file '%s'",
|
||||
fn)
|
||||
}
|
||||
}
|
||||
// owrite / owritef overwrite indiscriminately.
|
||||
|
||||
f, err := os.Create(fn)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var slice []Sexp
|
||||
switch sl := args[0].(type) {
|
||||
case *SexpArray:
|
||||
slice = sl.Val
|
||||
for i := range slice {
|
||||
s := slice[i].SexpString(nil)
|
||||
if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
|
||||
s = s[1 : len(s)-1]
|
||||
} else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
_, err = fmt.Fprintf(f, "%s\n", s)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
case *SexpRaw:
|
||||
_, err = f.Write([]byte(sl.Val))
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
default:
|
||||
s := sl.SexpString(nil)
|
||||
if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
|
||||
s = s[1 : len(s)-1]
|
||||
} else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
_, err = fmt.Fprintf(f, "%s\n", s)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
// SplitStringFunction splits a string based on an arbitrary delimiter
|
||||
func SplitStringFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
// make sure the two args are strings
|
||||
s1, ok := args[0].(*SexpStr)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("split requires a string to split, got %T", args[0])
|
||||
}
|
||||
s2, ok := args[1].(*SexpStr)
|
||||
if !ok {
|
||||
return SexpNull, fmt.Errorf("split requires a string as a delimiter, got %T", args[1])
|
||||
}
|
||||
|
||||
toSplit := s1.S
|
||||
splitter := s2.S
|
||||
s := strings.Split(toSplit, splitter)
|
||||
|
||||
split := make([]Sexp, len(s))
|
||||
for i := range split {
|
||||
split[i] = &SexpStr{S: s[i]}
|
||||
}
|
||||
|
||||
return env.NewSexpArray(split), nil
|
||||
}
|
||||
|
||||
// (nsplit "a\nb") -> ["a" "b"]
|
||||
func SplitStringOnNewlinesFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
args = append(args, &SexpStr{S: "\n"})
|
||||
|
||||
return SplitStringFunction(env, name, args)
|
||||
}
|
||||
150
vendor/github.com/glycerine/zygomys/zygo/source.go
generated
vendored
Normal file
150
vendor/github.com/glycerine/zygomys/zygo/source.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// alternative. simpler, currently panics.
|
||||
func SimpleSourceFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
src, isStr := args[0].(*SexpStr)
|
||||
if !isStr {
|
||||
return SexpNull, fmt.Errorf("-> error: first argument must be a string")
|
||||
}
|
||||
|
||||
file := src.S
|
||||
if !FileExists(file) {
|
||||
return SexpNull, fmt.Errorf("path '%s' does not exist", file)
|
||||
}
|
||||
|
||||
env2 := env.Duplicate()
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = env2.LoadFile(f)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
_, err = env2.Run()
|
||||
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
// existing
|
||||
|
||||
// SourceExpressions, this should be called from a user func context
|
||||
func (env *Zlisp) SourceExpressions(expressions []Sexp) error {
|
||||
gen := NewGenerator(env)
|
||||
|
||||
err := gen.GenerateBegin(expressions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//P("debug: in SourceExpressions, FROM expressions='%s'", (&SexpArray{Val: expressions, Env: env}).SexpString(0))
|
||||
//P("debug: in SourceExpressions, gen=")
|
||||
//DumpFunction(ZlispFunction(gen.instructions), -1)
|
||||
curfunc := env.curfunc
|
||||
curpc := env.pc
|
||||
|
||||
env.curfunc = env.MakeFunction("__source", 0, false,
|
||||
gen.instructions, nil)
|
||||
env.pc = 0
|
||||
|
||||
result, err := env.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//P("end of SourceExpressions, result going onto datastack is: '%s'", result.SexpString(0))
|
||||
env.datastack.PushExpr(result)
|
||||
|
||||
//P("debug done with Run in source, now stack is:")
|
||||
//env.datastack.PrintStack()
|
||||
|
||||
env.pc = curpc
|
||||
env.curfunc = curfunc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) SourceStream(stream io.RuneScanner) error {
|
||||
env.parser.ResetAddNewInput(stream)
|
||||
expressions, err := env.parser.ParseTokens()
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf(
|
||||
"Error parsing on line %d: %v\n", env.parser.lexer.Linenum(), err))
|
||||
}
|
||||
|
||||
return env.SourceExpressions(expressions)
|
||||
}
|
||||
|
||||
func (env *Zlisp) SourceFile(file *os.File) error {
|
||||
return env.SourceStream(bufio.NewReader(file))
|
||||
}
|
||||
|
||||
func SourceFileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) < 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
for _, v := range args {
|
||||
if err := env.sourceItem(v); err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
|
||||
result, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// helper for SourceFileFunction recursion
|
||||
func (env *Zlisp) sourceItem(item Sexp) error {
|
||||
switch t := item.(type) {
|
||||
case *SexpArray:
|
||||
for _, v := range t.Val {
|
||||
if err := env.sourceItem(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *SexpPair:
|
||||
expr := item
|
||||
for expr != SexpNull {
|
||||
list := expr.(*SexpPair)
|
||||
if err := env.sourceItem(list.Head); err != nil {
|
||||
return err
|
||||
}
|
||||
expr = list.Tail
|
||||
}
|
||||
case *SexpStr:
|
||||
var f *os.File
|
||||
var err error
|
||||
|
||||
if f, err = os.Open(t.S); err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err = env.SourceFile(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("source: Expected `string`, `list`, `array`. Instead found type %T val %v", item, item)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
278
vendor/github.com/glycerine/zygomys/zygo/stack.go
generated
vendored
Normal file
278
vendor/github.com/glycerine/zygomys/zygo/stack.go
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StackElem interface {
|
||||
IsStackElem()
|
||||
}
|
||||
|
||||
type Stack struct {
|
||||
tos int
|
||||
elements []StackElem
|
||||
env *Zlisp
|
||||
|
||||
Name string // type name
|
||||
|
||||
// package support:
|
||||
PackageName string
|
||||
IsPackage bool
|
||||
}
|
||||
|
||||
func (s *Stack) SexpString(ps *PrintState) string {
|
||||
if ps == nil {
|
||||
ps = NewPrintState()
|
||||
}
|
||||
var label string
|
||||
head := ""
|
||||
if s.IsPackage {
|
||||
head = "(package " + s.PackageName
|
||||
} else {
|
||||
label = "scope " + s.Name
|
||||
}
|
||||
|
||||
str, err := s.Show(s.env, ps, s.Name)
|
||||
if err != nil {
|
||||
return "(" + label + ")"
|
||||
}
|
||||
|
||||
return head + " " + str + " )"
|
||||
}
|
||||
|
||||
// Type() satisfies the Sexp interface, returning the type of the value.
|
||||
func (s *Stack) Type() *RegisteredType {
|
||||
return GoStructRegistry.Lookup("packageScopeStack")
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewStack(size int) *Stack {
|
||||
return &Stack{
|
||||
tos: -1,
|
||||
// elements: make([]StackElem, size),
|
||||
elements: make([]StackElem, 0),
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (stack *Stack) Clone() *Stack {
|
||||
ret := &Stack{}
|
||||
ret.tos = stack.tos
|
||||
ret.env = stack.env
|
||||
ret.elements = make([]StackElem, len(stack.elements))
|
||||
for i := range stack.elements {
|
||||
ret.elements[i] = stack.elements[i]
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (stack *Stack) Top() int {
|
||||
return stack.tos
|
||||
}
|
||||
|
||||
func (stack *Stack) PushAllTo(target *Stack) int {
|
||||
if stack.tos < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
for _, v := range stack.elements[0 : stack.tos+1] {
|
||||
target.Push(v)
|
||||
}
|
||||
|
||||
return stack.tos + 1
|
||||
}
|
||||
|
||||
func (stack *Stack) IsEmpty() bool {
|
||||
return stack.tos < 0
|
||||
}
|
||||
|
||||
func (stack *Stack) Push(elem StackElem) {
|
||||
|
||||
// we have to lazily recover from errors here where the stack
|
||||
// didn't get unwound...
|
||||
n := len(stack.elements)
|
||||
|
||||
// n-1 is the last legal entry, which should be top of stack too.
|
||||
switch {
|
||||
case stack.tos == n-1:
|
||||
// normal, 99% of the time.
|
||||
stack.tos++
|
||||
stack.elements = append(stack.elements, elem)
|
||||
case stack.tos > n-1:
|
||||
// really irretreivably problematic
|
||||
panic(fmt.Sprintf("stack %p is really messed up! starting size=%v > "+
|
||||
"len(stack.elements)=%v:\n here is stack: '%s'\n",
|
||||
stack, stack.tos-1, n, stack.SexpString(nil)))
|
||||
default:
|
||||
// INVAR stack.tos < n-1
|
||||
|
||||
// We might get here if an error caused the last operation to abort,
|
||||
// resulting in the call stack pop upon returning never happening.
|
||||
|
||||
// So we'll lazily cleanup now and carry on.
|
||||
stack.TruncateToSize(stack.tos + 1)
|
||||
stack.tos++
|
||||
stack.elements = append(stack.elements, elem)
|
||||
}
|
||||
}
|
||||
|
||||
func (stack *Stack) GetTop() StackElem {
|
||||
s, err := stack.Get(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
func (stack *Stack) Size() int {
|
||||
return stack.tos + 1
|
||||
}
|
||||
|
||||
var StackUnderFlowErr = fmt.Errorf("invalid stack access: underflow")
|
||||
|
||||
func (stack *Stack) Get(n int) (StackElem, error) {
|
||||
if stack.tos-n < 0 {
|
||||
err := StackUnderFlowErr
|
||||
return nil, err
|
||||
}
|
||||
return stack.elements[stack.tos-n], nil
|
||||
}
|
||||
|
||||
func (stack *Stack) Pop() (StackElem, error) {
|
||||
// always make a new array,
|
||||
// so we can use for the closure stack-of-scopes.
|
||||
|
||||
elem, err := stack.Get(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// invar n > 0
|
||||
n := stack.Size()
|
||||
if n == 0 {
|
||||
return nil, fmt.Errorf("Stack.Pop() on emtpy stack")
|
||||
}
|
||||
|
||||
el := make([]StackElem, n-1)
|
||||
copy(el, stack.elements)
|
||||
stack.elements = el
|
||||
stack.tos--
|
||||
return elem, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) IsStackElem() {}
|
||||
|
||||
func (stack *Stack) Show(env *Zlisp, ps *PrintState, label string) (string, error) {
|
||||
//P("debug: Stack.Show starting with stack = %p, ps = %p, Package: '%s', IsPkg: %v", stack, ps, stack.PackageName, stack.IsPackage)
|
||||
if ps.GetSeen(stack) {
|
||||
return fmt.Sprintf("already-saw Stack %p in Show", stack), nil
|
||||
} else {
|
||||
ps.SetSeen(stack, "Stack in Show")
|
||||
}
|
||||
|
||||
s := ""
|
||||
rep := strings.Repeat(" ", ps.GetIndent())
|
||||
s += fmt.Sprintf("%s %s\n", rep, label)
|
||||
n := stack.Top()
|
||||
for i := 0; i <= n; i++ {
|
||||
ele, err := stack.Get(n - i)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("stack access error on %v: %v", i, err))
|
||||
}
|
||||
showme, canshow := ele.(Showable)
|
||||
if canshow {
|
||||
r, err := showme.Show(env, ps.AddIndent(4),
|
||||
fmt.Sprintf("elem %v of %s:", i, label))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s += r
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// set newsize to 0 to truncate everything
|
||||
func (stack *Stack) TruncateToSize(newsize int) {
|
||||
el := make([]StackElem, newsize)
|
||||
copy(el, stack.elements)
|
||||
stack.elements = el
|
||||
stack.tos = newsize - 1
|
||||
}
|
||||
|
||||
// nestedPathGetSet does a top-down lookup, as opposed to LexicalLookupSymbol which is bottom up
|
||||
func (s *Stack) nestedPathGetSet(env *Zlisp, dotpaths []string, setVal *Sexp) (Sexp, error) {
|
||||
|
||||
if len(dotpaths) == 0 {
|
||||
return SexpNull, fmt.Errorf("internal error: in nestedPathGetSet() dotpaths" +
|
||||
" had zero length")
|
||||
}
|
||||
|
||||
curStack := s
|
||||
|
||||
var ret Sexp = SexpNull
|
||||
var err error
|
||||
var scop *Scope
|
||||
lenpath := len(dotpaths)
|
||||
//P("\n in nestedPathGetSet, dotpaths=%#v\n", dotpaths)
|
||||
for i := range dotpaths {
|
||||
|
||||
curSym := env.MakeSymbol(stripAnyDotPrefix(dotpaths[i]))
|
||||
if !curStack.IsPackage {
|
||||
return SexpNull, fmt.Errorf("error locating symbol '%s': current Stack is not a package", curSym.name)
|
||||
}
|
||||
|
||||
ret, err, scop = curStack.LookupSymbol(curSym, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("could not find symbol '%s' in current package '%v'",
|
||||
curSym.name, curStack.PackageName)
|
||||
}
|
||||
if setVal != nil && i == lenpath-1 {
|
||||
// check if private
|
||||
err = errIfPrivate(curSym.name, curStack)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
// assign now
|
||||
scop.Map[curSym.number] = *setVal
|
||||
// done with SET
|
||||
return *setVal, nil
|
||||
}
|
||||
|
||||
if i == lenpath-1 {
|
||||
// final element
|
||||
switch ret.(type) {
|
||||
case *Stack:
|
||||
// allow package within package to be inspected.
|
||||
// done with GET
|
||||
return ret, nil
|
||||
default:
|
||||
// don't allow private value within package to be inspected.
|
||||
err = errIfPrivate(curSym.name, curStack)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
}
|
||||
// done with GET
|
||||
return ret, nil
|
||||
}
|
||||
// invar: i < lenpath-1, so go deeper
|
||||
switch x := ret.(type) {
|
||||
case *SexpHash:
|
||||
err = errIfPrivate(curSym.name, curStack)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
//P("\n found hash in x at i=%d, looping to next i\n", i)
|
||||
return x.nestedPathGetSet(env, dotpaths[1:], setVal)
|
||||
case *Stack:
|
||||
curStack = x
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("not a record or scope: cannot get field '%s'"+
|
||||
" out of type %T)", dotpaths[i+1][1:], x)
|
||||
}
|
||||
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
63
vendor/github.com/glycerine/zygomys/zygo/strutils.go
generated
vendored
Normal file
63
vendor/github.com/glycerine/zygomys/zygo/strutils.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ConcatStr(str *SexpStr, rest []Sexp) (*SexpStr, error) {
|
||||
res := &SexpStr{S: str.S}
|
||||
for i, x := range rest {
|
||||
switch t := x.(type) {
|
||||
case *SexpStr:
|
||||
res.S += t.S
|
||||
case *SexpChar:
|
||||
res.S += string(t.Val)
|
||||
default:
|
||||
return &SexpStr{}, fmt.Errorf("ConcatStr error: %d-th argument (0-based) is "+
|
||||
"not a string (was %T)", i, t)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func AppendStr(str *SexpStr, expr Sexp) (*SexpStr, error) {
|
||||
var chr *SexpChar
|
||||
switch t := expr.(type) {
|
||||
case *SexpChar:
|
||||
chr = t
|
||||
case *SexpStr:
|
||||
return &SexpStr{S: str.S + t.S}, nil
|
||||
default:
|
||||
return &SexpStr{}, errors.New("second argument is not a char")
|
||||
}
|
||||
|
||||
return &SexpStr{S: str.S + string(chr.Val)}, nil
|
||||
}
|
||||
|
||||
func StringUtilFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
var s string
|
||||
switch str := args[0].(type) {
|
||||
case *SexpStr:
|
||||
s = str.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("string required, got %T", s)
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "chomp":
|
||||
n := len(s)
|
||||
if n > 0 && s[n-1] == '\n' {
|
||||
return &SexpStr{S: s[:n-1]}, nil
|
||||
}
|
||||
return &SexpStr{S: s}, nil
|
||||
case "trim":
|
||||
return &SexpStr{S: strings.TrimSpace(s)}, nil
|
||||
}
|
||||
return SexpNull, fmt.Errorf("unrecognized command '%s'", name)
|
||||
}
|
||||
125
vendor/github.com/glycerine/zygomys/zygo/system.go
generated
vendored
Normal file
125
vendor/github.com/glycerine/zygomys/zygo/system.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ShellCmd string = "/bin/bash"
|
||||
|
||||
func init() {
|
||||
SetShellCmd()
|
||||
}
|
||||
|
||||
// set ShellCmd as used by SystemFunction
|
||||
func SetShellCmd() {
|
||||
if runtime.GOOS == "windows" {
|
||||
ShellCmd = os.Getenv("COMSPEC")
|
||||
return
|
||||
}
|
||||
try := []string{"/usr/bin/bash"}
|
||||
if !FileExists(ShellCmd) {
|
||||
for i := range try {
|
||||
b := try[i]
|
||||
if FileExists(b) {
|
||||
ShellCmd = b
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sys is a builder. shell out, return the combined output.
|
||||
func SystemBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
//P("SystemBuilder called with args='%#v'", args)
|
||||
return SystemFunction(env, name, args)
|
||||
}
|
||||
|
||||
func SystemFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) == 0 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
flat, err := flattenToWordsHelper(args)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("flatten on '%#v' failed with error '%s'", args, err)
|
||||
}
|
||||
if len(flat) == 0 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
joined := strings.Join(flat, " ")
|
||||
cmd := ShellCmd
|
||||
|
||||
var out []byte
|
||||
if runtime.GOOS == "windows" {
|
||||
out, err = exec.Command(cmd, "/c", joined).CombinedOutput()
|
||||
} else {
|
||||
out, err = exec.Command(cmd, "-c", joined).CombinedOutput()
|
||||
}
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error from command: '%s'. Output:'%s'", err, string(Chomp(out)))
|
||||
}
|
||||
return &SexpStr{S: string(Chomp(out))}, nil
|
||||
}
|
||||
|
||||
// given strings/lists of strings with possible whitespace
|
||||
// flatten out to a array of SexpStr with no internal whitespace,
|
||||
// suitable for passing along to (system) / exec.Command()
|
||||
func FlattenToWordsFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
if len(args) == 0 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
stringArgs, err := flattenToWordsHelper(args)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
|
||||
// Now convert to []Sexp{SexpStr}
|
||||
res := make([]Sexp, len(stringArgs))
|
||||
for i := range stringArgs {
|
||||
res[i] = &SexpStr{S: stringArgs[i]}
|
||||
}
|
||||
return env.NewSexpArray(res), nil
|
||||
}
|
||||
|
||||
func flattenToWordsHelper(args []Sexp) ([]string, error) {
|
||||
stringArgs := []string{}
|
||||
|
||||
for i := range args {
|
||||
switch c := args[i].(type) {
|
||||
case *SexpStr:
|
||||
many := strings.Split(c.S, " ")
|
||||
stringArgs = append(stringArgs, many...)
|
||||
case *SexpSymbol:
|
||||
stringArgs = append(stringArgs, c.name)
|
||||
case *SexpPair:
|
||||
carry, err := ListToArray(c)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("tried to convert list of strings to array but failed with error '%s'. Input was type %T / val = '%#v'", err, c, c)
|
||||
}
|
||||
moreWords, err := flattenToWordsHelper(carry)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
stringArgs = append(stringArgs, moreWords...)
|
||||
default:
|
||||
return []string{}, fmt.Errorf("arguments to system must be strings; instead we have %T / val = '%#v'", c, c)
|
||||
}
|
||||
} // end i over args
|
||||
// INVAR: stringArgs has our flattened list.
|
||||
return stringArgs, nil
|
||||
}
|
||||
|
||||
func Chomp(by []byte) []byte {
|
||||
if len(by) > 0 {
|
||||
n := len(by)
|
||||
if by[n-1] == '\n' {
|
||||
return by[:n-1]
|
||||
}
|
||||
}
|
||||
return by
|
||||
}
|
||||
111
vendor/github.com/glycerine/zygomys/zygo/time.go
generated
vendored
Normal file
111
vendor/github.com/glycerine/zygomys/zygo/time.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var UtcTz *time.Location
|
||||
var NYC *time.Location
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
UtcTz, err = time.LoadLocation("UTC")
|
||||
panicOn(err)
|
||||
NYC, err = time.LoadLocation("America/New_York")
|
||||
panicOn(err)
|
||||
}
|
||||
|
||||
type SexpTime struct {
|
||||
Tm time.Time
|
||||
}
|
||||
|
||||
func (r *SexpTime) Type() *RegisteredType {
|
||||
return nil // TODO what should this be?
|
||||
}
|
||||
|
||||
func (t *SexpTime) SexpString(ps *PrintState) string {
|
||||
return t.Tm.String()
|
||||
}
|
||||
|
||||
func NowFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
return &SexpTime{Tm: time.Now()}, nil
|
||||
}
|
||||
|
||||
// string -> time.Time
|
||||
func AsTmFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var str *SexpStr
|
||||
switch t := args[0].(type) {
|
||||
case *SexpStr:
|
||||
str = t
|
||||
default:
|
||||
return SexpNull,
|
||||
errors.New("argument of astm should be a string RFC3999Nano timestamp that we want to convert to time.Time")
|
||||
}
|
||||
|
||||
tm, err := time.ParseInLocation(time.RFC3339Nano, str.S, NYC)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
return &SexpTime{Tm: tm.In(NYC)}, nil
|
||||
}
|
||||
|
||||
func TimeitFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
if len(args) != 1 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
|
||||
var fun *SexpFunction
|
||||
switch t := args[0].(type) {
|
||||
case *SexpFunction:
|
||||
fun = t
|
||||
default:
|
||||
return SexpNull,
|
||||
errors.New("argument of timeit should be function")
|
||||
}
|
||||
|
||||
starttime := time.Now()
|
||||
elapsed := time.Since(starttime)
|
||||
maxseconds := 10.0
|
||||
var iterations int
|
||||
|
||||
for iterations = 0; iterations < 10000; iterations++ {
|
||||
_, err := env.Apply(fun, []Sexp{})
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
elapsed = time.Since(starttime)
|
||||
if elapsed.Seconds() > maxseconds {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("ran %d iterations in %f seconds\n",
|
||||
iterations, elapsed.Seconds())
|
||||
fmt.Printf("average %f seconds per run\n",
|
||||
elapsed.Seconds()/float64(iterations))
|
||||
|
||||
return SexpNull, nil
|
||||
}
|
||||
|
||||
func MillisFunction(env *Zlisp, name string,
|
||||
args []Sexp) (Sexp, error) {
|
||||
millis := time.Now().UnixNano() / 1000000
|
||||
return &SexpInt{Val: int64(millis)}, nil
|
||||
}
|
||||
|
||||
func (env *Zlisp) ImportTime() {
|
||||
env.AddFunction("now", NowFunction)
|
||||
env.AddFunction("timeit", TimeitFunction)
|
||||
env.AddFunction("astm", AsTmFunction)
|
||||
env.AddFunction("millis", MillisFunction)
|
||||
}
|
||||
205
vendor/github.com/glycerine/zygomys/zygo/typeutils.go
generated
vendored
Normal file
205
vendor/github.com/glycerine/zygomys/zygo/typeutils.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func IsArray(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpArray:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsList(expr Sexp) bool {
|
||||
if expr == SexpNull {
|
||||
return true
|
||||
}
|
||||
switch list := expr.(type) {
|
||||
case *SexpPair:
|
||||
return IsList(list.Tail)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsAssignmentList(expr Sexp, pos int) (bool, int) {
|
||||
if expr == SexpNull {
|
||||
return false, -1
|
||||
}
|
||||
switch list := expr.(type) {
|
||||
case *SexpPair:
|
||||
sym, isSym := list.Head.(*SexpSymbol)
|
||||
if !isSym {
|
||||
return IsAssignmentList(list.Tail, pos+1)
|
||||
}
|
||||
if sym.name == "=" || sym.name == ":=" {
|
||||
return true, pos
|
||||
}
|
||||
return IsAssignmentList(list.Tail, pos+1)
|
||||
}
|
||||
return false, -1
|
||||
}
|
||||
|
||||
func IsFloat(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpFloat:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsInt(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpInt:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsString(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpStr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsChar(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpChar:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNumber(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpFloat:
|
||||
return true
|
||||
case *SexpInt:
|
||||
return true
|
||||
case *SexpChar:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsSymbol(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpSymbol:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsHash(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpHash:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsZero(expr Sexp) bool {
|
||||
switch e := expr.(type) {
|
||||
case *SexpInt:
|
||||
return int(e.Val) == 0
|
||||
case *SexpChar:
|
||||
return int(e.Val) == 0
|
||||
case *SexpFloat:
|
||||
return float64(e.Val) == 0.0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsEmpty(expr Sexp) bool {
|
||||
if expr == SexpNull {
|
||||
return true
|
||||
}
|
||||
|
||||
switch e := expr.(type) {
|
||||
case *SexpArray:
|
||||
return len(e.Val) == 0
|
||||
case *SexpHash:
|
||||
return HashIsEmpty(e)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsFunc(expr Sexp) bool {
|
||||
switch expr.(type) {
|
||||
case *SexpFunction:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TypeOf(expr Sexp) *SexpStr {
|
||||
v := ""
|
||||
switch e := expr.(type) {
|
||||
case *SexpRaw:
|
||||
v = "raw"
|
||||
case *SexpBool:
|
||||
v = "bool"
|
||||
case *SexpArray:
|
||||
v = "array"
|
||||
case *SexpInt:
|
||||
v = "int64"
|
||||
case *SexpUint64:
|
||||
v = "uint64"
|
||||
case *SexpStr:
|
||||
v = "string"
|
||||
case *SexpChar:
|
||||
v = "char"
|
||||
case *SexpFloat:
|
||||
v = "float64"
|
||||
case *SexpHash:
|
||||
v = e.TypeName
|
||||
case *SexpPair:
|
||||
v = "list"
|
||||
case *SexpSymbol:
|
||||
v = "symbol"
|
||||
case *SexpFunction:
|
||||
v = "func"
|
||||
case *SexpSentinel:
|
||||
v = "nil"
|
||||
case *SexpTime:
|
||||
v = "time.Time"
|
||||
case *RegisteredType:
|
||||
v = "regtype"
|
||||
case *SexpPointer:
|
||||
v = e.MyType.RegisteredName
|
||||
case *SexpArraySelector:
|
||||
v = "arraySelector"
|
||||
case *SexpHashSelector:
|
||||
v = "hashSelector"
|
||||
case *SexpReflect:
|
||||
rt := expr.Type()
|
||||
if rt != nil {
|
||||
return &SexpStr{S: rt.RegisteredName}
|
||||
}
|
||||
//v = reflect.Value(e).Type().Name()
|
||||
//if v == "Ptr" {
|
||||
// v = reflect.Value(e).Type().Elem().Kind().String()
|
||||
//}
|
||||
kind := reflect.Value(e.Val).Type().Kind()
|
||||
if kind == reflect.Ptr {
|
||||
v = reflect.Value(e.Val).Elem().Type().Name()
|
||||
} else {
|
||||
P("kind = %v", kind)
|
||||
v = "reflect.Value"
|
||||
}
|
||||
case *Stack:
|
||||
if e.IsPackage {
|
||||
v = "package"
|
||||
} else {
|
||||
v = "stack"
|
||||
}
|
||||
default:
|
||||
fmt.Printf("\n error: unknown type: %T in '%#v'\n", e, e)
|
||||
}
|
||||
return &SexpStr{S: v}
|
||||
}
|
||||
11
vendor/github.com/glycerine/zygomys/zygo/version.go
generated
vendored
Normal file
11
vendor/github.com/glycerine/zygomys/zygo/version.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package zygo
|
||||
|
||||
import "fmt"
|
||||
|
||||
// version information. See Makefile and gitcommit.go for update/init.
|
||||
var GITLASTTAG string
|
||||
var GITLASTCOMMIT string
|
||||
|
||||
func Version() string {
|
||||
return fmt.Sprintf("%s/%s", GITLASTTAG, GITLASTCOMMIT)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user