Skip to content
Merged
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
13 changes: 12 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ npm run test:performance # Performance benchmarks
const parsec = new Parsec()
await parsec.initialize()

// JavaScript-native evaluation (recommended)
const result = parsec.eval('2 + 3 * 4') // → 14 (number)

// Raw C++ JSON evaluation (for platform consistency)
const rawResult = parsec.evalRaw('2 + 3 * 4') // → '{"val": "14", "type": "i"}'

// Batch evaluation
const results = parsec.evaluateBatch(['2+2', 'sqrt(16)', 'sin(pi/2)'])

Expand Down Expand Up @@ -307,11 +313,16 @@ import Parsec from './js/equations_parser_wrapper.js'
const parsec = new Parsec()
await parsec.initialize()

// Evaluate equations
// Evaluate equations (JavaScript-native values)
const result = parsec.eval('2 + 3 * 4') // Returns: 14
const trig = parsec.eval('sin(pi/2)') // Returns: 1
const complex = parsec.eval('real(3+4i)') // Returns: 3
const string = parsec.eval('concat("a","b")') // Returns: "ab"

// Raw C++ JSON evaluation (for platform consistency)
const rawResult = parsec.evalRaw('2 + 3 * 4') // Returns: '{"val": "14", "type": "i"}'
const rawTrig = parsec.evalRaw('sin(pi/2)') // Returns: '{"val": "1", "type": "f"}'
const rawString = parsec.evalRaw('concat("a","b")') // Returns: '{"val": "ab", "type": "s"}'
```

### Test Structure Pattern
Expand Down
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,17 @@ npm install
**Direct Value Returns:**

```javascript
// JavaScript-native values (recommended)
parsec.eval('2 + 3') // → 5 (number)
parsec.eval('sin(pi/2)') // → 1.0 (number)
parsec.eval('5 > 3') // → true (boolean)
parsec.eval('concat("a","b")') // → "ab" (string)

// Raw C++ JSON strings (for platform consistency)
parsec.evalRaw('2 + 3') // → '{"val": "5", "type": "i"}'
parsec.evalRaw('sin(pi/2)') // → '{"val": "1", "type": "f"}'
parsec.evalRaw('5 > 3') // → '{"val": "true", "type": "b"}'
parsec.evalRaw('concat("a","b")') // → '{"val": "ab", "type": "s"}'
```

## 🧪 Comprehensive Testing
Expand Down Expand Up @@ -475,7 +482,7 @@ npm run style:fix

#### `parsec.eval(equation)`

Evaluate a single mathematical expression.
Evaluate a single mathematical expression and return JavaScript-native values.

```javascript
const result = parsec.eval('2 + 3 * 4')
Expand All @@ -488,6 +495,26 @@ const boolean = parsec.eval('5 > 3')
// Returns: true
```

#### `parsec.evalRaw(equation)`

Evaluate a single mathematical expression and return raw C++ JSON strings for platform consistency.

```javascript
const result = parsec.evalRaw('2 + 3 * 4')
// Returns: '{"val": "14", "type": "i"}'

const text = parsec.evalRaw('concat("Hello", " World")')
// Returns: '{"val": "Hello World", "type": "s"}'

const boolean = parsec.evalRaw('5 > 3')
// Returns: '{"val": "true", "type": "b"}'

// Parse the JSON to access individual components
const parsed = JSON.parse(result)
console.log(parsed.val) // "14"
console.log(parsed.type) // "i" (integer)
```

#### `parsec.evaluateBatch(equations)`

Evaluate multiple expressions in one call.
Expand Down
165 changes: 165 additions & 0 deletions js/equations_parser_wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,69 @@ class Parsec {
}
}

/**
* Evaluate a mathematical equation and return raw C++ JSON result (for platform consistency)
* @param {string} equation - The mathematical expression to evaluate
* @returns {string} The evaluated result as raw JSON string from C++ equations-parser
* @throws {Error} If the equation is invalid or evaluation fails
*
* This function provides platform-consistent output matching native implementations,
* returning the exact JSON format that C++ equations-parser produces.
*
* @example
* // Basic arithmetic
* evalRaw("2 + 3 * 4") // → '{"val": "14", "type": "i"}'
*
* @example
* // Trigonometric functions
* evalRaw("sin(pi/2)") // → '{"val": "1", "type": "f"}'
*
* @example
* // String functions
* evalRaw('concat("Hello", " World")') // → '{"val": "Hello World", "type": "s"}'
*
* @example
* // Boolean results
* evalRaw("5 > 3") // → '{"val": "true", "type": "b"}'
*
* @example
* // Special float values
* evalRaw("1/0") // → '{"val": "inf", "type": "f"}'
* evalRaw("sqrt(-1)") // → '{"val": "nan", "type": "f"}'
*
* @example
* // Error case (throws exception)
* evalRaw("invalid syntax") // throws Error: "Parse error message"
*/
evalRaw(equation) {
this._ensureModuleReady()

try {
this._validateEquationInput(equation)

console.log(`🧮 JS RAW: Evaluating equation: "${equation}"`)

const jsonResult = this.module.eval_equation(equation)
console.log(`🧮 JS RAW: Raw result from C++: ${jsonResult}`)

// Parse JSON only to check for errors and throw them as exceptions
const parsedResult = JSON.parse(jsonResult)

if (parsedResult.error) {
console.log(`❌ JS RAW: Equation evaluation error: ${parsedResult.error}`)
throw new Error(parsedResult.error)
}

console.log(`✅ JS RAW: Returning raw JSON result: ${jsonResult}`)

// Return the raw JSON string from C++ for platform consistency
return jsonResult
} catch (error) {
console.error('❌ Error in evalRaw:', error.message || error)
throw error
}
}

/**
* Get information about supported equation types and functions
* @returns {Object} Information about supported functions and operators
Expand Down Expand Up @@ -305,6 +368,60 @@ class Parsec {
return results
}

/**
* Run comprehensive tests of both eval() and evalRaw() functions
* @returns {Object} Test results with success/failure information for both functions
*/
runComprehensiveTestsBoth() {
this._ensureModuleReady()

console.log('🧪 Running comprehensive tests for both eval() and evalRaw()...')

const evalResults = this._createTestResultsContainer()
const evalRawResults = this._createTestResultsContainer()
const testCases = this._getTestCases()

// Test eval() function
console.log('🧪 Testing eval() function...')
for (const testCase of testCases) {
this._runSingleTest(testCase, evalResults)
}

// Test evalRaw() function
console.log('🧪 Testing evalRaw() function...')
for (const testCase of testCases) {
this._runSingleTestRaw(testCase, evalRawResults)
}

const totalResults = {
eval: {
passed: evalResults.passed,
failed: evalResults.failed,
tests: evalResults.tests,
errors: evalResults.errors
},
evalRaw: {
passed: evalRawResults.passed,
failed: evalRawResults.failed,
tests: evalRawResults.tests,
errors: evalRawResults.errors
},
summary: {
totalPassed: evalResults.passed + evalRawResults.passed,
totalFailed: evalResults.failed + evalRawResults.failed,
evalSuccess: evalResults.failed === 0,
evalRawSuccess: evalRawResults.failed === 0,
bothSuccess: evalResults.failed === 0 && evalRawResults.failed === 0
}
}

console.log(`🧪 eval() results: ${evalResults.passed} passed, ${evalResults.failed} failed`)
console.log(`🧪 evalRaw() results: ${evalRawResults.passed} passed, ${evalRawResults.failed} failed`)
console.log(`🧪 Overall: ${totalResults.summary.totalPassed} passed, ${totalResults.summary.totalFailed} failed`)

return totalResults
}

_createTestResultsContainer() {
return {
passed: 0,
Expand Down Expand Up @@ -363,6 +480,21 @@ class Parsec {
}
}

_runSingleTestRaw(testCase, results) {
try {
const jsonResult = this.evalRaw(testCase.equation)
const parsedResult = JSON.parse(jsonResult)

// For evalRaw, we expect the parsed 'val' field to match the expected value
const testResult = this._createTestResultRaw(testCase, jsonResult, parsedResult)

this._evaluateTestResultRaw(testResult, testCase, parsedResult)
this._recordTestResult(testResult, results)
} catch (error) {
this._handleTestError(testCase, error, results)
}
}

_createTestResult(testCase, result) {
return {
equation: testCase.equation,
Expand All @@ -373,6 +505,18 @@ class Parsec {
}
}

_createTestResultRaw(testCase, jsonResult, parsedResult) {
return {
equation: testCase.equation,
description: `${testCase.description} (RAW)`,
expected: testCase.expected,
actual: parsedResult.val,
actualJson: jsonResult,
actualType: parsedResult.type,
passed: false,
}
}

_evaluateTestResult(testResult, testCase, result) {
// With the new API, result is the direct value, not an object
const actualValue = result.toString()
Expand All @@ -383,6 +527,27 @@ class Parsec {
: this._compareValues(actualValue, expectedValue)
}

_evaluateTestResultRaw(testResult, testCase, parsedResult) {
// For evalRaw, we compare the 'val' field from the parsed JSON
const actualValue = parsedResult.val.toString()
const expectedValue = testCase.expected.toString()

// Also verify the JSON structure is correct
const hasValidStructure =
parsedResult.hasOwnProperty('val') &&
parsedResult.hasOwnProperty('type') &&
!parsedResult.hasOwnProperty('error')

if (!hasValidStructure) {
testResult.passed = false
return
}

testResult.passed = testCase.allowBooleanString
? this._compareBooleanValues(actualValue, expectedValue)
: this._compareValues(actualValue, expectedValue)
}

_compareBooleanValues(actualValue, expectedValue) {
return (
actualValue.toLowerCase() === expectedValue.toLowerCase() ||
Expand Down
Loading