|
@ -26,6 +26,28 @@ DEALINGS IN THE SOFTWARE. |
|
|
|
|
|
|
|
|
from .errors import UnexpectedQuoteError, InvalidEndOfQuotedStringError, ExpectedClosingQuoteError |
|
|
from .errors import UnexpectedQuoteError, InvalidEndOfQuotedStringError, ExpectedClosingQuoteError |
|
|
|
|
|
|
|
|
|
|
|
# map from opening quotes to closing quotes |
|
|
|
|
|
_quotes = { |
|
|
|
|
|
'"': '"', |
|
|
|
|
|
"‘": "’", |
|
|
|
|
|
"‚": "‛", |
|
|
|
|
|
"“": "”", |
|
|
|
|
|
"„": "‟", |
|
|
|
|
|
"⹂": "⹂", |
|
|
|
|
|
"「": "」", |
|
|
|
|
|
"『": "』", |
|
|
|
|
|
"〝": "〞", |
|
|
|
|
|
"﹁": "﹂", |
|
|
|
|
|
"﹃": "﹄", |
|
|
|
|
|
""": """, |
|
|
|
|
|
"「": "」", |
|
|
|
|
|
"«": "»", |
|
|
|
|
|
"‹": "›", |
|
|
|
|
|
"《": "》", |
|
|
|
|
|
"〈": "〉", |
|
|
|
|
|
} |
|
|
|
|
|
_all_quotes = set(_quotes.keys()) | set(_quotes.values()) |
|
|
|
|
|
|
|
|
class StringView: |
|
|
class StringView: |
|
|
def __init__(self, buffer): |
|
|
def __init__(self, buffer): |
|
|
self.index = 0 |
|
|
self.index = 0 |
|
@ -104,36 +126,8 @@ class StringView: |
|
|
self.index += pos |
|
|
self.index += pos |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
def get_quoted_word(self): |
|
|
return '<StringView pos: {0.index} prev: {0.previous} end: {0.end} eof: {0.eof}>'.format(self) |
|
|
current = self.current |
|
|
|
|
|
|
|
|
# Parser |
|
|
|
|
|
|
|
|
|
|
|
# map from opening quotes to closing quotes |
|
|
|
|
|
_quotes = { |
|
|
|
|
|
'"': '"', |
|
|
|
|
|
"‘": "’", |
|
|
|
|
|
"‚": "‛", |
|
|
|
|
|
"“": "”", |
|
|
|
|
|
"„": "‟", |
|
|
|
|
|
"⹂": "⹂", |
|
|
|
|
|
"「": "」", |
|
|
|
|
|
"『": "』", |
|
|
|
|
|
"〝": "〞", |
|
|
|
|
|
"﹁": "﹂", |
|
|
|
|
|
"﹃": "﹄", |
|
|
|
|
|
""": """, |
|
|
|
|
|
"「": "」", |
|
|
|
|
|
"«": "»", |
|
|
|
|
|
"‹": "›", |
|
|
|
|
|
"《": "》", |
|
|
|
|
|
"〈": "〉", |
|
|
|
|
|
} |
|
|
|
|
|
_all_quotes = set(_quotes.keys()) | set(_quotes.values()) |
|
|
|
|
|
|
|
|
|
|
|
def quoted_word(view): |
|
|
|
|
|
current = view.current |
|
|
|
|
|
|
|
|
|
|
|
if current is None: |
|
|
if current is None: |
|
|
return None |
|
|
return None |
|
|
|
|
|
|
|
@ -146,8 +140,8 @@ def quoted_word(view): |
|
|
result = [current] |
|
|
result = [current] |
|
|
_escaped_quotes = _all_quotes |
|
|
_escaped_quotes = _all_quotes |
|
|
|
|
|
|
|
|
while not view.eof: |
|
|
while not self.eof: |
|
|
current = view.get() |
|
|
current = self.get() |
|
|
if not current: |
|
|
if not current: |
|
|
if is_quoted: |
|
|
if is_quoted: |
|
|
# unexpected EOF |
|
|
# unexpected EOF |
|
@ -157,7 +151,7 @@ def quoted_word(view): |
|
|
# currently we accept strings in the format of "hello world" |
|
|
# currently we accept strings in the format of "hello world" |
|
|
# to embed a quote inside the string you must escape it: "a \"world\"" |
|
|
# to embed a quote inside the string you must escape it: "a \"world\"" |
|
|
if current == '\\': |
|
|
if current == '\\': |
|
|
next_char = view.get() |
|
|
next_char = self.get() |
|
|
if not next_char: |
|
|
if not next_char: |
|
|
# string ends with \ and no character after it |
|
|
# string ends with \ and no character after it |
|
|
if is_quoted: |
|
|
if is_quoted: |
|
@ -171,7 +165,7 @@ def quoted_word(view): |
|
|
result.append(next_char) |
|
|
result.append(next_char) |
|
|
else: |
|
|
else: |
|
|
# different escape character, ignore it |
|
|
# different escape character, ignore it |
|
|
view.undo() |
|
|
self.undo() |
|
|
result.append(current) |
|
|
result.append(current) |
|
|
continue |
|
|
continue |
|
|
|
|
|
|
|
@ -181,7 +175,7 @@ def quoted_word(view): |
|
|
|
|
|
|
|
|
# closing quote |
|
|
# closing quote |
|
|
if is_quoted and current == close_quote: |
|
|
if is_quoted and current == close_quote: |
|
|
next_char = view.get() |
|
|
next_char = self.get() |
|
|
valid_eof = not next_char or next_char.isspace() |
|
|
valid_eof = not next_char or next_char.isspace() |
|
|
if not valid_eof: |
|
|
if not valid_eof: |
|
|
raise InvalidEndOfQuotedStringError(next_char) |
|
|
raise InvalidEndOfQuotedStringError(next_char) |
|
@ -194,3 +188,7 @@ def quoted_word(view): |
|
|
return ''.join(result) |
|
|
return ''.join(result) |
|
|
|
|
|
|
|
|
result.append(current) |
|
|
result.append(current) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
|
|
return '<StringView pos: {0.index} prev: {0.previous} end: {0.end} eof: {0.eof}>'.format(self) |
|
|