Skip to content

Commit dc8d3a5

Browse files
committed
Port diagnostics format to Prism from CRuby
1 parent f277c70 commit dc8d3a5

17 files changed

Lines changed: 1272 additions & 174 deletions

File tree

ext/prism/extension.c

Lines changed: 270 additions & 100 deletions
Large diffs are not rendered by default.

include/prism.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern "C" {
1414
#include "prism/ast.h"
1515
#include "prism/buffer.h"
1616
#include "prism/diagnostic.h"
17+
#include "prism/errors_format.h"
1718
#include "prism/json.h"
1819
#include "prism/node.h"
1920
#include "prism/options.h"

include/prism/errors_format.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @file errors_format.h
3+
*
4+
* A function for formatting the errors in a parser into a buffer.
5+
*/
6+
#ifndef PRISM_ERRORS_FORMAT_H
7+
#define PRISM_ERRORS_FORMAT_H
8+
9+
#include "prism/compiler/exported.h"
10+
#include "prism/compiler/nodiscard.h"
11+
#include "prism/compiler/nonnull.h"
12+
13+
#include "prism/buffer.h"
14+
#include "prism/diagnostic.h"
15+
#include "prism/parser.h"
16+
17+
/** The type of formatting to use when formatting errors. */
18+
typedef enum {
19+
/** Format errors in a plain format with no colors or styles. Typically
20+
* used when outputting to a file or when the output is not a terminal.
21+
*/
22+
PM_ERRORS_FORMAT_PLAIN = 1,
23+
24+
/** Format errors in a style format with bold only. Typically used when
25+
* outputting to a terminal where you do not want colors.
26+
*/
27+
PM_ERRORS_FORMAT_STYLE = 2,
28+
29+
/** Format errors in a color format with bold and colors. The default case
30+
* when outputting on the terminal.
31+
*/
32+
PM_ERRORS_FORMAT_COLOR = 3
33+
} pm_errors_format_type_t;
34+
35+
/**
36+
* Format the errors in a parser into a buffer. After return, the buffer will
37+
* contain the formatted errors and any relevant source snippets if available
38+
* and possible.
39+
*
40+
* @param parser The parser containing the errors to format. Note that the
41+
* parser does not need to be fully initialized, but it will need to have
42+
* start, end, start_line, encoding, line_offsets, filepath, and error_list
43+
* initialized.
44+
* @param buffer The buffer to format the errors into.
45+
* @param format_type The type of formatting to use when formatting the errors.
46+
* @returns The error level of the error with the highest precedence that was
47+
* formatted. In practice this means it will be the argument or load error
48+
* level if any were found, otherwise it will be the syntax error level.
49+
* Callers should raise appropriate error types based on the returned level.
50+
*/
51+
PRISM_EXPORTED_FUNCTION pm_error_level_t pm_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, pm_errors_format_type_t format_type) PRISM_NONNULL(1, 2);
52+
53+
#endif

include/prism/internal/options.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ struct pm_options_t {
105105
*/
106106
int8_t frozen_string_literal;
107107

108+
/*
109+
* Whether or not the parser should raise an error when it encounters an
110+
* error. By default this is off, but can be turned on in the case that you
111+
* want nicely formatted syntax errors. The values that this can be set to
112+
* are:
113+
* - 0 - do not raise an error, just return the errors in the result
114+
* - PM_ERRORS_FORMAT_PLAIN - raise a plain error with no formatting
115+
* - PM_ERRORS_FORMAT_STYLE - raise an error with bold formatting
116+
* - PM_ERRORS_FORMAT_COLOR - raise an error with color formatting
117+
*/
118+
uint8_t raise_error;
119+
108120
/*
109121
* Whether or not the encoding magic comments should be respected. This is a
110122
* niche use-case where you want to parse a file with a specific encoding

include/prism/options.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,22 @@ PRISM_EXPORTED_FUNCTION bool pm_options_freeze(const pm_options_t *options) PRIS
249249
*/
250250
PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze) PRISM_NONNULL(1);
251251

252+
/**
253+
* Get the raise_error option on the given options struct.
254+
*
255+
* @param options The options struct to get the raise_error value from.
256+
* @returns The raise_error value.
257+
*/
258+
PRISM_EXPORTED_FUNCTION uint8_t pm_options_raise_error(const pm_options_t *options) PRISM_NONNULL(1);
259+
260+
/**
261+
* Set the raise_error option on the given options struct.
262+
*
263+
* @param options The options struct to set the raise_error value on.
264+
* @param raise_error The raise_error value to set.
265+
*/
266+
PRISM_EXPORTED_FUNCTION void pm_options_raise_error_set(pm_options_t *options, uint8_t raise_error) PRISM_NONNULL(1);
267+
252268
/**
253269
* Allocate and zero out the scopes array on the given options struct.
254270
*

include/prism/serialize.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "prism/compiler/nonnull.h"
1818

1919
#include "prism/buffer.h"
20+
#include "prism/errors_format.h"
2021
#include "prism/parser.h"
2122
#include "prism/source.h"
2223
#include "prism/stream.h"
@@ -91,6 +92,24 @@ PRISM_EXPORTED_FUNCTION void pm_serialize_parse_lex(pm_buffer_t *buffer, const u
9192
*/
9293
PRISM_EXPORTED_FUNCTION bool pm_serialize_parse_success_p(const uint8_t *source, size_t size, const char *data) PRISM_NONNULL(1);
9394

95+
/**
96+
* Parse the given source and format any errors that are encountered into the
97+
* given buffer using the given format type. If the source parses without any
98+
* errors, then -1 is returned and the buffer is left empty. Otherwise, the
99+
* name of the encoding of the source is written to the buffer, followed by a
100+
* null byte, followed by the formatted errors, and the error level of the
101+
* error with the highest precedence is returned.
102+
*
103+
* @param buffer The buffer to write the encoding name and formatted errors to.
104+
* @param source The source to parse.
105+
* @param size The size of the source.
106+
* @param data The optional data to pass to the parser.
107+
* @param format_type The type of formatting to use when formatting the errors.
108+
* @returns The error level of the error with the highest precedence, or -1 if
109+
* the source parsed without errors.
110+
*/
111+
PRISM_EXPORTED_FUNCTION int8_t pm_serialize_parse_errors_format(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data, pm_errors_format_type_t format_type) PRISM_NONNULL(1, 2);
112+
94113
#endif
95114

96115
#endif

lib/prism.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,23 @@ def self.find(callable, rubyvm: !!defined?(RubyVM))
103103
# def gets: (?Integer integer) -> (String | nil)
104104
# end
105105
#
106-
# def self.parse: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
107-
# def self.profile: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> void
108-
# def self.lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult
109-
# def self.parse_lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult
110-
# def self.dump: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> String
111-
# def self.parse_comments: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment]
112-
# def self.parse_success?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
113-
# def self.parse_failure?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
114-
# def self.parse_stream: (_Stream stream, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
115-
# def self.parse_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
116-
# def self.profile_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> void
117-
# def self.lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult
118-
# def self.parse_lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult
119-
# def self.dump_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> String
120-
# def self.parse_file_comments: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment]
121-
# def self.parse_file_success?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
122-
# def self.parse_file_failure?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
106+
# def self.parse: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
107+
# def self.profile: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> void
108+
# def self.lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult
109+
# def self.parse_lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult
110+
# def self.dump: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> String
111+
# def self.parse_comments: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment]
112+
# def self.parse_success?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
113+
# def self.parse_failure?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
114+
# def self.parse_stream: (_Stream stream, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
115+
# def self.parse_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult
116+
# def self.profile_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> void
117+
# def self.lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult
118+
# def self.parse_lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult
119+
# def self.dump_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> String
120+
# def self.parse_file_comments: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment]
121+
# def self.parse_file_success?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
122+
# def self.parse_file_failure?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?raise_error: Symbol | true, ?scopes: Array[Array[Symbol]], ?version: String) -> bool
123123
end
124124

125125
require_relative "prism/polyfill/byteindex"

0 commit comments

Comments
 (0)