mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-14 09:52:23 -07:00
155 lines
2.5 KiB
Odin
155 lines
2.5 KiB
Odin
package c_frontend_preprocess
|
|
|
|
import "core:unicode/utf8"
|
|
|
|
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
|
hex_to_int :: proc(c: byte) -> int {
|
|
switch c {
|
|
case '0'..='9': return int(c-'0')
|
|
case 'a'..='f': return int(c-'a')+10
|
|
case 'A'..='F': return int(c-'A')+10
|
|
}
|
|
return -1
|
|
}
|
|
w: int
|
|
|
|
if str[0] == quote && quote == '"' {
|
|
return
|
|
} else if str[0] >= 0x80 {
|
|
r, w = utf8.decode_rune_in_string(str)
|
|
return r, true, str[w:], true
|
|
} else if str[0] != '\\' {
|
|
return rune(str[0]), false, str[1:], true
|
|
}
|
|
|
|
if len(str) <= 1 {
|
|
return
|
|
}
|
|
s := str
|
|
c := s[1]
|
|
s = s[2:]
|
|
|
|
switch c {
|
|
case: r = rune(c)
|
|
|
|
case 'a': r = '\a'
|
|
case 'b': r = '\b'
|
|
case 'e': r = '\e'
|
|
case 'f': r = '\f'
|
|
case 'n': r = '\n'
|
|
case 'r': r = '\r'
|
|
case 't': r = '\t'
|
|
case 'v': r = '\v'
|
|
case '\\': r = '\\'
|
|
|
|
case '"': r = '"'
|
|
case '\'': r = '\''
|
|
|
|
case '0'..='7':
|
|
v := int(c-'0')
|
|
if len(s) < 2 {
|
|
return
|
|
}
|
|
for i in 0..<len(s) {
|
|
d := int(s[i]-'0')
|
|
if d < 0 || d > 7 {
|
|
return
|
|
}
|
|
v = (v<<3) | d
|
|
}
|
|
s = s[2:]
|
|
if v > 0xff {
|
|
return
|
|
}
|
|
r = rune(v)
|
|
|
|
case 'x', 'u', 'U':
|
|
count: int
|
|
switch c {
|
|
case 'x': count = 2
|
|
case 'u': count = 4
|
|
case 'U': count = 8
|
|
}
|
|
|
|
if len(s) < count {
|
|
return
|
|
}
|
|
|
|
for i in 0..<count {
|
|
d := hex_to_int(s[i])
|
|
if d < 0 {
|
|
return
|
|
}
|
|
r = (r<<4) | rune(d)
|
|
}
|
|
s = s[count:]
|
|
if c == 'x' {
|
|
break
|
|
}
|
|
if r > utf8.MAX_RUNE {
|
|
return
|
|
}
|
|
multiple_bytes = true
|
|
}
|
|
|
|
success = true
|
|
tail_string = s
|
|
return
|
|
}
|
|
|
|
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
|
contains_rune :: proc(s: string, r: rune) -> int {
|
|
for c, offset in s {
|
|
if c == r {
|
|
return offset
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
assert(len(lit) >= 2)
|
|
|
|
s := lit
|
|
quote := '"'
|
|
|
|
if s == `""` {
|
|
return "", false, true
|
|
}
|
|
|
|
if contains_rune(s, '\n') >= 0 {
|
|
return s, false, false
|
|
}
|
|
|
|
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
|
if quote == '"' {
|
|
return s, false, true
|
|
}
|
|
}
|
|
s = s[1:len(s)-1]
|
|
|
|
|
|
buf_len := 3*len(s) / 2
|
|
buf := make([]byte, buf_len, allocator)
|
|
offset := 0
|
|
for len(s) > 0 {
|
|
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
|
|
if !ok {
|
|
delete(buf)
|
|
return s, false, false
|
|
}
|
|
s = tail_string
|
|
if r < 0x80 || !multiple_bytes {
|
|
buf[offset] = byte(r)
|
|
offset += 1
|
|
} else {
|
|
b, w := utf8.encode_rune(r)
|
|
copy(buf[offset:], b[:w])
|
|
offset += w
|
|
}
|
|
}
|
|
|
|
new_string := string(buf[:offset])
|
|
|
|
return new_string, true, true
|
|
}
|