Browse Source

implement environments and refactor

master
Lephenixnoir 6 months ago
parent
commit
9be3414e96
25 changed files with 1122 additions and 726 deletions
  1. +16
    -4
      TODO.md
  2. +1
    -1
      config/cli.cfg
  3. +1
    -1
      config/sdl2.cfg
  4. +18
    -78
      include/TeX/TeX.h
  5. +61
    -0
      include/TeX/classes.h
  6. +76
    -0
      include/TeX/config.h
  7. +1
    -1
      include/TeX/defs.h
  8. +58
    -0
      include/TeX/env.h
  9. +57
    -0
      include/TeX/flow.h
  10. +14
    -0
      include/TeX/interface.h
  11. +94
    -0
      include/TeX/node.h
  12. +0
    -44
      include/TeX/parser.h
  13. +29
    -0
      include/TeX/print.h
  14. +0
    -23
      include/TeX/render.h
  15. +0
    -153
      include/TeX/structure.h
  16. +36
    -0
      include/TeX/vector.h
  17. +35
    -94
      src/TeX.c
  18. +127
    -219
      src/TeX.y
  19. +65
    -33
      src/classes.c
  20. +108
    -0
      src/env.c
  21. +105
    -41
      src/flow.c
  22. +156
    -0
      src/node.c
  23. +15
    -20
      src/platform/cli.c
  24. +15
    -14
      src/platform/sdl2.c
  25. +34
    -0
      src/vector.c

+ 16
- 4
TODO.md View File

@@ -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
- 1
config/cli.cfg View File

@@ -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
- 1
config/sdl2.cfg View File

@@ -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

+ 18
- 78
include/TeX/TeX.h View File

@@ -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 */

+ 61
- 0
include/TeX/classes.h View File

@@ -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);


+ 76
- 0
include/TeX/config.h View File

@@ -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 */

+ 1
- 1
include/TeX/defs.h View File

@@ -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); \

+ 58
- 0
include/TeX/env.h View File

@@ -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 */

+ 57
- 0
include/TeX/flow.h View File

@@ -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 */

+ 14
- 0
include/TeX/interface.h View File

@@ -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 */

+ 94
- 0
include/TeX/node.h View File

@@ -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 */

+ 0
- 44
include/TeX/parser.h View File

@@ -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 */

+ 29
- 0
include/TeX/print.h View File

@@ -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 */

+ 0
- 23
include/TeX/render.h View File

@@ -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 */

+ 0
- 153
include/TeX/structure.h View File

@@ -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 */

+ 36
- 0
include/TeX/vector.h View File

@@ -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 */

+ 35
- 94
src/TeX.c View File

@@ -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);
env->free(env);
}

//---
// Node operations: size computation and rendering
// Module functions
//---

/* size_node() - Calculate the size of node */
void size_node(struct TeX_Node *node)
/* TeX_parse(): Parse a TeX formula */
struct TeX_Env *TeX_parse(char const *formula)
{
/* 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);
}
/* Provided by the parser. */
struct TeX_Env *parse(char const *str);

/* 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);
}
struct TeX_Env *env = parse(formula);
if(!env) return NULL;

//---
// Module functions
//---
env->size(env);
return env;
}

/* TeX_parse() - Parse a TeX formula */
struct TeX_Flow *TeX_parse(const char *formula)
/* TeX_draw(): Render a parsed formula */
void TeX_draw(struct TeX_Env *formula, int x, int y, int color)
{
struct TeX_Flow *flow;

parse_start(formula);
flow = parse();
if(!flow) return NULL;

size_flow(flow);
return flow;
formula->render(formula, x, y, color);
}

/* TeX_draw() - Render a parsed formula */
void TeX_draw(struct TeX_Flow *formula, int x, int y, int color)
/* TeX_interpret(): Parse and render, in sequence */
void TeX_interpret(char const *formula, int x, int y, int color)
{
render_flow(formula, x, y, color);
struct TeX_Env *env = TeX_parse(formula);
if(!env) return;

TeX_draw(env, x, y, color);
TeX_free(env);
}

+ 127
- 219
src/TeX.y View File

@@ -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 */

+ 65
- 33
src/classes.c View File

@@ -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];
}

+ 108
- 0
src/env.c View File

@@ -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 */

+ 105
- 41
src/flow.c View File

@@ -1,8 +1,50 @@
#include <TeX/TeX.h>
#include <TeX/structure.h>
#include <TeX/render.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)
{