Exemple #1
0
func (p *parser) readService(srv *ast.Service) *parseError {
	if err := p.readToken("service"); err != nil {
		return err
	}
	srv.Position = p.cur.astPosition()

	tok := p.next()
	if tok.err != nil {
		return tok.err
	}
	srv.Name = tok.value // TODO: validate

	if err := p.readToken("{"); err != nil {
		return err
	}

	// Parse methods
	for !p.done {
		tok := p.next()
		if tok.err != nil {
			return tok.err
		}
		switch tok.value {
		case "}":
			// end of service
			return nil
		case "rpc":
			// handled below
		default:
			return p.errorf(`got %q, want "rpc" or "}"`, tok.value)
		}

		tok = p.next()
		if tok.err != nil {
			return tok.err
		}
		mth := new(ast.Method)
		srv.Methods = append(srv.Methods, mth)
		mth.Position = tok.astPosition()
		mth.Name = tok.value // TODO: validate
		mth.Up = srv

		if err := p.readToken("("); err != nil {
			return err
		}

		tok = p.next()
		if tok.err != nil {
			return tok.err
		}
		mth.InTypeName = tok.value // TODO: validate
		if err := p.readToken(")"); err != nil {
			return err
		}
		if err := p.readToken("returns"); err != nil {
			return err
		}
		if err := p.readToken("("); err != nil {
			return err
		}
		tok = p.next()
		if tok.err != nil {
			return tok.err
		}
		mth.OutTypeName = tok.value // TODO: validate

		if err := p.readToken(")"); err != nil {
			return err
		}
		tok = p.next()
		if tok.err != nil {
			return tok.err
		}
		if tok.value == "{" {
			p.back()
			if err := p.readMethodOptions(mth); err != nil {
				return err
			}
		} else if tok.value != ";" {
			return p.errorf("unexpected %v while parsing Method", tok.value)
		}
	}

	return p.errorf("unexpected EOF while parsing service")
}
Exemple #2
0
func (p *parser) readFile(f *ast.File) *parseError {
	// Parse top-level things.
	for !p.done {
		tok := p.next()
		if tok.err == eof {
			break
		} else if tok.err != nil {
			return tok.err
		}
		// TODO: enforce ordering? package, imports, remainder
		switch tok.value {
		case "package":
			if f.Package != nil {
				return p.errorf("duplicate package statement")
			}
			var pkg string
			for {
				tok := p.next()
				if tok.err != nil {
					return tok.err
				}
				if tok.value == ";" {
					break
				}
				if tok.value == "." {
					// okay if we already have at least one package component,
					// and didn't just read a dot.
					if pkg == "" || strings.HasSuffix(pkg, ".") {
						return p.errorf(`got ".", want package name`)
					}
				} else {
					// okay if we don't have a package component,
					// or just read a dot.
					if pkg != "" && !strings.HasSuffix(pkg, ".") {
						return p.errorf(`got %q, want "." or ";"`, tok.value)
					}
					// TODO: validate more
				}
				pkg += tok.value
			}
			f.Package = strings.Split(pkg, ".")
		case "option":
			tok := p.next()
			if tok.err != nil {
				return tok.err
			}
			key := tok.value
			if err := p.readToken("="); err != nil {
				return err
			}
			tok = p.next()
			if tok.err != nil {
				return tok.err
			}
			value := tok.value
			if err := p.readToken(";"); err != nil {
				return err
			}
			f.Options = append(f.Options, [2]string{key, value})
		case "syntax":
			if f.Syntax != "" {
				return p.errorf("duplicate syntax statement")
			}
			if err := p.readToken("="); err != nil {
				return err
			}
			tok, err := p.readString()
			if err != nil {
				return err
			}
			switch s := tok.unquoted; s {
			case "proto2", "proto3":
				f.Syntax = s
			default:
				return p.errorf("invalid syntax value %q", s)
			}
			if err := p.readToken(";"); err != nil {
				return err
			}
		case "import":
			if err := p.readToken("public"); err == nil {
				f.PublicImports = append(f.PublicImports, len(f.Imports))
			} else {
				p.back()
			}
			tok, err := p.readString()
			if err != nil {
				return err
			}
			f.Imports = append(f.Imports, tok.unquoted)
			if err := p.readToken(";"); err != nil {
				return err
			}
		case "message":
			p.back()
			msg := new(ast.Message)
			f.Messages = append(f.Messages, msg)
			if err := p.readMessage(msg); err != nil {
				return err
			}
			msg.Up = f
		case "enum":
			p.back()
			enum := new(ast.Enum)
			f.Enums = append(f.Enums, enum)
			if err := p.readEnum(enum); err != nil {
				return err
			}
			enum.Up = f
		case "service":
			p.back()
			srv := new(ast.Service)
			f.Services = append(f.Services, srv)
			if err := p.readService(srv); err != nil {
				return err
			}
			srv.Up = f
		case "extend":
			p.back()
			ext := new(ast.Extension)
			f.Extensions = append(f.Extensions, ext)
			if err := p.readExtension(ext); err != nil {
				return err
			}
			ext.Up = f
		default:
			return p.errorf("unknown top-level thing %q", tok.value)
		}
	}

	// Handle comments.
	for len(p.comments) > 0 {
		n := 1
		for ; n < len(p.comments); n++ {
			if p.comments[n].line != p.comments[n-1].line+1 {
				break
			}
		}
		c := &ast.Comment{
			Start: ast.Position{
				Line:   p.comments[0].line,
				Offset: p.comments[0].offset,
			},
			End: ast.Position{
				Line:   p.comments[n-1].line,
				Offset: p.comments[n-1].offset,
			},
		}
		for _, comm := range p.comments[:n] {
			c.Text = append(c.Text, comm.text)
		}
		p.comments = p.comments[n:]

		// Strip common whitespace prefix and any whitespace suffix.
		// TODO: this is a bodgy implementation of Longest Common Prefix,
		// and also doesn't do tabs vs. spaces well.
		var prefix string
		for i, line := range c.Text {
			line = strings.TrimRightFunc(line, unicode.IsSpace)
			c.Text[i] = line
			trim := len(line) - len(strings.TrimLeftFunc(line, unicode.IsSpace))
			if i == 0 {
				prefix = line[:trim]
			} else {
				// Check how much of prefix is in common.
				for !strings.HasPrefix(line, prefix) {
					prefix = prefix[:len(prefix)-1]
				}
			}
			if prefix == "" {
				break
			}
		}
		if prefix != "" {
			for i, line := range c.Text {
				c.Text[i] = strings.TrimPrefix(line, prefix)
			}
		}

		f.Comments = append(f.Comments, c)
	}
	// No need to sort comments; they are already in source order.

	return nil
}