Files
cell/scripts/text.cm

413 lines
10 KiB
Plaintext

/* text.cm - text conversion and formatting utilities */
/* -------- helper functions ----------------------------------------- */
var blob = use('blob')
var utf8 = use('utf8')
var that = this
// Convert number to string with given radix
function to_radix(num, radix) {
if (radix < 2 || radix > 36) return null;
var digits = "0123456789abcdefghijklmnopqrstuvwxyz";
var result = "";
var n = Math.trunc(num);
var negative = n < 0;
n = Math.abs(n);
if (n == 0) return "0";
while (n > 0) {
result = digits[n % radix] + result;
n = Math.floor(n / radix);
}
return negative ? "-" + result : result;
}
// Insert separator every n digits from right
function add_separator(str, sep, n) {
if (!n || n == 0) return str;
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var parts = str.split('.');
var integer = parts[0];
var decimal = parts[1] || '';
// Add separators to integer part
var result = "";
for (var i = integer.length - 1, count = 0; i >= 0; i--) {
if (count == n && i != integer.length - 1) {
result = sep + result;
count = 0;
}
result = integer[i] + result;
count++;
}
if (decimal) result += '.' + decimal;
return negative ? '-' + result : result;
}
// Format number with separator from left
function add_separator_left(str, sep, n) {
if (!n || n == 0) return str;
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var result = "";
for (var i = 0, count = 0; i < str.length; i++) {
if (count == n && i != 0) {
result += sep;
count = 0;
}
result += str[i];
count++;
}
return negative ? '-' + result : result;
}
/* -------- main text function --------------------------------------- */
function text(...arguments) {
var arg = arguments[0];
// Handle blob conversion
if (arg instanceof blob) {
if (!stone.p(arg))
throw new Error("text: blob must be stone for reading");
var format = arguments[1];
var bit_length = arg.length;
var result = "";
if (typeof format == 'string') {
// Extract style from format
var style = '';
for (var i = 0; i < format.length; i++) {
if ((format[i] >= 'a' && format[i] <= 'z') || (format[i] >= 'A' && format[i] <= 'Z')) {
style = format[i];
break;
}
}
// Handle blob encoding styles
switch (style) {
case 'h': // hexadecimal
return that.blob_to_hex(arg);
case 't': // base32
return that.blob_to_base32(arg);
case 'b': // binary
for (var i = 0; i < bit_length; i++) {
result += arg.read_logical(i) ? '1' : '0';
}
return result;
case 'o': // octal
var bits = 0;
var value = 0;
for (var i = 0; i < bit_length; i++) {
var bit = arg.read_logical(i);
value = (value << 1) | (bit ? 1 : 0);
bits++;
if (bits == 3) {
result += value.toString();
bits = 0;
value = 0;
}
}
// Handle remaining bits
if (bits > 0) {
value = value << (3 - bits);
result += value.toString();
}
return result;
}
}
// Default: interpret as UTF-8 text
// Use the utf8 module to decode the blob
return utf8.decode(arg);
}
// Handle array conversion
if (Array.isArray(arg)) {
var separator = arguments[1] || "";
// Check if all items are valid codepoints
var all_codepoints = true;
for (var i = 0; i < arg.length; i++) {
var item = arg[i];
if (!(typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item))) {
all_codepoints = false;
break;
}
}
if (all_codepoints && separator == "") {
// Use utf8 module to convert codepoints to string
return utf8.from_codepoints(arg);
} else {
// General array to string conversion
var result = "";
for (var i = 0; i < arg.length; i++) {
if (i > 0) result += separator;
var item = arg[i];
if (typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item)) {
// Single codepoint - use utf8 module
result += utf8.from_codepoints([item]);
} else {
result += String(item);
}
}
return result;
}
}
// Handle number conversion
if (typeof arg == 'number') {
var format = arguments[1];
// Simple radix conversion
if (typeof format == 'number') {
return to_radix(arg, format);
}
// Format string conversion
if (typeof format == 'string') {
return format_number(arg, format);
}
// Default conversion
return String(arg);
}
// Handle text operations
if (typeof arg == 'string') {
if (arguments.length == 1) return arg;
var from = arguments[1];
var to = arguments[2];
if (typeof from != 'number' || typeof to != 'number') return arg;
var len = arg.length;
// Adjust negative indices
if (from < 0) from += len;
if (to < 0) to += len;
// Default values
if (from == null) from = 0;
if (to == null) to = len;
// Validate range
if (from < 0 || from > to || to > len) return null;
return arg.substring(from, to);
}
return null;
}
/* -------- number formatting ---------------------------------------- */
function format_number(num, format) {
// Parse format string
var separation = 0;
var style = '';
var places = 0;
var i = 0;
// Parse separation digit
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
separation = parseInt(format[i]);
i++;
}
// Parse style letter
if (i < format.length) {
style = format[i];
i++;
} else {
return null;
}
// Parse places digits
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
places = parseInt(format[i]);
i++;
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
places = places * 10 + parseInt(format[i]);
i++;
}
}
// Invalid format if there's more
if (i < format.length) return null;
// Real number styles
if (style == 'e' || style == 'n' || style == 's' ||
style == 'u' || style == 'd' || style == 'v' || style == 'l') {
var decimal_point = '.';
var separator = '';
var default_separation = 0;
var default_places = 0;
switch (style) {
case 'e': // exponential
decimal_point = '.';
separator = '';
default_separation = 0;
default_places = 0;
break;
case 'n': // number
decimal_point = '.';
separator = '';
default_separation = 0;
default_places = 0;
break;
case 's': // space
decimal_point = '.';
separator = ' ';
default_separation = 3;
default_places = 0;
break;
case 'u': // underbar
decimal_point = '.';
separator = '_';
default_separation = 0;
default_places = 0;
break;
case 'd': // decimal
decimal_point = '.';
separator = ',';
default_separation = 3;
default_places = 2;
break;
case 'v': // comma (European style)
decimal_point = ',';
separator = '.';
default_separation = 0;
default_places = 0;
break;
case 'l': // locale (default to 'd' style for now)
decimal_point = '.';
separator = ',';
default_separation = 3;
default_places = 2;
break;
}
if (separation == 0) separation = default_separation;
if (places == 0 && style != 'e' && style != 'n') places = default_places;
// Format the number
if (style == 'e') {
// Scientific notation
var str = places > 0 ? num.toExponential(places) : num.toExponential();
return str;
} else if (style == 'n' && (Math.abs(num) >= 1e21 || (Math.abs(num) < 1e-6 && num != 0))) {
// Use scientific notation for extreme values
return num.toExponential();
} else {
// Regular decimal formatting
var str;
if (places > 0) {
str = num.toFixed(places);
} else {
str = num.toString();
}
// Replace decimal point if needed
if (decimal_point != '.') {
str = str.replace('.', decimal_point);
}
// Add separators
if (separation > 0 && separator) {
str = add_separator(str, separator, separation);
}
return str;
}
}
// Integer styles
if (style == 'i' || style == 'b' || style == 'o' ||
style == 'h' || style == 't') {
var radix = 10;
var default_separation = 0;
var default_places = 1;
switch (style) {
case 'i': // integer
radix = 10;
default_separation = 0;
default_places = 1;
break;
case 'b': // binary
radix = 2;
default_separation = 0;
default_places = 1;
break;
case 'o': // octal
radix = 8;
default_separation = 0;
default_places = 1;
break;
case 'h': // hexadecimal
radix = 16;
default_separation = 0;
default_places = 1;
break;
case 't': // base32
radix = 32;
default_separation = 0;
default_places = 1;
break;
}
if (separation == 0) separation = default_separation;
if (places == 0) places = default_places;
// Convert to integer
var n = Math.trunc(num);
var str = to_radix(n, radix).toUpperCase();
// Pad with zeros if needed
var negative = str[0] == '-';
if (negative) str = str.substring(1);
while (str.length < places) {
str = '0' + str;
}
// Add separators
if (separation > 0) {
str = add_separator_left(str, '_', separation);
}
return negative ? '-' + str : str;
}
return null;
}
return text;