summaryrefslogtreecommitdiff
path: root/prism
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2023-11-02 11:15:37 -0400
committerKevin Newton <kddnewton@gmail.com>2023-11-03 10:13:49 -0400
commit2a0f2b776325f949ba6d311b0d90cbaaea825b63 (patch)
tree0256f169a54c47eeb1db1e8b04d1f0c6b7860d1e /prism
parenta43a52d415dff3868297ab070da4d4fd820f6b37 (diff)
[ruby/prism] Create an options struct for passing all of the possible options
https://github.com/ruby/prism/commit/99e81619de
Diffstat (limited to 'prism')
-rw-r--r--prism/extension.c92
-rw-r--r--prism/options.c96
-rw-r--r--prism/options.h161
-rw-r--r--prism/prism.c58
-rw-r--r--prism/prism.h5
-rw-r--r--prism/templates/src/serialize.c.erb6
-rw-r--r--prism/util/pm_newline_list.c27
-rw-r--r--prism/util/pm_newline_list.h12
8 files changed, 425 insertions, 32 deletions
diff --git a/prism/extension.c b/prism/extension.c
index 42268c8538..ed166a5176 100644
--- a/prism/extension.c
+++ b/prism/extension.c
@@ -61,14 +61,14 @@ input_load_string(pm_string_t *input, VALUE string) {
* Dump the AST corresponding to the given input to a string.
*/
static VALUE
-dump_input(pm_string_t *input, const char *filepath) {
+dump_input(pm_string_t *input, const pm_options_t *options) {
pm_buffer_t buffer;
if (!pm_buffer_init(&buffer)) {
rb_raise(rb_eNoMemError, "failed to allocate memory");
}
pm_parser_t parser;
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath);
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
pm_node_t *node = pm_parse(&parser);
pm_serialize(&parser, node, &buffer);
@@ -103,7 +103,11 @@ dump(int argc, VALUE *argv, VALUE self) {
pm_string_constant_init(&input, dup, length);
#endif
- VALUE value = dump_input(&input, check_string(filepath));
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, check_string(filepath));
+
+ VALUE value = dump_input(&input, &options);
+ pm_options_free(&options);
#ifdef PRISM_DEBUG_MODE_BUILD
free(dup);
@@ -125,7 +129,12 @@ dump_file(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
- VALUE value = dump_input(&input, checked);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ VALUE value = dump_input(&input, &options);
+
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -316,9 +325,9 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) {
* the nodes and tokens.
*/
static VALUE
-parse_lex_input(pm_string_t *input, const char *filepath, bool return_nodes) {
+parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) {
pm_parser_t parser;
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath);
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback);
VALUE offsets = rb_ary_new();
@@ -385,7 +394,13 @@ lex(int argc, VALUE *argv, VALUE self) {
pm_string_t input;
input_load_string(&input, string);
- return parse_lex_input(&input, check_string(filepath), false);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, check_string(filepath));
+
+ VALUE result = parse_lex_input(&input, &options, false);
+ pm_options_free(&options);
+
+ return result;
}
/**
@@ -401,7 +416,12 @@ lex_file(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
- VALUE value = parse_lex_input(&input, checked, false);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ VALUE value = parse_lex_input(&input, &options, false);
+
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -415,9 +435,9 @@ lex_file(VALUE self, VALUE filepath) {
* Parse the given input and return a ParseResult instance.
*/
static VALUE
-parse_input(pm_string_t *input, const char *filepath) {
+parse_input(pm_string_t *input, const pm_options_t *options) {
pm_parser_t parser;
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath);
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
pm_node_t *node = pm_parse(&parser);
rb_encoding *encoding = rb_enc_find(parser.encoding.name);
@@ -462,7 +482,11 @@ parse(int argc, VALUE *argv, VALUE self) {
pm_string_constant_init(&input, dup, length);
#endif
- VALUE value = parse_input(&input, check_string(filepath));
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, check_string(filepath));
+
+ VALUE value = parse_input(&input, &options);
+ pm_options_free(&options);
#ifdef PRISM_DEBUG_MODE_BUILD
free(dup);
@@ -484,7 +508,11 @@ parse_file(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
- VALUE value = parse_input(&input, checked);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ VALUE value = parse_input(&input, &options);
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -494,9 +522,9 @@ parse_file(VALUE self, VALUE filepath) {
* Parse the given input and return an array of Comment objects.
*/
static VALUE
-parse_input_comments(pm_string_t *input, const char *filepath) {
+parse_input_comments(pm_string_t *input, const pm_options_t *options) {
pm_parser_t parser;
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath);
+ pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
pm_node_t *node = pm_parse(&parser);
rb_encoding *encoding = rb_enc_find(parser.encoding.name);
@@ -525,7 +553,13 @@ parse_comments(int argc, VALUE *argv, VALUE self) {
pm_string_t input;
input_load_string(&input, string);
- return parse_input_comments(&input, check_string(filepath));
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, check_string(filepath));
+
+ VALUE result = parse_input_comments(&input, &options);
+ pm_options_free(&options);
+
+ return result;
}
/**
@@ -541,7 +575,12 @@ parse_file_comments(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
- VALUE value = parse_input_comments(&input, checked);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ VALUE value = parse_input_comments(&input, &options);
+
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -568,7 +607,12 @@ parse_lex(int argc, VALUE *argv, VALUE self) {
pm_string_t input;
input_load_string(&input, string);
- VALUE value = parse_lex_input(&input, check_string(filepath), true);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, check_string(filepath));
+
+ VALUE value = parse_lex_input(&input, &options, true);
+
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -593,7 +637,12 @@ parse_lex_file(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
- VALUE value = parse_lex_input(&input, checked, true);
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
+ VALUE value = parse_lex_input(&input, &options, true);
+
+ pm_options_free(&options);
pm_string_free(&input);
return value;
@@ -670,13 +719,16 @@ profile_file(VALUE self, VALUE filepath) {
const char *checked = check_string(filepath);
if (!pm_string_mapped_init(&input, checked)) return Qnil;
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, checked);
+
pm_parser_t parser;
- pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), checked);
+ pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), &options);
pm_node_t *node = pm_parse(&parser);
pm_node_destroy(&parser, node);
pm_parser_free(&parser);
-
+ pm_options_free(&options);
pm_string_free(&input);
return Qnil;
diff --git a/prism/options.c b/prism/options.c
new file mode 100644
index 0000000000..7f45c2026c
--- /dev/null
+++ b/prism/options.c
@@ -0,0 +1,96 @@
+#include "prism/options.h"
+
+/**
+ * Set the filepath option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_filepath_set(pm_options_t *options, const char *filepath) {
+ options->filepath = filepath;
+}
+
+/**
+ * Set the encoding option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_encoding_set(pm_options_t *options, const char *encoding) {
+ options->encoding = encoding;
+}
+
+/**
+ * Set the line option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_line_set(pm_options_t *options, uint32_t line) {
+ options->line = line;
+}
+
+/**
+ * Set the frozen string literal option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) {
+ options->frozen_string_literal = frozen_string_literal;
+}
+
+/**
+ * Set the suppress warnings option on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings) {
+ options->suppress_warnings = suppress_warnings;
+}
+
+/**
+ * Allocate and zero out the scopes array on the given options struct.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_scopes_init(pm_options_t *options, size_t scopes_count) {
+ options->scopes_count = scopes_count;
+ options->scopes = calloc(scopes_count, sizeof(pm_options_scope_t));
+ if (options->scopes == NULL) abort();
+}
+
+/**
+ * Return a pointer to the scope at the given index within the given options.
+ */
+PRISM_EXPORTED_FUNCTION const pm_options_scope_t *
+pm_options_scope_get(const pm_options_t *options, size_t index) {
+ return &options->scopes[index];
+}
+
+/**
+ * Create a new options scope struct. This will hold a set of locals that are in
+ * scope surrounding the code that is being parsed.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) {
+ scope->locals_count = locals_count;
+ scope->locals = calloc(locals_count, sizeof(pm_string_t));
+ if (scope->locals == NULL) abort();
+}
+
+/**
+ * Return a pointer to the local at the given index within the given scope.
+ */
+PRISM_EXPORTED_FUNCTION const pm_string_t *
+pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) {
+ return &scope->locals[index];
+}
+
+/**
+ * Free the internal memory associated with the options.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_free(pm_options_t *options) {
+ for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
+ pm_options_scope_t *scope = &options->scopes[scope_index];
+
+ for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
+ pm_string_free(&scope->locals[local_index]);
+ }
+
+ free(scope->locals);
+ }
+
+ free(options->scopes);
+}
diff --git a/prism/options.h b/prism/options.h
new file mode 100644
index 0000000000..5abf16a601
--- /dev/null
+++ b/prism/options.h
@@ -0,0 +1,161 @@
+/**
+ * @file options.h
+ *
+ * The options that can be passed to parsing.
+ */
+#ifndef PRISM_OPTIONS_H
+#define PRISM_OPTIONS_H
+
+#include "prism/defines.h"
+#include "prism/util/pm_string.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * A scope of locals surrounding the code that is being parsed.
+ */
+typedef struct pm_options_scope {
+ /** The number of locals in the scope. */
+ size_t locals_count;
+
+ /** The names of the locals in the scope. */
+ pm_string_t *locals;
+} pm_options_scope_t;
+
+/**
+ * The options that can be passed to the parser.
+ */
+typedef struct {
+ /** The name of the file that is currently being parsed. */
+ const char *filepath;
+
+ /**
+ * The name of the encoding that the source file is in. Note that this must
+ * correspond to a name that can be found with Encoding.find in Ruby.
+ */
+ const char *encoding;
+
+ /**
+ * The line within the file that the parse starts on. This value is
+ * 0-indexed.
+ */
+ uint32_t line;
+
+ /**
+ * The number of scopes surrounding the code that is being parsed.
+ */
+ size_t scopes_count;
+
+ /**
+ * The scopes surrounding the code that is being parsed. For most parses
+ * this will be NULL, but for evals it will be the locals that are in scope
+ * surrounding the eval.
+ */
+ pm_options_scope_t *scopes;
+
+ /** Whether or not the frozen string literal option has been set. */
+ bool frozen_string_literal;
+
+ /**
+ * Whether or not we should suppress warnings. This is purposefully negated
+ * so that the default is to not suppress warnings, which allows us to still
+ * create an options struct with zeroed memory.
+ */
+ bool suppress_warnings;
+} pm_options_t;
+
+/**
+ * Set the filepath option on the given options struct.
+ *
+ * @param options The options struct to set the filepath on.
+ * @param filepath The filepath to set.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_filepath_set(pm_options_t *options, const char *filepath);
+
+/**
+ * Set the encoding option on the given options struct.
+ *
+ * @param options The options struct to set the encoding on.
+ * @param encoding The encoding to set.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_encoding_set(pm_options_t *options, const char *encoding);
+
+/**
+ * Set the line option on the given options struct.
+ *
+ * @param options The options struct to set the line on.
+ * @param line The line to set.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_line_set(pm_options_t *options, uint32_t line);
+
+/**
+ * Set the frozen string literal option on the given options struct.
+ *
+ * @param options The options struct to set the frozen string literal value on.
+ * @param frozen_string_literal The frozen string literal value to set.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal);
+
+/**
+ * Set the suppress warnings option on the given options struct.
+ *
+ * @param options The options struct to set the suppress warnings value on.
+ * @param suppress_warnings The suppress warnings value to set.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings);
+
+/**
+ * Allocate and zero out the scopes array on the given options struct.
+ *
+ * @param options The options struct to initialize the scopes array on.
+ * @param scopes_count The number of scopes to allocate.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_scopes_init(pm_options_t *options, size_t scopes_count);
+
+/**
+ * Return a pointer to the scope at the given index within the given options.
+ *
+ * @param options The options struct to get the scope from.
+ * @param index The index of the scope to get.
+ * @return A pointer to the scope at the given index.
+ */
+PRISM_EXPORTED_FUNCTION const pm_options_scope_t *
+pm_options_scope_get(const pm_options_t *options, size_t index);
+
+/**
+ * Create a new options scope struct. This will hold a set of locals that are in
+ * scope surrounding the code that is being parsed.
+ *
+ * @param scope The scope struct to initialize.
+ * @param locals_count The number of locals to allocate.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count);
+
+/**
+ * Return a pointer to the local at the given index within the given scope.
+ *
+ * @param scope The scope struct to get the local from.
+ * @param index The index of the local to get.
+ * @return A pointer to the local at the given index.
+ */
+PRISM_EXPORTED_FUNCTION const pm_string_t *
+pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index);
+
+/**
+ * Free the internal memory associated with the options.
+ *
+ * @param options The options struct whose internal memory should be freed.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_options_free(pm_options_t *options);
+
+#endif
diff --git a/prism/prism.c b/prism/prism.c
index de32ccae63..fc836a956d 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -16319,14 +16319,9 @@ pm_parser_metadata(pm_parser_t *parser, const char *metadata) {
* Initialize a parser with the given start and end pointers.
*/
PRISM_EXPORTED_FUNCTION void
-pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const char *filepath) {
+pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
assert(source != NULL);
- // Set filepath to the file that was passed
- if (!filepath) filepath = "";
- pm_string_t filepath_string;
- pm_string_constant_init(&filepath_string, filepath, strlen(filepath));
-
*parser = (pm_parser_t) {
.lex_state = PM_LEX_STATE_BEG,
.enclosure_nesting = 0,
@@ -16356,7 +16351,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch
.encoding_decode_callback = NULL,
.encoding_comment_start = source,
.lex_callback = NULL,
- .filepath_string = filepath_string,
+ .filepath_string = { 0 },
.constant_pool = { 0 },
.newline_list = { 0 },
.integer_base = 0,
@@ -16370,8 +16365,6 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch
.frozen_string_literal = false
};
- pm_accepts_block_stack_push(parser, true);
-
// Initialize the constant pool. We're going to completely guess as to the
// number of constants that we'll need based on the size of the input. The
// ratio we chose here is actually less arbitrary than you might think.
@@ -16395,6 +16388,53 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch
size_t newline_size = size / 22;
pm_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size);
+ // If options were provided to this parse, establish them here.
+ if (options != NULL) {
+ // filepath option
+ if (options->filepath == NULL) {
+ pm_string_constant_init(&parser->filepath_string, "", 0);
+ } else {
+ pm_string_constant_init(&parser->filepath_string, options->filepath, strlen(options->filepath));
+ }
+
+ // line option
+ if (options->line > 0) {
+ pm_newline_list_force(&parser->newline_list, options->line);
+ }
+
+ // encoding option
+ // if (options->encoding != NULL) {}
+
+ // frozen_string_literal option
+ if (options->frozen_string_literal) {
+ parser->frozen_string_literal = true;
+ }
+
+ // suppress_warnings option
+ // if (options->suppress_warnings) {}
+
+ // scopes option
+ for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
+ const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index);
+ pm_parser_scope_push(parser, scope_index == 0);
+
+ for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
+ const pm_string_t *local = pm_options_scope_local_get(scope, local_index);
+
+ const uint8_t *source = pm_string_source(local);
+ size_t length = pm_string_length(local);
+
+ uint8_t *allocated = malloc(length);
+ if (allocated == NULL) continue;
+
+ memcpy((void *) allocated, source, length);
+ pm_parser_local_add_owned(parser, allocated, length);
+ }
+ }
+ }
+
+ pm_accepts_block_stack_push(parser, true);
+
// Skip past the UTF-8 BOM if it exists.
if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
parser->current.end += 3;
diff --git a/prism/prism.h b/prism/prism.h
index d6b18a44a8..84bd3f84a7 100644
--- a/prism/prism.h
+++ b/prism/prism.h
@@ -15,6 +15,7 @@
#include "prism/ast.h"
#include "prism/diagnostic.h"
#include "prism/node.h"
+#include "prism/options.h"
#include "prism/pack.h"
#include "prism/parser.h"
#include "prism/prettyprint.h"
@@ -47,9 +48,9 @@ PRISM_EXPORTED_FUNCTION const char * pm_version(void);
* @param parser The parser to initialize.
* @param source The source to parse.
* @param size The size of the source.
- * @param filepath The optional filepath to pass to the parser.
+ * @param options The optional options to use when parsing.
*/
-PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const char *filepath);
+PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options);
/**
* Register a callback that will be called whenever prism changes the encoding
diff --git a/prism/templates/src/serialize.c.erb b/prism/templates/src/serialize.c.erb
index 5e166f1eef..db10fab6de 100644
--- a/prism/templates/src/serialize.c.erb
+++ b/prism/templates/src/serialize.c.erb
@@ -288,8 +288,11 @@ serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) {
*/
PRISM_EXPORTED_FUNCTION void
pm_lex_serialize(const uint8_t *source, size_t size, const char *filepath, pm_buffer_t *buffer) {
+ pm_options_t options = { 0 };
+ pm_options_filepath_set(&options, filepath);
+
pm_parser_t parser;
- pm_parser_init(&parser, source, size, filepath);
+ pm_parser_init(&parser, source, size, &options);
pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
.data = (void *) buffer,
@@ -310,6 +313,7 @@ pm_lex_serialize(const uint8_t *source, size_t size, const char *filepath, pm_bu
pm_node_destroy(&parser, node);
pm_parser_free(&parser);
+ pm_options_free(&options);
}
/**
diff --git a/prism/util/pm_newline_list.c b/prism/util/pm_newline_list.c
index f27bb75b63..978ebf3d0e 100644
--- a/prism/util/pm_newline_list.c
+++ b/prism/util/pm_newline_list.c
@@ -20,6 +20,33 @@ pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capac
}
/**
+ * Set up the newline list such that it believes it is starting on a specific
+ * line in the source. Basically this entails pushing on pointers to the start
+ * of the string until we hit the desired line.
+ */
+bool
+pm_newline_list_force(pm_newline_list_t *list, size_t count) {
+ size_t next_capacity = list->capacity == 0 ? 1 : list->capacity;
+ while (count > next_capacity) {
+ next_capacity *= 2;
+ }
+
+ size_t *offsets = list->offsets;
+ list->offsets = (size_t *) calloc(next_capacity, sizeof(size_t));
+ if (list->offsets == NULL) return false;
+
+ if (offsets != NULL) {
+ memcpy(list->offsets, offsets, list->size * sizeof(size_t));
+ free(offsets);
+ }
+
+ memset(list->offsets + list->size, 0, count * sizeof(size_t));
+ list->size += count;
+
+ return true;
+}
+
+/**
* Append a new offset to the newline list. Returns true if the reallocation of
* the offsets succeeds (if one was necessary), otherwise returns false.
*/
diff --git a/prism/util/pm_newline_list.h b/prism/util/pm_newline_list.h
index a31051f4e0..93816b0656 100644
--- a/prism/util/pm_newline_list.h
+++ b/prism/util/pm_newline_list.h
@@ -62,6 +62,18 @@ typedef struct {
bool pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity);
/**
+ * Set up the newline list such that it believes it is starting on a specific
+ * line in the source. Basically this entails pushing on pointers to the start
+ * of the string until we hit the desired line.
+ *
+ * @param list The list to set up.
+ * @param count The number of lines to push onto the list.
+ * @return True if no reallocation was needed or the reallocation of the offsets
+ * succeeds (if one was necessary), otherwise false.
+ */
+bool pm_newline_list_force(pm_newline_list_t *list, size_t count);
+
+/**
* Append a new offset to the newline list. Returns true if the reallocation of
* the offsets succeeds (if one was necessary), otherwise returns false.
*