Skip to content

Commit

Permalink
wdte: Add rev and method (#198)
Browse files Browse the repository at this point in the history
* wdte: Remove `Func` from `Assigner`.

* res/grammar: Add `rev` and `method` funcmods.

* scanner: Add `rev` and `method` keywords.

* wdte: Refactor function creation into a shared function.

* wdte: Implement `rev` and `method`.

* wdte: Don't use 'fmt.Sprint()' to write to a `strings.Builder`.

Huh. What in the world.
  • Loading branch information
DeedleFake authored Feb 19, 2020
1 parent 83dc3b2 commit d5eac1a
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 196 deletions.
278 changes: 142 additions & 136 deletions ast/internal/pgen/table.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions res/grammar.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<funcmods> -> <funcmod> <funcmods>
| ε
<funcmod> -> memo
| rev
| method
<argdecls> -> <argdecl> <argdecls>
| ε
<argdecl> -> id
Expand Down
2 changes: 2 additions & 0 deletions scanner/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var (

keywords = []string{
"memo",
"rev",
"method",
"let",
"import",
}
Expand Down
85 changes: 51 additions & 34 deletions translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ func (m *translator) fromFuncMod(funcMod *ast.NTerm) funcMod {
case "memo":
return funcModMemo

case "rev":
return funcModRev

case "method":
return funcModMethod

default:
panic(fmt.Errorf("Malformed AST with bad <funcmod>: %v", mod))
}
Expand Down Expand Up @@ -121,25 +127,9 @@ func (m *translator) fromLetExpr(expr *ast.NTerm) Func {
argIDs = append(argIDs, arg.IDs()...)
}

if mods&funcModMemo != 0 {
inner = &Memo{
Func: inner,
Args: argIDs,
}
}

right := inner
if len(args) > 0 {
right = &Lambda{
ID: id,
Expr: inner,
Args: args,
}
}

return &LetAssigner{
Assigner: SimpleAssigner(id),
Expr: right,
Expr: m.fromFuncDecl(mods, id, args, inner),
}

case "argdecl":
Expand Down Expand Up @@ -283,6 +273,47 @@ func (m *translator) fromCompound(compound *ast.NTerm) Func {
return r
}

func (m *translator) fromFuncDecl(mods funcMod, id ID, args []Assigner, expr Func) Func {
if (mods == 0) && (len(args) == 0) {
return expr
}

if mods&funcModMemo != 0 {
argIDs := make([]ID, 0, len(args))
for _, arg := range args {
argIDs = append(argIDs, arg.IDs()...)
}

expr = &Memo{
Func: expr,
Args: argIDs,
}
}

argSplit := func(assigners []Assigner, args []Func) ([]Assigner, []Assigner) {
return assigners[:len(args)], assigners[len(args):]
}
if mods&funcModRev != 0 {
argSplit = func(assigners []Assigner, args []Func) ([]Assigner, []Assigner) {
return assigners[len(assigners)-len(args):], assigners[:len(assigners)-len(args)]
}
}

var method Assigner
if mods&funcModMethod != 0 {
method = args[0]
args = args[1:]
}

return &Lambda{
ID: id,
Expr: expr,
Args: args,
ArgSplit: argSplit,
Method: method,
}
}

func (m *translator) fromLambda(lambda *ast.NTerm) (f Func) {
mods := m.fromFuncMods(lambda.Children()[1].(*ast.NTerm))
id := ID(lambda.Children()[2].(*ast.Term).Tok().Val.(string))
Expand All @@ -296,23 +327,7 @@ func (m *translator) fromLambda(lambda *ast.NTerm) (f Func) {
}
}

argIDs := make([]ID, 0, len(args))
for _, arg := range args {
argIDs = append(argIDs, arg.IDs()...)
}

if mods&funcModMemo != 0 {
inner = &Memo{
Func: inner,
Args: argIDs,
}
}

return &Lambda{
ID: id,
Expr: inner,
Args: args,
}
return m.fromFuncDecl(mods, id, args, inner)
}

func (m *translator) fromImport(im *ast.NTerm) Func {
Expand Down Expand Up @@ -380,4 +395,6 @@ type funcMod uint

const (
funcModMemo funcMod = 1 << iota
funcModRev
funcModMethod
)
64 changes: 38 additions & 26 deletions wdte.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ func (c Compound) Collect(frame Frame) (letScope *Scope, last Func) {
for _, f := range c {
switch f := f.(type) {
case Assigner:
letScope, last = f.Assign(frame, letScope, f)
letScope, last = f.Assign(frame, letScope, last)
default:
last = f.Call(frame.WithScope(frame.Scope().Sub(letScope)))
}
Expand Down Expand Up @@ -735,44 +735,65 @@ func (cache *memoCache) Set(args []Func, val Func) {
// and its first and second arguments under the IDs "x" and "y",
// respectively. It will then evaluate `+ x y` in that new scope.
type Lambda struct {
ID ID
Expr Func
Args []Assigner
ID ID
Expr Func
Args []Assigner
ArgSplit func([]Assigner, []Func) ([]Assigner, []Assigner)
Method Assigner

Scope *Scope
Original *Lambda
}

func (lambda *Lambda) original() *Lambda {
if lambda.Original == nil {
return lambda
}

return lambda.Original
}

func (lambda *Lambda) Call(frame Frame, args ...Func) Func { // nolint
scope := lambda.Scope
if scope == nil {
scope = frame.Scope()
}

original := lambda.Original
if original == nil {
original = lambda
}

if len(args) < len(lambda.Args) {
assigners, rem := lambda.ArgSplit(lambda.Args, args)
for i := range args {
scope, _ = lambda.Args[i].Assign(frame, scope, args[i])
scope, _ = assigners[i].Assign(frame, scope, args[i])
}

return &Lambda{
ID: lambda.ID,
Expr: lambda.Expr,
Args: lambda.Args[len(args):],
ID: lambda.ID,
Expr: lambda.Expr,
Args: rem,
ArgSplit: lambda.ArgSplit,
Method: lambda.Method,

Scope: scope,
Original: original,
Original: lambda.original(),
}
}

for i := range lambda.Args {
scope, _ = lambda.Args[i].Assign(frame, scope, args[i])
}

if lambda.Method != nil {
return &Lambda{
ID: lambda.ID,
Expr: lambda.Expr,
Args: []Assigner{lambda.Method},
ArgSplit: lambda.ArgSplit,

Scope: scope,
Original: lambda.original(),
}
}

original := lambda.original()
scope = scope.Add(original.ID, original)
return lambda.Expr.Call(frame.WithScope(scope))
}
Expand All @@ -791,10 +812,9 @@ func (lambda *Lambda) String() string { // nolint
}

// An Assigner places items into a scope. How exactly iy does this
// differs, but the general idea is to produce a subscope from a combination of frame, an existing scope, and a function.
// differs, but the general idea is to produce a subscope from a
// combination of frame, an existing scope, and a function.
type Assigner interface {
Func

// Assign produces a subscope from an existing frame, scope, and
// function, returning both the new subscope and a function. The
// returned function may or may not be related to the original
Expand All @@ -815,10 +835,6 @@ type Assigner interface {
// value.
type SimpleAssigner ID

func (a SimpleAssigner) Call(frame Frame, args ...Func) Func {
return a
}

func (a SimpleAssigner) Assign(frame Frame, scope *Scope, val Func) (*Scope, Func) {
frame = frame.WithScope(frame.Scope().Sub(scope))

Expand Down Expand Up @@ -855,10 +871,6 @@ func (a SimpleAssigner) String() string {
// c = 3
type PatternAssigner []Assigner

func (a PatternAssigner) Call(frame Frame, args ...Func) Func {
return a
}

func (a PatternAssigner) Assign(frame Frame, scope *Scope, val Func) (*Scope, Func) {
assignAtter := func(frame Frame, f interface {
Func
Expand Down Expand Up @@ -926,7 +938,7 @@ func (a PatternAssigner) String() string {
sep := ""
for _, a := range a {
buf.WriteString(sep)
buf.WriteString(fmt.Sprint(a))
fmt.Fprint(&buf, a)

sep = " "
}
Expand Down
15 changes: 15 additions & 0 deletions wdte_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,21 @@ func TestBasics(t *testing.T) {
script: `let memo test [a [b]] c => + a b c; (test [1; [2]] 3; test [1; [2]] 3);`,
ret: wdte.Number(6),
},
{
name: "Simple/Rev",
script: `let rev test a b c => [a; b; c]; (test 1 2) 3;`,
ret: wdte.Array{wdte.Number(3), wdte.Number(1), wdte.Number(2)},
},
{
name: "Simple/Method",
script: `let method test r a b => [r; a; b]; 3 -> test 1 2;`,
ret: wdte.Array{wdte.Number(3), wdte.Number(1), wdte.Number(2)},
},
{
name: "Simple/RevMethod",
script: `let method rev test r a b c => [r; a; b; c]; 0 -> (test 1 2) 3;`,
ret: wdte.Array{wdte.Number(0), wdte.Number(3), wdte.Number(1), wdte.Number(2)},
},
{
name: "Simple/VariableArgs",
script: `let test => +; test 3 5;`,
Expand Down

0 comments on commit d5eac1a

Please sign in to comment.