aboutsummaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rwxr-xr-xsrc/tools/automap.sh10
-rw-r--r--src/tools/build.zc13
-rwxr-xr-xsrc/tools/embed.sh89
-rw-r--r--src/tools/genmap.c13
-rw-r--r--src/tools/mkgenome.c11
-rwxr-xr-xsrc/tools/mklsp.sh12
-rwxr-xr-xsrc/tools/mklspwasm.sh6
-rwxr-xr-xsrc/tools/mkvsix.sh4
-rw-r--r--src/tools/zc0.c103
-rwxr-xr-xsrc/tools/zcc4
10 files changed, 230 insertions, 35 deletions
diff --git a/src/tools/automap.sh b/src/tools/automap.sh
index 8cefe6a..69849ca 100755
--- a/src/tools/automap.sh
+++ b/src/tools/automap.sh
@@ -13,10 +13,16 @@
# zc0 reads the map at build time, so it is built again for each layout.
# GENMAP can name a genmap that is already built.
set -e
+case "${1:-}" in
+ --help|-h) printf '%s\n' 'usage: src/tools/automap.sh run|small|default|lsp OUTDIR FILE...' 'builds a genome and memory map sized to the supplied source files.' 'output: OUTDIR/genome.h and OUTDIR/memmap.h' 'environment: CC selects the c compiler; DOCS=1 retains documentation; GENMAP selects genmap.'; exit 0 ;;
+ -*) echo "automap: unknown option $1; use --help for usage" >&2; exit 2 ;;
+esac
cd "$(dirname "$0")/../.."
+[[ $# -ge 2 ]] || { echo 'usage: src/tools/automap.sh run|small|default|lsp OUTDIR FILE...' >&2; exit 2; }
P=$1; O=$2; shift 2
[ -n "$P" ] && [ -n "$O" ] || { echo "usage: src/tools/automap.sh run|small|default|lsp OUTDIR FILE..." >&2; exit 2; }
CC=${CC:-gcc}
+bash src/tools/embed.sh all --check || bash src/tools/embed.sh all
mkdir -p "$O"
ZC0=$O/.zc0
if [ -z "$GENMAP" ]; then
@@ -29,14 +35,14 @@ lay() { # $1 = genome slot, or empty for the preset's own
$CC -O1 -w -fwrapv src/tools/zc0.c -I"$O/map" -Isrc/core -o "$ZC0"
}
build() {
- if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" "$@"; fi | "$ZC0"
+ if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" src/core/core.zc "$@"; fi | "$ZC0"
}
# with DOCS=1 the genome is made again with zc0 built for docs (ZC0DOCS), so the
# image carries the doc comments of the sources and the program can answer
# docofs(). the image is the same code either way; the docs are stored after it.
docs() {
$CC -O1 -w -fwrapv -DZC0DOCS src/tools/zc0.c -I"$O/map" -Isrc/core -o "$O/.zc0docs"
- if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" "$@"; fi | "$O/.zc0docs"
+ if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" src/core/core.zc "$@"; fi | "$O/.zc0docs"
}
len() { grep -m1 '#define GLEN' "$1" | awk '{print $3}'; }
lay ""
diff --git a/src/tools/build.zc b/src/tools/build.zc
index 547b70f..b0e3da0 100644
--- a/src/tools/build.zc
+++ b/src/tools/build.zc
@@ -1,13 +1,18 @@
+namespace;
+include rt, run;
+
// compiles the rest of stdin (after the backtick) as a genome image at GORG
// and writes it to stdout as little-endian 64-bit words.
+
+
docs on;
// writes the compiled genome image to stdout as little-endian 64-bit words.
int main() {
- int n = build_image_grow(GORG, 4096), i = 0, w;
+ int n = run.build_image_grow(rt.GORG, 4096), i = 0, w;
while (i < n) {
- w = image_words[i];
- put_c(w); put_c(w >> 8); put_c(w >> 16); put_c(w >> 24);
- put_c(w >> 32); put_c(w >> 40); put_c(w >> 48); put_c(w >> 56);
+ w = run.image_words[i];
+ rt.put_c(w); rt.put_c(w >> 8); rt.put_c(w >> 16); rt.put_c(w >> 24);
+ rt.put_c(w >> 32); rt.put_c(w >> 40); rt.put_c(w >> 48); rt.put_c(w >> 56);
i++;
}
return 0;
diff --git a/src/tools/embed.sh b/src/tools/embed.sh
new file mode 100755
index 0000000..59a377f
--- /dev/null
+++ b/src/tools/embed.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# embeds source text in the compiler, so the editor can show a built-in's definition and `include std;` needs
+# no file. a text becomes a function that returns it in pieces of at most 3000 bytes (the bootstrap
+# compiler's token buffer is 4 KB), one byte string per piece:
+# bstr NAME_part(int i) { if (i == 0) return b"..."; if (i == 1) return b"..."; ... return 0; }
+#
+# src/tools/embed.sh all embeds src/lib modules in the compiler
+# src/tools/embed.sh all --check exits 1 when an embedded module is out of date
+# src/tools/embed.sh lsp OUT writes OUT: rt_part, compiler_part and server_part, for the language server's image
+set -e
+case "${1:-}" in
+ --help|-h) printf '%s\n' 'usage: src/tools/embed.sh std|icon|all [--check]' ' src/tools/embed.sh lsp OUT' 'embeds library sources in the compiler, or writes the lsp source providers to OUT.' 'options:' ' --check report stale embedded library sources without changing files' ' --help, -h show this help and exit'; exit 0 ;;
+ -*) echo "embed: unknown option $1; use --help for usage" >&2; exit 2 ;;
+esac
+cd "$(dirname "$0")/../.."
+export LC_ALL=C
+# parts NAME FILE: the NAME_part function for FILE
+parts() {
+ echo "bstr $1_part(int i) {"
+ awk -v LIMIT=3000 '
+ function flush() { if (cnt > 0) { printf("\tif (i == %d) return b\"%s\";\n", n, buf); n++; buf = ""; cnt = 0 } }
+ {
+ line = $0; sub(/\r$/, "", line); line = line "\n"
+ len = length(line)
+ for (j = 1; j <= len; j++) {
+ if (cnt >= LIMIT) flush()
+ c = substr(line, j, 1)
+ if (c == "\\") e = "\\\\"; else if (c == "\"") e = "\\\""; else if (c == "\t") e = "\\t"; else if (c == "\n") e = "\\n"; else e = c
+ buf = buf e; cnt++
+ }
+ }
+ END { flush() }' "$2"
+ echo " return 0;"
+ echo "}"
+}
+embed() {
+ name=$1; file=$2; check=$3; C=src/compiler/compiler.zc; T=$(mktemp -d)
+ BEGIN_MARK="// ---- embedded $name library (generated by src/tools/embed.sh from $file; do not edit) ----"
+ END_MARK="// ---- end embedded $name library ----"
+ if grep -qF "$BEGIN_MARK" "$C"; then
+ awk -v b="$BEGIN_MARK" '$0 == b { exit } { print }' "$C" > "$T/head"
+ awk -v e="$END_MARK" 'found { print } $0 == e { found = 1; next }' "$C" > "$T/tail"
+ else
+ cp "$C" "$T/head"
+ : > "$T/tail"
+ fi
+ {
+ cat "$T/head"
+ echo "$BEGIN_MARK"
+ parts "$name" "$file"
+ echo "$END_MARK"
+ cat "$T/tail"
+ } > "$T/new"
+ if [ "$check" = "--check" ]; then
+ cmp -s "$T/new" "$C" && result=0 || result=1
+ rm -rf "$T"
+ return "$result"
+ fi
+ cp "$T/new" "$C"
+ rm -rf "$T"
+}
+case "$1" in
+std)
+ embed std src/lib/std.zc "$2"
+ ;;
+icon)
+ embed icon src/lib/icon.zc "$2"
+ ;;
+all)
+ if [ "$2" = "--check" ]; then
+ embed std src/lib/std.zc --check && embed icon src/lib/icon.zc --check
+ else
+ embed std src/lib/std.zc
+ embed icon src/lib/icon.zc
+ fi
+ ;;
+lsp)
+ [ -n "$2" ] || { echo "usage: src/tools/embed.sh lsp OUT" >&2; exit 2; }
+ {
+ echo "// the sources of this image, embedded (generated by src/tools/embed.sh; do not edit)"
+ parts rt src/runtime/rt.zc
+ parts compiler src/compiler/compiler.zc
+ parts server src/lsp/server.zc
+ parts core src/core/core.zc
+ } > "$2"
+ ;;
+*)
+ echo "usage: src/tools/embed.sh all [--check] | std | icon | lsp OUT" >&2; exit 2;;
+esac
diff --git a/src/tools/genmap.c b/src/tools/genmap.c
index dc1971c..8a258e1 100644
--- a/src/tools/genmap.c
+++ b/src/tools/genmap.c
@@ -10,7 +10,7 @@
./genmap --preset run -o build/run
./genmap --preset default --check
- ./genmap --mode compiler --heap-lo 8192 --heap 20480 --types 8192 --genome 245760 --frame 2048 --return 512 --mark 512 --data 3072 --snap-cap 1000 --quote-cap 300 -o build/small
+ ./genmap --mode compiler --heap-lo 8192 --heap 20480 --types 8192 --genome 393216 --frame 2048 --return 512 --mark 512 --data 3072 --snap-cap 1000 --quote-cap 300 -o build/small
./genmap --mode run --words 20480 -o build/run
--preset default puts the code area, the compiler tables, the genome and
@@ -194,7 +194,7 @@ static void preset_small(Spec *s)
s->compiler = 1;
s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000;
s->heap_lo = 8192; s->heap = 20480; s->types = 8192;
- s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072;
+ s->genome = 393216; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072;
tables_tight(s);
s->snap_cap = 1000; s->quote_cap = 300; s->snap = 1000; s->qbuf = 900; s->type_formula = 8192;
}
@@ -215,7 +215,7 @@ static void preset_default(Spec *s)
s->compiler = 1;
s->heap_top = 1; s->code_end = 0x80000; s->types = 65536;
s->heap = 0x60000; s->total = 0x8000000;
- s->genome = 0x50000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000;
+ s->genome = 0x60000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000;
s->data = 0x7FF0;
tables_big(s);
}
@@ -250,7 +250,7 @@ static void mode_defaults(Spec *s, int compiler)
if (compiler) {
s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000;
s->heap_lo = 8192; s->heap = 20480; s->types = 8192; s->type_formula = 8192;
- s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072;
+ s->genome = 393216; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072;
tables_tight(s);
} else {
s->heap_top = 1; s->code_end = 512; s->total = 0x8000000;
@@ -682,11 +682,12 @@ static void write_sources(const char *dir, const Spec *s, const Map *m)
static void usage_status(int status)
{
- fprintf(stderr,
+ fprintf(status ? stderr : stdout,
"usage: genmap --preset small|run|default|fixed|lsp [-o DIR] [--check]\n"
" genmap --mode compiler|run [-o DIR] [--words N] [options]\n"
" genmap small|run DIR\n"
"options:\n"
+ " --help, -h show this help and exit\n"
" --code N --total N --heap N --heap-lo N --types N --zfp-pad N --genome N\n"
" --frame N --return N --mark N --data N\n"
" --hash N --pool N --symbols N --locals N --fwd N\n"
@@ -714,7 +715,7 @@ static int take(int argc, char **argv, int *i)
int main(int argc, char **argv)
{
Spec s; Map m;
- if (argc == 2 && !strcmp(argv[1], "--help")) usage_status(0);
+ if (argc == 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) usage_status(0);
const char *outdir = 0, *preset = 0, *mode = 0;
int check = 0, grow = 0, i, have = 0;
int total = 0, set_total = 0, set_words = 0, set_heap = 0, set_heap_lo = 0, set_types = 0, set_zfp = 0;
diff --git a/src/tools/mkgenome.c b/src/tools/mkgenome.c
index bb2e787..3bf1533 100644
--- a/src/tools/mkgenome.c
+++ b/src/tools/mkgenome.c
@@ -2,13 +2,22 @@
src/tools/build.zc) into genome.h */
#include <stdio.h>
#include <stdint.h>
+#include <string.h>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include "memmap.h"
-int main(void)
+int main(int argc, char **argv)
{
+ if (argc == 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) {
+ fputs("usage: mkgenome [--help|-h] < image.bin > genome.h\n"
+ "converts little-endian 64-bit image words into a c genome header.\n"
+ "the image must match the memory layout selected when mkgenome is built.\n"
+ "options:\n --help, -h show this help and exit\n", stdout);
+ return 0;
+ }
+ if (argc != 1) { fputs("mkgenome: unexpected arguments; use --help for usage\n", stderr); return 2; }
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
diff --git a/src/tools/mklsp.sh b/src/tools/mklsp.sh
index 5edf14a..9bab550 100755
--- a/src/tools/mklsp.sh
+++ b/src/tools/mklsp.sh
@@ -4,11 +4,19 @@
# src/lsp/server.zc. its memory map is sized to that program by src/tools/automap.sh,
# so the genome slot is as big as it needs to be and no bigger.
set -e
+case "${1:-}" in
+ --help|-h) printf '%s\n' 'usage: src/tools/mklsp.sh [OUTDIR]' 'builds the native language server in OUTDIR/zclsp; defaults to build/lsp.' 'environment: CC selects the c compiler.'; exit 0 ;;
+ -*) echo "mklsp: unknown option $1; use --help for usage" >&2; exit 2 ;;
+esac
cd "$(dirname "$0")/../.."
O=${1:-build/lsp}; CC=${CC:-gcc}
# DOCS=1: the image carries the doc comments, so hover can show what a name is for
-DOCS=1 bash src/tools/automap.sh lsp "$O" src/lsp/server.zc >/dev/null
+mkdir -p "$O"
+bash src/tools/embed.sh all --check || bash src/tools/embed.sh all
+# the image carries its own sources (for go to definition into a built-in)
+bash src/tools/embed.sh lsp "$O/embed.zc"
+DOCS=1 bash src/tools/automap.sh lsp "$O" src/lsp/server.zc "$O/embed.zc" >/dev/null
rm -rf "$O/src"; mkdir -p "$O/src"
cp src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h "$O/memmap.h" "$O/src/"
cp "$O/genome.h" "$O/src/lsp_genome.h"
-$CC -O2 -w -fwrapv -DJTYPE=int -DZY_SANDBOX -DUSE_LSP_GENOME "$O/src/core.c" "$O/src/io_linux.c" -I"$O/src" -o "$O/zclsp"
+$CC -O2 -w -fwrapv -pthread -DJTYPE=int -DZY_SANDBOX -DUSE_LSP_GENOME "$O/src/core.c" "$O/src/io_linux.c" -I"$O/src" -o "$O/zclsp"
diff --git a/src/tools/mklspwasm.sh b/src/tools/mklspwasm.sh
index 56ba43b..86a902e 100755
--- a/src/tools/mklspwasm.sh
+++ b/src/tools/mklspwasm.sh
@@ -6,6 +6,10 @@
# needs clang with the wasm32 target and wasm-ld; run src/tools/mklsp.sh first.
# src/tools/mklspwasm.sh [outfile.wasm]
set -e
+case "${1:-}" in
+ --help|-h) printf '%s\n' 'usage: src/tools/mklspwasm.sh [OUTFILE.wasm]' 'builds the wasm language server; defaults to editors/vscode/server/lsp.wasm.' 'requires clang with wasm32 support and wasm-ld.' 'environment: LSP_BUILD_DIR selects the native lsp build; LSP_REUSE_BUILD=1 reuses its image.'; exit 0 ;;
+ -*) echo "mklspwasm: unknown option $1; use --help for usage" >&2; exit 2 ;;
+esac
cd "$(dirname "$0")/../.."
OUT=${1:-editors/vscode/server/lsp.wasm}
LSP_BUILD_DIR=${LSP_BUILD_DIR:-build/lsp}
@@ -14,7 +18,7 @@ if [ "${LSP_REUSE_BUILD:-0}" != 1 ]; then bash src/tools/mklsp.sh "$LSP_BUILD_DI
clang --target=wasm32 -O2 -w -fwrapv -nostdlib -fno-builtin -mbulk-memory -DJTYPE=int -DUSE_LSP_GENOME \
-I"$LSP_BUILD_DIR/src" -Isrc/micro src/micro/micro.c src/micro/port_wasm_stream.c \
-Wl,--no-entry -Wl,--export=wasm_chunk -Wl,--export=wasm_chunk_cap -Wl,--export=wasm_feed \
- -Wl,--export=wasm_close -Wl,--export=wasm_start -Wl,--export=wasm_resume \
+ -Wl,--export=wasm_polling -Wl,--export=wasm_close -Wl,--export=wasm_start -Wl,--export=wasm_resume \
-Wl,--export=memory -Wl,--allow-undefined -Wl,--initial-memory=41943040 -Wl,-z,stack-size=65536 \
-o "$OUT"
ls -l "$OUT"
diff --git a/src/tools/mkvsix.sh b/src/tools/mkvsix.sh
index 5ca8ef5..39fb9c4 100755
--- a/src/tools/mkvsix.sh
+++ b/src/tools/mkvsix.sh
@@ -6,6 +6,10 @@
# server build (src/tools/mklsp.sh) runs first if build/lsp is missing.
# src/tools/mkvsix.sh [outfile.vsix]
set -e
+case "${1:-}" in
+ --help|-h) printf '%s\n' 'usage: src/tools/mkvsix.sh [OUTFILE.vsix]' 'builds and packages the vscode extension; defaults to build/zc-language-support.vsix.' 'requires node, tsc and vsce; includes the wasm server when clang supports wasm32.' 'environment: VSCE selects the extension packager.'; exit 0 ;;
+ -*) echo "mkvsix: unknown option $1; use --help for usage" >&2; exit 2 ;;
+esac
cd "$(dirname "$0")/../.."
OUT=${1:-$PWD/build/zc-language-support.vsix}
case "$OUT" in /*) ;; *) OUT="$PWD/$OUT" ;; esac
diff --git a/src/tools/zc0.c b/src/tools/zc0.c
index 5ef5737..446557b 100644
--- a/src/tools/zc0.c
+++ b/src/tools/zc0.c
@@ -27,6 +27,7 @@ enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET,
O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT };
static const char *mn = "q@!$_~cjzr,?.+-*&|^><%";
+static char module_name[64];
static char *src; static int pos, line = 1, tstart, hdr_start;
/* the signature of the function being defined: its header's source text, for the language server (ZC0DOCS) */
static char sgtext[400], sgname[80];
@@ -78,6 +79,7 @@ static void doccomment(void) /* pos is on the first slash */
while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--;
bol = k < 0 || src[k] == '\n';
pos += 2;
+ if (src[pos] == ':') { dn = 0; dlast = cl; docpending = 0; while (src[pos] && src[pos] != '\n') pos++; return; }
if (!bol) { while (src[pos] && src[pos] != '\n') pos++; return; }
if (dn && dlast != cl - 1) dn = 0;
if (src[pos] == ' ') pos++;
@@ -176,6 +178,11 @@ static void next(void)
while (src[pos] && src[pos] != '"' && i < 4095) ts[i++] = (char)esc();
ts[i] = 0; if (src[pos] == '"') pos++; tk = T_BSTR; return;
}
+ if (src[pos] == '.' && (!strcmp(ts,"rt") || !strcmp(ts,"run") || !strcmp(ts,"core") || !strcmp(ts,"lsp") || !strcmp(ts,"std") || !strcmp(ts,"icon"))) {
+ ts[i++] = src[pos++];
+ while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++];
+ ts[i] = 0;
+ }
tk = T_ID; return;
}
if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; }
@@ -198,10 +205,24 @@ typedef struct { char name[64]; int kind, sig, defd; int64_t val; } Sym;
static Sym sy[16384]; static int nsy;
typedef struct { int at, sym; } Fix;
static Fix fx[65536]; static int nfx;
-static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; }
+static Sym *find_raw(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; }
+static void qualify(char *dst, size_t size, const char *name) {
+ if (!module_name[0] || strchr(name, '.')) snprintf(dst, size, "%s", name);
+ else if (!strncmp(name, "doc ", 4) || !strncmp(name, "sig ", 4)) snprintf(dst, size, "%.4s%s.%s", name, module_name, name + 4);
+ else snprintf(dst, size, "%s.%s", module_name, name);
+}
+static Sym *find(const char *name) {
+ char full[256]; Sym *s;
+ qualify(full, sizeof full, name); s = find_raw(full); if (s) return s;
+ s = find_raw(name); if (s || strchr(name, '.')) return s;
+ const char *modules[] = { "rt", "run", "core", "lsp", 0 };
+ for (int i = 0; modules[i]; i++) { snprintf(full,sizeof full,"%s.%s",modules[i],name); s=find_raw(full); if(s) return s; }
+ return 0;
+}
static Sym *add(const char *n, int kind, int64_t val)
{
- Sym *s = find(n);
+ char full[256]; qualify(full, sizeof full, n); n = full;
+ Sym *s = find_raw(n);
if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */
s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s;
}
@@ -318,11 +339,11 @@ static int64_t cexpr(void)
/* ---- types ----
zc0 has no types: a type is read and thrown away. all it has to know is the
- shape, so that [n]T still reserves n words and the rest reserve one. the
+ shape, so that T[n] still reserves n words and the rest reserve one. the
grammar is the compiler's own (see ptype in compiler.zc):
ptype a type, then any number of (params): int(int, int) is a function type
ptype0 a type with its stars: int*, vec<T>*, point**
- ptype1 a base: [n]T, []T, fixed []T, fn, a name, or a generic instance
+ ptype1 a base: T[n], T[?], fixed T[?], fn, a name, or a generic instance
each returns the words a variable of the type takes: n for a fixed array, 1
for a growable handle or anything scalar. */
static int ptype0(void);
@@ -330,12 +351,7 @@ static int ptype(void);
static int ptype1(void)
{
int n, e;
- if (accept("[")) {
- if (accept("]")) { ptype0(); return 1; } /* []T is a handle */
- n = (int)cexpr(); expect("]");
- e = ptype0();
- return n > 0 ? n * e : e;
- }
+ if (is("[")) { die("array types are postfix: T[N], T[] or T[?]"); return 1; }
if (is("fixed")) { next(); return ptype0(); }
if (is("fn")) { next(); return 1; } /* fn(int): the parameters follow, in ptype */
if (tk != T_ID) { die("a type name"); return 1; }
@@ -350,14 +366,30 @@ static int ptype1(void)
static int ptype0(void)
{
int n = ptype1();
- while (accept("*")) n = 1;
+ while (is("*") || is("[")) {
+ if (accept("*")) n = 1;
+ else {
+ int count = 0, cap = 8, *dims = malloc(cap * sizeof(int));
+ if (!dims) die("cannot allocate array dimensions");
+ while (accept("[")) {
+ int length;
+ if (accept("?")) { expect("]"); length = -2; }
+ else if (accept("]")) length = -1;
+ else { length = (int)cexpr(); expect("]"); }
+ if (count == cap) { cap *= 2; int *more = realloc(dims, cap * sizeof(int)); if (!more) die("cannot grow array dimensions"); dims = more; }
+ dims[count++] = length;
+ }
+ while (count) { int length = dims[--count]; if (length == -2) n = 1; else n = length < 0 ? 0 : n * length; }
+ free(dims);
+ }
+ }
return n;
}
static int ptype(void)
{
int n = ptype0();
while (accept("(")) {
- if (!accept(")")) { do { ptype(); } while (accept(",")); expect(")"); }
+ if (!accept(")")) { do { ptype(); if (accept(".")) ident(); } while (accept(",")); expect(")"); }
n = 1;
}
return n;
@@ -435,6 +467,7 @@ static void bstrlit(void)
}
static int returns_bstr(const char *n)
{
+ if (!strncmp(n, "rt.", 3)) n += 3;
return !strcmp(n, "cat_s") || !strcmp(n, "slice_s") || !strcmp(n, "n_to_s") || !strcmp(n, "new_s");
}
static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; }
@@ -457,6 +490,13 @@ static void primary(void)
if (s && s->kind == K_GARR) { lit(s->val); return; }
if (is("(")) {
int na = args();
+ if (s && s->kind == K_ENH) {
+ if (na != (s->sig & 15)) die("wrong number of enhancement inputs");
+ op(22 + (int)s->val);
+ if (((s->sig >> 4) & 15) == 0) lit(0);
+ else if (((s->sig >> 4) & 15) != 1) die("multiple enhancement outputs need asm");
+ xs = 0; xf = 0; return;
+ }
if (!strcmp(n, "put_s") && na == 1 && xs) fnref("put_bs");
else if (!strcmp(n, "len")) fnref("len_s");
else fnref(n);
@@ -671,7 +711,7 @@ static void fndef(int ret)
char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node;
if (dsn && !dnm[0]) strcpy(dnm, n);
char pn[16][64];
- s = find(n);
+ char full_name[256]; qualify(full_name, sizeof full_name, n); s = find_raw(full_name);
if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; }
expect("(");
if (!accept(")")) {
@@ -705,7 +745,7 @@ static void fndef(int ret)
nlo = 0; nslot = 0; nepi = 0; infn = 1;
for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].str = 0; lo[nlo++].slot = nslot++; }
/* retain the caller's pointer until the frame has its final segment. */
- helper = !find("frame_busy") || !strcmp(s->name, "frame_grow") || !strcmp(s->name, "frame_leave");
+ helper = !find("frame_busy") || !strcmp(s->name, "rt.frame_grow") || !strcmp(s->name, "rt.frame_leave");
lit(ZFP); op(O_LD); if (!helper) { lit(0); op(O_PICK); }
frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST);
if (!helper) {
@@ -756,6 +796,12 @@ static void enhdef(void)
}
static void top1(void)
{
+ if (accept("namespace")) {
+ if (is(";")) module_name[0] = 0;
+ else { snprintf(module_name, sizeof module_name, "%s", ident()); }
+ expect(";"); return;
+ }
+ if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; }
{
if (accept("unchecked")) { top1(); return; }
if (accept("fn")) { fndef(0); return; }
@@ -790,6 +836,21 @@ static void top1(void)
}
}
+/* section headings organize the source; they do not document a symbol. */
+static int doc_section(const char *text, int length)
+{
+ int start = 0, end, a, b;
+ while (start < length) {
+ end = start; while (end < length && text[end] != '\n') end++;
+ a = start; b = end;
+ while (a < b && isspace((unsigned char)text[a])) a++;
+ while (b > a && isspace((unsigned char)text[b - 1])) b--;
+ if (b - a >= 4 && text[a] == '-' && text[a + 1] == '-' && text[b - 1] == '-' && text[b - 2] == '-') return 1;
+ start = end + 1;
+ }
+ return 0;
+}
+
/* one item, with the doc comment above it kept under the name it declares */
static void top(void)
{
@@ -801,7 +862,7 @@ static void top(void)
if (dn && dline == line) { memcpy(dsnap, dbuf, dn); dsn = dn; dline = 0; }
top1();
#ifdef ZC0DOCS /* enabled for images that carry editor documentation */
- if (dsn && dnm[0]) {
+ if (dsn && dnm[0] && !doc_section(dsnap, dsn)) {
int j = here, a, i = 0, k, c; uint64_t w; char nm[80];
lit(0); op(O_JMP); a = here;
emit(dsn);
@@ -834,8 +895,16 @@ static void top(void)
}
}
-int main(void)
+int main(int argc, char **argv)
{
+ if (argc == 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) {
+ fputs("usage: zc0 [--help|-h] < source.zc > genome.h\n"
+ "compiles bootstrap source from stdin into a c genome header on stdout.\n"
+ "source must define start(); memory layout is selected when zc0 is built.\n"
+ "options:\n --help, -h show this help and exit\n", stdout);
+ return 0;
+ }
+ if (argc != 1) { fputs("zc0: unexpected arguments; use --help for usage\n", stderr); return 2; }
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
@@ -856,7 +925,7 @@ int main(void)
}
buf[n] = 0; src = buf; next();
/* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */
- lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0);
+ lit(FTOP); lit(ZFP); op(O_ST); fnref(strstr(src, "namespace run;") ? "run.start" : "start"); op(O_JMP); emit(0); emit(0);
top();
for (i = 0; i < nfx; i++) {
Sym *s = &sy[fx[i].sym];
diff --git a/src/tools/zcc b/src/tools/zcc
index 19904ea..3fb8f0c 100755
--- a/src/tools/zcc
+++ b/src/tools/zcc
@@ -22,7 +22,7 @@ while [ $# -gt 0 ]; do
case "$1" in
-o) [ $# -ge 2 ] || { echo "zcc: -o needs a name" >&2; exit 2; }; out="$2"; shift 2 ;;
-k) keep=1; shift ;;
- -h|--help) sed -n '2,15p' "$0"; exit 0 ;;
+ -h|--help) printf '%s\n' 'usage: src/tools/zcc [-o OUT] [-k] FILE.zc' 'compiles a source file into a standalone binary; links statically where supported.' 'options:' ' -o OUT output binary path; defaults to the source basename without .zc' ' -k keep the temporary build directory' ' --help, -h show this help and exit' 'environment: CC selects the c compiler; CFLAGS adds compiler flags.'; exit 0 ;;
-*) echo "zcc: unknown option $1" >&2; exit 2 ;;
*) break ;;
esac
@@ -49,6 +49,6 @@ if ! printf 'int main(void){return 0;}\n' | $CC -x c -static -o "$work/probe" -
static=""
echo "zcc: this toolchain cannot link -static; the binary is dynamic" >&2
fi
-$CC -O2 -w -fwrapv ${CFLAGS:-} $static "$work/run/micro.c" "$work/run/port_linux.c" \
+$CC -O2 -w -fwrapv -pthread -DZY_THREADS ${CFLAGS:-} $static "$work/run/micro.c" "$work/run/port_linux.c" \
-I"$work/run" -o "$out"
echo "zcc: wrote $out ($(wc -c < "$out" | tr -d ' ') bytes)"