cint

cint university / 201 / ced, the cint editor

201-00: start here

This is lesson 201-00 of CU-201.2, checked against cint 0.2.0.

You will set up cint, meet its four commands and see the C it emits from a small program.

This course is for readers who already program in C, Python or a language like them. If you have never programmed, start with 101 instead.

what cint is

cint is a language for whole numbers. Its signed integer types run from 8 to 1024 bits and its unsigned ones from 8 to 512. It has fixed-point types for fractions and no floating point. Every plain operator is checked. When +, -, *, /, a shift or a conversion has no exact result in its type, the program stops with a fault that names the operation, the operands, the exact result and the limit. Where wrapping or saturating is what a program means, it says so with a wrapping operator such as +% or a saturating one such as +|, and those never fault.

Three tools run cint programs:

  • cint, the command you build in the next section. It translates a program to C17, compiles that with your C compiler (GCC, Clang, MSVC or Apple Clang) and runs it. The compiler inside it, cintc, is written in cint and compiles itself.
  • The reference, an implementation of the language in Python that defines what every program means. On a four-core Intel Xeon at 2.10 GHz, finding the number below 1,000,000 with the longest Collatz path did not finish in five minutes on the reference, while cint run took under one second. The browser workspace on integerc.dev runs it.
  • The interpreter, which runs the same programs without a C compiler. Lesson 201-06 uses it.

For every program in this course, all three print the same bytes. If they ever disagree, the reference decides.

set up

Follow set up cint on your computer. It needs Python 3.12 or later and a C compiler, and two commands build cint and its debugging tools. It covers VS Code and plain text editors.

the four commands

Save this as squares.ci:

I64 square(I64 x) {
    return x * x;
}

void main() {
    for i in 1..=5 {
        "{i} squared is {square(i)}\n";
    }
}

test "small squares" {
    assert(square(3) == 9);
    assert(square(-4) == 16);
}

test "the largest square that fits" {
    assert(square(3_037_000_499) == 9_223_372_030_926_249_001);
}

test "one past it faults" expect_fault E_OVERFLOW {
    _ = square(3_037_000_500);
}

Run this program in ced

cint run builds the program in a cache and runs main:

$ cint run squares.ci
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25

cint test runs every test block. The third test passes because the multiplication faults: 3037000500 squared is 9223372037000250000, which is more than I64.max, 9223372036854775807.

$ cint test squares.ci
test squares.ci:11 "small squares" ... ok
test squares.ci:16 "the largest square that fits" ... ok
test squares.ci:20 "one past it faults" ... ok
3 of 3 tests passed

cint build writes an executable that runs without cint:

$ cint build squares.ci
built: ./build/squares

cint emit-c writes the C. Here is square, as emitted:

static bool ci_7_squares_6_square(cint_ctx *ctx, int64_t v_x, int64_t *result)
{
    int64_t t0 = 0;
    (void)ctx;
    (void)v_x;
    (void)result;
    static const cint_site cs1 = {0u, 1u}; (void)cs1;
    if (!cint_mul_i64(ctx, cs1, v_x, v_x, &t0)) goto fault;
    *result = t0;
    return true;
fault:
    return false;
}

The multiplication is a call to cint_mul_i64, which computes the exact product and returns false when it does not fit. cs1 is a site: an entry in a table, written beside the C, that holds the source position a fault report prints. Here that position is line 2, character 14. Each checked operation in the emitted C is a call like this.

scripts and main

A file of statements with no functions is a script: its statements run from top to bottom, as in course 101.

"hello, world\n";
I64 answer = 6 * 7;
"answer={answer}\n";

Run this program in ced

Once a program has functions, put its statements in void main() { ... }, as squares.ci does: variables declared outside a function are not visible inside functions or tests.

what this course covers

Lessons 201-01 to 201-04 cover the arithmetic at the core of the language in four steps: checked arithmetic and the fault record; the integer types and conversions; the wrapping and saturating operators; division and rounding. The labs build complete programs on top.

Each lesson shows, beside cint's output, what C, Python or JavaScript prints for the same program, with the version that printed it.

Next: lesson 201-01, numbers that hold.