|
|
@ -1,191 +1,136 @@ |
|
|
|
/* TODO Notes for the calculator version. |
|
|
|
Don't use GLR (= non-deterministic forks) |
|
|
|
Provide <alloca.h>, <malloc.h>, <stddef.h>, <stdlib.h> |
|
|
|
To access semantic value of an object, use "-> value" |
|
|
|
To suppress locations on-calc: #define YYLLOC_DEFAULT(Cur, Rhs, N) |
|
|
|
(or just don't pass --locations) */ |
|
|
|
|
|
|
|
%{ |
|
|
|
|
|
|
|
#include <TeX/TeX.h> |
|
|
|
#include <TeX/parser.h> |
|
|
|
#include <TeX/structure.h> |
|
|
|
#include <TeX/config.h> |
|
|
|
#include <TeX/node.h> |
|
|
|
#include <TeX/flow.h> |
|
|
|
#include <TeX/env.h> |
|
|
|
|
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
/* yylex() - The lexer, as usual */ |
|
|
|
/* yylex(): The lexer, as usual */ |
|
|
|
int yylex(void); |
|
|
|
/* yyerror() - The error function */ |
|
|
|
void yyerror(const char *); |
|
|
|
|
|
|
|
static struct TeX_Flow *result = NULL; |
|
|
|
/* yyerror(): The error function */ |
|
|
|
void yyerror(char const *message); |
|
|
|
|
|
|
|
//--- |
|
|
|
// Node allocation functions |
|
|
|
// Environment stack management |
|
|
|
//--- |
|
|
|
|
|
|
|
/* mkflow_cons() - Add a new node at the right edge of a flow |
|
|
|
@flow Updated flow |
|
|
|
@node New node to add |
|
|
|
Returns the new flow, which my be a different pointer if [flow] is NULL. */ |
|
|
|
static struct TeX_Flow *mkflow_cons(struct TeX_Flow*flow, struct TeX_Node*node) |
|
|
|
{ |
|
|
|
if(!node) return flow; |
|
|
|
/* Environment stack and current environment's index */ |
|
|
|
static struct TeX_Env *env_stack[TEX_ENV_DEPTH] = { NULL }; |
|
|
|
static int env_index = 0; |
|
|
|
|
|
|
|
if(!flow) |
|
|
|
{ |
|
|
|
flow = calloc(1, sizeof *flow); |
|
|
|
if(!flow) { yyerror("[[out of memory]]"); return NULL; } |
|
|
|
/* Current environment and its type */ |
|
|
|
#define env (env_stack[env_index]) |
|
|
|
|
|
|
|
flow->first = flow->last = node; |
|
|
|
return flow; |
|
|
|
} |
|
|
|
|
|
|
|
flow->last->next = node; |
|
|
|
flow->last = node; |
|
|
|
return flow; |
|
|
|
} |
|
|
|
|
|
|
|
/* mknode_text() - Create a new text container node |
|
|
|
@str 8-byte contained string |
|
|
|
Returns a new node with an internal format for the string. The original |
|
|
|
string may disappear. */ |
|
|
|
static struct TeX_Node *mknode_text(const char *str) |
|
|
|
void env_push(char const *type) |
|
|
|
{ |
|
|
|
struct TeX_Node *node = calloc(1, sizeof *node); |
|
|
|
if(!node) { yyerror("[[out of memory]]"); return NULL; } |
|
|
|
|
|
|
|
/* TODO: Don't use strdup(); use a format conversion instead */ |
|
|
|
node->text = malloc(strlen(str) + 1); |
|
|
|
if(!node->text) { |
|
|
|
yyerror("[[out of memory]]"); free(node); return NULL; } |
|
|
|
strcpy((void *)node->text, str); |
|
|
|
node->type = TEX_NODECLASS_TEXT; |
|
|
|
struct TeX_Env *new_env = NULL; |
|
|
|
|
|
|
|
return node; |
|
|
|
} |
|
|
|
|
|
|
|
/* mknode_command() - Create a new command node |
|
|
|
@name Command name |
|
|
|
This function looks up the command name in the class table and returns NULL |
|
|
|
if the command is not available. Otherwise it returns a new node without |
|
|
|
arguments for this command. */ |
|
|
|
static struct TeX_Node *mknode_command(const char *name) |
|
|
|
{ |
|
|
|
/* Get the class id from the class table */ |
|
|
|
int type = class_find(name); |
|
|
|
if(type < 0) { yyerror("[[ unknown command ]]"); return NULL; } |
|
|
|
if(!strcmp(type, "primary")) |
|
|
|
new_env = TeX_env_primary(); |
|
|
|
/* TODO: Matrix environment creation |
|
|
|
if(!strcmp(type, "matrix")) |
|
|
|
new_env = TeX_env_matrix(); */ |
|
|
|
|
|
|
|
struct TeX_Node *node = calloc(1, sizeof *node); |
|
|
|
if(!node) { yyerror("[[out of memory]]"); return NULL; } |
|
|
|
/* If new_env is NULL... still insert it. This is more likely to cause |
|
|
|
an error, and avoids asymmetry with pops */ |
|
|
|
|
|
|
|
node->type = type; |
|
|
|
node->next = NULL; |
|
|
|
return node; |
|
|
|
env_index++; |
|
|
|
env = new_env; |
|
|
|
} |
|
|
|
|
|
|
|
/* mknode_arg() - Add an argument to a command node |
|
|
|
@node Node to add an argument to |
|
|
|
@arg Argument flow |
|
|
|
Always returns node. If node is not a plain text node and has at least one |
|
|
|
free argument slot, then arg is added at the first free slot. Otherwise, |
|
|
|
arg is freed and node is returned unchanged. */ |
|
|
|
static struct TeX_Node *mknode_arg(struct TeX_Node *node, struct TeX_Flow *arg) |
|
|
|
struct TeX_Env *env_pop(void) |
|
|
|
{ |
|
|
|
if(!node || !arg) return node; |
|
|
|
|
|
|
|
/* Drop the argument if it's for a plain text node */ |
|
|
|
if(node->type == TEX_NODECLASS_TEXT) |
|
|
|
{ |
|
|
|
yyerror("[[dropping argument of plain text node]]"); |
|
|
|
TeX_free(arg); |
|
|
|
return node; |
|
|
|
} |
|
|
|
|
|
|
|
/* Otherwise look up a free slot in the argument array of node */ |
|
|
|
int i = 0; |
|
|
|
while(i < TEX_MAX_CHILDREN && node->args[i]) i++; |
|
|
|
|
|
|
|
if(i >= TEX_MAX_CHILDREN) |
|
|
|
{ |
|
|
|
yyerror("[[too much arguments for node]]"); |
|
|
|
TeX_free(arg); |
|
|
|
} |
|
|
|
else node->args[i] = arg; |
|
|
|
|
|
|
|
return node; |
|
|
|
struct TeX_Env *ret = env; |
|
|
|
env_index--; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
/* mknode_absarg() - Add an absorbed argument to a command node |
|
|
|
@node Command node |
|
|
|
@arg Text argument |
|
|
|
For special cases such as left/right, sets the subtype, otherwise creates |
|
|
|
a text flow as usual. */ |
|
|
|
static struct TeX_Node *mknode_absarg(struct TeX_Node *node, char const *text) |
|
|
|
{ |
|
|
|
char const *class = ""; |
|
|
|
if(node->type != TEX_NODECLASS_TEXT) |
|
|
|
class = TeX_table[node->type - 1].name; |
|
|
|
|
|
|
|
if(!strcmp(class, "left") || !strcmp(class, "right")) |
|
|
|
{ |
|
|
|
node->subtype = text[0]; |
|
|
|
return node; |
|
|
|
} |
|
|
|
|
|
|
|
return mknode_arg(node, mkflow_cons(NULL, mknode_text(text))); |
|
|
|
} |
|
|
|
//--- |
|
|
|
// Extra node allocation functions |
|
|
|
//--- |
|
|
|
|
|
|
|
/* mknode_f() - Make a function node with a flow argument |
|
|
|
/* mknode_f(): Make a function node with a flow argument |
|
|
|
@name Command name; intended for built-in nodes only |
|
|
|
@flow Flow argument |
|
|
|
Returns a node for command [name] with as argument [flow]. */ |
|
|
|
static struct TeX_Node *mknode_f(const char *name, struct TeX_Flow *flow) |
|
|
|
struct TeX_Node *mknode_f(char const *name, struct TeX_Flow *flow) |
|
|
|
{ |
|
|
|
struct TeX_Node *node = mknode_command(name); |
|
|
|
return mknode_arg(node, flow); |
|
|
|
struct TeX_Node *node = TeX_node_command(name); |
|
|
|
return TeX_node_add_arg(node, flow); |
|
|
|
} |
|
|
|
|
|
|
|
/* mknode_t() - Make a function node with a text argument */ |
|
|
|
#define mknode_t(name, text) \ |
|
|
|
mknode_f((name), mkflow_cons(NULL, mknode_text((text)))) |
|
|
|
/* mknode_t(): Make a function node with a text argument */ |
|
|
|
struct TeX_Node *mknode_t(char const *name, char const *text) |
|
|
|
{ |
|
|
|
struct TeX_Flow *flow = TeX_flow_add_node(NULL, TeX_node_text(text)); |
|
|
|
return mknode_f(name, flow); |
|
|
|
} |
|
|
|
|
|
|
|
%} |
|
|
|
|
|
|
|
%define api.value.type union |
|
|
|
|
|
|
|
%token <const char *> TEXT |
|
|
|
%token <const char *> COMMAND |
|
|
|
%token <const char *> COMMAND_ABS |
|
|
|
/* Basic text and commands */ |
|
|
|
%token <char const *> TEXT |
|
|
|
%token <char const *> COMMAND |
|
|
|
%token <char const *> COMMAND_ABS |
|
|
|
|
|
|
|
/* Special tokens for environments */ |
|
|
|
%token ENV_BEGIN |
|
|
|
%token ENV_END |
|
|
|
|
|
|
|
/* Separators ("&") and breaks ("\\") */ |
|
|
|
%token SEPARATOR |
|
|
|
%token BREAK |
|
|
|
|
|
|
|
%left '^' '_' |
|
|
|
|
|
|
|
%type <struct TeX_Flow *> flow |
|
|
|
%type <struct TeX_Node *> node |
|
|
|
%type <struct TeX_Node *> node_abs |
|
|
|
%type <struct TeX_Node *> env_node |
|
|
|
%type <struct TeX_Env *> env_end |
|
|
|
|
|
|
|
%% |
|
|
|
|
|
|
|
main: |
|
|
|
flow { result = $1; } |
|
|
|
env: |
|
|
|
%empty { } |
|
|
|
| env node { env->add_node(env, $2); } |
|
|
|
| env SEPARATOR { env->add_separator(env); } |
|
|
|
| env BREAK { env->add_break(env); } |
|
|
|
|
|
|
|
flow: |
|
|
|
%empty { $$ = NULL; } |
|
|
|
| flow node { $$ = mkflow_cons($1, $2); } |
|
|
|
| flow node { $$ = TeX_flow_add_node($1, $2); } |
|
|
|
|
|
|
|
node: |
|
|
|
TEXT { $$ = mknode_text($1); } |
|
|
|
| COMMAND { $$ = mknode_command($1); } |
|
|
|
| node_abs TEXT { $$ = mknode_absarg($1, $2); } |
|
|
|
| node '{' flow '}' { $$ = mknode_arg($1, $3); } |
|
|
|
TEXT { $$ = TeX_node_text($1); } |
|
|
|
| COMMAND { $$ = TeX_node_command($1); } |
|
|
|
| node_abs TEXT { $$ = TeX_node_absorb($1, $2); } |
|
|
|
| node '{' flow '}' { $$ = TeX_node_add_arg($1, $3); } |
|
|
|
/* Special shortcuts for the superscript and subscript classes */ |
|
|
|
| '^' TEXT { $$ = mknode_t("\\sup", $2); } |
|
|
|
| '_' TEXT { $$ = mknode_t("\\sub", $2); } |
|
|
|
| '^' '{' flow '}' { $$ = mknode_f("\\sup", $3); } |
|
|
|
| '_' '{' flow '}' { $$ = mknode_f("\\sub", $3); } |
|
|
|
/* Environments */ |
|
|
|
| env_node { $$ = $1; } |
|
|
|
|
|
|
|
node_abs: |
|
|
|
COMMAND_ABS { $$ = mknode_command($1); } |
|
|
|
COMMAND_ABS { $$ = TeX_node_command($1); } |
|
|
|
|
|
|
|
env_node: |
|
|
|
/* TODO: Add TeX_mknode_env() */ |
|
|
|
env_begin env env_end { $$ = NULL; } //TeX_mknode_env($3); } |
|
|
|
|
|
|
|
env_begin: |
|
|
|
ENV_BEGIN '{' TEXT '}' { env_push($3); } |
|
|
|
env_end: |
|
|
|
ENV_END '{' TEXT '}' { $$ = env_pop(); } |
|
|
|
|
|
|
|
%% |
|
|
|
|
|
|
|
//--- |
|
|
@ -207,7 +152,7 @@ node_abs: |
|
|
|
//--- |
|
|
|
|
|
|
|
/* Character source (input formula) */ |
|
|
|
static const char *lex; |
|
|
|
static char const *lex; |
|
|
|
|
|
|
|
/* Accumulator. This buffer contains text that will be emitted in the next |
|
|
|
token, more precisely everything between the start of the token and [lex] |
|
|
@ -262,15 +207,15 @@ int single; |
|
|
|
* '2' is seen as lookahead and single-character mode is activated */ |
|
|
|
static int la; |
|
|
|
|
|
|
|
/* forward() - Maybe return |
|
|
|
/* forward(): Maybe return |
|
|
|
Returns the value of @expr if it's nonnegative; does nothing otherwise. */ |
|
|
|
#define forward(expr) do { \ |
|
|
|
int v = expr; \ |
|
|
|
if(v >= 0) return v; \ |
|
|
|
} while(0) |
|
|
|
|
|
|
|
/* lexer_init() - TODO: Document lexer_init() */ |
|
|
|
void lexer_init(const char *formula) |
|
|
|
/* lexer_init(): TODO: Document lexer_init() */ |
|
|
|
void lexer_init(char const *formula) |
|
|
|
{ |
|
|
|
acc = acc_buffer; |
|
|
|
|
|
|
@ -290,7 +235,7 @@ void lexer_init(const char *formula) |
|
|
|
single = 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* release() - Release the lexer buffer as a textual token |
|
|
|
/* release(): Release the lexer buffer as a textual token |
|
|
|
This function returns produces a text token of the requested types using the |
|
|
|
contents of the lexer buffer, but only if the buffer is not empty. Thus the |
|
|
|
call must be wrapped into forward() and not return if there is a fallback |
|
|
@ -310,7 +255,7 @@ static int release(int token) |
|
|
|
return token; |
|
|
|
} |
|
|
|
|
|
|
|
/* accumulate() - Accumulate characters in the lexer buffer |
|
|
|
/* accumulate(): Accumulate characters in the lexer buffer |
|
|
|
Adds a new character @c to the lexer buffer. If the buffer is full or if |
|
|
|
single-character mode is active, emits a token and empties the buffer. For |
|
|
|
this return to work, the call to accumulate() must be wrapped in forward(). |
|
|
@ -337,14 +282,14 @@ static int accumulate(int c) |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* acccmp() - String comparison with the accumulator |
|
|
|
/* acccmp(): String comparison with the accumulator |
|
|
|
Like strcmp(), but uses the non-NUL-terminated accumulator as one input. */ |
|
|
|
static int acccmp(const char *str) |
|
|
|
static int acccmp(char const *str) |
|
|
|
{ |
|
|
|
return strncmp(acc_buffer, str, acc - acc_buffer); |
|
|
|
} |
|
|
|
|
|
|
|
/* lexer_text() - Execute a step of lexing from the text state |
|
|
|
/* lexer_text(): Execute a step of lexing from the text state |
|
|
|
|
|
|
|
In text state, we are accumulating characters as long as possible without |
|
|
|
releasing tokens. Longer chunks of text render faster on monochrome |
|
|
@ -412,7 +357,7 @@ static int lexer_text(void) |
|
|
|
return accumulate(c); |
|
|
|
} |
|
|
|
|
|
|
|
/* lexer_command() - Execute of step of lexing from the command state |
|
|
|
/* lexer_command(): Execute a step of lexing from the command state |
|
|
|
|
|
|
|
This state is transitioned into when a '\' followed by a letter is |
|
|
|
encountered. The lexer remains there until the end of the command, signaled |
|
|
@ -457,7 +402,7 @@ static int lexer_command(void) |
|
|
|
return release(COMMAND); |
|
|
|
} |
|
|
|
|
|
|
|
/* yylex() - The lexer |
|
|
|
/* yylex(): The lexer |
|
|
|
Returns the token type of the next token in the string initialized by the |
|
|
|
last call to parse_start(). */ |
|
|
|
int yylex(void) |
|
|
@ -482,47 +427,43 @@ int yylex(void) |
|
|
|
// Parser interface |
|
|
|
//--- |
|
|
|
|
|
|
|
/* parse_start(): Configure parser to run on a string */ |
|
|
|
void parse_start(const char *str) |
|
|
|
/* parse(): Parse into a TeX environment */ |
|
|
|
struct TeX_Env *parse(char const *str) |
|
|
|
{ |
|
|
|
/* Initialize lexer to run on a string */ |
|
|
|
lexer_init(str); |
|
|
|
} |
|
|
|
|
|
|
|
/* parse(): Parse into a TeX flow */ |
|
|
|
struct TeX_Flow *parse(void) |
|
|
|
{ |
|
|
|
/* Push a primary environment */ |
|
|
|
env_push("primary"); |
|
|
|
|
|
|
|
int x = yyparse(); |
|
|
|
return x ? NULL: result; |
|
|
|
|
|
|
|
/* Pop the primary environment out of stack */ |
|
|
|
return x ? NULL: env_pop(); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef TEX_DEBUG |
|
|
|
//--- |
|
|
|
// Printing and errors |
|
|
|
//--- |
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#ifndef TEX_PRINT |
|
|
|
|
|
|
|
void yyerror(const char *error) |
|
|
|
void yyerror(__attribute__((unused)) char const *error) |
|
|
|
{ |
|
|
|
fprintf(stderr, "Parsing failed: %s\n", error); |
|
|
|
} |
|
|
|
|
|
|
|
#else /* TEX_DEBUG */ |
|
|
|
#else |
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <TeX/print.h> |
|
|
|
|
|
|
|
void yyerror(__attribute__((unused)) const char *error) |
|
|
|
void yyerror(char const *error) |
|
|
|
{ |
|
|
|
fprintf(stderr, "Parsing failed: %s\n", error); |
|
|
|
} |
|
|
|
|
|
|
|
#endif /* TEX_DEBUG */ |
|
|
|
|
|
|
|
//--- |
|
|
|
// Debugging functions |
|
|
|
//--- |
|
|
|
|
|
|
|
#ifdef TEX_DEBUG |
|
|
|
|
|
|
|
#define GRAY "\e[30;1m" |
|
|
|
#define END "\e[0m" |
|
|
|
|
|
|
|
/* TeX_debug_lex(): Display the result of lexing on stdout */ |
|
|
|
void TeX_debug_lex(const char *formula) |
|
|
|
/* TeX_print_lex(): Print the result of lexing */ |
|
|
|
void TeX_print_lex(char const *formula) |
|
|
|
{ |
|
|
|
lexer_init(formula); |
|
|
|
int token; |
|
|
@ -530,57 +471,24 @@ void TeX_debug_lex(const char *formula) |
|
|
|
do |
|
|
|
{ |
|
|
|
token = yylex(); |
|
|
|
|
|
|
|
printf("%-3d ", token); |
|
|
|
if(strchr("{}^_", token)) printf("%c", token); |
|
|
|
if(token == 258) printf("TEXT %s", yylval.TEXT); |
|
|
|
if(token == 259) printf("COMMAND %s", yylval.COMMAND); |
|
|
|
if(token == 260) printf("COMMAND_ABS %s", yylval.COMMAND_ABS); |
|
|
|
printf("\n"); |
|
|
|
} |
|
|
|
while(token != 0); |
|
|
|
} |
|
|
|
|
|
|
|
/* TeX_debug_node(): Recursively display the structure of a node */ |
|
|
|
void TeX_debug_node(struct TeX_Node *node, int indent) |
|
|
|
{ |
|
|
|
printf("%*s", indent, ""); |
|
|
|
if(!node) { puts("node (null)"); return; } |
|
|
|
|
|
|
|
if(node->type == TEX_NODECLASS_TEXT) |
|
|
|
{ |
|
|
|
printf("\"%s\"", node->text); |
|
|
|
printf(GRAY " %dx%d,%d %+d%+d" END "\n", node->width, |
|
|
|
node->height, node->line, node->x, node->l); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
printf("<%s>", TeX_table[node->type - 1].name); |
|
|
|
if(node->subtype) |
|
|
|
printf(" subtype '%c'", node->subtype); |
|
|
|
printf(" "GRAY "%dx%d,%d %+d%+d" END "\n", node->width, |
|
|
|
node->height, node->line, node->x, node->l); |
|
|
|
for(int i = 0; i < TEX_MAX_CHILDREN && node->args[i]; i++) |
|
|
|
TeX_debug_flow(node->args[i], indent + 4); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* TeX_debug_flow(): Recursively display the structure of a flow */ |
|
|
|
void TeX_debug_flow(struct TeX_Flow *flow, int indent) |
|
|
|
{ |
|
|
|
printf("%*s", indent, ""); |
|
|
|
if(!flow) { puts("flow (null)"); return; } |
|
|
|
|
|
|
|
printf("flow " GRAY "%dx%d,%d" END "\n", flow->width, flow->height, |
|
|
|
flow->line); |
|
|
|
if(token > 0x20 && token < 0x7f) |
|
|
|
printf("%c", token); |
|
|
|
if(token == TEXT) |
|
|
|
printf("TEXT %s", yylval.TEXT); |
|
|
|
if(token == COMMAND) |
|
|
|
printf("COMMAND %s", yylval.COMMAND); |
|
|
|
if(token == COMMAND_ABS) |
|
|
|
printf("COMMAND_ABS %s", yylval.COMMAND_ABS); |
|
|
|
if(token == ENV_BEGIN) |
|
|
|
printf("ENV_BEGIN"); |
|
|
|
if(token == ENV_END) |
|
|
|
printf("ENV_END"); |
|
|
|
|
|
|
|
struct TeX_Node *node = flow->first; |
|
|
|
|
|
|
|
while(node) |
|
|
|
{ |
|
|
|
TeX_debug_node(node, indent + 4); |
|
|
|
node = node->next; |
|
|
|
printf("\n"); |
|
|
|
} |
|
|
|
while(token != 0); |
|
|
|
} |
|
|
|
|
|
|
|
#endif /* TEX_DEBUG */ |
|
|
|
#endif /* TEX_PRINT */ |