| 85 | } |
| 86 | |
| 87 | func (p *Parser) parseValue(propName string) (string, error) { |
| 88 | start := p.Pos |
| 89 | quotePos := 0 |
| 90 | parenPosStack := make([]int, 0) |
| 91 | for !p.eof() { |
| 92 | c := p.peekChar() |
| 93 | if p.InQuote { |
| 94 | if c == p.QuoteChar { |
| 95 | p.InQuote = false |
| 96 | } else if c == '\\' { |
| 97 | p.advance() |
| 98 | } |
| 99 | } else { |
| 100 | if c == '"' || c == '\'' { |
| 101 | p.InQuote = true |
| 102 | p.QuoteChar = c |
| 103 | quotePos = p.Pos |
| 104 | } else if c == '(' { |
| 105 | p.OpenParens++ |
| 106 | parenPosStack = append(parenPosStack, p.Pos) |
| 107 | } else if c == ')' { |
| 108 | if p.OpenParens == 0 { |
| 109 | return "", fmt.Errorf("unmatched ')' at pos %d", p.Pos+1) |
| 110 | } |
| 111 | p.OpenParens-- |
| 112 | parenPosStack = parenPosStack[:len(parenPosStack)-1] |
| 113 | } else if c == ';' && p.OpenParens == 0 { |
| 114 | break |
| 115 | } |
| 116 | } |
| 117 | p.advance() |
| 118 | } |
| 119 | if p.eof() && p.InQuote { |
| 120 | return "", fmt.Errorf("bad style attribute, while parsing attribute %q, unmatched quote at pos %d", propName, quotePos+1) |
| 121 | } |
| 122 | if p.eof() && p.OpenParens > 0 { |
| 123 | return "", fmt.Errorf("bad style attribute, while parsing property %q, unmatched '(' at pos %d", propName, parenPosStack[len(parenPosStack)-1]+1) |
| 124 | } |
| 125 | return strings.TrimSpace(p.Input[start:p.Pos]), nil |
| 126 | } |
| 127 | |
| 128 | func isIdentChar(r rune) bool { |
| 129 | return unicode.IsLetter(r) || unicode.IsDigit(r) |