mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 21:11:03 +01:00
added
This commit is contained in:
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)
|
||||
}
|
||||
847
vendor/github.com/glycerine/zygomys/zygo/vm.go
generated
vendored
Normal file
847
vendor/github.com/glycerine/zygomys/zygo/vm.go
generated
vendored
Normal file
@@ -0,0 +1,847 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Instruction interface {
|
||||
InstrString() string
|
||||
Execute(env *Zlisp) error
|
||||
}
|
||||
|
||||
type JumpInstr struct {
|
||||
addpc int
|
||||
where string
|
||||
}
|
||||
|
||||
var OutOfBounds error = errors.New("jump out of bounds")
|
||||
|
||||
func (j JumpInstr) InstrString() string {
|
||||
return fmt.Sprintf("jump %d %s", j.addpc, j.where)
|
||||
}
|
||||
|
||||
func (j JumpInstr) Execute(env *Zlisp) error {
|
||||
newpc := env.pc + j.addpc
|
||||
if newpc < 0 || newpc > env.CurrentFunctionSize() {
|
||||
return OutOfBounds
|
||||
}
|
||||
env.pc = newpc
|
||||
return nil
|
||||
}
|
||||
|
||||
type GotoInstr struct {
|
||||
location int
|
||||
}
|
||||
|
||||
func (g GotoInstr) InstrString() string {
|
||||
return fmt.Sprintf("goto %d", g.location)
|
||||
}
|
||||
|
||||
func (g GotoInstr) Execute(env *Zlisp) error {
|
||||
if g.location < 0 || g.location > env.CurrentFunctionSize() {
|
||||
return OutOfBounds
|
||||
}
|
||||
env.pc = g.location
|
||||
return nil
|
||||
}
|
||||
|
||||
type BranchInstr struct {
|
||||
direction bool
|
||||
location int
|
||||
}
|
||||
|
||||
func (b BranchInstr) InstrString() string {
|
||||
var format string
|
||||
if b.direction {
|
||||
format = "br %d"
|
||||
} else {
|
||||
format = "brn %d"
|
||||
}
|
||||
return fmt.Sprintf(format, b.location)
|
||||
}
|
||||
|
||||
func (b BranchInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b.direction == IsTruthy(expr) {
|
||||
return JumpInstr{addpc: b.location}.Execute(env)
|
||||
}
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushInstr struct {
|
||||
expr Sexp
|
||||
}
|
||||
|
||||
func (p PushInstr) InstrString() string {
|
||||
return "push " + p.expr.SexpString(nil)
|
||||
}
|
||||
|
||||
func (p PushInstr) Execute(env *Zlisp) error {
|
||||
env.datastack.PushExpr(p.expr)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type PopInstr int
|
||||
|
||||
func (p PopInstr) InstrString() string {
|
||||
return "pop"
|
||||
}
|
||||
|
||||
func (p PopInstr) Execute(env *Zlisp) error {
|
||||
_, err := env.datastack.PopExpr()
|
||||
env.pc++
|
||||
return err
|
||||
}
|
||||
|
||||
type DupInstr int
|
||||
|
||||
func (d DupInstr) InstrString() string {
|
||||
return "dup"
|
||||
}
|
||||
|
||||
func (d DupInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.GetExpr(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.datastack.PushExpr(expr)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnvToStackInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (g EnvToStackInstr) InstrString() string {
|
||||
return fmt.Sprintf("envToStack %s", g.sym.name)
|
||||
}
|
||||
|
||||
func (g EnvToStackInstr) Execute(env *Zlisp) error {
|
||||
VPrintf("in EnvToStackInstr\n")
|
||||
defer VPrintf("leaving EnvToStackInstr env.pc =%v\n", env.pc)
|
||||
|
||||
macxpr, isMacro := env.macros[g.sym.number]
|
||||
if isMacro {
|
||||
if macxpr.orig != nil {
|
||||
return fmt.Errorf("'%s' is a macro, with definition: %s\n", g.sym.name, macxpr.orig.SexpString(nil))
|
||||
}
|
||||
return fmt.Errorf("'%s' is a builtin macro.\n", g.sym.name)
|
||||
}
|
||||
var expr Sexp
|
||||
var err error
|
||||
expr, err, _ = env.LexicalLookupSymbol(g.sym, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.datastack.PushExpr(expr)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type PopStackPutEnvInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (p PopStackPutEnvInstr) InstrString() string {
|
||||
return fmt.Sprintf("popStackPutEnv %s", p.sym.name)
|
||||
}
|
||||
|
||||
func (p PopStackPutEnvInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.pc++
|
||||
return env.LexicalBindSymbol(p.sym, expr)
|
||||
|
||||
}
|
||||
|
||||
// Update takes top of datastack and
|
||||
// assigns it to sym when sym is found
|
||||
// already in the current scope or
|
||||
// up the stack. Used
|
||||
// to implement (set v 10) when v is
|
||||
// not in the local scope.
|
||||
//
|
||||
type UpdateInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (p UpdateInstr) InstrString() string {
|
||||
return fmt.Sprintf("putup %s", p.sym.name)
|
||||
}
|
||||
|
||||
func (p UpdateInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.pc++
|
||||
|
||||
if p.sym.isSigil {
|
||||
Q("UpdateInstr: ignoring sigil symbol '%s'", p.sym.SexpString(nil))
|
||||
return nil
|
||||
}
|
||||
if p.sym.isDot {
|
||||
Q("UpdateInstr: dot symbol '%s' being updated with dotGetSetHelper()",
|
||||
p.sym.SexpString(nil))
|
||||
_, err := dotGetSetHelper(env, p.sym.name, &expr)
|
||||
return err
|
||||
}
|
||||
|
||||
// if found up the stack, we will (set) expr
|
||||
_, err, _ = env.LexicalLookupSymbol(p.sym, &expr)
|
||||
if err != nil {
|
||||
// not found up the stack, so treat like (def)
|
||||
// instead of (set)
|
||||
return env.LexicalBindSymbol(p.sym, expr)
|
||||
}
|
||||
|
||||
//return scope.UpdateSymbolInScope(p.sym, expr) // LexicalLookupSymbol now takes a setVal pointer which does this automatically
|
||||
return err
|
||||
}
|
||||
|
||||
type CallInstr struct {
|
||||
sym *SexpSymbol
|
||||
nargs int
|
||||
}
|
||||
|
||||
func (c CallInstr) InstrString() string {
|
||||
return fmt.Sprintf("call %s %d", c.sym.name, c.nargs)
|
||||
}
|
||||
|
||||
func (c CallInstr) Execute(env *Zlisp) error {
|
||||
f, ok := env.builtins[c.sym.number]
|
||||
if ok {
|
||||
_, err := env.CallUserFunction(f, c.sym.name, c.nargs)
|
||||
return err
|
||||
}
|
||||
var funcobj, indirectFuncName Sexp
|
||||
var err error
|
||||
|
||||
funcobj, err, _ = env.LexicalLookupSymbol(c.sym, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//P("\n in CallInstr, after looking up c.sym='%s', got funcobj='%v'. datastack is:\n", c.sym.name, funcobj.SexpString(nil))
|
||||
//env.datastack.PrintStack()
|
||||
switch f := funcobj.(type) {
|
||||
case *SexpSymbol:
|
||||
// is it a dot-symbol call?
|
||||
//P("\n in CallInstr, found symbol\n")
|
||||
if c.sym.isDot {
|
||||
//P("\n in CallInstr, found symbol, c.sym.isDot is true\n")
|
||||
|
||||
dotSymRef, dotLookupErr := dotGetSetHelper(env, c.sym.name, nil)
|
||||
// cannot error out yet, we might be assigning to a new field,
|
||||
// not already set.
|
||||
|
||||
if dotLookupErr != nil {
|
||||
return dotLookupErr
|
||||
}
|
||||
|
||||
// are we a value request (no further args), or a fuction/method call?
|
||||
//P("\n in CallInstr, found dot-symbol\n")
|
||||
// now always be a function call here, allowing zero-argument calls.
|
||||
indirectFuncName = dotSymRef
|
||||
} else {
|
||||
// not isDot
|
||||
|
||||
// allow symbols to refer to dot-symbols, that then we call
|
||||
indirectFuncName, err = dotGetSetHelper(env, f.name, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' refers to symbol '%s', but '%s' could not be resolved: '%s'.",
|
||||
c.sym.name, f.name, f.name, err)
|
||||
}
|
||||
|
||||
// allow symbols to refer to functions that we then call
|
||||
/*
|
||||
indirectFuncName, err, _ = env.LexicalLookupSymbol(f, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' refers to symbol '%s', but '%s' could not be resolved: '%s'.",
|
||||
c.sym.name, f.name, f.name, err)
|
||||
}
|
||||
*/
|
||||
//P("\n in CallInstr, found symbol, c.sym.isDot is false. f of type %T/val = %v. indirectFuncName = '%v'\n", f, f.SexpString(nil), indirectFuncName.SexpString(nil))
|
||||
|
||||
}
|
||||
|
||||
//P("in CallInstr, reached switch on indirectFuncName.(type)")
|
||||
switch g := indirectFuncName.(type) {
|
||||
case *SexpFunction:
|
||||
if !g.user {
|
||||
return env.CallFunction(g, c.nargs)
|
||||
}
|
||||
_, err := env.CallUserFunction(g, f.name, c.nargs)
|
||||
return err
|
||||
default:
|
||||
if err != nil {
|
||||
return fmt.Errorf("symbol '%s' refers to '%s' which does not refer to a function.", c.sym.name, f.name)
|
||||
}
|
||||
}
|
||||
|
||||
case *SexpFunction:
|
||||
if !f.user {
|
||||
return env.CallFunction(f, c.nargs)
|
||||
}
|
||||
_, err := env.CallUserFunction(f, c.sym.name, c.nargs)
|
||||
return err
|
||||
|
||||
case *RegisteredType:
|
||||
if f.Constructor == nil {
|
||||
env.pc++
|
||||
res, err := baseConstruct(env, f, c.nargs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.datastack.PushExpr(res)
|
||||
return nil
|
||||
}
|
||||
//P("call instruction for RegisteredType!")
|
||||
_, err := env.CallUserFunction(f.Constructor, c.sym.name, c.nargs)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s is not a function", c.sym.name)
|
||||
}
|
||||
|
||||
type DispatchInstr struct {
|
||||
nargs int
|
||||
}
|
||||
|
||||
func (d DispatchInstr) InstrString() string {
|
||||
return fmt.Sprintf("dispatch %d", d.nargs)
|
||||
}
|
||||
|
||||
func (d DispatchInstr) Execute(env *Zlisp) error {
|
||||
funcobj, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch f := funcobj.(type) {
|
||||
case *SexpFunction:
|
||||
if !f.user {
|
||||
return env.CallFunction(f, d.nargs)
|
||||
}
|
||||
_, err := env.CallUserFunction(f, f.name, d.nargs)
|
||||
return err
|
||||
}
|
||||
// allow ([] int64) to express slice of int64.
|
||||
switch arr := funcobj.(type) {
|
||||
case *SexpArray:
|
||||
if len(arr.Val) == 0 {
|
||||
_, err := env.CallUserFunction(sxSliceOf, funcobj.SexpString(nil), d.nargs)
|
||||
return err
|
||||
}
|
||||
// call along with the array as an argument so we know the size of the
|
||||
// array / matrix / tensor to make. The 2nd argument will be the dimension array.
|
||||
env.datastack.PushExpr(arr)
|
||||
_, err := env.CallUserFunction(sxArrayOf, funcobj.SexpString(nil), d.nargs+1)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("unbalanced parenthesis in the input? : not a function on top of datastack: '%T/%#v'", funcobj, funcobj)
|
||||
}
|
||||
|
||||
type ReturnInstr struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (r ReturnInstr) Execute(env *Zlisp) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
return env.ReturnFromFunction()
|
||||
}
|
||||
|
||||
func (r ReturnInstr) InstrString() string {
|
||||
if r.err == nil {
|
||||
return "ret"
|
||||
}
|
||||
return "ret \"" + r.err.Error() + "\""
|
||||
}
|
||||
|
||||
type AddScopeInstr struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (a AddScopeInstr) InstrString() string {
|
||||
return "add scope " + a.Name
|
||||
}
|
||||
|
||||
func (a AddScopeInstr) Execute(env *Zlisp) error {
|
||||
sc := env.NewNamedScope(fmt.Sprintf("scope Name: '%s'",
|
||||
a.Name))
|
||||
env.linearstack.Push(sc)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type AddFuncScopeInstr struct {
|
||||
Name string
|
||||
Helper *AddFuncScopeHelper // we need a pointer we can update later once we know MyFunction
|
||||
}
|
||||
|
||||
type AddFuncScopeHelper struct {
|
||||
MyFunction *SexpFunction
|
||||
}
|
||||
|
||||
func (a AddFuncScopeInstr) InstrString() string {
|
||||
return "add func scope " + a.Name
|
||||
}
|
||||
|
||||
func (a AddFuncScopeInstr) Execute(env *Zlisp) error {
|
||||
sc := env.NewNamedScope(fmt.Sprintf("%s at pc=%v",
|
||||
env.curfunc.name, env.pc))
|
||||
sc.IsFunction = true
|
||||
sc.MyFunction = a.Helper.MyFunction
|
||||
env.linearstack.Push(sc)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type RemoveScopeInstr struct{}
|
||||
|
||||
func (a RemoveScopeInstr) InstrString() string {
|
||||
return "rem runtime scope"
|
||||
}
|
||||
|
||||
func (a RemoveScopeInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
return env.linearstack.PopScope()
|
||||
}
|
||||
|
||||
type ExplodeInstr int
|
||||
|
||||
func (e ExplodeInstr) InstrString() string {
|
||||
return "explode"
|
||||
}
|
||||
|
||||
func (e ExplodeInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
arr, err := ListToArray(expr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, val := range arr {
|
||||
env.datastack.PushExpr(val)
|
||||
}
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type SquashInstr int
|
||||
|
||||
func (s SquashInstr) InstrString() string {
|
||||
return "squash"
|
||||
}
|
||||
|
||||
func (s SquashInstr) Execute(env *Zlisp) error {
|
||||
var list Sexp = SexpNull
|
||||
|
||||
for {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expr == SexpMarker {
|
||||
break
|
||||
}
|
||||
list = Cons(expr, list)
|
||||
}
|
||||
env.datastack.PushExpr(list)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
// bind these symbols to the SexpPair list found at
|
||||
// datastack top.
|
||||
type BindlistInstr struct {
|
||||
syms []*SexpSymbol
|
||||
}
|
||||
|
||||
func (b BindlistInstr) InstrString() string {
|
||||
joined := ""
|
||||
for _, s := range b.syms {
|
||||
joined += s.name + " "
|
||||
}
|
||||
return fmt.Sprintf("bindlist %s", joined)
|
||||
}
|
||||
|
||||
func (b BindlistInstr) Execute(env *Zlisp) error {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
arr, err := ListToArray(expr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nsym := len(b.syms)
|
||||
narr := len(arr)
|
||||
if narr < nsym {
|
||||
return fmt.Errorf("bindlist failing: %d targets but only %d sources", nsym, narr)
|
||||
}
|
||||
|
||||
for i, bindThisSym := range b.syms {
|
||||
env.LexicalBindSymbol(bindThisSym, arr[i])
|
||||
}
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type VectorizeInstr int
|
||||
|
||||
func (s VectorizeInstr) InstrString() string {
|
||||
return fmt.Sprintf("vectorize %v", int(s))
|
||||
}
|
||||
|
||||
func (s VectorizeInstr) Execute(env *Zlisp) error {
|
||||
vec := make([]Sexp, 0)
|
||||
env.pc++
|
||||
for {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expr == SexpMarker {
|
||||
break
|
||||
}
|
||||
vec = append([]Sexp{expr}, vec...)
|
||||
}
|
||||
env.datastack.PushExpr(&SexpArray{Val: vec, Env: env})
|
||||
return nil
|
||||
}
|
||||
|
||||
type HashizeInstr struct {
|
||||
HashLen int
|
||||
TypeName string
|
||||
}
|
||||
|
||||
func (s HashizeInstr) InstrString() string {
|
||||
return "hashize"
|
||||
}
|
||||
|
||||
func (s HashizeInstr) Execute(env *Zlisp) error {
|
||||
a := make([]Sexp, 0)
|
||||
|
||||
for {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expr == SexpMarker {
|
||||
break
|
||||
}
|
||||
a = append(a, expr)
|
||||
}
|
||||
hash, err := MakeHash(a, s.TypeName, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.datastack.PushExpr(hash)
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type LabelInstr struct {
|
||||
label string
|
||||
}
|
||||
|
||||
func (s LabelInstr) InstrString() string {
|
||||
return fmt.Sprintf("label %s", s.label)
|
||||
}
|
||||
|
||||
func (s LabelInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type BreakInstr struct {
|
||||
loop *Loop
|
||||
pos int
|
||||
}
|
||||
|
||||
func (s BreakInstr) InstrString() string {
|
||||
if s.pos == 0 {
|
||||
return fmt.Sprintf("break %s", s.loop.stmtname.name)
|
||||
}
|
||||
return fmt.Sprintf("break %s (loop is at %d)", s.loop.stmtname.name, s.pos)
|
||||
}
|
||||
|
||||
func (s *BreakInstr) Execute(env *Zlisp) error {
|
||||
if s.pos == 0 {
|
||||
pos, err := env.FindLoop(s.loop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.pos = pos
|
||||
}
|
||||
env.pc = s.pos + s.loop.breakOffset
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContinueInstr struct {
|
||||
loop *Loop
|
||||
pos int
|
||||
}
|
||||
|
||||
func (s ContinueInstr) InstrString() string {
|
||||
if s.pos == 0 {
|
||||
return fmt.Sprintf("continue %s", s.loop.stmtname.name)
|
||||
}
|
||||
return fmt.Sprintf("continue %s (loop is at pos %d)", s.loop.stmtname.name, s.pos)
|
||||
}
|
||||
|
||||
func (s *ContinueInstr) Execute(env *Zlisp) error {
|
||||
VPrintf("\n executing ContinueInstr with loop: '%#v'\n", s.loop)
|
||||
if s.pos == 0 {
|
||||
pos, err := env.FindLoop(s.loop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.pos = pos
|
||||
}
|
||||
env.pc = s.pos + s.loop.continueOffset
|
||||
VPrintf("\n more detail ContinueInstr pos=%d, setting pc = %d\n", s.pos, env.pc)
|
||||
return nil
|
||||
}
|
||||
|
||||
type LoopStartInstr struct {
|
||||
loop *Loop
|
||||
}
|
||||
|
||||
func (s LoopStartInstr) InstrString() string {
|
||||
return fmt.Sprintf("loopstart %s", s.loop.stmtname.name)
|
||||
}
|
||||
|
||||
func (s LoopStartInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
// stack cleanup discipline instructions let us
|
||||
// ensure the stack gets reset to a previous
|
||||
// known good level. The sym designates
|
||||
// how far down to clean up, in a unique and
|
||||
// distinguishable gensym-ed manner.
|
||||
|
||||
// create a stack mark
|
||||
type PushStackmarkInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (s PushStackmarkInstr) InstrString() string {
|
||||
return fmt.Sprintf("push-stack-mark %s", s.sym.name)
|
||||
}
|
||||
|
||||
func (s PushStackmarkInstr) Execute(env *Zlisp) error {
|
||||
env.datastack.PushExpr(&SexpStackmark{sym: s.sym})
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanup until our stackmark, but leave it in place
|
||||
type PopUntilStackmarkInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (s PopUntilStackmarkInstr) InstrString() string {
|
||||
return fmt.Sprintf("pop-until-stack-mark %s", s.sym.name)
|
||||
}
|
||||
|
||||
func (s PopUntilStackmarkInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
toploop:
|
||||
for {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
P("alert: did not find SexpStackmark '%s'", s.sym.name)
|
||||
return err
|
||||
}
|
||||
switch m := expr.(type) {
|
||||
case *SexpStackmark:
|
||||
if m.sym.number == s.sym.number {
|
||||
env.datastack.PushExpr(m)
|
||||
break toploop
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// erase everything up-to-and-including our mark
|
||||
type ClearStackmarkInstr struct {
|
||||
sym *SexpSymbol
|
||||
}
|
||||
|
||||
func (s ClearStackmarkInstr) InstrString() string {
|
||||
return fmt.Sprintf("clear-stack-mark %s", s.sym.name)
|
||||
}
|
||||
|
||||
func (s ClearStackmarkInstr) Execute(env *Zlisp) error {
|
||||
toploop:
|
||||
for {
|
||||
expr, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch m := expr.(type) {
|
||||
case *SexpStackmark:
|
||||
if m.sym.number == s.sym.number {
|
||||
break toploop
|
||||
}
|
||||
}
|
||||
}
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
type DebugInstr struct {
|
||||
diagnostic string
|
||||
}
|
||||
|
||||
func (g DebugInstr) InstrString() string {
|
||||
return fmt.Sprintf("debug %s", g.diagnostic)
|
||||
}
|
||||
|
||||
func (g DebugInstr) Execute(env *Zlisp) error {
|
||||
switch g.diagnostic {
|
||||
case "showScopes":
|
||||
err := env.ShowStackStackAndScopeStack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("unknown diagnostic %v", g.diagnostic))
|
||||
}
|
||||
env.pc++
|
||||
return nil
|
||||
}
|
||||
|
||||
// when a defn or fn executes, capture the creation env.
|
||||
type CreateClosureInstr struct {
|
||||
sfun *SexpFunction
|
||||
}
|
||||
|
||||
func (a CreateClosureInstr) InstrString() string {
|
||||
return "create closure " + a.sfun.SexpString(nil)
|
||||
}
|
||||
|
||||
func (a CreateClosureInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
cls := NewClosing(a.sfun.name, env)
|
||||
myInvok := a.sfun.Copy()
|
||||
myInvok.SetClosing(cls)
|
||||
if env.curfunc != nil {
|
||||
a.sfun.parent = env.curfunc
|
||||
myInvok.parent = env.curfunc
|
||||
//P("myInvok is copy of a.sfun '%s' with parent = %s", a.sfun.name, myInvok.parent.name)
|
||||
}
|
||||
|
||||
ps8 := NewPrintStateWithIndent(8)
|
||||
shown, err := myInvok.ShowClosing(env, ps8,
|
||||
fmt.Sprintf("closedOverScopes of '%s'", myInvok.name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
VPrintf("+++ CreateClosure: assign to '%s' the stack:\n\n%s\n\n",
|
||||
myInvok.SexpString(nil), shown)
|
||||
top := cls.TopScope()
|
||||
VPrintf("222 CreateClosure: top of NewClosing Scope has addr %p and is\n",
|
||||
top)
|
||||
top.Show(env, ps8, fmt.Sprintf("top of NewClosing at %p", top))
|
||||
|
||||
env.datastack.PushExpr(myInvok)
|
||||
return nil
|
||||
}
|
||||
|
||||
type AssignInstr struct {
|
||||
}
|
||||
|
||||
func (a AssignInstr) InstrString() string {
|
||||
return "assign stack top to stack top -1"
|
||||
}
|
||||
|
||||
func (a AssignInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
rhs, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lhs, err := env.datastack.PopExpr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch x := lhs.(type) {
|
||||
case *SexpSymbol:
|
||||
return env.LexicalBindSymbol(x, rhs)
|
||||
case Selector:
|
||||
Q("AssignInstr: I see lhs is Selector")
|
||||
err := x.AssignToSelection(env, rhs)
|
||||
return err
|
||||
case *SexpArray:
|
||||
switch rhsArray := rhs.(type) {
|
||||
case *SexpArray:
|
||||
//Q("AssignInstr: lhs is SexpArray '%v', *and* rhs is SexpArray ='%v'",
|
||||
// x.SexpString(nil), rhsArray.SexpString(nil))
|
||||
nRhs := len(rhsArray.Val)
|
||||
nLhs := len(x.Val)
|
||||
if nRhs != nLhs {
|
||||
return fmt.Errorf("assignment count mismatch %v != %v", nLhs, nRhs)
|
||||
}
|
||||
for i := range x.Val {
|
||||
switch sym := x.Val[i].(type) {
|
||||
case *SexpSymbol:
|
||||
err = env.LexicalBindSymbol(sym, rhsArray.Val[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("assignment error: left-hand-side element %v needs to be a symbol but"+
|
||||
" we found %T", i, x.Val[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("AssignInstr: don't know how to assign rhs %T `%v` to lhs %T `%v`",
|
||||
rhs, rhs.SexpString(nil), lhs, lhs.SexpString(nil))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("AssignInstr: don't know how to assign to lhs %T", lhs)
|
||||
}
|
||||
|
||||
// PopScopeTransferToDataStackInstr is used to wrap up a package
|
||||
// and put it on the data stack as a value.
|
||||
type PopScopeTransferToDataStackInstr struct {
|
||||
PackageName string
|
||||
}
|
||||
|
||||
func (a PopScopeTransferToDataStackInstr) InstrString() string {
|
||||
return "pop scope transfer to data stack as package " + a.PackageName
|
||||
}
|
||||
|
||||
func (a PopScopeTransferToDataStackInstr) Execute(env *Zlisp) error {
|
||||
env.pc++
|
||||
stackClone := env.linearstack.Clone()
|
||||
stackClone.IsPackage = true // always/only used for packages.
|
||||
stackClone.PackageName = a.PackageName
|
||||
//P("PopScopeTransferToDataStackInstr: scope is '%v'", stackClone.SexpString(nil))
|
||||
env.linearstack.PopScope()
|
||||
env.datastack.PushExpr(stackClone)
|
||||
return nil
|
||||
}
|
||||
42
vendor/github.com/glycerine/zygomys/zygo/vprint.go
generated
vendored
Normal file
42
vendor/github.com/glycerine/zygomys/zygo/vprint.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var Verbose bool // set to true to debug
|
||||
var Working bool // currently under investigation
|
||||
|
||||
var V = VPrintf
|
||||
var W = WPrintf
|
||||
var Q = func(quietly_ignored ...interface{}) {} // quiet
|
||||
|
||||
// P is a shortcut for a call to fmt.Printf that implicitly starts
|
||||
// and ends its message with a newline.
|
||||
func P(format string, stuff ...interface{}) {
|
||||
fmt.Printf("\n "+format+"\n", stuff...)
|
||||
}
|
||||
|
||||
// get timestamp for logging purposes
|
||||
func ts() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05.999 -0700 MST")
|
||||
}
|
||||
|
||||
// time-stamped printf
|
||||
func TSPrintf(format string, a ...interface{}) {
|
||||
fmt.Printf("%s ", ts())
|
||||
fmt.Printf(format, a...)
|
||||
}
|
||||
|
||||
func VPrintf(format string, a ...interface{}) {
|
||||
if Verbose {
|
||||
TSPrintf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func WPrintf(format string, a ...interface{}) {
|
||||
if Working {
|
||||
TSPrintf(format, a...)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user