mirror of
https://github.com/Jguer/yay.git
synced 2024-11-07 17:47:21 +01:00
280 lines
6.2 KiB
Go
280 lines
6.2 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// based on the lexer from: src/pkg/text/template/parse/lex.go (golang source)
|
|
|
|
package pkgbuild
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// pos is a position in input being scanned
|
|
type pos int
|
|
|
|
type item struct {
|
|
typ itemType
|
|
pos pos
|
|
val string
|
|
}
|
|
|
|
func (i item) String() string {
|
|
switch {
|
|
case i.typ == itemEOF:
|
|
return "EOF"
|
|
case i.typ == itemError:
|
|
return i.val
|
|
case len(i.val) > 10:
|
|
return fmt.Sprintf("%.10q...", i.val)
|
|
}
|
|
return fmt.Sprintf("%q", i.val)
|
|
}
|
|
|
|
type itemType int
|
|
|
|
const (
|
|
itemError itemType = iota
|
|
itemEOF
|
|
itemVariable
|
|
itemValue
|
|
itemEndSplit
|
|
// PKGBUILD variables
|
|
itemPkgname // pkgname variable
|
|
itemPkgver // pkgver variable
|
|
itemPkgrel // pkgrel variable
|
|
itemPkgdir // pkgdir variable
|
|
itemEpoch // epoch variable
|
|
itemPkgbase // pkgbase variable
|
|
itemPkgdesc // pkgdesc variable
|
|
itemArch // arch variable
|
|
itemURL // url variable
|
|
itemLicense // license variable
|
|
itemGroups // groups variable
|
|
itemDepends // depends variable
|
|
itemOptdepends // optdepends variable
|
|
itemMakedepends // makedepends variable
|
|
itemCheckdepends // checkdepends variable
|
|
itemProvides // provides variable
|
|
itemConflicts // conflicts variable
|
|
itemReplaces // replaces variable
|
|
itemBackup // backup variable
|
|
itemOptions // options variable
|
|
itemInstall // install variable
|
|
itemChangelog // changelog variable
|
|
itemSource // source variable
|
|
itemNoextract // noextract variable
|
|
itemMd5sums // md5sums variable
|
|
itemSha1sums // sha1sums variable
|
|
itemSha224sums // sha224sums variable
|
|
itemSha256sums // sha256sums variable
|
|
itemSha384sums // sha384sums variable
|
|
itemSha512sums // sha512sums variable
|
|
itemValidpgpkeys // validpgpkeys variable
|
|
)
|
|
|
|
// PKGBUILD variables
|
|
var variables = map[string]itemType{
|
|
"pkgname": itemPkgname,
|
|
"pkgver": itemPkgver,
|
|
"pkgrel": itemPkgrel,
|
|
"pkgdir": itemPkgdir,
|
|
"epoch": itemEpoch,
|
|
"pkgbase": itemPkgbase,
|
|
"pkgdesc": itemPkgdesc,
|
|
"arch": itemArch,
|
|
"url": itemURL,
|
|
"license": itemLicense,
|
|
"groups": itemGroups,
|
|
"depends": itemDepends,
|
|
"optdepends": itemOptdepends,
|
|
"makedepends": itemMakedepends,
|
|
"checkdepends": itemCheckdepends,
|
|
"provides": itemProvides,
|
|
"conflicts": itemConflicts,
|
|
"replaces": itemReplaces,
|
|
"backup": itemBackup,
|
|
"options": itemOptions,
|
|
"install": itemInstall,
|
|
"changelog": itemChangelog,
|
|
"source": itemSource,
|
|
"noextract": itemNoextract,
|
|
"md5sums": itemMd5sums,
|
|
"sha1sums": itemSha1sums,
|
|
"sha224sums": itemSha224sums,
|
|
"sha256sums": itemSha256sums,
|
|
"sha384sums": itemSha384sums,
|
|
"sha512sums": itemSha512sums,
|
|
"validpgpkeys": itemValidpgpkeys,
|
|
}
|
|
|
|
const eof = -1
|
|
|
|
// stateFn represents the state of the scanner as a function that returns the next state
|
|
type stateFn func(*lexer) stateFn
|
|
|
|
// lexer holds the state of the scanner
|
|
type lexer struct {
|
|
input string
|
|
state stateFn
|
|
pos pos
|
|
start pos
|
|
width pos
|
|
lastPos pos
|
|
items chan item // channel of scanned items
|
|
}
|
|
|
|
// next returns the next rune in the input
|
|
func (l *lexer) next() rune {
|
|
if int(l.pos) >= len(l.input) {
|
|
l.width = 0
|
|
return eof
|
|
}
|
|
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
|
|
l.width = pos(w)
|
|
l.pos += l.width
|
|
return r
|
|
}
|
|
|
|
// peek returns but does not consume the next rune in the input
|
|
func (l *lexer) peek() rune {
|
|
r := l.next()
|
|
l.backup()
|
|
return r
|
|
}
|
|
|
|
// backup steps back one rune. Can only be called once per call of next
|
|
func (l *lexer) backup() {
|
|
l.pos -= l.width
|
|
}
|
|
|
|
// emit passes an item back to the client
|
|
func (l *lexer) emit(t itemType) {
|
|
l.items <- item{t, l.start, l.input[l.start:l.pos]}
|
|
l.start = l.pos
|
|
}
|
|
|
|
// ignore skips over the pending input before this point
|
|
func (l *lexer) ignore() {
|
|
l.start = l.pos
|
|
}
|
|
|
|
// errorf returns an error token and terminates the scan by passing
|
|
// back a nil pointer that will be the next state, terminating l.nextItem.
|
|
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
|
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
|
|
return nil
|
|
}
|
|
|
|
// nextItem returns the next item from the input.
|
|
func (l *lexer) nextItem() item {
|
|
item := <-l.items
|
|
l.lastPos = item.pos
|
|
return item
|
|
}
|
|
|
|
func lex(input string) *lexer {
|
|
l := &lexer{
|
|
input: input,
|
|
items: make(chan item),
|
|
}
|
|
go l.run()
|
|
return l
|
|
}
|
|
|
|
func (l *lexer) run() {
|
|
for l.state = lexEnv; l.state != nil; {
|
|
l.state = l.state(l)
|
|
}
|
|
}
|
|
|
|
func lexEnv(l *lexer) stateFn {
|
|
var r rune
|
|
for {
|
|
switch r = l.next(); {
|
|
case r == eof:
|
|
l.emit(itemEOF)
|
|
return nil
|
|
case isAlphaNumericUnderscore(r):
|
|
return lexVariable
|
|
case r == '\n':
|
|
if l.input[l.start:l.pos] == "\n\n" {
|
|
l.ignore()
|
|
l.emit(itemEndSplit)
|
|
}
|
|
case r == '\t':
|
|
l.ignore()
|
|
case r == '#':
|
|
return lexComment
|
|
default:
|
|
l.errorf("unable to parse character: %c", r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func lexComment(l *lexer) stateFn {
|
|
for {
|
|
switch l.next() {
|
|
case '\n':
|
|
l.ignore()
|
|
return lexEnv
|
|
case eof:
|
|
l.emit(itemEOF)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func lexVariable(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); {
|
|
case isAlphaNumericUnderscore(r):
|
|
// absorb
|
|
case r == ' ' && l.peek() == '=':
|
|
l.backup()
|
|
variable := l.input[l.start:l.pos]
|
|
|
|
// strip arch from source_arch like constructs
|
|
witharch := strings.SplitN(variable, "_", 2)
|
|
if len(witharch) == 2 {
|
|
if _, ok := archs[witharch[1]]; ok {
|
|
variable = witharch[0]
|
|
}
|
|
}
|
|
|
|
if _, ok := variables[variable]; ok {
|
|
l.emit(variables[variable])
|
|
// TODO to cut off ' = '
|
|
l.next()
|
|
l.next()
|
|
l.next()
|
|
l.ignore()
|
|
return lexValue
|
|
}
|
|
return l.errorf("invalid variable: %s", variable)
|
|
default:
|
|
pattern := l.input[l.start:l.pos]
|
|
return l.errorf("invalid pattern: %s", pattern)
|
|
}
|
|
}
|
|
}
|
|
|
|
func lexValue(l *lexer) stateFn {
|
|
for {
|
|
switch l.next() {
|
|
case '\n':
|
|
l.backup()
|
|
l.emit(itemValue)
|
|
return lexEnv
|
|
}
|
|
}
|
|
}
|
|
|
|
// isAlphaNumericUnderscore reports whether r is an alphabetic, digit, or underscore.
|
|
func isAlphaNumericUnderscore(r rune) bool {
|
|
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
|
}
|