Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <title>Lex analizator</title>
- </head>
- <body>
- <textarea id="input" cols="60" rows="5"></textarea>
- <button id="parseButton">parse</button>
- <br>
- <div id="outputContainer"></div>
- </body>
- <script>
- const outputContainer = document.getElementById('outputContainer');
- const inputElement = document.getElementById('input');
- inputElement.value = 'do while (a < b and a <= c or a > c) b=b+c-20 loop';
- </script>
- <script>
- function distinct(array) {
- return array.filter((value, index) => array.indexOf(value) == index);
- }
- (function () {
- let automateExample = {
- states: {
- list: ['a', 'b', 'c'],
- start: 'a',
- end: ['c']
- },
- alphabet: [1, 2, 3],
- translations: [
- { from: 'a', to: 'b', when: 1 },
- { from: 'b', to: 'c', when: 2 },
- { from: 'b', to: 'a', when: 2 },
- { from: 'a', to: 'c', when: 3 },
- ],
- epsilon: {
- useEpsilon: true,
- translations: [
- { from: 'b', to: 'a' },
- { from: 'b', to: 'b' },
- ],
- },
- config: {
- drowWhenNoTranslation: false,
- logEpsilonSeparately: true
- }
- };
- })();
- function validateAutomateTemplate(automateTemplate) {
- if (!automateTemplate) {
- throw new Error(`Template error! Input is undefined.`);
- }
- // --- --- States --- ---
- if (!automateTemplate.states) {
- throw new Error(`States error! States object is empty.`);
- }
- // --- list ---
- if (!automateTemplate.states.list) {
- throw new Error(`States error! States list is undefined.`);
- }
- if (automateTemplate.states.list.length < 1) {
- throw new Error(`States error! States list is empty.`);
- }
- // --- end list ---
- // --- start ---
- if (!automateTemplate.states.start) {
- throw new Error(`Start state error! Start state is undefined.`);
- }
- if (!automateTemplate.states.list.includes(automateTemplate.states.start)) {
- throw new Error(`Start state error! States not include '${automateTemplate.states.start}'.`);
- }
- // --- end start ---
- // --- end ---
- if (!automateTemplate.states.end) {
- throw new Error(`End states error! End states is undefined.`);
- }
- if (automateTemplate.states.end.length < 1) {
- throw new Error(`End states error! End states list is empty.`);
- }
- for (const state of automateTemplate.states.end) {
- if (!automateTemplate.states.list.includes(state)) {
- throw new Error(`End states error! States not include '${state}'.`);
- }
- }
- // --- end end ---
- // --- --- end States --- ---
- // --- --- Alphabet --- ---
- if (!automateTemplate.alphabet) {
- throw new Error(`Alphabet error! Alphabet is undefined.`);
- }
- if (automateTemplate.alphabet.length < 1) {
- throw new Error(`Alphabet error! Alphabet list is empty.`);
- }
- // --- --- end Alphabet --- ---
- // --- --- Translation --- ---
- if (!automateTemplate.translations) {
- throw new Error(`Translation error! Translations is undefined.`);
- }
- if (automateTemplate.translations.length < 1) {
- throw new Error(`Translation error! Translations list is empty.`);
- }
- for (const translation of automateTemplate.translations) {
- if (!automateTemplate.states.list.includes(translation.from)) {
- throw new Error(`Translation error! States not include 'from' value '${translation.from}' on transtalion ${JSON.stringify(translation)}.`);
- }
- if (!automateTemplate.states.list.includes(translation.to)) {
- throw new Error(`Translation error! States not include 'to' value '${translation.to}' on transtalion ${JSON.stringify(translation)}.`);
- }
- if (!automateTemplate.alphabet.includes(translation.when)) {
- throw new Error(`Translation error! Alphabet not include 'when' value '${translation.when}' on transtalion ${JSON.stringify(translation)}.`);
- }
- }
- // --- --- end Translation --- ---
- // --- --- Epsilon --- ---
- if (!automateTemplate.epsilon) {
- throw new Error(`Epsilon error! Epsilon is undefined.`);
- }
- if (automateTemplate.epsilon.useEpsilon) {
- if (!automateTemplate.epsilon.translations) {
- throw new Error(`Epsilon error! Epsilon translations is undefined.`);
- }
- if (automateTemplate.epsilon.translations.length < 1) {
- throw new Error(`Epsilon error! Epsilon translations list is empty.`);
- }
- for (const translation of automateTemplate.epsilon.translations) {
- if (!automateTemplate.states.list.includes(translation.from)) {
- throw new Error(`Epsilon error! States not include 'from' value '${translation.from}' on epsilon transtalion ${JSON.stringify(translation)}.`);
- }
- if (!automateTemplate.states.list.includes(translation.to)) {
- throw new Error(`Epsilon error! States not include 'to' value '${translation.to}' on epsilon transtalion ${JSON.stringify(translation)}.`);
- }
- }
- }
- // --- --- end Epsilon --- ---
- // --- --- Config --- ---
- if (!automateTemplate.config) {
- throw new Error(`Config error! Config is undefined.`);
- }
- // --- --- end Config --- ---
- }
- function Automate(automateTemplate) {
- validateAutomateTemplate(automateTemplate);
- this.automate = {
- states: {
- list: Array.from(automateTemplate.states.list),
- start: automateTemplate.states.start,
- end: Array.from(automateTemplate.states.end)
- },
- alphabet: Array.from(automateTemplate.alphabet),
- translations: {},
- epsilon: {
- useEpsilon: false,
- translations: {}
- },
- config: {
- drowWhenNoTranslation: automateTemplate.config.drowWhenNoTranslation,
- logEpsilonSeparately: automateTemplate.config.logEpsilonSeparately
- }
- };
- for (const state of this.automate.states.list) {
- this.automate.translations[state] = {};
- for (const letter of this.automate.alphabet) {
- this.automate.translations[state][letter] = [];
- }
- }
- for (const translation of automateTemplate.translations) {
- if (this.automate.translations[translation.from][translation.when].includes(translation.to)) {
- throw new Error(`Translation error! Transtalion ${JSON.stringify(translation)} already exists.`);
- }
- this.automate.translations[translation.from][translation.when].push(translation.to);
- }
- if (automateTemplate.epsilon.useEpsilon) {
- this.automate.epsilon.useEpsilon = true;
- for (const state of this.automate.states.list) {
- this.automate.epsilon.translations[state] = [];
- }
- for (const translation of automateTemplate.epsilon.translations) {
- if (this.automate.epsilon.translations[translation.from].includes(translation.to)) {
- throw new Error(`Epsilon translation error! Epsilon transtalion ${JSON.stringify(translation)} already exists.`);
- }
- this.automate.epsilon.translations[translation.from].push(translation.to);
- }
- }
- this.execution = {
- current: [this.automate.states.start],
- log: [],
- errors: []
- };
- }
- Automate.prototype = {
- clearExecution() {
- this.execution = {
- current: [this.automate.states.start],
- log: [],
- errors: []
- };
- this.logStep(null);
- },
- logStep(letter) {
- this.execution.log.push({
- getLetter: letter,
- resultStates: Array.from(this.execution.current)
- });
- },
- processEpsilon() {
- if (!this.automate.epsilon.useEpsilon) {
- return;
- }
- let next = [...this.execution.current];
- let currentChars = this.execution.current;
- let nextChars = [];
- while (currentChars.length > 0) {
- for (let curChar of currentChars) {
- nextChars.push(...this.automate.epsilon.translations[curChar]);
- }
- next.push(...nextChars);
- currentChars = distinct(nextChars).filter(value => !next.includes(value));
- nextChars = [];
- }
- this.execution.current = distinct(next);
- if (this.automate.config.logEpsilonSeparately) {
- this.logStep('epsilon');
- }
- },
- processLetter(letter) {
- if (!this.automate.alphabet.includes(letter)) {
- alert(`Input error! Alphabet not include ${letter}`);
- return;
- }
- let next = [];
- for (let currentState of this.execution.current) {
- let nextStates = this.automate.translations[currentState][letter];
- if (nextStates.length < 1) {
- let error = `No translation for ${JSON.stringify({ from: currentState, when: letter })}`;
- this.execution.errors.push(error);
- if (this.automate.config.drowWhenNoTranslation) {
- throw error;
- } else {
- nextStates = [currentState];
- }
- }
- next.push(...nextStates);
- }
- this.execution.current = distinct(next);
- if (this.automate.config.logEpsilonSeparately) {
- this.logStep(letter);
- this.processEpsilon();
- } else {
- this.processEpsilon();
- this.logStep(letter);
- }
- }
- };
- </script>
- <script>
- const LexClasses = {
- keywords: {
- loop: {
- while: {
- start: { value: 'keyword.loop.while.start', priority: 1 },
- end: { value: 'keyword.loop.while.end', priority: 1 },
- }
- },
- codeSegment: {
- start: { value: 'keyword.codeSegment.start', priority: 2 },
- end: { value: 'keyword.codeSegment.end', priority: 2 },
- },
- },
- separators: {
- skobka: {
- open: { value: 'separators.skobka.open', priority: 2 },
- close: { value: 'separators.skobka.close', priority: 2 },
- }
- },
- operations: {
- as: { value: 'operations.as', priority: 3 },
- logical: { value: 'operations.logical', priority: 4 },
- compare: { value: 'operations.compare', priority: 4 },
- math: { value: 'operations.math', priority: 4 },
- },
- values: {
- variable: { value: 'values.variable', priority: 5 },
- numConst: { value: 'values.numConst', priority: 5 },
- },
- };
- </script>
- <script>
- const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>=+-*/ ()'.split('');
- function createAutomateTemplate(keyword) {
- let template = {
- states: {
- list: ['start'],
- start: 'start',
- end: [keyword]
- },
- alphabet: alphabet,
- translations: [],
- epsilon: {
- useEpsilon: false,
- translations: [],
- },
- config: {
- drowWhenNoTranslation: true,
- logEpsilonSeparately: false
- }
- };
- let wordPart = '';
- for (const letter of keyword) {
- wordPart += letter;
- const prewState = template.states.list[template.states.list.length - 1];
- const curState = wordPart;
- template.states.list.push(curState);
- template.translations.push({ from: prewState, to: curState, when: letter });
- }
- return template;
- }
- function createAutomate(keyword) {
- let template = createAutomateTemplate(keyword);
- let automate = new Automate(template);
- return automate;
- }
- function createNumConstAtomate() {
- let template = {
- states: {
- list: ['start', 'num'],
- start: 'start',
- end: ['num']
- },
- alphabet: alphabet,
- translations: [],
- epsilon: {
- useEpsilon: false,
- translations: [],
- },
- config: {
- drowWhenNoTranslation: true,
- logEpsilonSeparately: false
- }
- };
- for (let letter of '123456789') {
- template.translations.push({ from: 'start', to: 'num', when: letter });
- template.translations.push({ from: 'num', to: 'num', when: letter });
- }
- template.translations.push({ from: 'num', to: 'num', when: '0' });
- let automate = new Automate(template);
- return automate;
- }
- function createVariableAtomate() {
- let template = {
- states: {
- list: ['start', 'var'],
- start: 'start',
- end: ['var']
- },
- alphabet: alphabet,
- translations: [],
- epsilon: {
- useEpsilon: false,
- translations: [],
- },
- config: {
- drowWhenNoTranslation: true,
- logEpsilonSeparately: false
- }
- };
- for (let letter of 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
- template.translations.push({ from: 'start', to: 'var', when: letter });
- template.translations.push({ from: 'var', to: 'var', when: letter });
- }
- for (let letter of '0123456789') {
- template.translations.push({ from: 'var', to: 'var', when: letter });
- }
- let automate = new Automate(template);
- return automate;
- }
- </script>
- <script>
- const FullAutomatesList = {
- // keywords,
- while: {
- automate: createAutomate('while'),
- class: LexClasses.keywords.loop.while.start
- },
- loop: {
- automate: createAutomate('loop'),
- class: LexClasses.keywords.loop.while.end
- },
- do: {
- automate: createAutomate('do'),
- class: LexClasses.keywords.codeSegment.start
- },
- end: {
- automate: createAutomate('end'),
- class: LexClasses.keywords.codeSegment.end
- },
- // end keywords
- // separators
- '(': {
- automate: createAutomate('('),
- class: LexClasses.separators.skobka.open
- },
- ')': {
- automate: createAutomate(')'),
- class: LexClasses.separators.skobka.close
- },
- // end separators
- // operations
- and: {
- automate: createAutomate('and'),
- class: LexClasses.operations.logical
- },
- or: {
- automate: createAutomate('or'),
- class: LexClasses.operations.logical
- },
- '<': {
- automate: createAutomate('<'),
- class: LexClasses.operations.compare
- },
- '>': {
- automate: createAutomate('>'),
- class: LexClasses.operations.compare
- },
- '<=': {
- automate: createAutomate('<='),
- class: LexClasses.operations.compare
- },
- '==': {
- automate: createAutomate('=='),
- class: LexClasses.operations.compare
- },
- '<>': {
- automate: createAutomate('<>'),
- class: LexClasses.operations.compare
- },
- '+': {
- automate: createAutomate('+'),
- class: LexClasses.operations.math
- },
- '-': {
- automate: createAutomate('-'),
- class: LexClasses.operations.math
- },
- '*': {
- automate: createAutomate('*'),
- class: LexClasses.operations.math
- },
- '/': {
- automate: createAutomate('/'),
- class: LexClasses.operations.math
- },
- '=': {
- automate: createAutomate('='),
- class: LexClasses.operations.as
- },
- // end operations
- // values
- variable: {
- automate: createVariableAtomate(),
- class: LexClasses.values.variable
- },
- numConst: {
- automate: createNumConstAtomate(),
- class: LexClasses.values.numConst
- },
- // end values
- }
- </script>
- <script>
- const HTMLTags = {
- Table: 'table',
- TableRow: 'tr',
- TableData: 'td'
- }
- const ItemTypes = {
- Value: 'VALUE',
- Container: 'CONTAINER'
- }
- function render(item) {
- if (item.element) {
- return item.element;
- }
- let element = document.createElement(item.tag);
- item.element = element;
- if (item.attributes) {
- for (let name in item.attributes) {
- let value = item.attributes[name];
- element.setAttribute(name, value);
- }
- }
- switch (item.type) {
- case ItemTypes.Value:
- if (item.value) {
- element.innerHTML = item.value;
- }
- break;
- case ItemTypes.Container:
- if (item.childs) {
- for (let i in item.childs) {
- let child = item.childs[i];
- if (!child.element) {
- render(child);
- }
- element.append(child.element);
- }
- }
- break;
- }
- return element;
- }
- </script>
- <script>
- function getAllAutomates() {
- let list = Object.values(FullAutomatesList);
- list.sort((a, b) => {
- if (a.class.priority < b.class.priority) {
- return -1;
- }
- if (a.class.priority > b.class.priority) {
- return 1;
- }
- return 0;
- });
- list.forEach(element => element.automate.clearExecution());
- return list;
- }
- function drawOutputTable(output) {
- let outputTable = {
- tag: HTMLTags.Table,
- type: ItemTypes.Container,
- attributes: { border: "1" },
- childs: []
- };
- outputTable.childs.push({
- tag: HTMLTags.TableRow,
- type: ItemTypes.Container,
- childs: [
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: 'Значение'
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: 'Начальная позиция'
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: 'Конечная позиция'
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: 'Класс'
- },
- ]
- });
- for (const o of output) {
- outputTable.childs.push({
- tag: HTMLTags.TableRow,
- type: ItemTypes.Container,
- childs: [
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: o.word
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: o.position.start
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: o.position.end
- },
- {
- tag: HTMLTags.TableData,
- type: ItemTypes.Value,
- value: o.class
- },
- ]
- });
- }
- outputContainer.append(render(outputTable));
- }
- function parseInput() {
- outputContainer.innerHTML = '';
- let output = [];
- let prevAutomates = getAllAutomates();
- let curAutomates = [];
- let accumulator = '';
- let input = inputElement.value.replaceAll(/ +/g, ' ');
- for (let i = 0; i < input.length; i++) {
- const letter = input[i];
- for (const automate of prevAutomates) {
- try {
- automate.automate.processLetter(letter);
- curAutomates.push(automate);
- } catch {
- automate.automate.clearExecution();
- }
- }
- if (curAutomates.length < 1) {
- if (prevAutomates.length > 0) {
- const lastAutomate = prevAutomates[0];
- output.push({
- word: accumulator,
- position: {
- start: i - accumulator.length,
- end: i - 1
- },
- class: lastAutomate.class.value
- });
- }
- prevAutomates = getAllAutomates();
- curAutomates = [];
- accumulator = '';
- if (letter != ' ') {
- i--;
- }
- } else {
- prevAutomates = curAutomates;
- curAutomates = [];
- accumulator += letter;
- }
- }
- if (curAutomates.length < 1) {
- if (prevAutomates.length > 0) {
- const lastAutomate = prevAutomates[0];
- output.push({
- word: accumulator,
- position: {
- start: input.length - accumulator.length,
- end: input.length - 1
- },
- class: lastAutomate.class.value
- });
- }
- }
- drawOutputTable(output);
- }
- document.getElementById('parseButton').onclick = parseInput;
- </script>
- </html>
Add Comment
Please, Sign In to add comment