615 lines
11 KiB
V
615 lines
11 KiB
V
module scanner
|
|
|
|
import v3.token
|
|
import v3.pref
|
|
|
|
@[flag]
|
|
pub enum Mode {
|
|
normal
|
|
scan_comments
|
|
skip_interpolation
|
|
}
|
|
|
|
pub struct Scanner {
|
|
pref &pref.Preferences
|
|
mode Mode
|
|
skip_interpolation bool
|
|
mut:
|
|
file &token.File = unsafe { nil }
|
|
insert_semi bool
|
|
pub mut:
|
|
src string
|
|
offset int
|
|
pos int
|
|
lit string
|
|
in_str_incomplete bool
|
|
in_str_inter bool
|
|
str_inter_cbr_depth int
|
|
str_quote u8
|
|
}
|
|
|
|
@[direct_array_access; inline]
|
|
fn (s &Scanner) peek_byte(n int) u8 {
|
|
idx := s.offset + n
|
|
if idx < 0 || idx >= s.src.len {
|
|
return 0
|
|
}
|
|
return s.src[idx]
|
|
}
|
|
|
|
pub fn new_scanner(prefs &pref.Preferences, mode Mode) Scanner {
|
|
unsafe {
|
|
return Scanner{
|
|
pref: prefs
|
|
mode: mode
|
|
skip_interpolation: mode.has(.skip_interpolation)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut s Scanner) init(file &token.File, src string) {
|
|
s.offset = 0
|
|
s.pos = 0
|
|
s.lit = ''
|
|
s.insert_semi = false
|
|
s.in_str_incomplete = false
|
|
s.in_str_inter = false
|
|
s.str_inter_cbr_depth = 0
|
|
s.str_quote = 0
|
|
s.file = unsafe { file }
|
|
s.src = src
|
|
}
|
|
|
|
fn (mut s Scanner) scan_char_literal(quote u8) token.Token {
|
|
s.offset++
|
|
for s.offset < s.src.len {
|
|
c2 := s.src[s.offset]
|
|
if c2 == quote {
|
|
break
|
|
}
|
|
if c2 == `\\` && s.offset + 1 < s.src.len {
|
|
s.offset += 2
|
|
continue
|
|
}
|
|
s.offset++
|
|
}
|
|
mut end := s.offset
|
|
if s.offset < s.src.len && s.src[s.offset] == quote {
|
|
end = s.offset
|
|
s.offset++
|
|
}
|
|
s.lit = s.src[s.pos + 1..end]
|
|
s.insert_semi = true
|
|
return .char
|
|
}
|
|
|
|
pub fn (s &Scanner) current_file() &token.File {
|
|
return unsafe { s.file }
|
|
}
|
|
|
|
@[direct_array_access]
|
|
pub fn (mut s Scanner) scan() token.Token {
|
|
if s.in_str_incomplete {
|
|
s.in_str_incomplete = false
|
|
s.pos = s.offset
|
|
s.string_literal(false, s.str_quote)
|
|
s.lit = s.src[s.pos..s.offset]
|
|
return .string
|
|
}
|
|
start:
|
|
s.whitespace()
|
|
if s.offset == s.src.len {
|
|
s.lit = ''
|
|
if s.insert_semi {
|
|
s.insert_semi = false
|
|
return .semicolon
|
|
}
|
|
s.file.add_line(s.offset)
|
|
return .eof
|
|
}
|
|
c := s.src[s.offset]
|
|
s.pos = s.offset
|
|
s.lit = ''
|
|
preserve_insert_semi := s.insert_semi
|
|
s.insert_semi = false
|
|
if c == `\n` {
|
|
s.lit = ''
|
|
return .semicolon
|
|
} else if c == `/` {
|
|
c2 := s.peek_byte(1)
|
|
if c2 == `/` || c2 == `*` {
|
|
if preserve_insert_semi {
|
|
s.insert_semi = true
|
|
}
|
|
s.comment()
|
|
if !s.mode.has(.scan_comments) {
|
|
unsafe {
|
|
goto start
|
|
}
|
|
}
|
|
s.lit = s.src[s.pos..s.offset]
|
|
return .comment
|
|
} else if c2 == `=` {
|
|
s.offset += 2
|
|
return .div_assign
|
|
}
|
|
s.offset++
|
|
return .div
|
|
} else if c >= `0` && c <= `9` {
|
|
s.number()
|
|
s.lit = s.src[s.pos..s.offset]
|
|
s.insert_semi = true
|
|
return .number
|
|
} else if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || c == `_` || c == `@` {
|
|
s.offset++
|
|
if c == `@` && s.peek_byte(0) == `[` {
|
|
s.offset++
|
|
return .attribute
|
|
}
|
|
for s.offset < s.src.len {
|
|
c3 := s.src[s.offset]
|
|
if c3.is_alnum() || c3 == `_` {
|
|
s.offset++
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
s.lit = s.src[s.pos..s.offset]
|
|
if s.lit == 'c' && s.offset < s.src.len && s.src[s.offset] == `'` {
|
|
s.pos = s.offset
|
|
tok := s.scan_char_literal(`'`)
|
|
s.lit = 'c:${s.lit}'
|
|
return tok
|
|
}
|
|
if s.lit == 'r' && s.offset < s.src.len
|
|
&& (s.src[s.offset] == `'` || s.src[s.offset] == `"`) {
|
|
quote := s.src[s.offset]
|
|
s.offset++
|
|
if !s.in_str_inter {
|
|
s.str_quote = quote
|
|
}
|
|
s.string_literal(true, quote)
|
|
s.lit = s.src[s.pos..s.offset]
|
|
s.insert_semi = true
|
|
return .string
|
|
}
|
|
tok := token.Token.from_string_tinyv(s.lit)
|
|
if tok in [.key_break, .key_continue, .key_nil, .key_none, .key_return, .key_false, .key_true,
|
|
.name] {
|
|
s.insert_semi = true
|
|
}
|
|
return tok
|
|
} else if c == `'` || c == `"` {
|
|
s.offset++
|
|
if !s.in_str_inter {
|
|
s.str_quote = c
|
|
}
|
|
s.string_literal(s.in_str_inter || (s.offset >= 2 && s.src[s.offset - 2] == `r`), c)
|
|
s.lit = s.src[s.pos..s.offset]
|
|
s.insert_semi = true
|
|
return .string
|
|
} else if c == `\`` {
|
|
return s.scan_char_literal(c)
|
|
}
|
|
s.lit = ''
|
|
s.offset++
|
|
match c {
|
|
`.` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 >= `0` && c2 <= `9` {
|
|
s.number()
|
|
s.lit = s.src[s.pos..s.offset]
|
|
return .number
|
|
} else if c2 == `.` {
|
|
s.offset++
|
|
if s.peek_byte(0) == `.` {
|
|
s.offset++
|
|
return .ellipsis
|
|
}
|
|
return .dotdot
|
|
}
|
|
return .dot
|
|
}
|
|
`:` {
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .decl_assign
|
|
}
|
|
return .colon
|
|
}
|
|
`!` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `=` {
|
|
s.offset++
|
|
return .ne
|
|
} else if c2 == `i` {
|
|
c3 := s.peek_byte(1)
|
|
c4 := s.peek_byte(2)
|
|
c4_is_space := c4 == ` ` || c4 == `\t`
|
|
if c3 == `n` && c4_is_space {
|
|
s.offset += 2
|
|
return .not_in
|
|
} else if c3 == `s` && c4_is_space {
|
|
s.offset += 2
|
|
return .not_is
|
|
}
|
|
}
|
|
s.insert_semi = true
|
|
return .not
|
|
}
|
|
`=` {
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .eq
|
|
}
|
|
return .assign
|
|
}
|
|
`+` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `+` {
|
|
s.offset++
|
|
return .inc
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .plus_assign
|
|
}
|
|
return .plus
|
|
}
|
|
`-` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `-` {
|
|
s.offset++
|
|
return .dec
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .minus_assign
|
|
}
|
|
return .minus
|
|
}
|
|
`%` {
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .mod_assign
|
|
}
|
|
return .mod
|
|
}
|
|
`*` {
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .mul_assign
|
|
}
|
|
return .mul
|
|
}
|
|
`^` {
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .xor_assign
|
|
}
|
|
return .xor
|
|
}
|
|
`&` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `&` {
|
|
if s.peek_byte(1) == `=` {
|
|
s.offset += 2
|
|
return .and_assign
|
|
}
|
|
s.offset++
|
|
return .and
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .and_assign
|
|
}
|
|
return .amp
|
|
}
|
|
`|` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `|` {
|
|
if s.peek_byte(1) == `=` {
|
|
s.offset += 2
|
|
return .or_assign
|
|
}
|
|
s.offset++
|
|
return .logical_or
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .or_assign
|
|
}
|
|
return .pipe
|
|
}
|
|
`<` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `<` {
|
|
s.offset++
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .left_shift_assign
|
|
}
|
|
return .left_shift
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .le
|
|
} else if c2 == `-` {
|
|
s.offset++
|
|
return .arrow
|
|
}
|
|
return .lt
|
|
}
|
|
`>` {
|
|
c2 := s.peek_byte(0)
|
|
if c2 == `>` {
|
|
s.offset++
|
|
c3 := s.peek_byte(0)
|
|
if c3 == `>` {
|
|
s.offset++
|
|
if s.peek_byte(0) == `=` {
|
|
s.offset++
|
|
return .right_shift_unsigned_assign
|
|
}
|
|
return .right_shift_unsigned
|
|
} else if c3 == `=` {
|
|
s.offset++
|
|
return .right_shift_assign
|
|
}
|
|
return .right_shift
|
|
} else if c2 == `=` {
|
|
s.offset++
|
|
return .ge
|
|
}
|
|
return .gt
|
|
}
|
|
`#` {
|
|
if s.peek_byte(0) == `[` {
|
|
s.offset++
|
|
return .lsbr
|
|
}
|
|
s.offset++
|
|
start := s.offset
|
|
for s.offset < s.src.len && s.src[s.offset] != `\n` {
|
|
s.offset++
|
|
}
|
|
s.lit = s.src[start..s.offset].trim_space()
|
|
s.insert_semi = true
|
|
return .hash
|
|
}
|
|
`~` {
|
|
return .bit_not
|
|
}
|
|
`,` {
|
|
return .comma
|
|
}
|
|
`$` {
|
|
if s.in_str_inter {
|
|
return .str_dollar
|
|
}
|
|
return .dollar
|
|
}
|
|
`{` {
|
|
if s.in_str_inter {
|
|
s.str_inter_cbr_depth++
|
|
}
|
|
return .lcbr
|
|
}
|
|
`}` {
|
|
if s.in_str_inter {
|
|
s.str_inter_cbr_depth--
|
|
if s.str_inter_cbr_depth == 0 {
|
|
s.in_str_incomplete = true
|
|
s.in_str_inter = false
|
|
}
|
|
}
|
|
s.insert_semi = true
|
|
return .rcbr
|
|
}
|
|
`(` {
|
|
return .lpar
|
|
}
|
|
`)` {
|
|
s.insert_semi = true
|
|
return .rpar
|
|
}
|
|
`[` {
|
|
return .lsbr
|
|
}
|
|
`]` {
|
|
s.insert_semi = true
|
|
return .rsbr
|
|
}
|
|
`;` {
|
|
return .semicolon
|
|
}
|
|
`?` {
|
|
s.insert_semi = true
|
|
return .question
|
|
}
|
|
else {
|
|
return .unknown
|
|
}
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn (mut s Scanner) whitespace() {
|
|
for s.offset < s.src.len {
|
|
c := s.src[s.offset]
|
|
if c == ` ` || c == `\t` || c == `\r` {
|
|
s.offset++
|
|
continue
|
|
} else if c == `\n` {
|
|
if s.insert_semi {
|
|
return
|
|
}
|
|
s.offset++
|
|
s.file.add_line(s.offset)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn (mut s Scanner) line() {
|
|
for s.offset < s.src.len {
|
|
if s.src[s.offset] == `\n` {
|
|
break
|
|
}
|
|
s.offset++
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn (mut s Scanner) comment() {
|
|
s.offset++
|
|
if s.offset >= s.src.len {
|
|
return
|
|
}
|
|
c := s.src[s.offset]
|
|
if c == `/` {
|
|
s.line()
|
|
} else if c == `*` {
|
|
s.offset++
|
|
mut ml_comment_depth := 1
|
|
for s.offset < s.src.len {
|
|
c2 := s.src[s.offset]
|
|
c3 := s.peek_byte(1)
|
|
if c2 == `\n` {
|
|
s.offset++
|
|
s.file.add_line(s.offset)
|
|
} else if c2 == `/` && c3 == `*` {
|
|
s.offset += 2
|
|
ml_comment_depth++
|
|
} else if c2 == `*` && c3 == `/` {
|
|
s.offset += 2
|
|
ml_comment_depth--
|
|
if ml_comment_depth == 0 {
|
|
break
|
|
}
|
|
} else {
|
|
s.offset++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn (mut s Scanner) string_literal(scan_as_raw bool, c_quote u8) {
|
|
if scan_as_raw {
|
|
for s.offset < s.src.len {
|
|
c := s.src[s.offset]
|
|
if c == c_quote {
|
|
break
|
|
}
|
|
if c == `\n` {
|
|
s.offset++
|
|
s.file.add_line(s.offset)
|
|
continue
|
|
}
|
|
s.offset++
|
|
}
|
|
if s.offset < s.src.len {
|
|
s.offset++
|
|
}
|
|
return
|
|
}
|
|
for s.offset < s.src.len {
|
|
c := s.src[s.offset]
|
|
if c == `\\` {
|
|
s.offset += 2
|
|
continue
|
|
} else if c == `\n` {
|
|
s.offset++
|
|
s.file.add_line(s.offset)
|
|
continue
|
|
} else if c == `$` && s.peek_byte(1) == `{` {
|
|
s.in_str_inter = true
|
|
if s.skip_interpolation {
|
|
s.str_inter_cbr_depth++
|
|
s.offset += 2
|
|
continue
|
|
} else {
|
|
return
|
|
}
|
|
} else if s.skip_interpolation && s.in_str_inter {
|
|
if c == `{` {
|
|
s.str_inter_cbr_depth++
|
|
} else if c == `}` {
|
|
s.str_inter_cbr_depth--
|
|
if s.str_inter_cbr_depth == 0 {
|
|
s.in_str_inter = false
|
|
}
|
|
}
|
|
} else if c == c_quote && !s.in_str_inter {
|
|
s.offset++
|
|
break
|
|
}
|
|
s.offset++
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn (mut s Scanner) number() {
|
|
if s.offset < s.src.len && s.src[s.offset] == `0` {
|
|
s.offset++
|
|
c := s.peek_byte(0)
|
|
if c == `b` || c == `B` {
|
|
s.offset++
|
|
for s.offset < s.src.len {
|
|
c2 := s.src[s.offset]
|
|
if c2 == `0` || c2 == `1` || c2 == `_` {
|
|
s.offset++
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
return
|
|
} else if c == `x` || c == `X` {
|
|
s.offset++
|
|
for s.offset < s.src.len {
|
|
c2 := s.src[s.offset]
|
|
if (c2 >= `0` && c2 <= `9`) || (c2 >= `a` && c2 <= `f`)
|
|
|| (c2 >= `A` && c2 <= `F`) || c2 == `_` {
|
|
s.offset++
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
return
|
|
} else if c == `o` || c == `O` {
|
|
s.offset++
|
|
for s.offset < s.src.len {
|
|
c2 := s.src[s.offset]
|
|
if (c2 >= `0` && c2 <= `7`) || c2 == `_` {
|
|
s.offset++
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
return
|
|
}
|
|
}
|
|
mut has_decimal := false
|
|
mut has_exponent := false
|
|
for s.offset < s.src.len {
|
|
c := s.src[s.offset]
|
|
if (c >= `0` && c <= `9`) || c == `_` {
|
|
s.offset++
|
|
continue
|
|
} else if !has_decimal && c == `.` {
|
|
next := s.peek_byte(1)
|
|
if next != `.` && next >= `0` && next <= `9` {
|
|
has_decimal = true
|
|
s.offset++
|
|
continue
|
|
}
|
|
} else if !has_exponent && (c == `e` || c == `E`) {
|
|
has_exponent = true
|
|
s.offset++
|
|
if s.offset < s.src.len && (s.src[s.offset] == `+` || s.src[s.offset] == `-`) {
|
|
s.offset++
|
|
}
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
}
|