Skip to content

Tutorial 8: Debugging

Ilya Lakhin edited this page Nov 22, 2013 · 3 revisions

There are a number of approaches to debug programming language parsers. And probably there is no the best solution. This artical describes approach that is used by Papa Carlo to test and debug it's own example parsers. And it could be adopted to third-party parsers made with Papa Carlo.

In the test directory of the Repository you can find two functional scala-tests of the example parsers. Each Test Spec loads different versions of the prepared source code files, listens to the logs of the various parser's components, and comparing them with prototype logs.

Utils directory contains a number of so called Monitor classes. Each monitor is responsible for listening the logs of specific parser's components. And the abstract class ParserSpec is a ready to use prototype of the configurable Test Spec that utilizes these monitors to make resulting output logs, and compare them with appropriate prototype logs.

Example of test spec for Calculator parser:

class CalculatorSpec extends ParserSpec(
  // Name of the directory contains approppriate resource files
  // and json configuration.
  parserName = "calculator",

  // Function-constructor of the lexical parser
  lexerConstructor = Calculator.lexer _,

  // Function-constructor of the syntax parser
  syntaxConstructor = Calculator.syntax
)

This test spec reads configuration details from the appropriate json file placed in resource dir:

{
    "expressions": {
        "steps": 3,
        "independentSteps": true,
        "monitors": ["node", "error", "token"]
    },
    "recovery": {
        "steps": 2,
        "independentSteps": true,
        "monitors": ["node", "error", "token"]
    }
}

The configuration lists specific test suites and their options. The name of test suite should match the name of the relative resource directory consists of input and prototype files. In this example input files should be placed to the src/test/resources/fixtures/calculator/expressions/input directory, and prototypes to the src/test/resources/fixtures/calculator/expressions/prototype directory.

This is description of the possible test suite configuration options:

Option Description
steps Number of input source code versions. Optional parameter. By default - the number of files found in the "input" directory. By convention each input file should be put to the "input" dir, and have name step<n>.txt, where the n - is version number starting from 0.
monitors Array of Monitor string names that should be used for this suite. By default all monitors are turned on.
shortOutput Boolean flag indicates whether the output logs should be simplified or not. Turned off by default.
outputFrom Number of the first input file that should be logged. All preceded input files will be processed by the parser, but their logs ignored. 0 by default.
independentSteps Boolean flag indicates whether the parser should be reset on each input step. Turned off by default. Turning on disables incremental parsing feature.

The list of provided Monitors:

  • "token": Logs all tokens and their fragment context. Useful to debug Tokenizer and Fragment specification of the parser.
  • "fragment": Logs fragments that were created, deleted and invalidated by FragmentController.
  • "cache": Logs fragment and appropriate node creation/deletion/invalidation events of internal AST caching process.
  • "node": Logs Node's creation and deletion events of the Syntax parser. And also the AST branch that were updated(so called "merge") during the parse phase.
  • "error": Logs syntax errors happens during the Syntax parsing step.

TestSpec puts together Monitor's output, comparing it with prototype logs got from appropriate resource files, and updates output files in relative "output" resource dir. So developer may manually review log output and/or update appropriate prototype files using these output files.