about summary refs log blame commit diff stats
path: root/modal/modal.c
blob: c7bf0f815f5d2e91218e37c3740ba13da7056758 (plain) (tree)



























































































































































































































































                                                                                            
/* 
	by https://wryl.tech/
 	from https://wiki.xxiivv.com/site/modal
 	cc modal.c -o modal
*/

#include <stdio.h>

typedef struct {
	int id;
	char *a, *b;
} Rule;

static int flip, rmin = 0xff, rmax = 0x00, cycles = 0x10000;
static Rule rules[0x1000], lambda, *rules_ = rules;
static char dict[0x8000], *dict_ = dict;
static char bank_a[0x4000], *src_ = bank_a;
static char bank_b[0x4000], *dst_ = bank_b;
static char *regs[0x100];

#define spacer(c) (c <= ' ' || c == '(' || c == ')')

static char *
walk(char *s)
{
	char c;
	int depth = 0;
	if(*s == '(') {
		while((c = *s++)) {
			if(c == '(') depth++;
			if(c == ')') --depth;
			if(!depth) return s;
		}
	}
	while((c = *s) && !spacer(c)) s++;
	return s;
}

static int
set_reg(int r, char *b)
{
	if(regs[r]) {
		char *a = regs[r], *aa = walk(a), *bb = walk(b);
		while(a < aa && b < bb)
			if(*a++ != *b++) return 0;
	} else {
		regs[r] = b;
		if(r < rmin) rmin = r;
		if(r > rmax) rmax = r;
	}
	return 1;
}

static void
put_reg(char r)
{
	char c, *s = regs[(int)r], *ss;
	if(!s) {
		*dst_++ = '?', *dst_++ = r;
		return;
	}
	ss = walk(s);
	if(r == '*') {
		int i, depth = 0;
		if(*s == '(') { /* special explode tuple */
			s++;
			while(s < ss) {
				while((c = *s) && !spacer(c))
					*dst_++ = c, s++;
				*dst_++ = ' ';
				*dst_++ = '(', s++, depth++;
			}
		} else { /* special explode token */
			while((c = *s++) && !spacer(c))
				*dst_++ = c, *dst_++ = ' ', *dst_++ = '(', depth++;
		}
		for(i = 0; i < depth; i++)
			*dst_++ = ')';
	} else if(r == '.') { /* special unpack */
		if(*s == '(') s++, --ss;
		while(s < ss) *dst_++ = *s++;
	} else if(r == '^') { /* special join */
		if(*s == '(') s++, --ss;
		while(s < ss && (c = *s++))
			if(!spacer(c)) *dst_++ = c;
	} else if(r == '~') { /* special stdin */
		while(fread(&c, 1, 1, stdin) && c >= ' ')
			*dst_++ = c;
	} else if(r == ':') { /* special stdout */
		if(*s == '(') s++, --ss;
		while(s < ss) {
			c = *s++;
			if(c == '\\') {
				switch(*s++) {
				case 't': putc(0x09, stdout); break;
				case 'n': putc(0x0a, stdout); break;
				case 's': putc(0x20, stdout); break;
				}
			} else
				putc(c, stdout);
		}
	} else
		while(s < ss) *dst_++ = *s++;
}

static char *
match_rule(Rule *r, char *p)
{
	int i;
	char c, last = 0, *a = r->a, *b = p;
	if(rmax) {
		for(i = rmin; i <= rmax; i++)
			regs[i] = 0;
		rmin = 0xff, rmax = 0x00;
	}
	while((c = *a)) {
		if(spacer(last) && c == '?') {
			if(!set_reg(*(++a), b)) return NULL;
			a++, b = walk(b);
			continue;
		}
		if(c != *b) return NULL;
		a++, b++, last = c;
	}
	c = *b;
	return spacer(c) ? b : NULL;
}

static int
commit_rule(Rule *r, char *s, int create)
{
	while((*dst_++ = *s++))
		;
	*dst_++ = 0;
	if((flip = !flip))
		src_ = bank_b, dst_ = bank_a;
	else
		src_ = bank_a, dst_ = bank_b;
	if(create)
		fprintf(stderr, "<> (%s) (%s)\n", r->a, r->b);
	else
		fprintf(stderr, "%02d %s\n", r->id, src_);
	return 1;
}

static int
write_rule(Rule *r, char last, char *res)
{
	char c, *b = r->b, *origin = dst_;
	while((c = *b++))
		if(spacer(last) && c == '?')
			put_reg(*b++);
		else
			*dst_++ = c, last = c;
	if(dst_ == origin) {
		while(*res == ' ') res++;
		if(*res == ')' && *(dst_ - 1) == ' ') dst_--;
	}
	return commit_rule(r, res, 0);
}

static char *
parse_frag(char *s)
{
	char c, *ss;
	while((c = *s) && c <= ' ') s++;
	if(*s != ')' && !(*s == '<' && s[1] == '>')) {
		ss = walk(s);
		if(*s == '(') {
			s++;
			while(s < ss - 1) *dict_++ = *s++;
			s++;
		} else
			while(s < ss) *dict_++ = *s++;
	}
	*dict_++ = 0;
	return s;
}

static char *
create_rule(Rule *r, int id, char *s)
{
	r->id = id, s += 2;
	r->a = dict_, s = parse_frag(s);
	r->b = dict_, s = parse_frag(s);
	return s;
}

static int
rewrite(void)
{
	char c, last = 0, *cap, *s = src_, *res;
	while((c = *s) && c <= ' ') s++;
	while((c = *s)) {
		if(spacer(last)) {
			Rule *r;
			if(*s == '<' && s[1] == '>') {
				r = rules_++;
				s = create_rule(r, rules_ - rules - 1, s);
				while((c = *s) && c <= ' ') s++;
				return commit_rule(r, s, 1);
			}
			if(*s == '?' && s[1] == '(') {
				r = &lambda, cap = walk(s + 1);
				create_rule(&lambda, -1, s), s = cap;
				while((c = *s) && c <= ' ') s++;
				if((res = match_rule(&lambda, s)) != NULL)
					return write_rule(&lambda, last, res);
			}
			for(r = rules; r < rules_; r++)
				if((res = match_rule(r, s)) != NULL)
					return write_rule(r, last, res);
		}
		*dst_++ = last = c;
		s++;
	}
	*dst_++ = 0;
	return 0;
}

int
main(int argc, char **argv)
{
	FILE *f;
	int i;
	char c, *w = bank_a;
	if(argc < 2)
		return !printf("usage: modal [-vqn] source.modal\n");
	for(i = 1; i < argc && *argv[i] == '-'; i++) {
		switch(argv[i][1]) {
		case 'v': /* version */ return !printf("Modal Interpreter, 18 Apr 2024.\n");
		case 'q': /* quiet */ fclose(stderr); break;
		case 'n': /* infinite */ cycles = 0xffffffff; break;
		}
	}
	if(!(f = fopen(argv[i], "r")))
		return !fprintf(stdout, "Invalid Modal file: %s.\n", argv[i]);
	while(fread(&c, 1, 1, f)) {
		c = c <= 0x20 ? 0x20 : c;
		if(w > bank_a) {
			if(c == ' ' && *(w - 1) == '(') continue;
			if(c == ')' && *(w - 1) == ' ') w--;
			if(c == ' ' && *(w - 1) == ' ') w--;
		}
		*w++ = c;
	}
	while(*(--w) <= ' ') *w = 0;
	fclose(f);
	while(rewrite())
		if(!cycles--) return !fprintf(stdout, "Modal rewrites exceeded.\n");
	return 0;
}