From 85ae7fc44b3db50972bebdf211e0f44de56892b4 Mon Sep 17 00:00:00 2001 From: Ernest Micklei Date: Wed, 18 Dec 2024 13:23:41 +0100 Subject: [PATCH] add Edition support (#147) * add Edition support * rm VisitEdition to be backward compat * update hist * add git build * add build badge * build for each pull --- .github/workflows/go.yml | 29 ++++++++++++++++++ CHANGES.md | 4 +++ README.md | 1 + edition.go | 65 ++++++++++++++++++++++++++++++++++++++++ edition_test.go | 39 ++++++++++++++++++++++++ noop_visitor.go | 5 ++++ parent_accessor.go | 3 ++ parent_test.go | 7 ++++- proto.go | 8 +++++ token.go | 3 ++ visitor.go | 3 +- 11 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/go.yml create mode 100644 edition.go create mode 100644 edition_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..733da72 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,29 @@ +# This workflow will test a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "master" ] + pull_request: + branches: + - '*' # matches every branch that doesn't contain a '/' + - '*/*' # matches every branch containing a single '/' + - '**' # matches every branch + - '!master' # excludes master + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.23' + + - name: Test + run: go test -v -cover ./... \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index d586b94..a33b1be 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## v1.14.0 (2024-12-18) + +- parse edition element (PR #147, ISSUE #145) + ## v1.13.4 (2024-12-17) - fixed handling identifiers known as numbers by scanner (PR #146) diff --git a/README.md b/README.md index c99a320..366f1f3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # proto +[![Go](https://github.com/emicklei/proto/actions/workflows/go.yml/badge.svg)](https://github.com/emicklei/proto/actions/workflows/go.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/emicklei/proto)](https://goreportcard.com/report/github.com/emicklei/proto) [![GoDoc](https://pkg.go.dev/badge/github.com/emicklei/proto)](https://pkg.go.dev/github.com/emicklei/proto) [![codecov](https://codecov.io/gh/emicklei/proto/branch/master/graph/badge.svg)](https://codecov.io/gh/emicklei/proto) diff --git a/edition.go b/edition.go new file mode 100644 index 0000000..279d733 --- /dev/null +++ b/edition.go @@ -0,0 +1,65 @@ +// Copyright (c) 2024 Ernest Micklei +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package proto + +import ( + "text/scanner" +) + +type Edition struct { + Position scanner.Position + Comment *Comment + Value string + InlineComment *Comment + Parent Visitee +} + +func (e *Edition) parse(p *Parser) error { + if _, tok, lit := p.next(); tok != tEQUALS { + return p.unexpected(lit, "edition =", e) + } + _, _, lit := p.next() + if !isString(lit) { + return p.unexpected(lit, "edition string constant", e) + } + e.Value, _ = unQuote(lit) + return nil +} + +// Accept dispatches the call to the visitor. +func (e *Edition) Accept(v Visitor) { + // v.VisitEdition(e) in v2 +} + +// Doc is part of Documented +func (e *Edition) Doc() *Comment { + return e.Comment +} + +// inlineComment is part of commentInliner. +func (e *Edition) inlineComment(c *Comment) { + e.InlineComment = c +} + +func (e *Edition) parent(v Visitee) { e.Parent = v } diff --git a/edition_test.go b/edition_test.go new file mode 100644 index 0000000..25d5fb6 --- /dev/null +++ b/edition_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2024 Ernest Micklei +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package proto + +import "testing" + +func TestEdition(t *testing.T) { + proto := `edition = "1967";` + p := newParserOn(proto) + pr, err := p.Parse() + if err != nil { + t.Fatal(err) + } + e := pr.elements()[0].(*Edition) + if got, want := e.Value, "1967"; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} diff --git a/noop_visitor.go b/noop_visitor.go index df4cf8d..ffb1ac8 100644 --- a/noop_visitor.go +++ b/noop_visitor.go @@ -23,6 +23,8 @@ package proto +var _ Visitor = NoopVisitor{} + // NoopVisitor is a no-operation visitor that can be used when creating your own visitor that is interested in only one or a few types. // It implements the Visitor interface. type NoopVisitor struct{} @@ -36,6 +38,9 @@ func (n NoopVisitor) VisitService(v *Service) {} // VisitSyntax is part of Visitor interface func (n NoopVisitor) VisitSyntax(s *Syntax) {} +// VisitSyntax is part of Visitor interface +func (n NoopVisitor) VisitEdition(e *Edition) {} + // VisitPackage is part of Visitor interface func (n NoopVisitor) VisitPackage(p *Package) {} diff --git a/parent_accessor.go b/parent_accessor.go index f85eb5e..8f3130b 100644 --- a/parent_accessor.go +++ b/parent_accessor.go @@ -85,4 +85,7 @@ func (p *parentAccessor) VisitGroup(g *Group) { func (p *parentAccessor) VisitExtensions(e *Extensions) { p.parent = e.Parent } +func (p *parentAccessor) VisitEdition(e *Edition) { + p.parent = e.Parent +} func (p *parentAccessor) VisitProto(*Proto) {} diff --git a/parent_test.go b/parent_test.go index cc986b6..ddb1313 100644 --- a/parent_test.go +++ b/parent_test.go @@ -1,6 +1,6 @@ // Copyright (c) 2018 Ernest Micklei // -// MIT License +// # MIT License // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -122,3 +122,8 @@ func (pc *parentChecker) VisitGroup(g *Group) { func (pc *parentChecker) VisitExtensions(e *Extensions) { pc.check("Extensions", "", e.Parent) } + +// edition (proto3+) +func (pc *parentChecker) VisitEdition(e *Edition) { + pc.check("Edition", "", e.Parent) +} diff --git a/proto.go b/proto.go index 2bbdb29..95d52ed 100644 --- a/proto.go +++ b/proto.go @@ -81,6 +81,14 @@ func (proto *Proto) parse(p *Parser) error { return err } proto.addElement(s) + case tEDITION == tok: + s := new(Edition) + s.Position = pos + s.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) + if err := s.parse(p); err != nil { + return err + } + proto.addElement(s) case tIMPORT == tok: im := new(Import) im.Position = pos diff --git a/token.go b/token.go index d1f59c9..65907c2 100644 --- a/token.go +++ b/token.go @@ -60,6 +60,7 @@ const ( // Keywords keywordsStart + tEDITION tSYNTAX tSERVICE tRPC @@ -192,6 +193,8 @@ func asToken(literal string) token { // words case "syntax": return tSYNTAX + case "edition": + return tEDITION case "service": return tSERVICE case "rpc": diff --git a/visitor.go b/visitor.go index be0850a..e62c56a 100644 --- a/visitor.go +++ b/visitor.go @@ -25,7 +25,6 @@ package proto // Visitor is for dispatching Proto elements. type Visitor interface { - //VisitProto(p *Proto) VisitMessage(m *Message) VisitService(v *Service) VisitSyntax(s *Syntax) @@ -44,6 +43,8 @@ type Visitor interface { // proto2 VisitGroup(g *Group) VisitExtensions(e *Extensions) + // edition (proto3+), v2 + // VisitEdition(e *Edition) } // Visitee is implemented by all Proto elements.