mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Add custom semicolon insertion to odin/tokenizer and odin/parser
This commit is contained in:
@@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||||
|
prev_parser := p^;
|
||||||
|
defer p^ = prev_parser;
|
||||||
|
|
||||||
|
p.tok.err = nil;
|
||||||
|
for i := 0; i <= lookahead; i += 1 {
|
||||||
|
advance_token(p);
|
||||||
|
}
|
||||||
|
tok = p.curr_tok;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||||
|
if .Insert_Semicolon not_in p.tok.flags {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev := p.curr_tok;
|
||||||
|
if tokenizer.is_newline(prev) {
|
||||||
|
advance_token(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
|
||||||
|
if .Insert_Semicolon not_in p.tok.flags {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
curr_pos := p.curr_tok.pos;
|
||||||
|
if tokenizer.is_newline(p.curr_tok) {
|
||||||
|
next := peek_token(p);
|
||||||
|
if curr_pos.line+1 >= next.pos.line {
|
||||||
|
#partial switch next.kind {
|
||||||
|
case .Open_Brace, .Else, .Where:
|
||||||
|
advance_token(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
next_token0 :: proc(p: ^Parser) -> bool {
|
next_token0 :: proc(p: ^Parser) -> bool {
|
||||||
p.curr_tok = tokenizer.scan(&p.tok);
|
p.curr_tok = tokenizer.scan(&p.tok);
|
||||||
@@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token
|
|||||||
prev := p.curr_tok;
|
prev := p.curr_tok;
|
||||||
if prev.kind != kind {
|
if prev.kind != kind {
|
||||||
e := tokenizer.to_string(kind);
|
e := tokenizer.to_string(kind);
|
||||||
g := tokenizer.to_string(prev.kind);
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected '%s', got '%s'", e, g);
|
error(p, prev.pos, "expected '%s', got '%s'", e, g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
|
|||||||
prev := p.curr_tok;
|
prev := p.curr_tok;
|
||||||
if prev.kind != kind {
|
if prev.kind != kind {
|
||||||
e := tokenizer.to_string(kind);
|
e := tokenizer.to_string(kind);
|
||||||
g := tokenizer.to_string(prev.kind);
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
|
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -303,7 +347,7 @@ expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
|
|||||||
if prev.kind == .If || prev.kind == .When {
|
if prev.kind == .If || prev.kind == .When {
|
||||||
// okay
|
// okay
|
||||||
} else if !tokenizer.is_operator(prev.kind) {
|
} else if !tokenizer.is_operator(prev.kind) {
|
||||||
g := tokenizer.to_string(prev.kind);
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected an operator, got '%s'", g);
|
error(p, prev.pos, "expected an operator, got '%s'", g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -400,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node != nil {
|
if node != nil {
|
||||||
if prev.pos.line != p.curr_tok.pos.line {
|
if .Insert_Semicolon in p.tok.flags {
|
||||||
|
#partial switch p.curr_tok.kind {
|
||||||
|
case .Close_Brace, .Close_Paren, .Else, .EOF:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_semicolon_optional_for_node(p, node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if prev.pos.line != p.curr_tok.pos.line {
|
||||||
if is_semicolon_optional_for_node(p, node) {
|
if is_semicolon_optional_for_node(p, node) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -420,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind));
|
error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, true);
|
body = parse_block_stmt(p, true);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if allow_token(p, .Else) {
|
if allow_token(p, .Else) {
|
||||||
@@ -568,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, false);
|
body = parse_block_stmt(p, false);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if allow_token(p, .Else) {
|
if allow_token(p, .Else) {
|
||||||
@@ -629,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_body(p);
|
body = parse_body(p);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
|
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
|
||||||
@@ -663,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_body(p);
|
body = parse_body(p);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -840,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
|||||||
attribute.elems = elems[:];
|
attribute.elems = elems[:];
|
||||||
attribute.close = close.pos;
|
attribute.close = close.pos;
|
||||||
|
|
||||||
|
skip_possible_newline(p);
|
||||||
|
|
||||||
decl := parse_stmt(p);
|
decl := parse_stmt(p);
|
||||||
switch d in &decl.derived {
|
switch d in &decl.derived {
|
||||||
case ast.Value_Decl:
|
case ast.Value_Decl:
|
||||||
@@ -1028,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, false);
|
body = parse_block_stmt(p, false);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if bad_stmt {
|
if bad_stmt {
|
||||||
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
||||||
@@ -1206,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tok := advance_token(p);
|
tok := advance_token(p);
|
||||||
error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind));
|
error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
|
||||||
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
|
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -2158,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
prev_level := p.expr_level;
|
prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
@@ -2334,7 +2397,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
where_prev_level := p.expr_level;
|
where_prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
@@ -2397,7 +2463,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
where_prev_level := p.expr_level;
|
where_prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
@@ -2730,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
case .Colon:
|
case .Colon:
|
||||||
interval = advance_token(p);
|
interval = advance_token(p);
|
||||||
is_slice_op = true;
|
is_slice_op = true;
|
||||||
if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) {
|
if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
|
||||||
indicies[1] = parse_expr(p, false);
|
indicies[1] = parse_expr(p, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,6 +283,19 @@ tokens := [Token_Kind.COUNT]string {
|
|||||||
|
|
||||||
custom_keyword_tokens: []string;
|
custom_keyword_tokens: []string;
|
||||||
|
|
||||||
|
|
||||||
|
is_newline :: proc(tok: Token) -> bool {
|
||||||
|
return tok.kind == .Semicolon && tok.text == "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
token_to_string :: proc(tok: Token) -> string {
|
||||||
|
if is_newline(tok) {
|
||||||
|
return "newline";
|
||||||
|
}
|
||||||
|
return to_string(tok.kind);
|
||||||
|
}
|
||||||
|
|
||||||
to_string :: proc(kind: Token_Kind) -> string {
|
to_string :: proc(kind: Token_Kind) -> string {
|
||||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||||
return tokens[kind];
|
return tokens[kind];
|
||||||
|
|||||||
@@ -1,22 +1,31 @@
|
|||||||
package odin_tokenizer
|
package odin_tokenizer
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:unicode"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||||
|
|
||||||
|
Flag :: enum {
|
||||||
|
Insert_Semicolon,
|
||||||
|
}
|
||||||
|
Flags :: distinct bit_set[Flag; u32];
|
||||||
|
|
||||||
Tokenizer :: struct {
|
Tokenizer :: struct {
|
||||||
// Immutable data
|
// Immutable data
|
||||||
path: string,
|
path: string,
|
||||||
src: []byte,
|
src: []byte,
|
||||||
err: Error_Handler,
|
err: Error_Handler,
|
||||||
|
|
||||||
|
flags: Flags,
|
||||||
|
|
||||||
// Tokenizing state
|
// Tokenizing state
|
||||||
ch: rune,
|
ch: rune,
|
||||||
offset: int,
|
offset: int,
|
||||||
read_offset: int,
|
read_offset: int,
|
||||||
line_offset: int,
|
line_offset: int,
|
||||||
line_count: int,
|
line_count: int,
|
||||||
|
insert_semicolon: bool,
|
||||||
|
|
||||||
// Mutable data
|
// Mutable data
|
||||||
error_count: int,
|
error_count: int,
|
||||||
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||||
for t.ch == ' ' ||
|
for {
|
||||||
t.ch == '\t' ||
|
switch t.ch {
|
||||||
t.ch == '\n' ||
|
case ' ', '\t', '\r':
|
||||||
t.ch == '\r' {
|
advance_rune(t);
|
||||||
advance_rune(t);
|
case '\n':
|
||||||
|
if t.insert_semicolon {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
advance_rune(t);
|
||||||
|
case:
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(bill): Add unicode lookup tables
|
return unicode.is_letter(r);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
is_digit :: proc(r: rune) -> bool {
|
is_digit :: proc(r: rune) -> bool {
|
||||||
// TODO(bill): Add unicode lookup tables
|
if '0' <= r && r <= '9' {
|
||||||
return '0' <= r && r <= '9';
|
return true;
|
||||||
|
}
|
||||||
|
return unicode.is_digit(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
lit: string;
|
lit: string;
|
||||||
pos := offset_to_pos(t, offset);
|
pos := offset_to_pos(t, offset);
|
||||||
|
|
||||||
|
insert_semicolon := false;
|
||||||
|
|
||||||
switch ch := t.ch; true {
|
switch ch := t.ch; true {
|
||||||
case is_letter(ch):
|
case is_letter(ch):
|
||||||
lit = scan_identifier(t);
|
lit = scan_identifier(t);
|
||||||
@@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
break check_keyword;
|
break check_keyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if kind == .Ident && lit == "notin" {
|
|
||||||
kind = .Not_In;
|
#partial switch kind {
|
||||||
|
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
|
||||||
|
insert_semicolon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '0' <= ch && ch <= '9':
|
case '0' <= ch && ch <= '9':
|
||||||
|
insert_semicolon = true;
|
||||||
kind, lit = scan_number(t, false);
|
kind, lit = scan_number(t, false);
|
||||||
case:
|
case:
|
||||||
advance_rune(t);
|
advance_rune(t);
|
||||||
switch ch {
|
switch ch {
|
||||||
case -1:
|
case -1:
|
||||||
kind = .EOF;
|
kind = .EOF;
|
||||||
|
if t.insert_semicolon {
|
||||||
|
t.insert_semicolon = false;
|
||||||
|
kind = .Semicolon;
|
||||||
|
lit = "\n";
|
||||||
|
}
|
||||||
|
case '\n':
|
||||||
|
t.insert_semicolon = false;
|
||||||
|
kind = .Semicolon;
|
||||||
|
lit = "\n";
|
||||||
case '"':
|
case '"':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .String;
|
kind = .String;
|
||||||
lit = scan_string(t);
|
lit = scan_string(t);
|
||||||
case '\'':
|
case '\'':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .Rune;
|
kind = .Rune;
|
||||||
lit = scan_rune(t);
|
lit = scan_rune(t);
|
||||||
case '`':
|
case '`':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .String;
|
kind = .String;
|
||||||
lit = scan_raw_string(t);
|
lit = scan_raw_string(t);
|
||||||
case '=':
|
case '=':
|
||||||
@@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case '#':
|
case '#':
|
||||||
kind = .Hash;
|
kind = .Hash;
|
||||||
if t.ch == '!' {
|
if t.ch == '!' {
|
||||||
|
insert_semicolon = t.insert_semicolon;
|
||||||
kind = .Comment;
|
kind = .Comment;
|
||||||
lit = scan_comment(t);
|
lit = scan_comment(t);
|
||||||
}
|
}
|
||||||
case '?': kind = .Question;
|
case '?':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Question;
|
||||||
case '@': kind = .At;
|
case '@': kind = .At;
|
||||||
case '$': kind = .Dollar;
|
case '$': kind = .Dollar;
|
||||||
case '^': kind = .Pointer;
|
case '^': kind = .Pointer;
|
||||||
@@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
||||||
case '/':
|
case '/':
|
||||||
if t.ch == '/' || t.ch == '*' {
|
if t.ch == '/' || t.ch == '*' {
|
||||||
|
insert_semicolon = t.insert_semicolon;
|
||||||
kind = .Comment;
|
kind = .Comment;
|
||||||
lit = scan_comment(t);
|
lit = scan_comment(t);
|
||||||
} else {
|
} else {
|
||||||
@@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case ',': kind = .Comma;
|
case ',': kind = .Comma;
|
||||||
case ';': kind = .Semicolon;
|
case ';': kind = .Semicolon;
|
||||||
case '(': kind = .Open_Paren;
|
case '(': kind = .Open_Paren;
|
||||||
case ')': kind = .Close_Paren;
|
case ')':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Paren;
|
||||||
case '[': kind = .Open_Bracket;
|
case '[': kind = .Open_Bracket;
|
||||||
case ']': kind = .Close_Bracket;
|
case ']':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Bracket;
|
||||||
case '{': kind = .Open_Brace;
|
case '{': kind = .Open_Brace;
|
||||||
case '}': kind = .Close_Brace;
|
case '}':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Brace;
|
||||||
|
|
||||||
case '\\': kind = .Back_Slash;
|
case '\\': kind = .Back_Slash;
|
||||||
|
|
||||||
@@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
if ch != utf8.RUNE_BOM {
|
if ch != utf8.RUNE_BOM {
|
||||||
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||||
}
|
}
|
||||||
|
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
|
||||||
kind = .Invalid;
|
kind = .Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if .Insert_Semicolon in t.flags {
|
||||||
|
t.insert_semicolon = insert_semicolon;
|
||||||
|
}
|
||||||
|
|
||||||
if lit == "" {
|
if lit == "" {
|
||||||
lit = string(t.src[offset : t.offset]);
|
lit = string(t.src[offset : t.offset]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user