summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpeter <peter@pmikkelsen.com>2026-08-31 20:22:47 +0000
committerpeter <peter@pmikkelsen.com>2026-08-31 20:22:47 +0000
commit8b0beb9e6a57c1d4f1e7b0b06523dd0fc538771a (patch)
tree9bb8b689020d5098ea3c31251ddcd2fbb9524a7c
parentd361abaeb8dcf19446a9322737ea600b22568a5b (diff)
parentf9f9b10c1d17077632d7a09e447e629b8d33b23d (diff)
Merge memory manager changes into trunk
FossilOrigin-Name: af583e3ba70b2d10efdead8e03a42fe5c1b94d6b387522833606c138b527a653
-rw-r--r--CMakeLists.txt38
-rw-r--r--src/bin/main.c59
-rw-r--r--src/include/aplwc.h33
-rw-r--r--src/lib/aplwc_internal.h66
-rw-r--r--src/lib/eval.c16
-rw-r--r--src/lib/main.c190
-rw-r--r--src/lib/memory.c359
-rw-r--r--src/lib/module.c20
-rw-r--r--src/lib/string.c5
-rw-r--r--src/lib/syntax.c26
-rw-r--r--tests/aplwc_errstr.c58
-rw-r--r--tests/aplwc_eval.c33
-rw-r--r--tests/aplwc_new.c26
-rw-r--r--tests/aplwc_read.c28
-rw-r--r--tests/aplwc_utf8.c31
-rwxr-xr-xtests/scripts/bad/1.aplwc1
-rwxr-xr-xtests/scripts/bad/2.aplwc1
-rwxr-xr-xtests/scripts/bad/3.aplwc1
-rwxr-xr-xtests/scripts/good/1.aplwc (renamed from tests/scripts/1.aplwc)0
-rwxr-xr-xtests/scripts/good/2.aplwc (renamed from tests/scripts/2.aplwc)0
-rwxr-xr-xtools/checkcov5
21 files changed, 684 insertions, 312 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d3450d7..9da17b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,43 +100,31 @@ foreach(source ${TEST_SOURCES})
endif()
endforeach()
-file(GLOB TEST_SCRIPTS CONFIGURE_DEPENDS "tests/scripts/*.aplwc")
-foreach(script ${TEST_SCRIPTS})
+file(GLOB TEST_SCRIPTS_GOOD CONFIGURE_DEPENDS "tests/scripts/good/*.aplwc")
+foreach(script ${TEST_SCRIPTS_GOOD})
add_test(NAME "${script}" COMMAND "${script}")
set_tests_properties("${script}" PROPERTIES
- ENVIRONMENT "APLWC_MEM=64000;PATH=$<TARGET_FILE_DIR:aplwc-repl>"
+ ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>"
)
add_test(NAME "aplwc-repl ${script}" COMMAND "aplwc-repl" "${script}")
set_tests_properties("aplwc-repl ${script}" PROPERTIES
- ENVIRONMENT "APLWC_MEM=64000;PATH=$<TARGET_FILE_DIR:aplwc-repl>"
+ ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>"
)
endforeach()
-add_test(NAME "aplwc-repl-tiny" COMMAND "aplwc-repl" "-x")
-set_tests_properties("aplwc-repl-tiny" PROPERTIES
- ENVIRONMENT "APLWC_MEM=16"
- WILL_FAIL TRUE
-)
-
-add_test(NAME "aplwc-repl-large" COMMAND "aplwc-repl" "-x")
-set_tests_properties("aplwc-repl-large" PROPERTIES
- ENVIRONMENT "APLWC_MEM=64000"
-)
-
-add_test(NAME "aplwc-repl-emptyvar" COMMAND "aplwc-repl" "-x")
-set_tests_properties("aplwc-repl-emptyvar" PROPERTIES
- ENVIRONMENT "APLWC_MEM="
-)
-
-add_test(NAME "aplwc-repl-badvar" COMMAND "aplwc-repl" "-x")
-set_tests_properties("aplwc-repl-badvar" PROPERTIES
- ENVIRONMENT "APLWC_MEM=1M"
-)
+file(GLOB TEST_SCRIPTS_BAD CONFIGURE_DEPENDS "tests/scripts/bad/*.aplwc")
+foreach(script ${TEST_SCRIPTS_BAD})
+ add_test(NAME "aplwc-repl ${script}" COMMAND "aplwc-repl" "${script}")
+ set_tests_properties("aplwc-repl ${script}" PROPERTIES
+ ENVIRONMENT "PATH=$<TARGET_FILE_DIR:aplwc-repl>"
+ WILL_FAIL TRUE
+ )
+endforeach()
add_test(NAME "aplwc-repl" COMMAND "aplwc-repl" "-x")
-set(REQUIRED_MALLOCS 1)
+set(REQUIRED_MALLOCS 3)
foreach(i RANGE 0 ${REQUIRED_MALLOCS})
set(name "aplwc-repl-${i}-${REQUIRED_MALLOCS}")
add_test(NAME "${name}" COMMAND "aplwc-repl" "-x")
diff --git a/src/bin/main.c b/src/bin/main.c
index 572d170..c3743f2 100644
--- a/src/bin/main.c
+++ b/src/bin/main.c
@@ -4,28 +4,48 @@
#include <errno.h>
#include "aplwc_repl.h"
+static void *
+my_alloc(size_t min, size_t max, size_t *actual)
+{
+ UNUSED_PARAMETER(max);
+
+ *actual = min;
+ return malloc(min);
+}
+
+static void
+my_dealloc(void *p)
+{
+ free(p);
+}
+
int
main(int argc, char *argv[])
{
+ struct aplwc_fns fns;
struct aplwc *aplwc;
- struct aplwc_line *line;
- struct aplwc_value *value;
- uint8_t *data;
+ enum aplwc_rc ret;
+ AplwcLine line;
+ AplwcValue value;
char *input, *src;
- int ret, exit_code, line_number;
- size_t memsize, input_size, src_size, i;
+ int exit_code, line_number;
+ size_t input_size, src_size, i;
int opt, xflag, input_file;
long tmp;
FILE *f;
+ aplwc_clear_line(&line);
+ aplwc_clear_value(&value);
input = NULL;
- line = NULL;
- value = NULL;
xflag = 0;
input_file = 0;
exit_code = 0;
line_number = 0;
+ memset(&fns, 0, sizeof(fns));
+ fns.alloc = my_alloc;
+ fns.dealloc = my_dealloc;
+
while((opt = getopt(argc, argv, "x")) != -1){
switch(opt){
case 'x':
@@ -50,18 +70,9 @@ usage:
else
goto usage;
- memsize = getenv_ulong("APLWC_MEM", APLWC_MIN_MEM);
-
- data = malloc(memsize);
- if(data == NULL){
- fprintf(stderr, "Couldn't allocate memory\n");
- return 1;
- }
-
- ret = aplwc_new(&aplwc, data, memsize);
+ aplwc = aplwc_new(fns, &ret);
if(ret){
fprintf(stderr, "Couldn't create new aplwc instance: %s\n", aplwc_errstr_rc(ret));
- free(data);
return 1;
}
@@ -115,19 +126,20 @@ file_size:
for(;;){
if(input)
free(input);
- if(line)
+ if(aplwc_valid_line(line))
aplwc_free_line(line);
- if(value)
+ if(aplwc_valid_value(value))
aplwc_free_value(value);
input = NULL;
if(getline(&input, &input_size, stdin) < 0){
free(input);
+ input = NULL;
break;
}else{
line_number++;
line = aplwc_read(aplwc, input);
- if(line == NULL){
+ if(!aplwc_valid_line(line)){
line_error:
fprintf(stderr, "error on line %d: %s: %s\n", line_number, aplwc_errstr(aplwc), input);
if(input_file){
@@ -137,7 +149,7 @@ line_error:
continue;
}
value = aplwc_eval(line);
- if(value == NULL)
+ if(!aplwc_valid_value(value))
goto line_error;
fprintf(stdout, "%s\n", aplwc_value_string(value));
}
@@ -146,12 +158,11 @@ line_error:
exit:
if(input)
free(input);
- if(line)
+ if(aplwc_valid_line(line))
aplwc_free_line(line);
- if(value)
+ if(aplwc_valid_value(value))
aplwc_free_value(value);
aplwc_destroy(aplwc);
- free(data);
return exit_code;
}
diff --git a/src/include/aplwc.h b/src/include/aplwc.h
index 3061500..6ba2e35 100644
--- a/src/include/aplwc.h
+++ b/src/include/aplwc.h
@@ -15,21 +15,34 @@ enum aplwc_rc {
};
struct aplwc;
-struct aplwc_line;
-struct aplwc_value;
-enum aplwc_rc aplwc_new(struct aplwc **, uint8_t *, size_t);
+struct aplwc_ref {
+ struct aplwc *aplwc;
+ int id;
+};
+
+typedef struct { struct aplwc_ref ref; } AplwcLine;
+typedef struct { struct aplwc_ref ref; } AplwcValue;
+
+struct aplwc_fns {
+ void *(*alloc)(size_t, size_t, size_t *);
+ void (*dealloc)(void *);
+};
+
+struct aplwc *aplwc_new(struct aplwc_fns, enum aplwc_rc *);
void aplwc_destroy(struct aplwc *);
+void aplwc_clear_line(AplwcLine *);
+void aplwc_clear_value(AplwcValue *);
+int aplwc_valid_line(AplwcLine);
+int aplwc_valid_value(AplwcValue);
const char *aplwc_errstr_rc(enum aplwc_rc);
const char *aplwc_errstr(struct aplwc *);
enum aplwc_rc aplwc_last_error(struct aplwc *);
-struct aplwc_line *aplwc_read(struct aplwc *, const char *);
-void aplwc_free_line(struct aplwc_line *);
-struct aplwc_value *aplwc_eval(struct aplwc_line *);
-void aplwc_free_value(struct aplwc_value *);
-const char *aplwc_value_string(struct aplwc_value *);
+AplwcLine aplwc_read(struct aplwc *, const char *);
+void aplwc_free_line(AplwcLine);
+AplwcValue aplwc_eval(AplwcLine);
+void aplwc_free_value(AplwcValue);
+const char *aplwc_value_string(AplwcValue);
enum aplwc_rc aplwc_load_module(struct aplwc *, const char *, const char *, int n);
-#define APLWC_MIN_MEM (64000)
-
#endif /* APLWC_H */
diff --git a/src/lib/aplwc_internal.h b/src/lib/aplwc_internal.h
index e394bd8..b87cfad 100644
--- a/src/lib/aplwc_internal.h
+++ b/src/lib/aplwc_internal.h
@@ -8,20 +8,32 @@
#include <stdbool.h>
#include <string.h>
-struct memory {
+struct aplwc {
+ struct aplwc_fns fns;
+ enum aplwc_rc last_error;
+ struct aplwc_module *modules;
+
+ /* Memory management */
uint8_t *data;
+ uint8_t *unaligned_data;
size_t size;
- size_t offset;
+ struct memory_ptr_frame *memptrs;
+ struct root_ptr *rootptrs;
};
-struct aplwc {
- enum aplwc_rc last_error;
- struct memory memory;
- struct aplwc_module *modules;
+struct memory_ptr_frame {
+ struct memory_ptr_frame *link;
+ void ***ptrs;
+ size_t count;
+};
+
+struct root_ptr {
+ int id;
+ void *ptr;
+ struct root_ptr *link;
};
struct aplwc_line {
- struct aplwc *aplwc;
struct string *text;
struct token_list *tokens;
};
@@ -66,35 +78,41 @@ enum value_tag {
};
struct aplwc_value {
- struct aplwc *aplwc;
enum value_tag tag;
};
struct aplwc_module {
- struct aplwc *aplwc;
struct string *name;
struct token_list *tokens;
struct aplwc_module *link;
};
/* eval.c */
-struct aplwc_value *eval_line(struct aplwc_line *);
+struct aplwc_value *eval_line(struct aplwc *, struct aplwc_line *);
/* memory.c */
-struct memory memory_init(uint8_t *, size_t);
-struct aplwc *memory_alloc_aplwc(struct memory *);
-struct aplwc_line *memory_alloc_aplwc_line(struct memory *);
-struct string *memory_alloc_string(struct memory *, size_t);
-struct token_list *memory_alloc_token_list(struct memory *, size_t);
-struct aplwc_value *memory_alloc_aplwc_value(struct memory *);
-struct aplwc_module *memory_alloc_aplwc_module(struct memory *);
-struct module_imports *memory_alloc_module_imports(struct memory *, size_t);
-void memory_free_aplwc(struct memory *, struct aplwc *);
-void memory_free_aplwc_line(struct memory *, struct aplwc_line *);
-void memory_free_string(struct memory *, struct string *);
-void memory_free_token_list(struct memory *, struct token_list *);
-void memory_free_aplwc_value(struct memory *, struct aplwc_value *);
-void memory_free_aplwc_module(struct memory *, struct aplwc_module *);
+void memory_init(struct aplwc *);
+void memory_destroy(struct aplwc *);
+struct aplwc_line *memory_alloc_aplwc_line(struct aplwc *);
+struct string *memory_alloc_string(struct aplwc *, size_t);
+struct token_list *memory_alloc_token_list(struct aplwc *, size_t);
+struct aplwc_value *memory_alloc_aplwc_value(struct aplwc *);
+struct aplwc_module *memory_alloc_aplwc_module(struct aplwc *);
+struct module_imports *memory_alloc_module_imports(struct aplwc *, size_t);
+struct aplwc_ref memory_root_new(struct aplwc *, void *);
+void *memory_root_get(struct aplwc_ref);
+void memory_root_free(struct aplwc_ref);
+void memptrs_add(struct aplwc *, struct memory_ptr_frame *, void ***, size_t);
+void memptrs_remove(struct aplwc *, struct memory_ptr_frame *);
+void clear_ref(struct aplwc_ref *);
+int valid_ref(struct aplwc_ref);
+
+#define MEMPTRS(aplwc, n) \
+ void **memptrs[n]; \
+ struct memory_ptr_frame memptrs_frame; \
+ memptrs_add(aplwc, &memptrs_frame, memptrs, n);
+#define MEMPTR(n, ptr) memptrs[n] = (void **)&ptr
+#define MEMPTRS_EXIT(aplwc) memptrs_remove(aplwc, &memptrs_frame)
/* string.c */
struct string *chars_to_string(struct aplwc *, const char *, int len);
diff --git a/src/lib/eval.c b/src/lib/eval.c
index f0cdc55..0da90db 100644
--- a/src/lib/eval.c
+++ b/src/lib/eval.c
@@ -1,19 +1,17 @@
#include "aplwc_internal.h"
struct aplwc_value *
-eval_line(struct aplwc_line *line)
+eval_line(struct aplwc *aplwc, struct aplwc_line *line)
{
struct aplwc_value *value;
- struct aplwc *aplwc;
- aplwc = line->aplwc;
+ UNUSED_PARAMETER(line);
+
assert(aplwc->last_error == APLWC_OK);
- value = memory_alloc_aplwc_value(&aplwc->memory);
- if(value == NULL)
- aplwc->last_error = APLWC_OUT_OF_MEMORY;
- value->aplwc = aplwc;
- value->tag = VALUE_NULL;
-
+ value = memory_alloc_aplwc_value(aplwc);
+ if(value != NULL)
+ value->tag = VALUE_NULL;
+
return value;
}
diff --git a/src/lib/main.c b/src/lib/main.c
index d6de538..4571c78 100644
--- a/src/lib/main.c
+++ b/src/lib/main.c
@@ -1,26 +1,45 @@
#include "aplwc_internal.h"
#include "boot.h"
-enum aplwc_rc
-aplwc_new(struct aplwc **p, uint8_t *data, size_t size)
+struct aplwc *
+aplwc_new(struct aplwc_fns fns, enum aplwc_rc *rcp)
{
- struct memory m;
+ enum aplwc_rc rc;
+ struct aplwc *aplwc;
+ size_t size;
- m = memory_init(data, size);
+ aplwc = NULL;
+ if(fns.alloc == NULL){
+ rc = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
+ aplwc = fns.alloc(sizeof(*aplwc), sizeof(*aplwc), &size);
+ if(aplwc == NULL){
+ rc = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
- *p = memory_alloc_aplwc(&m);
- if(*p == NULL)
- return APLWC_OUT_OF_MEMORY;
- (*p)->memory = m;
+ aplwc->fns = fns;
+ memory_init(aplwc);
- return aplwc_load_module(*p, "Boot", (const char *)boot_src, boot_src_len);
+ rc = aplwc_load_module(aplwc, "Boot", (const char *)boot_src, boot_src_len);
+end:
+ if(rc != APLWC_OK)
+ aplwc_destroy(aplwc);
+ assert(rcp);
+ *rcp = rc;
+ return aplwc;
}
void
aplwc_destroy(struct aplwc *p)
{
- if(p)
- memory_free_aplwc(&p->memory, p);
+ if(p == NULL)
+ return;
+
+ memory_destroy(p);
+ if(p->fns.dealloc)
+ p->fns.dealloc(p);
}
const char *
@@ -64,67 +83,80 @@ aplwc_last_error(struct aplwc *aplwc)
return aplwc->last_error;
}
-struct aplwc_line *
-aplwc_read(struct aplwc *aplwc, const char *line)
+AplwcLine
+aplwc_read(struct aplwc *aplwc, const char *linestr)
{
struct string *text;
struct token_list *tokens;
- struct aplwc_line *ret;
+ struct aplwc_line *line;
+ AplwcLine ret;
- text = chars_to_string(aplwc, line, -1);
- if(text == NULL)
- return NULL;
+ MEMPTRS(aplwc, 2);
- tokens = scan_string(aplwc, text, 1);
- if(tokens == NULL)
- return NULL;
+ aplwc_clear_line(&ret);
- aplwc->last_error = check_tokens(tokens);
- if(aplwc->last_error != APLWC_OK)
- return NULL;
+ if((text = chars_to_string(aplwc, linestr, -1)) == NULL)
+ goto end;
+ MEMPTR(0, text);
- ret = memory_alloc_aplwc_line(&aplwc->memory);
- if(ret == NULL){
- aplwc->last_error = APLWC_OUT_OF_MEMORY;
- return NULL;
- }
+ if((tokens = scan_string(aplwc, text, 1)) == NULL)
+ goto end;
+ MEMPTR(1, tokens);
+
+ if((aplwc->last_error = check_tokens(tokens)) != APLWC_OK)
+ goto end;
+
+ if((line = memory_alloc_aplwc_line(aplwc)) == NULL)
+ goto end;
- ret->aplwc = aplwc;
- ret->text = text;
- ret->tokens = tokens;
+ line->text = text;
+ line->tokens = tokens;
+ ret.ref = memory_root_new(aplwc, line);
+end:
+ MEMPTRS_EXIT(aplwc);
return ret;
}
void
-aplwc_free_line(struct aplwc_line *line)
+aplwc_free_line(AplwcLine line)
{
- if(line == NULL)
- return;
- memory_free_aplwc_line(&line->aplwc->memory, line);
+ memory_root_free(line.ref);
}
-struct aplwc_value *
-aplwc_eval(struct aplwc_line *line)
+AplwcValue
+aplwc_eval(AplwcLine line)
{
- line->aplwc->last_error = APLWC_OK;
- return eval_line(line);
+ AplwcValue value;
+ struct aplwc_line *l;
+ struct aplwc_value *v;
+ struct aplwc *aplwc;
+
+ aplwc = line.ref.aplwc;
+
+ aplwc_clear_value(&value);
+ l = memory_root_get(line.ref);
+ aplwc->last_error = APLWC_OK;
+ v = eval_line(aplwc, l);
+ if(v)
+ value.ref = memory_root_new(aplwc, v);
+ return value;
}
void
-aplwc_free_value(struct aplwc_value *value)
+aplwc_free_value(AplwcValue value)
{
- if(value == NULL)
- return;
- memory_free_aplwc_value(&value->aplwc->memory, value);
+ memory_root_free(value.ref);
}
const char *
-aplwc_value_string(struct aplwc_value *value)
+aplwc_value_string(AplwcValue value)
{
- assert(value != NULL);
+ struct aplwc_value *v;
+ assert(aplwc_valid_value(value));
- switch(value->tag){
+ v = memory_root_get(value.ref);
+ switch(v->tag){
default:
return "???";
}
@@ -136,32 +168,46 @@ aplwc_load_module(struct aplwc *aplwc, const char *name, const char *src, int sr
struct string *text, *namestr;
struct token_list *tokens;
- namestr = NULL;
- text = NULL;
- tokens = NULL;
+ MEMPTRS(aplwc, 3);
- namestr = chars_to_string(aplwc, name, -1);
- if(namestr == NULL)
- goto error;
+ if((namestr = chars_to_string(aplwc, name, -1)) == NULL)
+ goto end;
+ MEMPTR(0, namestr);
- text = chars_to_string(aplwc, src, srclen);
- if(text == NULL)
- goto error;
-
- tokens = scan_string(aplwc, text, 0);
- if(tokens == NULL)
- goto error;
-
- if(load_module(aplwc, namestr, tokens) == APLWC_OK)
- return APLWC_OK;
- else{
-error:
- if(tokens != NULL)
- memory_free_token_list(&aplwc->memory, tokens);
- if(text != NULL)
- memory_free_string(&aplwc->memory, text);
- if(namestr != NULL)
- memory_free_string(&aplwc->memory, namestr);
- return aplwc_last_error(aplwc);
- }
+ if((text = chars_to_string(aplwc, src, srclen)) == NULL)
+ goto end;
+ MEMPTR(1, text);
+
+ if((tokens = scan_string(aplwc, text, 0)) == NULL)
+ goto end;
+ MEMPTR(2, tokens);
+
+ aplwc->last_error = load_module(aplwc, namestr, tokens);
+end:
+ MEMPTRS_EXIT(aplwc);
+ return aplwc_last_error(aplwc);
+}
+
+void
+aplwc_clear_value(AplwcValue *v)
+{
+ clear_ref(&v->ref);
+}
+
+void
+aplwc_clear_line(AplwcLine *l)
+{
+ clear_ref(&l->ref);
+}
+
+int
+aplwc_valid_value(AplwcValue v)
+{
+ return valid_ref(v.ref);
+}
+
+int
+aplwc_valid_line(AplwcLine l)
+{
+ return valid_ref(l.ref);
}
diff --git a/src/lib/memory.c b/src/lib/memory.c
index a348b93..694a31b 100644
--- a/src/lib/memory.c
+++ b/src/lib/memory.c
@@ -1,136 +1,353 @@
#include "aplwc_internal.h"
-struct memory
-memory_init(uint8_t *data , size_t size)
+#define ALIGNMENT 8
+#define HEADER_SIZE (sizeof(struct chunk) + (sizeof(struct chunk) % ALIGNMENT))
+
+enum chunk_tag {
+ CHUNK_TAG_FREE,
+ CHUNK_TAG_APLWC_LINE,
+ CHUNK_TAG_STRING,
+ CHUNK_TAG_TOKEN_LIST,
+ CHUNK_TAG_APLWC_VALUE,
+ CHUNK_TAG_APLWC_MODULE
+};
+
+struct chunk {
+ struct chunk *next;
+ size_t size;
+ enum chunk_tag tag;
+ uint8_t *data;
+};
+
+void
+memory_init(struct aplwc *aplwc)
{
- struct memory mem;
+ aplwc->data = NULL;
+ aplwc->unaligned_data = NULL;
+ aplwc->size = 0;
+ aplwc->memptrs = NULL;
+ aplwc->rootptrs = NULL;
+}
- mem.data = data;
- mem.size = size;
- mem.offset = 0;
- return mem;
+void
+memory_destroy(struct aplwc *aplwc)
+{
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(aplwc->unaligned_data);
}
-static void *
-alloc(struct memory *m, size_t size, size_t align)
+static void
+fixrefs(struct aplwc *aplwc, uint8_t *new, uint8_t *old)
{
- uintptr_t ptr;
- size_t alloc_size, adjust;
- void *p;
+#define FIX(ptr) do{ \
+ if(ptr){ \
+ (ptr) = (void*)(((uint8_t*)(ptr))+add); \
+ (ptr) = (void*)(((uint8_t*)(ptr))-sub); \
+ } \
+}while(0)
- ptr = ((uintptr_t)m->data) + m->offset;
- alloc_size = size;
+#define FIX_DATA(type, ptr) FIX(((type *)c->data)->ptr)
- if(NEVER(align == 0))
- return NULL;
- adjust = ptr % align;
+ struct chunk *c;
+ struct memory_ptr_frame *frame;
+ struct root_ptr *root;
+ size_t add, sub, i;
+
+ if(new > old){
+ add = new-old;
+ sub = 0;
+ }else{
+ add = 0;
+ sub = old-new;
+ }
+ for(c = (struct chunk *)new; c; c = c->next){
+ FIX(c->next);
+ FIX(c->data);
+
+ switch(c->tag){
+ case CHUNK_TAG_APLWC_LINE:
+ FIX_DATA(struct aplwc_line, text);
+ FIX_DATA(struct aplwc_line, tokens);
+ break;
+ case CHUNK_TAG_STRING:
+ break;
+ case CHUNK_TAG_TOKEN_LIST:
+ FIX_DATA(struct token_list, text);
+ break;
+ case CHUNK_TAG_APLWC_VALUE:
+ break;
+ case CHUNK_TAG_APLWC_MODULE:
+ FIX_DATA(struct aplwc_module, name);
+ FIX_DATA(struct aplwc_module, tokens);
+ FIX_DATA(struct aplwc_module, link);
+ break;
+ case CHUNK_TAG_FREE:
+ break;
+ }
+ }
+
+ for(frame = aplwc->memptrs; frame; frame = frame->link)
+ for(i = 0; i < frame->count; i++)
+ if(frame->ptrs[i])
+ FIX(*frame->ptrs[i]);
+
+ for(root = aplwc->rootptrs; root; root = root->link)
+ if(root->ptr)
+ FIX(root->ptr);
+
+#undef FIX
+#undef FIX_DATA
+}
+
+static enum aplwc_rc
+grow(struct aplwc *aplwc, size_t extra)
+{
+ uint8_t *unaligned_data, *aligned_data;
+ size_t unaligned_newsize, aligned_newsize, adjust;
+ struct chunk **free, *head;
+
+ if(aplwc->fns.alloc == NULL)
+ goto error;
+
+ unaligned_data = aplwc->fns.alloc(aplwc->size + extra + HEADER_SIZE + 2*ALIGNMENT, 0, &unaligned_newsize);
+ if(unaligned_data == NULL)
+ goto error;
+
+ adjust = ((uintptr_t)unaligned_data) % ALIGNMENT;
if(adjust)
- alloc_size += align - adjust;
+ adjust = ALIGNMENT - adjust;
+ aligned_newsize = unaligned_newsize - adjust;
+ aligned_data = unaligned_data + adjust;
+
+ aligned_newsize -= aligned_newsize % ALIGNMENT;
- if((m->size - m->offset) >= alloc_size){
- p = m->data+m->offset+alloc_size-size;
- assert((((uintptr_t)p) % align) == 0);
- m->offset += alloc_size;
- memset(p, 0, size);
+ assert(aligned_newsize >= (aplwc->size + extra));
+ head = (struct chunk *)aligned_data;
+
+ if(aplwc->data){
+ memmove(aligned_data, aplwc->data, aplwc->size);
+ fixrefs(aplwc, aligned_data, aplwc->data);
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(aplwc->unaligned_data);
+ for(free = &head; (*free); free = &(*free)->next)
+ assert((*free)->tag != CHUNK_TAG_FREE);
}else
- p = NULL;
+ free = &head;
- return p;
+
+ (*free) = (struct chunk *)(aligned_data + aplwc->size);
+ (*free)->tag = CHUNK_TAG_FREE;
+ (*free)->data = NULL;
+ (*free)->size = aligned_newsize - aplwc->size;
+ (*free)->next = NULL;
+
+ aplwc->unaligned_data = unaligned_data;
+ aplwc->data = aligned_data;
+ aplwc->size = aligned_newsize;
+ return 1;
+
+error:
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ return 0;
}
-struct aplwc *
-memory_alloc_aplwc(struct memory *m)
+static struct chunk *
+find_free(struct aplwc *aplwc, size_t min)
{
- struct aplwc *p;
+ struct chunk *c;
- p = alloc(m, sizeof(struct aplwc), alignof(struct aplwc));
- return p;
+ min += HEADER_SIZE;
+
+ if(aplwc->data == NULL)
+ return NULL;
+
+ for(c = (struct chunk *)aplwc->data; c; c = c->next){
+ if((c->tag == CHUNK_TAG_FREE) && (c->size >= min))
+ break;
+ }
+
+ return c;
}
-void
-memory_free_aplwc(struct memory *m, struct aplwc *p)
+static void *
+alloc(struct aplwc *aplwc, size_t size, enum chunk_tag tag)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct chunk *free;
+ struct chunk *new;
+
+retry:
+ free = find_free(aplwc, size);
+ if(!free){
+ if(grow(aplwc, size))
+ goto retry;
+ else
+ return NULL;
+ }
+
+ assert(free->data == NULL);
+
+ new = free;
+
+ if(free->size > (size + 2*HEADER_SIZE)){
+ free = (struct chunk *)(((uint8_t*)free) + HEADER_SIZE + size);
+ free->tag = CHUNK_TAG_FREE;
+ free->size = new->size - (HEADER_SIZE + size);
+ free->data = NULL;
+ }
+
+ new->tag = tag;
+ new->size = sizeof(struct chunk) + size;
+ new->data = ((uint8_t*)new) + sizeof(struct chunk);
+
+ new->data += ((uintptr_t)new->data) % ALIGNMENT;
+
+ return new->data;
}
-struct aplwc_line *
-memory_alloc_aplwc_line(struct memory *m)
+struct aplwc_ref
+memory_root_new(struct aplwc *aplwc, void *p)
{
- struct aplwc_line *p;
+ struct aplwc_ref ref;
+ struct root_ptr *root;
+ size_t size;
- p = alloc(m, sizeof(struct aplwc_line), alignof(struct aplwc_line));
- return p;
+ clear_ref(&ref);
+ if(aplwc->fns.alloc == NULL){
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ root = aplwc->fns.alloc(sizeof(*root), sizeof(*root), &size);
+ if(root == NULL){
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ root->id = aplwc->rootptrs ? (aplwc->rootptrs->id+1) : 0;
+ root->ptr = p;
+ root->link = aplwc->rootptrs;
+ aplwc->rootptrs = root;
+
+ ref.id = root->id;
+ ref.aplwc = aplwc;
+end:
+ return ref;
+}
+
+void *
+memory_root_get(struct aplwc_ref ref)
+{
+ struct root_ptr *root;
+
+ if(!valid_ref(ref))
+ return NULL;
+
+ for(root = ref.aplwc->rootptrs; root; root = root->link)
+ if(root->id == ref.id)
+ return root->ptr;
+
+ return NULL;
}
void
-memory_free_aplwc_line(struct memory *m, struct aplwc_line *p)
+memory_root_free(struct aplwc_ref ref)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct aplwc *aplwc;
+ struct root_ptr **root, *tmp;
+
+ if(!valid_ref(ref))
+ return;
+
+ aplwc = ref.aplwc;
+
+ for(root = &aplwc->rootptrs; (*root); root = &(*root)->link){
+ if((*root)->id == ref.id){
+ tmp = *root;
+ *root = tmp->link;
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(tmp);
+ break;
+ }
+ }
}
-struct string *
-memory_alloc_string(struct memory *m, size_t bytes)
+void
+memptrs_add(struct aplwc *aplwc, struct memory_ptr_frame *frame, void ***ptrs, size_t count)
{
- struct string *p;
+ size_t i;
- p = alloc(m, sizeof(struct string) + bytes, alignof(struct string));
- return p;
+ frame->ptrs = ptrs;
+ frame->link = aplwc->memptrs;
+ frame->count = count;
+ aplwc->memptrs = frame;
+
+ for(i = 0; i < count; i++)
+ frame->ptrs[i] = NULL;
}
void
-memory_free_string(struct memory *m, struct string *p)
+memptrs_remove(struct aplwc *aplwc, struct memory_ptr_frame *frame)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ assert(aplwc->memptrs == frame);
+ aplwc->memptrs = frame->link;
}
-struct token_list *
-memory_alloc_token_list(struct memory *m, size_t tokens)
+struct aplwc_line *
+memory_alloc_aplwc_line(struct aplwc *aplwc)
{
- struct token_list *p;
+ struct aplwc_line *p;
- p = alloc(m, sizeof(struct token_list) + (tokens * sizeof(struct token)), alignof(struct token_list));
+ p = alloc(aplwc, sizeof(struct aplwc_line), CHUNK_TAG_APLWC_LINE);
return p;
}
-void
-memory_free_token_list(struct memory *m, struct token_list *p)
+struct string *
+memory_alloc_string(struct aplwc *aplwc, size_t bytes)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct string *p;
+
+ p = alloc(aplwc, sizeof(struct string) + bytes, CHUNK_TAG_STRING);
+
+ return p;
}
-struct aplwc_value *
-memory_alloc_aplwc_value(struct memory *m)
+struct token_list *
+memory_alloc_token_list(struct aplwc *aplwc, size_t tokens)
{
- struct aplwc_value *p;
+ struct token_list *p;
- p = alloc(m, sizeof(struct aplwc_value), alignof(struct aplwc_value));
+ p = alloc(aplwc, sizeof(struct token_list) + (tokens * sizeof(struct token)), CHUNK_TAG_TOKEN_LIST);
return p;
}
-void
-memory_free_aplwc_value(struct memory *m, struct aplwc_value *p)
+struct aplwc_value *
+memory_alloc_aplwc_value(struct aplwc *aplwc)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct aplwc_value *p;
+
+ p = alloc(aplwc, sizeof(struct aplwc_value), CHUNK_TAG_APLWC_VALUE);
+ return p;
}
struct aplwc_module *
-memory_alloc_aplwc_module(struct memory *m)
+memory_alloc_aplwc_module(struct aplwc *aplwc)
{
struct aplwc_module *p;
- p = alloc(m, sizeof(struct aplwc_module), alignof(struct aplwc_module));
+ p = alloc(aplwc, sizeof(struct aplwc_module), CHUNK_TAG_APLWC_MODULE);
return p;
}
void
-memory_free_aplwc_module(struct memory *m, struct aplwc_module *p)
+clear_ref(struct aplwc_ref *ref)
+{
+ ref->id = -1;
+ ref->aplwc = NULL;
+}
+
+int
+valid_ref(struct aplwc_ref ref)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ return ref.id >= 0;
}
diff --git a/src/lib/module.c b/src/lib/module.c
index 9a202f9..2524eca 100644
--- a/src/lib/module.c
+++ b/src/lib/module.c
@@ -58,12 +58,17 @@ load_module(struct aplwc *aplwc, struct string *name, struct token_list *tokens)
enum aplwc_rc rc;
struct aplwc_module *module;
- module = memory_alloc_aplwc_module(&aplwc->memory);
+ MEMPTRS(aplwc, 2);
+
+ MEMPTR(0, name);
+ MEMPTR(1, tokens);
+
+ module = memory_alloc_aplwc_module(aplwc);
if(module == NULL){
- aplwc->last_error = APLWC_OUT_OF_MEMORY;
- return aplwc->last_error;
+ rc = aplwc_last_error(aplwc);
+ goto end;
}
- module->aplwc = aplwc;
+
module->name = name;
module->tokens = tokens;
module->link = NULL;
@@ -72,10 +77,11 @@ load_module(struct aplwc *aplwc, struct string *name, struct token_list *tokens)
if(rc == APLWC_OK){
module->link = aplwc->modules;
aplwc->modules = module;
- }else{
- memory_free_aplwc_module(&aplwc->memory, module);
- }
+ }else
+ aplwc->last_error = rc;
+end:
+ MEMPTRS_EXIT(aplwc);
return rc;
}
diff --git a/src/lib/string.c b/src/lib/string.c
index 24f978c..cc6c6cb 100644
--- a/src/lib/string.c
+++ b/src/lib/string.c
@@ -69,13 +69,12 @@ invalid:
if(extra != 0)
goto invalid;
- string = memory_alloc_string(&aplwc->memory, bytecount);
+ string = memory_alloc_string(aplwc, bytecount);
if(string){
string->bytes = bytecount;
string->chars = charcount;
memcpy(string->utf8, utf8, bytecount);
- }else
- aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ }
return string;
}
diff --git a/src/lib/syntax.c b/src/lib/syntax.c
index 395c75e..ae52343 100644
--- a/src/lib/syntax.c
+++ b/src/lib/syntax.c
@@ -240,17 +240,17 @@ scan_string(struct aplwc *aplwc, struct string *text, int oneline)
struct token_list *tokens;
size_t n, i;
+ MEMPTRS(aplwc, 1);
+ MEMPTR(0, text);
+
+ tokens = NULL;
rc = check(text, &n);
- if(rc != APLWC_OK){
- aplwc->last_error = rc;
- return NULL;
- }
+ if(rc != APLWC_OK)
+ goto end;
- tokens = memory_alloc_token_list(&aplwc->memory, n);
- if(tokens == NULL){
- aplwc->last_error = APLWC_OUT_OF_MEMORY;
- return NULL;
- }
+ tokens = memory_alloc_token_list(aplwc, n);
+ if(tokens == NULL)
+ goto end;
tokens->text = text;
tokens->length = n;
scan(text, tokens);
@@ -261,12 +261,16 @@ scan_string(struct aplwc *aplwc, struct string *text, int oneline)
for(i++; i < n; i++){
if(tokens->tokens[i].tag != TOKEN_TAG_NEWLINE){
- memory_free_token_list(&aplwc->memory, tokens);
- return NULL;
+ rc = APLWC_INVALID_SYNTAX;
+ tokens = NULL;
+ goto end;
}
}
}
+end:
+ aplwc->last_error = rc;
+ MEMPTRS_EXIT(aplwc);
return tokens;
}
diff --git a/tests/aplwc_errstr.c b/tests/aplwc_errstr.c
index 8014c1e..fe9fa0e 100644
--- a/tests/aplwc_errstr.c
+++ b/tests/aplwc_errstr.c
@@ -1,47 +1,75 @@
-#include <aplwc.h>
+#include <stdlib.h>
#include <string.h>
+#include <aplwc.h>
+
+static void *
+my_alloc(size_t min, size_t max, size_t *actual)
+{
+ (void)max;
+
+ *actual = min;
+ return malloc(min);
+}
+
+static void
+my_dealloc(void *p)
+{
+ free(p);
+}
int
main(void)
{
- uint8_t data[APLWC_MIN_MEM];
+ enum aplwc_rc ret;
+ struct aplwc_fns fns;
struct aplwc *aplwc;
- int ret;
+ int success;
const char *err;
- ret = aplwc_new(&aplwc, data, sizeof(data));
+ success = 0;
+
+ memset(&fns, 0, sizeof(fns));
+ fns.alloc = my_alloc;
+ fns.dealloc = my_dealloc;
+
+ aplwc = aplwc_new(fns, &ret);
if(ret)
- return 1;
+ goto end;
err = aplwc_errstr(aplwc);
if(err == NULL)
- return 1;
+ goto end;
if(strlen(err) != 0)
- return 1;
+ goto end;
err = aplwc_errstr(NULL);
if(err == NULL)
- return 1;
+ goto end;
if(strcmp(err, "(unable to produce error string)") != 0)
- return 1;
+ goto end;
aplwc_destroy(aplwc);
- ret = aplwc_new(&aplwc, data, 0);
+ fns.alloc = NULL;
+ aplwc = aplwc_new(fns, &ret);
if(ret == 0)
- return 1;
+ goto end;
err = aplwc_errstr_rc(ret);
if(err == NULL)
- return 1;
+ goto end;
for(ret = 0; ret <= APLWC_RC_LAST; ret++){
err = aplwc_errstr_rc(ret);
if(err == NULL)
- return 1;
+ goto end;
if(strcmp(err, "(unable to produce error string)") == 0)
- return 1;
+ goto end;
}
- return 0;
+ success = 1;
+end:
+ if(aplwc)
+ aplwc_destroy(aplwc);
+ return !success;
}
diff --git a/tests/aplwc_eval.c b/tests/aplwc_eval.c
index 8f6abe0..3fd8c27 100644
--- a/tests/aplwc_eval.c
+++ b/tests/aplwc_eval.c
@@ -1,15 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <aplwc.h>
+static void *
+my_alloc(size_t min, size_t max, size_t *actual)
+{
+ (void)max;
+
+ *actual = min;
+ return malloc(min);
+}
+
+static void
+my_dealloc(void *p)
+{
+ free(p);
+}
+
int
main(int argc, char *argv[])
{
- uint8_t data[APLWC_MIN_MEM+1000];
char *input;
struct aplwc *aplwc;
- struct aplwc_line *line;
- struct aplwc_value *value;
+ AplwcLine line;
+ AplwcValue value;
+ struct aplwc_fns fns;
enum aplwc_rc ret;
FILE *f;
long input_size;
@@ -35,7 +51,12 @@ main(int argc, char *argv[])
return 1;
input[input_size] = 0;
- ret = aplwc_new(&aplwc, data, sizeof(data));
+
+ memset(&fns, 0, sizeof(fns));
+ fns.alloc = my_alloc;
+ fns.dealloc = my_dealloc;
+
+ aplwc = aplwc_new(fns, &ret);
if(ret)
return 1;
@@ -45,11 +66,11 @@ main(int argc, char *argv[])
input[i] = 0;
line = aplwc_read(aplwc, &input[j]);
- if(line == NULL)
+ if(!aplwc_valid_line(line))
return 1;
value = aplwc_eval(line);
- if((value == NULL) && (aplwc_last_error(aplwc) != APLWC_OUT_OF_MEMORY))
+ if((!aplwc_valid_value(value)) && (aplwc_last_error(aplwc) != APLWC_OUT_OF_MEMORY))
return 1;
aplwc_free_value(value);
diff --git a/tests/aplwc_new.c b/tests/aplwc_new.c
deleted file mode 100644
index 8d57ecd..0000000
--- a/tests/aplwc_new.c
+++ /dev/null
@@ -1,26 +0,0 @@
-#include <aplwc.h>
-
-int
-main(void)
-{
- struct aplwc *aplwc;
- uint8_t data[APLWC_MIN_MEM*2];
- int ret;
- size_t i, j;
-
- for(i = 0; i < 32; i++){
- for(j = 0; j <= sizeof(data)-i; j++){
- ret = aplwc_new(&aplwc, data+i, j);
- aplwc_destroy(aplwc);
- if(ret){
- if(ret != APLWC_OUT_OF_MEMORY)
- return 1;
- if(j >= APLWC_MIN_MEM)
- return 1;
- break;
- }
- }
- }
-
- return 0;
-}
diff --git a/tests/aplwc_read.c b/tests/aplwc_read.c
index c54f598..d090494 100644
--- a/tests/aplwc_read.c
+++ b/tests/aplwc_read.c
@@ -1,18 +1,38 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <aplwc.h>
+static void *
+my_alloc(size_t min, size_t max, size_t *actual)
+{
+ (void)max;
+
+ *actual = min;
+ return malloc(min);
+}
+
+static void
+my_dealloc(void *p)
+{
+ free(p);
+}
+
int
main(int argc, char *argv[])
{
- uint8_t data[APLWC_MIN_MEM+1000];
+ struct aplwc_fns fns;
char *input;
struct aplwc *aplwc;
- struct aplwc_line *line;
+ AplwcLine line;
enum aplwc_rc ret;
FILE *f;
long input_size;
+ memset(&fns, 0, sizeof(fns));
+ fns.alloc = my_alloc;
+ fns.dealloc = my_dealloc;
+
if(argc != 2)
return 1;
@@ -33,12 +53,12 @@ main(int argc, char *argv[])
return 1;
input[input_size] = 0;
- ret = aplwc_new(&aplwc, data, sizeof(data));
+ aplwc = aplwc_new(fns, &ret);
if(ret)
return 1;
line = aplwc_read(aplwc, input);
- if((line == NULL) && (aplwc_last_error(aplwc) != APLWC_OUT_OF_MEMORY))
+ if((!aplwc_valid_line(line)) && (aplwc_last_error(aplwc) != APLWC_OUT_OF_MEMORY))
return 1;
aplwc_free_line(line);
aplwc_destroy(aplwc);
diff --git a/tests/aplwc_utf8.c b/tests/aplwc_utf8.c
index b97b978..367db5d 100644
--- a/tests/aplwc_utf8.c
+++ b/tests/aplwc_utf8.c
@@ -1,3 +1,5 @@
+#include <stdlib.h>
+#include <string.h>
#include <aplwc.h>
uint8_t bad1[] = {0xff,0};
@@ -14,12 +16,27 @@ uint8_t good3[] = {0xed,0x95,0x9c,0xea,0xb5,0xad,0xec,0x96,0xb4,0};
uint8_t good4[] = {0xe6,0x97,0xa5,0xe6,0x9c,0xac,0xe8,0xaa,0x9e,0};
uint8_t good5[] = {0xef,0xbb,0xbf,0xf0,0xa3,0x8e,0xb4,0};
+static void *
+my_alloc(size_t min, size_t max, size_t *actual)
+{
+ (void)max;
+
+ *actual = min;
+ return malloc(min);
+}
+
+static void
+my_dealloc(void *p)
+{
+ free(p);
+}
+
int
main(void)
{
- uint8_t data[APLWC_MIN_MEM+64000];
struct aplwc *aplwc;
- struct aplwc_line *line;
+ AplwcLine line;
+ struct aplwc_fns fns;
enum aplwc_rc ret;
size_t i;
@@ -41,13 +58,17 @@ main(void)
good5
};
- ret = aplwc_new(&aplwc, data, sizeof(data));
+ memset(&fns, 0, sizeof(fns));
+ fns.alloc = my_alloc;
+ fns.dealloc = my_dealloc;
+
+ aplwc = aplwc_new(fns, &ret);
if(ret)
return 1;
for(i = 0; i < (sizeof(bad_tests)/sizeof(*bad_tests)); i++){
line = aplwc_read(aplwc, (char *)bad_tests[i]);
- if(line != NULL)
+ if(aplwc_valid_line(line))
return 1;
if(aplwc_last_error(aplwc) != APLWC_INVALID_UTF8)
return 1;
@@ -56,7 +77,7 @@ main(void)
for(i = 0; i < (sizeof(good_tests)/sizeof(*good_tests)); i++){
line = aplwc_read(aplwc, (char *)good_tests[i]);
- if((line == NULL) && (aplwc_last_error(aplwc) == APLWC_INVALID_UTF8))
+ if((!aplwc_valid_line(line)) && (aplwc_last_error(aplwc) == APLWC_INVALID_UTF8))
return 1;
aplwc_free_line(line);
}
diff --git a/tests/scripts/bad/1.aplwc b/tests/scripts/bad/1.aplwc
new file mode 100755
index 0000000..792d600
--- /dev/null
+++ b/tests/scripts/bad/1.aplwc
@@ -0,0 +1 @@
+#
diff --git a/tests/scripts/bad/2.aplwc b/tests/scripts/bad/2.aplwc
new file mode 100755
index 0000000..1e0a14c
--- /dev/null
+++ b/tests/scripts/bad/2.aplwc
@@ -0,0 +1 @@
+!#
diff --git a/tests/scripts/bad/3.aplwc b/tests/scripts/bad/3.aplwc
new file mode 100755
index 0000000..ba38a01
--- /dev/null
+++ b/tests/scripts/bad/3.aplwc
@@ -0,0 +1 @@
+##
diff --git a/tests/scripts/1.aplwc b/tests/scripts/good/1.aplwc
index e101d02..e101d02 100755
--- a/tests/scripts/1.aplwc
+++ b/tests/scripts/good/1.aplwc
diff --git a/tests/scripts/2.aplwc b/tests/scripts/good/2.aplwc
index e60e717..e60e717 100755
--- a/tests/scripts/2.aplwc
+++ b/tests/scripts/good/2.aplwc
diff --git a/tools/checkcov b/tools/checkcov
index c228c2f..ac3cf91 100755
--- a/tools/checkcov
+++ b/tools/checkcov
@@ -31,6 +31,7 @@ BEGIN {
function miss(t){
printf("%c %s:%d %s\n", t, file, line, src)
+ missing_count++;
}
/^condition outcomes covered [0-9]+\/[1-9][0-9]*/ {
@@ -70,4 +71,8 @@ function miss(t){
miss("L")
next
}
+
+END {
+ printf("\nMissing coverage: %d\n", missing_count);
+}
'