← All projects
InterpreterIn production

kaito

The interpreter behind the informatics track — running in WebAssembly on the student’s own device, with no sandbox service behind it.

v0.1.0
Language
Rust → wasm32-unknown-unknown
Notations
C++ subset · pseudocod
Split
Pure core crate + thin cdylib
Errors
English and Romanian

01Overview

Letting a student run code usually means a container somewhere and a round-trip for every execution. kaito does not. It is a tree-walking interpreter compiled into the app, so a program runs offline, instantly, and with no infrastructure to attack or pay for. An instruction budget bounds every run, which turns the usual hazard — an accidental infinite loop — into a plain timeout status.

It covers what the exam covers rather than what C++ contains: the integer, floating-point, character, boolean and string types, one- and two-dimensional arrays, the full operator set, the control-flow statements, functions with recursion, reference and array parameters, structs and simple classes, stream input and output with the usual formatting manipulators, a useful slice of the maths header, and the common string methods. Alongside it runs pseudocod, the notation Subiectul II algorithms are actually written in, through its own lexer and parser onto the same interpreter.

The feature that matters pedagogically is trace mode. Beyond the program’s output, a run can record a snapshot at every statement: which line executed, what each variable in scope now holds — scalars, arrays, matrices, struct fields — and the current call stack with its arguments. That is what lets the app show a loop executing one line at a time instead of just printing an answer.

The language core is deliberately free of any WebAssembly or host dependency, so it is an ordinary Rust library with an ordinary native test suite — around ninety-five tests, including runs against real exam problems. Only a thin outer crate faces the host.

02Usage

Natively it is a plain library call. In the app it is a headless module hosted by minato — the same interpreter either way.

From Rust
use kaito_core::{run, run_pseudo, run_trace, Status};

let result = run("int main(){ int n; cin >> n; cout << n * 2; }", "21");
assert_eq!(result.status, Status::Ok);
assert_eq!(result.stdout, "42");

// Same interpreter, the notation Subiectul II uses.
let result = run_pseudo("citeste n\nscrie n*2", "21");

// Trace mode: a snapshot per statement.
let trace = run_trace("int s=0; for(int i=1;i<=3;i++) s+=i;", "");
for step in &trace.steps {
    println!("line {} → {:?}", step.line, step.vars);
}
From the app, through minato
const { status, stdout, error, errorRo } =
  player.runCode(source, { lang: 'cpp', stdin });

if (status === 'timeout') {
  // The instruction budget ran out — almost always an infinite loop.
}

03Technologies

Front end
lexer · parser · ast

Two lexer/parser pairs — one for the C++ subset, one for pseudocod — producing the same abstract syntax tree, so everything downstream is written once.

Interpreter
interp · value · builtins

A tree walker over that AST with an explicit value model covering scalars, strings, arrays, matrices and struct instances, plus the standard-library surface the exam expects. Limits are a parameter, not a constant.

Trace
trace.rs

Per-statement snapshots of variables in scope and the call stack, capped by a configurable step budget. When the cap is hit the run still completes and the result says the trace was truncated.

Diagnostics
error · format

Compile and runtime failures come back as student-facing prose rather than parser jargon, in both English and Romanian, with output formatting that matches what the exam expects to see printed.