This commit is contained in:
Intege-rs
2026-08-15 08:44:02 -04:00
commit eb5172548a
102 changed files with 78046 additions and 0 deletions

View File

@@ -0,0 +1,383 @@
/**
* @file prism.h
*
* The main header file for the prism parser.
*/
#ifndef PRISM_H
#define PRISM_H
#include "prism/defines.h"
#include "prism/util/pm_buffer.h"
#include "prism/util/pm_char.h"
#include "prism/util/pm_integer.h"
#include "prism/util/pm_memchr.h"
#include "prism/util/pm_strncasecmp.h"
#include "prism/util/pm_strpbrk.h"
#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"
#include "prism/regexp.h"
#include "prism/static_literals.h"
#include "prism/version.h"
#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <strings.h>
#endif
/**
* The prism version and the serialization format.
*
* @returns The prism version as a constant string.
*/
PRISM_EXPORTED_FUNCTION const char * pm_version(void);
/**
* Initialize a parser with the given start and end pointers.
*
* @param parser The parser to initialize.
* @param source The source to parse.
* @param size The size of the source.
* @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 pm_options_t *options);
/**
* Register a callback that will be called whenever prism changes the encoding
* it is using to parse based on the magic comment.
*
* @param parser The parser to register the callback with.
* @param callback The callback to register.
*/
PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback);
/**
* Free any memory associated with the given parser.
*
* @param parser The parser to free.
*/
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser);
/**
* Initiate the parser with the given parser.
*
* @param parser The parser to use.
* @return The AST representing the source.
*/
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser);
/**
* This function is used in pm_parse_stream to retrieve a line of input from a
* stream. It closely mirrors that of fgets so that fgets can be used as the
* default implementation.
*/
typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream);
/**
* Parse a stream of Ruby source and return the tree.
*
* @param parser The parser to use.
* @param buffer The buffer to use.
* @param stream The stream to parse.
* @param stream_fgets The function to use to read from the stream.
* @param options The optional options to use when parsing.
* @return The AST representing the source.
*/
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options);
// We optionally support serializing to a binary string. For systems that don't
// want or need this functionality, it can be turned off with the
// PRISM_EXCLUDE_SERIALIZATION define.
#ifndef PRISM_EXCLUDE_SERIALIZATION
/**
* Parse and serialize the AST represented by the source that is read out of the
* given stream into to the given buffer.
*
* @param buffer The buffer to serialize to.
* @param stream The stream to parse.
* @param stream_fgets The function to use to read from the stream.
* @param data The optional data to pass to the parser.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data);
/**
* Serialize the given list of comments to the given buffer.
*
* @param parser The parser to serialize.
* @param list The list of comments to serialize.
* @param buffer The buffer to serialize to.
*/
void pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer);
/**
* Serialize the name of the encoding to the buffer.
*
* @param encoding The encoding to serialize.
* @param buffer The buffer to serialize to.
*/
void pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer);
/**
* Serialize the encoding, metadata, nodes, and constant pool.
*
* @param parser The parser to serialize.
* @param node The node to serialize.
* @param buffer The buffer to serialize to.
*/
void pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer);
/**
* Serialize the AST represented by the given node to the given buffer.
*
* @param parser The parser to serialize.
* @param node The node to serialize.
* @param buffer The buffer to serialize to.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer);
/**
* Parse the given source to the AST and dump the AST to the given buffer.
*
* @param buffer The buffer to serialize to.
* @param source The source to parse.
* @param size The size of the source.
* @param data The optional data to pass to the parser.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
/**
* Parse and serialize the comments in the given source to the given buffer.
*
* @param buffer The buffer to serialize to.
* @param source The source to parse.
* @param size The size of the source.
* @param data The optional data to pass to the parser.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
/**
* Lex the given source and serialize to the given buffer.
*
* @param source The source to lex.
* @param size The size of the source.
* @param buffer The buffer to serialize to.
* @param data The optional data to pass to the lexer.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
/**
* Parse and serialize both the AST and the tokens represented by the given
* source to the given buffer.
*
* @param buffer The buffer to serialize to.
* @param source The source to parse.
* @param size The size of the source.
* @param data The optional data to pass to the parser.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_parse_lex(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data);
#endif
/**
* Parse the source and return true if it parses without errors or warnings.
*
* @param source The source to parse.
* @param size The size of the source.
* @param data The optional data to pass to the parser.
* @return True if the source parses without errors or warnings.
*/
PRISM_EXPORTED_FUNCTION bool pm_parse_success_p(const uint8_t *source, size_t size, const char *data);
/**
* Returns a string representation of the given token type.
*
* @param token_type The token type to convert to a string.
* @return A string representation of the given token type.
*/
PRISM_EXPORTED_FUNCTION const char * pm_token_type_name(pm_token_type_t token_type);
/**
* Returns the human name of the given token type.
*
* @param token_type The token type to convert to a human name.
* @return The human name of the given token type.
*/
const char * pm_token_type_human(pm_token_type_t token_type);
// We optionally support dumping to JSON. For systems that don't want or need
// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define.
#ifndef PRISM_EXCLUDE_JSON
/**
* Dump JSON to the given buffer.
*
* @param buffer The buffer to serialize to.
* @param parser The parser that parsed the node.
* @param node The node to serialize.
*/
PRISM_EXPORTED_FUNCTION void pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node);
#endif
/**
* Represents the results of a slice query.
*/
typedef enum {
/** Returned if the encoding given to a slice query was invalid. */
PM_STRING_QUERY_ERROR = -1,
/** Returned if the result of the slice query is false. */
PM_STRING_QUERY_FALSE,
/** Returned if the result of the slice query is true. */
PM_STRING_QUERY_TRUE
} pm_string_query_t;
/**
* Check that the slice is a valid local variable name.
*
* @param source The source to check.
* @param length The length of the source.
* @param encoding_name The name of the encoding of the source.
* @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
* the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
*/
PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name);
/**
* Check that the slice is a valid constant name.
*
* @param source The source to check.
* @param length The length of the source.
* @param encoding_name The name of the encoding of the source.
* @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
* the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
*/
PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name);
/**
* Check that the slice is a valid method name.
*
* @param source The source to check.
* @param length The length of the source.
* @param encoding_name The name of the encoding of the source.
* @return PM_STRING_QUERY_TRUE if the query is true, PM_STRING_QUERY_FALSE if
* the query is false, and PM_STRING_QUERY_ERROR if the encoding was invalid.
*/
PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name);
/**
* @mainpage
*
* Prism is a parser for the Ruby programming language. It is designed to be
* portable, error tolerant, and maintainable. It is written in C99 and has no
* dependencies. It is currently being integrated into
* [CRuby](https://github.com/ruby/ruby),
* [JRuby](https://github.com/jruby/jruby),
* [TruffleRuby](https://github.com/oracle/truffleruby),
* [Sorbet](https://github.com/sorbet/sorbet), and
* [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree).
*
* @section getting-started Getting started
*
* If you're vendoring this project and compiling it statically then as long as
* you have a C99 compiler you will be fine. If you're linking against it as
* shared library, then you should compile with `-fvisibility=hidden` and
* `-DPRISM_EXPORT_SYMBOLS` to tell prism to make only its public interface
* visible.
*
* @section parsing Parsing
*
* In order to parse Ruby code, the structures and functions that you're going
* to want to use and be aware of are:
*
* * `pm_parser_t` - the main parser structure
* * `pm_parser_init` - initialize a parser
* * `pm_parse` - parse and return the root node
* * `pm_node_destroy` - deallocate the root node returned by `pm_parse`
* * `pm_parser_free` - free the internal memory of the parser
*
* Putting all of this together would look something like:
*
* ```c
* void parse(const uint8_t *source, size_t length) {
* pm_parser_t parser;
* pm_parser_init(&parser, source, length, NULL);
*
* pm_node_t *root = pm_parse(&parser);
* printf("PARSED!\n");
*
* pm_node_destroy(&parser, root);
* pm_parser_free(&parser);
* }
* ```
*
* All of the nodes "inherit" from `pm_node_t` by embedding those structures as
* their first member. This means you can downcast and upcast any node in the
* tree to a `pm_node_t`.
*
* @section serializing Serializing
*
* Prism provides the ability to serialize the AST and its related metadata into
* a binary format. This format is designed to be portable to different
* languages and runtimes so that you only need to make one FFI call in order to
* parse Ruby code. The structures and functions that you're going to want to
* use and be aware of are:
*
* * `pm_buffer_t` - a small buffer object that will hold the serialized AST
* * `pm_buffer_free` - free the memory associated with the buffer
* * `pm_serialize` - serialize the AST into a buffer
* * `pm_serialize_parse` - parse and serialize the AST into a buffer
*
* Putting all of this together would look something like:
*
* ```c
* void serialize(const uint8_t *source, size_t length) {
* pm_buffer_t buffer = { 0 };
*
* pm_serialize_parse(&buffer, source, length, NULL);
* printf("SERIALIZED!\n");
*
* pm_buffer_free(&buffer);
* }
* ```
*
* @section inspecting Inspecting
*
* Prism provides the ability to inspect the AST by pretty-printing nodes. You
* can do this with the `pm_prettyprint` function, which you would use like:
*
* ```c
* void prettyprint(const uint8_t *source, size_t length) {
* pm_parser_t parser;
* pm_parser_init(&parser, source, length, NULL);
*
* pm_node_t *root = pm_parse(&parser);
* pm_buffer_t buffer = { 0 };
*
* pm_prettyprint(&buffer, &parser, root);
* printf("%*.s\n", (int) buffer.length, buffer.value);
*
* pm_buffer_free(&buffer);
* pm_node_destroy(&parser, root);
* pm_parser_free(&parser);
* }
* ```
*/
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,260 @@
/**
* @file defines.h
*
* Macro definitions used throughout the prism library.
*
* This file should be included first by any *.h or *.c in prism for consistency
* and to ensure that the macros are defined before they are used.
*/
#ifndef PRISM_DEFINES_H
#define PRISM_DEFINES_H
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/**
* We want to be able to use the PRI* macros for printing out integers, but on
* some platforms they aren't included unless this is already defined.
*/
#define __STDC_FORMAT_MACROS
// Include sys/types.h before inttypes.h to work around issue with
// certain versions of GCC and newlib which causes omission of PRIx64
#include <sys/types.h>
#include <inttypes.h>
/**
* When we are parsing using recursive descent, we want to protect against
* malicious payloads that could attempt to crash our parser. We do this by
* specifying a maximum depth to which we are allowed to recurse.
*/
#ifndef PRISM_DEPTH_MAXIMUM
#define PRISM_DEPTH_MAXIMUM 10000
#endif
/**
* By default, we compile with -fvisibility=hidden. When this is enabled, we
* need to mark certain functions as being publically-visible. This macro does
* that in a compiler-agnostic way.
*/
#ifndef PRISM_EXPORTED_FUNCTION
# ifdef PRISM_EXPORT_SYMBOLS
# ifdef _WIN32
# define PRISM_EXPORTED_FUNCTION __declspec(dllexport) extern
# else
# define PRISM_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern
# endif
# else
# define PRISM_EXPORTED_FUNCTION
# endif
#endif
/**
* Certain compilers support specifying that a function accepts variadic
* parameters that look like printf format strings to provide a better developer
* experience when someone is using the function. This macro does that in a
* compiler-agnostic way.
*/
#if defined(__GNUC__)
# if defined(__MINGW_PRINTF_FORMAT)
# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, argument_index)))
# else
# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((format(printf, string_index, argument_index)))
# endif
#elif defined(__clang__)
# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index) __attribute__((__format__(__printf__, string_index, argument_index)))
#else
# define PRISM_ATTRIBUTE_FORMAT(string_index, argument_index)
#endif
/**
* GCC will warn if you specify a function or parameter that is unused at
* runtime. This macro allows you to mark a function or parameter as unused in a
* compiler-agnostic way.
*/
#if defined(__GNUC__)
# define PRISM_ATTRIBUTE_UNUSED __attribute__((unused))
#else
# define PRISM_ATTRIBUTE_UNUSED
#endif
/**
* Old Visual Studio versions do not support the inline keyword, so we need to
* define it to be __inline.
*/
#if defined(_MSC_VER) && !defined(inline)
# define inline __inline
#endif
/**
* Old Visual Studio versions before 2015 do not implement sprintf, but instead
* implement _snprintf. We standard that here.
*/
#if !defined(snprintf) && defined(_MSC_VER) && (_MSC_VER < 1900)
# define snprintf _snprintf
#endif
/**
* A simple utility macro to concatenate two tokens together, necessary when one
* of the tokens is itself a macro.
*/
#define PM_CONCATENATE(left, right) left ## right
/**
* We want to be able to use static assertions, but they weren't standardized
* until C11. As such, we polyfill it here by making a hacky typedef that will
* fail to compile due to a negative array size if the condition is false.
*/
#if defined(_Static_assert)
# define PM_STATIC_ASSERT(line, condition, message) _Static_assert(condition, message)
#else
# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
#endif
/**
* In general, libc for embedded systems does not support memory-mapped files.
* If the target platform is POSIX or Windows, we can map a file in memory and
* read it in a more efficient manner.
*/
#ifdef _WIN32
# define PRISM_HAS_MMAP
#else
# include <unistd.h>
# ifdef _POSIX_MAPPED_FILES
# define PRISM_HAS_MMAP
# endif
#endif
/**
* If PRISM_HAS_NO_FILESYSTEM is defined, then we want to exclude all filesystem
* related code from the library. All filesystem related code should be guarded
* by PRISM_HAS_FILESYSTEM.
*/
#ifndef PRISM_HAS_NO_FILESYSTEM
# define PRISM_HAS_FILESYSTEM
#endif
/**
* isinf on POSIX systems it accepts a float, a double, or a long double.
* But mingw didn't provide an isinf macro, only an isinf function that only
* accepts floats, so we need to use _finite instead.
*/
#ifdef __MINGW64__
#include <float.h>
#define PRISM_ISINF(x) (!_finite(x))
#else
#define PRISM_ISINF(x) isinf(x)
#endif
/**
* If you build prism with a custom allocator, configure it with
* "-D PRISM_XALLOCATOR" to use your own allocator that defines xmalloc,
* xrealloc, xcalloc, and xfree.
*
* For example, your `prism_xallocator.h` file could look like this:
*
* ```
* #ifndef PRISM_XALLOCATOR_H
* #define PRISM_XALLOCATOR_H
* #define xmalloc my_malloc
* #define xrealloc my_realloc
* #define xcalloc my_calloc
* #define xfree my_free
* #endif
* ```
*/
#ifdef PRISM_XALLOCATOR
#include "prism_xallocator.h"
#else
#ifndef xmalloc
/**
* The malloc function that should be used. This can be overridden with
* the PRISM_XALLOCATOR define.
*/
#define xmalloc malloc
#endif
#ifndef xrealloc
/**
* The realloc function that should be used. This can be overridden with
* the PRISM_XALLOCATOR define.
*/
#define xrealloc realloc
#endif
#ifndef xcalloc
/**
* The calloc function that should be used. This can be overridden with
* the PRISM_XALLOCATOR define.
*/
#define xcalloc calloc
#endif
#ifndef xfree
/**
* The free function that should be used. This can be overridden with the
* PRISM_XALLOCATOR define.
*/
#define xfree free
#endif
#endif
/**
* If PRISM_BUILD_MINIMAL is defined, then we're going to define every possible
* switch that will turn off certain features of prism.
*/
#ifdef PRISM_BUILD_MINIMAL
/** Exclude the serialization API. */
#define PRISM_EXCLUDE_SERIALIZATION
/** Exclude the JSON serialization API. */
#define PRISM_EXCLUDE_JSON
/** Exclude the Array#pack parser API. */
#define PRISM_EXCLUDE_PACK
/** Exclude the prettyprint API. */
#define PRISM_EXCLUDE_PRETTYPRINT
/** Exclude the full set of encodings, using the minimal only. */
#define PRISM_ENCODING_EXCLUDE_FULL
#endif
/**
* Support PRISM_LIKELY and PRISM_UNLIKELY to help the compiler optimize its
* branch predication.
*/
#if defined(__GNUC__) || defined(__clang__)
/** The compiler should predicate that this branch will be taken. */
#define PRISM_LIKELY(x) __builtin_expect(!!(x), 1)
/** The compiler should predicate that this branch will not be taken. */
#define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
/** Void because this platform does not support branch prediction hints. */
#define PRISM_LIKELY(x) (x)
/** Void because this platform does not support branch prediction hints. */
#define PRISM_UNLIKELY(x) (x)
#endif
/**
* We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
* Use PRISM_FALLTHROUGH to explicitly annotate cases where the fallthrough is intentional.
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L // C23 or later
#define PRISM_FALLTHROUGH [[fallthrough]];
#elif defined(__GNUC__) || defined(__clang__)
#define PRISM_FALLTHROUGH __attribute__((fallthrough));
#elif defined(_MSC_VER)
#define PRISM_FALLTHROUGH __fallthrough;
#else
#define PRISM_FALLTHROUGH
#endif
#endif

View File

@@ -0,0 +1,451 @@
/*----------------------------------------------------------------------------*/
/* This file is generated by the templates/template.rb script and should not */
/* be modified manually. See */
/* templates/include/prism/diagnostic.h.erb */
/* if you are looking to modify the */
/* template */
/*----------------------------------------------------------------------------*/
/**
* @file diagnostic.h
*
* A list of diagnostics generated during parsing.
*/
#ifndef PRISM_DIAGNOSTIC_H
#define PRISM_DIAGNOSTIC_H
#include "prism/ast.h"
#include "prism/defines.h"
#include "prism/util/pm_list.h"
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
/**
* The diagnostic IDs of all of the diagnostics, used to communicate the types
* of errors between the parser and the user.
*/
typedef enum {
// These are the error diagnostics.
PM_ERR_ALIAS_ARGUMENT,
PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE,
PM_ERR_AMPAMPEQ_MULTI_ASSIGN,
PM_ERR_ARGUMENT_AFTER_BLOCK,
PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES,
PM_ERR_ARGUMENT_BARE_HASH,
PM_ERR_ARGUMENT_BLOCK_FORWARDING,
PM_ERR_ARGUMENT_BLOCK_MULTI,
PM_ERR_ARGUMENT_CONFLICT_AMPERSAND,
PM_ERR_ARGUMENT_CONFLICT_STAR,
PM_ERR_ARGUMENT_CONFLICT_STAR_STAR,
PM_ERR_ARGUMENT_FORMAL_CLASS,
PM_ERR_ARGUMENT_FORMAL_CONSTANT,
PM_ERR_ARGUMENT_FORMAL_GLOBAL,
PM_ERR_ARGUMENT_FORMAL_IVAR,
PM_ERR_ARGUMENT_FORWARDING_UNBOUND,
PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND,
PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
PM_ERR_ARGUMENT_NO_FORWARDING_STAR,
PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR,
PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT,
PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT,
PM_ERR_ARGUMENT_TERM_PAREN,
PM_ERR_ARGUMENT_UNEXPECTED_BLOCK,
PM_ERR_ARRAY_ELEMENT,
PM_ERR_ARRAY_EXPRESSION,
PM_ERR_ARRAY_EXPRESSION_AFTER_STAR,
PM_ERR_ARRAY_SEPARATOR,
PM_ERR_ARRAY_TERM,
PM_ERR_BEGIN_LONELY_ELSE,
PM_ERR_BEGIN_TERM,
PM_ERR_BEGIN_UPCASE_BRACE,
PM_ERR_BEGIN_UPCASE_TERM,
PM_ERR_BEGIN_UPCASE_TOPLEVEL,
PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE,
PM_ERR_BLOCK_PARAM_PIPE_TERM,
PM_ERR_BLOCK_TERM_BRACE,
PM_ERR_BLOCK_TERM_END,
PM_ERR_CANNOT_PARSE_EXPRESSION,
PM_ERR_CANNOT_PARSE_STRING_PART,
PM_ERR_CASE_EXPRESSION_AFTER_CASE,
PM_ERR_CASE_EXPRESSION_AFTER_WHEN,
PM_ERR_CASE_MATCH_MISSING_PREDICATE,
PM_ERR_CASE_MISSING_CONDITIONS,
PM_ERR_CASE_TERM,
PM_ERR_CLASS_IN_METHOD,
PM_ERR_CLASS_NAME,
PM_ERR_CLASS_SUPERCLASS,
PM_ERR_CLASS_TERM,
PM_ERR_CLASS_UNEXPECTED_END,
PM_ERR_CLASS_VARIABLE_BARE,
PM_ERR_CONDITIONAL_ELSIF_PREDICATE,
PM_ERR_CONDITIONAL_IF_PREDICATE,
PM_ERR_CONDITIONAL_PREDICATE_TERM,
PM_ERR_CONDITIONAL_TERM,
PM_ERR_CONDITIONAL_TERM_ELSE,
PM_ERR_CONDITIONAL_UNLESS_PREDICATE,
PM_ERR_CONDITIONAL_UNTIL_PREDICATE,
PM_ERR_CONDITIONAL_WHILE_PREDICATE,
PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT,
PM_ERR_DEF_ENDLESS,
PM_ERR_DEF_ENDLESS_SETTER,
PM_ERR_DEF_NAME,
PM_ERR_DEF_PARAMS_TERM,
PM_ERR_DEF_PARAMS_TERM_PAREN,
PM_ERR_DEF_RECEIVER,
PM_ERR_DEF_RECEIVER_TERM,
PM_ERR_DEF_TERM,
PM_ERR_DEFINED_EXPRESSION,
PM_ERR_EMBDOC_TERM,
PM_ERR_EMBEXPR_END,
PM_ERR_EMBVAR_INVALID,
PM_ERR_END_UPCASE_BRACE,
PM_ERR_END_UPCASE_TERM,
PM_ERR_ESCAPE_INVALID_CONTROL,
PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT,
PM_ERR_ESCAPE_INVALID_HEXADECIMAL,
PM_ERR_ESCAPE_INVALID_META,
PM_ERR_ESCAPE_INVALID_META_REPEAT,
PM_ERR_ESCAPE_INVALID_UNICODE,
PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS,
PM_ERR_ESCAPE_INVALID_UNICODE_LIST,
PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL,
PM_ERR_ESCAPE_INVALID_UNICODE_LONG,
PM_ERR_ESCAPE_INVALID_UNICODE_SHORT,
PM_ERR_ESCAPE_INVALID_UNICODE_TERM,
PM_ERR_EXPECT_ARGUMENT,
PM_ERR_EXPECT_EOL_AFTER_STATEMENT,
PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ,
PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA,
PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL,
PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS,
PM_ERR_EXPECT_EXPRESSION_AFTER_LPAREN,
PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR,
PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ,
PM_ERR_EXPECT_EXPRESSION_AFTER_QUESTION,
PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT,
PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH,
PM_ERR_EXPECT_EXPRESSION_AFTER_STAR,
PM_ERR_EXPECT_FOR_DELIMITER,
PM_ERR_EXPECT_IDENT_REQ_PARAMETER,
PM_ERR_EXPECT_IN_DELIMITER,
PM_ERR_EXPECT_LPAREN_REQ_PARAMETER,
PM_ERR_EXPECT_MESSAGE,
PM_ERR_EXPECT_RBRACKET,
PM_ERR_EXPECT_RPAREN,
PM_ERR_EXPECT_RPAREN_AFTER_MULTI,
PM_ERR_EXPECT_RPAREN_REQ_PARAMETER,
PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER,
PM_ERR_EXPECT_STRING_CONTENT,
PM_ERR_EXPECT_WHEN_DELIMITER,
PM_ERR_EXPRESSION_BARE_HASH,
PM_ERR_EXPRESSION_NOT_WRITABLE,
PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING,
PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE,
PM_ERR_EXPRESSION_NOT_WRITABLE_FILE,
PM_ERR_EXPRESSION_NOT_WRITABLE_LINE,
PM_ERR_EXPRESSION_NOT_WRITABLE_NIL,
PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED,
PM_ERR_EXPRESSION_NOT_WRITABLE_SELF,
PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE,
PM_ERR_FLOAT_PARSE,
PM_ERR_FOR_COLLECTION,
PM_ERR_FOR_IN,
PM_ERR_FOR_INDEX,
PM_ERR_FOR_TERM,
PM_ERR_GLOBAL_VARIABLE_BARE,
PM_ERR_HASH_EXPRESSION_AFTER_LABEL,
PM_ERR_HASH_KEY,
PM_ERR_HASH_ROCKET,
PM_ERR_HASH_TERM,
PM_ERR_HASH_VALUE,
PM_ERR_HEREDOC_IDENTIFIER,
PM_ERR_HEREDOC_TERM,
PM_ERR_INCOMPLETE_QUESTION_MARK,
PM_ERR_INCOMPLETE_VARIABLE_CLASS,
PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3,
PM_ERR_INCOMPLETE_VARIABLE_INSTANCE,
PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3,
PM_ERR_INSTANCE_VARIABLE_BARE,
PM_ERR_INVALID_BLOCK_EXIT,
PM_ERR_INVALID_CHARACTER,
PM_ERR_INVALID_COMMA,
PM_ERR_INVALID_ENCODING_MAGIC_COMMENT,
PM_ERR_INVALID_ESCAPE_CHARACTER,
PM_ERR_INVALID_FLOAT_EXPONENT,
PM_ERR_INVALID_LOCAL_VARIABLE_READ,
PM_ERR_INVALID_LOCAL_VARIABLE_WRITE,
PM_ERR_INVALID_MULTIBYTE_CHAR,
PM_ERR_INVALID_MULTIBYTE_CHARACTER,
PM_ERR_INVALID_MULTIBYTE_ESCAPE,
PM_ERR_INVALID_NUMBER_BINARY,
PM_ERR_INVALID_NUMBER_DECIMAL,
PM_ERR_INVALID_NUMBER_FRACTION,
PM_ERR_INVALID_NUMBER_HEXADECIMAL,
PM_ERR_INVALID_NUMBER_OCTAL,
PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER,
PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING,
PM_ERR_INVALID_PERCENT,
PM_ERR_INVALID_PERCENT_EOF,
PM_ERR_INVALID_PRINTABLE_CHARACTER,
PM_ERR_INVALID_RETRY_AFTER_ELSE,
PM_ERR_INVALID_RETRY_AFTER_ENSURE,
PM_ERR_INVALID_RETRY_WITHOUT_RESCUE,
PM_ERR_INVALID_SYMBOL,
PM_ERR_INVALID_VARIABLE_GLOBAL,
PM_ERR_INVALID_VARIABLE_GLOBAL_3_3,
PM_ERR_INVALID_YIELD,
PM_ERR_IT_NOT_ALLOWED_NUMBERED,
PM_ERR_IT_NOT_ALLOWED_ORDINARY,
PM_ERR_LAMBDA_OPEN,
PM_ERR_LAMBDA_TERM_BRACE,
PM_ERR_LAMBDA_TERM_END,
PM_ERR_LIST_I_LOWER_ELEMENT,
PM_ERR_LIST_I_LOWER_TERM,
PM_ERR_LIST_I_UPPER_ELEMENT,
PM_ERR_LIST_I_UPPER_TERM,
PM_ERR_LIST_W_LOWER_ELEMENT,
PM_ERR_LIST_W_LOWER_TERM,
PM_ERR_LIST_W_UPPER_ELEMENT,
PM_ERR_LIST_W_UPPER_TERM,
PM_ERR_MALLOC_FAILED,
PM_ERR_MIXED_ENCODING,
PM_ERR_MODULE_IN_METHOD,
PM_ERR_MODULE_NAME,
PM_ERR_MODULE_TERM,
PM_ERR_MULTI_ASSIGN_MULTI_SPLATS,
PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST,
PM_ERR_NESTING_TOO_DEEP,
PM_ERR_NO_LOCAL_VARIABLE,
PM_ERR_NON_ASSOCIATIVE_OPERATOR,
PM_ERR_NOT_EXPRESSION,
PM_ERR_NUMBER_LITERAL_UNDERSCORE,
PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK,
PM_ERR_NUMBERED_PARAMETER_IT,
PM_ERR_NUMBERED_PARAMETER_ORDINARY,
PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK,
PM_ERR_OPERATOR_MULTI_ASSIGN,
PM_ERR_OPERATOR_WRITE_ARGUMENTS,
PM_ERR_OPERATOR_WRITE_BLOCK,
PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI,
PM_ERR_PARAMETER_BLOCK_MULTI,
PM_ERR_PARAMETER_CIRCULAR,
PM_ERR_PARAMETER_FORWARDING_AFTER_REST,
PM_ERR_PARAMETER_METHOD_NAME,
PM_ERR_PARAMETER_NAME_DUPLICATED,
PM_ERR_PARAMETER_NO_DEFAULT,
PM_ERR_PARAMETER_NO_DEFAULT_KW,
PM_ERR_PARAMETER_NUMBERED_RESERVED,
PM_ERR_PARAMETER_ORDER,
PM_ERR_PARAMETER_SPLAT_MULTI,
PM_ERR_PARAMETER_STAR,
PM_ERR_PARAMETER_UNEXPECTED_FWD,
PM_ERR_PARAMETER_UNEXPECTED_NO_KW,
PM_ERR_PARAMETER_WILD_LOOSE_COMMA,
PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS,
PM_ERR_PATTERN_CAPTURE_DUPLICATE,
PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET,
PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA,
PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET,
PM_ERR_PATTERN_EXPRESSION_AFTER_IN,
PM_ERR_PATTERN_EXPRESSION_AFTER_KEY,
PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN,
PM_ERR_PATTERN_EXPRESSION_AFTER_PIN,
PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE,
PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE,
PM_ERR_PATTERN_EXPRESSION_AFTER_REST,
PM_ERR_PATTERN_FIND_MISSING_INNER,
PM_ERR_PATTERN_HASH_IMPLICIT,
PM_ERR_PATTERN_HASH_KEY,
PM_ERR_PATTERN_HASH_KEY_DUPLICATE,
PM_ERR_PATTERN_HASH_KEY_INTERPOLATED,
PM_ERR_PATTERN_HASH_KEY_LABEL,
PM_ERR_PATTERN_HASH_KEY_LOCALS,
PM_ERR_PATTERN_IDENT_AFTER_HROCKET,
PM_ERR_PATTERN_LABEL_AFTER_COMMA,
PM_ERR_PATTERN_REST,
PM_ERR_PATTERN_TERM_BRACE,
PM_ERR_PATTERN_TERM_BRACKET,
PM_ERR_PATTERN_TERM_PAREN,
PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN,
PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH,
PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING,
PM_ERR_REGEXP_INVALID_UNICODE_RANGE,
PM_ERR_REGEXP_NON_ESCAPED_MBC,
PM_ERR_REGEXP_PARSE_ERROR,
PM_ERR_REGEXP_TERM,
PM_ERR_REGEXP_UNKNOWN_OPTIONS,
PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP,
PM_ERR_RESCUE_EXPRESSION,
PM_ERR_RESCUE_MODIFIER_VALUE,
PM_ERR_RESCUE_TERM,
PM_ERR_RESCUE_VARIABLE,
PM_ERR_RETURN_INVALID,
PM_ERR_SCRIPT_NOT_FOUND,
PM_ERR_SINGLETON_FOR_LITERALS,
PM_ERR_STATEMENT_ALIAS,
PM_ERR_STATEMENT_POSTEXE_END,
PM_ERR_STATEMENT_PREEXE_BEGIN,
PM_ERR_STATEMENT_UNDEF,
PM_ERR_STRING_CONCATENATION,
PM_ERR_STRING_INTERPOLATED_TERM,
PM_ERR_STRING_LITERAL_EOF,
PM_ERR_STRING_LITERAL_TERM,
PM_ERR_SYMBOL_INVALID,
PM_ERR_SYMBOL_TERM_DYNAMIC,
PM_ERR_SYMBOL_TERM_INTERPOLATED,
PM_ERR_TERNARY_COLON,
PM_ERR_TERNARY_EXPRESSION_FALSE,
PM_ERR_TERNARY_EXPRESSION_TRUE,
PM_ERR_UNARY_DISALLOWED,
PM_ERR_UNARY_RECEIVER,
PM_ERR_UNDEF_ARGUMENT,
PM_ERR_UNEXPECTED_BLOCK_ARGUMENT,
PM_ERR_UNEXPECTED_INDEX_BLOCK,
PM_ERR_UNEXPECTED_INDEX_KEYWORDS,
PM_ERR_UNEXPECTED_LABEL,
PM_ERR_UNEXPECTED_MULTI_WRITE,
PM_ERR_UNEXPECTED_RANGE_OPERATOR,
PM_ERR_UNEXPECTED_SAFE_NAVIGATION,
PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT,
PM_ERR_UNEXPECTED_TOKEN_IGNORE,
PM_ERR_UNTIL_TERM,
PM_ERR_VOID_EXPRESSION,
PM_ERR_WHILE_TERM,
PM_ERR_WRITE_TARGET_IN_METHOD,
PM_ERR_WRITE_TARGET_READONLY,
PM_ERR_WRITE_TARGET_UNEXPECTED,
PM_ERR_XSTRING_TERM,
// These are the warning diagnostics.
PM_WARN_AMBIGUOUS_BINARY_OPERATOR,
PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS,
PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS,
PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND,
PM_WARN_AMBIGUOUS_PREFIX_STAR,
PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR,
PM_WARN_AMBIGUOUS_SLASH,
PM_WARN_COMPARISON_AFTER_COMPARISON,
PM_WARN_DOT_DOT_DOT_EOL,
PM_WARN_EQUAL_IN_CONDITIONAL,
PM_WARN_EQUAL_IN_CONDITIONAL_3_3,
PM_WARN_END_IN_METHOD,
PM_WARN_DUPLICATED_HASH_KEY,
PM_WARN_DUPLICATED_WHEN_CLAUSE,
PM_WARN_FLOAT_OUT_OF_RANGE,
PM_WARN_IGNORED_FROZEN_STRING_LITERAL,
PM_WARN_INDENTATION_MISMATCH,
PM_WARN_INTEGER_IN_FLIP_FLOP,
PM_WARN_INVALID_CHARACTER,
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
PM_WARN_INVALID_NUMBERED_REFERENCE,
PM_WARN_KEYWORD_EOL,
PM_WARN_LITERAL_IN_CONDITION_DEFAULT,
PM_WARN_LITERAL_IN_CONDITION_VERBOSE,
PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE,
PM_WARN_SHEBANG_CARRIAGE_RETURN,
PM_WARN_UNEXPECTED_CARRIAGE_RETURN,
PM_WARN_UNREACHABLE_STATEMENT,
PM_WARN_UNUSED_LOCAL_VARIABLE,
PM_WARN_VOID_STATEMENT,
} pm_diagnostic_id_t;
/**
* This struct represents a diagnostic generated during parsing.
*
* @extends pm_list_node_t
*/
typedef struct {
/** The embedded base node. */
pm_list_node_t node;
/** The location of the diagnostic in the source. */
pm_location_t location;
/** The ID of the diagnostic. */
pm_diagnostic_id_t diag_id;
/** The message associated with the diagnostic. */
const char *message;
/**
* Whether or not the memory related to the message of this diagnostic is
* owned by this diagnostic. If it is, it needs to be freed when the
* diagnostic is freed.
*/
bool owned;
/**
* The level of the diagnostic, see `pm_error_level_t` and
* `pm_warning_level_t` for possible values.
*/
uint8_t level;
} pm_diagnostic_t;
/**
* The levels of errors generated during parsing.
*/
typedef enum {
/** For errors that should raise a syntax error. */
PM_ERROR_LEVEL_SYNTAX = 0,
/** For errors that should raise an argument error. */
PM_ERROR_LEVEL_ARGUMENT = 1,
/** For errors that should raise a load error. */
PM_ERROR_LEVEL_LOAD = 2
} pm_error_level_t;
/**
* The levels of warnings generated during parsing.
*/
typedef enum {
/** For warnings which should be emitted if $VERBOSE != nil. */
PM_WARNING_LEVEL_DEFAULT = 0,
/** For warnings which should be emitted if $VERBOSE == true. */
PM_WARNING_LEVEL_VERBOSE = 1
} pm_warning_level_t;
/**
* Get the human-readable name of the given diagnostic ID.
*
* @param diag_id The diagnostic ID.
* @return The human-readable name of the diagnostic ID.
*/
const char * pm_diagnostic_id_human(pm_diagnostic_id_t diag_id);
/**
* Append a diagnostic to the given list of diagnostics that is using shared
* memory for its message.
*
* @param list The list to append to.
* @param start The start of the diagnostic.
* @param end The end of the diagnostic.
* @param diag_id The diagnostic ID.
* @return Whether the diagnostic was successfully appended.
*/
bool pm_diagnostic_list_append(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id);
/**
* Append a diagnostic to the given list of diagnostics that is using a format
* string for its message.
*
* @param list The list to append to.
* @param start The start of the diagnostic.
* @param end The end of the diagnostic.
* @param diag_id The diagnostic ID.
* @param ... The arguments to the format string for the message.
* @return Whether the diagnostic was successfully appended.
*/
bool pm_diagnostic_list_append_format(pm_list_t *list, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id, ...);
/**
* Deallocate the internal state of the given diagnostic list.
*
* @param list The list to deallocate.
*/
void pm_diagnostic_list_free(pm_list_t *list);
#endif

View File

@@ -0,0 +1,283 @@
/**
* @file encoding.h
*
* The encoding interface and implementations used by the parser.
*/
#ifndef PRISM_ENCODING_H
#define PRISM_ENCODING_H
#include "prism/defines.h"
#include "prism/util/pm_strncasecmp.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* This struct defines the functions necessary to implement the encoding
* interface so we can determine how many bytes the subsequent character takes.
* Each callback should return the number of bytes, or 0 if the next bytes are
* invalid for the encoding and type.
*/
typedef struct {
/**
* Return the number of bytes that the next character takes if it is valid
* in the encoding. Does not read more than n bytes. It is assumed that n is
* at least 1.
*/
size_t (*char_width)(const uint8_t *b, ptrdiff_t n);
/**
* Return the number of bytes that the next character takes if it is valid
* in the encoding and is alphabetical. Does not read more than n bytes. It
* is assumed that n is at least 1.
*/
size_t (*alpha_char)(const uint8_t *b, ptrdiff_t n);
/**
* Return the number of bytes that the next character takes if it is valid
* in the encoding and is alphanumeric. Does not read more than n bytes. It
* is assumed that n is at least 1.
*/
size_t (*alnum_char)(const uint8_t *b, ptrdiff_t n);
/**
* Return true if the next character is valid in the encoding and is an
* uppercase character. Does not read more than n bytes. It is assumed that
* n is at least 1.
*/
bool (*isupper_char)(const uint8_t *b, ptrdiff_t n);
/**
* The name of the encoding. This should correspond to a value that can be
* passed to Encoding.find in Ruby.
*/
const char *name;
/**
* Return true if the encoding is a multibyte encoding.
*/
bool multibyte;
} pm_encoding_t;
/**
* All of the lookup tables use the first bit of each embedded byte to indicate
* whether the codepoint is alphabetical.
*/
#define PRISM_ENCODING_ALPHABETIC_BIT 1 << 0
/**
* All of the lookup tables use the second bit of each embedded byte to indicate
* whether the codepoint is alphanumeric.
*/
#define PRISM_ENCODING_ALPHANUMERIC_BIT 1 << 1
/**
* All of the lookup tables use the third bit of each embedded byte to indicate
* whether the codepoint is uppercase.
*/
#define PRISM_ENCODING_UPPERCASE_BIT 1 << 2
/**
* Return the size of the next character in the UTF-8 encoding.
*
* @param b The bytes to read.
* @param n The number of bytes that can be read.
* @returns The number of bytes that the next character takes if it is valid in
* the encoding, or 0 if it is not.
*/
size_t pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n);
/**
* Return the size of the next character in the UTF-8 encoding if it is an
* alphabetical character.
*
* @param b The bytes to read.
* @param n The number of bytes that can be read.
* @returns The number of bytes that the next character takes if it is valid in
* the encoding, or 0 if it is not.
*/
size_t pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n);
/**
* Return the size of the next character in the UTF-8 encoding if it is an
* alphanumeric character.
*
* @param b The bytes to read.
* @param n The number of bytes that can be read.
* @returns The number of bytes that the next character takes if it is valid in
* the encoding, or 0 if it is not.
*/
size_t pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n);
/**
* Return true if the next character in the UTF-8 encoding if it is an uppercase
* character.
*
* @param b The bytes to read.
* @param n The number of bytes that can be read.
* @returns True if the next character is valid in the encoding and is an
* uppercase character, or false if it is not.
*/
bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n);
/**
* This lookup table is referenced in both the UTF-8 encoding file and the
* parser directly in order to speed up the default encoding processing. It is
* used to indicate whether a character is alphabetical, alphanumeric, or
* uppercase in unicode mappings.
*/
extern const uint8_t pm_encoding_unicode_table[256];
/**
* These are all of the encodings that prism supports.
*/
typedef enum {
PM_ENCODING_UTF_8 = 0,
PM_ENCODING_US_ASCII,
PM_ENCODING_ASCII_8BIT,
PM_ENCODING_EUC_JP,
PM_ENCODING_WINDOWS_31J,
// We optionally support excluding the full set of encodings to only support the
// minimum necessary to process Ruby code without encoding comments.
#ifndef PRISM_ENCODING_EXCLUDE_FULL
PM_ENCODING_BIG5,
PM_ENCODING_BIG5_HKSCS,
PM_ENCODING_BIG5_UAO,
PM_ENCODING_CESU_8,
PM_ENCODING_CP51932,
PM_ENCODING_CP850,
PM_ENCODING_CP852,
PM_ENCODING_CP855,
PM_ENCODING_CP949,
PM_ENCODING_CP950,
PM_ENCODING_CP951,
PM_ENCODING_EMACS_MULE,
PM_ENCODING_EUC_JP_MS,
PM_ENCODING_EUC_JIS_2004,
PM_ENCODING_EUC_KR,
PM_ENCODING_EUC_TW,
PM_ENCODING_GB12345,
PM_ENCODING_GB18030,
PM_ENCODING_GB1988,
PM_ENCODING_GB2312,
PM_ENCODING_GBK,
PM_ENCODING_IBM437,
PM_ENCODING_IBM720,
PM_ENCODING_IBM737,
PM_ENCODING_IBM775,
PM_ENCODING_IBM852,
PM_ENCODING_IBM855,
PM_ENCODING_IBM857,
PM_ENCODING_IBM860,
PM_ENCODING_IBM861,
PM_ENCODING_IBM862,
PM_ENCODING_IBM863,
PM_ENCODING_IBM864,
PM_ENCODING_IBM865,
PM_ENCODING_IBM866,
PM_ENCODING_IBM869,
PM_ENCODING_ISO_8859_1,
PM_ENCODING_ISO_8859_2,
PM_ENCODING_ISO_8859_3,
PM_ENCODING_ISO_8859_4,
PM_ENCODING_ISO_8859_5,
PM_ENCODING_ISO_8859_6,
PM_ENCODING_ISO_8859_7,
PM_ENCODING_ISO_8859_8,
PM_ENCODING_ISO_8859_9,
PM_ENCODING_ISO_8859_10,
PM_ENCODING_ISO_8859_11,
PM_ENCODING_ISO_8859_13,
PM_ENCODING_ISO_8859_14,
PM_ENCODING_ISO_8859_15,
PM_ENCODING_ISO_8859_16,
PM_ENCODING_KOI8_R,
PM_ENCODING_KOI8_U,
PM_ENCODING_MAC_CENT_EURO,
PM_ENCODING_MAC_CROATIAN,
PM_ENCODING_MAC_CYRILLIC,
PM_ENCODING_MAC_GREEK,
PM_ENCODING_MAC_ICELAND,
PM_ENCODING_MAC_JAPANESE,
PM_ENCODING_MAC_ROMAN,
PM_ENCODING_MAC_ROMANIA,
PM_ENCODING_MAC_THAI,
PM_ENCODING_MAC_TURKISH,
PM_ENCODING_MAC_UKRAINE,
PM_ENCODING_SHIFT_JIS,
PM_ENCODING_SJIS_DOCOMO,
PM_ENCODING_SJIS_KDDI,
PM_ENCODING_SJIS_SOFTBANK,
PM_ENCODING_STATELESS_ISO_2022_JP,
PM_ENCODING_STATELESS_ISO_2022_JP_KDDI,
PM_ENCODING_TIS_620,
PM_ENCODING_UTF8_MAC,
PM_ENCODING_UTF8_DOCOMO,
PM_ENCODING_UTF8_KDDI,
PM_ENCODING_UTF8_SOFTBANK,
PM_ENCODING_WINDOWS_1250,
PM_ENCODING_WINDOWS_1251,
PM_ENCODING_WINDOWS_1252,
PM_ENCODING_WINDOWS_1253,
PM_ENCODING_WINDOWS_1254,
PM_ENCODING_WINDOWS_1255,
PM_ENCODING_WINDOWS_1256,
PM_ENCODING_WINDOWS_1257,
PM_ENCODING_WINDOWS_1258,
PM_ENCODING_WINDOWS_874,
#endif
PM_ENCODING_MAXIMUM
} pm_encoding_type_t;
/**
* This is the table of all of the encodings that prism supports.
*/
extern const pm_encoding_t pm_encodings[PM_ENCODING_MAXIMUM];
/**
* This is the default UTF-8 encoding. We need a reference to it to quickly
* create parsers.
*/
#define PM_ENCODING_UTF_8_ENTRY (&pm_encodings[PM_ENCODING_UTF_8])
/**
* This is the US-ASCII encoding. We need a reference to it to be able to
* compare against it when a string is being created because it could possibly
* need to fall back to ASCII-8BIT.
*/
#define PM_ENCODING_US_ASCII_ENTRY (&pm_encodings[PM_ENCODING_US_ASCII])
/**
* This is the ASCII-8BIT encoding. We need a reference to it so that pm_strpbrk
* can compare against it because invalid multibyte characters are not a thing
* in this encoding. It is also needed for handling Regexp encoding flags.
*/
#define PM_ENCODING_ASCII_8BIT_ENTRY (&pm_encodings[PM_ENCODING_ASCII_8BIT])
/**
* This is the EUC-JP encoding. We need a reference to it to quickly process
* regular expression modifiers.
*/
#define PM_ENCODING_EUC_JP_ENTRY (&pm_encodings[PM_ENCODING_EUC_JP])
/**
* This is the Windows-31J encoding. We need a reference to it to quickly
* process regular expression modifiers.
*/
#define PM_ENCODING_WINDOWS_31J_ENTRY (&pm_encodings[PM_ENCODING_WINDOWS_31J])
/**
* Parse the given name of an encoding and return a pointer to the corresponding
* encoding struct if one can be found, otherwise return NULL.
*
* @param start A pointer to the first byte of the name.
* @param end A pointer to the last byte of the name.
* @returns A pointer to the encoding struct if one is found, otherwise NULL.
*/
const pm_encoding_t * pm_encoding_find(const uint8_t *start, const uint8_t *end);
#endif

View File

@@ -0,0 +1,129 @@
/**
* @file node.h
*
* Functions related to nodes in the AST.
*/
#ifndef PRISM_NODE_H
#define PRISM_NODE_H
#include "prism/defines.h"
#include "prism/parser.h"
#include "prism/util/pm_buffer.h"
/**
* Loop through each node in the node list, writing each node to the given
* pm_node_t pointer.
*/
#define PM_NODE_LIST_FOREACH(list, index, node) \
for (size_t index = 0; index < (list)->size && ((node) = (list)->nodes[index]); index++)
/**
* Append a new node onto the end of the node list.
*
* @param list The list to append to.
* @param node The node to append.
*/
void pm_node_list_append(pm_node_list_t *list, pm_node_t *node);
/**
* Prepend a new node onto the beginning of the node list.
*
* @param list The list to prepend to.
* @param node The node to prepend.
*/
void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node);
/**
* Concatenate the given node list onto the end of the other node list.
*
* @param list The list to concatenate onto.
* @param other The list to concatenate.
*/
void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other);
/**
* Free the internal memory associated with the given node list.
*
* @param list The list to free.
*/
void pm_node_list_free(pm_node_list_t *list);
/**
* Deallocate a node and all of its children.
*
* @param parser The parser that owns the node.
* @param node The node to deallocate.
*/
PRISM_EXPORTED_FUNCTION void pm_node_destroy(pm_parser_t *parser, struct pm_node *node);
/**
* Returns a string representation of the given node type.
*
* @param node_type The node type to convert to a string.
* @return A string representation of the given node type.
*/
PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_type);
/**
* Visit each of the nodes in this subtree using the given visitor callback. The
* callback function will be called for each node in the subtree. If it returns
* false, then that node's children will not be visited. If it returns true,
* then the children will be visited. The data parameter is treated as an opaque
* pointer and is passed to the visitor callback for consumers to use as they
* see fit.
*
* As an example:
*
* ```c
* #include "prism.h"
*
* bool visit(const pm_node_t *node, void *data) {
* size_t *indent = (size_t *) data;
* for (size_t i = 0; i < *indent * 2; i++) putc(' ', stdout);
* printf("%s\n", pm_node_type_to_str(node->type));
*
* size_t next_indent = *indent + 1;
* size_t *next_data = &next_indent;
* pm_visit_child_nodes(node, visit, next_data);
*
* return false;
* }
*
* int main(void) {
* const char *source = "1 + 2; 3 + 4";
* size_t size = strlen(source);
*
* pm_parser_t parser;
* pm_options_t options = { 0 };
* pm_parser_init(&parser, (const uint8_t *) source, size, &options);
*
* size_t indent = 0;
* pm_node_t *node = pm_parse(&parser);
*
* size_t *data = &indent;
* pm_visit_node(node, visit, data);
*
* pm_node_destroy(&parser, node);
* pm_parser_free(&parser);
* return EXIT_SUCCESS;
* }
* ```
*
* @param node The root node to start visiting from.
* @param visitor The callback to call for each node in the subtree.
* @param data An opaque pointer that is passed to the visitor callback.
*/
PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
/**
* Visit the children of the given node with the given callback. This is the
* default behavior for walking the tree that is called from pm_visit_node if
* the callback returns true.
*
* @param node The node to visit the children of.
* @param visitor The callback to call for each child node.
* @param data An opaque pointer that is passed to the visitor callback.
*/
PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
#endif

View File

@@ -0,0 +1,442 @@
/**
* @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_char.h"
#include "prism/util/pm_string.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* String literals should be made frozen.
*/
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED ((int8_t) -1)
/**
* String literals may be frozen or mutable depending on the implementation
* default.
*/
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET ((int8_t) 0)
/**
* String literals should be made mutable.
*/
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED ((int8_t) 1)
/**
* 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;
/** Flags for the set of forwarding parameters in this scope. */
uint8_t forwarding;
} pm_options_scope_t;
/** The default value for parameters. */
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE = 0x0;
/** When the scope is fowarding with the * parameter. */
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1;
/** When the scope is fowarding with the ** parameter. */
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2;
/** When the scope is fowarding with the & parameter. */
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4;
/** When the scope is fowarding with the ... parameter. */
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL = 0x8;
// Forward declaration needed by the callback typedef.
struct pm_options;
/**
* The callback called when additional switches are found in a shebang comment
* that need to be processed by the runtime.
*
* @param options The options struct that may be updated by this callback.
* Certain fields will be checked for changes, specifically encoding,
* command_line, and frozen_string_literal.
* @param source The source of the shebang comment.
* @param length The length of the source.
* @param shebang_callback_data Any additional data that should be passed along
* to the callback.
*/
typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const uint8_t *source, size_t length, void *shebang_callback_data);
/**
* The version of Ruby syntax that we should be parsing with. This is used to
* allow consumers to specify which behavior they want in case they need to
* parse in the same way as a specific version of CRuby would have.
*/
typedef enum {
/** The current version of prism. */
PM_OPTIONS_VERSION_LATEST = 0,
/** The vendored version of prism in CRuby 3.3.x. */
PM_OPTIONS_VERSION_CRUBY_3_3 = 1,
/** The vendored version of prism in CRuby 3.4.x. */
PM_OPTIONS_VERSION_CRUBY_3_4 = 2
} pm_options_version_t;
/**
* The options that can be passed to the parser.
*/
typedef struct pm_options {
/**
* The callback to call when additional switches are found in a shebang
* comment.
*/
pm_options_shebang_callback_t shebang_callback;
/**
* Any additional data that should be passed along to the shebang callback
* if one was set.
*/
void *shebang_callback_data;
/** The name of the file that is currently being parsed. */
pm_string_t filepath;
/**
* The line within the file that the parse starts on. This value is
* 1-indexed.
*/
int32_t line;
/**
* 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.
*/
pm_string_t encoding;
/**
* 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. Scopes are ordered from the outermost scope to the
* innermost one.
*/
pm_options_scope_t *scopes;
/**
* The version of prism that we should be parsing with. This is used to
* allow consumers to specify which behavior they want in case they need to
* parse exactly as a specific version of CRuby.
*/
pm_options_version_t version;
/** A bitset of the various options that were set on the command line. */
uint8_t command_line;
/**
* Whether or not the frozen string literal option has been set.
* May be:
* - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
* - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
* - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
*/
int8_t frozen_string_literal;
/**
* Whether or not the encoding magic comments should be respected. This is a
* niche use-case where you want to parse a file with a specific encoding
* but ignore any encoding magic comments at the top of the file.
*/
bool encoding_locked;
/**
* When the file being parsed is the main script, the shebang will be
* considered for command-line flags (or for implicit -x). The caller needs
* to pass this information to the parser so that it can behave correctly.
*/
bool main_script;
/**
* When the file being parsed is considered a "partial" script, jumps will
* not be marked as errors if they are not contained within loops/blocks.
* This is used in the case that you're parsing a script that you know will
* be embedded inside another script later, but you do not have that context
* yet. For example, when parsing an ERB template that will be evaluated
* inside another script.
*/
bool partial_script;
/**
* Whether or not the parser should freeze the nodes that it creates. This
* makes it possible to have a deeply frozen AST that is safe to share
* between concurrency primitives.
*/
bool freeze;
} pm_options_t;
/**
* A bit representing whether or not the command line -a option was set. -a
* splits the input line $_ into $F.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_A = 0x1;
/**
* A bit representing whether or not the command line -e option was set. -e
* allow the user to specify a script to be executed. This is necessary for
* prism to know because certain warnings are not generated when -e is used.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_E = 0x2;
/**
* A bit representing whether or not the command line -l option was set. -l
* chomps the input line by default.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_L = 0x4;
/**
* A bit representing whether or not the command line -n option was set. -n
* wraps the script in a while gets loop.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_N = 0x8;
/**
* A bit representing whether or not the command line -p option was set. -p
* prints the value of $_ at the end of each loop.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_P = 0x10;
/**
* A bit representing whether or not the command line -x option was set. -x
* searches the input file for a shebang that matches the current Ruby engine.
*/
static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20;
/**
* Set the shebang callback option on the given options struct.
*
* @param options The options struct to set the shebang callback on.
* @param shebang_callback The shebang callback to set.
* @param shebang_callback_data Any additional data that should be passed along
* to the callback.
*/
PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data);
/**
* 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 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, int32_t line);
/**
* 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 encoding_locked option on the given options struct.
*
* @param options The options struct to set the encoding_locked value on.
* @param encoding_locked The encoding_locked value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked);
/**
* 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);
/**
* Sets the command line option on the given options struct.
*
* @param options The options struct to set the command line option on.
* @param command_line The command_line value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line);
/**
* Set the version option on the given options struct by parsing the given
* string. If the string contains an invalid option, this returns false.
* Otherwise, it returns true.
*
* @param options The options struct to set the version on.
* @param version The version to set.
* @param length The length of the version string.
* @return Whether or not the version was parsed successfully.
*/
PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
/**
* Set the main script option on the given options struct.
*
* @param options The options struct to set the main script value on.
* @param main_script The main script value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script);
/**
* Set the partial script option on the given options struct.
*
* @param options The options struct to set the partial script value on.
* @param partial_script The partial script value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script);
/**
* Set the freeze option on the given options struct.
*
* @param options The options struct to set the freeze value on.
* @param freeze The freeze value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze);
/**
* 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.
* @return Whether or not the scopes array was initialized successfully.
*/
PRISM_EXPORTED_FUNCTION bool 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.
* @return Whether or not the scope was initialized successfully.
*/
PRISM_EXPORTED_FUNCTION bool 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);
/**
* Set the forwarding option on the given scope struct.
*
* @param scope The scope struct to set the forwarding on.
* @param forwarding The forwarding value to set.
*/
PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding);
/**
* 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);
/**
* Deserialize an options struct from the given binary string. This is used to
* pass options to the parser from an FFI call so that consumers of the library
* from an FFI perspective don't have to worry about the structure of our
* options structs. Since the source of these calls will be from Ruby
* implementation internals we assume it is from a trusted source.
*
* `data` is assumed to be a valid pointer pointing to well-formed data. The
* layout of this data should be the same every time, and is described below:
*
* | # bytes | field |
* | ------- | -------------------------- |
* | `4` | the length of the filepath |
* | ... | the filepath bytes |
* | `4` | the line number |
* | `4` | the length the encoding |
* | ... | the encoding bytes |
* | `1` | frozen string literal |
* | `1` | -p command line option |
* | `1` | -n command line option |
* | `1` | -l command line option |
* | `1` | -a command line option |
* | `1` | the version |
* | `1` | encoding locked |
* | `1` | main script |
* | `1` | partial script |
* | `1` | freeze |
* | `4` | the number of scopes |
* | ... | the scopes |
*
* The version field is an enum, so it should be one of the following values:
*
* | value | version |
* | ----- | ------------------------- |
* | `0` | use the latest version of prism |
* | `1` | use the version of prism that is vendored in CRuby 3.3.0 |
*
* Each scope is laid out as follows:
*
* | # bytes | field |
* | ------- | -------------------------- |
* | `4` | the number of locals |
* | `1` | the forwarding flags |
* | ... | the locals |
*
* Each local is laid out as follows:
*
* | # bytes | field |
* | ------- | -------------------------- |
* | `4` | the length of the local |
* | ... | the local bytes |
*
* Some additional things to note about this layout:
*
* * The filepath can have a length of 0, in which case we'll consider it an
* empty string.
* * The line number should be 0-indexed.
* * The encoding can have a length of 0, in which case we'll use the default
* encoding (UTF-8). If it's not 0, it should correspond to a name of an
* encoding that can be passed to `Encoding.find` in Ruby.
* * The frozen string literal, encoding locked, main script, and partial script
* fields are booleans, so their values should be either 0 or 1.
* * The number of scopes can be 0.
*
* @param options The options struct to deserialize into.
* @param data The binary string to deserialize from.
*/
void pm_options_read(pm_options_t *options, const char *data);
#endif

View File

@@ -0,0 +1,163 @@
/**
* @file pack.h
*
* A pack template string parser.
*/
#ifndef PRISM_PACK_H
#define PRISM_PACK_H
#include "prism/defines.h"
// We optionally support parsing String#pack templates. For systems that don't
// want or need this functionality, it can be turned off with the
// PRISM_EXCLUDE_PACK define.
#ifdef PRISM_EXCLUDE_PACK
void pm_pack_parse(void);
#else
#include <stdint.h>
#include <stdlib.h>
/** The version of the pack template language that we are parsing. */
typedef enum pm_pack_version {
PM_PACK_VERSION_3_2_0
} pm_pack_version;
/** The type of pack template we are parsing. */
typedef enum pm_pack_variant {
PM_PACK_VARIANT_PACK,
PM_PACK_VARIANT_UNPACK
} pm_pack_variant;
/** A directive within the pack template. */
typedef enum pm_pack_type {
PM_PACK_SPACE,
PM_PACK_COMMENT,
PM_PACK_INTEGER,
PM_PACK_UTF8,
PM_PACK_BER,
PM_PACK_FLOAT,
PM_PACK_STRING_SPACE_PADDED,
PM_PACK_STRING_NULL_PADDED,
PM_PACK_STRING_NULL_TERMINATED,
PM_PACK_STRING_MSB,
PM_PACK_STRING_LSB,
PM_PACK_STRING_HEX_HIGH,
PM_PACK_STRING_HEX_LOW,
PM_PACK_STRING_UU,
PM_PACK_STRING_MIME,
PM_PACK_STRING_BASE64,
PM_PACK_STRING_FIXED,
PM_PACK_STRING_POINTER,
PM_PACK_MOVE,
PM_PACK_BACK,
PM_PACK_NULL,
PM_PACK_END
} pm_pack_type;
/** The signness of a pack directive. */
typedef enum pm_pack_signed {
PM_PACK_UNSIGNED,
PM_PACK_SIGNED,
PM_PACK_SIGNED_NA
} pm_pack_signed;
/** The endianness of a pack directive. */
typedef enum pm_pack_endian {
PM_PACK_AGNOSTIC_ENDIAN,
PM_PACK_LITTLE_ENDIAN, // aka 'VAX', or 'V'
PM_PACK_BIG_ENDIAN, // aka 'network', or 'N'
PM_PACK_NATIVE_ENDIAN,
PM_PACK_ENDIAN_NA
} pm_pack_endian;
/** The size of an integer pack directive. */
typedef enum pm_pack_size {
PM_PACK_SIZE_SHORT,
PM_PACK_SIZE_INT,
PM_PACK_SIZE_LONG,
PM_PACK_SIZE_LONG_LONG,
PM_PACK_SIZE_8,
PM_PACK_SIZE_16,
PM_PACK_SIZE_32,
PM_PACK_SIZE_64,
PM_PACK_SIZE_P,
PM_PACK_SIZE_NA
} pm_pack_size;
/** The type of length of a pack directive. */
typedef enum pm_pack_length_type {
PM_PACK_LENGTH_FIXED,
PM_PACK_LENGTH_MAX,
PM_PACK_LENGTH_RELATIVE, // special case for unpack @*
PM_PACK_LENGTH_NA
} pm_pack_length_type;
/** The type of encoding for a pack template string. */
typedef enum pm_pack_encoding {
PM_PACK_ENCODING_START,
PM_PACK_ENCODING_ASCII_8BIT,
PM_PACK_ENCODING_US_ASCII,
PM_PACK_ENCODING_UTF_8
} pm_pack_encoding;
/** The result of parsing a pack template. */
typedef enum pm_pack_result {
PM_PACK_OK,
PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE,
PM_PACK_ERROR_UNKNOWN_DIRECTIVE,
PM_PACK_ERROR_LENGTH_TOO_BIG,
PM_PACK_ERROR_BANG_NOT_ALLOWED,
PM_PACK_ERROR_DOUBLE_ENDIAN
} pm_pack_result;
/**
* Parse a single directive from a pack or unpack format string.
*
* @param variant (in) pack or unpack
* @param format (in, out) the start of the next directive to parse on calling,
* and advanced beyond the parsed directive on return, or as much of it as
* was consumed until an error was encountered
* @param format_end (in) the end of the format string
* @param type (out) the type of the directive
* @param signed_type (out) whether the value is signed
* @param endian (out) the endianness of the value
* @param size (out) the size of the value
* @param length_type (out) what kind of length is specified
* @param length (out) the length of the directive
* @param encoding (in, out) takes the current encoding of the string which
* would result from parsing the whole format string, and returns a possibly
* changed directive - the encoding should be `PM_PACK_ENCODING_START` when
* pm_pack_parse is called for the first directive in a format string
*
* @return `PM_PACK_OK` on success or `PM_PACK_ERROR_*` on error
* @note Consult Ruby documentation for the meaning of directives.
*/
PRISM_EXPORTED_FUNCTION pm_pack_result
pm_pack_parse(
pm_pack_variant variant,
const char **format,
const char *format_end,
pm_pack_type *type,
pm_pack_signed *signed_type,
pm_pack_endian *endian,
pm_pack_size *size,
pm_pack_length_type *length_type,
uint64_t *length,
pm_pack_encoding *encoding
);
/**
* Prism abstracts sizes away from the native system - this converts an abstract
* size to a native size.
*
* @param size The abstract size to convert.
* @return The native size.
*/
PRISM_EXPORTED_FUNCTION size_t pm_size_to_native(pm_pack_size size);
#endif
#endif

View File

@@ -0,0 +1,933 @@
/**
* @file parser.h
*
* The parser used to parse Ruby source.
*/
#ifndef PRISM_PARSER_H
#define PRISM_PARSER_H
#include "prism/defines.h"
#include "prism/ast.h"
#include "prism/encoding.h"
#include "prism/options.h"
#include "prism/static_literals.h"
#include "prism/util/pm_constant_pool.h"
#include "prism/util/pm_list.h"
#include "prism/util/pm_newline_list.h"
#include "prism/util/pm_string.h"
#include <stdbool.h>
/**
* This enum provides various bits that represent different kinds of states that
* the lexer can track. This is used to determine which kind of token to return
* based on the context of the parser.
*/
typedef enum {
PM_LEX_STATE_BIT_BEG,
PM_LEX_STATE_BIT_END,
PM_LEX_STATE_BIT_ENDARG,
PM_LEX_STATE_BIT_ENDFN,
PM_LEX_STATE_BIT_ARG,
PM_LEX_STATE_BIT_CMDARG,
PM_LEX_STATE_BIT_MID,
PM_LEX_STATE_BIT_FNAME,
PM_LEX_STATE_BIT_DOT,
PM_LEX_STATE_BIT_CLASS,
PM_LEX_STATE_BIT_LABEL,
PM_LEX_STATE_BIT_LABELED,
PM_LEX_STATE_BIT_FITEM
} pm_lex_state_bit_t;
/**
* This enum combines the various bits from the above enum into individual
* values that represent the various states of the lexer.
*/
typedef enum {
PM_LEX_STATE_NONE = 0,
PM_LEX_STATE_BEG = (1 << PM_LEX_STATE_BIT_BEG),
PM_LEX_STATE_END = (1 << PM_LEX_STATE_BIT_END),
PM_LEX_STATE_ENDARG = (1 << PM_LEX_STATE_BIT_ENDARG),
PM_LEX_STATE_ENDFN = (1 << PM_LEX_STATE_BIT_ENDFN),
PM_LEX_STATE_ARG = (1 << PM_LEX_STATE_BIT_ARG),
PM_LEX_STATE_CMDARG = (1 << PM_LEX_STATE_BIT_CMDARG),
PM_LEX_STATE_MID = (1 << PM_LEX_STATE_BIT_MID),
PM_LEX_STATE_FNAME = (1 << PM_LEX_STATE_BIT_FNAME),
PM_LEX_STATE_DOT = (1 << PM_LEX_STATE_BIT_DOT),
PM_LEX_STATE_CLASS = (1 << PM_LEX_STATE_BIT_CLASS),
PM_LEX_STATE_LABEL = (1 << PM_LEX_STATE_BIT_LABEL),
PM_LEX_STATE_LABELED = (1 << PM_LEX_STATE_BIT_LABELED),
PM_LEX_STATE_FITEM = (1 << PM_LEX_STATE_BIT_FITEM),
PM_LEX_STATE_BEG_ANY = PM_LEX_STATE_BEG | PM_LEX_STATE_MID | PM_LEX_STATE_CLASS,
PM_LEX_STATE_ARG_ANY = PM_LEX_STATE_ARG | PM_LEX_STATE_CMDARG,
PM_LEX_STATE_END_ANY = PM_LEX_STATE_END | PM_LEX_STATE_ENDARG | PM_LEX_STATE_ENDFN
} pm_lex_state_t;
/**
* The type of quote that a heredoc uses.
*/
typedef enum {
PM_HEREDOC_QUOTE_NONE,
PM_HEREDOC_QUOTE_SINGLE = '\'',
PM_HEREDOC_QUOTE_DOUBLE = '"',
PM_HEREDOC_QUOTE_BACKTICK = '`',
} pm_heredoc_quote_t;
/**
* The type of indentation that a heredoc uses.
*/
typedef enum {
PM_HEREDOC_INDENT_NONE,
PM_HEREDOC_INDENT_DASH,
PM_HEREDOC_INDENT_TILDE,
} pm_heredoc_indent_t;
/**
* All of the information necessary to store to lexing a heredoc.
*/
typedef struct {
/** A pointer to the start of the heredoc identifier. */
const uint8_t *ident_start;
/** The length of the heredoc identifier. */
size_t ident_length;
/** The type of quote that the heredoc uses. */
pm_heredoc_quote_t quote;
/** The type of indentation that the heredoc uses. */
pm_heredoc_indent_t indent;
} pm_heredoc_lex_mode_t;
/**
* When lexing Ruby source, the lexer has a small amount of state to tell which
* kind of token it is currently lexing. For example, when we find the start of
* a string, the first token that we return is a TOKEN_STRING_BEGIN token. After
* that the lexer is now in the PM_LEX_STRING mode, and will return tokens that
* are found as part of a string.
*/
typedef struct pm_lex_mode {
/** The type of this lex mode. */
enum {
/** This state is used when any given token is being lexed. */
PM_LEX_DEFAULT,
/**
* This state is used when we're lexing as normal but inside an embedded
* expression of a string.
*/
PM_LEX_EMBEXPR,
/**
* This state is used when we're lexing a variable that is embedded
* directly inside of a string with the # shorthand.
*/
PM_LEX_EMBVAR,
/** This state is used when you are inside the content of a heredoc. */
PM_LEX_HEREDOC,
/**
* This state is used when we are lexing a list of tokens, as in a %w
* word list literal or a %i symbol list literal.
*/
PM_LEX_LIST,
/**
* This state is used when a regular expression has been begun and we
* are looking for the terminator.
*/
PM_LEX_REGEXP,
/**
* This state is used when we are lexing a string or a string-like
* token, as in string content with either quote or an xstring.
*/
PM_LEX_STRING
} mode;
/** The data associated with this type of lex mode. */
union {
struct {
/** This keeps track of the nesting level of the list. */
size_t nesting;
/** Whether or not interpolation is allowed in this list. */
bool interpolation;
/**
* When lexing a list, it takes into account balancing the
* terminator if the terminator is one of (), [], {}, or <>.
*/
uint8_t incrementor;
/** This is the terminator of the list literal. */
uint8_t terminator;
/**
* This is the character set that should be used to delimit the
* tokens within the list.
*/
uint8_t breakpoints[11];
} list;
struct {
/**
* This keeps track of the nesting level of the regular expression.
*/
size_t nesting;
/**
* When lexing a regular expression, it takes into account balancing
* the terminator if the terminator is one of (), [], {}, or <>.
*/
uint8_t incrementor;
/** This is the terminator of the regular expression. */
uint8_t terminator;
/**
* This is the character set that should be used to delimit the
* tokens within the regular expression.
*/
uint8_t breakpoints[7];
} regexp;
struct {
/** This keeps track of the nesting level of the string. */
size_t nesting;
/** Whether or not interpolation is allowed in this string. */
bool interpolation;
/**
* Whether or not at the end of the string we should allow a :,
* which would indicate this was a dynamic symbol instead of a
* string.
*/
bool label_allowed;
/**
* When lexing a string, it takes into account balancing the
* terminator if the terminator is one of (), [], {}, or <>.
*/
uint8_t incrementor;
/**
* This is the terminator of the string. It is typically either a
* single or double quote.
*/
uint8_t terminator;
/**
* This is the character set that should be used to delimit the
* tokens within the string.
*/
uint8_t breakpoints[7];
} string;
struct {
/**
* All of the data necessary to lex a heredoc.
*/
pm_heredoc_lex_mode_t base;
/**
* This is the pointer to the character where lexing should resume
* once the heredoc has been completely processed.
*/
const uint8_t *next_start;
/**
* This is used to track the amount of common whitespace on each
* line so that we know how much to dedent each line in the case of
* a tilde heredoc.
*/
size_t *common_whitespace;
/** True if the previous token ended with a line continuation. */
bool line_continuation;
} heredoc;
} as;
/** The previous lex state so that it knows how to pop. */
struct pm_lex_mode *prev;
} pm_lex_mode_t;
/**
* We pre-allocate a certain number of lex states in order to avoid having to
* call malloc too many times while parsing. You really shouldn't need more than
* this because you only really nest deeply when doing string interpolation.
*/
#define PM_LEX_STACK_SIZE 4
/**
* The parser used to parse Ruby source.
*/
typedef struct pm_parser pm_parser_t;
/**
* While parsing, we keep track of a stack of contexts. This is helpful for
* error recovery so that we can pop back to a previous context when we hit a
* token that is understood by a parent context but not by the current context.
*/
typedef enum {
/** a null context, used for returning a value from a function */
PM_CONTEXT_NONE = 0,
/** a begin statement */
PM_CONTEXT_BEGIN,
/** an ensure statement with an explicit begin */
PM_CONTEXT_BEGIN_ENSURE,
/** a rescue else statement with an explicit begin */
PM_CONTEXT_BEGIN_ELSE,
/** a rescue statement with an explicit begin */
PM_CONTEXT_BEGIN_RESCUE,
/** expressions in block arguments using braces */
PM_CONTEXT_BLOCK_BRACES,
/** expressions in block arguments using do..end */
PM_CONTEXT_BLOCK_KEYWORDS,
/** an ensure statement within a do..end block */
PM_CONTEXT_BLOCK_ENSURE,
/** a rescue else statement within a do..end block */
PM_CONTEXT_BLOCK_ELSE,
/** a rescue statement within a do..end block */
PM_CONTEXT_BLOCK_RESCUE,
/** a case when statements */
PM_CONTEXT_CASE_WHEN,
/** a case in statements */
PM_CONTEXT_CASE_IN,
/** a class declaration */
PM_CONTEXT_CLASS,
/** an ensure statement within a class statement */
PM_CONTEXT_CLASS_ENSURE,
/** a rescue else statement within a class statement */
PM_CONTEXT_CLASS_ELSE,
/** a rescue statement within a class statement */
PM_CONTEXT_CLASS_RESCUE,
/** a method definition */
PM_CONTEXT_DEF,
/** an ensure statement within a method definition */
PM_CONTEXT_DEF_ENSURE,
/** a rescue else statement within a method definition */
PM_CONTEXT_DEF_ELSE,
/** a rescue statement within a method definition */
PM_CONTEXT_DEF_RESCUE,
/** a method definition's parameters */
PM_CONTEXT_DEF_PARAMS,
/** a defined? expression */
PM_CONTEXT_DEFINED,
/** a method definition's default parameter */
PM_CONTEXT_DEFAULT_PARAMS,
/** an else clause */
PM_CONTEXT_ELSE,
/** an elsif clause */
PM_CONTEXT_ELSIF,
/** an interpolated expression */
PM_CONTEXT_EMBEXPR,
/** a for loop */
PM_CONTEXT_FOR,
/** a for loop's index */
PM_CONTEXT_FOR_INDEX,
/** an if statement */
PM_CONTEXT_IF,
/** a lambda expression with braces */
PM_CONTEXT_LAMBDA_BRACES,
/** a lambda expression with do..end */
PM_CONTEXT_LAMBDA_DO_END,
/** an ensure statement within a lambda expression */
PM_CONTEXT_LAMBDA_ENSURE,
/** a rescue else statement within a lambda expression */
PM_CONTEXT_LAMBDA_ELSE,
/** a rescue statement within a lambda expression */
PM_CONTEXT_LAMBDA_RESCUE,
/** the predicate clause of a loop statement */
PM_CONTEXT_LOOP_PREDICATE,
/** the top level context */
PM_CONTEXT_MAIN,
/** a module declaration */
PM_CONTEXT_MODULE,
/** an ensure statement within a module statement */
PM_CONTEXT_MODULE_ENSURE,
/** a rescue else statement within a module statement */
PM_CONTEXT_MODULE_ELSE,
/** a rescue statement within a module statement */
PM_CONTEXT_MODULE_RESCUE,
/** a multiple target expression */
PM_CONTEXT_MULTI_TARGET,
/** a parenthesized expression */
PM_CONTEXT_PARENS,
/** an END block */
PM_CONTEXT_POSTEXE,
/** a predicate inside an if/elsif/unless statement */
PM_CONTEXT_PREDICATE,
/** a BEGIN block */
PM_CONTEXT_PREEXE,
/** a modifier rescue clause */
PM_CONTEXT_RESCUE_MODIFIER,
/** a singleton class definition */
PM_CONTEXT_SCLASS,
/** an ensure statement with a singleton class */
PM_CONTEXT_SCLASS_ENSURE,
/** a rescue else statement with a singleton class */
PM_CONTEXT_SCLASS_ELSE,
/** a rescue statement with a singleton class */
PM_CONTEXT_SCLASS_RESCUE,
/** a ternary expression */
PM_CONTEXT_TERNARY,
/** an unless statement */
PM_CONTEXT_UNLESS,
/** an until statement */
PM_CONTEXT_UNTIL,
/** a while statement */
PM_CONTEXT_WHILE,
} pm_context_t;
/** This is a node in a linked list of contexts. */
typedef struct pm_context_node {
/** The context that this node represents. */
pm_context_t context;
/** A pointer to the previous context in the linked list. */
struct pm_context_node *prev;
} pm_context_node_t;
/** This is the type of a comment that we've found while parsing. */
typedef enum {
PM_COMMENT_INLINE,
PM_COMMENT_EMBDOC
} pm_comment_type_t;
/**
* This is a node in the linked list of comments that we've found while parsing.
*
* @extends pm_list_node_t
*/
typedef struct pm_comment {
/** The embedded base node. */
pm_list_node_t node;
/** The location of the comment in the source. */
pm_location_t location;
/** The type of comment that we've found. */
pm_comment_type_t type;
} pm_comment_t;
/**
* This is a node in the linked list of magic comments that we've found while
* parsing.
*
* @extends pm_list_node_t
*/
typedef struct {
/** The embedded base node. */
pm_list_node_t node;
/** A pointer to the start of the key in the source. */
const uint8_t *key_start;
/** A pointer to the start of the value in the source. */
const uint8_t *value_start;
/** The length of the key in the source. */
uint32_t key_length;
/** The length of the value in the source. */
uint32_t value_length;
} pm_magic_comment_t;
/**
* When the encoding that is being used to parse the source is changed by prism,
* we provide the ability here to call out to a user-defined function.
*/
typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser);
/**
* When you are lexing through a file, the lexer needs all of the information
* that the parser additionally provides (for example, the local table). So if
* you want to properly lex Ruby, you need to actually lex it in the context of
* the parser. In order to provide this functionality, we optionally allow a
* struct to be attached to the parser that calls back out to a user-provided
* callback when each token is lexed.
*/
typedef struct {
/**
* This opaque pointer is used to provide whatever information the user
* deemed necessary to the callback. In our case we use it to pass the array
* that the tokens get appended into.
*/
void *data;
/**
* This is the callback that is called when a token is lexed. It is passed
* the opaque data pointer, the parser, and the token that was lexed.
*/
void (*callback)(void *data, pm_parser_t *parser, pm_token_t *token);
} pm_lex_callback_t;
/** The type of shareable constant value that can be set. */
typedef uint8_t pm_shareable_constant_value_t;
static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_NONE = 0x0;
static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL;
static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING;
static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY;
/**
* This tracks an individual local variable in a certain lexical context, as
* well as the number of times is it read.
*/
typedef struct {
/** The name of the local variable. */
pm_constant_id_t name;
/** The location of the local variable in the source. */
pm_location_t location;
/** The index of the local variable in the local table. */
uint32_t index;
/** The number of times the local variable is read. */
uint32_t reads;
/** The hash of the local variable. */
uint32_t hash;
} pm_local_t;
/**
* This is a set of local variables in a certain lexical context (method, class,
* module, etc.). We need to track how many times these variables are read in
* order to warn if they only get written.
*/
typedef struct pm_locals {
/** The number of local variables in the set. */
uint32_t size;
/** The capacity of the local variables set. */
uint32_t capacity;
/** The nullable allocated memory for the local variables in the set. */
pm_local_t *locals;
} pm_locals_t;
/** The flags about scope parameters that can be set. */
typedef uint8_t pm_scope_parameters_t;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NONE = 0x0;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS = 0x1;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS = 0x2;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_BLOCK = 0x4;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x8;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED = 0x10;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_INNER = 0x20;
static const pm_scope_parameters_t PM_SCOPE_PARAMETERS_NUMBERED_FOUND = 0x40;
/**
* This struct represents a node in a linked list of scopes. Some scopes can see
* into their parent scopes, while others cannot.
*/
typedef struct pm_scope {
/** A pointer to the previous scope in the linked list. */
struct pm_scope *previous;
/** The IDs of the locals in the given scope. */
pm_locals_t locals;
/**
* This is a list of the implicit parameters contained within the block.
* These will be processed after the block is parsed to determine the kind
* of parameters node that should be used and to check if any errors need to
* be added.
*/
pm_node_list_t implicit_parameters;
/**
* This is a bitfield that indicates the parameters that are being used in
* this scope. It is a combination of the PM_SCOPE_PARAMETERS_* constants.
* There are three different kinds of parameters that can be used in a
* scope:
*
* - Ordinary parameters (e.g., def foo(bar); end)
* - Numbered parameters (e.g., def foo; _1; end)
* - The it parameter (e.g., def foo; it; end)
*
* If ordinary parameters are being used, then certain parameters can be
* forwarded to another method/structure. Those are indicated by four
* additional bits in the params field. For example, some combinations of:
*
* - def foo(*); end
* - def foo(**); end
* - def foo(&); end
* - def foo(...); end
*/
pm_scope_parameters_t parameters;
/**
* The current state of constant shareability for this scope. This is
* changed by magic shareable_constant_value comments.
*/
pm_shareable_constant_value_t shareable_constant;
/**
* A boolean indicating whether or not this scope can see into its parent.
* If closed is true, then the scope cannot see into its parent.
*/
bool closed;
} pm_scope_t;
/**
* A struct that represents a stack of boolean values.
*/
typedef uint32_t pm_state_stack_t;
/**
* This struct represents the overall parser. It contains a reference to the
* source file, as well as pointers that indicate where in the source it's
* currently parsing. It also contains the most recent and current token that
* it's considering.
*/
struct pm_parser {
/**
* The next node identifier that will be assigned. This is a unique
* identifier used to track nodes such that the syntax tree can be dropped
* but the node can be found through another parse.
*/
uint32_t node_id;
/** The current state of the lexer. */
pm_lex_state_t lex_state;
/** Tracks the current nesting of (), [], and {}. */
int enclosure_nesting;
/**
* Used to temporarily track the nesting of enclosures to determine if a {
* is the beginning of a lambda following the parameters of a lambda.
*/
int lambda_enclosure_nesting;
/**
* Used to track the nesting of braces to ensure we get the correct value
* when we are interpolating blocks with braces.
*/
int brace_nesting;
/**
* The stack used to determine if a do keyword belongs to the predicate of a
* while, until, or for loop.
*/
pm_state_stack_t do_loop_stack;
/**
* The stack used to determine if a do keyword belongs to the beginning of a
* block.
*/
pm_state_stack_t accepts_block_stack;
/** A stack of lex modes. */
struct {
/** The current mode of the lexer. */
pm_lex_mode_t *current;
/** The stack of lexer modes. */
pm_lex_mode_t stack[PM_LEX_STACK_SIZE];
/** The current index into the lexer mode stack. */
size_t index;
} lex_modes;
/** The pointer to the start of the source. */
const uint8_t *start;
/** The pointer to the end of the source. */
const uint8_t *end;
/** The previous token we were considering. */
pm_token_t previous;
/** The current token we're considering. */
pm_token_t current;
/**
* This is a special field set on the parser when we need the parser to jump
* to a specific location when lexing the next token, as opposed to just
* using the end of the previous token. Normally this is NULL.
*/
const uint8_t *next_start;
/**
* This field indicates the end of a heredoc whose identifier was found on
* the current line. If another heredoc is found on the same line, then this
* will be moved forward to the end of that heredoc. If no heredocs are
* found on a line then this is NULL.
*/
const uint8_t *heredoc_end;
/** The list of comments that have been found while parsing. */
pm_list_t comment_list;
/** The list of magic comments that have been found while parsing. */
pm_list_t magic_comment_list;
/**
* An optional location that represents the location of the __END__ marker
* and the rest of the content of the file. This content is loaded into the
* DATA constant when the file being parsed is the main file being executed.
*/
pm_location_t data_loc;
/** The list of warnings that have been found while parsing. */
pm_list_t warning_list;
/** The list of errors that have been found while parsing. */
pm_list_t error_list;
/** The current local scope. */
pm_scope_t *current_scope;
/** The current parsing context. */
pm_context_node_t *current_context;
/**
* The hash keys for the hash that is currently being parsed. This is not
* usually necessary because it can pass it down the various call chains,
* but in the event that you're parsing a hash that is being directly
* pushed into another hash with **, we need to share the hash keys so that
* we can warn for the nested hash as well.
*/
pm_static_literals_t *current_hash_keys;
/**
* The encoding functions for the current file is attached to the parser as
* it's parsing so that it can change with a magic comment.
*/
const pm_encoding_t *encoding;
/**
* When the encoding that is being used to parse the source is changed by
* prism, we provide the ability here to call out to a user-defined
* function.
*/
pm_encoding_changed_callback_t encoding_changed_callback;
/**
* This pointer indicates where a comment must start if it is to be
* considered an encoding comment.
*/
const uint8_t *encoding_comment_start;
/**
* This is an optional callback that can be attached to the parser that will
* be called whenever a new token is lexed by the parser.
*/
pm_lex_callback_t *lex_callback;
/**
* This is the path of the file being parsed. We use the filepath when
* constructing SourceFileNodes.
*/
pm_string_t filepath;
/**
* This constant pool keeps all of the constants defined throughout the file
* so that we can reference them later.
*/
pm_constant_pool_t constant_pool;
/** This is the list of newline offsets in the source file. */
pm_newline_list_t newline_list;
/**
* We want to add a flag to integer nodes that indicates their base. We only
* want to parse these once, but we don't have space on the token itself to
* communicate this information. So we store it here and pass it through
* when we find tokens that we need it for.
*/
pm_node_flags_t integer_base;
/**
* This string is used to pass information from the lexer to the parser. It
* is particularly necessary because of escape sequences.
*/
pm_string_t current_string;
/**
* The line number at the start of the parse. This will be used to offset
* the line numbers of all of the locations.
*/
int32_t start_line;
/**
* When a string-like expression is being lexed, any byte or escape sequence
* that resolves to a value whose top bit is set (i.e., >= 0x80) will
* explicitly set the encoding to the same encoding as the source.
* Alternatively, if a unicode escape sequence is used (e.g., \\u{80}) that
* resolves to a value whose top bit is set, then the encoding will be
* explicitly set to UTF-8.
*
* The _next_ time this happens, if the encoding that is about to become the
* explicitly set encoding does not match the previously set explicit
* encoding, a mixed encoding error will be emitted.
*
* When the expression is finished being lexed, the explicit encoding
* controls the encoding of the expression. For the most part this means
* that the expression will either be encoded in the source encoding or
* UTF-8. This holds for all encodings except US-ASCII. If the source is
* US-ASCII and an explicit encoding was set that was _not_ UTF-8, then the
* expression will be encoded as ASCII-8BIT.
*
* Note that if the expression is a list, different elements within the same
* list can have different encodings, so this will get reset between each
* element. Furthermore all of this only applies to lists that support
* interpolation, because otherwise escapes that could change the encoding
* are ignored.
*
* At first glance, it may make more sense for this to live on the lexer
* mode, but we need it here to communicate back to the parser for character
* literals that do not push a new lexer mode.
*/
const pm_encoding_t *explicit_encoding;
/**
* When parsing block exits (e.g., break, next, redo), we need to validate
* that they are in correct contexts. For the most part we can do this by
* looking at our parent contexts. However, modifier while and until
* expressions can change that context to make block exits valid. In these
* cases, we need to keep track of the block exits and then validate them
* after the expression has been parsed.
*
* We use a pointer here because we don't want to keep a whole list attached
* since this will only be used in the context of begin/end expressions.
*/
pm_node_list_t *current_block_exits;
/** The version of prism that we should use to parse. */
pm_options_version_t version;
/** The command line flags given from the options. */
uint8_t command_line;
/**
* Whether or not we have found a frozen_string_literal magic comment with
* a true or false value.
* May be:
* - PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
* - PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
* - PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
*/
int8_t frozen_string_literal;
/**
* Whether or not we are parsing an eval string. This impacts whether or not
* we should evaluate if block exits/yields are valid.
*/
bool parsing_eval;
/**
* Whether or not we are parsing a "partial" script, which is a script that
* will be evaluated in the context of another script, so we should not
* check jumps (next/break/etc.) for validity.
*/
bool partial_script;
/** Whether or not we're at the beginning of a command. */
bool command_start;
/** Whether or not we're currently recovering from a syntax error. */
bool recovering;
/**
* This is very specialized behavior for when you want to parse in a context
* that does not respect encoding comments. Its main use case is translating
* into the whitequark/parser AST which re-encodes source files in UTF-8
* before they are parsed and ignores encoding comments.
*/
bool encoding_locked;
/**
* Whether or not the encoding has been changed by a magic comment. We use
* this to provide a fast path for the lexer instead of going through the
* function pointer.
*/
bool encoding_changed;
/**
* This flag indicates that we are currently parsing a pattern matching
* expression and impacts that calculation of newlines.
*/
bool pattern_matching_newlines;
/** This flag indicates that we are currently parsing a keyword argument. */
bool in_keyword_arg;
/**
* Whether or not the parser has seen a token that has semantic meaning
* (i.e., a token that is not a comment or whitespace).
*/
bool semantic_token_seen;
/**
* True if the current regular expression being lexed contains only ASCII
* characters.
*/
bool current_regular_expression_ascii_only;
/**
* By default, Ruby always warns about mismatched indentation. This can be
* toggled with a magic comment.
*/
bool warn_mismatched_indentation;
};
#endif

View File

@@ -0,0 +1,34 @@
/**
* @file prettyprint.h
*
* An AST node pretty-printer.
*/
#ifndef PRISM_PRETTYPRINT_H
#define PRISM_PRETTYPRINT_H
#include "prism/defines.h"
#ifdef PRISM_EXCLUDE_PRETTYPRINT
void pm_prettyprint(void);
#else
#include <stdio.h>
#include "prism/ast.h"
#include "prism/parser.h"
#include "prism/util/pm_buffer.h"
/**
* Pretty-prints the AST represented by the given node to the given buffer.
*
* @param output_buffer The buffer to write the pretty-printed AST to.
* @param parser The parser that parsed the AST.
* @param node The root node of the AST to pretty-print.
*/
PRISM_EXPORTED_FUNCTION void pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node);
#endif
#endif

View File

@@ -0,0 +1,43 @@
/**
* @file regexp.h
*
* A regular expression parser.
*/
#ifndef PRISM_REGEXP_H
#define PRISM_REGEXP_H
#include "prism/defines.h"
#include "prism/parser.h"
#include "prism/encoding.h"
#include "prism/util/pm_memchr.h"
#include "prism/util/pm_string.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/**
* This callback is called when a named capture group is found.
*/
typedef void (*pm_regexp_name_callback_t)(const pm_string_t *name, void *data);
/**
* This callback is called when a parse error is found.
*/
typedef void (*pm_regexp_error_callback_t)(const uint8_t *start, const uint8_t *end, const char *message, void *data);
/**
* Parse a regular expression.
*
* @param parser The parser that is currently being used.
* @param source The source code to parse.
* @param size The size of the source code.
* @param extended_mode Whether to parse the regular expression in extended mode.
* @param name_callback The optional callback to call when a named capture group is found.
* @param name_data The optional data to pass to the name callback.
* @param error_callback The callback to call when a parse error is found.
* @param error_data The data to pass to the error callback.
*/
PRISM_EXPORTED_FUNCTION void pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool extended_mode, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data);
#endif

View File

@@ -0,0 +1,121 @@
/**
* @file static_literals.h
*
* A set of static literal nodes that can be checked for duplicates.
*/
#ifndef PRISM_STATIC_LITERALS_H
#define PRISM_STATIC_LITERALS_H
#include "prism/defines.h"
#include "prism/ast.h"
#include "prism/util/pm_newline_list.h"
#include <assert.h>
#include <stdbool.h>
/**
* An internal hash table for a set of nodes.
*/
typedef struct {
/** The array of nodes in the hash table. */
pm_node_t **nodes;
/** The size of the hash table. */
uint32_t size;
/** The space that has been allocated in the hash table. */
uint32_t capacity;
} pm_node_hash_t;
/**
* Certain sets of nodes (hash keys and when clauses) check for duplicate nodes
* to alert the user of potential issues. To do this, we keep a set of the nodes
* that have been seen so far, and compare whenever we find a new node.
*
* We bucket the nodes based on their type to minimize the number of comparisons
* that need to be performed.
*/
typedef struct {
/**
* This is the set of IntegerNode and SourceLineNode instances.
*/
pm_node_hash_t integer_nodes;
/**
* This is the set of FloatNode instances.
*/
pm_node_hash_t float_nodes;
/**
* This is the set of RationalNode and ImaginaryNode instances.
*/
pm_node_hash_t number_nodes;
/**
* This is the set of StringNode and SourceFileNode instances.
*/
pm_node_hash_t string_nodes;
/**
* This is the set of RegularExpressionNode instances.
*/
pm_node_hash_t regexp_nodes;
/**
* This is the set of SymbolNode instances.
*/
pm_node_hash_t symbol_nodes;
/**
* A pointer to the last TrueNode instance that was inserted, or NULL.
*/
pm_node_t *true_node;
/**
* A pointer to the last FalseNode instance that was inserted, or NULL.
*/
pm_node_t *false_node;
/**
* A pointer to the last NilNode instance that was inserted, or NULL.
*/
pm_node_t *nil_node;
/**
* A pointer to the last SourceEncodingNode instance that was inserted, or
* NULL.
*/
pm_node_t *source_encoding_node;
} pm_static_literals_t;
/**
* Add a node to the set of static literals.
*
* @param newline_list The list of newline offsets to use to calculate lines.
* @param start_line The line number that the parser starts on.
* @param literals The set of static literals to add the node to.
* @param node The node to add to the set.
* @param replace Whether to replace the previous node if one already exists.
* @return A pointer to the node that is being overwritten, if there is one.
*/
pm_node_t * pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace);
/**
* Free the internal memory associated with the given static literals set.
*
* @param literals The set of static literals to free.
*/
void pm_static_literals_free(pm_static_literals_t *literals);
/**
* Create a string-based representation of the given static literal.
*
* @param buffer The buffer to write the string to.
* @param newline_list The list of newline offsets to use to calculate lines.
* @param start_line The line number that the parser starts on.
* @param encoding_name The name of the encoding of the source being parsed.
* @param node The node to create a string representation of.
*/
void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node);
#endif

View File

@@ -0,0 +1,228 @@
/**
* @file pm_buffer.h
*
* A wrapper around a contiguous block of allocated memory.
*/
#ifndef PRISM_BUFFER_H
#define PRISM_BUFFER_H
#include "prism/defines.h"
#include "prism/util/pm_char.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* A pm_buffer_t is a simple memory buffer that stores data in a contiguous
* block of memory.
*/
typedef struct {
/** The length of the buffer in bytes. */
size_t length;
/** The capacity of the buffer in bytes that has been allocated. */
size_t capacity;
/** A pointer to the start of the buffer. */
char *value;
} pm_buffer_t;
/**
* Return the size of the pm_buffer_t struct.
*
* @returns The size of the pm_buffer_t struct.
*/
PRISM_EXPORTED_FUNCTION size_t pm_buffer_sizeof(void);
/**
* Initialize a pm_buffer_t with the given capacity.
*
* @param buffer The buffer to initialize.
* @param capacity The capacity of the buffer.
* @returns True if the buffer was initialized successfully, false otherwise.
*/
bool pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity);
/**
* Initialize a pm_buffer_t with its default values.
*
* @param buffer The buffer to initialize.
* @returns True if the buffer was initialized successfully, false otherwise.
*/
PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer);
/**
* Return the value of the buffer.
*
* @param buffer The buffer to get the value of.
* @returns The value of the buffer.
*/
PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer);
/**
* Return the length of the buffer.
*
* @param buffer The buffer to get the length of.
* @returns The length of the buffer.
*/
PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer);
/**
* Append the given amount of space as zeroes to the buffer.
*
* @param buffer The buffer to append to.
* @param length The amount of space to append and zero.
*/
void pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length);
/**
* Append a formatted string to the buffer.
*
* @param buffer The buffer to append to.
* @param format The format string to append.
* @param ... The arguments to the format string.
*/
void pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) PRISM_ATTRIBUTE_FORMAT(2, 3);
/**
* Append a string to the buffer.
*
* @param buffer The buffer to append to.
* @param value The string to append.
* @param length The length of the string to append.
*/
void pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length);
/**
* Append a list of bytes to the buffer.
*
* @param buffer The buffer to append to.
* @param value The bytes to append.
* @param length The length of the bytes to append.
*/
void pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length);
/**
* Append a single byte to the buffer.
*
* @param buffer The buffer to append to.
* @param value The byte to append.
*/
void pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value);
/**
* Append a 32-bit unsigned integer to the buffer as a variable-length integer.
*
* @param buffer The buffer to append to.
* @param value The integer to append.
*/
void pm_buffer_append_varuint(pm_buffer_t *buffer, uint32_t value);
/**
* Append a 32-bit signed integer to the buffer as a variable-length integer.
*
* @param buffer The buffer to append to.
* @param value The integer to append.
*/
void pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value);
/**
* Append a double to the buffer.
*
* @param buffer The buffer to append to.
* @param value The double to append.
*/
void pm_buffer_append_double(pm_buffer_t *buffer, double value);
/**
* Append a unicode codepoint to the buffer.
*
* @param buffer The buffer to append to.
* @param value The character to append.
* @returns True if the codepoint was valid and appended successfully, false
* otherwise.
*/
bool pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value);
/**
* The different types of escaping that can be performed by the buffer when
* appending a slice of Ruby source code.
*/
typedef enum {
PM_BUFFER_ESCAPING_RUBY,
PM_BUFFER_ESCAPING_JSON
} pm_buffer_escaping_t;
/**
* Append a slice of source code to the buffer.
*
* @param buffer The buffer to append to.
* @param source The source code to append.
* @param length The length of the source code to append.
* @param escaping The type of escaping to perform.
*/
void pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping);
/**
* Prepend the given string to the buffer.
*
* @param buffer The buffer to prepend to.
* @param value The string to prepend.
* @param length The length of the string to prepend.
*/
void pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length);
/**
* Concatenate one buffer onto another.
*
* @param destination The buffer to concatenate onto.
* @param source The buffer to concatenate.
*/
void pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source);
/**
* Clear the buffer by reducing its size to 0. This does not free the allocated
* memory, but it does allow the buffer to be reused.
*
* @param buffer The buffer to clear.
*/
void pm_buffer_clear(pm_buffer_t *buffer);
/**
* Strip the whitespace from the end of the buffer.
*
* @param buffer The buffer to strip.
*/
void pm_buffer_rstrip(pm_buffer_t *buffer);
/**
* Checks if the buffer includes the given value.
*
* @param buffer The buffer to check.
* @param value The value to check for.
* @returns The index of the first occurrence of the value in the buffer, or
* SIZE_MAX if the value is not found.
*/
size_t pm_buffer_index(const pm_buffer_t *buffer, char value);
/**
* Insert the given string into the buffer at the given index.
*
* @param buffer The buffer to insert into.
* @param index The index to insert at.
* @param value The string to insert.
* @param length The length of the string to insert.
*/
void pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length);
/**
* Free the memory associated with the buffer.
*
* @param buffer The buffer to free.
*/
PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer);
#endif

View File

@@ -0,0 +1,204 @@
/**
* @file pm_char.h
*
* Functions for working with characters and strings.
*/
#ifndef PRISM_CHAR_H
#define PRISM_CHAR_H
#include "prism/defines.h"
#include "prism/util/pm_newline_list.h"
#include <stdbool.h>
#include <stddef.h>
/**
* Returns the number of characters at the start of the string that are
* whitespace. Disallows searching past the given maximum number of characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @return The number of characters at the start of the string that are
* whitespace.
*/
size_t pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length);
/**
* Returns the number of characters at the start of the string that are
* whitespace while also tracking the location of each newline. Disallows
* searching past the given maximum number of characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @param newline_list The list of newlines to populate.
* @return The number of characters at the start of the string that are
* whitespace.
*/
size_t pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newline_list_t *newline_list);
/**
* Returns the number of characters at the start of the string that are inline
* whitespace. Disallows searching past the given maximum number of characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @return The number of characters at the start of the string that are inline
* whitespace.
*/
size_t pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length);
/**
* Returns the number of characters at the start of the string that are decimal
* digits. Disallows searching past the given maximum number of characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @return The number of characters at the start of the string that are decimal
* digits.
*/
size_t pm_strspn_decimal_digit(const uint8_t *string, ptrdiff_t length);
/**
* Returns the number of characters at the start of the string that are
* hexadecimal digits. Disallows searching past the given maximum number of
* characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @return The number of characters at the start of the string that are
* hexadecimal digits.
*/
size_t pm_strspn_hexadecimal_digit(const uint8_t *string, ptrdiff_t length);
/**
* Returns the number of characters at the start of the string that are octal
* digits or underscores. Disallows searching past the given maximum number of
* characters.
*
* If multiple underscores are found in a row or if an underscore is
* found at the end of the number, then the invalid pointer is set to the index
* of the first invalid underscore.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @param invalid The pointer to set to the index of the first invalid
* underscore.
* @return The number of characters at the start of the string that are octal
* digits or underscores.
*/
size_t pm_strspn_octal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
/**
* Returns the number of characters at the start of the string that are decimal
* digits or underscores. Disallows searching past the given maximum number of
* characters.
*
* If multiple underscores are found in a row or if an underscore is
* found at the end of the number, then the invalid pointer is set to the index
* of the first invalid underscore.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @param invalid The pointer to set to the index of the first invalid
* underscore.
* @return The number of characters at the start of the string that are decimal
* digits or underscores.
*/
size_t pm_strspn_decimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
/**
* Returns the number of characters at the start of the string that are
* hexadecimal digits or underscores. Disallows searching past the given maximum
* number of characters.
*
* If multiple underscores are found in a row or if an underscore is
* found at the end of the number, then the invalid pointer is set to the index
* of the first invalid underscore.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @param invalid The pointer to set to the index of the first invalid
* underscore.
* @return The number of characters at the start of the string that are
* hexadecimal digits or underscores.
*/
size_t pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
/**
* Returns the number of characters at the start of the string that are regexp
* options. Disallows searching past the given maximum number of characters.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @return The number of characters at the start of the string that are regexp
* options.
*/
size_t pm_strspn_regexp_option(const uint8_t *string, ptrdiff_t length);
/**
* Returns the number of characters at the start of the string that are binary
* digits or underscores. Disallows searching past the given maximum number of
* characters.
*
* If multiple underscores are found in a row or if an underscore is
* found at the end of the number, then the invalid pointer is set to the index
* of the first invalid underscore.
*
* @param string The string to search.
* @param length The maximum number of characters to search.
* @param invalid The pointer to set to the index of the first invalid
* underscore.
* @return The number of characters at the start of the string that are binary
* digits or underscores.
*/
size_t pm_strspn_binary_number(const uint8_t *string, ptrdiff_t length, const uint8_t **invalid);
/**
* Returns true if the given character is a whitespace character.
*
* @param b The character to check.
* @return True if the given character is a whitespace character.
*/
bool pm_char_is_whitespace(const uint8_t b);
/**
* Returns true if the given character is an inline whitespace character.
*
* @param b The character to check.
* @return True if the given character is an inline whitespace character.
*/
bool pm_char_is_inline_whitespace(const uint8_t b);
/**
* Returns true if the given character is a binary digit.
*
* @param b The character to check.
* @return True if the given character is a binary digit.
*/
bool pm_char_is_binary_digit(const uint8_t b);
/**
* Returns true if the given character is an octal digit.
*
* @param b The character to check.
* @return True if the given character is an octal digit.
*/
bool pm_char_is_octal_digit(const uint8_t b);
/**
* Returns true if the given character is a decimal digit.
*
* @param b The character to check.
* @return True if the given character is a decimal digit.
*/
bool pm_char_is_decimal_digit(const uint8_t b);
/**
* Returns true if the given character is a hexadecimal digit.
*
* @param b The character to check.
* @return True if the given character is a hexadecimal digit.
*/
bool pm_char_is_hexadecimal_digit(const uint8_t b);
#endif

View File

@@ -0,0 +1,218 @@
/**
* @file pm_constant_pool.h
*
* A data structure that stores a set of strings.
*
* Each string is assigned a unique id, which can be used to compare strings for
* equality. This comparison ends up being much faster than strcmp, since it
* only requires a single integer comparison.
*/
#ifndef PRISM_CONSTANT_POOL_H
#define PRISM_CONSTANT_POOL_H
#include "prism/defines.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* When we allocate constants into the pool, we reserve 0 to mean that the slot
* is not yet filled. This constant is reused in other places to indicate the
* lack of a constant id.
*/
#define PM_CONSTANT_ID_UNSET 0
/**
* A constant id is a unique identifier for a constant in the constant pool.
*/
typedef uint32_t pm_constant_id_t;
/**
* A list of constant IDs. Usually used to represent a set of locals.
*/
typedef struct {
/** The number of constant ids in the list. */
size_t size;
/** The number of constant ids that have been allocated in the list. */
size_t capacity;
/** The constant ids in the list. */
pm_constant_id_t *ids;
} pm_constant_id_list_t;
/**
* Initialize a list of constant ids.
*
* @param list The list to initialize.
*/
void pm_constant_id_list_init(pm_constant_id_list_t *list);
/**
* Initialize a list of constant ids with a given capacity.
*
* @param list The list to initialize.
* @param capacity The initial capacity of the list.
*/
void pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity);
/**
* Append a constant id to a list of constant ids. Returns false if any
* potential reallocations fail.
*
* @param list The list to append to.
* @param id The id to append.
* @return Whether the append succeeded.
*/
bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id);
/**
* Insert a constant id into a list of constant ids at the specified index.
*
* @param list The list to insert into.
* @param index The index at which to insert.
* @param id The id to insert.
*/
void pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id);
/**
* Checks if the current constant id list includes the given constant id.
*
* @param list The list to check.
* @param id The id to check for.
* @return Whether the list includes the given id.
*/
bool pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id);
/**
* Free the memory associated with a list of constant ids.
*
* @param list The list to free.
*/
void pm_constant_id_list_free(pm_constant_id_list_t *list);
/**
* The type of bucket in the constant pool hash map. This determines how the
* bucket should be freed.
*/
typedef unsigned int pm_constant_pool_bucket_type_t;
/** By default, each constant is a slice of the source. */
static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_DEFAULT = 0;
/** An owned constant is one for which memory has been allocated. */
static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_OWNED = 1;
/** A constant constant is known at compile time. */
static const pm_constant_pool_bucket_type_t PM_CONSTANT_POOL_BUCKET_CONSTANT = 2;
/** A bucket in the hash map. */
typedef struct {
/** The incremental ID used for indexing back into the pool. */
unsigned int id: 30;
/** The type of the bucket, which determines how to free it. */
pm_constant_pool_bucket_type_t type: 2;
/** The hash of the bucket. */
uint32_t hash;
} pm_constant_pool_bucket_t;
/** A constant in the pool which effectively stores a string. */
typedef struct {
/** A pointer to the start of the string. */
const uint8_t *start;
/** The length of the string. */
size_t length;
} pm_constant_t;
/** The overall constant pool, which stores constants found while parsing. */
typedef struct {
/** The buckets in the hash map. */
pm_constant_pool_bucket_t *buckets;
/** The constants that are stored in the buckets. */
pm_constant_t *constants;
/** The number of buckets in the hash map. */
uint32_t size;
/** The number of buckets that have been allocated in the hash map. */
uint32_t capacity;
} pm_constant_pool_t;
/**
* Initialize a new constant pool with a given capacity.
*
* @param pool The pool to initialize.
* @param capacity The initial capacity of the pool.
* @return Whether the initialization succeeded.
*/
bool pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity);
/**
* Return a pointer to the constant indicated by the given constant id.
*
* @param pool The pool to get the constant from.
* @param constant_id The id of the constant to get.
* @return A pointer to the constant.
*/
pm_constant_t * pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id);
/**
* Find a constant in a constant pool. Returns the id of the constant, or 0 if
* the constant is not found.
*
* @param pool The pool to find the constant in.
* @param start A pointer to the start of the constant.
* @param length The length of the constant.
* @return The id of the constant.
*/
pm_constant_id_t pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length);
/**
* Insert a constant into a constant pool that is a slice of a source string.
* Returns the id of the constant, or 0 if any potential calls to resize fail.
*
* @param pool The pool to insert the constant into.
* @param start A pointer to the start of the constant.
* @param length The length of the constant.
* @return The id of the constant.
*/
pm_constant_id_t pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length);
/**
* Insert a constant into a constant pool from memory that is now owned by the
* constant pool. Returns the id of the constant, or 0 if any potential calls to
* resize fail.
*
* @param pool The pool to insert the constant into.
* @param start A pointer to the start of the constant.
* @param length The length of the constant.
* @return The id of the constant.
*/
pm_constant_id_t pm_constant_pool_insert_owned(pm_constant_pool_t *pool, uint8_t *start, size_t length);
/**
* Insert a constant into a constant pool from memory that is constant. Returns
* the id of the constant, or 0 if any potential calls to resize fail.
*
* @param pool The pool to insert the constant into.
* @param start A pointer to the start of the constant.
* @param length The length of the constant.
* @return The id of the constant.
*/
pm_constant_id_t pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length);
/**
* Free the memory associated with a constant pool.
*
* @param pool The pool to free.
*/
void pm_constant_pool_free(pm_constant_pool_t *pool);
#endif

View File

@@ -0,0 +1,126 @@
/**
* @file pm_integer.h
*
* This module provides functions for working with arbitrary-sized integers.
*/
#ifndef PRISM_NUMBER_H
#define PRISM_NUMBER_H
#include "prism/defines.h"
#include "prism/util/pm_buffer.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/**
* A structure represents an arbitrary-sized integer.
*/
typedef struct {
/**
* The number of allocated values. length is set to 0 if the integer fits
* into uint32_t.
*/
size_t length;
/**
* List of 32-bit integers. Set to NULL if the integer fits into uint32_t.
*/
uint32_t *values;
/**
* Embedded value for small integer. This value is set to 0 if the value
* does not fit into uint32_t.
*/
uint32_t value;
/**
* Whether or not the integer is negative. It is stored this way so that a
* zeroed pm_integer_t is always positive zero.
*/
bool negative;
} pm_integer_t;
/**
* An enum controlling the base of an integer. It is expected that the base is
* already known before parsing the integer, even though it could be derived
* from the string itself.
*/
typedef enum {
/** The default decimal base, with no prefix. Leading 0s will be ignored. */
PM_INTEGER_BASE_DEFAULT,
/** The binary base, indicated by a 0b or 0B prefix. */
PM_INTEGER_BASE_BINARY,
/** The octal base, indicated by a 0, 0o, or 0O prefix. */
PM_INTEGER_BASE_OCTAL,
/** The decimal base, indicated by a 0d, 0D, or empty prefix. */
PM_INTEGER_BASE_DECIMAL,
/** The hexadecimal base, indicated by a 0x or 0X prefix. */
PM_INTEGER_BASE_HEXADECIMAL,
/**
* An unknown base, in which case pm_integer_parse will derive it based on
* the content of the string. This is less efficient and does more
* comparisons, so if callers know the base ahead of time, they should use
* that instead.
*/
PM_INTEGER_BASE_UNKNOWN
} pm_integer_base_t;
/**
* Parse an integer from a string. This assumes that the format of the integer
* has already been validated, as internal validation checks are not performed
* here.
*
* @param integer The integer to parse into.
* @param base The base of the integer.
* @param start The start of the string.
* @param end The end of the string.
*/
void pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end);
/**
* Compare two integers. This function returns -1 if the left integer is less
* than the right integer, 0 if they are equal, and 1 if the left integer is
* greater than the right integer.
*
* @param left The left integer to compare.
* @param right The right integer to compare.
* @return The result of the comparison.
*/
int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right);
/**
* Reduce a ratio of integers to its simplest form.
*
* If either the numerator or denominator do not fit into a 32-bit integer, then
* this function is a no-op. In the future, we may consider reducing even the
* larger numbers, but for now we're going to keep it simple.
*
* @param numerator The numerator of the ratio.
* @param denominator The denominator of the ratio.
*/
void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator);
/**
* Convert an integer to a decimal string.
*
* @param buffer The buffer to append the string to.
* @param integer The integer to convert to a string.
*/
PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer);
/**
* Free the internal memory of an integer. This memory will only be allocated if
* the integer exceeds the size of a single node in the linked list.
*
* @param integer The integer to free.
*/
PRISM_EXPORTED_FUNCTION void pm_integer_free(pm_integer_t *integer);
#endif

View File

@@ -0,0 +1,97 @@
/**
* @file pm_list.h
*
* An abstract linked list.
*/
#ifndef PRISM_LIST_H
#define PRISM_LIST_H
#include "prism/defines.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/**
* This struct represents an abstract linked list that provides common
* functionality. It is meant to be used any time a linked list is necessary to
* store data.
*
* The linked list itself operates off a set of pointers. Because the pointers
* are not necessarily sequential, they can be of any size. We use this fact to
* allow the consumer of this linked list to extend the node struct to include
* any data they want. This is done by using the pm_list_node_t as the first
* member of the struct.
*
* For example, if we want to store a list of integers, we can do the following:
*
* ```c
* typedef struct {
* pm_list_node_t node;
* int value;
* } pm_int_node_t;
*
* pm_list_t list = { 0 };
* pm_int_node_t *node = xmalloc(sizeof(pm_int_node_t));
* node->value = 5;
*
* pm_list_append(&list, &node->node);
* ```
*
* The pm_list_t struct is used to represent the overall linked list. It
* contains a pointer to the head and tail of the list. This allows for easy
* iteration and appending of new nodes.
*/
typedef struct pm_list_node {
/** A pointer to the next node in the list. */
struct pm_list_node *next;
} pm_list_node_t;
/**
* This represents the overall linked list. It keeps a pointer to the head and
* tail so that iteration is easy and pushing new nodes is easy.
*/
typedef struct {
/** The size of the list. */
size_t size;
/** A pointer to the head of the list. */
pm_list_node_t *head;
/** A pointer to the tail of the list. */
pm_list_node_t *tail;
} pm_list_t;
/**
* Returns true if the given list is empty.
*
* @param list The list to check.
* @return True if the given list is empty, otherwise false.
*/
PRISM_EXPORTED_FUNCTION bool pm_list_empty_p(pm_list_t *list);
/**
* Returns the size of the list.
*
* @param list The list to check.
* @return The size of the list.
*/
PRISM_EXPORTED_FUNCTION size_t pm_list_size(pm_list_t *list);
/**
* Append a node to the given list.
*
* @param list The list to append to.
* @param node The node to append.
*/
void pm_list_append(pm_list_t *list, pm_list_node_t *node);
/**
* Deallocate the internal state of the given list.
*
* @param list The list to free.
*/
PRISM_EXPORTED_FUNCTION void pm_list_free(pm_list_t *list);
#endif

View File

@@ -0,0 +1,29 @@
/**
* @file pm_memchr.h
*
* A custom memchr implementation.
*/
#ifndef PRISM_MEMCHR_H
#define PRISM_MEMCHR_H
#include "prism/defines.h"
#include "prism/encoding.h"
#include <stddef.h>
/**
* We need to roll our own memchr to handle cases where the encoding changes and
* we need to search for a character in a buffer that could be the trailing byte
* of a multibyte character.
*
* @param source The source string.
* @param character The character to search for.
* @param number The maximum number of bytes to search.
* @param encoding_changed Whether the encoding changed.
* @param encoding A pointer to the encoding.
* @return A pointer to the first occurrence of the character in the source
* string, or NULL if no such character exists.
*/
void * pm_memchr(const void *source, int character, size_t number, bool encoding_changed, const pm_encoding_t *encoding);
#endif

View File

@@ -0,0 +1,113 @@
/**
* @file pm_newline_list.h
*
* A list of byte offsets of newlines in a string.
*
* When compiling the syntax tree, it's necessary to know the line and column
* of many nodes. This is necessary to support things like error messages,
* tracepoints, etc.
*
* It's possible that we could store the start line, start column, end line, and
* end column on every node in addition to the offsets that we already store,
* but that would be quite a lot of memory overhead.
*/
#ifndef PRISM_NEWLINE_LIST_H
#define PRISM_NEWLINE_LIST_H
#include "prism/defines.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
/**
* A list of offsets of newlines in a string. The offsets are assumed to be
* sorted/inserted in ascending order.
*/
typedef struct {
/** A pointer to the start of the source string. */
const uint8_t *start;
/** The number of offsets in the list. */
size_t size;
/** The capacity of the list that has been allocated. */
size_t capacity;
/** The list of offsets. */
size_t *offsets;
} pm_newline_list_t;
/**
* A line and column in a string.
*/
typedef struct {
/** The line number. */
int32_t line;
/** The column number. */
uint32_t column;
} pm_line_column_t;
/**
* Initialize a new newline list with the given capacity. Returns true if the
* allocation of the offsets succeeds, otherwise returns false.
*
* @param list The list to initialize.
* @param start A pointer to the start of the source string.
* @param capacity The initial capacity of the list.
* @return True if the allocation of the offsets succeeds, otherwise false.
*/
bool pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity);
/**
* Clear out the newlines that have been appended to the list.
*
* @param list The list to clear.
*/
void
pm_newline_list_clear(pm_newline_list_t *list);
/**
* Append a new offset to the newline list. Returns true if the reallocation of
* the offsets succeeds (if one was necessary), otherwise returns false.
*
* @param list The list to append to.
* @param cursor A pointer to the offset to append.
* @return True if the reallocation of the offsets succeeds (if one was
* necessary), otherwise false.
*/
bool pm_newline_list_append(pm_newline_list_t *list, const uint8_t *cursor);
/**
* Returns the line of the given offset. If the offset is not in the list, the
* line of the closest offset less than the given offset is returned.
*
* @param list The list to search.
* @param cursor A pointer to the offset to search for.
* @param start_line The line to start counting from.
* @return The line of the given offset.
*/
int32_t pm_newline_list_line(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line);
/**
* Returns the line and column of the given offset. If the offset is not in the
* list, the line and column of the closest offset less than the given offset
* are returned.
*
* @param list The list to search.
* @param cursor A pointer to the offset to search for.
* @param start_line The line to start counting from.
* @return The line and column of the given offset.
*/
pm_line_column_t pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line);
/**
* Free the internal memory allocated for the newline list.
*
* @param list The list to free.
*/
void pm_newline_list_free(pm_newline_list_t *list);
#endif

View File

@@ -0,0 +1,190 @@
/**
* @file pm_string.h
*
* A generic string type that can have various ownership semantics.
*/
#ifndef PRISM_STRING_H
#define PRISM_STRING_H
#include "prism/defines.h"
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
// The following headers are necessary to read files using demand paging.
#ifdef _WIN32
#include <windows.h>
#elif defined(_POSIX_MAPPED_FILES)
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#elif defined(PRISM_HAS_FILESYSTEM)
#include <fcntl.h>
#include <sys/stat.h>
#endif
/**
* A generic string type that can have various ownership semantics.
*/
typedef struct {
/** A pointer to the start of the string. */
const uint8_t *source;
/** The length of the string in bytes of memory. */
size_t length;
/** The type of the string. This field determines how the string should be freed. */
enum {
/** This string is a constant string, and should not be freed. */
PM_STRING_CONSTANT,
/** This is a slice of another string, and should not be freed. */
PM_STRING_SHARED,
/** This string owns its memory, and should be freed using `pm_string_free`. */
PM_STRING_OWNED,
#ifdef PRISM_HAS_MMAP
/** This string is a memory-mapped file, and should be freed using `pm_string_free`. */
PM_STRING_MAPPED
#endif
} type;
} pm_string_t;
/**
* Returns the size of the pm_string_t struct. This is necessary to allocate the
* correct amount of memory in the FFI backend.
*
* @return The size of the pm_string_t struct.
*/
PRISM_EXPORTED_FUNCTION size_t pm_string_sizeof(void);
/**
* Defines an empty string. This is useful for initializing a string that will
* be filled in later.
*/
#define PM_STRING_EMPTY ((pm_string_t) { .type = PM_STRING_CONSTANT, .source = NULL, .length = 0 })
/**
* Initialize a shared string that is based on initial input.
*
* @param string The string to initialize.
* @param start The start of the string.
* @param end The end of the string.
*/
void pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end);
/**
* Initialize an owned string that is responsible for freeing allocated memory.
*
* @param string The string to initialize.
* @param source The source of the string.
* @param length The length of the string.
*/
void pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length);
/**
* Initialize a constant string that doesn't own its memory source.
*
* @param string The string to initialize.
* @param source The source of the string.
* @param length The length of the string.
*/
void pm_string_constant_init(pm_string_t *string, const char *source, size_t length);
/**
* Represents the result of calling pm_string_mapped_init or
* pm_string_file_init. We need this additional information because there is
* not a platform-agnostic way to indicate that the file that was attempted to
* be opened was a directory.
*/
typedef enum {
/** Indicates that the string was successfully initialized. */
PM_STRING_INIT_SUCCESS = 0,
/**
* Indicates a generic error from a string_*_init function, where the type
* of error should be read from `errno` or `GetLastError()`.
*/
PM_STRING_INIT_ERROR_GENERIC = 1,
/**
* Indicates that the file that was attempted to be opened was a directory.
*/
PM_STRING_INIT_ERROR_DIRECTORY = 2
} pm_string_init_result_t;
/**
* Read the file indicated by the filepath parameter into source and load its
* contents and size into the given `pm_string_t`. The given `pm_string_t`
* should be freed using `pm_string_free` when it is no longer used.
*
* We want to use demand paging as much as possible in order to avoid having to
* read the entire file into memory (which could be detrimental to performance
* for large files). This means that if we're on windows we'll use
* `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
* `mmap`, and on other POSIX systems we'll use `read`.
*
* @param string The string to initialize.
* @param filepath The filepath to read.
* @return The success of the read, indicated by the value of the enum.
*/
PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_mapped_init(pm_string_t *string, const char *filepath);
/**
* Read the file indicated by the filepath parameter into source and load its
* contents and size into the given `pm_string_t`. The given `pm_string_t`
* should be freed using `pm_string_free` when it is no longer used.
*
* @param string The string to initialize.
* @param filepath The filepath to read.
* @return The success of the read, indicated by the value of the enum.
*/
PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath);
/**
* Ensure the string is owned. If it is not, then reinitialize it as owned and
* copy over the previous source.
*
* @param string The string to ensure is owned.
*/
void pm_string_ensure_owned(pm_string_t *string);
/**
* Compare the underlying lengths and bytes of two strings. Returns 0 if the
* strings are equal, a negative number if the left string is less than the
* right string, and a positive number if the left string is greater than the
* right string.
*
* @param left The left string to compare.
* @param right The right string to compare.
* @return The comparison result.
*/
int pm_string_compare(const pm_string_t *left, const pm_string_t *right);
/**
* Returns the length associated with the string.
*
* @param string The string to get the length of.
* @return The length of the string.
*/
PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string);
/**
* Returns the start pointer associated with the string.
*
* @param string The string to get the start pointer of.
* @return The start pointer of the string.
*/
PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string);
/**
* Free the associated memory of the given string.
*
* @param string The string to free.
*/
PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string);
#endif

View File

@@ -0,0 +1,32 @@
/**
* @file pm_strncasecmp.h
*
* A custom strncasecmp implementation.
*/
#ifndef PRISM_STRNCASECMP_H
#define PRISM_STRNCASECMP_H
#include "prism/defines.h"
#include <ctype.h>
#include <stddef.h>
#include <stdint.h>
/**
* Compare two strings, ignoring case, up to the given length. Returns 0 if the
* strings are equal, a negative number if string1 is less than string2, or a
* positive number if string1 is greater than string2.
*
* Note that this is effectively our own implementation of strncasecmp, but it's
* not available on all of the platforms we want to support so we're rolling it
* here.
*
* @param string1 The first string to compare.
* @param string2 The second string to compare
* @param length The maximum number of characters to compare.
* @return 0 if the strings are equal, a negative number if string1 is less than
* string2, or a positive number if string1 is greater than string2.
*/
int pm_strncasecmp(const uint8_t *string1, const uint8_t *string2, size_t length);
#endif

View File

@@ -0,0 +1,46 @@
/**
* @file pm_strpbrk.h
*
* A custom strpbrk implementation.
*/
#ifndef PRISM_STRPBRK_H
#define PRISM_STRPBRK_H
#include "prism/defines.h"
#include "prism/diagnostic.h"
#include "prism/parser.h"
#include <stddef.h>
#include <string.h>
/**
* Here we have rolled our own version of strpbrk. The standard library strpbrk
* has undefined behavior when the source string is not null-terminated. We want
* to support strings that are not null-terminated because pm_parse does not
* have the contract that the string is null-terminated. (This is desirable
* because it means the extension can call pm_parse with the result of a call to
* mmap).
*
* The standard library strpbrk also does not support passing a maximum length
* to search. We want to support this for the reason mentioned above, but we
* also don't want it to stop on null bytes. Ruby actually allows null bytes
* within strings, comments, regular expressions, etc. So we need to be able to
* skip past them.
*
* Finally, we want to support encodings wherein the charset could contain
* characters that are trailing bytes of multi-byte characters. For example, in
* Shift-JIS, the backslash character can be a trailing byte. In that case we
* need to take a slower path and iterate one multi-byte character at a time.
*
* @param parser The parser.
* @param source The source to search.
* @param charset The charset to search for.
* @param length The maximum number of bytes to search.
* @param validate Whether to validate that the source string is valid in the
* current encoding of the parser.
* @return A pointer to the first character in the source string that is in the
* charset, or NULL if no such character exists.
*/
const uint8_t * pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate);
#endif

View File

@@ -0,0 +1,29 @@
/**
* @file version.h
*
* The version of the Prism library.
*/
#ifndef PRISM_VERSION_H
#define PRISM_VERSION_H
/**
* The major version of the Prism library as an int.
*/
#define PRISM_VERSION_MAJOR 1
/**
* The minor version of the Prism library as an int.
*/
#define PRISM_VERSION_MINOR 4
/**
* The patch version of the Prism library as an int.
*/
#define PRISM_VERSION_PATCH 0
/**
* The version of the Prism library as a constant string.
*/
#define PRISM_VERSION "1.4.0"
#endif