summaryrefslogtreecommitdiff
path: root/src/main/smp/sys/text.smp
blob: a414ff45dc6c596b386d51816ca48f1e0c85a9c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Formatting primitives used directly by interpolation and named converters.
// Keep this module free of interpolation: sys can build itself from source.
// spec's low byte is the format kind; bits 8..12 are flags: - + space # 0.

val begin = { -> new aryu8(); };

val end = { aryu8 b ->
    val bytes = new u8[b.len];
    ary.copy(bytes,0,b.buf,0,b.len);
    u8[~] result = bytes;
    result;
};

val _pad = { !aryu8 b, i64 n, u8 c ->
    for( i64 i=0; i<n; i++ ) b.add(c);
    b;
};

val string = { !aryu8 b, u8[~] s, i64 spec, i64 width, i64 precision ->
    i64 len=s#;
    if( precision>=0 && precision<len ) len=precision;
    i64 pad=width-len;
    if( !(spec&256) ) _pad(b,pad,' ');
    for( i64 i=0; i<len; i++ ) b.add(s[i]);
    if( spec&256 ) _pad(b,pad,' ');
    b;
};

val integer = { !aryu8 b, i64 value, i64 spec, i64 width, i64 precision ->
    i64 kind=spec&255;
    if( kind=='b' ) return string(b,value ? "true" : "false",spec,width,-1);
    if( kind=='c' ) {
        if( !(spec&256) ) _pad(b,width-1,' ');
        u8 c=value;
        b.add(c);
        if( spec&256 ) _pad(b,width-1,' ');
        return b;
    }
    bool signed=kind==0 || kind=='d' || kind=='i';
    i64 base=kind=='o' ? 8 : (kind=='x' || kind=='X' ? 16 : 10);
    u8[~] alphabet=kind=='X' ? "0123456789ABCDEF" : "0123456789abcdef";
    val digits=new u8[22]; // Maximum: 22 octal digits for a 64-bit integer.
    i64 count=0, n=value;
    // Signed decimal stays nonpositive, including the minimum i64.
    if( signed && n>0 ) n=-n;
    if( n==0 && precision!=0 ) digits[count++]='0';
    while( n!=0 ) {
        i64 q=0, r=0;
        if( signed ) { q=n/base; r=q*base-n; }
        else {
            // Unsigned division without a u64 type. Halving first makes
            // the dividend nonnegative; the remainder needs at most one fix.
            q=((n>>>1)/base)*2;
            r=n-q*base;
            if( r>=base ) { q++; r-=base; }
        }
        digits[count++]=alphabet[r];
        n=q;
    }
    u8[~] !prefix="";
    if( signed ) {
        if( value<0 ) prefix="-";
        else if( spec&512 ) prefix="+";
        else if( spec&1024 ) prefix=" ";
    } else if( (spec&2048) && value!=0 ) {
        if( kind=='x' ) prefix="0x";
        if( kind=='X' ) prefix="0X";
    }
    i64 zeros=precision>count ? precision-count : 0;
    // Alternate octal always begins with a zero, including precision-zero 0.
    if( kind=='o' && (spec&2048) && zeros==0 && (count==0 || digits[count-1]!='0') ) zeros=1;
    i64 pad=width-prefix#-zeros-count;
    bool left=(spec&256)!=0;
    bool zero=(spec&4096)!=0 && precision<0 && !left;
    if( !left && !zero ) _pad(b,pad,' ');
    b.addAry(prefix);
    if( zero ) _pad(b,pad,'0');
    _pad(b,zeros,'0');
    while( count>0 ) b.add(digits[--count]);
    if( left ) _pad(b,pad,' ');
    b;
};

// Only conversion of binary floating-point values is delegated to libc.
// The fixed signature accepts a validated format and returns a byte string.
{ f64 i64 i64 i64 -> u8[~] } simple_text_float = "C";
val floating = { !aryu8 b, f64 value, i64 spec, i64 width, i64 precision ->
    b.addAry(simple_text_float(value,spec,width,precision));
};

// Explicitly named, typed conversions share the same formatting routines.
val int  = { i64  x -> end(integer (begin(),x, 0 ,0,-1)); };
val flt  = { f64  x -> end(floating(begin(),x, 0 ,0,-1)); };
val f32  = { f32  x -> end(floating(begin(),x,'g',0,-1)); };
val f64  = { f64  x -> end(floating(begin(),x, 0 ,0,-1)); };
val char = { i64  x -> end(integer (begin(),x,'c',0,-1)); };
val bool = { bool x -> end(integer (begin(),x,'b',0,-1)); };
val hex  = { i64  x -> end(integer (begin(),x,'x',0,-1)); };