Files
snapd/spdx/parser.go
2021-08-26 18:24:02 +01:00

144 lines
3.1 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2016 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package spdx
import (
"fmt"
"io"
)
const (
opAND = "AND"
opOR = "OR"
opWITH = "WITH"
)
func isOperator(tok string) bool {
return tok == opAND || tok == opOR || tok == opWITH
}
type licenseID string
func newLicenseID(s string) (licenseID, error) {
needle := s
for _, known := range allLicenses {
if needle == known {
return licenseID(s), nil
}
}
return "", fmt.Errorf("unknown license: %s", s)
}
type licenseExceptionID string
func newLicenseExceptionID(s string) (licenseExceptionID, error) {
for _, known := range licenseExceptions {
if s == known {
return licenseExceptionID(s), nil
}
}
return "", fmt.Errorf("unknown license exception: %s", s)
}
type parser struct {
s *Scanner
}
func newParser(r io.Reader) *parser {
return &parser{s: NewScanner(r)}
}
func (p *parser) Validate() error {
return p.validate(0)
}
func (p *parser) validate(depth int) error {
last := ""
for p.s.Scan() {
tok := p.s.Text()
switch {
case tok == "(":
if last == opWITH {
return fmt.Errorf("%q not allowed after WITH", tok)
}
if err := p.validate(depth + 1); err != nil {
return err
}
if p.s.Text() != ")" {
return fmt.Errorf(`expected ")" got %q`, p.s.Text())
}
case tok == ")":
if depth == 0 {
return fmt.Errorf(`unexpected ")"`)
}
if last == "" {
return fmt.Errorf("empty expression")
}
return nil
case isOperator(tok):
if last == "" {
return fmt.Errorf("missing license before %s", tok)
}
if last == opAND || last == opOR {
return fmt.Errorf("expected license name, got %q", tok)
}
if tok == opWITH && last == "(" {
return fmt.Errorf("expected license name before %s", tok)
}
if last == opWITH {
return fmt.Errorf("expected exception name, got %q", tok)
}
default:
switch {
case last == opWITH:
if _, err := newLicenseExceptionID(tok); err != nil {
return err
}
case last == "", last == opAND, last == opOR:
if _, err := newLicenseID(tok); err != nil {
return err
}
default:
if _, err := newLicenseID(last); err == nil {
if _, err := newLicenseID(tok); err == nil {
return fmt.Errorf("missing AND or OR between %q and %q", last, tok)
}
}
return fmt.Errorf("unexpected string: %q", tok)
}
}
last = tok
}
if err := p.s.Err(); err != nil {
return err
}
if isOperator(last) {
return fmt.Errorf("missing license after %s", last)
}
if last == "" {
return fmt.Errorf("empty expression")
}
return nil
}