Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "unicodemath-ml",
"version": "1.0.0",
"description": "JavaScript library for converting UnicodeMath to MathML in Node.js",
"main": "src/index.js",
"module": "src/index.mjs",
"types": "src/index.d.ts",
"exports": {
".": {
"import": "./src/index.mjs",
"require": "./src/index.js",
"types": "./src/index.d.ts"
}
},
"scripts": {
"build:parser": "pegjs --format commonjs -o src/parser.js src/unicodemathml-parser.pegjs",
"build": "npm run build:parser",
"test": "npm run build && node test/test.js",
"example": "npm run build && node examples/node-example.js",
"prepublishOnly": "npm run build"
},
"keywords": [
"math",
"mathml",
"unicodemath",
"mathematics",
"parser",
"converter"
],
"author": "Community (based on work by Noah Doersing and Murray Sargent III)",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/MurrayIII/UnicodeMathML.git"
},
"engines": {
"node": ">=12.0.0"
},
"files": [
"src/",
"dist/",
"README.md",
"LICENSE"
],
"devDependencies": {
"pegjs": "^0.10.0"
}
}
53 changes: 53 additions & 0 deletions src/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// scripts/build.js
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const util = require('util');

const execAsync = util.promisify(exec);

async function buildParser() {
console.log('Building parser from PEG.js grammar...');

try {
// Generate parser from .pegjs file
await execAsync('npx pegjs --format commonjs -o src/parser.js src/unicodemathml-parser.pegjs');
console.log('✓ Parser generated successfully');

// Verify the generated parser works
const parser = require('../src/parser.js');
const testResult = parser.parse('x^2');
console.log('✓ Parser test passed');

} catch (error) {
console.error('✗ Parser generation failed:', error.message);
process.exit(1);
}
}

async function copyGrammarFile() {
// Copy the original .pegjs file to src/ if it's not there
const sourcePath = path.join(__dirname, '../unicodemathml-parser.pegjs');
const destPath = path.join(__dirname, '../src/unicodemathml-parser.pegjs');

if (fs.existsSync(sourcePath) && !fs.existsSync(destPath)) {
console.log('Copying grammar file to src/...');
fs.copyFileSync(sourcePath, destPath);
console.log('✓ Grammar file copied');
}
}

async function main() {
console.log('Building UnicodeMathML Node.js module...\n');

await copyGrammarFile();
await buildParser();

console.log('\n✓ Build completed successfully');
}

if (require.main === module) {
main().catch(console.error);
}

module.exports = { buildParser, copyGrammarFile };
131 changes: 131 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const { resolveControlWords, normalizeOptions } = require('./utils');

// Import the main conversion function from the existing unicodemathml.js
const unicodemathmlModule = require('./unicodemathml.js');

// Check if unicodemathml function exists - it should be a global function in that file
// Since the file uses a UMD pattern, we need to extract it differently
let unicodemathmlFunction;

if (typeof unicodemathmlModule === 'function') {
unicodemathmlFunction = unicodemathmlModule;
} else if (typeof unicodemathml !== 'undefined') {
// The function might be global after requiring the file
unicodemathmlFunction = unicodemathml;
} else {
// Fallback: try to execute the file in a way that creates the global
const vm = require('vm');
const fs = require('fs');
const path = require('path');

const code = fs.readFileSync(path.join(__dirname, 'unicodemathml.js'), 'utf8');
const context = {
require: require,
module: { exports: {} },
exports: {},
console: console,
performance: { now: () => Date.now() },
// Create a minimal window-like object for the browser code
window: {},
document: {},
ummlParser: require('./parser.js')
};

vm.createContext(context);
vm.runInContext(code, context);

unicodemathmlFunction = context.unicodemathml;
}

/**
* Convert UnicodeMath string to MathML
* @param {string} unicodeMath - The UnicodeMath expression to convert
* @param {Object} options - Conversion options
* @returns {string} The resulting MathML string
*/
function convertUnicodeToMathML(unicodeMath, options = {}) {
const opts = normalizeOptions(options);

try {
// Set up global config for the unicodemathml function
global.ummlConfig = {
resolveControlWords: opts.resolveControlWords,
customControlWords: opts.customControlWords,
doubleStruckMode: opts.doubleStruckMode,
debug: false,
tracing: false
};

// Create a simple parser object that the unicodemathml.js expects
if (typeof global.ummlParser === 'undefined') {
global.ummlParser = require('./parser.js');
}

// Call the conversion function
const result = unicodemathmlFunction(unicodeMath, opts.displayMode);

if (result && result.mathml) {
return result.mathml;
} else if (typeof result === 'string') {
return result;
} else {
throw new Error('Unexpected result format from unicodemathml function');
}

} catch (error) {
throw new Error(`UnicodeMath conversion failed: ${error.message}`);
} finally {
// Clean up global config
if (global.ummlConfig) {
delete global.ummlConfig;
}
}
}

/**
* Parse UnicodeMath to AST (for advanced users)
* @param {string} unicodeMath - The UnicodeMath expression to parse
* @param {Object} options - Parse options
* @returns {Object} The Abstract Syntax Tree
*/
function parseUnicodeMath(unicodeMath, options = {}) {
const opts = normalizeOptions(options);

try {
// Set up global config
global.ummlConfig = {
resolveControlWords: opts.resolveControlWords,
customControlWords: opts.customControlWords,
doubleStruckMode: opts.doubleStruckMode,
debug: false,
tracing: false
};

if (typeof global.ummlParser === 'undefined') {
global.ummlParser = require('./parser.js');
}

const result = unicodemathmlFunction(unicodeMath, opts.displayMode);

if (result && result.details && result.details.intermediates) {
return result.details.intermediates.parse;
} else {
// Fallback: just try to parse directly
const parser = require('./parser.js');
return parser.parse(unicodeMath);
}

} catch (error) {
throw new Error(`UnicodeMath parsing failed: ${error.message}`);
} finally {
// Clean up
if (global.ummlConfig) {
delete global.ummlConfig;
}
}
}

module.exports = {
convertUnicodeToMathML,
parseUnicodeMath
};
Loading