🐐 Escape sequences
This commit is contained in:
@@ -343,10 +343,15 @@ class String(Node):
|
||||
return f"String({self.value})"
|
||||
|
||||
def print(self):
|
||||
v = (self.value
|
||||
.replace('\\', '\\\\')
|
||||
.replace('\n', '\\n')
|
||||
.replace('\t', '\\t')
|
||||
.replace('\r', '\\r'))
|
||||
if self.quote == "'":
|
||||
return f"'{self.value}'"
|
||||
escaped = self.value.replace('{', '{{').replace('}', '}}')
|
||||
return f'"{escaped}"'
|
||||
return f"'{v}'"
|
||||
v = v.replace('"', '\\"').replace('{', '{{').replace('}', '}}')
|
||||
return f'"{v}"'
|
||||
|
||||
def _eval(self, vtable):
|
||||
return vtable, ValStr(self.value)
|
||||
|
||||
@@ -66,7 +66,7 @@ builtin_tokens = [("BUILTIN", i) for i in [
|
||||
]]
|
||||
|
||||
data_tokens = [
|
||||
("DATA_STRING", r"(\".*?\"|'.*?')"),
|
||||
("DATA_STRING", r'("(?:[^"\\]|\\.)*"|' + r"'(?:[^'\\]|\\.)*')"),
|
||||
("DATA_FRACTION", r"([IVXLCDM][IVXLCDM_]*)?([S][S:.|]*|:[S:.|]+|\.[S:.|]*)"),
|
||||
("DATA_NUMERAL", r"[IVXLCDM][IVXLCDM_]*")
|
||||
]
|
||||
|
||||
@@ -7,19 +7,61 @@ from . import ast_nodes
|
||||
ALL_TOKENS = list(set([i[0] for i in all_tokens]))
|
||||
|
||||
|
||||
_ESCAPE_MAP = {
|
||||
'n': '\n',
|
||||
't': '\t',
|
||||
'r': '\r',
|
||||
'\\': '\\',
|
||||
'"': '"',
|
||||
"'": "'",
|
||||
}
|
||||
|
||||
|
||||
def _read_escape(s, i):
|
||||
"""Read a backslash escape at position i (the backslash). Returns (char, new_i)."""
|
||||
if i + 1 >= len(s):
|
||||
raise CentvrionError("Trailing backslash in string")
|
||||
nxt = s[i + 1]
|
||||
if nxt in _ESCAPE_MAP:
|
||||
return _ESCAPE_MAP[nxt], i + 2
|
||||
# unknown escapes pass through literally (e.g. \1 for regex backrefs)
|
||||
return '\\' + nxt, i + 2
|
||||
|
||||
|
||||
def _unescape(s):
|
||||
"""Process escape sequences in a string with no interpolation."""
|
||||
out = []
|
||||
i = 0
|
||||
while i < len(s):
|
||||
if s[i] == '\\':
|
||||
ch, i = _read_escape(s, i)
|
||||
out.append(ch)
|
||||
else:
|
||||
out.append(s[i])
|
||||
i += 1
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
def _parse_interpolated(raw_value):
|
||||
quote_char = raw_value[0]
|
||||
inner = raw_value[1:-1]
|
||||
|
||||
if quote_char == "'" or len(inner) == 0:
|
||||
if len(inner) == 0:
|
||||
return ast_nodes.String(inner)
|
||||
|
||||
if quote_char == "'":
|
||||
return ast_nodes.String(_unescape(inner))
|
||||
|
||||
parts = []
|
||||
i = 0
|
||||
current = []
|
||||
|
||||
while i < len(inner):
|
||||
ch = inner[i]
|
||||
if ch == '\\':
|
||||
c, i = _read_escape(inner, i)
|
||||
current.append(c)
|
||||
continue
|
||||
if ch == '{':
|
||||
if i + 1 < len(inner) and inner[i + 1] == '{':
|
||||
current.append('{')
|
||||
|
||||
Reference in New Issue
Block a user