mirror of https://github.com/dirtbags/tanks.git
Actually include files now :\
This commit is contained in:
parent
a27883ffe9
commit
35394f1925
|
@ -6,8 +6,6 @@ round-*.html
|
||||||
next-round
|
next-round
|
||||||
|
|
||||||
run-tanks
|
run-tanks
|
||||||
forf.c
|
|
||||||
forf.h
|
|
||||||
designer.cgi
|
designer.cgi
|
||||||
|
|
||||||
forf.html
|
forf.html
|
||||||
|
|
|
@ -0,0 +1,753 @@
|
||||||
|
/* forf: a crappy Forth implementation
|
||||||
|
* Copyright (C) 2010 Adam Glasgall
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Notes
|
||||||
|
* -------------------------------------------------------
|
||||||
|
*
|
||||||
|
* This is intended to be implemented as a library. As such, it doesn't
|
||||||
|
* use the libc memory allocation functions. This may be a different
|
||||||
|
* programming style than you're used to.
|
||||||
|
*
|
||||||
|
* There are two data types: numbers and stacks. Because we can't
|
||||||
|
* allocate memory, stacks are implemented with begin and end markers
|
||||||
|
* and not new stack types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "forf.h"
|
||||||
|
#include "dump.h"
|
||||||
|
|
||||||
|
#ifndef max
|
||||||
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *forf_error_str[] = {
|
||||||
|
"None",
|
||||||
|
"Runtime",
|
||||||
|
"Parse",
|
||||||
|
"Underflow",
|
||||||
|
"Overflow",
|
||||||
|
"Type",
|
||||||
|
"No such procedure",
|
||||||
|
"Divide by zero",
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Memory manipulation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
forf_memory_init(struct forf_memory *m,
|
||||||
|
long *values,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
m->mem = values;
|
||||||
|
m->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Stack manipulation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_stack_init(struct forf_stack *s,
|
||||||
|
struct forf_value *values,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
s->stack = values;
|
||||||
|
s->size = size;
|
||||||
|
s->top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_stack_reset(struct forf_stack *s)
|
||||||
|
{
|
||||||
|
s->top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
forf_stack_len(struct forf_stack *s)
|
||||||
|
{
|
||||||
|
return s->top;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_stack_push(struct forf_stack *s, struct forf_value *v)
|
||||||
|
{
|
||||||
|
if (s->top == s->size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s->stack[(s->top)++] = *v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_stack_pop(struct forf_stack *s, struct forf_value *v)
|
||||||
|
{
|
||||||
|
if (0 == s->top) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*v = s->stack[--(s->top)];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_stack_copy(struct forf_stack *dst, struct forf_stack *src)
|
||||||
|
{
|
||||||
|
int top = min(dst->size, src->top);
|
||||||
|
|
||||||
|
dst->top = top;
|
||||||
|
memcpy(dst->stack, src->stack, sizeof(*dst->stack) * top);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_stack_reverse(struct forf_stack *s)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
for (pos = 0; pos < (s->top)/2; pos += 1) {
|
||||||
|
size_t qos = s->top - pos - 1;
|
||||||
|
|
||||||
|
val = s->stack[pos];
|
||||||
|
s->stack[pos] = s->stack[qos];
|
||||||
|
s->stack[qos] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
forf_pop_num(struct forf_env *env)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
|
||||||
|
if (! forf_stack_pop(env->data, &val)) {
|
||||||
|
env->error = forf_error_underflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (forf_type_number != val.type) {
|
||||||
|
forf_stack_push(env->data, &val);
|
||||||
|
env->error = forf_error_type;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return val.v.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_push_num(struct forf_env *env, long i)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
|
||||||
|
val.type = forf_type_number;
|
||||||
|
val.v.i = i;
|
||||||
|
if (! forf_stack_push(env->data, &val)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Pop an entire stack
|
||||||
|
*
|
||||||
|
* DANGER WILL ROBINSON
|
||||||
|
*
|
||||||
|
* This returned stack points to values on the data stack. You must be
|
||||||
|
* finished with this stack before you push anything onto the data
|
||||||
|
* stack, otherwise your returned stack will be corrupted.
|
||||||
|
*/
|
||||||
|
struct forf_stack
|
||||||
|
forf_pop_stack(struct forf_env *env)
|
||||||
|
{
|
||||||
|
struct forf_stack s = { 0, 0, NULL };
|
||||||
|
struct forf_value val;
|
||||||
|
size_t depth = 1;
|
||||||
|
|
||||||
|
if (! forf_stack_pop(env->data, &val)) {
|
||||||
|
env->error = forf_error_underflow;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
if (forf_type_stack_end != val.type) {
|
||||||
|
forf_stack_push(env->data, &val);
|
||||||
|
env->error = forf_error_type;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/* Duplicate just the stack onto s. Begin with -1 to account for the
|
||||||
|
end of list marker. */
|
||||||
|
s.size = -1;
|
||||||
|
while (depth) {
|
||||||
|
s.size += 1;
|
||||||
|
if (! forf_stack_pop(env->data, &val)) {
|
||||||
|
/* You should never underflow here, there should at least be a
|
||||||
|
stack begin marker. */
|
||||||
|
env->error = forf_error_runtime;
|
||||||
|
s.size = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
switch (val.type) {
|
||||||
|
case forf_type_stack_end:
|
||||||
|
depth += 1;
|
||||||
|
break;
|
||||||
|
case forf_type_stack_begin:
|
||||||
|
depth -= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.top = s.size;
|
||||||
|
s.stack = (env->data->stack) + (env->data->top + 1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push an entire stack onto another stack.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
forf_push_stack(struct forf_stack *dst, struct forf_stack *src)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
|
||||||
|
while (forf_stack_pop(src, &val)) {
|
||||||
|
if (! forf_stack_push(dst, &val)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push an entire stack onto the command stack.
|
||||||
|
*
|
||||||
|
* This is meant to work with the return value from forf_pop_stack.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
forf_push_to_command_stack(struct forf_env *env, struct forf_stack *src)
|
||||||
|
{
|
||||||
|
if (! forf_push_stack(env->command, src)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move one value from src to dst. Note that one value could mean a
|
||||||
|
* whole substack, in which case dst gets the stack in reverse! dst can
|
||||||
|
* also be NULL, in which case a value is just discarded.
|
||||||
|
*
|
||||||
|
* Because of the reversing thing, it's important to make sure that the
|
||||||
|
* data stack is either src or dst. This way, the data stack will
|
||||||
|
* always have "reversed" substacks, and everything else will have them
|
||||||
|
* in the right order.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
forf_stack_move_value(struct forf_env *env,
|
||||||
|
struct forf_stack *dst,
|
||||||
|
struct forf_stack *src)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
size_t depth = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Pop from src */
|
||||||
|
if (! forf_stack_pop(env->command, &val)) {
|
||||||
|
env->error = forf_error_underflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push to dst (or discard if dst is NULL) */
|
||||||
|
if (dst) {
|
||||||
|
if (! forf_stack_push(env->data, &val)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deal with it being a substack marker */
|
||||||
|
switch (val.type) {
|
||||||
|
case forf_type_stack_begin:
|
||||||
|
depth += 1;
|
||||||
|
break;
|
||||||
|
case forf_type_stack_end:
|
||||||
|
depth -= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (depth > 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Procedures
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define unproc(name, op) \
|
||||||
|
static void \
|
||||||
|
forf_proc_ ## name(struct forf_env *env) \
|
||||||
|
{ \
|
||||||
|
long a = forf_pop_num(env); \
|
||||||
|
\
|
||||||
|
forf_push_num(env, op a); \
|
||||||
|
}
|
||||||
|
|
||||||
|
unproc(inv, ~)
|
||||||
|
unproc(not, !)
|
||||||
|
|
||||||
|
#define binproc(name, op) \
|
||||||
|
static void \
|
||||||
|
forf_proc_ ## name(struct forf_env *env) \
|
||||||
|
{ \
|
||||||
|
long a = forf_pop_num(env); \
|
||||||
|
long b = forf_pop_num(env); \
|
||||||
|
\
|
||||||
|
forf_push_num(env, b op a); \
|
||||||
|
}
|
||||||
|
|
||||||
|
binproc(add, +)
|
||||||
|
binproc(sub, -)
|
||||||
|
binproc(mul, *)
|
||||||
|
binproc(and, &)
|
||||||
|
binproc(or, |)
|
||||||
|
binproc(xor, ^)
|
||||||
|
binproc(lshift, <<)
|
||||||
|
binproc(rshift, >>)
|
||||||
|
binproc(gt, >)
|
||||||
|
binproc(ge, >=)
|
||||||
|
binproc(lt, <)
|
||||||
|
binproc(le, <=)
|
||||||
|
binproc(eq, ==)
|
||||||
|
binproc(ne, !=)
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_div(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long a = forf_pop_num(env);
|
||||||
|
long b = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (0 == a) {
|
||||||
|
env->error = forf_error_divzero;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forf_push_num(env, b / a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_mod(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long a = forf_pop_num(env);
|
||||||
|
long b = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (0 == a) {
|
||||||
|
env->error = forf_error_divzero;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forf_push_num(env, b % a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_abs(struct forf_env *env)
|
||||||
|
{
|
||||||
|
forf_push_num(env, abs(forf_pop_num(env)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_dup(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long a = forf_pop_num(env);
|
||||||
|
|
||||||
|
forf_push_num(env, a);
|
||||||
|
forf_push_num(env, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_pop(struct forf_env *env)
|
||||||
|
{
|
||||||
|
forf_pop_num(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_exch(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long a = forf_pop_num(env);
|
||||||
|
long b = forf_pop_num(env);
|
||||||
|
|
||||||
|
forf_push_num(env, a);
|
||||||
|
forf_push_num(env, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_if(struct forf_env *env)
|
||||||
|
{
|
||||||
|
struct forf_stack ifclause = forf_pop_stack(env);
|
||||||
|
long cond = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (cond) {
|
||||||
|
forf_push_to_command_stack(env, &ifclause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_ifelse(struct forf_env *env)
|
||||||
|
{
|
||||||
|
struct forf_stack elseclause = forf_pop_stack(env);
|
||||||
|
struct forf_stack ifclause = forf_pop_stack(env);
|
||||||
|
long cond = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (cond) {
|
||||||
|
forf_push_to_command_stack(env, &ifclause);
|
||||||
|
} else {
|
||||||
|
forf_push_to_command_stack(env, &elseclause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_memset(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long pos = forf_pop_num(env);
|
||||||
|
long a = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (pos >= env->memory->size) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->memory->mem[pos] = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forf_proc_memget(struct forf_env *env)
|
||||||
|
{
|
||||||
|
long pos = forf_pop_num(env);
|
||||||
|
|
||||||
|
if (pos >= env->memory->size) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
forf_push_num(env, env->memory->mem[pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Lexical environment
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct forf_lexical_env forf_base_lexical_env[] = {
|
||||||
|
{"~", forf_proc_inv},
|
||||||
|
{"!", forf_proc_not},
|
||||||
|
{"+", forf_proc_add},
|
||||||
|
{"-", forf_proc_sub},
|
||||||
|
{"*", forf_proc_mul},
|
||||||
|
{"/", forf_proc_div},
|
||||||
|
{"%", forf_proc_mod},
|
||||||
|
{"&", forf_proc_and},
|
||||||
|
{"|", forf_proc_or},
|
||||||
|
{"^", forf_proc_xor},
|
||||||
|
{"<<", forf_proc_lshift},
|
||||||
|
{">>", forf_proc_rshift},
|
||||||
|
{">", forf_proc_gt},
|
||||||
|
{">=", forf_proc_ge},
|
||||||
|
{"<", forf_proc_lt},
|
||||||
|
{"<=", forf_proc_le},
|
||||||
|
{"=", forf_proc_eq},
|
||||||
|
{"<>", forf_proc_ne},
|
||||||
|
{"abs", forf_proc_abs},
|
||||||
|
{"dup", forf_proc_dup},
|
||||||
|
{"pop", forf_proc_pop},
|
||||||
|
{"exch", forf_proc_exch},
|
||||||
|
{"if", forf_proc_if},
|
||||||
|
{"ifelse", forf_proc_ifelse},
|
||||||
|
{"mset", forf_proc_memset},
|
||||||
|
{"mget", forf_proc_memget},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Extend a lexical environment */
|
||||||
|
int
|
||||||
|
forf_extend_lexical_env(struct forf_lexical_env *dest,
|
||||||
|
struct forf_lexical_env *src,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
int base, i;
|
||||||
|
|
||||||
|
for (base = 0; dest[base].name; base += 1);
|
||||||
|
for (i = 0; (base+i < size) && (src[i].name); i += 1) {
|
||||||
|
dest[base+i] = src[i];
|
||||||
|
}
|
||||||
|
if (base + i == size) {
|
||||||
|
/* Not enough room */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dest[base+i].name = NULL;
|
||||||
|
dest[base+i].proc = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Parsing
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
forf_push_token(struct forf_env *env, char *token, size_t tokenlen)
|
||||||
|
{
|
||||||
|
long i;
|
||||||
|
char s[MAX_TOKEN_LEN + 1];
|
||||||
|
char *endptr;
|
||||||
|
struct forf_value val;
|
||||||
|
|
||||||
|
/* Zero-length token yields int:0 from strtol */
|
||||||
|
|
||||||
|
/* NUL-terminate it */
|
||||||
|
memcpy(s, token, tokenlen);
|
||||||
|
s[tokenlen] = '\0';
|
||||||
|
|
||||||
|
/* Try to make in an integer */
|
||||||
|
i = strtol(s, &endptr, 0);
|
||||||
|
if ('\0' == *endptr) {
|
||||||
|
/* Was an int */
|
||||||
|
val.type = forf_type_number;
|
||||||
|
val.v.i = i;
|
||||||
|
} else {
|
||||||
|
/* If not an int, a procedure name */
|
||||||
|
val.type = forf_type_proc;
|
||||||
|
for (i = 0; NULL != env->lenv[i].name; i += 1) {
|
||||||
|
if (0 == strcmp(s, env->lenv[i].name)) {
|
||||||
|
val.v.p = env->lenv[i].proc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (NULL == env->lenv[i].name) {
|
||||||
|
env->error = forf_error_noproc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! forf_stack_push(env->command, &val)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse an input stream onto the command stack */
|
||||||
|
int
|
||||||
|
forf_parse_stream(struct forf_env *env,
|
||||||
|
forf_getch_func *getch,
|
||||||
|
void *datum)
|
||||||
|
{
|
||||||
|
int running = 1;
|
||||||
|
long pos = 0;
|
||||||
|
char token[MAX_TOKEN_LEN];
|
||||||
|
size_t tokenlen = 0;
|
||||||
|
struct forf_value val;
|
||||||
|
size_t stack_depth = 0;
|
||||||
|
int comment = 0;
|
||||||
|
|
||||||
|
#define _tokenize() \
|
||||||
|
do { \
|
||||||
|
if (tokenlen) { \
|
||||||
|
if (! forf_push_token(env, token, tokenlen)) return pos; \
|
||||||
|
tokenlen = 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = getch(datum);
|
||||||
|
pos += 1;
|
||||||
|
|
||||||
|
/* Handle comments */
|
||||||
|
if (comment) {
|
||||||
|
if (')' == c) {
|
||||||
|
comment = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case EOF:
|
||||||
|
running = 0;
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
comment = 1;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
case '\f':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case '\t':
|
||||||
|
case '\v':
|
||||||
|
_tokenize();
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
_tokenize();
|
||||||
|
val.type = forf_type_stack_begin;
|
||||||
|
if (! forf_stack_push(env->command, &val)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
stack_depth += 1;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
_tokenize();
|
||||||
|
val.type = forf_type_stack_end;
|
||||||
|
if (! forf_stack_push(env->command, &val)) {
|
||||||
|
env->error = forf_error_overflow;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
stack_depth -= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (tokenlen < sizeof(token)) {
|
||||||
|
token[tokenlen++] = c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tokenize();
|
||||||
|
|
||||||
|
if (0 != stack_depth) {
|
||||||
|
env->error = forf_error_parse;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first thing we read should be the first thing we do
|
||||||
|
forf_stack_reverse(env->command);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct forf_char_stream {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
size_t pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
forf_string_getch(struct forf_char_stream *stream)
|
||||||
|
{
|
||||||
|
if (stream->pos >= stream->len) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
return stream->buf[stream->pos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_parse_buffer(struct forf_env *env,
|
||||||
|
char *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct forf_char_stream stream;
|
||||||
|
|
||||||
|
stream.buf = buf;
|
||||||
|
stream.len = len;
|
||||||
|
stream.pos = 0;
|
||||||
|
|
||||||
|
return forf_parse_stream(env, (forf_getch_func *)forf_string_getch, &stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_parse_string(struct forf_env *env,
|
||||||
|
char *str)
|
||||||
|
{
|
||||||
|
return forf_parse_buffer(env, str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_parse_file(struct forf_env *env,
|
||||||
|
FILE *f)
|
||||||
|
{
|
||||||
|
return forf_parse_stream(env, (forf_getch_func *)fgetc, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Forf environment
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
forf_env_init(struct forf_env *env,
|
||||||
|
struct forf_lexical_env *lenv,
|
||||||
|
struct forf_stack *data,
|
||||||
|
struct forf_stack *cmd,
|
||||||
|
struct forf_memory *mem,
|
||||||
|
void *udata)
|
||||||
|
{
|
||||||
|
env->lenv = lenv;
|
||||||
|
env->data = data;
|
||||||
|
env->command = cmd;
|
||||||
|
env->memory = mem;
|
||||||
|
env->udata = udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_eval_once(struct forf_env *env)
|
||||||
|
{
|
||||||
|
struct forf_value val;
|
||||||
|
|
||||||
|
if (! forf_stack_pop(env->command, &val)) {
|
||||||
|
env->error = forf_error_underflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
switch (val.type) {
|
||||||
|
case forf_type_number:
|
||||||
|
case forf_type_stack_begin:
|
||||||
|
// Push back on command stack, then move it
|
||||||
|
forf_stack_push(env->command, &val);
|
||||||
|
if (! forf_stack_move_value(env, env->data, env->command)) return 0;
|
||||||
|
break;
|
||||||
|
case forf_type_proc:
|
||||||
|
(val.v.p)(env);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
env->error = forf_error_runtime;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
forf_eval(struct forf_env *env)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
env->error = forf_error_none;
|
||||||
|
while (env->command->top) {
|
||||||
|
ret = forf_eval_once(env);
|
||||||
|
if ((! ret) || (env->error)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
#ifndef __FORF_H__
|
||||||
|
#define __FORF_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#define MAX_TOKEN_LEN 20
|
||||||
|
#define MAX_CMDSTACK 200
|
||||||
|
|
||||||
|
struct forf_env;
|
||||||
|
|
||||||
|
enum forf_value_type {
|
||||||
|
forf_type_number,
|
||||||
|
forf_type_proc,
|
||||||
|
forf_type_stack_begin,
|
||||||
|
forf_type_stack_end,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum forf_error_type {
|
||||||
|
forf_error_none,
|
||||||
|
forf_error_runtime,
|
||||||
|
forf_error_parse,
|
||||||
|
forf_error_underflow,
|
||||||
|
forf_error_overflow,
|
||||||
|
forf_error_type,
|
||||||
|
forf_error_noproc,
|
||||||
|
forf_error_divzero,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char *forf_error_str[];
|
||||||
|
|
||||||
|
typedef void (forf_proc)(struct forf_env *);
|
||||||
|
|
||||||
|
struct forf_value {
|
||||||
|
enum forf_value_type type;
|
||||||
|
union {
|
||||||
|
forf_proc *p;
|
||||||
|
long i;
|
||||||
|
} v;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct forf_stack {
|
||||||
|
size_t size;
|
||||||
|
size_t top;
|
||||||
|
struct forf_value *stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct forf_memory {
|
||||||
|
size_t size;
|
||||||
|
long *mem;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct forf_lexical_env {
|
||||||
|
char *name;
|
||||||
|
forf_proc *proc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct forf_env {
|
||||||
|
enum forf_error_type error;
|
||||||
|
struct forf_lexical_env *lenv;
|
||||||
|
struct forf_stack *data;
|
||||||
|
struct forf_stack *command;
|
||||||
|
struct forf_memory *memory;
|
||||||
|
void *udata;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Main entry points
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Initialize a memory structure, given an array of longs */
|
||||||
|
void forf_memory_init(struct forf_memory *m,
|
||||||
|
long *values,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
/** Initialize a stack, given an array of values */
|
||||||
|
void forf_stack_init(struct forf_stack *s,
|
||||||
|
struct forf_value *values,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
void forf_stack_reset(struct forf_stack *s);
|
||||||
|
void forf_stack_copy(struct forf_stack *dst, struct forf_stack *src);
|
||||||
|
int forf_stack_push(struct forf_stack *s, struct forf_value *v);
|
||||||
|
int forf_stack_pop(struct forf_stack *s, struct forf_value *v);
|
||||||
|
|
||||||
|
/** Pop a number off the data stack */
|
||||||
|
long forf_pop_num(struct forf_env *env);
|
||||||
|
|
||||||
|
/** Push a number onto the data stack */
|
||||||
|
void forf_push_num(struct forf_env *env, long i);
|
||||||
|
|
||||||
|
/** Pop a whole stack */
|
||||||
|
struct forf_stack forf_pop_stack(struct forf_env *env);
|
||||||
|
|
||||||
|
|
||||||
|
/** The base lexical environment */
|
||||||
|
extern struct forf_lexical_env forf_base_lexical_env[];
|
||||||
|
|
||||||
|
/** Extend a lexical environment */
|
||||||
|
int
|
||||||
|
forf_extend_lexical_env(struct forf_lexical_env *dest,
|
||||||
|
struct forf_lexical_env *src,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
/** Initialize a forf runtime environment.
|
||||||
|
*
|
||||||
|
* data, cmd, and mem should have already been initialized
|
||||||
|
*/
|
||||||
|
void forf_env_init(struct forf_env *env,
|
||||||
|
struct forf_lexical_env *lenv,
|
||||||
|
struct forf_stack *data,
|
||||||
|
struct forf_stack *cmd,
|
||||||
|
struct forf_memory *mem,
|
||||||
|
void *udata);
|
||||||
|
|
||||||
|
/** The type of a getch function (used for parsing) */
|
||||||
|
typedef int (forf_getch_func)(void *);
|
||||||
|
|
||||||
|
/** Parse something by calling getch(datum)
|
||||||
|
*
|
||||||
|
* Returns the character at which an error was encountered, or
|
||||||
|
* 0 for successful parse.
|
||||||
|
*/
|
||||||
|
int forf_parse_stream(struct forf_env *env,
|
||||||
|
forf_getch_func *getch,
|
||||||
|
void *datum);
|
||||||
|
|
||||||
|
/** Parse a buffer */
|
||||||
|
int forf_parse_buffer(struct forf_env *env,
|
||||||
|
char *buf,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
/** Parse a string */
|
||||||
|
int forf_parse_string(struct forf_env *env,
|
||||||
|
char *str);
|
||||||
|
|
||||||
|
/** Parse a FILE * */
|
||||||
|
int forf_parse_file(struct forf_env *env,
|
||||||
|
FILE *f);
|
||||||
|
|
||||||
|
/** Evaluate the topmost value on the command stack */
|
||||||
|
int forf_eval_once(struct forf_env *env);
|
||||||
|
|
||||||
|
/** Evaluate the entire command stack */
|
||||||
|
int forf_eval(struct forf_env *env);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,204 @@
|
||||||
|
The Forf Language
|
||||||
|
=================
|
||||||
|
|
||||||
|
Welcome to Forf! *PUNCH*
|
||||||
|
|
||||||
|
Forf is a simple, postfix, stack-based programming language. It was
|
||||||
|
designed to be used as an extension language for games. Forf programs
|
||||||
|
run in fixed size memory and can be time-constrained.
|
||||||
|
|
||||||
|
Forf was heavily influenced by the PostScript programming language.
|
||||||
|
|
||||||
|
|
||||||
|
About Stacks
|
||||||
|
============
|
||||||
|
|
||||||
|
Central to the operation of Forf is the notion of a stack, or queue. A
|
||||||
|
stack is a first-in-first-out (FIFO) list, like a stack of dishes.
|
||||||
|
Items can be "pushed" onto the top of the stack, or "popped" off the top
|
||||||
|
of the stack. Items may only be pushed or popped, meaning the top stack
|
||||||
|
element is the only element that can be accessed. To get to the third
|
||||||
|
item down in a stack, it is necessary to first pop two items off.
|
||||||
|
|
||||||
|
|
||||||
|
Data Types
|
||||||
|
==========
|
||||||
|
|
||||||
|
There are three data types in Forf: Integers, Substacks, and Procedures.
|
||||||
|
|
||||||
|
|
||||||
|
Integers
|
||||||
|
--------
|
||||||
|
|
||||||
|
Integers are stored as the "long" type and have whatever boundaries the
|
||||||
|
host CPU and C compiler enforce. They may be entered in decimal (12),
|
||||||
|
octal (014), or hex (0xC), and may be positive or negative.
|
||||||
|
|
||||||
|
The following are valid integers:
|
||||||
|
|
||||||
|
* 58
|
||||||
|
* -58
|
||||||
|
* 0x3A (hex)
|
||||||
|
* 072 (octal)
|
||||||
|
|
||||||
|
|
||||||
|
Procedures
|
||||||
|
----------
|
||||||
|
|
||||||
|
Data is read one by one from your program and either pushed onto the
|
||||||
|
data stack (integers and substacks) or evaluated (procedures). When a
|
||||||
|
procedures is evaluated, it pops zero or more elements off the data
|
||||||
|
stack, does something with them, and then pushes zero or more elements
|
||||||
|
back onto the stack.
|
||||||
|
|
||||||
|
The "+" procudure, for instance, pops two values, adds them, and pushes
|
||||||
|
the result. The following data stack:
|
||||||
|
|
||||||
|
[bottom] 58 88 5 [top]
|
||||||
|
|
||||||
|
When given to the "+" procedure, would yield:
|
||||||
|
|
||||||
|
[bottom] 58 93 [top]
|
||||||
|
|
||||||
|
|
||||||
|
Substacks
|
||||||
|
---------
|
||||||
|
|
||||||
|
Substacks are groups of data on the data stack. They are used only by
|
||||||
|
the "if" and "ifelse" procedures, and are denoted by "{" (start
|
||||||
|
substack) and "}" (end substack). Substacks may be nested.
|
||||||
|
|
||||||
|
The following will result in 58 on the top of the stack:
|
||||||
|
|
||||||
|
5 8 < { 50 8 + } { 50 8 - } ifelse
|
||||||
|
|
||||||
|
|
||||||
|
Built-in Procedures
|
||||||
|
===================
|
||||||
|
|
||||||
|
The following procedures are built in to Forf. Since the language was
|
||||||
|
designed to be extended, your game provides additional procedures.
|
||||||
|
|
||||||
|
|
||||||
|
Unary Operations
|
||||||
|
----------------
|
||||||
|
|
||||||
|
These procedures pop one value and push one.
|
||||||
|
|
||||||
|
* `x ~` (bitwise invert)
|
||||||
|
* `x !` (logical not)
|
||||||
|
* `x abs` (absolute value)
|
||||||
|
|
||||||
|
|
||||||
|
Binary Operations
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The following procedures pop two values and push one. They work as in
|
||||||
|
an RPN calculator, meaning that `8 4 /` yields `2`.
|
||||||
|
|
||||||
|
* `y x +` (y + x)
|
||||||
|
* `y x -` (y - x)
|
||||||
|
* `y x *` (y * x)
|
||||||
|
* `y x /` (y / x)
|
||||||
|
* `y x %` (y modulo x)
|
||||||
|
* `y x &` (y and x)
|
||||||
|
* `y x |` (y or x)
|
||||||
|
* `y x ^` (x xor x)
|
||||||
|
* `y x <<` (y shifted left by x)
|
||||||
|
* `y x >>` (y shifted right by x)
|
||||||
|
|
||||||
|
|
||||||
|
Comparison
|
||||||
|
----------
|
||||||
|
|
||||||
|
These procedures pop two numbers and compare them, pushing `1` if the
|
||||||
|
comparison is true, `0` if false. For instance, `5 3 >` yields `1`.
|
||||||
|
|
||||||
|
* `y x >` (y greater than x)
|
||||||
|
* `y x >=` (y greater than or equal to x)
|
||||||
|
* `y x <` (y less than x)
|
||||||
|
* `y x <=` (y less than or equal to x)
|
||||||
|
* `y x =` (y equal to x)
|
||||||
|
* `y x <>` (y not equal to x)
|
||||||
|
|
||||||
|
|
||||||
|
Conditional Procedures
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* `x { i ... } if` (if x, evaluate i)
|
||||||
|
* `x { i ... } { e ... } ifelse` (if x, evaluate i, otherwise evaluate e)
|
||||||
|
|
||||||
|
|
||||||
|
Stack Manipulation
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* `x pop` (discard x)
|
||||||
|
* `x dup` (duplicate x)
|
||||||
|
* `y x exch` (exchange x and y on the stack)
|
||||||
|
|
||||||
|
|
||||||
|
Memory
|
||||||
|
------
|
||||||
|
|
||||||
|
Your game may provide you with one or more memory slots. These are like
|
||||||
|
variables in other languages, and may persist across invocations of your
|
||||||
|
program.
|
||||||
|
|
||||||
|
* `y x mset` (store y in slot x)
|
||||||
|
* `x mget` (retrieve value in slot x)
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
Compute 58²:
|
||||||
|
|
||||||
|
58 58 *
|
||||||
|
|
||||||
|
Compute 58³:
|
||||||
|
|
||||||
|
58 58 58 * *
|
||||||
|
|
||||||
|
Another way to compute 58³:
|
||||||
|
|
||||||
|
58 dup dup * *
|
||||||
|
|
||||||
|
|
||||||
|
The ifelse example, which does a comparison and puts 58 on the stack:
|
||||||
|
|
||||||
|
5 8 < { 50 8 + } { 50 8 - } ifelse
|
||||||
|
|
||||||
|
Another way to do that:
|
||||||
|
|
||||||
|
50 5 8 < { 8 + } { 8 - } ifelse
|
||||||
|
|
||||||
|
Yet another way:
|
||||||
|
|
||||||
|
50 8 5 8 < { + } { - } ifelse
|
||||||
|
|
||||||
|
Is memory slot 3 greater than 100?
|
||||||
|
|
||||||
|
3 mget 100 >
|
||||||
|
|
||||||
|
Given x, if x² is greater than 100 yield x², otherwise 0:
|
||||||
|
|
||||||
|
dup dup * 100 > { dup * } { pop 0 } ifelse
|
||||||
|
|
||||||
|
Another way to do the same thing:
|
||||||
|
|
||||||
|
dup * dup 100 < { pop 0 } if
|
||||||
|
|
||||||
|
Given coordinates (x, y) on the stack, is the distance to (x, y) less
|
||||||
|
than 88? This compares x²+y² against 88².
|
||||||
|
|
||||||
|
dup * exch dup * + 88 88 * <
|
||||||
|
|
||||||
|
Perform different actions given some number between 0 and 3:
|
||||||
|
|
||||||
|
dup 0 = { action0 } if
|
||||||
|
dup 1 = { action1 } if
|
||||||
|
dup 2 = { action2 } if
|
||||||
|
dup 3 = { action3 } if
|
||||||
|
pop
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue