diff src/ploki/pp.c @ 4223:ac0403686959

<oerjan> rm -rf src/ploki; mv ploki src
author HackBot
date Fri, 20 Dec 2013 22:18:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ploki/pp.c	Fri Dec 20 22:18:50 2013 +0000
@@ -0,0 +1,957 @@
+#include "IO.h"
+#include "Str.h"
+#include "kork.h"
+#include "list.h"
+#include "main_io.h"
+#include "main_opt.h"
+#include "match.h"
+#include "pp.h"
+#include "re.h"
+#include "run.h"
+#include "val.h"
+#include "xmalloc.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void pp_abs(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, fabs(v->num));
+}
+
+void pp_acos(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, acos(v->num));
+}
+
+void pp_asin(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, asin(v->num));
+}
+
+void pp_atan(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, atan(v->num));
+}
+
+void pp_atan2(struct val *v) {
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	} else {
+		struct val *const a = li_at(v->magic.list, 0);
+		struct val *const b = li_at(v->magic.list, 1);
+		V_NUM(a);
+		V_NUM(b);
+		v_set_n(v, atan2(a->num, b->num));
+	}
+}
+
+void pp_chr(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	ko_cpy_c(v->ko, (unsigned char)(v->num + .5));
+	v->type = V_STR_K;
+}
+
+void pp_cos(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, cos(v->num));
+}
+
+void pp_defined(struct val *v) {
+	if (v->type != V_UNDEF) {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_eof(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	if (io_eof(v->magic.ext)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_error(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	if (io_err(v->magic.ext)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_escape(struct val *v) {
+	String tmp;
+	size_t i;
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	St_init(&tmp);
+	St_cpy(&tmp, ko_str(v->ko));
+	ko_zero(v->ko);
+	for (i = St_len(&tmp); i; ) {
+		--i;
+		if (!(ST_INDEX(&tmp, i) == '_' || isalnum(ST_INDEX(&tmp, i)))) {
+			ko_cat_c(v->ko, '!');
+		}
+		ko_cat_c(v->ko, ST_INDEX(&tmp, i));
+	}
+	St_clear(&tmp);
+	ko_reverse(v->ko);
+	v->type = V_STR_K;
+}
+
+void pp_getc(struct val *v) {
+	int c;
+
+	if (!V_EXT_P(v)) {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	c = io_getc(v->magic.ext);
+	#if EOF != -1
+		if (c == EOF) {
+			c = -1;
+		}
+	#endif
+	v_set_n(v, c);
+}
+
+void pp_getenv(struct val *v) {
+	const char *const tmp = getenv(v_sptr(v, NULL));
+	if (tmp) {
+		v_set_m(v, tmp, strlen(tmp));
+	} else {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_gets(struct val *v) {
+
+	if (!V_EXT_P(v)) {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+
+	if (ko_getline(v->magic.ext, v->ko) + 1u) {
+		V_xxx_OFF(v);
+		v->type = V_STR_K;
+	} else {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_int(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, floor(v->num + .5));
+}
+
+void pp_io(struct val *v) {
+	if (V_EXT_P(v)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_length(struct val *v) {
+	if (V_LIST_P(v)) {
+		v_set_n(v, li_length(v->magic.list));
+		return;
+	}
+	v_set_n(v, ko_length(v_kork(v)));
+}
+
+void pp_log(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, log(v->num));
+}
+
+void pp_log10(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, log10(v->num));
+}
+
+void pp_lower(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_lower(v->ko);
+}
+
+void pp_moend(struct val *v) {
+	size_t n;
+
+	V_NUM(v);
+	n = RINT(v->num);
+	if (n < Interp.m_end.size) {
+		v_set_n(v, Interp.m_end.index[n]);
+	} else {
+		v_set_undef(v);
+	}
+}
+
+void pp_mostart(struct val *v) {
+	size_t n;
+
+	V_NUM(v);
+	n = RINT(v->num);
+	if (n < Interp.m_start.size) {
+		v_set_n(v, Interp.m_start.index[n]);
+	} else {
+		v_set_undef(v);
+	}
+}
+
+void pp_neg(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->num = -v->num;
+	v->type = V_NUM_K;
+}
+
+void pp_not(struct val *v) {
+	if (v_true(v)) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_num(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->type = V_NUM_K;
+}
+
+void pp_open(struct val *v) {
+	IO *fh;
+	struct val *name, *mode;
+	enum io_flags flags;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	name = li_at(v->magic.list, 0);
+	V_STR(name);
+	if (ko_chr(name->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	mode = li_at(v->magic.list, 1);
+	flags = 0;
+	V_STR(mode);
+
+	if (ko_chr(mode->ko, 'A') + 1u) {
+		flags |= IO_APPEND;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_READ;
+		}
+	} else if (ko_chr(mode->ko, 'W') + 1u) {
+		flags |= IO_WRITE | IO_TRUNCATE;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_READ;
+		}
+	} else {
+		flags |= IO_READ;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_WRITE;
+		} else if (ko_chr(mode->ko, 'Z') + 1u) {
+			flags |= IO_BUFFERED;
+		}
+	}
+
+	if (ko_chr(mode->ko, 'B') + 1u) {
+		flags |= IO_BINARY;
+	}
+	if ((flags & IO_WRITE || flags & IO_APPEND) && ko_chr(mode->ko, 'F') + 1u) {
+		flags |= IO_AUTOFLUSH;
+	}
+
+	if (!(fh = io_open(ko_szp(name->ko), flags))) {
+		v_set_undef(v);
+		return;
+	}
+
+	v_set_io(v, fh);
+}
+
+void pp_openr(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+	} else if ((v->magic.ext = io_open(ko_szp(v->ko), IO_READ | IO_BINARY))) {
+		v->type = V_EXT_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_openw(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+	} else if ((v->magic.ext = io_open(ko_szp(v->ko), IO_WRITE | IO_TRUNCATE | IO_BINARY | IO_AUTOFLUSH))) {
+		v->type = V_EXT_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_ord(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_length(v->ko)) {
+		v->num = ko_at(v->ko, 0);
+		v->type = V_NUM_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_quote(struct val *v) {
+	String tmp;
+	size_t i;
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	St_init(&tmp);
+	St_cpy_m(&tmp, ko_ptr(v->ko), ko_length(v->ko));
+	ko_zero(v->ko);
+	for (i = 0; i < St_len(&tmp); ++i) {
+		if (!(ST_INDEX(&tmp, i) == '_' || isalnum(ST_INDEX(&tmp, i)))) {
+			ko_cat_c(v->ko, '\\');
+		}
+		ko_cat_c(v->ko, ST_INDEX(&tmp, i));
+	}
+	St_clear(&tmp);
+	v->type = V_STR_K;
+}
+
+void pp_remove(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	if (remove(ko_szp(v->ko))) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_rename(struct val *v) {
+	struct val *from, *to;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	from = li_at(v->magic.list, 0);
+	V_STR(from);
+	if (ko_chr(from->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	to = li_at(v->magic.list, 1);
+	V_STR(to);
+	if (ko_chr(to->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	}
+
+	if (rename(ko_szp(from->ko), ko_szp(to->ko))) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_reverse(struct val *v) {
+	if (V_LIST_P(v)) {
+		li_reverse(v->magic.list);
+		v->type = V_LIST_K;
+		return;
+	}
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_reverse(v->ko);
+	v->type = V_STR_K;
+}
+
+void pp_seek(struct val *v) {
+	struct val *fh, *tmp;
+	long off;
+	enum io_whence whence = IOS_START;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) < 2 || li_length(v->magic.list) > 3) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	}
+
+	fh = li_at(v->magic.list, 0);
+	if (!V_EXT_P(fh)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+
+	tmp = li_at(v->magic.list, 1);
+	V_NUM(tmp);
+	off = RINT(tmp->num);
+
+	if ((tmp = li_at(v->magic.list, 2))) {
+		V_NUM(tmp);
+		switch ((long)RINT(tmp->num)) {
+			case 0: whence = IOS_START; break;
+			case 1: whence = IOS_CUR;   break;
+			case 2: whence = IOS_END;   break;
+			default:
+				v_set_undef(v);
+				#ifdef EINVAL
+					errno = EINVAL;
+				#endif
+				return;
+		}
+	}
+
+	v_set_n(v, io_seek(fh->magic.ext, off, whence));
+}
+
+void pp_sin(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, sin(v->num));
+}
+
+void pp_sqrt(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->num = sqrt(v->num);
+	v->type = V_NUM_K;
+}
+
+void pp_str(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type |= V_STR_K;
+}
+
+void pp_tan(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, tan(v->num));
+}
+
+void pp_tell(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	v_set_n(v, io_tell(v->magic.ext));
+}
+
+void pp_typeof(struct val *v) {
+	if (V_SUB_P(v)) {
+		v_set_m(v, "stuff", 5);
+	} else if (V_EXT_P(v)) {
+		v_set_m(v, "stream", 6);
+	} else if (V_LIST_P(v)) {
+		v_set_m(v, "list", 4);
+	} else if (V_NUM_P(v)) {
+		v_set_m(v, "number", 6);
+	} else if (V_STR_P(v)) {
+		v_set_m(v, "string", 6);
+	} else {
+		v_set_m(v, "nothing", 7);
+	}
+}
+
+void pp_upper(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_upper(v->ko);
+}
+
+#define NUMB(expr)    \
+do {                  \
+	V_NUM(b);         \
+	V_NUM(v);         \
+	v_set_n(v, expr); \
+} while (0)
+
+#define ARITH(op) NUMB(v->num op b->num)
+
+void pp_add(struct val *v, struct val *b) {
+	ARITH(+);
+}
+
+#define LOGIC(init, cond)  \
+do {                       \
+	init;                  \
+	if (cond) {            \
+		v_set_n(v, 1.0);   \
+	} else {               \
+		v_set_m(v, "", 0); \
+	}                      \
+} while (0)
+
+void pp_and(struct val *v, struct val *b) {
+	LOGIC((void)0, v_true(v) && v_true(b));
+}
+
+void pp_comma(struct val *v, struct val *b) {
+	v_set(v, b);
+}
+
+void pp_concat(struct val *v, struct val *b) {
+	v_cat(v, b);
+}
+
+void pp_div(struct val *v, struct val *b) {
+	ARITH(/);
+}
+
+#define CMP_LS(op) LOGIC((void)0, v_cmp_ls(v, b) op 0)
+
+void pp_eq(struct val *v, struct val *b) {
+	CMP_LS(==);
+}
+
+#define CMP_N(op) LOGIC(V_NUM(v); V_NUM(b), v->num op b->num)
+
+void pp_eq_n(struct val *v, struct val *b) {
+	if (V_EXT_P(v)) {
+		if (V_EXT_P(b)) {
+			LOGIC((void)0, v->magic.ext == b->magic.ext);
+		} else if (V_SUB_P(b)) {
+			goto false_cmp;
+		} else {
+			goto default_cmp;
+		}
+	} else if (V_SUB_P(v)) {
+		if (V_SUB_P(b)) {
+			LOGIC((void)0, v->magic.sub == b->magic.sub);
+		} else if (V_EXT_P(b)) false_cmp: {
+			LOGIC((void)0, 0);
+		} else {
+			goto default_cmp;
+		}
+	} else default_cmp: {
+		CMP_N(==);
+	}
+}
+
+static const char xdigits[] = "0123456789" "abcdefghijklmnopqrstuvwxyz";
+
+void pp_frombase(struct val *v, struct val *b) {
+	double p;
+	size_t i;
+	int base, sign;
+	const char *tmp;
+
+	V_NUM(b);
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (floor(b->num + .5) < 2. || floor(b->num + .5) > 36.) {
+		v->type = V_UNDEF;
+		return;
+	}
+	base = floor(b->num + .5);
+	ko_shiftws(v->ko);
+
+	sign = 0;
+	if (ko_at(v->ko, 0) == '-') {
+		sign = 1;
+		ko_shift(v->ko, 1);
+	} else if (ko_at(v->ko, 0) == '+') {
+		ko_shift(v->ko, 1);
+	}
+
+	p = 0.0;
+	for (i = 0; i < ko_length(v->ko); ++i) {
+		if ((tmp = strchr(xdigits, tolower(ko_at(v->ko, i)))) &&
+				tmp - xdigits < base) {
+			p *= base;
+			p += tmp - xdigits;
+		} else {
+			break;
+		}
+	}
+
+	if (i < ko_length(v->ko)) {
+		if (ko_at(v->ko, i) == '.') {
+			double shift = 1.0;
+
+			for (++i; i < ko_length(v->ko); ++i) {
+				if ((tmp = strchr(xdigits, tolower(ko_at(v->ko, i)))) &&
+						tmp - xdigits < base) {
+					shift *= base;
+					p *= base;
+					p += tmp - xdigits;
+				} else {
+					break;
+				}
+			}
+
+			p /= shift;
+		}
+	}
+
+	if (sign) {
+		p = -p;
+	}
+	v_set_n(v, p);
+}
+
+void pp_gt(struct val *v, struct val *b) {
+	CMP_LS(>);
+}
+
+void pp_gt_n(struct val *v, struct val *b) {
+	CMP_N(>);
+}
+
+void pp_lt(struct val *v, struct val *b) {
+	CMP_LS(<);
+}
+
+void pp_lt_n(struct val *v, struct val *b) {
+	CMP_N(<);
+}
+
+void pp_match(struct val *v, struct val *b) {
+	t_regex *re;
+
+	V_STR(b);
+	re = re_compile(ko_str(b->ko));
+	do_match(v, re);
+	re_free(re);
+}
+
+void pp_mod(struct val *v, struct val *b) {
+	NUMB(fmod(v->num, b->num));
+}
+
+void pp_mult(struct val *v, struct val *b) {
+	ARITH(*);
+}
+
+void pp_ne(struct val *v, struct val *b) {
+	CMP_LS(!=);
+}
+
+void pp_ne_n(struct val *v, struct val *b) {
+	if (V_EXT_P(v)) {
+		if (V_EXT_P(b)) {
+			LOGIC((void)0, v->magic.ext != b->magic.ext);
+		} else if (V_SUB_P(b)) {
+			goto true_cmp;
+		} else {
+			goto default_cmp;
+		}
+	} else if (V_SUB_P(v)) {
+		if (V_SUB_P(b)) {
+			LOGIC((void)0, v->magic.sub != b->magic.sub);
+		} else if (V_EXT_P(b)) true_cmp: {
+			LOGIC((void)0, 1);
+		} else {
+			goto default_cmp;
+		}
+	} else default_cmp: {
+		CMP_N(!=);
+	}
+}
+
+void pp_or(struct val *v, struct val *b) {
+	LOGIC((void)0, v_true(v) || v_true(b));
+}
+
+void pp_pop(struct val *v, struct val *b) {
+	long p;
+
+	V_NUM(b);
+	p = RINT(b->num);
+
+	if (V_LIST_P(v)) {
+		v->type = V_LIST_K;
+		if (p < 0) {
+			p += li_length(v->magic.list);
+		}
+		if (p < 0) {
+			p = 0;
+		}
+		if ((size_t)p >= li_length(v->magic.list)) {
+			return;
+		}
+		li_trunc(v->magic.list, p);
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type = V_STR_K;
+	if (p < 0) {
+		p += ko_length(v->ko);
+	}
+	if (p < 0) {
+		p = 0;
+	}
+	if ((size_t)p >= ko_length(v->ko)) {
+		return;
+	}
+	ko_trunc(v->ko, p);
+}
+
+void pp_pow(struct val *v, struct val *b) {
+	NUMB(pow(v->num, b->num));
+}
+
+void pp_read(struct val *v, struct val *b) {
+	long n;
+
+	if (V_SUB_P(v)) {
+		const size_t curdepth = depth_get();
+		stack_store(&Interp.arg, b);
+		eval_into(sub_expr(v->magic.sub), v);
+		depth_restore(curdepth);
+		return;
+	}
+
+	if (V_EXT_P(v)) {
+		V_NUM(b);
+		ko_read(v->magic.ext, v->ko, b->num < 0.0 ? (size_t)-1 : b->num + .5);
+		V_xxx_OFF(v);
+		v->type = V_STR_K;
+		return;
+	}
+
+	if (V_LIST_P(v)) {
+		struct val *ptr;
+		V_NUM(b);
+		n = RINT(b->num);
+		if (n < 0) {
+			n += li_length(v->magic.list);
+		}
+
+		if (n >= 0 && (ptr = li_at(v->magic.list, n))) {
+			struct list *tmp = li_dup(v->magic.list);
+			v_set(v, ptr);
+			li_delete(tmp);
+		} else {
+			v_set_undef(v);
+		}
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	V_NUM(b);
+	n = RINT(b->num);
+	if (n < 0) {
+		n += ko_length(v->ko);
+	}
+	if (n >= 0 && (size_t)n < ko_length(v->ko)) {
+		ko_shift(v->ko, n);
+		ko_trunc(v->ko, 1);
+		v->type = V_STR_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_shift(struct val *v, struct val *b) {
+	long p;
+
+	V_NUM(b);
+	p = RINT(b->num);
+
+	if (V_LIST_P(v)) {
+		v->type = V_LIST_K;
+		if (p < 0) {
+			p += li_length(v->magic.list);
+		}
+		if (p <= 0) {
+			return;
+		}
+		if ((size_t)p >= li_length(v->magic.list)) {
+			li_zero(v->magic.list);
+		} else {
+			li_shift(v->magic.list, p);
+		}
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type = V_STR_K;
+	if (p < 0) {
+		p += ko_length(v->ko);
+	}
+	if (p <= 0) {
+		return;
+	}
+	if ((size_t)p >= ko_length(v->ko)) {
+		ko_zero(v->ko);
+	} else {
+		ko_shift(v->ko, p);
+	}
+}
+
+void pp_sub(struct val *v, struct val *b) {
+	ARITH(-);
+}
+
+void pp_tobase(struct val *v, struct val *b) {
+	double p;
+	int base, sign;
+	size_t i;
+
+	V_NUM(b);
+	V_NUM(v);
+	V_xxx_OFF(v);
+
+	if (floor(b->num + .5) < 2. || floor(b->num + .5) > 36.) {
+		v->type = V_UNDEF;
+		return;
+	}
+	base = floor(b->num + .5);
+
+	sign = 0;
+	if (v->num < 0.) {
+		v->num = -v->num;
+		sign = 1;
+	}
+
+	p = v->num;
+	ko_zero(v->ko);
+
+	if (floor(p) < p) {
+		double foo, bar;
+		size_t rounds;
+
+		foo = 1 + floor(log(pow(10, DBL_DIG)) / log(base));
+		bar = p > 0. ? 1 + floor(log(p) / log(base)) : 0.;
+		if (bar >= foo) {
+			rounds = 0;
+		} else {
+			rounds = foo - bar;
+		}
+
+		for (i = 0; i < rounds; ++i) {
+			p *= base;
+			if (floor(p) >= p) {
+				++i;
+				break;
+			}
+		}
+
+		p = floor(p);
+		while (i--) {
+			int c;
+			c = fmod(p, base);
+			p = floor(p / base);
+			if (c >= 0) {
+				ko_cat_c(v->ko, xdigits[c]);
+			}
+		}
+	}
+
+	while (ko_at(v->ko, 0) == '0') {
+		ko_shift(v->ko, 1);
+	}
+
+	if (ko_length(v->ko)) {
+		ko_cat_c(v->ko, '.');
+	}
+
+	while (p > 0.) {
+		int c;
+		c = fmod(p, base);
+		p = floor(p / base);
+		if (c >= 0) {
+			ko_cat_c(v->ko, xdigits[c]);
+		}
+	}
+
+	if (!ko_length(v->ko) || ko_lastchar(v->ko) == '.') {
+		ko_cat_c(v->ko, '0');
+	}
+	if (sign) {
+		ko_cat_c(v->ko, '-');
+	}
+
+	v->type = V_STR_K;
+}