Skip to content

Commit 77b8b9e

Browse files
sbarzowskisparkprime
authored andcommitted
Optional args parser/lexer/ast support (google#33)
* Optional args parser/lexer/ast support Just "frontend" part. They are ignored during actual execution.
1 parent 5cd467f commit 77b8b9e

File tree

10 files changed

+146
-67
lines changed

10 files changed

+146
-67
lines changed

ast/ast.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,19 @@ type CompSpecs []CompSpec
9595
type Apply struct {
9696
NodeBase
9797
Target Node
98-
Arguments Nodes
98+
Arguments Arguments
9999
TrailingComma bool
100100
TailStrict bool
101-
// TODO(sbarzowski) support named arguments
101+
}
102+
103+
type NamedArgument struct {
104+
Name Identifier
105+
Arg Node
106+
}
107+
108+
type Arguments struct {
109+
Positional Nodes
110+
Named []NamedArgument
102111
}
103112

104113
// ---------------------------------------------------------------------------
@@ -277,11 +286,21 @@ type Error struct {
277286
// Function represents a function definition
278287
type Function struct {
279288
NodeBase
280-
Parameters Identifiers // TODO(sbarzowski) support default arguments
289+
Parameters Parameters
281290
TrailingComma bool
282291
Body Node
283292
}
284293

294+
type NamedParameter struct {
295+
Name Identifier
296+
DefaultArg Node
297+
}
298+
299+
type Parameters struct {
300+
Positional Identifiers
301+
Named []NamedParameter
302+
}
303+
285304
// ---------------------------------------------------------------------------
286305

287306
// Import represents import "file".
@@ -328,7 +347,7 @@ type LocalBind struct {
328347
Variable Identifier
329348
Body Node
330349
FunctionSugar bool
331-
Params Identifiers // if functionSugar is true
350+
Params *Parameters // if functionSugar is true
332351
TrailingComma bool
333352
}
334353
type LocalBinds []LocalBind
@@ -413,19 +432,19 @@ type ObjectField struct {
413432
MethodSugar bool // f(x, y, z): ... (ignore if kind == astObjectAssert)
414433
Expr1 Node // Not in scope of the object
415434
Id *Identifier
416-
Ids Identifiers // If methodSugar == true then holds the params.
435+
Params *Parameters // If methodSugar == true then holds the params.
417436
TrailingComma bool // If methodSugar == true then remembers the trailing comma
418437
Expr2, Expr3 Node // In scope of the object (can see self).
419438
}
420439

421440
// TODO(jbeda): Add the remaining constructor helpers here
422441

423-
func ObjectFieldLocal(methodSugar bool, id *Identifier, ids Identifiers, trailingComma bool, body Node) ObjectField {
424-
return ObjectField{ObjectLocal, ObjectFieldVisible, false, methodSugar, nil, id, ids, trailingComma, body, nil}
442+
func ObjectFieldLocal(methodSugar bool, id *Identifier, params *Parameters, trailingComma bool, body Node) ObjectField {
443+
return ObjectField{ObjectLocal, ObjectFieldVisible, false, methodSugar, nil, id, params, trailingComma, body, nil}
425444
}
426445

427446
func ObjectFieldLocalNoMethod(id *Identifier, body Node) ObjectField {
428-
return ObjectField{ObjectLocal, ObjectFieldVisible, false, false, nil, id, Identifiers{}, false, body, nil}
447+
return ObjectField{ObjectLocal, ObjectFieldVisible, false, false, nil, id, nil, false, body, nil}
429448
}
430449

431450
type ObjectFields []ObjectField

desugarer.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ func desugarFields(location ast.LocationRange, fields *ast.ObjectFields, objLeve
143143
function := &ast.Function{
144144
// TODO(sbarzowski) better location
145145
NodeBase: ast.NewNodeBaseLoc(*origBody.Loc()),
146-
Parameters: field.Ids,
146+
Parameters: *field.Params,
147147
Body: origBody,
148148
}
149149
field.MethodSugar = false
150-
field.Ids = nil
150+
field.Params = nil
151151
field.Expr2 = function
152152
}
153153

@@ -247,7 +247,7 @@ func buildStdCall(builtinName ast.Identifier, args ...ast.Node) ast.Node {
247247
builtin := buildSimpleIndex(std, builtinName)
248248
return &ast.Apply{
249249
Target: builtin,
250-
Arguments: args,
250+
Arguments: ast.Arguments{Positional: args},
251251
}
252252
}
253253

@@ -269,8 +269,8 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
269269
switch node := node.(type) {
270270
case *ast.Apply:
271271
desugar(&node.Target, objLevel)
272-
for i := range node.Arguments {
273-
err = desugar(&node.Arguments[i], objLevel)
272+
for i := range node.Arguments.Positional {
273+
err = desugar(&node.Arguments.Positional[i], objLevel)
274274
if err != nil {
275275
return
276276
}
@@ -421,7 +421,7 @@ func desugar(astPtr *ast.Node, objLevel int) (err error) {
421421
function := &ast.Function{
422422
// TODO(sbarzowski) better location
423423
NodeBase: ast.NewNodeBaseLoc(*origBody.Loc()),
424-
Parameters: node.Binds[i].Params,
424+
Parameters: *node.Binds[i].Params,
425425
Body: origBody,
426426
}
427427
node.Binds[i] = ast.LocalBind{

interpreter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,9 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
439439
argEnv := i.getCurrentEnv(a)
440440

441441
arguments := callArguments{
442-
positional: make([]potentialValue, len(ast.Arguments)),
442+
positional: make([]potentialValue, len(ast.Arguments.Positional)),
443443
}
444-
for i, arg := range ast.Arguments {
444+
for i, arg := range ast.Arguments.Positional {
445445
// TODO(sbarzowski) better thunk name
446446
arguments.positional[i] = makeThunk("arg", argEnv, arg)
447447
}

main_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ func TestMain(t *testing.T) {
105105
if bytes.Compare(golden, []byte(output)) != 0 {
106106
// TODO(sbarzowski) better reporting of differences in whitespace
107107
// missing newline issues can be very subtle now
108-
t.Errorf("%#v %#v\n", golden, []byte(output))
109108
t.Fail()
110109
t.Errorf("Mismatch when running %s.input. Golden: %s\n", test.name, test.golden)
111110
data := diff(output, string(golden))

parser/parser.go

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -115,54 +115,99 @@ func (p *parser) peek() *token {
115115
return &p.t[p.currT]
116116
}
117117

118-
func (p *parser) parseIdentifierList(elementKind string) (ast.Identifiers, bool, error) {
119-
_, exprs, gotComma, err := p.parseCommaList(tokenParenR, elementKind)
120-
if err != nil {
121-
return ast.Identifiers{}, false, err
118+
func (p *parser) doublePeek() *token {
119+
return &p.t[p.currT+1]
120+
}
121+
122+
// in some cases it's convenient to parse something as an expression, and later
123+
// decide that it should be just an identifer
124+
func astVarToIdentifier(node ast.Node) (*ast.Identifier, bool) {
125+
v, ok := node.(*ast.Var)
126+
if ok {
127+
return &v.Id, true
122128
}
123-
var ids ast.Identifiers
124-
for _, n := range exprs {
125-
v, ok := n.(*ast.Var)
126-
if !ok {
127-
return ast.Identifiers{}, false, MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *n.Loc())
128-
}
129-
ids = append(ids, v.Id)
129+
return nil, false
130+
}
131+
132+
func (p *parser) parseArgument() (*ast.Identifier, ast.Node, error) {
133+
var id *ast.Identifier
134+
if p.peek().kind == tokenIdentifier && p.doublePeek().kind == tokenOperator && p.doublePeek().data == "=" {
135+
ident := p.pop()
136+
var tmpID = ast.Identifier(ident.data)
137+
id = &tmpID
138+
p.pop() // "=" token
139+
}
140+
expr, err := p.parse(maxPrecedence)
141+
if err != nil {
142+
return nil, nil, err
130143
}
131-
return ids, gotComma, nil
144+
return id, expr, nil
132145
}
133146

134-
func (p *parser) parseCommaList(end tokenKind, elementKind string) (*token, ast.Nodes, bool, error) {
135-
var exprs ast.Nodes
147+
// TODO(sbarzowski) - this returned bool is weird
148+
// TODO(sbarzowski) - name - it's also used for parameters
149+
func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, bool, error) {
150+
args := &ast.Arguments{}
136151
gotComma := false
152+
namedArgumentAdded := false
137153
first := true
138154
for {
139155
next := p.peek()
140-
if !first && !gotComma {
141-
if next.kind == tokenComma {
142-
p.pop()
143-
next = p.peek()
144-
gotComma = true
145-
}
146-
}
147-
if next.kind == end {
156+
157+
if next.kind == tokenParenR {
148158
// gotComma can be true or false here.
149-
return p.pop(), exprs, gotComma, nil
159+
return p.pop(), args, gotComma, nil
150160
}
151161

152162
if !first && !gotComma {
153-
return nil, nil, false, MakeStaticError(fmt.Sprintf("Expected a comma before next %s.", elementKind), next.loc)
163+
return nil, nil, false, MakeStaticError(fmt.Sprintf("Expected a comma before next %s, got %s.", elementKind, next), next.loc)
154164
}
155165

156-
expr, err := p.parse(maxPrecedence)
166+
id, expr, err := p.parseArgument()
157167
if err != nil {
158168
return nil, nil, false, err
159169
}
160-
exprs = append(exprs, expr)
161-
gotComma = false
170+
if id == nil {
171+
if namedArgumentAdded {
172+
return nil, nil, false, MakeStaticError("Positional argument after a named argument is not allowed", next.loc)
173+
}
174+
args.Positional = append(args.Positional, expr)
175+
} else {
176+
namedArgumentAdded = true
177+
args.Named = append(args.Named, ast.NamedArgument{Name: *id, Arg: expr})
178+
}
179+
180+
if p.peek().kind == tokenComma {
181+
p.pop()
182+
gotComma = true
183+
} else {
184+
gotComma = false
185+
}
186+
162187
first = false
163188
}
164189
}
165190

191+
// TODO(sbarzowski) - this returned bool is weird
192+
func (p *parser) parseParameters(elementKind string) (*ast.Parameters, bool, error) {
193+
_, args, trailingComma, err := p.parseArguments(elementKind)
194+
if err != nil {
195+
return nil, false, err
196+
}
197+
var params ast.Parameters
198+
for _, arg := range args.Positional {
199+
id, ok := astVarToIdentifier(arg)
200+
if !ok {
201+
return nil, false, MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *arg.Loc())
202+
}
203+
params.Positional = append(params.Positional, *id)
204+
}
205+
for _, arg := range args.Named {
206+
params.Named = append(params.Named, ast.NamedParameter{Name: arg.Name, DefaultArg: arg.Arg})
207+
}
208+
return &params, trailingComma, nil
209+
}
210+
166211
func (p *parser) parseBind(binds *ast.LocalBinds) error {
167212
varID, err := p.popExpect(tokenIdentifier)
168213
if err != nil {
@@ -176,7 +221,7 @@ func (p *parser) parseBind(binds *ast.LocalBinds) error {
176221

177222
if p.peek().kind == tokenParenL {
178223
p.pop()
179-
params, gotComma, err := p.parseIdentifierList("function parameter")
224+
params, gotComma, err := p.parseParameters("function parameter")
180225
if err != nil {
181226
return err
182227
}
@@ -373,11 +418,11 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) {
373418

374419
isMethod := false
375420
methComma := false
376-
var params ast.Identifiers
421+
var params *ast.Parameters
377422
if p.peek().kind == tokenParenL {
378423
p.pop()
379424
var err error
380-
params, methComma, err = p.parseIdentifierList("method parameter")
425+
params, methComma, err = p.parseParameters("method parameter")
381426
if err != nil {
382427
return nil, nil, err
383428
}
@@ -413,7 +458,7 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) {
413458
MethodSugar: isMethod,
414459
Expr1: expr1,
415460
Id: id,
416-
Ids: params,
461+
Params: params,
417462
TrailingComma: methComma,
418463
Expr2: body,
419464
})
@@ -432,11 +477,11 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) {
432477

433478
isMethod := false
434479
funcComma := false
435-
var params ast.Identifiers
480+
var params *ast.Parameters
436481
if p.peek().kind == tokenParenL {
437482
p.pop()
438483
isMethod = true
439-
params, funcComma, err = p.parseIdentifierList("function parameter")
484+
params, funcComma, err = p.parseParameters("function parameter")
440485
if err != nil {
441486
return nil, nil, err
442487
}
@@ -459,7 +504,7 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) {
459504
SuperSugar: false,
460505
MethodSugar: isMethod,
461506
Id: &id,
462-
Ids: params,
507+
Params: params,
463508
TrailingComma: funcComma,
464509
Expr2: body,
465510
})
@@ -830,7 +875,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) {
830875
p.pop()
831876
next := p.pop()
832877
if next.kind == tokenParenL {
833-
params, gotComma, err := p.parseIdentifierList("function parameter")
878+
params, gotComma, err := p.parseParameters("function parameter")
834879
if err != nil {
835880
return nil, err
836881
}
@@ -840,7 +885,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) {
840885
}
841886
return &ast.Function{
842887
NodeBase: ast.NewNodeBaseLoc(locFromTokenAST(begin, body)),
843-
Parameters: params,
888+
Parameters: *params,
844889
TrailingComma: gotComma,
845890
Body: body,
846891
}, nil
@@ -1048,7 +1093,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) {
10481093
Id: &id,
10491094
}
10501095
case tokenParenL:
1051-
end, args, gotComma, err := p.parseCommaList(tokenParenR, "function argument")
1096+
end, args, gotComma, err := p.parseArguments("function argument")
10521097
if err != nil {
10531098
return nil, err
10541099
}
@@ -1060,7 +1105,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) {
10601105
lhs = &ast.Apply{
10611106
NodeBase: ast.NewNodeBaseLoc(locFromTokens(begin, end)),
10621107
Target: lhs,
1063-
Arguments: args,
1108+
Arguments: *args,
10641109
TrailingComma: gotComma,
10651110
TailStrict: tailStrict,
10661111
}

0 commit comments

Comments
 (0)