Download Game! Currently 65 players and visitors. Last logged in:AceduckDesertDregasTalon

Blitzer's Blog >> 72000

Back to blogs index
Posted: 04 Sep 2026 09:46 [ permalink ]
user@elli:~/Downloads/Remote/LPC/GEM$ node harness.js tests/int.c             
                                                   [+] Compiling tests/int.c
into VM Memory...

[+] Launching Javascript V8 Native Environment...                
                                                                 --- BOOTING
VM ---                                               X: 7

                                                                 --- BOOTING
VM ---                                               X: 7 [VM HALTED CLEANLY] 
                                         user@elli:~/Downloads/Remote/LPC/GEM$

Let's examine:

user@elli:~/Downloads/Remote/LPC/GEM$ cat tests/float.c          void create
()
{
  float f;

  f = 0.5;

  printf ("f: %s
", f);
  printf ("f/2: %s
", f / 2.0);
  printf ("\"\"+f/2: %s
", "" + f / 2.0);
  printf ("
");

  printf ("float_int(f): %d
", float_int (f));
  printf ("int_float(float_int(f)): %f
", int_float (float_int (f)));

  printf ("f: %.2f
", f);
}
user@elli:~/Downloads/Remote/LPC/GEM$

==

Spot on! The tests/int.c benchmark proves the AST mathematically respects the
a + b * c evaluation order correctly out of the box.
Now, stepping into tests/float.c immediately highlights three new challenges
for our JavaScript backend:
 * Explicit Typecasting Functions: The LPC engine uses float_int() and
int_float() to explicitly cast memory types.
 * Implicit String Coercion: It pushes a float into a %s string format
specifier.
 * Format Precision: It uses %.2f to tell printf to truncate the float to
exactly two decimal places.
Because JavaScript natively treats all numbers as double-precision floats, we
don't have to change anything in the AST to support the math. However, our
JSCodeGenerator needs a slightly smarter printf regex to catch the %.2f
syntax, and we need to add the casting functions to the runtime.
Here are the precise updates to make tests/float.c work.
1. Update the Runtime (JSCodeGenerator)
Replace your generateRuntime() method. This adds float_int and int_float, and
upgrades the printf regex to /%(\.\d+)?([sdcf])/g so it captures the precision
dot (e.g., .2):
    generateRuntime() {
        return `const rt = {
    blueprints: {},
    clone_object: (name) => {
        if (!rt.blueprints[name]) { console.warn(\`[VM] Blueprint missing:
\${name}\`); return null; }
        let ob = rt.blueprints[name]();
        if (ob.create) ob.create();
        return ob;
    },
    call_other: (ob, func, ...args) => {
        if (!ob) return 0;
        if (typeof ob[func] === 'function') return ob[func](...args);
        return 0;
    },
    printf: (fmt, ...args) => {
        let i = 0;
        let formatValue = (val) => {
            if (Array.isArray(val)) return "({ " + val.map(formatValue).join(",
 ") + " })";
            if (val !== null && typeof val === 'object') {
                if (val.__filename) return \`Object:\${val.__filename}\`;
                let pairs = Object.entries(val).map(([k, v]) =>
\`\${k}:\${formatValue(v)}\`);
                return "([ " + pairs.join(", ") + " ])";
            }
            return val;
        };
        // UPDATED REGEX: Captures optional precision like .2 in %.2f
        let res = fmt.replace(/%(\\.\\d+)?([sdcf])/g, (match, precision, type)
=> {
            let val = args[i++];
            if (type === 'c') return String.fromCharCode(val); 
            if (type === 'd') return Math.trunc(Number(val)); // Strict int
cast
            if (type === 'f') { 
                if (precision) return Number(val).toFixed(parseInt(precision.sl
ice(1)));
                return Number(val).toFixed(6); // Default float precision
            }
            if (type === 's') { 
                val = String(formatValue(val)); 
                let nullIdx = val.indexOf(String.fromCharCode(0)); 
                return nullIdx !== -1 ? val.substring(0, nullIdx) : val; 
            }
            return val;
        });
        process_stdout.write(res);
    },
    float_int: (f) => Math.trunc(Number(f)),
    int_float: (i) => Number(i),
    timer: (() => {
        let start = null;
        return (stop) => {
            if (stop) {
                let end = process.hrtime(start);
                return end[0] + end[1] / 1e9;
            } else {
                start = process.hrtime();
                return 0;
            }
        };
    })()
};
`;
    }

2. Route the new Efuns
Inside your output_token method, locate the function-call transpilation block
near the bottom. Update the rt_functions array so the compiler knows that
float_int and int_float belong to the runtime (rt.float_int) rather than the
local blueprint (_this.float_int):
            const rt_functions = ["printf", "timer", "float_int",
"int_float"];
            if (rt_functions.includes(func_name)) return `rt.${func_name}(${arg
s.join(", ")})`;

            return `_this.${func_name}(${args.join(", ")})`;

Save that, and run node harness.js tests/float.c.
It will perfectly cast 0.5 into 0, bounce it back into 0.000000, format %.2f
correctly as 0.50, and evaluate the string coercion identically to C!