From ebf57591a8ac08da8a312855fc3a6d9c1ee6dcb2 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Thu, 30 Dec 2021 15:29:38 -0700 Subject: [PATCH] Got basic demo working, ran go fmt --- README.md | 47 +++++++++++++++------------------------------ cmd/eval/main.go | 41 +++++++++++++++++++++++++++++++++++++++ examples/fib.gg | 19 ++++++++++++++++++ gg/decoder.go | 29 ++++++++++++++++++++++++++-- gg/decoder_test.go | 2 +- graph/graph.go | 25 ++++++++++++------------ graph/graph_test.go | 20 +++++++++---------- vm/op.go | 3 +-- vm/scope.go | 8 ++++---- vm/scope_global.go | 1 - vm/vm.go | 2 +- 11 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 cmd/eval/main.go create mode 100644 examples/fib.gg diff --git a/README.md b/README.md index c8ee356..493c5bf 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,7 @@ # Ginger -Fibonacci function in ginger: - -``` -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; -``` +A programming language utilizing a graph datastructure for syntax. Currently in +super-early-alpha-don't-actually-use-this-for-anything 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 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! + +## 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. diff --git a/cmd/eval/main.go b/cmd/eval/main.go new file mode 100644 index 0000000..0d559ab --- /dev/null +++ b/cmd/eval/main.go @@ -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 "in = "\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) +} diff --git a/examples/fib.gg b/examples/fib.gg new file mode 100644 index 0000000..38943a7 --- /dev/null +++ b/examples/fib.gg @@ -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;); diff --git a/gg/decoder.go b/gg/decoder.go index 697fc98..b5b82aa 100644 --- a/gg/decoder.go +++ b/gg/decoder.go @@ -11,7 +11,7 @@ import ( // Type aliases for convenience type ( - Graph = graph.Graph[Value, Value] + Graph = graph.Graph[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 } -func (d *decoder) decode(lexer Lexer) (*Graph, error) { +func (d *decoder) readAllTokens(lexer Lexer) ([]LexerToken, error) { var toks []LexerToken @@ -309,6 +309,17 @@ func (d *decoder) decode(lexer Lexer) (*Graph, error) { 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) if err != nil { @@ -326,3 +337,17 @@ func DecodeLexer(lexer Lexer) (*Graph, error) { decoder := &decoder{} 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 +} diff --git a/gg/decoder_test.go b/gg/decoder_test.go index 5140a97..417668b 100644 --- a/gg/decoder_test.go +++ b/gg/decoder_test.go @@ -52,7 +52,7 @@ func TestDecoder(t *testing.T) { tOut( n("a"), vOut(n("b"), - i(1)), + i(1)), ), ), }, diff --git a/graph/graph.go b/graph/graph.go index 92202d6..6ee326f 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -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. func ValueOut[E, V Value](edgeVal E, val V) *OpenEdge[E, V] { return &OpenEdge[E, V]{ - val: &val, + val: &val, edgeVal: edgeVal, } } @@ -127,7 +127,7 @@ func TupleOut[E, V Value](edgeVal E, ins ...*OpenEdge[E, V]) *OpenEdge[E, V] { var ( zero V - in = ins[0] + in = ins[0] ) 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]{ - tup: ins, + tup: ins, edgeVal: edgeVal, } } type graphValueIn[E, V Value] struct { - val V + val 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 // in memory. type Graph[E, V Value] struct { - edges []*OpenEdge[E, V] + edges []*OpenEdge[E, V] valIns []graphValueIn[E, V] } func (g *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)), } 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] { valIn := graphValueIn[E, V]{ - val: val, + val: val, edge: oe, } @@ -284,14 +284,13 @@ outer: return true } - func mapReduce[Ea, Va Value, Vb any]( root *OpenEdge[Ea, Va], mapVal func(Va) (Vb, error), reduceEdge func(*OpenEdge[Ea, Va], []Vb) (Vb, error), ) ( Vb, error, -){ +) { 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 { edgeA *OpenEdge[Ea, Va] - valB Vb // result + valB Vb // result } // 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), ) ( Vb, error, -){ +) { var ( zeroB Vb @@ -370,7 +369,7 @@ func MapReduce[Ea, Va Value, Vb any]( // reduction is only performed a single time for each value/edge. // // NOTE this is not implemented very efficiently. - mappedVals []mappedVal[Va, Vb] + mappedVals []mappedVal[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]{ edgeA: edgeA, - valB: valB, + valB: valB, }) return valB, nil diff --git a/graph/graph_test.go b/graph/graph_test.go index 33e682b..5b31604 100644 --- a/graph/graph_test.go +++ b/graph/graph_test.go @@ -1,8 +1,8 @@ package graph import ( - "fmt" "errors" + "fmt" "strconv" "testing" @@ -129,7 +129,7 @@ func TestEqual(t *testing.T) { type mapReduceTestEdge struct { name string - fn func([]int) int + fn func([]int) int done bool } @@ -162,9 +162,9 @@ func (e *mapReduceTestEdge) do(ii []int) int { func TestMapReduce(t *testing.T) { type ( - Va = I - Vb = int - Ea = *mapReduceTestEdge + Va = I + Vb = int + Ea = *mapReduceTestEdge edge = OpenEdge[Ea, Va] ) @@ -180,7 +180,7 @@ func TestMapReduce(t *testing.T) { return TupleOut[Ea, Va](edge, ins...) } - add := func() *mapReduceTestEdge{ + add := func() *mapReduceTestEdge { return &mapReduceTestEdge{ name: "add", fn: func(ii []int) int { @@ -193,7 +193,7 @@ func TestMapReduce(t *testing.T) { } } - mul := func() *mapReduceTestEdge{ + mul := func() *mapReduceTestEdge { return &mapReduceTestEdge{ name: "mul", fn: func(ii []int) int { @@ -225,15 +225,15 @@ func TestMapReduce(t *testing.T) { } tests := []struct { - in *edge + in *edge exp int }{ { - in: vOut(nil, 1), + in: vOut(nil, 1), exp: 10, }, { - in: vOut(add(), 1), + in: vOut(add(), 1), exp: 10, }, { diff --git a/vm/op.go b/vm/op.go index 660d295..3e8f011 100644 --- a/vm/op.go +++ b/vm/op.go @@ -30,7 +30,7 @@ func evalThunks(args []Thunk) Thunk { return func() (Value, error) { var ( - err error + err error 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 // having been evaluated yet) and return a Thunk which will perform some // internal processing on those arguments and return a resultant Value. diff --git a/vm/scope.go b/vm/scope.go index 8a552bc..8a619c0 100644 --- a/vm/scope.go +++ b/vm/scope.go @@ -115,9 +115,9 @@ func (m ScopeMap) NewScope() Scope { type graphScope struct { *gg.Graph - in Thunk + in Thunk parent Scope - op Operation + op Operation } // 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 { return &graphScope{ Graph: g, - in: in, + in: in, parent: parent, - op: op, + op: op, } } diff --git a/vm/scope_global.go b/vm/scope_global.go index 75e2c52..f8149c7 100644 --- a/vm/scope_global.go +++ b/vm/scope_global.go @@ -74,5 +74,4 @@ var GlobalScope = ScopeMap{ "recur": Value{Operation: OperationFunc(func(args []Thunk, op Operation) (Thunk, error) { return op.Perform(args, op) })}, - } diff --git a/vm/vm.go b/vm/vm.go index 0aa7f50..04710f7 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -2,8 +2,8 @@ package vm import ( - "io" "fmt" + "io" "strings" "github.com/mediocregopher/ginger/gg"