Actually include files now :\

This commit is contained in:
Neale Pickett 2010-07-27 15:41:25 -06:00
parent a27883ffe9
commit 35394f1925
4 changed files with 1107 additions and 2 deletions

2
.gitignore vendored
View File

@ -6,8 +6,6 @@ round-*.html
next-round
run-tanks
forf.c
forf.h
designer.cgi
forf.html

753
forf.c Normal file
View File

@ -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;
}

150
forf.h Normal file
View File

@ -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

204
forf.txt Normal file
View File

@ -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