Extending the interpreter itself
Adding a new native function (the most common change): pick the right registerBuiltin*() in src/Interpreter.cpp (or defineNatives() for something that should always be available, no IMPORT:// needed), and add a def("name", [...](std::vector<Value>& args) -> Value { ... }) call. Look at a neighboring function for the argument-checking convention (throw std::runtime_error with a clear message on the wrong type/count). Then document it in this file, and — if it's pure computation with no filesystem/network/process access — consider porting it to exemples/ton618-lite.js too, so the browser playground stays in sync (see that file's own header comment for exactly what it can and can't support).
Adding a new operator or keyword: add a TokenType (Token.hpp), teach the Lexer to produce it (src/Lexer.cpp — the keywords map for a word, scanToken() for punctuation), then teach the Parser to consume it at the right precedence level (see Parser.hpp's comment on the expression -> assignment -> ternary -> ... chain) and the Interpreter to evaluate it.
Adding a new statement kind (like switch was): add a StmtType in Ast.hpp (reusing existing fields where they fit, rather than growing the struct), a parsing method in Parser.cpp dispatched from statement(), and a case in Interpreter::execute().
Adding a new value/type kind (e.g. a ton.set):
- Add a
ValueTypeand storage field ininclude/Value.hpp; extendtypeName(),toString(),toJson(). - Add a
VarKindininclude/Ast.hppand aTokenType/keyword (Token.hpp,Lexer.cpp) if it needs its ownton.<type>declaration syntax. - Extend
Interpreter::checkType(src/Interpreter.cpp) soton.<type> x = ...enforces it. - Add literal syntax in
Parser::primary()if the type needs one (see howARRAY/DICTliterals are parsed there), and evaluation support inInterpreter::evaluate.
After any change, run make and sanity-check with the scripts in exemples/ (in particular exemples/test_new_features.ton) before relying on the change.