From 4f9baf7514b8099ce4b28e7a91e8308d2e704c9f Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Fri, 1 Jul 2016 17:31:32 -0600 Subject: [PATCH] starting over again. I'm just gonna keep doing this about once a year for the rest of my life --- .gitignore | 2 - .go.yaml | 2 - Makefile | 10 - README.md | 41 --- core/Makefile | 2 - core/cast.go | 81 ------ core/math.go | 604 --------------------------------------- core/mathgen/README.md | 18 -- core/mathgen/mathgen.go | 84 ------ core/mathgen/mathgen.tpl | 57 ---- docs/compilation.md | 113 -------- docs/functions.md | 86 ------ docs/go-interop.md | 53 ---- docs/packages.md | 95 ------ docs/syntax.md | 144 ---------- eval/eval.go | 76 ----- eval/eval_test.go | 41 --- macros/builtins.go | 10 - macros/macros.go | 30 -- macros/pkgctx/pkgctx.go | 113 -------- parse/lex/lex.go | 229 --------------- parse/lex/lex_test.go | 56 ---- parse/parse.go | 155 ---------- parse/parse_test.go | 93 ------ seq/hashmap.go | 166 ----------- seq/hashmap_test.go | 143 --------- seq/hashset.go | 418 --------------------------- seq/hashset_test.go | 273 ------------------ seq/lazy.go | 160 ----------- seq/lazy_test.go | 58 ---- seq/list.go | 172 ----------- seq/list_test.go | 184 ------------ seq/seq.go | 312 -------------------- seq/seq_test.go | 427 --------------------------- seq/util.go | 113 -------- types/types.go | 38 --- 36 files changed, 4659 deletions(-) delete mode 100644 .gitignore delete mode 100644 .go.yaml delete mode 100644 Makefile delete mode 100644 README.md delete mode 100644 core/Makefile delete mode 100644 core/cast.go delete mode 100644 core/math.go delete mode 100644 core/mathgen/README.md delete mode 100644 core/mathgen/mathgen.go delete mode 100644 core/mathgen/mathgen.tpl delete mode 100644 docs/compilation.md delete mode 100644 docs/functions.md delete mode 100644 docs/go-interop.md delete mode 100644 docs/packages.md delete mode 100644 docs/syntax.md delete mode 100644 eval/eval.go delete mode 100644 eval/eval_test.go delete mode 100644 macros/builtins.go delete mode 100644 macros/macros.go delete mode 100644 macros/pkgctx/pkgctx.go delete mode 100644 parse/lex/lex.go delete mode 100644 parse/lex/lex_test.go delete mode 100644 parse/parse.go delete mode 100644 parse/parse_test.go delete mode 100644 seq/hashmap.go delete mode 100644 seq/hashmap_test.go delete mode 100644 seq/hashset.go delete mode 100644 seq/hashset_test.go delete mode 100644 seq/lazy.go delete mode 100644 seq/lazy_test.go delete mode 100644 seq/list.go delete mode 100644 seq/list_test.go delete mode 100644 seq/seq.go delete mode 100644 seq/seq_test.go delete mode 100644 seq/util.go delete mode 100644 types/types.go diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 38f78a2..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.goat -*.test diff --git a/.go.yaml b/.go.yaml deleted file mode 100644 index e82fa5a..0000000 --- a/.go.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -path: github.com/mediocregopher/ginger diff --git a/Makefile b/Makefile deleted file mode 100644 index b917775..0000000 --- a/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: gen - -test: gen - go test ./... - -fmt: gen - go fmt ./... - -gen: - (cd core && make gen) diff --git a/README.md b/README.md deleted file mode 100644 index 42107e9..0000000 --- a/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Ginger - -A lisp-like language built on the go programming language. The ideas are still a -work-in-progress, and this repo is where I'm jotting down my notes: - -# Language manifesto - -* Anything written in go should be writeable in ginger in as many lines or - fewer. - -* When deciding whether to be more go-like or more like an existing lisp - language, err on being go-like. - -* The fewer built-in functions, the better. The standard library should be - easily discoverable and always importable so helper functions can be made - available. - -* When choosing between adding a syntax rule/datatype and not adding a feature, - err on not adding the feature. - -* It is not a goal to make ginger code be usable from go code. - -* Naming should use words instead of symbols, except when those symbols are - existing go operators. - -* Overloading functions should be used as little as possible. Possibly not at - all - -# Documentation - -See the [docs](/docs) folder for more details. Keep in mind that most of ginger -is still experimental and definitely not ready for the spotlight. - -Here is a list of the docs more or less in the order they should be read for a -complete overview of the language: - -* [syntax](/docs/syntax.md) -* [functions](/docs/functions.md) -* [compilation](/docs/compilation.md) -* [packages](/docs/packages.md) -* [go-interop](/docs/go-interop.md) diff --git a/core/Makefile b/core/Makefile deleted file mode 100644 index d4f4b2b..0000000 --- a/core/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -gen: - go run mathgen/mathgen.go > math.go diff --git a/core/cast.go b/core/cast.go deleted file mode 100644 index 23a39d2..0000000 --- a/core/cast.go +++ /dev/null @@ -1,81 +0,0 @@ -package core - -import ( - "github.com/mediocregopher/ginger/types" -) - -func Int(e types.Elem) int { - return e.(types.GoType).V.(int) -} - -func Int8(e types.Elem) int8 { - return e.(types.GoType).V.(int8) -} - -func Int16(e types.Elem) int16 { - return e.(types.GoType).V.(int16) -} - -func Int32(e types.Elem) int32 { - return e.(types.GoType).V.(int32) -} - -func Int64(e types.Elem) int64 { - return e.(types.GoType).V.(int64) -} - -func Uint(e types.Elem) uint { - return e.(types.GoType).V.(uint) -} - -func Uint8(e types.Elem) uint8 { - return e.(types.GoType).V.(uint8) -} - -func Uint16(e types.Elem) uint16 { - return e.(types.GoType).V.(uint16) -} - -func Uint32(e types.Elem) uint32 { - return e.(types.GoType).V.(uint32) -} - -func Uint64(e types.Elem) uint64 { - return e.(types.GoType).V.(uint64) -} - -func Float32(e types.Elem) float32 { - return e.(types.GoType).V.(float32) -} - -func Float64(e types.Elem) float64 { - return e.(types.GoType).V.(float64) -} - -func Complex64(e types.Elem) complex64 { - return e.(types.GoType).V.(complex64) -} - -func Complex128(e types.Elem) complex128 { - return e.(types.GoType).V.(complex128) -} - -func Bool(e types.Elem) bool { - return e.(types.GoType).V.(bool) -} - -func Byte(e types.Elem) byte { - return e.(types.GoType).V.(byte) -} - -func Rune(e types.Elem) rune { - return e.(types.GoType).V.(rune) -} - -func String(e types.Elem) string { - return e.(types.GoType).V.(string) -} - -func Error(e types.Elem) error { - return e.(types.GoType).V.(error) -} diff --git a/core/math.go b/core/math.go deleted file mode 100644 index db899d0..0000000 --- a/core/math.go +++ /dev/null @@ -1,604 +0,0 @@ -package core - -import ( - "fmt" - - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -type mathReduceFn func(types.Elem, types.Elem) types.Elem - -func mathReduce(fn mathReduceFn, zero types.Elem, s seq.Seq) types.Elem { - reduceFn := func(acc, el types.Elem) (types.Elem, bool) { - return fn(acc, el), false - } - return seq.Reduce(reduceFn, zero, s) -} - -func plusInt(a, b types.Elem) types.Elem { - return types.GoType{Int(a) + Int(b)} -} - -func plusInt8(a, b types.Elem) types.Elem { - return types.GoType{Int8(a) + Int8(b)} -} - -func plusInt16(a, b types.Elem) types.Elem { - return types.GoType{Int16(a) + Int16(b)} -} - -func plusInt32(a, b types.Elem) types.Elem { - return types.GoType{Int32(a) + Int32(b)} -} - -func plusInt64(a, b types.Elem) types.Elem { - return types.GoType{Int64(a) + Int64(b)} -} - -func plusUint(a, b types.Elem) types.Elem { - return types.GoType{Uint(a) + Uint(b)} -} - -func plusUint8(a, b types.Elem) types.Elem { - return types.GoType{Uint8(a) + Uint8(b)} -} - -func plusUint16(a, b types.Elem) types.Elem { - return types.GoType{Uint16(a) + Uint16(b)} -} - -func plusUint32(a, b types.Elem) types.Elem { - return types.GoType{Uint32(a) + Uint32(b)} -} - -func plusUint64(a, b types.Elem) types.Elem { - return types.GoType{Uint64(a) + Uint64(b)} -} - -func plusFloat32(a, b types.Elem) types.Elem { - return types.GoType{Float32(a) + Float32(b)} -} - -func plusFloat64(a, b types.Elem) types.Elem { - return types.GoType{Float64(a) + Float64(b)} -} - -func plusComplex64(a, b types.Elem) types.Elem { - return types.GoType{Complex64(a) + Complex64(b)} -} - -func plusComplex128(a, b types.Elem) types.Elem { - return types.GoType{Complex128(a) + Complex128(b)} -} - -func plusString(a, b types.Elem) types.Elem { - return types.GoType{String(a) + String(b)} -} - -func Plus(s seq.Seq) types.Elem { - var first, zero types.Elem - - if seq.Empty(s) { - return types.GoType{0} - } - first, _, _ = s.FirstRest() - - var fn mathReduceFn - switch first.(types.GoType).V.(type) { - - case int: - fn = plusInt - zero = types.GoType{int(0)} - - case int8: - fn = plusInt8 - zero = types.GoType{int8(0)} - - case int16: - fn = plusInt16 - zero = types.GoType{int16(0)} - - case int32: - fn = plusInt32 - zero = types.GoType{int32(0)} - - case int64: - fn = plusInt64 - zero = types.GoType{int64(0)} - - case uint: - fn = plusUint - zero = types.GoType{uint(0)} - - case uint8: - fn = plusUint8 - zero = types.GoType{uint8(0)} - - case uint16: - fn = plusUint16 - zero = types.GoType{uint16(0)} - - case uint32: - fn = plusUint32 - zero = types.GoType{uint32(0)} - - case uint64: - fn = plusUint64 - zero = types.GoType{uint64(0)} - - case float32: - fn = plusFloat32 - zero = types.GoType{float32(0)} - - case float64: - fn = plusFloat64 - zero = types.GoType{float64(0)} - - case complex64: - fn = plusComplex64 - zero = types.GoType{complex64(0)} - - case complex128: - fn = plusComplex128 - zero = types.GoType{complex128(0)} - - case string: - fn = plusString - zero = types.GoType{string("")} - - default: - panic(fmt.Sprintf("$#v cannot have Plus called on it", first)) - } - - return mathReduce(fn, zero, s) -} -func minusInt(a, b types.Elem) types.Elem { - return types.GoType{Int(a) - Int(b)} -} - -func minusInt8(a, b types.Elem) types.Elem { - return types.GoType{Int8(a) - Int8(b)} -} - -func minusInt16(a, b types.Elem) types.Elem { - return types.GoType{Int16(a) - Int16(b)} -} - -func minusInt32(a, b types.Elem) types.Elem { - return types.GoType{Int32(a) - Int32(b)} -} - -func minusInt64(a, b types.Elem) types.Elem { - return types.GoType{Int64(a) - Int64(b)} -} - -func minusUint(a, b types.Elem) types.Elem { - return types.GoType{Uint(a) - Uint(b)} -} - -func minusUint8(a, b types.Elem) types.Elem { - return types.GoType{Uint8(a) - Uint8(b)} -} - -func minusUint16(a, b types.Elem) types.Elem { - return types.GoType{Uint16(a) - Uint16(b)} -} - -func minusUint32(a, b types.Elem) types.Elem { - return types.GoType{Uint32(a) - Uint32(b)} -} - -func minusUint64(a, b types.Elem) types.Elem { - return types.GoType{Uint64(a) - Uint64(b)} -} - -func minusFloat32(a, b types.Elem) types.Elem { - return types.GoType{Float32(a) - Float32(b)} -} - -func minusFloat64(a, b types.Elem) types.Elem { - return types.GoType{Float64(a) - Float64(b)} -} - -func minusComplex64(a, b types.Elem) types.Elem { - return types.GoType{Complex64(a) - Complex64(b)} -} - -func minusComplex128(a, b types.Elem) types.Elem { - return types.GoType{Complex128(a) - Complex128(b)} -} - -func Minus(s seq.Seq) types.Elem { - var first, zero types.Elem - - if seq.Empty(s) { - panic("Minus cannot be called with no arguments") - } - zero, s, _ = s.FirstRest() - first = zero - - var fn mathReduceFn - switch first.(types.GoType).V.(type) { - - case int: - fn = minusInt - - case int8: - fn = minusInt8 - - case int16: - fn = minusInt16 - - case int32: - fn = minusInt32 - - case int64: - fn = minusInt64 - - case uint: - fn = minusUint - - case uint8: - fn = minusUint8 - - case uint16: - fn = minusUint16 - - case uint32: - fn = minusUint32 - - case uint64: - fn = minusUint64 - - case float32: - fn = minusFloat32 - - case float64: - fn = minusFloat64 - - case complex64: - fn = minusComplex64 - - case complex128: - fn = minusComplex128 - - default: - panic(fmt.Sprintf("$#v cannot have Minus called on it", first)) - } - - return mathReduce(fn, zero, s) -} -func multInt(a, b types.Elem) types.Elem { - return types.GoType{Int(a) * Int(b)} -} - -func multInt8(a, b types.Elem) types.Elem { - return types.GoType{Int8(a) * Int8(b)} -} - -func multInt16(a, b types.Elem) types.Elem { - return types.GoType{Int16(a) * Int16(b)} -} - -func multInt32(a, b types.Elem) types.Elem { - return types.GoType{Int32(a) * Int32(b)} -} - -func multInt64(a, b types.Elem) types.Elem { - return types.GoType{Int64(a) * Int64(b)} -} - -func multUint(a, b types.Elem) types.Elem { - return types.GoType{Uint(a) * Uint(b)} -} - -func multUint8(a, b types.Elem) types.Elem { - return types.GoType{Uint8(a) * Uint8(b)} -} - -func multUint16(a, b types.Elem) types.Elem { - return types.GoType{Uint16(a) * Uint16(b)} -} - -func multUint32(a, b types.Elem) types.Elem { - return types.GoType{Uint32(a) * Uint32(b)} -} - -func multUint64(a, b types.Elem) types.Elem { - return types.GoType{Uint64(a) * Uint64(b)} -} - -func multFloat32(a, b types.Elem) types.Elem { - return types.GoType{Float32(a) * Float32(b)} -} - -func multFloat64(a, b types.Elem) types.Elem { - return types.GoType{Float64(a) * Float64(b)} -} - -func multComplex64(a, b types.Elem) types.Elem { - return types.GoType{Complex64(a) * Complex64(b)} -} - -func multComplex128(a, b types.Elem) types.Elem { - return types.GoType{Complex128(a) * Complex128(b)} -} - -func Mult(s seq.Seq) types.Elem { - var first, zero types.Elem - - if seq.Empty(s) { - return types.GoType{1} - } - first, _, _ = s.FirstRest() - - var fn mathReduceFn - switch first.(types.GoType).V.(type) { - - case int: - fn = multInt - zero = types.GoType{int(1)} - - case int8: - fn = multInt8 - zero = types.GoType{int8(1)} - - case int16: - fn = multInt16 - zero = types.GoType{int16(1)} - - case int32: - fn = multInt32 - zero = types.GoType{int32(1)} - - case int64: - fn = multInt64 - zero = types.GoType{int64(1)} - - case uint: - fn = multUint - zero = types.GoType{uint(1)} - - case uint8: - fn = multUint8 - zero = types.GoType{uint8(1)} - - case uint16: - fn = multUint16 - zero = types.GoType{uint16(1)} - - case uint32: - fn = multUint32 - zero = types.GoType{uint32(1)} - - case uint64: - fn = multUint64 - zero = types.GoType{uint64(1)} - - case float32: - fn = multFloat32 - zero = types.GoType{float32(1)} - - case float64: - fn = multFloat64 - zero = types.GoType{float64(1)} - - case complex64: - fn = multComplex64 - zero = types.GoType{complex64(1)} - - case complex128: - fn = multComplex128 - zero = types.GoType{complex128(1)} - - default: - panic(fmt.Sprintf("$#v cannot have Mult called on it", first)) - } - - return mathReduce(fn, zero, s) -} -func divInt(a, b types.Elem) types.Elem { - return types.GoType{Int(a) / Int(b)} -} - -func divInt8(a, b types.Elem) types.Elem { - return types.GoType{Int8(a) / Int8(b)} -} - -func divInt16(a, b types.Elem) types.Elem { - return types.GoType{Int16(a) / Int16(b)} -} - -func divInt32(a, b types.Elem) types.Elem { - return types.GoType{Int32(a) / Int32(b)} -} - -func divInt64(a, b types.Elem) types.Elem { - return types.GoType{Int64(a) / Int64(b)} -} - -func divUint(a, b types.Elem) types.Elem { - return types.GoType{Uint(a) / Uint(b)} -} - -func divUint8(a, b types.Elem) types.Elem { - return types.GoType{Uint8(a) / Uint8(b)} -} - -func divUint16(a, b types.Elem) types.Elem { - return types.GoType{Uint16(a) / Uint16(b)} -} - -func divUint32(a, b types.Elem) types.Elem { - return types.GoType{Uint32(a) / Uint32(b)} -} - -func divUint64(a, b types.Elem) types.Elem { - return types.GoType{Uint64(a) / Uint64(b)} -} - -func divFloat32(a, b types.Elem) types.Elem { - return types.GoType{Float32(a) / Float32(b)} -} - -func divFloat64(a, b types.Elem) types.Elem { - return types.GoType{Float64(a) / Float64(b)} -} - -func divComplex64(a, b types.Elem) types.Elem { - return types.GoType{Complex64(a) / Complex64(b)} -} - -func divComplex128(a, b types.Elem) types.Elem { - return types.GoType{Complex128(a) / Complex128(b)} -} - -func Div(s seq.Seq) types.Elem { - var first, zero types.Elem - - if seq.Empty(s) { - panic("Div cannot be called with no arguments") - } - zero, s, _ = s.FirstRest() - first = zero - - var fn mathReduceFn - switch first.(types.GoType).V.(type) { - - case int: - fn = divInt - - case int8: - fn = divInt8 - - case int16: - fn = divInt16 - - case int32: - fn = divInt32 - - case int64: - fn = divInt64 - - case uint: - fn = divUint - - case uint8: - fn = divUint8 - - case uint16: - fn = divUint16 - - case uint32: - fn = divUint32 - - case uint64: - fn = divUint64 - - case float32: - fn = divFloat32 - - case float64: - fn = divFloat64 - - case complex64: - fn = divComplex64 - - case complex128: - fn = divComplex128 - - default: - panic(fmt.Sprintf("$#v cannot have Div called on it", first)) - } - - return mathReduce(fn, zero, s) -} -func modInt(a, b types.Elem) types.Elem { - return types.GoType{Int(a) % Int(b)} -} - -func modInt8(a, b types.Elem) types.Elem { - return types.GoType{Int8(a) % Int8(b)} -} - -func modInt16(a, b types.Elem) types.Elem { - return types.GoType{Int16(a) % Int16(b)} -} - -func modInt32(a, b types.Elem) types.Elem { - return types.GoType{Int32(a) % Int32(b)} -} - -func modInt64(a, b types.Elem) types.Elem { - return types.GoType{Int64(a) % Int64(b)} -} - -func modUint(a, b types.Elem) types.Elem { - return types.GoType{Uint(a) % Uint(b)} -} - -func modUint8(a, b types.Elem) types.Elem { - return types.GoType{Uint8(a) % Uint8(b)} -} - -func modUint16(a, b types.Elem) types.Elem { - return types.GoType{Uint16(a) % Uint16(b)} -} - -func modUint32(a, b types.Elem) types.Elem { - return types.GoType{Uint32(a) % Uint32(b)} -} - -func modUint64(a, b types.Elem) types.Elem { - return types.GoType{Uint64(a) % Uint64(b)} -} - -func Mod(s seq.Seq) types.Elem { - var first, zero types.Elem - - if seq.Empty(s) { - panic("Mod cannot be called with no arguments") - } - zero, s, _ = s.FirstRest() - first = zero - - var fn mathReduceFn - switch first.(types.GoType).V.(type) { - - case int: - fn = modInt - - case int8: - fn = modInt8 - - case int16: - fn = modInt16 - - case int32: - fn = modInt32 - - case int64: - fn = modInt64 - - case uint: - fn = modUint - - case uint8: - fn = modUint8 - - case uint16: - fn = modUint16 - - case uint32: - fn = modUint32 - - case uint64: - fn = modUint64 - - default: - panic(fmt.Sprintf("$#v cannot have Mod called on it", first)) - } - - return mathReduce(fn, zero, s) -} diff --git a/core/mathgen/README.md b/core/mathgen/README.md deleted file mode 100644 index 0fef2fb..0000000 --- a/core/mathgen/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# mathgen, or why I'm so so sorry - -Go's type system is a double-edged sword, and the inner edge will definitely get -you when you go to do generic math. I opted against using the `reflect` library, -and instead went for codegen. So while the result is barely readable, it will at -least be somewhat faster, theoretically. - -This module generates the `math.go` file which resides in the core library. The -`mathgen.tpl` file is where most of the magic happens. It is virtually -unreadable. The biggest reason for this is that I want it to be able to pass -through `go fmt` unphased, so that it doesn't end up getting changed back and -forth throughout the code's history. - -## Running - -You can run code gen by running `make gen` in the `core/` directory. The -`Makefile` in the root of the project should also reference this, so running -that in the root should also result in the file getting regenerated. diff --git a/core/mathgen/mathgen.go b/core/mathgen/mathgen.go deleted file mode 100644 index fa96320..0000000 --- a/core/mathgen/mathgen.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "os" - "text/template" -) - -// Represents a type (like int or float32) which can have a math operation -// called upon it (like + or -) -type MathOpType struct { - CastFn string - Type string -} - -// The standard types for which math operations work upon. These don't include -// byte and rune because those are aliases of int8 and int32, respectively -var MathOpTypes = []MathOpType{ - {"Int", "int"}, - {"Int8", "int8"}, - {"Int16", "int16"}, - {"Int32", "int32"}, - {"Int64", "int64"}, - {"Uint", "uint"}, - {"Uint8", "uint8"}, - {"Uint16", "uint16"}, - {"Uint32", "uint32"}, - {"Uint64", "uint64"}, - {"Float32", "float32"}, - {"Float64", "float64"}, - {"Complex64", "complex64"}, - {"Complex128", "complex128"}, -} - -var MathOpsIntOnly = []MathOpType{ - {"Int", "int"}, - {"Int8", "int8"}, - {"Int16", "int16"}, - {"Int32", "int32"}, - {"Int64", "int64"}, - {"Uint", "uint"}, - {"Uint8", "uint8"}, - {"Uint16", "uint16"}, - {"Uint32", "uint32"}, - {"Uint64", "uint64"}, -} - -// Represents a single math operation which can be performed (like + or -) -type MathOp struct { - Public string - Private string - Op string - - // Will be the first item in the reduce, and allows for the function being - // called with an empty seq. If empty string than the first item in the - // given sequence is used and an empty sequence is not allowed - Unit string - - // This is going to be the same for all ops, it's just convenient to have - // here - OpTypes []MathOpType - - // This only applies for plus, which allows for adding two strings together - IncludeString bool -} - -var MathOps = []MathOp{ - {"Plus", "plus", "+", "0", MathOpTypes, true}, - {"Minus", "minus", "-", "", MathOpTypes, false}, - - {"Mult", "mult", "*", "1", MathOpTypes, false}, - {"Div", "div", "/", "", MathOpTypes, false}, - - {"Mod", "mod", "%", "", MathOpsIntOnly, false}, -} - -func main() { - tpl, err := template.ParseFiles("mathgen/mathgen.tpl") - if err != nil { - panic(err) - } - if err := tpl.Execute(os.Stdout, MathOps); err != nil { - panic(err) - } -} diff --git a/core/mathgen/mathgen.tpl b/core/mathgen/mathgen.tpl deleted file mode 100644 index 71476f7..0000000 --- a/core/mathgen/mathgen.tpl +++ /dev/null @@ -1,57 +0,0 @@ -package core - -import ( - "fmt" - - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -type mathReduceFn func(types.Elem, types.Elem) types.Elem - -func mathReduce(fn mathReduceFn, zero types.Elem, s seq.Seq) types.Elem { - reduceFn := func(acc, el types.Elem) (types.Elem, bool) { - return fn(acc, el), false - } - return seq.Reduce(reduceFn, zero, s) -} -{{range .}}{{$mathOp := .}}{{range .OpTypes}} -func {{$mathOp.Private}}{{.CastFn}}(a, b types.Elem) types.Elem { - return types.GoType{{`{`}}{{.CastFn}}(a) {{$mathOp.Op}} {{.CastFn}}(b)} -} -{{end}}{{if $mathOp.IncludeString}} -func {{$mathOp.Private}}String(a, b types.Elem) types.Elem { - return types.GoType{String(a) {{$mathOp.Op}} String(b)} -} -{{end}} -func {{$mathOp.Public}}(s seq.Seq) types.Elem { - var first, zero types.Elem -{{if (eq $mathOp.Unit "")}} - if seq.Empty(s) { - panic("{{$mathOp.Public}} cannot be called with no arguments") - } - zero, s, _ = s.FirstRest() - first = zero -{{else}} - if seq.Empty(s) { - return types.GoType{{`{`}}{{$mathOp.Unit}}} - } - first, _, _ = s.FirstRest() -{{end}} - var fn mathReduceFn - switch first.(types.GoType).V.(type) { -{{range .OpTypes}} - case {{.Type}}: - fn = {{$mathOp.Private}}{{.CastFn}}{{if (ne $mathOp.Unit "")}} - zero = types.GoType{{`{`}}{{.Type}}({{$mathOp.Unit}})}{{end}} -{{end}}{{if $mathOp.IncludeString}} - case string: - fn = {{$mathOp.Private}}String - zero = types.GoType{string("")} -{{end}} - default: - panic(fmt.Sprintf("$#v cannot have {{$mathOp.Public}} called on it", first)) - } - - return mathReduce(fn, zero, s) -}{{end}} diff --git a/docs/compilation.md b/docs/compilation.md deleted file mode 100644 index dbfdf96..0000000 --- a/docs/compilation.md +++ /dev/null @@ -1,113 +0,0 @@ -# Hello world - -The following code in a file called `hello-world.gg` can be compiled to a static -binary which will output "Hello World!" and exit: - -``` -(. package "github.com/mediocregopher/ginger-hello-world" - (. defn main [] - (: fmt.Println "Hello World!")) -) -``` - -While in the same directory as `hello-world.gg`, this can be compiled and run -with: - -``` -ginger run -``` - -and built into a static binary with: - -``` -ginger build -``` - -## package - -The package string defines where in the namespace tree this file belongs. Code -in this file can directly reference variables, private or public, from other -files in the same package. - -The physical layout of the files is not consequential, ginger only cares what -package they say they belong to, it's not required that the directory tree -matches the namespace tree. - -## main - -Every executable which ginger compiles needs one `main` function to use as the -entrypoint. Ginger looks for a `main` function in all the `.gg` files in the -current working directory in order to compile. If it finds one it uses that, if -it finds zero or more than one it errors and tells the user they need to specify -either a package or file name. - -## Compilation - -Ginger is first compiled into go code, and the subsequently uses the go tool to -compile that code into a static binary. Because of this ginger is able to use -any code from the go standard library, as well as any third-party go libraries -which may want to be used. - -Ginger takes advantage of the `GOPATH` when compiling in order to find packages. -Upon starting up ginger will set the `GOPATH` as follows for the duration of the -run: - -``` -GOPATH=./target:$GOPATH -``` - -The following steps are then taken for compilation: - -* Create a folder in the cwd called `target`, if it isn't already there. Also - create a `src` subfolder inside of that. - -* Scan the cwd and any subdirectories (including `target`) for `.gg` files. - Translate them into `.go` files and place them according to their package name - in the `target/src` directory. So if a file has a package - `github.com/mediocregopher/foobar` it would be compiled and placed as - `target/src/github.com/mediocregopher/foobar/file.go`. The `main` function is - found and the package for it is determined in this step as well. - -* As the cwd `.gg` files are scanned a set of packages which are required is - built. If any are not present in `target` at the end of the last step and are - not present as go projects in the `GOPATH` then they are searched for as - ginger projects in the `GOPATH`. Packages found will be translated into the - `target` directory (very important, this prevents the global GOPATH from - getting cluttered with tranlated `.go` files which aren't actually part of the - project). This step is repeated until all dependencies are met, or until - they're not and an error is thrown. - -* At this point all necessary `.go` files to build the project are present in - the `GOPATH` (global or `target`). `go build` is called on the `main` package - and output to the `target` directory. - -### Properties - -Given those compilation steps ginger has the following properties: - -* Dependencies for a ginger project, either go or ginger, can be installed - globally or to the `target` folder (sandboxed) by a dependency management tool - (probably built into ginger). - -* It is easy to find exactly what your code is being translated to since it will - always be in the `target` directory. - -* Translated code will not clutter up the global GOPATH. - -* Only the `target` directory needs to be added to a `.gitignore` file. - -* Some amount of ginger-to-ginger monkey patching may be possible. Not sure if - this is a good or bad thing. - -* Compilation may take a while. There is some amount of hunting for `.gg` files - required, and all found ones *must* be compiled even if they're not going to - be used, since package names can be arbitrarily stated. There are some ways to - help this: - - * Might be worth taking a shortcut like grepping through all files in the - `GOPATH` for the package string only compiling the ones which pass the - grep. - - * Put placeholder `.go` files in the `target` directory to indicate that the - package isn't needed for subsequent installs. Not the *best* idea, since - changes to the dependency list in the project may not correctly process. diff --git a/docs/functions.md b/docs/functions.md deleted file mode 100644 index bfdd9b8..0000000 --- a/docs/functions.md +++ /dev/null @@ -1,86 +0,0 @@ -# Functions - -Functions are first-class citizens in ginger. The simplest anonymous function -can be created like so: - -``` -(. fn [x] - (: + x 1)) -``` - -This function takes in a single number and returns that number plus one. It -could be assigned to a variable on the package using `def`, or by using the -shortcut `defn`. The following two statements are equivalent: - -``` -(. def incr - (. fn [x] - (: + x 1))) - -(. defn incr [x] - (: + x 1)) -``` - -## Function returns - -### Single returns - -A function returns whatever the last expression in its execution returned. For -example: - -``` -(. defn abs [x] - (. if (: >= x 0) - x - (: * -1 x))) -``` - -This function will return the argument `x` if it is greater than or equal to 0, -or `(: * -1 x)` if it's not. - -### Multiple returns - -A function which wishes to return multiple arguments should return them as a -vector or list of arguments. The `let` function, which can be used to define -temporary variables in a scope, can deconstruct these multiple-returns: - -``` -# Returns two numbers which sum up to 10 -(. defn sum-10 [] - [4 6]) - -(. let [[foo bar] (: sum-10) - (: fmt.Printf "%d + %d = 10\n" foo bar)) -``` - -Functions defined within a go library which return multiple values can also be -deconstructed in a `let` statement: - -``` -(. let [[conn err] (: net.Dial "tcp" "localhost:80")] - # do stuff) -``` - -## Variadic function - -Functions may take in any number of variables using the `...` syntax, similar to -how go does variadic functions. The variadic variable must be the last in the -function's argument list, and is used as a list inside of the function. A list -may also be used as the input for a variadic function, also using the `...` -syntax. - -The following is an example of both defining a variadic function and both ways -of using it. `+` is a variadic function in this example: - -``` -(. defn avg [...x] - (/ - (+ x...) - (len x))) - -(: fmt.Println (avg 1 2 3 4 5)) -``` - -## Tail-recursion - -TODO diff --git a/docs/go-interop.md b/docs/go-interop.md deleted file mode 100644 index 558a8fe..0000000 --- a/docs/go-interop.md +++ /dev/null @@ -1,53 +0,0 @@ -# Go interop - -Ginger translates down to go code, and many of its conventions and rules follow -from go's conventions and rules. In most cases these decisions were made to help -with interoperability with existing go code. - -## Referencing go package variables/functions - -See the package doc for more on this - -## Types - -Go types and ginger types share a lot of overlap: - -* Ginger strings are of go's `string` type - -* Ginger integers are of go's `int` type - -* Ginger floats are of go's `float32` type - -* Ginger characters are of go's `rune` type - -* Ginger errors are of go's `error` type - -## Casting - -Each go type has a corresponding ginger casting function: - -``` -(: int64 5) -(: float64 5.5) -(: rune 'a') -``` - -## go-drop - -the `go-drop` form can be used for furthur interoperability. The rationale -behind `go-drop` is that there are simply too many cases to be able to create -enough individual functions, or a few generic functions, that would cover all -cases. Instead we use a single function, `go-drop`, which lets us drop down into -go code and interact with it directly. There are a number of pre-made functions -which implement commonly needed behaviors, such as `StringSlice` and -`ByteSlice`, which cast from either go or ginger types into `[]string` and -`[]byte`, respectively. - -``` -(. go-drop - "func StringSlice(v ginger.Elem) []string { - ret := []string{} - // do some stuff - return ret - }") -``` diff --git a/docs/packages.md b/docs/packages.md deleted file mode 100644 index 8f2bdb8..0000000 --- a/docs/packages.md +++ /dev/null @@ -1,95 +0,0 @@ -# Packages - -Ginger packages follow many of the same packaging rules as go packages. This -stems from ginger compiling down to go and needing to inter-op with go packages. - -As discussed in the compilation doc, packages are defined as follows: - -``` -(. package "github.com/mediocregopher/awesome" - - (. def AwesomeThing "totally") - - (. defn AwesomeFunction [] - (: rand)) -) -``` - -This expression does not have to appear in a particular folder heirarchy, and -multiple packages can appear in a single file. A package's definition can be -split up into multiple package statements across different files. This is -discussed more in the compilation doc. - -## Variable/Function naming - -Variables and functions follow the go rule of upper camel casing for public -variables/functions and lower cammel casing for private variables/functions. -This rule is enforced by the go compiler for both go packages and translated -ginger packages. - -### Private - -A private variable/function can only be referenced from within a package. If a -package is split into multiple parts across a project a private -variable/function defined in one part can be used in another part. - -### Public - -A public variable/function can be used within a package without any extra -embelishment (in the above example, `(: AwesomeFunction)` could simply be called -from within the package). - -Outside of a package variables/functions can be used as follows: - -``` -(. package "show-and-tell" - - (. defn main [] - (: fmt.Println - (: github.com/mediocregopher/awesome.AwesomeFunction))) -) -``` - -`show-and-tell.main` uses both the `Println` function from the `fmt` package and the -`AwesomeFunction` function from the `github.com/mediocregopher/awesome` package. -This syntax is rather cumbersome, however, and can be shortcutted using the -`alias` function in a package - -``` -(. package "show-and-tell" - - (. alias "github.com/mediocregopher/awesome" "aw") - - (. defn main [] - (: fmt.Println - (: aw.AwesomeFunction))) -``` - -Like go, aliasing a package to `"."` imports it directly: - -``` -(. package "show-and-tell" - - (. alias "github.com/mediocregopher/awesome" ".") - - (. defn main [] - (: fmt.Println - (: AwesomeFunction))) -``` - -Aliasing a package requires that you use it in the package you've aliased it in, -unless you alias to `"_"`. - -## Idiomatic package usage - -While it is not a requirement that your package namespaces follow the directory -heierarchy they show (in fact, you could have an entire project, with multiple -packages, all within a giant flat file), it's definitely recommended that you -do. It will make the code much easier to create a mental map of for newcomers to -it. - -## Circular dependencies - -Go enforces that a package may not have circular dependencies. That is, -`packageA` may not import `packageB` while `packageB` also imports `packageA`. -Ginger will also, be way of being translated to go, also enforce this rule. diff --git a/docs/syntax.md b/docs/syntax.md deleted file mode 100644 index 423da2f..0000000 --- a/docs/syntax.md +++ /dev/null @@ -1,144 +0,0 @@ -# Syntax - -This document describes the ginger syntax and data-structures, and how they are -evaluated. - -# Goals - -I have some immediate goals I'm trying to achieve with this syntax: - -* Everything is strings (except numbers, functions, and composites). There is no - symbol type, atom type, keyword type, etc... they're all just strings. - -* There is no `defmacro`. Macro creation and usage is simply an inherent feature - of the language syntax. - -# Walkthrough - -This is a number which evalutates to 5: - -``` -5 -``` - -This is a string, it can contain anything: - -``` -"! I'm the king of the world !" -``` - -This is a list. It evaluates to a linked-list of four strings: - -``` -("a" "b" "c" "d") -``` - -This is a vector of those same elements. It's like a list, but has some slightly -different properties. We'll mostly be using lists: - -``` -["a" "b" "c" "d"] -``` - -This is a string - -``` -"+" -``` - -`:` is the evaluator. A string beginning with `:` is evaluated to whatever it -references. This evaluates to a function which adds its arguments: - -``` -":+" -``` - -This evaluates to list whose elements are a function and two numbers: - -``` -(":+" 1 2) -``` - -A list whose first element is a `:` calls the second element as a function with -the rest of the elements as arguments. This evaluates to the number 5: - -``` -(":" ":+" 1 2) -``` - -A bare string (lacking in `"`) is automatically prefixed with a `:`, if it -doesn't already have one. So `":+"`, `:+`, and `+`, are equivalent. `":"` and -`:` are also equivalent. This is equivalent to the previous example: - -``` -(: + 1 2) -``` - -The `fn` function can be used to define a new function. Note the `.` instead of -`:`. We'll cover that in a bit. This evaluates to an anonymous function which -adds one to its argument and returns it: - -``` -(. fn [x] - (: + x 1)) -``` - -The `def` function can be used to bind some value to a new variable. This -defines a variable `foo` which evaluates to the string `"bar"`: - -``` -(. def foo "bar") -``` - -This defines a variable `incr` which evaluates to a function which adds one to -its argument: - -``` -(. def incr - (. fn [x] - (: + x 1))) -``` - -This uses `defn` as a shortcut for the above: -``` -(. defn incr [x] - (: + x 1)) -``` - -There are also maps. A map's keys can be any value(?). A map's values can be any -value. This evaluates to a map with 2 key/val pairs: - -``` -{ "foo" foo - "bar" (: incr 4) } -``` - -`.` is the half-evaluator. It only works on lists, and runs the function given -in the first argument with the unevaluated arguments (even if they have `:`). -You can generate new code to run (compile-time macros) using the normal `fn`. -The returned value is evaluated in place of the original. This evaluates to a -`let`-like function, except it forces you to use the capitalized variable names -in the body (utterly useless): - -``` -# -# map-alternate is a made up function which maps over every other element in a -# list, starting with the first. -# E.g. (: map-alternate (. fn [x] (: + x 1)) (1 2 3 4 5)) -> (2 2 4 4 6) -# -# capitalize is a made up function which looks for the first letter in a string -# and capitalizes it -# -(. defn caplet [mapping body...] - ("." let - (: map-alternate - (. fn [x] (: capitalize x)) - mapping) - body...)) - -#Usage -(. caplet [foo "this is foo" - dog "this is dog"] - (: println Foo) - (: println Dog)) -``` diff --git a/eval/eval.go b/eval/eval.go deleted file mode 100644 index c8100c7..0000000 --- a/eval/eval.go +++ /dev/null @@ -1,76 +0,0 @@ -// The eval package encompasses all that is necesary to do runtime evaluation of -// ginger structures. These are different than macros in that they aren't turned -// into go code, instead the compiled go code evaluates them at runtime -package eval - -import ( - "fmt" - "os" - "time" - - "github.com/mediocregopher/ginger/macros/pkgctx" - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -// Evaler is a function which can be used inside of eval. It must take in its -// arguments as a sequence of Elems, and return a resulting Elem -type Evaler func(seq.Seq) types.Elem - -// Bail stops compilation. The given element should be the reason compilation -// has stopped -func Bail(el types.Elem, reason string) { - fmt.Fprintln(os.Stderr, reason) - time.Sleep(100 * time.Second) - os.Exit(1) -} - -// Bailf is like Bail, but takes in formatting -func Bailf(el types.Elem, format string, args ...interface{}) { - reason := fmt.Sprintf(format, args...) - Bail(el, reason) -} - -var colon = types.GoType{":"} - -// Eval takes in the pkgctx it is being executed in, as well as a single Elem to -// be evaluated, and returns the Elem it evaluates to -func Eval(p *pkgctx.PkgCtx, el types.Elem) types.Elem { - l, ok := el.(*seq.List) - if !ok { - return el - } - - first, rest, ok := l.FirstRest() - if !ok || !first.Equal(colon) { - return el - } - - fnEl, args, ok := rest.FirstRest() - if !ok { - Bail(el, "Empty list after colon, no function given") - } - - var fnName string - if gt, ok := fnEl.(types.GoType); ok { - fnName, _ = gt.V.(string) - } - - if fnName == "" || fnName[0] != ':' { - Bail(el, "Must give a function reference to execute") - } - fnName = fnName[1:] - - fn, ok := p.CallMap[fnName] - if !ok { - Bailf(el, "Unknown function name %q", fnName) - } - - evalArgFn := func(el types.Elem) types.Elem { - return Eval(p, el) - } - - evaldArgs := seq.Map(evalArgFn, args) - - return fn.(Evaler)(evaldArgs) -} diff --git a/eval/eval_test.go b/eval/eval_test.go deleted file mode 100644 index bc8d075..0000000 --- a/eval/eval_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package eval - -import ( - . "testing" - - "github.com/mediocregopher/ginger/core" - "github.com/mediocregopher/ginger/macros/pkgctx" - "github.com/mediocregopher/ginger/parse" - "github.com/mediocregopher/ginger/types" -) - -// This is NOT how I want eval to really work in the end, but I wanted to get -// something down before I kept thinking about it, so I would know what would -// work - -func TestShittyPlus(t *T) { - p := &pkgctx.PkgCtx{ - CallMap: map[string]interface{}{ - "Plus": Evaler(core.Plus), - }, - } - - m := map[string]types.Elem{ - "(: Plus)": types.GoType{0}, - "(: Plus 1 2 3)": types.GoType{6}, - `(: Plus 1 2 3 - (: Plus 1 2 3))`: types.GoType{12}, - } - - for input, output := range m { - parsed, err := parse.ParseString(input) - if err != nil { - t.Fatal(err) - } - - evald := Eval(p, parsed) - if !evald.Equal(output) { - t.Fatalf("input: %q %#v != %#v", input, output, evald) - } - } -} diff --git a/macros/builtins.go b/macros/builtins.go deleted file mode 100644 index 8d76f2d..0000000 --- a/macros/builtins.go +++ /dev/null @@ -1,10 +0,0 @@ -package macros - -import ( - "github.com/mediocregopher/ginger/macros/pkgctx" - "github.com/mediocregopher/ginger/types" -) - -func Package(p *pkgctx.PkgCtx, el types.Elem) string { - return "" -} diff --git a/macros/macros.go b/macros/macros.go deleted file mode 100644 index 5f953f5..0000000 --- a/macros/macros.go +++ /dev/null @@ -1,30 +0,0 @@ -package macros - -import ( - "fmt" - "os" - "time" - - "github.com/mediocregopher/ginger/macros/pkgctx" - "github.com/mediocregopher/ginger/types" -) - -// A Macro takes in a ginger structure and returns the go code which corresponds -// to it. The structure will contain everything in the calling list after the -// macro name (for example, (. jkjkNo error is returned, Bail can be called to stop compilation mid-way -// instead. -type Macro func(*pkgctx.PkgCtx, types.Elem) string - -// Bail stops compilation. The given element should be the reason compilation -// has stopped -func Bail(el types.Elem, reason string) { - fmt.Fprintln(os.Stderr, reason) - time.Sleep(100 * time.Second) - os.Exit(1) -} - -// Bailf is like Bail, but takes in formatting -func Bailf(el types.Elem, format string, args ...interface{}) { - reason := fmt.Sprintf(format, args...) - Bail(el, reason) -} diff --git a/macros/pkgctx/pkgctx.go b/macros/pkgctx/pkgctx.go deleted file mode 100644 index 6c87329..0000000 --- a/macros/pkgctx/pkgctx.go +++ /dev/null @@ -1,113 +0,0 @@ -package pkgctx - -import ( - "strings" - - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -const ( - coreAbs = "github.com/mediocregopher/ginger/core" - coreAlias = "gingercore" -) - -// PkgCtx is given to all macros and represents the package that they are being -// evaluated within. -type PkgCtx struct { - - // Packages describes the external packages imported by this one. Each key - // is the absolute package path, the value is the alias for it (or empty - // string for no alias) - Packages map[string]string - - // CallMap is a map used by Eval for making actual calls dynamically. The - // key is the string representation of the call to be used (for example, - // "fmt.Println") and must agree with the aliases being used in Packages. - // The value need not be set during actual compilation, but it is useful to - // use it during testing - CallMap map[string]interface{} -} - -// Returns an empty PkgCtx -func New() *PkgCtx { - return &PkgCtx{ - Packages: map[string]string{}, - CallMap: map[string]interface{}{}, - } -} - -// A returns a new PkgCtx, which is a copy of a merge between p and p2. p2's -// keys overwrite any conflicting keys in p. p and p2 are unaffected by this -// operation -func (p *PkgCtx) MergeLeft(p2 *PkgCtx) *PkgCtx { - p3 := New() - for pkg := range p.Packages { - p3.Packages[pkg] = p.Packages[pkg] - } - for pkg := range p2.Packages { - p3.Packages[pkg] = p2.Packages[pkg] - } - for fn := range p.CallMap { - p3.CallMap[fn] = p.CallMap[fn] - } - for fn := range p2.CallMap { - p3.CallMap[fn] = p2.CallMap[fn] - } - return p3 -} - -// Returns a copy of p -func (p *PkgCtx) Copy() *PkgCtx { - return p.MergeLeft(New()) -} - -func (p *PkgCtx) PopulateFromCode(el types.Elem) bool { - if s, ok := el.(seq.Seq); ok { - return seq.Traverse(p.PopulateFromCode, s) - } - - gt, ok := el.(types.GoType) - if !ok { - return true - } - - str, ok := gt.V.(string) - if !ok { - return true - } - - if len(str) < 2 { - return true - } - - if str[0] != ':' { - return true - } - str = str[1:] - - // At this point str is a reference to something. We check if it's already - // pointing somewhere first - if _, ok = p.CallMap[str]; ok { - return true - } - - // If there isn't a '.' in the string, it's not directly referencing another - // package. Since it isn't in this context either, it must be referencing - // something in core - var i int - if i = strings.IndexRune(str, '.'); i < 1 { - p.Packages[coreAbs] = coreAlias - p.CallMap[str] = nil - - // TODO there needs to be a CodeGen interface or something. The - // CallMap's value type needs to be that, because in all likelyhood the - // CallMap will be directly translated to a map variable in the - // generated package, with the values being different depending on what - // they're being used for (external functions will be references to - // those functions, local variables may just end up being just GoType's - // of the actual value. - } - - return true -} diff --git a/parse/lex/lex.go b/parse/lex/lex.go deleted file mode 100644 index 4a41c2e..0000000 --- a/parse/lex/lex.go +++ /dev/null @@ -1,229 +0,0 @@ -// The lex package implements a lexical reader which can take in any io.Reader. -// It does not care about the meaning or logical validity of the tokens it -// parses out, it simply does its job. -package lex - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "unicode" -) - -type TokenType int - -const ( - BareString TokenType = iota - QuotedString - Open - Close - Err - eof -) - -var invalidBareStringRunes = map[rune]bool{ - '"': true, - '\'': true, - '(': true, - ')': true, - '[': true, - ']': true, - '{': true, - '}': true, -} - -// Token represents a single set of characters which *could* be a valid token of -// the given type -type Token struct { - Type TokenType - Val string -} - -// Returns the token's value as an error, or nil if the token is not of type -// Err. If the token is nil returns io.EOF, since that is the ostensible meaning -func (t *Token) AsError() error { - if t == nil { - return io.EOF - } - if t.Type != Err { - return nil - } - return errors.New(t.Val) -} - -var ( - errInvalidUTF8 = errors.New("invalid utf8 character") -) - -// Lexer reads through an io.Reader and emits Tokens from it. -type Lexer struct { - r *bufio.Reader - outbuf *bytes.Buffer - ch chan *Token -} - -// NewLexer constructs a new Lexer struct and returns it. r is internally -// wrapped with a bufio.Reader, unless it already is one. This will spawn a -// go-routine which reads from r until it hits an error, at which point it will -// end execution. -func NewLexer(r io.Reader) *Lexer { - var br *bufio.Reader - var ok bool - if br, ok = r.(*bufio.Reader); !ok { - br = bufio.NewReader(r) - } - - l := Lexer{ - r: br, - ch: make(chan *Token), - outbuf: bytes.NewBuffer(make([]byte, 0, 1024)), - } - - go l.spin() - - return &l -} - -func (l *Lexer) spin() { - f := lexWhitespace - for { - f = f(l) - if f == nil { - return - } - } -} - -// Returns the next available token, or nil if EOF has been reached. If an error -// other than EOF has been reached it will be returned as the Err token type, -// and this method should not be called again after that. -func (l *Lexer) Next() *Token { - t := <-l.ch - if t.Type == eof { - return nil - } - return t -} - -func (l *Lexer) emit(t TokenType) { - str := l.outbuf.String() - l.ch <- &Token{ - Type: t, - Val: str, - } - l.outbuf.Reset() -} - -func (l *Lexer) peek() (rune, error) { - r, err := l.readRune() - if err != nil { - return 0, err - } - if err = l.r.UnreadRune(); err != nil { - return 0, err - } - return r, nil -} - -func (l *Lexer) readRune() (rune, error) { - r, i, err := l.r.ReadRune() - if err != nil { - return 0, err - } else if r == unicode.ReplacementChar && i == 1 { - return 0, errInvalidUTF8 - } - return r, nil -} - -func (l *Lexer) err(err error) lexerFunc { - if err == io.EOF { - l.ch <- &Token{eof, ""} - } else { - l.ch <- &Token{Err, err.Error()} - } - close(l.ch) - return nil -} - -func (l *Lexer) errf(format string, args ...interface{}) lexerFunc { - s := fmt.Sprintf(format, args...) - l.ch <- &Token{Err, s} - close(l.ch) - return nil -} - -type lexerFunc func(*Lexer) lexerFunc - -func lexWhitespace(l *Lexer) lexerFunc { - r, err := l.readRune() - if err != nil { - return l.err(err) - } - - if unicode.IsSpace(r) { - return lexWhitespace - } - - l.outbuf.WriteRune(r) - - switch r { - case '"': - return lexQuotedString - case '(': - l.emit(Open) - case ')': - l.emit(Close) - case '[': - l.emit(Open) - case ']': - l.emit(Close) - case '{': - l.emit(Open) - case '}': - l.emit(Close) - default: - return lexBareString - } - - return lexWhitespace -} - -func lexQuotedString(l *Lexer) lexerFunc { - r, err := l.readRune() - if err != nil { - l.emit(QuotedString) - return l.err(err) - } - - l.outbuf.WriteRune(r) - buf := l.outbuf.Bytes() - - if r == '"' && buf[len(buf)-2] != '\\' { - l.emit(QuotedString) - return lexWhitespace - } - return lexQuotedString -} - -func lexBareString(l *Lexer) lexerFunc { - r, err := l.peek() - if err != nil { - l.emit(BareString) - return l.err(err) - } - - if _, ok := invalidBareStringRunes[r]; ok || unicode.IsSpace(r) { - l.emit(BareString) - return lexWhitespace - } - - if _, err = l.readRune(); err != nil { - l.emit(BareString) - return l.err(err) - } - - l.outbuf.WriteRune(r) - return lexBareString -} diff --git a/parse/lex/lex_test.go b/parse/lex/lex_test.go deleted file mode 100644 index 712fa7f..0000000 --- a/parse/lex/lex_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package lex - -import ( - "bytes" - . "testing" -) - -func TestLexer(t *T) { - m := map[string][]Token{ - "": {{eof, ""}}, - " \t": {{eof, ""}}, - "a b c": {{BareString, "a"}, - {BareString, "b"}, - {BareString, "c"}, - {eof, ""}}, - "\"foo\" bar": {{QuotedString, "\"foo\""}, - {BareString, "bar"}, - {eof, ""}}, - "\"foo\nbar\" baz": {{QuotedString, "\"foo\nbar\""}, - {BareString, "baz"}, - {eof, ""}}, - "( foo bar ) baz": {{Open, "("}, - {BareString, "foo"}, - {BareString, "bar"}, - {Close, ")"}, - {BareString, "baz"}, - {eof, ""}}, - "((foo-bar))": {{Open, "("}, - {Open, "("}, - {BareString, "foo-bar"}, - {Close, ")"}, - {Close, ")"}, - {eof, ""}}, - "(\"foo\nbar\")": {{Open, "("}, - {QuotedString, "\"foo\nbar\""}, - {Close, ")"}, - {eof, ""}}, - } - - for input, output := range m { - buf := bytes.NewBufferString(input) - l := NewLexer(buf) - for i := range output { - tok := l.Next() - if tok == nil { - if output[i].Type == eof { - continue - } - t.Fatalf("input: %q (%d) %#v != %#v", input, i, output[i], tok) - } - if *tok != output[i] { - t.Fatalf("input: %s (%d) %#v != %#v", input, i, output[i], tok) - } - } - } -} diff --git a/parse/parse.go b/parse/parse.go deleted file mode 100644 index f8d3ced..0000000 --- a/parse/parse.go +++ /dev/null @@ -1,155 +0,0 @@ -// The parse package implements a syntax parser for the ginger syntax. It can -// read in any io.Reader and returns fully parsed Elem's from the types package -// that it finds. -package parse - -import ( - "bytes" - "fmt" - "io" - "strconv" - "unsafe" - - "github.com/mediocregopher/ginger/parse/lex" - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -const int_bits = int(unsafe.Sizeof(int(0)) * 8) - -var closers = map[string]string{ - "(": ")", - "[": "]", - "{": "}", -} - -// The lexer only indicates a bare string, but technically an integer or a float -// is a bare string so we must try and convert to one of those first -func parseBareString(tok *lex.Token) types.Elem { - if i, err := strconv.ParseInt(tok.Val, 10, int_bits); err == nil { - return types.GoType{int(i)} - - } else if int_bits == 64 { - // We don't want to bother with the next case if int_bits is 64 - - } else if i64, err := strconv.ParseInt(tok.Val, 10, 64); err == nil { - return types.GoType{int64(i64)} - } - - if f32, err := strconv.ParseFloat(tok.Val, 32); err == nil { - return types.GoType{float32(f32)} - } - - if f64, err := strconv.ParseFloat(tok.Val, 64); err == nil { - return types.GoType{float64(f64)} - } - - if tok.Val[0] != ':' { - return types.GoType{":" + tok.Val} - } - - return types.GoType{tok.Val} -} - -func parseQuotedString(tok *lex.Token) (types.Elem, error) { - s, err := strconv.Unquote(tok.Val) - if err != nil { - return nil, err - } - - return types.GoType{s}, nil -} - -type Parser struct { - l *lex.Lexer -} - -// Returns a NewParser, using the lex package as the tokenizer -func NewParser(r io.Reader) *Parser { - p := Parser{ - l: lex.NewLexer(r), - } - return &p -} - -// Reads a full element, and any sub-elements (if the top-level element is a -// data-structure) into an Elem and returns it. Returns any errors, including -// io.EOF, if it runs into them instead -func (p *Parser) ReadElem() (types.Elem, error) { - tok := p.l.Next() - return p.parseToken(tok) -} - -func (p *Parser) parseToken(tok *lex.Token) (types.Elem, error) { - if tok == nil { - return nil, io.EOF - } - - switch tok.Type { - case lex.Err: - return nil, tok.AsError() - case lex.BareString: - return parseBareString(tok), nil - case lex.QuotedString: - return parseQuotedString(tok) - case lex.Open: - series, err := p.readUntil(closers[tok.Val]) - if err != nil { - return nil, err - } - if tok.Val == "(" { - return seq.NewList(series...), nil - } else if tok.Val == "{" { - if len(series)%2 != 0 { - return nil, fmt.Errorf("hash must have even number of elements") - } - kvs := make([]*seq.KV, 0, len(series)/2) - for i := 0; i < len(series); i += 2 { - kv := seq.KV{series[i], series[i+1]} - kvs = append(kvs, &kv) - } - return seq.NewHashMap(kvs...), nil - } - - panic("should never get here") - - default: - return nil, fmt.Errorf("Unexpected %q", tok.Val) - } -} - -func (p *Parser) readUntil(closer string) ([]types.Elem, error) { - series := make([]types.Elem, 0, 4) - for { - tok := p.l.Next() - switch err := tok.AsError(); err { - case nil: - case io.EOF: - return nil, fmt.Errorf("Unexpected EOF") - default: - return nil, err - } - - if tok.Type != lex.Close { - e, err := p.parseToken(tok) - if err != nil { - return nil, err - } - series = append(series, e) - continue - } - - if tok.Val != closer { - return nil, fmt.Errorf("Unexpected %q", tok.Val) - } - - return series, nil - } -} - -// Parses the first Elem it finds out of the given string and returns it -func ParseString(input string) (types.Elem, error) { - buf := bytes.NewBufferString(input) - p := NewParser(buf) - return p.ReadElem() -} diff --git a/parse/parse_test.go b/parse/parse_test.go deleted file mode 100644 index 316920b..0000000 --- a/parse/parse_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package parse - -import ( - "bytes" - "io" - . "testing" - - "github.com/mediocregopher/ginger/seq" - "github.com/mediocregopher/ginger/types" -) - -func TestParse(t *T) { - m := map[string]types.Elem{ - "1": types.GoType{int(1)}, - "-1": types.GoType{int(-1)}, - "+1": types.GoType{int(1)}, - - "1.5": types.GoType{float32(1.5)}, - "-1.5": types.GoType{float32(-1.5)}, - "+1.5": types.GoType{float32(1.5)}, - "1.5e1": types.GoType{float32(15)}, - - "foo": types.GoType{":foo"}, - - "()": seq.NewList(), - - "(foo)": seq.NewList( - types.GoType{":foo"}, - ), - - "(foo (bar))": seq.NewList( - types.GoType{":foo"}, - seq.NewList(types.GoType{":bar"}), - ), - - "{}": seq.NewHashMap(), - - "{foo bar}": seq.NewHashMap( - seq.KeyVal(types.GoType{":foo"}, types.GoType{":bar"}), - ), - } - - for input, output := range m { - parsed, err := ParseString(input) - if err != nil { - t.Fatal(err) - } - - if !output.Equal(parsed) { - t.Fatalf("input: %q %#v != %#v", input, output, parsed) - } - } -} - -func TestParseMulti(t *T) { - m := map[string][]types.Elem{ - "foo 4 bar": { - types.GoType{":foo"}, - types.GoType{4}, - types.GoType{":bar"}, - }, - - "foo \"bar\"": { - types.GoType{":foo"}, - types.GoType{"bar"}, - }, - } - - for input, output := range m { - buf := bytes.NewBufferString(input) - p := NewParser(buf) - parsed := make([]types.Elem, 0, len(output)) - for { - el, err := p.ReadElem() - if err == io.EOF { - break - } else if err != nil { - t.Fatal(err) - } - parsed = append(parsed, el) - } - - if len(output) != len(parsed) { - t.Fatalf("input: %q %#v != %#v", input, output, parsed) - } - - for i := range output { - if !output[i].Equal(parsed[i]) { - t.Fatalf("input: %q (%d) %#v != %#v", input, i, output[i], parsed[i]) - } - } - } -} diff --git a/seq/hashmap.go b/seq/hashmap.go deleted file mode 100644 index eaa087a..0000000 --- a/seq/hashmap.go +++ /dev/null @@ -1,166 +0,0 @@ -package seq - -import ( - "fmt" - - "github.com/mediocregopher/ginger/types" -) - -// Hash maps are built on top of hash sets. KeyVal implements Setable, but the -// Hash and Equal methods only apply to the key and ignore the value. - -// Container for a key/value pair, used by HashMap to hold its data -type KV struct { - Key types.Elem - Val types.Elem -} - -func KeyVal(key, val types.Elem) *KV { - return &KV{key, val} -} - -// Implementation of Hash for Setable. Only actually hashes the Key field -func (kv *KV) Hash(i uint32) uint32 { - return hash(kv.Key, i) -} - -// Implementation of Equal for Setable. Only actually compares the key field. If -// compared to another KV, only compares the other key as well. -func (kv *KV) Equal(v types.Elem) bool { - if kv2, ok := v.(*KV); ok { - return kv.Key.Equal(kv2.Key) - } - return kv.Key.Equal(v) -} - -func (kv *KV) fullEqual(v types.Elem) bool { - kv2, ok := v.(*KV) - if !ok { - return false - } - - return kv.Key.Equal(kv2.Key) && kv.Val.Equal(kv2.Val) -} - -// Implementation of String for Stringer -func (kv *KV) String() string { - return fmt.Sprintf("%v -> %v", kv.Key, kv.Val) -} - -// HashMaps are actually built on top of Sets, just with some added convenience -// methods for interacting with them as actual key/val stores -type HashMap struct { - set *Set -} - -// Returns a new HashMap of the given KVs (or possibly just an empty HashMap) -func NewHashMap(kvs ...*KV) *HashMap { - ints := make([]types.Elem, len(kvs)) - for i := range kvs { - ints[i] = kvs[i] - } - return &HashMap{ - set: NewSet(ints...), - } -} - -// Implementation of FirstRest for Seq interface. First return value will -// always be a *KV or nil. Completes in O(log(N)) time. -func (hm *HashMap) FirstRest() (types.Elem, Seq, bool) { - if hm == nil { - return nil, nil, false - } - el, nset, ok := hm.set.FirstRest() - return el, &HashMap{nset.(*Set)}, ok -} - -// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M)) -// time if e is another HashMap, where M is the size of the given HashMap -func (hm *HashMap) Equal(e types.Elem) bool { - // This can't just use Set's Equal because that would end up using KeyVal's - // Equal, which is not a true Equal - - hm2, ok := e.(*HashMap) - if !ok { - return false - } - - var el types.Elem - s := Seq(hm) - size := uint64(0) - - for { - el, s, ok = s.FirstRest() - if !ok { - return size == hm2.Size() - } - size++ - - kv := el.(*KV) - k, v := kv.Key, kv.Val - - v2, ok := hm2.Get(k) - if !ok || !v.Equal(v2) { - return false - } - } -} - -// Returns a new HashMap with the given value set on the given key. Also returns -// whether or not this was the first time setting that key (false if it was -// already there and was overwritten). Has the same complexity as Set's SetVal -// method. -func (hm *HashMap) Set(key, val types.Elem) (*HashMap, bool) { - if hm == nil { - hm = NewHashMap() - } - - nset, ok := hm.set.SetVal(KeyVal(key, val)) - return &HashMap{nset}, ok -} - -// Returns a new HashMap with the given key removed from it. Also returns -// whether or not the key was already there (true if so, false if not). Has the -// same time complexity as Set's DelVal method. -func (hm *HashMap) Del(key types.Elem) (*HashMap, bool) { - if hm == nil { - hm = NewHashMap() - } - - nset, ok := hm.set.DelVal(KeyVal(key, nil)) - return &HashMap{nset}, ok -} - -// Returns a value for a given key from the HashMap, along with a boolean -// indicating whether or not the value was found. Has the same time complexity -// as Set's GetVal method. -func (hm *HashMap) Get(key types.Elem) (types.Elem, bool) { - if hm == nil { - return nil, false - } else if kv, ok := hm.set.GetVal(KeyVal(key, nil)); ok { - return kv.(*KV).Val, true - } else { - return nil, false - } -} - -// Same as FirstRest, but returns values already casted, which may be convenient -// in some cases. -func (hm *HashMap) FirstRestKV() (*KV, *HashMap, bool) { - if el, nhm, ok := hm.FirstRest(); ok { - return el.(*KV), nhm.(*HashMap), true - } else { - return nil, nil, false - } -} - -// Implementation of String for Stringer interface -func (hm *HashMap) String() string { - return ToString(hm, "{", "}") -} - -// Returns the number of KVs in the HashMap. Has the same complexity as Set's -// Size method. -func (hm *HashMap) Size() uint64 { - return hm.set.Size() -} diff --git a/seq/hashmap_test.go b/seq/hashmap_test.go deleted file mode 100644 index 36e778b..0000000 --- a/seq/hashmap_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package seq - -import ( - . "testing" - - "github.com/mediocregopher/ginger/types" -) - -func kvints(kvs ...*KV) ([]*KV, []types.Elem) { - ints := make([]types.Elem, len(kvs)) - for i := range kvs { - ints[i] = kvs[i] - } - return kvs, ints -} - -// Test that HashMap implements types.Elem (compile-time check) -func TestHashMapElem(t *T) { - _ = types.Elem(NewHashMap()) -} - -// Test creating a Set and calling the Seq interface methods on it -func TestHashMapSeq(t *T) { - kvs, ints := kvints( - keyValV(1, "one"), - keyValV(2, "two"), - ) - - // Testing creation and Seq interface methods - m := NewHashMap(kvs...) - ms := testSeqNoOrderGen(t, m, ints) - - // ms should be empty at this point - assertEmpty(ms, t) -} - -// Test that the Equal method on HashMaps works -func TestHashMapEqual(t *T) { - hm, hm2 := NewHashMap(), NewHashMap() - assertValue(hm.Equal(hm2), true, t) - assertValue(hm2.Equal(hm), true, t) - - hm = NewHashMap(keyValV(1, "one"), keyValV(2, "two")) - assertValue(hm.Equal(hm2), false, t) - assertValue(hm2.Equal(hm), false, t) - - hm2 = NewHashMap(keyValV(1, "one")) - assertValue(hm.Equal(hm2), false, t) - assertValue(hm2.Equal(hm), false, t) - - hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "three?")) - assertValue(hm.Equal(hm2), false, t) - assertValue(hm2.Equal(hm), false, t) - - hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "two")) - assertValue(hm.Equal(hm2), true, t) - assertValue(hm2.Equal(hm), true, t) -} - -// Test getting values from a HashMap -func TestHashMapGet(t *T) { - kvs := []*KV{ - keyValV(1, "one"), - keyValV(2, "two"), - } - - // Degenerate case - m := NewHashMap() - assertEmpty(m, t) - v, ok := m.Get(types.GoType{1}) - assertValue(v, nil, t) - assertValue(ok, false, t) - - m = NewHashMap(kvs...) - v, ok = m.Get(types.GoType{1}) - assertSeqContentsHashMap(m, kvs, t) - assertValue(v, types.GoType{"one"}, t) - assertValue(ok, true, t) - - v, ok = m.Get(types.GoType{3}) - assertSeqContentsHashMap(m, kvs, t) - assertValue(v, nil, t) - assertValue(ok, false, t) -} - -// Test setting values on a HashMap -func TestHashMapSet(t *T) { - - // Set on empty - m := NewHashMap() - m1, ok := m.Set(types.GoType{1}, types.GoType{"one"}) - assertEmpty(m, t) - assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t) - assertValue(ok, true, t) - - // Set on same key - m2, ok := m1.Set(types.GoType{1}, types.GoType{"wat"}) - assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t) - assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t) - assertValue(ok, false, t) - - // Set on second new key - m3, ok := m2.Set(types.GoType{2}, types.GoType{"two"}) - assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t) - assertSeqContentsHashMap(m3, []*KV{keyValV(1, "wat"), keyValV(2, "two")}, t) - assertValue(ok, true, t) - -} - -// Test deleting keys from sets -func TestHashMapDel(t *T) { - - kvs := []*KV{ - keyValV(1, "one"), - keyValV(2, "two"), - keyValV(3, "three"), - } - kvs1 := []*KV{ - keyValV(2, "two"), - keyValV(3, "three"), - } - - // Degenerate case - m := NewHashMap() - m1, ok := m.Del(types.GoType{1}) - assertEmpty(m, t) - assertEmpty(m1, t) - assertValue(ok, false, t) - - // Delete actual key - m = NewHashMap(kvs...) - m1, ok = m.Del(types.GoType{1}) - assertSeqContentsHashMap(m, kvs, t) - assertSeqContentsHashMap(m1, kvs1, t) - assertValue(ok, true, t) - - // Delete it again! - m2, ok := m1.Del(types.GoType{1}) - assertSeqContentsHashMap(m1, kvs1, t) - assertSeqContentsHashMap(m2, kvs1, t) - assertValue(ok, false, t) - -} diff --git a/seq/hashset.go b/seq/hashset.go deleted file mode 100644 index b0dadc6..0000000 --- a/seq/hashset.go +++ /dev/null @@ -1,418 +0,0 @@ -package seq - -import ( - "fmt" - "hash/crc32" - "reflect" - - "github.com/mediocregopher/ginger/types" -) - -// This is an implementation of a persistent tree, which will then be used as -// the basis for vectors, hash maps, and hash sets. - -type Setable interface { - - // Returns an integer for the value. For two equivalent values (as defined - // by ==) Hash(i) should always return the same number. For multiple values - // of i, Hash should return different values if possible. - Hash(uint32) uint32 - - // Given an arbitrary value found in a Set, returns whether or not the two - // are equal - Equal(types.Elem) bool -} - -// Returns an arbitrary integer for the given value/iteration tuple -func hash(v types.Elem, i uint32) uint32 { - switch vt := v.(type) { - - case Setable: - return vt.Hash(i) % ARITY - - case types.GoType: - switch gvt := vt.V.(type) { - case uint: - return uint32(gvt) % ARITY - case uint8: - return uint32(gvt) % ARITY - case uint32: - return uint32(gvt) % ARITY - case uint64: - return uint32(gvt) % ARITY - case int: - return uint32(gvt) % ARITY - case int8: - return uint32(gvt) % ARITY - case int16: - return uint32(gvt) % ARITY - case int32: - return uint32(gvt) % ARITY - case int64: - return uint32(gvt) % ARITY - case float32: - return uint32(gvt) % ARITY - case float64: - return uint32(gvt) % ARITY - - case string: - return crc32.ChecksumIEEE([]byte(gvt)) % ARITY - - case []byte: - return crc32.ChecksumIEEE(gvt) % ARITY - } - } - - err := fmt.Sprintf("%s not hashable", reflect.TypeOf(v)) - panic(err) -} - -// The number of children each node in Set (implemented as a hash tree) can have -const ARITY = 32 - -// A Set is an implementation of Seq in the form of a persistant hash-tree. All -// public operations on it return a new, immutable form of the modified -// variable, leaving the old one intact. Immutability is implemented through -// node sharing, so operations aren't actually copying the entire hash-tree -// everytime, only the nodes which change, making the implementation very -// efficient compared to just copying. -// -// Items in sets need to be hashable and comparable. This means they either need -// to be some real numeric type (int, float32, etc...), string, []byte, or -// implement the Setable interface. -type Set struct { - - // The value being held - val types.Elem - - // Whether or not the held value has been set yet. Needed because the value - // could be nil - full bool - - // Slice of kids of this node. Could be an empty slice - kids []*Set - - // Number of values in this Set. - size uint64 -} - -// Returns a new Set of the given elements (or no elements, for an empty set) -func NewSet(vals ...types.Elem) *Set { - if len(vals) == 0 { - return nil - } - set := new(Set) - for i := range vals { - set.setValDirty(vals[i], 0) - } - set.size = uint64(len(vals)) - return set -} - -// Methods marked as "dirty" operate on the node in place, and potentially -// change it or its children. - -// Dirty. Tries to set the val on this Set node, or initialize the kids slice if -// it can't. Returns whether or not the value was set and whether or not it was -// already set. -func (set *Set) shallowTrySetOrInit(val types.Elem) (bool, bool) { - if !set.full { - set.val = val - set.full = true - return true, false - } else if set.val.Equal(val) { - set.val = val - set.full = true - return true, true - } else if set.kids == nil { - set.kids = make([]*Set, ARITY) - } - return false, false -} - -// dirty (obviously). Sets a value on this node in place. Only used during -// initialization. -func (set *Set) setValDirty(val types.Elem, i uint32) { - if ok, _ := set.shallowTrySetOrInit(val); ok { - return - } - - h := hash(val, i) - if kid := set.kids[h]; kid != nil { - kid.setValDirty(val, i+1) - } else { - set.kids[h] = NewSet(val) - } -} - -// Returns a copy of this set node, including allocating and copying the kids -// slice. -func (set *Set) clone() *Set { - var newkids []*Set - if set.kids != nil { - newkids = make([]*Set, ARITY) - copy(newkids, set.kids) - } - cs := &Set{ - val: set.val, - full: set.full, - kids: newkids, - size: set.size, - } - return cs -} - -// The actual implementation of SetVal, because we need to pass i down the stack -func (set *Set) internalSetVal(val types.Elem, i uint32) (*Set, bool) { - if set == nil { - return NewSet(val), true - } - cset := set.clone() - if ok, prev := cset.shallowTrySetOrInit(val); ok { - return cset, !prev - } - - h := hash(val, i) - newkid, ok := cset.kids[h].internalSetVal(val, i+1) - cset.kids[h] = newkid - return cset, ok -} - -// Returns a new Set with the given value added to it. Also returns whether or -// not this is the first time setting this value (false if it was already there -// and was overwritten). Completes in O(log(N)) time. -func (set *Set) SetVal(val types.Elem) (*Set, bool) { - nset, ok := set.internalSetVal(val, 0) - if ok { - nset.size++ - } - return nset, ok -} - -// The actual implementation of DelVal, because we need to pass i down the stack -func (set *Set) internalDelVal(val types.Elem, i uint32) (*Set, bool) { - if set == nil { - return nil, false - } else if set.full && set.val.Equal(val) { - cset := set.clone() - cset.val = nil - cset.full = false - return cset, true - } else if set.kids == nil { - return set, false - } - - h := hash(val, i) - if newkid, ok := set.kids[h].internalDelVal(val, i+1); ok { - cset := set.clone() - cset.kids[h] = newkid - return cset, true - } - return set, false -} - -// Returns a new Set with the given value removed from it and whether or not the -// value was actually removed. Completes in O(log(N)) time. -func (set *Set) DelVal(val types.Elem) (*Set, bool) { - nset, ok := set.internalDelVal(val, 0) - if ok && nset != nil { - nset.size-- - } - return nset, ok -} - -// The actual implementation of GetVal, because we need to pass i down the stack -func (set *Set) internalGetVal(val types.Elem, i uint32) (types.Elem, bool) { - if set == nil { - return nil, false - } else if set.full && set.val.Equal(val) { - return set.val, true - } else if set.kids == nil { - return nil, false - } - - h := hash(val, i) - return set.kids[h].internalGetVal(val, i+1) -} - -// Returns a value from the Set, along with a boolean indiciating whether or -// not the value was found. Completes in O(log(N)) time. -func (set *Set) GetVal(val types.Elem) (types.Elem, bool) { - return set.internalGetVal(val, 0) -} - -// Actual implementation of FirstRest. Because we need it to return a *Set -// instead of Seq for one case. -func (set *Set) internalFirstRest() (types.Elem, *Set, bool) { - if set == nil { - return nil, nil, false - } - - if set.kids != nil { - var el types.Elem - var rest *Set - var ok bool - for i := range set.kids { - if el, rest, ok = set.kids[i].internalFirstRest(); ok { - cset := set.clone() - cset.kids[i] = rest - return el, cset, true - } - } - } - - // We're not nil, but we don't have a value and no kids had values. We might - // as well be nil. - if !set.full { - return nil, nil, false - } - - return set.val, nil, true -} - -// Implementation of FirstRest for Seq interface. Completes in O(log(N)) time. -func (set *Set) FirstRest() (types.Elem, Seq, bool) { - el, restSet, ok := set.internalFirstRest() - if ok && restSet != nil { - restSet.size-- - } - return el, Seq(restSet), ok -} - -// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M)) -// time if e is another Set, where M is the size of the given Set -func (set *Set) Equal(e types.Elem) bool { - set2, ok := e.(*Set) - if !ok { - return false - } - - var el types.Elem - s := Seq(set) - size := uint64(0) - - for { - el, s, ok = s.FirstRest() - if !ok { - return size == set2.Size() - } - size++ - - _, ok = set2.GetVal(el) - if !ok { - return false - } - } -} - -// Implementation of String for Stringer interface -func (set *Set) String() string { - return ToString(set, "#{", "}#") -} - -// Returns the number of elements in the Set. Completes in O(1) time. -func (set *Set) Size() uint64 { - if set == nil { - return 0 - } - return set.size -} - -// Returns a Set with all of the elements of the original Set along with -// everything in the given Seq. If an element is present in both the Set and the -// Seq, the element in the Seq overwrites. Completes in O(M*log(N)), with M -// being the number of elements in the Seq and N the number of elements in the -// Set -func (set *Set) Union(s Seq) *Set { - if set == nil { - return ToSet(s) - } - - cset := set.clone() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); !ok { - return cset - } else if cset, ok = cset.SetVal(el); ok { - cset.size++ - } - } -} - -// Returns a Set with all of the elements in Seq that are also in Set. Completes -// in O(M*log(N)), with M being the number of elements in the Seq and N the -// number of elements in the Set -func (set *Set) Intersection(s Seq) *Set { - if set == nil { - return nil - } - - iset := NewSet() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); !ok { - return iset - } else if _, ok = set.GetVal(el); ok { - iset, _ = iset.SetVal(el) - } - } -} - -// Returns a Set of all elements in the original Set that aren't in the Seq. -// Completes in O(M*log(N)), with M being the number of elements in the Seq and -// N the number of elements in the Set -func (set *Set) Difference(s Seq) *Set { - if set == nil { - return nil - } - - cset := set.clone() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); !ok { - return cset - } else { - cset, _ = cset.DelVal(el) - } - } -} - -// Returns a Set of all elements that are either in the original Set or the -// given Seq, but not in both. Completes in O(M*log(N)), with M being the number -// of elements in the Seq and N the number of elements in the Set. -func (set *Set) SymDifference(s Seq) *Set { - if set == nil { - return ToSet(s) - } - - cset := set.clone() - var cset2 *Set - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); !ok { - return cset - } else if cset2, ok = cset.DelVal(el); ok { - cset = cset2 - } else { - cset, _ = cset.SetVal(el) - } - } -} - -// Returns the elements in the Seq as a set. In general this completes in -// O(N*log(N)) time (I think...). If the given Seq is already a Set it will -// complete in O(1) time. If it is a HashMap it will complete in O(1) time, and -// the resultant Set will be comprised of all KVs -func ToSet(s Seq) *Set { - if set, ok := s.(*Set); ok { - return set - } else if hm, ok := s.(*HashMap); ok { - return hm.set - } - vals := ToSlice(s) - return NewSet(vals...) -} diff --git a/seq/hashset_test.go b/seq/hashset_test.go deleted file mode 100644 index 80e83e0..0000000 --- a/seq/hashset_test.go +++ /dev/null @@ -1,273 +0,0 @@ -package seq - -import ( - . "testing" - - "github.com/mediocregopher/ginger/types" -) - -// Test that HashSet implements types.Elem (compile-time check) -func TestSetElem(t *T) { - _ = types.Elem(NewSet()) -} - -// Test creating a Set and calling the Seq interface methods on it -func TestSetSeq(t *T) { - ints := elemSliceV(nil, 1, "a", 5.0) - - // Testing creation and Seq interface methods - s := NewSet(ints...) - ss := testSeqNoOrderGen(t, s, ints) - - // ss should be empty at this point - s = ToSet(ss) - var nilpointer *Set - assertEmpty(s, t) - assertValue(s, nilpointer, t) - assertValue(len(ToSlice(s)), 0, t) -} - -// Test that the Equal method on Sets works -func TestSetEqual(t *T) { - s, s2 := NewSet(), NewSet() - assertValue(s.Equal(s2), true, t) - assertValue(s2.Equal(s), true, t) - - s = NewSet(elemSliceV(0, 1, 2)...) - assertValue(s.Equal(s2), false, t) - assertValue(s2.Equal(s), false, t) - - s2 = NewSet(elemSliceV(0, 1)...) - assertValue(s.Equal(s2), false, t) - assertValue(s2.Equal(s), false, t) - - s2 = NewSet(elemSliceV(0, 1, 3)...) - assertValue(s.Equal(s2), false, t) - assertValue(s2.Equal(s), false, t) - - s2 = NewSet(elemSliceV(0, 1, 2)...) - assertValue(s.Equal(s2), true, t) - assertValue(s2.Equal(s), true, t) -} - -// Test setting a value on a Set -func TestSetVal(t *T) { - ints := elemSliceV(0, 1, 2, 3, 4) - ints1 := elemSliceV(0, 1, 2, 3, 4, 5) - - // Degenerate case - s := NewSet() - assertEmpty(s, t) - s, ok := s.SetVal(types.GoType{0}) - assertSeqContentsSet(s, elemSliceV(0), t) - assertValue(ok, true, t) - - s = NewSet(ints...) - s1, ok := s.SetVal(types.GoType{5}) - assertSeqContentsSet(s, ints, t) - assertSeqContentsSet(s1, ints1, t) - assertValue(ok, true, t) - - s2, ok := s1.SetVal(types.GoType{5}) - assertSeqContentsSet(s1, ints1, t) - assertSeqContentsSet(s2, ints1, t) - assertValue(ok, false, t) -} - -// Test deleting a value from a Set -func TestDelVal(t *T) { - ints := elemSliceV(0, 1, 2, 3, 4) - ints1 := elemSliceV(0, 1, 2, 3) - ints2 := elemSliceV(1, 2, 3, 4) - ints3 := elemSliceV(1, 2, 3, 4, 5) - - // Degenerate case - s := NewSet() - assertEmpty(s, t) - s, ok := s.DelVal(types.GoType{0}) - assertEmpty(s, t) - assertValue(ok, false, t) - - s = NewSet(ints...) - s1, ok := s.DelVal(types.GoType{4}) - assertSeqContentsSet(s, ints, t) - assertSeqContentsSet(s1, ints1, t) - assertValue(ok, true, t) - - s1, ok = s1.DelVal(types.GoType{4}) - assertSeqContentsSet(s1, ints1, t) - assertValue(ok, false, t) - - // 0 is the value on the root node of s, which is kind of a special case. We - // want to test deleting it and setting a new value (which should get put on - // the root node). - s2, ok := s.DelVal(types.GoType{0}) - assertSeqContentsSet(s, ints, t) - assertSeqContentsSet(s2, ints2, t) - assertValue(ok, true, t) - - s2, ok = s2.DelVal(types.GoType{0}) - assertSeqContentsSet(s2, ints2, t) - assertValue(ok, false, t) - - s3, ok := s2.SetVal(types.GoType{5}) - assertSeqContentsSet(s2, ints2, t) - assertSeqContentsSet(s3, ints3, t) - assertValue(ok, true, t) -} - -// Test getting values from a Set -func GetVal(t *T) { - //Degenerate case - s := NewSet() - v, ok := s.GetVal(types.GoType{1}) - assertValue(v, nil, t) - assertValue(ok, false, t) - - s = NewSet(elemSliceV(0, 1, 2, 3, 4)...) - v, ok = s.GetVal(types.GoType{1}) - assertValue(v, 1, t) - assertValue(ok, true, t) - - // After delete - s, _ = s.DelVal(types.GoType{1}) - v, ok = s.GetVal(types.GoType{1}) - assertValue(v, nil, t) - assertValue(ok, false, t) - - // After set - s, _ = s.SetVal(types.GoType{1}) - v, ok = s.GetVal(types.GoType{1}) - assertValue(v, 1, t) - assertValue(ok, true, t) - - // After delete root node - s, _ = s.DelVal(types.GoType{0}) - v, ok = s.GetVal(types.GoType{0}) - assertValue(v, nil, t) - assertValue(ok, false, t) - - // After set root node - s, _ = s.SetVal(types.GoType{5}) - v, ok = s.GetVal(types.GoType{5}) - assertValue(v, 5, t) - assertValue(ok, true, t) -} - -// Test that Size functions properly for all cases -func TestSetSize(t *T) { - // Degenerate case - s := NewSet() - assertValue(s.Size(), uint64(0), t) - - // Initialization case - s = NewSet(elemSliceV(0, 1, 2)...) - assertValue(s.Size(), uint64(3), t) - - // Setting (both value not in and a value already in) - s, _ = s.SetVal(types.GoType{3}) - assertValue(s.Size(), uint64(4), t) - s, _ = s.SetVal(types.GoType{3}) - assertValue(s.Size(), uint64(4), t) - - // Deleting (both value already in and a value not in) - s, _ = s.DelVal(types.GoType{3}) - assertValue(s.Size(), uint64(3), t) - s, _ = s.DelVal(types.GoType{3}) - assertValue(s.Size(), uint64(3), t) - - // Deleting and setting the root node - s, _ = s.DelVal(types.GoType{0}) - assertValue(s.Size(), uint64(2), t) - s, _ = s.SetVal(types.GoType{5}) - assertValue(s.Size(), uint64(3), t) - -} - -// Test that Union functions properly -func TestUnion(t *T) { - // Degenerate case - empty := NewSet() - assertEmpty(empty.Union(empty), t) - - ints1 := elemSliceV(0, 1, 2) - ints2 := elemSliceV(3, 4, 5) - intsu := append(ints1, ints2...) - s1 := NewSet(ints1...) - s2 := NewSet(ints2...) - - assertSeqContentsSet(s1.Union(empty), ints1, t) - assertSeqContentsSet(empty.Union(s1), ints1, t) - - su := s1.Union(s2) - assertSeqContentsSet(s1, ints1, t) - assertSeqContentsSet(s2, ints2, t) - assertSeqContentsSet(su, intsu, t) -} - -// Test that Intersection functions properly -func TestIntersection(t *T) { - // Degenerate case - empty := NewSet() - assertEmpty(empty.Intersection(empty), t) - - ints1 := elemSliceV(0, 1, 2) - ints2 := elemSliceV(1, 2, 3) - ints3 := elemSliceV(4, 5, 6) - intsi := elemSliceV(1, 2) - s1 := NewSet(ints1...) - s2 := NewSet(ints2...) - s3 := NewSet(ints3...) - - assertEmpty(s1.Intersection(empty), t) - assertEmpty(empty.Intersection(s1), t) - - si := s1.Intersection(s2) - assertEmpty(s1.Intersection(s3), t) - assertSeqContentsSet(s1, ints1, t) - assertSeqContentsSet(s2, ints2, t) - assertSeqContentsSet(s3, ints3, t) - assertSeqContentsSet(si, intsi, t) -} - -// Test that Difference functions properly -func TestDifference(t *T) { - // Degenerate case - empty := NewSet() - assertEmpty(empty.Difference(empty), t) - - ints1 := elemSliceV(0, 1, 2, 3) - ints2 := elemSliceV(2, 3, 4) - intsd := elemSliceV(0, 1) - s1 := NewSet(ints1...) - s2 := NewSet(ints2...) - - assertSeqContentsSet(s1.Difference(empty), ints1, t) - assertEmpty(empty.Difference(s1), t) - - sd := s1.Difference(s2) - assertSeqContentsSet(s1, ints1, t) - assertSeqContentsSet(s2, ints2, t) - assertSeqContentsSet(sd, intsd, t) -} - -// Test that SymDifference functions properly -func TestSymDifference(t *T) { - // Degenerate case - empty := NewSet() - assertEmpty(empty.SymDifference(empty), t) - - ints1 := elemSliceV(0, 1, 2, 3) - ints2 := elemSliceV(2, 3, 4) - intsd := elemSliceV(0, 1, 4) - s1 := NewSet(ints1...) - s2 := NewSet(ints2...) - - assertSeqContentsSet(s1.SymDifference(empty), ints1, t) - assertSeqContentsSet(empty.SymDifference(s1), ints1, t) - - sd := s1.SymDifference(s2) - assertSeqContentsSet(s1, ints1, t) - assertSeqContentsSet(s2, ints2, t) - assertSeqContentsSet(sd, intsd, t) -} diff --git a/seq/lazy.go b/seq/lazy.go deleted file mode 100644 index adc1323..0000000 --- a/seq/lazy.go +++ /dev/null @@ -1,160 +0,0 @@ -package seq - -import ( - "github.com/mediocregopher/ginger/types" -) - -// A Lazy is an implementation of a Seq which only actually evaluates its -// contents as those contents become needed. Lazys can be chained together, so -// if you have three steps in a pipeline there aren't two intermediate Seqs -// created, only the final resulting one. Lazys are also thread-safe, so -// multiple routines can interact with the same Lazy pointer at the same time -// but the contents will only be evalutated once. -type Lazy struct { - this types.Elem - next *Lazy - ok bool - ch chan struct{} -} - -// Given a Thunk, returns a Lazy around that Thunk. -func NewLazy(t Thunk) *Lazy { - l := &Lazy{ch: make(chan struct{})} - go func() { - l.ch <- struct{}{} - el, next, ok := t() - l.this = el - l.next = NewLazy(next) - l.ok = ok - close(l.ch) - }() - return l -} - -// Implementation of FirstRest for Seq interface. Completes in O(1) time. -func (l *Lazy) FirstRest() (types.Elem, Seq, bool) { - if l == nil { - return nil, l, false - } - - // Reading from the channel tells the Lazy to populate the data and prepare - // the next item in the seq, it closes the channel when it's done that. - if _, ok := <-l.ch; ok { - <-l.ch - } - - if l.ok { - return l.this, l.next, true - } else { - return nil, nil, false - } -} - -// Implementation of Equal for types.Elem interface. Treats a List as another -// Lazy. Completes in O(N) time if e is another List or List. -func (l *Lazy) Equal(e types.Elem) bool { - var ls2 *List - if l2, ok := e.(*Lazy); ok { - ls2 = ToList(l2) - } else if ls2, ok = e.(*List); ok { - } else { - return false - } - ls := ToList(l) - return ls.Equal(ls2) -} - -// Implementation of String for Stringer -func (l *Lazy) String() string { - return ToString(l, "<<", ">>") -} - -// Thunks are the building blocks a Lazy. A Thunk returns an element, another -// Thunk, and a boolean representing if the call yielded any results or if it -// was actually empty (true indicates it yielded results). -type Thunk func() (types.Elem, Thunk, bool) - -func mapThunk(fn func(types.Elem) types.Elem, s Seq) Thunk { - return func() (types.Elem, Thunk, bool) { - el, ns, ok := s.FirstRest() - if !ok { - return nil, nil, false - } - - return fn(el), mapThunk(fn, ns), true - } -} - -// Lazy implementation of Map -func LMap(fn func(types.Elem) types.Elem, s Seq) Seq { - return NewLazy(mapThunk(fn, s)) -} - -func filterThunk(fn func(types.Elem) bool, s Seq) Thunk { - return func() (types.Elem, Thunk, bool) { - for { - el, ns, ok := s.FirstRest() - if !ok { - return nil, nil, false - } - - if keep := fn(el); keep { - return el, filterThunk(fn, ns), true - } else { - s = ns - } - } - } -} - -// Lazy implementation of Filter -func LFilter(fn func(types.Elem) bool, s Seq) Seq { - return NewLazy(filterThunk(fn, s)) -} - -func takeThunk(n uint64, s Seq) Thunk { - return func() (types.Elem, Thunk, bool) { - el, ns, ok := s.FirstRest() - if !ok || n == 0 { - return nil, nil, false - } - return el, takeThunk(n-1, ns), true - } -} - -// Lazy implementation of Take -func LTake(n uint64, s Seq) Seq { - return NewLazy(takeThunk(n, s)) -} - -func takeWhileThunk(fn func(types.Elem) bool, s Seq) Thunk { - return func() (types.Elem, Thunk, bool) { - el, ns, ok := s.FirstRest() - if !ok || !fn(el) { - return nil, nil, false - } - return el, takeWhileThunk(fn, ns), true - } -} - -// Lazy implementation of TakeWhile -func LTakeWhile(fn func(types.Elem) bool, s Seq) Seq { - return NewLazy(takeWhileThunk(fn, s)) -} - -func toLazyThunk(s Seq) Thunk { - return func() (types.Elem, Thunk, bool) { - el, ns, ok := s.FirstRest() - if !ok { - return nil, nil, false - } - return el, toLazyThunk(ns), true - } -} - -// Returns the Seq as a Lazy. Pointless for linked-lists, but possibly useful -// for other implementations where FirstRest might be costly and the same Seq -// needs to be iterated over many times. -func ToLazy(s Seq) *Lazy { - return NewLazy(toLazyThunk(s)) -} diff --git a/seq/lazy_test.go b/seq/lazy_test.go deleted file mode 100644 index ba9fb77..0000000 --- a/seq/lazy_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package seq - -import ( - . "testing" - "time" - - "github.com/mediocregopher/ginger/types" -) - -// Test that Lazy implements types.Elem (compile-time check) -func TestLazyElem(t *T) { - _ = types.Elem(NewLazy(nil)) -} - -// Test lazy operation and thread-safety -func TestLazyBasic(t *T) { - ch := make(chan types.GoType) - mapfn := func(el types.Elem) types.Elem { - i := el.(types.GoType) - ch <- i - return i - } - - intl := elemSliceV(0, 1, 2, 3, 4) - l := NewList(intl...) - ml := LMap(mapfn, l) - - // ml is a lazy list of intl, which will write to ch the first time any of - // the elements are read. This for loop ensures ml is thread-safe - for i := 0; i < 10; i++ { - go func() { - mlintl := ToSlice(ml) - if !intSlicesEq(mlintl, intl) { - panic("contents not right") - } - }() - } - - // This loop and subsequent close ensure that ml only ever "creates" each - // element once - for _, el := range intl { - select { - case elch := <-ch: - assertValue(el, elch, t) - case <-time.After(1 * time.Millisecond): - t.Fatalf("Took too long reading result") - } - } - close(ch) -} - -// Test that arbitrary Seqs can turn into Lazy -func TestToLazy(t *T) { - intl := elemSliceV(0, 1, 2, 3, 4) - l := NewList(intl...) - ll := ToLazy(l) - assertSeqContents(ll, intl, t) -} diff --git a/seq/list.go b/seq/list.go deleted file mode 100644 index 46ae6a7..0000000 --- a/seq/list.go +++ /dev/null @@ -1,172 +0,0 @@ -package seq - -import ( - "github.com/mediocregopher/ginger/types" -) - -// A List is an implementation of Seq in the form of a single-linked-list, and -// is used as the underlying structure for Seqs for most methods that return a -// Seq. It is probably the most efficient and simplest of the implementations. -// Even though, conceptually, all Seq operations return a new Seq, the old Seq -// can actually share nodes with the new Seq (if both are Lists), thereby saving -// memory and copies. -type List struct { - el types.Elem - next *List -} - -// Returns a new List comprised of the given elements (or no elements, for an -// empty list) -func NewList(els ...types.Elem) *List { - elsl := len(els) - if elsl == 0 { - return nil - } - - var cur *List - for i := 0; i < elsl; i++ { - cur = &List{els[elsl-i-1], cur} - } - return cur -} - -// Implementation of FirstRest for Seq interface. Completes in O(1) time. -func (l *List) FirstRest() (types.Elem, Seq, bool) { - if l == nil { - return nil, l, false - } else { - return l.el, l.next, true - } -} - -// Implementation of Equal for types.Elem interface. Completes in O(N) time if e -// is another List. -func (l *List) Equal(e types.Elem) bool { - l2, ok := e.(*List) - if !ok { - return false - } - - var el, el2 types.Elem - var ok2 bool - - s, s2 := Seq(l), Seq(l2) - - for { - el, s, ok = s.FirstRest() - el2, s2, ok2 = s2.FirstRest() - - if !ok && !ok2 { - return true - } - if ok != ok2 { - return false - } - - if !el.Equal(el2) { - return false - } - } -} - -// Implementation of String for Stringer interface. -func (l *List) String() string { - return ToString(l, "(", ")") -} - -// Prepends the given element to the front of the list, returning a copy of the -// new list. Completes in O(1) time. -func (l *List) Prepend(el types.Elem) *List { - return &List{el, l} -} - -// Prepends the argument Seq to the beginning of the callee List, returning a -// copy of the new List. Completes in O(N) time, N being the length of the -// argument Seq -func (l *List) PrependSeq(s Seq) *List { - var first, cur, prev *List - var el types.Elem - var ok bool - for { - el, s, ok = s.FirstRest() - if !ok { - break - } - cur = &List{el, nil} - if first == nil { - first = cur - } - if prev != nil { - prev.next = cur - } - prev = cur - } - - // prev will be nil if s is empty - if prev == nil { - return l - } - - prev.next = l - return first -} - -// Appends the given element to the end of the List, returning a copy of the new -// List. While most methods on List don't actually copy much data, this one -// copies the entire list. Completes in O(N) time. -func (l *List) Append(el types.Elem) *List { - var first, cur, prev *List - for l != nil { - cur = &List{l.el, nil} - if first == nil { - first = cur - } - if prev != nil { - prev.next = cur - } - prev = cur - l = l.next - } - final := &List{el, nil} - if prev == nil { - return final - } - prev.next = final - return first -} - -// Returns the nth index element (starting at 0), with bool being false if i is -// out of bounds. Completes in O(N) time. -func (l *List) Nth(n uint64) (types.Elem, bool) { - var el types.Elem - var ok bool - s := Seq(l) - for i := uint64(0); ; i++ { - el, s, ok = s.FirstRest() - if !ok { - return nil, false - } else if i == n { - return el, true - } - } -} - -// Returns the elements in the Seq as a List. Has similar properties as -// ToSlice. In general this completes in O(N) time. If the given Seq is already -// a List it will complete in O(1) time. -func ToList(s Seq) *List { - var ok bool - var l *List - if l, ok = s.(*List); ok { - return l - } - - var el types.Elem - for ret := NewList(); ; { - if el, s, ok = s.FirstRest(); ok { - ret = ret.Prepend(el) - } else { - return Reverse(ret).(*List) - } - } -} diff --git a/seq/list_test.go b/seq/list_test.go deleted file mode 100644 index cec136a..0000000 --- a/seq/list_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package seq - -import ( - . "testing" - - "github.com/mediocregopher/ginger/types" -) - -// Test that List implements types.Elem (compile-time check) -func TestListElem(t *T) { - _ = types.Elem(NewList()) -} - -// Asserts that the given list is properly formed and has all of its size fields -// filled in correctly -func assertSaneList(l *List, t *T) { - if Size(l) == 0 { - var nilpointer *List - assertValue(l, nilpointer, t) - return - } - - size := Size(l) - assertValue(Size(l.next), size-1, t) - assertSaneList(l.next, t) -} - -// Test creating a list and calling the Seq interface methods on it -func TestListSeq(t *T) { - ints := elemSliceV(1, "a", 5.0) - - // Testing creation and Seq interface methods - l := NewList(ints...) - sl := testSeqGen(t, l, ints) - - // sl should be empty at this point - l = ToList(sl) - var nilpointer *List - assertEmpty(l, t) - assertValue(l, nilpointer, t) - assertValue(len(ToSlice(l)), 0, t) - - // Testing creation of empty List. - emptyl := NewList() - assertValue(emptyl, nilpointer, t) -} - -// Test that the Equal method on Lists works -func TestListEqual(t *T) { - l, l2 := NewList(), NewList() - assertValue(l.Equal(l2), true, t) - assertValue(l2.Equal(l), true, t) - - l2 = NewList(elemSliceV(1, 2, 3)...) - assertValue(l.Equal(l2), false, t) - assertValue(l2.Equal(l), false, t) - - l = NewList(elemSliceV(1, 2, 3, 4)...) - assertValue(l.Equal(l2), false, t) - assertValue(l2.Equal(l), false, t) - - l2 = NewList(elemSliceV(1, 2, 3, 4)...) - assertValue(l.Equal(l2), true, t) - assertValue(l2.Equal(l), true, t) -} - -// Test the string representation of a List -func TestStringSeq(t *T) { - l := NewList(elemSliceV(0, 1, 2, 3)...) - assertValue(l.String(), "( 0 1 2 3 )", t) - - l = NewList(elemSliceV( - 0, 1, 2, - NewList(elemSliceV(3, 4)...), - 5, - NewList(elemSliceV(6, 7, 8)...))...) - assertValue(l.String(), "( 0 1 2 ( 3 4 ) 5 ( 6 7 8 ) )", t) -} - -// Test prepending an element to the beginning of a list -func TestPrepend(t *T) { - // Normal case - intl := elemSliceV(3, 2, 1, 0) - l := NewList(intl...) - nl := l.Prepend(types.GoType{4}) - assertSaneList(l, t) - assertSaneList(nl, t) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(4, 3, 2, 1, 0), t) - - // Degenerate case - l = NewList() - nl = l.Prepend(types.GoType{0}) - assertEmpty(l, t) - assertSaneList(nl, t) - assertSeqContents(nl, elemSliceV(0), t) -} - -// Test prepending a Seq to the beginning of a list -func TestPrependSeq(t *T) { - //Normal case - intl1 := elemSliceV(3, 4) - intl2 := elemSliceV(0, 1, 2) - l1 := NewList(intl1...) - l2 := NewList(intl2...) - nl := l1.PrependSeq(l2) - assertSaneList(l1, t) - assertSaneList(l2, t) - assertSaneList(nl, t) - assertSeqContents(l1, intl1, t) - assertSeqContents(l2, intl2, t) - assertSeqContents(nl, elemSliceV(0, 1, 2, 3, 4), t) - - // Degenerate cases - blank1 := NewList() - blank2 := NewList() - nl = blank1.PrependSeq(blank2) - assertEmpty(blank1, t) - assertEmpty(blank2, t) - assertEmpty(nl, t) - - nl = blank1.PrependSeq(l1) - assertEmpty(blank1, t) - assertSaneList(nl, t) - assertSeqContents(nl, intl1, t) - - nl = l1.PrependSeq(blank1) - assertEmpty(blank1, t) - assertSaneList(nl, t) - assertSeqContents(nl, intl1, t) -} - -// Test appending to the end of a List -func TestAppend(t *T) { - // Normal case - intl := elemSliceV(3, 2, 1) - l := NewList(intl...) - nl := l.Append(types.GoType{0}) - assertSaneList(l, t) - assertSaneList(nl, t) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(3, 2, 1, 0), t) - - // Edge case (algorithm gets weird here) - l = NewList(elemSliceV(1)...) - nl = l.Append(types.GoType{0}) - assertSaneList(l, t) - assertSaneList(nl, t) - assertSeqContents(l, elemSliceV(1), t) - assertSeqContents(nl, elemSliceV(1, 0), t) - - // Degenerate case - l = NewList() - nl = l.Append(types.GoType{0}) - assertEmpty(l, t) - assertSaneList(nl, t) - assertSeqContents(nl, elemSliceV(0), t) -} - -// Test retrieving items from a List -func TestNth(t *T) { - // Normal case, in bounds - intl := elemSliceV(0, 2, 4, 6, 8) - l := NewList(intl...) - r, ok := l.Nth(3) - assertSaneList(l, t) - assertSeqContents(l, intl, t) - assertValue(r, 6, t) - assertValue(ok, true, t) - - // Normal case, out of bounds - r, ok = l.Nth(8) - assertSaneList(l, t) - assertSeqContents(l, intl, t) - assertValue(r, nil, t) - assertValue(ok, false, t) - - // Degenerate case - l = NewList() - r, ok = l.Nth(0) - assertEmpty(l, t) - assertValue(r, nil, t) - assertValue(ok, false, t) -} diff --git a/seq/seq.go b/seq/seq.go deleted file mode 100644 index cb1d8fc..0000000 --- a/seq/seq.go +++ /dev/null @@ -1,312 +0,0 @@ -// This package describes ginger datastructures, and many of the operations -// which can be used on those data structures -package seq - -import ( - "bytes" - "fmt" - - "github.com/mediocregopher/ginger/types" -) - -// The general interface which most operations will actually operate on. Acts as -// an interface onto any data structure -type Seq interface { - - // Returns the "first" element in the data structure as well as a Seq - // containing a copy of the rest of the elements in the data structure. The - // "first" element can be random for structures which don't have a concept - // of order (like Set). Calling FirstRest on an empty Seq (Size() == 0) will - // return "first" as nil, the same empty Seq , and false. The third return - // value is true in all other cases. - FirstRest() (types.Elem, Seq, bool) -} - -// Returns the number of elements contained in the data structure. In general -// this completes in O(N) time, except for Set and HashMap for which it -// completes in O(1) -func Size(s Seq) uint64 { - switch st := s.(type) { - case *Set: - return st.Size() - case *HashMap: - return st.Size() - default: - } - - var ok bool - for i := uint64(0); ; { - if _, s, ok = s.FirstRest(); ok { - i++ - } else { - return i - } - } -} - -// Returns whether or not the given Seq is empty. This is accomplished using -// FirstRest and NOT just by naively returning Size(s) == 0. -func Empty(s Seq) bool { - _, _, ok := s.FirstRest() - return !ok -} - -// Returns the elements in the Seq as a slice. If the underlying Seq has any -// implicit order to it that order will be kept. An empty Seq will return an -// empty slice; nil is never returned. In general this completes in O(N) time. -func ToSlice(s Seq) []types.Elem { - var el types.Elem - var ok bool - for ret := make([]types.Elem, 0, 8); ; { - if el, s, ok = s.FirstRest(); ok { - ret = append(ret, el) - } else { - return ret - } - } -} - -// Turns a Seq into a string, with each element separated by a space and with a -// dstart and dend wrapping the whole thing -func ToString(s Seq, dstart, dend string) string { - buf := bytes.NewBufferString(dstart) - buf.WriteString(" ") - var el types.Elem - var strel fmt.Stringer - var rest Seq - var ok bool - for { - if el, rest, ok = s.FirstRest(); ok { - if strel, ok = el.(fmt.Stringer); ok { - buf.WriteString(strel.String()) - } else { - buf.WriteString(fmt.Sprintf("%v", el)) - } - buf.WriteString(" ") - s = rest - } else { - break - } - } - buf.WriteString(dend) - return buf.String() -} - -// Returns a reversed copy of the List. Completes in O(N) time. -func Reverse(s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - l = l.Prepend(el) - } else { - return l - } - } -} - -// Returns a Seq consisting of the result of applying fn to each element in the -// given Seq. Completes in O(N) time. -func Map(fn func(types.Elem) types.Elem, s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - l = l.Prepend(fn(el)) - } else { - break - } - } - return Reverse(l) -} - -// A function used in a reduce. The first argument is the accumulator, the -// second is an element from the Seq being reduced over. The ReduceFn returns -// the accumulator to be used in the next iteration, wherein that new -// accumulator will be called alongside the next element in the Seq. ReduceFn -// also returns a boolean representing whether or not the reduction should stop -// at this step. If true, the reductions will stop and any remaining elements in -// the Seq will be ignored. -type ReduceFn func(acc, el types.Elem) (types.Elem, bool) - -// Reduces over the given Seq using ReduceFn, with acc as the first accumulator -// value in the reduce. See ReduceFn for more details on how it works. The -// return value is the result of the reduction. Completes in O(N) time. -func Reduce(fn ReduceFn, acc types.Elem, s Seq) types.Elem { - var el types.Elem - var ok, stop bool - for { - if el, s, ok = s.FirstRest(); ok { - acc, stop = fn(acc, el) - if stop { - break - } - } else { - break - } - } - return acc -} - -// Returns the first element in Seq for which fn returns true, or nil. The -// returned boolean indicates whether or not a matching element was found. -// Completes in O(N) time. -func Any(fn func(el types.Elem) bool, s Seq) (types.Elem, bool) { - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - if fn(el) { - return el, true - } - } else { - return nil, false - } - } -} - -// Returns true if fn returns true for all elements in the Seq. Completes in -// O(N) time. -func All(fn func(types.Elem) bool, s Seq) bool { - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - if !fn(el) { - return false - } - } else { - return true - } - } -} - -// Returns a Seq containing all elements in the given Seq for which fn returned -// true. Completes in O(N) time. -func Filter(fn func(el types.Elem) bool, s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - if fn(el) { - l = l.Prepend(el) - } - } else { - return Reverse(l) - } - } -} - -// Flattens the given Seq into a single, one-dimensional Seq. This method only -// flattens Seqs found in the top level of the given Seq, it does not recurse -// down to multiple layers. Completes in O(N*M) time, where N is the number of -// elements in the Seq and M is how large the Seqs in those elements actually -// are. -func Flatten(s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for { - if el, s, ok = s.FirstRest(); ok { - if els, ok := el.(Seq); ok { - l = l.PrependSeq(Reverse(els)) - } else { - l = l.Prepend(el) - } - } else { - return Reverse(l) - } - } -} - -// Returns a Seq containing the first n elements in the given Seq. If n is -// greater than the length of the given Seq then the whole Seq is returned. -// Completes in O(N) time. -func Take(n uint64, s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for i := uint64(0); i < n; i++ { - el, s, ok = s.FirstRest() - if !ok { - break - } - l = l.Prepend(el) - } - return Reverse(l) -} - -// Goes through each item in the given Seq until an element returns false from -// pred. Returns a new Seq containing these truthful elements. Completes in O(N) -// time. -func TakeWhile(pred func(types.Elem) bool, s Seq) Seq { - l := NewList() - var el types.Elem - var ok bool - for { - el, s, ok = s.FirstRest() - if !ok || !pred(el) { - break - } - l = l.Prepend(el) - } - return Reverse(l) -} - -// Returns a Seq the is the previous Seq without the first n elements. If n is -// greater than the length of the Seq, returns an empty Seq. Completes in O(N) -// time. -func Drop(n uint64, s Seq) Seq { - var ok bool - for i := uint64(0); i < n; i++ { - _, s, ok = s.FirstRest() - if !ok { - break - } - } - return s -} - -// Drops elements from the given Seq until pred returns false for an element. -// Returns a Seq of the remaining elements (including the one which returned -// false). Completes in O(N) time. -func DropWhile(pred func(types.Elem) bool, s Seq) Seq { - var el types.Elem - var curs Seq - var ok bool - for { - el, curs, ok = s.FirstRest() - if !ok || !pred(el) { - break - } - s = curs - } - return s -} - -// Runs pred on each element in the sequence, descending recursively if it -// encounters another Seq (without calling pred on that Seq). This amounts to a -// depth-first traverse. If pred ever returns false the traverse will stop. -// Returns false if the Traverse was stopped by pred, true otherwise. -func Traverse(pred func(types.Elem) bool, s Seq) bool { - var el types.Elem - var ok bool - for { - el, s, ok = s.FirstRest() - if !ok { - return true - } - var predRet bool - if inners, ok := el.(Seq); ok { - predRet = Traverse(pred, inners) - } else { - predRet = pred(el) - } - if !predRet { - return false - } - } -} diff --git a/seq/seq_test.go b/seq/seq_test.go deleted file mode 100644 index f053fb7..0000000 --- a/seq/seq_test.go +++ /dev/null @@ -1,427 +0,0 @@ -package seq - -import ( - . "testing" - - "github.com/mediocregopher/ginger/types" -) - -// Tests the FirstRest, Size, Empty, and ToSlice methods of a Seq -func testSeqGen(t *T, s Seq, ints []types.Elem) Seq { - intsl := uint64(len(ints)) - for i := range ints { - assertSaneList(ToList(s), t) - assertValue(Size(s), intsl-uint64(i), t) - assertSeqContents(s, ints[i:], t) - - first, rest, ok := s.FirstRest() - assertValue(ok, true, t) - assertValue(first, ints[i], t) - - empty := Empty(s) - assertValue(empty, false, t) - - s = rest - } - empty := Empty(s) - assertValue(empty, true, t) - return s -} - -// Tests the FirstRest, Size, and ToSlice methods of an unordered Seq -func testSeqNoOrderGen(t *T, s Seq, ints []types.Elem) Seq { - intsl := uint64(len(ints)) - - m := map[types.Elem]bool{} - for i := range ints { - m[ints[i]] = true - } - - for i := range ints { - assertSaneList(ToList(s), t) - assertValue(Size(s), intsl-uint64(i), t) - assertSeqContentsNoOrderMap(s, m, t) - - first, rest, ok := s.FirstRest() - assertValue(ok, true, t) - assertInMap(first, m, t) - - delete(m, first) - s = rest - } - return s -} - -// Test reversing a Seq -func TestReverse(t *T) { - // Normal case - intl := elemSliceV(3, 2, 1) - l := NewList(intl...) - nl := Reverse(l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(1, 2, 3), t) - - // Degenerate case - l = NewList() - nl = Reverse(l) - assertEmpty(l, t) - assertEmpty(nl, t) -} - -func testMapGen(t *T, mapFn func(func(types.Elem) types.Elem, Seq) Seq) { - fn := func(n types.Elem) types.Elem { - return types.GoType{n.(types.GoType).V.(int) + 1} - } - - // Normal case - intl := elemSliceV(1, 2, 3) - l := NewList(intl...) - nl := mapFn(fn, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(2, 3, 4), t) - - // Degenerate case - l = NewList() - nl = mapFn(fn, l) - assertEmpty(l, t) - assertEmpty(nl, t) -} - -// Test mapping over a Seq -func TestMap(t *T) { - testMapGen(t, Map) -} - -// Test lazily mapping over a Seq -func TestLMap(t *T) { - testMapGen(t, LMap) -} - -// Test reducing over a Seq -func TestReduce(t *T) { - fn := func(acc, el types.Elem) (types.Elem, bool) { - acci := acc.(types.GoType).V.(int) - eli := el.(types.GoType).V.(int) - return types.GoType{acci + eli}, false - } - - // Normal case - intl := elemSliceV(1, 2, 3, 4) - l := NewList(intl...) - r := Reduce(fn, types.GoType{0}, l) - assertSeqContents(l, intl, t) - assertValue(r, 10, t) - - // Short-circuit case - fns := func(acc, el types.Elem) (types.Elem, bool) { - acci := acc.(types.GoType).V.(int) - eli := el.(types.GoType).V.(int) - return types.GoType{acci + eli}, eli > 2 - } - r = Reduce(fns, types.GoType{0}, l) - assertSeqContents(l, intl, t) - assertValue(r, 6, t) - - // Degenerate case - l = NewList() - r = Reduce(fn, types.GoType{0}, l) - assertEmpty(l, t) - assertValue(r, 0, t) -} - -// Test the Any function -func TestAny(t *T) { - fn := func(el types.Elem) bool { - return el.(types.GoType).V.(int) > 3 - } - - // Value found case - intl := elemSliceV(1, 2, 3, 4) - l := NewList(intl...) - r, ok := Any(fn, l) - assertSeqContents(l, intl, t) - assertValue(r, 4, t) - assertValue(ok, true, t) - - // Value not found case - intl = elemSliceV(1, 2, 3) - l = NewList(intl...) - r, ok = Any(fn, l) - assertSeqContents(l, intl, t) - assertValue(r, nil, t) - assertValue(ok, false, t) - - // Degenerate case - l = NewList() - r, ok = Any(fn, l) - assertEmpty(l, t) - assertValue(r, nil, t) - assertValue(ok, false, t) -} - -// Test the All function -func TestAll(t *T) { - fn := func(el types.Elem) bool { - return el.(types.GoType).V.(int) > 3 - } - - // All match case - intl := elemSliceV(4, 5, 6) - l := NewList(intl...) - ok := All(fn, l) - assertSeqContents(l, intl, t) - assertValue(ok, true, t) - - // Not all match case - intl = elemSliceV(3, 4, 2, 5) - l = NewList(intl...) - ok = All(fn, l) - assertSeqContents(l, intl, t) - assertValue(ok, false, t) - - // Degenerate case - l = NewList() - ok = All(fn, l) - assertEmpty(l, t) - assertValue(ok, true, t) -} - -func testFilterGen(t *T, filterFn func(func(types.Elem) bool, Seq) Seq) { - fn := func(el types.Elem) bool { - return el.(types.GoType).V.(int)%2 != 0 - } - - // Normal case - intl := elemSliceV(1, 2, 3, 4, 5) - l := NewList(intl...) - r := filterFn(fn, l) - assertSeqContents(l, intl, t) - assertSeqContents(r, elemSliceV(1, 3, 5), t) - - // Degenerate cases - l = NewList() - r = filterFn(fn, l) - assertEmpty(l, t) - assertEmpty(r, t) -} - -// Test the Filter function -func TestFilter(t *T) { - testFilterGen(t, Filter) -} - -// Test the lazy Filter function -func TestLFilter(t *T) { - testFilterGen(t, LFilter) -} - -// Test Flatten-ing of a Seq -func TestFlatten(t *T) { - // Normal case - intl1 := elemSliceV(0, 1, 2) - intl2 := elemSliceV(3, 4, 5) - l1 := NewList(intl1...) - l2 := NewList(intl2...) - blank := NewList() - intl := elemSliceV(-1, l1, l2, 6, blank, 7) - l := NewList(intl...) - nl := Flatten(l) - assertSeqContents(l1, intl1, t) - assertSeqContents(l2, intl2, t) - assertEmpty(blank, t) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(-1, 0, 1, 2, 3, 4, 5, 6, 7), t) - - // Degenerate case - nl = Flatten(blank) - assertEmpty(blank, t) - assertEmpty(nl, t) -} - -func testTakeGen(t *T, takeFn func(uint64, Seq) Seq) { - // Normal case - intl := elemSliceV(0, 1, 2, 3, 4) - l := NewList(intl...) - nl := takeFn(3, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(0, 1, 2), t) - - // Edge cases - nl = takeFn(5, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, intl, t) - - nl = takeFn(6, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, intl, t) - - // Degenerate cases - empty := NewList() - nl = takeFn(1, empty) - assertEmpty(empty, t) - assertEmpty(nl, t) - - nl = takeFn(0, l) - assertSeqContents(l, intl, t) - assertEmpty(nl, t) -} - -// Test taking from a Seq -func TestTake(t *T) { - testTakeGen(t, Take) -} - -// Test lazily taking from a Seq -func TestLTake(t *T) { - testTakeGen(t, LTake) -} - -func testTakeWhileGen(t *T, takeWhileFn func(func(types.Elem) bool, Seq) Seq) { - pred := func(el types.Elem) bool { - return el.(types.GoType).V.(int) < 3 - } - - // Normal case - intl := elemSliceV(0, 1, 2, 3, 4, 5) - l := NewList(intl...) - nl := takeWhileFn(pred, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(0, 1, 2), t) - - // Edge cases - intl = elemSliceV(5, 5, 5) - l = NewList(intl...) - nl = takeWhileFn(pred, l) - assertSeqContents(l, intl, t) - assertEmpty(nl, t) - - intl = elemSliceV(0, 1, 2) - l = NewList(intl...) - nl = takeWhileFn(pred, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(0, 1, 2), t) - - // Degenerate case - l = NewList() - nl = takeWhileFn(pred, l) - assertEmpty(l, t) - assertEmpty(nl, t) -} - -// Test taking from a Seq until a given condition -func TestTakeWhile(t *T) { - testTakeWhileGen(t, TakeWhile) -} - -// Test lazily taking from a Seq until a given condition -func TestLTakeWhile(t *T) { - testTakeWhileGen(t, LTakeWhile) -} - -// Test dropping from a Seq -func TestDrop(t *T) { - // Normal case - intl := elemSliceV(0, 1, 2, 3, 4) - l := NewList(intl...) - nl := Drop(3, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(3, 4), t) - - // Edge cases - nl = Drop(5, l) - assertSeqContents(l, intl, t) - assertEmpty(nl, t) - - nl = Drop(6, l) - assertSeqContents(l, intl, t) - assertEmpty(nl, t) - - // Degenerate cases - empty := NewList() - nl = Drop(1, empty) - assertEmpty(empty, t) - assertEmpty(nl, t) - - nl = Drop(0, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, intl, t) -} - -// Test dropping from a Seq until a given condition -func TestDropWhile(t *T) { - pred := func(el types.Elem) bool { - return el.(types.GoType).V.(int) < 3 - } - - // Normal case - intl := elemSliceV(0, 1, 2, 3, 4, 5) - l := NewList(intl...) - nl := DropWhile(pred, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, elemSliceV(3, 4, 5), t) - - // Edge cases - intl = elemSliceV(5, 5, 5) - l = NewList(intl...) - nl = DropWhile(pred, l) - assertSeqContents(l, intl, t) - assertSeqContents(nl, intl, t) - - intl = elemSliceV(0, 1, 2) - l = NewList(intl...) - nl = DropWhile(pred, l) - assertSeqContents(l, intl, t) - assertEmpty(nl, t) - - // Degenerate case - l = NewList() - nl = DropWhile(pred, l) - assertEmpty(l, t) - assertEmpty(nl, t) -} - -// Test Traversing a Seq until a given condition -func TestTraverse(t *T) { - var acc int - pred := func(el types.Elem) bool { - acc += el.(types.GoType).V.(int) - return true - } - - l := NewList() - acc = 0 - Traverse(pred, l) - assertValue(acc, 0, t) - - l2 := NewList(elemSliceV(0, 1, 2, 3)...) - acc = 0 - Traverse(pred, l2) - assertValue(acc, 6, t) - - l3 := NewList( - types.GoType{1}, - types.GoType{2}, - NewList(elemSliceV(4, 5, 6)...), - types.GoType{3}, - ) - acc = 0 - Traverse(pred, l3) - assertValue(acc, 21, t) - - pred = func(el types.Elem) bool { - i := el.(types.GoType).V.(int) - if i > 4 { - return false - } - acc += i - return true - } - - acc = 0 - Traverse(pred, l2) - assertValue(acc, 6, t) - - acc = 0 - Traverse(pred, l3) - assertValue(acc, 7, t) -} diff --git a/seq/util.go b/seq/util.go deleted file mode 100644 index 4b8f6de..0000000 --- a/seq/util.go +++ /dev/null @@ -1,113 +0,0 @@ -package seq - -import ( - "testing" - - "github.com/mediocregopher/ginger/types" -) - -// Returns whether or not two types.Elem slices contain the same elements -func intSlicesEq(a, b []types.Elem) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -func elemSliceV(a ...interface{}) []types.Elem { - ret := make([]types.Elem, 0, len(a)) - for i := range a { - if e, ok := a[i].(types.Elem); ok { - ret = append(ret, e) - } else { - ret = append(ret, types.GoType{a[i]}) - } - } - return ret -} - -// Asserts that the given Seq is empty (contains no elements) -func assertEmpty(s Seq, t *testing.T) { - if Size(s) != 0 { - t.Fatalf("Seq isn't empty: %v", ToSlice(s)) - } -} - -// Asserts that the given Seq has the given elements -func assertSeqContents(s Seq, intl []types.Elem, t *testing.T) { - if ls := ToSlice(s); !intSlicesEq(ls, intl) { - t.Fatalf("Slice contents wrong: %v not %v", ls, intl) - } -} - -// Asserts that the given Seq has all elements, and only the elements, in the -// given map -func assertSeqContentsNoOrderMap(s Seq, m map[types.Elem]bool, t *testing.T) { - ls := ToSlice(s) - if len(ls) != len(m) { - t.Fatalf("Slice contents wrong: %v not %v", ls, m) - } - for i := range ls { - if _, ok := m[ls[i]]; !ok { - t.Fatalf("Slice contents wrong: %v not %v", ls, m) - } - } -} - -// Asserts that the given Seq has all the elements, and only the elements -// (duplicates removed), in the given slice, although no necessarily in the -// order given in the slice -func assertSeqContentsSet(s Seq, ints []types.Elem, t *testing.T) { - m := map[types.Elem]bool{} - for i := range ints { - m[ints[i]] = true - } - assertSeqContentsNoOrderMap(s, m, t) -} - -func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) { - m := map[KV]bool{} - for i := range kvs { - m[*kvs[i]] = true - } - ls := ToSlice(s) - if len(ls) != len(m) { - t.Fatalf("Slice contents wrong: %v not %v", ls, m) - } - for i := range ls { - kv := ls[i].(*KV) - if _, ok := m[*kv]; !ok { - t.Fatalf("Slice contents wrong: %v not %v", ls, m) - } - } -} - -// Asserts that v1 is the same as v2 -func assertValue(v1, v2 interface{}, t *testing.T) { - if gv1, ok := v1.(types.GoType); ok { - v1 = gv1.V - } - if gv2, ok := v2.(types.GoType); ok { - v2 = gv2.V - } - if v1 != v2 { - t.Logf("Value wrong: %v not %v", v1, v2) - panic("bail") - } -} - -// Asserts that v1 is a key in the given map -func assertInMap(v1 types.Elem, m map[types.Elem]bool, t *testing.T) { - if _, ok := m[v1]; !ok { - t.Fatalf("Value not in set: %v not in %v", v1, m) - } -} - -func keyValV(k, v interface{}) *KV { - return KeyVal(types.GoType{k}, types.GoType{v}) -} diff --git a/types/types.go b/types/types.go deleted file mode 100644 index 062724b..0000000 --- a/types/types.go +++ /dev/null @@ -1,38 +0,0 @@ -// This package describes ginger's base types and the interfaces covered by them -package types - -import ( - "fmt" -) - -// Elem is a generic type which can be used as a wrapper type for all ginger -// types, both base types and data structures -type Elem interface { - - // Returns whether one element is equal to another. Since all ginger values - // are immutable, this must be a deep-equals check. - Equal(Elem) bool -} - -// Number can either be either an Int or a Float -type Number interface { - Elem -} - -// Wraps a go type like int, string, or []byte. GoType is a struct whose only -// field is an interface{}, so using a pointer to is not necessary. Just pass -// around the value type. -type GoType struct { - V interface{} -} - -func (g GoType) Equal(e Elem) bool { - if g2, ok := e.(GoType); ok { - return g.V == g2.V - } - return false -} - -func (g GoType) String() string { - return fmt.Sprintf("%v", g.V) -}