v0.3.0: restructure directories

- src/        ← compiler/selfhost/  (canonical Bux compiler)
- bootstrap/  ← compiler/bootstrap/ (Nim bootstrap)
- lib/        ← library/std/        (standard library)
- rt/         ← library/runtime/    (C runtime)
- tests/      ← compiler/tests/     (unit tests)
- Remove _selfhost/ (built into build/selfhost/ now)
- Update all path references (Makefile, cli.nim, cli.bux, docs)
- Bump version to 0.3.0
This commit is contained in:
2026-06-06 04:53:39 +03:00
parent 0dade151d2
commit ac969b37c1
65 changed files with 68 additions and 0 deletions
+816
View File
@@ -0,0 +1,816 @@
// lexer.bux — Lexer state machine (ported from lexer.nim)
// Tokenizes Bux source into a stream of tokens.
module Lexer {
extern func bux_strlen(s: String) -> uint;
// ---------------------------------------------------------------------------
// Character helpers (wrap C ctype)
// ---------------------------------------------------------------------------
func Lex_IsDigit(c: uint32) -> bool {
return c >= 48 && c <= 57; // '0'..'9'
}
func Lex_IsHexDigit(c: uint32) -> bool {
if c >= 48 && c <= 57 { return true; } // 0-9
if c >= 65 && c <= 70 { return true; } // A-F
if c >= 97 && c <= 102 { return true; } // a-f
return false;
}
func Lex_IsBinDigit(c: uint32) -> bool {
return c == 48 || c == 49; // '0' or '1'
}
func Lex_IsOctDigit(c: uint32) -> bool {
return c >= 48 && c <= 55; // '0'..'7'
}
func Lex_IsIdentStart(c: uint32) -> bool {
if c >= 97 && c <= 122 { return true; } // a-z
if c >= 65 && c <= 90 { return true; } // A-Z
return c == 95; // '_'
}
func Lex_IsIdentChar(c: uint32) -> bool {
if Lex_IsIdentStart(c) { return true; }
return Lex_IsDigit(c);
}
// ---------------------------------------------------------------------------
// Lexer state
// ---------------------------------------------------------------------------
const maxTokens: int = 32768;
const maxDiags: int = 128;
struct LexerDiag {
line: uint32;
column: uint32;
message: String;
}
struct Lexer {
source: String;
sourceLen: int;
pos: int;
line: uint32;
column: uint32;
startLine: uint32;
startColumn: uint32;
startPos: int;
tokenCount: int;
tokens: *LexToken;
diagCount: int;
diags: *LexerDiag;
}
struct LexToken {
kind: int;
text: String;
line: uint32;
column: uint32;
}
// ---------------------------------------------------------------------------
// Init
// ---------------------------------------------------------------------------
func Lexer_Init(source: String) -> Lexer {
let len: uint = bux_strlen(source);
let tokBuf: *LexToken = bux_alloc(maxTokens as uint * sizeof(LexToken)) as *LexToken;
let diagBuf: *LexerDiag = bux_alloc(maxDiags as uint * sizeof(LexerDiag)) as *LexerDiag;
return Lexer {
source: source, sourceLen: len as int,
pos: 0, line: 1, column: 1,
startLine: 0, startColumn: 0, startPos: 0,
tokenCount: 0, tokens: tokBuf,
diagCount: 0, diags: diagBuf
};
}
// ---------------------------------------------------------------------------
// Core primitives
// ---------------------------------------------------------------------------
func lexIsAtEnd(lex: *Lexer) -> bool {
return lex.pos >= lex.sourceLen;
}
func lexPeek(lex: *Lexer, ahead: int) -> uint32 {
let i: int = lex.pos + ahead;
if i < lex.sourceLen {
return (lex.source[i] as uint32) & 255;
}
return 0;
}
func lexAdvance(lex: *Lexer) -> uint32 {
let c: uint32 = lexPeek(lex, 0);
if !lexIsAtEnd(lex) {
lex.pos = lex.pos + 1;
if c == 10 { // '\n'
lex.line = lex.line + 1;
lex.column = 1;
} else {
lex.column = lex.column + 1;
}
}
return c;
}
func lexMatch(lex: *Lexer, expected: uint32) -> bool {
if lexIsAtEnd(lex) { return false; }
if lexPeek(lex, 0) != expected { return false; }
discard lexAdvance(lex);
return true;
}
func lexMatchStr(lex: *Lexer, s: String) -> bool {
let len: uint = bux_strlen(s);
var i: int = 0;
while i < (len as int) {
if lexPeek(lex, i) != (s[i] as uint32) {
return false;
}
i = i + 1;
}
i = 0;
while i < (len as int) {
discard lexAdvance(lex);
i = i + 1;
}
return true;
}
// ---------------------------------------------------------------------------
// Token emission
// ---------------------------------------------------------------------------
func lexMarkStart(lex: *Lexer) {
lex.startLine = lex.line;
lex.startColumn = lex.column;
lex.startPos = lex.pos;
}
func lexMakeToken(lex: *Lexer, kind: int) -> LexToken {
var text: String = "";
let endPos: int = lex.pos;
let startPos: int = lex.startPos;
if endPos > startPos {
let len: int = endPos - startPos;
let buf: *char8 = bux_alloc((len + 1) as uint) as *char8;
var i: int = 0;
while i < len {
buf[i] = lex.source[startPos + i] as char8;
i = i + 1;
}
buf[len] = 0 as char8;
text = buf;
} else {
text = "";
}
return LexToken {
kind: kind, text: text,
line: lex.startLine, column: lex.startColumn
};
}
func lexEmitToken(lex: *Lexer, kind: int) {
let tok: LexToken = lexMakeToken(lex, kind);
if lex.tokenCount < maxTokens {
lex.tokens[lex.tokenCount] = tok;
lex.tokenCount = lex.tokenCount + 1;
}
}
func lexSetLastTokenText(lex: *Lexer, text: String) {
if lex.tokenCount > 0 {
lex.tokens[lex.tokenCount - 1].text = text;
}
}
func lexEmitDiag(lex: *Lexer, msg: String) {
if lex.diagCount < maxDiags {
lex.diags[lex.diagCount] = LexerDiag {
line: lex.line, column: lex.column, message: msg
};
lex.diagCount = lex.diagCount + 1;
}
}
// ---------------------------------------------------------------------------
// Whitespace / comments
// ---------------------------------------------------------------------------
func lexSkipLineComment(lex: *Lexer) {
while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 10 { // '\n'
discard lexAdvance(lex);
}
}
func lexSkipBlockComment(lex: *Lexer) {
var depth: int = 1;
while !lexIsAtEnd(lex) && depth > 0 {
if lexPeek(lex, 0) == 47 && lexPeek(lex, 1) == 42 { // /*
discard lexAdvance(lex);
discard lexAdvance(lex);
depth = depth + 1;
} else if lexPeek(lex, 0) == 42 && lexPeek(lex, 1) == 47 { // */
discard lexAdvance(lex);
discard lexAdvance(lex);
depth = depth - 1;
} else {
discard lexAdvance(lex);
}
}
if depth > 0 {
lexEmitDiag(lex, "unterminated block comment");
}
}
func lexSkipWhitespace(lex: *Lexer) {
while !lexIsAtEnd(lex) {
let c: uint32 = lexPeek(lex, 0);
if c == 32 || c == 9 || c == 13 {
discard lexAdvance(lex);
} else {
if c == 47 && lexPeek(lex, 1) == 47 {
lexSkipLineComment(lex);
} else {
if c == 47 && lexPeek(lex, 1) == 42 {
discard lexAdvance(lex);
discard lexAdvance(lex);
lexSkipBlockComment(lex);
} else {
break;
}
}
}
}
}
// ---------------------------------------------------------------------------
// Identifiers
// ---------------------------------------------------------------------------
func lexIsIdentKind(tok: int) -> bool {
return tok == tkFunc || tok == tkLet || tok == tkVar || tok == tkConst
|| tok == tkType || tok == tkStruct || tok == tkEnum || tok == tkUnion
|| tok == tkInterface || tok == tkExtend || tok == tkModule || tok == tkImport
|| tok == tkPub || tok == tkExtern || tok == tkIf || tok == tkElse
|| tok == tkWhile || tok == tkDo || tok == tkLoop || tok == tkFor
|| tok == tkIn || tok == tkBreak || tok == tkContinue || tok == tkReturn
|| tok == tkMatch || tok == tkAs || tok == tkIs || tok == tkNull
|| tok == tkSelf || tok == tkSuper;
}
func lexKeywordKind(text: String) -> int {
if String_Eq(text, "true") { return tkBoolLiteral; }
if String_Eq(text, "false") { return tkBoolLiteral; }
if String_Eq(text, "func") { return tkFunc; }
if String_Eq(text, "let") { return tkLet; }
if String_Eq(text, "var") { return tkVar; }
if String_Eq(text, "const") { return tkConst; }
if String_Eq(text, "type") { return tkType; }
if String_Eq(text, "struct") { return tkStruct; }
if String_Eq(text, "enum") { return tkEnum; }
if String_Eq(text, "union") { return tkUnion; }
if String_Eq(text, "interface") { return tkInterface; }
if String_Eq(text, "extend") { return tkExtend; }
if String_Eq(text, "module") { return tkModule; }
if String_Eq(text, "import") { return tkImport; }
if String_Eq(text, "pub") { return tkPub; }
if String_Eq(text, "extern") { return tkExtern; }
if String_Eq(text, "if") { return tkIf; }
if String_Eq(text, "else") { return tkElse; }
if String_Eq(text, "while") { return tkWhile; }
if String_Eq(text, "do") { return tkDo; }
if String_Eq(text, "loop") { return tkLoop; }
if String_Eq(text, "for") { return tkFor; }
if String_Eq(text, "in") { return tkIn; }
if String_Eq(text, "break") { return tkBreak; }
if String_Eq(text, "continue") { return tkContinue; }
if String_Eq(text, "return") { return tkReturn; }
if String_Eq(text, "match") { return tkMatch; }
if String_Eq(text, "as") { return tkAs; }
if String_Eq(text, "is") { return tkIs; }
if String_Eq(text, "null") { return tkNull; }
if String_Eq(text, "self") { return tkSelf; }
if String_Eq(text, "super") { return tkSuper; }
if String_Eq(text, "sizeof") { return tkSizeOf; }
if String_Eq(text, "discard") { return tkDiscard; }
if String_Eq(text, "async") { return tkAsync; }
if String_Eq(text, "await") { return tkAwait; }
if String_Eq(text, "spawn") { return tkSpawn; }
return tkIdent;
}
func lexScanIdent(lex: *Lexer) {
lexMarkStart(lex);
while !lexIsAtEnd(lex) && Lex_IsIdentChar(lexPeek(lex, 0)) {
discard lexAdvance(lex);
}
let tok: LexToken = lexMakeToken(lex, tkIdent);
let kind: int = lexKeywordKind(tok.text);
tok.kind = kind;
if lex.tokenCount < maxTokens {
lex.tokens[lex.tokenCount] = tok;
lex.tokenCount = lex.tokenCount + 1;
}
}
// ---------------------------------------------------------------------------
// Numbers
// ---------------------------------------------------------------------------
func lexScanDigits(lex: *Lexer) {
while !lexIsAtEnd(lex) && Lex_IsDigit(lexPeek(lex, 0)) {
discard lexAdvance(lex);
}
}
func lexScanHexDigits(lex: *Lexer) {
while !lexIsAtEnd(lex) && Lex_IsHexDigit(lexPeek(lex, 0)) {
discard lexAdvance(lex);
}
}
func lexScanNumber(lex: *Lexer) {
lexMarkStart(lex);
var isFloat: bool = false;
if lexPeek(lex, 0) == 48 { // '0'
let p1: uint32 = lexPeek(lex, 1);
if p1 == 120 || p1 == 88 || p1 == 98 || p1 == 66 || p1 == 111 || p1 == 79 { // x X b B o O
discard lexAdvance(lex); // 0
discard lexAdvance(lex); // prefix
if p1 == 120 || p1 == 88 { lexScanHexDigits(lex); }
else if p1 == 98 || p1 == 66 {
while !lexIsAtEnd(lex) && Lex_IsBinDigit(lexPeek(lex, 0)) {
discard lexAdvance(lex);
}
}
else {
while !lexIsAtEnd(lex) && Lex_IsOctDigit(lexPeek(lex, 0)) {
discard lexAdvance(lex);
}
}
lexEmitToken(lex, tkIntLiteral);
return;
}
}
lexScanDigits(lex);
if lexPeek(lex, 0) == 46 && Lex_IsDigit(lexPeek(lex, 1)) { // . digit
isFloat = true;
discard lexAdvance(lex); // .
lexScanDigits(lex);
}
let e: uint32 = lexPeek(lex, 0);
if e == 101 || e == 69 { // e E
isFloat = true;
discard lexAdvance(lex);
let sign: uint32 = lexPeek(lex, 0);
if sign == 43 || sign == 45 { discard lexAdvance(lex); } // + -
lexScanDigits(lex);
}
if isFloat {
lexEmitToken(lex, tkFloatLiteral);
} else {
lexEmitToken(lex, tkIntLiteral);
}
}
// ---------------------------------------------------------------------------
// Strings and chars
// ---------------------------------------------------------------------------
func lexScanBacktickString(lex: *Lexer) {
lexMarkStart(lex);
if lexPeek(lex, 0) == 96 { discard lexAdvance(lex); } // opening backtick
while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 96 {
discard lexAdvance(lex);
}
if lexIsAtEnd(lex) {
lexEmitDiag(lex, "unterminated backtick string literal");
} else {
discard lexAdvance(lex); // closing backtick
}
lexEmitToken(lex, tkStringLiteral);
}
func lexScanString(lex: *Lexer) {
lexMarkStart(lex);
// Collect the prefix (before opening quote) for the token text
var prefix: String = "";
var prefixLen: int = 0;
if lex.pos > lex.startPos {
let plen: int = lex.pos - lex.startPos;
let pbuf: *char8 = bux_alloc((plen + 1) as uint) as *char8;
var pi: int = 0;
while pi < plen {
pbuf[pi] = lex.source[lex.startPos + pi] as char8;
pi = pi + 1;
}
pbuf[plen] = 0 as char8;
prefix = pbuf;
prefixLen = plen;
}
// Allocate buffer for resolved content (max = remaining source length)
let maxLen: int = 4096;
let resolved: *char8 = bux_alloc(maxLen as uint) as *char8;
var rpos: int = 0;
if lexPeek(lex, 0) == 34 { discard lexAdvance(lex); } // opening "
while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 34 { // closing "
if lexPeek(lex, 0) == 10 { // \n
lexEmitDiag(lex, "unterminated string literal");
break;
}
if lexPeek(lex, 0) == 92 { // backslash
discard lexAdvance(lex);
if !lexIsAtEnd(lex) {
let ec: uint32 = lexAdvance(lex);
var rc: char8 = ec as char8;
if ec == 110 { rc = 10 as char8; } // \n
else if ec == 114 { rc = 13 as char8; } // \r
else if ec == 116 { rc = 9 as char8; } // \t
else if ec == 48 { rc = 0 as char8; } // \0
else if ec == 92 { rc = 92 as char8; } // \\
else if ec == 34 { rc = 34 as char8; } // \"
else if ec == 39 { rc = 39 as char8; } // \'
resolved[rpos] = rc;
rpos = rpos + 1;
}
} else {
let c: uint32 = lexAdvance(lex);
resolved[rpos] = c as char8;
rpos = rpos + 1;
}
}
resolved[rpos] = 0 as char8;
if lexIsAtEnd(lex) {
lexEmitDiag(lex, "unterminated string literal");
} else {
discard lexAdvance(lex); // closing "
}
lexEmitToken(lex, tkStringLiteral);
// Replace token text with resolved version: prefix + " + resolved + "
let totalLen: int = prefixLen + 1 + rpos + 1;
let finalBuf: *char8 = bux_alloc((totalLen + 1) as uint) as *char8;
var fi: int = 0;
var pi2: int = 0;
while pi2 < prefixLen { finalBuf[fi] = prefix[pi2] as char8; fi = fi + 1; pi2 = pi2 + 1; }
finalBuf[fi] = 34 as char8; fi = fi + 1; // opening "
var ri: int = 0;
while ri < rpos { finalBuf[fi] = resolved[ri]; fi = fi + 1; ri = ri + 1; }
finalBuf[fi] = 34 as char8; fi = fi + 1; // closing "
finalBuf[fi] = 0 as char8;
lexSetLastTokenText(lex, finalBuf);
}
func lexScanChar(lex: *Lexer) {
lexMarkStart(lex);
// Collect the prefix for the token text
var prefix: String = "";
var prefixLen: int = 0;
if lex.pos > lex.startPos {
let plen: int = lex.pos - lex.startPos;
let pbuf: *char8 = bux_alloc((plen + 1) as uint) as *char8;
var pi: int = 0;
while pi < plen {
pbuf[pi] = lex.source[lex.startPos + pi] as char8;
pi = pi + 1;
}
pbuf[plen] = 0 as char8;
prefix = pbuf;
prefixLen = plen;
}
var resolved: char8 = 0 as char8;
if lexPeek(lex, 0) == 39 { discard lexAdvance(lex); } // opening '
if lexIsAtEnd(lex) {
lexEmitDiag(lex, "unterminated char literal");
lexEmitToken(lex, tkCharLiteral);
return;
}
if lexPeek(lex, 0) == 10 { // \n
lexEmitDiag(lex, "newline in char literal");
} else if lexPeek(lex, 0) == 92 { // backslash
discard lexAdvance(lex);
if !lexIsAtEnd(lex) {
let ec: uint32 = lexAdvance(lex);
if ec == 110 { resolved = 10 as char8; } // \n
else if ec == 114 { resolved = 13 as char8; } // \r
else if ec == 116 { resolved = 9 as char8; } // \t
else if ec == 48 { resolved = 0 as char8; } // \0
else if ec == 92 { resolved = 92 as char8; } // \\
else if ec == 34 { resolved = 34 as char8; } // \"
else if ec == 39 { resolved = 39 as char8; } // \'
else { resolved = ec as char8; }
}
} else {
resolved = lexAdvance(lex) as char8;
}
if lexIsAtEnd(lex) || lexPeek(lex, 0) != 39 {
lexEmitDiag(lex, "expected closing ' for char literal");
} else {
discard lexAdvance(lex); // closing '
}
lexEmitToken(lex, tkCharLiteral);
// Replace token text with resolved version
let totalLen: int = prefixLen + 1 + 1 + 1;
let finalBuf: *char8 = bux_alloc((totalLen + 1) as uint) as *char8;
var fi: int = 0;
var pi2: int = 0;
while pi2 < prefixLen { finalBuf[fi] = prefix[pi2] as char8; fi = fi + 1; pi2 = pi2 + 1; }
finalBuf[fi] = 39 as char8; fi = fi + 1; // opening '
finalBuf[fi] = resolved; fi = fi + 1; // resolved char
finalBuf[fi] = 39 as char8; fi = fi + 1; // closing '
finalBuf[fi] = 0 as char8;
lexSetLastTokenText(lex, finalBuf);
}
// ---------------------------------------------------------------------------
// Symbols / operators
// ---------------------------------------------------------------------------
func lexScanSymbol(lex: *Lexer) {
lexMarkStart(lex);
let c: uint32 = lexAdvance(lex);
// Single-char tokens
if c == 40 { lexEmitToken(lex, tkLParen); return; }
if c == 41 { lexEmitToken(lex, tkRParen); return; }
if c == 123 { lexEmitToken(lex, tkLBrace); return; }
if c == 125 { lexEmitToken(lex, tkRBrace); return; }
if c == 91 { lexEmitToken(lex, tkLBracket); return; }
if c == 93 { lexEmitToken(lex, tkRBracket); return; }
if c == 44 { lexEmitToken(lex, tkComma); return; }
if c == 59 { lexEmitToken(lex, tkSemicolon); return; }
if c == 64 { lexEmitToken(lex, tkAt); return; }
if c == 63 { lexEmitToken(lex, tkQuestion); return; }
if c == 126 { lexEmitToken(lex, tkTilde); return; }
// : ::
if c == 58 {
if lexMatch(lex, 58) { lexEmitToken(lex, tkColonColon); }
else { lexEmitToken(lex, tkColon); }
return;
}
// . .. ... ..=
if c == 46 {
if lexPeek(lex, 0) == 46 && lexPeek(lex, 1) == 46 {
discard lexAdvance(lex); discard lexAdvance(lex);
lexEmitToken(lex, tkDotDotDot); return;
}
if lexPeek(lex, 0) == 46 && lexPeek(lex, 1) == 61 {
discard lexAdvance(lex); discard lexAdvance(lex);
lexEmitToken(lex, tkDotDotEqual); return;
}
if lexMatch(lex, 46) { lexEmitToken(lex, tkDotDot); return; }
lexEmitToken(lex, tkDot); return;
}
// - -> -- -=
if c == 45 {
if lexMatch(lex, 62) { lexEmitToken(lex, tkArrow); return; }
if lexMatch(lex, 45) { lexEmitToken(lex, tkMinusMinus); return; }
if lexMatch(lex, 61) { lexEmitToken(lex, tkMinusAssign); return; }
lexEmitToken(lex, tkMinus); return;
}
// + ++ +=
if c == 43 {
if lexMatch(lex, 43) { lexEmitToken(lex, tkPlusPlus); return; }
if lexMatch(lex, 61) { lexEmitToken(lex, tkPlusAssign); return; }
lexEmitToken(lex, tkPlus); return;
}
// * ** *=
if c == 42 {
if lexMatch(lex, 42) { lexEmitToken(lex, tkStarStar); return; }
if lexMatch(lex, 61) { lexEmitToken(lex, tkStarAssign); return; }
lexEmitToken(lex, tkStar); return;
}
// / /=
if c == 47 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkSlashAssign); return; }
lexEmitToken(lex, tkSlash); return;
}
// % %=
if c == 37 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkPercentAssign); return; }
lexEmitToken(lex, tkPercent); return;
}
// = == =>
if c == 61 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkEq); return; }
if lexMatch(lex, 62) { lexEmitToken(lex, tkFatArrow); return; }
lexEmitToken(lex, tkAssign); return;
}
// ! !=
if c == 33 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkNe); return; }
lexEmitToken(lex, tkBang); return;
}
// < <= << <<=
if c == 60 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkLe); return; }
if lexMatch(lex, 60) {
if lexMatch(lex, 61) { lexEmitToken(lex, tkShlAssign); return; }
lexEmitToken(lex, tkShl); return;
}
lexEmitToken(lex, tkLt); return;
}
// > >= >> >>=
if c == 62 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkGe); return; }
if lexMatch(lex, 62) {
if lexMatch(lex, 61) { lexEmitToken(lex, tkShrAssign); return; }
lexEmitToken(lex, tkShr); return;
}
lexEmitToken(lex, tkGt); return;
}
// & && &=
if c == 38 {
if lexMatch(lex, 38) { lexEmitToken(lex, tkAmpAmp); return; }
if lexMatch(lex, 61) { lexEmitToken(lex, tkAmpAssign); return; }
lexEmitToken(lex, tkAmp); return;
}
// | || |=
if c == 124 {
if lexMatch(lex, 124) { lexEmitToken(lex, tkPipePipe); return; }
if lexMatch(lex, 61) { lexEmitToken(lex, tkPipeAssign); return; }
lexEmitToken(lex, tkPipe); return;
}
// ^ ^=
if c == 94 {
if lexMatch(lex, 61) { lexEmitToken(lex, tkCaretAssign); return; }
lexEmitToken(lex, tkCaret); return;
}
// # intrinsics
if c == 35 {
if lexMatchStr(lex, "line") { lexEmitToken(lex, tkHashLine); return; }
if lexMatchStr(lex, "column") { lexEmitToken(lex, tkHashColumn); return; }
if lexMatchStr(lex, "file") { lexEmitToken(lex, tkHashFile); return; }
if lexMatchStr(lex, "function") { lexEmitToken(lex, tkHashFunction); return; }
if lexMatchStr(lex, "date") { lexEmitToken(lex, tkHashDate); return; }
if lexMatchStr(lex, "time") { lexEmitToken(lex, tkHashTime); return; }
if lexMatchStr(lex, "module") { lexEmitToken(lex, tkHashModule); return; }
lexEmitToken(lex, tkHash); return;
}
lexEmitDiag(lex, "unexpected character");
lexEmitToken(lex, tkUnknown);
}
// ---------------------------------------------------------------------------
// Next token
// ---------------------------------------------------------------------------
func lexNextToken(lex: *Lexer) {
lexSkipWhitespace(lex);
if lexIsAtEnd(lex) {
lexMarkStart(lex);
lexEmitToken(lex, tkEndOfFile);
return;
}
let c: uint32 = lexPeek(lex, 0);
if c == 10 { // \n
lexMarkStart(lex);
discard lexAdvance(lex);
lexEmitToken(lex, tkNewLine);
return;
}
// String prefixes: c8" c16" c32"
if c == 99 { // 'c'
let d: uint32 = lexPeek(lex, 1);
if d == 56 && lexPeek(lex, 2) == 34 { // c8"
discard lexAdvance(lex); discard lexAdvance(lex); // c 8
lexScanString(lex); return;
}
if d == 49 && lexPeek(lex, 2) == 54 && lexPeek(lex, 3) == 34 { // c16"
discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex);
lexScanString(lex); return;
}
if d == 51 && lexPeek(lex, 2) == 50 && lexPeek(lex, 3) == 34 { // c32"
discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex);
lexScanString(lex); return;
}
}
if c == 34 { // "
lexScanString(lex); return;
}
// Backtick raw string
if c == 96 { // backtick
lexScanBacktickString(lex); return;
}
// Char prefixes: c8' c16' c32'
if c == 99 { // 'c'
let d: uint32 = lexPeek(lex, 1);
if d == 56 && lexPeek(lex, 2) == 39 { // c8'
discard lexAdvance(lex); discard lexAdvance(lex);
lexScanChar(lex); return;
}
if d == 49 && lexPeek(lex, 2) == 54 && lexPeek(lex, 3) == 39 { // c16'
discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex);
lexScanChar(lex); return;
}
if d == 51 && lexPeek(lex, 2) == 50 && lexPeek(lex, 3) == 39 { // c32'
discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex);
lexScanChar(lex); return;
}
}
if c == 39 { // '
lexScanChar(lex); return;
}
if Lex_IsIdentStart(c) {
lexScanIdent(lex); return;
}
if Lex_IsDigit(c) {
lexScanNumber(lex); return;
}
lexScanSymbol(lex);
}
// ---------------------------------------------------------------------------
// Tokenize — main entry point
// ---------------------------------------------------------------------------
func Lexer_Tokenize(source: String) -> *Lexer {
let lex: *Lexer = bux_alloc(sizeof(Lexer)) as *Lexer;
lex.source = source;
lex.sourceLen = bux_strlen(source) as int;
lex.pos = 0;
lex.line = 1;
lex.column = 1;
lex.startLine = 0;
lex.startColumn = 0;
lex.startPos = 0;
let tokBuf: *LexToken = bux_alloc(maxTokens as uint * sizeof(LexToken)) as *LexToken;
let diagBuf: *LexerDiag = bux_alloc(maxDiags as uint * sizeof(LexerDiag)) as *LexerDiag;
lex.tokens = tokBuf;
lex.diags = diagBuf;
lex.tokenCount = 0;
lex.diagCount = 0;
while true {
lexNextToken(lex);
if lex.tokenCount >= maxTokens {
lexEmitDiag(lex, "too many tokens");
break;
}
if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile {
break;
}
}
return lex;
}
func Lexer_TokenCount(lex: *Lexer) -> int {
return lex.tokenCount;
}
func Lexer_GetToken(lex: *Lexer, index: int) -> LexToken {
if index >= 0 && index < lex.tokenCount {
return lex.tokens[index];
}
var empty: LexToken = LexToken { kind: tkEndOfFile, text: "", line: 0, column: 0 };
return empty;
}
func Lexer_DiagCount(lex: *Lexer) -> int {
return lex.diagCount;
}
func Lexer_Free(lex: *Lexer) {
bux_free(lex.tokens as *void);
bux_free(lex.diags as *void);
bux_free(lex as *void);
}
}