Skip to content

c_extensions_demo.tc

c_extensions_demo.tc

Source on GitHub

// c_extensions_demo.tc
// Demonstrates all C compatibility extensions added to TinyC (2026-03)
//
// Extensions covered:
//   1. Variadic sprintf
//   2. Ternary operator  (?:)
//   3. do-while loop
//   4. enum
//   5. const keyword
//   6. Compound assignments  (%=  &=  |=  ^=  <<=  >>=)
//   7. Hex escape sequences  (\x41)
//   8. static local variables
//   9. struct
//  10. char name[] = "..."  (size inferred from string literal)
//  11. typedef

// ── Buffers ───────────────────────────────────────────────────────────────────
char buf[64];
char tmp[32];

// ── 4. enum (global) ──────────────────────────────────────────────────────────
enum Color {
    COLOR_RED   = 0,
    COLOR_GREEN = 1,
    COLOR_BLUE  = 2,
};

enum Status {
    STATUS_OK   =  0,
    STATUS_WARN =  1,
    STATUS_ERR  = -1,   // negative enum values supported
};

// ── 5. const (global) ─────────────────────────────────────────────────────────
const int   MAX_RETRIES = 5;
const float ALERT_TEMP  = 30.0;

// ── 11. typedef ───────────────────────────────────────────────────────────────
typedef int   millisec_t;       // primitive alias
typedef float celsius_t;

// Anonymous struct typedef (no 'struct' keyword needed at use site)
typedef struct {
    int   r;
    int   g;
    int   b;
} Color;

// ── 9. struct ─────────────────────────────────────────────────────────────────
struct SensorReading {
    float temperature;
    float humidity;
    int   quality;
};

struct Point {
    int x;
    int y;
};

// Global struct instance
struct SensorReading last_reading;

// ── 8. static local counter (helper) ─────────────────────────────────────────
// Returns the number of times it has been called.
int counter() {
    static int n = 0;
    n = n + 1;
    return n;
}

// ── Main ──────────────────────────────────────────────────────────────────────
void main() {
    addLog("=== TinyC C Extensions Demo ===");
    int fail = 0;

    // ── 1. Variadic sprintf ───────────────────────────────────────────────────
    // Single sprintf call with mixed int, float, and string args.
    int   count = 42;
    float ratio = 3.14;
    sprintf(buf, "n=%d r=%.2f", count, ratio);
    // expected: "n=42 r=3.14"
    if (buf[2]=='4' && buf[3]=='2' && buf[7]=='3') {
        addLog("PASS variadic sprintf");
    } else {
        addLog("FAIL variadic sprintf");
        fail = fail + 1;
    }

    // Three args including a string variable
    // 10. char name[] = "..." (size inferred) ─────────────────────────────────
    char unit[] = "degC";          // size = 5 (4 chars + null terminator)
    sprintf(buf, "val=%.1f %s", ratio, unit);
    // "val=3.1 degC"  → [0]=v [1]=a [2]=l [3]== [4]=3 [5]=. [6]=1 [7]=space [8]=d
    if (buf[4]=='3' && buf[8]=='d') {
        addLog("PASS char[] inferred size + sprintf %s");
    } else {
        addLog("FAIL char[] inferred size + sprintf %s");
        fail = fail + 1;
    }

    // ── 7. Hex escape sequences ───────────────────────────────────────────────
    char hex_str[] = "\x41\x42\x43";   // "ABC"  (A=0x41, B=0x42, C=0x43)
    char hex_ch    = '\x5A';            // 'Z' = 90
    if (hex_str[0]==65 && hex_str[1]==66 && hex_str[2]==67 && hex_ch==90) {
        addLog("PASS hex escape in string and char");
    } else {
        addLog("FAIL hex escape in string and char");
        fail = fail + 1;
    }

    // ── 2. Ternary operator ───────────────────────────────────────────────────
    int temp = 35;
    int hot_flag = (temp > 30) ? 1 : 0;
    if (hot_flag == 1) {
        addLog("PASS ternary operator");
    } else {
        addLog("FAIL ternary operator");
        fail = fail + 1;
    }

    // Nested ternary
    int level = (temp > 40) ? 3 : (temp > 30) ? 2 : 1;
    if (level == 2) {
        addLog("PASS nested ternary");
    } else {
        addLog("FAIL nested ternary");
        fail = fail + 1;
    }

    // Ternary with float
    float limit = (temp > 30) ? ALERT_TEMP + 5.0 : ALERT_TEMP;
    if (limit > 34.9 && limit < 35.1) {
        addLog("PASS ternary with float");
    } else {
        addLog("FAIL ternary with float");
        fail = fail + 1;
    }

    // ── 3. do-while loop ──────────────────────────────────────────────────────
    int i = 0;
    int sum = 0;
    do {
        sum = sum + i;
        i = i + 1;
    } while (i < 5);
    // sum = 0+1+2+3+4 = 10
    if (sum == 10) {
        addLog("PASS do-while loop");
    } else {
        addLog("FAIL do-while loop");
        fail = fail + 1;
    }

    // do-while body executes at least once even when condition starts false
    int once = 0;
    do { once = 1; } while (0);
    if (once == 1) {
        addLog("PASS do-while executes at least once");
    } else {
        addLog("FAIL do-while executes at least once");
        fail = fail + 1;
    }

    // ── 4. enum ───────────────────────────────────────────────────────────────
    if (COLOR_RED == 0 && COLOR_GREEN == 1 && COLOR_BLUE == 2) {
        addLog("PASS enum values");
    } else {
        addLog("FAIL enum values");
        fail = fail + 1;
    }

    if (STATUS_ERR == -1 && STATUS_OK == 0 && STATUS_WARN == 1) {
        addLog("PASS negative enum value");
    } else {
        addLog("FAIL negative enum value");
        fail = fail + 1;
    }

    // Inline enum inside function
    enum Direction { NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3 };
    int dir = EAST;
    if (dir == 1 && SOUTH == 2 && WEST == 3) {
        addLog("PASS inline enum");
    } else {
        addLog("FAIL inline enum");
        fail = fail + 1;
    }

    // ── 5. const ──────────────────────────────────────────────────────────────
    const int  LOCAL_LIMIT = 100;
    const float PI_APPROX  = 3.14159;
    if (LOCAL_LIMIT == 100 && MAX_RETRIES == 5) {
        addLog("PASS const int");
    } else {
        addLog("FAIL const int");
        fail = fail + 1;
    }
    if (PI_APPROX > 3.141 && PI_APPROX < 3.142 && ALERT_TEMP == 30.0) {
        addLog("PASS const float");
    } else {
        addLog("FAIL const float");
        fail = fail + 1;
    }

    // ── 6. Compound assignments ───────────────────────────────────────────────
    int v = 100;
    v %= 30;             // 100 % 30 = 10
    if (v == 10) { addLog("PASS %="); } else { addLog("FAIL %="); fail = fail + 1; }

    int flags = 0xFF;
    flags &= 0x0F;       // 0xFF & 0x0F = 15
    if (flags == 15) { addLog("PASS &="); } else { addLog("FAIL &="); fail = fail + 1; }

    flags |= 0x30;       // 0x0F | 0x30 = 63
    if (flags == 63) { addLog("PASS |="); } else { addLog("FAIL |="); fail = fail + 1; }

    flags ^= 0x0F;       // 0x3F ^ 0x0F = 48
    if (flags == 48) { addLog("PASS ^="); } else { addLog("FAIL ^="); fail = fail + 1; }

    int bits = 1;
    bits <<= 4;          // 1 << 4 = 16
    if (bits == 16) { addLog("PASS <<="); } else { addLog("FAIL <<="); fail = fail + 1; }

    bits >>= 2;          // 16 >> 2 = 4
    if (bits == 4)  { addLog("PASS >>="); } else { addLog("FAIL >>="); fail = fail + 1; }

    // float compound
    float fv = 10.0;
    fv += 5.0;    fv -= 3.0;    fv *= 2.0;    fv /= 6.0;
    // ((10+5-3)*2)/6 = 24/6 = 4
    if (fv > 3.9 && fv < 4.1) {
        addLog("PASS float compound += -= *= /=");
    } else {
        addLog("FAIL float compound += -= *= /=");
        fail = fail + 1;
    }

    // ── 8. static local variables ─────────────────────────────────────────────
    // counter() increments and returns a persistent local across calls
    int c1 = counter();
    int c2 = counter();
    int c3 = counter();
    if (c1 == 1 && c2 == 2 && c3 == 3) {
        addLog("PASS static local");
    } else {
        addLog("FAIL static local");
        fail = fail + 1;
    }

    // ── 9. struct ─────────────────────────────────────────────────────────────
    // Local struct, member assignment
    struct Point p;
    p.x = 10;
    p.y = 20;
    if (p.x == 10 && p.y == 20) {
        addLog("PASS struct local member access");
    } else {
        addLog("FAIL struct local member access");
        fail = fail + 1;
    }

    // Struct initializer list
    struct Point origin = {0, 0};
    struct Point corner = {100, 200};
    if (origin.x == 0 && corner.x == 100 && corner.y == 200) {
        addLog("PASS struct initializer list");
    } else {
        addLog("FAIL struct initializer list");
        fail = fail + 1;
    }

    // Struct compound member assignment
    corner.x += 5;
    corner.y -= 10;
    corner.x *= 2;
    if (corner.x == 210 && corner.y == 190) {
        addLog("PASS struct compound member assign");
    } else {
        addLog("FAIL struct compound member assign");
        fail = fail + 1;
    }

    // Struct with float fields + enum field
    struct SensorReading r;
    r.temperature = 23.5;
    r.humidity    = 60.0;
    r.quality     = STATUS_OK;
    if (r.temperature > 23.0 && r.humidity == 60.0 && r.quality == 0) {
        addLog("PASS struct float fields");
    } else {
        addLog("FAIL struct float fields");
        fail = fail + 1;
    }

    // Global struct
    last_reading.temperature = ALERT_TEMP + 2.0;   // 32.0
    last_reading.quality     = STATUS_WARN;
    if (last_reading.temperature > 31.9 && last_reading.quality == 1) {
        addLog("PASS global struct");
    } else {
        addLog("FAIL global struct");
        fail = fail + 1;
    }

    // Struct fields used in expressions
    float avg_t = (r.temperature + last_reading.temperature) / 2.0;
    // (23.5 + 32.0) / 2 = 27.75
    if (avg_t > 27.0 && avg_t < 28.0) {
        addLog("PASS struct fields in expression");
    } else {
        addLog("FAIL struct fields in expression");
        fail = fail + 1;
    }

    // ── 11. typedef ───────────────────────────────────────────────────────────
    millisec_t delay_ms = 500;
    celsius_t  boiling  = 100.0;
    if (delay_ms == 500 && boiling == 100.0) {
        addLog("PASS typedef primitive");
    } else {
        addLog("FAIL typedef primitive");
        fail = fail + 1;
    }

    // Anonymous struct typedef — use Color directly, no 'struct' prefix
    Color red = {255, 0, 0};
    Color sky = {135, 206, 235};
    red.r -= 55;    // compound assign on typedef'd struct
    if (red.r == 200 && sky.b == 235) {
        addLog("PASS typedef anonymous struct");
    } else {
        addLog("FAIL typedef anonymous struct");
        fail = fail + 1;
    }

    // Chained typedef
    typedef millisec_t duration_t;
    duration_t timeout = 1000;
    if (timeout == 1000) {
        addLog("PASS typedef chained alias");
    } else {
        addLog("FAIL typedef chained alias");
        fail = fail + 1;
    }

    // ── Combined: struct + enum + ternary + variadic sprintf ─────────────────
    char quality_str[] = "ok  ";
    if (r.quality == STATUS_OK) {
        strcpy(quality_str, "ok");
    } else {
        strcpy(quality_str, "warn");
    }
    sprintf(buf, "T=%.1f H=%.0f Q=%s", r.temperature, r.humidity, quality_str);
    // expected: "T=23.5 H=60 Q=ok"
    if (buf[2]=='2' && buf[3]=='3' && buf[9]=='6' && buf[10]=='0') {
        addLog("PASS combined struct+enum+sprintf");
    } else {
        addLog("FAIL combined struct+enum+sprintf");
        fail = fail + 1;
    }

    // ── Summary ───────────────────────────────────────────────────────────────
    if (fail == 0) {
        addLog("=== ALL TESTS PASSED ===");
    } else {
        sprintf(buf, "=== %d TEST(S) FAILED ===", fail);
        addLog(buf);
    }
}