starting over again. I'm just gonna keep doing this about once a year for the rest of my life

This commit is contained in:
Brian Picciano 2016-07-01 17:31:32 -06:00
parent 2d779f8182
commit 4f9baf7514
36 changed files with 0 additions and 4659 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
.goat
*.test

View File

@ -1,2 +0,0 @@
---
path: github.com/mediocregopher/ginger

View File

@ -1,10 +0,0 @@
all: gen
test: gen
go test ./...
fmt: gen
go fmt ./...
gen:
(cd core && make gen)

View File

@ -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)

View File

@ -1,2 +0,0 @@
gen:
go run mathgen/mathgen.go > math.go

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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)
}
}

View File

@ -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}}

View File

@ -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.

View File

@ -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

View File

@ -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
}")
```

View File

@ -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.

View File

@ -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))
```

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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 ""
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}
}

View File

@ -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()
}

View File

@ -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])
}
}
}
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -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)
}

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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
}
}
}

View File

@ -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)
}

View File

@ -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})
}

View File

@ -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)
}