github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/erlang/erlang_parser.go (about) 1 package erlang 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strings" 8 "unicode" 9 ) 10 11 type erlangNode struct { 12 value interface{} 13 } 14 15 func (e erlangNode) Slice() []erlangNode { 16 out, ok := e.value.([]erlangNode) 17 if ok { 18 return out 19 } 20 return []erlangNode{} 21 } 22 23 func (e erlangNode) String() string { 24 out, ok := e.value.(string) 25 if ok { 26 return out 27 } 28 return "" 29 } 30 31 func (e erlangNode) Get(index int) erlangNode { 32 s := e.Slice() 33 if len(s) > index { 34 return s[index] 35 } 36 return erlangNode{} 37 } 38 39 func node(value interface{}) erlangNode { 40 return erlangNode{ 41 value: value, 42 } 43 } 44 45 // parseErlang basic parser for erlang, used by rebar.lock 46 func parseErlang(reader io.Reader) (erlangNode, error) { 47 data, err := io.ReadAll(reader) 48 if err != nil { 49 return node(nil), err 50 } 51 52 out := erlangNode{ 53 value: []erlangNode{}, 54 } 55 56 i := 0 57 for i < len(data) { 58 item, err := parseErlangBlock(data, &i) 59 if err != nil { 60 return node(nil), fmt.Errorf("%w\n%s", err, printError(data, i)) 61 } 62 63 skipWhitespace(data, &i) 64 65 if i, ok := item.value.(string); ok && i == "." { 66 continue 67 } 68 69 out.value = append(out.value.([]erlangNode), item) 70 } 71 return out, nil 72 } 73 74 func printError(data []byte, i int) string { 75 line := 1 76 char := 1 77 78 prev := []string{} 79 curr := bytes.Buffer{} 80 81 for idx, c := range data { 82 if c == '\n' { 83 prev = append(prev, curr.String()) 84 curr.Reset() 85 86 if idx >= i { 87 break 88 } 89 90 line++ 91 char = 1 92 continue 93 } 94 if idx < i { 95 char++ 96 } 97 curr.WriteByte(c) 98 } 99 100 l1 := fmt.Sprintf("%d", line-1) 101 l2 := fmt.Sprintf("%d", line) 102 103 if len(l1) < len(l2) { 104 l1 = " " + l1 105 } 106 107 sep := ": " 108 109 lines := "" 110 if len(prev) > 1 { 111 lines += fmt.Sprintf("%s%s%s\n", l1, sep, prev[len(prev)-2]) 112 } 113 if len(prev) > 0 { 114 lines += fmt.Sprintf("%s%s%s\n", l2, sep, prev[len(prev)-1]) 115 } 116 117 pointer := strings.Repeat(" ", len(l2)+len(sep)+char-1) + "^" 118 119 return fmt.Sprintf("line: %v, char: %v\n%s%s", line, char, lines, pointer) 120 } 121 122 func skipWhitespace(data []byte, i *int) { 123 for *i < len(data) && isWhitespace(data[*i]) { 124 *i++ 125 } 126 } 127 128 func parseErlangBlock(data []byte, i *int) (erlangNode, error) { 129 block, err := parseErlangNode(data, i) 130 if err != nil { 131 return node(nil), err 132 } 133 134 skipWhitespace(data, i) 135 *i++ // skip the trailing . 136 return block, nil 137 } 138 139 func parseErlangNode(data []byte, i *int) (erlangNode, error) { 140 skipWhitespace(data, i) 141 c := data[*i] 142 switch c { 143 case '[', '{': 144 return parseErlangList(data, i) 145 case '"': 146 return parseErlangString(data, i) 147 case '<': 148 return parseErlangAngleString(data, i) 149 } 150 151 if isLiteral(c) { 152 return parseErlangLiteral(data, i) 153 } 154 155 return erlangNode{}, fmt.Errorf("invalid literal character: %s", string(c)) 156 } 157 158 func isWhitespace(c byte) bool { 159 return unicode.IsSpace(rune(c)) 160 } 161 162 func isLiteral(c byte) bool { 163 r := rune(c) 164 return unicode.IsNumber(r) || unicode.IsLetter(r) || r == '.' || r == '_' 165 } 166 167 func parseErlangLiteral(data []byte, i *int) (erlangNode, error) { 168 var buf bytes.Buffer 169 for *i < len(data) { 170 c := data[*i] 171 if isLiteral(c) { 172 buf.WriteByte(c) 173 } else { 174 break 175 } 176 *i++ 177 } 178 return node(buf.String()), nil 179 } 180 181 func parseErlangAngleString(data []byte, i *int) (erlangNode, error) { 182 *i += 2 183 out, err := parseErlangString(data, i) 184 *i += 2 185 return out, err 186 } 187 188 func parseErlangString(data []byte, i *int) (erlangNode, error) { 189 delim := data[*i] 190 *i++ 191 var buf bytes.Buffer 192 for *i < len(data) { 193 c := data[*i] 194 if c == delim { 195 *i++ 196 return node(buf.String()), nil 197 } 198 if c == '\\' { 199 *i++ 200 if len(data) >= *i { 201 return node(nil), fmt.Errorf("invalid escape without closed string at %d", *i) 202 } 203 c = data[*i] 204 } 205 buf.WriteByte(c) 206 *i++ 207 } 208 return node(buf.String()), nil 209 } 210 211 func parseErlangList(data []byte, i *int) (erlangNode, error) { 212 *i++ 213 out := erlangNode{ 214 value: []erlangNode{}, 215 } 216 for *i < len(data) { 217 item, err := parseErlangNode(data, i) 218 if err != nil { 219 return node(nil), err 220 } 221 out.value = append(out.value.([]erlangNode), item) 222 skipWhitespace(data, i) 223 c := data[*i] 224 switch c { 225 case ',': 226 *i++ 227 continue 228 case ']', '}': 229 *i++ 230 return out, nil 231 default: 232 return node(nil), fmt.Errorf("unexpected character: %s", string(c)) 233 } 234 } 235 return out, nil 236 }