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