Если вы читаете это, вы признаете необходимость разбираться в абстрактных синтаксических деревьях и уже прочитали учебник здесь. Если вы не читали букварь, предлагаю вам начать с него.
Визуализация - отличный способ запечатлеть ментальные модели; особенно для установления интимной связи с данными. Учитывая, что AST - это деревья, имеет смысл визуализировать данные в иерархической структуре, чтобы обосновать нашу ментальную модель.
Инструмент, который вы создадите, состоит из 2 компонентов - редактора кода и вкладки с 3 разделами: структура дерева, использование конструкции и сам AST. Если вы хотите продолжить, возьмите копию с GitHub.
Благодаря программному обеспечению с открытым исходным кодом в нашем распоряжении есть все инструменты, позволяющие быстро собрать решение. Наш план действий:
- Создать макет страницы
- Вставьте редактор кода в левый контейнер
- Вставьте вкладку в правый контейнер
- Слушайте изменения кода и генерируйте AST
- Визуализировать AST
Для макета давайте воспользуемся materializecss - с этой библиотекой создание адаптивного макета займет пару шагов.
<div class="row">
<div class="col s12 m4 l3"></div>
<div class="col s12 m8 l9"> </div>
</div>
Теперь, когда у нас есть макет, давайте перейдем к созданию нашего первого компонента - редактора кода. Редакторы кода хороши тем, что позволяют пользователям копаться в ваших API и фрагментах кода. CodeFlask - один из таких редакторов микрокода, который можно использовать на веб-страницах. Его использование не может быть проще -
let flask = new CodeFlask; let defaultCode = "function init() {\n console.log('hello world'); \n} \ninit();";
flask.run('#my-code-wrapper', { language: 'js' }); flask.update(defaultCode);
Codeflask также предоставляет хуки onUpdate. По мере того, как пользователь вводит тип, полный блок кода передается в качестве параметра String функции обратного вызова.
flask.onUpdate((code) => {
let ast = generateAst(code);
renderVisuals(ast);
});
Двигаясь дальше, нам нужно преобразовать код в AST. Введите Esprima - при вызове метода синтаксического анализа для esprima со строкой кода в качестве параметра создается AST.
let ast = esprima.parse(code);
Как только AST сгенерирован, мир открыт для вас. Вы можете перемещаться по нему, чтобы внести необходимые изменения. В нашем случае мы используем библиотеку обхода ast-traverse для нормализации данных для визуализации. Если вы внимательный наблюдатель, вы могли заметить, что AST не обязательно имеет массив элементов в свойстве body. Поскольку наша библиотека визуализации требует, чтобы к родительскому элементу был прикреплен массив дочерних элементов, мы нормализуем данные, оборачивая все свойство тела объектом в виде массива. По мере того, как мы проходили, мы также можем изменять значения или любые другие конструкции, но для краткости этого достаточно.
Наконец-то мы подошли к последнему этапу нашего плана действий - визуализации AST. Поскольку мы также будем считать конструкции и отображать AST, давайте обернем весь наш контент на вкладки. К счастью для нас, materializecss предлагает виджет вкладок. Здорово! сшить их. Содержание нашей первой вкладки будет представлять визуальный элемент. Среди множества библиотек визуализации javascript давайте воспользуемся популярной библиотекой - d3.js. Он предлагает различные макеты, такие как связка, аккорд, сила, иерархия, раздел, круговая диаграмма, стек, гистограмма, упаковка, дерево, кластер и Древовидная карта. Наш интерес представляет древовидная структура.
Создание визуального элемента дерева с помощью d3.js включает объявление макета дерева, преобразование данных и сопоставление преобразованных данных с макетом дерева. В результате получился декорированный объект d3 на стероидах.
// declares a tree layout and assigns the size let treemap = d3.tree().size([width, height]);
// assigns the data to a hierarchy using parent-child relationships let nodes = d3.hierarchy(treeData);
// maps the node data to the tree layout nodes = treemap(nodes);
После преобразования и нормализации данных все готово к запуску. Первым делом создайте базовый элемент SVG.
var svg = d3.select(container).append("svg");
..
Затем создаем элементы SVG «Path» для соединения узлов. Возвращенный массив из вызова nodes.descendants () используется для создания путей с классом link.
let link = svg.selectAll(".link") .data(nodes.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.attr("d", function (d) {
return "M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y;
});
Тот же вызов nodes.descendants () также передает данные для создания элементов SVG «круга».
let node = g.selectAll(".node") .data(nodes.descendants()) .enter() .append("g") .attr("class", function (d) { return "node" + (d.children ? " node--internal" : " node--leaf"); }) .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("circle") .attr("r", 10);
После создания узлов мы добавляем к ним «текстовый» элемент.
node.append("text")
.attr({
"dy": ".35em",
"y": function (d) { return d.children ? -20 : 20; }
})
.text(function (d) { return d.data.type; });
Вот и все! Есть еще 2 вкладки, которые дополняют нашу визуализацию - Использование и AST. Чтобы подсчитать каждый тип конструкции и частоту ее использования, давайте вернемся к нашему набору инструментов для ast-traverse.
let hash = {};
traverse(ast,{ post: function (node) { hash.hasOwnProperty(node.type) ? hash[node.type] = hash[node.type] + 1 : hash[node.type] = 1; } });
Когда у нас есть объект с типом и количеством, создайте элемент таблицы, переберите каждое свойство и создайте строку.
Наконец, чтобы отобразить AST, создайте еще один экземпляр CodeFlask и обновите редактор с помощью AST. Это вполне может быть текстовое поле, но CodeFlask предлагает форматирование и цвета, так почему бы и нет.
Собирая все вместе, код в редакторе трансформируется в AST, который визуализируется и анализируется. В одном из следующих постов мы рассмотрим использование AST для решения реальных программных проблем.
Первоначально опубликовано на javascriptstore.com 15 октября 2017 г.