mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 16:07:01 +00:00
Production pass: floats, GC, type checking, and tooling
Adds floats, bitwise ops, UTF-8 strings with methods, try/catch, closures, generics validation, a compile-time type checker, a mark-and-sweep GC, source-level debug info, constant folding, and the repl/fmt/package-manager commands. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
+115
-45
@@ -2,7 +2,7 @@
|
||||
module compiler
|
||||
|
||||
pub fn tokenize(src string) ![]Tok {
|
||||
mut l := Lexer{ src: src }
|
||||
mut l := Lexer{ src: src, line: 1 }
|
||||
mut toks := []Tok{}
|
||||
for {
|
||||
t := l.next()!
|
||||
@@ -16,9 +16,10 @@ pub fn tokenize(src string) ![]Tok {
|
||||
|
||||
struct Lexer {
|
||||
mut:
|
||||
src string
|
||||
pos int
|
||||
line int
|
||||
src string
|
||||
pos int
|
||||
line int
|
||||
line_start int // byte offset where the current line begins
|
||||
}
|
||||
|
||||
fn (mut l Lexer) peek() u8 {
|
||||
@@ -40,6 +41,7 @@ fn (mut l Lexer) advance() u8 {
|
||||
l.pos++
|
||||
if c == `\n` {
|
||||
l.line++
|
||||
l.line_start = l.pos
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -61,34 +63,35 @@ fn (mut l Lexer) next() !Tok {
|
||||
break
|
||||
}
|
||||
line := l.line
|
||||
col := l.pos - l.line_start + 1 // 1-based column
|
||||
if l.pos >= l.src.len {
|
||||
return Tok{ kind: .eof, lit: '', line: line }
|
||||
return Tok{ kind: .eof, lit: '', line: line, col: col }
|
||||
}
|
||||
c := l.peek()
|
||||
match c {
|
||||
`(` {
|
||||
l.advance()
|
||||
return Tok{ kind: .lparen, lit: '(', line: line }
|
||||
return Tok{ kind: .lparen, lit: '(', line: line, col: col }
|
||||
}
|
||||
`)` {
|
||||
l.advance()
|
||||
return Tok{ kind: .rparen, lit: ')', line: line }
|
||||
return Tok{ kind: .rparen, lit: ')', line: line, col: col }
|
||||
}
|
||||
`{` {
|
||||
l.advance()
|
||||
return Tok{ kind: .lbrace, lit: '{', line: line }
|
||||
return Tok{ kind: .lbrace, lit: '{', line: line, col: col }
|
||||
}
|
||||
`}` {
|
||||
l.advance()
|
||||
return Tok{ kind: .rbrace, lit: '}', line: line }
|
||||
return Tok{ kind: .rbrace, lit: '}', line: line, col: col }
|
||||
}
|
||||
`[` {
|
||||
l.advance()
|
||||
return Tok{ kind: .lbracket, lit: '[', line: line }
|
||||
return Tok{ kind: .lbracket, lit: '[', line: line, col: col }
|
||||
}
|
||||
`]` {
|
||||
l.advance()
|
||||
return Tok{ kind: .rbracket, lit: ']', line: line }
|
||||
return Tok{ kind: .rbracket, lit: ']', line: line, col: col }
|
||||
}
|
||||
`.` {
|
||||
l.advance()
|
||||
@@ -96,112 +99,165 @@ fn (mut l Lexer) next() !Tok {
|
||||
l.advance()
|
||||
if l.peek() == `.` {
|
||||
l.advance()
|
||||
return Tok{ kind: .dotdotdot, lit: '...', line: line }
|
||||
return Tok{ kind: .dotdotdot, lit: '...', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .dotdot, lit: '..', line: line }
|
||||
return Tok{ kind: .dotdot, lit: '..', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .dot, lit: '.', line: line }
|
||||
return Tok{ kind: .dot, lit: '.', line: line, col: col }
|
||||
}
|
||||
`,` {
|
||||
l.advance()
|
||||
return Tok{ kind: .comma, lit: ',', line: line }
|
||||
return Tok{ kind: .comma, lit: ',', line: line, col: col }
|
||||
}
|
||||
`:` {
|
||||
l.advance()
|
||||
return Tok{ kind: .colon, lit: ':', line: line }
|
||||
return Tok{ kind: .colon, lit: ':', line: line, col: col }
|
||||
}
|
||||
`+` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .plus_eq, lit: '+=', line: line }
|
||||
return Tok{ kind: .plus_eq, lit: '+=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .plus, lit: '+', line: line }
|
||||
return Tok{ kind: .plus, lit: '+', line: line, col: col }
|
||||
}
|
||||
`-` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .minus_eq, lit: '-=', line: line }
|
||||
return Tok{ kind: .minus_eq, lit: '-=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .minus, lit: '-', line: line }
|
||||
return Tok{ kind: .minus, lit: '-', line: line, col: col }
|
||||
}
|
||||
`*` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .star_eq, lit: '*=', line: line }
|
||||
return Tok{ kind: .star_eq, lit: '*=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .star, lit: '*', line: line }
|
||||
return Tok{ kind: .star, lit: '*', line: line, col: col }
|
||||
}
|
||||
`/` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .slash_eq, lit: '/=', line: line }
|
||||
return Tok{ kind: .slash_eq, lit: '/=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .slash, lit: '/', line: line }
|
||||
return Tok{ kind: .slash, lit: '/', line: line, col: col }
|
||||
}
|
||||
`%` {
|
||||
l.advance()
|
||||
return Tok{ kind: .percent, lit: '%', line: line }
|
||||
return Tok{ kind: .percent, lit: '%', line: line, col: col }
|
||||
}
|
||||
`&` {
|
||||
l.advance()
|
||||
return Tok{ kind: .amp, lit: '&', line: line, col: col }
|
||||
}
|
||||
`|` {
|
||||
l.advance()
|
||||
return Tok{ kind: .pipe, lit: '|', line: line, col: col }
|
||||
}
|
||||
`^` {
|
||||
l.advance()
|
||||
return Tok{ kind: .caret, lit: '^', line: line, col: col }
|
||||
}
|
||||
`~` {
|
||||
l.advance()
|
||||
return Tok{ kind: .tilde, lit: '~', line: line, col: col }
|
||||
}
|
||||
`=` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .eq_eq, lit: '==', line: line }
|
||||
return Tok{ kind: .eq_eq, lit: '==', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .assign, lit: '=', line: line }
|
||||
return Tok{ kind: .assign, lit: '=', line: line, col: col }
|
||||
}
|
||||
`!` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .not_eq, lit: '!=', line: line }
|
||||
return Tok{ kind: .not_eq, lit: '!=', line: line, col: col }
|
||||
}
|
||||
return error('unexpected character "!" at line ${line} (did you mean "not"?)')
|
||||
return error('unexpected character "!" at line ${line}, col ${col} (did you mean "not"?)')
|
||||
}
|
||||
`<` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .le, lit: '<=', line: line }
|
||||
return Tok{ kind: .le, lit: '<=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .lt, lit: '<', line: line }
|
||||
if l.peek() == `<` {
|
||||
l.advance()
|
||||
return Tok{ kind: .lt_lt, lit: '<<', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .lt, lit: '<', line: line, col: col }
|
||||
}
|
||||
`>` {
|
||||
l.advance()
|
||||
if l.peek() == `=` {
|
||||
l.advance()
|
||||
return Tok{ kind: .ge, lit: '>=', line: line }
|
||||
return Tok{ kind: .ge, lit: '>=', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .gt, lit: '>', line: line }
|
||||
if l.peek() == `>` {
|
||||
l.advance()
|
||||
return Tok{ kind: .gt_gt, lit: '>>', line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .gt, lit: '>', line: line, col: col }
|
||||
}
|
||||
`\"` {
|
||||
return l.lex_string(line)!
|
||||
return l.lex_string(line, col)!
|
||||
}
|
||||
`0`...`9` {
|
||||
return l.lex_number(line)
|
||||
return l.lex_number(line, col)
|
||||
}
|
||||
else {
|
||||
if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || c == `_` {
|
||||
return l.lex_ident(line)
|
||||
return l.lex_ident(line, col)
|
||||
}
|
||||
return error('unexpected character "${c.ascii_str()}" at line ${line}')
|
||||
return error('unexpected character "${c.ascii_str()}" at line ${line}, col ${col}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut l Lexer) lex_number(line int) Tok {
|
||||
fn (mut l Lexer) lex_number(line int, col int) Tok {
|
||||
start := l.pos
|
||||
for l.pos < l.src.len && l.peek() >= `0` && l.peek() <= `9` {
|
||||
l.advance()
|
||||
}
|
||||
return Tok{ kind: .int_lit, lit: l.src[start..l.pos], line: line }
|
||||
mut is_float := false
|
||||
// fractional part: `.` followed by a digit (so `1..3` and `1...3` stay ints)
|
||||
if l.peek() == `.` && l.pos + 1 < l.src.len && l.peek2() >= `0` && l.peek2() <= `9` {
|
||||
is_float = true
|
||||
l.advance() // consume `.`
|
||||
for l.pos < l.src.len && l.peek() >= `0` && l.peek() <= `9` {
|
||||
l.advance()
|
||||
}
|
||||
}
|
||||
// exponent part: e / E followed by optional sign and digits
|
||||
if l.peek() == `e` || l.peek() == `E` {
|
||||
save := l.pos
|
||||
l.advance()
|
||||
if l.peek() == `+` || l.peek() == `-` {
|
||||
l.advance()
|
||||
}
|
||||
if l.peek() >= `0` && l.peek() <= `9` {
|
||||
is_float = true
|
||||
for l.pos < l.src.len && l.peek() >= `0` && l.peek() <= `9` {
|
||||
l.advance()
|
||||
}
|
||||
} else {
|
||||
l.pos = save // not an exponent after all
|
||||
}
|
||||
}
|
||||
lit := l.src[start..l.pos]
|
||||
if is_float {
|
||||
return Tok{ kind: .float_lit, lit: lit, line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .int_lit, lit: lit, line: line, col: col }
|
||||
}
|
||||
|
||||
fn (mut l Lexer) lex_ident(line int) Tok {
|
||||
fn (mut l Lexer) lex_ident(line int, col int) Tok {
|
||||
start := l.pos
|
||||
for l.pos < l.src.len {
|
||||
c := l.peek()
|
||||
@@ -236,18 +292,26 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
||||
'import' { TokKind.kw_import }
|
||||
'enum' { TokKind.kw_enum }
|
||||
'const' { TokKind.kw_const }
|
||||
'interface' { TokKind.kw_interface }
|
||||
'try' { TokKind.kw_try }
|
||||
'catch' { TokKind.kw_catch }
|
||||
'throw' { TokKind.kw_throw }
|
||||
else { TokKind.ident }
|
||||
}
|
||||
return Tok{ kind: kind, lit: lit, line: line }
|
||||
return Tok{ kind: kind, lit: lit, line: line, col: col }
|
||||
}
|
||||
|
||||
fn (mut l Lexer) lex_string(line int) !Tok {
|
||||
fn (mut l Lexer) lex_string(line int, col int) !Tok {
|
||||
l.advance() // opening quote
|
||||
mut s := ''
|
||||
mut has_interp := false
|
||||
for l.pos < l.src.len {
|
||||
c := l.advance()
|
||||
if c == `\"` {
|
||||
return Tok{ kind: .str_lit, lit: s, line: line }
|
||||
if has_interp {
|
||||
return Tok{ kind: .str_interp, lit: s, line: line, col: col }
|
||||
}
|
||||
return Tok{ kind: .str_lit, lit: s, line: line, col: col }
|
||||
}
|
||||
if c == `\\` {
|
||||
if l.pos >= l.src.len {
|
||||
@@ -268,12 +332,18 @@ fn (mut l Lexer) lex_string(line int) !Tok {
|
||||
s += '\\'
|
||||
}
|
||||
else {
|
||||
return error('invalid escape \\${e.ascii_str()} at line ${line}')
|
||||
return error('invalid escape \\${e.ascii_str()} at line ${line}, col ${col}')
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c == `$` && l.peek() == `{` {
|
||||
has_interp = true
|
||||
s += '\${'
|
||||
l.advance() // skip the '{'
|
||||
continue
|
||||
}
|
||||
s += c.ascii_str()
|
||||
}
|
||||
return error('unterminated string at line ${line}')
|
||||
return error('unterminated string at line ${line}, col ${col}')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user