Skip to content

Commit d5eac1a

Browse files
authored
wdte: Add rev and method (#198)
* 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.
1 parent 83dc3b2 commit d5eac1a

File tree

6 files changed

+250
-196
lines changed

6 files changed

+250
-196
lines changed

Diff for: ast/internal/pgen/table.go

+142-136
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: res/grammar.ebnf

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<funcmods> -> <funcmod> <funcmods>
33
| ε
44
<funcmod> -> memo
5+
| rev
6+
| method
57
<argdecls> -> <argdecl> <argdecls>
68
| ε
79
<argdecl> -> id

Diff for: scanner/util.go

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ var (
2727

2828
keywords = []string{
2929
"memo",
30+
"rev",
31+
"method",
3032
"let",
3133
"import",
3234
}

Diff for: translate.go

+51-34
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ func (m *translator) fromFuncMod(funcMod *ast.NTerm) funcMod {
4949
case "memo":
5050
return funcModMemo
5151

52+
case "rev":
53+
return funcModRev
54+
55+
case "method":
56+
return funcModMethod
57+
5258
default:
5359
panic(fmt.Errorf("Malformed AST with bad <funcmod>: %v", mod))
5460
}
@@ -121,25 +127,9 @@ func (m *translator) fromLetExpr(expr *ast.NTerm) Func {
121127
argIDs = append(argIDs, arg.IDs()...)
122128
}
123129

124-
if mods&funcModMemo != 0 {
125-
inner = &Memo{
126-
Func: inner,
127-
Args: argIDs,
128-
}
129-
}
130-
131-
right := inner
132-
if len(args) > 0 {
133-
right = &Lambda{
134-
ID: id,
135-
Expr: inner,
136-
Args: args,
137-
}
138-
}
139-
140130
return &LetAssigner{
141131
Assigner: SimpleAssigner(id),
142-
Expr: right,
132+
Expr: m.fromFuncDecl(mods, id, args, inner),
143133
}
144134

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

276+
func (m *translator) fromFuncDecl(mods funcMod, id ID, args []Assigner, expr Func) Func {
277+
if (mods == 0) && (len(args) == 0) {
278+
return expr
279+
}
280+
281+
if mods&funcModMemo != 0 {
282+
argIDs := make([]ID, 0, len(args))
283+
for _, arg := range args {
284+
argIDs = append(argIDs, arg.IDs()...)
285+
}
286+
287+
expr = &Memo{
288+
Func: expr,
289+
Args: argIDs,
290+
}
291+
}
292+
293+
argSplit := func(assigners []Assigner, args []Func) ([]Assigner, []Assigner) {
294+
return assigners[:len(args)], assigners[len(args):]
295+
}
296+
if mods&funcModRev != 0 {
297+
argSplit = func(assigners []Assigner, args []Func) ([]Assigner, []Assigner) {
298+
return assigners[len(assigners)-len(args):], assigners[:len(assigners)-len(args)]
299+
}
300+
}
301+
302+
var method Assigner
303+
if mods&funcModMethod != 0 {
304+
method = args[0]
305+
args = args[1:]
306+
}
307+
308+
return &Lambda{
309+
ID: id,
310+
Expr: expr,
311+
Args: args,
312+
ArgSplit: argSplit,
313+
Method: method,
314+
}
315+
}
316+
286317
func (m *translator) fromLambda(lambda *ast.NTerm) (f Func) {
287318
mods := m.fromFuncMods(lambda.Children()[1].(*ast.NTerm))
288319
id := ID(lambda.Children()[2].(*ast.Term).Tok().Val.(string))
@@ -296,23 +327,7 @@ func (m *translator) fromLambda(lambda *ast.NTerm) (f Func) {
296327
}
297328
}
298329

299-
argIDs := make([]ID, 0, len(args))
300-
for _, arg := range args {
301-
argIDs = append(argIDs, arg.IDs()...)
302-
}
303-
304-
if mods&funcModMemo != 0 {
305-
inner = &Memo{
306-
Func: inner,
307-
Args: argIDs,
308-
}
309-
}
310-
311-
return &Lambda{
312-
ID: id,
313-
Expr: inner,
314-
Args: args,
315-
}
330+
return m.fromFuncDecl(mods, id, args, inner)
316331
}
317332

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

381396
const (
382397
funcModMemo funcMod = 1 << iota
398+
funcModRev
399+
funcModMethod
383400
)

Diff for: wdte.go

+38-26
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ func (c Compound) Collect(frame Frame) (letScope *Scope, last Func) {
580580
for _, f := range c {
581581
switch f := f.(type) {
582582
case Assigner:
583-
letScope, last = f.Assign(frame, letScope, f)
583+
letScope, last = f.Assign(frame, letScope, last)
584584
default:
585585
last = f.Call(frame.WithScope(frame.Scope().Sub(letScope)))
586586
}
@@ -735,44 +735,65 @@ func (cache *memoCache) Set(args []Func, val Func) {
735735
// and its first and second arguments under the IDs "x" and "y",
736736
// respectively. It will then evaluate `+ x y` in that new scope.
737737
type Lambda struct {
738-
ID ID
739-
Expr Func
740-
Args []Assigner
738+
ID ID
739+
Expr Func
740+
Args []Assigner
741+
ArgSplit func([]Assigner, []Func) ([]Assigner, []Assigner)
742+
Method Assigner
741743

742744
Scope *Scope
743745
Original *Lambda
744746
}
745747

748+
func (lambda *Lambda) original() *Lambda {
749+
if lambda.Original == nil {
750+
return lambda
751+
}
752+
753+
return lambda.Original
754+
}
755+
746756
func (lambda *Lambda) Call(frame Frame, args ...Func) Func { // nolint
747757
scope := lambda.Scope
748758
if scope == nil {
749759
scope = frame.Scope()
750760
}
751761

752-
original := lambda.Original
753-
if original == nil {
754-
original = lambda
755-
}
756-
757762
if len(args) < len(lambda.Args) {
763+
assigners, rem := lambda.ArgSplit(lambda.Args, args)
758764
for i := range args {
759-
scope, _ = lambda.Args[i].Assign(frame, scope, args[i])
765+
scope, _ = assigners[i].Assign(frame, scope, args[i])
760766
}
761767

762768
return &Lambda{
763-
ID: lambda.ID,
764-
Expr: lambda.Expr,
765-
Args: lambda.Args[len(args):],
769+
ID: lambda.ID,
770+
Expr: lambda.Expr,
771+
Args: rem,
772+
ArgSplit: lambda.ArgSplit,
773+
Method: lambda.Method,
766774

767775
Scope: scope,
768-
Original: original,
776+
Original: lambda.original(),
769777
}
770778
}
771779

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

784+
if lambda.Method != nil {
785+
return &Lambda{
786+
ID: lambda.ID,
787+
Expr: lambda.Expr,
788+
Args: []Assigner{lambda.Method},
789+
ArgSplit: lambda.ArgSplit,
790+
791+
Scope: scope,
792+
Original: lambda.original(),
793+
}
794+
}
795+
796+
original := lambda.original()
776797
scope = scope.Add(original.ID, original)
777798
return lambda.Expr.Call(frame.WithScope(scope))
778799
}
@@ -791,10 +812,9 @@ func (lambda *Lambda) String() string { // nolint
791812
}
792813

793814
// An Assigner places items into a scope. How exactly iy does this
794-
// differs, but the general idea is to produce a subscope from a combination of frame, an existing scope, and a function.
815+
// differs, but the general idea is to produce a subscope from a
816+
// combination of frame, an existing scope, and a function.
795817
type Assigner interface {
796-
Func
797-
798818
// Assign produces a subscope from an existing frame, scope, and
799819
// function, returning both the new subscope and a function. The
800820
// returned function may or may not be related to the original
@@ -815,10 +835,6 @@ type Assigner interface {
815835
// value.
816836
type SimpleAssigner ID
817837

818-
func (a SimpleAssigner) Call(frame Frame, args ...Func) Func {
819-
return a
820-
}
821-
822838
func (a SimpleAssigner) Assign(frame Frame, scope *Scope, val Func) (*Scope, Func) {
823839
frame = frame.WithScope(frame.Scope().Sub(scope))
824840

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

858-
func (a PatternAssigner) Call(frame Frame, args ...Func) Func {
859-
return a
860-
}
861-
862874
func (a PatternAssigner) Assign(frame Frame, scope *Scope, val Func) (*Scope, Func) {
863875
assignAtter := func(frame Frame, f interface {
864876
Func
@@ -926,7 +938,7 @@ func (a PatternAssigner) String() string {
926938
sep := ""
927939
for _, a := range a {
928940
buf.WriteString(sep)
929-
buf.WriteString(fmt.Sprint(a))
941+
fmt.Fprint(&buf, a)
930942

931943
sep = " "
932944
}

Diff for: wdte_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,21 @@ func TestBasics(t *testing.T) {
220220
script: `let memo test [a [b]] c => + a b c; (test [1; [2]] 3; test [1; [2]] 3);`,
221221
ret: wdte.Number(6),
222222
},
223+
{
224+
name: "Simple/Rev",
225+
script: `let rev test a b c => [a; b; c]; (test 1 2) 3;`,
226+
ret: wdte.Array{wdte.Number(3), wdte.Number(1), wdte.Number(2)},
227+
},
228+
{
229+
name: "Simple/Method",
230+
script: `let method test r a b => [r; a; b]; 3 -> test 1 2;`,
231+
ret: wdte.Array{wdte.Number(3), wdte.Number(1), wdte.Number(2)},
232+
},
233+
{
234+
name: "Simple/RevMethod",
235+
script: `let method rev test r a b c => [r; a; b; c]; 0 -> (test 1 2) 3;`,
236+
ret: wdte.Array{wdte.Number(0), wdte.Number(3), wdte.Number(1), wdte.Number(2)},
237+
},
223238
{
224239
name: "Simple/VariableArgs",
225240
script: `let test => +; test 3 5;`,

0 commit comments

Comments
 (0)