|
|
|
@ -1,12 +1,11 @@
|
|
|
|
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
module compiler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
os
|
|
|
|
|
//strings
|
|
|
|
|
// strings
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
@ -30,17 +29,17 @@ mut:
|
|
|
|
|
line_comment string
|
|
|
|
|
started bool
|
|
|
|
|
// vfmt fields TODO move to a separate struct
|
|
|
|
|
//fmt_out strings.Builder
|
|
|
|
|
// fmt_out strings.Builder
|
|
|
|
|
fmt_lines []string
|
|
|
|
|
//fmt_line string
|
|
|
|
|
// fmt_line string
|
|
|
|
|
fmt_indent int
|
|
|
|
|
fmt_line_empty bool
|
|
|
|
|
//fmt_needs_nl bool
|
|
|
|
|
// fmt_needs_nl bool
|
|
|
|
|
prev_tok TokenKind
|
|
|
|
|
fn_name string // needed for @FN
|
|
|
|
|
should_print_line_on_error bool
|
|
|
|
|
should_print_errors_in_color bool
|
|
|
|
|
should_print_relative_paths_on_error bool
|
|
|
|
|
print_line_on_error bool
|
|
|
|
|
print_colored_error bool
|
|
|
|
|
print_rel_paths_on_error bool
|
|
|
|
|
quote byte // which quote is used to denote current string: ' or "
|
|
|
|
|
line_ends []int // the positions of source lines ends (i.e. \n signs)
|
|
|
|
|
nlines int // total number of lines in the source file that were scanned
|
|
|
|
@ -48,56 +47,51 @@ mut:
|
|
|
|
|
is_fmt bool // Used only for skipping ${} in strings, since we need literal
|
|
|
|
|
// string values when generating formatted code.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// new scanner from file.
|
|
|
|
|
fn new_scanner_file(file_path string) &Scanner {
|
|
|
|
|
if !os.exists(file_path) {
|
|
|
|
|
verror("$file_path doesn't exist")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mut raw_text := os.read_file(file_path) or {
|
|
|
|
|
mut raw_text := os.read_file(file_path)or{
|
|
|
|
|
verror('scanner: failed to open $file_path')
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BOM check
|
|
|
|
|
if raw_text.len >= 3 {
|
|
|
|
|
c_text := raw_text.str
|
|
|
|
|
|
|
|
|
|
if c_text[0] == 0xEF && c_text[1] == 0xBB && c_text[2] == 0xBF {
|
|
|
|
|
// skip three BOM bytes
|
|
|
|
|
offset_from_begin := 3
|
|
|
|
|
raw_text = tos(c_text[offset_from_begin], vstrlen(c_text) - offset_from_begin)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mut s := new_scanner(raw_text)
|
|
|
|
|
s.init_fmt()
|
|
|
|
|
s.file_path = file_path
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// new scanner from string.
|
|
|
|
|
fn new_scanner(text string) &Scanner {
|
|
|
|
|
return &Scanner {
|
|
|
|
|
return &Scanner{
|
|
|
|
|
text: text
|
|
|
|
|
//fmt_out: strings.new_builder(1000)
|
|
|
|
|
should_print_line_on_error: true
|
|
|
|
|
should_print_errors_in_color: true
|
|
|
|
|
should_print_relative_paths_on_error: true
|
|
|
|
|
// fmt_out: strings.new_builder(1000)
|
|
|
|
|
|
|
|
|
|
print_line_on_error: true
|
|
|
|
|
print_colored_error: true
|
|
|
|
|
print_rel_paths_on_error: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO remove once multiple return values are implemented
|
|
|
|
|
struct ScanRes {
|
|
|
|
|
tok TokenKind
|
|
|
|
|
lit string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn scan_res(tok TokenKind, lit string) ScanRes {
|
|
|
|
|
return ScanRes{tok, lit}
|
|
|
|
|
fn scan_res(tok TokenKind,lit string) ScanRes {
|
|
|
|
|
return ScanRes{
|
|
|
|
|
tok,lit}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) ident_name() string {
|
|
|
|
@ -146,7 +140,8 @@ fn (s mut Scanner) ident_oct_number() string {
|
|
|
|
|
if !c.is_oct_digit() {
|
|
|
|
|
s.error('malformed octal constant')
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
s.pos++
|
|
|
|
@ -158,12 +153,10 @@ fn (s mut Scanner) ident_oct_number() string {
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) ident_dec_number() string {
|
|
|
|
|
start_pos := s.pos
|
|
|
|
|
|
|
|
|
|
// scan integer part
|
|
|
|
|
for s.pos < s.text.len && s.text[s.pos].is_digit() {
|
|
|
|
|
s.pos++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// e.g. 1..9
|
|
|
|
|
// we just return '1' and don't scan '..9'
|
|
|
|
|
if s.expect('..', s.pos) {
|
|
|
|
@ -171,7 +164,6 @@ fn (s mut Scanner) ident_dec_number() string {
|
|
|
|
|
s.pos--
|
|
|
|
|
return number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scan fractional part
|
|
|
|
|
if s.pos < s.text.len && s.text[s.pos] == `.` {
|
|
|
|
|
s.pos++
|
|
|
|
@ -182,7 +174,6 @@ fn (s mut Scanner) ident_dec_number() string {
|
|
|
|
|
s.error('no `f` is needed for floats')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scan exponential part
|
|
|
|
|
mut has_exponential_part := false
|
|
|
|
|
if s.expect('e+', s.pos) || s.expect('e-', s.pos) {
|
|
|
|
@ -195,7 +186,6 @@ fn (s mut Scanner) ident_dec_number() string {
|
|
|
|
|
}
|
|
|
|
|
has_exponential_part = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// error check: 1.23.4, 123.e+3.4
|
|
|
|
|
if s.pos < s.text.len && s.text[s.pos] == `.` {
|
|
|
|
|
if has_exponential_part {
|
|
|
|
@ -205,7 +195,6 @@ fn (s mut Scanner) ident_dec_number() string {
|
|
|
|
|
s.error('too many decimal points in number')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
number := s.text[start_pos..s.pos]
|
|
|
|
|
s.pos--
|
|
|
|
|
return number
|
|
|
|
@ -215,27 +204,23 @@ fn (s mut Scanner) ident_number() string {
|
|
|
|
|
if s.expect('0x', s.pos) {
|
|
|
|
|
return s.ident_hex_number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.expect('0.', s.pos) || s.expect('0e', s.pos) {
|
|
|
|
|
return s.ident_dec_number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.text[s.pos] == `0` {
|
|
|
|
|
return s.ident_oct_number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s.ident_dec_number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) skip_whitespace() {
|
|
|
|
|
//if s.is_vh { println('vh') return }
|
|
|
|
|
// if s.is_vh { println('vh') return }
|
|
|
|
|
for s.pos < s.text.len && s.text[s.pos].is_white() {
|
|
|
|
|
if is_nl(s.text[s.pos]) && s.is_vh {
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// Count \r\n as one line
|
|
|
|
|
if is_nl(s.text[s.pos]) && !s.expect('\r\n', s.pos-1) {
|
|
|
|
|
if is_nl(s.text[s.pos]) && !s.expect('\r\n', s.pos - 1) {
|
|
|
|
|
s.inc_line_number()
|
|
|
|
|
}
|
|
|
|
|
s.pos++
|
|
|
|
@ -249,10 +234,10 @@ fn (s mut Scanner) end_of_file() ScanRes {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
//if s.line_comment != '' {
|
|
|
|
|
//s.fgenln('// LC "$s.line_comment"')
|
|
|
|
|
//s.line_comment = ''
|
|
|
|
|
//}
|
|
|
|
|
// if s.line_comment != '' {
|
|
|
|
|
// s.fgenln('// LC "$s.line_comment"')
|
|
|
|
|
// s.line_comment = ''
|
|
|
|
|
// }
|
|
|
|
|
if s.started {
|
|
|
|
|
s.pos++
|
|
|
|
|
}
|
|
|
|
@ -288,7 +273,7 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
name := s.ident_name()
|
|
|
|
|
// tmp hack to detect . in ${}
|
|
|
|
|
// Check if not .eof to prevent panic
|
|
|
|
|
next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` }
|
|
|
|
|
next_char := if s.pos + 1 < s.text.len {s.text[s.pos + 1]}else {`\0`}
|
|
|
|
|
if is_key(name) {
|
|
|
|
|
return scan_res(key_to_token(name), '')
|
|
|
|
|
}
|
|
|
|
@ -308,8 +293,8 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
s.inter_start = false
|
|
|
|
|
}
|
|
|
|
|
if s.pos == 0 && next_char == ` ` {
|
|
|
|
|
//If a single letter name at the start of the file, increment
|
|
|
|
|
//Otherwise the scanner would be stuck at s.pos = 0
|
|
|
|
|
// If a single letter name at the start of the file, increment
|
|
|
|
|
// Otherwise the scanner would be stuck at s.pos = 0
|
|
|
|
|
s.pos++
|
|
|
|
|
}
|
|
|
|
|
return scan_res(.name, name)
|
|
|
|
@ -323,7 +308,7 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
if c == `)` && s.inter_start {
|
|
|
|
|
s.inter_end = true
|
|
|
|
|
s.inter_start = false
|
|
|
|
|
next_char := if s.pos + 1 < s.text.len { s.text[s.pos + 1] } else { `\0` }
|
|
|
|
|
next_char := if s.pos + 1 < s.text.len {s.text[s.pos + 1]}else {`\0`}
|
|
|
|
|
if next_char == s.quote {
|
|
|
|
|
s.inside_string = false
|
|
|
|
|
}
|
|
|
|
@ -377,14 +362,14 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
`?` {
|
|
|
|
|
return scan_res(.question, '')
|
|
|
|
|
}
|
|
|
|
|
single_quote, double_quote {
|
|
|
|
|
single_quote,double_quote {
|
|
|
|
|
return scan_res(.str, s.ident_string())
|
|
|
|
|
}
|
|
|
|
|
`\`` { // ` // apostrophe balance comment. do not remove
|
|
|
|
|
`\`` {
|
|
|
|
|
// ` // apostrophe balance comment. do not remove
|
|
|
|
|
return scan_res(.chartoken, s.ident_char())
|
|
|
|
|
}
|
|
|
|
|
`(` {
|
|
|
|
|
|
|
|
|
|
return scan_res(.lpar, '')
|
|
|
|
|
}
|
|
|
|
|
`)` {
|
|
|
|
@ -406,7 +391,8 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
`$` {
|
|
|
|
|
if s.inside_string {
|
|
|
|
|
return scan_res(.str_dollar, '')
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return scan_res(.dollar, '')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -461,11 +447,21 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
// This allows things like this:
|
|
|
|
|
// println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @FN)
|
|
|
|
|
// ... which is useful while debugging/tracing
|
|
|
|
|
if name == 'FN' { return scan_res(.str, s.fn_name) }
|
|
|
|
|
if name == 'FILE' { return scan_res(.str, cescaped_path(os.realpath(s.file_path))) }
|
|
|
|
|
if name == 'LINE' { return scan_res(.str, (s.line_nr+1).str()) }
|
|
|
|
|
if name == 'COLUMN' { return scan_res(.str, (s.current_column()).str()) }
|
|
|
|
|
if name == 'VHASH' { return scan_res(.str, vhash()) }
|
|
|
|
|
if name == 'FN' {
|
|
|
|
|
return scan_res(.str, s.fn_name)
|
|
|
|
|
}
|
|
|
|
|
if name == 'FILE' {
|
|
|
|
|
return scan_res(.str, cescaped_path(os.realpath(s.file_path)))
|
|
|
|
|
}
|
|
|
|
|
if name == 'LINE' {
|
|
|
|
|
return scan_res(.str, (s.line_nr + 1).str())
|
|
|
|
|
}
|
|
|
|
|
if name == 'COLUMN' {
|
|
|
|
|
return scan_res(.str, (s.current_column()).str())
|
|
|
|
|
}
|
|
|
|
|
if name == 'VHASH' {
|
|
|
|
|
return scan_res(.str, vhash())
|
|
|
|
|
}
|
|
|
|
|
if !is_key(name) {
|
|
|
|
|
s.error('@ must be used before keywords (e.g. `@type string`)')
|
|
|
|
|
}
|
|
|
|
@ -484,10 +480,11 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
return scan_res(.nl, '')
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
`.` {
|
|
|
|
|
if nextc == `.` {
|
|
|
|
|
s.pos++
|
|
|
|
|
if s.text[s.pos+1] == `.` {
|
|
|
|
|
if s.text[s.pos + 1] == `.` {
|
|
|
|
|
s.pos++
|
|
|
|
|
return scan_res(.ellipsis, '')
|
|
|
|
|
}
|
|
|
|
@ -501,7 +498,7 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
if nextc == `!` {
|
|
|
|
|
// treat shebang line (#!) as a comment
|
|
|
|
|
s.line_comment = s.text[start + 1..s.pos].trim_space()
|
|
|
|
|
//s.fgenln('// shebang line "$s.line_comment"')
|
|
|
|
|
// s.fgenln('// shebang line "$s.line_comment"')
|
|
|
|
|
return s.scan()
|
|
|
|
|
}
|
|
|
|
|
hash := s.text[start..s.pos]
|
|
|
|
@ -525,7 +522,7 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
0xE2 {
|
|
|
|
|
//case `≠`:
|
|
|
|
|
// case `≠`:
|
|
|
|
|
if nextc == 0x89 && s.text[s.pos + 2] == 0xA0 {
|
|
|
|
|
s.pos += 2
|
|
|
|
|
return scan_res(.ne, '')
|
|
|
|
@ -610,7 +607,7 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
s.line_nr--
|
|
|
|
|
return scan_res(.line_comment, s.line_comment)
|
|
|
|
|
}
|
|
|
|
|
//s.fgenln('// ${s.prev_tok.str()} "$s.line_comment"')
|
|
|
|
|
// s.fgenln('// ${s.prev_tok.str()} "$s.line_comment"')
|
|
|
|
|
// Skip the comment (return the next token)
|
|
|
|
|
return s.scan()
|
|
|
|
|
}
|
|
|
|
@ -649,8 +646,8 @@ fn (s mut Scanner) scan() ScanRes {
|
|
|
|
|
}
|
|
|
|
|
return scan_res(.div, '')
|
|
|
|
|
}
|
|
|
|
|
else { }
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
}}
|
|
|
|
|
$if windows {
|
|
|
|
|
if c == `\0` {
|
|
|
|
|
return s.end_of_file()
|
|
|
|
@ -664,9 +661,9 @@ fn (s &Scanner) current_column() int {
|
|
|
|
|
return s.pos - s.last_nl_pos
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s Scanner) count_symbol_before(p int, sym byte) int {
|
|
|
|
|
fn (s Scanner) count_symbol_before(p int,sym byte) int {
|
|
|
|
|
mut count := 0
|
|
|
|
|
for i:=p; i>=0; i-- {
|
|
|
|
|
for i := p; i >= 0; i-- {
|
|
|
|
|
if s.text[i] != sym {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
@ -678,14 +675,14 @@ fn (s Scanner) count_symbol_before(p int, sym byte) int {
|
|
|
|
|
fn (s mut Scanner) ident_string() string {
|
|
|
|
|
q := s.text[s.pos]
|
|
|
|
|
is_quote := q == single_quote || q == double_quote
|
|
|
|
|
is_raw := is_quote && s.text[s.pos-1] == `r`
|
|
|
|
|
is_raw := is_quote && s.text[s.pos - 1] == `r`
|
|
|
|
|
if is_quote && !s.inside_string {
|
|
|
|
|
s.quote = q
|
|
|
|
|
}
|
|
|
|
|
//if s.file_path.contains('string_test') {
|
|
|
|
|
//println('\nident_string() at char=${s.text[s.pos].str()}')
|
|
|
|
|
//println('linenr=$s.line_nr quote= $qquote ${qquote.str()}')
|
|
|
|
|
//}
|
|
|
|
|
// if s.file_path.contains('string_test') {
|
|
|
|
|
// println('\nident_string() at char=${s.text[s.pos].str()}')
|
|
|
|
|
// println('linenr=$s.line_nr quote= $qquote ${qquote.str()}')
|
|
|
|
|
// }
|
|
|
|
|
mut start := s.pos
|
|
|
|
|
s.inside_string = false
|
|
|
|
|
slash := `\\`
|
|
|
|
@ -706,9 +703,9 @@ fn (s mut Scanner) ident_string() string {
|
|
|
|
|
}
|
|
|
|
|
// Don't allow \0
|
|
|
|
|
if c == `0` && s.pos > 2 && s.text[s.pos - 1] == slash {
|
|
|
|
|
if s.pos < s.text.len - 1 && s.text[s.pos+1].is_digit() {
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
if s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit() {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
s.error('0 character in a string literal')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -717,18 +714,14 @@ fn (s mut Scanner) ident_string() string {
|
|
|
|
|
s.error('0 character in a string literal')
|
|
|
|
|
}
|
|
|
|
|
// ${var} (ignore in vfmt mode)
|
|
|
|
|
if c == `{` && prevc == `$` && !is_raw && !s.is_fmt &&
|
|
|
|
|
s.count_symbol_before(s.pos-2, slash) % 2 == 0
|
|
|
|
|
{
|
|
|
|
|
if c == `{` && prevc == `$` && !is_raw && !s.is_fmt && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
|
|
|
|
s.inside_string = true
|
|
|
|
|
// so that s.pos points to $ at the next step
|
|
|
|
|
s.pos -= 2
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
// $var
|
|
|
|
|
if (c.is_letter() || c == `_`) && prevc == `$` && !s.is_fmt &&
|
|
|
|
|
!is_raw && s.count_symbol_before(s.pos-2, slash) % 2 == 0
|
|
|
|
|
{
|
|
|
|
|
if (c.is_letter() || c == `_`) && prevc == `$` && !s.is_fmt && !is_raw && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 {
|
|
|
|
|
s.inside_string = true
|
|
|
|
|
s.inter_start = true
|
|
|
|
|
s.pos -= 2
|
|
|
|
@ -743,7 +736,8 @@ fn (s mut Scanner) ident_string() string {
|
|
|
|
|
if s.inside_string {
|
|
|
|
|
end++
|
|
|
|
|
}
|
|
|
|
|
if start > s.pos{}
|
|
|
|
|
if start > s.pos {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lit = s.text[start..end]
|
|
|
|
|
}
|
|
|
|
@ -763,7 +757,8 @@ fn (s mut Scanner) ident_char() string {
|
|
|
|
|
len++
|
|
|
|
|
}
|
|
|
|
|
double_slash := s.expect('\\\\', s.pos - 2)
|
|
|
|
|
if s.text[s.pos] == `\`` && (s.text[s.pos - 1] != slash || double_slash) { // ` // apostrophe balance comment. do not remove
|
|
|
|
|
if s.text[s.pos] == `\`` && (s.text[s.pos - 1] != slash || double_slash) {
|
|
|
|
|
// ` // apostrophe balance comment. do not remove
|
|
|
|
|
if double_slash {
|
|
|
|
|
len++
|
|
|
|
|
}
|
|
|
|
@ -775,18 +770,17 @@ fn (s mut Scanner) ident_char() string {
|
|
|
|
|
if len != 1 {
|
|
|
|
|
u := c.ustring()
|
|
|
|
|
if u.len != 1 {
|
|
|
|
|
s.error('invalid character literal (more than one character)\n' +
|
|
|
|
|
'use quotes for strings, backticks for characters')
|
|
|
|
|
s.error('invalid character literal (more than one character)\n' + 'use quotes for strings, backticks for characters')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if c == '\\`' {
|
|
|
|
|
return '`'
|
|
|
|
|
}
|
|
|
|
|
// Escapes a `'` character
|
|
|
|
|
return if c == '\'' { '\\' + c } else { c }
|
|
|
|
|
return if c == "\'" {'\\' + c}else {c}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s &Scanner) expect(want string, start_pos int) bool {
|
|
|
|
|
fn (s &Scanner) expect(want string,start_pos int) bool {
|
|
|
|
|
end_pos := start_pos + want.len
|
|
|
|
|
if start_pos < 0 || start_pos >= s.text.len {
|
|
|
|
|
return false
|
|
|
|
@ -794,8 +788,8 @@ fn (s &Scanner) expect(want string, start_pos int) bool {
|
|
|
|
|
if end_pos < 0 || end_pos > s.text.len {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for pos in start_pos..end_pos {
|
|
|
|
|
if s.text[pos] != want[pos-start_pos] {
|
|
|
|
|
for pos in start_pos .. end_pos {
|
|
|
|
|
if s.text[pos] != want[pos - start_pos] {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -806,10 +800,8 @@ fn (s mut Scanner) debug_tokens() {
|
|
|
|
|
s.pos = 0
|
|
|
|
|
s.started = false
|
|
|
|
|
s.debug = true
|
|
|
|
|
|
|
|
|
|
fname := s.file_path.all_after(os.path_separator)
|
|
|
|
|
println('\n===DEBUG TOKENS $fname===')
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
res := s.scan()
|
|
|
|
|
tok := res.tok
|
|
|
|
@ -828,13 +820,12 @@ fn (s mut Scanner) debug_tokens() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) ignore_line() {
|
|
|
|
|
s.eat_to_end_of_line()
|
|
|
|
|
s.inc_line_number()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (s mut Scanner) eat_to_end_of_line(){
|
|
|
|
|
fn (s mut Scanner) eat_to_end_of_line() {
|
|
|
|
|
for s.pos < s.text.len && s.text[s.pos] != `\n` {
|
|
|
|
|
s.pos++
|
|
|
|
|
}
|
|
|
|
@ -851,9 +842,8 @@ fn (s mut Scanner) inc_line_number() {
|
|
|
|
|
|
|
|
|
|
fn (s Scanner) line(n int) string {
|
|
|
|
|
mut res := ''
|
|
|
|
|
if n >= 0 &&
|
|
|
|
|
n < s.line_ends.len {
|
|
|
|
|
nline_start := if n == 0 { 0 } else { s.line_ends[ n - 1 ] }
|
|
|
|
|
if n >= 0 && n < s.line_ends.len {
|
|
|
|
|
nline_start := if n == 0 {0}else {s.line_ends[n - 1]}
|
|
|
|
|
nline_end := s.line_ends[n]
|
|
|
|
|
if nline_start <= nline_end {
|
|
|
|
|
res = s.text[nline_start..nline_end]
|
|
|
|
@ -887,7 +877,7 @@ fn good_type_name(s string) bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
for i in 2 .. s.len {
|
|
|
|
|
if s[i].is_capital() && s[i-1].is_capital() && s[i-2].is_capital() {
|
|
|
|
|
if s[i].is_capital() && s[i - 1].is_capital() && s[i - 2].is_capital() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -898,11 +888,6 @@ fn good_type_name(s string) bool {
|
|
|
|
|
// registrationdate bad
|
|
|
|
|
fn (s &Scanner) validate_var_name(name string) {
|
|
|
|
|
if name.len > 15 && !name.contains('_') {
|
|
|
|
|
s.error('bad variable name `$name`\n' +
|
|
|
|
|
'looks like you have a multi-word name without separating them with `_`' +
|
|
|
|
|
'\nfor example, use `registration_date` instead of `registrationdate` ')
|
|
|
|
|
|
|
|
|
|
s.error('bad variable name `$name`\n' + 'looks like you have a multi-word name without separating them with `_`' + '\nfor example, use `registration_date` instead of `registrationdate` ')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|