diff options
Diffstat (limited to 'support/ltx2mathml/src')
-rw-r--r-- | support/ltx2mathml/src/ltx2mathml.h | 22 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmlclasses.cpp | 185 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmlclasses.h | 40 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmlexceptions.h | 51 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmlparser.cpp | 2367 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmltables.cpp | 672 | ||||
-rw-r--r-- | support/ltx2mathml/src/ltx2mathmltables.h | 125 | ||||
-rw-r--r-- | support/ltx2mathml/src/test.cpp | 41 |
8 files changed, 3503 insertions, 0 deletions
diff --git a/support/ltx2mathml/src/ltx2mathml.h b/support/ltx2mathml/src/ltx2mathml.h new file mode 100644 index 0000000000..4bbd227245 --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathml.h @@ -0,0 +1,22 @@ +#pragma once + +#include <string> + +using namespace std; + +// 'input': the latex formula to convert +// 'len' : the length of 'input'; use a value <0 to indicate that 'input' is null terminated +// 'errorIndex': on error, this gives the location in 'input' where the error was found + +bool convertFormula( const char *input, int len, int *errorIndex ); + +// raw copy, i.e., without the '<math>' tag +const char *getMathMLOutput(); + +// 'buf': the buffer where the output will be written to +// 'display: flag indicating whether the output is either an inline or a display equation +bool getMathMLOutput(string &buf, bool display); + +// function for obtaining the error message + +const char *getLastError(); diff --git a/support/ltx2mathml/src/ltx2mathmlclasses.cpp b/support/ltx2mathml/src/ltx2mathmlclasses.cpp new file mode 100644 index 0000000000..81dbc603dd --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmlclasses.cpp @@ -0,0 +1,185 @@ + +#include "ltx2mathmlclasses.h" +#include <stdio.h> + +Buffer::Buffer() +{ + m_buf = NULL; + m_index = 0; + m_size = 0; +} + +Buffer::~Buffer() +{ + destroy(); +} + +void Buffer::destroy() +{ + if( m_buf != NULL ) + free( m_buf ); + + m_buf = NULL; + m_index = 0; +} + +void Buffer::setlength( size_t len ) +{ + char *tmp = (char *) realloc( m_buf, len + 1 ); + + if( tmp != NULL ) + { + m_buf = tmp; + + m_size = len; + if( m_index > len ) // truncated + { + m_index = len; + } + else + { + ZeroMemory( &m_buf[ m_index ], ( len - m_index ) ); + } + } + else + throw ex_out_of_memory; +} + +size_t Buffer::length() +{ + return m_index; +} + +void Buffer::format( const char *fmt, ... ) +{ + va_list list; + size_t len; + char s[256]; + + va_start( list, fmt ); + //len = vsprintf_s( s, sizeof( s ) - 1, fmt, list ); + len = vsprintf( s, fmt, list ); + va_end( list ); + _write( m_index, s, len ); +} + + +void Buffer::_write( size_t index, const char *s, size_t len ) +{ + if( !s || ( len <= 0 ) ) + { + return; + } + + if( ( m_size - m_index ) <= len ) + { + setlength( m_size + len + 1); + + } + + memcpy( &m_buf[ index ], s, len ); + + + m_index += len; +} + + +void Buffer::write( const char *s, size_t len ) +{ + _write( m_index, s, len ); +} + +void Buffer::write( const char *s ) +{ + _write( m_index, s, strlen( s ) ); +} + +char *Buffer::data( size_t *len ) +{ + if( len != NULL ) + { + *len = m_index; + } + + return m_buf; +} + + +void Buffer::append( Buffer &buf, bool transfer ) +{ + if( transfer && m_index == 0 ) + { + BufferStruct temp; + + buf.releaseBuffer( temp ); + + m_buf = temp.m_buf; + m_index = temp.m_index; + m_size = temp.m_size; + } + else + { + _write( m_index, buf.m_buf, buf.m_index ); + } +} + +void Buffer::releaseBuffer( BufferStruct &buf ) +{ + buf.m_buf = m_buf; + buf.m_index = m_index; + buf.m_size = m_size; + + m_size = 0; + m_index = 0; + m_buf = NULL; +} + +void Buffer::reset() +{ + ZeroMemory( m_buf, m_index ); + m_index = 0; +} + +char *Buffer::release() +{ + char *temp; + + temp = m_buf; + m_size = 0; + m_index = 0; + m_buf = NULL; + + return temp; +} + +void Buffer::insertAt( size_t index, const char *s ) +{ + size_t len, count; + char *src, *dest; + + len = strlen( s ); + + if( len == 0 ) + return; + + if( index >= m_index ) + { + _write( index, s, len ); + return; + } + + if( ( m_size - m_index ) <= len ) + { + setlength( m_size + len + 1); + } + src = m_buf + index; + dest = src + len; + + count = m_index - index; + + //memmove_s( dest, m_size, src, count ); + + memmove( dest, src, count ); + memcpy( m_buf + index, s, len ); + m_index += len; +} diff --git a/support/ltx2mathml/src/ltx2mathmlclasses.h b/support/ltx2mathml/src/ltx2mathmlclasses.h new file mode 100644 index 0000000000..4e3eed5dfe --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmlclasses.h @@ -0,0 +1,40 @@ +#pragma once + +#include <memory.h> +#include <stdarg.h> +#include <malloc.h> +#include <string.h> +#include "ltx2mathmlexceptions.h" + +#ifndef ZeroMemory +#define ZeroMemory(p,n) memset( (p), 0, (n) ) +#endif + + + +struct BufferStruct { + char *m_buf; + size_t m_index, m_size; +}; + +struct Buffer { + char *m_buf; + size_t m_index, m_size; + Buffer(); + ~Buffer(); + void setlength( size_t len ); + void write( const char *s, size_t len ); + void write( const char *s ); + size_t length(); + void append( Buffer &buf, bool transfer = false ); + char *data( size_t *len = NULL ); + void insertAt( size_t index, const char *s ); + void format( const char *fmt, ... ); + void releaseBuffer( BufferStruct &buf ); + void reset(); + char *release(); + void destroy(); +private: + void _write( size_t index, const char *s, size_t len ); +}; + diff --git a/support/ltx2mathml/src/ltx2mathmlexceptions.h b/support/ltx2mathml/src/ltx2mathmlexceptions.h new file mode 100644 index 0000000000..5131c5dec3 --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmlexceptions.h @@ -0,0 +1,51 @@ +#ifndef __exception +#define __exception + + +enum ex_exception { + ex_syntax_error, + ex_out_of_memory, //"Out of memory"}, + ex_missing_lbrace, //"Missing '{'"}, + ex_prefix_superscript, //"Illegal prefix superscript: use the '\\lsup' command"}, + ex_prefix_subscript, //"Illegal prefix superscript: use the '\\lsub' command"}, + ex_misplaced_column_separator, //"Misplaced column separator"}, + ex_more_rbrace_than_lbrace, //"Syntax error: more '}' than '{'"}, + ex_control_name_too_long, //"Control name too long: maximum is 32"}, + ex_misplaced_row_separator, //"Misplaced row separator"}, + ex_illegal_subscript, //"Illegal subscript"}, + ex_illegal_superscript, //"Illegal superscript"}, + ex_undefined_control_sequence, //"Undefined control sequence"}, + ex_misplaced_inline_formula, //"Misplaced inline formula"}, + ex_missing_parameter, //"Missing parameter"}, + ex_more_lbrace_than_rbrace, //"Syntax error: more '{' than '}'"}, + ex_double_superscript, //"Double superscript"}, + ex_double_subscript, //"Double subscript"}, + ex_use_subscript_before_superscript, //"Use subscript first as the element is <msubsup>"}, + ex_internal_error, //"Internal error"}, + ex_missing_end_tag, //"Missing end tag"}, + ex_undefined_environment_type, //"Undefined environment type"}, + ex_unknown_alignment_character, //"Unknown alignment character"}, + ex_missing_begin, //"Missing \\begin"}, + ex_missing_end, //"Missing \\end"}, + ex_mismatched_environment_type, //"Mismatched environment type"} + ex_too_many_columns, + ex_unknown_attribute, + ex_misplaced_limits, + ex_misplaced_nolimits, + ex_no_command_allowed, + ex_missing_fence_parameter, + ex_not_math_mode, + ex_missing_right_sq_bracket, + ex_missing_dollar_symbol, + ex_missing_left_fence, + ex_missing_right_fence, + ex_ambiguous_script, + ex_misplaced_eqno, + ex_duplicate_eqno, + ex_missing_column_alignment, + ex_missing_subsup_base, + ex_unknown_character, + ex_unhandled_mathtype +}; + +#endif
\ No newline at end of file diff --git a/support/ltx2mathml/src/ltx2mathmlparser.cpp b/support/ltx2mathml/src/ltx2mathmlparser.cpp new file mode 100644 index 0000000000..a9a2ded9ee --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmlparser.cpp @@ -0,0 +1,2367 @@ + +#include "ltx2mathmlclasses.h" +#include "ltx2mathmltables.h" +#include "ltx2mathmlexceptions.h" +#include <ctype.h> +#include <string> + +using namespace std; +// GLOBALS + + +enum sub_expression { se_none, se_use_default, se_braced, se_optional_param, se_inline_math, se_fence, + se_matrix };//, se_eqalign, se_array, se_eqnarray }; + +enum element_type { et_unknown, et_tag_off, et_tag_on, et_tag_open, et_tag_on_open }; + + +enum skip_input { sp_skip_all, sp_skip_once, sp_no_skip }; + +enum { MAX_CONTROL_NAME = 32, EXTRA_BUF = 8 }; + + +#define char_null '\0' +#define char_backslash '\\' +#define char_prime '\'' + + +struct ArrayStruct { + short maxColumn, columnCount; + //sub_expression subType; + command_id id; +}; + +struct ErrorMessage { + const char *msg; + int index; + int code; + string msg2; +}; + +struct InputStream { + token_type token; + char buffer[MAX_CONTROL_NAME+EXTRA_BUF+1]; + char *start; + char nextChar; +}; + + +static SymbolTable limits[] = { + { "<munder>", "</munder>" }, + { "<mover>", "</mover>" }, + { "<munderover>", "</munderover>" } +}; + +static SymbolTable nolimits[] = { + { "<msub>", "</msub>" }, + { "<msup>", "</msup>" }, + { "<msubsup>", "</msubsup>" } +}; + +static SymbolTable limitsMovable[] = { + { "<msub movablelimits='true'>", "</msub>" }, + { "<msup movablelimits='true'>", "</msup>" }, + { "<msubsup movablelimits='true'>", "</msubsup>" } +}; + +static char *pStart = NULL; +static char *pCur = NULL; +static char *pEnd = NULL; +static bool isNumberedFormula = false; +static Buffer globalBuf, eqNumber; +static ErrorMessage errMsg; + + +void onDigit( Buffer &prevBuf ); +void onAlpha( Buffer &prevBuf ); +void onSymbol( Buffer &prevBuf, InputStream &input, bool checkSubSup = true ); +void onSubscript( Buffer &prevBuf ); +void onSuperscript( Buffer &prevBuf ); +void onControlName( Buffer &prevBuf, InputStream &input, bool &quit ); +void onEntity( Buffer &prevBuf, EntityStruct *entity, bool checkLimits = true, bool checkSubSup = true ); +void onFunction( Buffer &prevBuf, FunctionStruct *function, bool checkLimits = true ); +bool onCommand( Buffer &prevBuf, ControlStruct &control, sub_expression subType, void *paramExtra ); +void getCommandParam( Buffer &prevBuf, sub_expression subType ); +bool followedBy( char **p, const char *pattern, skip_input skip ); +bool parseExpression(const char *input, int len, int *errorIndex ); +void runLoop( Buffer &prevBuf, sub_expression subType, void *paramExtra = NULL ); +EnvironmentStruct *getEnvironmentType(); +void onBeginEnvironment( Buffer &prevBuf ); +bool onEndEnvironment( sub_expression subType, void *paramExtra ); +void precondition( char **p ); +void skipSpaces( char **p ); +void skipChar( char **p ); +bool getInput( InputStream &input, skip_input white_space ); +bool scriptNext( char *p ); +int getLastTagIndex(const char *p, size_t length, element_type element ); +token_type getControlTypeEx( InputStream &input, ControlStruct &control ); +void onColumn( Buffer &prevBuf, const char *pos, ArrayStruct &ar ); +void onRow( Buffer &prevBuf, ArrayStruct &ar ); +bool needsMrow(const char *p ); +void onMathFont( Buffer &prevBuf, const char *tagOn, const char *tagOff ); +void onTextFont( Buffer &prevBuf, const char *tagOn, const char *tagOff, command_id id, bool allowInline = true ); +bool onFence( Buffer &prevBuf, command_id id, sub_expression subType, const char *tagOn, const char *tagOff ); +void onEndExpression( sub_expression subType, token_type token, CommandStruct *command ); +void getPrime( char **p, char *buf ); +void onPrime( Buffer &prevBuf ); + +bool convertFormula(const char *input, int len, int *errorIndex ) +{ + + if( len < 0 ) + { + len = (int) strlen( input ); + } + + if( len == 0 ) + { + return false; + } + + return parseExpression( input, len, errorIndex ); +} + +bool parseExpression( const char *input, int len, int *errorIndex ) +{ + + bool result; + + pStart = (char *)input; + pCur = pStart; + pEnd = pStart + len; + + ZeroMemory( &errMsg, sizeof( errMsg ) ); + + globalBuf.destroy(); + eqNumber.destroy(); + isNumberedFormula = false; + + result = true; + try + { + precondition( &pCur ); + runLoop( globalBuf, se_use_default ); + if( needsMrow( globalBuf.data() ) ) + { + globalBuf.insertAt( 0, "<mrow>" ); + globalBuf.write( "</mrow>" ); + } + if( isNumberedFormula ) + { + globalBuf.insertAt( 0, eqNumber.data() ); + globalBuf.write( "</mtd></mlabeledtr></mtable>" ); + } + } + catch( const ErrorMessage &err ) + { + result = false; + *errorIndex = err.index; + } + + return result; +} + + +const char *getMathMLOutput() +{ + if( globalBuf.length() != 0 ) + { + return globalBuf.data(); + } + + return NULL; +} + +bool getMathMLOutput(string& buf, bool display) +{ + if (globalBuf.length() != 0) + { + const char *data = globalBuf.data(); + + buf = display ? "<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\">" : "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">"; + + buf.append(data); + + buf.append("</math>"); + + return true; + } + return false; +} + +static void getControlName(const char* start, string& name) +{ + char* p = (char*)(start+1); + + name.push_back('\\'); + + if (!isalpha(*p)) + { + name.push_back(*p); + return; + } + while (*p && isalnum(*p)) + { + name.push_back(*p); + ++p; + } +} + +ErrorMessage &error(const char *index, ex_exception code ) +{ + errMsg.code = (int) code; + errMsg.index = (int) (index - pStart); + + if (ex_undefined_control_sequence == code) + { + string name; + + errMsg.msg2 = getErrorMsg(code); + errMsg.msg2.append(": "); + + getControlName(index, name); + + errMsg.msg2.append(name); + + errMsg.msg = errMsg.msg2.c_str(); + } + else + { + errMsg.msg = getErrorMsg(code); + } + return errMsg; +} + +ErrorMessage& error(const char* index, ex_exception code, const string &msg) +{ + errMsg.code = (int)code; + errMsg.index = (int)(index - pStart); + errMsg.msg = getErrorMsg(code); + + return errMsg; +} + + +const char *getLastError() +{ + return errMsg.msg; +} + +/* + + PRECONDITION traps as many errors as possible + +*/ + + +static void precondition( char **p ) +{ + int braces; + char *s, *lastLeftBrace; + + skipSpaces( p ); + + // these chars can't start an equation + + //lastPos = *p; + + s = *p; + + switch( *s ) + { + case '}': + throw error( s, ex_missing_lbrace ); + case '^': + throw error( s, ex_prefix_superscript ); + case '_': + throw error( s, ex_prefix_subscript ); + case '&': + throw error( s, ex_misplaced_column_separator ); + } + + braces = 0; + lastLeftBrace = NULL; + + while( *s ) + { + if( *s == '{' ) + { + lastLeftBrace = s; + ++braces; + skipChar( &s ); + if( *s == '}' ) + { + skipChar( &s ); + --braces; + } + switch( *s ) + { + case '^': + throw error( s, ex_prefix_superscript ); + case '_': + throw error( s, ex_prefix_subscript ); + } + } + else if( *s == '}' ) + { + --braces; + if( braces < 0 ) + { + throw error( s, ex_more_rbrace_than_lbrace ); + } + ++s; + } + else if( *s == char_backslash ) + { + ++s; + if( isdigit( *s ) ) + { + throw error( s-1, ex_undefined_control_sequence ); + } + else if( isalpha( *s ) ) + { + char *start; + + start = s; + + // compute the length of the control name + do + { + ++s; + } + while( isalpha( *s ) ); + + if( ( s - start ) > MAX_CONTROL_NAME ) + { + throw error( start - 1, ex_control_name_too_long ); + } + } + else + { + // only the following chars can be escaped + + switch( *s ) + { + case '}': + case '{': + case '&': + case '^': + case '_': + case '-': + case '$': + case '#': + case '!': + case ';': + case '>': + case ':': // check + case ',': + case '|': + case ' ': + ++s; + break; + case char_backslash: + skipChar( &s ); + if( scriptNext( s ) ) + { + if( *s == '_' ) + { + throw error( s, ex_prefix_subscript ); + } + else + { + throw error( s, ex_prefix_superscript ); + } + } + break; + default: + throw error( s-1, ex_undefined_control_sequence ); + } + } + } + else if( *s == '&' ) + { + skipChar( &s ); + if( scriptNext( s ) ) + { + if( *s == '_' ) + { + throw error( s, ex_prefix_subscript ); + } + else + { + throw error( s, ex_prefix_superscript ); + } + } + } + else if( *s == '$' ) + { + if( braces == 0 ) + { + throw error( s, ex_misplaced_inline_formula ); + } + + skipChar( &s ); + if( scriptNext( s ) ) + { + if( *s == '_' ) + { + throw error( s, ex_prefix_subscript ); + } + else + { + throw error( s, ex_prefix_superscript ); + } + } + } + else if( scriptNext( s ) ) + { + char *pos; + + pos = s; + skipChar( &s ); + switch( *s ) + { + case char_null: + case '}': + case '$': + case '&': + throw error( pos, ex_missing_parameter ); + case char_backslash: + if( s[1] == char_backslash ) // row separator + { + throw error( pos, ex_missing_parameter ); + } + } + } + else + { + ++s; + } + + } + + if( braces != 0 ) + { + throw error( lastLeftBrace, ex_more_lbrace_than_rbrace ); + } + // check backwards + + do { + --s; + } + while( (s > *p ) && isspace( *s ) ); + + pEnd = s+1; +} + +static void skipSpaces( char **p ) +{ + char *s; + + if( p == NULL ) + { + return; + } + + s = *p; + + while( (*s) && isspace( *s ) ) + { + ++s; + } + + *p = s; +} + +static void skipChar( char **p ) +{ + char *s; + + if( p == NULL ) + { + return; + } + + s = *p; + + ++s; // skip one char, then spaces + + while( (*s) && isspace( *s ) ) + { + ++s; + } + + *p = s; +} + + +static bool followedBy( char **p, const char *pattern, skip_input skip ) +{ + char *s; + + if( p == NULL ) + { + return false; + } + + s = *p; + + do { + if( *s == *pattern ) + { + ++s; + ++pattern; + } + else + { + return false; + } + } while( *s && *pattern ); + + if( ( *pattern == char_null ) && !isalpha( *s ) ) + { + if( skip != sp_no_skip ) + { + if( isspace( *s ) ) + { + skipSpaces( &s ); + } + *p = s; + } + return true; + } + + return false; +} + + +static bool scriptNext( char *p ) +{ + return ( *p == '_' ) || ( *p == '^' ); +} + +static int getLastTagIndex( char *p, size_t length, element_type element ) +{ + int inside; // inside a tag + element_type current; + size_t i; + + if( ( p == NULL ) || (length == 0) ) + { + return -1; + } + + i = length-1; + inside = 0; + current = et_unknown; + + while( i >= 0 ) + { + if( p[i] == '>' ) + { + if( p[i-1] == '/' ) + { + current = et_tag_open; + --i; // to skip twice + } + } + else if( p[i] == '<' ) + { + if( p[i+1] == '/' ) + { + current = et_tag_off; + if( element == current ) + { + return (int) i; + } + inside++; + } + else + { + --inside; + if( inside == 0 ) + { + return (int) i; + } + } + } + if( i == 0 ) + { + break; + } + --i; + } + + return -1; +} + +static bool getInput( InputStream &input, skip_input white_space ) +{ + + ZeroMemory( &input, sizeof( input ) ); + + if( white_space == sp_skip_all ) + { + if( isspace( *pCur ) ) + { + skipSpaces( &pCur ); + } + } + + if( *pCur == char_null ) + { + input.token = token_eof; + return false; + } + + + while( *pCur ) + { + input.start = pCur; + if( isalpha( *pCur ) ) + { + input.token = token_alpha; + break; + } + else if( isdigit( *pCur ) ) + { + input.token = token_digit; + break; + } + else if( *pCur == '&' ) + { + input.token = token_column_sep; + skipChar( &pCur ); + break; + } + else if( *pCur == '{' ) + { + input.token = token_left_brace; + skipChar( &pCur ); + break; + } + else if( *pCur == '}' ) + { + input.token = token_right_brace; + skipChar( &pCur ); + break; + } + else if( *pCur == '^' ) + { + input.token = token_superscript; + skipChar( &pCur ); + break; + } + else if( *pCur == '_' ) + { + input.token = token_subscript; + skipChar( &pCur ); + break; + } + else if( isspace( *pCur ) ) + { + if( *pCur == ' ' ) + { + if( white_space == sp_skip_all ) + { + skipSpaces( &pCur ); + } + else if ( white_space == sp_skip_once ) + { + + input.token = token_white_space; + ++pCur; + break; + } + else + { + //input.token = token_white_space; + skipSpaces( &pCur ); + } + } + else // other spaces \n\r + { + skipSpaces( &pCur ); + } + } + else if( *pCur == char_backslash ) + { + ++pCur; + if( isalpha( *pCur ) ) + { + input.token = token_control_name; + for( int i = 0; i < MAX_CONTROL_NAME; ++i ) + { + input.buffer[i] = *pCur; + ++pCur; + if( !isalpha( *pCur ) ) + { + break; + } + } + // use this to determine whether control name + // is followed IMMEDIATELY by digits + // cf. \abc123 vs.\abc 123 + input.nextChar = *pCur; + if( isspace( *pCur ) ) + { + skipSpaces( &pCur ); + } + } + else + { + if( *pCur == char_backslash ) + { + input.token = token_row_sep; + } + else + { + input.token = token_control_symbol; + } + input.buffer[0] = char_backslash; + input.buffer[1] = *pCur; + skipChar( &pCur ); + input.nextChar = *pCur; + } + break; + } + else if( *pCur == ']' ) + { + input.token = token_right_sq_bracket; + input.buffer[0] = *pCur; + skipChar( &pCur ); + input.nextChar = *pCur; + break; + } + else if( *pCur == '$' ) + { + input.token = token_inline_math; + input.buffer[0] = *pCur; + //skipChar( &pCur ); don't skip + input.nextChar = *pCur; + break; + } + else if( *pCur == char_prime ) + { + input.token = token_prime; + input.buffer[0] = *pCur; + input.nextChar = pCur[1]; + // don't skip + break; + } + else // symbols + { + input.token = token_symbol; + input.buffer[0] = *pCur; + skipChar( &pCur ); + input.nextChar = *pCur; + break; + } + } + + return true; +} + +static void onPrime( Buffer &prevBuf ) +{ + SymbolStruct *sym; + char buf[5]; + + getPrime( &pCur, buf ); + + sym = getSymbol( buf ); + + prevBuf.write( sym->element ); +} + +static void getPrime( char **p, char *buf ) +{ + int i = 0; + do { + *buf = **p; + ++buf; + ++(*p); + ++i; + if( i == 3 ) + { + break; + } + } while( **p == char_prime ); + + *buf = char_null; + + if( isspace( *pCur ) ) + { + skipSpaces( &pCur ); + } +} + +static token_type getControlTypeEx( InputStream &input, ControlStruct &control ) +{ + Buffer buf; + + control.start = input.start; + input.token = getControlType( input.buffer, control ); + + if( input.token != token_unknown ) + { + return input.token; + } + + if( isdigit( input.nextChar ) ) + { + buf.write( input.buffer ); + buf.write( pCur, 1 ); // write the next digit + ++pCur; + } + else + { + return input.token; + } + + while( ( input.token = getControlType( buf.data(), control ) ) == token_unknown ) + { + if( isdigit( *pCur ) ) + { + buf.write( pCur, 1 ); + ++pCur; + } + else + { + break; + } + } + + return input.token; +} + +static void runLoop( Buffer &prevBuf, sub_expression subType, void *paramExtra ) +{ + InputStream input; + ControlStruct control; + Buffer str; + bool quitLoop; + + ZeroMemory( &control, sizeof( control ) ); + + quitLoop = false; + + while( getInput( input, sp_skip_all ) ) + { + switch( input.token ) + { + case token_alpha: + onAlpha( str ); + break; + + case token_digit: + onDigit( str ); + break; + + case token_prime: + onPrime( str ); + break; + case token_symbol: + case token_control_symbol: + onSymbol( str, input ); + break; + /* + case token_white_space: + onWhiteSpace( str ); + break; + */ + case token_inline_math: + + if( subType != se_inline_math ) + { + throw error( pCur, ex_misplaced_inline_formula ); + } + quitLoop = true; + break; + case token_left_brace: + runLoop( str, se_braced ); + break; + case token_right_brace: + quitLoop = true; + break; + case token_right_sq_bracket: + if( subType == se_optional_param ) + { + quitLoop = true; + } + else + { + onSymbol( str, input ); + } + break; + case token_superscript: + onSuperscript( str ); + break; + + case token_subscript: + onSubscript( str ); + break; + + case token_column_sep: + if( subType < se_matrix ) + { + throw error( input.start, ex_misplaced_column_separator ); + } + else + { + onColumn( str, input.start, *((ArrayStruct *)paramExtra) ); + } + break; + case token_row_sep: + if( subType < se_matrix ) + { + throw error( input.start, ex_misplaced_row_separator ); + } + else + { + onRow( str, *((ArrayStruct *)paramExtra) ); + } + break; + case token_control_name: + //onControlName( str, input, quit ); + + input.token = getControlTypeEx( input, control ); + + switch( input.token ) + { + case token_control_entity: + onEntity( str, control.entity ); + break; + case token_control_command: + quitLoop = onCommand( str, control, subType, paramExtra ); + break; + case token_control_function: + onFunction( str, control.function ); + break; + //case token_unknown: + default: + throw error( input.start, ex_undefined_control_sequence ); + } + + break; + } + + if( quitLoop ) + { + break; + } + } + + onEndExpression( subType, input.token, control.command ); + + prevBuf.append( str, true ); +} + +//se_optional_param, se_inline_math, se_fence, +// se_matrix +static void onEndExpression( sub_expression subType, token_type token, CommandStruct *command ) +{ + switch( subType ) + { + case se_optional_param: + if( token != token_right_sq_bracket ) + { + throw error( pCur, ex_missing_right_sq_bracket ); + } + break; + case se_inline_math: + if( token != token_inline_math ) + { + throw error( pCur, ex_missing_dollar_symbol ); + } + break; + case se_matrix: + if( token != token_control_command ) + { + throw error( pCur, ex_missing_end ); + } + else if( command->id != ci_end ) + { + throw error( pCur, ex_missing_end ); + } + break; + case se_fence: + if( token != token_control_command ) + { + throw error( pCur, ex_missing_right_fence ); + } + else if( command->id != ci_right ) + { + throw error( pCur, ex_missing_right_fence ); + } + } + +} + + +static void onAlpha( Buffer &prevBuf ) +{ + char tag[] = "<mi>?</mi>"; + char *p; + Buffer str; + + + str.setlength( 50 ); + + p = strchr( tag, '?' ); + + do { + *p = *pCur; + str.write( tag, sizeof( tag ) - 1 ); + ++pCur; + } + while( isalpha( *pCur ) ); + + prevBuf.append( str, true ); + +} + +static void onDigit( Buffer &prevBuf ) +{ + char tagOn[] = "<mn>"; + char tagOff[] = "</mn>"; + + Buffer str; + char *start; + + + str.setlength( 50 ); + + + str.write(tagOn, sizeof( tagOn ) - 1 ); + + + start = pCur; + + do { + ++pCur; + } + while( isdigit( *pCur ) ); + + str.write( start, (pCur - start) ); + + str.write(tagOff, sizeof( tagOff ) - 1 ); + + prevBuf.append( str, true ); + +} + +static void onSymbol( Buffer &prevBuf, InputStream &input, bool checkSubSup ) +{ + SymbolStruct *symbol; + + symbol = getSymbol( input.buffer ); + + if( symbol == NULL ) + { + throw error( pCur, ex_unknown_character ); + } + + + switch( symbol->mathType ) + { + case mt_fence: + case mt_left_fence: + case mt_right_fence: + if( checkSubSup && scriptNext( pCur ) ) + { + throw error( pCur, ex_ambiguous_script ); + } + break; + } + + prevBuf.write( symbol->element ); +} + +static bool needsMrow( const char *p ) +{ + element_type et; + short inside, count; + + if( p == NULL ) + { + return false; + } + + inside = count = 0; + et = et_unknown; + + while( *p ) + { + if( *p == '<' ) + { + if( p[1] == '/' ) + { + et = et_tag_off; + ++p; + } + else + { + et = et_tag_on; + } + } + else if( *p == '>' ) + { + if( *(p-1) == '/' ) // tag open + { + if( inside == 0 ) + { + ++count; + } + } + else if( et == et_tag_off ) + { + --inside; + } + else + { + if( inside == 0 ) + { + ++count; + } + ++inside; + } + if( count > 1 ) + { + return true; + } + } + ++p; + } + + return false; +} + +static void getCommandParam( Buffer &prevBuf, sub_expression subType ) +{ + InputStream input; + Buffer str; + ControlStruct control; + + getInput( input, sp_skip_all ); + + switch( input.token ) + { + case token_alpha: + prevBuf.format( "<mi>%c</mi>", *pCur ); + skipChar( &pCur ); + break; + + case token_digit: + prevBuf.format( "<mn>%c</mn>", *pCur ); + skipChar( &pCur ); + break; + case token_prime: + prevBuf.write( "<mo>′</mo>" ); + skipChar( &pCur ); + break; + case token_symbol: + case token_control_symbol: + onSymbol( prevBuf, input, false ); // ignore subscript/superscript + break; + + case token_left_brace: + if( subType == se_use_default ) + { + runLoop( str, se_braced ); + } + else + { + runLoop( str, subType ); + } + if( needsMrow( str.data() ) ) + { + str.insertAt( 0, "<mrow>" ); + str.write( "</mrow>" ); + } + prevBuf.append( str, true ); + break; + + case token_right_brace: + case token_superscript: + case token_subscript: + case token_column_sep: + case token_row_sep: + case token_eof: + throw error( pCur, ex_missing_parameter ); + + case token_control_name: + //onControlName( str, input, quit ); + + input.token = getControlTypeEx( input, control ); + + switch( input.token ) + { + case token_control_entity: + // don't check limits and subscript + onEntity( prevBuf, control.entity, false, false ); + break; + case token_control_command: + if( control.command->id == ci_frac ) + { + onCommand( str, control, se_use_default, NULL ); + prevBuf.append( str, true ); + } + else + { + throw error( input.start, ex_no_command_allowed ); + } + break; + case token_control_function: + onFunction( prevBuf, control.function, false ); + break; + //case token_unknown: + default: + throw error( input.start, ex_undefined_control_sequence ); + } + break; + default: + break; + } +} + +static void getSuperscript( Buffer &prevBuf, bool subsup ) +{ + if( *pCur == char_prime ) + { + throw error( pCur, ex_missing_lbrace ); + } + + getCommandParam( prevBuf, se_use_default ); + + if( ( *pCur == '^' ) || ( *pCur == char_prime ) ) + { + throw error( pCur, ex_double_superscript ); + } + else if( *pCur == '_' ) + { + if( subsup ) + { + throw error( pCur, ex_double_subscript ); + } + else + { + throw error( pCur, ex_use_subscript_before_superscript ); + } + } +} + +static void onSuperscript( Buffer &prevBuf ) +{ + Buffer str; + int index; + SymbolTable *sup; + + sup = &nolimits[1]; + + + index = getLastTagIndex( prevBuf.data(), prevBuf.length(), et_tag_on_open ); + + if( index < 0 ) + { + throw error( pCur, ex_missing_subsup_base ); + } + + str.setlength( 50 ); + + prevBuf.insertAt( index, sup->tagOn ); + + getSuperscript( str, false ); + + str.write( sup->tagOff ); + + prevBuf.append( str, true ); +} + +static void getSubscript( Buffer &prevBuf, command_id &which ) +{ + + if( *pCur == char_prime ) + { + throw error( pCur, ex_missing_lbrace ); + } + + getCommandParam( prevBuf, se_use_default ); + + if( *pCur == '^' ) + { + skipChar( &pCur ); + getSuperscript( prevBuf, true ); + + which = ci_msubsup; + } + else if( *pCur == char_prime ) + { + onPrime( prevBuf ); + which = ci_msubsup; + } + else + { + which = ci_msub; + } +} + +static void onSubscript( Buffer &prevBuf ) +{ + Buffer str; + int index; + command_id which; + SymbolTable *sub; + + index = getLastTagIndex( prevBuf.data(), prevBuf.length(), et_tag_on_open ); + + if( index < 0 ) + { + throw error( pCur, ex_missing_subsup_base ); + } + + str.setlength( 50 ); + + getSubscript( str, which ); + + if( which == ci_msub ) + { + sub = &nolimits[0]; + } + else + { + sub = &nolimits[2]; + } + + prevBuf.insertAt( index, sub->tagOn ); + str.write( sub->tagOff ); + + prevBuf.append( str, true ); +} + + + +enum limits_type { lt_default, lt_subsup, lt_underover }; + +static void onLimits( Buffer &prevBuf, math_type mathType ) +{ + limits_type useLimits; + + useLimits = lt_default; + + do { + if( followedBy( &pCur, "\\limits", sp_skip_all ) ) + { + useLimits = lt_underover; + } + else if( followedBy( &pCur, "\\nolimits", sp_skip_all ) ) + { + useLimits = lt_subsup; + } + else + { + break; + } + } while( 1 ); + + if( *pCur == char_null ) + { + return; + } + else if( scriptNext( pCur ) || *pCur == char_prime ) + { + Buffer str; + command_id which; + //char *nextChar; + int index; + SymbolTable *lim; + + index = getLastTagIndex( prevBuf.data(), prevBuf.length(), et_tag_on_open ); + + if( index < 0 ) + { + index = 0; + } + + + if( *pCur == '_' ) + { + skipChar( &pCur ); + getSubscript( str, which ); + } + else if( *pCur == '^' ) + { + skipChar( &pCur ); + getSuperscript( str, false ); + which = ci_msup; + } + else // prime///// + { + onPrime( str ); + which = ci_msup; + + if( *pCur == '^' || *pCur == char_prime ) + { + throw error( pCur, ex_double_superscript ); + } + else if( *pCur == '_' ) + { + throw error( pCur, ex_use_subscript_before_superscript ); + } + } + + if( useLimits == lt_underover ) + { + lim = limits; + } + else if( useLimits == lt_subsup ) + { + lim = nolimits; + } + else + { + if( mathType == mt_limits ) + { + lim = nolimits; + } + else + { + lim = limitsMovable; + } + } + + switch( which ) + { + case ci_msub: + prevBuf.insertAt( index, lim[0].tagOn ); + str.write( lim[0].tagOff ); + break; + case ci_msup: + prevBuf.insertAt( index, lim[1].tagOn ); + str.write( lim[1].tagOff ); + break; + case ci_msubsup: + prevBuf.insertAt( index, lim[2].tagOn ); + str.write( lim[2].tagOff ); + break; + } + prevBuf.append( str, true ); + } +} + +static void onEntity( Buffer &prevBuf, EntityStruct *entity, bool checkLimits, bool checkSubSup ) +{ + + switch( entity->mathType ) + { + case mt_ident: + prevBuf.format( "<mi>&#x%x;</mi>", entity->code ); + break; + case mt_digit: + prevBuf.format( "<mn>&#x%x;</mn>", entity->code ); + break; + case mt_ord: + case mt_punct: + prevBuf.format( "<mo>&#x%x;</mo>", entity->code ); + break; + case mt_limits: + case mt_mov_limits: + + prevBuf.format( "<mo>&#x%x;</mo>", entity->code ); + + if( checkLimits ) + { + onLimits( prevBuf, entity->mathType ); + } + break; + case mt_left_fence: + case mt_right_fence: + case mt_fence: + if( checkSubSup && scriptNext( pCur ) ) + { + throw error( pCur, ex_ambiguous_script ); + } + prevBuf.format( "<mo mathsize='1'>&#x%x;</mo>", entity->code ); + break; + case mt_text: + prevBuf.format( "<mtext>&#x%x;</mtext>", entity->code ); + break; + case mt_rel: + case mt_bin: + case mt_unary: + case mt_bin_unary: + prevBuf.format( "<mo>&#x%x;</mo>", entity->code ); + break; + default: + throw error( pCur, ex_unhandled_mathtype ); + } +} + +static void onFunction( Buffer &prevBuf, FunctionStruct *function, bool checkLimits ) +{ + prevBuf.format( "<mi>%s</mi>", function->output ); + + if( ( function->mathType == mt_func_limits ) && checkLimits ) + { + onLimits( prevBuf, function->mathType ); + } +} +/* +enum param_type { pt_unknown, pt_none, pt_one, pt_two, pt_three, pt_table, pt_others, + pt_especial }; +*/ + +static void onSqrt( Buffer &prevBuf, const char *tagOn, const char *tagOff ) +{ + + if( *pCur == char_prime ) + { + throw error( pCur, ex_missing_lbrace ); + } + else if( *pCur == '[' ) + { + Buffer str, radix; + + skipChar( &pCur ); + runLoop( radix, se_optional_param ); + if( radix.length() != 0 ) + { + str.write( "<mroot>" ); + getCommandParam( str, se_use_default ); + if( needsMrow( radix.data() ) ) + { + radix.insertAt( 0, "<mrow>" ); + radix.write( "</mrow>" ); + } + str.append( radix, true ); + str.write( "</mroot>" ); + } + else + { + prevBuf.write( tagOn ); + getCommandParam( str, se_use_default ); + str.write( tagOff ); + } + + prevBuf.append( str, true ); + } + else + { + prevBuf.write( tagOn ); + getCommandParam( prevBuf, se_use_default ); + prevBuf.write( tagOff ); + } +} + +static void getAttribute( Buffer &prevBuf, char lastChar ) +{ + char *start, *end; + + start = pCur; + + while( *pCur && ( *pCur != lastChar ) ) + { + ++pCur; + } + + if( *pCur != lastChar ) + { + throw error( pCur, ex_missing_end_tag ); + } + + end = pCur - 1; + + + while( isspace( *end ) ) + { + --end; + } + + if( *end == char_backslash ) + { + end += 2; + } + else + { + end++; + } + + prevBuf.write( start, (end - start ) ); + + skipChar( &pCur ); + +} + +static void onMiMnMo( Buffer &prevBuf, const char *tagOn, const char *tagOff ) +{ + Buffer str; + const char* attrib; + char *start; + + if( *pCur == '[' ) + { + start = pCur+1; + skipChar( &pCur ); + getAttribute( str, ']' ); + + attrib = getMathVariant( str.data() ); + + if( attrib == NULL ) + { + throw error( start, ex_unknown_attribute ); + } + + str.destroy(); + str.write( tagOn, strlen(tagOn) - 1 ); // don't include '>' + str.format( " mathvariant='%s'>", attrib ); + } + else + { + str.write( tagOn ); + } + onMathFont( prevBuf, str.data(), tagOff ); + //prevBuf.write( tagOff ); +} + + +static EnvironmentStruct *getEnvironmentType() +{ + Buffer str; + char *curPos; + EnvironmentStruct *environment; + + if( *pCur != '{' ) + { + throw error( pCur, ex_missing_lbrace ); + } + + skipChar( &pCur); + + curPos = pCur; + + getAttribute( str, '}' ); + + environment = getEnvironmentType( str.data() ); + + if( environment != NULL ) + { + return environment; + } + else + { + throw error( curPos, ex_undefined_environment_type ); + } +} + +static void getColumnAlignment( Buffer &align, short &maxColumn, const char *tagOn ) +{ + char *curPos, *p, *attrib; + Buffer str; + + if( *pCur != '{' ) + { + throw error( pCur, ex_missing_lbrace ); + } + + skipChar( &pCur ); + + curPos = pCur; + + getAttribute( str, '}' ); + + if( str.length() == 0 ) + { + throw error( pCur, ex_missing_column_alignment ); + } + + p = str.data(); + + maxColumn = 0; + + attrib = (char *)strchr( tagOn, '>' ); + + align.write( tagOn, size_t( attrib - tagOn ) ); + + align.write( " columnalign='" ); + + while( *p ) + { + switch( *p ) + { + case 'l': + align.write( "left" ); + break; + case 'c': + align.write( "center" ); + break; + case 'r': + align.write( "right" ); + break; + default: + if( isspace( *p ) ) + { + ++p; + continue; + } + throw error( curPos, ex_unknown_alignment_character ); + } + + ++maxColumn; + ++p; + if( *p ) + { + align.write( " " ); + } + } + align.format( "'%s", attrib ); +} + +static void onBeginEnvironment( Buffer &prevBuf ) +{ + ArrayStruct ar; + Buffer str, align; + EnvironmentStruct *environment; + + environment = getEnvironmentType(); + + ar.id = environment->id; + ar.columnCount = 1; + ar.maxColumn = 5000; // arbitrary + + + + switch( environment->id ) + { + case ci_array: + getColumnAlignment( align, ar.maxColumn, environment->tagOn ); + prevBuf.append( align, true ); + runLoop( str, se_matrix, &ar ); + break; + case ci_eqnarray: + ar.maxColumn = 3; + // fall through + default: + prevBuf.write( environment->tagOn ); + runLoop( str, se_matrix, &ar ); + break; + } + str.write( environment->tagOff ); + prevBuf.append( str, true ); +} + +static bool onEndEnvironment( sub_expression subType, void *paramExtra ) +{ + command_id id; + char *temp; + EnvironmentStruct *environment; + + temp = pCur; + if( subType == se_matrix ) + { + id = ((ArrayStruct *)paramExtra)->id; + } + else + { + throw error( temp, ex_missing_begin ); + } + + environment = getEnvironmentType(); + + if( environment->id != id ) + { + throw error( temp, ex_mismatched_environment_type ); + } + return true; +} + +static void onColumn( Buffer &prevBuf, const char *pos, ArrayStruct &ar ) +{ + ++ar.columnCount; + + if( ar.columnCount > ar.maxColumn ) + { + throw error( pos, ex_too_many_columns ); + } + + prevBuf.write( "</mtd><mtd>" ); +} + +static void onRow( Buffer &prevBuf, ArrayStruct &ar ) +{ + if( (*pCur == char_backslash ) && (pCur[1] == 'e' ) ) // \end? + { + if( followedBy( &pCur, "\\end", sp_no_skip ) ) + { + return; // do nothing + } + } + + prevBuf.write( "</mtd></mtr><mtr><mtd>" ); + ar.columnCount = 1; // reset columns +} + + +static void onHfill( Buffer &prevBuf, sub_expression subType, void *paramExtra ) +{ + if( scriptNext( pCur ) ) + { + if( *pCur == '^' ) + { + throw error( pCur, ex_prefix_superscript ); + } + else + { + throw error( pCur, ex_prefix_subscript ); + } + } + if( subType < se_matrix ) // not in a table + { + return; + } +} + +static void onArrows( Buffer &prevBuf, const char *tagOn, const char *tagOff ) +{ + Buffer str; + + //tagOn is the base + if( *pCur == '[' ) + { + Buffer underscript; + + skipChar( &pCur ); + runLoop( underscript, se_optional_param ); + if( underscript.length() != 0 ) + { + if( needsMrow( underscript.data() ) ) + { + underscript.insertAt( 0, "<mrow>" ); + underscript.write( "</mrow>" ); + } + str.format( "<munderover>%s", tagOn ); + + str.append( underscript, true ); + + getCommandParam( str, se_use_default ); + + str.write( "</munderover>" ); + prevBuf.append( str, true ); + return; + } + } + + // fall through + //prevBuf.write( tagOn ); + str.format( "<mover>%s", tagOn ); + getCommandParam( str, se_use_default ); + str.write( tagOff ); + prevBuf.append( str, true ); +} + +static void onCfrac( Buffer &prevBuf, const char *tagOn, const char *tagOff ) +{ + Buffer str; + const char *extra = "<mstyle displaystyle='true' scriptlevel='0'>"; + + str.format( "%s%s", tagOn, extra ); + getCommandParam( str, se_use_default ); + str.format( "</mstyle>%s", extra ); + getCommandParam( str, se_use_default ); + str.write( tagOff ); + prevBuf.append( str, true ); +} + + +static bool onCommand( Buffer &prevBuf, ControlStruct &control, sub_expression subType, void *paramExtra ) +{ + Buffer str, str2; + CommandStruct *command; + + command = control.command; + + switch( command->param ) + { + case pt_plain: + switch( command->id ) + { + case ci_mn: + case ci_mo: + onMiMnMo( prevBuf, command->tagOn, command->tagOff ); + break; + case ci_mathop: + onMathFont( str, command->tagOn, command->tagOff ); + //str.write( command->tagOff ); + onLimits(str, mt_limits ); + prevBuf.append( str, true ); + break; + /* + case ci_mathrm: + case ci_mathit: + case ci_mathbf: + case ci_mathbi: + + case ci_mathord, + case ci_func: + */ + case ci_mathfont: + case ci_mathord: + case ci_mathbin: + case ci_mathrel: + //str.write( command->tagOn ); + onMathFont( str, command->tagOn, command->tagOff ); + //str.write( command->tagOff ); + prevBuf.append( str, true ); + } + break; + case pt_especial: + switch( command->id ) + { + //case ci_mi: + + case ci_sqrt: + onSqrt( prevBuf, command->tagOn, command->tagOff ); + break; + case ci_begin: + onBeginEnvironment( prevBuf ); + break; + case ci_end: + return onEndEnvironment( subType, paramExtra ); + case ci_stackrel: + str.write( command->tagOn ); + getCommandParam( str2, se_use_default ); + getCommandParam( str, se_use_default ); + str.append( str2, true ); + str.write( command->tagOff ); + prevBuf.append( str, true ); + break; + case ci_hfill: + onHfill( prevBuf, subType, paramExtra ); + break; + case ci_strut: + prevBuf.write( command->tagOn ); + break; + case ci_limits: + case ci_nolimits: + throw error( control.start, ex_misplaced_limits ); + case ci_mathstring: + onTextFont( prevBuf, command->tagOn, command->tagOff, command->id, false ); + break; + case ci_text: + onTextFont( prevBuf, command->tagOn, command->tagOff, command->id ); + break; + case ci_eqno: + case ci_leqno: + if( subType != se_use_default ) + { + throw error( pCur, ex_misplaced_eqno ); + } + else if ( isNumberedFormula ) + { + throw error( pCur, ex_duplicate_eqno ); + } + isNumberedFormula = true; + onTextFont( eqNumber, command->tagOn, command->tagOff, ci_eqno, false ); + break; + case ci_left: + case ci_right: + return onFence( prevBuf, command->id, subType, command->tagOn, command->tagOff ); + case ci_ext_arrows: + onArrows( prevBuf, command->tagOn, command->tagOff ); + break; + case ci_cfrac: + onCfrac( prevBuf, command->tagOn, command->tagOff ); + break; + case ci_underoverbrace: + str.write( command->tagOn ); + getCommandParam( str, se_use_default ); + str.write( command->tagOff ); + onLimits( str, mt_mov_limits ); + prevBuf.append( str, true ); + break; + case ci_lsub: + str.write( command->tagOn ); + getCommandParam( str, se_use_default ); + str.write( "<mprescripts/>" ); + getCommandParam( str, se_use_default ); + str.write( "<none/>" ); + str.write( command->tagOff ); + prevBuf.append( str, true ); + break; + case ci_lsup: + str.write( command->tagOn ); + getCommandParam( str, se_use_default ); + str.write( "<mprescripts/><none/>" ); + getCommandParam( str, se_use_default ); + str.write( command->tagOff ); + prevBuf.append( str, true ); + break; + case ci_lsubsup: + str.write( command->tagOn ); + getCommandParam( str, se_use_default ); + str.write( "<mprescripts/>" ); + getCommandParam( str, se_use_default ); + getCommandParam( str, se_use_default ); + str.write( command->tagOff ); + prevBuf.append( str, true ); + break; + default: + break; + } + break; + + case pt_one: + prevBuf.write( command->tagOn ); + getCommandParam( prevBuf, se_use_default ); + prevBuf.write( command->tagOff ); + break; + + case pt_two: + prevBuf.write( command->tagOn ); + getCommandParam( prevBuf, se_use_default ); + getCommandParam( prevBuf, se_use_default ); + prevBuf.write( command->tagOff ); + break; + + case pt_three: + break; + + case pt_table: + break; + + case pt_none: + break; + + case pt_others: + default: + break; + } + + return false; +} + +static void onMathFont( Buffer &prevBuf, const char *tagOn, const char *tagOff ) +{ + InputStream input; + ControlStruct control; + SymbolStruct *symbol; + Buffer str; + int brace; + bool quitLoop; + + brace = 0; + + if( *pCur == '{' ) + { + quitLoop = false; // loop + } + else + { + quitLoop = true; // read only one char + } + + + while( getInput( input, sp_skip_all ) ) + { + switch( input.token ) + { + case token_alpha: + case token_digit: + str.write( pCur, 1 ); + ++pCur; + break; + case token_prime: + { + SymbolStruct *sym; + char buf[5]; + + getPrime( &pCur, buf ); + + sym = getSymbol( buf ); + str.write( sym->literal ); + } + break; + case token_symbol: + case token_control_symbol: + case token_right_sq_bracket: + symbol = getSymbol( input.buffer ); + str.write( symbol->literal ); + break; + + case token_white_space: + str.write( " " ); + if( isspace( *pCur ) ) + { + skipSpaces( &pCur ); + } + break; + case token_left_brace: + ++brace; + break; + case token_right_brace: + --brace; + if( brace < 0 ) + { + throw error( input.start, ex_missing_parameter ); + } + quitLoop = true; + break; + case token_inline_math: + throw error( input.start, ex_misplaced_inline_formula ); + + case token_superscript: + case token_subscript: + throw error( input.start, ex_no_command_allowed ); + case token_column_sep: + throw error( input.start, ex_misplaced_column_separator ); + case token_row_sep: + throw error( input.start, ex_misplaced_row_separator ); + + case token_control_name: + + switch( getControlTypeEx( input, control ) ) + { + case token_control_entity: + str.format( "&#x%x;", control.entity->code ); + break; + case token_control_function: + str.write( control.function->output ); + break; + case token_control_command: + throw error( input.start, ex_no_command_allowed ); + break; + //case token_unknown: + default: + throw error( input.start, ex_undefined_control_sequence ); + } + break; + default: + break; + } + if( quitLoop ) + { + break; + } + } + + prevBuf.write( tagOn ); + str.write( tagOff ); + prevBuf.append( str, true ); +} + + +static void onTextFont( Buffer &prevBuf, const char *tagOn, const char *tagOff, command_id id, bool allowInline ) +{ + InputStream input; + ControlStruct control; + SymbolStruct *symbol; + Buffer str, temp; + int brace; + bool quitLoop; + + brace = 0; + + if( ( id != ci_eqno ) && ( id != ci_leqno ) ) + { + if( *pCur != '{' ) + { + throw error( pCur, ex_missing_lbrace ); + } + } + + quitLoop = false; + + while( getInput( input, sp_skip_once ) ) + { + switch( input.token ) + { + case token_alpha: + case token_digit: + str.write( pCur, 1 ); + ++pCur; + break; + + case token_symbol: + case token_control_symbol: + case token_right_sq_bracket: + symbol = getSymbol( input.buffer ); + str.write( symbol->literal ); + break; + case token_prime: + if( pCur[1] == char_prime ) + { + str.write( "”" ); + pCur += 2; + } + else + { + str.write( "’" ); + ++pCur; + } + break; + case token_inline_math: + if( !allowInline ) + { + throw error( input.start, ex_misplaced_inline_formula ); + } + if( str.length() > 0 ) + { + temp.write( tagOn ); + str.write( tagOff ); + temp.append( str, true ); + str.reset(); + } + + skipChar( &pCur ); + runLoop( str, se_inline_math, NULL ); + // skip end $ + ++pCur; + + if( needsMrow( str.data() ) ) + { + str.insertAt( 0, "<mrow>" ); + str.write( "</mrow>" ); + } + temp.append( str, true ); + str.reset(); + break; + + case token_white_space: + str.write( " " ); + if( isspace( *pCur ) ) + { + skipSpaces( &pCur ); + } + break; + case token_left_brace: + ++brace; + break; + case token_right_brace: + --brace; + if( brace < 0 ) + { + throw error( input.start, ex_missing_parameter ); + } + quitLoop = true; + break; + case token_superscript: + case token_subscript: + throw error( input.start, ex_no_command_allowed ); + case token_column_sep: + throw error( input.start, ex_misplaced_column_separator ); + case token_row_sep: + throw error( input.start, ex_misplaced_row_separator ); + + case token_control_name: + + switch( getControlTypeEx( input, control ) ) + { + case token_control_entity: + str.format( "&#x%x;", control.entity->code ); + break; + case token_control_function: + throw error( input.start, ex_not_math_mode ); + case token_control_command: + throw error( input.start, ex_no_command_allowed ); + //case token_unknown: + default: + throw error( input.start, ex_undefined_control_sequence ); + } + break; + default: + break; + } + if( quitLoop ) + { + break; + } + } + + + if( str.length() ) + { + temp.write( tagOn ); + str.write( tagOff ); + } + temp.append( str, true ); + + if( needsMrow( temp.data() ) ) + { + temp.insertAt( 0, "<mrow>" ); + temp.write( "</mrow>" ); + } + prevBuf.append( temp, true ); +} + +static void getFence( Buffer &prevBuf, const char *tagOn, command_id id ) +{ + InputStream input; + FenceStruct fence; + size_t len; + + + len = strlen( tagOn ); + + getInput( input, sp_skip_all ); + + switch( input.token ) + { + case token_control_symbol: + case token_symbol: + case token_control_name: + if( !getFenceType( input.buffer, fence ) ) + { + throw error( input.start, ex_missing_fence_parameter ); + } + + if ( id == ci_left ) + { + if( *input.start == '(' ) + { + prevBuf.write( tagOn, len ); + } + else + { + prevBuf.write( tagOn, len - 1 ); // don't write > + prevBuf.format( " left='%s'", fence.output ); + } + } + else + { + if( *input.start == ')' ) + { + prevBuf.write( "><mrow>" ); + } + else + { + prevBuf.format( " right='%s'><mrow>", fence.output ); + } + } + break; + default: + throw error( input.start, ex_missing_fence_parameter ); + } +} + +static bool onFence( Buffer &prevBuf, command_id id, sub_expression subType, const char *tagOn, const char *tagOff ) +{ + Buffer str, fence; + + if( id == ci_right ) + { + if( subType == se_fence ) + { + return true; + } + throw error( pCur, ex_missing_left_fence ); + } + + getFence( fence, tagOn, ci_left ); + runLoop( str, se_fence, NULL ); + getFence( fence, tagOn, ci_right ); + + str.write( tagOff ); + prevBuf.append( fence, true ); + prevBuf.append( str, true ); + return false; +} + diff --git a/support/ltx2mathml/src/ltx2mathmltables.cpp b/support/ltx2mathml/src/ltx2mathmltables.cpp new file mode 100644 index 0000000000..b41016ad08 --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmltables.cpp @@ -0,0 +1,672 @@ +#include "ltx2mathmltables.h" +#include <stdio.h> + +static CommandStruct commandTable[] = { +{"Overleftarrow", ci_accent,pt_one, "<mover accent='true'>", "<mo stretchy='true'>⇐</mo></mover>" }, +{"Overleftrightarrow", ci_accent,pt_one, "<mover accent='true'>", "<mo stretchy='true'>⇔</mo></mover>" }, +{"Overrightarrow", ci_accent,pt_one, "<mover accent='true'>", "<mo stretchy='true'>⇒</mo></mover>" }, +{"actuarial", ci_menclose, pt_one,"<menclose notation='actuarial'>", "</menclose>" }, +{"acute", ci_accent,pt_one, "<mover accent='true'>", "<mo>´</mo></mover>" }, +{"bar", ci_accent,pt_one, "<mover accent='true'>", "<mo stretchy='false'>¯</mo></mover>" }, +{"begin", ci_begin,pt_especial,"", "" }, +{"binom", ci_binom,pt_two, "<mfenced><mrow><mfrac linethickness='0'>","</mfrac></mrow></mfenced>" }, +{"breve", ci_accent,pt_one, "<mover accent='true'>", "<mo>˘</mo></mover>" }, +{"cfrac", ci_cfrac,pt_especial, "<mfrac>", "</mstyle></mfrac>" }, +{"check", ci_accent,pt_one, "<mover accent='true'>", "<mo>ˇ</mo></mover>" }, +{"ddddot", ci_accent,pt_one, "<mover accent='true'>", "<mo>¨¨</mo></mover>" }, +{"dddot", ci_accent, pt_one, "<mover accent='true'>", "<mo>⃛</mo></mover>" }, +{"ddot", ci_accent, pt_one, "<mover accent='true'>", "<mo>¨</mo></mover>" }, +{"dfrac", ci_mfrac, pt_two, "<mstyle displaystyle='true' scriptlevel='0'><mfrac>", "</mfrac></mstyle>" }, +{"dot", ci_accent, pt_one, "<mover accent='true'>", "<mo>˙</mo></mover>" }, +{"end", ci_end, pt_especial, "","" }, +{"eqno", ci_eqno, pt_especial, "<mtable><mlabeledtr><mtd><mtext>", "</mtext></mtd><mtd>" }, +{"frac", ci_frac, pt_two, "<mfrac>", "</mfrac>" }, +{"func", ci_func, pt_plain, "<mi>", "</mi>" }, +{"grave", ci_accent, pt_one, "<mover accent='true'>", "<mo>̀</mo></mover>" }, +{"hat", ci_accent, pt_one, "<mover accent='true'>", "<mo>ˆ</mo></mover>" }, +{"hfill", ci_hfill, pt_especial, "","" }, +{"hphantom", ci_phantom, pt_one, "<mphantom><mpadded height='0%' depth='0'>", "</mpadded></mphantom>" }, +{"hungarumlaut", ci_accent, pt_one, "<mover accent='true'>", "<mo>˝</mo></mover>" }, +{"left", ci_left, pt_especial, "<mfenced>", "</mrow></mfenced>" }, +{"leqno", ci_eqno, pt_especial, "<mtable side='left'><mlabeledtr><mtd><mtext>", "</mtext></mtd><mtd>" }, +{"limits", ci_limits, pt_especial, "","" }, +{"longdiv", ci_menclose, pt_one, "<menclose notation='longdiv'>", "</menclose>" }, +{"lsub", ci_lsub, pt_especial, "<mmultiscripts>", "</mmultiscripts>" }, +{"lsubsup", ci_lsubsup, pt_especial, "<mmultiscripts>", "</mmultiscripts>" }, +{"lsup", ci_lsup, pt_especial, "<mmultiscripts>", "</mmultiscripts>" }, +{"mathbb", ci_mathfont, pt_plain, "<mi mathvariant='double-struck'>", "</mi>" }, +{"mathbf", ci_mathfont, pt_plain, "<mi mathvariant='bold'>", "</mi>" }, +{"mathbfrak", ci_mathfont, pt_plain, "<mi mathvariant='bold-fraktur'>", "</mi>" }, +{"mathbi", ci_mathfont, pt_plain, "<mi mathvariant='bold-italic'>", "</mi>" }, +{"mathbin", ci_mathbin, pt_plain, "<mo lspace='.222222em' rspace='.222222em'>", "</mo>" }, +{"mathbsc", ci_mathfont, pt_plain, "<mi mathvariant='bold-script'>", "</mi>" }, +{"mathbss", ci_mathfont, pt_plain, "<mi mathvariant='bold-sans-serif'>", "</mi>" }, +{"mathfrak", ci_mathfont, pt_plain, "<mi mathvariant='fraktur'>", "</mi>" }, +{"mathit", ci_mathfont, pt_plain, "<mi mathvariant='italic'>", "</mi>" }, +{"mathop", ci_mathop, pt_plain, "<mo>", "</mo>" }, +{"mathord", ci_mathord, pt_plain, "<mo lspace='0' rspace='0'>", "</mo>" }, +{"mathord", ci_mathord, pt_plain, "<mo lspace='0' rspace='0'>", "</mo>"}, +{"mathrel", ci_mathrel, pt_plain, "<mo lspace='.27777em' rspace='.27777em'>", "</mo>" }, +{"mathring", ci_accent, pt_one, "<mover accent='true'>", "<mo>˚</mo></mover>" }, +{"mathrm", ci_mathfont, pt_plain, "<mi mathvariant='normal'>", "</mi>" }, +{"mathsc", ci_mathfont, pt_plain, "<mi mathvariant='script'>", "</mi>" }, +{"mathss", ci_mathfont, pt_plain, "<mi mathvariant='sans-serif'>", "</mi>" }, +{"mathssbi", ci_mathfont, pt_plain, "<mi mathvariant='sans-serif-bold-italic'>", "</mi>" }, +{"mathssi", ci_mathfont, pt_plain, "<mi mathvariant='sans-serif-italic'>", "</mi>" }, +{"mathstrut", ci_strut, pt_especial, "<mphantom><mpadded width='0%' lspace='0'><mo>(</mo></mpadded></mphantom>","" }, +{"mathtt", ci_mathfont, pt_plain, "<mi mathvariant='monospace'>", "</mi>" }, +{"mn", ci_mn, pt_plain, "<mn>", "</mn>" }, +{"mo", ci_mo, pt_plain, "<mo>", "</mo>" }, +{"ms", ci_mathstring, pt_especial, "<ms>", "</ms>" }, +{"nolimits", ci_limits, pt_especial, "","" }, +{"overbrace", ci_underoverbrace, pt_especial, "<mover accent='false'>", "<mo stretchy='true'>︷</mo></mover>" }, +{"overbrack", ci_accent, pt_one, "<mover accent='false'>", "<mo stretchy='true'>⎴</mo></mover>" }, +{"overleftarrow", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>←</mo></mover>" }, +{"overleftrightarrow", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>↔</mo></mover>" }, +{"overline", ci_accent, pt_one, "<mover accent='false'>", "<mo stretchy='true'>¯</mo></mover>" }, +{"overparen", ci_accent, pt_one, "<mover accent='false'>", "<mo stretchy='true'>⌢</mo></mover>" }, +{"overrightarrow", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>→</mo></mover>" }, +{"phantom", ci_phantom, pt_one, "<mphantom>", "</mphantom>" }, +{"qdot", ci_accent, pt_one, "<mover accent='true'>", "<mo>¨¨</mo></mover>" }, +{"right", ci_right, pt_especial, "","" }, +{"sqrt", ci_sqrt, pt_especial, "<msqrt>", "</msqrt>" }, +{"stack", ci_stack, pt_two, "<mfrac linethickness='0'>", "</mfrac>" }, +{"stackrel", ci_stackrel, pt_especial, "<mover>", "</mover>" }, +{"strut", ci_strut, pt_especial, "<mspace width='0pt' height='8.5pt' depth='3.5pt'/>","" }, +{"tbinom", ci_binom, pt_two, "<mstyle scriptlevel='1'><mfenced><mrow><mfrac linethickness='0'>", "</mfrac></mrow></mfenced></mstyle>" }, +{"tdot", ci_accent, pt_one, "<mover accent='true'>", "<mo>⃛</mo></mover>" }, +{"text", ci_text, pt_especial, "<mtext>", "</mtext>" }, +{"textbf", ci_text, pt_especial, "<mtext mathvariant='bold'>", "</mtext>" }, +{"textbi", ci_text, pt_especial, "<mtext mathvariant='bold-italic'>", "</mtext>" }, +{"textbsf", ci_text, pt_especial, "<mtext mathvariant='bold-sans-serif'>", "</mtext>" }, +{"textit", ci_text, pt_especial, "<mtext mathvariant='italic'>", "</mtext>" }, +{"textsf", ci_text, pt_especial, "<mtext mathvariant='sans-serif'>", "</mtext>" }, +{"textsfbi", ci_text, pt_especial, "<mtext mathvariant='sans-serif-bold-italic'>", "</mtext>" }, +{"textsfit", ci_text, pt_especial, "<mtext mathvariant='sans-serif-italic'>", "</mtext>" }, +{"texttt", ci_text, pt_especial, "<mtext mathvariant='monospace'>", "</mtext>" }, +{"tfrac", ci_mfrac, pt_two, "<mstyle displaystyle='false' scriptlevel='0'><mfrac>", "</mfrac></mstyle>" }, +{"tilde", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='false'>˜</mo></mover>" }, +{"underbrace", ci_underoverbrace, pt_especial, "<munder accentunder='false'>", "<mo stretchy='true'>︸</mo></munder>" }, +{"underbrack", ci_accent, pt_one, "<munder accentunder='false'>", "<mo stretchy='true'>⎵</mo></munder>" }, +{"underleftarrow", ci_accent, pt_one, "<munder accentunder='true'>", "<mo stretchy='true'>←</mo></munder>" }, +{"underleftrightarrow", ci_accent, pt_one, "<munder accentunder='true'>", "<mo stretchy='true'>↔</mo></munder>" }, +{"underline", ci_accent, pt_one, "<munder accentunder='false'>", "<mo stretchy='true'>̲</mo></munder>" }, +{"underparen", ci_accent, pt_one, "<munder accentunder='false'>", "<mo stretchy='true'></mo></munder>" }, +{"underrightarrow", ci_accent, pt_one, "<munder accentunder='true'>", "<mo stretchy='true'>→</mo></munder>" }, +{"undertilde", ci_accent, pt_one, "<munder accentunder='false'>", "<mo stretchy='true'>˜</mo></munder>" }, +{"vec", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='false'>→</mo></mover>" }, +{"vphantom", ci_phantom, pt_one, "<mphantom><mpadded width='0%' lspace='0'>", "</mpadded></mphantom>" }, +{"widehat", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>̂</mo></mover>" }, +{"widetilde", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>˜</mo></mover>" }, +{"widevec", ci_accent, pt_one, "<mover accent='true'>", "<mo stretchy='true'>→</mo></mover>" }, +{"xleftarrow", ci_ext_arrows, pt_especial, "<mo stretchy='true'>←</mo>", "</mover>" }, +{"xleftrightarrow", ci_ext_arrows, pt_especial, "<mo stretchy='true'>↔</mo>", "</mover>"}, +{"xrightarrow", ci_ext_arrows, pt_especial, "<mo stretchy='true'>→</mo>", "</mover>" } +}; + +struct EnvironmentStruct environmentTable[] = { + { "array", ci_array, "<mtable><mtr><mtd>", "</mtd></mtr></mtable>" }, + { "bmatrix", ci_bmatrix, "<mfenced open='[' close=']' separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" }, + { "cases", ci_cases, "<mfenced open='{' close='' separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" }, + { "eqnarray", ci_eqnarray, "<mtable columnalign='right center left' columnspacing='.222222em'><mtr><mtd>", "</mtd></mtr></mtable>" }, + { "matrix", ci_matrix, "<mtable><mtr><mtd>", "</mtd></mtr></mtable>" }, + { "pmatrix", ci_pmatrix, "<mfenced separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" }, + { "vmatrix", ci_vmatrix, "<mfenced open='|' close='|' separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" }, + { "Bmatrix", ci_Bmatrix, "<mfenced open='{' close='}' separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" }, + { "Vmatrix", ci_Vmatrix, "<mfenced open='‖' close='‖' separators=''><mtable><mtr><mtd>", "</mtd></mtr></mtable></mfenced>" } + +}; + +static FunctionStruct functionTable[] = { + { "Pr", "Pr", mt_func_limits}, + { "arccos", "arccos", mt_func}, + { "arcsin", "arcsin", mt_func}, + { "arctan", "arctan", mt_func}, + { "arg", "arg", mt_func}, + { "cos", "cos", mt_func}, + { "cosh", "cosh", mt_func}, + { "cot", "cot", mt_func}, + { "coth", "coth", mt_func}, + { "csc", "csc", mt_func}, + { "deg", "deg", mt_func}, + { "det", "det", mt_func_limits}, + { "dim", "dim", mt_func}, + { "exp", "exp", mt_func}, + { "gcd", "gcd", mt_func_limits}, + { "hom", "hom", mt_func}, + { "inf", "inf", mt_func_limits}, + { "ker", "ker", mt_func}, + { "lg", "lg", mt_func}, + { "lim", "lim", mt_func_limits}, + { "liminf", "lim sup", mt_func_limits}, + { "limsup", "lim sup", mt_func_limits}, + { "ln", "ln", mt_func}, + { "log", "log", mt_func}, + { "max", "max", mt_func_limits}, + { "min", "min", mt_func_limits}, + { "sec", "sec", mt_func}, + { "sin", "sin", mt_func}, + { "sinh", "sinh", mt_func}, + { "sup", "sup", mt_func_limits}, + { "tan", "tan", mt_func}, + { "tanh", "tanh", mt_func} +}; + + +static EntityStruct entityTable[] = { + +{ "Delta", 0x394, mt_ident }, +{ "Gamma", 0x393, mt_ident }, +{ "Lambda", 0x39B, mt_ident }, +{ "Omega", 0x3A9, mt_ident }, +{ "Phi", 0x3A6, mt_ident }, +{ "Psi", 0x3A8, mt_ident }, +{ "Sigma", 0x3A3, mt_ident }, +{ "Xi", 0x39E, mt_ident }, +{ "alpha", 0x3B1, mt_ident }, +{ "ast", 0x2A, mt_bin }, +{ "beta", 0x3B2, mt_ident }, +{ "bigcap", 0x22C2, mt_mov_limits }, + +{ "bigcup", 0x22C3, mt_mov_limits }, + +{ "bigodot", 0x2299, mt_mov_limits }, + +{ "bigoplus", 0x2295, mt_mov_limits }, + +{ "bigotimes", 0x2297, mt_mov_limits }, + +{ "bigsqcup", 0x2A06, mt_mov_limits }, + +{ "biguplus", 0x2A04, mt_mov_limits }, + +{ "bigvee", 0x22C1, mt_mov_limits }, + +{ "bigwedge", 0x22C0, mt_mov_limits }, + +{ "cdot", 0xB7, mt_bin }, +{ "cdots", 0x22EF, mt_ord }, +{ "centerdot", 0xB7, mt_bin }, +{ "chi", 0x3C7, mt_ident }, +{ "coprod", 0x2210, mt_mov_limits }, + +{ "ddots", 0x22F1, mt_ord }, +{ "delta", 0x3B4, mt_ident }, +{ "div", 0xF7, mt_bin }, +{ "epsilon", 0x3B5, mt_ident }, +{ "gamma", 0x3B3, mt_ident }, +{ "ge", 0x2265, mt_rel }, +{ "iiiint", 0x2A0C, mt_limits }, + +{ "iiint", 0x222D, mt_limits }, + +{ "iint", 0x222C, mt_limits }, + +{ "infinity", 0x221E, mt_rel }, +{ "int", 0x222B, mt_limits }, + +{ "kappa", 0x3BA, mt_ident }, +{ "lambda", 0x3BB, mt_ident }, +{ "ldots", 0x2026, mt_ord }, +{ "le", 0x2264, mt_rel }, +{ "leftarrow", 0x2190, mt_rel }, + + + +{ "mu", 0x3BC, mt_ident }, + +{ "nabla", 0x2207, mt_ident }, +{ "nu", 0x3BD, mt_ident }, +{ "oint", 0x222E, mt_limits }, + + +{ "omega", 0x3C9, mt_ident }, +{ "partial", 0x2202, mt_ord }, +{ "phi", 0x3C6, mt_ident }, +{ "pi", 0x3C0, mt_ident }, +{ "pm", 0xB1, mt_rel }, + +{ "prod", 0x220f, mt_mov_limits }, +{ "psi", 0x3C8, mt_ident }, + +{ "rho", 0x3C1, mt_ident }, +{ "rightarrow", 0x2192, mt_rel }, +{ "sigma", 0x3C3, mt_ident }, +{ "sim", 0x223C, mt_rel }, +{ "sum", 0x2211, mt_mov_limits }, + +{ "tau", 0x3C4, mt_ident }, +{ "theta", 0x3B8, mt_ident }, +{ "times", 0xD7, mt_bin }, +{ "to", 0x2192, mt_rel }, +{ "upsilon", 0x3C5, mt_ident }, +{ "varepsilon", 0x3B5, mt_ident }, +{ "vdots", 0x22EE, mt_ord }, +{ "xi", 0x3BE, mt_ident }, +{ "zeta", 0x3B6, mt_ident } + + +}; + +static ErrorTable errorTable[] = { + { ex_out_of_memory, "Out of memory" }, + { ex_missing_lbrace, "Missing '{'" }, + { ex_prefix_superscript, "Illegal prefix superscript: use the '\\lsup' command" }, + { ex_prefix_subscript, "Illegal prefix subscript: use the '\\lsub' command" }, + { ex_misplaced_column_separator, "Misplaced column separator" }, + { ex_more_rbrace_than_lbrace, "Syntax error: more '}' than '{'" }, + { ex_control_name_too_long, "Control name too long: maximum is 32" }, + { ex_misplaced_row_separator, "Misplaced row separator" }, + { ex_illegal_subscript, "Illegal subscript" }, + { ex_illegal_superscript, "Illegal superscript" }, + { ex_undefined_control_sequence, "Undefined control sequence" }, + { ex_misplaced_inline_formula, "Misplaced inline formula" }, + { ex_missing_parameter, "Missing parameter" }, + { ex_more_lbrace_than_rbrace, "Syntax error: more '{' than '}'" }, + { ex_double_superscript, "Double superscript" }, + { ex_double_subscript, "Double subscript" }, + { ex_use_subscript_before_superscript, "Use subscript first as the element is <msubsup>" }, + { ex_internal_error, "Internal error" }, + { ex_missing_end_tag, "Missing end tag" }, + { ex_undefined_environment_type, "Undefined environment type" }, + { ex_unknown_alignment_character, "Unknown alignment character" }, + { ex_missing_begin, "Missing \\begin" }, + { ex_missing_end, "Missing \\end" }, + { ex_mismatched_environment_type, "Mismatched environment type"}, + { ex_too_many_columns, "Too many columns" }, + { ex_unknown_attribute, "Unknown attribute" }, + { ex_no_command_allowed, "Command not allowed here" }, + { ex_misplaced_limits, "Limit controls must follow a math operator" }, + { ex_missing_fence_parameter, "Missing fence parameter" }, + { ex_not_math_mode, "Not in math mode" }, + { ex_missing_right_sq_bracket, "Missing ']'" }, + { ex_missing_dollar_symbol, "Missing '$'" }, + { ex_missing_left_fence, "Missing \\left" }, + { ex_missing_right_fence, "Missing \\right" }, + { ex_ambiguous_script, "Ambiguous script; use \\left and \\right" }, + { ex_misplaced_eqno, "Equation number not allowed here" }, + { ex_duplicate_eqno, "Duplicate equation number" }, + { ex_missing_column_alignment, "Missing column alignment" }, + { ex_missing_subsup_base, "Missing subscript/superscript base" }, + { ex_unknown_character, "Internal error: Unknown character" }, + { ex_unhandled_mathtype, "Internal error: unhandled math type" } + //{ ex_misplaced_nolimits, "Nolimits control must follow a math operator" } +}; + +SymbolTable mathvariant[]= { + {"bb", "double-struck"}, + {"bf", "bold"}, + {"bfrak", "bold-fraktur"}, + {"bi", "bold-italic"}, + {"bsc", "bold-script"}, + {"bss", "bold-sans-serif"}, + {"frak", "fraktur"}, + {"it", "italic"}, + {"rm", "normal"}, + {"sc", "script"}, + {"ss", "sans-serif"}, + {"ssbi", "sans-serif-bold-italic"}, + {"ssi", "sans-serif-italic"}, + {"tt", "monospace"} +}; + +static EntityStruct fenceTable[] = { + + { "[", '[', mt_left_fence }, + { "]", ']', mt_right_fence }, + { "\\{", '{', mt_left_fence }, + { "\\}", '}', mt_right_fence }, + { "/", '/', mt_ord }, + { "(", '(', mt_left_fence }, + { ")", ')', mt_right_fence }, + { "|", 0x007C, mt_ord }, + { "\\|", 0x2016, mt_ord }, + { "<", 0x2329, mt_left_fence }, + { ">", 0x232A, mt_right_fence }, + { ".", 0, mt_ord }, //see function below + { "lgroup", '(', mt_left_fence }, + { "rgroup", ')', mt_right_fence }, + { "langle", 0x2329, mt_left_fence }, + { "rangle", 0x232A, mt_right_fence }, + { "lAngle", 0x300A, mt_left_fence }, + { "rAngle", 0x300B, mt_right_fence }, + { "lfloor", 0x230A, mt_left_fence },//'&lfloor}, //0x230A}, + { "rfloor", 0x230B, mt_right_fence },//'&rfloor}, //0x230B}, + { "lceil", 0x2308, mt_left_fence },//'&lceil}, // 0x2308}, + { "rceil", 0x2309, mt_right_fence },//'&rceil}, // 0x2309}, + { "lbrack", '[', mt_left_fence }, + { "rbrack", ']', mt_right_fence }, + { "lBrack", 0x301A, mt_left_fence }, + { "rBrack", 0x301B, mt_right_fence }, + { "lbrace", '{', mt_left_fence }, + { "rbrace", '}', mt_right_fence }, + { "backslash", '\\', mt_ord }, + { "vert", 0x007C, mt_ord }, + { "Vert", 0x2016, mt_ord }, + { "uparrow", 0x2191, mt_ord }, + { "Uparrow", 0x21D1, mt_ord }, + { "downarrow", 0x2193, mt_ord }, + { "Downarrow", 0x21D3, mt_ord }, + { "updownarrow", 0x2195, mt_ord }, + { "Updownarrow", 0x21D5, mt_ord }, + { "lmoustache", 0x23B0, mt_left_fence }, //0x23B0}, + { "rmoustache", 0x23B1, mt_right_fence }, //0x23B1}, + { "lmoust", 0x23B0, mt_left_fence }, + { "rmoust", 0x23B1, mt_right_fence } +}; + +// thickspace .27777 +// medspace .222222em +// thinspace .16667em +static SymbolStruct symbols[]= { + {"\\ ", " ", "<mspace width='.25em'/>", mt_ord }, + {"\\,", " ", "<mspace width='.16667em'/>", mt_ord }, + {"\\:", " ", "<mspace width='.222222em'/>", mt_ord }, + {"\\>", " ", "<mspace width='.222222em'/>", mt_ord }, + {"\\;", " ", "<mspace width='.27777em'/>", mt_ord }, + {"\\!", " ", "<mspace width='-.16667em'/>", mt_ord }, + {"\\~", " ", "<mo> </mo>", mt_ord }, + {"\\|", "‖", "<mo mathsize='1'>‖</mo>", mt_fence }, + {"\\{", "{", "<mo mathsize='1'>{</mo>", mt_left_fence }, + {"\\}", "}", "<mo mathsize='1'>}</mo>", mt_right_fence }, + //{"|", "|", "<mo mathsize='1'>|</mo>", mt_ord }, + {"|", "|", "<mo mathsize='1'>|</mo>", mt_ord }, + {"[", "[", "<mo mathsize='1'>[</mo>", mt_left_fence }, + {"]", "]", "<mo mathsize='1'>]</mo>", mt_right_fence }, + {"(", "(", "<mo mathsize='1'>(</mo>", mt_left_fence }, + {")", ")", "<mo mathsize='1'>)</mo>", mt_right_fence }, + {"<", "<", "<mo mathsize='1'><</mo>", mt_left_fence }, + {">", ">", "<mo mathsize='1'>></mo>", mt_right_fence }, + {"-", "-", "<mo>−</mo>", mt_bin_unary }, + {"`", "̀", "<mo>̀</mo>", mt_ord }, + {"@", "@", "<mo>@</mo>", mt_ord }, + {"*", "*", "<mo>*</mo>", mt_ord }, + {"'", "′", "<mo>′</mo>", mt_ord }, + {"''", "″", "<mo>″</mo>", mt_ord }, + {"'''", "‴", "<mo>‴</mo>", mt_ord }, + {"\"", "”", "<mo>”</mo>", mt_ord }, + {"/", "/", "<mtext>/</mtext>", mt_ord }, + {"\\/", "​", "<mo>​</mo>", mt_ord }, + {"\\%", "%", "<mo>%</mo>", mt_ord }, + {"\\#", "#", "<mo>#</mo>", mt_ord }, + {"\\$", "$", "<mtext>$</mtext>", mt_ord }, + {"\\^", "ˆ", "<mo>ˆ</mo>", mt_ord }, + {"\\&", "&", "<mo>&</mo>", mt_ord }, + {"\\_", "_", "<mo>_</mo>", mt_ord }, + {"\\-", "​", "<mo>​</mo>", mt_ord }, + {"!", "!", "<mo>!</mo>", mt_ord }, + {"+", "+", "<mo>+</mo>", mt_bin_unary }, + {"=", "=", "<mo>=</mo>", mt_bin }, + {":", ":", "<mo>:</mo>", mt_bin }, + {";", ";", "<mo>;</mo>", mt_ord }, + {",", ",", "<mo>,</mo>", mt_punct }, + {".", ".", "<mo>.</mo>", mt_ord }, + {"?", "?", "<mo>?</mo>", mt_ord } +}; + +#define TABLE_SIZE(x) ( sizeof( (x) )/sizeof( (x)[0] )) + + +inline int fastCompare( const char *s1, const char *s2 ) +{ + return ( ( *s1 == *s2 ) ? strcmp( s1, s2 ) : *s1 - *s2 ); +} + + +CommandStruct *isCommand( const char *name ) +{ + const int size = TABLE_SIZE( commandTable ); + + int result, low, mid, high; + + low = 0; + high = size - 1; + + while( low <= high ) + { + mid = ( low + high )/2; + + result = fastCompare( name, commandTable[mid].name ); + + if( result < 0 ) + { + high = mid - 1; + } + else if( result > 0 ) + { + low = mid + 1; + } + else + { + return &commandTable[mid]; + } + } + + return NULL; +} + + +EntityStruct *isEntity( const char *name ) +{ + const int size = TABLE_SIZE( entityTable ); + + int result, low, mid, high; + + low = 0; + high = size - 1; + + while( low <= high ) + { + mid = ( low + high )/2; + + result = fastCompare( name, entityTable[mid].name ); + + if( result < 0 ) + { + high = mid - 1; + } + else if( result > 0 ) + { + low = mid + 1; + } + else + { + return &entityTable[mid]; + } + } + + return NULL; + +} + + +FunctionStruct *isFunction( const char *name ) +{ + const int size = TABLE_SIZE( functionTable ); + + int result, low, mid, high; + + low = 0; + high = size - 1; + + while( low <= high ) + { + mid = ( low + high )/2; + + result = fastCompare( name, functionTable[mid].name ); + + if( result < 0 ) + { + high = mid - 1; + } + else if( result > 0 ) + { + low = mid + 1; + } + else + { + return &functionTable[mid]; + } + } + + return NULL; +} + +token_type getControlType( const char *name, ControlStruct &control ) +{ + + if( ( control.command = isCommand( name ) ) != NULL ) + { + control.token = token_control_command; + } + else if( ( control.entity = isEntity( name ) ) != NULL ) + { + control.token = token_control_entity; + } + else if( ( control.function = isFunction( name ) ) != NULL ) + { + control.token = token_control_function; + } + else + { + control.token = token_unknown; + } + + return control.token; +} + +/* + +enum math_type { mt_unknown, mt_ident, mt_digit, mt_ord, mt_bin, mt_unary, mt_rel, mt_fence, + mt_mov_limits, mt_limits, mt_func, mt_func_limits, mt_text }; +*/ + +const char *getErrorMsg( ex_exception code ) +{ + const int size = TABLE_SIZE( errorTable ); + + for( int i = 0; i < size; ++i ) + { + if( code == errorTable[i].code ) + { + return errorTable[i].msg; + } + } + + return NULL; +} + +const char *getMathVariant( const char *attrib ) +{ + const int size = TABLE_SIZE( mathvariant ); + + int result, low, mid, high; + + low = 0; + high = size - 1; + + while( low <= high ) + { + mid = ( low + high )/2; + + result = fastCompare( attrib, mathvariant[mid].key ); + + if( result < 0 ) + { + high = mid - 1; + } + else if( result > 0 ) + { + low = mid + 1; + } + else + { + return mathvariant[mid].value; + } + } + + return NULL; +} + + +bool getFenceType( const char *name, FenceStruct &fence ) +{ + const int size = TABLE_SIZE( fenceTable ); + + for( int i = 0; i < size; ++i ) + { + if( strcmp( name, fenceTable[i].name ) == 0 ) + { + fence.entity = &fenceTable[i]; + + + if( fence.entity->code < 256 ) // ascii + { + fence.output[0] = (char)fence.entity->code; + fence.output[1] = '\0'; + } + else + { + //sprintf_s( fence.output, sizeof( fence.output ) - 1, "&#x%x;", fence.entity->code ); + sprintf( fence.output, "&#x%x;", fence.entity->code ); + } + return true; + } + } + + fence.entity = NULL; + return false; +} + + + +EnvironmentStruct *getEnvironmentType( const char *name ) +{ + const int size = TABLE_SIZE( environmentTable ); + + int result, low, mid, high; + + low = 0; + high = size - 1; + + while( low <= high ) + { + mid = ( low + high )/2; + + result = fastCompare( name, environmentTable[mid].name ); + + if( result < 0 ) + { + high = mid - 1; + } + else if( result > 0 ) + { + low = mid + 1; + } + else + { + return &environmentTable[mid]; + } + } + + + return NULL; +} + + +SymbolStruct *getSymbol( const char *name ) +{ + const int size = TABLE_SIZE( symbols ); + + for( int i = 0; i < size; ++i ) + { + if( strcmp( name, symbols[i].name ) == 0 ) + { + return &symbols[i]; + } + } + + return NULL; +} + + diff --git a/support/ltx2mathml/src/ltx2mathmltables.h b/support/ltx2mathml/src/ltx2mathmltables.h new file mode 100644 index 0000000000..ebc9baef24 --- /dev/null +++ b/support/ltx2mathml/src/ltx2mathmltables.h @@ -0,0 +1,125 @@ +#pragma once + +#include <string.h> +#include "ltx2mathmlexceptions.h" + +enum command_id { + ci_unknown, ci_msub, ci_msup, ci_msubsup, ci_munder, ci_mover, ci_munderover, + + ci_mi, ci_mn, ci_mo, ci_text, ci_cfrac, ci_mfrac, ci_frac, ci_mathfont, ci_sqrt, ci_begin, ci_end, + ci_array, ci_eqnarray, ci_cases, ci_matrix, ci_bmatrix, ci_Bmatrix, ci_pmatrix, ci_vmatrix, + ci_Vmatrix, ci_mathop, ci_accent, ci_ext_arrows, + ci_func, ci_binom, ci_stack, ci_stackrel, ci_hfill, ci_limits, ci_nolimits, + ci_menclose, ci_strut, ci_phantom, ci_left, ci_right, ci_underoverbrace, + ci_mathstring, ci_lsub, ci_lsup, ci_lsubsup, ci_eqno, ci_leqno, ci_mathord, ci_mathbin, ci_mathrel +}; + +enum math_type { mt_unknown, mt_ident, mt_digit, mt_ord, + mt_bin, mt_unary, mt_bin_unary, mt_rel, + mt_left_fence, mt_right_fence, mt_fence, + mt_mov_limits, mt_limits, mt_func, mt_func_limits, + mt_text, mt_punct }; + +enum param_type { pt_unknown, pt_none, pt_plain, pt_one, pt_two, pt_three, pt_table, pt_others, + pt_especial }; + +enum token_type { + token_eof = -1, + token_unknown = 0, + token_alpha, + token_digit, + token_symbol, + token_white_space, + token_left_brace, + token_right_brace, + token_right_sq_bracket, + token_superscript, + token_subscript, + token_column_sep, + token_row_sep, + token_control_symbol, + token_control_name, + token_control_command, + token_control_entity, + token_control_function, + token_inline_math, + token_prime +}; + +struct SymbolTable { + union { + char const *tagOn; + char const*key; + char const*name; + //exception code; + }; + union { + char const*tagOff; + char const*value; + math_type mathType; + //char *errorMsg; + }; +}; + +struct ErrorTable { + ex_exception code; + char const *msg; +}; + +struct EntityStruct { + char const *name; + unsigned int code; + math_type mathType; +}; + +struct FunctionStruct { + char const *name; + char const *output; + math_type mathType; +}; + +struct FenceStruct { + EntityStruct *entity; + char output[20]; +}; + +struct CommandStruct { + char const *name; + command_id id; + param_type param; + char const *tagOn; + char const *tagOff; +}; + +struct EnvironmentStruct { + char const *name; + command_id id; + char const *tagOn; + char const*tagOff; +}; + +struct SymbolStruct { + char const* name; + char const* literal; + char const* element; + math_type mathType; +}; + +struct ControlStruct { + CommandStruct *command; + token_type token; + char *start; + union { + EntityStruct *entity; + FunctionStruct *function; + }; +}; + + +token_type getControlType(const char *name, ControlStruct &control ); +const char *getErrorMsg( ex_exception code ); +const char *getMathVariant(const char *attrib ); +bool getFenceType(const char *name, FenceStruct &fence ); +EnvironmentStruct *getEnvironmentType(const char *name ); +SymbolStruct *getSymbol(const char *name ); + diff --git a/support/ltx2mathml/src/test.cpp b/support/ltx2mathml/src/test.cpp new file mode 100644 index 0000000000..99b68a5d58 --- /dev/null +++ b/support/ltx2mathml/src/test.cpp @@ -0,0 +1,41 @@ +// testtex.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include <iostream> +#include "ltx2mathml.h" + + +int main() +{ + int index; + char input[256]; + + while (true) + { + printf("> "); + + if( !fgets(input, 255, stdin) || ('\n' == input[0])) + break; + else + { + if (convertFormula(input, -1, &index))// -1 indicates 'input' is null terminated + { + string result; + + if (getMathMLOutput(result, true)) + { + std::cout << result << std::endl; + } + else + { + std::cout << "Input produced no output\n"; + } + } + else + { + std::cout << "[Index: " << index << "] " << getLastError() << std::endl; + } + } + } + return 0; +} |