diff --git a/TODO.md b/TODO.md index 680f717..1b44d49 100644 --- a/TODO.md +++ b/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 diff --git a/config/cli.cfg b/config/cli.cfg index fc847e8..3d207a0 100644 --- a/config/cli.cfg +++ b/config/cli.cfg @@ -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 diff --git a/config/sdl2.cfg b/config/sdl2.cfg index 5412cb4..5cb7f8b 100644 --- a/config/sdl2.cfg +++ b/config/sdl2.cfg @@ -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 diff --git a/include/TeX/TeX.h b/include/TeX/TeX.h index 9ff16fe..5f2726b 100644 --- a/include/TeX/TeX.h +++ b/include/TeX/TeX.h @@ -2,72 +2,12 @@ // TeX: Natural rendering for mathematical formulae //--- -#ifndef TEX_H -#define TEX_H +#ifndef TEX_TEX +#define TEX_TEX +#include #include -//--- -// 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 for the details */ -struct TeX_Flow; +/* Opaque declaration: see 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 */ diff --git a/include/TeX/classes.h b/include/TeX/classes.h new file mode 100644 index 0000000..79575fe --- /dev/null +++ b/include/TeX/classes.h @@ -0,0 +1,61 @@ +//--- +// class: Node classes +//--- + +#include + +/* 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 + 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 + 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); + diff --git a/include/TeX/config.h b/include/TeX/config.h new file mode 100644 index 0000000..3b025a7 --- /dev/null +++ b/include/TeX/config.h @@ -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 */ diff --git a/include/TeX/defs.h b/include/TeX/defs.h index 3fdbe3b..33cf822 100644 --- a/include/TeX/defs.h +++ b/include/TeX/defs.h @@ -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); \ diff --git a/include/TeX/env.h b/include/TeX/env.h new file mode 100644 index 0000000..940328c --- /dev/null +++ b/include/TeX/env.h @@ -0,0 +1,58 @@ +//--- +// env: Environments +//--- + +#ifndef TEX_ENV +#define TEX_ENV + +#include + +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 */ diff --git a/include/TeX/flow.h b/include/TeX/flow.h new file mode 100644 index 0000000..f12952a --- /dev/null +++ b/include/TeX/flow.h @@ -0,0 +1,57 @@ +//--- +// flow: Horizontal flows of nodes arranged from left to right +//--- + +#ifndef TEX_FLOW +#define TEX_FLOW + +#include + +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 */ diff --git a/include/TeX/interface.h b/include/TeX/interface.h new file mode 100644 index 0000000..28c85a5 --- /dev/null +++ b/include/TeX/interface.h @@ -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 */ diff --git a/include/TeX/node.h b/include/TeX/node.h new file mode 100644 index 0000000..2c010d3 --- /dev/null +++ b/include/TeX/node.h @@ -0,0 +1,94 @@ +//--- +// node: Mathematical constructs in the formula tree +//--- + +#ifndef TEX_NODE +#define TEX_NODE + +#include +#include +#include +#include + +/* 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 */ diff --git a/include/TeX/parser.h b/include/TeX/parser.h deleted file mode 100644 index acb5515..0000000 --- a/include/TeX/parser.h +++ /dev/null @@ -1,44 +0,0 @@ -//--- -// parser: interface to the TeX parser -//--- - -#ifndef TEX_PARSER -#define TEX_PARSER - -#include -#include - -/* 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 */ diff --git a/include/TeX/print.h b/include/TeX/print.h new file mode 100644 index 0000000..09f9246 --- /dev/null +++ b/include/TeX/print.h @@ -0,0 +1,29 @@ +//--- +// print: Command-line structure printing +//--- + +#ifdef TEX_PRINT + +#include +#include +#include + +#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 */ diff --git a/include/TeX/render.h b/include/TeX/render.h deleted file mode 100644 index 5baf9b0..0000000 --- a/include/TeX/render.h +++ /dev/null @@ -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 */ diff --git a/include/TeX/structure.h b/include/TeX/structure.h deleted file mode 100644 index d8ddaf1..0000000 --- a/include/TeX/structure.h +++ /dev/null @@ -1,153 +0,0 @@ -//--- -// structure: Recursive node/flow data structure -//--- - -#ifndef TEX_STRUCTURE -#define TEX_STRUCTURE - -#include -#include -#include - -/* 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 */ diff --git a/include/TeX/vector.h b/include/TeX/vector.h new file mode 100644 index 0000000..c8d8ff2 --- /dev/null +++ b/include/TeX/vector.h @@ -0,0 +1,36 @@ +//--- +// vector: Simple vectors that only grow +//--- + +#ifndef TEX_VECTOR +#define TEX_VECTOR + +#include +#include + +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 */ diff --git a/src/TeX.c b/src/TeX.c index 3b31116..d6a8d82 100644 --- a/src/TeX.c +++ b/src/TeX.c @@ -1,8 +1,11 @@ #include +#include +#include +#include + #include -#include -#include -#include +#include + #include #include @@ -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); } diff --git a/src/TeX.y b/src/TeX.y index 18d3663..12bf030 100644 --- a/src/TeX.y +++ b/src/TeX.y @@ -1,191 +1,136 @@ -/* TODO Notes for the calculator version. - Don't use GLR (= non-deterministic forks) - Provide , , , - 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 -#include -#include +#include +#include +#include +#include #include #include -/* 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 TEXT -%token COMMAND -%token COMMAND_ABS +/* Basic text and commands */ +%token TEXT +%token COMMAND +%token COMMAND_ABS + +/* Special tokens for environments */ +%token ENV_BEGIN +%token ENV_END + +/* Separators ("&") and breaks ("\\") */ +%token SEPARATOR +%token BREAK + %left '^' '_' %type flow %type node %type node_abs +%type env_node +%type 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 +#include -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 */ diff --git a/src/classes.c b/src/classes.c index bf154ce..8b527c8 100644 --- a/src/classes.c +++ b/src/classes.c @@ -2,10 +2,14 @@ // classes: Node classes used for mathematical notations //--- -#include -#include -#include +#include +#include +#include +#include + +#include #include + #include @@ -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]; +} diff --git a/src/env.c b/src/env.c new file mode 100644 index 0000000..445bb9a --- /dev/null +++ b/src/env.c @@ -0,0 +1,108 @@ +#include +#include +#include + +#include +#include + +//--- +// 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 +#include + +/* 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 */ diff --git a/src/flow.c b/src/flow.c index 6b5bbf2..08d1248 100644 --- a/src/flow.c +++ b/src/flow.c @@ -1,8 +1,50 @@ -#include -#include -#include -#include +#include +#include +#include +#include +#include + +//--- +// 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 +#include + +/* 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 */ diff --git a/src/node.c b/src/node.c new file mode 100644 index 0000000..9603807 --- /dev/null +++ b/src/node.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include +#include + +//--- +// 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 +#include + +/* 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 */ diff --git a/src/platform/cli.c b/src/platform/cli.c index 07be0dd..70d3bc4 100644 --- a/src/platform/cli.c +++ b/src/platform/cli.c @@ -5,8 +5,7 @@ #ifdef TEX_PLATFORM_CLI #include -#include -#include +#include #include #include @@ -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; diff --git a/src/platform/sdl2.c b/src/platform/sdl2.c index dd4a66d..11307db 100644 --- a/src/platform/sdl2.c +++ b/src/platform/sdl2.c @@ -4,10 +4,12 @@ #ifdef TEX_PLATFORM_SDL -#include -#include #include -#include +#include + +#include + +#include /* 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; } diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..99721b3 --- /dev/null +++ b/src/vector.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#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); +}