icpp - running C as a script

Copyright (c) 2026 Muhammad Anisur Rahman. All rights reserved.

Version 1.0.0

icpp is kcpp -icpp: the same compiler, running your C in-process instead of producing assembly. No build step, no object files, no target hardware involved - it executes on the machine you are sitting at.

Running something

// hello.icpp
void main()
{
    int i;
    for (i = 0; i < 3; i++) iprint(i);
    iprint("icpp works");
}
kcpp -icpp -icppquiet hello.icpp
0
1
2
icpp works

-icppquiet suppresses the compiler's own status lines (Compiling:, Total errors:). Real error text and your program's output are unaffected, so it is the right flag for a script whose output you intend to consume.

Options

OptionDoes
-icppinterpret and execute in-process. No .asm or .o is produced
-icppquietdrop kcpp's status lines from stdout
-Ipathadditional include directory (repeatable)
-Dname[=value]define a macro
-llmtracewalk through every LLM-pipeline builtin as it runs, explaining each step

The compiler options that describe a target - -cpu32, exe=, -core= - have no meaning here. icpp runs host-native, which is the point: it is independent of whatever hardware kcpp would otherwise be compiling for.

Libraries

An icpp library is just a .icpp file of reusable functions, pulled in with an ordinary include. There is no packaging step and no archive format.

#include "stringutil.icpp"

void main()
{
    iprint(su_shout("hi"));            // -> HI!
    iprint(su_wordcount("a b c"));     // -> 3
}

lib_icpp/stringutil.icpp is the reference example.

The manifest

By convention a library starts with a block of tagged comments. They are comments, so the interpreter ignores them when running the library - but a script can read them, which is what makes version checking possible.

//@icpp-library    stringutil
//@icpp-version    1.2.0
//@icpp-author     Muhammad Anisur Rahman
//@icpp-copyright  (c) 2026 Muhammad Anisur Rahman - MIT
//@icpp-contact    anisfrombd693@gmail.com
//@icpp-requires   0.40                ; minimum interpreter version
//@icpp-since      1.2.0: added su_repeat()
FieldMeaning
librarythe library's name
versiondotted numbers, MAJOR.MINOR.PATCH
authorwho wrote it
copyrightcopyright or licence line
contactemail or URL
requiresminimum interpreter version it needs
sincewhat changed, newest first

Rules: one tag per line; the value runs to end-of-line or an inline ; comment, whichever comes first, and is trimmed; the first occurrence of a tag wins; put the manifest in the first ~200 lines, because that is where the reader stops looking. Extra fields of your own are fine.

Checking a version before you rely on it

icpp_lib_field(file, field)   // the @icpp-<field> value, or "" if absent
icpp_lib_version(file)        // shorthand for the "version" field
icpp_version_cmp(a, b)        // -1 / 0 / 1, comparing dotted numbers

icpp_version_cmp compares semantically, not as text - so 1.10.0 is correctly greater than 1.9.0, which a string comparison gets backwards.

#include "stringutil.icpp"

void main()
{
    if (icpp_version_cmp(icpp_lib_version("stringutil.icpp"), "1.2.0") < 0)
    {
        iprint("stringutil >= 1.2.0 required");
        icpp_exit(1);
    }
    iprint(su_repeat("ab", 3));        // -> ababab
}

A gate like that is worth writing whenever a script depends on a function added in a particular version. Without it the failure is a missing symbol at the point of use, which says nothing about which version you needed.

Teaching mode

-llmtrace prints a step-by-step walkthrough of the LLM-pipeline builtins as they run - tokenising, embeddings, positional encoding, attention, softmax, loss, backward pass, SGD - including why each step is there. It exists to show how a language model works rather than to help you debug one.

Verilog from a script

kcpp -rtl adder.icpp        # -> adder.v and adder_tb.v

-rtl translates the script's integer functions into synthesizable Verilog plus a self-checking testbench, instead of running them. The testbench matters: it is what lets you confirm the generated hardware agrees with the C you wrote, rather than assuming the translation was faithful.

Conformance suite

KCPP=<path to kcpp.exe> bash test/icpp_conformance/run_conformance.sh

Prints per-test results and a final RESULT: line. Always pass KCPP= explicitly - the runner otherwise defaults to a binary that may not be the one you just built, and a suite that silently tested the wrong compiler is worse than one that failed.

Planned

python(<file>) and java(<file>) - system functions to read and execute a Python script or a Java source file from inside icpp. Not in this release.