add break and continue statements to El

This commit is contained in:
Will Anderson
2026-05-03 15:43:20 -05:00
parent 3cc9b1cc3d
commit 252ad04c96
3 changed files with 60 additions and 35 deletions
+16 -14
View File
@@ -1,4 +1,4 @@
// lexer.el el self-hosting lexer
// lexer.el - el self-hosting lexer
//
// Tokenises an el source string into a list of token maps.
// Each token is a Map<String, Any> with keys:
@@ -8,9 +8,9 @@
// Entry point: fn lex(source: String) -> [Map<String, Any>]
//
// Uses native_string_chars to split the source into a chars list,
// then indexes it with native_list_get avoids O(N²) string cloning.
// then indexes it with native_list_get - avoids O(N-) string cloning.
// Character helpers
// -- Character helpers ---------------------------------------------------------
fn lex_is_digit(ch: String) -> Bool {
if ch == "0" { return true }
@@ -101,7 +101,7 @@ fn make_tok(kind: String, value: String) -> Map<String, Any> {
{ "kind": kind, "value": value }
}
// Keyword lookup
// -- Keyword lookup ------------------------------------------------------------
fn keyword_kind(word: String) -> String {
if word == "let" { return "Let" }
@@ -147,13 +147,15 @@ fn keyword_kind(word: String) -> String {
if word == "accessor" { return "Accessor" }
if word == "vessel" { return "Vessel" }
if word == "extern" { return "Extern" }
if word == "break" { return "Break" }
if word == "continue" { return "Continue" }
""
}
// Scan helpers
// -- Scan helpers --------------------------------------------------------------
// All scan helpers receive the chars list and total length.
// scan_digits advance i while chars[i] is a digit
// scan_digits - advance i while chars[i] is a digit
// Returns { "text": ..., "pos": i }
fn scan_digits(chars: [String], start: Int, total: Int) -> Map<String, Any> {
let i = start
@@ -175,7 +177,7 @@ fn scan_digits(chars: [String], start: Int, total: Int) -> Map<String, Any> {
{ "text": str_join(parts, ""), "pos": i }
}
// scan_ident advance i while chars[i] is alphanumeric or underscore
// scan_ident - advance i while chars[i] is alphanumeric or underscore
fn scan_ident(chars: [String], start: Int, total: Int) -> Map<String, Any> {
let i = start
let parts: [String] = native_list_empty()
@@ -196,14 +198,14 @@ fn scan_ident(chars: [String], start: Int, total: Int) -> Map<String, Any> {
{ "text": str_join(parts, ""), "pos": i }
}
// Code-bearing string detection + comment strip
// Inline JS/CSS literals embedded in El source (e.g. <script></script> blobs
// -- Code-bearing string detection + comment strip ----------------------------
// Inline JS/CSS literals embedded in El source (e.g. <script>-</script> blobs
// or stylesheet payloads inside string literals) carry their own line and
// block comments. Those comments leak into the served HTML and reveal build
// notes the visitor should never see. We strip them at the lexer so every
// downstream consumer (codegen-c, codegen-js, parser) gets the cleaned form.
//
// looks_like_code heuristic gate so we only strip strings that actually
// looks_like_code - heuristic gate so we only strip strings that actually
// embed JS or CSS. Plain prose, hex blobs, JSON, etc. pass through verbatim.
fn substr_at(chars: [String], start: Int, total: Int, needle: String) -> Bool {
@@ -245,7 +247,7 @@ fn looks_like_code(s: String) -> Bool {
false
}
// strip_code_comments character-by-character walk. Tracks JS string state
// strip_code_comments - character-by-character walk. Tracks JS string state
// (single, double, backtick) and never strips inside one. Backslash escapes
// inside JS strings consume the next char verbatim. URLs like https:// are
// preserved by checking the previous char before treating // as a line
@@ -398,7 +400,7 @@ fn strip_code_comments(s: String) -> String {
str_join(out_parts, "")
}
// scan_string scan a quoted string literal, handling \" escapes.
// scan_string - scan a quoted string literal, handling \" escapes.
// Starts AFTER the opening quote. Returns { "text": content, "pos": i_after_close }
fn scan_string(chars: [String], start: Int, total: Int) -> Map<String, Any> {
let i = start
@@ -458,7 +460,7 @@ fn scan_string(chars: [String], start: Int, total: Int) -> Map<String, Any> {
{ "text": str_join(parts, ""), "pos": i }
}
// Main lexer
// -- Main lexer ----------------------------------------------------------------
fn lex(source: String) -> [Map<String, Any>] {
let chars: [String] = native_string_chars(source)
@@ -711,7 +713,7 @@ fn lex(source: String) -> [Map<String, Any>] {
let tokens = native_list_append(tokens, make_tok("QuestionMark", "?"))
let i = i + 1
} else {
// unknown char skip
// unknown char - skip
let i = i + 1
}
}