Chomik For Programmers — Part 0: Dissecting the Main


If you want to extend or embed Chomik, the best place to start is not with advanced features, but with the small main
function inside chomik.cc. That function demonstrates the three core components and how they cooperate:
Program — holds parsed Chomik code and metadata.
Parser — reads source from a file or string and fills the program.
Machine — contains runtime memory, variables, streams, types, and execution state.
The parser is constructed with the program so it can populate it.
Register the Parser
Before using the parser, you must register it:
chomik::parser::register_parser(&the_parser);
Parsing Without Execution
If you want to only parse without executing:
if (the_parser.parse(filename.c_str()) == 0)
{
// parsing succeeded!
}
Parsing from a File
The normal case is parsing from a file:
the_parser.parse(filename.c_str());
Parsing from a String
If you want to embed Chomik code directly into your C++ code at runtime, use parse_string
:
the_parser.parse_string(code_string, std::cerr);
When using parse_string
, scanner-level file operations such as include
directives are disabled.
This means the parser will not open other files during parsing.
⚠️ Important: This does not disable runtime file operations in Chomik code. The code can still perform file I/O unless you explicitly forbid it by creating your own machine subclass and overriding:
bool get_can_create_files() override;
void create_predefined_streams() override;
Creating and Initializing the Machine
For execution, create a machine and initialize it with predefined types, variables, and streams:
chomik::machine m;
m.create_predefined_types();
m.create_predefined_variables();
m.create_predefined_streams();
Executing the Program
Once the machine is ready, execute the program:
the_program.execute(m);
If you want to preserve state across multiple program executions, reuse the same machine instance — this way, variables and other runtime data are retained.
Accessing the Program Return Value
To access special built-in variables like "the program return"
, you must first construct a chomik::generic_name
, then wrap it in a chomik::signature
, because the machine understands signatures rather than raw names.
chomik::generic_name gn; gn.add_generic_name_item(std::make_shared<chomik::identifier_name_item>("the")); gn.add_generic_name_item(std::make_shared<chomik::identifier_name_item>("program")); gn.add_generic_name_item(std::make_shared<chomik::identifier_name_item>("return"));
chomik::signature s0{gn};
int return_value = m.get_variable_value_integer(s0);
About Parsers in Dialects
In theory, you could write a new parser for a Chomik dialect.
In practice, it’s rare. Most dialects — for example, SDL_Chomik — reuse the regular chomik::parser
.
Chomik does not introduce native opaque types like C’s FILE
structure.
Instead, resources such as streams, images, or fonts are represented as integers, which keeps the parser unchanged and simplifies integration.
Practical Tips
Register your parser early.
parse_string
disables scanner-level includes, but not runtime I/O.To forbid runtime file creation or access, override
get_can_create_files
andcreate_predefined_streams
in your machine subclass.Always call
create_predefined_types
,create_predefined_variables
, andcreate_predefined_streams
. This must be done once per machine, before any executing.Reuse the same machine when you want persistent state.
Understanding this basic parse → machine → execute flow makes extending or embedding Chomik much easier.
Whether you’re adding new built-in variables, integrating with a host application, or creating a specialized dialect, the same small pattern applies.
Subscribe to my newsletter
Read articles from Pawel Biernacki directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Pawel Biernacki
Pawel Biernacki
I was born in 1973, in Cracow, Poland. I am a software engineer.