Got basic demo working, ran go fmt
This commit is contained in:
parent
6257495fe4
commit
ebf57591a8
47
README.md
47
README.md
@ -1,37 +1,7 @@
|
|||||||
# Ginger
|
# Ginger
|
||||||
|
|
||||||
Fibonacci function in ginger:
|
A programming language utilizing a graph datastructure for syntax. Currently in
|
||||||
|
super-early-alpha-don't-actually-use-this-for-anything development.
|
||||||
```
|
|
||||||
fib = {
|
|
||||||
|
|
||||||
decr = { out = add < (in; -1;); };
|
|
||||||
|
|
||||||
out = {
|
|
||||||
|
|
||||||
n = tupEl < (in; 0;);
|
|
||||||
a = tupEl < (in; 1;);
|
|
||||||
b = tupEl < (in; 2;);
|
|
||||||
|
|
||||||
out = if < (
|
|
||||||
zero? < n;
|
|
||||||
a;
|
|
||||||
recur < (
|
|
||||||
decr < n;
|
|
||||||
b;
|
|
||||||
add < (a;b;);
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
} < (in; 0; 1;);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage of the function to generate the 6th fibonnaci number:
|
|
||||||
|
|
||||||
```
|
|
||||||
fib < 5;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@ -48,3 +18,16 @@ from the repo root and you will be dropped into a shell with all dependencies
|
|||||||
(including the correct go version) in your PATH, ready to use. This could
|
(including the correct go version) in your PATH, ready to use. This could
|
||||||
probably be expanded to other OSs/architectures easily, if you care to do so
|
probably be expanded to other OSs/architectures easily, if you care to do so
|
||||||
please check out the `default.nix` file and submit a PR!
|
please check out the `default.nix` file and submit a PR!
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
An example program which computes the Nth fibonacci number can be found at
|
||||||
|
`examples/fib.gg`. You can try it out by doing:
|
||||||
|
|
||||||
|
```
|
||||||
|
go run ./cmd/eval/main.go "$(cat examples/fib.gg)" 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Where you can replace `5` with any number. The vm has only been given enough
|
||||||
|
capability to run this program as a demo, and is extremely poorly optimized (as
|
||||||
|
will be evident if you input any large number). Further work is obviously TODO.
|
||||||
|
41
cmd/eval/main.go
Normal file
41
cmd/eval/main.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/ginger/gg"
|
||||||
|
"github.com/mediocregopher/ginger/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
fmt.Printf(`Usage: %s <operation source> "in = <value>"\n`, os.Args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opSrc := os.Args[1]
|
||||||
|
inSrc := os.Args[2]
|
||||||
|
|
||||||
|
inVal, err := gg.DecodeSingleValueFromLexer(
|
||||||
|
gg.NewLexer(bytes.NewBufferString(inSrc + ";")),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("decoding input: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := vm.EvaluateSource(
|
||||||
|
bytes.NewBufferString(opSrc),
|
||||||
|
inVal,
|
||||||
|
vm.GlobalScope,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("evaluating: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(res)
|
||||||
|
}
|
19
examples/fib.gg
Normal file
19
examples/fib.gg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
out = {
|
||||||
|
|
||||||
|
decr = { out = add < (in; -1;); };
|
||||||
|
|
||||||
|
n = tupEl < (in; 0;);
|
||||||
|
a = tupEl < (in; 1;);
|
||||||
|
b = tupEl < (in; 2;);
|
||||||
|
|
||||||
|
out = if < (
|
||||||
|
isZero < n;
|
||||||
|
a;
|
||||||
|
recur < (
|
||||||
|
decr < n;
|
||||||
|
b;
|
||||||
|
add < (a;b;);
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
} < (in; 0; 1;);
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// Type aliases for convenience
|
// Type aliases for convenience
|
||||||
type (
|
type (
|
||||||
Graph = graph.Graph[Value, Value]
|
Graph = graph.Graph[Value, Value]
|
||||||
OpenEdge = graph.OpenEdge[Value, Value]
|
OpenEdge = graph.OpenEdge[Value, Value]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ func (d *decoder) parseValIn(into *Graph, toks []LexerToken) (*Graph, []LexerTok
|
|||||||
return into.AddValueIn(dstVal, oe), toks, nil
|
return into.AddValueIn(dstVal, oe), toks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decode(lexer Lexer) (*Graph, error) {
|
func (d *decoder) readAllTokens(lexer Lexer) ([]LexerToken, error) {
|
||||||
|
|
||||||
var toks []LexerToken
|
var toks []LexerToken
|
||||||
|
|
||||||
@ -309,6 +309,17 @@ func (d *decoder) decode(lexer Lexer) (*Graph, error) {
|
|||||||
toks = append(toks, tok)
|
toks = append(toks, tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return toks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) decode(lexer Lexer) (*Graph, error) {
|
||||||
|
|
||||||
|
toks, err := d.readAllTokens(lexer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
val, _, _, err := d.parseGraphValue(toks, false)
|
val, _, _, err := d.parseGraphValue(toks, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -326,3 +337,17 @@ func DecodeLexer(lexer Lexer) (*Graph, error) {
|
|||||||
decoder := &decoder{}
|
decoder := &decoder{}
|
||||||
return decoder.decode(lexer)
|
return decoder.decode(lexer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeSingleValueFromLexer(lexer Lexer) (Value, error) {
|
||||||
|
decoder := &decoder{}
|
||||||
|
|
||||||
|
toks, err := decoder.readAllTokens(lexer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ZeroValue, err
|
||||||
|
}
|
||||||
|
|
||||||
|
val, _, _, err := decoder.parseSingleValue(toks)
|
||||||
|
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
@ -52,7 +52,7 @@ func TestDecoder(t *testing.T) {
|
|||||||
tOut(
|
tOut(
|
||||||
n("a"),
|
n("a"),
|
||||||
vOut(n("b"),
|
vOut(n("b"),
|
||||||
i(1)),
|
i(1)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -113,7 +113,7 @@ func (oe OpenEdge[E, V]) FromTuple() ([]*OpenEdge[E, V], bool) {
|
|||||||
// an edge (with edgeVal attached to it) coming from the vertex containing val.
|
// an edge (with edgeVal attached to it) coming from the vertex containing val.
|
||||||
func ValueOut[E, V Value](edgeVal E, val V) *OpenEdge[E, V] {
|
func ValueOut[E, V Value](edgeVal E, val V) *OpenEdge[E, V] {
|
||||||
return &OpenEdge[E, V]{
|
return &OpenEdge[E, V]{
|
||||||
val: &val,
|
val: &val,
|
||||||
edgeVal: edgeVal,
|
edgeVal: edgeVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ func TupleOut[E, V Value](edgeVal E, ins ...*OpenEdge[E, V]) *OpenEdge[E, V] {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
zero V
|
zero V
|
||||||
in = ins[0]
|
in = ins[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
if edgeVal.Equal(zero) {
|
if edgeVal.Equal(zero) {
|
||||||
@ -141,13 +141,13 @@ func TupleOut[E, V Value](edgeVal E, ins ...*OpenEdge[E, V]) *OpenEdge[E, V] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &OpenEdge[E, V]{
|
return &OpenEdge[E, V]{
|
||||||
tup: ins,
|
tup: ins,
|
||||||
edgeVal: edgeVal,
|
edgeVal: edgeVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type graphValueIn[E, V Value] struct {
|
type graphValueIn[E, V Value] struct {
|
||||||
val V
|
val V
|
||||||
edge *OpenEdge[E, V]
|
edge *OpenEdge[E, V]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,13 +163,13 @@ func (valIn graphValueIn[E, V]) equal(valIn2 graphValueIn[E, V]) bool {
|
|||||||
// lots of O(N) operations, unnecessary copying on changes, and duplicate data
|
// lots of O(N) operations, unnecessary copying on changes, and duplicate data
|
||||||
// in memory.
|
// in memory.
|
||||||
type Graph[E, V Value] struct {
|
type Graph[E, V Value] struct {
|
||||||
edges []*OpenEdge[E, V]
|
edges []*OpenEdge[E, V]
|
||||||
valIns []graphValueIn[E, V]
|
valIns []graphValueIn[E, V]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graph[E, V]) cp() *Graph[E, V] {
|
func (g *Graph[E, V]) cp() *Graph[E, V] {
|
||||||
cp := &Graph[E, V]{
|
cp := &Graph[E, V]{
|
||||||
edges: make([]*OpenEdge[E, V], len(g.edges)),
|
edges: make([]*OpenEdge[E, V], len(g.edges)),
|
||||||
valIns: make([]graphValueIn[E, V], len(g.valIns)),
|
valIns: make([]graphValueIn[E, V], len(g.valIns)),
|
||||||
}
|
}
|
||||||
copy(cp.edges, g.edges)
|
copy(cp.edges, g.edges)
|
||||||
@ -243,7 +243,7 @@ func (g *Graph[E, V]) ValueIns(val Value) []*OpenEdge[E, V] {
|
|||||||
func (g *Graph[E, V]) AddValueIn(val V, oe *OpenEdge[E, V]) *Graph[E, V] {
|
func (g *Graph[E, V]) AddValueIn(val V, oe *OpenEdge[E, V]) *Graph[E, V] {
|
||||||
|
|
||||||
valIn := graphValueIn[E, V]{
|
valIn := graphValueIn[E, V]{
|
||||||
val: val,
|
val: val,
|
||||||
edge: oe,
|
edge: oe,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,14 +284,13 @@ outer:
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func mapReduce[Ea, Va Value, Vb any](
|
func mapReduce[Ea, Va Value, Vb any](
|
||||||
root *OpenEdge[Ea, Va],
|
root *OpenEdge[Ea, Va],
|
||||||
mapVal func(Va) (Vb, error),
|
mapVal func(Va) (Vb, error),
|
||||||
reduceEdge func(*OpenEdge[Ea, Va], []Vb) (Vb, error),
|
reduceEdge func(*OpenEdge[Ea, Va], []Vb) (Vb, error),
|
||||||
) (
|
) (
|
||||||
Vb, error,
|
Vb, error,
|
||||||
){
|
) {
|
||||||
|
|
||||||
if valA, ok := root.FromValue(); ok {
|
if valA, ok := root.FromValue(); ok {
|
||||||
|
|
||||||
@ -333,7 +332,7 @@ type mappedVal[Va Value, Vb any] struct {
|
|||||||
|
|
||||||
type reducedEdge[Ea, Va Value, Vb any] struct {
|
type reducedEdge[Ea, Va Value, Vb any] struct {
|
||||||
edgeA *OpenEdge[Ea, Va]
|
edgeA *OpenEdge[Ea, Va]
|
||||||
valB Vb // result
|
valB Vb // result
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapReduce recursively computes a resultant Value of type Vb from an
|
// MapReduce recursively computes a resultant Value of type Vb from an
|
||||||
@ -361,7 +360,7 @@ func MapReduce[Ea, Va Value, Vb any](
|
|||||||
reduceEdge func(Ea, []Vb) (Vb, error),
|
reduceEdge func(Ea, []Vb) (Vb, error),
|
||||||
) (
|
) (
|
||||||
Vb, error,
|
Vb, error,
|
||||||
){
|
) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
zeroB Vb
|
zeroB Vb
|
||||||
@ -370,7 +369,7 @@ func MapReduce[Ea, Va Value, Vb any](
|
|||||||
// reduction is only performed a single time for each value/edge.
|
// reduction is only performed a single time for each value/edge.
|
||||||
//
|
//
|
||||||
// NOTE this is not implemented very efficiently.
|
// NOTE this is not implemented very efficiently.
|
||||||
mappedVals []mappedVal[Va, Vb]
|
mappedVals []mappedVal[Va, Vb]
|
||||||
reducedEdges []reducedEdge[Ea, Va, Vb]
|
reducedEdges []reducedEdge[Ea, Va, Vb]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -413,7 +412,7 @@ func MapReduce[Ea, Va Value, Vb any](
|
|||||||
|
|
||||||
reducedEdges = append(reducedEdges, reducedEdge[Ea, Va, Vb]{
|
reducedEdges = append(reducedEdges, reducedEdge[Ea, Va, Vb]{
|
||||||
edgeA: edgeA,
|
edgeA: edgeA,
|
||||||
valB: valB,
|
valB: valB,
|
||||||
})
|
})
|
||||||
|
|
||||||
return valB, nil
|
return valB, nil
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package graph
|
package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ func TestEqual(t *testing.T) {
|
|||||||
|
|
||||||
type mapReduceTestEdge struct {
|
type mapReduceTestEdge struct {
|
||||||
name string
|
name string
|
||||||
fn func([]int) int
|
fn func([]int) int
|
||||||
done bool
|
done bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +162,9 @@ func (e *mapReduceTestEdge) do(ii []int) int {
|
|||||||
func TestMapReduce(t *testing.T) {
|
func TestMapReduce(t *testing.T) {
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Va = I
|
Va = I
|
||||||
Vb = int
|
Vb = int
|
||||||
Ea = *mapReduceTestEdge
|
Ea = *mapReduceTestEdge
|
||||||
edge = OpenEdge[Ea, Va]
|
edge = OpenEdge[Ea, Va]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ func TestMapReduce(t *testing.T) {
|
|||||||
return TupleOut[Ea, Va](edge, ins...)
|
return TupleOut[Ea, Va](edge, ins...)
|
||||||
}
|
}
|
||||||
|
|
||||||
add := func() *mapReduceTestEdge{
|
add := func() *mapReduceTestEdge {
|
||||||
return &mapReduceTestEdge{
|
return &mapReduceTestEdge{
|
||||||
name: "add",
|
name: "add",
|
||||||
fn: func(ii []int) int {
|
fn: func(ii []int) int {
|
||||||
@ -193,7 +193,7 @@ func TestMapReduce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mul := func() *mapReduceTestEdge{
|
mul := func() *mapReduceTestEdge {
|
||||||
return &mapReduceTestEdge{
|
return &mapReduceTestEdge{
|
||||||
name: "mul",
|
name: "mul",
|
||||||
fn: func(ii []int) int {
|
fn: func(ii []int) int {
|
||||||
@ -225,15 +225,15 @@ func TestMapReduce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in *edge
|
in *edge
|
||||||
exp int
|
exp int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
in: vOut(nil, 1),
|
in: vOut(nil, 1),
|
||||||
exp: 10,
|
exp: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: vOut(add(), 1),
|
in: vOut(add(), 1),
|
||||||
exp: 10,
|
exp: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
3
vm/op.go
3
vm/op.go
@ -30,7 +30,7 @@ func evalThunks(args []Thunk) Thunk {
|
|||||||
return func() (Value, error) {
|
return func() (Value, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
tupVals = make([]Value, len(args))
|
tupVals = make([]Value, len(args))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,7 +44,6 @@ func evalThunks(args []Thunk) Thunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Operation is an entity which can accept one or more arguments (each not
|
// Operation is an entity which can accept one or more arguments (each not
|
||||||
// having been evaluated yet) and return a Thunk which will perform some
|
// having been evaluated yet) and return a Thunk which will perform some
|
||||||
// internal processing on those arguments and return a resultant Value.
|
// internal processing on those arguments and return a resultant Value.
|
||||||
|
@ -115,9 +115,9 @@ func (m ScopeMap) NewScope() Scope {
|
|||||||
|
|
||||||
type graphScope struct {
|
type graphScope struct {
|
||||||
*gg.Graph
|
*gg.Graph
|
||||||
in Thunk
|
in Thunk
|
||||||
parent Scope
|
parent Scope
|
||||||
op Operation
|
op Operation
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopeFromGraph returns a Scope which will use the given Graph for evaluation.
|
// ScopeFromGraph returns a Scope which will use the given Graph for evaluation.
|
||||||
@ -139,9 +139,9 @@ type graphScope struct {
|
|||||||
func ScopeFromGraph(g *gg.Graph, in Thunk, parent Scope, op Operation) Scope {
|
func ScopeFromGraph(g *gg.Graph, in Thunk, parent Scope, op Operation) Scope {
|
||||||
return &graphScope{
|
return &graphScope{
|
||||||
Graph: g,
|
Graph: g,
|
||||||
in: in,
|
in: in,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
op: op,
|
op: op,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,5 +74,4 @@ var GlobalScope = ScopeMap{
|
|||||||
"recur": Value{Operation: OperationFunc(func(args []Thunk, op Operation) (Thunk, error) {
|
"recur": Value{Operation: OperationFunc(func(args []Thunk, op Operation) (Thunk, error) {
|
||||||
return op.Perform(args, op)
|
return op.Perform(args, op)
|
||||||
})},
|
})},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user