mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
144 lines
3.1 KiB
Go
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
|
|
}
|