-rw-r--r-- 76692 holtrace-20250617/holtrace-verify.c raw
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "vector.h"
#include "in.h"
typedef long long num;
VECTOR_define_inline(char);
VECTOR_define_inline(num);
// ===== state
static num curline = 1; // 1-numbered for error messages
static num typeid;
static num termid;
static num thmid;
static num infid;
static num typedefid;
static num ptid;
static vector_char type2tag_vec;
static num type2tag(num type) { return vector_char_read(&type2tag_vec,type); }
static vector_char term2tag_vec;
static char term2tag(num term) { return vector_char_read(&term2tag_vec,term); }
static vector_num typedata;
static vector_num type2datapos;
static vector_num type2hash;
static vector_num termdata;
static vector_num term2datapos;
static vector_num termfrees;
static vector_num term2freepos;
static vector_num term2freelen;
static vector_num term2hash;
static vector_num thmdata;
static vector_num thm2datapos;
static vector_num thm2conclusion;
static vector_char typedef2split;
static vector_num typedef2reptype;
static vector_num typedef2repprop;
static vector_num typedef2numtyvars;
static vector_num typedef2infid;
static vector_num typedef2abstypename;
static vector_num typedef2absname;
static vector_num typedef2repname;
static vector_num typedef2tyvarnames;
static char lastinftag;
// ===== general constraints
#define CUTOFF 288230376151711743LL
// but likely to abort before that from running out of heap space
// also, could abort from running out of stack space for recursion
// want this in production, ignoring NDEBUG, always having side effects
#define know(property) ((property) ? 0 : (fprintf(stderr,"code line %d: internal error: failed %s\n",__LINE__,#property), exit(100)))
static void reject(const char *err)
{
fprintf(stderr,"line %lld: %s\n",curline,err);
exit(100);
}
// ===== general parsing
// some allowed variations:
// will accept leading zeros
// will not require, e.g., id-1 to be !
// will accept space even when not needed
static num subid_core(const char **s,num id)
{
const char *t = *s;
if (*t == '\n') reject("missing numeric argument");
if (*t == '!') { *s = t+1; return id-1; }
if (*t == '@') { *s = t+1; return id-2; }
if (*t == '#') { *s = t+1; return id-3; }
if (*t == '$') { *s = t+1; return id-4; }
if (*t == '%') { *s = t+1; return id-5; }
if (*t == '^') { *s = t+1; return id-6; }
if (*t == '&') { *s = t+1; return id-7; }
if (*t == '*') { *s = t+1; return id-8; }
if (*t == '(') { *s = t+1; return id-9; }
if (*t == ')') { *s = t+1; return id-10; }
int negate = 0;
if (*t == '-') { ++t; negate = 1; }
num result = 0;
for (;;) {
char c = *t;
if (result >= CUTOFF) {
fprintf(stderr,"line %lld: refusing to handle number %lld\n",curline,result);
exit(100);
}
if (c >= '0' && c <= '9')
result = 32*result+(c-'0');
else if (c >= 'A' && c <= 'V')
result = 32*result+(c-'7');
else break;
++t;
}
if (*t == ' ') ++t;
if (t == *s) reject("non-numeric argument where numeric is expected");
*s = t;
return negate ? id-result : result;
}
static num subid(const char **s,num id)
{
num result = subid_core(s,id);
if (result < 0 || (result >= id && id >= 0)) {
fprintf(stderr,"line %lld: result %lld out of range\n",curline,result);
exit(100);
}
return result;
}
static void noteol(const char *s)
{
if (*s == '\n') reject("empty name");
}
static void eol(const char *s)
{
if (*s != '\n') reject("extra arguments");
}
// ===== name operations
static int name_cmp(const char *s,const char *t)
{
for (;;) {
char a = *s++;
char b = *t++;
if (a > b) return 1;
if (a < b) return -1;
if (a == '\n') return 0;
}
}
static int name_is(const char *s,const char *t)
{
for (;;) {
char a = *s++;
char b = *t++;
if (a != b) return 0;
if (a == '\n') return 1;
}
}
static int name_is_withsplit(const char *s,const char *t,char split)
{
for (;;) {
char a = *s++;
char b = *t++;
if (a == '\n') return b == split;
if (b == split) return 0;
if (a != b) return 0;
}
}
static int nameprimes_is(const char *s,num sprimes,const char *t,num tprimes)
{
for (;;) {
char a = *s;
if (a != '\n') ++s; else if (sprimes) { a = '\''; --sprimes; }
char b = *t;
if (b != '\n') ++t; else if (tprimes) { b = '\''; --tprimes; }
if (a != b) return 0;
if (a == '\n') return 1;
}
}
static num hash_mix(num h)
{
uint32_t h0 = h;
uint32_t h1 = h>>32;
h0 ^= (h1<<5)|(h1>>27);
h1 += (h0<<7)|(h0>>25);
h0 ^= (h1<<11)|(h1>>21);
h1 ^= (h0<<16)|(h0>>16);
h = h1;
h <<= 32;
h |= h0;
return h;
}
static num name_hash(const char *s)
{
num result = 632422315470652589LL;
for (;;) {
char c = *s++;
if (c == '\n') return result;
result = hash_mix(result+c);
}
}
// ===== name->num dictionaries
// XXX speed: maybe use faster associative array
typedef struct {
// for one->3, two->1, three->4, four->1:
vector_char names; // one\ntwo\nthree\nfour\n
vector_num index2pos; // 0 3 6 11
vector_num index2result; // 3 1 4 1
} dict;
static int dict_namematch(dict *d,const char *s,char split,num pos)
{
for (;;) {
char a = *s++;
char b = vector_char_read(&d->names,pos++);
if (a == split) return b == '\n';
if (b == '\n') return 0;
if (a != b) return 0;
}
}
static num dict_lookup(dict *d,const char *s,char split,int *flag)
{
num len = vector_num_len(&d->index2pos);
for (num i = 0;i < len;++i) {
num pos = vector_num_read(&d->index2pos,i);
if (dict_namematch(d,s,split,pos)) {
if (flag) *flag = 1;
return vector_num_read(&d->index2result,i);
}
}
if (flag) *flag = 0;
return -1;
}
static void dict_add(dict *d,const char *s,char split,num result)
{
vector_num_append(&d->index2result,result);
num pos = vector_char_len(&d->names);
vector_num_append(&d->index2pos,pos);
while (*s != split)
vector_char_append(&d->names,*s++);
vector_char_append(&d->names,'\n');
}
static dict const2type_dict;
static num const2type(const char *s) { return dict_lookup(&const2type_dict,s,'\n',0); }
static dict absname2typedef_dict;
static num absname2typedef(const char *s) { return dict_lookup(&absname2typedef_dict,s,'\n',0); }
static dict repname2typedef_dict;
static num repname2typedef(const char *s) { return dict_lookup(&repname2typedef_dict,s,'\n',0); }
static void reject_previous_constnames(const char *s,char split)
{
if (name_is_withsplit("=\n",s,split)) reject("const name =");
if (name_is_withsplit("@\n",s,split)) reject("const name @");
if (dict_lookup(&const2type_dict,s,split,0) >= 0) reject("colliding const name");
if (dict_lookup(&absname2typedef_dict,s,split,0) >= 0) reject("colliding const name");
if (dict_lookup(&repname2typedef_dict,s,split,0) >= 0) reject("colliding const name");
}
static void const2type_add(const char *s,char split,num result)
{
reject_previous_constnames(s,split);
dict_add(&const2type_dict,s,split,result);
}
static void absname2typedef_add(const char *s,char split,num result)
{
reject_previous_constnames(s,split);
dict_add(&absname2typedef_dict,s,split,result);
}
static void repname2typedef_add(const char *s,char split,num result)
{
reject_previous_constnames(s,split);
dict_add(&repname2typedef_dict,s,split,result);
}
static dict app2arity_dict;
static num app2arity(const char *s)
{
if (name_is(s,"fun\n")) return 2;
if (name_is(s,"bool\n")) return 0;
if (name_is(s,"ind\n")) return 0;
return dict_lookup(&app2arity_dict,s,'\n',0);
}
// trying to match new_type in fusion.ml
static void app2arity_add(const char *s,char split,num arity)
{
if (name_is_withsplit("fun\n",s,split)) reject("colliding tyapp name");
if (name_is_withsplit("bool\n",s,split)) reject("colliding tyapp name");
if (name_is_withsplit("ind\n",s,split)) reject("colliding tyapp name");
if (dict_lookup(&app2arity_dict,s,split,0) >= 0) reject("colliding tyapp name");
dict_add(&app2arity_dict,s,split,arity);
}
// ===== type operations
static int type_cmp(num type,num TYPE)
{
if (type == TYPE) return 0;
char tag = type2tag(type);
char TAG = type2tag(TYPE);
if (tag > TAG) return 1;
if (tag < TAG) return -1;
num pos1 = vector_num_read(&type2datapos,type+1);
num POS1 = vector_num_read(&type2datapos,TYPE+1);
char *s = in_buf+vector_num_read(&typedata,pos1-1);
char *S = in_buf+vector_num_read(&typedata,POS1-1);
int result = name_cmp(s,S);
if (result) return result;
if (tag == 'a') return 0;
know(tag == 'b');
num pos0 = vector_num_read(&type2datapos,type);
num POS0 = vector_num_read(&type2datapos,TYPE);
know(POS1-POS0 == pos1-pos0);
for (num i = 0;i < pos1-1-pos0;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
num SUBTYPE = vector_num_read(&typedata,POS0+i);
result = type_cmp(subtype,SUBTYPE);
if (result) return result;
}
return 0;
}
static int type_is(num type,num TYPE)
{
if (type == TYPE) return 1;
if (vector_num_read(&type2hash,type) != vector_num_read(&type2hash,TYPE)) return 0;
return type_cmp(type,TYPE) == 0;
}
static int type_is_tyvarnamed(num type,const char *name,char split)
{
if (type2tag(type) != 'a') return 0;
num pos0 = vector_num_read(&type2datapos,type);
num pos1 = vector_num_read(&type2datapos,type+1);
if (pos1-pos0 != 1) return 0;
const char *typename = in_buf+vector_num_read(&typedata,pos0);
return name_is_withsplit(typename,name,split);
}
// does not constrain arguments
static int type_is_appnamed(num type,const char *name,char split)
{
if (type2tag(type) != 'b') return 0;
num pos1 = vector_num_read(&type2datapos,type+1);
const char *typename = in_buf+vector_num_read(&typedata,pos1-1);
return name_is_withsplit(typename,name,split);
}
static int type_is_bool(num type)
{
if (type2tag(type) != 'b') return 0;
num pos0 = vector_num_read(&type2datapos,type);
num pos1 = vector_num_read(&type2datapos,type+1);
if (pos1-pos0 != 1) return 0;
return name_is(in_buf+vector_num_read(&typedata,pos0),"bool\n");
}
static int type_is_fun(num type,num *from,num *to)
{
if (type2tag(type) != 'b') return 0;
num pos0 = vector_num_read(&type2datapos,type);
num pos1 = vector_num_read(&type2datapos,type+1);
if (pos1-pos0 != 3) return 0;
if (!name_is(in_buf+vector_num_read(&typedata,pos0+2),"fun\n")) return 0;
if (from) *from = vector_num_read(&typedata,pos0);
if (to) *to = vector_num_read(&typedata,pos0+1);
return 1;
}
static int type_is_funfromto(num type,num from,num to)
{
num frommatch,tomatch;
if (!type_is_fun(type,&frommatch,&tomatch)) return 0;
if (!type_is(from,frommatch)) return 0;
if (!type_is(to,tomatch)) return 0;
return 1;
}
static int type_is_abstype(num abstype,const char *abstypename,const char *tyvarnames,num numtyvars,char split)
{
if (type2tag(abstype) != 'b') return 0;
num pos0 = vector_num_read(&type2datapos,abstype);
num pos1 = vector_num_read(&type2datapos,abstype+1);
if (pos1-pos0 != numtyvars+1) return 0;
if (!name_is_withsplit(in_buf+vector_num_read(&typedata,pos1-1),abstypename,split)) return 0;
const char *s = tyvarnames;
for (num i = 0;i < numtyvars;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
if (!type_is_tyvarnamed(subtype,s,split)) return 0;
while (*s != split) ++s;
++s;
}
return 1;
}
// 1 if TYPE is result of tyvarnames->abstype substitution into type
static int type_tyvarnames_subst_is(num type,num TYPE,num abstype,num numtyvars,const char *tyvarnames,char split)
{
if (!numtyvars) return type_is(type,TYPE);
char tag = type2tag(type);
if (tag == 'b') {
if (type2tag(TYPE) != 'b') return 0;
num pos1 = vector_num_read(&type2datapos,type+1);
num POS1 = vector_num_read(&type2datapos,TYPE+1);
char *s = in_buf+vector_num_read(&typedata,pos1-1);
char *S = in_buf+vector_num_read(&typedata,POS1-1);
if (!name_is(s,S)) return 0;
num pos0 = vector_num_read(&type2datapos,type);
num POS0 = vector_num_read(&type2datapos,TYPE);
know(POS1-POS0 == pos1-pos0);
for (num i = 0;i < pos1-1-pos0;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
num SUBTYPE = vector_num_read(&typedata,POS0+i);
if (!type_tyvarnames_subst_is(subtype,SUBTYPE,abstype,numtyvars,tyvarnames,split))
return 0;
}
return 1;
}
know(tag == 'a');
const char *t = tyvarnames;
for (num i = 0;i < numtyvars;++i) {
if (type_is_tyvarnamed(type,t,split)) {
// e.g. tyvars are A B C
// and we've found that type matches B
// so now want to check that TYPE matches second position in abstype
know(type2tag(abstype) == 'b');
num pos0 = vector_num_read(&type2datapos,abstype);
num pos1 = vector_num_read(&type2datapos,abstype+1);
know(pos1-pos0 == numtyvars+1);
num abstypei = vector_num_read(&typedata,pos0+i);
return type_is(TYPE,abstypei);
}
while (*t != split) ++t;
++t;
}
// type is tyvar not in tyvars
// so look for exact match
return type_is(type,TYPE);
}
static int type_subst_is(vector_num *from,vector_num *to,num type,num TYPE)
{
char tag = type2tag(type);
if (tag == 'b') {
if (type2tag(TYPE) != 'b') return 0;
num pos1 = vector_num_read(&type2datapos,type+1);
num POS1 = vector_num_read(&type2datapos,TYPE+1);
char *s = in_buf+vector_num_read(&typedata,pos1-1);
char *S = in_buf+vector_num_read(&typedata,POS1-1);
if (!name_is(s,S)) return 0;
num pos0 = vector_num_read(&type2datapos,type);
num POS0 = vector_num_read(&type2datapos,TYPE);
know(POS1-POS0 == pos1-pos0);
for (num i = 0;i < pos1-1-pos0;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
num SUBTYPE = vector_num_read(&typedata,POS0+i);
if (!type_subst_is(from,to,subtype,SUBTYPE))
return 0;
}
return 1;
}
know(tag == 'a');
num len = vector_num_len(from);
know(len == vector_num_len(to));
for (num i = 0;i < len;++i)
if (type_is(type,vector_num_read(from,i)))
return type_is(TYPE,vector_num_read(to,i));
return type_is(type,TYPE);
}
static int type_substboth_is(vector_num *from,vector_num *to,num type,num TYPE)
{
char tag = type2tag(type);
char TAG = type2tag(TYPE);
if (tag == 'a') {
num len = vector_num_len(from);
know(len == vector_num_len(to));
for (num i = 0;i < len;++i)
if (type_is(type,vector_num_read(from,i))) {
type = vector_num_read(to,i);
break;
}
return type_subst_is(from,to,TYPE,type);
}
if (TAG == 'a') {
num len = vector_num_len(from);
know(len == vector_num_len(to));
for (num i = 0;i < len;++i)
if (type_is(TYPE,vector_num_read(from,i))) {
TYPE = vector_num_read(to,i);
break;
}
return type_subst_is(from,to,type,TYPE);
}
know(tag == 'b');
know(TAG == 'b');
num pos1 = vector_num_read(&type2datapos,type+1);
num POS1 = vector_num_read(&type2datapos,TYPE+1);
char *s = in_buf+vector_num_read(&typedata,pos1-1);
char *S = in_buf+vector_num_read(&typedata,POS1-1);
if (!name_is(s,S)) return 0;
num pos0 = vector_num_read(&type2datapos,type);
num POS0 = vector_num_read(&type2datapos,TYPE);
know(POS1-POS0 == pos1-pos0);
for (num i = 0;i < pos1-1-pos0;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
num SUBTYPE = vector_num_read(&typedata,POS0+i);
if (!type_substboth_is(from,to,subtype,SUBTYPE))
return 0;
}
return 1;
}
// XXX simplicity: maybe merge with type_subst_is
static int type_find_subst_append(vector_num *from,vector_num *to,num type,num TYPE)
{
char tag = type2tag(type);
if (tag == 'b') {
if (type2tag(TYPE) != 'b') return 0;
num pos1 = vector_num_read(&type2datapos,type+1);
num POS1 = vector_num_read(&type2datapos,TYPE+1);
char *s = in_buf+vector_num_read(&typedata,pos1-1);
char *S = in_buf+vector_num_read(&typedata,POS1-1);
if (!name_is(s,S)) return 0;
num pos0 = vector_num_read(&type2datapos,type);
num POS0 = vector_num_read(&type2datapos,TYPE);
know(POS1-POS0 == pos1-pos0);
for (num i = 0;i < pos1-1-pos0;++i) {
num subtype = vector_num_read(&typedata,pos0+i);
num SUBTYPE = vector_num_read(&typedata,POS0+i);
if (!type_find_subst_append(from,to,subtype,SUBTYPE))
return 0;
}
return 1;
}
know(tag == 'a');
num len = vector_num_len(from);
know(len == vector_num_len(to));
for (num i = 0;i < len;++i)
if (type_is(type,vector_num_read(from,i)))
return type_is(TYPE,vector_num_read(to,i));
vector_num_append(from,type);
vector_num_append(to,TYPE);
return 1;
}
// 1 if TYPE is result of some substitution into type
static int type_some_subst_is(num type,num TYPE)
{
static vector_num from;
static vector_num to;
vector_num_empty(&from);
vector_num_empty(&to);
if (!type_find_subst_append(&from,&to,type,TYPE)) return 0;
num len = vector_num_len(&from);
know(type_find_subst_append(&from,&to,type,TYPE));
know(len == vector_num_len(&from));
return 1;
}
// XXX simplicity: could merge with general const pattern matching
static int type_is_equality(num type)
{
num from,to,tofrom,toto;
if (!type_is_fun(type,&from,&to)) return 0;
if (!type_is_fun(to,&tofrom,&toto)) return 0;
if (!type_is(from,tofrom)) return 0;
return type_is_bool(toto);
}
// XXX simplicity: could merge with general const pattern matching
static int type_is_choice(num type)
{
num from,to,fromfrom,fromto;
if (!type_is_fun(type,&from,&to)) return 0;
if (!type_is_fun(from,&fromfrom,&fromto)) return 0;
if (!type_is(fromfrom,to)) return 0;
return type_is_bool(fromto);
}
// accumulates unique tyvars in result (not necessarily sorted)
// on top of any existing tyvars
// each tyvar represented as an index within in_buf
// and uniqueness being determined by type_is
static void type_vars_append(vector_num *result,num type)
{
char tag = type2tag(type);
if (tag == 'a') {
for (num i = 0;i < vector_num_len(result);++i)
if (type_is(vector_num_read(result,i),type))
return;
vector_num_append(result,type);
return;
}
know(tag == 'b');
num pos0 = vector_num_read(&type2datapos,type);
num pos1 = vector_num_read(&type2datapos,type+1);
for (num i = pos0;i < pos1-1;++i)
type_vars_append(result,vector_num_read(&typedata,i));
}
static void type_vars(vector_num *result,num type)
{
vector_num_empty(result);
type_vars_append(result,type);
}
// ===== term operations
static num term2type(num term)
{
return vector_num_read(&termdata,vector_num_read(&term2datapos,term));
}
static int term_is_const(num term,num *type,const char **name)
{
if (term2tag(term) != 'c') return 0;
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 2);
if (type) *type = vector_num_read(&termdata,pos0);
if (name) *name = in_buf+vector_num_read(&termdata,pos0+1);
return 1;
}
static int term_is_var(num term,num *type,const char **name)
{
if (term2tag(term) != 'd') return 0;
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 2);
if (type) *type = vector_num_read(&termdata,pos0);
if (name) *name = in_buf+vector_num_read(&termdata,pos0+1);
return 1;
}
static int term_is_comb(num term,num *type,num *funterm,num *valueterm)
{
if (term2tag(term) != 'e') return 0;
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 3);
if (type) *type = vector_num_read(&termdata,pos0);
if (funterm) *funterm = vector_num_read(&termdata,pos0+1);
if (valueterm) *valueterm = vector_num_read(&termdata,pos0+2);
return 1;
}
static int term_is_abs(num term,num *type,num *fromterm,num *toterm)
{
if (term2tag(term) != 'f') return 0;
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 3);
if (type) *type = vector_num_read(&termdata,pos0);
if (fromterm) *fromterm = vector_num_read(&termdata,pos0+1);
if (toterm) *toterm = vector_num_read(&termdata,pos0+2);
return 1;
}
static int term_is(num term,num TERM)
{
if (term == TERM) return 1;
if (vector_num_read(&term2hash,term) != vector_num_read(&term2hash,TERM)) return 0;
char tag = term2tag(term);
char TAG = term2tag(TERM);
if (TAG != tag) return 0;
num pos0 = vector_num_read(&term2datapos,term);
num POS0 = vector_num_read(&term2datapos,TERM);
num type = vector_num_read(&termdata,pos0);
num TYPE = vector_num_read(&termdata,POS0);
if (TYPE != type) return 0;
num pos1 = vector_num_read(&term2datapos,term+1);
num POS1 = vector_num_read(&term2datapos,TERM+1);
if (tag == 'c' || tag == 'd') {
know(pos1-pos0 == 2);
know(POS1-POS0 == 2);
const char *name = in_buf+vector_num_read(&termdata,pos0+1);
const char *NAME = in_buf+vector_num_read(&termdata,POS0+1);
return name_is(name,NAME);
}
know(pos1-pos0 == 3);
num subterm0 = vector_num_read(&termdata,pos0+1);
num SUBTERM0 = vector_num_read(&termdata,POS0+1);
if (!term_is(subterm0,SUBTERM0)) return 0;
num subterm1 = vector_num_read(&termdata,pos0+2);
num SUBTERM1 = vector_num_read(&termdata,POS0+2);
if (!term_is(subterm1,SUBTERM1)) return 0;
return 1;
}
// should match ordav in hol-light/fusion.ml
// which is orda when t,u are each variables
// caller here guarantees that t,u are variables
static int term_alphaorder_cmp_vars(num t,num u,vector_num *L)
{
if (t == u) return 0;
num lenL = vector_num_len(L);
for (num i = 2*(lenL/2)-2;i >= 0;i -= 2) {
num v = vector_num_read(L,i);
num w = vector_num_read(L,i+1);
if (term_is(t,v)) return term_is(u,w) ? 0 : -1;
if (term_is(u,w)) return 1;
}
num tpos = vector_num_read(&term2datapos,t);
const char *tname = in_buf+vector_num_read(&termdata,tpos+1);
num upos = vector_num_read(&term2datapos,u);
const char *uname = in_buf+vector_num_read(&termdata,upos+1);
return name_cmp(tname,uname);
}
// can adjust L while running but restores it
static int term_alphaorder_cmp_recursive(num t,num u,vector_num *L)
{
if (t == u) return 0;
char ttag = term2tag(t);
char utag = term2tag(u);
if (ttag > utag) return 1;
if (ttag < utag) return -1;
if (ttag == 'd')
return term_alphaorder_cmp_vars(t,u,L);
if (ttag == 'c') {
num tpos = vector_num_read(&term2datapos,t);
num upos = vector_num_read(&term2datapos,u);
const char *tname = in_buf+vector_num_read(&termdata,tpos+1);
const char *uname = in_buf+vector_num_read(&termdata,upos+1);
int result = name_cmp(tname,uname);
if (result) return result;
num ttype = vector_num_read(&termdata,tpos);
num utype = vector_num_read(&termdata,upos);
return type_cmp(ttype,utype);
}
if (ttag == 'e') {
num tpos = vector_num_read(&term2datapos,t);
num upos = vector_num_read(&term2datapos,u);
num tfun = vector_num_read(&termdata,tpos+1);
num ufun = vector_num_read(&termdata,upos+1);
int result = term_alphaorder_cmp_recursive(tfun,ufun,L);
if (result) return result;
num tpoint = vector_num_read(&termdata,tpos+2);
num upoint = vector_num_read(&termdata,upos+2);
return term_alphaorder_cmp_recursive(tpoint,upoint,L);
}
num tfrom,tto;
num ufrom,uto;
know(term_is_abs(t,0,&tfrom,&tto));
know(term_is_abs(u,0,&ufrom,&uto));
num tfromtype,ufromtype;
know(term_is_var(tfrom,&tfromtype,0));
know(term_is_var(ufrom,&ufromtype,0));
int result = type_cmp(tfromtype,ufromtype);
if (result) return result;
num lenL = vector_num_len(L);
vector_num_append(L,tfrom);
vector_num_append(L,ufrom);
result = term_alphaorder_cmp_recursive(tto,uto,L);
vector_num_trunc(L,lenL);
return result;
}
// accumulates unique tyvars in result (not necessarily sorted)
// on top of any existing tyvars
static void term_tyvars_append(vector_num *result,num term)
{
char tag = term2tag(term);
if (tag == 'c' || tag == 'd') {
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 2);
type_vars_append(result,vector_num_read(&termdata,pos0));
return;
}
know(tag == 'e' || tag == 'f');
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 3);
num term0 = vector_num_read(&termdata,pos0+1);
num term1 = vector_num_read(&termdata,pos0+2);
term_tyvars_append(result,term0);
term_tyvars_append(result,term1);
// note: could also scan type
// but this would be redundant for both e and f
}
static void term_tyvars(vector_num *result,num term)
{
vector_num_empty(result);
term_tyvars_append(result,term);
}
static int term_is_eqsymbol(num term)
{
const char *name;
if (!term_is_const(term,0,&name)) return 0;
return name_is(name,"=\n");
}
static int term_is_equality(num term,num *left,num *right)
{
num funterm,funfunterm;
if (!term_is_comb(term,0,&funterm,right)) return 0;
if (!term_is_comb(funterm,0,&funfunterm,left)) return 0;
if (!term_is_eqsymbol(funfunterm)) return 0;
return 1;
}
static int term_alphaorder_cmp(num t,num u)
{
static vector_num tmp;
know(vector_num_len(&tmp) == 0);
return term_alphaorder_cmp_recursive(t,u,&tmp);
}
static int term_alphaequivalent(num t,num u)
{
static vector_num tmp;
know(vector_num_len(&tmp) == 0);
return term_alphaorder_cmp_recursive(t,u,&tmp) == 0;
}
static void term_freevars_append(vector_num *result,num term)
{
num origlen = vector_num_len(result);
num len = vector_num_read(&term2freelen,term);
num pos = vector_num_read(&term2freepos,term);
for (num i = 0;i < len;++i) {
num v = vector_num_read(&termfrees,pos+i);
int found = 0;
for (num j = 0;j < origlen;++j)
if (term_is(v,vector_num_read(result,j))) {
found = 1;
break;
}
if (!found)
vector_num_append(result,vector_num_read(&termfrees,pos+i));
}
}
static void term_freevars(vector_num *result,num term)
{
vector_num_empty(result);
num len = vector_num_read(&term2freelen,term);
num pos = vector_num_read(&term2freepos,term);
for (num i = 0;i < len;++i)
vector_num_append(result,vector_num_read(&termfrees,pos+i));
}
static int term_has_freevars(num term)
{
return vector_num_read(&term2freelen,term) > 0;
}
// 1 if v''' is a free variable in term
static int term_has_free(num term,num v,num primes)
{
num vtype;
const char *vname;
if (!term_is_var(v,&vtype,&vname)) return 0;
num len = vector_num_read(&term2freelen,term);
num pos = vector_num_read(&term2freepos,term);
for (num i = 0;i < len;++i) {
num w = vector_num_read(&termfrees,pos+i);
num wtype;
const char *wname;
know(term_is_var(w,&wtype,&wname));
if (type_is(wtype,vtype))
if (nameprimes_is(wname,0,vname,primes))
return 1;
}
return 0;
}
// later entries override earlier entries
// return value -1 means not found _or_ found -1
static int term_map(vector_num *from,vector_num *to,num term)
{
num i = vector_num_len(from);
while (i-- > 0) {
num fromi = vector_num_read(from,i);
if (term_is(fromi,term))
return vector_num_read(to,i);
}
return -1;
}
static int term_subst_obvious_id(num term,vector_num *from,vector_num *to)
{
num len = vector_num_read(&term2freelen,term);
num pos = vector_num_read(&term2freepos,term);
for (num i = 0;i < len;++i) {
num w = vector_num_read(&termfrees,pos+i);
if (term_map(from,to,w) >= 0) return 0;
}
return 1;
}
// 1 if substituting from->to+primed in term gives TERM
// should match subst in fusion.ml (but algorithm is different)
// primed[j]!=0 implies primed[j]>=0 and to[j] is a variable
static int term_subst_is(num term,vector_num *from,vector_num *to,vector_num *primed,num TERM)
{
if (term_subst_obvious_id(term,from,to))
return term_is(term,TERM);
char tag = term2tag(term);
know(tag != 'c');
if (tag == 'd') {
num m = term_map(from,to,term);
num mprimes = term_map(from,primed,term);
know(m >= 0);
know(mprimes >= 0);
if (mprimes == 0) return term_is(m,TERM);
// now m+mprimes is a variable
// want to check if same variable as TERM
num TERMtype;
const char *TERMname;
if (!term_is_var(TERM,&TERMtype,&TERMname)) return 0;
num mtype;
const char *mname;
know(term_is_var(m,&mtype,&mname));
if (!type_is(TERMtype,mtype)) return 0;
return nameprimes_is(TERMname,0,mname,mprimes);
}
if (term2tag(TERM) != tag) return 0;
num pos0 = vector_num_read(&term2datapos,term);
num pos1 = vector_num_read(&term2datapos,term+1);
know(pos1-pos0 == 3);
num term0 = vector_num_read(&termdata,pos0+1);
num term1 = vector_num_read(&termdata,pos0+2);
if (tag == 'e') {
num POS0 = vector_num_read(&term2datapos,TERM);
num POS1 = vector_num_read(&term2datapos,TERM+1);
know(POS1-POS0 == 3);
num TERM0 = vector_num_read(&termdata,POS0+1);
num TERM1 = vector_num_read(&termdata,POS0+2);
if (!term_subst_is(term0,from,to,primed,TERM0)) return 0;
if (!term_subst_is(term1,from,to,primed,TERM1)) return 0;
return 1;
}
know(tag == 'f');
num v = term0;
num vtype;
const char *vname;
know(term_is_var(v,&vtype,&vname));
num len = vector_num_len(from);
know(len == vector_num_len(to));
know(len == vector_num_len(primed));
for (num vprimes = 0;;++vprimes) {
if (vprimes >= CUTOFF) reject("too many primes");
// have v |-> term1
// considering rewriting as subst(v) |-> subst(term1)
// where subst replaces v with v'''
// where number of primes is vprimes
// and subst replaces other vars as per from->to
int ok = 1;
// goal: set ok to 0 if v''' has a problem
// original algorithm determines problem as follows:
// * first try vprimes = 0
// problem is then: some subst x->m with x!=v has x free in term1 and v free in m
// * if problem, move on to larger vprimes
// * for vprimes>0, define problem as:
// v''' free in origsubst(term1)
// where origsubst is from->to ignoring v
// there are really three cases for the v''' problem:
// * if v is free in term1: stays as v
// so no match for vprimes>0
// (and this also can be ignored for vprimes=0)
// * if x!=v is free in term1 and x->m:
// frees(origsubst(term1)) includes frees(m)
// so problem if v''' free in m
// (which is also a test we want for vprimes=0)
// * if f!=v is free in term1 and f not mapped from->to:
// frees(origsubst(term1)) includes f
// so problem if v''' free in f (i.e., v''' = f)
// in other words, for each f free in term1:
// * if f=v: no problem
// * else if f->m: problem if v''' free in m
// * else: problem if v''' free in f
num freelen = vector_num_read(&term2freelen,term1);
num freepos = vector_num_read(&term2freepos,term1);
for (num i = 0;i < freelen;++i) {
num f = vector_num_read(&termfrees,freepos+i);
if (term_is(f,v)) continue;
num m = term_map(from,to,f);
num mprimes = term_map(from,primed,f);
if (m < 0) { m = f; mprimes = 0; }
// is v+vprimes free in m+mprimes?
if (mprimes == 0) {
if (!term_has_free(m,v,vprimes)) continue;
} else { // both v and m are now variables
know(mprimes > 0);
num mtype;
const char *mname;
know(term_is_var(m,&mtype,&mname));
if (!type_is(vtype,mtype)) continue;
if (!nameprimes_is(vname,vprimes,mname,mprimes)) continue;
}
ok = 0;
break;
}
if (ok) {
vector_num_append(from,v);
vector_num_append(to,v);
vector_num_append(primed,vprimes);
num POS0 = vector_num_read(&term2datapos,TERM);
num POS1 = vector_num_read(&term2datapos,TERM+1);
know(POS1-POS0 == 3);
num TERM1 = vector_num_read(&termdata,POS0+2);
int result = term_subst_is(term1,from,to,primed,TERM1);
vector_num_trunc(from,len);
vector_num_trunc(to,len);
vector_num_trunc(primed,len);
return result;
}
}
}
// 1 if substituting tyfrom->tyto and varfrom->varto in term gives TERM
// note: if variable is handled by varfrom->varto, ignore tyfrom->tyto
// should match inst in fusion.ml (but algorithm is different)
static int term_tyvar_subst_is_recursive(num term,vector_num *tyfrom,vector_num *tyto,vector_num *varfrom,vector_num *varto,num TERM)
{
char tag = term2tag(term);
if (term2tag(TERM) != tag) return 0;
if (tag == 'c') {
num termtype;
num TERMtype;
const char *termname;
const char *TERMname;
know(term_is_const(term,&termtype,&termname));
know(term_is_const(TERM,&TERMtype,&TERMname));
if (!name_is(termname,TERMname)) return 0;
return type_subst_is(tyfrom,tyto,termtype,TERMtype);
}
if (tag == 'd') {
num m = term_map(varfrom,varto,term);
if (m >= 0) return term_is(m,TERM);
num termtype;
num TERMtype;
const char *termname;
const char *TERMname;
know(term_is_var(term,&termtype,&termname));
know(term_is_var(TERM,&TERMtype,&TERMname));
if (!name_is(termname,TERMname)) return 0;
return type_subst_is(tyfrom,tyto,termtype,TERMtype);
}
if (tag == 'e') {
num termfun,termpoint;
know(term_is_comb(term,0,&termfun,&termpoint));
num TERMfun,TERMpoint;
know(term_is_comb(TERM,0,&TERMfun,&TERMpoint));
if (!term_tyvar_subst_is_recursive(termfun,tyfrom,tyto,varfrom,varto,TERMfun)) return 0;
return term_tyvar_subst_is_recursive(termpoint,tyfrom,tyto,varfrom,varto,TERMpoint);
}
num varlen = vector_num_len(varfrom);
know(varlen == vector_num_len(varto));
int result = 0;
num v,tterm;
know(term_is_abs(term,0,&v,&tterm));
num vtype;
const char *vname;
know(term_is_var(v,&vtype,&vname));
// at this point v is no longer renamed
// so could mark (v,-1) in (varfrom,varto) at this point
// (if v is already in varfrom)
// instead: be careful to override v in varfrom lookups below
num V,TTERM;
know(term_is_abs(TERM,0,&V,&TTERM));
num Vtype;
const char *Vname;
know(term_is_var(V,&Vtype,&Vname));
// variable capture at this level means:
// subst(v) matches subst(f) for some f free in tterm
// without f being v
// then have to rename v as v''' for some number of primes
num freelen = vector_num_read(&term2freelen,tterm);
num freepos = vector_num_read(&term2freepos,tterm);
num wprimes;
for (wprimes = 0;;++wprimes) {
if (wprimes >= CUTOFF) reject("too many primes");
// considering v''' as replacement for v
// (or no replacement if wprimes == 0)
// does subst(v''') match subst(f) for some free f != v?
int collision = 0;
for (num i = 0;i < freelen;++i) {
num f = vector_num_read(&termfrees,freepos+i);
if (term_is(f,v)) continue;
// does subst(v''') match subst(f)?
num m = term_map(varfrom,varto,f);
if (m >= 0) {
// f is in varfrom, so being renamed
// now want to know:
// does subst(v''') match m?
num mtype;
const char *mname;
know(term_is_var(m,&mtype,&mname));
if (nameprimes_is(mname,0,vname,wprimes))
if (type_subst_is(tyfrom,tyto,vtype,mtype)) {
collision = 1;
break;
}
} else {
// f is not in varfrom, so just do tyfrom->tyto
num ftype;
const char *fname;
know(term_is_var(f,&ftype,&fname));
if (nameprimes_is(fname,0,vname,wprimes))
if (type_substboth_is(tyfrom,tyto,vtype,ftype)) {
collision = 1;
break;
}
}
}
if (!collision) break;
}
if (!nameprimes_is(Vname,0,vname,wprimes)) goto done;
if (!type_subst_is(tyfrom,tyto,vtype,Vtype)) goto done;
if (wprimes > 0) {
vector_num_append(varfrom,v);
vector_num_append(varto,V);
}
result = term_tyvar_subst_is_recursive(tterm,tyfrom,tyto,varfrom,varto,TTERM);
done:
vector_num_trunc(varfrom,varlen);
vector_num_trunc(varto,varlen);
return result;
}
static int term_tyvar_subst_is(num term,vector_num *from,vector_num *to,num TERM)
{
static vector_num varfrom;
static vector_num varto;
know(vector_num_len(&varfrom) == 0);
know(vector_num_len(&varto) == 0);
return term_tyvar_subst_is_recursive(term,from,to,&varfrom,&varto,TERM);
}
// ===== theorem operations
// see whether thmdata[start:stop] exactly matches h
static int hypotheses_match(num start,num stop,vector_num *h)
{
if (vector_num_len(h) != stop-start) return 0;
for (num i = start;i < stop;++i) {
num hi = vector_num_read(h,i-start);
if (!term_is(hi,vector_num_read(&thmdata,i))) return 0;
}
return 1;
}
// extract hypotheses from thmdata[start:stop]
// skipping terms alpha-equivalent to skip if skip>=0
static void hypotheses_extract(vector_num *result,num start,num stop,num skip)
{
vector_num_empty(result);
// XXX speed: can usually speed up alpha test with first-pass equality test
// since we know thmdata[start:stop] are alpha-inequivalent
for (num i = start;i < stop;++i) {
num h = vector_num_read(&thmdata,i);
if (skip >= 0 && term_alphaequivalent(h,skip)) continue;
vector_num_append(result,h);
}
}
// same as hypotheses_extract but taking theorem as input
static void hypotheses_extract_th(vector_num *result,num th,num skip)
{
num start = vector_num_read(&thm2datapos,th);
num stop = vector_num_read(&thm2datapos,th+1);
hypotheses_extract(result,start,stop,skip);
}
// merge modulo alpha-equivalence
static void hypotheses_merge(vector_num *result,vector_num *h,vector_num *H)
{
vector_num_empty(result);
num hlen = vector_num_len(h);
num Hlen = vector_num_len(H);
num i = 0;
num I = 0;
for (;;) {
if (i == hlen) {
while (I < Hlen) vector_num_append(result,vector_num_read(H,I++));
return;
}
if (I == Hlen) {
while (i < hlen) vector_num_append(result,vector_num_read(h,i++));
return;
}
num hi = vector_num_read(h,i);
num HI = vector_num_read(H,I);
int order = term_alphaorder_cmp(hi,HI);
vector_num_append(result,(order <= 0 ? hi : HI));
i += (order <= 0);
I += (order >= 0);
}
}
static int hypotheses_aremerged(num start,num stop,num th,num TH,num thskip,num THskip)
{
static vector_num h;
static vector_num H;
static vector_num result;
hypotheses_extract_th(&h,th,thskip);
hypotheses_extract_th(&H,TH,THskip);
hypotheses_merge(&result,&h,&H);
return hypotheses_match(start,stop,&result);
}
static int hypotheses_arecopied(num start,num stop,num th)
{
static vector_num h;
hypotheses_extract_th(&h,th,-1);
return hypotheses_match(start,stop,&h);
}
// ===== type tags
static void tag_a(const char *s) // Tyvar
{
noteol(s);
vector_num_append(&typedata,s-in_buf);
vector_num_append(&type2hash,name_hash(s));
}
static void tag_b(const char *s) // Tyapp
{
num arity = subid(&s,-1);
num hash = hash_mix(7152249725402530200+arity);
for (num i = 0;i < arity;++i) {
num subtype = subid(&s,typeid);
vector_num_append(&typedata,subtype);
hash = hash_mix(hash+vector_num_read(&type2hash,subtype));
}
noteol(s);
hash = hash_mix(hash+name_hash(s));
vector_num_append(&typedata,s-in_buf);
vector_num_append(&type2hash,hash);
num expectedarity = app2arity(s);
if (expectedarity < 0) reject("undefined app name");
if (expectedarity != arity) reject("wrong number of app arguments");
}
// ===== term tags
static void tag_c(const char *s) // Const
{
num type = subid(&s,typeid);
noteol(s);
num hash = 1260898140613410013LL;
hash = hash_mix(hash+vector_num_read(&type2hash,type));
hash = hash_mix(hash+name_hash(s));
vector_num_append(&termdata,type);
vector_num_append(&termdata,s-in_buf);
vector_num_append(&term2freelen,0);
vector_num_append(&term2freepos,0);
vector_num_append(&term2hash,hash);
if (name_is(s,"=\n")) {
if (!type_is_equality(type)) reject("non-equality type");
return;
}
if (name_is(s,"@\n")) {
if (!type_is_choice(type)) reject("non-choice type");
return;
}
num registeredtype = const2type(s);
if (registeredtype >= 0) {
if (!type_some_subst_is(registeredtype,type)) reject("const type mismatch");
return;
}
// XXX simplicity: merge the absname/repname code segments
num registeredtypedef = absname2typedef(s);
if (registeredtypedef >= 0) {
num from,to;
if (!type_is_fun(type,&from,&to)) reject("non-function type with typedef name");
char split = vector_char_read(&typedef2split,registeredtypedef);
num reptype = vector_num_read(&typedef2reptype,registeredtypedef);
num numtyvars = vector_num_read(&typedef2numtyvars,registeredtypedef);
const char *abstypename = in_buf+vector_num_read(&typedef2abstypename,registeredtypedef);
const char *absname = in_buf+vector_num_read(&typedef2absname,registeredtypedef);
const char *tyvarnames = in_buf+vector_num_read(&typedef2tyvarnames,registeredtypedef);
know(name_is_withsplit(s,absname,split));
// expecting: absname:reptype->abstype
// modulo substitution for tyvars
if (!type_is_appnamed(to,abstypename,split)) reject("typedef absname-abstype mismatch");
if (!type_tyvarnames_subst_is(reptype,from,to,numtyvars,tyvarnames,split)) reject("typedef absname-reptype mismatch");
return;
}
registeredtypedef = repname2typedef(s);
if (registeredtypedef >= 0) {
num from,to;
if (!type_is_fun(type,&from,&to)) reject("non-function type with typedef name");
char split = vector_char_read(&typedef2split,registeredtypedef);
num reptype = vector_num_read(&typedef2reptype,registeredtypedef);
num numtyvars = vector_num_read(&typedef2numtyvars,registeredtypedef);
const char *abstypename = in_buf+vector_num_read(&typedef2abstypename,registeredtypedef);
const char *repname = in_buf+vector_num_read(&typedef2repname,registeredtypedef);
const char *tyvarnames = in_buf+vector_num_read(&typedef2tyvarnames,registeredtypedef);
know(name_is_withsplit(s,repname,split));
// expecting: repname:abstype->reptype
// modulo substitution for tyvars
if (!type_is_appnamed(from,abstypename,split)) reject("typedef repname-abstype mismatch");
if (!type_tyvarnames_subst_is(reptype,to,from,numtyvars,tyvarnames,split)) reject("typedef repname-reptype mismatch");
return;
}
reject("undefined constant");
}
static void tag_d(const char *s) // Var
{
num type = subid(&s,typeid);
noteol(s);
num hash = 5725918395758143510LL;
hash = hash_mix(hash+vector_num_read(&type2hash,type));
hash = hash_mix(hash+name_hash(s));
vector_num_append(&termdata,type);
vector_num_append(&termdata,s-in_buf);
vector_num_append(&term2freepos,vector_num_len(&termfrees));
vector_num_append(&termfrees,termid);
vector_num_append(&term2freelen,1);
vector_num_append(&term2hash,hash);
}
static void tag_e(const char *s) // Comb
{
num outputtype = subid(&s,typeid);
num funterm = subid(&s,termid);
num pointterm = subid(&s,termid);
eol(s);
vector_num_append(&termdata,outputtype);
vector_num_append(&termdata,funterm);
vector_num_append(&termdata,pointterm);
num hash = 3679975592003923231LL;
hash = hash_mix(hash+vector_num_read(&term2hash,funterm));
hash = hash_mix(hash+vector_num_read(&term2hash,pointterm));
if (!type_is_funfromto(term2type(funterm),term2type(pointterm),outputtype)) reject("combination mismatch");
num pos,len;
if (!term_has_freevars(funterm)) {
// closed function; common case
pos = vector_num_read(&term2freepos,pointterm);
len = vector_num_read(&term2freelen,pointterm);
} else {
static vector_num frees;
term_freevars(&frees,funterm);
term_freevars_append(&frees,pointterm);
pos = vector_num_len(&termfrees);
len = vector_num_len(&frees);
for (num i = 0;i < len;++i)
vector_num_append(&termfrees,vector_num_read(&frees,i));
}
vector_num_append(&term2freepos,pos);
vector_num_append(&term2freelen,len);
vector_num_append(&term2hash,hash);
}
static void tag_f(const char *s) // Abs
{
num outputtype = subid(&s,typeid);
num fromterm = subid(&s,termid);
num toterm = subid(&s,termid);
eol(s);
vector_num_append(&termdata,outputtype);
vector_num_append(&termdata,fromterm);
vector_num_append(&termdata,toterm);
num hash = 4019844455831122549LL;
hash = hash_mix(hash+vector_num_read(&term2hash,fromterm));
hash = hash_mix(hash+vector_num_read(&term2hash,toterm));
if (!term_is_var(fromterm,0,0)) reject("non-variable lambda");
if (!type_is_funfromto(outputtype,term2type(fromterm),term2type(toterm))) reject("lambda mismatch");
num pos = vector_num_read(&term2freepos,toterm);
num len = vector_num_read(&term2freelen,toterm);
for (num i = 0;i < len;++i) {
num v = vector_num_read(&termfrees,pos+i);
if (term_is(fromterm,v)) {
num newpos = vector_num_len(&termfrees);
num newlen = 0;
for (num j = 0;j < len;++j)
if (j != i) {
vector_num_append(&termfrees,vector_num_read(&termfrees,pos+j));
++newlen;
}
pos = newpos;
len = newlen;
break;
}
}
vector_num_append(&term2freepos,pos);
vector_num_append(&term2freelen,len);
vector_num_append(&term2hash,hash);
}
// ===== A: TYPE_DEFINITION
// (plus D for followup)
static vector_num tag_A_tyvars;
static void tag_A(const char *s)
{
num reptype = subid(&s,typeid);
num repprop = subid(&s,termid);
num th = subid(&s,thmid);
noteol(s);
int state = 0;
char split = 0;
const char *abstypename = 0;
const char *absname = 0;
const char *repname = 0;
const char *tyvarnames = 0;
for (;*s != '\n';++s)
switch(state) {
case 0: split = *s; ++state; break;
case 1: abstypename = s; ++state; // fall through
case 2: if (*s == split) ++state; break;
case 3: absname = s; ++state; // fall through
case 4: if (*s == split) ++state; break;
case 5: repname = s; ++state; // fall through
case 6: if (*s == split) { ++state; tyvarnames = s+1; }
}
if (state != 7) reject("TYPE_DEFINITION: misformatted");
if (split != ']') reject("TYPE_DEFINITION: refusing non-bracket");
know(abstypename);
know(absname);
know(repname);
know(tyvarnames);
num numtyvarnames = 0;
for (s = tyvarnames;*s != '\n';++s)
if (*s == split)
++numtyvarnames;
if (s[-1] != split) reject("TYPE_DEFINITION: partial final component");
num thstart = vector_num_read(&thm2datapos,th);
num thstop = vector_num_read(&thm2datapos,th+1);
if (thstart != thstop) reject("TYPE_DEFINITION: input theorem has hypotheses");
num thconclusion = vector_num_read(&thm2conclusion,th);
num reppropcheck;
num reppoint;
if (!term_is_comb(thconclusion,0,&reppropcheck,&reppoint)) reject("TYPE_DEFINITION: input theorem is not a combination");
if (!term_is(repprop,reppropcheck)) reject("TYPE_DEFINITION: repprop mismatch");
if (!type_is(reptype,term2type(reppoint))) reject("TYPE_DEFINITION: reptype mismatch");
if (term_has_freevars(repprop)) reject("TYPE_DEFINITION: free variables in repprop");
term_tyvars(&tag_A_tyvars,repprop);
if (vector_num_len(&tag_A_tyvars) != numtyvarnames) reject("TYPE_DEFINITION: wrong number of tyvars");
const char *tyvarname = tyvarnames;
num prevpos = -1;
for (s = tyvarnames;*s != '\n';++s)
if (*s == split) {
num pos;
for (pos = 0;pos < numtyvarnames;++pos) {
num tyvar = vector_num_read(&tag_A_tyvars,pos);
if (type_is_tyvarnamed(tyvar,tyvarname,split)) break;
}
if (pos >= numtyvarnames) reject("TYPE_DEFINITION: tyvar not in repprop");
if (prevpos >= 0) {
num tyvar = vector_num_read(&tag_A_tyvars,pos);
num prevtyvar = vector_num_read(&tag_A_tyvars,prevpos);
if (type_cmp(tyvar,prevtyvar) != 1) reject("TYPE_DEFINITION: tyvars not in increasing order");
}
prevpos = pos;
tyvarname = s+1;
}
num typedefpos = vector_num_len(&typedef2reptype);
app2arity_add(abstypename,split,numtyvarnames);
absname2typedef_add(absname,split,typedefpos);
repname2typedef_add(repname,split,typedefpos);
vector_num_append(&typedef2reptype,reptype);
vector_num_append(&typedef2repprop,repprop);
vector_num_append(&typedef2numtyvars,numtyvarnames);
vector_num_append(&typedef2abstypename,abstypename-in_buf);
vector_num_append(&typedef2absname,absname-in_buf);
vector_num_append(&typedef2repname,repname-in_buf);
vector_num_append(&typedef2tyvarnames,tyvarnames-in_buf);
vector_char_append(&typedef2split,split);
vector_num_append(&typedef2infid,infid);
}
static void finish_A(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("TYPE_DEFINITION: hypotheses");
num typedefpos = vector_num_len(&typedef2reptype)-1;
know(typedefpos >= 0);
know(typedefpos == vector_num_len(&typedef2numtyvars)-1);
know(typedefpos == vector_num_len(&typedef2abstypename)-1);
know(typedefpos == vector_num_len(&typedef2absname)-1);
know(typedefpos == vector_num_len(&typedef2repname)-1);
know(typedefpos == vector_num_len(&typedef2tyvarnames)-1);
know(typedefpos == vector_char_len(&typedef2split)-1);
num reptype = vector_num_read(&typedef2reptype,typedefpos);
num numtyvars = vector_num_read(&typedef2numtyvars,typedefpos);
const char *abstypename = in_buf+vector_num_read(&typedef2abstypename,typedefpos);
const char *absname = in_buf+vector_num_read(&typedef2absname,typedefpos);
const char *repname = in_buf+vector_num_read(&typedef2repname,typedefpos);
const char *tyvarnames = in_buf+vector_num_read(&typedef2tyvarnames,typedefpos);
char split = vector_char_read(&typedef2split,typedefpos);
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("TYPE_DEFINITION: conclusion has operation mismatch");
num abstype;
const char *name;
if (!term_is_var(right,&abstype,&name)) reject("TYPE_DEFINITION: right side of conclusion is not variable");
if (!name_is(name,"a\n")) reject("TYPE_DEFINITION: refusing non-a variable name in conclusion");
// now have: left = a; a:abstype
num funterm;
num pointterm;
if (!term_is_comb(left,0,&funterm,&pointterm)) reject("TYPE_DEFINITION: left side of conclusion is not combination");
// now have: funterm(pointterm) = a; a:abstype
num pointfunterm;
num pointpointterm;
if (!term_is_comb(pointterm,0,&pointfunterm,&pointpointterm)) reject("TYPE_DEFINITION: left side of conclusion is not evaluation at combination");
// now have: funterm(pointfunterm(pointpointterm)) = a; a:abstype
if (!term_is(pointpointterm,right)) reject("TYPE_DEFINITION: variable mismatch");
// now have: funterm(pointfunterm(a)) = a; a:abstype
num funtype;
const char *funname;
if (!term_is_const(funterm,&funtype,&funname)) reject("TYPE_DEFINITION: outer function is not constant");
if (!name_is_withsplit(funname,absname,split)) reject("TYPE_DEFINITION: absname mismatch");
// now have: absname(pointfunterm(a)) = a; absname:funtype; a:abstype
num pointfuntype;
const char *pointfunname;
if (!term_is_const(pointfunterm,&pointfuntype,&pointfunname)) reject("TYPE_DEFINITION: inner function is not constant");
if (!name_is_withsplit(pointfunname,repname,split)) reject("TYPE_DEFINITION: repname mismatch");
// now have: absname(repname(a)) = a; absname:funtype; repname:pointfuntype; a:abstype
know(type_is_funfromto(funtype,reptype,abstype));
know(type_is_funfromto(pointfuntype,abstype,reptype));
// now have: absname(repname(a)) = a; absname:reptype->abstype; repname:abstype->reptype; a:abstype
if (!type_is_abstype(abstype,abstypename,tyvarnames,numtyvars,split)) reject("TYPE_DEFINITION: abstype mismatch");
}
static num tag_D_pos;
static void tag_D(const char *s)
{
tag_D_pos = subid(&s,typedefid);
eol(s);
}
static void finish_D(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("TYPE_DEFINITION followup: hypotheses");
num typedefpos = tag_D_pos;
num reptype = vector_num_read(&typedef2reptype,typedefpos);
num repprop = vector_num_read(&typedef2repprop,typedefpos);
num numtyvars = vector_num_read(&typedef2numtyvars,typedefpos);
const char *abstypename = in_buf+vector_num_read(&typedef2abstypename,typedefpos);
const char *absname = in_buf+vector_num_read(&typedef2absname,typedefpos);
const char *repname = in_buf+vector_num_read(&typedef2repname,typedefpos);
const char *tyvarnames = in_buf+vector_num_read(&typedef2tyvarnames,typedefpos);
char split = vector_char_read(&typedef2split,typedefpos);
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("TYPE_DEFINITION followup: conclusion has operation mismatch");
num funterm;
num pointterm;
if (!term_is_comb(left,0,&funterm,&pointterm)) reject("TYPE_DEFINITION followup: left side of conclusion is not combination");
// now have: funterm(pointterm) = right
if (!term_is(funterm,repprop)) reject("TYPE_DEFINITION followup: left side of conclusion is not repprop combination");
// now have: repterm(pointterm) = right
num reptypecheck;
const char *name;
if (!term_is_var(pointterm,&reptypecheck,&name)) reject("TYPE_DEFINITION followup: left side of conclusion is not repprop of variable");
if (!name_is(name,"r\n")) reject("TYPE_DEFINITION followup: refusing non-r variable name in conclusion");
if (!type_is(reptypecheck,reptype)) reject("TYPE_DEFINITION followup: reptype mismatch");
// now have: repterm(r) = right; r:reptype
num rightleft,rightright;
if (!term_is_equality(right,&rightleft,&rightright)) reject("TYPE_DEFINITION followup: second-level operation mismatch");
// now have: repterm(r) = (rightleft = rightright); r:reptype
if (!term_is(rightright,pointterm)) reject("TYPE_DEFINITION followup: variable mismatch");
// now have: repterm(r) = (rightleft = r); r:reptype
num rightfun,rightpoint;
if (!term_is_comb(rightleft,0,&rightfun,&rightpoint)) reject("TYPE_DEFINITION followup: second level is not combination");
// now have: repterm(r) = (rightfun(rightpoint) = r); r:reptype
num rightpointfun,rightpointpoint;
if (!term_is_comb(rightpoint,0,&rightpointfun,&rightpointpoint)) reject("TYPE_DEFINITION followup: third level is not combination");
// now have: repterm(r) = (rightfun(rightpointfun(rightpointpoint)) = r); r:reptype
if (!term_is(rightpointpoint,pointterm)) reject("TYPE_DEFINITION followup: bottom-level variable mismatch");
// now have: repterm(r) = (rightfun(rightpointfun(r)) = r); r:reptype
num rightfuntype;
const char *rightfunname;
if (!term_is_const(rightfun,&rightfuntype,&rightfunname)) reject("TYPE_DEFINITION followup: outer function is not constant");
if (!name_is_withsplit(rightfunname,repname,split)) reject("TYPE_DEFINITION followup: repname mismatch");
// now have: repterm(r) = (repname(rightpointfun(r)) = r); r:reptype; repname:rightfuntype
num rightpointfuntype;
const char *rightpointfunname;
if (!term_is_const(rightpointfun,&rightpointfuntype,&rightpointfunname)) reject("TYPE_DEFINITION followup: inner function is not constant");
if (!name_is_withsplit(rightpointfunname,absname,split)) reject("TYPE_DEFINITION followup: absname mismatch");
// now have: repterm(r) = (repname(absname(r)) = r); r:reptype; repname:rightfuntype; absname:rightpointfuntype
num abstype,rightfunto;
know(type_is_fun(rightfuntype,&abstype,&rightfunto));
know(type_is(rightfunto,reptype));
know(type_is_funfromto(rightpointfuntype,reptype,abstype));
// now have: repterm(r) = (repname(absname(r)) = r); r:reptype; repname:abstype->reptype; absname:reptype->abstype
if (!type_is_abstype(abstype,abstypename,tyvarnames,numtyvars,split)) reject("TYPE_DEFINITION followup: abstype mismatch");
}
// ==== B: BETA
// input: (x|->y)(x)
// output: (x|->y)(x) = y
static num tag_B_xyx;
static num tag_B_y;
static void tag_B(const char *s)
{
num term = subid(&s,termid);
eol(s);
num funterm,pointterm;
if (!term_is_comb(term,0,&funterm,&pointterm)) reject("BETA: operation mismatch");
// now have: funterm(pointterm)
if (!term_is_var(pointterm,0,0)) reject("BETA: combination is not with variable");
// redundant given abs restrictions below, but keep for clarity
num fromterm,toterm;
if (!term_is_abs(funterm,0,&fromterm,&toterm)) reject("BETA: combination is not of abstraction");
// now have: (fromterm|->toterm)(pointterm)
if (!term_is(fromterm,pointterm)) reject("BETA: combination is not of same variable");
// now have: (fromterm|->toterm)(fromterm)
tag_B_xyx = term;
tag_B_y = toterm;
}
static void finish_B(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("BETA: hypotheses");
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("BETA: operation mismatch");
if (!term_is(left,tag_B_xyx)) reject("BETA: left-side mismatch");
if (!term_is(right,tag_B_y)) reject("BETA: right-side mismatch");
}
// ===== C: DEFINITION
static vector_num tag_C_tyvars;
static vector_num tag_C_tyvarsright;
static const char *tag_C_name;
static num tag_C_type;
static num tag_C_defn;
static void tag_C(const char *s)
{
num term = subid(&s,termid);
noteol(s);
num left,right;
if (!term_is_equality(term,&left,&right)) reject("DEFINITION: operation mismatch");
num type;
const char *name;
if (!term_is_var(left,&type,&name)) reject("DEFINITION: not a variable");
if (!name_is(name,s)) reject("DEFINITION: name mismatch");
if (term_has_freevars(right)) reject("DEFINITION: free variables on right side");
type_vars(&tag_C_tyvars,type);
term_tyvars(&tag_C_tyvarsright,right);
for (num i = 0;i < vector_num_len(&tag_C_tyvarsright);++i) {
num ty2 = vector_num_read(&tag_C_tyvarsright,i);
int found = 0;
for (num j = 0;j < vector_num_len(&tag_C_tyvars);++j) {
num ty = vector_num_read(&tag_C_tyvars,j);
if (type_is(ty,ty2)) {
found = 1;
break;
}
}
if (!found) reject("DEFINITION: type variables on right side not included in constant");
}
const2type_add(name,'\n',type);
tag_C_name = name;
tag_C_type = type;
tag_C_defn = right;
}
static void finish_C(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("DEFINITION: hypotheses");
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("DEFINITION: operation mismatch");
// now have: left = right
num type;
const char *name;
if (!term_is_const(left,&type,&name)) reject("DEFINITION: left side is not constant");
if (!name_is(name,tag_C_name)) reject("DEFINITION: name mismatch");
if (!type_is(type,tag_C_type)) reject("DEFINITION: type mismatch");
// now have: Const(tag_C_name,tag_C_type) = right
if (!term_is(right,tag_C_defn)) reject("DEFINITION: right-side mismatch");
}
// ===== E: MK_COMB
// input: theorem thleft = thright
// input: theorem THleft = THright
// output: theorem thleft(THleft) = thright(THright)
static num tag_E_th;
static num tag_E_thleft;
static num tag_E_thright;
static num tag_E_TH;
static num tag_E_THleft;
static num tag_E_THright;
static void tag_E(const char *s)
{
num th = subid(&s,thmid);
num TH = subid(&s,thmid);
eol(s);
num thconclusion = vector_num_read(&thm2conclusion,th);
num THconclusion = vector_num_read(&thm2conclusion,TH);
num thleft,thright;
if (!term_is_equality(thconclusion,&thleft,&thright)) reject("MK_COMB: first theorem is not equality");
num THleft,THright;
if (!term_is_equality(THconclusion,&THleft,&THright)) reject("MK_COMB: second theorem is not equality");
num from;
if (!type_is_fun(term2type(thleft),&from,0)) reject("first MK_COMB: type is not function");
if (!type_is(term2type(THleft),from)) reject("MK_COMB: combination mismatch");
tag_E_th = th;
tag_E_thleft = thleft;
tag_E_thright = thright;
tag_E_TH = TH;
tag_E_THleft = THleft;
tag_E_THright = THright;
}
static void finish_E(num hypstart,num hypstop,num conclusion)
{
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("MK_COMB: operation mismatch");
// now have: left = right
num leftfun,leftpoint;
if (!term_is_comb(left,0,&leftfun,&leftpoint)) reject("MK_COMB: left-side operation mismatch");
// now have: leftfun(leftpoint) = right
num rightfun,rightpoint;
if (!term_is_comb(right,0,&rightfun,&rightpoint)) reject("MK_COMB: right-side operation mismatch");
// now have: leftfun(leftpoint) = rightfun(rightpoint)
if (!term_is(leftfun,tag_E_thleft)) reject("MK_COMB: left-outer mismatch");
if (!term_is(leftpoint,tag_E_THleft)) reject("MK_COMB: left-inner mismatch");
if (!term_is(rightfun,tag_E_thright)) reject("MK_COMB: right-outer mismatch");
if (!term_is(rightpoint,tag_E_THright)) reject("MK_COMB: right-inner mismatch");
if (!hypotheses_aremerged(hypstart,hypstop,tag_E_th,tag_E_TH,-1,-1)) reject("MK_COMB: hypothesis mismatch");
}
// ===== F: ABS
// input: variable v
// input: theorem l = r
// output: (v|->l) = (v|->r)
static num tag_F_var;
static num tag_F_th;
static num tag_F_left;
static num tag_F_right;
static vector_num tag_F_free;
static void tag_F(const char *s)
{
num var = subid(&s,termid);
num th = subid(&s,thmid);
eol(s);
if (!term_is_var(var,0,0)) reject("ABS: first input is not a variable");
num thconclusion = vector_num_read(&thm2conclusion,th);
num left,right;
if (!term_is_equality(thconclusion,&left,&right)) reject("ABS: second input is not an equality");
num thstart = vector_num_read(&thm2datapos,th);
num thstop = vector_num_read(&thm2datapos,th+1);
for (num i = thstart;i < thstop;++i) {
num h = vector_num_read(&thmdata,i);
term_freevars(&tag_F_free,h);
for (num j = 0;j < vector_num_len(&tag_F_free);++j)
if (term_is(var,vector_num_read(&tag_F_free,j)))
reject("ABS variable is free in hypothesis");
}
tag_F_var = var;
tag_F_th = th;
tag_F_left = left;
tag_F_right = right;
}
static void finish_F(num hypstart,num hypstop,num conclusion)
{
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("ABS: operation mismatch");
// now have: left = right
num leftv,leftt;
if (!term_is_abs(left,0,&leftv,&leftt)) reject("ABS: left-side operation mismatch");
// now have: (leftv|->leftt) = right
num rightv,rightt;
if (!term_is_abs(right,0,&rightv,&rightt)) reject("ABS: right-side operation mismatch");
// now have: (leftv|->leftt) = (rightv|->rightt)
if (!term_is(leftv,tag_F_var)) reject("ABS: left-side variable mismatch");
if (!term_is(rightv,tag_F_var)) reject("ABS: right-side variable mismatch");
if (!term_is(leftt,tag_F_left)) reject("ABS: left-side output mismatch");
if (!term_is(rightt,tag_F_right)) reject("ABS: right-side output mismatch");
// now have: (tag_F_var|->tag_F_left) = (tag_F_var|->tag_F_right)
if (!hypotheses_arecopied(hypstart,hypstop,tag_F_th)) reject("ABS: hypothesis mismatch");
}
// ===== P: EQ_MP
// input: theorem L = R
// input: theorem L (modulo alpha-equivalence)
// output: theorem R
static num tag_P_right;
static num tag_P_th;
static num tag_P_TH;
static void tag_P(const char *s)
{
num th = subid(&s,thmid);
num TH = subid(&s,thmid);
eol(s);
num thconclusion = vector_num_read(&thm2conclusion,th);
num THconclusion = vector_num_read(&thm2conclusion,TH);
num left,right;
if (!term_is_equality(thconclusion,&left,&right)) reject("EQ_MP: operation mismatch");
if (!term_alphaequivalent(left,THconclusion)) reject("EQ_MP: left-side mismatch");
tag_P_th = th;
tag_P_TH = TH;
tag_P_right = right;
}
static void finish_P(num hypstart,num hypstop,num conclusion)
{
if (!term_is(conclusion,tag_P_right)) reject("EQ_MP: conclusion mismatch");
if (!hypotheses_aremerged(hypstart,hypstop,tag_P_th,tag_P_TH,-1,-1)) reject("EQ_MP: hypothesis mismatch");
}
// ===== R: REFL
static num tag_R_term;
static void tag_R(const char *s)
{
tag_R_term = subid(&s,termid);
eol(s);
}
static void finish_R(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("REFL: hypotheses");
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("REFL: conclusion mismatch");
if (!term_is(left,tag_R_term)) reject("REFL: left-side mismatch");
if (!term_is(right,tag_R_term)) reject("REFL: right-side mismatch");
}
// ===== S: INST
static num tag_S_th;
static vector_num tag_S_from;
static vector_num tag_S_to;
static vector_num tag_S_primed;
static void tag_S(const char *s)
{
tag_S_th = subid(&s,thmid);
vector_num_empty(&tag_S_from);
vector_num_empty(&tag_S_to);
vector_num_empty(&tag_S_primed);
for (;;) {
if (*s == '\n') break;
num from = subid(&s,termid);
if (*s == '\n') reject("INST: odd number of term arguments");
num to = subid(&s,termid);
num fromtype;
if (!term_is_var(from,&fromtype,0)) reject("INST: substituting for non-variable (note: S th from to, not S th to from)");
if (!type_is(fromtype,term2type(to))) reject("INST: type mismatch");
for (num i = 0;i < vector_num_len(&tag_S_from);++i)
if (term_is(from,vector_num_read(&tag_S_from,i)))
goto skip;
vector_num_append(&tag_S_from,from);
vector_num_append(&tag_S_to,to);
vector_num_append(&tag_S_primed,0);
skip: ;
}
}
static num finish_S_hyptotal;
static void finish_S(num hypstart,num hypstop,num conclusion)
{
finish_S_hyptotal += hypstop-hypstart;
num th = tag_S_th;
num thconclusion = vector_num_read(&thm2conclusion,th);
if (!term_subst_is(thconclusion,&tag_S_from,&tag_S_to,&tag_S_primed,conclusion))
reject("INST: conclusion mismatch");
num thstart = vector_num_read(&thm2datapos,th);
num thstop = vector_num_read(&thm2datapos,th+1);
static vector_num hypused;
vector_num_empty(&hypused);
// XXX speed: maybe better to compute the map and sort
// XXX completeness: also need this in case of alpha squeeze
for (num i = thstart;i < thstop;++i) {
num oldh = vector_num_read(&thmdata,i);
int found = 0;
for (num j = hypstart;j < hypstop;++j) {
num newh = vector_num_read(&thmdata,j);
if (term_subst_is(oldh,&tag_S_from,&tag_S_to,&tag_S_primed,newh)) {
vector_num_write(&hypused,j-hypstart,1);
found = 1;
break;
}
}
if (!found) reject("INST: hypothesis mismatch (maybe alpha squeeze?)");
}
if (vector_num_len(&hypused) != hypstop-hypstart) reject("INST: extra hypotheses");
for (num j = hypstart;j < hypstop;++j)
if (vector_num_read(&hypused,j-hypstart) != 1) reject("INST: extra hypotheses");
}
// ===== T: INST_TYPE
static num tag_T_th;
static vector_num tag_T_from;
static vector_num tag_T_to;
static void tag_T(const char *s)
{
tag_T_th = subid(&s,thmid);
vector_num_empty(&tag_T_from);
vector_num_empty(&tag_T_to);
for (;;) {
if (*s == '\n') break;
num from = subid(&s,typeid);
if (*s == '\n') reject("INST_TYPE: odd number of type arguments");
num to = subid(&s,typeid);
if (type2tag(from) != 'a') reject("INST_TYPE: substituting for non-tyvar (note: T th from to, not T th to from)");
for (num i = 0;i < vector_num_len(&tag_T_from);++i)
if (type_is(from,vector_num_read(&tag_T_from,i)))
goto skip;
vector_num_append(&tag_T_from,from);
vector_num_append(&tag_T_to,to);
skip: ;
}
}
num finish_T_hyptotal;
static void finish_T(num hypstart,num hypstop,num conclusion)
{
finish_T_hyptotal += hypstop-hypstart;
num th = tag_T_th;
num thconclusion = vector_num_read(&thm2conclusion,th);
if (!term_tyvar_subst_is(thconclusion,&tag_T_from,&tag_T_to,conclusion))
reject("INST_TYPE: conclusion mismatch");
num thstart = vector_num_read(&thm2datapos,th);
num thstop = vector_num_read(&thm2datapos,th+1);
static vector_num hypused;
vector_num_empty(&hypused);
// XXX speed: maybe better to compute the map and sort
// XXX completeness: also need this in case of alpha squeeze
for (num i = thstart;i < thstop;++i) {
num oldh = vector_num_read(&thmdata,i);
int found = 0;
for (num j = hypstart;j < hypstop;++j) {
num newh = vector_num_read(&thmdata,j);
if (term_tyvar_subst_is(oldh,&tag_T_from,&tag_T_to,newh)) {
vector_num_write(&hypused,j-hypstart,1);
found = 1;
break;
}
}
if (!found) reject("INST_TYPE: hypothesis mismatch (maybe alpha squeeze?)");
}
if (vector_num_len(&hypused) != hypstop-hypstart) reject("INST_TYPE: extra hypotheses");
for (num j = hypstart;j < hypstop;++j)
if (vector_num_read(&hypused,j-hypstart) != 1) reject("INST_TYPE: extra hypotheses");
}
// ===== U: ASSUME
static num tag_U_term;
static void tag_U(const char *s)
{
num term = subid(&s,termid);
eol(s);
if (!type_is_bool(term2type(term))) reject("ASSUME: non-bool input");
tag_U_term = term;
}
static void finish_U(num hypstart,num hypstop,num conclusion)
{
if (!term_is(conclusion,tag_U_term)) reject("ASSUME: wrong conclusion");
if (hypstop-hypstart != 1) reject("ASSUME: wrong number of hypotheses");
num h = vector_num_read(&thmdata,hypstart);
if (!term_is(h,tag_U_term)) reject("ASSUME: wrong hypothesis");
}
// ===== V: TRANS
static num tag_V_th;
static num tag_V_TH;
static num tag_V_left;
static num tag_V_right;
static void tag_V(const char *s)
{
num th = subid(&s,thmid);
num TH = subid(&s,thmid);
eol(s);
num thconclusion = vector_num_read(&thm2conclusion,th);
num THconclusion = vector_num_read(&thm2conclusion,TH);
num thleft,thright;
if (!term_is_equality(thconclusion,&thleft,&thright)) reject("TRANS: first operation mismatch");
num THleft,THright;
if (!term_is_equality(THconclusion,&THleft,&THright)) reject("TRANS: second operation mismatch");
if (!term_alphaequivalent(thright,THleft)) reject("TRANS: input mismatch");
tag_V_th = th;
tag_V_TH = TH;
tag_V_left = thleft;
tag_V_right = THright;
}
static void finish_V(num hypstart,num hypstop,num conclusion)
{
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("TRANS: output operation mismatch");
if (!term_is(left,tag_V_left)) reject("TRANS: output left-side mismatch");
if (!term_is(right,tag_V_right)) reject("TRANS: output right-side mismatch");
if (!hypotheses_aremerged(hypstart,hypstop,tag_V_th,tag_V_TH,-1,-1)) reject("TRANS: hypothesis mismatch");
}
// ===== X: AXIOM
static num tag_X_term;
static void tag_X(const char *s)
{
num term = subid(&s,termid);
eol(s);
if (!type_is_bool(term2type(term))) reject("AXIOM: non-bool input");
tag_X_term = term;
}
static void finish_X(num hypstart,num hypstop,num conclusion)
{
if (hypstop != hypstart) reject("AXIOM: hypotheses");
if (!term_is(conclusion,tag_X_term)) reject("AXIOM: wrong conclusion");
}
// ===== Z: DEDUCT_ANTISYM_RULE
// if q entails p and p entails q then p = q
static num tag_Z_th;
static num tag_Z_TH;
static void tag_Z(const char *s)
{
tag_Z_th = subid(&s,thmid);
tag_Z_TH = subid(&s,thmid);
eol(s);
}
static void finish_Z(num hypstart,num hypstop,num conclusion)
{
num th = tag_Z_th;
num TH = tag_Z_TH;
num left,right;
if (!term_is_equality(conclusion,&left,&right)) reject("DEDUCT_ANTISYM_RULE: operation mismatch");
if (!term_is(left,vector_num_read(&thm2conclusion,th))) reject("DEDUCT_ANTISYM_RULE: left-side mismatch");
if (!term_is(right,vector_num_read(&thm2conclusion,TH))) reject("DEDUCT_ANTISYM_RULE: right-side mismatch");
if (!hypotheses_aremerged(hypstart,hypstop,tag_Z_th,tag_Z_TH,right,left)) reject("DEDUCT_ANTISYM_RULE: hypothesis mismatch");
}
// ===== theorems
static void tag_t(const char *s)
{
know(vector_num_len(&thm2conclusion) == thmid);
num hypstart = vector_num_len(&thmdata);
num numargs = 0;
num z = 0;
while (*s != '\n') {
z = subid(&s,termid);
++numargs;
if (*s == '\n')
vector_num_append(&thm2conclusion,z);
else
vector_num_append(&thmdata,z);
}
if (numargs < 1) reject("theorem with no conclusion");
know(vector_num_len(&thm2conclusion) == thmid+1);
for (num i = hypstart;i+1 < vector_num_len(&thmdata);++i) {
num hthis = vector_num_read(&thmdata,i);
num hnext = vector_num_read(&thmdata,i+1);
if (term_alphaorder_cmp(hthis,hnext) != -1)
reject("refusing out-of-order hypotheses");
}
void (*finish)(num,num,num);
switch(lastinftag) {
case 'A': finish = finish_A; break;
case 'B': finish = finish_B; break;
case 'C': finish = finish_C; break;
case 'D': finish = finish_D; break;
case 'E': finish = finish_E; break;
case 'F': finish = finish_F; break;
case 'P': finish = finish_P; break;
case 'R': finish = finish_R; break;
case 'S': finish = finish_S; break;
case 'T': finish = finish_T; break;
case 'U': finish = finish_U; break;
case 'V': finish = finish_V; break;
case 'X': finish = finish_X; break;
case 'Z': finish = finish_Z; break;
default:
reject("theorem without preceding inference");
}
finish(hypstart,vector_num_len(&thmdata),z);
}
// ===== tags not relevant to logic; just check topology
static void tag_slash(const char *s)
{
subid(&s,thmid);
noteol(s);
}
static void tag_comma(const char *s)
{
subid(&s,thmid);
subid(&s,infid);
if (*s != '\n') subid(&s,ptid);
if (*s != '\n') subid(&s,ptid);
eol(s);
}
// ===== dispatcher
static void handleline(num start,num stop)
{
know(0 <= start);
know(start <= stop);
know(stop < in_bytes);
know(in_buf[stop] == '\n');
++curline;
if (stop <= start) {
fprintf(stderr,"blank line %lld prohibited\n",curline);
exit(100);
}
char tag = in_buf[start];
const char *s = in_buf+start+1;
switch(tag) {
case 'a': tag_a(s); break;
case 'b': tag_b(s); break;
case 'c': tag_c(s); break;
case 'd': tag_d(s); break;
case 'e': tag_e(s); break;
case 'f': tag_f(s); break;
case 't': tag_t(s); break;
case 'A': tag_A(s); break;
case 'B': tag_B(s); break;
case 'C': tag_C(s); break;
case 'D': tag_D(s); break;
case 'E': tag_E(s); break;
case 'F': tag_F(s); break;
case 'P': tag_P(s); break;
case 'R': tag_R(s); break;
case 'S': tag_S(s); break;
case 'T': tag_T(s); break;
case 'U': tag_U(s); break;
case 'V': tag_V(s); break;
case 'X': tag_X(s); break;
case 'Z': tag_Z(s); break;
case '/': tag_slash(s); break;
case ',': tag_comma(s); break;
case ';': break;
default:
fprintf(stderr,"line %lld: unrecognized tag %c\n",curline,tag);
exit(100);
}
switch(tag) {
case 'a': case 'b':
++typeid;
vector_char_append(&type2tag_vec,tag);
vector_num_append(&type2datapos,vector_num_len(&typedata));
break;
case 'c': case 'd': case 'e': case 'f':
++termid;
vector_char_append(&term2tag_vec,tag);
vector_num_append(&term2datapos,vector_num_len(&termdata));
break;
case 't':
++thmid;
vector_num_append(&thm2datapos,vector_num_len(&thmdata));
break;
case 'A': case 'B': case 'C': case 'D':
case 'E': case 'F': case 'P': case 'R':
case 'S': case 'T': case 'U': case 'V':
case 'X': case 'Z':
if (tag == 'A') ++typedefid;
++infid;
lastinftag = tag;
break;
case ',': case ';':
++ptid;
break;
case '/':
break;
default:
fprintf(stderr,"line %lld: unrecognized tag %c\n",curline,tag);
exit(100);
}
}
// ===== overall driver
static void verify(void)
{
if (in_bytes < 0) {
fprintf(stderr,"internal error: negative length\n");
exit(100);
}
if (in_bytes > CUTOFF) {
fprintf(stderr,"refusing to handle more than %lld bytes\n",CUTOFF);
exit(100);
}
vector_num_append(&type2datapos,0);
vector_num_append(&term2datapos,0);
vector_num_append(&thm2datapos,0);
num start = 0;
for (num i = 0;i < in_bytes;++i) {
char c = in_buf[i];
if (c == '\n') {
if (start) handleline(start,i);
start = i+1;
continue;
}
if (c < 32 || c > 126) {
fprintf(stderr,"byte %d is not printable ASCII\n",(int) c);
exit(100);
}
}
if (start != in_bytes) reject("subsequent partial final line");
}
int main()
{
in_init();
if (in_bytes < 11 || memcmp(in_buf,"HOLTrace 1\n",11)) {
fprintf(stderr,"input does not start with HOLTrace 1\n");
exit(100);
}
verify();
fprintf(stderr,"verified %lld bytes, %lld lines, %lld types, %lld terms, %lld infs, %lld thms, %lld typedefs\n"
,in_bytes,curline
,typeid
,termid
,infid
,thmid
,typedefid
);
return 0;
}