Проблема при создании компилятора Antlr4 в JavaScript: неожиданный вывод при преобразовании JavaScript в Python - PullRequest
0 голосов
/ 31 октября 2018

Я следовал этому руководству , которое описывает, как создать компилятор JavaScript для грамматики ANTLR4 (ECMAScript.g4). В качестве примера также описывается, как преобразовать что-то из JavaScript в Python с использованием методов visit (), visitChildren (), visitTerminal () и visitErrorNode (), реализованных с использованием функций файла ECMAScriptVisitor.js.

Для этого в качестве входных данных из JavaScript дается выражение {x: 1}, где выходные данные должны быть {'x': 1} в соответствии с принятым Python форматом выражений.

Все работало нормально, пока я не запустил программу, где я получил следующий вывод:

enter image description here

В чем может быть причина появления этой ошибки? Это ссылка на репозиторий github, куда я загрузил часть проекта, которую я до сих пор выполнил. Вот index.js:

const antlr4 = require('antlr4');
const ECMAScriptLexer = require('./lib/ECMAScriptLexer.js');
const ECMAScriptParser = require('./lib/ECMAScriptParser.js');
const PythonGenerator = require('./codegeneration/PythonGenerator.js');

const input = '{x: 1}';

const chars = new antlr4.InputStream(input);
const lexer = new ECMAScriptLexer.ECMAScriptLexer(chars);

lexer.strictMode = false; // do not use js strictMode

const tokens = new antlr4.CommonTokenStream(lexer);
const parser = new ECMAScriptParser.ECMAScriptParser(tokens);
const tree = parser.program();

console.log('JavaScript input:');
console.log(input);
console.log('Python output:');

const output = new PythonGenerator().start(tree);

console.log(output);

А вот и PythonGenerator.js:

const ECMAScriptVisitor = require('../lib/ECMAScriptVisitor').ECMAScriptVisitor;

class Visitor extends ECMAScriptVisitor {
  start(ctx) {
    return this.visitExpressionSequence(ctx);
  }

visitChildren(ctx) {
  let code = '';

  for (let i = 0; i < ctx.getChildCount(); i++) {
    code += this.visit(ctx.getChild(i));
  }

  return code.trim();
}

visitTerminal(ctx) {
  return ctx.getText();
}

visitPropertyExpressionAssignment(ctx) {
  const key = this.visit(ctx.propertyName());
  const value = this.visit(ctx.singleExpression());

  return `'${key}': ${value}`;
}

}
module.exports = Visitor;

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Первое, что следует отметить, это то, что вы вызываете start с объектом ProgContext, а затем вызываете visitExpressionSequence для него. Это на самом деле не имеет значения в этом случае, потому что вы не переопределяете ни vistExpressionSequence, ни visitProg, так что на самом деле оба по умолчанию просто вызывают visitChildren, поэтому не имеет значения, какой из них вы вызываете. Тем не менее, вы действительно должны вызывать visit только явно, а не какой-либо из методов visitFoo. visit всегда будет следить за тем, чтобы был вызван правильный метод visitFoo.

Переходя к актуальной проблеме, у вас есть две проблемы: во-первых, ключ в вашем выводе не имеет кавычек, даже если ваш метод visitPropertyExpressionAssignment должен выполнить именно это. А во-вторых, в конце вывода есть <EOF>, который, я полагаю, вам не нужен.

Первая проблема заключается в том, что ваш ввод при синтаксическом анализе выдает синтаксическую ошибку. Это связано с тем, что {x: 1} на самом деле не является допустимой программой на JavaScript, поскольку { в начале оператора рассматривается как начало блока, а не как литерал объекта. Вам нужно было бы заключить скобки вокруг литерала, чтобы сделать его действительным в начале оператора.

Вторая проблема заключается в том, что правило prog завершается в конце файла, создавая маркер EOF. Чтобы не печатать это, вы можете просто переопределить visitProg, чтобы вызывать только visit в своем списке выписок, а не в EOF токене.

Вы устранили первую проблему, вызвав правило expressionSequence в своем парсере вместо prog. Если анализировать выражение вместо программы, это нормально (хотя в контексте, подобном REPL, вы можете вместо этого попытаться проанализировать входные данные как выражение и, если это не получится, проанализировать его как оператор). Может показаться, что это также решит вашу вторую проблему, но это не совсем так:

Вы больше не получаете <EOF> в выводе, потому что вы больше не соответствует концу файла. Это означает, что если входные данные состоят из действительного выражения, за которым следует полная фигня (скажем, const input = '{x: 1} krgsfkjhwruei';), это не вызовет синтаксической ошибки, а вместо этого удачно проанализирует часть {x: 1}, а затем полностью проигнорирует часть мусора без Любое указание на проблему. Это почти никогда не то, что вы хотите. Вместо этого вы можете определить новое правило в вашей грамматике, которое соответствует выражению, за которым следует конец файла, например:

expressionInput: expressionSequence EOF;

Теперь, если вход содержит мусор после правильного выражения, вы получите синтаксическую ошибку. Однако это вновь введет проблему <EOF> в выводе. Но опять же, вы можете просто исправить это, переопределив visitExpressionInput, а затем вызывая только visit(ctx.expressionSequence);.

0 голосов
/ 31 октября 2018

Я обнаружил, что после замены оператора const tree = parser.program(); в index.js на const tree = parser.expressionSequence(); проблема решена.

...