{"id":24564,"date":"2026-09-16T14:44:12","date_gmt":"2026-09-16T14:44:12","guid":{"rendered":"https:\/\/www.mailgun.com\/blog\/como-creamos-un-parser-inspirado-en-lucene-en-go\/"},"modified":"2026-09-16T14:44:12","modified_gmt":"2026-09-16T14:44:12","slug":"como-creamos-un-parser-inspirado-en-lucene-en-go","status":"publish","type":"blog","link":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/","title":{"rendered":"C\u00f3mo creamos un parser inspirado en Lucene en Go"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En Mailgun tenemos numerosos sistemas que generan much\u00edsimos eventos cada hora del d\u00eda. Es un n\u00famero tan grande que resulta imposible para un equipo humano clasificar los resultados de ElasticSearch y esperar resultados coherentes o mantener la cordura.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como muchas empresas, <b>seguimos necesitando encontrar una manera de lidiar con todos los datos<\/b>. Esto supone una tarea considerable: crear un sistema que permita a nuestros clientes internos reaccionar a los eventos en tiempo real.<\/p>\n\n\n\r\n    <nav data-content-type=\"longform\" class=\"toc-block longform-spacings px-5 py-6 px-md-6 px-lg-7 py-md-7 bg-light fs-sm rounded-lg\" aria-labelledby=\"toc-title-7652\"><p class=\"h5 m-0\" id=\"toc-title-7652\">\u00cdndice<\/p><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">01<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#lluvia-de-ideas\">Lluvia de ideas<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">02<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#hablemos-de-gramatica\">Hablemos de gram\u00e1tica<\/a><a class=\"scrollme link-body-color\" href=\"#gramaticas-libres-de-contexto-y-gramaticas-de-expresiones-de-analisis-sintactico\">Gram\u00e1ticas libres de contexto y gram\u00e1ticas de expresiones de an\u00e1lisis sint\u00e1ctico<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">03<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#alzando-el-vuelo\">Alzando el vuelo<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">04<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#como-una-hoja-al-viento\">Como una hoja al viento<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">05<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#genial-pero-ahora-que\">Genial, pero \u00bfahora qu\u00e9?<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">06<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#consideraciones\">Consideraciones<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">07<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#proximos-pasos\">Pr\u00f3ximos pasos<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">08<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#pros-y-contras\">Pros y contras<\/a><a class=\"scrollme link-body-color\" href=\"#pros\">Pros:<\/a><a class=\"scrollme link-body-color\" href=\"#contras\">Contras:<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">09<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#el-final-por-fin\">El final, por fin<\/a><\/div><\/div><\/nav>\n\n\n<p class=\"wp-block-paragraph\">Tradicionalmente, las opciones para resolver este tipo de problema incluyen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Introducir los datos en una base de datos y luego ejecutar consultas, a menudo en un intervalo de tiempo fijo<\/li>\n\n\n\n<li>Soluciones MapReduce como Hadoop<\/li>\n\n\n\n<li>Mechanical Turk \u00a0<\/li>\n\n\n\n<li>Filtros en tiempo real.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. Nos centramos en construir un sistema que permita b\u00e1sicamente a cualquier persona de los equipos de ingenier\u00eda y asistencia <b>escribir reglas que reaccionen y realicen acciones en su nombre.<\/b><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Lluvia de ideas<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Al dise\u00f1ar este sistema, intentamos crear algo que diera a todo el mundo las herramientas para iterar r\u00e1pidamente y responder a las amenazas de nuestro sistema, y que cumpliera con las siguientes reglas:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Los usuarios deber\u00edan poder a\u00f1adir sus propias reglas a voluntad<\/li>\n\n\n\n<li>El lenguaje de dominio espec\u00edfico (en adelante, DSL) deber\u00eda ser lo m\u00e1s sencillo posible e idealmente parecerse a algo que ya conocen.<\/li>\n\n\n\n<li>La gente deber\u00eda poder probar sus reglas con datos hist\u00f3ricos antes de aplicarlas.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">A nivel interno usamos Kibana, <b>as\u00ed que lo ideal era algo que pudiera probarse con \u00e9l para cumplir el punto n\u00famero 3<\/b>. Esto nos lleva a una conclusi\u00f3n sencilla: \u00a1deber\u00edamos escribir un parser de DSL inspirado en Lucene!\u00a0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La primera iteraci\u00f3n del sistema se bas\u00f3 en expresiones regulares y mucho an\u00e1lisis car\u00e1cter por car\u00e1cter. Cumpl\u00eda su funci\u00f3n, pero las iteraciones posteriores aumentaron r\u00e1pidamente en complejidad. Si alguna vez has hecho un proyecto grande de an\u00e1lisis de cadenas (strings), sabes a qu\u00e9 nos referimos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">De cara al futuro, <b>sab\u00edamos que ten\u00edamos que crear algo sobre lo que se pudiera iterar r\u00e1pidamente<\/b> y, a ser posible, que se ajustara a un est\u00e1ndar conocido.<\/p>\n\n\n\r\n    <aside data-content-type=\"longform\"  class=\"banner-block longform-spacings rounded-lg shadow-lg px-5 py-6 px-md-6 py-md-7 p-lg-7 bg-light\" data-theme=\"dark\" aria-labelledby=\"banner-block-block_cdfac0c762019e32a40e0f147f301a3d\">\r\n                    <p class=\"text-uppercase section-caption text-body-color\">\r\n                Reg\u00edstrate            <\/p>\r\n                            <p class=\"h4 text-accent fw-bold\" id=\"banner-block-block_cdfac0c762019e32a40e0f147f301a3d\">\r\n                Empezar es facil\u00edsimo. Y gratis.            <\/p>\r\n                            <div class=\"mb-0 text-body-color\">\r\n                                    <div class=\"content-body mb-0 text-body-color\">Descubre todo lo que puedes lograr con la mejor plataforma de entrega de emails del mundo.<\/div>\r\n                            <\/div>\r\n        \r\n        <div class=\"text-start mt-5\"><a href=\"https:\/\/signup.mailgun.com\/new\/signup\"  class=\"btn btn-secondary\" >Get started<\/a><\/div>\r\n    <\/aside>\r\n\r\n\n\n\n<h2 class=\"wp-block-heading\">Hablemos de gram\u00e1tica<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quienes hemos hecho cursos universitarios de teor\u00eda de aut\u00f3matas y similares, probablemente recordemos las gram\u00e1ticas en el contexto de los lenguajes de programaci\u00f3n. Adem\u00e1s, es un hecho que parte del p\u00fablico tendr\u00e1 mucha m\u00e1s experiencia en esta materia que quien escribe estas l\u00edneas, por lo que cualquier explicaci\u00f3n en profundidad deber\u00e1 consultarse en tu motor de b\u00fasqueda de confianza.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sigue siendo \u00fatil hablar brevemente sobre qu\u00e9 compone una gram\u00e1tica y por qu\u00e9 son \u00fatiles. Citando a Wikipedia, una <a href=\"https:\/\/en.wikipedia.org\/wiki\/Formal_grammar\" target=\"_tabs\" rel=\"noopener noreferrer\">gram\u00e1tica formal<\/a> es \u201c&#8230;un conjunto de reglas de producci\u00f3n de cadenas (strings) en un lenguaje formal. <b>Las reglas describen c\u00f3mo formar cadenas a partir del alfabeto del lenguaje que sean v\u00e1lidas seg\u00fan su sintaxis<\/b>. Una gram\u00e1tica no describe el significado de las cadenas ni lo que se puede hacer con ellas en un contexto determinado; solo su forma\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A riesgo de simplificar demasiado, nos permite tomar una cadena como \u201c\u00a1Tienes un email!\u201d y desglosarla en tokens que podemos analizar sem\u00e1nticamente. En otras palabras, si nuestra gram\u00e1tica est\u00e1 bien definida, podemos escribir c\u00f3digo adicional para determinar el significado de esa cadena.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La historia y los tipos de gram\u00e1ticas pueden llenar montones de libros, por lo que van mucho m\u00e1s all\u00e1 del alcance de un art\u00edculo de blog. En su lugar, nos centraremos en las <b>gram\u00e1ticas libres de contexto<\/b> y, m\u00e1s espec\u00edficamente, en las gram\u00e1ticas de expresiones de an\u00e1lisis sint\u00e1ctico.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gram\u00e1ticas libres de contexto y gram\u00e1ticas de expresiones de an\u00e1lisis sint\u00e1ctico<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Citando de nuevo a Wikipedia, una<a href=\"https:\/\/en.m.wikipedia.org\/wiki\/Context-free_grammar\" target=\"_tabs\" rel=\"noopener noreferrer\"> gram\u00e1tica libre de contexto<\/a> es \u201cun determinado tipo de gram\u00e1tica formal: un conjunto de reglas de producci\u00f3n que describen todas las cadenas posibles en un lenguaje formal dado. Las reglas de producci\u00f3n son simples reemplazos\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dicho de otra forma, una gram\u00e1tica libre de contexto <b>define un grafo que a su vez define c\u00f3mo analizar sint\u00e1cticamente un lenguaje<\/b>. Como hemos mencionado antes, <i>no <\/i>nos dice c\u00f3mo entender el lenguaje. Tambi\u00e9n cabe mencionar, como veremos m\u00e1s adelante, que el grafo puede tener ciclos, pero acaba terminando.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Las gram\u00e1ticas libres de contexto tienen una especie de prima en la<a href=\"https:\/\/en.m.wikipedia.org\/wiki\/Parsing_expression_grammar\" target=\"_tabs\" rel=\"noopener noreferrer\"> gram\u00e1tica de expresiones de an\u00e1lisis sint\u00e1ctico<\/a>, o PEG (por sus siglas en ingl\u00e9s). Se <i>parecen<\/i> mucho a una gram\u00e1tica libre de contexto, o CFG, pero incluyen una distinci\u00f3n muy \u00fatil: ante una opci\u00f3n ambigua, la PEG siempre elegir\u00e1 la primera regla de producci\u00f3n definida. En cambio, la CFG deja la opci\u00f3n ambigua, y \u00bfpor qu\u00e9 lidiar con la ambig\u00fcedad cuando no tenemos que hacerlo?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lo que esto significa realmente es que <b>resulta m\u00e1s f\u00e1cil escribir herramientas para definir las PEG<\/b>. Por suerte, mucha gente ya lo ha hecho por nosotros.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Alzando el vuelo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Tras un poco de investigaci\u00f3n, nos decidimos por <a href=\"https:\/\/github.com\/mna\/pigeon\" target=\"_tabs\" rel=\"noopener noreferrer\">Pigeon<\/a> como la base de nuestra PEG. Pigeon sigue el mismo paradigma que muchas herramientas de Go al generar c\u00f3digo que se compila junto con tu programa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Su uso es bastante f\u00e1cil: defines tanto la gram\u00e1tica como el c\u00f3digo de Go para manejar cada una de las reglas en el mismo paquete, y simplemente llamas a Parse() en el parser generado. El desaf\u00edo consiste en escribir controladores para cada una de esas reglas. Al igual que las gram\u00e1ticas, el tema de la compilaci\u00f3n podr\u00eda llenar una biblioteca entera de libros. Por suerte para nosotros, <b>escribir un int\u00e9rprete es menos complejo<\/b> y el c\u00f3digo resultante es lo bastante r\u00e1pido para nuestras necesidades.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Adem\u00e1s, la sintaxis intenta alinearse estrechamente con la propia sintaxis de Go, lo que lo hace m\u00e1s f\u00e1cil a la hora de escribir tu PEG junto a tu programa de Go que utilice la gram\u00e1tica.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Como una hoja al viento<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Supongamos que tienes un evento generado al enviar un email, codificado en JSON, parecido al siguiente:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>{<br>\n\u00a0\"event\": \"sent\",<br>\n\u00a0\"subject\": \"A special offer just for you!\",<br>\n\u00a0\"account\": \"12345\",<br>\n}<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hay un par de maneras de ver este evento: podr\u00edas decidir que se trata de un email inofensivo, o podr\u00edas decir \u201cEsto podr\u00eda ser spam, pero no hay suficiente informaci\u00f3n para decidirlo\u201d. Este es un problema muy com\u00fan en nuestro sistema, y siempre requiere much\u00edsimos datos agregados para llegar a una conclusi\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As\u00ed pues, vamos a escribir una regla que coincida con el evento anterior:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    event:&quot;sent&quot; AND subject:&quot;A special offer just for you!&quot;\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Es bastante f\u00e1cil de analizar, y lo que es mejor, \u00a1tambi\u00e9n se puede comprobar en Kibana!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ahora vamos a repasar <b>la construcci\u00f3n de una PEG muy sencilla <\/b>que coincidir\u00eda con esta regla. Implementaremos cada regla de producci\u00f3n de una en una y explicaremos qu\u00e9 hace. Los nombres de las reglas normalmente siguen<a href=\"https:\/\/en.wikipedia.org\/wiki\/Pascal_(programming_language)#Language_constructs\" target=\"_tabs\" rel=\"noopener noreferrer\"> PascalCase<\/a> , adem\u00e1s de permitir n\u00fameros y guiones bajos. Cada regla adopta la forma:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    RuleName &lt;- \r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Ten en cuenta que, en realidad, hay varios caracteres legales para el operador de definici\u00f3n de reglas, pero nos gusta &lt;- porque es f\u00e1cil de escribir y se parece m\u00e1s a las gram\u00e1ticas tal y como se definen acad\u00e9micamente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La primera regla de la gram\u00e1tica se trata como el punto de entrada:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Input &lt;- Term !.\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Nuestra regla <code>Input<\/code> dice \u201cHaz que coincida toda la cadena de entrada\u201d con <code>!.<\/code> que significa \u201cHaz que coincida con el final del archivo\u201d. Esto pasar\u00e1 la cadena de entrada completa a una regla llamada Term que tiene el siguiente aspecto:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Term &lt;- Variable AndVar*\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Esto pasa el flujo de control a la regla de producci\u00f3n Variable:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Variable &lt;-  FieldChars+ _ &quot;:&quot; _ Value\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Ahora la cosa se vuelve un poco m\u00e1s compleja. Primero, hacemos coincidir de forma codiciosa todos los caracteres hasta que se cumpla la regla FieldChars. F\u00edjate en el <code>+<\/code> del final. La sintaxis PEG comparte mucho con las expresiones regulares, as\u00ed que esto dice \u201cHaz que la regla <code>FieldChars<\/code> coincida una o m\u00e1s veces\u201d.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    FieldChars &lt;- [a-z]\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">De nuevo, si est\u00e1s familiarizado con la sintaxis regex esto deber\u00eda ser sencillo: simplemente haz coincidir cualquier car\u00e1cter individual entre \u201ca\u201d y \u201cz\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Volviendo a Variable, la siguiente parte <code>_<\/code> es una regla que dice \u201chaz que coincida con cualquier car\u00e1cter de espacio en blanco\u201d. Ten en cuenta que <code>_<\/code> es un identificador completamente v\u00e1lido para una regla de producci\u00f3n, y escribir menos suele ser algo bueno.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Java                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-Java\">\r\n                                <code id=\"code_0_content\" class=\"language-Java\">\r\n\r\n                                    _ &quot;whitespace&quot; &lt;- [ \\n\\t\\r]*\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed hay un par de cosas a tener en cuenta:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>La cadena \u201cwhitespace\u201d es el \u201cnombre descriptivo\u201d y existe para fines de documentaci\u00f3n y para tu futura salud mental.<\/li>\n\n\n\n<li>Al igual que con la sintaxis de expresiones regulares, esto coincidir\u00e1 con cualquier forma de espacio en blanco, saltos de l\u00ednea, tabulaciones o retornos de carro.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">La siguiente parte de la regla Variable coincide con el literal de cadena \u201cAND\u201d. No puede ser m\u00e1s f\u00e1cil que eso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A continuaci\u00f3n, tenemos otra regla de espacio en blanco, y finalmente la regla Value.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Value &lt;- &#039;&quot;&#039; ValueChars* &#039;&quot;&#039;\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Esta regla coincide con el literal de cadena <code>'\"'<\/code>, con cero o m\u00e1s reglas ValueChars y, por \u00faltimo, con otra comilla.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>ValueChars<\/code> tiene el siguiente aspecto:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    How we built a Lucene-inspired parser in Go\r\n\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Estos <code>ValueChars<\/code> coincidir\u00e1n con el alfabeto en min\u00fasculas y may\u00fasculas, cualquier n\u00famero, espacios y el signo de exclamaci\u00f3n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00bfPor qu\u00e9 definimos Value de esta manera? Porque <b>la regla puede quitarnos las comillas dobles para que no tengamos que hacerlo nosotros<\/b>, y adem\u00e1s porque somos algo vagos. Sin embargo, es simplemente una comodidad que podr\u00edamos haber omitido en favor de incluir la forma de la regla de Value con la definici\u00f3n de la regla Variable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finalmente, <code>AndVar<\/code> deber\u00eda parecer bastante sencillo llegados a este punto. Observa que hace referencia a Term e implementa el ciclo mencionado anteriormente.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    ValueChars &lt;- [a-zA-Z0-9 !]\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">La definici\u00f3n completa tiene el siguiente aspecto.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Input &lt;- Term !.\r\nTerm &lt;- Variable AndVar*\r\nAndVar &lt;- _ &quot;AND&quot; _ Term\r\nVariable &lt;- FieldChars+ _ &quot;:&quot; _ Value\r\n_ &quot;whitespace&quot; &lt;- [ \\n\\t\\r]*\r\nValue &lt;- &#039;&quot;&#039; ValueChars &#039;&quot;&#039;\r\nFieldChars &lt;- [a-z]\r\nValueChars &lt;- [a-zA-Z0-9 !]\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<h2 class=\"wp-block-heading\">Genial, pero \u00bfahora qu\u00e9?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">El valor real de definir tu propia gram\u00e1tica surge cuando proporcionas implementaciones para las acciones de las reglas. Veamos la definici\u00f3n real de PEG:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    {\r\n package main\r\n\r\n import (\r\n   &quot;strings&quot;\r\n )\r\n\r\n type Node interface {\r\n   Evaluate(input Event) (bool, error)\r\n }\r\n\r\n type Term struct {\r\n   node Node\r\n }\r\n\r\n func (t *Term) Evaluate(input Event) (bool, error) {\r\n   return t.node.Evaluate(input)\r\n }\r\n\r\n type AndNode struct {\r\n   Nodes []Node\r\n }\r\n\r\n func (n *AndNode) Evaluate(input Event) (bool, error) {\r\n   for _, node := range n.Nodes {\r\n     matched, err := node.Evaluate(input)\r\n     if err != nil {\r\n       return false, err\r\n     }\r\n     if !matched {\r\n       return false, nil\r\n     }\r\n   }\r\n   return true, nil\r\n }\r\n\r\n type Variable struct {\r\n   Field string\r\n   Value string\r\n }\r\n\r\n func (v *Variable) Evaluate(input Event) (bool, error) {\r\n   fieldVal, ok := input[v.Field]\r\n   if !ok {\r\n     return false, fmt.Errorf(&quot;Field &#039;%s&#039; not present in the event&quot;, v.Field)\r\n   }\r\n\r\n   return fieldVal == v.Value, nil\r\n }\r\n\r\n func toString(label interface{}) string {\r\n   var sb strings.Builder\r\n   value := label.([]interface{})\r\n   for _, i := range(value) {\r\n     if i == nil {\r\n       continue\r\n     }\r\n     switch b := i.(type) {\r\n     case []byte:\r\n       sb.WriteByte(b[0])\r\n     case []interface{}:\r\n       s := toString(i)\r\n       sb.WriteString(s)\r\n     default:\r\n       fmt.Printf(&quot;She&#039;s dead, Jim %T %+v\\n&quot;, i, i)\r\n     }\r\n   }\r\n   return sb.String()\r\n }\r\n}\r\n\r\nInput &lt;- Term !.\r\n\r\nTerm &lt;- variable:Variable rest:AndVar* {\r\n andVars := rest.([]interface{})\r\n variables := make([]Node, 0, len(andVars))\r\n variables = append(variables, variable.(Node))\r\n for _, r := range(andVars) {\r\n      variables = append(variables, r.(Node))\r\n   }\r\n   return &amp;AndNode{Nodes: variables}, nil\r\n}\r\n\r\nAndVar &lt;- _ &quot;AND&quot; _ rightSide:Term {\r\n   return rightSide, nil\r\n}\r\n\u200b\r\nVariable &lt;- field:FieldChars+ _ &quot;:&quot; _ value:Value {\r\n   return &amp;Variable{Field: toString(field), Value: toString(value)}, nil\r\n}\r\n\r\nValue &lt;- &#039;&quot;&#039; value:ValueChars* &#039;&quot;&#039; {\r\n   return value, nil\r\n}\r\n\r\n_ &quot;whitespace&quot; &lt;- [ \\n\\t\\r]*\r\nFieldChars &lt;- [a-z]\r\nValueChars &lt;- [a-zA-Z0-9 !]\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Hay mucho que asimilar aqu\u00ed, as\u00ed que vamos a ir paso a paso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En la parte superior, tenemos una secci\u00f3n de c\u00f3digo rodeada de llaves. Esta es una convenci\u00f3n de Pigeon que toma el c\u00f3digo adjunto tal cual y lo inyecta en la salida final del c\u00f3digo generado. Ten en cuenta que puedes definir tipos relevantes para tu gram\u00e1tica aqu\u00ed por conveniencia, o puedes ponerlos en otro archivo, ya que Pigeon usar\u00e1 el nombre del paquete que declares aqu\u00ed. Todo lo que va despu\u00e9s es la definici\u00f3n de la propia gram\u00e1tica.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Llegados a este punto, probablemente te habr\u00e1s dado cuenta de que las reglas difieren un poco de c\u00f3mo las definimos arriba. Echemos un vistazo a <code>Term<\/code>.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    Term &lt;- variable:Variable rest:AndVar*\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Esta es casi nuestra definici\u00f3n de <code>Term<\/code> de antes, pero ahora la hemos hecho \u00fatil. Anteponer a una regla en la definici\u00f3n <code>name<\/code>: asigna el valor de retorno de esa regla a name y luego pasa <code>name<\/code> a la funci\u00f3n definida por la acci\u00f3n de tu regla cuando se genera el c\u00f3digo. Todo el c\u00f3digo entre las llaves <b>se convierte en una funci\u00f3n asociada a esa regla <\/b>y se llama cuando se cumple la regla:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    func (c *current) onTerm1(variable, rest interface{}) (interface{}, error) {\r\n andVars := rest.([]interface{})\r\n variables := make([]Node, 0, len(andVars))\r\n variables = append(variables, variable.(Node))\r\n for _, r := range andVars {\r\n   variables = append(variables, r.(Node))\r\n }\r\n return &amp;AndNode{Nodes: variables}, nil\r\n}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed podemos ver que Pigeon trabaja enteramente con tipos de interfaz vac\u00edos. Es genial para la flexibilidad, pero no tanto para escribir la implementaci\u00f3n, ya que tenemos que tenerlos en cuenta.<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    andVars := rest.([]interface{})\r\nvariables := make([]Node, 0, len(andVars))\r\nvariables = append(variables, variable.(Node))\r\nfor _, r := range andVars {\r\n variables = append(variables, r.(Node))\r\n}\r\nreturn &amp;AndNode{Nodes: variables}, nil\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Como hemos mencionado anteriormente, <b>podemos tener ciclos en las reglas<\/b>. Teniendo esto en cuenta, sabemos que es posible recibir un n\u00famero infinito de Terms mapeados a la variable <code>rest<\/code>. Pigeon lidia con esto entreg\u00e1ndonos un slice de interfaces vac\u00edas. Hemos creado una interfaz <code>Node<\/code> hacia la cual todo debe inferirse por tipo antes de que podamos usarlo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tras recorrer toda la lista de variables con el operador <code>AND<\/code>, creamos un <code>AndNode<\/code> que contiene cada uno de los Terms. La definici\u00f3n del AndNode se puede encontrar en la secci\u00f3n del literal de c\u00f3digo definida en la parte superior del PEG. Como implementador de la interfaz Node, define un m\u00e9todo Evaluate que establece que la expresi\u00f3n se eval\u00faa como verdadera (true) solo si todos los t\u00e9rminos individuales tambi\u00e9n se eval\u00faan como verdaderos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En este punto, el resto de la definici\u00f3n deber\u00eda cobrar sentido, as\u00ed que vamos a pasar a su ejecuci\u00f3n. El siguiente listado de c\u00f3digo muestra el an\u00e1lisis de nuestra regla, su uso y un par de ejemplos adicionales para mostrar fallos y la gesti\u00f3n de errores:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                PHP                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copia al portapapeles\" data-clipboard-success-label=\"\u00a1Copiado!\" data-clipboard-error-label=\"No se ha podido copiar\" data-tooltip=\"Copia al portapapeles\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copia al portapapeles\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/www.mailgun.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-PHP\">\r\n                                <code id=\"code_0_content\" class=\"language-PHP\">\r\n\r\n                                    package main\r\n\r\nimport (\r\n &quot;fmt&quot;\r\n)\r\n\r\ntype Event map[string]string\r\n\r\nfunc Match(rule string, event Event) (bool, error) {\r\n \/\/ Parse is generated by pigeon in this package\r\n tree, err := Parse(&quot;parsing&quot;, []byte(rule))\r\n if err != nil {\r\n   return false, err\r\n }\r\n\r\n \/\/ Yes, this is a bit ugly. It&#039;s a consequence of pigeon dealing in interface slices\r\n iface, ok := tree.([]interface{})\r\n if !ok {\r\n   return false, fmt.Errorf(&quot;Internal Error&quot;)\r\n }\r\n\r\n ast, ok := iface[0].(Node)\r\n if !ok {\r\n   return false, fmt.Errorf(&quot;Internal Error&quot;)\r\n }\r\n\r\n \/\/ ast is the tree generated by our supplementary Go defined by the production rules\r\n return ast.Evaluate(event)\r\n}\r\n\r\nfunc main() {\r\n  rules := []string{\r\n   &quot;event:\\&quot;sent\\&quot; AND subject:\\&quot;A special offer just for you!\\&quot;&quot;,\r\n   &quot;event:\\&quot;found\\&quot; AND subject:\\&quot;A special offer just for you!\\&quot;&quot;,\r\n   &quot;event:\\&quot;sent\\&quot; AND badfield:\\&quot;A special offer just for you!\\&quot;&quot;,\r\n  }\r\n\r\n  event := map[string]string{\r\n    &quot;event&quot;:   &quot;sent&quot;,\r\n    &quot;subject&quot;: &quot;A special offer just for you!&quot;,\r\n    &quot;account&quot;: &quot;12345&quot;,\r\n  }\r\n\r\n  for _, rule := range rules {\r\n    matched, err := Match(rule, Event(event))\r\n    if err != nil {\r\n      fmt.Printf(&quot;Rule &#039;%s&#039; failed with error &#039;%s&#039;\\n&quot;, rule, err.Error())\r\n    } else {\r\n      fmt.Printf(&quot;Rule &#039;%s&#039; matched: %t\\n&quot;, rule, matched)\r\n    }\r\n  }\r\n}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Solo tienes que ejecutar:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>~&gt; go get\u00a0<\/code><a href=\"https:\/\/github.com\/mna\/pigeon\"><code>github.com\/mna\/pigeon<\/code><\/a><code>\u00a0\u00a0 ~&gt; pigeon rules.peg &gt; rules.go \u00a0 ~&gt; go run . Rule 'event:\"sent\" AND subject:\"A special offer just for you!\"' matched: true Rule 'event:\"found\" AND subject:\"A special offer just for you!\"' matched: false Rule 'event:\"sent\" AND badfield:\"A special offer just for you!\"' failed with error 'Field 'badfield' not present in the event'<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00a1Enhorabuena! <b>\u00a1Acabas de escribir tu propio lenguaje de utilidad extremadamente limitada!<\/b><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Consideraciones<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Llegados a este punto tal vez te est\u00e9s preguntando:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u00bfNo hay muchos remitentes que usan asuntos as\u00ed?<\/li>\n\n\n\n<li>\u00bfEs tan \u00fatil un motor de reglas que solo hace coincidir literales de cadenas?<\/li>\n\n\n\n<li>\u00bfNo podr\u00eda haber sido la mitad de largo este art\u00edculo?<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">A lo que responder\u00edamos:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Nos gustar\u00eda implementar alg\u00fan tipo de manejo para la cuenta (<code>account<\/code>) tal y como se define en el evento original. Mailgun resuelve este problema incluyendo l\u00f3gica de negocio en el c\u00f3digo para ofrecer asistencia a la limitaci\u00f3n de peticiones basada en un campo definido. En otras palabras, podemos decir \u201cSi esta regla se cumple, guarda la cuenta y la hora actual y no realices esta acci\u00f3n con esta cuenta durante los pr\u00f3ximos N minutos\u201d. Obviamente, si no tuvi\u00e9ramos esta gesti\u00f3n y el remitente en cuesti\u00f3n enviara un mill\u00f3n de emails, el servicio de asistencia recibir\u00eda un mill\u00f3n de notificaciones. Esa ser\u00eda una forma estupenda de ahogar a tu equipo de asistencia en el ruido y tambi\u00e9n de que te asalten en el aparcamiento al salir del trabajo.<\/li>\n\n\n\n<li>Se podr\u00edan implementar reglas para analizar sint\u00e1cticamente expresiones regulares y subcadenas que resultar\u00edan mucho m\u00e1s \u00fatiles que una coincidencia expl\u00edcita de cadenas. De hecho, hemos hecho exactamente eso.<\/li>\n\n\n\n<li>Lo siento.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Por \u00faltimo, es probable que te hayas dado cuenta de que no definimos lo que har\u00eda la regla aqu\u00ed cuando <i>s\u00ed<\/i> coincide. Una posible soluci\u00f3n es hacer que la regla se publique en un canal de Slack cuando salte el disparador. Esta es una soluci\u00f3n com\u00fan aqu\u00ed en Mailgun, y tiene el siguiente aspecto:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>slack:#channel-name<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pr\u00f3ximos pasos<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Con suerte, en este punto, tu cabeza estar\u00e1 d\u00e1ndole vueltas a todas las posibilidades. Estas son algunas de las ideas que hemos tenido:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Hay mucho m\u00e1s que emular en la sintaxis de Lucene, como las declaraciones NOT y OR, y los par\u00e9ntesis.<\/li>\n\n\n\n<li>Proporcionar una gram\u00e1tica independiente para las acciones.<\/li>\n\n\n\n<li>Implementar el uso de plantillas para inyectar valores del evento en la acci\u00f3n.<\/li>\n\n\n\n<li>A\u00f1adir az\u00facar sint\u00e1ctico para facilitar la redacci\u00f3n de algunos filtros.<\/li>\n\n\n\n<li>Implementar un sistema para contar y reaccionar ante un n\u00famero espec\u00edfico de apariciones de eventos a lo largo de un per\u00edodo de tiempo.<\/li>\n\n\n\n<li>Implementar una memoria cach\u00e9 porque analizar las reglas resulta un poco costoso.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Hay un mont\u00f3n de cosas m\u00e1s que puedes hacer, sobre todo en lo que respecta a desarrollar el parser de acciones. <b>Podr\u00edas llamar a otros servicios, avisar a los clientes, encender y apagar las luces, y mucho m\u00e1s.<\/b> A la hora de inventar tus propias herramientas bas\u00e1ndote en esto, el cielo es el l\u00edmite.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pros y contras<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Llegados a este punto, tal vez te preguntes: \u201c\u00bfPor qu\u00e9 no usaron $SOME_OTHER_TOOL?\u201d\u00a0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La respuesta es porque todav\u00eda no necesitamos la potencia de ninguna de esas herramientas ni la complejidad que a menudo conlleva operarlas. Nuestra actual herramienta de procesamiento de flujos es un \u00fanico y sencillo binario desplegado en un contenedor que simplemente funciona (Just Works\u2122). Hay muy poco que gestionar, y sigue f\u00e1cilmente el ritmo de nuestro flujo de eventos de alt\u00edsimo volumen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pros:<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Es f\u00e1cil de desarrollar<\/li>\n\n\n\n<li>Sigue utilizando herramientas de c\u00f3digo abierto (FOSS) y comerciales est\u00e1ndar<\/li>\n\n\n\n<li>Se puede adaptar exactamente a nuestras necesidades<\/li>\n\n\n\n<li>La sintaxis de nuestro DSL es muy peque\u00f1a, f\u00e1cil de articular y de razonar, y obliga a todo el mundo a escribir las reglas m\u00e1s o menos de la misma forma. Tambi\u00e9n hace que sea m\u00e1s dif\u00edcil crear efectos secundarios no deseados<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Contras:<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Las caracter\u00edsticas del lenguaje que van m\u00e1s all\u00e1 de lo que ya hemos implementado representan un salto sustancial en complejidad.<\/li>\n\n\n\n<li>Actualizar el c\u00f3digo fuente del PEG generar\u00e1 nuevo c\u00f3digo, y realmente ensucia los diffs de tu c\u00f3digo.<\/li>\n\n\n\n<li>La simplicidad deliberada de Lucene puede dificultar la redacci\u00f3n de reglas m\u00e1s complejas.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Con el tiempo, puede que decidamos que $SOME_OTHER_TOOL es m\u00e1s adecuada para nuestras necesidades, pero a corto o medio plazo, <b>la potencia que aporta nuestro DSL de Mailgun es m\u00e1s que suficiente.<\/b><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El final, por fin<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Llegados a este punto, esperamos haber demostrado c\u00f3mo escribir tu propio DSL podr\u00eda resultar \u00fatil. Nuestra implementaci\u00f3n procesa una enorme cantidad de eventos al d\u00eda mientras que facilita mucho la vida a muchos integrantes de nuestro equipo (o al menos eso es lo que les decimos).\u00a0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En cuanto a las caracter\u00edsticas a las que da asistencia, <b>solo acabamos de empezar<\/b>. Si esto te ha inspirado para estudiar la implementaci\u00f3n de tu propia gram\u00e1tica de procesamiento de flujos, av\u00edsanos. \u00a1Nos encantar\u00eda saberlo!<\/p>\n\n\n\r\n    <aside data-content-type=\"longform\"  class=\"banner-block longform-spacings rounded-lg shadow-lg px-5 py-6 px-md-6 py-md-7 p-lg-7 bg-light\" data-theme=\"dark\" aria-labelledby=\"banner-block-block_cdfac0c762019e32a40e0f147f301a3d\">\r\n                    <p class=\"text-uppercase section-caption text-body-color\">\r\n                Reg\u00edstrate            <\/p>\r\n                            <p class=\"h4 text-accent fw-bold\" id=\"banner-block-block_cdfac0c762019e32a40e0f147f301a3d\">\r\n                Empezar es facil\u00edsimo. Y gratis.            <\/p>\r\n                            <div class=\"mb-0 text-body-color\">\r\n                                    <div class=\"content-body mb-0 text-body-color\">Descubre todo lo que puedes lograr con la mejor plataforma de entrega de emails del mundo.<\/div>\r\n                            <\/div>\r\n        \r\n        <div class=\"text-start mt-5\"><a href=\"https:\/\/signup.mailgun.com\/new\/signup\"  class=\"btn btn-secondary\" >Get started<\/a><\/div>\r\n    <\/aside>\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>En Mailgun tenemos numerosos sistemas que generan much\u00edsimos eventos cada hora del d\u00eda. Es un n\u00famero tan grande que resulta imposible para un equipo humano clasificar los resultados de ElasticSearch y esperar resultados coherentes o mantener la cordura.<\/p>\n","protected":false},"author":62,"featured_media":4843,"menu_order":0,"template":"","meta":{"_acf_changed":true,"footnotes":""},"blog_category":[23],"class_list":["post-24564","blog","type-blog","status-publish","has-post-thumbnail","hentry","blog_category-it-and-engineering"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun<\/title>\n<meta name=\"description\" content=\"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun\" \/>\n<meta property=\"og:description\" content=\"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/\" \/>\n<meta property=\"og:site_name\" content=\"Transactional Email API Service For Developers | Mailgun\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-IT-Engineering.png\" \/>\n\t<meta property=\"og:image:width\" content=\"720\" \/>\n\t<meta property=\"og:image:height\" content=\"448\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"11 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/\",\"name\":\"C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-IT-Engineering.png\",\"datePublished\":\"2026-09-16T14:44:12+00:00\",\"description\":\"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-IT-Engineering.png\",\"contentUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/07\\\/MG-Blog-IT-Engineering.png\",\"width\":720,\"height\":448},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/it-and-engineering\\\/como-creamos-un-parser-inspirado-en-lucene-en-go\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"C\u00f3mo creamos un parser inspirado en Lucene en Go\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#website\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\",\"name\":\"Transactional Email API Service For Developers | Mailgun\",\"description\":\"Potentes API de email transaccional que te permiten enviar, recibir y hacer seguimiento de emails, creadas pensando en los desarrolladores. \u00a1Descubre m\u00e1s!\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#organization\",\"name\":\"Transactional Email API Service For Developers | Mailgun\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/cropped-android-chrome-512x512-1.png\",\"contentUrl\":\"https:\\\/\\\/www.mailgun.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/cropped-android-chrome-512x512-1.png\",\"width\":512,\"height\":512,\"caption\":\"Transactional Email API Service For Developers | Mailgun\"},\"image\":{\"@id\":\"https:\\\/\\\/www.mailgun.com\\\/es\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun","description":"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/","og_locale":"es_ES","og_type":"article","og_title":"C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun","og_description":"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013","og_url":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/","og_site_name":"Transactional Email API Service For Developers | Mailgun","og_image":[{"width":720,"height":448,"url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-IT-Engineering.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"11 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/","url":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/","name":"C\u00f3mo creamos un parser inspirado en Lucene en GoLang - Transactional Email API Service For Developers | Mailgun","isPartOf":{"@id":"https:\/\/www.mailgun.com\/es\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/#primaryimage"},"image":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/#primaryimage"},"thumbnailUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-IT-Engineering.png","datePublished":"2026-09-16T14:44:12+00:00","description":"Nos enorgullece hacer mucho con muy poco, as\u00ed que nos propusimos descubrir c\u00f3mo hacer que los filtros en tiempo real nos sirvieran. M\u00e1s informaci\u00f3n \u2013","breadcrumb":{"@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/#primaryimage","url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-IT-Engineering.png","contentUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/07\/MG-Blog-IT-Engineering.png","width":720,"height":448},{"@type":"BreadcrumbList","@id":"https:\/\/www.mailgun.com\/es\/blog\/it-and-engineering\/como-creamos-un-parser-inspirado-en-lucene-en-go\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.mailgun.com\/es\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/www.mailgun.com\/es\/blog\/"},{"@type":"ListItem","position":3,"name":"C\u00f3mo creamos un parser inspirado en Lucene en Go"}]},{"@type":"WebSite","@id":"https:\/\/www.mailgun.com\/es\/#website","url":"https:\/\/www.mailgun.com\/es\/","name":"Transactional Email API Service For Developers | Mailgun","description":"Potentes API de email transaccional que te permiten enviar, recibir y hacer seguimiento de emails, creadas pensando en los desarrolladores. \u00a1Descubre m\u00e1s!","publisher":{"@id":"https:\/\/www.mailgun.com\/es\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.mailgun.com\/es\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.mailgun.com\/es\/#organization","name":"Transactional Email API Service For Developers | Mailgun","url":"https:\/\/www.mailgun.com\/es\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.mailgun.com\/es\/#\/schema\/logo\/image\/","url":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/06\/cropped-android-chrome-512x512-1.png","contentUrl":"https:\/\/www.mailgun.com\/wp-content\/uploads\/2025\/06\/cropped-android-chrome-512x512-1.png","width":512,"height":512,"caption":"Transactional Email API Service For Developers | Mailgun"},"image":{"@id":"https:\/\/www.mailgun.com\/es\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog\/24564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/users\/62"}],"version-history":[{"count":0,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog\/24564\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/media\/4843"}],"wp:attachment":[{"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/media?parent=24564"}],"wp:term":[{"taxonomy":"blog_category","embeddable":true,"href":"https:\/\/www.mailgun.com\/es\/wp-json\/wp\/v2\/blog_category?post=24564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}