/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
This file is part of groff.
groff 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 1, or (at your option) any later
groff 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
You should have received a copy of the GNU General Public License along
with groff; see the file LICENSE. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
definition::definition() : is_macro(1), contents(0), is_simple(0)
definition::~definition()
declare_ptable(definition
)
implement_ptable(definition
)
PTABLE(definition
) macro_table
;
"max", "{type \"operator\" roman \"max\"}",
"min", "{type \"operator\" roman \"min\"}",
"lim", "{type \"operator\" roman \"lim\"}",
"sin", "{type \"operator\" roman \"sin\"}",
"cos", "{type \"operator\" roman \"cos\"}",
"tan", "{type \"operator\" roman \"tan\"}",
"sinh", "{type \"operator\" roman \"sinh\"}",
"cosh", "{type \"operator\" roman \"cosh\"}",
"tanh", "{type \"operator\" roman \"tanh\"}",
"arc", "{type \"operator\" roman \"arc\"}",
"log", "{type \"operator\" roman \"log\"}",
"ln", "{type \"operator\" roman \"ln\"}",
"exp", "{type \"operator\" roman \"exp\"}",
"Re", "{type \"operator\" roman \"Re\"}",
"Im", "{type \"operator\" roman \"Im\"}",
"det", "{type \"operator\" roman \"det\"}",
"and", "{roman \"and\"}",
"for", "{roman \"for\"}",
"sum", "{type \"operator\" vcenter size +5 \\(*S}",
"prod", "{type \"operator\" vcenter size +5 \\(*P}",
"int", "{type \"operator\" vcenter size +8 \\(is}",
"union", "{type \"operator\" vcenter size +5 \\(cu}",
"inter", "{type \"operator\" vcenter size +5 \\(ca}",
"times", "type \"binary\" \\(mu",
"ldots", "type \"inner\" { . . . }",
"half", "{1 smallover 2}",
"hat", "accent { hat_def }",
"dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"",
"dot", "accent { dot_def }",
"dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"",
"dotdot", "accent { dotdot_def }",
"tilde", "accent { tilde_def }",
"utilde_def", "\"\\v'75M'~\\v'-75M'\"",
"utilde", "uaccent { utilde_def }",
"vec_def", "up 52 size -5 \\(->",
"vec", "accent { vec_def }",
"dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}",
"dyad", "accent { dyad_def }",
"==", "type \"relation\" \\(==",
"!=", "type \"relation\" \\(!=",
"+-", "type \"binary\" \\(+-",
"->", "type \"relation\" \\(->",
"<-", "type \"relation\" \\(<-",
"...", "type \"inner\" vcenter { . . . }",
"approx", "type \"relation\" \"\\(~=\"",
"cdot", "type \"binary\" vcenter ."
void init_table(const char *device
)
for (int i
= 0; i
< sizeof(token_table
)/sizeof(token_table
[0]); i
++) {
definition
*def
= new definition
;
def
->tok
= token_table
[i
].token
;
macro_table
.define(token_table
[i
].name
, def
);
for (i
= 0; i
< sizeof(def_table
)/sizeof(def_table
[0]); i
++) {
definition
*def
= new definition
;
def
->contents
= strsave(def_table
[i
].def
);
macro_table
.define(def_table
[i
].name
, def
);
definition
*def
= new definition
;
def
->contents
= strsave("1");
macro_table
.define(device
, def
);
virtual int get_location(char **, int *);
friend int get_location(char **, int *);
friend void init_lex(const char *str
, const char *filename
, int lineno
);
class file_input
: public input
{
file_input(FILE *, const char *, input
*);
int get_location(char **, int *);
class macro_input
: public input
{
macro_input(const char *, input
*);
class top_input
: public macro_input
{
top_input(const char *, const char *, int, input
*);
int get_location(char **, int *);
class argument_macro_input
: public input
{
argument_macro_input(const char *, int, char **, input
*);
input::input(input
*x
) : next(x
)
int input::get_location(char **, int *)
file_input::file_input(FILE *f
, const char *fn
, input
*p
)
: input(p
), lineno(0), ptr("")
file_input::~file_input()
int file_input::read_line()
else if (illegal_input_char(c
))
lex_error("illegal input character code %1", c
);
if (!(line
.length() >= 3 && line
[0] == '.' && line
[1] == 'E'
&& (line
[2] == 'Q' || line
[2] == 'N')
&& (line
.length() == 3 || line
[3] == ' ' || line
[3] == '\n'
if (*ptr
!= '\0' || read_line())
if (*ptr
!= '\0' || read_line())
int file_input::get_location(char **fnp
, int *lnp
)
macro_input::macro_input(const char *str
, input
*x
) : input(x
)
macro_input::~macro_input()
if (p
== 0 || *p
== '\0')
if (p
== 0 || *p
== '\0')
top_input::top_input(const char *str
, const char *fn
, int ln
, input
*x
)
: macro_input(str
, x
), lineno(ln
)
int c
= macro_input::get();
int top_input::get_location(char **fnp
, int *lnp
)
argument_macro_input::argument_macro_input(const char *body
, int ac
,
: input(x
), argc(ac
), ap(0)
for (int i
= 0; i
< argc
; i
++)
for (i
= 0; s
[i
] != '\0'; i
++)
if (s
[i
] == '$' && s
[i
+1] >= '0' && s
[i
+1] <= '9') {
s
[j
++] = ARG1
+ s
[++i
] - '1';
argument_macro_input::~argument_macro_input()
for (int i
= 0; i
< argc
; i
++)
int argument_macro_input::get()
while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
int argument_macro_input::peek()
while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
static input
*current_input
= 0;
/* we insert a newline between input from different levels */
int c
= current_input
->get();
input
*tem
= current_input
;
current_input
= current_input
->next
;
int c
= current_input
->peek();
int get_location(char **fnp
, int *lnp
)
for (input
*p
= current_input
; p
; p
= p
->next
)
if (p
->get_location(fnp
, lnp
))
string context_ring
[NCONTEXT
];
for (int i
= 0; i
< NCONTEXT
; i
++)
fputs(" context is\n\t", stderr
);
int j
= (i
+ 1) % NCONTEXT
;
if (j
== context_index
) {
put_string(context_ring
[i
], stderr
);
else if (context_ring
[i
].length() > 0) {
put_string(context_ring
[i
], stderr
);
void add_context(const string
&s
)
context_ring
[context_index
] = s
;
context_index
= (context_index
+ 1) % NCONTEXT
;
context_ring
[context_index
] = c
;
context_index
= (context_index
+ 1) % NCONTEXT
;
void add_quoted_context(const string
&s
)
string
&r
= context_ring
[context_index
];
for (int i
= 0; i
< s
.length(); i
++)
context_index
= (context_index
+ 1) % NCONTEXT
;
void init_lex(const char *str
, const char *filename
, int lineno
)
while (current_input
!= 0) {
input
*tem
= current_input
;
current_input
= current_input
->next
;
current_input
= new top_input(str
, filename
, lineno
, 0);
void get_delimited_text()
int got_location
= get_location(&filename
, &lineno
);
while (start
== ' ' || start
== '\n')
error_with_file_and_line(filename
, lineno
,
"end of input while defining macro");
error("end of input while defining macro");
error_with_file_and_line(filename
, lineno
,
"end of input while defining macro");
error("end of input while defining macro");
add_context(start
+ token_buffer
);
add_context(start
+ token_buffer
+ start
);
void interpolate_macro_with_args(const char *body
)
for (int i
= 0; i
< 9; i
++)
lex_error("end of input while scanning macro arguments");
if (level
== 0 && (c
== ',' || c
== ')')) {
if (token_buffer
.length() > 0) {
argv
[argc
] = strsave(token_buffer
.contents());
if (argc
> 0 || c
!= ')' || i
> 0)
} while (c
!= ')' && c
!= EOF
);
current_input
= new argument_macro_input(body
, argc
, argv
, current_input
);
/* If lookup flag is non-zero the token will be looked up to see
if it is macro. If it's 1, it will looked up to see if it's a token.
int get_token(int lookup_flag
= 0)
while (c
== ' ' || c
== '\n')
add_context("end of input");
lex_error("newline before end of quoted text");
token_buffer
[token_buffer
.length() - 1] = '"';
quoted
= quoted
? 0 : c
== '\\';
add_quoted_context(token_buffer
);
if (!quoted
&& lookup_flag
!= 0 && c
== '(') {
definition
*def
= macro_table
.lookup(token_buffer
.contents());
if (def
&& def
->is_macro
&& !def
->is_simple
) {
(void)get_char(); // skip initial '('
interpolate_macro_with_args(def
->contents
);
token_buffer
.set_length(token_buffer
.length() - 1);
lex_error("`\\' ignored at end of equation");
lex_error("`\\' ignored because followed by newline");
lex_error("`\\' ignored because followed by tab");
if (break_flag
|| token_buffer
.length() == 0)
definition
*def
= macro_table
.lookup(token_buffer
.contents());
token_buffer
.set_length(token_buffer
.length() - 1);
current_input
= new macro_input(def
->contents
, current_input
);
else if (lookup_flag
== 1) {
add_context(token_buffer
);
add_context(token_buffer
);
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad filename for include");
const char *filename
= token_buffer
.contents();
FILE *fp
= fopen(filename
, "r");
lex_error("can't open included file `%1'", filename
);
current_input
= new file_input(fp
, filename
, current_input
);
lex_error("bad definition");
void do_definition(int is_simple
)
lex_error("bad definition");
const char *name
= token_buffer
.contents();
definition
*def
= macro_table
.lookup(name
);
macro_table
.define(name
, def
);
else if (def
->is_macro
) {
def
->contents
= strsave(token_buffer
.contents());
def
->is_simple
= is_simple
;
lex_error("bad undef command");
macro_table
.define(token_buffer
.contents(), 0);
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad argument to gsize command");
long n
= strtol(token_buffer
.contents(), &ptr
, 10);
if (n
== 0 && ptr
== token_buffer
.contents())
lex_error("bad argument `%1' to gsize command");
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad argument to gfont command");
set_gfont(token_buffer
.contents());
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad argument to grfont command");
set_grfont(token_buffer
.contents());
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad argument to gbfont command");
set_gbfont(token_buffer
.contents());
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad argument to space command");
long n
= strtol(token_buffer
.contents(), &ptr
, 10);
if (n
== 0 && ptr
== token_buffer
.contents())
lex_error("bad argument `%1' to space command");
definition
*def
= macro_table
.lookup(token_buffer
.contents());
int result
= def
&& def
->is_macro
&& !def
->is_simple
;
current_input
= new macro_input(token_buffer
.contents(), current_input
);
while (c
== ' ' || c
== '\n')
if (c
== EOF
|| (d
= get_char()) == EOF
)
lex_error("end of file while reading argument to `delim'");
if (c
== 'o' && d
== 'f' && peek_char() == 'f') {
start_delim
= end_delim
= '\0';
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad chartype");
string type
= token_buffer
;
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad chartype");
set_char_type(type
.contents(), strsave(token_buffer
.contents()));
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad chartype");
string param
= token_buffer
;
if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
lex_error("bad chartype");
if (sscanf(&token_buffer
[0], "%d", &n
) != 1) {
lex_error("bad number `%1'", token_buffer
.contents());
set_param(param
.contents(), n
);
yylval
.str
= strsave(token_buffer
.contents());
void lex_error(const char *message
,
if (!get_location(&filename
, &lineno
))
error(message
, arg1
, arg2
, arg3
);
error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
void yyerror(const char *s
)
if (!get_location(&filename
, &lineno
))
error_with_file_and_line(filename
, lineno
, s
);