a basic printf (and etc) implementation

This commit is contained in:
Lisa Milne 2023-11-28 15:31:38 +10:00
parent abc7e6c4a5
commit 3aeae461f0
3 changed files with 353 additions and 4 deletions

View file

@ -697,7 +697,8 @@ clite.commands.load('test',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
stdio.printf('Hello world');
stdio.printf('uid:'+stdlib.getuid());
stdio.printf('uid:%d',stdlib.getuid());
stdio.printf('%#15x:%15o:%15.5f:%0$15.5f:%%:',10,12,8.123456789);
function rcb(data) {
stdio.printf(data);
io.exit(0);

View file

@ -1160,8 +1160,353 @@ clite.io = {
return fd.node.data.istty;
}
clite.io.vfprintf = function(pid,fd,fmt,args) {
var str = '';
var parts = fmt.replace(/%%/g,'%').split('%');
var i = 0;
var d = false;
parts.forEach(function(p,k) {
var fs = p;
if (!k || d) {
str += p;
d = false;
return;
}
if (p == '' && j<parts.length-1) {
str += '%';
d = true;
return;
}
// %[parameter][flags][width][.precision][length]type
//clite.log.write(fs);
var param = i;
var nparam = true;
var ind = fs.indexOf('$');
if (ind > -1) {
var pp = fs.substring(0,ind);
var pi = parseInt(pp);
if (pp == pi.toString()) {
param = pi;
nparam = false;
fs = fs.substring(ind+1);
}
}
//clite.log.write('(param='+param+')'+fs);
// TODO: support multiple flags
var flag = false;
if (fs[0] == '-' || fs[0] == '+' || fs[0] == ' ' || fs[0] == '0' || fs[0] == "'" || fs[0] == '#') {
flag = fs[0];
fs = fs.substring(1);
}
//clite.log.write('(param='+param+')(flag='+flag+')'+fs);
var width = 0;
if (fs[0] == '*') {
width = parseInt(args[param]);
param++;
}else{
ind = -1;
for (var j=0; j<fs.length; j++) {
var v = parseInt(fs[j],10);
if (v > -1 && v < 10) {
ind = j;
}else{
break;
}
}
if (ind > -1) {
var pp = fs.substring(0,ind+1);
var pi = parseInt(pp);
if (pi.toString() == pp) {
width = pi;
fs = fs.substring(ind+1);
}
}
}
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')'+fs);
var precision = 0;
if (fs[0] == '.') {
fs = fs.substring(1);
ind = -1;
for (var j=0; j<fs.length; j++) {
var v = parseInt(fs[j],10);
if (v > -1 && v < 10) {
ind = j;
}else{
break;
}
}
if (ind > -1) {
var pp = fs.substring(0,ind+1);
var pi = parseInt(pp);
if (pp.toString() == pp) {
precision = pi;
fs = fs.substring(ind+1);
}
}
}
// ignore length
if (fs.substring(0,2) == 'hh' || fs.substring(0,2) == 'll') {
fs = fs.substring(2);
}else if (['h','l','L','z','j','t'].indexOf(fs[0]) > -1) {
fs = fs.substring(1);
}
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')'+fs);
var type = fs[0];
fs = fs.substring(1);
//clite.log.write('(param='+param+')(flag='+flag+')(width='+width+')(type='+type+')'+fs);
switch (type) {
case 'd':
case 'i': // signed int
var val = parseInt(args[param],10);
if (typeof flag === 'string' && val > 0) {
if (flag == '+')
str += '+';
if (flag == ' ')
str += ' ';
}
if (width > 0) {
var c = ' ';
if (flag == '0')
c = '0';
if (flag == '-') {
str += val.toFixed(0).padEnd(width,c);
}else{
str += val.toFixed(0).padStart(width,c);
}
}else{
str += val.toFixed(0);
}
break;
case 'u': // unsigned int
var sval = args[param].toString(10);
if (sval[0] == '-')
sval = sval.substring(1);
var val = parseInt(sval,10);
if (width > 0) {
var c = ' ';
if (flag == '0')
c = '0';
if (flag == '-') {
str += val.toFixed(0).padEnd(width,c);
}else{
str += val.toFixed(0).padStart(width,c);
}
}else{
str += val.toFixed(0);
}
break;
case 'f': // float
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'nan';
}else if (!Number.isFinite(val)) {
str += 'inf';
}else{
if (precision == 0) {
var s = String(val).split('.');
if (s.length > 1)
precision = s[1].length;
}
var s = val.toFixed(precision).toLowerCase();
if (precision == 0)
s += '.';
if (typeof flag === 'string' && val > 0) {
if (flag == '+')
s = '+'+s;
if (flag == ' ') {
clite.log.write('pad space');
s = ' '+s;
}
}
if (width > 0) {
var c = ' ';
if (flag == '0')
c = '0';
if (flag == '-') {
s = s.padEnd(width,c);
}else{
s = s.padStart(width,c);
}
}
str += s;
}
break;
case 'F': // FLOAT
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'NAN';
}else if (!Number.isFinite(val)) {
str += 'INF';
}else{
if (precision == 0) {
var s = String(val).split('.');
if (s.length > 1)
precision = s[1].length;
}
var s = val.toFixed(precision).toUpperCase();
if (precision == 0)
s += '.';
if (typeof flag === 'string' && val > 0) {
if (flag == '+')
s = '+'+s;
if (flag == ' ')
s = ' '+s;
}
if (width > 0) {
var c = ' ';
if (flag == '0')
c = '0';
if (flag == '-') {
s = s.padEnd(width,c);
}else{
s = s.padStart(width,c);
}
}
str += s;
}
break;
case 'e': // float with exponent
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'NAN';
}else if (!Number.isFinite(val)) {
str += 'INF';
}else{
str += val.toExponential().toLowerCase();
}
break;
case 'E': // FLOAT with exponent
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'NAN';
}else if (!Number.isFinite(val)) {
str += 'INF';
}else{
str += val.toExponential().toUpperCase();
}
break;
case 'g': // either float or float with exponent
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'nan';
}else if (!Number.isFinite(val)) {
str += 'inf';
}else{
str += val.toString(10).toLowerCase();
}
break;
case 'G': // either FLOAT or FLOAT with exponent
var val = parseFloat(args[param].toString(10));
if (isNaN(val)) {
str += 'NAN';
}else if (!Number.isFinite(val)) {
str += 'INF';
}else{
str += val.toString(10).toUpperCase();
}
break;
case 'x': // hexadecimal
var val = parseInt(args[param]);
var s = '';
if (flag == '#')
s += '0x';
s += val.toString(16).toLowerCase();
if (width > 0) {
if (flag == '-') {
s = s.padEnd(width,' ');
}else{
s = s.padStart(width,' ');
}
}
str += s;
break;
case 'X': // HEXADECIMAL
var val = parseInt(args[param]);
var s = '';
if (flag == '#')
s += '0X';
s += val.toString(16).toUpperCase();
if (width > 0) {
if (flag == '-') {
s = s.padEnd(width,' ');
}else{
s = s.padStart(width,' ');
}
}
str += s;
break;
case 'o': // octal
var val = parseInt(args[param],10);
var s = '';
if (flag == '#')
s += '0';
s += val.toString(8);
if (width > 0) {
if (flag == '-') {
s = s.padEnd(width,' ');
}else{
s = s.padStart(width,' ');
}
}
str += s;
break;
case 's': // string
var s = args[param].toString();
if (width > 0) {
if (flag == '-') {
s = s.padEnd(width,' ');
}else{
s = s.padStart(width,' ');
}
}
if (precision > 0)
s = s.substring(0,precision);
str += s;
break;
case 'c': // character
if (width > 0) {
if (flag == '-') {
str += args[param].toString()[0].padEnd(width,' ');
}else{
str += args[param].toString()[0].padStart(width,' ');
}
}else{
str += args[param].toString()[0];
}
break;
case 'p': // pointer (NULL)
if (width > 0) {
if (flag == '-') {
str += 'NULL'.padEnd(width,' ');
}else{
str += 'NULL'.padStart(width,' ');
}
}else{
str += 'NULL';
}
break;
case 'a': // hexfloat
var val = parseFloat(args[param]);
str += val.toString(16).toLowerCase();
break;
case 'A': // HEXFLOAT
var val = parseFloat(args[param]);
str += val.toString(16).toUpperCase();
break;
case 'n': // number of chars written so far returned
args[param].value = str.length;
break;
default:;
}
if (nparam)
i = param+1;
str += fs;
})
str = str.replace(/&percnt;/g,'%');
// TODO: actually support formatting
return clite.io.write(pid,fd,fmt);
return clite.io.write(pid,fd,str);
}
},
// 0: unknown

View file

@ -79,12 +79,15 @@ return Object.create({
},
fprintf:function(fd,fmt) {
var args = [];
var args = Array.from(arguments);
args.shift();
args.shift();
return clite.io.vfprintf(io.pid,fd,fmt,args);
},
printf:function(fmt) {
var args = [];
var args = Array.from(arguments);
args.shift();
return clite.io.vfprintf(io.pid,io.stdout,fmt,args);
}