Reporting Errors

Good language tools are able to not only provide functionality, but report errors if and when they occur, in a clear and informative manner. scarf places priority on this; the user should be able to easily understand any error, as well as where changes need to be made.

This is reflected in the Span class, which accompanies many objects (including Tokens, Nodes, and any errors) and provides an exact location in the source files, so that errors may be reported.

To report these errors, the Report class can be used. This class is created with a particular error location, but can have many labeled sections of source code included in the error printout (see Report.label). For example, take the following SystemVerilog code, referred to as test.v:

module test_module(;
endmodule

Notice how we forgot the ending parentheses before the semicolon. Attempting to parse() this will result in a ParserResult.ParserErr; the contained VerboseError provides a Span that could be used to construct a Report

from scarf_python import parse, ParserResult, Report, ReportKind

with open("test.v", "r") as f:
    content = f.read()

parse_result = parse(content, "test.v", ["."], [])
assert isinstance(parse_result, ParserResult.ParserErr)
err_span = parse_result.error.span

report = Report(ReportKind.Error(), err_span, "ERR", "Missing a )")
report.label(err_span, ReportKind.Error(), "Put a ) here")
report.print()

To display the content to a user, use the Report.print() or Report.eprint() methods to print a Report to stdout or stderr, respectively. Calling one of these on the above report produces the following output

[ERR] Error: Missing a )
   ╭─[ test.v:1:20 ]
   │
 1 │ module test_module(;
   │                    ┬
   │                    ╰── Put a ) here
───╯

For convenience, both VerboseError and PreprocessorError also contain a report method, which produces a Report suitable for printing; one could instead modify the above code to use this directly

from scarf_python import parse, ParserResult, Report, ReportKind

with open("test.v", "r") as f:
    content = f.read()

parse_result = parse(content, "test.v", ["."], [])
assert isinstance(parse_result, ParserResult.ParserErr)
report = parse_result.error.report()
report.print()

which produces the following verbose but informative message

[P1] Error: found ;, expected ), ., an identifier, {, a comma, (, input, output, inout, ref, supply0, supply1, tri, triand, trior, trireg, tri0, tri1, uwire, wire, wand, wor, $unit, signed, unsigned, [, interface, bit, logic, reg, byte, shortint, int, longint, integer, time, shortreal, real, realtime, struct, union, enum, string, chandle, virtual, event, type, interconnect, or var
   ╭─[ test.v:1:20 ]
   │
 1 │ module test_module(;
   │                    ┬
   │                    ╰── Didn't expect ;
───╯

Finally, if you ever find yourself using Span whose Span.file doesn’t exist, use Report.include() to include file content associated with a particular name, so that a Report can print appropriately