parse_decl.mx raw
1 package syntax
2
3 import (
4 "unsafe"
5 "git.smesh.lol/moxie/pkg/token"
6 )
7
8 // SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
9 func (p *Parser) fileOrNil() (f *File) {
10 if trace {
11 defer p.trace("file")()
12 }
13
14 f := (*File)(p.nodeAlloc(unsafe.Sizeof(File{})))
15 f.pos = p.pos()
16
17 // PackageClause
18 f.GoVersion = p.GoVersion
19 p.Top = false
20 if !p.got(token.Package) {
21 p.syntaxError("package statement must be first")
22 return nil
23 }
24 f.Pragma = p.takePragma()
25 f.PkgName = p.name()
26 p.want(token.Semi)
27
28 // don't bother continuing if package clause has errors
29 if p.First != nil {
30 return nil
31 }
32
33 // Accept import declarations anywhere for error tolerance, but complain.
34 // { ( ImportDecl | TopLevelDecl ) ";" }
35 prev := token.Import
36 for p.Tok != token.EOF {
37 if p.Tok == token.Import && prev != token.Import {
38 p.syntaxError("imports must appear before other declarations")
39 }
40 prev = p.Tok
41
42 switch p.Tok {
43 case token.Import:
44 p.Next()
45 f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
46
47 case token.Const:
48 p.Next()
49 f.DeclList = p.appendGroup(f.DeclList, p.constDecl)
50
51 case token.TypeType:
52 p.Next()
53 f.DeclList = p.appendGroup(f.DeclList, p.typeDecl)
54
55 case token.Var:
56 p.Next()
57 f.DeclList = p.appendGroup(f.DeclList, p.varDecl)
58
59 case token.Func:
60 p.Next()
61 if d := p.funcDeclOrNil(); d != nil {
62 push(f.DeclList, d)
63 }
64
65 default:
66 if p.Tok == token.Lbrace && len(f.DeclList) > 0 && isEmptyFuncDecl(f.DeclList[len(f.DeclList)-1]) {
67 // opening { of function declaration on next line
68 p.syntaxError("unexpected semicolon or newline before {")
69 } else {
70 p.syntaxError("non-declaration statement outside function body")
71 }
72 p.advance(token.Import, token.Const, token.TypeType, token.Var, token.Func)
73 continue
74 }
75
76 // Reset p.pragma BEFORE advancing to the next token (consuming ';')
77 // since comments before may set pragmas for the next function decl.
78 p.clearPragma()
79
80 if p.Tok != token.EOF && !p.got(token.Semi) {
81 p.syntaxError("after top level declaration")
82 p.advance(token.Import, token.Const, token.TypeType, token.Var, token.Func)
83 }
84 }
85 // p.tok == _EOF
86
87 p.clearPragma()
88 f.EOF = p.pos()
89
90 return f
91 }
92
93 func isEmptyFuncDecl(dcl Decl) (ok bool) {
94 f, ok := dcl.(*FuncDecl)
95 return ok && f.Body == nil
96 }
97
98 // ----------------------------------------------------------------------------
99 // Declarations
100
101 // list parses a possibly empty, sep-separated list of elements, optionally
102 // followed by sep, and closed by close (or EOF). sep must be one of _Comma
103 // or _Semi, and close must be one of _Rparen, _Rbrace, or _Rbrack.
104 //
105 // For each list element, f is called. Specifically, unless we're at close
106 // (or EOF), f is called at least once. After f returns true, no more list
107 // elements are accepted. list returns the position of the closing token.
108 //
109 // list = [ f { sep f } [sep] ] close .
110 func (p *Parser) list(context string, sep, closeTok token.Token, f func() bool) (pv token.Pos) {
111 if debug && (sep != token.Comma && sep != token.Semi || closeTok != token.Rparen && closeTok != token.Rbrace && closeTok != token.Rbrack) {
112 panic("invalid sep or close argument for list")
113 }
114
115 done := false
116 for p.Tok != token.EOF && p.Tok != closeTok && !done {
117 done = f()
118 // sep is optional before closeTok
119 if !p.got(sep) && p.Tok != closeTok {
120 p.syntaxError("in " | context | "; possibly missing " | tokstring(sep) | " or " | tokstring(closeTok))
121 p.advance(token.Rparen, token.Rbrack, token.Rbrace)
122 if p.Tok != closeTok {
123 // position could be better but we had an error so we don't care
124 return p.pos()
125 }
126 }
127 }
128
129 pos := p.pos()
130 p.want(closeTok)
131 return pos
132 }
133
134 // appendGroup(f) = f + "(" { f ";" } ")" . // ";" is optional before ")"
135 func (p *Parser) appendGroup(list []Decl, f func(*Group) Decl) (ds []Decl) {
136 if p.Tok == token.Lparen {
137 g := (*Group)(p.nodeAlloc(unsafe.Sizeof(Group{})))
138 p.clearPragma()
139 p.Next() // must consume "(" after calling clearPragma!
140 p.list("grouped declaration", token.Semi, token.Rparen, func() bool {
141 if x := f(g); x != nil {
142 push(list, x)
143 }
144 return false
145 })
146 } else {
147 if x := f(nil); x != nil {
148 push(list, x)
149 }
150 }
151 return list
152 }
153
154 // ImportSpec = [ "." + PackageName ] ImportPath .
155 // ImportPath = string_lit .
156 func (p *Parser) importDecl(group *Group) (ret Decl) {
157 if trace {
158 defer p.trace("importDecl")()
159 }
160
161 d := (*ImportDecl)(p.nodeAlloc(unsafe.Sizeof(ImportDecl{})))
162 d.pos = p.pos()
163 d.Group = group
164 d.Pragma = p.takePragma()
165
166 switch p.Tok {
167 case token.NameType:
168 n := p.name()
169 if n.Value == "_" {
170 p.syntaxErrorAt(n.Pos(), "blank imports are not allowed in Moxie")
171 }
172 d.LocalPkgName = n
173 case token.Dot:
174 d.LocalPkgName = NewName(p.pos(), ".")
175 p.Next()
176 }
177 d.Path = p.oliteral()
178 if d.Path == nil {
179 p.syntaxError("missing import path")
180 p.advance(token.Semi, token.Rparen)
181 return d
182 }
183 if !d.Path.Bad && d.Path.Kind != token.StringLit {
184 p.syntaxErrorAt(d.Path.Pos(), "import path must be a string")
185 d.Path.Bad = true
186 }
187 // d.Path.Bad || d.Path.Kind == StringLit
188
189 return d
190 }
191
192 // ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
193 func (p *Parser) constDecl(group *Group) (ret Decl) {
194 if trace {
195 defer p.trace("constDecl")()
196 }
197
198 d := (*ConstDecl)(p.nodeAlloc(unsafe.Sizeof(ConstDecl{})))
199 d.pos = p.pos()
200 d.Group = group
201 d.Pragma = p.takePragma()
202
203 d.NameList = p.nameList(p.name())
204 if p.Tok != token.EOF && p.Tok != token.Semi && p.Tok != token.Rparen {
205 d.Type = p.typeOrNil()
206 if p.gotAssign() {
207 d.Values = p.exprList()
208 }
209 }
210
211 return d
212 }
213
214 // TypeSpec = identifier [ TypeParams ] [ "=" ] Type .
215 func (p *Parser) typeDecl(group *Group) (ret Decl) {
216 if trace {
217 defer p.trace("typeDecl")()
218 }
219
220 d := (*TypeDecl)(p.nodeAlloc(unsafe.Sizeof(TypeDecl{})))
221 d.pos = p.pos()
222 d.Group = group
223 d.Pragma = p.takePragma()
224
225 d.Name = p.name()
226 if p.Tok == token.Lbrack {
227 // d.Name "[" ...
228 // array/slice type or type parameter list
229 pos := p.pos()
230 p.Next()
231 switch p.Tok {
232 case token.NameType:
233 // We may have an array type or a type parameter list.
234 // In either case we expect an expression x (which may
235 // just be a name, or a more complex expression) which
236 // we can analyze further.
237 //
238 // A type parameter list may have a type bound starting
239 // with a "[" as in: P []E. In that case, simply parsing
240 // an expression would lead to an error: P[] is invalid.
241 // But since index or slice expressions are never constant
242 // and thus invalid array length expressions, if the name
243 // is followed by "[" it must be the start of an array or
244 // slice constraint. Only if we don't see a "[" do we
245 // need to parse a full expression. Notably, name <- x
246 // is not a concern because name <- x is a statement and
247 // not an expression.
248 var x Expr = p.name()
249 if p.Tok != token.Lbrack {
250 // To parse the expression starting with name, expand
251 // the call sequence we would get by passing in name
252 // to parser.expr, and pass in name to parser.pexpr.
253 p.Xnest++
254 x = p.binaryExpr(p.pexpr(x, false), 0)
255 p.Xnest--
256 }
257 // Analyze expression x. If we can split x into a type parameter
258 // name, possibly followed by a type parameter type, we consider
259 // this the start of a type parameter list, with some caveats:
260 // a single name followed by "]" tilts the decision towards an
261 // array declaration; a type parameter type that could also be
262 // an ordinary expression but which is followed by a comma tilts
263 // the decision towards a type parameter list.
264 if pname, ptype := extractName(x, p.Tok == token.Comma); pname != nil && (ptype != nil || p.Tok != token.Rbrack) {
265 // d.Name "[" pname ...
266 // d.Name "[" pname ptype ...
267 // d.Name "[" pname ptype "," ...
268 d.TParamList = p.paramList(pname, ptype, token.Rbrack, true, false) // ptype may be nil
269 d.Alias = p.gotAssign()
270 d.Type = p.typeOrNil()
271 } else {
272 // d.Name "[" pname "]" ...
273 // d.Name "[" x ...
274 d.Type = p.arrayType(pos, x)
275 }
276 case token.Rbrack:
277 // d.Name "[" "]" ...
278 p.Next()
279 d.Type = p.sliceType(pos)
280 default:
281 // d.Name "[" ...
282 d.Type = p.arrayType(pos, nil)
283 }
284 } else {
285 d.Alias = p.gotAssign()
286 d.Type = p.typeOrNil()
287 }
288
289 if d.Type == nil {
290 d.Type = p.badExpr()
291 p.syntaxError("in type declaration")
292 p.advance(token.Semi, token.Rparen)
293 }
294
295 return d
296 }
297
298 // extractName splits the expression x into (name, expr) if syntactically
299 // x can be written as name expr. The split only happens if expr is a type
300 // element (per the isTypeElem predicate) or if force is set.
301 // If x is just a name, the result is (name, nil). If the split succeeds,
302 // the result is (name, expr). Otherwise the result is (nil, x).
303 func extractName(x Expr, force bool) (nm *Name, ex Expr) {
304 switch xn := x.(type) {
305 case *Name:
306 return xn, nil
307 case *Operation:
308 if xn.Y == nil {
309 break // unary expr
310 }
311 switch xn.Op {
312 case token.Mul:
313 if name, _ := xn.X.(*Name); name != nil && (force || isTypeElem(xn.Y)) {
314 // xn = name *xn.Y
315 op := *xn
316 op.X, op.Y = op.Y, nil // change op into unary *op.Y
317 return name, &op
318 }
319 case token.Or:
320 if name, lhs := extractName(xn.X, force || isTypeElem(xn.Y)); name != nil && lhs != nil {
321 // xn = name lhs|xn.Y
322 op := *xn
323 op.X = lhs
324 return name, &op
325 }
326 }
327 case *CallExpr:
328 if name, _ := xn.Fun.(*Name); name != nil {
329 if len(xn.ArgList) == 1 && !xn.HasDots && (force || isTypeElem(xn.ArgList[0])) {
330 // The parser doesn't keep unnecessary parentheses.
331 // Set the flag below to keep them, for testing
332 // (see go.dev/issues/69206).
333 const keep_parens = false
334 if keep_parens {
335 // xn = name (xn.ArgList[0])
336 px := &ParenExpr{} // dead code (keep_parens = false)
337 px.pos = xn.pos // position of "(" in call
338 px.X = xn.ArgList[0]
339 return name, px
340 } else {
341 // xn = name xn.ArgList[0]
342 return name, Unparen(xn.ArgList[0])
343 }
344 }
345 }
346 }
347 return nil, x
348 }
349
350 // isTypeElem reports whether x is a (possibly parenthesized) type element expression.
351 // The result is false if x could be a type element OR an ordinary (value) expression.
352 func isTypeElem(x Expr) (ok bool) {
353 switch xn := x.(type) {
354 case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType:
355 return true
356 case *Operation:
357 return isTypeElem(xn.X) || (xn.Y != nil && isTypeElem(xn.Y)) || xn.Op == token.Tilde
358 case *ParenExpr:
359 return isTypeElem(xn.X)
360 }
361 return false
362 }
363
364 // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] + "=" ExpressionList ) .
365 func (p *Parser) varDecl(group *Group) (ret Decl) {
366 if trace {
367 defer p.trace("varDecl")()
368 }
369
370 d := (*VarDecl)(p.nodeAlloc(unsafe.Sizeof(VarDecl{})))
371 d.pos = p.pos()
372 d.Group = group
373 d.Pragma = p.takePragma()
374
375 d.NameList = p.nameList(p.name())
376 if p.gotAssign() {
377 d.Values = p.exprList()
378 } else {
379 d.Type = p.type_()
380 if p.gotAssign() {
381 d.Values = p.exprList()
382 }
383 }
384
385 return d
386 }
387
388 // FunctionDecl = "func" FunctionName [ TypeParams ] ( Function | Signature ) .
389 // FunctionName = identifier .
390 // Function = Signature FunctionBody .
391 // MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
392 // Receiver = Parameters .
393 func (p *Parser) funcDeclOrNil() (f *FuncDecl) {
394 if trace {
395 defer p.trace("funcDecl")()
396 }
397
398 f := (*FuncDecl)(p.nodeAlloc(unsafe.Sizeof(FuncDecl{})))
399 f.pos = p.pos()
400 f.Pragma = p.takePragma()
401
402 var context string
403 if p.got(token.Lparen) {
404 context = "method"
405 rcvr := p.paramList(nil, nil, token.Rparen, false, false)
406 switch len(rcvr) {
407 case 0:
408 p.error("method has no receiver")
409 default:
410 p.error("method has multiple receivers")
411 f.Recv = rcvr[0]
412 case 1:
413 f.Recv = rcvr[0]
414 }
415 }
416
417 if p.Tok == token.NameType {
418 f.Name = p.name()
419 f.TParamList, f.Type = p.funcType(context)
420 } else {
421 f.Name = NewName(p.pos(), "_")
422 f.Type = (*FuncType)(p.nodeAlloc(unsafe.Sizeof(FuncType{})))
423 f.Type.pos = p.pos()
424 msg := "expected name or ("
425 if context != "" {
426 msg = "expected name"
427 }
428 p.syntaxError(msg)
429 p.advance(token.Lbrace, token.Semi)
430 }
431
432 if p.Tok == token.Lbrace {
433 f.Body = p.funcBody()
434 }
435
436 return f
437 }
438
439 func (p *Parser) funcBody() (b *BlockStmt) {
440 p.Fnest++
441 body := p.blockStmt("")
442 p.Fnest--
443
444 return body
445 }
446