mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-04 14:20:26 +02:00
Random bug fixes
This commit is contained in:
parent
edb3b2ca56
commit
37f2e24212
1 changed files with 0 additions and 986 deletions
|
@ -1,986 +0,0 @@
|
||||||
/* guile-snarf.c --- extract declarations from Guile source code
|
|
||||||
Jim Blandy <jimb@red-bean.com> --- September 1999
|
|
||||||
|
|
||||||
Copyright (C) 1999 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
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 2, 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 software; see the file COPYING. If not, write to
|
|
||||||
the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
||||||
Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
As a special exception, the Free Software Foundation gives permission
|
|
||||||
for additional uses of the text contained in its release of GUILE.
|
|
||||||
|
|
||||||
The exception is that, if you link the GUILE library with other files
|
|
||||||
to produce an executable, this does not by itself cause the
|
|
||||||
resulting executable to be covered by the GNU General Public License.
|
|
||||||
Your use of that executable is in no way restricted on account of
|
|
||||||
linking the GUILE library code into it.
|
|
||||||
|
|
||||||
This exception does not however invalidate any other reasons why
|
|
||||||
the executable file might be covered by the GNU General Public License.
|
|
||||||
|
|
||||||
This exception applies only to the code released by the
|
|
||||||
Free Software Foundation under the name GUILE. If you copy
|
|
||||||
code from other Free Software Foundation releases into a copy of
|
|
||||||
GUILE, as the General Public License permits, the exception does
|
|
||||||
not apply to the code that you add in this way. To avoid misleading
|
|
||||||
anyone as to the status of such modified files, you must delete
|
|
||||||
this exception notice from them.
|
|
||||||
|
|
||||||
If you write modifications of your own for GUILE, it is your choice
|
|
||||||
whether to permit this exception to apply to your modifications.
|
|
||||||
If you do not wish that, delete this exception notice. */
|
|
||||||
|
|
||||||
|
|
||||||
/* The problem:
|
|
||||||
|
|
||||||
It's really nice to be able to create Scheme procedures by simply
|
|
||||||
adding a declaration above the C function's definition. And you
|
|
||||||
can use similar facilities for variables, symbols, etc.
|
|
||||||
|
|
||||||
Using the C preprocessor to extract declarations from the C code is
|
|
||||||
a clever idea, but it's not very robust. Finding the C
|
|
||||||
preprocessor, and getting it to run without errors, is too prone to
|
|
||||||
interference from outside influences. We end up having to pass all
|
|
||||||
the -I flags and compiler flags to guile-snarf, whose job doesn't
|
|
||||||
have anything to do with them.
|
|
||||||
|
|
||||||
Thus, we redefine snarfing as a process which operates on a single
|
|
||||||
text file, and ignores #inclusion.
|
|
||||||
|
|
||||||
The general strategy:
|
|
||||||
|
|
||||||
Scan the source file in just enough detail to correctly recognize C
|
|
||||||
identifiers or reserved words. This means watching for comments,
|
|
||||||
string and character literals, etc.
|
|
||||||
|
|
||||||
When we see an identifier from our selected list, parse an argument
|
|
||||||
list after it according to the C preprocessor's rules, and then
|
|
||||||
pass it to an appropriate function to handle.
|
|
||||||
|
|
||||||
Still to do:
|
|
||||||
|
|
||||||
- Test by comparing output with old shell script.
|
|
||||||
- Add a test for this to Makefile.am.
|
|
||||||
- Change build process to actually use this.
|
|
||||||
- Make sure that errors cause .x file to be deleted. `make' will do
|
|
||||||
this if the process exits with a non-zero status, won't it? If not,
|
|
||||||
delete it ourselves.
|
|
||||||
- Could we generate both the declarations and the initializations?
|
|
||||||
- Can we simplify or improve the argument lists for some of the
|
|
||||||
snarfing keywords, now that we can parse them any way we like?
|
|
||||||
- When a file declares a *lot* of something, would it save space to
|
|
||||||
emit a table and a loop, instead of a zillion function calls? */
|
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
#include "versiondat.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Utility functions. */
|
|
||||||
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
static void *
|
|
||||||
check_ptr (void *p)
|
|
||||||
{
|
|
||||||
if (! p)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: out of memory\n", program_name);
|
|
||||||
exit (2);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
xmalloc (size_t size)
|
|
||||||
{
|
|
||||||
return check_ptr (malloc (size));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
xrealloc (void *ptr, size_t size)
|
|
||||||
{
|
|
||||||
return check_ptr (realloc (ptr, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
system_error (char *message, char *filename)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: ", program_name);
|
|
||||||
if (filename) fprintf (stderr, "%s: ", filename);
|
|
||||||
perror (message);
|
|
||||||
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Character table. */
|
|
||||||
|
|
||||||
struct chartable {
|
|
||||||
|
|
||||||
enum action {
|
|
||||||
|
|
||||||
/* Just skip this character. */
|
|
||||||
act_normal,
|
|
||||||
|
|
||||||
/* This character is whitespace. */
|
|
||||||
act_space,
|
|
||||||
|
|
||||||
/* This character begins and ends a string/char literal. */
|
|
||||||
act_literal,
|
|
||||||
|
|
||||||
/* This character is '/'. If followed by a '*', begin a block
|
|
||||||
comment. If followed by another '/', begin a line comment. */
|
|
||||||
act_comment_start,
|
|
||||||
|
|
||||||
/* This character increments the nesting level, and must be
|
|
||||||
matched by the character in `follow'. */
|
|
||||||
act_open,
|
|
||||||
|
|
||||||
/* This character decrements the nesting level, if it matches properly. */
|
|
||||||
act_close,
|
|
||||||
|
|
||||||
/* This character could appear anywhere in an identifier. */
|
|
||||||
act_id_start,
|
|
||||||
|
|
||||||
/* This character could appear anywhere in an identifier, except
|
|
||||||
as the first character. */
|
|
||||||
act_id_follow,
|
|
||||||
|
|
||||||
/* This character separates arguments in a keyword argument list,
|
|
||||||
if it occurs at the outermost nesting level. */
|
|
||||||
act_arg_separator
|
|
||||||
|
|
||||||
} action;
|
|
||||||
|
|
||||||
int follow;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct chartable chartable[256];
|
|
||||||
#define CHARTABLE(i) (chartable[(unsigned) (i)])
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
match (char open, char close)
|
|
||||||
{
|
|
||||||
CHARTABLE (open).action = act_open;
|
|
||||||
CHARTABLE (open).follow = close;
|
|
||||||
CHARTABLE (close).action = act_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_chartable ()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
char *c;
|
|
||||||
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
CHARTABLE (i).action = act_normal;
|
|
||||||
|
|
||||||
for (c = " \t\n\f"; *c; c++)
|
|
||||||
CHARTABLE (*c).action = act_space;
|
|
||||||
|
|
||||||
CHARTABLE ('"' ).action = act_literal;
|
|
||||||
CHARTABLE ('\'').action = act_literal;
|
|
||||||
|
|
||||||
CHARTABLE ('/').action = act_comment_start;
|
|
||||||
|
|
||||||
match ('(', ')');
|
|
||||||
match ('[', ']');
|
|
||||||
match ('{', '}');
|
|
||||||
|
|
||||||
CHARTABLE (',').action = act_arg_separator;
|
|
||||||
|
|
||||||
for (i = 'a'; i <= 'z'; i++)
|
|
||||||
CHARTABLE (i).action = act_id_start;
|
|
||||||
for (i = 'A'; i <= 'Z'; i++)
|
|
||||||
CHARTABLE (i).action = act_id_start;
|
|
||||||
CHARTABLE ('_').action = act_id_start;
|
|
||||||
|
|
||||||
for (i = '0'; i <= '9'; i++)
|
|
||||||
CHARTABLE (i).action = act_id_follow;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Reading text while tracking the current line number. */
|
|
||||||
|
|
||||||
/* The input file and the line number are global, because it's too
|
|
||||||
much of a pain to pass them around everywhere as arguments. Sorry. */
|
|
||||||
|
|
||||||
FILE *in; /* The current input file. */
|
|
||||||
char *in_name; /* Its filename. */
|
|
||||||
int in_line; /* The current line number in that file. */
|
|
||||||
|
|
||||||
FILE *out; /* The current output file. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
source_getc ()
|
|
||||||
{
|
|
||||||
int c = getc (in);
|
|
||||||
if (c == '\n')
|
|
||||||
in_line++;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
source_ungetc (int c)
|
|
||||||
{
|
|
||||||
if (c == '\n')
|
|
||||||
in_line--;
|
|
||||||
ungetc (c, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
syntax_error (char *message, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start (args, message);
|
|
||||||
|
|
||||||
fprintf (stderr, "%s:%d: ", in_name, in_line);
|
|
||||||
vfprintf (stderr, message, args);
|
|
||||||
va_end (args);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Skipping comments. */
|
|
||||||
|
|
||||||
/* If we've read the first character of a comment start (that is, the
|
|
||||||
character '/'), then check the second character. If it's '*',
|
|
||||||
we're in a block comment; read to the end of it. If it's '/',
|
|
||||||
we're starting a line comment; skip to the end of the line.
|
|
||||||
Otherwise, put the second character back on the input stream.
|
|
||||||
|
|
||||||
Return non-zero if this was a comment, zero otherwise. */
|
|
||||||
static int
|
|
||||||
maybe_skip_comment ()
|
|
||||||
{
|
|
||||||
int d = source_getc ();
|
|
||||||
|
|
||||||
if (d == '*')
|
|
||||||
{
|
|
||||||
int prev;
|
|
||||||
int start_line = in_line;
|
|
||||||
|
|
||||||
/* Make sure the asterisk of the comment start doesn't
|
|
||||||
get mistaken for the asterisk of the comment end. */
|
|
||||||
d = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
prev = d;
|
|
||||||
d = source_getc ();
|
|
||||||
}
|
|
||||||
while (d != EOF && ! (prev == '*' && d == '/'));
|
|
||||||
|
|
||||||
if (d == EOF)
|
|
||||||
{
|
|
||||||
in_line = start_line;
|
|
||||||
syntax_error ("unterminated comment");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (d == '/')
|
|
||||||
{
|
|
||||||
do
|
|
||||||
d = source_getc ();
|
|
||||||
while (d != '\n' && d != EOF);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
source_ungetc (d);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Parsing argument lists for keywords. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
arglist_getc (char *keyword)
|
|
||||||
{
|
|
||||||
int c = source_getc ();
|
|
||||||
if (c == EOF)
|
|
||||||
syntax_error ("file ends in midst of arguments to keyword `%s'\n",
|
|
||||||
keyword);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct buffer {
|
|
||||||
int size, len;
|
|
||||||
char *text;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_buffer (struct buffer *b)
|
|
||||||
{
|
|
||||||
b->size = 1;
|
|
||||||
b->text = (char *) xmalloc (b->size);
|
|
||||||
b->len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_char (struct buffer *b, int c)
|
|
||||||
{
|
|
||||||
if (b->len >= b->size)
|
|
||||||
{
|
|
||||||
b->size *= 2;
|
|
||||||
b->text = (char *) xrealloc (b->text, b->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
b->text[b->len++] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse a string or character literal, appending its text to b. */
|
|
||||||
static void
|
|
||||||
read_literal (char *keyword, struct buffer *b, int start)
|
|
||||||
{
|
|
||||||
add_char (b, start);
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int c = arglist_getc (keyword);
|
|
||||||
add_char (b, c);
|
|
||||||
if (c == '\\')
|
|
||||||
add_char (b, arglist_getc (keyword));
|
|
||||||
else if (c == start)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Parse the argument list following a keyword, just as the
|
|
||||||
C preprocessor would parse the arguments to a macro invocation.
|
|
||||||
Return the arguments as an array of strings, terminated by a null
|
|
||||||
pointer.
|
|
||||||
|
|
||||||
If there's an error parsing the arguments, print an error message
|
|
||||||
using KEYWORD as the keyword name, and exit.
|
|
||||||
|
|
||||||
Caller must free the array and each string; see the free_args
|
|
||||||
function. */
|
|
||||||
static char **
|
|
||||||
parse_args (char *keyword)
|
|
||||||
{
|
|
||||||
/* Our read-ahead character. */
|
|
||||||
int c = arglist_getc (keyword);
|
|
||||||
|
|
||||||
/* String of closing parens we are expecting, outermost first. */
|
|
||||||
struct buffer stack;
|
|
||||||
|
|
||||||
/* Current list of arguments. */
|
|
||||||
int arglist_size = 1;
|
|
||||||
char **arglist = (char **) xmalloc (arglist_size * sizeof (*arglist));
|
|
||||||
int arglist_len = 0;
|
|
||||||
|
|
||||||
init_buffer (&stack);
|
|
||||||
|
|
||||||
/* Skip any initial whitespace. */
|
|
||||||
while (CHARTABLE (c).action == act_space)
|
|
||||||
c = arglist_getc (keyword);
|
|
||||||
|
|
||||||
/* Require an opening paren. */
|
|
||||||
if (c != '(')
|
|
||||||
syntax_error ("arguments to snarfing keyword `%s' missing", keyword);
|
|
||||||
add_char (&stack, ')');
|
|
||||||
c = arglist_getc (keyword);
|
|
||||||
|
|
||||||
/* Read arguments, separated by commas outside of any (), {}, or []
|
|
||||||
pairs. */
|
|
||||||
while (stack.len > 0)
|
|
||||||
{
|
|
||||||
/* start a new argument. */
|
|
||||||
struct buffer arg;
|
|
||||||
int arg_incomplete = 1;
|
|
||||||
|
|
||||||
init_buffer (&arg);
|
|
||||||
|
|
||||||
/* Skip whitespace. */
|
|
||||||
while (CHARTABLE (c).action == act_space)
|
|
||||||
c = arglist_getc (keyword);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
switch (CHARTABLE (c).action)
|
|
||||||
{
|
|
||||||
case act_normal:
|
|
||||||
case act_space:
|
|
||||||
case act_id_start:
|
|
||||||
case act_id_follow:
|
|
||||||
add_char (&arg, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_literal:
|
|
||||||
read_literal (keyword, &arg, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_comment_start:
|
|
||||||
if (maybe_skip_comment ())
|
|
||||||
add_char (&arg, ' ');
|
|
||||||
else
|
|
||||||
add_char (&arg, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_open:
|
|
||||||
add_char (&stack, CHARTABLE (c).follow);
|
|
||||||
add_char (&arg, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_close:
|
|
||||||
if (stack.len == 0)
|
|
||||||
/* We should have finished argument list parsing
|
|
||||||
when the stack became empty. */
|
|
||||||
abort ();
|
|
||||||
if (c != stack.text[stack.len - 1])
|
|
||||||
syntax_error ("mismatched parenthesis: '%c' and '%c'",
|
|
||||||
stack.text[stack.len - 1], c);
|
|
||||||
stack.len--;
|
|
||||||
/* Closing parens are part of the argument, except for the
|
|
||||||
outermost closing paren. */
|
|
||||||
if (stack.len > 0)
|
|
||||||
add_char (&arg, c);
|
|
||||||
else
|
|
||||||
arg_incomplete = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_arg_separator:
|
|
||||||
/* Commas are part of the argument, unless they occur at
|
|
||||||
the top level within the argument list. */
|
|
||||||
if (stack.len == 1)
|
|
||||||
arg_incomplete = 0;
|
|
||||||
else
|
|
||||||
add_char (&arg, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
c = arglist_getc (keyword);
|
|
||||||
}
|
|
||||||
while (arg_incomplete);
|
|
||||||
|
|
||||||
/* Add this argument to the list. */
|
|
||||||
add_char (&arg, '\0');
|
|
||||||
if (arglist_len >= arglist_size)
|
|
||||||
{
|
|
||||||
arglist_size *= 2;
|
|
||||||
arglist = (char **) xrealloc (arglist,
|
|
||||||
arglist_size * sizeof (*arglist));
|
|
||||||
}
|
|
||||||
arglist[arglist_len++] = arg.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Null-terminate the argument list. */
|
|
||||||
arglist = (char **) xrealloc (arglist,
|
|
||||||
(arglist_len + 1) * sizeof (*arglist));
|
|
||||||
arglist[arglist_len] = 0;
|
|
||||||
return arglist;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
count_args (char **args)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; args[i]; i++)
|
|
||||||
;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_args (char **args)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; args[i]; i++)
|
|
||||||
free (args[i]);
|
|
||||||
free (args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Individual routines for processing keywords. */
|
|
||||||
|
|
||||||
/* Flags these routines might use to select details of their behavior. */
|
|
||||||
enum keyword_flags {
|
|
||||||
kw_gsubr = 0x01, /* create a gsubr */
|
|
||||||
kw_generic = 0x02, /* create a generic function */
|
|
||||||
kw_global = 0x04, /* global declaration, not static */
|
|
||||||
kw_init = 0x08, /* initialized value */
|
|
||||||
kw_long = 0x10, /* initialize it with a long */
|
|
||||||
kw_keyword = 0x20 /* make a keyword, not a symbol */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_arg_count (char *keyword, char **args, int expected)
|
|
||||||
{
|
|
||||||
int actual = count_args (args);
|
|
||||||
if (actual != expected)
|
|
||||||
syntax_error ("keyword `%s' expects %d args, but got %d",
|
|
||||||
keyword, expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
proc_keyword (char *keyword, char **args, int flags)
|
|
||||||
{
|
|
||||||
int expected_args;
|
|
||||||
|
|
||||||
if (flags & kw_gsubr)
|
|
||||||
expected_args = 6;
|
|
||||||
else
|
|
||||||
expected_args = 4;
|
|
||||||
|
|
||||||
if (flags & kw_generic)
|
|
||||||
expected_args++;
|
|
||||||
|
|
||||||
check_arg_count (keyword, args, expected_args);
|
|
||||||
|
|
||||||
/* Print some nice indentation. */
|
|
||||||
fputs (" ", out);
|
|
||||||
|
|
||||||
/* Print out the function name. */
|
|
||||||
fprintf (out, "scm_make_%s%s",
|
|
||||||
(flags & kw_gsubr) ? "gsubr" : "subr",
|
|
||||||
(flags & kw_generic) ? "_with_generic" : "");
|
|
||||||
|
|
||||||
/* And the arguments. */
|
|
||||||
fputs (" (", out);
|
|
||||||
if (flags & kw_gsubr)
|
|
||||||
fprintf (out, "%s, %s, %s, %s, (SCM (*) (...)) %s",
|
|
||||||
args[0], args[2], args[3], args[4], args[5]);
|
|
||||||
else
|
|
||||||
fprintf (out, "%s, %s, (SCM (*) (...)) %s",
|
|
||||||
args[0], args[2], args[3]);
|
|
||||||
|
|
||||||
/* The generic versions have an extra argument at the end,
|
|
||||||
which is a pointer to a generic function variable. */
|
|
||||||
if (flags & kw_generic)
|
|
||||||
fprintf (out, ", &%s", (flags & kw_gsubr) ? args[6] : args[4]);
|
|
||||||
|
|
||||||
fputs (");\n", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
syntax_keyword (char *keyword, char **args, int flags)
|
|
||||||
{
|
|
||||||
check_arg_count (keyword, args, 4);
|
|
||||||
|
|
||||||
fprintf (out, " scm_make_synt (%s, %s, %s);\n",
|
|
||||||
args[0], args[2], args[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
symbol_keyword (char *keyword, char **args, int flags)
|
|
||||||
{
|
|
||||||
check_arg_count (keyword, args, 2);
|
|
||||||
|
|
||||||
fprintf (out, " %s = scm_permanent_object (", args[0]);
|
|
||||||
if (flags & kw_keyword)
|
|
||||||
fprintf (out, "scm_c_make_keyword (%s)", args[1]);
|
|
||||||
else
|
|
||||||
fprintf (out, "SCM_CAR (scm_intern0 (%s))", args[1]);
|
|
||||||
fputs (");\n", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vcell_keyword (char *keyword, char **args, int flags)
|
|
||||||
{
|
|
||||||
check_arg_count (keyword, args, (flags & kw_init) ? 3 : 2);
|
|
||||||
|
|
||||||
fprintf (out, " %s = scm_permanent_object (scm_intern0 (%s));\n",
|
|
||||||
args[0], args[1]);
|
|
||||||
fprintf (out, " SCM_SETCDR (%s, ", args[0]);
|
|
||||||
|
|
||||||
if (flags & kw_long)
|
|
||||||
fprintf (out, "scm_long2num (%s)", args[2]);
|
|
||||||
else if (flags & kw_init)
|
|
||||||
fputs (args[2], out);
|
|
||||||
else
|
|
||||||
fputs ("SCM_BOOL_F", out);
|
|
||||||
|
|
||||||
fputs (");\n", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* The keyword table. */
|
|
||||||
|
|
||||||
struct keyword {
|
|
||||||
char *name;
|
|
||||||
void (*func) (char *keyword, char **args, int flags);
|
|
||||||
int flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The maximum length of any keyword, in bytes. */
|
|
||||||
#define MAX_KEYWORD_LEN (20)
|
|
||||||
|
|
||||||
struct keyword keywords[] =
|
|
||||||
{
|
|
||||||
{ "SCM_PROC", proc_keyword, kw_gsubr },
|
|
||||||
{ "SCM_GPROC", proc_keyword, kw_gsubr | kw_generic },
|
|
||||||
{ "SCM_PROC1", proc_keyword, 0 },
|
|
||||||
{ "SCM_GPROC1", proc_keyword, kw_generic },
|
|
||||||
|
|
||||||
{ "SCM_SYNTAX", syntax_keyword, 0 },
|
|
||||||
|
|
||||||
{ "SCM_SYMBOL", symbol_keyword, 0 },
|
|
||||||
{ "SCM_GLOBAL_SYMBOL", symbol_keyword, kw_global },
|
|
||||||
{ "SCM_KEYWORD", symbol_keyword, kw_keyword },
|
|
||||||
{ "SCM_GLOBAL_KEYWORD", symbol_keyword, kw_keyword | kw_global },
|
|
||||||
|
|
||||||
{ "SCM_VCELL", vcell_keyword, 0 },
|
|
||||||
{ "SCM_GLOBAL_VCELL", vcell_keyword, kw_global },
|
|
||||||
{ "SCM_VCELL_INIT", vcell_keyword, kw_init },
|
|
||||||
{ "SCM_GLOBAL_VCELL_INIT", vcell_keyword, kw_init | kw_global },
|
|
||||||
{ "SCM_CONST_LONG", vcell_keyword, kw_init | kw_long },
|
|
||||||
|
|
||||||
{ 0, 0, 0 }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The bigger this is, the less likely a random identifier is
|
|
||||||
to clash with the user's program. */
|
|
||||||
#define KEYWORD_HASH_SIZE (1009)
|
|
||||||
struct keyword *keyword_hash[KEYWORD_HASH_SIZE];
|
|
||||||
|
|
||||||
static unsigned long
|
|
||||||
hash (char *text)
|
|
||||||
{
|
|
||||||
long h = 0;
|
|
||||||
|
|
||||||
while (*text)
|
|
||||||
{
|
|
||||||
unsigned char c = (unsigned char) *text++;
|
|
||||||
h = (h << 4) + c + (c << 9) + (h >> 24) + 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct keyword *
|
|
||||||
is_keyword (char *name)
|
|
||||||
{
|
|
||||||
unsigned long h = hash (name) % KEYWORD_HASH_SIZE;
|
|
||||||
if (keyword_hash[h])
|
|
||||||
{
|
|
||||||
if (strcmp (name, keyword_hash[h]->name))
|
|
||||||
{
|
|
||||||
/* Just for testing. */
|
|
||||||
fprintf (stderr, "%s: keyword/user id hash collision: %s and %s\n",
|
|
||||||
program_name, keyword_hash[h]->name, name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyword_hash[h];
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
process_keyword (struct keyword *k)
|
|
||||||
{
|
|
||||||
char **args = parse_args (k->name);
|
|
||||||
k->func (k->name, args, k->flags);
|
|
||||||
free_args (args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_keyword_hash_table ()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; keywords[i].func; i++)
|
|
||||||
{
|
|
||||||
unsigned long h = hash (keywords[i].name) % KEYWORD_HASH_SIZE;
|
|
||||||
if (keyword_hash[h])
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: keyword hash collision: %s and %s\n",
|
|
||||||
program_name, keyword_hash[h]->name, keywords[i].name);
|
|
||||||
exit (2);
|
|
||||||
}
|
|
||||||
keyword_hash[h] = &keywords[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Scanning a file of C code. */
|
|
||||||
|
|
||||||
/* Skip a string or character literal that started with the character
|
|
||||||
FOLLOW. */
|
|
||||||
static void
|
|
||||||
skip_literal (int follow)
|
|
||||||
{
|
|
||||||
int start_line = in_line;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int c = source_getc ();
|
|
||||||
|
|
||||||
if (c == EOF)
|
|
||||||
{
|
|
||||||
in_line = start_line;
|
|
||||||
syntax_error ("unterminated character or string literal");
|
|
||||||
}
|
|
||||||
else if (c == '\\')
|
|
||||||
source_getc ();
|
|
||||||
else if (c == follow)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
read_id (char *buf, size_t size)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int c = source_getc ();
|
|
||||||
int action;
|
|
||||||
|
|
||||||
if (c == EOF)
|
|
||||||
break;
|
|
||||||
|
|
||||||
action = CHARTABLE (c).action;
|
|
||||||
if (action == act_id_start || action == act_id_follow)
|
|
||||||
{
|
|
||||||
if (i < size)
|
|
||||||
buf[i] = c;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
source_ungetc (c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* It doesn't matter that we truncate the keyword to SIZE-1
|
|
||||||
characters, since the buffer is large enough to hold any valid
|
|
||||||
keyword. */
|
|
||||||
buf[(i >= size) ? size - 1 : i] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
process_stream ()
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
c = source_getc ();
|
|
||||||
if (c == EOF)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (CHARTABLE (c).action)
|
|
||||||
{
|
|
||||||
case act_normal:
|
|
||||||
case act_space:
|
|
||||||
case act_open:
|
|
||||||
case act_close:
|
|
||||||
case act_id_follow:
|
|
||||||
case act_arg_separator:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_literal:
|
|
||||||
skip_literal (c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_comment_start:
|
|
||||||
maybe_skip_comment ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case act_id_start:
|
|
||||||
{
|
|
||||||
char buf[MAX_KEYWORD_LEN + 1];
|
|
||||||
struct keyword *k;
|
|
||||||
|
|
||||||
source_ungetc (c);
|
|
||||||
read_id (buf, sizeof (buf));
|
|
||||||
k = is_keyword (buf);
|
|
||||||
if (k)
|
|
||||||
process_keyword (k);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
process_file (char *inname, char *outname)
|
|
||||||
{
|
|
||||||
in = fopen (inname, "r");
|
|
||||||
if (! in)
|
|
||||||
system_error ("error opening input file", inname);
|
|
||||||
in_line = 1;
|
|
||||||
in_name = inname;
|
|
||||||
|
|
||||||
out = fopen (outname, "w");
|
|
||||||
if (! out)
|
|
||||||
system_error ("error opening output file", outname);
|
|
||||||
|
|
||||||
process_stream (in, out);
|
|
||||||
|
|
||||||
/* I'm told the AIX C preprocessor doesn't like to #include empty files. */
|
|
||||||
putc ('\n', out);
|
|
||||||
|
|
||||||
if (ferror (in))
|
|
||||||
system_error ("error reading input file", inname);
|
|
||||||
if (ferror (out))
|
|
||||||
system_error ("error writing output file", outname);
|
|
||||||
|
|
||||||
if (fclose (in) == EOF)
|
|
||||||
system_error ("error closing input file", inname);
|
|
||||||
if (fclose (out) == EOF)
|
|
||||||
system_error ("error closing output file", outname);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* The main function. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
version ()
|
|
||||||
{
|
|
||||||
fprintf (stderr, "guile-snarf (Guile) %s\n", GUILE_VERSION);
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
usage ()
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Usage: %s [-o OUTFILE] INFILE\n", program_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
help ()
|
|
||||||
{
|
|
||||||
fprintf (stderr, "guile-snarf -- extract declarations from Guile C code.\n");
|
|
||||||
usage ();
|
|
||||||
fprintf (stderr, "\
|
|
||||||
This program makes it easier to write C code which defines Scheme\n\
|
|
||||||
functions and variables and uses Scheme symbols in Guile. Instead of\n\
|
|
||||||
writing code yourself to build the Scheme objects when your code is\n\
|
|
||||||
initialized, you can write brief declarations above your C functions\n\
|
|
||||||
indicating how they should be called from Scheme. Then, if you run\n\
|
|
||||||
this program on your source file, it will write code for you that\n\
|
|
||||||
defines functions, builds Scheme objects, and so on. You can then\n\
|
|
||||||
#include this program's output into your initialization function.\n\
|
|
||||||
\n\
|
|
||||||
`INFILE' is the file from which we should extract declarations and\n\
|
|
||||||
generate initialization code.\n\
|
|
||||||
\n\
|
|
||||||
`-o OUTFILE' specifies the file where we should place the output. If\n\
|
|
||||||
omitted, it defaults to `INFILE', with its extension changed to `.x'.\n");
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
bad_argument ()
|
|
||||||
{
|
|
||||||
usage ();
|
|
||||||
fprintf (stderr, "Type `%s --help' for more information.\n", program_name);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
char *infile, *outfile;
|
|
||||||
|
|
||||||
program_name = strrchr (argv[0], '/');
|
|
||||||
if (! program_name)
|
|
||||||
program_name = argv[0];
|
|
||||||
else
|
|
||||||
program_name++;
|
|
||||||
|
|
||||||
infile = 0;
|
|
||||||
outfile = 0;
|
|
||||||
argc--, argv++;
|
|
||||||
while (argc > 0)
|
|
||||||
{
|
|
||||||
if (! strcmp (argv[0], "--version"))
|
|
||||||
version ();
|
|
||||||
else if (! strcmp (argv[0], "--help"))
|
|
||||||
help ();
|
|
||||||
else if (! strcmp (argv[0], "-o"))
|
|
||||||
outfile = *++argv, argc--;
|
|
||||||
else if (argv[0][0] == '-')
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: unrecognized switch: `%s'\n",
|
|
||||||
program_name, argv[0]);
|
|
||||||
bad_argument ();
|
|
||||||
}
|
|
||||||
else if (infile)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: more than input file given: `%s' and `%s'\n",
|
|
||||||
program_name, infile, argv[0]);
|
|
||||||
bad_argument ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
infile = argv[0];
|
|
||||||
|
|
||||||
argc--, argv++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! infile)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s: no input file given\n", program_name);
|
|
||||||
bad_argument ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! outfile)
|
|
||||||
{
|
|
||||||
/* Choose a default output file name. This should be the input
|
|
||||||
file name, with the extension changed to `.x'. */
|
|
||||||
char *ext = strrchr (infile, '.');
|
|
||||||
|
|
||||||
/* Make sure we haven't mistaken a dot in an earlier path
|
|
||||||
component for the start of the extension. */
|
|
||||||
if (ext && strchr (ext, '/'))
|
|
||||||
ext = 0;
|
|
||||||
|
|
||||||
if (ext)
|
|
||||||
{
|
|
||||||
outfile = (char *) xmalloc (ext - infile + 3);
|
|
||||||
strcpy (outfile, infile);
|
|
||||||
strcpy (outfile + (ext - infile), ".x");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outfile = (char *) xmalloc (strlen (infile) + 3);
|
|
||||||
strcpy (outfile, infile);
|
|
||||||
strcat (outfile, ".x");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init_chartable ();
|
|
||||||
init_keyword_hash_table ();
|
|
||||||
process_file (infile, outfile);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue