БлогNot. Как написать простой интерпретатор на JavaScript

Как написать простой интерпретатор на JavaScript

Здесь выложен на просто некий встроенный в язык eval, а набросок более-менее похожего на настоящий интерпретатора, тем более, код аналогично может выглядеть на любом другом "си-подобном" по виду и духу языке.

В основе работы интерпретатора лежат 3 функции, как обычно и бывает:

  • lexems - делает лексический анализ текста и формирует список (ассоциативный массив) лексем (токенов); например, для ввода
    (3*2)
    +5
    

    получится аж 8 элементов:

    [
      {    "type": "(",    "numstr": 1  },
      {    "type": "number",    "value": 3,    "numstr": 1  },
      {    "type": "*",    "numstr": 1  },
      {    "type": "number",    "value": 2,    "numstr": 1  },
      {    "type": ")",    "numstr": 1  },
      {    "type": "+",    "numstr": 2  },
      {    "type": "number",    "value": 5,    "numstr": 2  },
      {    "type": "(end)",    "numstr": 3  }
    ]
  • parser - получает массив токенов, анализирует его на соответствие ряду синтаксических правил и, при удачном стечении обстоятельств, выдаёт представление синтаксической структуры, называемое деревом синтаксического анализа; наше единственное выражение превратится вот в такое симпатичное деревце:
    {
      "type": "+",
      "left": {
        "type": "*",
        "left": {      "type": "number",      "value": 3,      "numstr": 1    },
        "right": {      "type": "number",      "value": 2,      "numstr": 1    }
      },
      "right": {    "type": "number",    "value": 5,    "numstr": 2  }
    }
    

    Наш анализ довольно примитивен, например, вот такое 4+3( он всё равно построит в виде дерева и расчёт "упадёт" уже на этапе оценивания.

  • evaluator - берёт дерево разбора, созданное парсером, и анализирует его. Эти функции-"оценщики" обычно рекурсивно обходят дерево разбора. Учитывая приведенное выше дерево синтаксиса, оценщик может сначала оценить левый операнд верхнего уровня операции "*", затем правый операнд, а затем вернуть результат сложения.

Теперь остаётся только всё вызвать:

let calculate = function (input) { 
 try {
  let tokens = lexems (input);
  let parseTree = parser (tokens);
  let output = evaluator (parseTree);
  return output;
 } 
 catch (e) { return e; }
};

Интерпретатор безразличен к разбиению на строки, так что номер строки numstr можно было и не тащить по структурам данных, но это сделано для более "человечного" сообщения об ошибках.

Полный исходник, наверное, проще всего увидеть из исходника приложенного файла (правая кнопка мыши на свободном месте страницы, пункт меню "Исходный код страницы", "Просмотреть источник" и т.п., название пункта зависит от браузера). Файл формата .html опубликован в кодировке Юникода utf-8, возможно, что-то будет ещё меняться в этом наброске.

 Открыть Interpreter.html в новом окне/вкладке


теги: textprocessing javascript программирование

20.03.2020, 19:36; рейтинг: 39