#! /bin/jsh
#
# Copyright 2013-2014 Yuichiro Moriguchi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
if [ -z "$2" ]
then
  putlicense_cstyle.sh > /output/${OUTPUT_FILENAME}.h
  file_nm="${FILENAME}"
  class_nm="${FILENAME}"
  type_nm='void *'
  null_nm='NULL'
else
  file_nm="$2"
  class_nm="$3"
  type_nm='int '
  null_nm='0'
fi

[ -z "$2" ] && cat >> /output/${OUTPUT_FILENAME}.h << EOF

#ifndef DEFINED_NINA_COMMON
#define DEFINED_NINA_COMMON

#include <stdio.h>
#include <setjmp.h>
#define ENDMARKER ((void *)1)
#define STATE (__o__->state)
#define INITIAL 0

typedef void **nina_stream_t;
struct nina_work_tag {
	int state;
	int unread;
	void *iseof;
	char buf[${BUFSIZE}];
	int gotoSymbol;
};

struct nina_dfa_tag {
	int state;
	int unread;
	void *iseof;
	char buf[${BUFSIZE}];
	int gotoSymbol;
	int (*read)(nina_stream_t);
};
#endif

#ifndef DEFINED_${FILENAME}
#define DEFINED_${FILENAME}

struct ${FILENAME}_tag {
	int state;
	int unread;
	void *iseof;
	char buf[${BUFSIZE}];
	int gotoSymbol;

	int (*lookingAt)(struct ${FILENAME}_tag *b, const char *s);
	int (*find)(struct ${FILENAME}_tag *b, const char *s);
	void *(*searchToken)(struct ${FILENAME}_tag *s, FILE *f);
	void *(*searchTokenStr)(struct ${FILENAME}_tag *b, char **t);
	void (*reset)(struct ${FILENAME}_tag *b);
};

#ifdef __cplusplus
extern "C" {
	int ${FILENAME}_matches(char *s);
	void ${FILENAME}_init(struct ${FILENAME}_tag *s);
}

class ${FILENAME} {
private:
	struct ${FILENAME}_tag ccls;
public:
	${FILENAME}() {
		${FILENAME}_init(&ccls);
	}

	int lookingAt(const char *s) {
		return ccls.lookingAt(&ccls, s);
	}

	int find(const char *s) {
		return ccls.find(&ccls, s);
	}

	void *searchToken(FILE *f) {
		return ccls.parse(&ccls, f);
	}

	void *searchTokenStr(char **s) {
		return ccls.parse_string(&ccls, s);
	}

	void reset() {
		ccls.parse_string(&ccls, s);
	}
};
#else
typedef struct ${FILENAME}_tag ${FILENAME};
extern int ${FILENAME}_matches(char *s);
extern void ${FILENAME}_init(struct ${FILENAME}_tag *s);
#endif
#endif
EOF

putlicense_cstyle.sh
[ -z "$2" ] && echo '/**'
[ -z "$2" ] && awk '{ printf(" * %s\n", $0) }' description
[ -z "$2" ] && echo ' */'
[ -z "$2" ] && cat << EOF
#include <stdio.h>
#include <string.h>
#include "${FILENAME}.h"

static int nina__read(struct nina_work_tag *b, FILE* f) {
	int c;

	if(b->unread > 0) {
		c = b->unread;
		b->unread = -1;
	} else if((c = fgetc(f)) < 0) {
		b->iseof = ENDMARKER;
	}
	return c;
}

static int nina__readstr(struct nina_work_tag *b, char** p) {
	int c;

	if(b->unread > 0) {
		c = b->unread;  b->unread = -1;
		return c;
	} else if(**p == 0) {
		b->iseof = ENDMARKER;
		return -1;
	} else {
		return *((*p)++);
	}
}
EOF

nina_template.nfa.c.sub2.sh "$1" "$file_nm" "$type_nm" "$class_nm" "$null_nm"

[ -z "$2" ] && cat << EOF

int ${FILENAME}_matches(char *s) {
	${FILENAME} b;
	char *n;

	memset(&b, 0, sizeof(b));
	for(n = s; *n != 0; n++) {
		if(!${FILENAME}__step(&b, *n)) {
			return 0;
		}
	}
	return ${FILENAME}__accepted(&b);
}

static int ${FILENAME}_lookingAt(struct ${FILENAME}_tag *b, const char *s) {
	const char *n;
	char *p, *q;

	p = b->buf;  q = NULL;
	memset(b->buf, 0, sizeof(b->buf));
	for(n = s; *n != 0; *(p++) = *(n++)) {
		if(${FILENAME}__accepted(b)) {
			q = p;
		}

		if(p - b->buf >= sizeof(b->buf) - 1 ||
				!${FILENAME}__step(b, *n)) {
			if(q != NULL) {
				*q = 0;
				return 1;
			} else {
				return 0;
			}
		}
	}

	if(${FILENAME}__accepted(b)) {
		*p = 0;
		return 1;
	} else if(q != NULL) {
		*q = 0;
		return 1;
	} else {
		return 0;
	}
}

static int ${FILENAME}_find(struct ${FILENAME}_tag *b, const char *s) {
	const char *n, *k;
	char *p, *q;
	int x = 0;

	p = b->buf;  q = NULL;
	memset(b->buf, 0, sizeof(b->buf));
	for(k = s; *k != 0; k++) {
		for(n = k; *n != 0; *(p++) = *(n++)) {
			if(${FILENAME}__accepted(b)) {
				q = p;
			}

			if(p - b->buf >= sizeof(b->buf) - 1) {
				if(q != NULL) {
					*q = 0;
					return 1;
				} else {
					return 0;
				}
			} else if(!${FILENAME}__step(b, *n)) {
				if(q != NULL) {
					*q = 0;
					return 1;
				} else {
					b->state = 0;
					p = b->buf;  *p = 0;
					break;
				}
			}
		}

		if(${FILENAME}__accepted(b)) {
			*p = 0;
			return 1;
		} else if(q != NULL) {
			*q = 0;
			return 1;
		}
	}
	return 0;
}

static void ${FILENAME}_reset(struct ${FILENAME}_tag *b) {
	b->state = 0;
	b->iseof = NULL;
	b->unread = -1;
}

static void *${FILENAME}_searchToken(struct ${FILENAME}_tag *b, FILE *t) {
	void *o = NULL;
	int f = 0, c;
	char *p;

	p = b->buf;
	memset(b->buf, 0, sizeof(b->buf));
	if(b->iseof != NULL)  return b->iseof;
	while((c = nina__read((struct nina_work_tag *)b, t)) >= 0) {
		*p++ = c;
		if(!${FILENAME}__step(b, c)) {
			if(f) {
				b->unread = c;
				b->state = 0;
				return o;
			} else {
				return NULL;
			}
		} else if((f = ${FILENAME}__accepted(b)) != 0) {
			o = ${FILENAME}__gettoken(b, b->buf);
			if(${FILENAME}__isdead(b)) {
				b->state = 0;
				return o;
			}
		} else if(${FILENAME}__isdead(b)) {
			return NULL;
		}
	}
	return f ? o : NULL;
}

static void *${FILENAME}_searchTokenStr(struct ${FILENAME}_tag *b, char **t) {
	void *o = NULL;
	int f = 0, c;
	char *p;

	p = b->buf;
	memset(b->buf, 0, sizeof(b->buf));
	if(b->iseof != NULL)  return b->iseof;
	while((c = nina__readstr((struct nina_work_tag *)b, t)) >= 0) {
		*p++ = c;
		if(!${FILENAME}__step(b, c)) {
			if(f) {
				b->unread = c;
				b->state = 0;
				return o;
			} else {
				return NULL;
			}
		} else if((f = ${FILENAME}__accepted(b)) != 0) {
			o = ${FILENAME}__gettoken(b, b->buf);
			if(${FILENAME}__isdead(b)) {
				b->state = 0;
				return o;
			}
		} else if(${FILENAME}__isdead(b)) {
			return NULL;
		}
	}
	return f ? o : NULL;
}

void ${FILENAME}_init(struct ${FILENAME}_tag *b) {
	memset(b, 0, sizeof(struct ${FILENAME}_tag));
	b->lookingAt      = ${FILENAME}_lookingAt;
	b->find           = ${FILENAME}_find;
	b->searchToken    = ${FILENAME}_searchToken;
	b->searchTokenStr = ${FILENAME}_searchTokenStr;
	b->reset          = ${FILENAME}_reset;
}
EOF
[ -z "$2" ] && cat fragment
