xxeell

TFLAT-1-lex-analizer

Oct 17th, 2021 (edited)
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5.     <title>Lex analizator</title>
  6. </head>
  7.  
  8. <body>
  9.     <textarea id="input" cols="60" rows="5"></textarea>
  10.     <button id="parseButton">parse</button>
  11.     <br>
  12.     <div id="outputContainer"></div>
  13. </body>
  14.  
  15. <script>
  16.     const outputContainer = document.getElementById('outputContainer');
  17.     const inputElement = document.getElementById('input');
  18.     inputElement.value = 'do while (a < b and a <= c or a > c) b=b+c-20 loop';
  19. </script>
  20.  
  21. <script>
  22.     function distinct(array) {
  23.         return array.filter((value, index) => array.indexOf(value) == index);
  24.     }
  25.  
  26.     (function () {
  27.         let automateExample = {
  28.             states: {
  29.                 list: ['a', 'b', 'c'],
  30.                 start: 'a',
  31.                 end: ['c']
  32.             },
  33.             alphabet: [1, 2, 3],
  34.             translations: [
  35.                 { from: 'a', to: 'b', when: 1 },
  36.                 { from: 'b', to: 'c', when: 2 },
  37.                 { from: 'b', to: 'a', when: 2 },
  38.                 { from: 'a', to: 'c', when: 3 },
  39.             ],
  40.             epsilon: {
  41.                 useEpsilon: true,
  42.                 translations: [
  43.                     { from: 'b', to: 'a' },
  44.                     { from: 'b', to: 'b' },
  45.                 ],
  46.             },
  47.             config: {
  48.                 drowWhenNoTranslation: false,
  49.                 logEpsilonSeparately: true
  50.             }
  51.         };
  52.     })();
  53.  
  54.     function validateAutomateTemplate(automateTemplate) {
  55.         if (!automateTemplate) {
  56.             throw new Error(`Template error! Input is undefined.`);
  57.         }
  58.  
  59.         // --- --- States --- ---
  60.         if (!automateTemplate.states) {
  61.             throw new Error(`States error! States object is empty.`);
  62.         }
  63.         // --- list ---
  64.         if (!automateTemplate.states.list) {
  65.             throw new Error(`States error! States list is undefined.`);
  66.         }
  67.  
  68.         if (automateTemplate.states.list.length < 1) {
  69.             throw new Error(`States error! States list is empty.`);
  70.         }
  71.         // --- end list ---
  72.         // --- start ---
  73.         if (!automateTemplate.states.start) {
  74.             throw new Error(`Start state error! Start state is undefined.`);
  75.         }
  76.  
  77.         if (!automateTemplate.states.list.includes(automateTemplate.states.start)) {
  78.             throw new Error(`Start state error! States not include '${automateTemplate.states.start}'.`);
  79.         }
  80.         // --- end start ---
  81.         // --- end ---
  82.         if (!automateTemplate.states.end) {
  83.             throw new Error(`End states error! End states is undefined.`);
  84.         }
  85.  
  86.         if (automateTemplate.states.end.length < 1) {
  87.             throw new Error(`End states error! End states list is empty.`);
  88.         }
  89.  
  90.         for (const state of automateTemplate.states.end) {
  91.             if (!automateTemplate.states.list.includes(state)) {
  92.                 throw new Error(`End states error! States not include '${state}'.`);
  93.             }
  94.         }
  95.         // --- end end ---
  96.         // --- --- end States --- ---
  97.  
  98.         // --- --- Alphabet --- ---
  99.         if (!automateTemplate.alphabet) {
  100.             throw new Error(`Alphabet error! Alphabet is undefined.`);
  101.         }
  102.  
  103.         if (automateTemplate.alphabet.length < 1) {
  104.             throw new Error(`Alphabet error! Alphabet list is empty.`);
  105.         }
  106.         // --- --- end Alphabet --- ---
  107.  
  108.         // --- --- Translation --- ---
  109.         if (!automateTemplate.translations) {
  110.             throw new Error(`Translation error! Translations is undefined.`);
  111.         }
  112.  
  113.         if (automateTemplate.translations.length < 1) {
  114.             throw new Error(`Translation error! Translations list is empty.`);
  115.         }
  116.  
  117.         for (const translation of automateTemplate.translations) {
  118.             if (!automateTemplate.states.list.includes(translation.from)) {
  119.                 throw new Error(`Translation error! States not include 'from' value '${translation.from}' on transtalion ${JSON.stringify(translation)}.`);
  120.             }
  121.             if (!automateTemplate.states.list.includes(translation.to)) {
  122.                 throw new Error(`Translation error! States not include 'to' value '${translation.to}' on transtalion ${JSON.stringify(translation)}.`);
  123.             }
  124.             if (!automateTemplate.alphabet.includes(translation.when)) {
  125.                 throw new Error(`Translation error! Alphabet not include 'when' value '${translation.when}' on transtalion ${JSON.stringify(translation)}.`);
  126.             }
  127.         }
  128.         // --- --- end Translation --- ---
  129.  
  130.         // --- --- Epsilon --- ---
  131.         if (!automateTemplate.epsilon) {
  132.             throw new Error(`Epsilon error! Epsilon is undefined.`);
  133.         }
  134.  
  135.         if (automateTemplate.epsilon.useEpsilon) {
  136.             if (!automateTemplate.epsilon.translations) {
  137.                 throw new Error(`Epsilon error! Epsilon translations is undefined.`);
  138.             }
  139.  
  140.             if (automateTemplate.epsilon.translations.length < 1) {
  141.                 throw new Error(`Epsilon error! Epsilon translations list is empty.`);
  142.             }
  143.  
  144.             for (const translation of automateTemplate.epsilon.translations) {
  145.                 if (!automateTemplate.states.list.includes(translation.from)) {
  146.                     throw new Error(`Epsilon error! States not include 'from' value '${translation.from}' on epsilon transtalion ${JSON.stringify(translation)}.`);
  147.                 }
  148.                 if (!automateTemplate.states.list.includes(translation.to)) {
  149.                     throw new Error(`Epsilon error! States not include 'to' value '${translation.to}' on epsilon transtalion ${JSON.stringify(translation)}.`);
  150.                 }
  151.             }
  152.         }
  153.         // --- --- end Epsilon --- ---
  154.  
  155.         // --- --- Config --- ---
  156.         if (!automateTemplate.config) {
  157.             throw new Error(`Config error! Config is undefined.`);
  158.         }
  159.         // --- --- end Config --- ---
  160.     }
  161.  
  162.     function Automate(automateTemplate) {
  163.         validateAutomateTemplate(automateTemplate);
  164.  
  165.         this.automate = {
  166.             states: {
  167.                 list: Array.from(automateTemplate.states.list),
  168.                 start: automateTemplate.states.start,
  169.                 end: Array.from(automateTemplate.states.end)
  170.             },
  171.             alphabet: Array.from(automateTemplate.alphabet),
  172.             translations: {},
  173.             epsilon: {
  174.                 useEpsilon: false,
  175.                 translations: {}
  176.             },
  177.             config: {
  178.                 drowWhenNoTranslation: automateTemplate.config.drowWhenNoTranslation,
  179.                 logEpsilonSeparately: automateTemplate.config.logEpsilonSeparately
  180.             }
  181.         };
  182.  
  183.         for (const state of this.automate.states.list) {
  184.             this.automate.translations[state] = {};
  185.             for (const letter of this.automate.alphabet) {
  186.                 this.automate.translations[state][letter] = [];
  187.             }
  188.         }
  189.         for (const translation of automateTemplate.translations) {
  190.             if (this.automate.translations[translation.from][translation.when].includes(translation.to)) {
  191.                 throw new Error(`Translation error! Transtalion ${JSON.stringify(translation)} already exists.`);
  192.             }
  193.             this.automate.translations[translation.from][translation.when].push(translation.to);
  194.         }
  195.  
  196.         if (automateTemplate.epsilon.useEpsilon) {
  197.             this.automate.epsilon.useEpsilon = true;
  198.             for (const state of this.automate.states.list) {
  199.                 this.automate.epsilon.translations[state] = [];
  200.             }
  201.             for (const translation of automateTemplate.epsilon.translations) {
  202.                 if (this.automate.epsilon.translations[translation.from].includes(translation.to)) {
  203.                     throw new Error(`Epsilon translation error! Epsilon transtalion ${JSON.stringify(translation)} already exists.`);
  204.                 }
  205.                 this.automate.epsilon.translations[translation.from].push(translation.to);
  206.             }
  207.         }
  208.  
  209.         this.execution = {
  210.             current: [this.automate.states.start],
  211.             log: [],
  212.             errors: []
  213.         };
  214.     }
  215.  
  216.     Automate.prototype = {
  217.         clearExecution() {
  218.             this.execution = {
  219.                 current: [this.automate.states.start],
  220.                 log: [],
  221.                 errors: []
  222.             };
  223.             this.logStep(null);
  224.         },
  225.         logStep(letter) {
  226.             this.execution.log.push({
  227.                 getLetter: letter,
  228.                 resultStates: Array.from(this.execution.current)
  229.             });
  230.         },
  231.         processEpsilon() {
  232.             if (!this.automate.epsilon.useEpsilon) {
  233.                 return;
  234.             }
  235.  
  236.             let next = [...this.execution.current];
  237.             let currentChars = this.execution.current;
  238.             let nextChars = [];
  239.  
  240.             while (currentChars.length > 0) {
  241.                 for (let curChar of currentChars) {
  242.                     nextChars.push(...this.automate.epsilon.translations[curChar]);
  243.                 }
  244.                 next.push(...nextChars);
  245.                 currentChars = distinct(nextChars).filter(value => !next.includes(value));
  246.                 nextChars = [];
  247.             }
  248.  
  249.             this.execution.current = distinct(next);
  250.             if (this.automate.config.logEpsilonSeparately) {
  251.                 this.logStep('epsilon');
  252.             }
  253.         },
  254.         processLetter(letter) {
  255.             if (!this.automate.alphabet.includes(letter)) {
  256.                 alert(`Input error! Alphabet not include ${letter}`);
  257.                 return;
  258.             }
  259.  
  260.             let next = [];
  261.  
  262.             for (let currentState of this.execution.current) {
  263.                 let nextStates = this.automate.translations[currentState][letter];
  264.                 if (nextStates.length < 1) {
  265.                     let error = `No translation for ${JSON.stringify({ from: currentState, when: letter })}`;
  266.                     this.execution.errors.push(error);
  267.                     if (this.automate.config.drowWhenNoTranslation) {
  268.                         throw error;
  269.                     } else {
  270.                         nextStates = [currentState];
  271.                     }
  272.                 }
  273.                 next.push(...nextStates);
  274.             }
  275.  
  276.             this.execution.current = distinct(next);
  277.  
  278.             if (this.automate.config.logEpsilonSeparately) {
  279.                 this.logStep(letter);
  280.                 this.processEpsilon();
  281.             } else {
  282.                 this.processEpsilon();
  283.                 this.logStep(letter);
  284.             }
  285.         }
  286.     };
  287. </script>
  288.  
  289. <script>
  290.     const LexClasses = {
  291.         keywords: {
  292.             loop: {
  293.                 while: {
  294.                     start: { value: 'keyword.loop.while.start', priority: 1 },
  295.                     end: { value: 'keyword.loop.while.end', priority: 1 },
  296.                 }
  297.             },
  298.             codeSegment: {
  299.                 start: { value: 'keyword.codeSegment.start', priority: 2 },
  300.                 end: { value: 'keyword.codeSegment.end', priority: 2 },
  301.             },
  302.         },
  303.         separators: {
  304.             skobka: {
  305.                 open: { value: 'separators.skobka.open', priority: 2 },
  306.                 close: { value: 'separators.skobka.close', priority: 2 },
  307.             }
  308.         },
  309.         operations: {
  310.             as:  { value: 'operations.as', priority: 3 },
  311.             logical: { value: 'operations.logical', priority: 4 },
  312.             compare: { value: 'operations.compare', priority: 4 },
  313.             math: { value: 'operations.math', priority: 4 },
  314.         },
  315.         values: {
  316.             variable: { value: 'values.variable', priority: 5 },
  317.             numConst: { value: 'values.numConst', priority: 5 },
  318.         },
  319.     };
  320. </script>
  321.  
  322. <script>
  323.     const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>=+-*/ ()'.split('');
  324.  
  325.     function createAutomateTemplate(keyword) {
  326.         let template = {
  327.             states: {
  328.                 list: ['start'],
  329.                 start: 'start',
  330.                 end: [keyword]
  331.             },
  332.             alphabet: alphabet,
  333.             translations: [],
  334.             epsilon: {
  335.                 useEpsilon: false,
  336.                 translations: [],
  337.             },
  338.             config: {
  339.                 drowWhenNoTranslation: true,
  340.                 logEpsilonSeparately: false
  341.             }
  342.         };
  343.  
  344.         let wordPart = '';
  345.         for (const letter of keyword) {
  346.             wordPart += letter;
  347.             const prewState = template.states.list[template.states.list.length - 1];
  348.             const curState = wordPart;
  349.             template.states.list.push(curState);
  350.             template.translations.push({ from: prewState, to: curState, when: letter });
  351.         }
  352.  
  353.         return template;
  354.     }
  355.  
  356.     function createAutomate(keyword) {
  357.         let template = createAutomateTemplate(keyword);
  358.         let automate = new Automate(template);
  359.         return automate;
  360.     }
  361.  
  362.     function createNumConstAtomate() {
  363.         let template = {
  364.             states: {
  365.                 list: ['start', 'num'],
  366.                 start: 'start',
  367.                 end: ['num']
  368.             },
  369.             alphabet: alphabet,
  370.             translations: [],
  371.             epsilon: {
  372.                 useEpsilon: false,
  373.                 translations: [],
  374.             },
  375.             config: {
  376.                 drowWhenNoTranslation: true,
  377.                 logEpsilonSeparately: false
  378.             }
  379.         };
  380.  
  381.         for (let letter of '123456789') {
  382.             template.translations.push({ from: 'start', to: 'num', when: letter });
  383.             template.translations.push({ from: 'num', to: 'num', when: letter });
  384.         }
  385.         template.translations.push({ from: 'num', to: 'num', when: '0' });
  386.  
  387.         let automate = new Automate(template);
  388.         return automate;
  389.     }
  390.  
  391.     function createVariableAtomate() {
  392.         let template = {
  393.             states: {
  394.                 list: ['start', 'var'],
  395.                 start: 'start',
  396.                 end: ['var']
  397.             },
  398.             alphabet: alphabet,
  399.             translations: [],
  400.             epsilon: {
  401.                 useEpsilon: false,
  402.                 translations: [],
  403.             },
  404.             config: {
  405.                 drowWhenNoTranslation: true,
  406.                 logEpsilonSeparately: false
  407.             }
  408.         };
  409.  
  410.         for (let letter of 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
  411.             template.translations.push({ from: 'start', to: 'var', when: letter });
  412.             template.translations.push({ from: 'var', to: 'var', when: letter });
  413.         }
  414.  
  415.         for (let letter of '0123456789') {
  416.             template.translations.push({ from: 'var', to: 'var', when: letter });
  417.         }
  418.  
  419.         let automate = new Automate(template);
  420.         return automate;
  421.     }
  422. </script>
  423.  
  424. <script>
  425.     const FullAutomatesList = {
  426.         // keywords,
  427.         while: {
  428.             automate: createAutomate('while'),
  429.             class: LexClasses.keywords.loop.while.start
  430.         },
  431.         loop: {
  432.             automate: createAutomate('loop'),
  433.             class: LexClasses.keywords.loop.while.end
  434.         },
  435.         do: {
  436.             automate: createAutomate('do'),
  437.             class: LexClasses.keywords.codeSegment.start
  438.         },
  439.         end: {
  440.             automate: createAutomate('end'),
  441.             class: LexClasses.keywords.codeSegment.end
  442.         },
  443.         // end keywords
  444.         // separators
  445.         '(': {
  446.             automate: createAutomate('('),
  447.             class: LexClasses.separators.skobka.open
  448.         },
  449.         ')': {
  450.             automate: createAutomate(')'),
  451.             class: LexClasses.separators.skobka.close
  452.         },
  453.         // end separators
  454.         // operations
  455.         and: {
  456.             automate: createAutomate('and'),
  457.             class: LexClasses.operations.logical
  458.         },
  459.         or: {
  460.             automate: createAutomate('or'),
  461.             class: LexClasses.operations.logical
  462.         },
  463.         '<': {
  464.             automate: createAutomate('<'),
  465.             class: LexClasses.operations.compare
  466.         },
  467.         '>': {
  468.             automate: createAutomate('>'),
  469.             class: LexClasses.operations.compare
  470.         },
  471.         '<=': {
  472.             automate: createAutomate('<='),
  473.             class: LexClasses.operations.compare
  474.         },
  475.         '==': {
  476.             automate: createAutomate('=='),
  477.             class: LexClasses.operations.compare
  478.         },
  479.         '<>': {
  480.             automate: createAutomate('<>'),
  481.             class: LexClasses.operations.compare
  482.         },
  483.         '+': {
  484.             automate: createAutomate('+'),
  485.             class: LexClasses.operations.math
  486.         },
  487.         '-': {
  488.             automate: createAutomate('-'),
  489.             class: LexClasses.operations.math
  490.         },
  491.         '*': {
  492.             automate: createAutomate('*'),
  493.             class: LexClasses.operations.math
  494.         },
  495.         '/': {
  496.             automate: createAutomate('/'),
  497.             class: LexClasses.operations.math
  498.         },
  499.         '=': {
  500.             automate: createAutomate('='),
  501.             class: LexClasses.operations.as
  502.         },
  503.         // end operations
  504.         // values
  505.         variable: {
  506.             automate: createVariableAtomate(),
  507.             class: LexClasses.values.variable
  508.         },
  509.         numConst: {
  510.             automate: createNumConstAtomate(),
  511.             class: LexClasses.values.numConst
  512.         },
  513.         // end values
  514.     }
  515. </script>
  516.  
  517. <script>
  518.     const HTMLTags = {
  519.         Table: 'table',
  520.         TableRow: 'tr',
  521.         TableData: 'td'
  522.     }
  523.  
  524.     const ItemTypes = {
  525.         Value: 'VALUE',
  526.         Container: 'CONTAINER'
  527.     }
  528.  
  529.     function render(item) {
  530.         if (item.element) {
  531.             return item.element;
  532.         }
  533.  
  534.         let element = document.createElement(item.tag);
  535.         item.element = element;
  536.  
  537.         if (item.attributes) {
  538.             for (let name in item.attributes) {
  539.                 let value = item.attributes[name];
  540.                 element.setAttribute(name, value);
  541.             }
  542.         }
  543.  
  544.         switch (item.type) {
  545.             case ItemTypes.Value:
  546.                 if (item.value) {
  547.                     element.innerHTML = item.value;
  548.                 }
  549.                 break;
  550.             case ItemTypes.Container:
  551.                 if (item.childs) {
  552.                     for (let i in item.childs) {
  553.                         let child = item.childs[i];
  554.                         if (!child.element) {
  555.                             render(child);
  556.                         }
  557.                         element.append(child.element);
  558.                     }
  559.                 }
  560.                 break;
  561.         }
  562.  
  563.         return element;
  564.     }
  565. </script>
  566.  
  567. <script>
  568.     function getAllAutomates() {
  569.         let list = Object.values(FullAutomatesList);
  570.         list.sort((a, b) => {
  571.             if (a.class.priority < b.class.priority) {
  572.                 return -1;
  573.             }
  574.             if (a.class.priority > b.class.priority) {
  575.                 return 1;
  576.             }
  577.             return 0;
  578.         });
  579.         list.forEach(element => element.automate.clearExecution());
  580.         return list;
  581.     }
  582.  
  583.     function drawOutputTable(output) {
  584.         let outputTable = {
  585.             tag: HTMLTags.Table,
  586.             type: ItemTypes.Container,
  587.             attributes: { border: "1" },
  588.             childs: []
  589.         };
  590.  
  591.         outputTable.childs.push({
  592.             tag: HTMLTags.TableRow,
  593.             type: ItemTypes.Container,
  594.             childs: [
  595.                 {
  596.                     tag: HTMLTags.TableData,
  597.                     type: ItemTypes.Value,
  598.                     value: 'Значение'
  599.                 },
  600.                 {
  601.                     tag: HTMLTags.TableData,
  602.                     type: ItemTypes.Value,
  603.                     value: 'Начальная позиция'
  604.                 },
  605.                 {
  606.                     tag: HTMLTags.TableData,
  607.                     type: ItemTypes.Value,
  608.                     value: 'Конечная позиция'
  609.                 },
  610.                 {
  611.                     tag: HTMLTags.TableData,
  612.                     type: ItemTypes.Value,
  613.                     value: 'Класс'
  614.                 },
  615.             ]
  616.         });
  617.  
  618.         for (const o of output) {
  619.             outputTable.childs.push({
  620.                 tag: HTMLTags.TableRow,
  621.                 type: ItemTypes.Container,
  622.                 childs: [
  623.                     {
  624.                         tag: HTMLTags.TableData,
  625.                         type: ItemTypes.Value,
  626.                         value: o.word
  627.                     },
  628.                     {
  629.                         tag: HTMLTags.TableData,
  630.                         type: ItemTypes.Value,
  631.                         value: o.position.start
  632.                     },
  633.                     {
  634.                         tag: HTMLTags.TableData,
  635.                         type: ItemTypes.Value,
  636.                         value: o.position.end
  637.                     },
  638.                     {
  639.                         tag: HTMLTags.TableData,
  640.                         type: ItemTypes.Value,
  641.                         value: o.class
  642.                     },
  643.                 ]
  644.             });
  645.         }
  646.  
  647.         outputContainer.append(render(outputTable));
  648.     }
  649.  
  650.     function parseInput() {
  651.         outputContainer.innerHTML = '';
  652.         let output = [];
  653.         let prevAutomates = getAllAutomates();
  654.         let curAutomates = [];
  655.         let accumulator = '';
  656.  
  657.         let input = inputElement.value.replaceAll(/ +/g, ' ');
  658.         for (let i = 0; i < input.length; i++) {
  659.             const letter = input[i];
  660.  
  661.             for (const automate of prevAutomates) {
  662.                 try {
  663.                     automate.automate.processLetter(letter);
  664.                     curAutomates.push(automate);
  665.                 } catch {
  666.                     automate.automate.clearExecution();
  667.                 }
  668.             }
  669.  
  670.             if (curAutomates.length < 1) {
  671.                 if (prevAutomates.length > 0) {
  672.                     const lastAutomate = prevAutomates[0];
  673.                     output.push({
  674.                         word: accumulator,
  675.                         position: {
  676.                             start: i - accumulator.length,
  677.                             end: i - 1
  678.                         },
  679.                         class: lastAutomate.class.value
  680.                     });
  681.                 }
  682.  
  683.                 prevAutomates = getAllAutomates();
  684.                 curAutomates = [];
  685.                 accumulator = '';
  686.  
  687.                 if (letter != ' ') {
  688.                     i--;
  689.                 }
  690.             } else {
  691.                 prevAutomates = curAutomates;
  692.                 curAutomates = [];
  693.                 accumulator += letter;
  694.             }
  695.         }
  696.  
  697.         if (curAutomates.length < 1) {
  698.             if (prevAutomates.length > 0) {
  699.                 const lastAutomate = prevAutomates[0];
  700.                 output.push({
  701.                     word: accumulator,
  702.                     position: {
  703.                         start: input.length - accumulator.length,
  704.                         end: input.length - 1
  705.                     },
  706.                     class: lastAutomate.class.value
  707.                 });
  708.             }
  709.         }
  710.  
  711.         drawOutputTable(output);
  712.     }
  713.  
  714.     document.getElementById('parseButton').onclick = parseInput;
  715. </script>
  716.  
  717. </html>
Add Comment
Please, Sign In to add comment