From 3aeae461f01e749edcdbbbd895d02baf0edd0801 Mon Sep 17 00:00:00 2001 From: Lisa Milne Date: Tue, 28 Nov 2023 15:31:38 +1000 Subject: [PATCH] a basic printf (and etc) implementation --- clite/commands.js | 3 +- clite/core.js | 347 +++++++++++++++++++++++++++++++++++++++++++++- clite/libstdio.js | 7 +- 3 files changed, 353 insertions(+), 4 deletions(-) diff --git a/clite/commands.js b/clite/commands.js index ff96797..17a9173 100644 --- a/clite/commands.js +++ b/clite/commands.js @@ -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); diff --git a/clite/core.js b/clite/core.js index 83545dd..0261add 100644 --- a/clite/core.js +++ b/clite/core.js @@ -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 -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 -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 -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(/%/g,'%'); // TODO: actually support formatting - return clite.io.write(pid,fd,fmt); + return clite.io.write(pid,fd,str); } }, // 0: unknown diff --git a/clite/libstdio.js b/clite/libstdio.js index 84cce3d..b3613f4 100644 --- a/clite/libstdio.js +++ b/clite/libstdio.js @@ -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); }