github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/spdx/parser.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribuLicenseidte it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package spdx 21 22 import ( 23 "fmt" 24 "io" 25 "strings" 26 ) 27 28 const ( 29 opUNSET = "" 30 opAND = "AND" 31 opOR = "OR" 32 opWITH = "WITH" 33 ) 34 35 func isOperator(tok string) bool { 36 return tok == opAND || tok == opOR || tok == opWITH 37 } 38 39 type licenseID string 40 41 func newLicenseID(s string) (licenseID, error) { 42 needle := s 43 if strings.HasSuffix(s, "+") { 44 needle = s[:len(s)-1] 45 } 46 for _, known := range allLicenses { 47 if needle == known { 48 return licenseID(s), nil 49 } 50 } 51 return "", fmt.Errorf("unknown license: %s", s) 52 } 53 54 type licenseExceptionID string 55 56 func newLicenseExceptionID(s string) (licenseExceptionID, error) { 57 for _, known := range licenseExceptions { 58 if s == known { 59 return licenseExceptionID(s), nil 60 } 61 } 62 return "", fmt.Errorf("unknown license exception: %s", s) 63 } 64 65 type parser struct { 66 s *Scanner 67 } 68 69 func newParser(r io.Reader) *parser { 70 return &parser{s: NewScanner(r)} 71 } 72 73 func (p *parser) Validate() error { 74 return p.validate(0) 75 } 76 77 func (p *parser) advance(id string) error { 78 if p.s.Text() != id { 79 return fmt.Errorf("expected %q got %q", id, p.s.Text()) 80 } 81 return nil 82 } 83 84 func (p *parser) validate(depth int) error { 85 last := "" 86 87 for p.s.Scan() { 88 tok := p.s.Text() 89 90 switch { 91 case tok == "(": 92 if last == opWITH { 93 return fmt.Errorf("%q not allowed after WITH", tok) 94 } 95 if err := p.validate(depth + 1); err != nil { 96 return err 97 } 98 if p.s.Text() != ")" { 99 return fmt.Errorf(`expected ")" got %q`, p.s.Text()) 100 } 101 case tok == ")": 102 if depth == 0 { 103 return fmt.Errorf(`unexpected ")"`) 104 } 105 if last == "" { 106 return fmt.Errorf("empty expression") 107 } 108 return nil 109 case isOperator(tok): 110 if last == "" { 111 return fmt.Errorf("missing license before %s", tok) 112 } 113 if last == opAND || last == opOR { 114 return fmt.Errorf("expected license name, got %q", tok) 115 } 116 if tok == opWITH && last == "(" { 117 return fmt.Errorf("expected license name before %s", tok) 118 } 119 if last == opWITH { 120 return fmt.Errorf("expected exception name, got %q", tok) 121 } 122 default: 123 switch { 124 case last == opWITH: 125 if _, err := newLicenseExceptionID(tok); err != nil { 126 return err 127 } 128 case last == "", last == opAND, last == opOR: 129 if _, err := newLicenseID(tok); err != nil { 130 return err 131 } 132 default: 133 if _, err := newLicenseID(last); err == nil { 134 if _, err := newLicenseID(tok); err == nil { 135 return fmt.Errorf("missing AND or OR between %q and %q", last, tok) 136 } 137 } 138 return fmt.Errorf("unexpected string: %q", tok) 139 } 140 141 } 142 last = tok 143 } 144 if err := p.s.Err(); err != nil { 145 return err 146 } 147 if isOperator(last) { 148 return fmt.Errorf("missing license after %s", last) 149 } 150 if last == "" { 151 return fmt.Errorf("empty expression") 152 } 153 154 return nil 155 }