shithub: femtolisp

Download patch

ref: 4ca4045c4d9956882813aba344b0b9430063e3e8
parent: 97d0c80fcc6e076d3a856f3ec2230194ce0e7b3f
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Mon Oct 28 22:13:16 EDT 2024

make → meson; fix a whole bunch of warnings; reorganize some of the platform-specific logic

--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,7 @@
-*.[05678qvt]
-*.o
+*.[05678qvtoa]
 *.out
-*.a
-/flisp
-/flisp.boot.bak
-/flisp.boot.new
-boot.h
+flisp
+flisp.boot.*
 instructions.lsp
 builtins.lsp
 builtin_fns.h
--- a/3rd/lookup3.c
+++ b/3rd/lookup3.c
@@ -135,99 +135,6 @@
 }
 
 /*
---------------------------------------------------------------------
- This works on all machines.  To be useful, it requires
- -- that the key be an array of uint32_t's, and
- -- that the length be the number of uint32_t's in the key
-
- The function hashword() is identical to hashlittle() on little-endian
- machines, and identical to hashbig() on big-endian machines,
- except that the length has to be measured in uint32_ts rather than in
- bytes.  hashlittle() is more complicated than hashword() only because
- hashlittle() has to dance around fitting the key bytes into registers.
---------------------------------------------------------------------
-*/
-uint32_t hashword(
-const uint32_t *k,                   /* the key, an array of uint32_t values */
-size_t          length,               /* the length of the key, in uint32_ts */
-uint32_t        initval)         /* the previous hash, or an arbitrary value */
-{
-  uint32_t a,b,c;
-
-  /* Set up the internal state */
-  a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
-
-  /*------------------------------------------------- handle most of the key */
-  while (length > 3)
-  {
-    a += k[0];
-    b += k[1];
-    c += k[2];
-    mix(a,b,c);
-    length -= 3;
-    k += 3;
-  }
-
-  /*------------------------------------------- handle the last 3 uint32_t's */
-  switch(length)                     /* all the case statements fall through */
-  {
-  case 3 : c+=k[2]; // fallthrough
-  case 2 : b+=k[1]; // fallthrough
-  case 1 : a+=k[0]; // fallthrough
-    final(a,b,c);
-  case 0:     /* case 0: nothing left to add */
-    break;
-  }
-  /*------------------------------------------------------ report the result */
-  return c;
-}
-
-/*
---------------------------------------------------------------------
-hashword2() -- same as hashword(), but take two seeds and return two
-32-bit values.  pc and pb must both be nonnull, and *pc and *pb must
-both be initialized with seeds.  If you pass in (*pb)==0, the output
-(*pc) will be the same as the return value from hashword().
---------------------------------------------------------------------
-*/
-void hashword2 (
-const uint32_t *k,                   /* the key, an array of uint32_t values */
-size_t          length,               /* the length of the key, in uint32_ts */
-uint32_t       *pc,                      /* IN: seed OUT: primary hash value */
-uint32_t       *pb)               /* IN: more seed OUT: secondary hash value */
-{
-  uint32_t a,b,c;
-
-  /* Set up the internal state */
-  a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
-  c += *pb;
-
-  /*------------------------------------------------- handle most of the key */
-  while (length > 3)
-  {
-    a += k[0];
-    b += k[1];
-    c += k[2];
-    mix(a,b,c);
-    length -= 3;
-    k += 3;
-  }
-
-  /*------------------------------------------- handle the last 3 uint32_t's */
-  switch(length)                     /* all the case statements fall through */
-  {
-  case 3 : c+=k[2]; // fallthrough
-  case 2 : b+=k[1]; // fallthrough
-  case 1 : a+=k[0]; // fallthrough
-    final(a,b,c);
-  case 0:     /* case 0: nothing left to add */
-    break;
-  }
-  /*------------------------------------------------------ report the result */
-  *pc=c; *pb=b;
-}
-
-/*
  * hashlittle2: return 2 32-bit hash values
  *
  * This is identical to hashlittle(), except it returns two 32-bit hash
@@ -237,7 +144,7 @@
  * the key.  *pc is better mixed than *pb, so use *pc first.  If you want
  * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
  */
-void hashlittle2(
+static void hashlittle2(
   const void *key,       /* the key to hash */
   size_t      length,    /* length of the key */
   uint32_t   *pc,        /* IN: primary initval, OUT: primary hash */
--- a/Makefile
+++ /dev/null
@@ -1,107 +1,0 @@
-DESTDIR?=
-PREFIX?=/usr/local
-BIN=${DESTDIR}${PREFIX}/bin
-
-TARG=flisp
-CFLAGS?=-O2 -g
-CFLAGS+=-Wall -Wextra -Wno-parentheses -std=c99 -I3rd -Iposix
-LDFLAGS?=
-
-OBJS=\
-	3rd/mp/mpadd.o\
-	3rd/mp/mpaux.o\
-	3rd/mp/mpcmp.o\
-	3rd/mp/mpdigdiv.o\
-	3rd/mp/mpdiv.o\
-	3rd/mp/mpfmt.o\
-	3rd/mp/mpleft.o\
-	3rd/mp/mplogic.o\
-	3rd/mp/mpmul.o\
-	3rd/mp/mpright.o\
-	3rd/mp/mpsub.o\
-	3rd/mp/mptobe.o\
-	3rd/mp/mptober.o\
-	3rd/mp/mptod.o\
-	3rd/mp/mptoi.o\
-	3rd/mp/mptoui.o\
-	3rd/mp/mptouv.o\
-	3rd/mp/mptov.o\
-	3rd/mp/mpvecadd.o\
-	3rd/mp/mpveccmp.o\
-	3rd/mp/mpvecdigmuladd.o\
-	3rd/mp/mpvecsub.o\
-	3rd/mp/mpvectscmp.o\
-	3rd/mp/strtomp.o\
-	3rd/mp/u16.o\
-	3rd/mp/u32.o\
-	3rd/mp/u64.o\
-	3rd/mt19937-64.o\
-	3rd/utf/rune.o\
-	3rd/utf/runeistype.o\
-	3rd/utf/runetotype.o\
-	3rd/utf/utfnlen.o\
-	bitvector-ops.o\
-	bitvector.o\
-	builtins.o\
-	cvalues.o\
-	dump.o\
-	equal.o\
-	equalhash.o\
-	flisp.o\
-	flmain.o\
-	hashing.o\
-	htable.o\
-	ios.o\
-	iostream.o\
-	llt.o\
-	main_posix.o\
-	operators.o\
-	print.o\
-	ptrhash.o\
-	random.o\
-	read.o\
-	string.o\
-	table.o\
-	timefuncs.o\
-	types.o\
-	utf8.o\
-
-.PHONY: all default test bootstrap clean
-
-all: default
-
-default: ${TARG}
-
-test: ${TARG}
-	cd test && ../$(TARG) unittest.lsp
-
-${TARG}: ${OBJS}
-	${CC} -o $@ ${OBJS} ${LDFLAGS} -lm
-
-.SUFFIXES: .c .o
-.c.o:
-	${CC} -o $@ -c $< ${CFLAGS}
-
-flisp.o: flisp.c flisp.h operators.h cvalues.h maxstack.inc opcodes.h builtin_fns.h
-flmain.o: flmain.c boot.h flisp.h cvalues.h
-main_posix.o: main_posix.c builtin_fns.h
-3rd/utf/runeistype.o: 3rd/utf/runeistypedata
-3rd/utf/runetotype.o: 3rd/utf/runetotypedata
-
-boot.h: flisp.boot
-	sed 's,\\,\\\\,g;s,",\\",g;s,^,",g;s,$$,\\n",g' flisp.boot >$@
-
-builtin_fns.h: *.c
-	sed -nE 's/^BUILTIN[_]?(\(".*)/BUILTIN_FN\1/gp' *.c >$@
-
-bootstrap: ${TARG} builtin_fns.h mkboot0.lsp mkboot1.lsp
-	cp flisp.boot flisp.boot.bak
-	./${TARG} gen.lsp
-	./${TARG} mkboot0.lsp instructions.lsp builtins.lsp system.lsp compiler.lsp >flisp.boot
-	${MAKE} clean
-	${MAKE} ${TARG}
-	./${TARG} mkboot1.lsp
-	${MAKE} test
-
-clean:
-	rm -f ${OBJS} ${TARG}
--- a/README.md
+++ b/README.md
@@ -22,6 +22,17 @@
  * fixed bootstrap (makes it work properly when opcodes change)
  * bigints
 
+## Building
+
+### POSIX
+
+	meson setup build -Dbuildtype=release
+	ninja -C build
+
+### Plan 9
+
+	mk
+
 ## Characteristics
 
   * lexical scope, lisp-1
--- /dev/null
+++ b/boot2h.sh
@@ -1,0 +1,3 @@
+#!/bin/sh
+set -e
+sed 's#\\#\\\\#g;s#"#\\"#g;s#^#"#g;s#$#\\n"#g' $*
--- /dev/null
+++ b/bootstrap.sh
@@ -1,0 +1,9 @@
+#!/bin/sh
+test -e
+F=./build/flisp
+test -x $F || { CC=clang meson setup -Dbuildtype=debug build . && ninja -C build || exit 1; }
+test -x $F || { echo no $F found; exit 1; }
+$F gen.lsp && \
+$F mkboot0.lsp system.lsp compiler.lsp > flisp.boot && \
+$F mkboot1.lsp && \
+ninja -C build || exit 1
--- a/builtins.c
+++ b/builtins.c
@@ -294,7 +294,7 @@
 {
 	argcount(nargs, 0);
 	USED(args);
-	return mk_double(clock_now());
+	return mk_double(sec_realtime());
 }
 
 static double
--- /dev/null
+++ b/builtins2h.sh
@@ -1,0 +1,3 @@
+#!/bin/sh
+set -e
+sed -nE 's/^BUILTIN[_]?(\(".*)/BUILTIN_FN\1/gp' $* | sort
--- a/cvalues.c
+++ b/cvalues.c
@@ -274,6 +274,7 @@
 num_init(double, double, T_DOUBLE)
 
 #define num_ctor_init(typenam, ctype, tag) \
+	static \
 	BUILTIN(#typenam, typenam) \
 	{ \
 		if(nargs == 0){ \
@@ -298,20 +299,20 @@
 	num_ctor_init(typenam, ctype, tag) \
 	num_ctor_ctor(typenam, ctype, tag)
 
-num_ctor(int8, int8_t, T_INT8)
-num_ctor(uint8, uint8_t, T_UINT8)
-num_ctor(int16, int16_t, T_INT16)
-num_ctor(uint16, uint16_t, T_UINT16)
+num_ctor_init(int8, int8_t, T_INT8)
+num_ctor_init(uint8, uint8_t, T_UINT8)
+num_ctor_init(int16, int16_t, T_INT16)
+num_ctor_init(uint16, uint16_t, T_UINT16)
 num_ctor(int32, int32_t, T_INT32)
 num_ctor(uint32, uint32_t, T_UINT32)
 num_ctor(int64, int64_t, T_INT64)
 num_ctor(uint64, uint64_t, T_UINT64)
-num_ctor(byte,  uint8_t, T_UINT8)
+num_ctor_init(byte,  uint8_t, T_UINT8)
 #if defined(ULONG64)
-num_ctor(long, int64_t, T_INT64)
+num_ctor_init(long, int64_t, T_INT64)
 num_ctor(ulong, uint64_t, T_UINT64)
 #else
-num_ctor(long, int32_t, T_INT32)
+num_ctor_init(long, int32_t, T_INT32)
 num_ctor(ulong, uint32_t, T_UINT32)
 #endif
 num_ctor(float, float, T_FLOAT)
@@ -907,7 +908,7 @@
 	cv->type = builtintype;
 	cv->data = &cv->_space[0];
 	cv->len = sizeof(value_t);
-	*(void**)cv->data = f;
+	*(builtin_t*)cv->data = f;
 
 	value_t sym = symbol(name);
 	((symbol_t*)ptr(sym))->dlcache = cv;
--- a/cvalues.h
+++ b/cvalues.h
@@ -1,5 +1,4 @@
-#ifndef CVALUES_H
-#define CVALUES_H
+#pragma once
 
 #ifdef BITS64
 #define NWORDS(sz) (((sz)+7)>>3)
@@ -77,9 +76,11 @@
 value_t mk_int64(int64_t n);
 value_t mk_uint64(uint64_t n);
 value_t mk_rune(Rune n);
+#if defined(ULONG64)
+value_t mk_ulong(uint64_t n);
+#else
+value_t mk_ulong(uint32_t n);
+#endif
 
 /* builtins.c */
 size_t llength(value_t v);
-
-#endif
-
--- a/equal.h
+++ b/equal.h
@@ -1,5 +1,4 @@
-#ifndef EQUAL_H
-#define EQUAL_H
+#pragma once
 
 // comparable with ==
 #define eq_comparable(a, b) (!(((a)|(b))&1))
@@ -10,6 +9,4 @@
 int equal_lispvalue(value_t a, value_t b);
 uintptr_t hash_lispvalue(value_t a);
 value_t compare_(value_t a, value_t b, int eq);
-
-#endif
-
+void comparehash_init(void);
--- a/flisp.c
+++ b/flisp.c
@@ -15,6 +15,8 @@
 #include "read.h"
 #include "equal.h"
 #include "hashing.h"
+#include "table.h"
+#include "iostream.h"
 
 typedef struct {
         char *name;
@@ -195,7 +197,7 @@
 SAFECAST_OP(cons, cons_t*, ptr)
 SAFECAST_OP(symbol, symbol_t*, ptr)
 SAFECAST_OP(fixnum, fixnum_t, numval)
-SAFECAST_OP(cvalue, cvalue_t*, ptr)
+//SAFECAST_OP(cvalue, cvalue_t*, ptr)
 SAFECAST_OP(string, char*, cvalue_data)
 #undef isstring
 
@@ -1035,7 +1037,7 @@
 			}else if(iscbuiltin(func)){
 				s = SP;
 				curr_fname = cvalue_name(func);
-				v = ((builtin_t)(((void**)ptr(func))[3]))(&Stack[SP-n], n);
+				v = (((builtin_t*)ptr(func))[3])(&Stack[SP-n], n);
 				SP = s-n;
 				Stack[SP-1] = v;
 				NEXT_OP;
@@ -2054,10 +2056,6 @@
 };
 
 // initialization -------------------------------------------------------------
-
-extern void comparehash_init(void);
-extern void table_init(void);
-extern void iostream_init(void);
 
 static void
 lisp_init(size_t initial_heapsize)
--- a/flisp.h
+++ b/flisp.h
@@ -1,5 +1,4 @@
-#ifndef FLISP_H
-#define FLISP_H
+#pragma once
 
 enum {
 	TAG_NUM,
@@ -351,6 +350,3 @@
 #define BUILTIN_FN(l, c) extern BUILTIN(l, c);
 #include "builtin_fns.h"
 #undef BUILTIN_FN
-
-#endif
-
--- a/flmain.c
+++ b/flmain.c
@@ -21,11 +21,8 @@
 extern fltype_t *iostreamtype;
 
 int
-flmain(int argc, char **argv)
+flmain(const char *boot, int bootsz, int argc, char **argv)
 {
-	static const char bootraw[] = {
-#include "boot.h"
-	};
 	value_t f;
 	ios_t *s;
 	int r;
@@ -34,7 +31,7 @@
 
 	f = cvalue(iostreamtype, sizeof(ios_t));
 	s = value2c(ios_t*, f);
-	ios_static_buffer(s, bootraw, sizeof(bootraw));
+	ios_static_buffer(s, boot, bootsz);
 
 	r = 1;
 	FL_TRY_EXTERN{
--- a/hashing.c
+++ b/hashing.c
@@ -1,4 +1,5 @@
 #include "llt.h"
+#include "hashing.h"
 
 lltuint_t
 nextipow2(lltuint_t i)
--- a/hashing.h
+++ b/hashing.h
@@ -1,5 +1,4 @@
-#ifndef HASHING_H_
-#define HASHING_H_
+#pragma once
 
 lltuint_t nextipow2(lltuint_t i);
 uint32_t int32hash(uint32_t a);
@@ -7,5 +6,3 @@
 uint32_t int64to32hash(uint64_t key);
 uint64_t memhash(const char* buf, size_t n);
 uint32_t memhash32(const char* buf, size_t n);
-
-#endif
--- a/htable.h
+++ b/htable.h
@@ -1,5 +1,4 @@
-#ifndef __HTABLE_H_
-#define __HTABLE_H_
+#pragma once
 
 #define HT_N_INLINE 32
 
@@ -18,5 +17,3 @@
 
 // clear and (possibly) change size
 void htable_reset(htable_t *h, size_t sz);
-
-#endif
--- a/ieee754.h
+++ b/ieee754.h
@@ -1,5 +1,4 @@
-#ifndef __IEEE754_H_
-#define __IEEE754_H_
+#pragma once
 
 union ieee754_float {
 	float f;
@@ -62,5 +61,3 @@
 };
 
 #define IEEE854_LONG_DOUBLE_BIAS 0x3fff
-
-#endif
--- a/ios.h
+++ b/ios.h
@@ -60,6 +60,8 @@
 	char local[IOS_INLSIZE];
 }ios_t;
 
+void *llt_memrchr(const void *s, int c, size_t n);
+
 /* low-level interface functions */
 size_t ios_read(ios_t *s, char *dest, size_t n);
 size_t ios_write(ios_t *s, const char *data, size_t n);
--- a/iostream.c
+++ b/iostream.c
@@ -4,12 +4,13 @@
 #include "types.h"
 #include "print.h"
 #include "read.h"
+#include "iostream.h"
 
 static value_t iostreamsym, rdsym, wrsym, apsym, crsym, truncsym;
 static value_t instrsym, outstrsym;
 fltype_t *iostreamtype;
 
-void
+static void
 print_iostream(value_t v, ios_t *f)
 {
 	USED(v);
@@ -16,7 +17,7 @@
 	fl_print_str("#<io stream>", f);
 }
 
-void
+static void
 free_iostream(value_t self)
 {
 	ios_t *s = value2c(ios_t*, self);
@@ -23,7 +24,7 @@
 	ios_close(s);
 }
 
-void
+static void
 relocate_iostream(value_t oldv, value_t newv)
 {
 	ios_t *olds = value2c(ios_t*, oldv);
@@ -39,8 +40,8 @@
 	nil
 };
 
-int
-fl_isiostream(value_t v)
+static int
+isiostream(value_t v)
 {
 	return iscvalue(v) && cv_class(ptr(v)) == iostreamtype;
 }
@@ -48,7 +49,7 @@
 BUILTIN("iostream?", iostreamp)
 {
 	argcount(nargs, 1);
-	return fl_isiostream(args[0]) ? FL_T : FL_F;
+	return isiostream(args[0]) ? FL_T : FL_F;
 }
 
 BUILTIN("eof-object", eof_object)
@@ -64,10 +65,10 @@
 	return args[0] == FL_EOF ? FL_T : FL_F;
 }
 
-ios_t *
-fl_toiostream(value_t v)
+static ios_t *
+toiostream(value_t v)
 {
-	if(!fl_isiostream(v))
+	if(!isiostream(v))
 		type_error("iostream", v);
 	return value2c(ios_t*, v);
 }
@@ -121,7 +122,7 @@
 		arg = symbol_value(instrsym);
 	else
 		arg = args[0];
-	ios_t *s = fl_toiostream(arg);
+	ios_t *s = toiostream(arg);
 	fl_gc_handle(&arg);
 	value_t v = fl_read_sexpr(arg);
 	fl_free_gc_handles(1);
@@ -133,7 +134,7 @@
 BUILTIN("io-getc", io_getc)
 {
 	argcount(nargs, 1);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	Rune r;
 	int res;
 	if((res = ios_getutf8(s, &r)) == IOS_EOF)
@@ -147,7 +148,7 @@
 BUILTIN("io-putc", io_putc)
 {
 	argcount(nargs, 2);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	if(!iscprim(args[1]) || ((cprim_t*)ptr(args[1]))->type != runetype)
 		type_error("rune", args[1]);
 	Rune r = *(Rune*)cp_data((cprim_t*)ptr(args[1]));
@@ -157,7 +158,7 @@
 BUILTIN("io-skip", io_skip)
 {
 	argcount(nargs, 2);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	off_t off = tooffset(args[1]);
 	off_t res = ios_skip(s, off);
 	if(res < 0)
@@ -168,13 +169,13 @@
 BUILTIN("io-flush", io_flush)
 {
 	argcount(nargs, 1);
-	return ios_flush(fl_toiostream(args[0])) == 0 ? FL_T : FL_F;
+	return ios_flush(toiostream(args[0])) == 0 ? FL_T : FL_F;
 }
 
 BUILTIN("io-close", io_close)
 {
 	argcount(nargs, 1);
-	ios_close(fl_toiostream(args[0]));
+	ios_close(toiostream(args[0]));
 	return FL_T;
 }
 
@@ -181,7 +182,7 @@
 BUILTIN("io-discardbuffer", io_discardbuffer)
 {
 	argcount(nargs, 1);
-	ios_purge(fl_toiostream(args[0]));
+	ios_purge(toiostream(args[0]));
 	return FL_T;
 }
 
@@ -188,13 +189,13 @@
 BUILTIN("io-eof?", io_eofp)
 {
 	argcount(nargs, 1);
-	return ios_eof(fl_toiostream(args[0])) ? FL_T : FL_F;
+	return ios_eof(toiostream(args[0])) ? FL_T : FL_F;
 }
 
 BUILTIN("io-seek", io_seek)
 {
 	argcount(nargs, 2);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	size_t pos = toulong(args[1]);
 	off_t res = ios_seek(s, (off_t)pos);
 	if(res == -1)
@@ -205,7 +206,7 @@
 BUILTIN("io-pos", io_pos)
 {
 	argcount(nargs, 1);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	off_t res = ios_pos(s);
 	if(res == -1)
 		return FL_F;
@@ -217,7 +218,7 @@
 	if(nargs < 1 || nargs > 2)
 		argcount(nargs, 1);
 	ios_t *s;
-	s = nargs == 2 ? fl_toiostream(args[1]) : fl_toiostream(symbol_value(outstrsym));
+	s = nargs == 2 ? toiostream(args[1]) : toiostream(symbol_value(outstrsym));
 	fl_print(s, args[0]);
 	return args[0];
 }
@@ -226,7 +227,7 @@
 {
 	if(nargs != 3)
 		argcount(nargs, 2);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	size_t n;
 	fltype_t *ft;
 	if(nargs == 3){
@@ -267,7 +268,7 @@
 {
 	if(nargs < 2 || nargs > 4)
 		argcount(nargs, 2);
-	ios_t *s = fl_toiostream(args[0]);
+	ios_t *s = toiostream(args[0]);
 	if(iscprim(args[1]) && ((cprim_t*)ptr(args[1]))->type == runetype){
 		if(nargs > 2)
 			lerrorf(ArgError, "offset argument not supported for characters");
@@ -289,7 +290,7 @@
 {
 	if(nargs < 1 || nargs > 3)
 		argcount(nargs, 1);
-	ios_t *s = fl_toiostream(symbol_value(outstrsym));
+	ios_t *s = toiostream(symbol_value(outstrsym));
 	char *data;
 	size_t sz, offs = 0;
 	to_sized_ptr(args[0], &data, &sz);
@@ -324,7 +325,7 @@
 	ios_mem(&dest, 0);
 	ios_setbuf(&dest, data, 80, 0);
 	char delim = get_delim_arg(args[1]);
-	ios_t *src = fl_toiostream(args[0]);
+	ios_t *src = toiostream(args[0]);
 	size_t n = ios_copyuntil(&dest, src, delim);
 	cv->len = n;
 	if(dest.buf != data){
@@ -345,8 +346,8 @@
 BUILTIN("io-copyuntil", io_copyuntil)
 {
 	argcount(nargs, 3);
-	ios_t *dest = fl_toiostream(args[0]);
-	ios_t *src = fl_toiostream(args[1]);
+	ios_t *dest = toiostream(args[0]);
+	ios_t *src = toiostream(args[1]);
 	char delim = get_delim_arg(args[2]);
 	return size_wrap(ios_copyuntil(dest, src, delim));
 }
@@ -355,8 +356,8 @@
 {
 	if(nargs < 2 || nargs > 3)
 		argcount(nargs, 2);
-	ios_t *dest = fl_toiostream(args[0]);
-	ios_t *src = fl_toiostream(args[1]);
+	ios_t *dest = toiostream(args[0]);
+	ios_t *src = toiostream(args[1]);
 	if(nargs == 3)
 		return size_wrap(ios_copy(dest, src, toulong(args[2])));
 	return size_wrap(ios_copyall(dest, src));
@@ -387,7 +388,7 @@
 BUILTIN("io-tostring!", io_tostring)
 {
 	argcount(nargs, 1);
-	ios_t *src = fl_toiostream(args[0]);
+	ios_t *src = toiostream(args[0]);
 	if(src->bm != bm_mem)
 		lerrorf(ArgError, "requires memory stream");
 	return stream_to_string(&args[0]);
--- /dev/null
+++ b/iostream.h
@@ -1,0 +1,2 @@
+value_t stream_to_string(value_t *ps);
+void iostream_init(void);
--- a/llt.h
+++ b/llt.h
@@ -1,5 +1,4 @@
-#ifndef __LLT_H_
-#define __LLT_H_
+#pragma once
 
 #include "platform.h"
 #include "utf8.h"
@@ -69,4 +68,4 @@
 int isdigit_base(char c, int base);
 void llt_init(void);
 
-#endif
+int flmain(const char *boot, int bootsz, int argc, char **argv);
--- a/main_plan9.c
+++ b/main_plan9.c
@@ -1,6 +1,8 @@
-#include "platform.h"
+#include "llt.h"
 
-int flmain(int argc, char **argv);
+static const char boot[] = {
+#include "flisp.boot.h"
+};
 
 void
 main(int argc, char **argv)
@@ -8,5 +10,5 @@
 	argv0 = argv[0];
 	setfcr(FPPDBL|FPRNR|FPOVFL);
 	tmfmtinstall();
-	exit(flmain(argc, argv));
+	exit(flmain(boot, sizeof(boot), argc, argv));
 }
--- a/main_posix.c
+++ b/main_posix.c
@@ -1,9 +1,11 @@
-#include "platform.h"
+#include "llt.h"
 
-int flmain(int argc, char **argv);
+static const char boot[] =
+#include "flisp.boot.h"
+;
 
 int
 main(int argc, char **argv)
 {
-	return flmain(argc, argv);
+	return flmain(boot, sizeof(boot), argc, argv);
 }
--- /dev/null
+++ b/meson.build
@@ -1,0 +1,145 @@
+project(
+	'femtolisp',
+	'c',
+	version: '0.999',
+	default_options: [
+		'c_std=c2x',
+		'warning_level=3',
+		'buildtype=debugoptimized',
+		#'b_coverage=true',
+	],
+)
+
+add_project_arguments(
+	#'-Wconversion',
+	#'-Wsign-conversion',
+	'-Wmissing-prototypes',
+	'-Werror=odr',
+	'-Werror=strict-aliasing',
+	'-Wno-parentheses',
+	'-Wno-overlength-strings',
+	'-D_DEFAULT_SOURCE',
+	language: 'c',
+)
+
+src = [
+	'3rd/mt19937-64.c',
+	'3rd/mp/mpadd.c',
+	'3rd/mp/mpaux.c',
+	'3rd/mp/mpcmp.c',
+	'3rd/mp/mpdigdiv.c',
+	'3rd/mp/mpdiv.c',
+	'3rd/mp/mpfmt.c',
+	'3rd/mp/mpleft.c',
+	'3rd/mp/mplogic.c',
+	'3rd/mp/mpmul.c',
+	'3rd/mp/mpright.c',
+	'3rd/mp/mpsub.c',
+	'3rd/mp/mptobe.c',
+	'3rd/mp/mptober.c',
+	'3rd/mp/mptod.c',
+	'3rd/mp/mptoi.c',
+	'3rd/mp/mptoui.c',
+	'3rd/mp/mptouv.c',
+	'3rd/mp/mptov.c',
+	'3rd/mp/mpvecadd.c',
+	'3rd/mp/mpveccmp.c',
+	'3rd/mp/mpvecdigmuladd.c',
+	'3rd/mp/mpvecsub.c',
+	'3rd/mp/mpvectscmp.c',
+	'3rd/mp/strtomp.c',
+	'3rd/mp/u16.c',
+	'3rd/mp/u32.c',
+	'3rd/mp/u64.c',
+	'3rd/utf/rune.c',
+	'3rd/utf/runeistype.c',
+	'3rd/utf/runetotype.c',
+	'3rd/utf/utfnlen.c',
+	'bitvector-ops.c',
+	'bitvector.c',
+	'builtins.c',
+	'cvalues.c',
+	'dump.c',
+	'equal.c',
+	'equalhash.c',
+	'flisp.c',
+	'flmain.c',
+	'hashing.c',
+	'htable.c',
+	'ios.c',
+	'iostream.c',
+	'llt.c',
+	'main_posix.c',
+	'operators.c',
+	'print.c',
+	'ptrhash.c',
+	'random.c',
+	'read.c',
+	'string.c',
+	'table.c',
+	'time_posix.c',
+	'types.c',
+	'utf8.c',
+]
+
+cc = meson.get_compiler('c')
+
+if cc.get_id() == 'clang'
+	add_project_arguments(
+		'-D__wchar_t=__please_no_wchar_t_thank_you',
+		language: 'c',
+	)
+else
+	add_project_arguments(
+		'-Werror=lto-type-mismatch',
+		language: 'c',
+	)
+endif
+
+math = cc.find_library('m', required: false)
+
+boot = custom_target(
+	'boot',
+	capture: true,
+	input: [
+		'flisp.boot',
+	],
+	output: [
+		'flisp.boot.h',
+	],
+	command: [
+		'boot2h.sh', '@INPUT@',
+	],
+)
+
+builtins = custom_target(
+	'builtins',
+	capture: true,
+	input: [
+		src,
+	],
+	output: [
+		'builtin_fns.h',
+	],
+	command: [
+		'builtins2h.sh', '@INPUT@',
+	],
+)
+
+executable(
+	'flisp',
+	sources: [
+		src,
+		boot,
+		builtins,
+	],
+	dependencies: [
+		math,
+	],
+	include_directories: include_directories(
+		'3rd',
+		'3rd/mp',
+		'3rd/utf',
+		'posix',
+	),
+)
--- a/mkfile
+++ b/mkfile
@@ -3,7 +3,7 @@
 BIN=/$objtype/bin
 TARG=flisp
 CFLAGS=$CFLAGS -p -D__plan9__ -D__${objtype}__ -I3rd -Iplan9
-CLEANFILES=boot.h builtin_fns.h
+CLEANFILES=plan9/flisp.boot.h plan9/builtin_fns.h
 
 HFILES=\
 	equalhash.h\
@@ -34,7 +34,7 @@
 	read.$O\
 	string.$O\
 	table.$O\
-	timefuncs.$O\
+	time_plan9.$O\
 	types.$O\
 	utf8.$O\
 
@@ -42,16 +42,15 @@
 
 </sys/src/cmd/mkone
 
-boot.h: flisp.boot
+plan9/flisp.boot.h: flisp.boot
 	sed 's,\\,\\\\,g;s,",\\",g;s,^,",g;s,$,\\n",g' $prereq >$target
 
-builtin_fns.h:
-	sed -n 's/^BUILTIN[_]?(\(".*)/BUILTIN_FN\1/gp' *.c >$target
+plan9/builtin_fns.h:
+	sed -n 's/^BUILTIN[_]?(\(".*)/BUILTIN_FN\1/gp' *.c | sort >$target
 
-main_plan9.$O: boot.h builtin_fns.h
-flisp.$O: maxstack.inc opcodes.h builtin_fns.h
-flmain.$O: boot.h
-builtins.$O: builtin_fns.h
+main_plan9.$O: plan9/flisp.boot.h plan9/builtin_fns.h
+flisp.$O: maxstack.inc opcodes.h plan9/builtin_fns.h
+builtins.$O: plan9/builtin_fns.h
 
 %.$O: %.c
 	$CC $CFLAGS -o $target $stem.c
--- a/operators.h
+++ b/operators.h
@@ -1,5 +1,4 @@
-#ifndef OPERATORS_H
-#define OPERATORS_H
+#pragma once
 
 mpint * conv_to_mpint(void *data, numerictype_t tag);
 double conv_to_double(void *data, numerictype_t tag);
@@ -23,6 +22,3 @@
 #define conv_to_long conv_to_int32
 #define conv_to_ulong conv_to_uint32
 #endif
-
-#endif
-
--- a/posix/mp.h
+++ b/posix/mp.h
@@ -1,5 +1,4 @@
-#ifndef _MPINT_H_
-#define _MPINT_H_
+#pragma once
 
 typedef uint32_t mpdigit;
 typedef uint8_t uchar;
@@ -232,5 +231,3 @@
 
 Mfield *gmfield(mpint*);
 Mfield *cnfield(mpint*);
-
-#endif
--- a/posix/platform.h
+++ b/posix/platform.h
@@ -10,6 +10,7 @@
 #include <locale.h>
 #include <math.h>
 #include <setjmp.h>
+#include <stdbool.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stddef.h>
--- a/print.c
+++ b/print.c
@@ -545,7 +545,7 @@
 	outc('"', f);
 }
 
-int
+static int
 double_exponent(double d)
 {
 	union ieee754_double dl;
@@ -554,7 +554,7 @@
 	return dl.ieee.exponent - IEEE754_DOUBLE_BIAS;
 }
 
-void
+static void
 snprint_real(char *s, size_t cnt, double r,
              int width, // printf field width, or 0
 			 int dec, // # decimal digits desired, recommend 16
--- a/print.h
+++ b/print.h
@@ -1,5 +1,4 @@
-#ifndef PRINT_H
-#define PRINT_H
+#pragma once
 
 extern htable_t printconses;
 extern int SCR_WIDTH;
@@ -9,6 +8,3 @@
 void fl_print_chr(char c, ios_t *f);
 void fl_print_str(char *s, ios_t *f);
 void fl_print_child(ios_t *f, value_t v);
-
-#endif
-
--- a/random.c
+++ b/random.c
@@ -1,9 +1,7 @@
-/*
-  random numbers
-*/
 #include "llt.h"
 #include "mt19937-64.h"
 #include "timefuncs.h"
+#include "random.h"
 
 static mt19937_64 ctx;
 
@@ -34,6 +32,6 @@
 void
 randomize(void)
 {
-	unsigned long long tm = i64time();
+	unsigned long long tm = nanosec_monotonic();
 	init_by_array64(&ctx, &tm, 1);
 }
--- a/random.h
+++ b/random.h
@@ -1,5 +1,4 @@
-#ifndef RANDOM_H_
-#define RANDOM_H_
+#pragma once
 
 void randomize(void);
 double genrand_double(void);
@@ -6,5 +5,3 @@
 uint64_t genrand_uint64(void);
 uint32_t genrand_uint32(void);
 int64_t genrand_int63(void);
-
-#endif
--- a/read.h
+++ b/read.h
@@ -1,5 +1,4 @@
-#ifndef READ_H
-#define READ_H
+#pragma once
 
 value_t fl_read_sexpr(value_t f);
 int isnumtok_base(char *tok, value_t *pval, int base);
@@ -10,5 +9,3 @@
 // unless it's the only character in the symbol, and '#', which is
 // an ordinary symbol character unless it's the first character.
 #define symchar(c) (!strchr("()[]{}'\";`,\\| \a\b\f\n\r\t\v", (c)))
-
-#endif
--- a/string.c
+++ b/string.c
@@ -8,6 +8,7 @@
 #include "print.h"
 #include "read.h"
 #include "equal.h"
+#include "iostream.h"
 
 BUILTIN("string?", stringp)
 {
@@ -111,7 +112,6 @@
 }
 
 extern BUILTIN("buffer", buffer);
-extern value_t stream_to_string(value_t *ps);
 
 BUILTIN("string", string)
 {
--- a/table.c
+++ b/table.c
@@ -4,11 +4,12 @@
 #include "cvalues.h"
 #include "types.h"
 #include "print.h"
+#include "table.h"
 
 static value_t tablesym;
 static fltype_t *tabletype;
 
-void
+static void
 print_htable(value_t v, ios_t *f)
 {
 	htable_t *h = (htable_t*)cv_data(ptr(v));
@@ -28,7 +29,7 @@
 	fl_print_chr(')', f);
 }
 
-void
+static void
 print_traverse_htable(value_t self)
 {
 	htable_t *h = (htable_t*)cv_data(ptr(self));
@@ -41,7 +42,7 @@
 	}
 }
 
-void
+static void
 free_htable(value_t self)
 {
 	htable_t *h = (htable_t*)cv_data((cvalue_t*)ptr(self));
@@ -48,7 +49,7 @@
 	htable_free(h);
 }
 
-void
+static void
 relocate_htable(value_t oldv, value_t newv)
 {
 	htable_t *oldh = (htable_t*)cv_data(ptr(oldv));
@@ -69,7 +70,7 @@
 	print_traverse_htable,
 };
 
-int
+static int
 ishashtable(value_t v)
 {
 	return iscvalue(v) && cv_class((cvalue_t*)ptr(v)) == tabletype;
--- /dev/null
+++ b/table.h
@@ -1,0 +1,1 @@
+void table_init(void);
--- /dev/null
+++ b/time_plan9.c
@@ -1,0 +1,68 @@
+#include "llt.h"
+#include "timefuncs.h"
+#include <tos.h>
+
+double
+sec_realtime(void)
+{
+	vlong t = nsec();
+	vlong ns = t % 1000000000LL;
+	vlong s = (t - ns) / 1000000000LL;
+	return (double)s + (double)ns/1.0e9;
+}
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uint64_t
+nanosec_monotonic(void)
+{
+	static uint64_t fasthz, xstart;
+	uint64_t x, div;
+
+	if(fasthz == ~0ULL)
+		return nsec() - xstart;
+
+	if(fasthz == 0){
+		if(_tos->cyclefreq){
+			fasthz = _tos->cyclefreq;
+			cycles(&xstart);
+		} else {
+			fasthz = ~0ULL;
+			xstart = nsec();
+		}
+		return 0;
+	}
+	cycles(&x);
+	x -= xstart;
+
+	/* this is ugly */
+	for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+	return x / (fasthz / div);
+}
+
+void
+timestring(double s, char *buf, int sz)
+{
+	Tm tm;
+	snprint(buf, sz, "%τ", tmfmt(tmtime(&tm, s, tzload("local")), nil));
+}
+
+double
+parsetime(const char *s)
+{
+	Tm tm;
+	if(tmparse(&tm, "?WWW, ?MM ?DD hh:mm:ss ?Z YYYY", s, tzload("local"), nil) == nil)
+		return -1;
+	return tmnorm(&tm);
+}
+
+void
+sleep_ms(int ms)
+{
+	if(ms != 0)
+		sleep(ms);
+}
--- /dev/null
+++ b/time_posix.c
@@ -1,0 +1,70 @@
+#include "llt.h"
+#include "timefuncs.h"
+
+double
+sec_realtime(void)
+{
+	struct timespec now;
+	if(clock_gettime(CLOCK_REALTIME, &now) != 0)
+		return 0;
+	return (double)now.tv_sec + (double)now.tv_nsec/1.0e9;
+}
+
+uint64_t
+nanosec_monotonic(void)
+{
+	static int64_t z;
+	struct timespec now;
+
+	if(clock_gettime(CLOCK_MONOTONIC, &now) != 0)
+		return 0;
+	if(z == 0){
+		z = now.tv_sec*1000000000LL + now.tv_nsec;
+		return 0;
+	}
+	return now.tv_sec*1000000000LL + now.tv_nsec - z;
+}
+
+void
+timestring(double s, char *buf, int sz)
+{
+	time_t tme = (time_t)s;
+	char *fmt = "%c"; /* needed to suppress GCC warning */
+	struct tm tm;
+
+	localtime_r(&tme, &tm);
+	strftime(buf, sz, fmt, &tm);
+}
+
+double
+parsetime(const char *s)
+{
+	char *fmt = "%c"; /* needed to suppress GCC warning */
+	char *res;
+	time_t t;
+	struct tm tm;
+
+	res = strptime(s, fmt, &tm);
+	if(res != nil){
+		/* Not set by strptime(); tells mktime() to determine 
+		 * whether daylight saving time is in effect
+		 */
+		tm.tm_isdst = -1;
+		t = mktime(&tm);
+		if(t == (time_t)-1)
+			return -1;
+		return (double)t;
+	}
+	return -1;
+}
+
+void
+sleep_ms(int ms)
+{
+	if(ms != 0){
+		struct timeval timeout;
+		timeout.tv_sec = ms/1000;
+		timeout.tv_usec = (ms % 1000) * 1000;
+		select(0, nil, nil, nil, &timeout);
+	}
+}
--- a/timefuncs.c
+++ /dev/null
@@ -1,116 +1,0 @@
-#include "platform.h"
-
-#if defined(__plan9__)
-double
-floattime(void)
-{
-	return (double)nsec() / 1.0e9;
-}
-#else
-double
-tv2float(struct timeval *tv)
-{
-	return (double)tv->tv_sec + (double)tv->tv_usec/1.0e6;
-}
-
-double
-diff_time(struct timeval *tv1, struct timeval *tv2)
-{
-	return tv2float(tv1) - tv2float(tv2);
-}
-#endif
-
-// return as many bits of system randomness as we can get our hands on
-uint64_t
-i64time(void)
-{
-	uint64_t a;
-#if defined(__plan9__)
-	a = nsec();
-#else
-	struct timeval now;
-	gettimeofday(&now, nil);
-	a = (((uint64_t)now.tv_sec)<<32) + (uint64_t)now.tv_usec;
-#endif
-
-	return a;
-}
-
-double
-clock_now(void)
-{
-#if defined(__plan9__)
-	return floattime();
-#else
-	struct timeval now;
-	gettimeofday(&now, nil);
-	return tv2float(&now);
-#endif
-}
-
-void
-timestring(double seconds, char *buffer, size_t len)
-{
-#if defined(__plan9__)
-	Tm tm;
-	snprint(buffer, len, "%τ", tmfmt(tmtime(&tm, seconds, tzload("local")), nil));
-#else
-	time_t tme = (time_t)seconds;
-
-	char *fmt = "%c"; /* needed to suppress GCC warning */
-	struct tm tm;
-
-	localtime_r(&tme, &tm);
-	strftime(buffer, len, fmt, &tm);
-#endif
-}
-
-#if defined(__plan9__)
-double
-parsetime(const char *str)
-{
-	Tm tm;
-	if(tmparse(&tm, "?WWW, ?MM ?DD hh:mm:ss ?Z YYYY", str, tzload("local"), nil) == nil)
-		return -1;
-	return tmnorm(&tm);
-}
-#else
-double
-parsetime(const char *str)
-{
-	char *fmt = "%c"; /* needed to suppress GCC warning */
-	char *res;
-	time_t t;
-	struct tm tm;
-
-	res = strptime(str, fmt, &tm);
-	if(res != nil){
-		/* Not set by strptime(); tells mktime() to determine 
-		 * whether daylight saving time is in effect
-		 */
-		tm.tm_isdst = -1;
-		t = mktime(&tm);
-		if(t == (time_t)-1)
-			return -1;
-		return (double)t;
-	}
-	return -1;
-}
-#endif
-
-void
-sleep_ms(int ms)
-{
-	if(ms == 0)
-		return;
-
-#if defined(__plan9__)
-	sleep(ms);
-#else
-	struct timeval timeout;
-
-	timeout.tv_sec = ms/1000;
-	timeout.tv_usec = (ms % 1000) * 1000;
-	select(0, nil, nil, nil, &timeout);
-#endif
-}
--- a/timefuncs.h
+++ b/timefuncs.h
@@ -1,10 +1,7 @@
-#ifndef TIMEFUNCS_H_
-#define TIMEFUNCS_H_
+#pragma once
 
-uint64_t i64time(void);
-double clock_now(void);
-void timestring(double seconds, char *buffer, size_t len);
-double parsetime(const char *str);
+double sec_realtime(void);
+uint64_t nanosec_monotonic(void);
+void timestring(double s, char *buf, int sz);
+double parsetime(const char *s);
 void sleep_ms(int ms);
-
-#endif
--- a/types.c
+++ b/types.c
@@ -2,6 +2,7 @@
 #include "flisp.h"
 #include "cvalues.h"
 #include "equalhash.h"
+#include "types.h"
 
 fltype_t *
 get_type(value_t t)
--- a/types.h
+++ b/types.h
@@ -1,10 +1,6 @@
-#ifndef TYPES_H
-#define TYPES_H
+#pragma once
 
 fltype_t *get_type(value_t t);
 fltype_t *get_array_type(value_t eltype);
 fltype_t *define_opaque_type(value_t sym, size_t sz, cvtable_t *vtab, cvinitfunc_t init);
 void relocate_typetable(void);
-
-#endif
-
--- a/utf8.c
+++ b/utf8.c
@@ -111,7 +111,7 @@
 	case 't': return '\t';
 	case 'a': return '\a';
 	case 'b': return '\b';
-	case 'e': return '\e';
+	case 'e': return 0x1b;
 	case 'f': return '\f';
 	case 'r': return '\r';
 	case 'v': return '\v';
@@ -147,7 +147,7 @@
 	case '\\': return buf_put2c(buf, "\\\\");
 	case '\a': return buf_put2c(buf, "\\a");
 	case '\b': return buf_put2c(buf, "\\b");
-	case '\e': return buf_put2c(buf, "\\e");
+	case 0x1b: return buf_put2c(buf, "\\e");
 	case '\f': return buf_put2c(buf, "\\f");
 	case '\r': return buf_put2c(buf, "\\r");
 	case '\v': return buf_put2c(buf, "\\v");
--- a/utf8.h
+++ b/utf8.h
@@ -1,5 +1,4 @@
-#ifndef __UTF8_H_
-#define __UTF8_H_
+#pragma once
 
 /* is c the start of a utf8 sequence? */
 #define isutf(c) (((c)&0xC0) != 0x80)
@@ -57,5 +56,3 @@
 /* reverse a UTF-8 string. len is length in bytes. dest and src must both
    be allocated to at least len+1 bytes. returns 1 for error, 0 otherwise */
 int u8_reverse(char *dest, char *src, size_t len);
-
-#endif