implement environments and refactor
This commit is contained in:
parent
9b14ec8755
commit
9be3414e96
20
TODO.md
20
TODO.md
|
@ -2,14 +2,26 @@
|
|||
Available blocks:
|
||||
-> U+0020 .. U+007F (128) ASCII
|
||||
-> U+00A0 .. U+00FF (96) Latin-1 Supplement
|
||||
-> U+0390 .. U+03CF (64) Greek
|
||||
-> U+2010 .. U+215F (80) General punctuation
|
||||
-> U+0100 .. U+017F (128) Latin Extended-A
|
||||
-> U+0370 .. U+03FF (144) Greek
|
||||
-> U+0400 .. U+047F (128) Cyrillic
|
||||
-> U+2010 .. U+205F (80) General punctuation
|
||||
-> U+2070 .. U+209F (48) Subscripts and superscripts
|
||||
-> U+2160 .. U+217F (32) Roman numerals
|
||||
-> U+2190 .. U+21FF (112) Arrows
|
||||
Blocks to come:
|
||||
-> U+2200 .. ? Mathematical operators
|
||||
-> U+2200 .. U+22FF (256) Mathematical operators
|
||||
-> U+25A0 .. U+25FF (96) Geometric shapes
|
||||
-> U+2800 .. U+28FF (256) Braille patterns
|
||||
Other interesting blocks?
|
||||
-> Finish cyrillic
|
||||
-> IPA extenstions and Phonetic extensions
|
||||
-> Currency symbols
|
||||
-> Hiragana and Katakana
|
||||
Other characters supported in FONTCHARACTER:
|
||||
-> U+2139 Imaginary number
|
||||
-> U+231F Fraction symbol
|
||||
-> U+3010
|
||||
-> U+3011
|
||||
|
||||
* Don't use TEX_LAYOUT_SPACING for everything, just make it the default
|
||||
* Add a parameter to resolve as inline style or display style
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Simple command-line parsing and layout troubleshooter
|
||||
TARGET = TeX-cli
|
||||
CFLAGS = -D TEX_PLATFORM_CLI -D TEX_DEBUG -g
|
||||
CFLAGS = -D TEX_PLATFORM_CLI -D TEX_PRINT -g
|
||||
CC = gcc
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# SDL2 layout and rendering (not interactive)
|
||||
SRC = main-SDL.c
|
||||
TARGET = TeX-SDL
|
||||
CFLAGS = -D TEX_PLATFORM_SDL -D TEX_DEBUG -g
|
||||
CFLAGS = -D TEX_PLATFORM_SDL -D TEX_PRINT -g
|
||||
LDFLAGS = -lSDL2
|
||||
CC = gcc
|
||||
|
|
|
@ -2,72 +2,12 @@
|
|||
// TeX: Natural rendering for mathematical formulae
|
||||
//---
|
||||
|
||||
#ifndef TEX_H
|
||||
#define TEX_H
|
||||
#ifndef TEX_TEX
|
||||
#define TEX_TEX
|
||||
|
||||
#include <TeX/config.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//---
|
||||
// Graphical settings
|
||||
// A few quirks that can be adjusted to your needs.
|
||||
//---
|
||||
|
||||
/* Thickness and horizontal margin of fraction bars */
|
||||
#define TEX_FRACTION_BAR_THICKNESS 1
|
||||
#define TEX_FRACTION_BAR_MARGIN 1
|
||||
|
||||
/* Vertical placement of subscripts and superscripts (relative to object) */
|
||||
#define TEX_SUBSCRIPT_ELEVATION 3
|
||||
#define TEX_SUPERSCRIPT_DEPTH 3
|
||||
|
||||
/* Align the center of the resizable brackets with the baseline */
|
||||
#define TEX_LEFTRIGHT_ALIGNED 0
|
||||
/* Make them extend symmetrically around baseline */
|
||||
#define TEX_LEFTRIGHT_SYMMETRICAL 0
|
||||
/* Width of resizable "<" and ">" delimiters */
|
||||
#define TEX_LEFTRIGHT_ANGLE_WIDTH 4
|
||||
|
||||
/* Spacing: between layout elements, letters, and width of space character */
|
||||
#define TEX_LAYOUT_SPACING 1
|
||||
#define TEX_LETTER_SPACING 1
|
||||
#define TEX_WORD_SPACING 6
|
||||
|
||||
/* Whether to place bounds above and below integrals (display mode) */
|
||||
#define TEX_INT_DISPLAY 0
|
||||
/* Height of the integral symbol */
|
||||
#define TEX_INT_HEIGHT 21
|
||||
|
||||
/* Length of arrow extensions from the tip of a vector */
|
||||
#define TEX_VEC_ARROW_LENGTH 2
|
||||
/* Horizontal margin around vector */
|
||||
#define TEX_VEC_MARGIN 0
|
||||
/* Extra height between argument and vector */
|
||||
#define TEX_VEC_SPACING TEX_LAYOUT_SPACING
|
||||
|
||||
/* Make the vertical part of the square root symbol slanted at low heights */
|
||||
#define TEX_SQRT_SLANTED 1
|
||||
/* Length of the top-right bar of square roots (0 to disable) */
|
||||
#define TEX_SQRT_BAR_LENGTH 0
|
||||
|
||||
//---
|
||||
// Implementation settings
|
||||
//---
|
||||
|
||||
/* Size of the lexer buffer; must be large enough to hold all command names.
|
||||
Longer than the longest literal string is a waste of space */
|
||||
#define TEX_LEXER_BUFSIZE 64
|
||||
|
||||
/* Maximum number of nested left/right pairs */
|
||||
#define TEX_LEFTRIGHT_NESTING 16
|
||||
|
||||
/* Enable or disable debugging functions with TEX_DEBUG. Currently this is
|
||||
enabled (and used) by all computer targets and disabled on calculator. */
|
||||
#if defined(TEX_PLATFORM_FX9860G) || defined(TEX_PLATFORM_FXCG50)
|
||||
#undef TEX_DEBUG
|
||||
#elif !defined(TEX_DEBUG)
|
||||
#define TEX_DEBUG
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Module interface
|
||||
// This TeX module does not have its own primitive rendering methods;
|
||||
|
@ -77,10 +17,10 @@
|
|||
// having pixel() and text() translate coordinates and check bounds.
|
||||
//---
|
||||
|
||||
/* Opaque declaration - see <TeX/structure.h> for the details */
|
||||
struct TeX_Flow;
|
||||
/* Opaque declaration: see <TeX/env.h> for the details */
|
||||
struct TeX_Env;
|
||||
|
||||
/* TeX_intf_pixel() - Set a single pixel
|
||||
/* TeX_intf_pixel(): Set a single pixel
|
||||
This function configures the pixel-rendering procedure used by the module.
|
||||
The argument should expect three parameters:
|
||||
@x Horizontal position of the pixel (left --> right)
|
||||
|
@ -88,7 +28,7 @@ struct TeX_Flow;
|
|||
@color Requested color */
|
||||
void TeX_intf_pixel(void (*draw_pixel)(int x, int y, int color));
|
||||
|
||||
/* TeX_intf_line() - Draw a line
|
||||
/* TeX_intf_line(): Draw a line
|
||||
This function sets the line drawing procedure of the module. The argument
|
||||
should take give parameters:
|
||||
@x1 @y1 Location of the first endpoint of the line
|
||||
|
@ -97,7 +37,7 @@ void TeX_intf_pixel(void (*draw_pixel)(int x, int y, int color));
|
|||
void TeX_intf_line(void (*draw_line)(int x1, int y1, int x2, int y2,
|
||||
int color));
|
||||
|
||||
/* TeX_intf_size() - Get the dimensions of a string
|
||||
/* TeX_intf_size(): Get the dimensions of a string
|
||||
This function configures the procedure used by the module to compute the
|
||||
size of a string node. The argument should expect three parameters:
|
||||
@str String whose dimensions are requested
|
||||
|
@ -105,7 +45,7 @@ void TeX_intf_line(void (*draw_line)(int x1, int y1, int x2, int y2,
|
|||
@height Pointer to height value (must be updated by function) */
|
||||
void TeX_intf_size(void (*text_size)(char const *str, int *width,int *height));
|
||||
|
||||
/* TeX_intf_text() - Draw variable-width text
|
||||
/* TeX_intf_text(): Draw variable-width text
|
||||
This function configures the text-rendering procedure used by the module.
|
||||
Four arguments will be passed:
|
||||
@x x coordinate of the left-side of the bounding box (included)
|
||||
|
@ -124,16 +64,16 @@ void TeX_intf_text(void (*draw_text)(char const *str, int x, int y,int color));
|
|||
// are rendered very quickly.
|
||||
//---
|
||||
|
||||
/* TeX_parse() - Parse a TeX formula
|
||||
/* TeX_parse(): Parse a TeX formula
|
||||
@formula TeX formula to parse
|
||||
Returns a dynamically-allocated TeX_Flow object that can be used in further
|
||||
Returns a dynamically-allocated TeX_Env object that can be used in further
|
||||
calls to TeX_draw() and must be freed by a call to TeX_free(). */
|
||||
struct TeX_Flow *TeX_parse(char const *formula);
|
||||
struct TeX_Env *TeX_parse(char const *formula);
|
||||
|
||||
/* TeX_free() - Free an allocated TeX formula
|
||||
/* TeX_free(): Free an allocated TeX formula
|
||||
Freed formulas become dangling and must not be used in any further call.
|
||||
@formula Formula to free, assumed allocated by TeX_parse() */
|
||||
void TeX_free(struct TeX_Flow *formula);
|
||||
void TeX_free(struct TeX_Env *formula);
|
||||
|
||||
//---
|
||||
// Rendering
|
||||
|
@ -142,14 +82,14 @@ void TeX_free(struct TeX_Flow *formula);
|
|||
// as much as possible by reusing the same TeX_Flow object.
|
||||
//---
|
||||
|
||||
/* TeX_draw() - Render a parsed formula
|
||||
/* TeX_draw(): Render a parsed formula
|
||||
@formula Formula to render
|
||||
@x x coordinate of the left-side of the bounding box (included)
|
||||
@y y coordinate of the top-size of the bounding box (included)
|
||||
@color Requested color for the rendered pixels */
|
||||
void TeX_draw(struct TeX_Flow *formula, int x, int y, int color);
|
||||
void TeX_draw(struct TeX_Env *formula, int x, int y, int color);
|
||||
|
||||
/* TeX_interpret() - Parse and render, in sequence
|
||||
/* TeX_interpret(): Parse and render, in sequence
|
||||
This function parses the provided formula, renders it and then frees it.
|
||||
Only use it if you're going to render the formula only once or if you're
|
||||
very short on memory.
|
||||
|
@ -159,4 +99,4 @@ void TeX_draw(struct TeX_Flow *formula, int x, int y, int color);
|
|||
@color Requested color for the rendered pixels */
|
||||
void TeX_interpret(char const *formula, int x, int y, int color);
|
||||
|
||||
#endif /* TEX_H */
|
||||
#endif /* TEX_TEX */
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
//---
|
||||
// class: Node classes
|
||||
//---
|
||||
|
||||
#include <TeX/node.h>
|
||||
|
||||
/* TeX_Class
|
||||
Essentially the prototype of a node class, which implements the layout and
|
||||
rendering function of a fixed mathematical construct. */
|
||||
struct TeX_Class
|
||||
{
|
||||
/* Most of the time this name appears in the formula using the "\name"
|
||||
notation, but some classes (superscript, parentheses, matrices...)
|
||||
have special syntax and their names are hardcoded. To avoid
|
||||
conflicts, built-in class names start with a backslash. */
|
||||
char const *name;
|
||||
|
||||
/* layout()
|
||||
This function must calculate the width, height and base line of the
|
||||
node and store the results in the structure fields. It is allowed to
|
||||
access the .width, .height and .line fields of its children because
|
||||
they will have had their size calculated beforehand.
|
||||
|
||||
This TeX module provides classes with a primitive function that
|
||||
calculates the size of raw strings:
|
||||
#include <TeX/interface.h>
|
||||
void TeX_size(char const *str, int *width, int *height);
|
||||
|
||||
@node A node of the described class, for size calculation */
|
||||
void (*layout)(struct TeX_Node *node);
|
||||
|
||||
/* render()
|
||||
This function must render the given node at the provided (x, y)
|
||||
coordinates. The (x, y) point is the top-left corner of the bounding
|
||||
box of the expression and is inside the box (ie. it can be drawn
|
||||
to). This function must honor the size estimates provided by
|
||||
calculate_size() and not draw outside the bounding box.
|
||||
|
||||
This TeX module provides three primitive functions for rendering:
|
||||
#include <TeX/interface.h>
|
||||
void TeX_pixel(int x, int y, int color);
|
||||
void TeX_line(int x1, int y1, int x2, int y2, int color);
|
||||
void TeX_text(char const *str, int x, int y, int color);
|
||||
|
||||
@node A node of the described class, for rendering
|
||||
@x Horizontal coordinate of the requested rendering position
|
||||
@y Vertical coordinate of the requested rendering position
|
||||
@color Requested rendering color */
|
||||
void (*render)(struct TeX_Node const * node, int x, int y, int color);
|
||||
};
|
||||
|
||||
/* TeX_class_find(): Find a class using a command name
|
||||
@name Command name
|
||||
Returns a positive class id representing the requested TeX_Class object (if
|
||||
a class with this name is found in the table, a negative number otherwise.
|
||||
This function never returns 0, which is reserved for TEX_NODECLASS_TEXT. */
|
||||
int TeX_class_find(char const *name);
|
||||
|
||||
/* TeX_class_of(): Get the class pointer of a node */
|
||||
struct TeX_Class const *TeX_class_of(struct TeX_Node const * node);
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
//---
|
||||
// config: Implementation and graphical settings of the library
|
||||
//---
|
||||
|
||||
#ifndef TEX_CONFIG
|
||||
#define TEX_CONFIG
|
||||
|
||||
//---
|
||||
// Implementation settings
|
||||
//---
|
||||
|
||||
/* Size of the lexer buffer; must be large enough to hold all command names.
|
||||
Longer than the longest literal string is a waste of space */
|
||||
#define TEX_LEXER_BUFSIZE 64
|
||||
|
||||
/* Maximum number of nested left/right pairs */
|
||||
#define TEX_LEFTRIGHT_DEPTH 16
|
||||
|
||||
/* Maximum number of nested environments */
|
||||
#define TEX_ENV_DEPTH 16
|
||||
|
||||
/* Maximum number of command arguments allowed by the library (each node has
|
||||
exactly this number of children; memory usage is better if this is small) */
|
||||
#define TEX_NODE_MAX_CHILDREN 2
|
||||
|
||||
/* Enable or disable printing functions with TEX_PRINT. Currently this is
|
||||
enabled (and used) by all computer targets and disabled on calculator. */
|
||||
#if defined(TEX_PLATFORM_FX9860G) || defined(TEX_PLATFORM_FXCG50)
|
||||
#undef TEX_PRINT
|
||||
#elif !defined(TEX_PRINT)
|
||||
#define TEX_PRINT
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Graphical settings
|
||||
// A few quirks that can be adjusted to your needs.
|
||||
//---
|
||||
|
||||
/* Thickness and horizontal margin of fraction bars */
|
||||
#define TEX_FRACTION_BAR_THICKNESS 1
|
||||
#define TEX_FRACTION_BAR_MARGIN 1
|
||||
|
||||
/* Vertical placement of subscripts and superscripts (relative to object) */
|
||||
#define TEX_SUBSCRIPT_ELEVATION 3
|
||||
#define TEX_SUPERSCRIPT_DEPTH 3
|
||||
|
||||
/* Align the center of the resizable brackets with the baseline */
|
||||
#define TEX_LEFTRIGHT_ALIGNED 0
|
||||
/* Make them extend symmetrically around baseline */
|
||||
#define TEX_LEFTRIGHT_SYMMETRICAL 0
|
||||
/* Width of resizable "<" and ">" delimiters */
|
||||
#define TEX_LEFTRIGHT_ANGLE_WIDTH 4
|
||||
|
||||
/* Spacing: between layout elements, letters, and width of space character */
|
||||
#define TEX_LAYOUT_SPACING 1
|
||||
#define TEX_LETTER_SPACING 1
|
||||
#define TEX_WORD_SPACING 6
|
||||
|
||||
/* Whether to place bounds above and below integrals (display mode) */
|
||||
#define TEX_INT_DISPLAY 0
|
||||
/* Height of the integral symbol */
|
||||
#define TEX_INT_HEIGHT 21
|
||||
|
||||
/* Length of arrow extensions from the tip of a vector */
|
||||
#define TEX_VEC_ARROW_LENGTH 2
|
||||
/* Horizontal margin around vector */
|
||||
#define TEX_VEC_MARGIN 0
|
||||
/* Extra height between argument and vector */
|
||||
#define TEX_VEC_SPACING TEX_LAYOUT_SPACING
|
||||
|
||||
/* Make the vertical part of the square root symbol slanted at low heights */
|
||||
#define TEX_SQRT_SLANTED 1
|
||||
/* Length of the top-right bar of square roots (0 to disable) */
|
||||
#define TEX_SQRT_BAR_LENGTH 0
|
||||
|
||||
#endif /* TEX_CONFIG */
|
|
@ -8,7 +8,7 @@
|
|||
/* uint type for bit fields */
|
||||
typedef unsigned int uint;
|
||||
|
||||
/* min(), max() - But don't duplicate side-effects */
|
||||
/* min(), max(): But don't duplicate side-effects */
|
||||
#define min(a, b) ({ \
|
||||
__auto_type _a = (a); \
|
||||
__auto_type _b = (b); \
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
//---
|
||||
// env: Environments
|
||||
//---
|
||||
|
||||
#ifndef TEX_ENV
|
||||
#define TEX_ENV
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct TeX_Node;
|
||||
|
||||
/* Simple type definition trick to use struct TeX_Env inside prototypes within
|
||||
the struct definition itself */
|
||||
typedef struct TeX_Env TeX_Env;
|
||||
|
||||
struct TeX_Env
|
||||
{
|
||||
/* Environment name */
|
||||
char const * name;
|
||||
|
||||
/* free(): Free an environment instance */
|
||||
void (*free)(TeX_Env *env);
|
||||
|
||||
/* add_node(): Add a node to an environment */
|
||||
void (*add_node)(TeX_Env *env, struct TeX_Node *node);
|
||||
|
||||
/* add_separator(): Add a separator ("&") */
|
||||
void (*add_separator)(TeX_Env *env);
|
||||
|
||||
/* add_break(): Add a break ("\\") */
|
||||
void (*add_break)(TeX_Env *env);
|
||||
|
||||
/* size(): Compute environment size */
|
||||
void (*size)(TeX_Env *env);
|
||||
|
||||
/* render(): Render environment */
|
||||
void (*render)(TeX_Env *env, int x, int y, int color);
|
||||
|
||||
/* Dimensions */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t line;
|
||||
|
||||
/* More data, each TeX_Env_* subtype will decide */
|
||||
};
|
||||
|
||||
//---
|
||||
// Environment construction functions
|
||||
//---
|
||||
|
||||
/* TeX_env_primary(): make a primary, single-flow environment
|
||||
This is the environment type of whole formulas. It consists of a single
|
||||
flow and ignores separators and breaks.
|
||||
Creates and returns a new environment [env] which must be freed by a call
|
||||
to [env->free(env)]. */
|
||||
struct TeX_Env *TeX_env_primary(void);
|
||||
|
||||
#endif /* TEX_ENV */
|
|
@ -0,0 +1,57 @@
|
|||
//---
|
||||
// flow: Horizontal flows of nodes arranged from left to right
|
||||
//---
|
||||
|
||||
#ifndef TEX_FLOW
|
||||
#define TEX_FLOW
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct TeX_Node;
|
||||
|
||||
/* TeX_Flow
|
||||
This object represents a horizontal line of nodes following the flow of the
|
||||
text; each node can be a plain string or a functional node. All share the
|
||||
same base line when rendered. */
|
||||
struct TeX_Flow
|
||||
{
|
||||
/* Pointer to first and last elements of the line */
|
||||
struct TeX_Node *first;
|
||||
struct TeX_Node *last;
|
||||
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t line;
|
||||
|
||||
} __attribute__((packed, aligned(sizeof (void *))));
|
||||
|
||||
//---
|
||||
// Flow construction and destruction functions
|
||||
//---
|
||||
|
||||
/* TeX_flow_add_node(): Add a new node to a flow
|
||||
The provided node is added at the right of the flow. If [flow] is NULL,
|
||||
creates and returns a new flow. Otherwise, always returns [flow]. */
|
||||
struct TeX_Flow *TeX_flow_add_node(struct TeX_Flow *flow,
|
||||
struct TeX_Node *node);
|
||||
|
||||
/* TeX_flow_free(): Free TeX_Flow objects
|
||||
Destroys a flow and all of its contained elements. */
|
||||
void TeX_flow_free(struct TeX_Flow *flow);
|
||||
|
||||
//---
|
||||
// Layout and rendering
|
||||
//---
|
||||
|
||||
/* TeX_flow_layout(): Calculate the layout of a flow
|
||||
This function calculates the size taken by nodes sharing the same baseline.
|
||||
It heeds for special size and alignment exceptions such as parenthesis-type
|
||||
characters and subscripts/superscripts. Modifies the flow's metadata. */
|
||||
void TeX_flow_layout(struct TeX_Flow *flow);
|
||||
|
||||
/* TeX_flow_render(): Render a flow and all its components
|
||||
This function renders all horizontal objects in a flow. The layout of the
|
||||
flow must have been computed. */
|
||||
void TeX_flow_render(struct TeX_Flow const * flow, int x, int y, int color);
|
||||
|
||||
#endif /* TEX_FLOW */
|
|
@ -0,0 +1,14 @@
|
|||
//---
|
||||
// interface: Interface functions
|
||||
//---
|
||||
|
||||
#ifndef TEX_INTF
|
||||
#define TEX_INTF
|
||||
|
||||
/* Rendering-related interface functions */
|
||||
extern void (*TeX_pixel)(int x, int y, int color);
|
||||
extern void (*TeX_line)(int x1, int y1, int x2, int y2, int color);
|
||||
extern void (*TeX_size)(char const * str, int *width, int *height);
|
||||
extern void (*TeX_text)(char const * str, int x, int y, int color);
|
||||
|
||||
#endif /* TEX_INTF */
|
|
@ -0,0 +1,94 @@
|
|||
//---
|
||||
// node: Mathematical constructs in the formula tree
|
||||
//---
|
||||
|
||||
#ifndef TEX_NODE
|
||||
#define TEX_NODE
|
||||
|
||||
#include <TeX/config.h>
|
||||
#include <TeX/defs.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* A special class number for nodes that contain plain text. Class numbers from
|
||||
1 onwards are used by TeX_Class. See also the class array in nodes.c. */
|
||||
#define TEX_NODECLASS_TEXT 0
|
||||
|
||||
struct TeX_Flow;
|
||||
|
||||
/* TeX_Node
|
||||
This object represents a mathematical construct. It can be plain text, a
|
||||
fraction, a square root or a matrix. It has specific layout and rendering
|
||||
that are supplied by its class. */
|
||||
struct TeX_Node
|
||||
{
|
||||
/* Pointer to next element in the flow */
|
||||
struct TeX_Node *next;
|
||||
|
||||
union {
|
||||
/* Plain text content encoded as UTF-8 */
|
||||
uint8_t *text;
|
||||
/* Functional arguments */
|
||||
struct TeX_Flow *args[TEX_NODE_MAX_CHILDREN];
|
||||
};
|
||||
|
||||
/* Node is invalid or hidden */
|
||||
uint hidden :1;
|
||||
/* Node class identifier */
|
||||
uint type :7;
|
||||
/* Class variant or fixed argument */
|
||||
uint subtype :8;
|
||||
|
||||
/* Size and placement */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t line;
|
||||
|
||||
/* Location within the flow, as x and baseline displacement */
|
||||
uint16_t x;
|
||||
int16_t l;
|
||||
|
||||
} __attribute__((packed, aligned(sizeof (void *))));
|
||||
|
||||
//---
|
||||
// Node creation and destruction functions
|
||||
//---
|
||||
|
||||
/* TeX_node_text(): Make a text node */
|
||||
struct TeX_Node *TeX_node_text(char const *utf8);
|
||||
|
||||
/* TeX_node_command(): Make a command node without arguments
|
||||
Returns NULL if no such command is defined. */
|
||||
struct TeX_Node *TeX_node_command(char const *command);
|
||||
|
||||
/* TeX_node_add_arg(): Add an argument to a command node
|
||||
If [node] is a command node and has at least on free argument slot, then
|
||||
adds [arg] to it. Otherwise, destroys [arg].
|
||||
Always returns [flow] (useful for nested calls). */
|
||||
struct TeX_Node *TeX_node_add_arg(struct TeX_Node *node, struct TeX_Flow *arg);
|
||||
|
||||
/* TeX_node_absorb(): Absorb text into a command node
|
||||
This special function is for command nodes that use the absorption syntax,
|
||||
eg. "\left(" where the argument consists of a single character and is not
|
||||
surrounded by braces. The first character of [text] (which should be only
|
||||
one character if it comes from the parser) is saved as argument. [text] is
|
||||
not freed. Always returns [node]. */
|
||||
struct TeX_Node *TeX_node_absorb(struct TeX_Node *node, char const *text);
|
||||
|
||||
/* TeX_node_free(): Free a TeX_Node and all its children */
|
||||
void TeX_node_free(struct TeX_Node *node);
|
||||
|
||||
//---
|
||||
// Layout and rendering
|
||||
//---
|
||||
|
||||
/* TeX_node_layout(): Compute the layout of a node
|
||||
This functions computes the layout of a node and its children, and stores
|
||||
the results in the node's metadata. */
|
||||
void TeX_node_layout(struct TeX_Node *node);
|
||||
|
||||
/* TeX_node_render(): Render a node and its children
|
||||
The node's layout must have been computed first. */
|
||||
void TeX_node_render(struct TeX_Node const * node, int x, int y, int color);
|
||||
|
||||
#endif /* TEX_NODE */
|
|
@ -1,44 +0,0 @@
|
|||
//---
|
||||
// parser: interface to the TeX parser
|
||||
//---
|
||||
|
||||
#ifndef TEX_PARSER
|
||||
#define TEX_PARSER
|
||||
|
||||
#include <TeX/TeX.h>
|
||||
#include <TeX/structure.h>
|
||||
|
||||
/* parse_start(): Configure parser to run on a string
|
||||
@str Input data, must be NULL-terminated */
|
||||
void parse_start(const char *str);
|
||||
|
||||
/* parse(): Parse into a TeX flow
|
||||
Uses input data set by parse_start(). Returns a flow object on success (free
|
||||
with TeX_free()), NULL on error (generally syntax issues).
|
||||
TODO: Fine-grained error detection? */
|
||||
struct TeX_Flow *parse(void);
|
||||
|
||||
#ifdef TEX_DEBUG
|
||||
|
||||
/* TeX_debug_lex(): Display the result of lexing
|
||||
Makes a lexical analysis of the given formula and prints the stream of
|
||||
tokens to standard output. */
|
||||
void TeX_debug_lex(const char *formula);
|
||||
|
||||
/* TeX_debug_node(): Recursively display the structure of a node
|
||||
Displays the nature and dimensions of the given node and its children,
|
||||
making recursive calls to TeX_debug_flow().
|
||||
@node Displayed node
|
||||
@indent Indent level, in spaces (goes up by groups of 4) */
|
||||
void TeX_debug_node(struct TeX_Node *node, int indent);
|
||||
|
||||
/* TeX_debug_flow(): Recursively display the structure of a flow
|
||||
Displays the dimensions of the given flow and its contents, making recursive
|
||||
calls to TeX_debug_node().
|
||||
@flow Displayed flow
|
||||
@indent Indent level, in spaces (goes up by groups of 4) */
|
||||
void TeX_debug_flow(struct TeX_Flow *flow, int indent);
|
||||
|
||||
#endif /* TEX_DEBUG */
|
||||
|
||||
#endif /* TEX_PARSER */
|
|
@ -0,0 +1,29 @@
|
|||
//---
|
||||
// print: Command-line structure printing
|
||||
//---
|
||||
|
||||
#ifdef TEX_PRINT
|
||||
|
||||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/env.h>
|
||||
|
||||
#define GRAY "\e[30;1m"
|
||||
#define END "\e[0m"
|
||||
|
||||
/* TeX_print_node(): Print a node's tree */
|
||||
void TeX_print_node(struct TeX_Node *node, int indent);
|
||||
|
||||
/* TeX_print_flow(): Print a flow's tree */
|
||||
void TeX_print_flow(struct TeX_Flow *flow, int indent);
|
||||
|
||||
/* TeX_print_env(): Recursively print an environment */
|
||||
void TeX_print_env(struct TeX_Env *env, int indent);
|
||||
|
||||
/* TeX_print_env_primary(): Print a primary environment to stdout */
|
||||
void TeX_print_env_primary(struct TeX_Env *env, int indent);
|
||||
|
||||
/* TeX_print_lex(): Print the result of lexing */
|
||||
void TeX_print_lex(char const *formula);
|
||||
|
||||
#endif /* TEX_PRINT */
|
|
@ -1,23 +0,0 @@
|
|||
//---
|
||||
// render: Rendering functions
|
||||
//---
|
||||
|
||||
#ifndef TEX_RENDER
|
||||
#define TEX_RENDER
|
||||
|
||||
/* Rendering-related interface functions */
|
||||
extern void (*TeX_pixel)(int x, int y, int color);
|
||||
extern void (*TeX_line)(int x1, int y1, int x2, int y2, int color);
|
||||
extern void (*TeX_size)(char const * str, int *width, int *height);
|
||||
extern void (*TeX_text)(char const * str, int x, int y, int color);
|
||||
|
||||
/* render_node() - Recursively render a node
|
||||
This function all nodes types (including text). The node's size must have
|
||||
been computed first. */
|
||||
void render_node(struct TeX_Node const * node, int x, int y, int color);
|
||||
|
||||
/* render_flow() - Render a flow and all its components
|
||||
This function arranges and renders all horizontal objects in a flow. */
|
||||
void render_flow(struct TeX_Flow const * flow, int x, int y, int color);
|
||||
|
||||
#endif /* TEX_RENDER */
|
|
@ -1,153 +0,0 @@
|
|||
//---
|
||||
// structure: Recursive node/flow data structure
|
||||
//---
|
||||
|
||||
#ifndef TEX_STRUCTURE
|
||||
#define TEX_STRUCTURE
|
||||
|
||||
#include <TeX/defs.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Maximum number of command arguments allowed by the library (each node has
|
||||
exactly this number of children; memory usage is better if this is small) */
|
||||
#define TEX_MAX_CHILDREN 2
|
||||
|
||||
/* A special class number for nodes that contain plain text. Class numbers from
|
||||
1 onwards are used by TeX_Class. See also the class array in nodes.c. */
|
||||
#define TEX_NODECLASS_TEXT 0
|
||||
|
||||
//---
|
||||
// Data structures
|
||||
// The structure mixes linked lists (text flow along a line, TeX_Flow) and
|
||||
// trees (arguments to commands, TeX_Node) in a mutually-recursive way.
|
||||
//---
|
||||
|
||||
/* TeX_Flow
|
||||
This object represents a horizontal line of nodes following the flow of the
|
||||
text; each node can be a plain string or a functional node. All share the
|
||||
same base line when rendered. */
|
||||
struct TeX_Flow
|
||||
{
|
||||
/* Pointer to first and last elements of the line */
|
||||
struct TeX_Node *first;
|
||||
struct TeX_Node *last;
|
||||
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t line;
|
||||
|
||||
} __attribute__((packed, aligned(sizeof (void *))));
|
||||
|
||||
/* TeX_Node
|
||||
Here's the main deal. TeX_Flow objects handle the flow and TeX_Node objects
|
||||
handle the content and its structure. */
|
||||
struct TeX_Node
|
||||
{
|
||||
/* Pointer to next element in the flow */
|
||||
struct TeX_Node *next;
|
||||
|
||||
union {
|
||||
/* Plain text content encoded as UTF-8 */
|
||||
uint8_t *text;
|
||||
/* Functional arguments */
|
||||
struct TeX_Flow *args[TEX_MAX_CHILDREN];
|
||||
};
|
||||
|
||||
/* Invalid or hidden node */
|
||||
uint hidden :1;
|
||||
/* Node class identifier */
|
||||
uint type :7;
|
||||
/* Class variant or fixed argument */
|
||||
uint subtype :8;
|
||||
|
||||
/* Size and placement */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t line;
|
||||
|
||||
/* Location withing flow, as x and baseline displacement */
|
||||
uint16_t x;
|
||||
int16_t l;
|
||||
|
||||
} __attribute__((packed, aligned(sizeof (void *))));
|
||||
|
||||
//---
|
||||
// Class nodes
|
||||
// These are the nodes that have arguments, and specific size-calculation
|
||||
// and rendering methods. Most interesting nodes (fractions, sums,
|
||||
// matrices...) fall into this category.
|
||||
//---
|
||||
|
||||
/* TeX_Class
|
||||
A class of nodes (ie. "\name{arg}{arg}..."): function name, size-calculation
|
||||
method, rendering method. */
|
||||
struct TeX_Class
|
||||
{
|
||||
/* Most of the time this name appears in the formula using the "\name"
|
||||
notation, but some classes (superscript, parentheses, matrices...)
|
||||
have special syntax and their names are hardcoded. To avoid
|
||||
conflicts, built-in class names start with a backslash. */
|
||||
const char *name;
|
||||
|
||||
/* size()
|
||||
This function must calculate the width, height and base line of the
|
||||
node and store the results in the structure fields. It is allowed to
|
||||
access the .width, .height and .line fields of its children because
|
||||
they will have had their size calculated beforehand.
|
||||
|
||||
This TeX module provides classes with a primitive function that
|
||||
calculates the size of raw strings:
|
||||
void text_size(const char *str, int *width, int *height);
|
||||
|
||||
@node A node of the described class, for size calculation */
|
||||
void (*size)(struct TeX_Node *node);
|
||||
|
||||
/* render()
|
||||
This function must render the given node at the provided (x, y)
|
||||
coordinates. The (x, y) point is the top-left corner of the bounding
|
||||
box of the expression and is inside the box (ie. it can be drawn
|
||||
to). This function must honor the size estimates provided by
|
||||
calculate_size() and not draw outside the bounding box.
|
||||
|
||||
This TeX module provides two primitive functions for rendering:
|
||||
void pixel(int x, int y, int color);
|
||||
void text(const char *str, int x, int y, int color);
|
||||
|
||||
@node A node of the described class, for rendering
|
||||
@x Horizontal coordinate of the requested rendering position
|
||||
@y Vertical coordinate of the requested rendering position
|
||||
@color Requested rendering color */
|
||||
void (*render)(struct TeX_Node const * node, int x, int y, int color);
|
||||
};
|
||||
|
||||
/* class_find() - Find a class using a command name
|
||||
@name Command name
|
||||
Returns a positive class id representing the requested TeX_Class object (if
|
||||
a class with this name is found in the table, a negative number otherwise.
|
||||
This function never returns 0, which is reserved for TEX_NODECLASS_TEXT. */
|
||||
int class_find(const char *name);
|
||||
|
||||
/* Class table. This is an array that can be indexed by class_id - 1 where
|
||||
class_id is an id returned by class_find(). It is terminated by a class
|
||||
with a NULL command name. Be careful because the first element in this array
|
||||
has class id 1. */
|
||||
extern struct TeX_Class TeX_table[];
|
||||
|
||||
//---
|
||||
// Recursive size computation
|
||||
//---
|
||||
|
||||
/* size_node() - Calculate the size of node
|
||||
This function handles all node classes (including text) and computes the
|
||||
size of the children in advance, as required by the class' calculate_size()
|
||||
method. Modifies the node structure. */
|
||||
void size_node(struct TeX_Node *node);
|
||||
|
||||
/* size_flow() - Calculate the size of a flow
|
||||
This function calculates the size taken by nodes sharing the same baseline.
|
||||
It heeds for special size and alignment exceptions such as parenthesis-type
|
||||
characters and subscripts/superscripts. Modifies the flow structure. */
|
||||
void size_flow(struct TeX_Flow *flow);
|
||||
|
||||
#endif /* TEX_STRUCTURE */
|
|
@ -0,0 +1,36 @@
|
|||
//---
|
||||
// vector: Simple vectors that only grow
|
||||
//---
|
||||
|
||||
#ifndef TEX_VECTOR
|
||||
#define TEX_VECTOR
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
int size;
|
||||
int elsize;
|
||||
|
||||
/* This pointer can be cast to the vector type and indexed */
|
||||
void *data;
|
||||
} vector_t;
|
||||
|
||||
/* vector_make(): Make a new vector of a given type
|
||||
@vector Pointer to preallocated vector structure
|
||||
@type Data type */
|
||||
void vector_make(vector_t *vector, size_t elsize);
|
||||
|
||||
#define vector_make(v, T) vector_make(v, sizeof(T))
|
||||
|
||||
/* vector_append(): Append a new element to a vector
|
||||
@vector Pointer to initialized vector
|
||||
@element Pointer to new element, which will be *copied* */
|
||||
void vector_append(vector_t *v, void *element);
|
||||
|
||||
/* vector_free(): Free a vector's contents */
|
||||
void vector_free(vector_t *v);
|
||||
|
||||
#endif /* TEX_VECTOR */
|
131
src/TeX.c
131
src/TeX.c
|
@ -1,8 +1,11 @@
|
|||
#include <TeX/TeX.h>
|
||||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/env.h>
|
||||
|
||||
#include <TeX/defs.h>
|
||||
#include <TeX/structure.h>
|
||||
#include <TeX/parser.h>
|
||||
#include <TeX/render.h>
|
||||
#include <TeX/interface.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -16,27 +19,27 @@ void (*TeX_line)(int x1, int y1, int x2, int y2, int color) = NULL;
|
|||
void (*TeX_size)(char const *str, int *width, int *height) = NULL;
|
||||
void (*TeX_text)(char const *str, int x, int y, int color) = NULL;
|
||||
|
||||
/* TeX_intf_pixel() - Set a single pixel */
|
||||
/* TeX_intf_pixel(): Set a single pixel */
|
||||
void TeX_intf_pixel(void (*draw_pixel)(int x, int y, int color))
|
||||
{
|
||||
TeX_pixel = draw_pixel;
|
||||
}
|
||||
|
||||
/* TeX_intf_line() - Draw a line */
|
||||
/* TeX_intf_line(): Draw a line */
|
||||
void TeX_intf_line(void (*draw_line)(int x1, int y1, int x2, int y2,
|
||||
int color))
|
||||
{
|
||||
TeX_line = draw_line;
|
||||
}
|
||||
|
||||
/* TeX_intf_size() - Get the dimensions of a string */
|
||||
void TeX_intf_size(void (*text_size)(const char *str, int *width,int *height))
|
||||
/* TeX_intf_size(): Get the dimensions of a string */
|
||||
void TeX_intf_size(void (*text_size)(char const *str, int *width,int *height))
|
||||
{
|
||||
TeX_size = text_size;
|
||||
}
|
||||
|
||||
/* TeX_intf_text() - Draw variable-width text */
|
||||
void TeX_intf_text(void (*draw_text)(const char *str, int x, int y,int color))
|
||||
/* TeX_intf_text(): Draw variable-width text */
|
||||
void TeX_intf_text(void (*draw_text)(char const *str, int x, int y,int color))
|
||||
{
|
||||
TeX_text = draw_text;
|
||||
}
|
||||
|
@ -45,103 +48,41 @@ void TeX_intf_text(void (*draw_text)(const char *str, int x, int y,int color))
|
|||
// Object management
|
||||
//---
|
||||
|
||||
/* TeX_free_node() - Free TeX_Node objects
|
||||
@node Node to free. Its parameters will also be freed */
|
||||
static void TeX_free_node(struct TeX_Node *node)
|
||||
/* TeX_free(): Free an allocated TeX formula */
|
||||
void TeX_free(struct TeX_Env *env)
|
||||
{
|
||||
/* Text nodes: free the allocated string */
|
||||
if(node->type == TEX_NODECLASS_TEXT) free(node->text);
|
||||
|
||||
/* Class nodes: recursively free children */
|
||||
else for(int i = 0; i < TEX_MAX_CHILDREN; i++)
|
||||
{
|
||||
if(node->args[i]) TeX_free(node->args[i]);
|
||||
}
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* TeX_free() - Free an allocated TeX formula */
|
||||
void TeX_free(struct TeX_Flow *flow)
|
||||
{
|
||||
if(!flow) return;
|
||||
|
||||
struct TeX_Node *node, *next = NULL;
|
||||
|
||||
for(node = flow->first; node; node = next)
|
||||
{
|
||||
next = node->next;
|
||||
TeX_free_node(node);
|
||||
}
|
||||
|
||||
free(flow);
|
||||
}
|
||||
|
||||
//---
|
||||
// Node operations: size computation and rendering
|
||||
//---
|
||||
|
||||
/* size_node() - Calculate the size of node */
|
||||
void size_node(struct TeX_Node *node)
|
||||
{
|
||||
/* Text nodes */
|
||||
if(node->type == TEX_NODECLASS_TEXT)
|
||||
{
|
||||
int w, h;
|
||||
TeX_size((char const *)node->text, &w, &h);
|
||||
|
||||
node->width = w;
|
||||
node->height = h;
|
||||
node->line = h >> 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* For other classes, first compute the size of children */
|
||||
for(int i = 0; i < TEX_MAX_CHILDREN && node->args[i]; i++)
|
||||
size_flow(node->args[i]);
|
||||
|
||||
/* Then use the class' special size computation function */
|
||||
if(TeX_table[node->type - 1].size)
|
||||
TeX_table[node->type - 1].size(node);
|
||||
}
|
||||
|
||||
/* render_node() - Recursively render a node */
|
||||
void render_node(struct TeX_Node const * node, int x, int y, int color)
|
||||
{
|
||||
/* Don't render hidden or invalid elements */
|
||||
if(node->hidden) return;
|
||||
|
||||
if(node->type == TEX_NODECLASS_TEXT)
|
||||
{
|
||||
TeX_text((void *)node->text, x, y, color);
|
||||
return;
|
||||
}
|
||||
|
||||
/* For non trivial classes, use the class' special function */
|
||||
if(TeX_table[node->type - 1].render)
|
||||
TeX_table[node->type - 1].render(node, x, y, color);
|
||||
env->free(env);
|
||||
}
|
||||
|
||||
//---
|
||||
// Module functions
|
||||
//---
|
||||
|
||||
/* TeX_parse() - Parse a TeX formula */
|
||||
struct TeX_Flow *TeX_parse(const char *formula)
|
||||
/* TeX_parse(): Parse a TeX formula */
|
||||
struct TeX_Env *TeX_parse(char const *formula)
|
||||
{
|
||||
struct TeX_Flow *flow;
|
||||
/* Provided by the parser. */
|
||||
struct TeX_Env *parse(char const *str);
|
||||
|
||||
parse_start(formula);
|
||||
flow = parse();
|
||||
if(!flow) return NULL;
|
||||
struct TeX_Env *env = parse(formula);
|
||||
if(!env) return NULL;
|
||||
|
||||
size_flow(flow);
|
||||
return flow;
|
||||
env->size(env);
|
||||
return env;
|
||||
}
|
||||
|
||||
/* TeX_draw() - Render a parsed formula */
|
||||
void TeX_draw(struct TeX_Flow *formula, int x, int y, int color)
|
||||
/* TeX_draw(): Render a parsed formula */
|
||||
void TeX_draw(struct TeX_Env *formula, int x, int y, int color)
|
||||
{
|
||||
render_flow(formula, x, y, color);
|
||||
formula->render(formula, x, y, color);
|
||||
}
|
||||
|
||||
/* TeX_interpret(): Parse and render, in sequence */
|
||||
void TeX_interpret(char const *formula, int x, int y, int color)
|
||||
{
|
||||
struct TeX_Env *env = TeX_parse(formula);
|
||||
if(!env) return;
|
||||
|
||||
TeX_draw(env, x, y, color);
|
||||
TeX_free(env);
|
||||
}
|
||||
|
|
354
src/TeX.y
354
src/TeX.y
|
@ -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)
|
||||
/* Environment stack and current environment's index */
|
||||
static struct TeX_Env *env_stack[TEX_ENV_DEPTH] = { NULL };
|
||||
static int env_index = 0;
|
||||
|
||||
/* Current environment and its type */
|
||||
#define env (env_stack[env_index])
|
||||
|
||||
void env_push(char const *type)
|
||||
{
|
||||
if(!node) return flow;
|
||||
struct TeX_Env *new_env = NULL;
|
||||
|
||||
if(!flow)
|
||||
{
|
||||
flow = calloc(1, sizeof *flow);
|
||||
if(!flow) { yyerror("[[out of memory]]"); return NULL; }
|
||||
if(!strcmp(type, "primary"))
|
||||
new_env = TeX_env_primary();
|
||||
/* TODO: Matrix environment creation
|
||||
if(!strcmp(type, "matrix"))
|
||||
new_env = TeX_env_matrix(); */
|
||||
|
||||
flow->first = flow->last = node;
|
||||
return flow;
|
||||
}
|
||||
/* If new_env is NULL... still insert it. This is more likely to cause
|
||||
an error, and avoids asymmetry with pops */
|
||||
|
||||
flow->last->next = node;
|
||||
flow->last = node;
|
||||
return flow;
|
||||
env_index++;
|
||||
env = new_env;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
struct TeX_Env *env_pop(void)
|
||||
{
|
||||
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;
|
||||
|
||||
return node;
|
||||
struct TeX_Env *ret = env;
|
||||
env_index--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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; }
|
||||
//---
|
||||
// Extra node allocation functions
|
||||
//---
|
||||
|
||||
struct TeX_Node *node = calloc(1, sizeof *node);
|
||||
if(!node) { yyerror("[[out of memory]]"); return NULL; }
|
||||
|
||||
node->type = type;
|
||||
node->next = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)));
|
||||
}
|
||||
|
||||
/* 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
|
||||
//---
|
||||
|
||||
#ifndef TEX_PRINT
|
||||
|
||||
void yyerror(__attribute__((unused)) char const *error)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <TeX/print.h>
|
||||
|
||||
void yyerror(const char *error)
|
||||
void yyerror(char const *error)
|
||||
{
|
||||
fprintf(stderr, "Parsing failed: %s\n", error);
|
||||
}
|
||||
|
||||
#else /* TEX_DEBUG */
|
||||
|
||||
void yyerror(__attribute__((unused)) const char *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);
|
||||
|
||||
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");
|
||||
|
||||
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);
|
||||
|
||||
struct TeX_Node *node = flow->first;
|
||||
|
||||
while(node)
|
||||
{
|
||||
TeX_debug_node(node, indent + 4);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TEX_DEBUG */
|
||||
#endif /* TEX_PRINT */
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
// classes: Node classes used for mathematical notations
|
||||
//---
|
||||
|
||||
#include <TeX/TeX.h>
|
||||
#include <TeX/structure.h>
|
||||
#include <TeX/render.h>
|
||||
#include <TeX/config.h>
|
||||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/classes.h>
|
||||
|
||||
#include <TeX/interface.h>
|
||||
#include <TeX/defs.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
@ -15,6 +19,27 @@
|
|||
#define h(n) (args[n]->height)
|
||||
#define l(n) (args[n]->line)
|
||||
|
||||
//---
|
||||
// Text.
|
||||
// * args: 0 (can't have any)
|
||||
// * flow: normal
|
||||
//---
|
||||
|
||||
void text_layout(struct TeX_Node *node)
|
||||
{
|
||||
int w, h;
|
||||
TeX_size((char const *)node->text, &w, &h);
|
||||
|
||||
node->width = w;
|
||||
node->height = h;
|
||||
node->line = h >> 1;
|
||||
}
|
||||
|
||||
void text_render(struct TeX_Node const * node, int x, int y, int color)
|
||||
{
|
||||
TeX_text((void *)node->text, x, y, color);
|
||||
}
|
||||
|
||||
//---
|
||||
// Fractions.
|
||||
// * args: 2 (<2 invalidate, >2 ignore)
|
||||
|
@ -38,7 +63,7 @@
|
|||
// TEX_FRACTION_BAR_MARGIN
|
||||
//---
|
||||
|
||||
void frac_size(struct TeX_Node *node)
|
||||
void frac_layout(struct TeX_Node *node)
|
||||
{
|
||||
/* Drop the fraction if there are less than two arguments */
|
||||
if(!args[1]) { node->hidden = 1; return; }
|
||||
|
@ -62,13 +87,13 @@ void frac_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
}
|
||||
|
||||
/* First argument */
|
||||
render_flow(args[0],
|
||||
TeX_flow_render(args[0],
|
||||
x + ((node->width - w(0)) >> 1),
|
||||
y,
|
||||
color);
|
||||
|
||||
/* Second argument */
|
||||
render_flow(args[1],
|
||||
TeX_flow_render(args[1],
|
||||
x + ((node->width - w(1)) >> 1),
|
||||
y + h(0) + 2 * TEX_LAYOUT_SPACING + TEX_FRACTION_BAR_THICKNESS,
|
||||
color);
|
||||
|
@ -81,7 +106,7 @@ void frac_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
//
|
||||
// Left and right delimiters are resizable bracket-like elements used to
|
||||
// parenthesize or decorate terms. They serve a special flow purpose and
|
||||
// are matched together by the size_flow() routine.
|
||||
// are matched together by the TeX_flow_layout() routine.
|
||||
//
|
||||
// Graphical parameters:
|
||||
//
|
||||
|
@ -106,7 +131,7 @@ void frac_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
// seems to resize the contents to make it smoother. Not possible here.
|
||||
//---
|
||||
|
||||
void leftright_size(struct TeX_Node *node)
|
||||
void leftright_layout(struct TeX_Node *node)
|
||||
{
|
||||
int width, height;
|
||||
TeX_size("#", &width, &height);
|
||||
|
@ -224,7 +249,7 @@ void leftright_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
// +-----+
|
||||
//---
|
||||
|
||||
void supsubscript_size(struct TeX_Node *node)
|
||||
void supsubscript_layout(struct TeX_Node *node)
|
||||
{
|
||||
/* Invalidate node if no argument is provided */
|
||||
if(!args[0]) { node->hidden = 1; return; }
|
||||
|
@ -237,7 +262,7 @@ void supsubscript_size(struct TeX_Node *node)
|
|||
void supsubscript_render(struct TeX_Node const * node, int x, int y,
|
||||
int color)
|
||||
{
|
||||
render_flow(args[0], x, y, color);
|
||||
TeX_flow_render(args[0], x, y, color);
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -248,7 +273,7 @@ void supsubscript_render(struct TeX_Node const * node, int x, int y,
|
|||
// Summation symbols are just nodes with a constant size and function.
|
||||
//---
|
||||
|
||||
void sum_size(struct TeX_Node *node)
|
||||
void sum_layout(struct TeX_Node *node)
|
||||
{
|
||||
node->width = 9;
|
||||
node->height = 9;
|
||||
|
@ -272,7 +297,7 @@ void sum_render(__attribute__((unused)) struct TeX_Node const * node, int x,
|
|||
// * flow: display mode when available
|
||||
//---
|
||||
|
||||
void prod_size(struct TeX_Node *node)
|
||||
void prod_layout(struct TeX_Node *node)
|
||||
{
|
||||
node->width = 9;
|
||||
node->height = 9;
|
||||
|
@ -295,7 +320,7 @@ void prod_render(__attribute__((unused)) struct TeX_Node const * node, int x,
|
|||
// * flow: default
|
||||
//---
|
||||
|
||||
void int_size(struct TeX_Node *node)
|
||||
void int_layout(struct TeX_Node *node)
|
||||
{
|
||||
node->width = 5;
|
||||
node->height = TEX_INT_HEIGHT;
|
||||
|
@ -337,7 +362,7 @@ void int_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
// TEX_VEC_MARGIN
|
||||
//---
|
||||
|
||||
void vec_size(struct TeX_Node *node)
|
||||
void vec_layout(struct TeX_Node *node)
|
||||
{
|
||||
int arrow_height = 2 * TEX_VEC_ARROW_LENGTH + 1;
|
||||
|
||||
|
@ -359,7 +384,7 @@ void vec_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
color);
|
||||
|
||||
/* First argument */
|
||||
render_flow(args[0],
|
||||
TeX_flow_render(args[0],
|
||||
x + TEX_VEC_MARGIN,
|
||||
y + TEX_VEC_SPACING + arrow_height,
|
||||
color);
|
||||
|
@ -371,13 +396,13 @@ void vec_render(struct TeX_Node const * node, int x, int y, int color)
|
|||
// * flow: display mode when available
|
||||
//---
|
||||
|
||||
void lim_size(struct TeX_Node *node)
|
||||
void lim_layout(struct TeX_Node *node)
|
||||
{
|
||||
int w, h;
|
||||
TeX_size("lim", &w, &h);
|
||||
node->width = w;
|
||||
node->height = h;
|
||||
node->line = node->height >> 1;
|
||||
node->line = h >> 1;
|
||||
}
|
||||
|
||||
void lim_render(__attribute__((unused)) struct TeX_Node const * node, int x,
|
||||
|
@ -390,41 +415,48 @@ void lim_render(__attribute__((unused)) struct TeX_Node const * node, int x,
|
|||
// The class table and lookup functions
|
||||
//---
|
||||
|
||||
struct TeX_Class TeX_table[] = {
|
||||
/* Text is omitted from the table and has ID 0 */
|
||||
static struct TeX_Class const class_table[] = {
|
||||
/* Text has ID 0 */
|
||||
{ "\\text", text_layout, text_render },
|
||||
|
||||
/* Fractions */
|
||||
{ "frac", frac_size, frac_render },
|
||||
{ "frac", frac_layout, frac_render },
|
||||
|
||||
/* Size-aware opening and closing elements */
|
||||
{ "left", leftright_size, leftright_render },
|
||||
{ "right", leftright_size, leftright_render },
|
||||
{ "left", leftright_layout, leftright_render },
|
||||
{ "right", leftright_layout, leftright_render },
|
||||
|
||||
/* Superscripts and subscripts */
|
||||
{ "\\sup", supsubscript_size, supsubscript_render },
|
||||
{ "\\sub", supsubscript_size, supsubscript_render },
|
||||
{ "\\sup", supsubscript_layout, supsubscript_render },
|
||||
{ "\\sub", supsubscript_layout, supsubscript_render },
|
||||
|
||||
/* Large operator symbols, integral */
|
||||
{ "sum", sum_size, sum_render },
|
||||
{ "prod", prod_size, prod_render },
|
||||
{ "int", int_size, int_render },
|
||||
{ "sum", sum_layout, sum_render },
|
||||
{ "prod", prod_layout, prod_render },
|
||||
{ "int", int_layout, int_render },
|
||||
|
||||
/* Vectors, limits */
|
||||
{ "vec", vec_size, vec_render },
|
||||
{ "lim", lim_size, lim_render },
|
||||
{ "vec", vec_layout, vec_render },
|
||||
{ "lim", lim_layout, lim_render },
|
||||
|
||||
/* NULL terminator */
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
/* class_find() - Find a class using a command name */
|
||||
int class_find(const char *name)
|
||||
/* TeX_class_find(): Find a class using a command name */
|
||||
int TeX_class_find(char const *name)
|
||||
{
|
||||
/* TODO: Do a binary search instead of a linear one */
|
||||
for(int i = 0; TeX_table[i].name; i++)
|
||||
for(int i = 1; class_table[i].name; i++)
|
||||
{
|
||||
if(!strcmp(TeX_table[i].name, name)) return i + 1;
|
||||
if(!strcmp(class_table[i].name, name)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TeX_class_of(): Get the class pointer of a node */
|
||||
struct TeX_Class const *TeX_class_of(struct TeX_Node const * node)
|
||||
{
|
||||
return &class_table[node->type];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/env.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//---
|
||||
// Primary environment: a simple flow
|
||||
//
|
||||
// This environment ignores separators and breaks and concatenates all
|
||||
// nodes in a single flow. It is the type of the outermost environment of
|
||||
// any formula.
|
||||
//---
|
||||
|
||||
struct TeX_Env_Primary
|
||||
{
|
||||
struct TeX_Env env;
|
||||
|
||||
struct TeX_Flow *flow;
|
||||
};
|
||||
|
||||
static void primary_free(struct TeX_Env *env)
|
||||
{
|
||||
struct TeX_Env_Primary *p = (void *)env;
|
||||
TeX_flow_free(p->flow);
|
||||
}
|
||||
|
||||
static void primary_add_node(struct TeX_Env *env, struct TeX_Node *node)
|
||||
{
|
||||
struct TeX_Env_Primary *p = (void *)env;
|
||||
p->flow = TeX_flow_add_node(p->flow, node);
|
||||
}
|
||||
|
||||
static void primary_size(struct TeX_Env *env)
|
||||
{
|
||||
struct TeX_Env_Primary *p = (void *)env;
|
||||
TeX_flow_layout(p->flow);
|
||||
|
||||
p->env.width = p->flow->width;
|
||||
p->env.height = p->flow->height;
|
||||
p->env.line = p->flow->line;
|
||||
}
|
||||
|
||||
static void primary_render(struct TeX_Env *env, int x, int y, int color)
|
||||
{
|
||||
struct TeX_Env_Primary *p = (void *)env;
|
||||
TeX_flow_render(p->flow, x, y, color);
|
||||
}
|
||||
|
||||
struct TeX_Env *TeX_env_primary(void)
|
||||
{
|
||||
struct TeX_Env_Primary *p = malloc(sizeof *p);
|
||||
struct TeX_Env *env = (void *)p;
|
||||
|
||||
env->name = "primary";
|
||||
env->free = primary_free;
|
||||
env->add_node = primary_add_node;
|
||||
env->add_separator = NULL;
|
||||
env->add_break = NULL;
|
||||
env->size = primary_size;
|
||||
env->render = primary_render;
|
||||
|
||||
p->flow = NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
//---
|
||||
// Matrix environment.
|
||||
//---
|
||||
|
||||
/* TODO */
|
||||
|
||||
//---
|
||||
// Environment printers
|
||||
//---
|
||||
|
||||
#ifdef TEX_PRINT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <TeX/print.h>
|
||||
|
||||
/* TeX_print_env_primary(): Print a primary environment to stdout */
|
||||
void TeX_print_env_primary(struct TeX_Env *env, int indent)
|
||||
{
|
||||
struct TeX_Env_Primary *p = (void *)env;
|
||||
|
||||
printf("%*s", indent, "");
|
||||
printf("{primary}\n");
|
||||
|
||||
TeX_print_flow(p->flow, indent + 2);
|
||||
}
|
||||
|
||||
/* TeX_print_env(): Recursively print an environment */
|
||||
void TeX_print_env(struct TeX_Env *env, int indent)
|
||||
{
|
||||
if(!strcmp(env->name, "primary"))
|
||||
{
|
||||
TeX_print_env_primary(env, indent);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%*s{%s} (?)\n", indent, "", env->name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TEX_PRINT */
|
150
src/flow.c
150
src/flow.c
|
@ -1,8 +1,50 @@
|
|||
#include <TeX/TeX.h>
|
||||
#include <TeX/structure.h>
|
||||
#include <TeX/render.h>
|
||||
#include <string.h>
|
||||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/classes.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//---
|
||||
// Flow construction and destruction functions
|
||||
//---
|
||||
|
||||
/* TeX_flow_add_node(): Add a new node to a flow */
|
||||
struct TeX_Flow *TeX_flow_add_node(struct TeX_Flow *flow, struct TeX_Node*node)
|
||||
{
|
||||
if(!node) return flow;
|
||||
|
||||
/* If the flow is non-empty, add the node to it */
|
||||
if(flow)
|
||||
{
|
||||
flow->last->next = node;
|
||||
flow->last = node;
|
||||
return flow;
|
||||
}
|
||||
|
||||
/* Otherwise, create and return a new flow */
|
||||
flow = calloc(1, sizeof *flow);
|
||||
if(!flow) return NULL;
|
||||
|
||||
flow->first = flow->last = node;
|
||||
return flow;
|
||||
}
|
||||
|
||||
/* TeX_flow_free(): Free TeX_Flow objects */
|
||||
void TeX_flow_free(struct TeX_Flow *flow)
|
||||
{
|
||||
if(!flow) return;
|
||||
|
||||
struct TeX_Node *node, *next = NULL;
|
||||
|
||||
for(node = flow->first; node; node = next)
|
||||
{
|
||||
next = node->next;
|
||||
TeX_node_free(node);
|
||||
}
|
||||
|
||||
free(flow);
|
||||
}
|
||||
//---
|
||||
// Chunks to place subscripts and superscripts
|
||||
//---
|
||||
|
@ -46,7 +88,7 @@ struct chunk
|
|||
};
|
||||
|
||||
/* chunk_make(): Make a new chunk for a base node */
|
||||
void chunk_make(struct chunk *chunk, struct TeX_Node *base, int mode)
|
||||
static void chunk_make(struct chunk *chunk, struct TeX_Node *base, int mode)
|
||||
{
|
||||
chunk->base = base;
|
||||
chunk->mode = mode;
|
||||
|
@ -60,19 +102,19 @@ void chunk_make(struct chunk *chunk, struct TeX_Node *base, int mode)
|
|||
}
|
||||
|
||||
/* chunk_reset(): Empty a chunk */
|
||||
void chunk_reset(struct chunk *chunk)
|
||||
static void chunk_reset(struct chunk *chunk)
|
||||
{
|
||||
memset(chunk, 0, sizeof *chunk);
|
||||
}
|
||||
|
||||
/* chunk_set(): Check whether a chunk is empty */
|
||||
int chunk_set(struct chunk *chunk)
|
||||
static int chunk_set(struct chunk *chunk)
|
||||
{
|
||||
return chunk->base != NULL;
|
||||
}
|
||||
|
||||
/* chunk_set_subscript(): Add a subscript on a chunk */
|
||||
void chunk_set_subscript(struct chunk *chunk, struct TeX_Node *sub)
|
||||
static void chunk_set_subscript(struct chunk *chunk, struct TeX_Node *sub)
|
||||
{
|
||||
/* Silently refuse double index */
|
||||
if(chunk->sub) return;
|
||||
|
@ -81,7 +123,7 @@ void chunk_set_subscript(struct chunk *chunk, struct TeX_Node *sub)
|
|||
}
|
||||
|
||||
/* chunk_set_superscript(): Add a superscript on a chunk */
|
||||
void chunk_set_superscript(struct chunk *chunk, struct TeX_Node *sup)
|
||||
static void chunk_set_superscript(struct chunk *chunk, struct TeX_Node *sup)
|
||||
{
|
||||
/* Silently refuse double exponent */
|
||||
if(chunk->sup) return;
|
||||
|
@ -89,8 +131,8 @@ void chunk_set_superscript(struct chunk *chunk, struct TeX_Node *sup)
|
|||
chunk->sup = sup;
|
||||
}
|
||||
|
||||
/* chunk_compute_inline(): Compute chunk layout for inline mode */
|
||||
void chunk_compute_inline(struct chunk *chunk)
|
||||
/* chunk_layout_inline(): Compute chunk layout for inline mode */
|
||||
static void chunk_layout_inline(struct chunk *chunk)
|
||||
{
|
||||
struct TeX_Node *base = chunk->base;
|
||||
struct TeX_Node *sup = chunk->sup;
|
||||
|
@ -132,8 +174,8 @@ void chunk_compute_inline(struct chunk *chunk)
|
|||
chunk->line = over;
|
||||
}
|
||||
|
||||
/* chunk_compute_display(): Compute chunk layout for display mode */
|
||||
void chunk_compute_display(struct chunk *chunk)
|
||||
/* chunk_layout_display(): Compute chunk layout for display mode */
|
||||
static void chunk_layout_display(struct chunk *chunk)
|
||||
{
|
||||
struct TeX_Node *base = chunk->base;
|
||||
struct TeX_Node *sup = chunk->sup;
|
||||
|
@ -174,27 +216,27 @@ void chunk_compute_display(struct chunk *chunk)
|
|||
base->x += (chunk->width - base->width) >> 1;
|
||||
}
|
||||
|
||||
/* chunk_compute_slanted(): Compute chunk layout for slanted mode */
|
||||
void chunk_compute_slanted(struct chunk *chunk)
|
||||
/* chunk_layout_slanted(): Compute chunk layout for slanted mode */
|
||||
static void chunk_layout_slanted(struct chunk *chunk)
|
||||
{
|
||||
/* TODO: Slanted mode for exponents and subscripts */
|
||||
chunk_compute_inline(chunk);
|
||||
chunk_layout_inline(chunk);
|
||||
}
|
||||
|
||||
/* chunk_compute(): Compute the layout of a chunk after it's filled */
|
||||
void chunk_compute(struct chunk *chunk)
|
||||
/* chunk_layout(): Compute the layout of a chunk after it's filled */
|
||||
static void chunk_layout(struct chunk *chunk)
|
||||
{
|
||||
if(chunk->mode == TEX_CHUNK_INLINE)
|
||||
{
|
||||
chunk_compute_inline(chunk);
|
||||
chunk_layout_inline(chunk);
|
||||
}
|
||||
if(chunk->mode == TEX_CHUNK_DISPLAY)
|
||||
{
|
||||
chunk_compute_display(chunk);
|
||||
chunk_layout_display(chunk);
|
||||
}
|
||||
if(chunk->mode == TEX_CHUNK_SLANTED)
|
||||
{
|
||||
chunk_compute_slanted(chunk);
|
||||
chunk_layout_slanted(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +255,7 @@ struct group
|
|||
};
|
||||
|
||||
/* group_open(): Create new group initialized with a left node */
|
||||
void group_open(struct group *group, struct TeX_Node *left)
|
||||
static void group_open(struct group *group, struct TeX_Node *left)
|
||||
{
|
||||
group->left = left;
|
||||
group->above = 0;
|
||||
|
@ -221,7 +263,7 @@ void group_open(struct group *group, struct TeX_Node *left)
|
|||
}
|
||||
|
||||
/* group_update(): Update a group with a new node */
|
||||
void group_update(struct group *group, struct chunk *chunk)
|
||||
static void group_update(struct group *group, struct chunk *chunk)
|
||||
{
|
||||
/* Account for node displacement around baseline */
|
||||
group->above = max(chunk->line, group->above);
|
||||
|
@ -229,7 +271,7 @@ void group_update(struct group *group, struct chunk *chunk)
|
|||
}
|
||||
|
||||
/* group_close(): Close a group with its right node */
|
||||
void group_close(struct group *group, struct TeX_Node *right)
|
||||
static void group_close(struct group *group, struct TeX_Node *right)
|
||||
{
|
||||
struct TeX_Node *left = group->left;
|
||||
int above = group->above;
|
||||
|
@ -256,14 +298,14 @@ void group_close(struct group *group, struct TeX_Node *right)
|
|||
}
|
||||
|
||||
//---
|
||||
// Laying out flows
|
||||
// Layout and rendering
|
||||
//---
|
||||
|
||||
static void update(struct chunk *c, struct group *g, int *x, int *above,
|
||||
int *below)
|
||||
{
|
||||
if(!chunk_set(c)) return;
|
||||
chunk_compute(c);
|
||||
chunk_layout(c);
|
||||
group_update(g, c);
|
||||
|
||||
*x += c->width + TEX_LAYOUT_SPACING;
|
||||
|
@ -273,8 +315,8 @@ static void update(struct chunk *c, struct group *g, int *x, int *above,
|
|||
chunk_reset(c);
|
||||
}
|
||||
|
||||
/* size_flow(): Calculate the layout of a flow */
|
||||
void size_flow(struct TeX_Flow *flow)
|
||||
/* TeX_flow_layout(): Calculate the layout of a flow */
|
||||
void TeX_flow_layout(struct TeX_Flow *flow)
|
||||
{
|
||||
/* Current node and current chunk */
|
||||
struct TeX_Node *node;
|
||||
|
@ -285,16 +327,13 @@ void size_flow(struct TeX_Flow *flow)
|
|||
int above = 0;
|
||||
int below = 0;
|
||||
/* Nested groups and current position */
|
||||
struct group groups[TEX_LEFTRIGHT_NESTING];
|
||||
struct group groups[TEX_LEFTRIGHT_DEPTH];
|
||||
struct group *g = groups;
|
||||
|
||||
for(node = flow->first; node; node = node->next)
|
||||
{
|
||||
char const *class = "";
|
||||
size_node(node);
|
||||
|
||||
if(node->type != TEX_NODECLASS_TEXT)
|
||||
class = TeX_table[node->type - 1].name;
|
||||
char const *class = TeX_class_of(node)->name;
|
||||
TeX_node_layout(node);
|
||||
|
||||
if(!strcmp(class, "\\sup"))
|
||||
{
|
||||
|
@ -309,7 +348,7 @@ void size_flow(struct TeX_Flow *flow)
|
|||
/* Leave the last chunk and make a new one */
|
||||
update(&c, g, &x, &above, &below);
|
||||
|
||||
if(!strcmp(class, "left") && g-groups < TEX_LEFTRIGHT_NESTING)
|
||||
if(!strcmp(class, "left") && g-groups < TEX_LEFTRIGHT_DEPTH)
|
||||
{
|
||||
g++;
|
||||
group_open(g, node);
|
||||
|
@ -344,18 +383,43 @@ void size_flow(struct TeX_Flow *flow)
|
|||
flow->width = max(x - TEX_LAYOUT_SPACING, 0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Rendering flows
|
||||
//---
|
||||
|
||||
/* render_flow() - Render a flow and all its components */
|
||||
void render_flow(struct TeX_Flow const * flow, int x, int y, int color)
|
||||
/* TeX_flow_render(): Render a flow and all its components */
|
||||
void TeX_flow_render(struct TeX_Flow const * flow, int x, int y, int color)
|
||||
{
|
||||
struct TeX_Node const * node;
|
||||
|
||||
for(node = flow->first; node; node = node->next)
|
||||
{
|
||||
render_node(node, x + node->x, y + flow->line - node->line +
|
||||
node->l, color);
|
||||
TeX_node_render(node, x + node->x, y + flow->line -
|
||||
node->line + node->l, color);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Printing function
|
||||
//---
|
||||
|
||||
#ifdef TEX_PRINT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <TeX/print.h>
|
||||
|
||||
/* TeX_print_flow(): Print a flow's tree */
|
||||
void TeX_print_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);
|
||||
|
||||
struct TeX_Node *node = flow->first;
|
||||
|
||||
while(node)
|
||||
{
|
||||
TeX_print_node(node, indent + 4);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TEX_PRINT */
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
#include <TeX/config.h>
|
||||
#include <TeX/node.h>
|
||||
#include <TeX/flow.h>
|
||||
#include <TeX/classes.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//---
|
||||
// Node creation and destruction functions
|
||||
//---
|
||||
|
||||
/* 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 node */
|
||||
if(node->type == TEX_NODECLASS_TEXT)
|
||||
{
|
||||
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_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);
|
||||
|
||||
/* 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(node->type != TEX_NODECLASS_TEXT)
|
||||
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
|
||||
{
|
||||
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 */
|
|
@ -5,8 +5,7 @@
|
|||
#ifdef TEX_PLATFORM_CLI
|
||||
|
||||
#include <TeX/TeX.h>
|
||||
#include <TeX/structure.h>
|
||||
#include <TeX/parser.h>
|
||||
#include <TeX/print.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -15,17 +14,17 @@
|
|||
// Interface functions
|
||||
//---
|
||||
|
||||
/* dpixel() - Set a single pixel */
|
||||
/* dpixel(): Set a single pixel */
|
||||
void dpixel(int x, int y, int color)
|
||||
{
|
||||
}
|
||||
|
||||
/* dline() - Draw a line */
|
||||
/* dline(): Draw a line */
|
||||
void dline(int x1, int y1, int x2, int y2, int color)
|
||||
{
|
||||
}
|
||||
|
||||
/* dsize() - Get the dimensions of a string */
|
||||
/* dsize(): Get the dimensions of a string */
|
||||
void dsize(char const *str, int *width, int *height)
|
||||
{
|
||||
/* Use fx9860g size standards */
|
||||
|
@ -33,7 +32,7 @@ void dsize(char const *str, int *width, int *height)
|
|||
*height = 7;
|
||||
}
|
||||
|
||||
/* dtext() - Draw variable-width text */
|
||||
/* dtext(): Draw variable-width text */
|
||||
void dtext(char const *str, int x, int y, int color)
|
||||
{
|
||||
}
|
||||
|
@ -50,7 +49,7 @@ void test_auto(void)
|
|||
|
||||
printf("\e[36m<><><>\e[0m \e[1mParsing\e[0m\n\n"); */
|
||||
|
||||
struct TeX_Flow *flow = NULL;
|
||||
struct TeX_Env *env = NULL;
|
||||
|
||||
/* flow = TeX_parse("\\frac{1}{2}vt^2 = \\int_a^{b} f(x) dx");
|
||||
if(!flow) puts("parsing error!");
|
||||
|
@ -64,14 +63,10 @@ void test_auto(void)
|
|||
TeX_free(flow);
|
||||
printf("\n"); */
|
||||
|
||||
flow = TeX_parse("\\frac{1}{\\left(\\frac{12}{27}\\right)^2}");
|
||||
if(!flow) puts("parsing error!");
|
||||
else
|
||||
{
|
||||
size_flow(flow);
|
||||
TeX_debug_flow(flow, 0);
|
||||
}
|
||||
TeX_free(flow);
|
||||
env = TeX_parse("\\frac{1}{\\left(\\frac{12}{27}\\right)^2}");
|
||||
if(!env) puts("parsing error!");
|
||||
else TeX_print_env(env, 0);
|
||||
TeX_free(env);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -97,13 +92,13 @@ int main(void)
|
|||
}
|
||||
|
||||
printf("\n\e[36m<><><>\e[0m \e[1mLexing\e[0m\n\n");
|
||||
TeX_debug_lex(formula);
|
||||
TeX_print_lex(formula);
|
||||
|
||||
printf("\n\e[36m<><><>\e[0m \e[1mParsing\e[0m\n\n");
|
||||
struct TeX_Flow *flow = TeX_parse(formula);
|
||||
if(!flow) puts("parsing error!");
|
||||
else TeX_debug_flow(flow, 0);
|
||||
TeX_free(flow);
|
||||
struct TeX_Env *env = TeX_parse(formula);
|
||||
if(!env) puts("parsing error!");
|
||||
else TeX_print_env(env, 0);
|
||||
TeX_free(env);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
|
||||
#ifdef TEX_PLATFORM_SDL
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stdlib.h>
|
||||
#include <TeX/TeX.h>
|
||||
#include <TeX/parser.h>
|
||||
#include <TeX/print.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Rendering window and renderer */
|
||||
static SDL_Window *w = NULL;
|
||||
|
@ -50,7 +52,7 @@ static SDL_Texture *font = NULL;
|
|||
// Interface functions
|
||||
//---
|
||||
|
||||
/* intf_pixel() - Set a single pixel */
|
||||
/* intf_pixel(): Set a single pixel */
|
||||
void intf_pixel(int x, int y, int color)
|
||||
{
|
||||
int R = color >> 16;
|
||||
|
@ -61,7 +63,7 @@ void intf_pixel(int x, int y, int color)
|
|||
SDL_RenderDrawPoint(r, x, y);
|
||||
}
|
||||
|
||||
/* intf_line() - Draw a line */
|
||||
/* intf_line(): Draw a line */
|
||||
void intf_line(int x1, int y1, int x2, int y2, int color)
|
||||
{
|
||||
int R = color >> 16;
|
||||
|
@ -72,14 +74,14 @@ void intf_line(int x1, int y1, int x2, int y2, int color)
|
|||
SDL_RenderDrawLine(r, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/* intf_size() - Get the dimensions of a string */
|
||||
/* intf_size(): Get the dimensions of a string */
|
||||
void intf_size(char const *str, int *width, int *height)
|
||||
{
|
||||
*width = (FONT_WIDTH + 1) * strlen(str) - 1;
|
||||
*height = FONT_HEIGHT;
|
||||
}
|
||||
|
||||
/* intf_text() - Draw variable-width text */
|
||||
/* intf_text(): Draw variable-width text */
|
||||
void intf_text(char const *str, int x, int y, int color)
|
||||
{
|
||||
int R = color >> 16;
|
||||
|
@ -166,17 +168,16 @@ int main(void)
|
|||
{
|
||||
char const * formula =
|
||||
"\\frac{x^7\\left[X,Y\\right]+3\\left|\\frac{A}{B}\\right>}"
|
||||
"{\\lim_{x->0}\\left\\{\\frac{a_k+b_k}{k!}\\right\\}^5}"
|
||||
" + \\int_a^b\\frac{\\left(b-t\\right)^{n+1}}{n!}dt"
|
||||
" + \\vec{A}.\\vec{B}";
|
||||
"{\\left\\{\\frac{a_k+b_k}{k!}\\right\\}^5}"
|
||||
" + \\int_a^b\\frac{\\left(b-t\\right)^{n+1}}{n!}dt";
|
||||
|
||||
struct TeX_Flow *flow = TeX_parse(formula);
|
||||
if(!flow) { puts("parsing error!"); return 1; }
|
||||
TeX_debug_flow(flow, 0);
|
||||
struct TeX_Env *env = TeX_parse(formula);
|
||||
if(!env) { puts("parsing error!"); return 1; }
|
||||
TeX_print_env(env, 0);
|
||||
|
||||
SDL_SetRenderDrawColor(r, 0xff, 0xff, 0xff, 0xff);
|
||||
SDL_RenderClear(r);
|
||||
TeX_draw(flow, 10, 10, 0x000000);
|
||||
TeX_draw(env, 10, 10, 0x000000);
|
||||
SDL_RenderPresent(r);
|
||||
|
||||
SDL_Event e;
|
||||
|
@ -186,7 +187,7 @@ int main(void)
|
|||
if(e.type == SDL_QUIT) break;
|
||||
}
|
||||
|
||||
TeX_free(flow);
|
||||
TeX_free(env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#include <TeX/vector.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef vector_make
|
||||
|
||||
/* vector_make(): Make a new vector of a given type */
|
||||
void vector_make(vector_t *v, size_t elsize)
|
||||
{
|
||||
v->len = 0;
|
||||
v->size = 0;
|
||||
v->elsize = elsize;
|
||||
v->data = NULL;
|
||||
}
|
||||
|
||||
/* vector_append(): Append a new element to a vector */
|
||||
void vector_append(vector_t *v, void *element)
|
||||
{
|
||||
/* Realloc whenever the vector runs out of space */
|
||||
if(v->len >= v->size)
|
||||
{
|
||||
v->data = realloc(v->data, (v->size + 8) * v->elsize);
|
||||
v->size += 8;
|
||||
}
|
||||
|
||||
memcpy(v->data + v->len * v->elsize, element, v->elsize);
|
||||
v->len++;
|
||||
}
|
||||
|
||||
/* vector_free(): Free a vector's contents */
|
||||
void vector_free(vector_t *v)
|
||||
{
|
||||
free(v->data);
|
||||
}
|
Loading…
Reference in New Issue