TeX/src/node.c

184 lines
4.4 KiB
C

#include <TeX/config.h>
#include <TeX/node.h>
#include <TeX/flow.h>
#include <TeX/env.h>
#include <TeX/classes.h>
#include <stdlib.h>
#include <string.h>
//---
// Node creation and destruction functions
//---
#define special(node) \
((node)->type == TEX_NODECLASS_TEXT || \
(node)->type == TEX_NODECLASS_ENV)
/* TeX_node_text(): Make a text node */
struct TeX_Node *TeX_node_text(char const *utf8)
{
struct TeX_Node *node = calloc(1, sizeof *node);
if(!node) return NULL;
/* TODO: Don't use strdup(); convert from utf8 instead */
node->text = malloc(strlen(utf8) + 1);
if(!node->text) { free(node); return NULL; }
strcpy((void *)node->text, utf8);
node->type = TEX_NODECLASS_TEXT;
return node;
}
/* TeX_node_command(): Make a command node without arguments */
struct TeX_Node *TeX_node_command(char const *name)
{
/* Get the class id from the class table */
int type = TeX_class_find(name);
if(type < 0) return NULL;
struct TeX_Node *node = calloc(1, sizeof *node);
if(!node) return NULL;
node->type = type;
node->next = NULL;
return node;
}
/* TeX_node_add_arg(): Add an argument to a command node */
struct TeX_Node *TeX_node_add_arg(struct TeX_Node *node, struct TeX_Flow *arg)
{
if(!node || !arg) return node;
/* Drop the argument if it's for a plain text or environment node */
if(special(node))
{
TeX_flow_free(arg);
return node;
}
/* Otherwise look up a free slot in the argument array of node */
int i = 0;
while(i < TEX_NODE_MAX_CHILDREN && node->args[i]) i++;
if(i < TEX_NODE_MAX_CHILDREN) node->args[i] = arg;
else TeX_flow_free(arg);
return node;
}
/* TeX_node_absorb(): Absorb text into a command node */
struct TeX_Node *TeX_node_absorb(struct TeX_Node *node, char const *text)
{
char const *class = TeX_class_of(node)->name;
if(!strcmp(class, "left") || !strcmp(class, "right"))
{
node->subtype = text[0];
return node;
}
return TeX_node_add_arg(node, TeX_flow_add_node(NULL,
TeX_node_text(text)));
}
/* TeX_node_env(): Make a environment node */
struct TeX_Node *TeX_node_env(struct TeX_Env *env)
{
struct TeX_Node *node = calloc(1, sizeof *node);
if(!node) return NULL;
node->env = env;
node->type = TEX_NODECLASS_ENV;
return node;
}
/* TeX_node_free(): Free a TeX_Node and all its children */
void TeX_node_free(struct TeX_Node *node)
{
/* Text nodes: free the allocated string */
if(node->type == TEX_NODECLASS_TEXT) free(node->text);
/* Environment nodes: free the environment */
else if(node->type == TEX_NODECLASS_ENV) node->env->free(node->env);
/* Class nodes: recursively free children */
else for(int i = 0; i < TEX_NODE_MAX_CHILDREN; i++)
{
if(node->args[i]) TeX_flow_free(node->args[i]);
}
/* TODO: Extend with environment nodes */
free(node);
}
//---
// Layout and rendering
//---
/* TeX_node_layout(): Compute the layout of a node */
void TeX_node_layout(struct TeX_Node *node)
{
/* First compute the layout of the children */
if(!special(node))
for(int i = 0; i < TEX_NODE_MAX_CHILDREN && node->args[i]; i++)
TeX_flow_layout(node->args[i]);
/* Then use the class' special layout computation function */
TeX_class_of(node)->layout(node);
}
/* TeX_node_render(): Render a node and its children */
void TeX_node_render(struct TeX_Node const * node, int x, int y, int color)
{
/* Don't render hidden or invalid elements */
if(node->hidden) return;
/* For non trivial classes, use the class' special function */
TeX_class_of(node)->render(node, x, y, color);
}
//---
// Printing function
//---
#ifdef TEX_PRINT
#include <stdio.h>
#include <TeX/print.h>
/* TeX_print_node(): Print a node's tree */
void TeX_print_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 if(node->type == TEX_NODECLASS_ENV)
{
printf("\\env" GRAY " %dx%d,%d %+d%+d" END "\n", node->width,
node->height, node->line, node->x, node->l);
TeX_print_env(node->env, indent + 4);
}
else
{
printf("<%s>", TeX_class_of(node)->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_NODE_MAX_CHILDREN && node->args[i]; i++)
TeX_print_flow(node->args[i], indent + 4);
}
}
#endif /* TEX_PRINT */