home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-04-09 | 50.4 KB | 1,549 lines |
- /* mouth.c
- *
- * That portion of mkdrawf 3 dealing with macro expansion rather than
- * with what happens once things are expanded.
- *
- * The mouth/stomach terminology is, I believe, due to Knuth.
- */
-
- /* Main differences from mkdrawf 2:
- *
- * 1. There is a single global hash table, which contains every
- * keyword and every macro definition seen so far.
- * Of course that means it's rather bigger than the
- * pathetic 677-entry one used in mkdrawf 2.
- *
- * 2. Token lists (for macros, loops etc) are refcounted,
- * and discarded when they're finished with. (NOT DONE YET)
- */
-
- #include <ctype.h>
- #include <math.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "mkdrawf.h"
-
-
- /* -------------------------- tokens -------------------------- */
-
-
- /* We use a null token to denote "no value".
- * This is the default value for hash table entries because
- * static arrays are initialised to be full of zeros.
- */
- static Token null_token={0,0};
-
- /* For debugging we need the following, which displays the current
- * token in a fairly sane form.
- * If |x!=0| this is coming from |get_x_token()| rather than |get_token()|,
- * and we indicate the fact.
- */
- #if defined(DEBUG_TOKENS) || defined(DEBUG_XTOKENS)
- static char *name();
- static void show_token(int x) {
- if (x) printf("\nX: ");
- switch(curr_token.type) {
- case t_NoValue: printf("<no value>"); break;
- case t_keyword: printf("keyword:#%d",curr_token.value.I); break;
- case t_real: printf("%lg",curr_token.value.D); break;
- case t_string: printf("\"%s\"",curr_token.value.CP); break;
- case t_colour: printf("colour:0x%08X",curr_token.value.I); break;
- case t_toklist: printf("<token list>"); break;
- case t_special: printf("special:#%d",curr_token.value.I); break;
- case t_macro: printf("macro:??"); break;
- case t_global: printf("global:%s",name(curr_token.value.HP)); break;
- case t_local: printf("local:%s",name(curr_token.value.HP)); break;
- case t_localP: printf("pospar:%%%d",curr_token.value.I); break;
- case t_openbr: printf("{"); break;
- case t_closebr: printf("}"); break;
- case t_unready: printf("unready:%s",name(curr_token.value.HP)); break;
- case t_magicEOM: printf("EOM"); break;
- case t_magicNEXT: printf("NEXT"); break;
- default: printf("illegal:%d",curr_token.type);
- }
- putchar(x?'\n':' ');
- }
- #endif
-
-
- /* -------------------------- forward declarations -------------------------- */
-
-
- static int toklists_on_stack;
- static TwoWords *next_token;
-
- static int get_token(void);
-
-
- /* -------------------------- memory management -------------------------- */
-
-
- /* We allocate new |Token| and |TwoWords| objects using a simple-minded
- * but fast method: we have a freelist, and grab a block of space using
- * |malloc()| when necessary. We never return freed stuff to the system;
- * only to the free list.
- */
-
- /* We do something similar for saved-variable records too, but that's
- * dealt with later.
- */
-
- #define alloc_unit 4096 /* number of lumps to claim from system */
-
- static int *free_list_2=0; /* for |TwoWords| objects (2 words) */
- static int *free_list_3=0; /* for |Token| objects (3 words) */
-
- static TwoWords *new_two(void) {
- int *t=free_list_2;
- #ifdef DEBUG_MEMORY
- fprintf(stderr,"[+2]");
- #endif
- if (!t) {
- int i;
- t=free_list_2=(int*)xmalloc(alloc_unit*8,"2-word objects");
- for (i=0;i<alloc_unit;++i) {
- *free_list_2=(int)(free_list_2+2); free_list_2+=2; }
- free_list_2[-2]=0;
- }
- free_list_2=(int*)t[0]; return (TwoWords*)t;
- }
-
- static Token *new_three(void) {
- int *t=free_list_3;
- #ifdef DEBUG_MEMORY
- fprintf(stderr,"[+3]");
- #endif
- if (!t) {
- int i;
- t=free_list_3=(int*)xmalloc(alloc_unit*12,"3-word objects");
- for (i=0;i<alloc_unit;++i) {
- *free_list_3=(int)(free_list_3+3); free_list_3+=3; }
- free_list_3[-3]=0;
- }
- free_list_3=(int*)t[0]; return (Token*)t;
- }
-
- /* To free an object, just stick it back on the free-list it came from.
- * NOTE: At the moment, nothing is *ever* freed. I'll work on this.
- */
-
- #if 0 /* we never use these, see */
-
- static void free_two(TwoWords *p) {
- #ifdef DEBUG_MEMORY
- fprintf(stderr,"[-2]");
- #endif
- *((int*)p)=(int)free_list_2;
- free_list_2=(int*)p;
- }
-
- static void free_three(Token *p) {
- #ifdef DEBUG_MEMORY
- fprintf(stderr,"[-3]");
- #endif
- *((int*)p)=(int)free_list_3;
- free_list_3=(int*)p;
- }
-
- #endif
-
- #define new_pair new_two
- #define free_pair free_two
- #define new_token new_three
- #define free_token free_three
-
-
- /* -------------------------- the hash table -------------------------- */
-
-
- /* We use the method of coalescing lists.
- * The way this works is that the table is larger than the number of
- * possible hash values; the list heads go in the low bit of the table,
- * and extra entries, when they get chained onto lists, are allocated
- * starting at the high end.
- * It's called the "method of coalescing lists" because once allocation
- * of non-head items reaches the low part of the hash table we just allow
- * them to occupy "list-head" positions; this means that from then on, the
- * corresponding list starts in the middle of the earlier one.
- * Making the low part occupy about 85% of the whole table results in
- * very little degradation from this coalescing; on average a full table
- * will require <2 probes per lookup.
- *
- * It's hard to delete tokens from this sort of hash table without
- * disaster, because doing that disconnects lists. It can be done,
- * but it's painful. It's easier just to leave things in the table
- * for ever, so we do that.
- *
- * This is the same algorithm used in TeX and METAFONT, by Don Knuth
- * (although I'm pretty sure he didn't invent it.) I have used the
- * same values of |hash_size| and |hash_prime| as he did; this should
- * be very much more than enough.
- */
-
- #define hash_size 2100 /* size of whole table */
- #define hash_prime 1777 /* amount available to list heads */
-
- static uint next_free=hash_size-1;
-
- /* A hash table entry contains a key (a string, the name of the variable
- * or keyword or whatever), a value (a token) and a "next entry in list"
- * field. Unfortunately this is 5 words, which isn't going to make things
- * very efficient. And with 2000-odd entries, we really want each entry
- * to be small.
- *
- * Well, we only need 16 bits for the "next" field. We can squeeze the
- * "key" field into 16 bits too, by putting variable names etc into a
- * special-purpose array of characters. With about 2000 items, we can
- * allow 32k (say) of string space without any fear of overflow.
- *
- * There's a little problem: we want to start with all entries zero,
- * and use |next==0| to mean end-of-list. But 0 is a valid offset into
- * the hash table. The solution I've adopted, with regret, is to
- * refrain from ever linking to the 0th element of the hash table.
- * This would only happen one insertion before it overflows anyway.
- */
-
- #define string_table_size 16384
- static char string_table[string_table_size];
- static uint next_free_char=0;
-
- /* OK, so this is now what a hash table entry looks like:
- */
- typedef struct hashent {
- uint both; /* high bits contain |key|; low bits contain |next| */
- struct token value;
- } HashEntry;
-
- /* |name(v)| is the name of the variable whose hash address is |v|.
- * If speed is important, it's better to write this out explicitly.
- */
- static char *name(HashEntry *v) {
- return string_table+(v->both>>16);
- }
-
- /* And here's the hash table itself:
- */
- static HashEntry hash_table[hash_size];
-
- /* When we look up an entry and it's not there, we *always* want to
- * create an entry for it.
- * If there wasn't one before, the new entry will contain a null token,
- * because |static| arrays are initialised to all-zeros.
- */
- static HashEntry *hashloc(const char *key) {
- uint h=0,i,l=0;
- /* Put hash value into h, and length of string into l: */
- while ((i=key[l++])!=0) { h=(h<<1)+i; while (h>=hash_prime) h-=hash_prime; }
- /* Search table: */
- while ((i=hash_table[h].both)!=0) {
- int j=i>>16;
- if (!strcmp(string_table+j,key)) return hash_table+h; /* found */
- j=i-(j<<16); if (!j) break; /* end of list */
- h=j;
- }
- /* Not found. */
- if (i) {
- while (next_free>0 && hash_table[next_free].both) --next_free;
- if (!next_free) error("Hash table overflow");
- hash_table[h].both=(i&0xFFFF0000)+next_free; h=next_free--;
- }
- /* Now we need to install |key| in the string table, and make
- * "h.key" point to it.
- */
- if (next_free_char+l>=string_table_size) error("String table overflow");
- memcpy(string_table+next_free_char,key,l);
- hash_table[h].both=next_free_char<<16; /* key=n_f_c, next=0 */
- next_free_char+=l;
- return hash_table+h;
- }
-
-
- /* -------------------------- positional parameters ------------------------ */
-
-
- /* NB: Read the next section before this one! */
-
- /* It's possible to refer in a macro to the tokens that follow the macro
- * invocation, by using "positional parameters" looking like %1 .. %9.
- * As many are read as are needed.
- * Bear in mind that they are read *with* expansion, so if they include
- * <Set> or similar tokens they may cause funny things to happen at
- * funny times... (Also, such things will refer to the variable bindings
- * extant when they're expanded.)
- */
-
- #define max_pos_param 9
-
- /* We need to stack these, just as we do with ordinary local variables.
- * We also need to remember, for each macro invocation, how many pp's
- * have been used so far.
- * Most macros don't use *any* positional parameters; those that do
- * tend not to use very many. So it makes sense to use the same
- * mechanism as that for ordinary variables, rather than storing
- * all 9 pp values every time.
- */
-
- #define max_macro_level 256
- static char n_pps_[max_macro_level];
- static char *n_pps=n_pps_-1; /* because 0 means no macros used... */
-
- /* So that we *can* treat these sort of as like ordinary locals,
- * we need |HashEntry|s for them. So here we are.
- * I'm afraid |pp_val[0]| is the entry for %1. Sorry.
- */
-
- static HashEntry pp_val[max_pos_param];
-
- /* When we are reading tokens so as to set a positional parameter,
- * we need to be sure of reading them from the right place.
- * So when we enter a macro, we remember what our token-list state
- * was just before the invocation got started.
- * Reading pp's may cause some of these token-lists to end, but that's
- * OK; it will just result in some 0s in the array of token-lists.
- */
-
- typedef struct {
- int toks_depth;
- TwoWords *next_tok;
- } MacroContext;
-
- static MacroContext macro_context_[max_macro_level];
- static MacroContext *macro_context=macro_context_-1;
-
-
- /* -------------------------- local variables -------------------------- */
-
-
- /* When we set a local variable while expanding a macro call,
- * we need to make sure we can restore its value later.
- * But it's only the first time it's referred to that we need
- * to bother saving the old value. How can we make sure that
- * the value gets saved the first time a variable is changed,
- * but not other times?
- *
- * Answer: maintain a count of how deeply nested macros are at
- * any given moment. When a local variable is changed,
- * check whether it's been changed before at this level;
- * if it has, leave it alone. When a variable's value
- * is restored, we need to restore the level too.
- *
- * This unfortunately requires yet another field in each hash
- * table entry.
- * We use the top byte of the |type| field of the token for this.
- * This is pretty unpleasant, but it seems to be the best way
- * to deal with the problem. Note that this implies a maximum
- * nesting depth of 256.
- *
- * Of course, we must remember to clear those bits any time we
- * reference a variable...
- */
-
- static uint macro_level=0;
-
- /* A saved value is described by saying where in the hash table it
- * belongs, what its value was and when it was saved.
- * The |next| field is used to hold the save stack together.
- */
- typedef struct saved {
- HashEntry *loc;
- Token value;
- int level;
- struct saved *next;
- } Saved;
-
- /* We allocate and free saved-variable records in the same sort of
- * straightforward way as we used for tokens and such.
- */
- static Saved *free_list_Saved=0;
- #ifdef DEBUG_MEMORY
- static Saved *new_saved_(void) {
- Saved *t=free_list_Saved;
- fprintf(stderr,"[+S]");
- if (!t) {
- int i;
- t=free_list_Saved=(Saved *)xmalloc(256*sizeof(Saved),
- "saved-local records");
- for (i=0;i<256;++i) {
- *(Saved**)free_list_Saved=free_list_Saved+1; ++free_list_Saved; }
- *(int*)(free_list_Saved-1)=0;
- }
- free_list_Saved=*(Saved**)t;
- return t;
- }
- #define New_saved(x) (x)=new_saved()
- static void free_saved(Saved *p) {
- fprintf(stderr,"[-S]");
- *(Saved**)p=free_list_Saved;
- free_list_Saved=p;
- }
- #else
- static Saved *new_saved_(void) {
- int i;
- { Saved *t=free_list_Saved=(Saved *)xmalloc(256*sizeof(Saved),
- "saved-local records");
- for (i=0;i<256;++i) { *(Saved**)t=t+1; ++t; }
- *(int*)(t-1)=0;
- }
- { Saved *u=free_list_Saved;
- free_list_Saved=*(Saved**)u;
- return u;
- }
- }
- #define New_saved(x) { if (free_list_Saved) { x=free_list_Saved;\
- free_list_Saved=*(Saved**)free_list_Saved; }\
- else x=new_saved_(); }
- #define free_saved(p) { *(Saved**)(p)=free_list_Saved; free_list_Saved=p; }
- #endif
-
- /* Saved values are held on a linked list. (This means that there
- * is no restriction on the number of variables we can save in each
- * macro invocation.)
- */
-
- static Saved *last_saved; /* most recently saved thing */
-
- /* |set_local| sets the value of the local variable whose hash address
- * is |var| to be |value|.
- */
- static void set_local(HashEntry *var, Token *value) {
- int l=var->value.type>>24;
- if (l!=macro_level) {
- /* Need to save the old value */
- Saved *s;
- New_saved(s);
- s->loc=var; s->value=var->value; s->level=macro_level;
- s->next=last_saved; last_saved=s;
- }
- var->value.type=value->type | (macro_level<<24);
- var->value.value=value->value;
- }
-
- /* Decrement |macro_level|, restoring any saved locals.
- */
- static void pop_level(void) {
- if (!macro_level) minor("Misplaced |pop_level()|");
- else {
- Saved *s=last_saved;
- while (s && s->level==macro_level) {
- s->loc->value=s->value;
- { Saved *t=s; s=s->next; free_saved(t); }
- }
- last_saved=s;
- /* Next two lines explained in next section */
- toklists_on_stack=macro_context[macro_level].toks_depth;
- next_token=macro_context[macro_level].next_tok;
- --macro_level;
- }
- }
-
-
- /* -------------------------- input and output -------------------------- */
-
-
- /* We read data from an input file, and write it to an output file.
- */
-
- FILE *input_file=0;
- char *input_file_name=0;
-
- /* With the |Include| command we may have several input files
- * open at once. We hold the names and FILE *'s of the inactive
- * ones in a stack. Highest number = most recently opened.
- */
-
- #define max_files_on_stack 16 /* so can have 17 open at once */
-
- static int files_on_stack=0;
- static struct {
- char *name;
- FILE *file;
- int line;
- } file_stack[max_files_on_stack];
-
- /* Note: |get_line| and |line_tail| below are NOT |static| because they
- * are accessed directly by |do_textarea()| in stomach.c. This is ugly,
- * but necessitated by the syntax used; avoiding it would make using
- * text areas even more unpleasant than it already is.
- */
-
- /* The currently-being-processed line of input is contained in
- * |curr_line|. The first character in it that hasn't been
- * processed is in |line_tail|.
- */
-
- static char curr_line[max_line_length]="";
- char *line_tail=curr_line;
-
- /* |get_line()| reads a new line of input into |curr_line|. It deals
- * with closing finished input files, etc.
- * It returns 1 if there are no more lines available, else 0.
- */
- int get_line(void) {
- while (!fgets(curr_line,max_line_length,input_file)) {
- if (ferror(input_file))
- minor("Read error for input file `%s'",input_file_name);
- if (!files_on_stack) return 1;
- fclose(input_file);
- input_file=file_stack[--files_on_stack].file;
- input_file_name=file_stack[files_on_stack].name;
- line_number=file_stack[files_on_stack].line;
- }
- line_tail=curr_line;
- ++line_number;
- return 0;
- }
-
- /* |open_new_file(s)| checks that we aren't nesting files too deeply,
- * then tries to open the file whose name is |s|. If it succeeds, it
- * pushes the current input file onto the stack and replaces it with
- * the new file.
- */
- static void open_new_file(char *s) {
- FILE *f;
- if (files_on_stack>=max_files_on_stack) {
- minor("Files nested too deeply (maximum=%d)",max_files_on_stack);
- return;
- }
- f=fopen(s,"r");
- if (!f) error("Couldn't open file `%s' for inclusion",s);
- file_stack[files_on_stack].name=input_file_name;
- file_stack[files_on_stack].file=input_file;
- file_stack[files_on_stack++].line=line_number;
- input_file_name=s;
- input_file=f;
- line_number=0;
- }
-
-
- /* -------------------------- conditionals -------------------------- */
-
-
- /* To keep track of If/Else/EndIf constructs we have a stack of
- * "pending" <If>s. Let's be more precise about how this works...
- *
- * If we encounter an <If> with a *false* condition, we skip tokens
- * until reaching an <Else> or an <EndIf>; if it's an <EndIf> we're done;
- * if it's an <Else> we stack the fact that we are waiting for an <EndIf>.
- *
- * If we encounter an <If> with a *true* condition, we stack the fact
- * that we're waiting for an <Else>.
- *
- * If we encounter an <Else>, we look at the top of the If-stack. If
- * it says "waiting for an <Else>", we skip until finding an <EndIf>.
- * If it says "waiting for an <EndIf>", we complain "two Elses".
- *
- * If we encounter an <EndIf>, we check that the If-stack isn't empty,
- * and pop off its top element.
- *
- * Thus we only really need one *bit* per entry in this stack. Mucking
- * about with bit-arrays is a pain, though. We use an array of |char|s.
- * An entry of 0 means "looking for an <EndIf>"; 1 means "looking for an
- * <Else> (or an <EndIf>)".
- */
-
- #define max_ifs_on_stack 256
- static char if_stack[max_ifs_on_stack];
- static int ifs_on_stack=0;
-
- /* |skip_tokens()| does just what it says: skips past tokens looking
- * for an |Else| or an |EndIf|, taking care over the matching of Ifs.
- * (We don't worry about the matching of braces, because we might want
- * code like
- * If blah { ..... EndIf
- * ...
- * If blah } EndIf
- * ... maybe.)
- *
- * |skip_tokens()| returns 0 if it finds an <EndIf>, a <1> if it finds
- * an <Else>, a 99 if it hits EOF or a file error. So we're happy iff
- * the number this returns is <= the appropriate number from the stack.
- */
- static int skip_tokens(void) {
- int depth=0;
- while (1) {
- if (get_token()) return 99;
- if (curr_token.type==t_special) {
- /* What follows is naughty, yes. I guarantee to keep the relevant
- * values <32. */
- int q=1<<curr_token.value.U;
- if (q & ((1<<s_IfExists) | (1<<s_IfLess) | (1<<s_IfEqual)
- | (1<<s_EndIf) | (1<<s_Else))) {
- if (q==1<<s_EndIf) { if (depth) --depth; else return 0; }
- else if (q==1<<s_Else) { if (!depth) return 1; }
- else ++depth;
- }
- }
- }
- /* This point is never reached. */
- return 99;
- }
-
- /* Purely for convenience, macros for stacking things on the
- * If-stack.
- * |stack_else| means "remember that we're waiting for an <Else>";
- * |stack_endif| means "remember that we're waiting for an <EndIf>".
- */
- #define stack_else {\
- if (ifs_on_stack>=max_ifs_on_stack) error("Too many nested Ifs");\
- if_stack[ifs_on_stack++]=1; }
- #define stack_endif {\
- if (ifs_on_stack>=max_ifs_on_stack) error("Too many nested Ifs");\
- if_stack[ifs_on_stack++]=0; }
-
-
- /* -------------------------- loops -------------------------- */
-
-
- /* When we encounter a <For> token, we create a token list
- * which ends with a special token. This special token has
- * type |t_magicNEXT|, and its |value| field points to a
- * record describing the loop. It needs to record the variable
- * being used, its limit value, and a pointer to the start of
- * the token list.
- *
- * The final `next-pointer' in the loop token list -- the one
- * associated with the special token -- points to the value
- * of |next_token| to use for continuing after the end of the
- * loop.
- */
-
- typedef struct looprec {
- HashEntry *var; /* variable being used */
- double limit; /* max. value for loop variable */
- TwoWords *start; /* start of token list */
- } LoopRecord;
-
-
- /* -------------------------- lexing -------------------------- */
-
-
- /* The token we have just read is in |curr_token|.
- */
- Token curr_token;
-
- /* When we are reading tokens from a token list, the address
- * of the next token to be read is in |*(next_token->P.first.TP)|,
- * and the next value for |next_token| is in |next_token->P.rest.PP|.
- * When we are not reading from a token list, this contains 0.
- * It always contains either 0 or a pointer to a genuine token.
- */
- static TwoWords *next_token;
-
- /* There may be tokens waiting from several different token-lists.
- * We have a stack of waiting token-lists, the top element of which
- * is in effect cached in |next_token|.
- * Each item on this stack is ready to be put into |next_token| when
- * it's time to use it.
- */
-
- #define max_toklists_on_stack 256
- static int toklists_on_stack=0;
- static TwoWords *tok_stack[max_toklists_on_stack];
-
- /* |get_token()| returns 1 when there are no more tokens available,
- * which signals the end of input; or else returns 0 having put
- * a token in |curr_token|.
- */
- #ifdef DEBUG_TOKENS
- static int get_token_(void);
- static int get_token(void) {
- int i=get_token_();
- show_token(0);
- return i;
- }
- static int get_token_(void) {
- #else
- static int get_token(void) {
- #endif
- /* Reading from a token list? */
- again:
- if (next_token) {
- curr_token=*(next_token->P.first.TP);
- next_token=next_token->P.rest.PP;
- if (curr_token.type>=t_magicEOM) {
- /* end of token list. Need to do clever things */
- /* At the end of this block we should read another token */
- switch(curr_token.type) {
- case t_magicEOM:
- /* end of macro. Restore variables. */
- pop_level();
- break;
- case t_magicNEXT: {
- /* end of loop. Update variable, & do the right thing. */
- LoopRecord *l=curr_token.value.LP;
- HashEntry *v=l->var;
- double x;
- if ((v->value.type&0xFF)!=t_real) {
- minor("Abuse of loop variable `%s'",name(v));
- x=l->limit+1; }
- else x=v->value.value.D+1;
- if (x<l->limit) { v->value.value.D=x; next_token=l->start; }
- break; }
- default: minor("Mysterious token, type=%d (magicEOM=%d)",
- curr_token.type,t_magicEOM);
- }
- goto again;
- }
- /* here: not a magic token. Just use it */
- return 0;
- }
- /* No tokens waiting in |next_token|... Any on stack? */
- if (toklists_on_stack) {
- next_token=tok_stack[--toklists_on_stack];
- goto again; }
- /* No tokens waiting at all. Extract next token from current line. */
- lex:
- while (isspace(*line_tail)) ++line_tail;
- if (!*line_tail) { if (get_line()) return 1; else goto lex; }
- /* OK, now we can actually get to work. */
- { char tok[max_line_length];
- char *cp;
- char *olt;
- olt=line_tail; cp=tok;
- while (*line_tail && !isspace(*line_tail)) *cp++=tolower(*line_tail++);
- *cp=0;
- /* There's now a token at |tok|. */
- if (cp==tok+1) {
- if (*tok=='{') { curr_token.type=t_openbr; return 0; }
- if (*tok=='}') { curr_token.type=t_closebr; return 0; }
- }
- /* A variable? */
- if (*tok=='%') {
- if (cp==tok+2 && isdigit(tok[1])) {
- curr_token.type=t_localP;
- curr_token.value.U=tok[1]-'0'; }
- else {
- curr_token.type=t_local;
- curr_token.value.HP=hashloc(tok); }
- return 0; }
- if (*tok=='$') {
- curr_token.type=t_global;
- curr_token.value.HP=hashloc(tok);
- return 0; }
- /* No. A string? */
- if (*tok=='"') {
- char *s=xmalloc(max_line_length,"a string");
- char *sp=s;
- int bs=0;
- for (cp=olt+1;*cp&&(bs||*cp!='"');) {
- if (!bs) {
- if (*cp=='\\') { bs=1; cp++; continue; }
- }
- else bs=0;
- *sp++=*cp++;
- }
- line_tail=cp+1;
- do *sp++=0; while ((sp-s)&3); /* pad to word boundary */
- if (!*cp) warn("Unterminated string");
- curr_token.type=t_string;
- curr_token.value.CP=s;
- return 0; }
- /* No. A comment? */
- if (*tok=='#') { if (get_line()) return 1; else goto lex; }
- /* No. A number? */
- { double d;
- char *cp2;
- d=strtod(tok,&cp2);
- /* If |strtod()| fails, try for a hex number */
- if (cp2!=cp && *tok=='0' && tok[1]=='x')
- d=(double)strtoul(tok+2,&cp2,16);
- if (cp2==cp) {
- curr_token.type=t_real;
- curr_token.value.D=d;
- return 0;
- }
- }
- /* No. A colour? */
- if (*tok=='r') {
- int r,g,b;
- if (sscanf(tok,"r%dg%db%d",&r,&g,&b)==3) {
- curr_token.type=t_colour;
- curr_token.value.U=(r<<8)+(g<<16)+(b<<24);
- return 0;
- }
- }
- if (!strcmp(tok,"none") || !strcmp(tok,"transparent")) {
- curr_token.type=t_colour;
- curr_token.value.I=-1;
- return 0;
- }
- /* No. Presumably a macro, then. Or perhaps a keyword or special. */
- { HashEntry *h=hashloc(tok);
- if (h->value.type)
- /* Must be a keyword or special */
- curr_token=h->value;
- else {
- curr_token.type=t_unready;
- curr_token.value.HP=h;
- }
- return 0;
- }
- }
- }
-
-
- /* -------------------------- expanding -------------------------- */
-
-
- /* In a sense, this is the heart of the program. The function
- * |get_x_token()| expands things until it's found a `primitive'
- * token, and then returns with |curr_token| set.
- *
- * Primitive tokens are numbers, colours, keywords, strings, open/close
- * braces (usually) and sometimes token-lists (which are, indeed, much
- * less primitive than the rest).
- *
- * If |seq!=0| and the first primitive token found is a <{>, we
- * collect together all tokens between that and the matching <}>
- * into a token-list.
- */
-
- #define EOFerr(x) { minor("EOF or error in " x " construct"); return 1; }
-
- #ifdef DEBUG_XTOKENS
- static int get_x_token_(int);
- int get_x_token(int seq) {
- int i=get_x_token_(seq);
- show_token(1);
- return i;
- }
- static int get_x_token_(int seq) {
- #else
- int get_x_token(int seq) {
- #endif
- next:
- if (get_token()) return 1;
- again:
- switch(curr_token.type) {
- case t_NoValue: {
- minor("Null token (illegal)");
- goto next; }
- case t_real:
- case t_string:
- case t_colour:
- case t_keyword:
- return 0;
- case t_toklist:
- if (seq) return 0;
- if (toklists_on_stack>=max_toklists_on_stack)
- error("Too many nested token lists");
- tok_stack[toklists_on_stack++]=curr_token.value.PP;
- goto next;
- case t_special:
- /* Warning: this is long. */
- switch(curr_token.value.I) {
- case s_Define: {
- HashEntry *h;
- TwoWords *p=0,*q;
- int depth=0;
- if (get_token()) EOFerr("Define");
- if (curr_token.type!=t_unready)
- error("Illegal Define");
- h=curr_token.value.HP;
- if (get_token()) error("EOF or error in Define construct");
- if (curr_token.type!=t_openbr) error("Illegal Define");
- while (1) {
- if (get_token()) EOFerr("Define");
- if (curr_token.type==t_openbr) ++depth;
- if (curr_token.type==t_closebr && !depth--) break;
- { Token *t=new_token(); TwoWords *u=new_pair();
- *t=curr_token; u->P.first.TP=t;
- if (p) q=(q->P.rest.PP=u); else q=p=u;
- }
- }
- /* add end-of-macro token */
- { Token *t=new_token(); TwoWords *u=new_pair();
- t->type=t_magicEOM;
- u->P.first.TP=t; u->P.rest.PP=0;
- if (p) q->P.rest.PP=u; else p=u; }
- /* now p points to text of macro expansion */
- h->value.type=t_macro;
- h->value.value.PP=p;
- goto next; } /* definition expands to nothing */
- case s_Set: {
- if (get_token()) EOFerr("Set");
- if (curr_token.type==t_local) {
- HashEntry *h=curr_token.value.HP;
- if (!macro_level) { minor("Local variable reference outside macro");
- goto next; }
- if (get_x_token(1)) EOFerr("Set");
- set_local(h,&curr_token);
- goto next; }
- if (curr_token.type==t_global) {
- HashEntry *h=curr_token.value.HP;
- if (get_x_token(1)) EOFerr("Set");
- h->value=curr_token;
- goto next; }
- minor("Attempt to Set a non-variable");
- goto next; }
- case s_IfExists:
- if (get_token()) EOFerr("If");
- if (curr_token.type==t_global || curr_token.type==t_local) {
- if (curr_token.value.HP->value.type) { stack_else; goto next; }
- iffalse: { int i=skip_tokens();
- if (i==99) EOFerr("If");
- if (i==1) stack_endif; }
- goto next;
- }
- else {
- minor("IfExists with non-variable");
- goto iffalse;
- }
- case s_IfLess: {
- double x,y;
- if (get_x_token(0)) EOFerr("If");
- if (curr_token.type!=t_real) { minor("Non-number in IfLess"); x=0; }
- else x=curr_token.value.D;
- if (get_x_token(0)) EOFerr("If");
- if (curr_token.type!=t_real) { minor("Non-number in IfLess"); y=0; }
- else y=curr_token.value.D;
- if (x<y) { stack_else; goto next; }
- else goto iffalse; }
- case s_IfEqual: {
- Token the_other_token;
- int yes;
- if (get_x_token(0)) EOFerr("If");
- the_other_token=curr_token;
- if (get_x_token(0)) EOFerr("If");
- if (curr_token.type!=the_other_token.type) goto iffalse;
- switch(the_other_token.type) {
- case t_keyword:
- case t_colour:
- yes=(curr_token.value.I==the_other_token.value.I); break;
- case t_real:
- yes=(curr_token.value.D==the_other_token.value.D); break;
- case t_string:
- yes=!strcmp(curr_token.value.CP,the_other_token.value.CP); break;
- default:
- minor("Illegal type in IfEqual"); goto iffalse;
- }
- if (yes) { stack_else; goto next; } else goto iffalse; }
- case s_Else:
- if (ifs_on_stack<=0) { minor("Else with no If"); goto next; }
- if (if_stack[--ifs_on_stack]) {
- /* We were waiting for an Else. Skip. */
- while (skip_tokens()) minor("One If has two Elses");
- goto next; }
- /* We were waiting for an EndIf. Moan. */
- minor("One If has two Elses"); ++ifs_on_stack;
- goto next;
- case s_EndIf:
- if (--ifs_on_stack<0) { ++ifs_on_stack; minor("EndIf with no If"); }
- goto next;
- case s_For: {
- HashEntry *h;
- double limit;
- int local;
- if (get_token()) EOFerr("For");
- if (curr_token.type!=t_global && curr_token.type!=t_local) {
- minor("For with non-variable"); goto again; }
- h=curr_token.value.HP;
- if (curr_token.type==t_local) {
- if (!macro_level) {
- minor("Local variable reference outside macro");
- get_x_token(0); get_x_token(0); /* skip numbers */
- goto next; }
- local=1;
- } else local=0;
- if (get_x_token(0)) EOFerr("For");
- if (curr_token.type!=t_real) {
- minor("Non-numeric initial value in For");
- curr_token.type=t_real;
- curr_token.value.D=0; }
- if (local) set_local(h,&curr_token);
- else h->value=curr_token;
- if (get_x_token(0)) EOFerr("For");
- if (curr_token.type!=t_real) {
- minor("Non-numeric limit in For"); limit=0; }
- else limit=curr_token.value.D;
- /* Now make a token list ending with a magicNEXT token. */
- if (get_token()) EOFerr("For");
- if (curr_token.type!=t_openbr) {
- minor("Non-tokenlist as body of For"); goto again; }
- { int depth=0;
- TwoWords *p=0,*q;
- while (1) {
- if (get_token()) EOFerr("For");
- if (curr_token.type==t_openbr) ++depth;
- if (curr_token.type==t_closebr && !depth--) break;
- { Token *t=new_token(); TwoWords *u=new_pair();
- *t=curr_token; u->P.first.TP=t;
- if (p) q=(q->P.rest.PP=u); else q=p=u;
- }
- }
- { Token *t=new_token();
- LoopRecord *l=xmalloc(sizeof(LoopRecord),"a loop record");
- TwoWords *u=new_pair();
- l->var=h; l->limit=limit;
- l->start=p;
- t->type=t_magicNEXT; t->value.LP=l;
- u->P.first.TP=t; u->P.rest.PP=next_token;
- if (p) q->P.rest.PP=u; else p=u;
- }
- next_token=p;
- goto next;
- }
- }
- /* That's the end of the control-structure specials.
- * Now we have the more boring ones that might have been
- * better called operators.
- * Actually there are a few weirdies yet.
- */
- case s_Plus:
- curr_token.value.D=read_double()+read_double();
- curr_token.type=t_real;
- return 0;
- case s_Minus: {
- double x=read_double();
- curr_token.value.D=x-read_double();
- curr_token.type=t_real;
- return 0; }
- case s_Times:
- curr_token.value.D=read_double()*read_double();
- curr_token.type=t_real;
- return 0;
- case s_Over: {
- double x=read_double();
- double y=read_double();
- if (y==0) { minor("Division by zero"); y=1; }
- curr_token.type=t_real;
- curr_token.value.D=x/y;
- return 0; }
- case s_Sqrt: {
- double x=read_double();
- if (x<0) { minor("Negative square root"); x=-x; }
- curr_token.type=t_real;
- curr_token.value.D=sqrt(x);
- return 0; }
- case s_Sin:
- curr_token.value.D=sin(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Cos:
- curr_token.value.D=cos(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Tan:
- curr_token.value.D=tan(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Arcsin:
- curr_token.value.D=asin(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Arccos:
- curr_token.value.D=acos(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Arctan:
- curr_token.value.D=atan(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Arctan2: {
- double x=read_double();
- double y=read_double();
- /* NOTE: the Shared C Library headers and the Shared C Library
- * itself do not agree about what atan2() does.
- * The following is correct given what the function actually does.
- */
- curr_token.value.D=atan2(y,x);
- curr_token.type=t_real;
- return 0; }
- case s_Floor:
- curr_token.value.D=floor(read_double());
- curr_token.type=t_real;
- return 0;
- case s_Include:
- open_new_file(read_string());
- goto next;
- #ifdef TAGS
- case s_TagOpen:
- tag_open(read_string());
- goto next;
- case s_TagLookup: {
- char *s=tag_lookup(read_string());
- if (!s) { minor("Non-existent tag"); s=""; } /* copy_string("")? */
- curr_token.type=t_string;
- curr_token.value.CP=s;
- return 0; }
- case s_TagClose:
- if (!tags) warn("There isn't an open tagfile");
- tag_close();
- goto next;
- #endif
- case s_Append: {
- /* This code is inefficient. Tough. */
- int i,n; char **s; int l=0;
- n=read_int();
- if (n<0) { minor("Negative number of strings"); n=0; }
- s=(char**)xmalloc(n*4,"strings to append");
- for (i=0;i<n;++i)
- l+=strlen(s[i]=read_string());
- curr_token.type=t_string;
- curr_token.value.CP=(char*)xmalloc(l+1,"appended string");
- *curr_token.value.CP=0;
- for (i=0;i<n;++i) strcat(curr_token.value.CP,s[i]);
- xfree(s);
- return 0; }
- case s_GSTrans: {
- char *s=read_string();
- int len=2*strlen(s);
- int t;
- char *buf=xmalloc(len,"GSTrans-ed string");
- while ((t=gstrans(s,buf,len))==1) {
- xfree(buf);
- buf=xmalloc(len<<=1,"GSTrans-ed string"); }
- if (t) { minor("Bad string passed to GSTrans"); *buf=0; }
- curr_token.type=t_string;
- curr_token.value.CP=copy_string(buf);
- xfree(buf);
- return 0; }
- case s_Font: {
- char *s=read_string();
- curr_token.type=t_real;
- curr_token.value.D=(double)font_number(s);
- return 0; }
- case s_Str2Num: {
- /* this code should be essentially the same as that in
- * |get_token()|:
- */
- char *s=read_string();
- double d; char *cp;
- d=strtod(s,&cp);
- if (*cp && *s=='0' && (s[1]=='x'||s[1]=='X'))
- d=(double)strtoul(s+2,&cp,16);
- /* now |*cp==0| iff it was a number */
- curr_token.type=t_real;
- if (*cp) minor("Str2Num requires a number");
- curr_token.value.D=(*cp) ? 0 : d;
- return 0; }
- case s_Num2Str: {
- double d=read_double();
- char s[40]; /* way more than we need */
- sprintf(s,"%.10lg",d);
- /* '-' is not a minus sign; char 153 is */
- { char *cp=s; while (*cp) { if (*cp=='-') *cp=153; ++cp; } }
- curr_token.type=t_string;
- curr_token.value.CP=copy_string(s);
- return 0; }
- case s_Random:
- curr_token.type=t_real;
- curr_token.value.D=rand()/(((double)RAND_MAX)+1);
- return 0;
- case s_Units:
- unit=read_double();
- scaling = (unit!=1.0);
- goto next;
- case s_Unit:
- curr_token.type=t_real;
- curr_token.value.D=unit;
- return 0;
- default:
- error("Unknown `special' token, number=%d",curr_token.value.I);
- }
- /* End of the specials. Back to other token types. */
- #undef EOFerr
- #define EOFerr(x) { minor("EOF or error in " x); return 1; }
- case t_macro: {
- TwoWords *hh=curr_token.value.PP;
- if (macro_level>=max_macro_level)
- error("Too many nested macros");
- if (get_token()) EOFerr("macro invocation");
- if (curr_token.type!=t_openbr) {
- warn("Macro invocation without params");
- goto again; }
- while (1) {
- HashEntry *h;
- if (get_token()) EOFerr("macro invocation");
- if (curr_token.type==t_closebr) break;
- if (curr_token.type!=t_local) {
- minor("Invalid macro parameter");
- continue; }
- h=curr_token.value.HP;
- if (get_x_token(1)) EOFerr("macro invocation");
- if (curr_token.type==t_closebr) curr_token=null_token;
- ++macro_level;
- set_local(h,&curr_token);
- --macro_level;
- }
- ++macro_level;
- macro_context[macro_level].toks_depth=toklists_on_stack;
- macro_context[macro_level].next_tok=next_token;
- next_token=hh;
- n_pps[macro_level]=0;
- goto next; }
- case t_global:
- curr_token=curr_token.value.HP->value;
- goto again;
- case t_local:
- if (!macro_level) {
- minor("Local variable reference outside macro");
- goto next; }
- curr_token=curr_token.value.HP->value;
- curr_token.type&=~0xFF000000; /* remove level indicator */
- goto again;
- case t_localP: {
- int n,m;
- if (!macro_level) {
- minor("Positional parameter reference outside macro");
- goto next; }
- n=curr_token.value.U;
- if (n>(m=n_pps[macro_level])) {
- int od=toklists_on_stack;
- TwoWords *on=next_token;
- toklists_on_stack=macro_context[macro_level].toks_depth;
- next_token=macro_context[macro_level].next_tok;
- do {
- if (get_x_token(1))
- EOFerr("macro invocation (reading pos. param.)");
- set_local(pp_val+m++,&curr_token);
- } while (m<n);
- n_pps[macro_level]=m;
- macro_context[macro_level].toks_depth=toklists_on_stack;
- macro_context[macro_level].next_tok=next_token;
- toklists_on_stack=od;
- next_token=on;
- }
- curr_token=pp_val[n-1].value;
- curr_token.type&=~0xFF000000;
- return 0; }
- case t_openbr:
- if (seq) {
- TwoWords *p=0,*q;
- int depth=0;
- while (1) {
- if (get_token()) EOFerr("token list");
- if (curr_token.type==t_openbr) ++depth;
- if (curr_token.type==t_closebr && !depth--) break;
- { Token *t=new_token(); TwoWords *u=new_pair();
- *t=curr_token; u->P.first.TP=t;
- if (p) q=(q->P.rest.PP=u); else q=p=u;
- }
- }
- if (p) q->P.rest.PP=0;
- curr_token.type=t_toklist;
- curr_token.value.PP=p;
- }
- return 0;
- case t_closebr:
- return 0;
- case t_unready:
- curr_token.type=t_macro;
- curr_token.value.PP=curr_token.value.HP->value.value.PP;
- goto again;
- default:
- minor("Impossible token (type=%d) found during expansion",
- curr_token.type);
- goto next;
- }
- error("This can't happen: fall-through in |get_x_token()|");
- return 1; /* pacify compiler */
- }
- #undef EOFerr
-
-
- /* -------------------------- scaling -------------------------- */
-
-
- /* Unfortunately, the following need to be visible everywhere because
- * some scaling happens at one place in stomach.c.
- */
-
- extern int scaling=0; /* non-zero iff we are using units other than points */
- extern double unit=1; /* size of 1 unit, in points */
-
-
- /* -------------------------- automatic variables -------------------------- */
-
-
- /* Not "automatic" as in C. We set some variables at the very start
- * of the program, because they might be useful.
- * |set_variable()| is not static, because it's used to set text bbox
- * variables for Text and XfText objects.
- */
-
- void set_variable(const char *name, double value) {
- Token *t=&(hashloc(name)->value);
- t->type=t_real;
- t->value.D=value;
- }
-
-
- void init_vars(void) {
- set_variable("$points",1);
- set_variable("$inches",72);
- set_variable("$centimetres",28.3464566929134);
- set_variable("$millimetres",2.83464566929134);
- set_variable("$scaledpoints",1.0/640);
- set_variable("$osunits",72.0/180);
- }
-
-
- /* -------------------------- special readers -------------------------- */
-
-
- /* Each of these functions just reads a single token (with expansion)
- * and makes sure it's of a suitable type.
- */
-
- double read_double(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred, looking for a number"); return 0; }
- if (curr_token.type!=t_real) {
- curr_token.type=t_real;
- minor("Wrong type; number expected"); return 0; }
- return curr_token.value.D;
- }
-
- int read_int(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred, looking for an integer"); return 0; }
- if (curr_token.type!=t_real) {
- curr_token.type=t_real;
- minor("Wrong type; integer expected"); return 0; }
- { double x=curr_token.value.D;
- if (x!=floor(x)) minor("Non-integer occurred when integer expected");
- return (int)x;
- }
- }
-
- char *read_string(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred, looking for a string"); return ""; }
- if (curr_token.type!=t_string) {
- minor("Wrong type; string expected"); return ""; }
- return curr_token.value.CP;
- }
-
- /* |read_real640()| and |read_real1000()| read real numbers and interpret
- * them as dimensions, with units of either 1/640pt or 1/1000pt.
- * |cvt_real640()| and |cvt_real1000()| do the same, but don't actually
- * read a new token first.
- */
-
- static int cvt_real640(void) {
- if (curr_token.type!=t_real) {
- curr_token.type=t_real;
- minor("Wrong type; dimension expected"); return 0; }
- return (int)floor(curr_token.value.D*(scaling?640*unit:640)+.5);
- }
-
- static int cvt_real1000(void) {
- if (curr_token.type!=t_real) {
- curr_token.type=t_real;
- minor("Wrong type; dimension expected"); return 0; }
- return (int)floor(curr_token.value.D*(scaling?1000*unit:1000)+.5);
- }
-
- int read_real640(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred, looking for a dimension"); return 0; }
- return cvt_real640();
- }
-
- int read_real1000(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred, looking for a dimension"); return 0; }
- return cvt_real1000();
- }
-
- /* |read_kwd()| returns the number of the keyword.
- * |read_kwd_or_cbr()| returns -1 for close-brace (or EOF, with error)
- */
-
- int read_kwd(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred when keyword expected"); return k_Illegal; }
- if (curr_token.type!=t_keyword) {
- minor("Wrong type; keyword expected"); return k_Illegal; }
- return curr_token.value.U;
- }
-
- int read_kwd_or_cbr(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred when keyword or `}' expected");
- return -1; } /* so loops terminate on EOF */
- /* } */
- if (curr_token.type==t_closebr) return -1;
- if (curr_token.type!=t_keyword) {
- minor("Wrong type: keyword expected"); return k_Illegal; }
- return curr_token.value.I;
- }
-
- /* |read_openbr()| and |read_closebr()| do just what you might
- * expect.
- */
-
- void read_openbr(void) {
- if (get_x_token(0)) minor("EOF or error occurred when `{' expected");
- if (curr_token.type!=t_openbr) minor("Wrong type; `{' expected");
- /* }} */
- }
-
- void read_closebr(void) {
- /* {{ */
- if (get_x_token(0)) minor("EOF or error occurred when `}' expected");
- if (curr_token.type!=t_closebr) minor("Wrong type; `}' expected");
- }
-
- /* |read_colour()| returns an integer representing a colour in the usual
- * RISC OS way, namely 0xBBGGRR00 or -1 for transparent.
- */
- int read_colour(void) {
- if (get_x_token(0)) {
- minor("EOF or error occurred when colour expected"); return -1; }
- if (curr_token.type!=t_colour) {
- int i;
- if (curr_token.type==t_real
- && (double)(i=(int)floor(curr_token.value.D))==curr_token.value.D
- && i>=0 && i<256) {
- int j,k;
- if ((j=read_int())>=0 && j<256 &&
- (k=read_int())>=0 && k<256) return (i<<8)+(j<<16)+(k<<24);
- minor("Malformed 3-component colour"); return -1; }
- minor("Wrong type; colour expected"); return -1; }
- return curr_token.value.U;
- }
-
-
- /* -------------------------- initialising htable -------------------------- */
-
-
- #define insert_key(str,num) { HashEntry *h=hashloc(str);\
- h->value.type=t_keyword; h->value.value.I=num; }
-
- #define insert_spec(str,num) { HashEntry *h=hashloc(str);\
- h->value.type=t_special; h->value.value.I=num; }
-
- void init_global_hash(void) {
- insert_key("header",k_Header);
- insert_key("fonttable",k_FontTable);
- insert_key("text",k_Text);
- insert_key("path",k_Path);
- insert_key("sprite",k_Sprite);
- insert_key("group",k_Group);
- insert_key("tagged",k_Tagged);
- insert_key("textarea",k_TextArea);
- insert_key("column",k_Column);
- insert_key("options",k_Options);
- insert_key("xftext",k_XfText);
- insert_key("xfsprite",k_XfSprite);
- insert_key("version",k_Version);
- insert_key("creator",k_Creator);
- insert_key("boundingbox",k_BoundingBox);
- insert_key("colour",k_Colour);
- insert_key("background",k_Background);
- insert_key("style",k_Style);
- insert_key("size",k_Size);
- insert_key("startat",k_StartAt);
- insert_key("fillcolour",k_FillColour);
- insert_key("outlinecolour",k_OutlineColour);
- insert_key("width",k_Width);
- insert_key("move",k_Move);
- insert_key("close",k_Close);
- insert_key("line",k_Line);
- insert_key("curve",k_Curve);
- insert_key("rmove",k_RMove);
- insert_key("rline",k_RLine);
- insert_key("rcurve",k_RCurve);
- insert_key("mitred",k_Mitred);
- insert_key("round",k_Round);
- insert_key("bevelled",k_Bevelled);
- insert_key("endcap",k_EndCap);
- insert_key("startcap",k_StartCap);
- insert_key("butt",k_Butt);
- insert_key("square",k_Square);
- insert_key("triangular",k_Triangular);
- insert_key("windingrule",k_WindingRule);
- insert_key("dash",k_Dash);
- insert_key("capwidth",k_CapWidth);
- insert_key("caplength",k_CapLength);
- insert_key("nonzero",k_NonZero);
- insert_key("evenodd",k_EvenOdd);
- insert_key("offset",k_Offset);
- insert_key("pattern",k_Pattern);
- insert_key("name",k_Name);
- insert_key("identifier",k_Identifier);
- insert_key("otherdata",k_OtherData);
- insert_key("matrix",k_Matrix);
- insert_key("kerned",k_Kerned);
- insert_key("righttoleft",k_RightToLeft);
- insert_key("papersize",k_PaperSize);
- insert_key("limits",k_Limits);
- insert_key("grid",k_Grid);
- insert_key("zoom",k_Zoom);
- insert_key("notoolbox",k_NoToolbox);
- insert_key("mode",k_Mode);
- insert_key("undosize",k_UndoSize);
- insert_key("shown",k_Shown);
- insert_key("landscape",k_Landscape);
- insert_key("nondefault",k_NonDefault);
- insert_key("spacing",k_Spacing);
- insert_key("divisions",k_Divisions);
- insert_key("isometric",k_Isometric);
- insert_key("autoadjust",k_AutoAdjust);
- insert_key("lock",k_Lock);
- insert_key("inches",k_Inches);
- insert_key("ratio",k_Ratio);
- insert_key("closedline",k_ClosedLine);
- insert_key("closedcurve",k_ClosedCurve);
- insert_key("rectangle",k_Rectangle);
- insert_key("ellipse",k_Ellipse);
- insert_key("select",k_Select);
- insert_key("fromfile",k_FromFile);
- insert_key("hcentrein",k_HCentreIn);
- insert_key("centrein",k_CentreIn);
- insert_key("hcentreon",k_HCentreOn);
- insert_key("centreat",k_CentreAt);
- insert_key("virtual",k_Virtual);
- #ifndef NO_JPEG
- insert_key("jpeg",k_JPEG);
- insert_key("dpi",k_DPI);
- insert_key("length",k_Length);
- #endif
- insert_spec("define",s_Define);
- insert_spec("set",s_Set);
- insert_spec("ifexists",s_IfExists);
- insert_spec("ifless",s_IfLess);
- insert_spec("ifequal",s_IfEqual);
- insert_spec("else",s_Else);
- insert_spec("endif",s_EndIf);
- insert_spec("for",s_For);
- insert_spec("plus",s_Plus);
- insert_spec("minus",s_Minus);
- insert_spec("times",s_Times);
- insert_spec("over",s_Over);
- insert_spec("sqrt",s_Sqrt);
- insert_spec("sin",s_Sin);
- insert_spec("cos",s_Cos);
- insert_spec("tan",s_Tan);
- insert_spec("arcsin",s_Arcsin);
- insert_spec("arccos",s_Arccos);
- insert_spec("arctan",s_Arctan);
- insert_spec("arctan2",s_Arctan2);
- insert_spec("floor",s_Floor);
- insert_spec("include",s_Include);
- #ifdef TAGS
- insert_spec("tagopen",s_TagOpen);
- insert_spec("taglookup",s_TagLookup);
- insert_spec("tagclose",s_TagClose);
- #endif
- insert_spec("append",s_Append);
- insert_spec("gstrans",s_GSTrans);
- insert_spec("font",s_Font);
- insert_spec("str2num",s_Str2Num);
- insert_spec("num2str",s_Num2Str);
- insert_spec("random",s_Random);
- insert_spec("units",s_Units);
- }
-
-
- /* -------------------------- -------------------------- */
- /* -------------------------- -------------------------- */
- /* -------------------------- -------------------------- */
- /* -------------------------- -------------------------- */
- /* -------------------------- -------------------------- */
- /* -------------------------- -------------------------- */
-