Decidí probar y hacer un tokenizador de idioma (ni siquiera sé si esa es una palabra real) e hice alrededor de 4 tokens que tokenizaron con éxito un programa completo con saltos de línea y espacios múltiples, etc., pero comencé desde cero y me estoy encontrando un problema; Actualmente tengo dos tokens, int y variableSet . El programa que se lee tiene el contenido de 1 sv 1 2 como solo una prueba, y el tokenizador devuelve una matriz de int, int, int, int con sv que tiene un valor de 1 .
const code = `1 sv 1 2` var validTokens = require("./tokens"); // just an object with the structure tokenName: RegExp object function reverseTokenSearch(regex){ for (const [index, [key, value]] of Object.entries(Object.entries(validTokens))) { if (value === regex){ return key; } } return false; } function throughTokens (code,lastidx=0) { for (const tokentype in validTokens){ // loop through all of the valid tokens validTokens[tokentype].lastIndex = lastidx; const searchresult = validTokens[tokentype] const tokenresult = searchresult.exec(code.toString()); if (tokenresult) { return [searchresult, tokenresult[0], tokenresult.index, lastidx+tokenresult[0].length+1, tokenresult.groups] } } } function resetIndexes (){ for (const tt in validTokens){ validTokens[tt].lastidx = 0; } } resetIndexes(); var lst = 0 var tokens = [] var res = 1; console.log("\ntokenizer; original input:\n"+code+"\n"); while (lst !== undefined && lst !== null){ if (lst > code.length){ console.error("Fatal error: tokenizer over-reached program length.") process.exit(1) } const res = throughTokens(code,lst); if(res){ console.log(res,lst) const current = [] current[0] = reverseTokenSearch(res[0]) current[1] = res[1] const currentidx = 2 for (const x in res[4]) { current[currentidx] = x; } tokens.push(current) lst = res[3] } else { lst = null } } console.log(tokens) // What outputs: /* tokenizer; original input: 1 sv 1 2 [ /\d+/g { lastidx: 0 }, '1', 0, 2, undefined ] 0 [ /\d+/g { lastidx: 0 }, '1', 5, 4, undefined ] 2 [ /\d+/g { lastidx: 0 }, '1', 5, 6, undefined ] 4 [ /\d+/g { lastidx: 0 }, '2', 7, 8, undefined ] 6 [ [ 'int', '1' ], [ 'int', '1' ], [ 'int', '1' ], [ 'int', '2' ] ] */Creo que se debe al orden de la matriz, pero no tengo ni idea de por dónde empezar a arreglarlo y agradecería mucho que me empujaran en la dirección correcta. (editar): Intenté eliminar el indicador "g" en el objeto RegExp y todo lo que hizo fue romper el programa en un bucle infinito.
El problema es que está asumiendo silenciosamente que cada coincidencia encontrada por la expresión regular comenzará en lastidx , lo que no siempre es así. Si registra tokenresult y lastidx antes de regresar de throughTokens , verá:
0 [ '1', index: 0, input: '1 sv 1 2', groups: undefined ] 2 [ '1', index: 5, input: '1 sv 1 2', groups: undefined ] 4 [ '1', index: 5, input: '1 sv 1 2', groups: undefined ] 6 [ '2', index: 7, input: '1 sv 1 2', groups: undefined ] En la segunda iteración, la coincidencia está en el índice 5, pero asume que está en el índice 2, lo cual no es así (por lo que también incrementa incorrectamente lastidx a 4). También al final de throughTokens asume que cada coincidencia va seguida de un espacio, lo que también es incorrecto para el último token.
La forma más sencilla de arreglar este código es reemplazar
//if (tokenresult) { // replace in throughTokens with below if (tokenresult && tokenresult.index === lastidx) {para asegurarse de que está haciendo coincidir en el lugar correcto y luego en el ciclo principal
//while (lst !== undefined && lst !== null){ // replace with below while (lst !== undefined && lst !== null && lst < code.length){para manejar el final de la entrada correctamente.
Con estos cambios, las impresiones que agregamos anteriormente serán
0 [ '1', index: 0, input: '1 sv 1 2', groups: undefined ] 2 [ 'sv', index: 2, input: '1 sv 1 2', groups: undefined ] 5 [ '1', index: 5, input: '1 sv 1 2', groups: undefined ] 7 [ '2', index: 7, input: '1 sv 1 2', groups: undefined ]que es correcto y la salida sería
[ [ 'int', '1' ], [ 'variableSet', 'sv' ], [ 'int', '1' ], [ 'int', '2' ] ]Hay muchos otros problemas lógicos y programáticos con este código en los que no entraré, pero mi consejo es revisar cada parte del código y comprender qué hace y si se puede hacer de una manera más sencilla.
En un nivel general, en lugar de devolver una matriz con datos [d1, d2, d3, ...] devuelve un objeto con propiedades con nombre { result: d1, index: d2, ... } . Entonces es mucho más fácil que otra persona entienda su código. También vaya a través de nombres de métodos.
En lo que respecta a este enfoque, si sabe que habrá un espacio después de cada token, extraiga solo el token actual y envíelo a throughToken . Luego puede hacer que esa función sea más eficiente y robusta contra errores.