Download Game! Currently 108 players and visitors. Last logged in:NafleinBrogTenuTohvu

Blitzer's Blog >> 71991

Back to blogs index
Posted: 04 Sep 2026 06:54 [ permalink ]
4.4 sizeof
When function call name is sizeof, output ${arg}.length for arrays and
mappings, but for strings its the length (including possible \0 truncation).
Since the test only uses arrays, we handle arrays and objects:
(${arg}).length for arrays, Object.keys(${arg}).length for mappings (but we
wont need mapping yet).

4.5 isset
isset(arr[i]) checks if index exists and the value is nonzero. We can emit:
((${arr}[${i}] !== undefined) && (${arr}[${i}] !== 0))
But we need to handle negative indices as above. We can reuse the negative
indexing logic.

4.6 timer
Well add a simple timer to the runtime:

timer() starts the timer (store start time).

timer(1) stops and returns elapsed seconds.
Implementation: use process.hrtime() or Date.now(). Well add a rt.timer_start
variable.

5. Concrete Code Patches
5.1 Lexer: Add compound assignment operators to precedence table
In GDCompiler.reset(), add to operator_priorities:

js
"+=": 1, "-=": 1,
5.2 Lexer: Detect array literals
Add a new method parse_array_literal(buf, pos) to GDCompiler. It will be
called from tokenize_data when c === '(' and buf[pos+1] === '{'.

Pseudocode:

js
parse_array_literal(buf, pos) {
    // buf[pos] == '(' and buf[pos+1] == '{'
    let end = this.find_array_literal_end(buf, pos);
    if (end === -1) return null; // malformed

    let inner = buf.substring(pos + 2, end - 1); // between ({ and })
    // Split inner by top-level commas
    let parts = inner.length > 0 ? this.explode_str_on_level(",", inner, 1) :
[];
    let elements = parts.map(p => this.resolve_precedences(this.tokenize_data(p
)));
    
    let token = this.new_token("array-literal", "({})");
    token.elements = elements;
    return { token, newIndex: end };
}
We need a helper to find the matching }), respecting nested braces and
parentheses. Implement find_array_literal_end(buf, start):

js
find_array_literal_end(buf, start) {
    // start points to '('
    let parenDepth = 0, braceDepth = 0;
    for (let i = start; i < buf.length; i++) {
        if (buf[i] === '(') parenDepth++;
        else if (buf[i] === ')') parenDepth--;
        else if (buf[i] === '{') braceDepth++;
        else if (buf[i] === '}') braceDepth--;
        if (parenDepth === 0 && braceDepth === 0 && buf[i] === '}' && buf[i-1]
=== ')') {
            // we've found the closing '})'
            return i + 1;
        }
    }
    return -1;
}
Actually, the condition should be parenDepth === 0 && braceDepth === 0 after
processing }? We can adjust. Simpler: find the matching } for the { that
follows (. We can track both and stop when both depths go back to zero after
having started. We'll finetune.

In tokenize_data, before the generic ( handling, add:

js
if (c === '(' && buf[i+1] === '{') {
    let result = this.parse_array_literal(buf, i);
    if (result) {
        tokens.push(result.token);
        i = result.newIndex - 1; // continue after the literal
        continue;
    }
}
5.3 Code Generator: Array Literal Output
In JSCodeGenerator.output_token(), add a case for "array-literal":

js
if (type === "array-literal") {
    return "[" + token.elements.map(e => this.output_tokens(e)).join(", ") +
"]";
}
5.4 Code Generator: += and -= for arrays
Modify the operator case:

js
if (op === "+=" || op === "-=") {
    let left = this.output_token(token.a);
    let right = this.output_token(token.b);
    if (op === "+=") {
        return `(${left} = ${left}.concat(Array.isArray(${right}) ? ${right} :
[${right}]))`;
    } else { // "-="
        return `(${left} = ${left}.filter(x => !(Array.isArray(${right}) ?
${right}.includes(x) : x === ${right})))`;
    }
}
This assumes left is an array variable; it will be because these operators are
only used on arrays. For safety, we could add type checks, but not needed now.

5.5 Code Generator: Negative Indexing
In the [ operator case (or [.. for slices, but slices not needed yet), we need
to emit special code. Currently:

js
if (op === "[") return `${this.output_token(token.a)}[${this.output_token(token
.b)}]`;
Change to:

js
if (op === "[") {
    let arr = this.output_token(token.a);
    let idx = this.output_token(token.b);
    return `(${arr}[(${idx} < 0 ? ${arr}.length + ${idx} : ${idx})])`;
}
For arr[i] this works. For arr[0] its fine. For arr[-idx] where idx is a
variable, it will produce the correct negative indexing.

5.6 Code Generator: sizeof and isset
In output_token for function-call, add special cases before the generic _this.
call:

js
if (func_name === "sizeof") {
    let arg = this.output_token(token.args[0]);
    return `(${arg}).length`;
}
if (func_name === "isset") {
    // Assume isset(array[index])
    let arg0 = token.args[0]; // should be an indexing operator
    if (arg0 && arg0.operator === "[") {
        let arr = this.output_token(arg0.a);
        let idx = this.output_token(arg0.b);
        // Use the same negative-index handling
        let indexExpr = `(${idx} < 0 ? ${arr}.length + ${idx} : ${idx})`;
        return `((${arr}[${indexExpr}] !== undefined) && (${arr}[${indexExpr}]
!== 0))`;