POLITICA DE COOKIES

Q2BSTUDIO.COM utiliza cookies técnicas, analíticas, de sesión y de publicidad con la finalidad de prestar un mejor servicio. No obstante, necesitamos su consentimiento explícito para poder utilizarlas. Así mismo puede cambiar la configuración de las cookies u obtener más información aquí .

Crea un parser de expresiones matemáticas en JavaScript

## Parser de expresiones matemáticas en JavaScript: guía y ejemplo práctico function parseExpressionString(input){ const T_NUMBER = 1, T_PLUS = 2, T_MINUS = 3, T_MUL = 4, T_DIV = 5, T_POW = 6, T_LP = 7, T_RP = 8, T_COMMA = 9, T_IDENT = 10, T_EOF = 11; function tokenize(s){ const tokens = [], len = s.length; let i = 0; while (i < len){ const c = s.charCodeAt(i); if (c <= 32){ i++; continue; } // whitespace // number if ((c >= 48 && c <= 57) || c === 46){ let j = i, dot = false; while (j < len){ const cc = s.charCodeAt(j); if (cc === 46){ if (dot) break; dot = true; j++; continue; } if (cc >= 48 && cc <= 57){ j++; continue; } break; } const val = Number(s.substring(i, j)); tokens.push({ type: T_NUMBER, value: val }); i = j; continue; } // identifier if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95){ let j = i; while (j < len){ const cc = s.charCodeAt(j); if ((cc >= 65 && cc <= 90) || (cc >= 97 && cc <= 122) || (cc >= 48 && cc <= 57) || cc === 95) j++; else break; } const name = s.substring(i, j); tokens.push({ type: T_IDENT, value: name }); i = j; continue; } // operators and parens if (c === 43){ tokens.push({ type: T_PLUS }); i++; continue; } // + if (c === 45){ tokens.push({ type: T_MINUS }); i++; continue; } // - if (c === 42){ tokens.push({ type: T_MUL }); i++; continue; } // * if (c === 47){ tokens.push({ type: T_DIV }); i++; continue; } // / if (c === 94){ tokens.push({ type: T_POW }); i++; continue; } // ^ if (c === 40){ tokens.push({ type: T_LP }); i++; continue; } // ( if (c === 41){ tokens.push({ type: T_RP }); i++; continue; } // ) if (c === 44){ tokens.push({ type: T_COMMA }); i++; continue; } // , throw 100; } tokens.push({ type: T_EOF }); return tokens; } const tokens = tokenize(input); let p = 0; const current = () => tokens[p]; const advance = () => { if (p < tokens.length) p++; }; const expect = t => { if (current().type !== t) throw 101; advance(); }; const KIND_NUMBER = 1, KIND_UNARY = 2, KIND_BINARY = 3, KIND_CALL = 4; function parseExpression(){ let node = parseTerm(); while (true){ const t = current().type; if (t === T_PLUS || t === T_MINUS){ const op = t; advance(); const right = parseTerm(); node = { kind: KIND_BINARY, op, left: node, right }; } else break; } return node; } function parseTerm(){ let node = parsePower(); while (true){ const t = current().type; if (t === T_MUL || t === T_DIV){ const op = t; advance(); const right = parsePower(); node = { kind: KIND_BINARY, op, left: node, right }; } else break; } return node; } function parsePower(){ let left = parseUnary(); if (current().type === T_POW){ const op = current().type; advance(); const right = parsePower(); return { kind: KIND_BINARY, op, left, right }; } return left; } function parseUnary(){ const t = current().type; if (t === T_PLUS || t === T_MINUS){ const op = t; advance(); const arg = parseUnary(); return { kind: KIND_UNARY, op, arg }; } return parsePrimary(); } function parsePrimary(){ const t = current().type; if (t === T_NUMBER){ const val = current().value; advance(); return { kind: KIND_NUMBER, value: val }; } if (t === T_IDENT){ const name = current().value; advance(); if (current().type === T_LP){ advance(); const args = []; if (current().type !== T_RP){ args.push(parseExpression()); while (current().type === T_COMMA){ advance(); args.push(parseExpression()); } } expect(T_RP); return { kind: KIND_CALL, name, args }; } else { const lower = name.toLowerCase(); const piName = String.fromCharCode(112,105); if (lower === piName) return { kind: KIND_NUMBER, value: Math.PI }; const eName = String.fromCharCode(101); if (lower === eName) return { kind: KIND_NUMBER, value: Math.E }; throw 102; } } if (t === T_LP){ advance(); const expr = parseExpression(); expect(T_RP); return expr; } throw 103; } const ast = parseExpression(); if (current().type !== T_EOF) throw 104; function evalNode(n){ switch (n.kind){ case 1: return n.value; case 2:{ const v = evalNode(n.arg); if (n.op === T_PLUS) return +v; if (n.op === T_MINUS) return -v; throw 105; } case 3:{ const a = evalNode(n.left); const b = evalNode(n.right); switch (n.op){ case T_PLUS: return a + b; case T_MINUS: return a - b; case T_MUL: return a * b; case T_DIV: return a / b; case T_POW: return Math.pow(a, b); default: throw 106; } } case 4:{ const f = resolveFunction(n.name); if (!f) throw 107; const argVals = n.args.map(evalNode); return f.apply(null, argVals); } default: throw 108; } } function resolveFunction(nm){ const lower = nm.toLowerCase(); const sSin = String.fromCharCode(115,105,110); const sCos = String.fromCharCode(99,111,115); const sTan = String.fromCharCode(116,97,110); const sSqrt = String.fromCharCode(115,113,114,116); const sLog = String.fromCharCode(108,111,103); const sAbs = String.fromCharCode(97,98,115); const sExp = String.fromCharCode(101,120,112); if (lower === sSin) return Math.sin; if (lower === sCos) return Math.cos; if (lower === sTan) return Math.tan; if (lower === sSqrt) return Math.sqrt; if (lower === sLog) return Math.log; if (lower === sAbs) return Math.abs; if (lower === sExp) return Math.exp; return undefined; } const result = evalNode(ast); return result; }

Publicado el 31/08/2025

Crear un analizador de expresiones matemáticas en JavaScript es un ejercicio ideal para dominar gramáticas y evaluación segura. Sobre todo lo explorado en artículos previos, aquí damos un salto cualitativo usando el algoritmo Earley para analizar y evaluar expresiones con precisión, soportando precedencia, asociatividad, paréntesis y signos unarios. En Q2BSTUDIO, empresa de desarrollo de software, combinamos rigor académico con entrega industrial para construir aplicaciones a medida y software a medida robusto, listos para integrarse en tu plataforma web o backend.

Por qué Earley para un parser matemático en JavaScript

El algoritmo Earley es generalista y eficiente para gramáticas libres de contexto, maneja ambigüedades y se adapta bien cuando la gramática crece con nuevas funciones o operadores. A diferencia de enfoques recursivo descendentes, reduce el trabajo manual de backtracking y permite añadir reglas sin reescribir todo el analizador.

Gramática base de expresiones

Usaremos una gramática clásica con precedencia y asociatividad correcta

Expr -> Expr + Term | Expr - Term | Term

Term -> Term * Factor | Term / Factor | Factor

Factor -> Base ^ Factor | Base

Base -> numero | ( Expr ) | - Base

Esta forma captura el exponente como asociativo a la derecha, respeta el signo unario y permite anidar paréntesis sin límites. Añadir funciones como sin, cos o max resulta tan simple como extender Base a nombre ( listaArgs ).

Fase de tokenización

Antes de alimentar el algoritmo, dividimos la entrada en tokens numero, operador y paréntesis. Un tokenizador simple identifica secuencias de dígitos con punto decimal opcional, ignora espacios y produce símbolos para + - * / ^ ( ). Separar tokenización del parseo evita ambigüedades y simplifica errores.

Cómo trabaja Earley paso a paso

El algoritmo mantiene un chart, una lista de columnas, una por posición del input. Cada columna almacena estados con regla, punto de progreso y origen

Predicción añade estados cuando el punto espera un no terminal

Escaneo avanza si el token coincide con el terminal esperado

Completado propaga avances a los estados que esperaban el no terminal recién completado

Para evaluación, cada estado guarda también nodos del AST. Cuando completamos una regla, construimos un nodo con sus hijos. Así, al terminar con Expr desde la posición cero hasta el final, ya tenemos un árbol evaluable incluso si hubo varias derivaciones posibles. En gramáticas matemáticas bien definidas, la derivación es única.

Construcción y evaluación del AST

El AST usa nodos para operadores binarios y unarios, números y agrupaciones. La evaluación es una pasada postorden que aplica operaciones con control de errores y límites de precisión. Mejoras prácticas

Plegado de constantes para optimizar subárboles numéricos

Detección de división entre cero y rangos seguros

Soporte de variables a través de un entorno clave valor

Funciones con tabla de símbolos, por ejemplo sin, cos, pow y extensibles

Manejo de errores claro

Si no existe un estado completo Expr que cubra toda la entrada, reportamos el token y la posición esperada, sugiriendo por ejemplo falta de paréntesis o operador duplicado. Con el chart es sencillo proponer correcciones cercanas.

Extensiones útiles

Soporte de separadores de miles y locales

Funciones personalizadas con validación de aridad

Operadores personalizados como mod o operador ternario con gramática extendida

Integración en UI con resaltado de errores en tiempo real

Rendimiento y buenas prácticas

Earley rinde muy bien en gramáticas no ambiguas como esta. Aun así, limita retrocesos con memorización de nodos, reusa objetos y corta evaluación cuando detectes errores tempranos. Para cargas intensivas, encapsula el parser en un worker y añade caché por expresión normalizada.

Cómo integrarlo en tu arquitectura

En frontend, encapsula el analizador en un módulo con una API parse y evaluate. En backend Node, expón un microservicio que reciba expresiones, variables y devuelva el resultado con trazas del AST si se requiere auditoría. Donde haya necesidad de cálculos seguros, desde hojas de cálculo web hasta reglas de negocio, este enfoque elimina vulnerabilidades por eval y asegura precisión.

Q2BSTUDIO, tu partner tecnológico

En Q2BSTUDIO diseñamos e implantamos aplicaciones a medida y software a medida que incluyen componentes críticos como parsers, motores de reglas y validadores. Nuestro equipo también impulsa inteligencia artificial, ia para empresas y agentes IA, fortalece la ciberseguridad con auditorías y pentesting, y despliega plataformas escalables con servicios cloud aws y azure. Integramos analítica avanzada mediante servicios inteligencia de negocio y cuadros de mando con power bi para convertir datos en decisiones.

Si buscas un equipo que construya soluciones sólidas de extremo a extremo, desde el parser hasta la interfaz y el despliegue, visita nuestra página de aplicaciones a medida y descubre cómo aceleramos tu roadmap con calidad y velocidad.

Conclusión

Crear un parser de expresiones matemáticas en JavaScript con Earley te da una base extensible, segura y mantenible. Es perfecto para motores de cálculo, validación de reglas y escenarios donde la confiabilidad es clave. En Q2BSTUDIO podemos integrarlo en tu producto, optimizarlo para producción y conectarlo con analítica, seguridad y nube, todo dentro de una solución de software a medida lista para escalar.

Fin del artículo, inicio de la diversión
Construyendo software juntos

Dando vida a tus ideas desde 2008

Diseñamos aplicaciones móviles y de escritorio innovadoras que cumplen con tus requisitos específicos y mejoran la eficiencia operativa.
Más info
Cuéntanos tu visión
Sea cual sea el alcance, podemos convertir tu idea en realidad. Envíanosla y charlemos sobre tu proyecto o una colaboración futura.
Contáctanos
artículos destacados
Live Chat
Enviado correctamente.

Gracias por confiar en Q2BStudio