issue #198 Include Ravi Compiler
parent
31774723ff
commit
2aeeea4dd3
@ -0,0 +1,7 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
UseTab: Always
|
||||
BreakBeforeBraces: Linux
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
ColumnLimit: 120
|
@ -0,0 +1,110 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(RaviCompiler VERSION 0.0.1 LANGUAGES C)
|
||||
|
||||
option(ASAN "Controls whether address sanitizer should be enabled" OFF)
|
||||
|
||||
set(PUBLIC_HEADERS
|
||||
include/ravi_compiler.h
|
||||
include/ravi_api.h)
|
||||
|
||||
set(HEADERS
|
||||
${PUBLIC_HEADERS}
|
||||
src/allocate.h
|
||||
src/bitset.h
|
||||
src/ptrlist.h
|
||||
src/fnv_hash.h
|
||||
src/graph.h
|
||||
src/hash_table.h
|
||||
src/set.h
|
||||
src/membuf.h
|
||||
src/cfg.h
|
||||
src/dominator.h
|
||||
src/linearizer.h
|
||||
src/common.h
|
||||
src/dataflow_framework.h
|
||||
src/optimizer.h
|
||||
src/parser.h
|
||||
src/codegen.h)
|
||||
|
||||
set(SRCS
|
||||
src/allocate.c
|
||||
src/ast_walker.c
|
||||
src/ast_simplify.c
|
||||
src/bitset.c
|
||||
src/ptrlist.c
|
||||
src/fnv_hash.c
|
||||
src/graph.c
|
||||
src/cfg.c
|
||||
src/dominator.c
|
||||
src/hash_table.c
|
||||
src/set.c
|
||||
src/lexer.c
|
||||
src/parser.c
|
||||
src/ast_printer.c
|
||||
src/typechecker.c
|
||||
src/linearizer.c
|
||||
src/dataflow_framework.c
|
||||
src/opt_unusedcode.c
|
||||
src/membuf.c
|
||||
src/df_liveness.c
|
||||
src/codegen.c
|
||||
src/ravi_binding.c
|
||||
)
|
||||
|
||||
message("SOURCE dir is ${RaviCompiler_SOURCE_DIR}")
|
||||
|
||||
if ($ENV{CLION_IDE})
|
||||
# CLion seems unable to handle include paths set on sources
|
||||
include_directories("${RaviCompiler_SOURCE_DIR}/include")
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# disable warnings about C string functions
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wmissing-prototypes -Wstrict-prototypes -Werror=return-type")
|
||||
if (ASAN)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
check_c_compiler_flag("-fsanitize=address" COMPILER_ASAN_SUPPORTED)
|
||||
if (COMPILER_ASAN_SUPPORTED AND NOT CMAKE_C_FLAGS_DEBUG MATCHES "-fsanitize=address")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
|
||||
endif ()
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
|
||||
|
||||
if (NOT WIN32)
|
||||
set(EXTRA_LIBRARIES m)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
set(LIBTYPE STATIC)
|
||||
else()
|
||||
set(LIBTYPE SHARED)
|
||||
endif()
|
||||
add_library(ravicomp ${LIBTYPE}
|
||||
${HEADERS}
|
||||
${SRCS})
|
||||
target_include_directories(ravicomp
|
||||
PUBLIC "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
PUBLIC "${RaviCompiler_SOURCE_DIR}/include"
|
||||
PRIVATE "${RaviCompiler_SOURCE_DIR}/src")
|
||||
target_link_libraries(ravicomp ${EXTRA_LIBRARIES})
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(ravicomp)
|
||||
|
||||
install(FILES ${PUBLIC_HEADERS}
|
||||
DESTINATION include/ravicomp)
|
||||
install(TARGETS ravicomp
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT RaviCompiler_Runtime
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Development
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Runtime)
|
||||
install(FILES
|
||||
${PROJECT_BINARY_DIR}/ravicomp_export.h DESTINATION include/ravicomp
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
# ravi-compiler
|
||||
A compiler for Ravi and Lua that processes Lua/Ravi source code and generates C code.
|
||||
|
||||
This is a mirror of the project at [https://github.com/dibyendumajumdar/ravi-compiler](https://github.com/dibyendumajumdar/ravi-compiler).
|
||||
Note that the command line tools are not included here.
|
||||
|
@ -0,0 +1,36 @@
|
||||
#ifndef ravicomp_RAVIAPI_H
|
||||
#define ravicomp_RAVIAPI_H
|
||||
|
||||
#include "ravicomp_export.h"
|
||||
#include "ravi_compiler.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct Ravi_CompilerInterface {
|
||||
/* ------------------------ Inputs ------------------------------ */
|
||||
void *context; /* Ravi supplied context */
|
||||
|
||||
const char *source; /* Source code to be compiled - managed by Ravi */
|
||||
size_t source_len; /* Size of source code */
|
||||
const char *source_name; /* Name of the source */
|
||||
const char *compiler_options; /* flags to be passed to compiler */
|
||||
|
||||
char main_func_name[31]; /* Name of the generated function that when called will set up the Lua closure */
|
||||
|
||||
/* ------------------------- Outputs ------------------------------ */
|
||||
const char* generated_code; /* Output of the compiler, must be freed by caller. */
|
||||
|
||||
/* ------------------------ Debugging and error handling ----------------------------------------- */
|
||||
void (*debug_message)(void *context, const char *filename, long long line, const char *message);
|
||||
void (*error_message)(void *context, const char *message);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the API exposed by the Compiler itself. This function is invoked by
|
||||
* Ravi when it is necessary to compile some Ravi code.
|
||||
* @param compiler_interface The interface expected by the compiler must be setup
|
||||
* @return 0 for success, non-zero for failure
|
||||
*/
|
||||
RAVICOMP_EXPORT int raviX_compile(struct Ravi_CompilerInterface *compiler_interface);
|
||||
|
||||
#endif
|
@ -0,0 +1,589 @@
|
||||
/*
|
||||
A compiler for Ravi and Lua 5.3. This is work in progress.
|
||||
Once ready it will be used to create a JIT compiler for Ravi.
|
||||
|
||||
This header file defines the public api
|
||||
|
||||
Copyright 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#ifndef ravicomp_COMPILER_H
|
||||
#define ravicomp_COMPILER_H
|
||||
|
||||
#include "ravicomp_export.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct CompilerState CompilerState;
|
||||
typedef struct LexerState LexerState;
|
||||
typedef struct LinearizerState LinearizerState;
|
||||
typedef struct VariableType VariableType;
|
||||
|
||||
typedef long long lua_Integer;
|
||||
typedef double lua_Number;
|
||||
|
||||
/* Initialize the compiler state */
|
||||
/* During compilation all data structures are stored in the compiler state */
|
||||
RAVICOMP_EXPORT CompilerState *raviX_init_compiler(void);
|
||||
/* Destroy the compiler state */
|
||||
RAVICOMP_EXPORT void raviX_destroy_compiler(CompilerState *compiler);
|
||||
|
||||
/* ------------------------ LEXICAL ANALYZER API -------------------------------*/
|
||||
/* Note: following enum was generate using utils/tokenenum.h */
|
||||
enum TokenType {
|
||||
TOK_OFS = 256,
|
||||
|
||||
TOK_and,
|
||||
TOK_break,
|
||||
TOK_do,
|
||||
TOK_else,
|
||||
TOK_elseif,
|
||||
TOK_end,
|
||||
TOK_false,
|
||||
TOK_for,
|
||||
TOK_function,
|
||||
TOK_goto,
|
||||
TOK_if,
|
||||
TOK_in,
|
||||
TOK_local,
|
||||
TOK_defer,
|
||||
TOK_nil,
|
||||
TOK_not,
|
||||
TOK_or,
|
||||
TOK_repeat,
|
||||
TOK_return,
|
||||
TOK_then,
|
||||
TOK_true,
|
||||
TOK_until,
|
||||
TOK_while,
|
||||
TOK_IDIV,
|
||||
TOK_CONCAT,
|
||||
TOK_DOTS,
|
||||
TOK_EQ,
|
||||
TOK_GE,
|
||||
TOK_LE,
|
||||
TOK_NE,
|
||||
TOK_SHL,
|
||||
TOK_SHR,
|
||||
TOK_DBCOLON,
|
||||
TOK_TO_INTEGER,
|
||||
TOK_TO_NUMBER,
|
||||
TOK_TO_INTARRAY,
|
||||
TOK_TO_NUMARRAY,
|
||||
TOK_TO_TABLE,
|
||||
TOK_TO_STRING,
|
||||
TOK_TO_CLOSURE,
|
||||
TOK_EOS,
|
||||
TOK_FLT,
|
||||
TOK_INT,
|
||||
TOK_NAME,
|
||||
TOK_STRING,
|
||||
|
||||
FIRST_RESERVED = TOK_OFS + 1,
|
||||
LAST_RESERVED = TOK_while - TOK_OFS
|
||||
};
|
||||
|
||||
/*
|
||||
* Lua strings can have embedded 0 bytes therefore we
|
||||
* need a string type that has a length associated with it.
|
||||
*
|
||||
* The compiler stores a single copy of each string so that strings
|
||||
* can be compared by equality.
|
||||
*/
|
||||
typedef struct StringObject {
|
||||
uint32_t len; /* length of the string */
|
||||
int32_t reserved; /* if is this a keyword then token id else -1 */
|
||||
uint32_t hash; /* hash value of the string */
|
||||
const char *str; /* string data */
|
||||
} StringObject;
|
||||
|
||||
/*
|
||||
* Lua literals
|
||||
*/
|
||||
typedef union {
|
||||
lua_Number r;
|
||||
lua_Integer i;
|
||||
const StringObject *ts;
|
||||
} SemInfo;
|
||||
|
||||
typedef struct Token {
|
||||
int token; /* Token value or character value; token values start from FIRST_RESERVED which is 257, values < 256
|
||||
are characters */
|
||||
SemInfo seminfo; /* Literal associated with the token, only valid when token is a literal or an identifier, i.e.
|
||||
token is > TOK_EOS */
|
||||
} Token;
|
||||
|
||||
/*
|
||||
* Everything below should be treated as readonly; for efficiency these fields are exposed, however treat them
|
||||
* as fields managed by the lexer.
|
||||
*/
|
||||
typedef struct {
|
||||
int current; /* current character (char value as int) */
|
||||
int linenumber; /* current input line counter */
|
||||
int lastline; /* line number of the last token 'consumed' */
|
||||
Token t; /* current token, set after call to raviX_next() */
|
||||
Token lookahead; /* look ahead token, set after call to raviX_lookahead() */
|
||||
} LexerInfo;
|
||||
|
||||
/* Following is a dynamic buffer implementation that is not strictly part of the
|
||||
* compiler api but is relied upon by various compiler parts. We should perhaps avoid
|
||||
* exposing it.
|
||||
*
|
||||
* The reason for exposing this is that we use it for getting the token string in one of the
|
||||
* api calls.
|
||||
*/
|
||||
typedef struct {
|
||||
char *buf; /* pointer to allocated memory, can be reallocated */
|
||||
size_t capacity; /* allocated size */
|
||||
size_t pos; /* current position in the buffer */
|
||||
} TextBuffer;
|
||||
|
||||
/* all strings are interned and stored in a hash set, strings may have embedded
|
||||
* 0 bytes therefore explicit length is necessary
|
||||
*/
|
||||
RAVICOMP_EXPORT const StringObject *raviX_create_string(CompilerState *compiler_state, const char *s,
|
||||
uint32_t len);
|
||||
|
||||
/* Initialize lexical analyser. Takes as input a buffer containing Lua/Ravi source and the source name */
|
||||
RAVICOMP_EXPORT LexerState *raviX_init_lexer(CompilerState *compiler_state, const char *buf,
|
||||
size_t buflen, const char *source_name);
|
||||
/* Gets the public part of the lexer data structure to allow access the current token. Note that the returned
|
||||
* value should be treated as readonly data structure
|
||||
*/
|
||||
RAVICOMP_EXPORT const LexerInfo *raviX_get_lexer_info(LexerState *ls);
|
||||
/* Retrieves the next token and saves it is LexState structure. If a lookahead was set then that is retrieved
|
||||
* (and reset to EOS) else the next token is retrieved
|
||||
*/
|
||||
RAVICOMP_EXPORT void raviX_next(LexerState *ls);
|
||||
/* Retrieves the next token and sets it as the lookahead. This means that a next call will get the lookahead.
|
||||
* Returns the token id.
|
||||
*/
|
||||
RAVICOMP_EXPORT int raviX_lookahead(LexerState *ls);
|
||||
/* Convert a token to text format. The token will be written to current position in mb. */
|
||||
RAVICOMP_EXPORT void raviX_token2str(int token, TextBuffer *mb);
|
||||
/* Release all data structures used by the lexer */
|
||||
RAVICOMP_EXPORT void raviX_destroy_lexer(LexerState *);
|
||||
|
||||
/* ---------------- PARSER API -------------------------- */
|
||||
|
||||
/*
|
||||
* Parse a Lua chunk (i.e. script).
|
||||
* The Lua chunk will be wrapped in an anonymous Lua function (the 'main' function), so all the code
|
||||
* in the chunk will be part of that function. Any functions defined in the chunk will become child functions
|
||||
* of the 'main' function.
|
||||
*
|
||||
* Each Lua chunk / script therefore has an anonymous 'main' function. The name 'main' is just to refer
|
||||
* to this function as it has no name in reality.
|
||||
*
|
||||
* Note that at present a new compiler state should be created when processing a Lua chunk.
|
||||
*
|
||||
* Returns 0 on success, non-zero on failure.
|
||||
*/
|
||||
RAVICOMP_EXPORT int raviX_parse(CompilerState *compiler_state, const char *buffer, size_t buflen,
|
||||
const char *source_name);
|
||||
/* Prints out the AST to the file */
|
||||
RAVICOMP_EXPORT void raviX_output_ast(CompilerState *compiler_state, FILE *fp);
|
||||
/* Performs type checks on the AST and annotates types of expressions nad variables where possible.
|
||||
* As a result the AST will be modified.
|
||||
*
|
||||
* Returns 0 on success, non-zero on failure.
|
||||
*/
|
||||
RAVICOMP_EXPORT int
|
||||
raviX_ast_typecheck(CompilerState *compiler_state); /* Perform type checks and assign types to AST */
|
||||
|
||||
/* ---------------------------- LINEARIZER API --------------------------------------- */
|
||||
/* linear IR generator.
|
||||
* The goal of this component is to convert the AST to a linear IR.
|
||||
* This is work in progress, therefore the IR is not yet publicly exposed.
|
||||
*/
|
||||
RAVICOMP_EXPORT LinearizerState *raviX_init_linearizer(CompilerState *compiler_state);
|
||||
/* Attempts to create linear IR for given AST.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
RAVICOMP_EXPORT int raviX_ast_linearize(LinearizerState *linearizer);
|
||||
/* Prints out the content of the linear IR */
|
||||
RAVICOMP_EXPORT void raviX_output_linearizer(LinearizerState *linearizer, FILE *fp);
|
||||
/* Cleanup the linearizer */
|
||||
RAVICOMP_EXPORT void raviX_destroy_linearizer(LinearizerState *linearizer);
|
||||
|
||||
/* utilies */
|
||||
RAVICOMP_EXPORT const char *raviX_get_last_error(CompilerState *compiler_state);
|
||||
|
||||
/* ----------------------- AST WALKING API ------------------------ */
|
||||
|
||||
/* Binary operators */
|
||||
typedef enum BinaryOperatorType {
|
||||
BINOPR_ADD,
|
||||
BINOPR_SUB,
|
||||
BINOPR_MUL,
|
||||
BINOPR_MOD,
|
||||
BINOPR_POW,
|
||||
BINOPR_DIV,
|
||||
BINOPR_IDIV,
|
||||
BINOPR_BAND,
|
||||
BINOPR_BOR,
|
||||
BINOPR_BXOR,
|
||||
BINOPR_SHL,
|
||||
BINOPR_SHR,
|
||||
BINOPR_CONCAT,
|
||||
BINOPR_EQ,
|
||||
BINOPR_LT,
|
||||
BINOPR_LE,
|
||||
BINOPR_NE,
|
||||
BINOPR_GT,
|
||||
BINOPR_GE,
|
||||
BINOPR_AND,
|
||||
BINOPR_OR,
|
||||
BINOPR_NOBINOPR
|
||||
} BinaryOperatorType;
|
||||
|
||||
RAVICOMP_EXPORT const char *raviX_get_binary_opr_str(BinaryOperatorType op);
|
||||
|
||||
/* Unary operators */
|
||||
typedef enum UnaryOperatorType {
|
||||
UNOPR_MINUS = BINOPR_NOBINOPR + 1,
|
||||
UNOPR_BNOT,
|
||||
UNOPR_NOT,
|
||||
UNOPR_LEN,
|
||||
UNOPR_TO_INTEGER,
|
||||
UNOPR_TO_NUMBER,
|
||||
UNOPR_TO_INTARRAY,
|
||||
UNOPR_TO_NUMARRAY,
|
||||
UNOPR_TO_TABLE,
|
||||
UNOPR_TO_STRING,
|
||||
UNOPR_TO_CLOSURE,
|
||||
UNOPR_TO_TYPE,
|
||||
UNOPR_NOUNOPR
|
||||
} UnaryOperatorType;
|
||||
|
||||
RAVICOMP_EXPORT const char *raviX_get_unary_opr_str(UnaryOperatorType op);
|
||||
|
||||
/* Types of AST nodes */
|
||||
enum AstNodeType {
|
||||
AST_NONE, /* Will never be set on a properly initialized node */
|
||||
STMT_RETURN,
|
||||
STMT_GOTO,
|
||||
STMT_LABEL,
|
||||
STMT_DO,
|
||||
STMT_LOCAL,
|
||||
STMT_FUNCTION,
|
||||
STMT_IF,
|
||||
STMT_TEST_THEN,
|
||||
STMT_WHILE,
|
||||
STMT_FOR_IN,
|
||||
STMT_FOR_NUM,
|
||||
STMT_REPEAT,
|
||||
STMT_EXPR, /* Also used for assignment statements */
|
||||
EXPR_LITERAL,
|
||||
EXPR_SYMBOL,
|
||||
EXPR_Y_INDEX, /* [] operator */
|
||||
EXPR_FIELD_SELECTOR, /* table field access - '.' or ':' operator */
|
||||
EXPR_TABLE_ELEMENT_ASSIGN, /* table element assignment in table constructor */
|
||||
EXPR_SUFFIXED,
|
||||
EXPR_UNARY,
|
||||
EXPR_BINARY,
|
||||
EXPR_FUNCTION, /* function literal */
|
||||
EXPR_TABLE_LITERAL, /* table constructor */
|
||||
EXPR_FUNCTION_CALL
|
||||
};
|
||||
|
||||
typedef struct Statement Statement;
|
||||
typedef struct ReturnStatement ReturnStatement;
|
||||
typedef struct LabelStatement LabelStatement;
|
||||
typedef struct GotoStatement GotoStatement;
|
||||
typedef struct LocalStatement LocalStatement;
|
||||
typedef struct ExpressionStatement ExpressionStatement;
|
||||
typedef struct FunctionStatement FunctionStatement;
|
||||
typedef struct DoStatement DoStatement;
|
||||
typedef struct TestThenStatement TestThenStatement;
|
||||
typedef struct IfStatement IfStatement;
|
||||
typedef struct WhileOrRepeatStatement WhileOrRepeatStatement;
|
||||
typedef struct ForStatement ForStatement;
|
||||
|
||||
typedef struct Expression Expression;
|
||||
typedef struct LiteralExpression LiteralExpression;
|
||||
typedef struct SymbolExpression SymbolExpression;
|
||||
typedef struct IndexExpression IndexExpression;
|
||||
typedef struct UnaryExpression UnaryExpression;
|
||||
typedef struct BinaryExpression BinaryExpression;
|
||||
typedef struct FunctionExpression FunctionExpression;
|
||||
typedef struct TableElementAssignmentExpression TableElementAssignmentExpression;
|
||||
typedef struct TableLiteralExpression TableLiteralExpression;
|
||||
typedef struct SuffixedExpression SuffixedExpression;
|
||||
typedef struct FunctionCallExpression FunctionCallExpression;
|
||||
|
||||
typedef struct Scope Scope;
|
||||
|
||||
/* Types of symbols */
|
||||
enum SymbolType {
|
||||
SYM_LOCAL, /* lua_variable_symbol */
|
||||
SYM_UPVALUE, /* lua_upvalue_symbol */
|
||||
SYM_GLOBAL, /* lua_variable_symbol, Global symbols are never added to a scope so they are always looked up */
|
||||
SYM_LABEL, /* lua_label_symbol */
|
||||
SYM_ENV /* Special symbol type for _ENV */
|
||||
};
|
||||
typedef struct LuaSymbol LuaSymbol;
|
||||
typedef struct LuaUpvalueSymbol LuaUpvalueSymbol;
|
||||
typedef struct LuaVariableSymbol LuaVariableSymbol;
|
||||
typedef struct LuaLabelSymbol LuaLabelSymbol;
|
||||
|
||||
/* As described before each parsed Lua script or chunk is wrapped in an anonymous 'main'
|
||||
* function hence the AST root is this function.
|
||||
*/
|
||||
RAVICOMP_EXPORT const FunctionExpression *
|
||||
raviX_ast_get_main_function(const CompilerState *compiler_state);
|
||||
|
||||
/* return statement walking */
|
||||
RAVICOMP_EXPORT void raviX_return_statement_foreach_expression(const ReturnStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
|
||||
/* label statement walking */
|
||||
RAVICOMP_EXPORT const StringObject *raviX_label_statement_label_name(const LabelStatement *statement);
|
||||
RAVICOMP_EXPORT const Scope *raviX_label_statement_label_scope(const LabelStatement *statement);
|
||||
|
||||
/* goto statement walking */
|
||||
RAVICOMP_EXPORT const StringObject *raviX_goto_statement_label_name(const GotoStatement *statement);
|
||||
RAVICOMP_EXPORT const Scope *raviX_goto_statement_scope(const GotoStatement *statement);
|
||||
RAVICOMP_EXPORT bool raviX_goto_statement_is_break(const GotoStatement *statement);
|
||||
|
||||
/* local statement walking */
|
||||
RAVICOMP_EXPORT void raviX_local_statement_foreach_expression(const LocalStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
RAVICOMP_EXPORT void raviX_local_statement_foreach_symbol(const LocalStatement *statement, void *userdata,
|
||||
void (*callback)(void *,
|
||||
const LuaVariableSymbol *expr));
|
||||
|
||||
/* expression or assignment statement walking */
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_expression_statement_foreach_lhs_expression(const ExpressionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_expression_statement_foreach_rhs_expression(const ExpressionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
|
||||
/* function statement walking */
|
||||
RAVICOMP_EXPORT const SymbolExpression *
|
||||
raviX_function_statement_name(const FunctionStatement *statement);
|
||||
RAVICOMP_EXPORT bool raviX_function_statement_is_method(const FunctionStatement *statement);
|
||||
RAVICOMP_EXPORT const IndexExpression *
|
||||
raviX_function_statement_method_name(const FunctionStatement *statement);
|
||||
RAVICOMP_EXPORT bool raviX_function_statement_has_selectors(const FunctionStatement *statement);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_statement_foreach_selector(const FunctionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const IndexExpression *expr));
|
||||
RAVICOMP_EXPORT const FunctionExpression *raviX_function_ast(const FunctionStatement *statement);
|
||||
|
||||
/* do statement walking */
|
||||
RAVICOMP_EXPORT const Scope *raviX_do_statement_scope(const DoStatement *statement);
|
||||
RAVICOMP_EXPORT void raviX_do_statement_foreach_statement(const DoStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const Statement *statement));
|
||||
/* if statement walking */
|
||||
/* Lua if statements are a mix of select/case and if/else statements in
|
||||
* other languages. The AST represents the initial if condition block and all subsequent
|
||||
* elseif blocks as test_then_statments. The final else block is treated as an optional
|
||||
* else block.
|
||||
*/
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_if_statement_foreach_test_then_statement(const IfStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const TestThenStatement *stmt));
|
||||
RAVICOMP_EXPORT const Scope *raviX_if_then_statement_else_scope(const IfStatement *statement);
|
||||
RAVICOMP_EXPORT void raviX_if_statement_foreach_else_statement(const IfStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const Statement *statement));
|
||||
RAVICOMP_EXPORT const Scope *raviX_test_then_statement_scope(const TestThenStatement *statement);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_test_then_statement_foreach_statement(const TestThenStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement));
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_test_then_statement_condition(const TestThenStatement *statement);
|
||||
|
||||
/* while or repeat statement walking */
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_while_or_repeat_statement_condition(const WhileOrRepeatStatement *statement);
|
||||
RAVICOMP_EXPORT const Scope *
|
||||
raviX_while_or_repeat_statement_scope(const WhileOrRepeatStatement *statement);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_while_or_repeat_statement_foreach_statement(const WhileOrRepeatStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement));
|
||||
|
||||
/* for statement walking */
|
||||
RAVICOMP_EXPORT const Scope *raviX_for_statement_scope(const ForStatement *statement);
|
||||
RAVICOMP_EXPORT void raviX_for_statement_foreach_symbol(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *,
|
||||
const LuaVariableSymbol *expr));
|
||||
RAVICOMP_EXPORT void raviX_for_statement_foreach_expression(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
RAVICOMP_EXPORT const Scope *raviX_for_statement_body_scope(const ForStatement *statement);
|
||||
RAVICOMP_EXPORT void raviX_for_statement_body_foreach_statement(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const Statement *statement));
|
||||
|
||||
/* literal expression */
|
||||
/* Note: '...' value has type RAVI_TVARARGS and no associated SemInfo. */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_literal_expression_type(const LiteralExpression *expression);
|
||||
RAVICOMP_EXPORT const SemInfo *raviX_literal_expression_literal(const LiteralExpression *expression);
|
||||
|
||||
/* symbol expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_symbol_expression_type(const SymbolExpression *expression);
|
||||
RAVICOMP_EXPORT const LuaSymbol *raviX_symbol_expression_symbol(const SymbolExpression *expression);
|
||||
|
||||
/* index expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_index_expression_type(const IndexExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *raviX_index_expression_expression(const IndexExpression *expression);
|
||||
|
||||
/* unary expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_unary_expression_type(const UnaryExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *raviX_unary_expression_expression(const UnaryExpression *expression);
|
||||
RAVICOMP_EXPORT UnaryOperatorType raviX_unary_expression_operator(const UnaryExpression *expression);
|
||||
|
||||
/* binary expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_binary_expression_type(const BinaryExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_binary_expression_left_expression(const BinaryExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_binary_expression_right_expression(const BinaryExpression *expression);
|
||||
RAVICOMP_EXPORT BinaryOperatorType raviX_binary_expression_operator(const BinaryExpression *expression);
|
||||
|
||||
/* function expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_function_type(const FunctionExpression *function_expression);
|
||||
RAVICOMP_EXPORT bool raviX_function_is_vararg(const FunctionExpression *function_expression);
|
||||
RAVICOMP_EXPORT bool raviX_function_is_method(const FunctionExpression *function_expression);
|
||||
RAVICOMP_EXPORT const FunctionExpression *
|
||||
raviX_function_parent(const FunctionExpression *function_expression);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_foreach_child(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const FunctionExpression *function_expression));
|
||||
RAVICOMP_EXPORT const Scope *raviX_function_scope(const FunctionExpression *function_expression);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_foreach_statement(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement));
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_foreach_argument(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaVariableSymbol *symbol));
|
||||
RAVICOMP_EXPORT void raviX_function_foreach_local(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const LuaVariableSymbol *lua_local_symbol));
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_foreach_upvalue(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaUpvalueSymbol *symbol));
|
||||
|
||||
/* table element assignment expression */
|
||||
RAVICOMP_EXPORT const VariableType *
|
||||
raviX_table_element_assignment_expression_type(const TableElementAssignmentExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_table_element_assignment_expression_key(const TableElementAssignmentExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_table_element_assignment_expression_value(const TableElementAssignmentExpression *expression);
|
||||
|
||||
/* table_literal_expression */
|
||||
RAVICOMP_EXPORT const VariableType *
|
||||
raviX_table_literal_expression_type(const TableLiteralExpression *expression);
|
||||
RAVICOMP_EXPORT void raviX_table_literal_expression_foreach_element(
|
||||
const TableLiteralExpression *expression, void *userdata,
|
||||
void (*callback)(void *, const TableElementAssignmentExpression *expr));
|
||||
|
||||
/* suffixed_expression */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_suffixed_expression_type(const SuffixedExpression *expression);
|
||||
RAVICOMP_EXPORT const Expression *
|
||||
raviX_suffixed_expression_primary(const SuffixedExpression *expression);
|
||||
RAVICOMP_EXPORT void raviX_suffixed_expression_foreach_suffix(const SuffixedExpression *expression,
|
||||
void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
|
||||
/* function call expression */
|
||||
RAVICOMP_EXPORT const VariableType *
|
||||
raviX_function_call_expression_type(const FunctionCallExpression *expression);
|
||||
// can return NULL
|
||||
RAVICOMP_EXPORT const StringObject *
|
||||
raviX_function_call_expression_method_name(const FunctionCallExpression *expression);
|
||||
RAVICOMP_EXPORT void
|
||||
raviX_function_call_expression_foreach_argument(const FunctionCallExpression *expression, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr));
|
||||
|
||||
/* Convert a statement to the correct type */
|
||||
RAVICOMP_EXPORT enum AstNodeType raviX_statement_type(const Statement *statement);
|
||||
RAVICOMP_EXPORT const ReturnStatement *raviX_return_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const LabelStatement *raviX_label_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const GotoStatement *raviX_goto_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const LocalStatement *raviX_local_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const ExpressionStatement *raviX_expression_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const FunctionStatement *raviX_function_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const DoStatement *raviX_do_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const TestThenStatement *raviX_test_then_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const IfStatement *raviX_if_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const WhileOrRepeatStatement *raviX_while_or_repeat_statement(const Statement *stmt);
|
||||
RAVICOMP_EXPORT const ForStatement *raviX_for_statement(const Statement *stmt);
|
||||
|
||||
/* Convert an expression to the correct type */
|
||||
RAVICOMP_EXPORT enum AstNodeType raviX_expression_type(const Expression *expression);
|
||||
RAVICOMP_EXPORT const LiteralExpression *raviX_literal_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const SymbolExpression *raviX_symbol_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const IndexExpression *raviX_index_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const UnaryExpression *raviX_unary_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const BinaryExpression *raviX_binary_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const FunctionExpression *raviX_function_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const TableElementAssignmentExpression *
|
||||
raviX_table_element_assignment_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const TableLiteralExpression *raviX_table_literal_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const SuffixedExpression *raviX_suffixed_expression(const Expression *expr);
|
||||
RAVICOMP_EXPORT const FunctionCallExpression *raviX_function_call_expression(const Expression *expr);
|
||||
|
||||
RAVICOMP_EXPORT const FunctionExpression *raviX_scope_owning_function(const Scope *scope);
|
||||
RAVICOMP_EXPORT const Scope *raviX_scope_parent_scope(const Scope *scope);
|
||||
RAVICOMP_EXPORT void raviX_scope_foreach_symbol(const Scope *scope, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaSymbol *symbol));
|
||||
|
||||
RAVICOMP_EXPORT enum SymbolType raviX_symbol_type(const LuaSymbol *symbol);
|
||||
/* symbol downcast */
|
||||
RAVICOMP_EXPORT const LuaVariableSymbol *raviX_symbol_variable(const LuaSymbol *symbol);
|
||||
RAVICOMP_EXPORT const LuaUpvalueSymbol *raviX_symbol_upvalue(const LuaSymbol *symbol);
|
||||
RAVICOMP_EXPORT const LuaLabelSymbol *raviX_symbol_label(const LuaSymbol *symbol);
|
||||
|
||||
/* variable symbol - local and global variables */
|
||||
RAVICOMP_EXPORT const StringObject *
|
||||
raviX_variable_symbol_name(const LuaVariableSymbol *lua_local_symbol);
|
||||
RAVICOMP_EXPORT const VariableType *raviX_variable_symbol_type(const LuaVariableSymbol *lua_local_symbol);
|
||||
// NULL if global
|
||||
RAVICOMP_EXPORT const Scope *
|
||||
raviX_variable_symbol_scope(const LuaVariableSymbol *lua_local_symbol);
|
||||
|
||||
/* label symbol */
|
||||
RAVICOMP_EXPORT const StringObject *raviX_label_name(const LuaLabelSymbol *symbol);
|
||||
RAVICOMP_EXPORT const Scope *raviX_label_scope(const LuaLabelSymbol *symbol);
|
||||
|
||||
/* upvalue symbol */
|
||||
RAVICOMP_EXPORT const VariableType *raviX_upvalue_symbol_type(const LuaUpvalueSymbol *symbol);
|
||||
RAVICOMP_EXPORT const LuaVariableSymbol *
|
||||
raviX_upvalue_target_variable(const LuaUpvalueSymbol *symbol);
|
||||
RAVICOMP_EXPORT const FunctionExpression *
|
||||
raviX_upvalue_target_function(const LuaUpvalueSymbol *symbol);
|
||||
RAVICOMP_EXPORT unsigned raviX_upvalue_index(const LuaUpvalueSymbol *symbol);
|
||||
|
||||
/* Utilities */
|
||||
#ifdef __GNUC__
|
||||
#define FORMAT_ATTR(pos) __attribute__((__format__(__printf__, pos, pos + 1)))
|
||||
#else
|
||||
#define FORMAT_ATTR(pos)
|
||||
#endif
|
||||
|
||||
RAVICOMP_EXPORT void raviX_buffer_init(TextBuffer *mb, size_t initial_size);
|
||||
RAVICOMP_EXPORT void raviX_buffer_resize(TextBuffer *mb, size_t new_size);
|
||||
RAVICOMP_EXPORT void raviX_buffer_reserve(TextBuffer *mb, size_t n);
|
||||
RAVICOMP_EXPORT void raviX_buffer_free(TextBuffer *mb);
|
||||
static inline char *raviX_buffer_data(const TextBuffer *mb) { return mb->buf; }
|
||||
static inline size_t raviX_buffer_size(const TextBuffer *mb) { return mb->capacity; }
|
||||
static inline size_t raviX_buffer_len(const TextBuffer *mb) { return mb->pos; }
|
||||
static inline void raviX_buffer_reset(TextBuffer *mb) { mb->pos = 0; }
|
||||
|
||||
/* following convert input to string before adding */
|
||||
RAVICOMP_EXPORT void raviX_buffer_add_string(TextBuffer *mb, const char *str);
|
||||
RAVICOMP_EXPORT void raviX_buffer_add_bytes(TextBuffer *mb, const char *str, size_t len);
|
||||
RAVICOMP_EXPORT void raviX_buffer_add_fstring(TextBuffer *mb, const char *str, ...) FORMAT_ATTR(2);
|
||||
|
||||
/* strncpy() replacement with guaranteed 0 termination */
|
||||
RAVICOMP_EXPORT void raviX_string_copy(char *buf, const char *src, size_t buflen);
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
# Sources
|
||||
|
||||
* `lexer.c` - derived from Lua 5.3 lexer but modified to work as a standalone lexer
|
||||
* `parser.c` - responsible for generating abstract syntax tree (AST) - consumes lexer output.
|
||||
* `ast_printer.c` - responsible for printing out the AST
|
||||
* `ast_walker.c` - API for walking the AST
|
||||
* `ast_simplify.c` - responsible for simplifications done on AST such as constant folding
|
||||
* `typechecker.c` - responsible for performing typechecking and assigning types to various things. Runs on the AST.
|
||||
* `linearizer.c` (WIP) - responsible for generating linear intermediate code (linear IR).
|
||||
* `cfg.c` - responsible for constructing a control flow graph from the output of the linearizer.
|
||||
* `dominator.c` - implementation of dominator tree calculation - this is not used yet
|
||||
* `dataflow_framework.c` - a framework for calculating dataflow equations - not used yet
|
||||
* `opt_unusedcode.c` - a simple optimization pass that deletes unreachable basic blocks
|
||||
* `codegen.c` - responsible for generating C code from the linear IR
|
||||
|
||||
## Utilities
|
||||
|
||||
* `allocate.c` - memory allocator
|
||||
* `fnv_hash.c` - string hashing function
|
||||
* `hash_table.c` - hash table
|
||||
* `set.c` - set data structure
|
||||
* `ptrlist.c` - a hybrid array/linked list data structure
|
||||
* `membuf.c` - dynamic memory buffer that supports formatted input - used to build strings incrementally
|
||||
* `graph.c` - simple graph data structure used to generate control flow graph.
|
||||
* `bitset.c` - bitset data structure
|
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* allocate.c - simple space-efficient blob allocator.
|
||||
*
|
||||
* Copyright (C) 2003 Transmeta Corp.
|
||||
* 2003-2004 Linus Torvalds
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Simple allocator for data that doesn't get partially free'd.
|
||||
* The tokenizer and parser allocate a _lot_ of small data structures
|
||||
* (often just two-three bytes for things like small integers),
|
||||
* and since they all depend on each other you can't free them
|
||||
* individually _anyway_. So do something that is very space-
|
||||
* efficient: allocate larger "blobs", and give out individual
|
||||
* small bits and pieces of it with no maintenance overhead.
|
||||
*/
|
||||
/*
|
||||
* This version is part of the Ravi Compiler project.
|
||||
* Copyright (C) 2017-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include <allocate.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void *blob_alloc(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = malloc(size);
|
||||
if (ptr != NULL)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void blob_free(void *addr, size_t size)
|
||||
{
|
||||
(void)size;
|
||||
free(addr);
|
||||
}
|
||||
|
||||
void raviX_allocator_init(Allocator *A, const char *name, size_t size, unsigned int alignment,
|
||||
unsigned int chunking)
|
||||
{
|
||||
A->name_ = name;
|
||||
A->blobs_ = NULL;
|
||||
A->size_ = size;
|
||||
A->alignment_ = alignment;
|
||||
A->chunking_ = chunking;
|
||||
A->freelist_ = NULL;
|
||||
A->allocations = 0;
|
||||
A->total_bytes = 0;
|
||||
A->useful_bytes = 0;
|
||||
}
|
||||
|
||||
void *raviX_allocator_allocate(Allocator *A, size_t extra)
|
||||
{
|
||||
size_t size = extra + A->size_;
|
||||
size_t alignment = A->alignment_;
|
||||
AllocationBlob *blob = A->blobs_;
|
||||
void *retval;
|
||||
|
||||
if (size > A->chunking_) {
|
||||
fprintf(stderr, "allocation failure: requested size %lld is larger than maximum chunk size %lld\n",
|
||||
(long long)size, (long long) A->chunking_);
|
||||
exit(1);
|
||||
}
|
||||
/*
|
||||
* NOTE! The freelist only works with things that are
|
||||
* (a) sufficiently aligned
|
||||
* (b) use a constant size
|
||||
* Don't try to free allocators that don't follow
|
||||
* these rules.
|
||||
*/
|
||||
if (A->freelist_) {
|
||||
void **p = (void **)A->freelist_;
|
||||
retval = p;
|
||||
A->freelist_ = *p;
|
||||
memset(retval, 0, size);
|
||||
return retval;
|
||||
}
|
||||
|
||||
A->allocations++;
|
||||
A->useful_bytes += size;
|
||||
size = (size + alignment - 1) & ~(alignment - 1);
|
||||
if (!blob || blob->left < size) {
|
||||
size_t offset, chunking = A->chunking_;
|
||||
AllocationBlob *newblob = (AllocationBlob *)blob_alloc(chunking);
|
||||
if (!newblob) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
A->total_bytes += chunking;
|
||||
newblob->next = blob;
|
||||
blob = newblob;
|
||||
A->blobs_ = newblob;
|
||||
offset = offsetof(AllocationBlob, data);
|
||||
offset = (offset + alignment - 1) & ~(alignment - 1);
|
||||
blob->left = chunking - offset;
|
||||
blob->offset = offset - offsetof(AllocationBlob, data);
|
||||
}
|
||||
retval = blob->data + blob->offset;
|
||||
blob->offset += size;
|
||||
blob->left -= size;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void raviX_allocator_free(Allocator *A, void *entry)
|
||||
{
|
||||
void **p = (void **)entry;
|
||||
*p = A->freelist_;
|
||||
A->freelist_ = p;
|
||||
}
|
||||
void raviX_allocator_show_allocations(Allocator *A)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s: %d allocations, %d bytes (%d total bytes, "
|
||||
"%6.2f%% usage, %6.2f average size)\n",
|
||||
A->name_, (int)A->allocations, (int)A->useful_bytes, (int)A->total_bytes,
|
||||
100 * (double)A->useful_bytes / A->total_bytes, (double)A->useful_bytes / A->allocations);
|
||||
}
|
||||
void raviX_allocator_drop_all_allocations(Allocator *A)
|
||||
{
|
||||
AllocationBlob *blob = A->blobs_;
|
||||
A->blobs_ = NULL;
|
||||
A->allocations = 0;
|
||||
A->total_bytes = 0;
|
||||
A->useful_bytes = 0;
|
||||
A->freelist_ = NULL;
|
||||
while (blob) {
|
||||
AllocationBlob *next = blob->next;
|
||||
blob_free(blob, A->chunking_);
|
||||
blob = next;
|
||||
}
|
||||
}
|
||||
void raviX_allocator_destroy(Allocator *A)
|
||||
{
|
||||
raviX_allocator_drop_all_allocations(A);
|
||||
A->blobs_ = NULL;
|
||||
A->allocations = 0;
|
||||
A->total_bytes = 0;
|
||||
A->useful_bytes = 0;
|
||||
A->freelist_ = NULL;
|
||||
}
|
||||
void raviX_allocator_transfer(Allocator *A, Allocator *transfer_to)
|
||||
{
|
||||
assert(transfer_to->blobs_ == NULL);
|
||||
assert(transfer_to->freelist_ == NULL);
|
||||
transfer_to->blobs_ = A->blobs_;
|
||||
transfer_to->allocations = A->allocations;
|
||||
transfer_to->total_bytes = A->total_bytes;
|
||||
transfer_to->useful_bytes = A->useful_bytes;
|
||||
transfer_to->freelist_ = A->freelist_;
|
||||
transfer_to->alignment_ = A->alignment_;
|
||||
transfer_to->chunking_ = A->chunking_;
|
||||
transfer_to->size_ = A->size_;
|
||||
A->blobs_ = NULL;
|
||||
A->allocations = 0;
|
||||
A->total_bytes = 0;
|
||||
A->useful_bytes = 0;
|
||||
A->freelist_ = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Reallocate array from old_n to new_n. If new_n is 0 then array memeory is freed.
|
||||
If new_n is greater than old_n then old data is copied across and the
|
||||
additional allocated space is zeroed out so caller can rely on the extra space being
|
||||
initialized to zeros.
|
||||
*/
|
||||
void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n)
|
||||
{
|
||||
if (new_n == 0) {
|
||||
free(oldp);
|
||||
return NULL;
|
||||
}
|
||||
assert (new_n > old_n);
|
||||
size_t newsize = element_size * new_n;
|
||||
void *newp = realloc(oldp, newsize);
|
||||
if (!newp) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
abort();
|
||||
}
|
||||
size_t oldsize = old_n * element_size;
|
||||
char *p = newp;
|
||||
memset(p + oldsize, 0, newsize - oldsize);
|
||||
return newp;
|
||||
}
|
||||
|
||||
/*
|
||||
Delete n elements starting at i from array a of size array_size, where sizeof(each element) is element_size.
|
||||
The freed up space will be zero initialized.
|
||||
*/
|
||||
size_t raviX_del_array_element(void *a, size_t element_size, size_t array_size, size_t i, size_t n)
|
||||
{
|
||||
assert(i + n <= array_size);
|
||||
char *p = (char *)a;
|
||||
char *dest = p + i * element_size;
|
||||
char *src = p + (i + n) * element_size;
|
||||
size_t count = element_size * (array_size - n - i);
|
||||
memmove(dest, src, count);
|
||||
size_t new_array_size = array_size - n;
|
||||
size_t newsize = element_size * new_array_size;
|
||||
size_t oldsize = element_size * array_size;
|
||||
memset(p + newsize, 0, oldsize - newsize);
|
||||
return new_array_size;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
struct foo {
|
||||
int a, b;
|
||||
};
|
||||
|
||||
int raviX_test_allocator() {
|
||||
Allocator alloc;
|
||||
raviX_allocator_init(&alloc, "foo", sizeof(struct foo), __alignof__(struct foo),
|
||||
sizeof(AllocationBlob) + sizeof(struct foo) * 2);
|
||||
struct foo *t1 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
|
||||
if (t1 == NULL)
|
||||
return 1;
|
||||
if (alloc.alignment_ != __alignof__(struct foo))
|
||||
return 1;
|
||||
if (alloc.allocations != 1)
|
||||
return 1;
|
||||
if (alloc.freelist_ != NULL)
|
||||
return 1;
|
||||
struct foo *t2 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
|
||||
if (t2 != t1 + 1)
|
||||
return 1;
|
||||
//dmrC_allocator_show_allocations(&alloc);
|
||||
raviX_allocator_free(&alloc, t1);
|
||||
raviX_allocator_free(&alloc, t2);
|
||||
struct foo *t3 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
|
||||
if (t3 != t2)
|
||||
return 1;
|
||||
struct foo *t4 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
|
||||
if (t4 != t1)
|
||||
return 1;
|
||||
struct foo *t5 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
|
||||
(void)t5;
|
||||
if (alloc.total_bytes !=
|
||||
(sizeof(AllocationBlob) + sizeof(struct foo) * 2) * 2)
|
||||
return 1;
|
||||
Allocator alloc2;
|
||||
memset(&alloc2, 0, sizeof alloc2);
|
||||
AllocationBlob *saved = alloc.blobs_;
|
||||
raviX_allocator_transfer(&alloc, &alloc2);
|
||||
if (alloc.blobs_ != NULL)
|
||||
return 1;
|
||||
if (alloc2.blobs_ != saved)
|
||||
return 1;
|
||||
raviX_allocator_destroy(&alloc2);
|
||||
printf("allocator tests okay\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,130 @@
|
||||
#ifndef ravicomp_ALLOCATOR_H
|
||||
#define ravicomp_ALLOCATOR_H
|
||||
|
||||
/*
|
||||
* allocate.c - simple space-efficient blob allocator.
|
||||
*
|
||||
* Copyright (C) 2003 Transmeta Corp.
|
||||
* 2003-2004 Linus Torvalds
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Simple allocator for data that doesn't get partially free'd.
|
||||
* The tokenizer and parser allocate a _lot_ of small data structures
|
||||
* (often just two-three bytes for things like small integers),
|
||||
* and since they all depend on each other you can't free them
|
||||
* individually _anyway_. So do something that is very space-
|
||||
* efficient: allocate larger "blobs", and give out individual
|
||||
* small bits and pieces of it with no maintenance overhead.
|
||||
*/
|
||||
/*
|
||||
* Portions Copyright (C) 2017-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct AllocationBlob AllocationBlob;
|
||||
struct AllocationBlob {
|
||||
AllocationBlob *next;
|
||||
size_t left, offset;
|
||||
unsigned char data[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Our "blob" allocator works on chunks that are multiples
|
||||
* of this size (the underlying allocator may be a mmap that
|
||||
* cannot handle smaller chunks, for example, so trying to
|
||||
* allocate blobs that aren't aligned is not going to work).
|
||||
*/
|
||||
#define CHUNK 32768
|
||||
|
||||
typedef struct Allocator {
|
||||
const char *name_;
|
||||
AllocationBlob *blobs_;
|
||||
size_t size_;
|
||||
unsigned int alignment_;
|
||||
unsigned int chunking_;
|
||||
void *freelist_;
|
||||
size_t allocations, total_bytes, useful_bytes;
|
||||
} Allocator;
|
||||
|
||||
extern void raviX_allocator_init(Allocator *A, const char *name, size_t size, unsigned int alignment,
|
||||
unsigned int chunking);
|
||||
|
||||
extern void *raviX_allocator_allocate(Allocator *A, size_t extra);
|
||||
|
||||
extern void raviX_allocator_free(Allocator *A, void *entry);
|
||||
|
||||
extern void raviX_allocator_show_allocations(Allocator *A);
|
||||
|
||||
extern void raviX_allocator_drop_all_allocations(Allocator *A);
|
||||
|
||||
extern void raviX_allocator_destroy(Allocator *A);
|
||||
|
||||
extern void raviX_allocator_transfer(Allocator *A, Allocator *transfer_to);
|
||||
|
||||
/*
|
||||
Reallocate array from old_n to new_n. If new_n is 0 then array memory is freed.
|
||||
If new_n is greater than old_n then old data is copied across and the
|
||||
additional allocated space is zeroed out so caller can rely on the extra space being
|
||||
initialized to zeros.
|
||||
*/
|
||||
extern void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n);
|
||||
/*
|
||||
Delete n elements starting at i from array a of size array_size, where sizeof(each element) is element_size.
|
||||
The freed up space will be zero initialized. Returns the new array_size.
|
||||
*/
|
||||
extern size_t raviX_del_array_element(void *p, size_t element_size, size_t array_size, size_t i, size_t n);
|
||||
|
||||
/* structure of a node */
|
||||
#define DECLARE_ARRAY(array_type, TYPE) \
|
||||
typedef struct array_type { \
|
||||
unsigned allocated; \
|
||||
unsigned count; \
|
||||
TYPE *data; \
|
||||
} array_type
|
||||
#define array_push(A, value) \
|
||||
{ \
|
||||
if ((A)->count == (A)->allocated) { \
|
||||
unsigned newsize = (A)->allocated += 10; \
|
||||
(A)->data = raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->allocated, newsize); \
|
||||
(A)->allocated = newsize; \
|
||||
} \
|
||||
(A)->data[(A)->count++] = value; \
|
||||
}
|
||||
#define array_clearmem(A) \
|
||||
{ \
|
||||
raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->allocated, 0); \
|
||||
(A)->data = NULL; \
|
||||
(A)->allocated = 0; \
|
||||
(A)->count = 0; \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,543 @@
|
||||
/*
|
||||
Copyright (C) 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include <parser.h>
|
||||
|
||||
const char *raviX_get_type_name(ravitype_t tt)
|
||||
{
|
||||
switch (tt) {
|
||||
case RAVI_TANY:
|
||||
return "any";
|
||||
case RAVI_TNIL:
|
||||
return "nil";
|
||||
case RAVI_TBOOLEAN:
|
||||
return "boolean";
|
||||
case RAVI_TNUMFLT:
|
||||
return "number";
|
||||
case RAVI_TNUMINT:
|
||||
return "integer";
|
||||
case RAVI_TTABLE:
|
||||
return "table";
|
||||
case RAVI_TSTRING:
|
||||
return "string";
|
||||
case RAVI_TARRAYINT:
|
||||
return "integer[]";
|
||||
case RAVI_TARRAYFLT:
|
||||
return "number[]";
|
||||
case RAVI_TFUNCTION:
|
||||
return "closure";
|
||||
case RAVI_TUSERDATA:
|
||||
return "userdata";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static void printf_buf(TextBuffer *buf, const char *format, ...)
|
||||
{
|
||||
static const char *PADDING = " ";
|
||||
char tbuf[128] = {0};
|
||||
va_list ap;
|
||||
const char *cp;
|
||||
va_start(ap, format);
|
||||
for (cp = format; *cp; cp++) {
|
||||
if (cp[0] == '%' && cp[1] == 'p') { /* padding */
|
||||
int level = va_arg(ap, int);
|
||||
snprintf(tbuf, sizeof tbuf, "%.*s", level, PADDING);
|
||||
raviX_buffer_add_string(buf, tbuf);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 't') { /* string_object */
|
||||
const StringObject *s = va_arg(ap, const StringObject *);
|
||||
raviX_buffer_add_string(buf, s->str);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 'T') { /* VariableType */
|
||||
const VariableType *type;
|
||||
type = va_arg(ap, const VariableType *);
|
||||
if (type->type_code == RAVI_TUSERDATA) {
|
||||
const StringObject *s = type->type_name;
|
||||
raviX_buffer_add_string(buf, s->str);
|
||||
} else {
|
||||
raviX_buffer_add_string(buf, raviX_get_type_name(type->type_code));
|
||||
}
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 's') { /* const char * */
|
||||
const char *s;
|
||||
s = va_arg(ap, const char *);
|
||||
raviX_buffer_add_string(buf, s);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 'c') { /* comment */
|
||||
const char *s;
|
||||
s = va_arg(ap, const char *);
|
||||
raviX_buffer_add_fstring(buf, "--%s", s);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 'i') { /* integer */
|
||||
lua_Integer i;
|
||||
i = va_arg(ap, lua_Integer);
|
||||
raviX_buffer_add_longlong(buf, i);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 'f') { /* float */
|
||||
double d;
|
||||
d = va_arg(ap, double);
|
||||
raviX_buffer_add_fstring(buf, "%.16f", d);
|
||||
cp++;
|
||||
} else if (cp[0] == '%' && cp[1] == 'b') { /* boolean */
|
||||
lua_Integer i;
|
||||
i = va_arg(ap, lua_Integer);
|
||||
raviX_buffer_add_bool(buf, i != 0);
|
||||
cp++;
|
||||
} else {
|
||||
raviX_buffer_add_char(buf, *cp);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void print_ast_node_list(TextBuffer *buf, AstNodeList *list, int level, const char *delimiter)
|
||||
{
|
||||
AstNode *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node)
|
||||
{
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else if (delimiter)
|
||||
printf_buf(buf, "%p%s\n", level, delimiter);
|
||||
raviX_print_ast_node(buf, node, level + 1);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void print_statement_list(TextBuffer *buf, AstNodeList *statement_list, int level)
|
||||
{
|
||||
print_ast_node_list(buf, statement_list, level + 1, NULL);
|
||||
}
|
||||
|
||||
static inline const char *get_as_str(const StringObject *ts) { return ts ? ts->str : ""; }
|
||||
|
||||
static void print_symbol(TextBuffer *buf, LuaSymbol *sym, int level)
|
||||
{
|
||||
switch (sym->symbol_type) {
|
||||
case SYM_ENV: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "_ENV",
|
||||
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
|
||||
break;
|
||||
}
|
||||
case SYM_GLOBAL: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "global symbol",
|
||||
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
|
||||
break;
|
||||
}
|
||||
case SYM_LOCAL: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "local symbol",
|
||||
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
|
||||
break;
|
||||
}
|
||||
case SYM_UPVALUE: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->upvalue.target_variable->variable.var_name, "upvalue",
|
||||
raviX_get_type_name(sym->upvalue.target_variable->variable.value_type.type_code),
|
||||
get_as_str(sym->upvalue.target_variable->variable.value_type.type_name));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_symbol_name(TextBuffer *buf, LuaSymbol *sym)
|
||||
{
|
||||
switch (sym->symbol_type) {
|
||||
case SYM_LOCAL:
|
||||
case SYM_ENV:
|
||||
case SYM_GLOBAL: {
|
||||
printf_buf(buf, "%t", sym->variable.var_name);
|
||||
break;
|
||||
}
|
||||
case SYM_UPVALUE: {
|
||||
if (sym->upvalue.target_variable->symbol_type == SYM_ENV) {
|
||||
printf_buf(buf, "%t*", sym->upvalue.target_variable->variable.var_name);
|
||||
}
|
||||
else {
|
||||
printf_buf(buf, "%t", sym->upvalue.target_variable->variable.var_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_symbol_list(TextBuffer *buf, LuaSymbolList *list, int level, const char *delimiter)
|
||||
{
|
||||
LuaSymbol *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node)
|
||||
{
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else if (delimiter)
|
||||
printf_buf(buf, "%p%s\n", level, delimiter);
|
||||
print_symbol(buf, node, level + 1);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void print_symbol_names(TextBuffer *buf, LuaSymbolList *list)
|
||||
{
|
||||
LuaSymbol *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node)
|
||||
{
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else
|
||||
printf_buf(buf, ", ");
|
||||
print_symbol_name(buf, node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
const char *raviX_get_unary_opr_str(UnaryOperatorType op)
|
||||
{
|
||||
switch (op) {
|
||||
case UNOPR_NOT:
|
||||
return "not";
|
||||
case UNOPR_MINUS:
|
||||
return "-";
|
||||
case UNOPR_BNOT:
|
||||
return "~";
|
||||
case UNOPR_LEN:
|
||||
return "#";
|
||||
case UNOPR_TO_INTEGER:
|
||||
return "@integer";
|
||||
case UNOPR_TO_NUMBER:
|
||||
return "@number";
|
||||
case UNOPR_TO_INTARRAY:
|
||||
return "@integer[]";
|
||||
case UNOPR_TO_NUMARRAY:
|
||||
return "@number[]";
|
||||
case UNOPR_TO_TABLE:
|
||||
return "@table";
|
||||
case UNOPR_TO_CLOSURE:
|
||||
return "@closure";
|
||||
case UNOPR_TO_STRING:
|
||||
return "@string";
|
||||
case UNOPR_TO_TYPE:
|
||||
return "@<usertype>";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char *raviX_get_binary_opr_str(BinaryOperatorType op)
|
||||
{
|
||||
switch (op) {
|
||||
case BINOPR_ADD:
|
||||
return "+";
|
||||
case BINOPR_SUB:
|
||||
return "-";
|
||||
case BINOPR_MUL:
|
||||
return "*";
|
||||
case BINOPR_MOD:
|
||||
return "%";
|
||||
case BINOPR_POW:
|
||||
return "^";
|
||||
case BINOPR_DIV:
|
||||
return "/";
|
||||
case BINOPR_IDIV:
|
||||
return "//";
|
||||
case BINOPR_BAND:
|
||||
return "&";
|
||||
case BINOPR_BOR:
|
||||
return "|";
|
||||
case BINOPR_BXOR:
|
||||
return "~";
|
||||
case BINOPR_SHL:
|
||||
return "<<";
|
||||
case BINOPR_SHR:
|
||||
return ">>";
|
||||
case BINOPR_CONCAT:
|
||||
return "..";
|
||||
case BINOPR_NE:
|
||||
return "~=";
|
||||
case BINOPR_EQ:
|
||||
return "==";
|
||||
case BINOPR_LT:
|
||||
return "<";
|
||||
case BINOPR_LE:
|
||||
return "<=";
|
||||
case BINOPR_GT:
|
||||
return ">";
|
||||
case BINOPR_GE:
|
||||
return ">=";
|
||||
case BINOPR_AND:
|
||||
return "and";
|
||||
case BINOPR_OR:
|
||||
return "or";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_print_ast_node(TextBuffer *buf, AstNode *node, int level)
|
||||
{
|
||||
switch (node->type) {
|
||||
case EXPR_FUNCTION: {
|
||||
if (node->function_expr.args) {
|
||||
printf_buf(buf, "%pfunction(\n", level);
|
||||
print_symbol_list(buf, node->function_expr.args, level + 1, ",");
|
||||
printf_buf(buf, "%p)\n", level);
|
||||
} else {
|
||||
printf_buf(buf, "%pfunction()\n", level);
|
||||
}
|
||||
if (node->function_expr.locals) {
|
||||
printf_buf(buf, "%p%c ", level, "locals ");
|
||||
print_symbol_names(buf, node->function_expr.locals);
|
||||
printf_buf(buf, "\n");
|
||||
}
|
||||
if (node->function_expr.upvalues) {
|
||||
printf_buf(buf, "%p%c ", level, "upvalues ");
|
||||
print_symbol_names(buf, node->function_expr.upvalues);
|
||||
printf_buf(buf, "\n");
|
||||
}
|
||||
print_statement_list(buf, node->function_expr.function_statement_list, level);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_NONE:
|
||||
break;
|
||||
case STMT_RETURN: {
|
||||
printf_buf(buf, "%preturn\n", level);
|
||||
print_ast_node_list(buf, node->return_stmt.expr_list, level + 1, ",");
|
||||
break;
|
||||
}
|
||||
case STMT_LOCAL: {
|
||||
printf_buf(buf, "%plocal\n", level);
|
||||
printf_buf(buf, "%p%c\n", level, "[symbols]");
|
||||
print_symbol_list(buf, node->local_stmt.var_list, level + 1, ",");
|
||||
if (node->local_stmt.expr_list) {
|
||||
printf_buf(buf, "%p%c\n", level, "[expressions]");
|
||||
print_ast_node_list(buf, node->local_stmt.expr_list, level + 1, ",");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STMT_FUNCTION: {
|
||||
raviX_print_ast_node(buf, node->function_stmt.name, level);
|
||||
if (node->function_stmt.selectors) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[selectors]");
|
||||
print_ast_node_list(buf, node->function_stmt.selectors, level + 2, NULL);
|
||||
}
|
||||
if (node->function_stmt.method_name) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[method name]");
|
||||
raviX_print_ast_node(buf, node->function_stmt.method_name, level + 2);
|
||||
}
|
||||
printf_buf(buf, "%p=\n", level + 1);
|
||||
raviX_print_ast_node(buf, node->function_stmt.function_expr, level + 2);
|
||||
break;
|
||||
}
|
||||
case STMT_LABEL: {
|
||||
printf_buf(buf, "%p::%t::\n", level, node->label_stmt.symbol->label.label_name);
|
||||
break;
|
||||
}
|
||||
case STMT_GOTO: {
|
||||
printf_buf(buf, "%pgoto %t\n", level, node->goto_stmt.name);
|
||||
break;
|
||||
}
|
||||
case STMT_DO: {
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_ast_node_list(buf, node->do_stmt.do_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case STMT_EXPR: {
|
||||
printf_buf(buf, "%p%c\n", level, "[expression statement start]");
|
||||
if (node->expression_stmt.var_expr_list) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[var list start]");
|
||||
print_ast_node_list(buf, node->expression_stmt.var_expr_list, level + 2, ",");
|
||||
printf_buf(buf, "%p= %c\n", level + 1, "[var list end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[expression list start]");
|
||||
print_ast_node_list(buf, node->expression_stmt.expr_list, level + 2, ",");
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[expression list end]");
|
||||
printf_buf(buf, "%p%c\n", level, "[expression statement end]");
|
||||
break;
|
||||
}
|
||||
case STMT_IF: {
|
||||
AstNode *test_then_block;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block)
|
||||
{
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
printf_buf(buf, "%pif\n", level);
|
||||
} else
|
||||
printf_buf(buf, "%pelseif\n", level);
|
||||
raviX_print_ast_node(buf, test_then_block->test_then_block.condition, level + 1);
|
||||
printf_buf(buf, "%pthen\n", level);
|
||||
print_ast_node_list(buf, test_then_block->test_then_block.test_then_statement_list, level + 1,
|
||||
NULL);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
if (node->if_stmt.else_block) {
|
||||
printf_buf(buf, "%pelse\n", level);
|
||||
print_ast_node_list(buf, node->if_stmt.else_statement_list, level + 1, NULL);
|
||||
}
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case STMT_WHILE: {
|
||||
printf_buf(buf, "%pwhile\n", level);
|
||||
raviX_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case STMT_REPEAT: {
|
||||
printf_buf(buf, "%prepeat\n", level);
|
||||
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%puntil\n", level);
|
||||
raviX_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[repeat end]");
|
||||
break;
|
||||
}
|
||||
case STMT_FOR_IN: {
|
||||
printf_buf(buf, "%pfor\n", level);
|
||||
print_symbol_list(buf, node->for_stmt.symbols, level + 1, ",");
|
||||
printf_buf(buf, "%pin\n", level);
|
||||
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case STMT_FOR_NUM: {
|
||||
printf_buf(buf, "%pfor\n", level);
|
||||
print_symbol_list(buf, node->for_stmt.symbols, level + 1, NULL);
|
||||
printf_buf(buf, "%p=\n", level);
|
||||
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case EXPR_SUFFIXED: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[suffixed expr start]", &node->suffixed_expr.type);
|
||||
printf_buf(buf, "%p%c %T\n", level + 1, "[primary start]",
|
||||
&node->suffixed_expr.primary_expr->common_expr.type);
|
||||
raviX_print_ast_node(buf, node->suffixed_expr.primary_expr, level + 2);
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[primary end]");
|
||||
if (node->suffixed_expr.suffix_list) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[suffix list start]");
|
||||
print_ast_node_list(buf, node->suffixed_expr.suffix_list, level + 2, NULL);
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[suffix list end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level, "[suffixed expr end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_FUNCTION_CALL: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[function call start]", &node->function_call_expr.type);
|
||||
if (node->function_call_expr.method_name) {
|
||||
printf_buf(buf, "%p: %t (\n", level + 1, node->function_call_expr.method_name);
|
||||
} else {
|
||||
printf_buf(buf, "%p(\n", level + 1);
|
||||
}
|
||||
print_ast_node_list(buf, node->function_call_expr.arg_list, level + 2, ",");
|
||||
printf_buf(buf, "%p)\n", level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[function call end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_SYMBOL: {
|
||||
print_symbol(buf, node->symbol_expr.var, level + 1);
|
||||
break;
|
||||
}
|
||||
case EXPR_BINARY: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[binary expr start]", &node->binary_expr.type);
|
||||
raviX_print_ast_node(buf, node->binary_expr.expr_left, level + 1);
|
||||
printf_buf(buf, "%p%s\n", level, raviX_get_binary_opr_str(node->binary_expr.binary_op));
|
||||
raviX_print_ast_node(buf, node->binary_expr.expr_right, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[binary expr end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_UNARY: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[unary expr start]", &node->unary_expr.type);
|
||||
printf_buf(buf, "%p%s\n", level, raviX_get_unary_opr_str(node->unary_expr.unary_op));
|
||||
raviX_print_ast_node(buf, node->unary_expr.expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[unary expr end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_LITERAL: {
|
||||
printf_buf(buf, "%p", level);
|
||||
switch (node->literal_expr.type.type_code) {
|
||||
case RAVI_TNIL:
|
||||
printf_buf(buf, "nil");
|
||||
break;
|
||||
case RAVI_TBOOLEAN:
|
||||
printf_buf(buf, "%b", node->literal_expr.u.i);
|
||||
break;
|
||||
case RAVI_TNUMINT:
|
||||
printf_buf(buf, "%i", node->literal_expr.u.i);
|
||||
break;
|
||||
case RAVI_TNUMFLT:
|
||||
printf_buf(buf, "%f", node->literal_expr.u.r);
|
||||
break;
|
||||
case RAVI_TSTRING:
|
||||
printf_buf(buf, "'%t'", node->literal_expr.u.ts);
|
||||
break;
|
||||
case RAVI_TVARARGS:
|
||||
printf_buf(buf, "...");
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
printf_buf(buf, "\n");
|
||||
break;
|
||||
}
|
||||
case EXPR_FIELD_SELECTOR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[field selector start]", &node->index_expr.type);
|
||||
printf_buf(buf, "%p.\n", level + 1);
|
||||
raviX_print_ast_node(buf, node->index_expr.expr, level + 2);
|
||||
printf_buf(buf, "%p%c\n", level, "[field selector end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_Y_INDEX: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[Y index start]", &node->index_expr.type);
|
||||
printf_buf(buf, "%p[\n", level + 1);
|
||||
raviX_print_ast_node(buf, node->index_expr.expr, level + 2);
|
||||
printf_buf(buf, "%p]\n", level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[Y index end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_TABLE_ELEMENT_ASSIGN: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[indexed assign start]", &node->table_elem_assign_expr.type);
|
||||
if (node->table_elem_assign_expr.key_expr) {
|
||||
printf_buf(buf, "%p%c\n", level, "[index start]");
|
||||
raviX_print_ast_node(buf, node->table_elem_assign_expr.key_expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[index end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level, "[value start]");
|
||||
raviX_print_ast_node(buf, node->table_elem_assign_expr.value_expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[value end]");
|
||||
printf_buf(buf, "%p%c\n", level, "[indexed assign end]");
|
||||
break;
|
||||
}
|
||||
case EXPR_TABLE_LITERAL: {
|
||||
printf_buf(buf, "%p{ %c %T\n", level, "[table constructor start]", &node->table_expr.type);
|
||||
print_ast_node_list(buf, node->table_expr.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%p} %c\n", level, "[table constructor end]");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf_buf(buf, "%pUnsupported node type %d\n", level, node->type);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_output_ast(CompilerState *container, FILE *fp)
|
||||
{
|
||||
TextBuffer mbuf;
|
||||
raviX_buffer_init(&mbuf, 1024);
|
||||
raviX_print_ast_node(&mbuf, container->main_function, 0);
|
||||
fputs(mbuf.buf, fp);
|
||||
raviX_buffer_free(&mbuf);
|
||||
}
|
@ -0,0 +1,516 @@
|
||||
/* Replace constant expressions with constants, and simply any other expressions if possible */
|
||||
/* Portions Copyright (C) 1994-2019 Lua.org, PUC-Rio.*/
|
||||
|
||||
#include <parser.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
static void process_expression_list(CompilerState *container, AstNodeList *node);
|
||||
static void process_statement_list(CompilerState *container, AstNodeList *node);
|
||||
static void process_statement(CompilerState *container, AstNode *node);
|
||||
|
||||
#define l_mathop(op) op
|
||||
|
||||
#ifndef CHAR_BIT
|
||||
#define CHAR_BIT 8
|
||||
#endif
|
||||
/* number of bits in an integer */
|
||||
#define NBITS ((int)(sizeof(lua_Integer) * CHAR_BIT))
|
||||
/*
|
||||
@@ lua_numbertointeger converts a float number with an integral value
|
||||
** to an integer, or returns 0 if float is not within the range of
|
||||
** a lua_Integer. (The range comparisons are tricky because of
|
||||
** rounding. The tests here assume a two-complement representation,
|
||||
** where MININTEGER always has an exact representation as a float;
|
||||
** MAXINTEGER may not have one, and therefore its conversion to float
|
||||
** may have an ill-defined value.)
|
||||
*/
|
||||
#define lua_numbertointeger(n, p) \
|
||||
((n) >= (lua_Number)(LUA_MININTEGER) && (n) < -(lua_Number)(LUA_MININTEGER) && (*(p) = (lua_Integer)(n), 1))
|
||||
|
||||
/*
|
||||
** Rounding modes for float->integer coercion
|
||||
*/
|
||||
typedef enum {
|
||||
F2Ieq, /* no rounding; accepts only integral values */
|
||||
F2Ifloor, /* takes the floor of the number */
|
||||
F2Iceil /* takes the ceil of the number */
|
||||
} F2Imod;
|
||||
|
||||
#if !defined(LUA_FLOORN2I)
|
||||
#define LUA_FLOORN2I F2Ieq
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The luai_num* macros define the primitive operations over numbers.
|
||||
*/
|
||||
|
||||
/* floor division (defined as 'floor(a/b)') */
|
||||
#if !defined(luai_numidiv)
|
||||
#define luai_numidiv(a, b) (l_floor(luai_numdiv(a, b)))
|
||||
#endif
|
||||
|
||||
/* float division */
|
||||
#if !defined(luai_numdiv)
|
||||
#define luai_numdiv(a, b) ((a) / (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when
|
||||
** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of
|
||||
** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b)
|
||||
** ~= floor(a/b)'. That happens when the division has a non-integer
|
||||
** negative result, which is equivalent to the test below.
|
||||
*/
|
||||
#if !defined(luai_nummod)
|
||||
#define luai_nummod(a, b, m) \
|
||||
{ \
|
||||
(m) = l_mathop(fmod)(a, b); \
|
||||
if ((m) * (b) < 0) \
|
||||
(m) += (b); \
|
||||
}
|
||||
#endif
|
||||
#define l_floor(x) (l_mathop(floor)(x))
|
||||
|
||||
/* exponentiation */
|
||||
#if !defined(luai_numpow)
|
||||
#define luai_numpow(a, b) (l_mathop(pow)(a, b))
|
||||
#endif
|
||||
|
||||
/* the others are quite standard operations */
|
||||
#if !defined(luai_numadd)
|
||||
#define luai_numadd(a, b) ((a) + (b))
|
||||
#define luai_numsub(a, b) ((a) - (b))
|
||||
#define luai_nummul(a, b) ((a) * (b))
|
||||
#define luai_numunm(a) (-(a))
|
||||
#define luai_numeq(a, b) ((a) == (b))
|
||||
#define luai_numlt(a, b) ((a) < (b))
|
||||
#define luai_numle(a, b) ((a) <= (b))
|
||||
#define luai_numisnan(a) (!luai_numeq((a), (a)))
|
||||
#endif
|
||||
|
||||
/* cast a signed lua_Integer to lua_Unsigned */
|
||||
#if !defined(l_castS2U)
|
||||
#define l_castS2U(i) ((lua_Unsigned)(i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** cast a lua_Unsigned to a signed lua_Integer; this cast is
|
||||
** not strict ISO C, but two-complement architectures should
|
||||
** work fine.
|
||||
*/
|
||||
#if !defined(l_castU2S)
|
||||
#define l_castU2S(i) ((lua_Integer)(i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** macros to improve jump prediction (used mainly for error handling)
|
||||
*/
|
||||
#if !defined(likely)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define likely(x) (__builtin_expect(((x) != 0), 1))
|
||||
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define cast(t, exp) ((t)(exp))
|
||||
#define cast_num(i) cast(lua_Number, (i))
|
||||
#define cast_int(i) cast(int, (i))
|
||||
|
||||
#define ttisfloat(o) (o->type.type_code == RAVI_TNUMFLT)
|
||||
#define ttisinteger(o) (o->type.type_code == RAVI_TNUMINT)
|
||||
#define fltvalue(o) (o->u.r)
|
||||
#define ivalue(o) (o->u.i)
|
||||
#define setivalue(o, v) (o->type.type_code = RAVI_TNUMINT, o->u.i = (v))
|
||||
#define setfltvalue(o, v) (o->type.type_code = RAVI_TNUMFLT, o->u.r = (v))
|
||||
|
||||
/* convert an object to a float (without string coercion) */
|
||||
#define tonumberns(o, n) (ttisfloat(o) ? ((n) = fltvalue(o), 1) : (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))
|
||||
|
||||
/* convert an object to an integer (including string coercion) */
|
||||
#define tointeger(o, i) (RAVI_LIKELY(ttisinteger(o)) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o, i, LUA_FLOORN2I))
|
||||
|
||||
/* convert an object to an integer (without string coercion) */
|
||||
#define tointegerns(o, i) (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o, i, LUA_FLOORN2I))
|
||||
|
||||
static void handle_error(CompilerState *container, const char *msg)
|
||||
{
|
||||
// TODO source and line number
|
||||
raviX_buffer_add_string(&container->error_message, msg);
|
||||
longjmp(container->env, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Integer division; return 'm // n', that is, floor(m/n).
|
||||
** C division truncates its result (rounds towards zero).
|
||||
** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer,
|
||||
** otherwise 'floor(q) == trunc(q) - 1'.
|
||||
*/
|
||||
static lua_Integer luaV_idiv(CompilerState *compiler_state, lua_Integer m, lua_Integer n)
|
||||
{
|
||||
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
||||
if (n == 0)
|
||||
handle_error(compiler_state, "attempt to divide by zero");
|
||||
return 0 - m; /* n==-1; avoid overflow with 0x80000...//-1 */
|
||||
} else {
|
||||
lua_Integer q = m / n; /* perform C division */
|
||||
if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */
|
||||
q -= 1; /* correct result for different rounding */
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Integer modulus; return 'm % n'. (Assume that C '%' with
|
||||
** negative operands follows C99 behavior. See previous comment
|
||||
** about luaV_idiv.)
|
||||
*/
|
||||
static lua_Integer luaV_mod(CompilerState *compiler_state, lua_Integer m, lua_Integer n)
|
||||
{
|
||||
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
||||
if (n == 0)
|
||||
handle_error(compiler_state, "attempt to perform 'n%%0'");
|
||||
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
|
||||
} else {
|
||||
lua_Integer r = m % n;
|
||||
if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */
|
||||
r += n; /* correct result for different rounding */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Float modulus
|
||||
*/
|
||||
static lua_Number luaV_modf(lua_Number m, lua_Number n)
|
||||
{
|
||||
lua_Number r;
|
||||
luai_nummod(m, n, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** Shift left operation. (Shift right just negates 'y'.)
|
||||
*/
|
||||
static lua_Integer luaV_shiftl(lua_Integer x, lua_Integer y)
|
||||
{
|
||||
if (y < 0) { /* shift right? */
|
||||
if (y <= -NBITS)
|
||||
return 0;
|
||||
else
|
||||
return x >> (-y);
|
||||
} else { /* shift left */
|
||||
if (y >= NBITS)
|
||||
return 0;
|
||||
else
|
||||
return x << y;
|
||||
}
|
||||
}
|
||||
|
||||
static lua_Integer intarith(CompilerState *compiler_state, int op, lua_Integer v1, lua_Integer v2)
|
||||
{
|
||||
switch (op) {
|
||||
case BINOPR_ADD:
|
||||
return v1 + v2;
|
||||
case BINOPR_SUB:
|
||||
return v1 - v2;
|
||||
case BINOPR_MUL:
|
||||
return v1 * v2;
|
||||
case BINOPR_MOD:
|
||||
return luaV_mod(compiler_state, v1, v2);
|
||||
case BINOPR_IDIV:
|
||||
return luaV_idiv(compiler_state, v1, v2);
|
||||
case BINOPR_BAND:
|
||||
return v1 & v2;
|
||||
case BINOPR_BOR:
|
||||
return v1 | v2;
|
||||
case BINOPR_BXOR:
|
||||
return v1 ^ v2;
|
||||
case BINOPR_SHL:
|
||||
return luaV_shiftl(v1, v2);
|
||||
case BINOPR_SHR:
|
||||
return luaV_shiftl(v1, -v2);
|
||||
case UNOPR_MINUS:
|
||||
return 0 - v1;
|
||||
case UNOPR_BNOT:
|
||||
return ~l_castS2U(0) ^ v1;
|
||||
default:
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static lua_Number numarith(CompilerState *compiler_state, int op, lua_Number v1, lua_Number v2)
|
||||
{
|
||||
switch (op) {
|
||||
case BINOPR_ADD:
|
||||
return luai_numadd(v1, v2);
|
||||
case BINOPR_SUB:
|
||||
return luai_numsub(v1, v2);
|
||||
case BINOPR_MUL:
|
||||
return luai_nummul(v1, v2);
|
||||
case BINOPR_DIV:
|
||||
return luai_numdiv(v1, v2);
|
||||
case BINOPR_POW:
|
||||
return luai_numpow(v1, v2);
|
||||
case BINOPR_IDIV:
|
||||
return luai_numidiv(v1, v2);
|
||||
case UNOPR_MINUS:
|
||||
return luai_numunm(v1);
|
||||
case BINOPR_MOD:
|
||||
return luaV_modf(v1, v2);
|
||||
default:
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** try to convert a float to an integer, rounding according to 'mode'.
|
||||
*/
|
||||
static int luaV_flttointeger(lua_Number n, lua_Integer *p, F2Imod mode)
|
||||
{
|
||||
lua_Number f = l_floor(n);
|
||||
if (n != f) { /* not an integral value? */
|
||||
if (mode == F2Ieq)
|
||||
return 0; /* fails if mode demands integral value */
|
||||
else if (mode == F2Iceil) /* needs ceil? */
|
||||
f += 1; /* convert floor to ceil (remember: n != f) */
|
||||
}
|
||||
return lua_numbertointeger(f, p);
|
||||
}
|
||||
|
||||
/*
|
||||
** try to convert a value to an integer, rounding according to 'mode',
|
||||
** without string coercion.
|
||||
** ("Fast track" handled by macro 'tointegerns'.)
|
||||
*/
|
||||
static int luaV_tointegerns(const LiteralExpression *obj, lua_Integer *p, F2Imod mode)
|
||||
{
|
||||
if (ttisfloat(obj))
|
||||
return luaV_flttointeger(fltvalue(obj), p, mode);
|
||||
else if (ttisinteger(obj)) {
|
||||
*p = ivalue(obj);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaO_rawarith(CompilerState *compiler_state, int op, const LiteralExpression *p1,
|
||||
const LiteralExpression *p2, LiteralExpression *res)
|
||||
{
|
||||
switch (op) {
|
||||
case BINOPR_BAND:
|
||||
case BINOPR_BOR:
|
||||
case BINOPR_BXOR:
|
||||
case BINOPR_SHL:
|
||||
case BINOPR_SHR:
|
||||
case UNOPR_BNOT: { /* operate only on integers */
|
||||
lua_Integer i1;
|
||||
lua_Integer i2;
|
||||
if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
|
||||
setivalue(res, intarith(compiler_state, op, i1, i2));
|
||||
return 1;
|
||||
} else
|
||||
return 0; /* fail */
|
||||
}
|
||||
case BINOPR_DIV:
|
||||
case BINOPR_POW: { /* operate only on floats */
|
||||
lua_Number n1;
|
||||
lua_Number n2;
|
||||
if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
||||
setfltvalue(res, numarith(compiler_state, op, n1, n2));
|
||||
return 1;
|
||||
} else
|
||||
return 0; /* fail */
|
||||
}
|
||||
default: { /* other operations */
|
||||
lua_Number n1;
|
||||
lua_Number n2;
|
||||
if (ttisinteger(p1) && ttisinteger(p2)) {
|
||||
setivalue(res, intarith(compiler_state, op, ivalue(p1), ivalue(p2)));
|
||||
return 1;
|
||||
} else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
||||
setfltvalue(res, numarith(compiler_state, op, n1, n2));
|
||||
return 1;
|
||||
} else
|
||||
return 0; /* fail */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_expression(CompilerState *container, AstNode *node)
|
||||
{
|
||||
switch (node->type) {
|
||||
case EXPR_FUNCTION:
|
||||
process_statement_list(container, node->function_expr.function_statement_list);
|
||||
break;
|
||||
case EXPR_SUFFIXED:
|
||||
process_expression(container, node->suffixed_expr.primary_expr);
|
||||
if (node->suffixed_expr.suffix_list) {
|
||||
process_expression_list(container, node->suffixed_expr.suffix_list);
|
||||
} else {
|
||||
// We can simplify and get rid of the suffixed expr
|
||||
// TODO free primary_expr
|
||||
memcpy(node, node->suffixed_expr.primary_expr, sizeof(AstNode));
|
||||
}
|
||||
break;
|
||||
case EXPR_FUNCTION_CALL:
|
||||
process_expression_list(container, node->function_call_expr.arg_list);
|
||||
break;
|
||||
case EXPR_SYMBOL:
|
||||
break;
|
||||
case EXPR_BINARY:
|
||||
process_expression(container, node->binary_expr.expr_left);
|
||||
process_expression(container, node->binary_expr.expr_right);
|
||||
if (node->binary_expr.expr_left->type == EXPR_LITERAL &&
|
||||
node->binary_expr.expr_right->type == EXPR_LITERAL &&
|
||||
node->binary_expr.binary_op >= BINOPR_ADD &&
|
||||
node->binary_expr.binary_op <= BINOPR_SHR) {
|
||||
LiteralExpression result = {.type.type_code = RAVI_TANY};
|
||||
if (luaO_rawarith(container, node->binary_expr.binary_op,
|
||||
&node->binary_expr.expr_left->literal_expr,
|
||||
&node->binary_expr.expr_right->literal_expr, &result)) {
|
||||
node->type = EXPR_LITERAL;
|
||||
node->literal_expr.type.type_code = result.type.type_code;
|
||||
if (node->literal_expr.type.type_code == RAVI_TNUMFLT)
|
||||
node->literal_expr.u.r = result.u.r;
|
||||
else {
|
||||
assert(node->literal_expr.type.type_code == RAVI_TNUMINT);
|
||||
node->literal_expr.u.i = result.u.i;
|
||||
}
|
||||
// TODO free expr_left and expr_right
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXPR_UNARY:
|
||||
process_expression(container, node->unary_expr.expr);
|
||||
if (node->unary_expr.expr->type == EXPR_LITERAL &&
|
||||
(node->unary_expr.unary_op == UNOPR_BNOT || node->unary_expr.unary_op == UNOPR_MINUS)) {
|
||||
LiteralExpression result = {.type.type_code = RAVI_TANY};
|
||||
if (luaO_rawarith(container, node->unary_expr.unary_op, &node->unary_expr.expr->literal_expr,
|
||||
&node->unary_expr.expr->literal_expr, &result)) {
|
||||
node->type = EXPR_LITERAL;
|
||||
node->literal_expr.type.type_code = result.type.type_code;
|
||||
if (node->literal_expr.type.type_code == RAVI_TNUMFLT)
|
||||
node->literal_expr.u.r = result.u.r;
|
||||
else {
|
||||
assert(node->literal_expr.type.type_code == RAVI_TNUMINT);
|
||||
node->literal_expr.u.i = result.u.i;
|
||||
}
|
||||
// TODO free unary_expr.expr
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXPR_LITERAL:
|
||||
break;
|
||||
case EXPR_FIELD_SELECTOR:
|
||||
process_expression(container, node->index_expr.expr);
|
||||
break;
|
||||
case EXPR_Y_INDEX:
|
||||
process_expression(container, node->index_expr.expr);
|
||||
break;
|
||||
case EXPR_TABLE_ELEMENT_ASSIGN:
|
||||
if (node->table_elem_assign_expr.key_expr) {
|
||||
process_expression(container, node->table_elem_assign_expr.key_expr);
|
||||
}
|
||||
process_expression(container, node->table_elem_assign_expr.value_expr);
|
||||
break;
|
||||
case EXPR_TABLE_LITERAL:
|
||||
process_expression_list(container, node->table_expr.expr_list);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_expression_list(CompilerState *container, AstNodeList *list)
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(list, node) { process_expression(container, node); }
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void process_statement_list(CompilerState *container, AstNodeList *list)
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(list, node) { process_statement(container, node); }
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void process_statement(CompilerState *container, AstNode *node)
|
||||
{
|
||||
switch (node->type) {
|
||||
case AST_NONE:
|
||||
break;
|
||||
case STMT_RETURN:
|
||||
process_expression_list(container, node->return_stmt.expr_list);
|
||||
break;
|
||||
case STMT_LOCAL:
|
||||
process_expression_list(container, node->local_stmt.expr_list);
|
||||
break;
|
||||
case STMT_FUNCTION:
|
||||
process_expression(container, node->function_stmt.function_expr);
|
||||
break;
|
||||
case STMT_LABEL:
|
||||
case STMT_GOTO:
|
||||
break;
|
||||
case STMT_DO:
|
||||
process_statement_list(container, node->do_stmt.do_statement_list);
|
||||
break;
|
||||
case STMT_EXPR:
|
||||
if (node->expression_stmt.var_expr_list) {
|
||||
process_expression_list(container, node->expression_stmt.var_expr_list);
|
||||
}
|
||||
process_expression_list(container, node->expression_stmt.expr_list);
|
||||
break;
|
||||
case STMT_IF: {
|
||||
AstNode *test_then_block;
|
||||
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block)
|
||||
{
|
||||
process_expression(container, test_then_block->test_then_block.condition);
|
||||
process_statement_list(container, test_then_block->test_then_block.test_then_statement_list);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
if (node->if_stmt.else_block) {
|
||||
process_statement_list(container, node->if_stmt.else_statement_list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STMT_WHILE:
|
||||
process_expression(container, node->while_or_repeat_stmt.condition);
|
||||
process_statement_list(container, node->while_or_repeat_stmt.loop_statement_list);
|
||||
break;
|
||||
case STMT_REPEAT:
|
||||
process_statement_list(container, node->while_or_repeat_stmt.loop_statement_list);
|
||||
process_expression(container, node->while_or_repeat_stmt.condition);
|
||||
break;
|
||||
case STMT_FOR_IN:
|
||||
case STMT_FOR_NUM:
|
||||
process_expression_list(container, node->for_stmt.expr_list);
|
||||
process_statement_list(container, node->for_stmt.for_statement_list);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "AST = %d\n", node->type);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int raviX_ast_simplify(CompilerState *container)
|
||||
{
|
||||
int rc = setjmp(container->env);
|
||||
if (rc == 0) {
|
||||
process_expression(container, container->main_function);
|
||||
} else {
|
||||
// dump it?
|
||||
}
|
||||
return rc;
|
||||
}
|
@ -0,0 +1,617 @@
|
||||
#include <ravi_compiler.h>
|
||||
|
||||
#include <parser.h>
|
||||
|
||||
const FunctionExpression *raviX_ast_get_main_function(const CompilerState *compiler_state)
|
||||
{
|
||||
return &compiler_state->main_function->function_expr;
|
||||
}
|
||||
const VariableType *raviX_function_type(const FunctionExpression *function_expression)
|
||||
{
|
||||
return &function_expression->type;
|
||||
}
|
||||
bool raviX_function_is_vararg(const FunctionExpression *function_expression)
|
||||
{
|
||||
return function_expression->is_vararg;
|
||||
}
|
||||
bool raviX_function_is_method(const FunctionExpression *function_expression)
|
||||
{
|
||||
return function_expression->is_method;
|
||||
}
|
||||
const FunctionExpression *raviX_function_parent(const FunctionExpression *function_expression)
|
||||
{
|
||||
if (function_expression->parent_function == NULL)
|
||||
return NULL;
|
||||
else
|
||||
return &function_expression->parent_function->function_expr;
|
||||
}
|
||||
void raviX_function_foreach_child(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const FunctionExpression *function_expression))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(function_expression->child_functions, node) { callback(userdata, &node->function_expr); }
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Scope *raviX_function_scope(const FunctionExpression *function_expression)
|
||||
{
|
||||
return function_expression->main_block;
|
||||
}
|
||||
void raviX_function_foreach_statement(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(function_expression->function_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
enum AstNodeType raviX_statement_type(const Statement *statement) { return statement->type; }
|
||||
void raviX_function_foreach_argument(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaVariableSymbol *symbol))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(function_expression->args, symbol) { callback(userdata, &symbol->variable); }
|
||||
END_FOR_EACH_PTR(symbol)
|
||||
}
|
||||
void raviX_function_foreach_local(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaVariableSymbol *lua_local_symbol))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(function_expression->locals, symbol) { callback(userdata, &symbol->variable); }
|
||||
END_FOR_EACH_PTR(symbol)
|
||||
}
|
||||
void raviX_function_foreach_upvalue(const FunctionExpression *function_expression, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaUpvalueSymbol *symbol))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(function_expression->upvalues, symbol) { callback(userdata, &symbol->upvalue); }
|
||||
END_FOR_EACH_PTR(symbol)
|
||||
}
|
||||
|
||||
const StringObject *raviX_variable_symbol_name(const LuaVariableSymbol *lua_local_symbol)
|
||||
{
|
||||
return lua_local_symbol->var_name;
|
||||
}
|
||||
|
||||
const VariableType *raviX_variable_symbol_type(const LuaVariableSymbol *lua_local_symbol)
|
||||
{
|
||||
return &lua_local_symbol->value_type;
|
||||
}
|
||||
|
||||
const Scope *raviX_variable_symbol_scope(const LuaVariableSymbol *lua_local_symbol)
|
||||
{
|
||||
return lua_local_symbol->block;
|
||||
}
|
||||
|
||||
#define n(v) ((AstNode *)v)
|
||||
const ReturnStatement *raviX_return_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_RETURN);
|
||||
return &n(stmt)->return_stmt;
|
||||
}
|
||||
const LabelStatement *raviX_label_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_LABEL);
|
||||
return &n(stmt)->label_stmt;
|
||||
}
|
||||
const GotoStatement *raviX_goto_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_GOTO);
|
||||
return &n(stmt)->goto_stmt;
|
||||
}
|
||||
const LocalStatement *raviX_local_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_LOCAL);
|
||||
return &n(stmt)->local_stmt;
|
||||
}
|
||||
const ExpressionStatement *raviX_expression_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_EXPR);
|
||||
return &n(stmt)->expression_stmt;
|
||||
}
|
||||
const FunctionStatement *raviX_function_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_FUNCTION);
|
||||
return &n(stmt)->function_stmt;
|
||||
}
|
||||
const DoStatement *raviX_do_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_DO);
|
||||
return &n(stmt)->do_stmt;
|
||||
}
|
||||
const TestThenStatement *raviX_test_then_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_TEST_THEN);
|
||||
return &n(stmt)->test_then_block;
|
||||
}
|
||||
const IfStatement *raviX_if_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_IF);
|
||||
return &n(stmt)->if_stmt;
|
||||
}
|
||||
const WhileOrRepeatStatement *raviX_while_or_repeat_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_WHILE || stmt->type == STMT_REPEAT);
|
||||
return &n(stmt)->while_or_repeat_stmt;
|
||||
}
|
||||
const ForStatement *raviX_for_statement(const Statement *stmt)
|
||||
{
|
||||
assert(stmt->type == STMT_FOR_IN || stmt->type == STMT_FOR_NUM);
|
||||
return &n(stmt)->for_stmt;
|
||||
}
|
||||
enum AstNodeType raviX_expression_type(const Expression *expression) { return expression->type; }
|
||||
const LiteralExpression *raviX_literal_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_LITERAL);
|
||||
return &n(expr)->literal_expr;
|
||||
}
|
||||
const SymbolExpression *raviX_symbol_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_SYMBOL);
|
||||
return &n(expr)->symbol_expr;
|
||||
}
|
||||
const IndexExpression *raviX_index_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_Y_INDEX || expr->type == EXPR_FIELD_SELECTOR);
|
||||
return &n(expr)->index_expr;
|
||||
}
|
||||
const UnaryExpression *raviX_unary_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_UNARY);
|
||||
return &n(expr)->unary_expr;
|
||||
}
|
||||
const BinaryExpression *raviX_binary_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_BINARY);
|
||||
return &n(expr)->binary_expr;
|
||||
}
|
||||
const FunctionExpression *raviX_function_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_FUNCTION);
|
||||
return &n(expr)->function_expr;
|
||||
}
|
||||
const TableElementAssignmentExpression *
|
||||
raviX_table_element_assignment_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_TABLE_ELEMENT_ASSIGN);
|
||||
return &n(expr)->table_elem_assign_expr;
|
||||
}
|
||||
const TableLiteralExpression *raviX_table_literal_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_TABLE_LITERAL);
|
||||
return &n(expr)->table_expr;
|
||||
}
|
||||
const SuffixedExpression *raviX_suffixed_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_SUFFIXED);
|
||||
return &n(expr)->suffixed_expr;
|
||||
}
|
||||
const FunctionCallExpression *raviX_function_call_expression(const Expression *expr)
|
||||
{
|
||||
assert(expr->type == EXPR_FUNCTION_CALL);
|
||||
return &n(expr)->function_call_expr;
|
||||
}
|
||||
#undef n
|
||||
|
||||
void raviX_return_statement_foreach_expression(const ReturnStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->expr_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
|
||||
const StringObject *raviX_label_statement_label_name(const LabelStatement *statement)
|
||||
{
|
||||
return statement->symbol->label.label_name;
|
||||
}
|
||||
const Scope *raviX_label_statement_label_scope(const LabelStatement *statement)
|
||||
{
|
||||
return statement->symbol->label.block;
|
||||
}
|
||||
|
||||
const StringObject *raviX_goto_statement_label_name(const GotoStatement *statement)
|
||||
{
|
||||
return statement->name;
|
||||
}
|
||||
const Scope *raviX_goto_statement_scope(const GotoStatement *statement)
|
||||
{
|
||||
return statement->goto_scope;
|
||||
}
|
||||
bool raviX_goto_statement_is_break(const GotoStatement *statement) { return statement->is_break; }
|
||||
|
||||
void raviX_local_statement_foreach_expression(const LocalStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->expr_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
void raviX_local_statement_foreach_symbol(const LocalStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const LuaVariableSymbol *expr))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(statement->var_list, symbol)
|
||||
{
|
||||
assert(symbol->symbol_type == SYM_LOCAL);
|
||||
callback(userdata, &symbol->variable);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
void raviX_expression_statement_foreach_lhs_expression(const ExpressionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->var_expr_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
void raviX_expression_statement_foreach_rhs_expression(const ExpressionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->expr_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const SymbolExpression *raviX_function_statement_name(const FunctionStatement *statement)
|
||||
{
|
||||
assert(statement->name->type == EXPR_SYMBOL);
|
||||
return &statement->name->symbol_expr;
|
||||
}
|
||||
bool raviX_function_statement_is_method(const FunctionStatement *statement)
|
||||
{
|
||||
return statement->method_name != NULL;
|
||||
}
|
||||
const IndexExpression *raviX_function_statement_method_name(const FunctionStatement *statement)
|
||||
{
|
||||
assert(statement->method_name->type == EXPR_Y_INDEX || statement->method_name->type == EXPR_FIELD_SELECTOR);
|
||||
return &statement->method_name->index_expr;
|
||||
}
|
||||
bool raviX_function_statement_has_selectors(const FunctionStatement *statement)
|
||||
{
|
||||
return statement->selectors != NULL;
|
||||
}
|
||||
void raviX_function_statement_foreach_selector(const FunctionStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const IndexExpression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->selectors, node)
|
||||
{
|
||||
assert(node->type == EXPR_Y_INDEX || node->type == EXPR_FIELD_SELECTOR);
|
||||
callback(userdata, &node->index_expr);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const FunctionExpression *raviX_function_ast(const FunctionStatement *statement)
|
||||
{
|
||||
assert(statement->function_expr->type == EXPR_FUNCTION);
|
||||
return &statement->function_expr->function_expr;
|
||||
}
|
||||
const Scope *raviX_do_statement_scope(const DoStatement *statement) { return statement->scope; }
|
||||
void raviX_do_statement_foreach_statement(const DoStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->do_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Scope *raviX_test_then_statement_scope(const TestThenStatement *statement)
|
||||
{
|
||||
return statement->test_then_scope;
|
||||
}
|
||||
void raviX_test_then_statement_foreach_statement(const TestThenStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->test_then_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Expression *raviX_test_then_statement_condition(const TestThenStatement *statement)
|
||||
{
|
||||
assert(statement->condition->type >= EXPR_LITERAL && statement->condition->type <= EXPR_FUNCTION_CALL);
|
||||
return (Expression *)statement->condition;
|
||||
}
|
||||
void raviX_if_statement_foreach_test_then_statement(const IfStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const TestThenStatement *stmt))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->if_condition_list, node)
|
||||
{
|
||||
assert(node->type == STMT_TEST_THEN);
|
||||
callback(userdata, &node->test_then_block);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Scope *raviX_if_then_statement_else_scope(const IfStatement *statement)
|
||||
{
|
||||
return statement->else_block;
|
||||
}
|
||||
void raviX_if_statement_foreach_else_statement(const IfStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->else_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
|
||||
const Expression *raviX_while_or_repeat_statement_condition(const WhileOrRepeatStatement *statement)
|
||||
{
|
||||
assert(statement->condition->type >= EXPR_LITERAL && statement->condition->type <= EXPR_FUNCTION_CALL);
|
||||
return (Expression *)statement->condition;
|
||||
}
|
||||
const Scope *raviX_while_or_repeat_statement_scope(const WhileOrRepeatStatement *statement)
|
||||
{
|
||||
return statement->loop_scope;
|
||||
}
|
||||
void raviX_while_or_repeat_statement_foreach_statement(const WhileOrRepeatStatement *statement,
|
||||
void *userdata,
|
||||
void (*callback)(void *userdata,
|
||||
const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->loop_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Scope *raviX_for_statement_scope(const ForStatement *statement)
|
||||
{
|
||||
return statement->for_scope;
|
||||
}
|
||||
void raviX_for_statement_foreach_symbol(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const LuaVariableSymbol *expr))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(statement->symbols, symbol)
|
||||
{
|
||||
assert(symbol->symbol_type == SYM_LOCAL);
|
||||
callback(userdata, &symbol->variable);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
void raviX_for_statement_foreach_expression(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->expr_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const Scope *raviX_for_statement_body_scope(const ForStatement *statement)
|
||||
{
|
||||
return statement->for_body;
|
||||
}
|
||||
void raviX_for_statement_body_foreach_statement(const ForStatement *statement, void *userdata,
|
||||
void (*callback)(void *userdata, const Statement *statement))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(statement->for_statement_list, node)
|
||||
{
|
||||
assert(node->type <= STMT_EXPR);
|
||||
callback(userdata, (Statement *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const VariableType *raviX_literal_expression_type(const LiteralExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const SemInfo *raviX_literal_expression_literal(const LiteralExpression *expression) { return &expression->u; }
|
||||
const VariableType *raviX_symbol_expression_type(const SymbolExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const LuaSymbol *raviX_symbol_expression_symbol(const SymbolExpression *expression)
|
||||
{
|
||||
return expression->var;
|
||||
}
|
||||
const VariableType *raviX_index_expression_type(const IndexExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const Expression *raviX_index_expression_expression(const IndexExpression *expression)
|
||||
{
|
||||
assert(expression->expr->type >= EXPR_LITERAL && expression->expr->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->expr;
|
||||
}
|
||||
const VariableType *raviX_unary_expression_type(const UnaryExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const Expression *raviX_unary_expression_expression(const UnaryExpression *expression)
|
||||
{
|
||||
assert(expression->expr->type >= EXPR_LITERAL && expression->expr->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->expr;
|
||||
}
|
||||
UnaryOperatorType raviX_unary_expression_operator(const UnaryExpression *expression)
|
||||
{
|
||||
return expression->unary_op;
|
||||
}
|
||||
const VariableType *raviX_binary_expression_type(const BinaryExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const Expression *raviX_binary_expression_left_expression(const BinaryExpression *expression)
|
||||
{
|
||||
assert(expression->expr_left->type >= EXPR_LITERAL && expression->expr_left->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->expr_left;
|
||||
}
|
||||
const Expression *raviX_binary_expression_right_expression(const BinaryExpression *expression)
|
||||
{
|
||||
assert(expression->expr_right->type >= EXPR_LITERAL && expression->expr_right->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->expr_right;
|
||||
}
|
||||
BinaryOperatorType raviX_binary_expression_operator(const BinaryExpression *expression)
|
||||
{
|
||||
return expression->binary_op;
|
||||
}
|
||||
const VariableType *
|
||||
raviX_table_element_assignment_expression_type(const TableElementAssignmentExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const Expression *
|
||||
raviX_table_element_assignment_expression_key(const TableElementAssignmentExpression *expression)
|
||||
{
|
||||
if (!expression->key_expr)
|
||||
return NULL;
|
||||
assert(expression->key_expr->type >= EXPR_LITERAL && expression->key_expr->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->key_expr;
|
||||
}
|
||||
const Expression *
|
||||
raviX_table_element_assignment_expression_value(const TableElementAssignmentExpression *expression)
|
||||
{
|
||||
assert(expression->value_expr->type >= EXPR_LITERAL && expression->value_expr->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->value_expr;
|
||||
}
|
||||
const VariableType *raviX_table_literal_expression_type(const TableLiteralExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
void raviX_table_literal_expression_foreach_element(
|
||||
const TableLiteralExpression *expression, void *userdata,
|
||||
void (*callback)(void *, const TableElementAssignmentExpression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(expression->expr_list, node)
|
||||
{
|
||||
assert(node->type == EXPR_TABLE_ELEMENT_ASSIGN);
|
||||
callback(userdata, &node->table_elem_assign_expr);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
|
||||
const VariableType *raviX_suffixed_expression_type(const SuffixedExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
const Expression *raviX_suffixed_expression_primary(const SuffixedExpression *expression)
|
||||
{
|
||||
assert(expression->primary_expr->type >= EXPR_LITERAL && expression->primary_expr->type <= EXPR_FUNCTION_CALL);
|
||||
return (const Expression *)expression->primary_expr;
|
||||
}
|
||||
void raviX_suffixed_expression_foreach_suffix(const SuffixedExpression *expression, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(expression->suffix_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
|
||||
const VariableType *raviX_function_call_expression_type(const FunctionCallExpression *expression)
|
||||
{
|
||||
return &expression->type;
|
||||
}
|
||||
// Can return NULL
|
||||
const StringObject *
|
||||
raviX_function_call_expression_method_name(const FunctionCallExpression *expression)
|
||||
{
|
||||
return expression->method_name;
|
||||
}
|
||||
void raviX_function_call_expression_foreach_argument(const FunctionCallExpression *expression, void *userdata,
|
||||
void (*callback)(void *, const Expression *expr))
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(expression->arg_list, node)
|
||||
{
|
||||
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
|
||||
callback(userdata, (Expression *)node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
const FunctionExpression *raviX_scope_owning_function(const Scope *scope)
|
||||
{
|
||||
assert(scope->function->type == EXPR_FUNCTION);
|
||||
return &scope->function->function_expr;
|
||||
}
|
||||
RAVICOMP_EXPORT const Scope *raviX_scope_parent_scope(const Scope *scope)
|
||||
{
|
||||
return scope->parent;
|
||||
}
|
||||
RAVICOMP_EXPORT void raviX_scope_foreach_symbol(const Scope *scope, void *userdata,
|
||||
void (*callback)(void *userdata, const LuaSymbol *symbol))
|
||||
{
|
||||
LuaSymbol *symbol;
|
||||
FOR_EACH_PTR(scope->symbol_list, symbol) { callback(userdata, symbol); }
|
||||
END_FOR_EACH_PTR(node)
|
||||
}
|
||||
enum SymbolType raviX_symbol_type(const LuaSymbol *symbol) { return symbol->symbol_type; }
|
||||
const LuaVariableSymbol *raviX_symbol_variable(const LuaSymbol *symbol)
|
||||
{
|
||||
assert(symbol->symbol_type == SYM_GLOBAL || symbol->symbol_type == SYM_LOCAL);
|
||||
return &symbol->variable;
|
||||
}
|
||||
const LuaUpvalueSymbol *raviX_symbol_upvalue(const LuaSymbol *symbol)
|
||||
{
|
||||
assert(symbol->symbol_type == SYM_UPVALUE);
|
||||
return &symbol->upvalue;
|
||||
}
|
||||
const LuaLabelSymbol *raviX_symbol_label(const LuaSymbol *symbol)
|
||||
{
|
||||
assert(symbol->symbol_type == SYM_LABEL);
|
||||
return &symbol->label;
|
||||
}
|
||||
const StringObject *raviX_label_name(const LuaLabelSymbol *symbol) { return symbol->label_name; }
|
||||
const Scope *raviX_label_scope(const LuaLabelSymbol *symbol) { return symbol->block; }
|
||||
const VariableType *raviX_upvalue_symbol_type(const LuaUpvalueSymbol *symbol)
|
||||
{
|
||||
return &symbol->value_type;
|
||||
}
|
||||
const LuaVariableSymbol *raviX_upvalue_target_variable(const LuaUpvalueSymbol *symbol)
|
||||
{
|
||||
if (symbol->target_variable->symbol_type == SYM_ENV) {
|
||||
assert(symbol->target_function == NULL);
|
||||
return NULL;
|
||||
}
|
||||
assert(symbol->target_variable->symbol_type == SYM_LOCAL);
|
||||
return &symbol->target_variable->variable;
|
||||
}
|
||||
const FunctionExpression *raviX_upvalue_target_function(const LuaUpvalueSymbol *symbol)
|
||||
{
|
||||
if (symbol->target_variable->symbol_type == SYM_ENV) {
|
||||
assert(symbol->target_function == NULL);
|
||||
return NULL;
|
||||
}
|
||||
assert(symbol->target_function->type == EXPR_FUNCTION);
|
||||
return &symbol->target_function->function_expr;
|
||||
}
|
||||
unsigned raviX_upvalue_index(const LuaUpvalueSymbol *symbol) { return symbol->upvalue_index; }
|
@ -0,0 +1,274 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
/*
|
||||
* Adapted for Ravi Compiler project
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <allocate.h>
|
||||
#include <bitset.h>
|
||||
|
||||
#if !defined(BITMAP_ENABLE_CHECKING) && !defined(NDEBUG)
|
||||
#define BITMAP_ENABLE_CHECKING
|
||||
#endif
|
||||
|
||||
#ifndef BITMAP_ENABLE_CHECKING
|
||||
#define BITMAP_ASSERT(EXPR, OP) ((void) (EXPR))
|
||||
|
||||
#else
|
||||
static inline void mir_bitset_assert_fail (const char *op) {
|
||||
fprintf (stderr, "wrong %s for a bitset", op);
|
||||
assert (0);
|
||||
}
|
||||
|
||||
#define BITMAP_ASSERT(EXPR, OP) (void) ((EXPR) ? 0 : (mir_bitset_assert_fail (#OP), 0))
|
||||
|
||||
#endif
|
||||
|
||||
#define BITMAP_WORD_BITS 64
|
||||
|
||||
void raviX_bitset_create2(BitSet *bm, size_t init_bits_num) {
|
||||
bm->els_num = 0;
|
||||
bm->size = (init_bits_num + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS;
|
||||
bm->varr = calloc(bm->size, sizeof(bitset_el_t));
|
||||
}
|
||||
|
||||
void raviX_bitset_destroy(BitSet * bm)
|
||||
{
|
||||
free(bm->varr);
|
||||
}
|
||||
|
||||
static void bitset_expand (BitSet * bm, size_t nb) {
|
||||
size_t new_len = (nb + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS;
|
||||
if (new_len > bm->els_num) {
|
||||
if (new_len > bm->size) {
|
||||
bm->varr = raviX_realloc_array(bm->varr, sizeof(bitset_el_t), bm->size, new_len);
|
||||
bm->size = new_len;
|
||||
}
|
||||
bm->els_num = new_len;
|
||||
}
|
||||
}
|
||||
|
||||
int raviX_bitset_bit_p(const BitSet * bm, size_t nb) {
|
||||
size_t nw, sh, len = bm->els_num;
|
||||
bitset_el_t *addr = bm->varr;
|
||||
|
||||
if (nb >= BITMAP_WORD_BITS * len) return 0;
|
||||
nw = nb / BITMAP_WORD_BITS;
|
||||
sh = nb % BITMAP_WORD_BITS;
|
||||
return (addr[nw] >> sh) & 1;
|
||||
}
|
||||
|
||||
/* Set the given bit to 1, and return true if the bit was previously unset, i.e.
|
||||
* this set caused bit to change from 0 to 1
|
||||
*/
|
||||
int raviX_bitset_set_bit_p(BitSet * bm, size_t bit) {
|
||||
size_t nw, sh;
|
||||
bitset_el_t *addr;
|
||||
int res;
|
||||
|
||||
bitset_expand (bm, bit + 1);
|
||||
addr = bm->varr;
|
||||
nw = bit / BITMAP_WORD_BITS;
|
||||
sh = bit % BITMAP_WORD_BITS;
|
||||
res = ((addr[nw] >> sh) & 1) == 0; /* Was this bit previously unset? */
|
||||
assert(nw < bm->els_num);
|
||||
addr[nw] |= (bitset_el_t) 1 << sh;
|
||||
return res;
|
||||
}
|
||||
|
||||
int raviX_bitset_clear_bit_p(BitSet * bm, size_t nb) {
|
||||
size_t nw, sh, len = bm->els_num;
|
||||
bitset_el_t *addr = bm->varr;
|
||||
int res;
|
||||
|
||||
if (nb >= BITMAP_WORD_BITS * len) return 0;
|
||||
nw = nb / BITMAP_WORD_BITS;
|
||||
sh = nb % BITMAP_WORD_BITS;
|
||||
res = (addr[nw] >> sh) & 1;
|
||||
addr[nw] &= ~((bitset_el_t) 1 << sh);
|
||||
return res;
|
||||
}
|
||||
|
||||
int raviX_bitset_set_or_clear_bit_range_p(BitSet * bm, size_t nb, size_t len, int set_p) {
|
||||
size_t nw, lsh, rsh, range_len;
|
||||
bitset_el_t mask, *addr;
|
||||
int res = 0;
|
||||
|
||||
bitset_expand (bm, nb + len);
|
||||
addr = bm->varr;
|
||||
while (len > 0) {
|
||||
nw = nb / BITMAP_WORD_BITS;
|
||||
lsh = nb % BITMAP_WORD_BITS;
|
||||
rsh = len >= BITMAP_WORD_BITS - lsh ? 0 : BITMAP_WORD_BITS - (nb + len) % BITMAP_WORD_BITS;
|
||||
mask = ((~(bitset_el_t) 0) >> (rsh + lsh)) << lsh;
|
||||
if (set_p) {
|
||||
res |= (~addr[nw] & mask) != 0;
|
||||
addr[nw] |= mask;
|
||||
} else {
|
||||
res |= (addr[nw] & mask) != 0;
|
||||
addr[nw] &= ~mask;
|
||||
}
|
||||
range_len = BITMAP_WORD_BITS - rsh - lsh;
|
||||
len -= range_len;
|
||||
nb += range_len;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void raviX_bitset_copy(BitSet * dst, const BitSet * src) {
|
||||
|
||||
size_t dst_len = dst->els_num;
|
||||
size_t src_len = src->els_num;
|
||||
|
||||
if (dst_len >= src_len)
|
||||
dst->els_num = src_len;
|
||||
else
|
||||
bitset_expand (dst, src_len * BITMAP_WORD_BITS);
|
||||
memcpy (dst->varr, src->varr,
|
||||
src_len * sizeof (bitset_el_t));
|
||||
}
|
||||
|
||||
int raviX_bitset_equal_p(const BitSet * bm1, const BitSet * bm2) {
|
||||
const BitSet * temp_bm;
|
||||
size_t i, temp_len, bm1_len = bm1->els_num;
|
||||
size_t bm2_len = bm2->els_num;
|
||||
bitset_el_t *addr1, *addr2;
|
||||
|
||||
if (bm1_len > bm2_len) {
|
||||
temp_bm = bm1;
|
||||
bm1 = bm2;
|
||||
bm2 = temp_bm;
|
||||
temp_len = bm1_len;
|
||||
bm1_len = bm2_len;
|
||||
bm2_len = temp_len;
|
||||
}
|
||||
addr1 = bm1->varr;
|
||||
addr2 = bm2->varr;
|
||||
if (memcmp (addr1, addr2, bm1_len * sizeof (bitset_el_t)) != 0) return false;
|
||||
for (i = bm1_len; i < bm2_len; i++)
|
||||
if (addr2[i] != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int raviX_bitset_intersect_p(const BitSet * bm1, const BitSet * bm2) {
|
||||
size_t i, min_len, bm1_len = bm1->els_num;
|
||||
size_t bm2_len = bm2->els_num;
|
||||
bitset_el_t *addr1 = bm1->varr;
|
||||
bitset_el_t *addr2 = bm2->varr;
|
||||
|
||||
min_len = bm1_len <= bm2_len ? bm1_len : bm2_len;
|
||||
for (i = 0; i < min_len; i++)
|
||||
if ((addr1[i] & addr2[i]) != 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int raviX_bitset_empty_p(const BitSet * bm) {
|
||||
size_t i, len = bm->els_num;
|
||||
bitset_el_t *addr = bm->varr;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (addr[i] != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bitset_el_t bitset_el_max2 (bitset_el_t el1, bitset_el_t el2) {
|
||||
return el1 < el2 ? el2 : el1;
|
||||
}
|
||||
|
||||
static bitset_el_t bitset_el_max3 (bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
|
||||
if (el1 <= el2) return el2 < el3 ? el3 : el2;
|
||||
return el1 < el3 ? el3 : el1;
|
||||
}
|
||||
|
||||
/* Return the number of bits set in BM. */
|
||||
size_t raviX_bitset_bit_count(const BitSet * bm) {
|
||||
size_t i, len = bm->els_num;
|
||||
bitset_el_t el, *addr = bm->varr;
|
||||
size_t count = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((el = addr[i]) != 0) {
|
||||
for (; el != 0; el >>= 1)
|
||||
if (el & 1) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int raviX_bitset_op2(BitSet * dst, const BitSet * src1, const BitSet * src2,
|
||||
bitset_el_t (*op) (bitset_el_t, bitset_el_t)) {
|
||||
size_t i, len, bound, src1_len, src2_len;
|
||||
bitset_el_t old, *dst_addr, *src1_addr, *src2_addr;
|
||||
int change_p = false;
|
||||
|
||||
src1_len = src1->els_num;
|
||||
src2_len = src2->els_num;
|
||||
len = bitset_el_max2 (src1_len, src2_len);
|
||||
bitset_expand (dst, len * BITMAP_WORD_BITS);
|
||||
dst_addr = dst->varr;
|
||||
src1_addr = src1->varr;
|
||||
src2_addr = src2->varr;
|
||||
for (bound = i = 0; i < len; i++) {
|
||||
old = dst_addr[i];
|
||||
if ((dst_addr[i] = op (i >= src1_len ? 0 : src1_addr[i], i >= src2_len ? 0 : src2_addr[i]))
|
||||
!= 0)
|
||||
bound = i + 1;
|
||||
if (old != dst_addr[i]) change_p = true;
|
||||
}
|
||||
dst->els_num = bound;
|
||||
return change_p;
|
||||
}
|
||||
|
||||
int raviX_bitset_op3(BitSet * dst, const BitSet * src1, const BitSet * src2,
|
||||
const BitSet * src3, bitset_el_t (*op) (bitset_el_t, bitset_el_t, bitset_el_t)) {
|
||||
size_t i, len, bound, src1_len, src2_len, src3_len;
|
||||
bitset_el_t old, *dst_addr, *src1_addr, *src2_addr, *src3_addr;
|
||||
int change_p = false;
|
||||
|
||||
src1_len = src1->els_num;
|
||||
src2_len = src2->els_num;
|
||||
src3_len = src3->els_num;
|
||||
len = bitset_el_max3 (src1_len, src2_len, src3_len);
|
||||
bitset_expand (dst, len * BITMAP_WORD_BITS);
|
||||
dst_addr = dst->varr;
|
||||
src1_addr = src1->varr;
|
||||
src2_addr = src2->varr;
|
||||
src3_addr = src3->varr;
|
||||
for (bound = i = 0; i < len; i++) {
|
||||
old = dst_addr[i];
|
||||
if ((dst_addr[i] = op (i >= src1_len ? 0 : src1_addr[i], i >= src2_len ? 0 : src2_addr[i],
|
||||
i >= src3_len ? 0 : src3_addr[i]))
|
||||
!= 0)
|
||||
bound = i + 1;
|
||||
if (old != dst_addr[i]) change_p = true;
|
||||
}
|
||||
dst->els_num = bound;
|
||||
return change_p;
|
||||
}
|
||||
|
||||
int raviX_bitset_iterator_next(BitSetIterator *iter, size_t *nbit) {
|
||||
const size_t el_bits_num = sizeof (bitset_el_t) * CHAR_BIT;
|
||||
size_t curr_nel = iter->nbit / el_bits_num, len = iter->bitset->els_num;
|
||||
bitset_el_t el, *addr = iter->bitset->varr;
|
||||
|
||||
for (; curr_nel < len; curr_nel++, iter->nbit = curr_nel * el_bits_num)
|
||||
if ((el = addr[curr_nel]) != 0)
|
||||
for (el >>= iter->nbit % el_bits_num; el != 0; el >>= 1, iter->nbit++)
|
||||
if (el & 1) {
|
||||
*nbit = iter->nbit++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,107 @@
|
||||
/* This file is a part of MIR project.
|
||||
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
||||
*/
|
||||
/*
|
||||
* Adapted for Ravi Compiler project
|
||||
*/
|
||||
|
||||
#ifndef ravicomp_BITSET_H
|
||||
#define ravicomp_BITSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint64_t bitset_el_t;
|
||||
|
||||
typedef struct BitSet {
|
||||
size_t els_num;
|
||||
size_t size;
|
||||
bitset_el_t *varr;
|
||||
} BitSet;
|
||||
|
||||
extern void raviX_bitset_create2(BitSet *, size_t init_bits_num);
|
||||
static inline void raviX_bitset_create(BitSet *bm)
|
||||
{
|
||||
raviX_bitset_create2(bm, 0);
|
||||
}
|
||||
extern void raviX_bitset_destroy(BitSet * bm);
|
||||
static inline void raviX_bitset_clear(BitSet * bm)
|
||||
{
|
||||
bm->els_num = 0;
|
||||
}
|
||||
extern int raviX_bitset_bit_p(const BitSet * bm, size_t nb);
|
||||
/* Sets a bit ON and returns true if previously bit was not set */
|
||||
extern int raviX_bitset_set_bit_p(BitSet * bm, size_t bit);
|
||||
extern int raviX_bitset_clear_bit_p(BitSet * bm, size_t nb);
|
||||
extern int raviX_bitset_set_or_clear_bit_range_p(BitSet * bm, size_t nb, size_t len, int set_p);
|
||||
static inline int raviX_bitset_set_bit_range_p(BitSet * bm, size_t nb, size_t len) {
|
||||
return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, true);
|
||||
}
|
||||
static inline int raviX_bitset_clear_bit_range_p(BitSet * bm, size_t nb, size_t len) {
|
||||
return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, false);
|
||||
}
|
||||
extern void raviX_bitset_copy(BitSet * dst, const BitSet * src);
|
||||
extern int raviX_bitset_equal_p(const BitSet * bm1, const BitSet * bm2);
|
||||
extern int raviX_bitset_intersect_p(const BitSet * bm1, const BitSet * bm2);
|
||||
extern int raviX_bitset_empty_p(const BitSet * bm);
|
||||
/* Return the number of bits set in BM. */
|
||||
extern size_t raviX_bitset_bit_count(const BitSet * bm);
|
||||
extern int raviX_bitset_op2(BitSet * dst, const BitSet * src1, const BitSet * src2,
|
||||
bitset_el_t (*op) (bitset_el_t, bitset_el_t));
|
||||
static inline bitset_el_t raviX_bitset_el_and(bitset_el_t el1, bitset_el_t el2) { return el1 & el2; }
|
||||
static inline int raviX_bitset_and(BitSet * dst, BitSet * src1, BitSet * src2) {
|
||||
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and);
|
||||
}
|
||||
static inline bitset_el_t raviX_bitset_el_and_compl(bitset_el_t el1, bitset_el_t el2) {
|
||||
return el1 & ~el2;
|
||||
}
|
||||
static inline int raviX_bitset_and_compl(BitSet * dst, BitSet * src1, BitSet * src2) {
|
||||
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and_compl);
|
||||
}
|
||||
static inline bitset_el_t raviX_bitset_el_ior(bitset_el_t el1, bitset_el_t el2) { return el1 | el2; }
|
||||
static inline int raviX_bitset_ior(BitSet * dst, BitSet * src1, BitSet * src2) {
|
||||
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_ior);
|
||||
}
|
||||
int raviX_bitset_op3(BitSet * dst, const BitSet * src1, const BitSet * src2,
|
||||
const BitSet * src3, bitset_el_t (*op) (bitset_el_t, bitset_el_t, bitset_el_t));
|
||||
static inline bitset_el_t raviX_bitset_el_ior_and(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
|
||||
return el1 | (el2 & el3);
|
||||
}
|
||||
/* DST = SRC1 | (SRC2 & SRC3). Return true if DST changed. */
|
||||
static inline int raviX_bitset_ior_and(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) {
|
||||
return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and);
|
||||
}
|
||||
static inline bitset_el_t raviX_bitset_el_ior_and_compl(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
|
||||
return el1 | (el2 & ~el3);
|
||||
}
|
||||
/* DST = SRC1 | (SRC2 & ~SRC3). Return true if DST changed. */
|
||||
static inline int raviX_bitset_ior_and_compl(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) {
|
||||
return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and_compl);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BitSet * bitset;
|
||||
size_t nbit;
|
||||
} BitSetIterator;
|
||||
static inline void raviX_bitset_iterator_init(BitSetIterator *iter, BitSet * bitset) {
|
||||
iter->bitset = bitset;
|
||||
iter->nbit = 0;
|
||||
}
|
||||
extern int raviX_bitset_iterator_next(BitSetIterator *iter, size_t *nbit);
|
||||
#define FOREACH_BITSET_BIT(iter, bitset, nbit) \
|
||||
for (raviX_bitset_iterator_init (&iter, bitset); raviX_bitset_iterator_next (&iter, &nbit);)
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,81 @@
|
||||
/* Build CFG */
|
||||
|
||||
|
||||
#include "graph.h"
|
||||
#include "cfg.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* Recursively create control flow graph for each proc
|
||||
* Return 0 on success
|
||||
*/
|
||||
int raviX_construct_cfg(Proc *proc)
|
||||
{
|
||||
Graph *g = raviX_init_graph(ENTRY_BLOCK, EXIT_BLOCK, proc);
|
||||
for (unsigned i = 0; i < proc->node_count; i++) {
|
||||
BasicBlock *block = proc->nodes[i];
|
||||
Instruction *insn = raviX_last_instruction(block);
|
||||
if (insn == NULL)
|
||||
continue;
|
||||
if (insn->opcode == op_br || insn->opcode == op_cbr || insn->opcode == op_ret) {
|
||||
Pseudo *pseudo;
|
||||
FOR_EACH_PTR(insn->targets, pseudo)
|
||||
{
|
||||
assert(pseudo->type == PSEUDO_BLOCK);
|
||||
raviX_add_edge(g, block->index, pseudo->block->index);
|
||||
}
|
||||
END_FOR_EACH_PTR(pseudo)
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
proc->cfg = g;
|
||||
Proc *childproc;
|
||||
FOR_EACH_PTR(proc->procs, childproc)
|
||||
{
|
||||
if (raviX_construct_cfg(childproc) != 0)
|
||||
return 1;
|
||||
}
|
||||
END_FOR_EACH_PTR(childproc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CfgArg {
|
||||
FILE *fp;
|
||||
Proc *proc;
|
||||
};
|
||||
|
||||
static void output_node(void *arg, Graph *g, uint32_t nodeid)
|
||||
{
|
||||
struct CfgArg *myargs = (struct CfgArg *)arg;
|
||||
FILE *fp = myargs->fp;
|
||||
Proc *proc = myargs->proc;
|
||||
GraphNodeList *successors = raviX_successors(raviX_graph_node(g, nodeid));
|
||||
if (!successors)
|
||||
return;
|
||||
BasicBlock *block = proc->nodes[nodeid];
|
||||
if (raviX_ptrlist_size((const struct ptr_list *)block->insns) > 0) {
|
||||
TextBuffer buf;
|
||||
raviX_buffer_init(&buf, 1024);
|
||||
raviX_output_basic_block_as_table(proc, block, &buf);
|
||||
fprintf(fp, "L%d [shape=none, margin=0, label=<%s>];\n", nodeid, raviX_buffer_data(&buf));
|
||||
raviX_buffer_free(&buf);
|
||||
}
|
||||
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
|
||||
fprintf(fp, "L%d -> L%d\n", nodeid, raviX_node_list_at(successors, i));
|
||||
}
|
||||
Proc *childproc;
|
||||
FOR_EACH_PTR(proc->procs, childproc) { raviX_output_cfg(childproc, fp); }
|
||||
END_FOR_EACH_PTR(childproc)
|
||||
}
|
||||
|
||||
void raviX_output_cfg(Proc *proc, FILE *fp)
|
||||
{
|
||||
Graph *g = proc->cfg;
|
||||
if (!g)
|
||||
return;
|
||||
fprintf(fp, "digraph Proc%d {\n", proc->id);
|
||||
struct CfgArg args = {.proc = proc, .fp = fp};
|
||||
raviX_for_each_node(g, output_node, &args);
|
||||
fprintf(fp, "}\n");
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#ifndef ravicomp_CFG_H
|
||||
#define ravicomp_CFG_H
|
||||
|
||||
#include "linearizer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int raviX_construct_cfg(Proc *proc);
|
||||
void raviX_output_cfg(Proc *proc, FILE *fp);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
#ifndef ravicomp_CODEGEN_H
|
||||
#define ravicomp_CODEGEN_H
|
||||
|
||||
#include "ravi_compiler.h"
|
||||
#include "ravi_api.h"
|
||||
#include "linearizer.h"
|
||||
|
||||
RAVICOMP_EXPORT int raviX_generate_C(LinearizerState *linearizer, TextBuffer *mb, struct Ravi_CompilerInterface *ravi_interface);
|
||||
RAVICOMP_EXPORT void raviX_generate_C_tofile(LinearizerState *linearizer, const char *mainfunc, FILE *fp);
|
||||
|
||||
#endif
|
@ -0,0 +1,14 @@
|
||||
#ifndef ravicomp_COMMON_H
|
||||
#define ravicomp_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t nodeId_t; /* The type used to identify nodes in CFG */
|
||||
|
||||
/* We have two distinguished basic blocks in every proc */
|
||||
enum {
|
||||
ENTRY_BLOCK = 0,
|
||||
EXIT_BLOCK = 1
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* A framework for performing data flow analysis.
|
||||
* The framework is based upon similar framework in MIR project (https://github.com/vnmakarov/mir)
|
||||
*/
|
||||
|
||||
#include "dataflow_framework.h"
|
||||
#include "allocate.h"
|
||||
#include "graph.h"
|
||||
#include "bitset.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
DECLARE_ARRAY(GraphNodeArray, GraphNode *);
|
||||
|
||||
struct dataflow_context {
|
||||
Graph *g;
|
||||
GraphNodeArray worklist;
|
||||
GraphNodeArray pending;
|
||||
BitSet bb_to_consider;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
static void init_data_flow(struct dataflow_context *dataflow_context, Graph *g)
|
||||
{
|
||||
memset(dataflow_context, 0, sizeof *dataflow_context);
|
||||
raviX_bitset_create2(&dataflow_context->bb_to_consider, 512);
|
||||
dataflow_context->g = g;
|
||||
}
|
||||
|
||||
static void finish_data_flow(struct dataflow_context *dataflow_context)
|
||||
{
|
||||
array_clearmem(&dataflow_context->worklist);
|
||||
array_clearmem(&dataflow_context->pending);
|
||||
raviX_bitset_destroy(&dataflow_context->bb_to_consider);
|
||||
}
|
||||
|
||||
void raviX_solve_dataflow(Graph *g, bool forward_p,
|
||||
int (*join_function)(void *, nodeId_t, bool),
|
||||
int (*transfer_function)(void *, nodeId_t), void *userdata)
|
||||
{
|
||||
unsigned iter;
|
||||
struct dataflow_context ctx;
|
||||
GraphNodeArray *worklist;
|
||||
GraphNodeArray *pending;
|
||||
|
||||
init_data_flow(&ctx, g);
|
||||
worklist = &ctx.worklist;
|
||||
pending = &ctx.pending;
|
||||
|
||||
/* ensure that the graph has RPO calculated */
|
||||
raviX_classify_edges(ctx.g);
|
||||
|
||||
worklist->count = 0;
|
||||
/* Initially the basic blocks are added to the worklist */
|
||||
for (uint32_t i = 0; i < raviX_graph_size(ctx.g); i++) {
|
||||
array_push(worklist, raviX_graph_node(ctx.g, i));
|
||||
}
|
||||
iter = 0;
|
||||
while (worklist->count != 0) {
|
||||
GraphNode **addr = worklist->data;
|
||||
raviX_sort_nodes_by_RPO(addr, worklist->count, forward_p);
|
||||
raviX_bitset_clear(&ctx.bb_to_consider);
|
||||
pending->count = 0;
|
||||
for (unsigned i = 0; i < worklist->count; i++) {
|
||||
int changed_p = iter == 0;
|
||||
GraphNode *bb = addr[i];
|
||||
GraphNodeList *nodes = forward_p ? raviX_predecessors(bb) : raviX_successors(bb);
|
||||
// TODO should we pass the nodes array to the join function?
|
||||
if (raviX_node_list_size(nodes) == 0)
|
||||
join_function(ctx.userdata, raviX_node_index(bb), true);
|
||||
else
|
||||
changed_p |= join_function(ctx.userdata, raviX_node_index(bb), false);
|
||||
if (changed_p && transfer_function(ctx.userdata, raviX_node_index(bb))) {
|
||||
GraphNodeList *list = forward_p ? raviX_successors(bb) : raviX_predecessors(bb);
|
||||
for (unsigned i = 0; i < raviX_node_list_size(list); i++) {
|
||||
nodeId_t index = raviX_node_list_at(list, i);
|
||||
/* If this bb is not already been added to pending then add it */
|
||||
if (raviX_bitset_set_bit_p(&ctx.bb_to_consider, index)) {
|
||||
array_push(pending, raviX_graph_node(ctx.g, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iter++;
|
||||
{
|
||||
/* Swap worklist and pending */
|
||||
GraphNodeArray *t = worklist;
|
||||
worklist = pending;
|
||||
pending = t;
|
||||
}
|
||||
}
|
||||
|
||||
finish_data_flow(&ctx);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
#ifndef ravicomp_DATAFLOW_FRAMEWORK_H
|
||||
#define ravicomp_DATAFLOW_FRAMEWORK_H
|
||||
|
||||
#include "graph.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* Data Flow Analysis framework.
|
||||
* The Join/Transfer functions should return 1 if they made any changes else 0.
|
||||
*/
|
||||
extern void raviX_solve_dataflow(
|
||||
Graph *g,
|
||||
bool forward_p, /* Set to true for forward data flow */
|
||||
int (*join_function)(void *userdata, nodeId_t, bool init), /* Join/Meet operator - if init is true reset the bitsets */
|
||||
int (*transfer_function)(void *userdata, nodeId_t), /* transfer function */
|
||||
void *userdata); /* pointer to user data, will be passed to join/transfer functions */
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Calculate variable liveness
|
||||
* This will use the Dataflow Framework.
|
||||
* Implementation inspired by one in MIR
|
||||
*/
|
||||
|
||||
|
||||
#include "bitset.h"
|
||||
#include "dataflow_framework.h"
|
||||
#include "linearizer.h"
|
||||
|
||||
struct liveness_info {
|
||||
nodeId_t node_id;
|
||||
BitSet in;
|
||||
BitSet out;
|
||||
BitSet use;
|
||||
BitSet def;
|
||||
};
|
||||
|
||||
DECLARE_ARRAY(liveness_info_array, struct liveness_info *);
|
||||
|
||||
struct liveness_data {
|
||||
Proc *proc;
|
||||
struct liveness_info_array lives;
|
||||
};
|
||||
|
||||
static void init_liveness_data(Proc *proc, struct liveness_data *liveness_data)
|
||||
{
|
||||
memset(liveness_data, 0, sizeof(*liveness_data));
|
||||
for (unsigned i = 0; i < proc->node_count; i++) {
|
||||
struct liveness_info *liveness_info = (struct liveness_info *)calloc(1, sizeof(struct liveness_info));
|
||||
liveness_info->node_id = i;
|
||||
raviX_bitset_create(&liveness_info->use);
|
||||
raviX_bitset_create(&liveness_info->def);
|
||||
raviX_bitset_create(&liveness_info->in);
|
||||
raviX_bitset_create(&liveness_info->out);
|
||||
array_push(&liveness_data->lives, liveness_info);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_liveness_data(struct liveness_data *liveness_data)
|
||||
{
|
||||
for (unsigned i = 0; i < liveness_data->lives.count; i++) {
|
||||
raviX_bitset_create(&liveness_data->lives.data[i]->use);
|
||||
raviX_bitset_create(&liveness_data->lives.data[i]->def);
|
||||
raviX_bitset_create(&liveness_data->lives.data[i]->in);
|
||||
raviX_bitset_create(&liveness_data->lives.data[i]->out);
|
||||
}
|
||||
array_clearmem(&liveness_data->lives);
|
||||
}
|
||||
|
||||
static inline struct liveness_info *get_liveness_info(struct liveness_data *liveness_data, nodeId_t id)
|
||||
{
|
||||
return liveness_data->lives.data[id];
|
||||
}
|
||||
|
||||
/* Life analysis */
|
||||
static int live_join_func(void *userdata, nodeId_t id, bool init)
|
||||
{
|
||||
struct liveness_data *liveness_data = (struct liveness_data *)userdata;
|
||||
struct liveness_info *liveness_info = get_liveness_info(liveness_data, id);
|
||||
if (init) {
|
||||
raviX_bitset_clear(&liveness_info->in);
|
||||
return 0;
|
||||
} else {
|
||||
GraphNodeList *successors = raviX_successors(raviX_graph_node(liveness_data->proc->cfg, id));
|
||||
int changed = 0;
|
||||
// out[n] = Union of in[s] where s in succ[n]
|
||||
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
|
||||
nodeId_t succ_id = raviX_node_list_at(successors, i);
|
||||
struct liveness_info *successor_liveness_info = get_liveness_info(liveness_data, succ_id);
|
||||
changed |=
|
||||
raviX_bitset_ior(&liveness_info->out, &liveness_info->out, &successor_liveness_info->in);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
static int live_transfer_func(void *userdata, nodeId_t id)
|
||||
{
|
||||
struct liveness_data *liveness_data = (struct liveness_data *)userdata;
|
||||
struct liveness_info *liveness_info = get_liveness_info(liveness_data, id);
|
||||
// out[n] = use[n] U (out[n] - def[n])
|
||||
// In bitset terms out[n] = use[n] | (out[n] & ~def[n])
|
||||
return raviX_bitset_ior_and_compl(&liveness_info->in, &liveness_info->use, &liveness_info->out,
|
||||
&liveness_info->def);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
// Compute use/def sets of each node
|
||||
// If a reg appears as the target of an instruction that's a def
|
||||
// If a reg is used as operand then its a use
|
||||
// Need to handle ranges / var args too
|
||||
// Or should we restrict analysis to certain types of regs?
|
||||
|
||||
// Right now we have disjoint sets for temps / locals - to do this efficiently we need a merged set of regs for each proc
|
||||
// Liveness analysis is a backward data flow problem
|
||||
// see calculate_func_cfg_live_info in mir_genc.c
|
@ -0,0 +1,149 @@
|
||||
#include "dominator.h"
|
||||
|
||||
#include "ravi_compiler.h"
|
||||
#include "graph.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* The dominator tree construction algorithm is based on figure 9.24,
|
||||
* chapter 9, p 532, of Engineering a Compiler.
|
||||
*
|
||||
* The algorithm is also described in the paper 'A Simple, Fast
|
||||
* Dominance Algorithm' by Keith D. Cooper, Timothy J. Harvey and
|
||||
* Ken Kennedy.
|
||||
*/
|
||||
|
||||
/*
|
||||
Some terminology:
|
||||
|
||||
DOM(b): A node n in the CFG dominates b if n lies on every path from the entry node of the CFG to b.
|
||||
DOM9b) contains every node n that dominates b.
|
||||
|
||||
IDOM(b): For a node b, the set IDOM(b) contains exactly one node, the immediate dominator of b.
|
||||
If n is b's immediate dominator then every node in {DOM(b) - b} is also in DOM(n).
|
||||
|
||||
The dominator tree algorithm is an optimised version of forward data flow solver. The
|
||||
algorithm iterates until a fixed point is reached. The output of the algorithm is the IDOM
|
||||
array that describes the dominator tree.
|
||||
*/
|
||||
|
||||
struct dominator_tree {
|
||||
Graph *g;
|
||||
GraphNode **IDOM; /* IDOM[] - array of immediate dominators, one per node in the graph, indexed by node id */
|
||||
uint32_t N; /* sizeof IDOM */
|
||||
};
|
||||
|
||||
struct dominator_tree *raviX_new_dominator_tree(Graph *g)
|
||||
{
|
||||
struct dominator_tree *state = (struct dominator_tree *)calloc(1, sizeof(struct dominator_tree));
|
||||
state->N = raviX_graph_size(g);
|
||||
state->IDOM = (GraphNode **)calloc(state->N, sizeof(GraphNode *));
|
||||
state->g = g;
|
||||
return state;
|
||||
}
|
||||
|
||||
void raviX_destroy_dominator_tree(struct dominator_tree *state)
|
||||
{
|
||||
free(state->IDOM);
|
||||
free(state);
|
||||
}
|
||||
|
||||
/* Finds nearest common ancestor */
|
||||
/* The algorithm starts at the two nodes whose sets are being intersected, and walks
|
||||
* upward from each toward the root. By comparing the nodes with their RPO numbers
|
||||
* the algorithm finds the common ancestor - the immediate dominator of i and j.
|
||||
*/
|
||||
static GraphNode *intersect(struct dominator_tree *state, GraphNode *i, GraphNode *j)
|
||||
{
|
||||
GraphNode *finger1 = i;
|
||||
GraphNode *finger2 = j;
|
||||
while (finger1 != finger2) {
|
||||
while (raviX_node_RPO(finger1) > raviX_node_RPO(finger2)) {
|
||||
finger1 = state->IDOM[raviX_node_index(finger1)];
|
||||
assert(finger1);
|
||||
}
|
||||
while (raviX_node_RPO(finger2) > raviX_node_RPO(finger1)) {
|
||||
finger2 = state->IDOM[raviX_node_index(finger2)];
|
||||
assert(finger2);
|
||||
}
|
||||
}
|
||||
return finger1;
|
||||
}
|
||||
|
||||
/* Look for the first predecessor whose immediate dominator has been calculated.
|
||||
* Because of the order in which this search occurs, we will always find at least 1
|
||||
* such predecessor.
|
||||
*/
|
||||
static GraphNode *find_first_predecessor_with_idom(struct dominator_tree *state, GraphNodeList *predlist)
|
||||
{
|
||||
for (uint32_t i = 0; i < raviX_node_list_size(predlist); i++) {
|
||||
nodeId_t id = raviX_node_list_at(predlist, i);
|
||||
if (state->IDOM[id])
|
||||
return raviX_graph_node(state->g, id);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dominator tree.
|
||||
* Before this is called the graph links should have been numbered in
|
||||
* reverse post order.
|
||||
*/
|
||||
void raviX_calculate_dominator_tree(struct dominator_tree *state)
|
||||
{
|
||||
/*
|
||||
Some implementation details:
|
||||
The graph and links reference nodes by node ids.
|
||||
However the IDOM array references the node objects - i.e.
|
||||
pointers to 'GraphNode'. So we have some conversion from node id
|
||||
to the node, and vice versa at various points.
|
||||
*/
|
||||
|
||||
uint32_t N = raviX_graph_size(state->g);
|
||||
GraphNode **nodes_in_reverse_postorder = raviX_graph_nodes_sorted_by_RPO(state->g, false);
|
||||
for (uint32_t i = 0; i < state->N; i++) {
|
||||
state->IDOM[i] = NULL; /* undefined - set to a invalid value */
|
||||
}
|
||||
// Set IDom entry for root to itself
|
||||
state->IDOM[ENTRY_BLOCK] = raviX_graph_node(state->g, ENTRY_BLOCK);
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
// for all nodes, b, in reverse postorder (except root)
|
||||
for (uint32_t i = 0; i < N; i++) {
|
||||
GraphNode *b = nodes_in_reverse_postorder[i];
|
||||
nodeId_t bid = raviX_node_index(b);
|
||||
if (bid == ENTRY_BLOCK) // skip root
|
||||
continue;
|
||||
GraphNodeList *predecessors = raviX_predecessors(b); // Predecessors of b
|
||||
// NewIDom = first (processed) predecessor of b, pick one
|
||||
GraphNode *firstpred = find_first_predecessor_with_idom(state, predecessors);
|
||||
assert(firstpred != NULL);
|
||||
GraphNode *NewIDom = firstpred;
|
||||
// for all other predecessors, p, of b
|
||||
for (uint32_t k = 0; k < raviX_node_list_size(predecessors); k++) {
|
||||
nodeId_t pid = raviX_node_list_at(predecessors, k);
|
||||
GraphNode *p = raviX_graph_node(state->g, pid);
|
||||
if (p == firstpred)
|
||||
continue; // all other predecessors
|
||||
if (state->IDOM[raviX_node_index(p)] != NULL) {
|
||||
// i.e. IDoms[p] calculated
|
||||
NewIDom = intersect(state, p, NewIDom);
|
||||
}
|
||||
}
|
||||
if (state->IDOM[bid] != NewIDom) {
|
||||
state->IDOM[bid] = NewIDom;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(nodes_in_reverse_postorder);
|
||||
}
|
||||
|
||||
void raviX_dominator_tree_output(struct dominator_tree *tree, FILE *fp)
|
||||
{
|
||||
for (uint32_t i = 0; i < tree->N; i++) {
|
||||
fprintf(stdout, "IDOM[%d] = %d\n", i, raviX_node_index(tree->IDOM[i]));
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef ravicomp_DOMINATOR_H
|
||||
#define ravicomp_DOMINATOR_H
|
||||
|
||||
#include "graph.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct dominator_tree;
|
||||
|
||||
struct dominator_tree *raviX_new_dominator_tree(Graph *g);
|
||||
void raviX_calculate_dominator_tree(struct dominator_tree *state);
|
||||
void raviX_destroy_dominator_tree(struct dominator_tree *state);
|
||||
void raviX_dominator_tree_output(struct dominator_tree *tree, FILE *fp);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
/* Quick FNV-1a hash implementation based on:
|
||||
* http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
*
|
||||
* FNV-1a may not be the best hash out there -- Jenkins's lookup3 is supposed
|
||||
* to be quite good, and it may beat FNV. But FNV has the advantage that it
|
||||
* involves almost no code.
|
||||
*/
|
||||
#include <fnv_hash.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
uint32_t
|
||||
fnv1_hash_string(const char *key)
|
||||
{
|
||||
uint32_t hash = 2166136261ul;
|
||||
const uint8_t *bytes = (uint8_t *)key;
|
||||
|
||||
while (*bytes != 0) {
|
||||
hash ^= *bytes;
|
||||
hash = hash * 0x01000193;
|
||||
bytes++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
fnv1_hash_data(const void *data, size_t size)
|
||||
{
|
||||
uint32_t hash = 2166136261ul;
|
||||
const uint8_t *bytes = (uint8_t *)data;
|
||||
|
||||
while (size-- != 0) {
|
||||
hash ^= *bytes;
|
||||
hash = hash * 0x01000193;
|
||||
bytes++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int
|
||||
string_key_equals(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
* Copyright © 2014 Broadcom
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
/* Quick FNV-1 hash implementation based on:
|
||||
* http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
*/
|
||||
|
||||
#ifndef ravicomp_FNV_HASH_H
|
||||
#define ravicomp_FNV_HASH_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
uint32_t fnv1_hash_string(const char *key);
|
||||
uint32_t fnv1_hash_data(const void *data, size_t size);
|
||||
|
||||
int string_key_equals(const void *a, const void *b);
|
||||
|
||||
#define hash_table_create_for_string() \
|
||||
raviX_hash_table_create((uint32_t (*)(const void *key))fnv1_hash_string, \
|
||||
string_key_equals)
|
||||
|
||||
#define set_create_for_string() \
|
||||
set_create((uint32_t (*)(const void *key))fnv1_hash_string, \
|
||||
string_key_equals)
|
||||
|
||||
#endif
|
@ -0,0 +1,389 @@
|
||||
#include "graph.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct Graph {
|
||||
unsigned allocated; /* tracks allocated size of nodes */
|
||||
GraphNode **nodes; /* array[allocated] indexed by nodeId_t, note user must check if nodes[i] != NULL */
|
||||
Allocator node_allocator;
|
||||
nodeId_t entry, exit; /* entry and exit nodes */
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
struct GraphNodeLink {
|
||||
nodeId_t node_index;
|
||||
unsigned char edge_type;
|
||||
};
|
||||
/* A node_list is simply a dynamic array/vector of node ids.
|
||||
*/
|
||||
struct GraphNodeList {
|
||||
unsigned count : 16; /* in use */
|
||||
unsigned allocated : 16; /* tracks allocated size of links array */
|
||||
struct GraphNodeLink *links; /* array[allocated] of links, populated [0..count) */
|
||||
};
|
||||
|
||||
/* A node in the graph. For each node we maintain a list of predecessor nodes and successor nodes.
|
||||
*/
|
||||
struct GraphNode {
|
||||
nodeId_t index; /* the id of the basic_block */
|
||||
uint32_t pre; /* preorder */
|
||||
uint32_t rpost; /* reverse postorder */
|
||||
GraphNodeList preds; /* predecessor nodes */
|
||||
GraphNodeList succs; /* successor nodes */
|
||||
};
|
||||
|
||||
static GraphNode *raviX_add_node(Graph *g, nodeId_t index);
|
||||
|
||||
static void node_list_init(GraphNodeList *node_list)
|
||||
{
|
||||
node_list->count = 0;
|
||||
node_list->allocated = 0;
|
||||
node_list->links = NULL;
|
||||
}
|
||||
|
||||
static void node_list_destroy(GraphNodeList *node_list)
|
||||
{
|
||||
node_list->count = 0;
|
||||
node_list->allocated = 0;
|
||||
free(node_list->links);
|
||||
node_list->links = NULL;
|
||||
}
|
||||
|
||||
/* Gets the offset of the node or -1 if not found */
|
||||
static int64_t node_list_search(const GraphNodeList *node_list, nodeId_t index)
|
||||
{
|
||||
for (unsigned i = 0; i < node_list->count; i++) {
|
||||
if (node_list->links[i].node_index == index) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Gets the given node or NULL if node does not exist */
|
||||
static inline struct GraphNodeLink *node_list_get(const GraphNodeList *node_list, nodeId_t index)
|
||||
{
|
||||
int64_t i = node_list_search(node_list, index);
|
||||
if (i < 0)
|
||||
return NULL;
|
||||
return &node_list->links[i];
|
||||
}
|
||||
|
||||
/* Grows the node list array */
|
||||
static void node_list_grow(GraphNodeList *node_list)
|
||||
{
|
||||
unsigned new_size = node_list->allocated + 8u;
|
||||
struct GraphNodeLink *edges = raviX_realloc_array(node_list->links, sizeof(struct GraphNodeLink), node_list->allocated,
|
||||
new_size);
|
||||
node_list->allocated = new_size;
|
||||
node_list->links = edges;
|
||||
}
|
||||
|
||||
/* add an node to the node_list if not already added */
|
||||
static void node_list_add(GraphNodeList *node_list, nodeId_t index)
|
||||
{
|
||||
if (node_list_search(node_list, index) != -1)
|
||||
return;
|
||||
if (node_list->count >= node_list->allocated) {
|
||||
node_list_grow(node_list);
|
||||
}
|
||||
assert(node_list->count < node_list->allocated);
|
||||
node_list->links[node_list->count].node_index = index;
|
||||
assert(node_list->links[node_list->count].edge_type == 0);
|
||||
|
||||
node_list->count++;
|
||||
}
|
||||
|
||||
/* delete an node from the node_list if it exists */
|
||||
static void node_list_delete(GraphNodeList *node_list, nodeId_t index)
|
||||
{
|
||||
int64_t i = node_list_search(node_list, index);
|
||||
if (i < 0)
|
||||
return;
|
||||
node_list->count = (unsigned) raviX_del_array_element(node_list->links, sizeof node_list->links[0], node_list->count, i, 1);
|
||||
}
|
||||
|
||||
uint32_t raviX_node_list_size(GraphNodeList *list) { return list->count; }
|
||||
|
||||
nodeId_t raviX_node_list_at(GraphNodeList *list, uint32_t i)
|
||||
{
|
||||
if (i < list->count)
|
||||
return list->links[i].node_index;
|
||||
assert(false);
|
||||
return (nodeId_t)-1;
|
||||
}
|
||||
|
||||
Graph *raviX_init_graph(nodeId_t entry, nodeId_t exit, void *userdata)
|
||||
{
|
||||
Graph *g = (Graph *)calloc(1, sizeof(Graph));
|
||||
g->allocated = 0;
|
||||
g->nodes = NULL;
|
||||
raviX_allocator_init(&g->node_allocator, "node_allocator", sizeof(GraphNode), sizeof(double),
|
||||
sizeof(GraphNode) * 32);
|
||||
raviX_add_node(g, entry);
|
||||
raviX_add_node(g, exit);
|
||||
g->entry = entry;
|
||||
g->exit = exit;
|
||||
g->userdata = userdata;
|
||||
return g;
|
||||
}
|
||||
|
||||
static void raviX_destroy_node(GraphNode *n)
|
||||
{
|
||||
if (n == NULL)
|
||||
return;
|
||||
node_list_destroy(&n->preds);
|
||||
node_list_destroy(&n->succs);
|
||||
}
|
||||
|
||||
void raviX_destroy_graph(Graph *g)
|
||||
{
|
||||
for (unsigned i = 0; i < g->allocated; i++) {
|
||||
raviX_destroy_node(g->nodes[i]);
|
||||
}
|
||||
raviX_allocator_destroy(&g->node_allocator);
|
||||
free(g->nodes);
|
||||
free(g);
|
||||
}
|
||||
|
||||
static GraphNode *raviX_get_node(const Graph *g, nodeId_t index)
|
||||
{
|
||||
if (index < g->allocated && g->nodes[index] != NULL) {
|
||||
// already allocated
|
||||
return g->nodes[index];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void raviX_graph_grow(Graph *g, nodeId_t needed)
|
||||
{
|
||||
unsigned new_size = needed + 8;
|
||||
GraphNode **new_data =
|
||||
raviX_realloc_array(g->nodes, sizeof(GraphNode*), g->allocated, new_size);
|
||||
g->allocated = new_size;
|
||||
g->nodes = new_data;
|
||||
}
|
||||
|
||||
static GraphNode *raviX_add_node(Graph *g, nodeId_t index)
|
||||
{
|
||||
if (index < g->allocated && g->nodes[index] != NULL) {
|
||||
// already allocated
|
||||
return g->nodes[index];
|
||||
}
|
||||
if (index >= g->allocated) {
|
||||
raviX_graph_grow(g, index);
|
||||
}
|
||||
assert(index < g->allocated);
|
||||
GraphNode *n = raviX_allocator_allocate(&g->node_allocator, 0);
|
||||
assert(n->pre == 0);
|
||||
assert(n->rpost == 0);
|
||||
node_list_init(&n->preds);
|
||||
node_list_init(&n->succs);
|
||||
/* note that each node must have an index such that n = nodes[index] */
|
||||
n->index = index;
|
||||
g->nodes[index] = n;
|
||||
return n;
|
||||
}
|
||||
|
||||
void raviX_add_edge(Graph *g, nodeId_t from, nodeId_t to)
|
||||
{
|
||||
GraphNode *prednode = raviX_add_node(g, from);
|
||||
GraphNode *succnode = raviX_add_node(g, to);
|
||||
|
||||
node_list_add(&prednode->succs, to);
|
||||
node_list_add(&succnode->preds, from);
|
||||
}
|
||||
|
||||
void raviX_delete_edge(Graph *g, nodeId_t a, nodeId_t b)
|
||||
{
|
||||
GraphNodeList *successors_of_a = raviX_successors(raviX_graph_node(g, a));
|
||||
GraphNodeList *predecessors_of_b = raviX_predecessors(raviX_graph_node(g, b));
|
||||
|
||||
assert(successors_of_a);
|
||||
assert(predecessors_of_b);
|
||||
|
||||
if (successors_of_a == NULL || predecessors_of_b == NULL)
|
||||
return;
|
||||
|
||||
node_list_delete(successors_of_a, b);
|
||||
node_list_delete(predecessors_of_b, a);
|
||||
}
|
||||
|
||||
bool raviX_has_edge(Graph *g, nodeId_t from, nodeId_t to)
|
||||
{
|
||||
GraphNode *prednode = raviX_get_node(g, from);
|
||||
if (prednode == NULL)
|
||||
return false;
|
||||
return node_list_search(&prednode->succs, to) != -1;
|
||||
}
|
||||
|
||||
enum EdgeType raviX_get_edge_type(Graph *g, nodeId_t from, nodeId_t to)
|
||||
{
|
||||
GraphNode *prednode = raviX_get_node(g, from);
|
||||
if (prednode == NULL)
|
||||
return EDGE_TYPE_UNCLASSIFIED;
|
||||
struct GraphNodeLink *node_link = node_list_get(&prednode->succs, to);
|
||||
if (node_link == NULL)
|
||||
return EDGE_TYPE_UNCLASSIFIED;
|
||||
return node_link->edge_type;
|
||||
}
|
||||
|
||||
void raviX_for_each_node(Graph *g, void (*callback)(void *arg, Graph *g, nodeId_t nodeid), void *arg)
|
||||
{
|
||||
for (unsigned i = 0; i < g->allocated; i++) {
|
||||
if (g->nodes[i] != NULL) {
|
||||
callback(arg, g, g->nodes[i]->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* says how many nodes are in the graph */
|
||||
uint32_t raviX_graph_size(Graph *g)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
for (unsigned i = 0; i < g->allocated; i++) {
|
||||
if (g->nodes[i] != NULL) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t raviX_node_RPO(GraphNode *n)
|
||||
{
|
||||
assert(n);
|
||||
return n->rpost;
|
||||
}
|
||||
nodeId_t raviX_node_index(GraphNode *n)
|
||||
{
|
||||
assert(n);
|
||||
return n->index;
|
||||
}
|
||||
GraphNode *raviX_graph_node(Graph *g, nodeId_t index)
|
||||
{
|
||||
assert(index < g->allocated);
|
||||
return g->nodes[index];
|
||||
}
|
||||
GraphNodeList *raviX_predecessors(GraphNode *n)
|
||||
{
|
||||
assert(n);
|
||||
return &n->preds;
|
||||
}
|
||||
GraphNodeList *raviX_successors(GraphNode *n)
|
||||
{
|
||||
assert(n);
|
||||
return &n->succs;
|
||||
}
|
||||
|
||||
struct classifier_state {
|
||||
uint32_t preorder;
|
||||
uint32_t rpostorder;
|
||||
};
|
||||
|
||||
/*
|
||||
* Do a recursive depth first search and mark nodes with pre/reverse post order sequence, as well
|
||||
* as classify links. Algorithm from figure 3.2 in Building and Optimizing Compiler
|
||||
*/
|
||||
static void DFS_classify(Graph *g, GraphNode *n, struct classifier_state *state)
|
||||
{
|
||||
assert(n);
|
||||
|
||||
n->pre = state->preorder;
|
||||
state->preorder++;
|
||||
|
||||
/* For each successor node */
|
||||
for (unsigned i = 0; i < n->succs.count; i++) {
|
||||
struct GraphNodeLink *E = &n->succs.links[i];
|
||||
GraphNode *S = g->nodes[E->node_index];
|
||||
if (S->pre == 0) {
|
||||
E->edge_type = EDGE_TYPE_TREE;
|
||||
DFS_classify(g, S, state);
|
||||
} else if (S->rpost == 0) {
|
||||
E->edge_type = EDGE_TYPE_BACKWARD;
|
||||
} else if (n->pre < S->pre) {
|
||||
E->edge_type = EDGE_TYPE_FORWARD;
|
||||
} else {
|
||||
E->edge_type = EDGE_TYPE_CROSS;
|
||||
}
|
||||
}
|
||||
|
||||
n->rpost = state->rpostorder;
|
||||
state->rpostorder--;
|
||||
}
|
||||
|
||||
/*
|
||||
Classify links in the graph. Implements algorithm described in
|
||||
figure 3.2 - Building an Optimizing Compiler. This algorithm is also implemented
|
||||
in MIR.
|
||||
*/
|
||||
void raviX_classify_edges(Graph *g)
|
||||
{
|
||||
uint32_t N = raviX_graph_size(g);
|
||||
if (N == 0)
|
||||
return;
|
||||
|
||||
struct classifier_state state = {.preorder = 1, .rpostorder = N};
|
||||
|
||||
/* reset all data we will be computing */
|
||||
for (unsigned i = 0; i < g->allocated; i++) {
|
||||
if (g->nodes[i] != NULL) {
|
||||
g->nodes[i]->pre = 0;
|
||||
g->nodes[i]->rpost = 0;
|
||||
for (unsigned i = 0; i < g->nodes[i]->succs.count; i++) {
|
||||
struct GraphNodeLink *E = &g->nodes[i]->succs.links[i];
|
||||
E->edge_type = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DFS_classify(g, g->nodes[g->entry], &state);
|
||||
}
|
||||
|
||||
static int rpost_cmp(const void *a1, const void *a2)
|
||||
{
|
||||
const GraphNode *n1 = *((const GraphNode **)a1);
|
||||
const GraphNode *n2 = *((const GraphNode **)a2);
|
||||
int result = n1->rpost - n2->rpost;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int post_cmp(const void *a1, const void *a2) { return -rpost_cmp(a1, a2); }
|
||||
|
||||
void raviX_sort_nodes_by_RPO(GraphNode **nodes, size_t count, bool forward)
|
||||
{
|
||||
qsort(nodes, count, sizeof(GraphNode *), forward ? post_cmp : rpost_cmp);
|
||||
}
|
||||
|
||||
GraphNode **raviX_graph_nodes_sorted_by_RPO(Graph *g, bool forward)
|
||||
{
|
||||
uint32_t N = raviX_graph_size(g);
|
||||
GraphNode **nodes = calloc(N, sizeof(GraphNode *));
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < g->allocated; i++) {
|
||||
if (g->nodes[i] == NULL)
|
||||
continue;
|
||||
nodes[j++] = g->nodes[i];
|
||||
}
|
||||
assert(j == N);
|
||||
raviX_sort_nodes_by_RPO(nodes, N, forward);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static void draw_node(void *arg, Graph *g, uint32_t nodeid)
|
||||
{
|
||||
FILE *fp = (FILE *)arg;
|
||||
GraphNodeList *successors = raviX_successors(raviX_graph_node(g, nodeid));
|
||||
if (!successors)
|
||||
return;
|
||||
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
|
||||
fprintf(fp, "L%d -> L%d\n", nodeid, raviX_node_list_at(successors, i));
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_draw_graph(Graph *g, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "digraph {\n");
|
||||
raviX_for_each_node(g, draw_node, fp);
|
||||
fprintf(fp, "}\n");
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
#ifndef ravicomp_GRAPH_H
|
||||
#define ravicomp_GRAPH_H
|
||||
|
||||
#include "allocate.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* Various graph manipulation routines.
|
||||
* The graph is designed to manage nodes that are just integer ids.
|
||||
* Node ids range from [0..n) - hence one can simply represent nodes as arrays.
|
||||
*
|
||||
* The graph structure does not care what the node represents and
|
||||
* knows nothing about it. The benefit of this approach is that we can make
|
||||
* the graph algorithms reusable. There may be some performance cost as we
|
||||
* need to map node ids to nodes.
|
||||
*
|
||||
* The assumption here is that each node corresponds to a basic block in
|
||||
* the program intermediate code. And each basic block is identified by a node
|
||||
* id which can be used to construct the control flow graph.
|
||||
*/
|
||||
|
||||
/* nodeId_t is declared elsewhere */
|
||||
typedef struct Graph Graph;
|
||||
typedef struct GraphNode GraphNode;
|
||||
typedef struct GraphNodeList GraphNodeList;
|
||||
enum EdgeType {
|
||||
EDGE_TYPE_UNCLASSIFIED = 0,
|
||||
EDGE_TYPE_TREE = 1,
|
||||
EDGE_TYPE_BACKWARD = 2,
|
||||
EDGE_TYPE_FORWARD = 4,
|
||||
EDGE_TYPE_CROSS = 8
|
||||
};
|
||||
|
||||
|
||||
/* Initialize the graph data structure and associate some userdata with it. */
|
||||
Graph *raviX_init_graph(nodeId_t entry, nodeId_t exit, void *userdata);
|
||||
/* Destroy the graph data structure */
|
||||
void raviX_destroy_graph(Graph *g);
|
||||
|
||||
/* Add an edge from one node a to b. Both nodes a and b will be implicitly added
|
||||
* to the graph if they do not already exist.
|
||||
*/
|
||||
void raviX_add_edge(Graph *g, nodeId_t a, nodeId_t b);
|
||||
/* Check if an edge exists from one node a to b */
|
||||
bool raviX_has_edge(Graph *g, nodeId_t a, nodeId_t b);
|
||||
/* Delete an edge from a to b */
|
||||
void raviX_delete_edge(Graph *g, nodeId_t a, nodeId_t b);
|
||||
/* Get the edge classification for edge from a to b; this is only available if graph has been
|
||||
* analyzed for edges. */
|
||||
enum EdgeType raviX_get_edge_type(Graph *g, nodeId_t a, nodeId_t b);
|
||||
|
||||
/* Get node identified by index */
|
||||
GraphNode *raviX_graph_node(Graph *g, nodeId_t index);
|
||||
/* Get the RPO - reverse post order index of the node */
|
||||
uint32_t raviX_node_RPO(GraphNode *n);
|
||||
/* Get the node's id */
|
||||
nodeId_t raviX_node_index(GraphNode *n);
|
||||
/* Get list of predecessors */
|
||||
GraphNodeList *raviX_predecessors(GraphNode *n);
|
||||
/* Get list of successors */
|
||||
GraphNodeList *raviX_successors(GraphNode *n);
|
||||
|
||||
/* Number of entries in the node_list */
|
||||
uint32_t raviX_node_list_size(GraphNodeList *list);
|
||||
/* Get the nodeId at given node_link position */
|
||||
nodeId_t raviX_node_list_at(GraphNodeList *list, uint32_t i);
|
||||
|
||||
void raviX_for_each_node(Graph *g, void (*callback)(void *arg, Graph *g, nodeId_t nodeid), void *arg);
|
||||
|
||||
/*
|
||||
* Classifies links in the graph and also computes the
|
||||
* reverse post order value.
|
||||
*/
|
||||
void raviX_classify_edges(Graph *g);
|
||||
/*
|
||||
* Returns a sorted array (allocated).
|
||||
* Sorted by reverse postorder value.
|
||||
* If forward=true then
|
||||
* it will be the opposite direction, so to get reverse postorder,
|
||||
* set forward=false.
|
||||
* You must deallocate the array when done.
|
||||
* The array size will be equal to raviX_graph_size(g).
|
||||
* Before attempting to sort, you must have called
|
||||
* raviX_classify_edges(g).
|
||||
*/
|
||||
GraphNode **raviX_graph_nodes_sorted_by_RPO(Graph *g, bool forward);
|
||||
|
||||
void raviX_sort_nodes_by_RPO(GraphNode **nodes, size_t count, bool forward);
|
||||
|
||||
/* says how many nodes are in the graph */
|
||||
uint32_t raviX_graph_size(Graph *g);
|
||||
/* Generates GraphViz (dot) output */
|
||||
void raviX_draw_graph(Graph *g, FILE *fp);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
* Copyright © 1988-2004 Keith Packard and Bart Massey.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the names of the authors
|
||||
* or their institutions shall not be used in advertising or
|
||||
* otherwise to promote the sale, use or other dealings in this
|
||||
* Software without prior written authorization from the
|
||||
* authors.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Keith Packard <keithp@keithp.com>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <hash_table.h>
|
||||
|
||||
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
|
||||
|
||||
/*
|
||||
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
|
||||
* p and p-2 are both prime. These tables are sized to have an extra 10%
|
||||
* free to avoid exponential performance degradation as the hash table fills
|
||||
*/
|
||||
|
||||
static const uint32_t deleted_key_value;
|
||||
static const void *deleted_key = &deleted_key_value;
|
||||
|
||||
static const struct {
|
||||
uint32_t max_entries, size, rehash;
|
||||
} hash_sizes[] = {
|
||||
{ 2, 5, 3 },
|
||||
{ 4, 7, 5 },
|
||||
{ 8, 13, 11 },
|
||||
{ 16, 19, 17 },
|
||||
{ 32, 43, 41 },
|
||||
{ 64, 73, 71 },
|
||||
{ 128, 151, 149 },
|
||||
{ 256, 283, 281 },
|
||||
{ 512, 571, 569 },
|
||||
{ 1024, 1153, 1151 },
|
||||
{ 2048, 2269, 2267 },
|
||||
{ 4096, 4519, 4517 },
|
||||
{ 8192, 9013, 9011 },
|
||||
{ 16384, 18043, 18041 },
|
||||
{ 32768, 36109, 36107 },
|
||||
{ 65536, 72091, 72089 },
|
||||
{ 131072, 144409, 144407 },
|
||||
{ 262144, 288361, 288359 },
|
||||
{ 524288, 576883, 576881 },
|
||||
{ 1048576, 1153459, 1153457 },
|
||||
{ 2097152, 2307163, 2307161 },
|
||||
{ 4194304, 4613893, 4613891 },
|
||||
{ 8388608, 9227641, 9227639 },
|
||||
{ 16777216, 18455029, 18455027 },
|
||||
{ 33554432, 36911011, 36911009 },
|
||||
{ 67108864, 73819861, 73819859 },
|
||||
{ 134217728, 147639589, 147639587 },
|
||||
{ 268435456, 295279081, 295279079 },
|
||||
{ 536870912, 590559793, 590559791 },
|
||||
{ 1073741824, 1181116273, 1181116271},
|
||||
{ 2147483648ul, 2362232233ul, 2362232231ul}
|
||||
};
|
||||
|
||||
static int
|
||||
entry_is_free(const HashEntry *entry)
|
||||
{
|
||||
return entry->key == NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_deleted(const HashEntry *entry)
|
||||
{
|
||||
return entry->key == deleted_key;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_present(const HashEntry *entry)
|
||||
{
|
||||
return entry->key != NULL && entry->key != deleted_key;
|
||||
}
|
||||
|
||||
HashTable *
|
||||
raviX_hash_table_create(uint32_t (*hash_function)(const void *key),
|
||||
int (*key_equals_function)(const void *a,
|
||||
const void *b))
|
||||
{
|
||||
HashTable *ht;
|
||||
|
||||
ht = malloc(sizeof(*ht));
|
||||
if (ht == NULL)
|
||||
return NULL;
|
||||
|
||||
ht->size_index = 0;
|
||||
ht->size = hash_sizes[ht->size_index].size;
|
||||
ht->rehash = hash_sizes[ht->size_index].rehash;
|
||||
ht->max_entries = hash_sizes[ht->size_index].max_entries;
|
||||
ht->hash_function = hash_function;
|
||||
ht->key_equals_function = key_equals_function;
|
||||
ht->table = calloc(ht->size, sizeof(*ht->table));
|
||||
ht->entries = 0;
|
||||
ht->deleted_entries = 0;
|
||||
|
||||
if (ht->table == NULL) {
|
||||
free(ht);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the given hash table.
|
||||
*
|
||||
* If delete_function is passed, it gets called on each entry present before
|
||||
* freeing.
|
||||
*/
|
||||
void
|
||||
raviX_hash_table_destroy(HashTable *ht,
|
||||
void (*delete_function)(HashEntry *entry))
|
||||
{
|
||||
if (!ht)
|
||||
return;
|
||||
|
||||
if (delete_function) {
|
||||
HashEntry *entry;
|
||||
|
||||
hash_table_foreach(ht, entry) {
|
||||
delete_function(entry);
|
||||
}
|
||||
}
|
||||
free(ht->table);
|
||||
free(ht);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a hash table entry with the given key.
|
||||
*
|
||||
* Returns NULL if no entry is found. Note that the data pointer may be
|
||||
* modified by the user.
|
||||
*/
|
||||
HashEntry *
|
||||
raviX_hash_table_search(HashTable *ht, const void *key)
|
||||
{
|
||||
uint32_t hash = ht->hash_function(key);
|
||||
|
||||
return raviX_hash_table_search_pre_hashed(ht, hash, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a hash table entry with the given key and hash of that key.
|
||||
*
|
||||
* Returns NULL if no entry is found. Note that the data pointer may be
|
||||
* modified by the user.
|
||||
*/
|
||||
HashEntry *
|
||||
raviX_hash_table_search_pre_hashed(HashTable *ht, uint32_t hash,
|
||||
const void *key)
|
||||
{
|
||||
uint32_t start_hash_address = hash % ht->size;
|
||||
uint32_t hash_address = start_hash_address;
|
||||
|
||||
do {
|
||||
uint32_t double_hash;
|
||||
|
||||
HashEntry *entry = ht->table + hash_address;
|
||||
|
||||
if (entry_is_free(entry)) {
|
||||
return NULL;
|
||||
} else if (entry_is_present(entry) && entry->hash == hash) {
|
||||
if (ht->key_equals_function(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
double_hash = 1 + hash % ht->rehash;
|
||||
|
||||
hash_address = (hash_address + double_hash) % ht->size;
|
||||
} while (hash_address != start_hash_address);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
hash_table_rehash(HashTable *ht, int new_size_index)
|
||||
{
|
||||
HashTable old_ht;
|
||||
HashEntry *table, *entry;
|
||||
|
||||
if (new_size_index >= ARRAY_SIZE(hash_sizes))
|
||||
return;
|
||||
|
||||
table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table));
|
||||
if (table == NULL)
|
||||
return;
|
||||
|
||||
old_ht = *ht;
|
||||
|
||||
ht->table = table;
|
||||
ht->size_index = new_size_index;
|
||||
ht->size = hash_sizes[ht->size_index].size;
|
||||
ht->rehash = hash_sizes[ht->size_index].rehash;
|
||||
ht->max_entries = hash_sizes[ht->size_index].max_entries;
|
||||
ht->entries = 0;
|
||||
ht->deleted_entries = 0;
|
||||
|
||||
hash_table_foreach(&old_ht, entry) {
|
||||
raviX_hash_table_insert_pre_hashed(ht, entry->hash,
|
||||
entry->key, entry->data);
|
||||
}
|
||||
|
||||
free(old_ht.table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the key into the table.
|
||||
*
|
||||
* Note that insertion may rearrange the table on a resize or rehash,
|
||||
* so previously found hash_entries are no longer valid after this function.
|
||||
*/
|
||||
HashEntry *
|
||||
raviX_hash_table_insert(HashTable *ht, const void *key, void *data)
|
||||
{
|
||||
uint32_t hash = ht->hash_function(key);
|
||||
|
||||
/* Make sure nobody tries to add one of the magic values as a
|
||||
* key. If you need to do so, either do so in a wrapper, or
|
||||
* store keys with the magic values separately in the struct
|
||||
* hash_table.
|
||||
*/
|
||||
assert(key != NULL);
|
||||
|
||||
return raviX_hash_table_insert_pre_hashed(ht, hash, key, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the key with the given hash into the table.
|
||||
*
|
||||
* Note that insertion may rearrange the table on a resize or rehash,
|
||||
* so previously found hash_entries are no longer valid after this function.
|
||||
*/
|
||||
HashEntry *
|
||||
raviX_hash_table_insert_pre_hashed(HashTable *ht, uint32_t hash,
|
||||
const void *key, void *data)
|
||||
{
|
||||
uint32_t start_hash_address, hash_address;
|
||||
HashEntry *available_entry = NULL;
|
||||
|
||||
if (ht->entries >= ht->max_entries) {
|
||||
hash_table_rehash(ht, ht->size_index + 1);
|
||||
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
|
||||
hash_table_rehash(ht, ht->size_index);
|
||||
}
|
||||
|
||||
start_hash_address = hash % ht->size;
|
||||
hash_address = start_hash_address;
|
||||
do {
|
||||
HashEntry *entry = ht->table + hash_address;
|
||||
uint32_t double_hash;
|
||||
|
||||
if (!entry_is_present(entry)) {
|
||||
/* Stash the first available entry we find */
|
||||
if (available_entry == NULL)
|
||||
available_entry = entry;
|
||||
if (entry_is_free(entry))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Implement replacement when another insert happens
|
||||
* with a matching key. This is a relatively common
|
||||
* feature of hash tables, with the alternative
|
||||
* generally being "insert the new value as well, and
|
||||
* return it first when the key is searched for".
|
||||
*
|
||||
* Note that the hash table doesn't have a delete
|
||||
* callback. If freeing of old data pointers is
|
||||
* required to avoid memory leaks, perform a search
|
||||
* before inserting.
|
||||
*/
|
||||
if (!entry_is_deleted(entry) &&
|
||||
entry->hash == hash &&
|
||||
ht->key_equals_function(key, entry->key)) {
|
||||
entry->key = key;
|
||||
entry->data = data;
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
double_hash = 1 + hash % ht->rehash;
|
||||
|
||||
hash_address = (hash_address + double_hash) % ht->size;
|
||||
} while (hash_address != start_hash_address);
|
||||
|
||||
if (available_entry) {
|
||||
if (entry_is_deleted(available_entry))
|
||||
ht->deleted_entries--;
|
||||
available_entry->hash = hash;
|
||||
available_entry->key = key;
|
||||
available_entry->data = data;
|
||||
ht->entries++;
|
||||
return available_entry;
|
||||
}
|
||||
|
||||
/* We could hit here if a required resize failed. An unchecked-malloc
|
||||
* application could ignore this result.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function searches for, and removes an entry from the hash table.
|
||||
*
|
||||
* If the caller has previously found a HashEntry pointer,
|
||||
* (from calling hash_table_search or remembering it from
|
||||
* hash_table_insert), then hash_table_remove_entry can be called
|
||||
* instead to avoid an extra search.
|
||||
*/
|
||||
void
|
||||
raviX_hash_table_remove(HashTable *ht, const void *key)
|
||||
{
|
||||
HashEntry *entry;
|
||||
|
||||
entry = raviX_hash_table_search(ht, key);
|
||||
|
||||
raviX_hash_table_remove_entry(ht, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function deletes the given hash table entry.
|
||||
*
|
||||
* Note that deletion doesn't otherwise modify the table, so an iteration over
|
||||
* the table deleting entries is safe.
|
||||
*/
|
||||
void
|
||||
raviX_hash_table_remove_entry(HashTable *ht, HashEntry *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->key = deleted_key;
|
||||
ht->entries--;
|
||||
ht->deleted_entries++;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is an iterator over the hash table.
|
||||
*
|
||||
* Pass in NULL for the first entry, as in the start of a for loop. Note that
|
||||
* an iteration over the table is O(table_size) not O(entries).
|
||||
*/
|
||||
HashEntry *
|
||||
raviX_hash_table_next_entry(HashTable *ht, HashEntry *entry)
|
||||
{
|
||||
if (entry == NULL)
|
||||
entry = ht->table;
|
||||
else
|
||||
entry = entry + 1;
|
||||
|
||||
for (; entry != ht->table + ht->size; entry++) {
|
||||
if (entry_is_present(entry)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Returns a random entry from the hash table.
|
||||
*
|
||||
* This may be useful in implementing random replacement (as opposed
|
||||
* to just removing everything) in caches based on this hash table
|
||||
* implementation. @predicate may be used to filter entries, or may
|
||||
* be set to NULL for no filtering.
|
||||
*/
|
||||
HashEntry *
|
||||
hash_table_random_entry(HashTable *ht,
|
||||
int (*predicate)(HashEntry *entry))
|
||||
{
|
||||
HashEntry *entry;
|
||||
uint32_t i = random() % ht->size;
|
||||
|
||||
if (ht->entries == 0)
|
||||
return NULL;
|
||||
|
||||
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
|
||||
if (entry_is_present(entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = ht->table; entry != ht->table + i; entry++) {
|
||||
if (entry_is_present(entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ravicomp_HASH_TABLE_H
|
||||
#define ravicomp_HASH_TABLE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef struct HashEntry {
|
||||
uint32_t hash;
|
||||
const void *key;
|
||||
void *data;
|
||||
} HashEntry;
|
||||
|
||||
typedef struct HashTable {
|
||||
HashEntry *table;
|
||||
uint32_t (*hash_function)(const void *key);
|
||||
int (*key_equals_function)(const void *a, const void *b);
|
||||
uint32_t size;
|
||||
uint32_t rehash;
|
||||
uint32_t max_entries;
|
||||
uint32_t size_index;
|
||||
uint32_t entries;
|
||||
uint32_t deleted_entries;
|
||||
} HashTable;
|
||||
|
||||
HashTable *
|
||||
raviX_hash_table_create(uint32_t (*hash_function)(const void *key),
|
||||
int (*key_equals_function)(const void *a,
|
||||
const void *b));
|
||||
void
|
||||
raviX_hash_table_destroy(HashTable *ht,
|
||||
void (*delete_function)(HashEntry *entry));
|
||||
|
||||
HashEntry *
|
||||
raviX_hash_table_insert(HashTable *ht, const void *key, void *data);
|
||||
|
||||
HashEntry *
|
||||
raviX_hash_table_search(HashTable *ht, const void *key);
|
||||
|
||||
void
|
||||
raviX_hash_table_remove(HashTable *ht, const void *key);
|
||||
|
||||
void
|
||||
raviX_hash_table_remove_entry(HashTable *ht, HashEntry *entry);
|
||||
|
||||
HashEntry *
|
||||
raviX_hash_table_next_entry(HashTable *ht,
|
||||
HashEntry *entry);
|
||||
|
||||
//HashEntry *
|
||||
//hash_table_random_entry(HashTable *ht,
|
||||
// int (*predicate)(HashEntry *entry));
|
||||
|
||||
/**
|
||||
* This foreach function is safe against deletion (which just replaces
|
||||
* an entry's data with the deleted marker), but not against insertion
|
||||
* (which may rehash the table, making entry a dangling pointer).
|
||||
*/
|
||||
#define hash_table_foreach(ht, entry) \
|
||||
for (entry = raviX_hash_table_next_entry(ht, NULL); \
|
||||
entry != NULL; \
|
||||
entry = raviX_hash_table_next_entry(ht, entry))
|
||||
|
||||
/* Alternate interfaces to reduce repeated calls to hash function. */
|
||||
HashEntry *
|
||||
raviX_hash_table_search_pre_hashed(HashTable *ht,
|
||||
uint32_t hash,
|
||||
const void *key);
|
||||
|
||||
HashEntry *
|
||||
raviX_hash_table_insert_pre_hashed(HashTable *ht,
|
||||
uint32_t hash,
|
||||
const void *key, void *data);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,990 @@
|
||||
/**
|
||||
* The lexer is basically a hacked version of Lua 5.3 lexer.
|
||||
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
|
||||
*/
|
||||
|
||||
#include "fnv_hash.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
|
||||
enum { EOZ = -1 }; /* end of stream */
|
||||
#define cast(t, v) ((t)v)
|
||||
#define cast_int(v) cast(int, v)
|
||||
#define cast_uchar(c) cast(unsigned char, c)
|
||||
#define cast_num(n) cast(lua_Number, n)
|
||||
#define l_castU2S(i) ((lua_Integer)(i))
|
||||
static inline int zgetc(LexerState *z) { return z->n-- > 0 ? cast_uchar(*z->p++) : EOZ; }
|
||||
static inline void next(LexerState *ls) { ls->current = zgetc(ls); }
|
||||
static inline bool currIsNewline(LexerState *ls) { return ls->current == '\n' || ls->current == '\r'; }
|
||||
|
||||
static inline char lua_getlocaledecpoint(void) { return localeconv()->decimal_point[0]; }
|
||||
|
||||
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
|
||||
/*Note: Following array was generated using utils/tokenstr.h */
|
||||
static const char *const luaX_tokens[] = {
|
||||
"and",
|
||||
"break",
|
||||
"do",
|
||||
"else",
|
||||
"elseif",
|
||||
"end",
|
||||
"false",
|
||||
"for",
|
||||
"function",
|
||||
"goto",
|
||||
"if",
|
||||
"in",
|
||||
"local",
|
||||
"defer",
|
||||
"nil",
|
||||
"not",
|
||||
"or",
|
||||
"repeat",
|
||||
"return",
|
||||
"then",
|
||||
"true",
|
||||
"until",
|
||||
"while",
|
||||
"/"
|
||||
"/",
|
||||
"..",
|
||||
"...",
|
||||
"==",
|
||||
">=",
|
||||
"<=",
|
||||
"~=",
|
||||
"<<",
|
||||
">>",
|
||||
"::",
|
||||
"@integer",
|
||||
"@number",
|
||||
"@integer[]",
|
||||
"@number[]",
|
||||
"@table",
|
||||
"@string",
|
||||
"@closure",
|
||||
"<eof>",
|
||||
"<number>",
|
||||
"<integer>",
|
||||
"<name>",
|
||||
"<string>",
|
||||
};
|
||||
/* Says whether the given string represents a Lua/Ravi keyword i.e. reserved word */
|
||||
static inline int is_reserved(const StringObject *s) { return s->reserved; }
|
||||
|
||||
enum { ALPHABIT = 0, DIGITBIT = 1, PRINTBIT = 2, SPACEBIT = 3, XDIGITBIT = 4 };
|
||||
|
||||
#define MASK(B) (1 << (B))
|
||||
|
||||
static const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||
0x00, /* EOZ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
|
||||
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
/*
|
||||
** add 1 to char to allow index -1 (EOZ)
|
||||
*/
|
||||
#define testprop(c, p) (luai_ctype_[(c) + 1] & (p))
|
||||
|
||||
/*
|
||||
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
|
||||
*/
|
||||
static inline bool lislalpha(int c) { return testprop(c, MASK(ALPHABIT)); }
|
||||
static inline bool lislalnum(int c) { return testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))); }
|
||||
static inline bool lisdigit(int c) { return testprop(c, MASK(DIGITBIT)); }
|
||||
static inline bool lisspace(int c) { return testprop(c, MASK(SPACEBIT)); }
|
||||
// static inline bool lisprint(int c) { return testprop(c, MASK(PRINTBIT)); }
|
||||
static inline bool lisxdigit(int c) { return testprop(c, MASK(XDIGITBIT)); }
|
||||
|
||||
/*
|
||||
** this 'ltolower' only works for alphabetic characters
|
||||
*/
|
||||
static inline int ltolower(int c) { return ((c) | ('A' ^ 'a')); }
|
||||
|
||||
/*
|
||||
Creates a new string object. string objects are interned in a hash set.
|
||||
If the string matches a keyword then the reserved attribute will be set to the token id associated
|
||||
with the keyword else this attribute will be -1. The hash value of the string is stored the 'hash'
|
||||
attribute. Note that we need to allow strings that have embedded 0 character hence the length
|
||||
is explicit. But all tokens and reserved keywords are expected to be standard C strings.
|
||||
*/
|
||||
const StringObject *raviX_create_string(CompilerState *compiler_state, const char *input, uint32_t len)
|
||||
{
|
||||
StringObject temp = {.len = len, .str = input, .hash = fnv1_hash_data(input, len), .reserved = -1};
|
||||
SetEntry *entry = raviX_set_search_pre_hashed(compiler_state->strings, temp.hash, &temp);
|
||||
if (entry != NULL)
|
||||
/* found the string */
|
||||
return (StringObject *)entry->key;
|
||||
else {
|
||||
StringObject *new_string = raviX_allocator_allocate(&compiler_state->string_object_allocator, 0);
|
||||
char *s = raviX_allocator_allocate(&compiler_state->string_allocator, len + 1); /* allow for 0 terminator */
|
||||
memcpy(s, input, len);
|
||||
s[len] = 0; /* 0 terminate string, however string may contain embedded 0 characters */
|
||||
new_string->str = s;
|
||||
new_string->len = len;
|
||||
new_string->hash = temp.hash;
|
||||
new_string->reserved = -1;
|
||||
/* Check if this is a keyword, linear search is okay as we do this only when we first
|
||||
* encounter a keyword
|
||||
*/
|
||||
for (int i = 0; i < ARRAY_SIZE(luaX_tokens); i++) {
|
||||
if (lislalpha(luaX_tokens[i][0]) || luaX_tokens[i][0] == '@') {
|
||||
if (strcmp(luaX_tokens[i], s) == 0) {
|
||||
new_string->reserved = i; /* save index of the keyword */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
raviX_set_add_pre_hashed(compiler_state->strings, temp.hash, new_string);
|
||||
return new_string;
|
||||
}
|
||||
}
|
||||
|
||||
#define lua_str2number(s, p) ((lua_Number)strtod((s), (p)))
|
||||
static void save(LexerState *ls, int c);
|
||||
|
||||
void raviX_token2str(int token, TextBuffer *mb)
|
||||
{
|
||||
if (token < FIRST_RESERVED) { /* single-byte symbols? */
|
||||
assert(token == cast_uchar(token));
|
||||
raviX_buffer_add_fstring(mb, "'%c'", token);
|
||||
} else {
|
||||
const char *s = luaX_tokens[token - FIRST_RESERVED];
|
||||
if (token < TOK_EOS) /* fixed format - reserved keywords */
|
||||
raviX_buffer_add_fstring(mb, "'%s'", s);
|
||||
else /* names, strings, and numerals - note that @<usertype> is covered here */
|
||||
raviX_buffer_add_string(mb, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void txtToken(LexerState *ls, int token)
|
||||
{
|
||||
switch (token) {
|
||||
case TOK_NAME:
|
||||
case TOK_STRING:
|
||||
case TOK_FLT:
|
||||
case TOK_INT:
|
||||
save(ls, '\0');
|
||||
raviX_buffer_add_fstring(&ls->container->error_message, "'%s'", raviX_buffer_data(ls->buff));
|
||||
break;
|
||||
default:
|
||||
raviX_token2str(token, &ls->container->error_message);
|
||||
}
|
||||
}
|
||||
static void lexerror(LexerState *ls, const char *msg, int token)
|
||||
{
|
||||
raviX_buffer_add_fstring(&ls->container->error_message, "%s(%d): %s", ls->source, ls->linenumber, msg);
|
||||
if (token) {
|
||||
raviX_buffer_add_string(&ls->container->error_message, " near ");
|
||||
txtToken(ls, token);
|
||||
}
|
||||
longjmp(ls->container->env, 1);
|
||||
}
|
||||
|
||||
void raviX_syntaxerror(LexerState *ls, const char *msg) { lexerror(ls, msg, ls->t.token); }
|
||||
|
||||
static void save(LexerState *ls, int c)
|
||||
{
|
||||
TextBuffer *b = ls->buff;
|
||||
if (raviX_buffer_len(b) + 1 > raviX_buffer_size(b)) {
|
||||
size_t newsize;
|
||||
if (raviX_buffer_size(b) >= INT_MAX / 2)
|
||||
lexerror(ls, "lexical element too long", 0);
|
||||
size_t oldsize = raviX_buffer_size(b);
|
||||
if (oldsize == 0)
|
||||
newsize = 32;
|
||||
else
|
||||
newsize = oldsize * 2;
|
||||
raviX_buffer_resize(b, newsize);
|
||||
}
|
||||
raviX_buffer_addc(b, c);
|
||||
}
|
||||
|
||||
static inline void save_and_next(LexerState *ls)
|
||||
{
|
||||
save(ls, ls->current);
|
||||
next(ls);
|
||||
}
|
||||
|
||||
/*
|
||||
** creates a new interned string.
|
||||
*/
|
||||
static const StringObject *luaX_newstring(LexerState *ls, const char *str, uint32_t l)
|
||||
{
|
||||
return raviX_create_string(ls->container, str, l);
|
||||
}
|
||||
|
||||
/*
|
||||
** increment line number and skips newline sequence (any of
|
||||
** \n, \r, \n\r, or \r\n)
|
||||
*/
|
||||
static void inclinenumber(LexerState *ls)
|
||||
{
|
||||
int old = ls->current;
|
||||
assert(currIsNewline(ls));
|
||||
next(ls); /* skip '\n' or '\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip '\n\r' or '\r\n' */
|
||||
if (++ls->linenumber >= INT_MAX)
|
||||
lexerror(ls, "chunk has too many lines", 0);
|
||||
}
|
||||
|
||||
LexerState *raviX_init_lexer(CompilerState *container, const char *buf, size_t buflen,
|
||||
const char *source)
|
||||
{
|
||||
LexerState *ls = (LexerState *)calloc(1, sizeof(LexerState));
|
||||
ls->container = container;
|
||||
ls->t.token = 0;
|
||||
ls->buf = buf;
|
||||
ls->bufsize = buflen;
|
||||
ls->n = ls->bufsize;
|
||||
ls->p = ls->buf;
|
||||
ls->current = zgetc(ls);
|
||||
ls->lookahead.token = TOK_EOS; /* no look-ahead token */
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
ls->source = source;
|
||||
ls->envn = raviX_create_string(ls->container, LUA_ENV, (uint32_t)strlen(LUA_ENV))->str; /* get env name */
|
||||
ls->buff = &container->buff;
|
||||
for (int i = 0; i < NUM_RESERVED; i++) {
|
||||
raviX_create_string(ls->container, luaX_tokens[i], (uint32_t)strlen(luaX_tokens[i]));
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
|
||||
void raviX_destroy_lexer(LexerState *ls)
|
||||
{
|
||||
if (ls == NULL)
|
||||
return;
|
||||
free(ls);
|
||||
}
|
||||
|
||||
const LexerInfo *raviX_get_lexer_info(LexerState *ls) { return (LexerInfo *)ls; }
|
||||
|
||||
/*
|
||||
** =======================================================
|
||||
** LEXICAL ANALYZER
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static int check_next1(LexerState *ls, int c)
|
||||
{
|
||||
if (ls->current == c) {
|
||||
next(ls);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_save_next1(LexerState *ls, int c)
|
||||
{
|
||||
if (ls->current == c) {
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check whether current char is in set 'set' (with two chars) and
|
||||
** saves it
|
||||
*/
|
||||
static int check_next2(LexerState *ls, const char *set)
|
||||
{
|
||||
assert(set[2] == '\0');
|
||||
if (ls->current == set[0] || ls->current == set[1]) {
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaO_hexavalue(int c)
|
||||
{
|
||||
if (lisdigit(c))
|
||||
return c - '0';
|
||||
else
|
||||
return (ltolower(c) - 'a') + 10;
|
||||
}
|
||||
|
||||
static int isneg(const char **s)
|
||||
{
|
||||
if (**s == '-') {
|
||||
(*s)++;
|
||||
return 1;
|
||||
} else if (**s == '+')
|
||||
(*s)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Lua's implementation for 'lua_strx2number'
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(lua_strx2number)
|
||||
|
||||
/* maximum number of significant digits to read (to avoid overflows
|
||||
even with single floats) */
|
||||
#define MAXSIGDIG 30
|
||||
|
||||
/*
|
||||
** convert an hexadecimal numeric string to a number, following
|
||||
** C99 specification for 'strtod'
|
||||
*/
|
||||
static lua_Number lua_strx2number(const char *s, char **endptr)
|
||||
{
|
||||
int dot = lua_getlocaledecpoint();
|
||||
lua_Number r = 0.0; /* result (accumulator) */
|
||||
int sigdig = 0; /* number of significant digits */
|
||||
int nosigdig = 0; /* number of non-significant digits */
|
||||
int e = 0; /* exponent correction */
|
||||
int neg; /* 1 if number is negative */
|
||||
int hasdot = 0; /* true after seen a dot */
|
||||
*endptr = (char *)s; /* nothing is valid yet */
|
||||
while (lisspace(cast_uchar(*s)))
|
||||
s++; /* skip initial spaces */
|
||||
neg = isneg(&s); /* check signal */
|
||||
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
||||
return 0.0; /* invalid format (no '0x') */
|
||||
for (s += 2;; s++) { /* skip '0x' and read numeral */
|
||||
if (*s == dot) {
|
||||
if (hasdot)
|
||||
break; /* second dot? stop loop */
|
||||
else
|
||||
hasdot = 1;
|
||||
} else if (lisxdigit(cast_uchar(*s))) {
|
||||
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
|
||||
nosigdig++;
|
||||
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
|
||||
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
|
||||
else
|
||||
e++; /* too many digits; ignore, but still count for exponent */
|
||||
if (hasdot)
|
||||
e--; /* decimal digit? correct exponent */
|
||||
} else
|
||||
break; /* neither a dot nor a digit */
|
||||
}
|
||||
if (nosigdig + sigdig == 0) /* no digits? */
|
||||
return 0.0; /* invalid format */
|
||||
*endptr = (char *)s; /* valid up to here */
|
||||
e *= 4; /* each digit multiplies/divides value by 2^4 */
|
||||
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
||||
int exp1 = 0; /* exponent value */
|
||||
int neg1; /* exponent signal */
|
||||
s++; /* skip 'p' */
|
||||
neg1 = isneg(&s); /* signal */
|
||||
if (!lisdigit(cast_uchar(*s)))
|
||||
return 0.0; /* invalid; must have at least one digit */
|
||||
while (lisdigit(cast_uchar(*s))) /* read exponent */
|
||||
exp1 = exp1 * 10 + *(s++) - '0';
|
||||
if (neg1)
|
||||
exp1 = -exp1;
|
||||
e += exp1;
|
||||
*endptr = (char *)s; /* valid up to here */
|
||||
}
|
||||
if (neg)
|
||||
r = -r;
|
||||
return (lua_Number)ldexp(r, e);
|
||||
}
|
||||
|
||||
#endif
|
||||
/* }====================================================== */
|
||||
|
||||
/* maximum length of a numeral */
|
||||
#if !defined(L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
static const char *l_str2dloc(const char *s, lua_Number *result, int mode)
|
||||
{
|
||||
char *endptr;
|
||||
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
|
||||
: lua_str2number(s, &endptr);
|
||||
if (endptr == s)
|
||||
return NULL; /* nothing recognized? */
|
||||
while (lisspace(cast_uchar(*endptr)))
|
||||
endptr++; /* skip trailing spaces */
|
||||
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result'). Return NULL
|
||||
** on fail or the address of the ending '\0' on success.
|
||||
** 'pmode' points to (and 'mode' contains) special things in the string:
|
||||
** - 'x'/'X' means an hexadecimal numeral
|
||||
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
|
||||
** - '.' just optimizes the search for the common case (nothing special)
|
||||
** This function accepts both the current locale or a dot as the radix
|
||||
** mark. If the convertion fails, it may mean number has a dot but
|
||||
** locale accepts something else. In that case, the code copies 's'
|
||||
** to a buffer (because 's' is read-only), changes the dot to the
|
||||
** current locale radix mark, and tries to convert again.
|
||||
*/
|
||||
static const char *l_str2d(const char *s, lua_Number *result)
|
||||
{
|
||||
const char *endptr;
|
||||
const char *pmode = strpbrk(s, ".xXnN");
|
||||
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
|
||||
if (mode == 'n') /* reject 'inf' and 'nan' */
|
||||
return NULL;
|
||||
endptr = l_str2dloc(s, result, mode); /* try to convert */
|
||||
if (endptr == NULL) { /* failed? may be a different locale */
|
||||
char buff[L_MAXLENNUM + 1];
|
||||
const char *pdot = strchr(s, '.');
|
||||
if (strlen(s) > L_MAXLENNUM || pdot == NULL)
|
||||
return NULL; /* string too long or no dot; fail */
|
||||
strcpy(buff, s); /* copy string to buffer */
|
||||
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
|
||||
endptr = l_str2dloc(buff, result, mode); /* try again */
|
||||
if (endptr != NULL)
|
||||
endptr = s + (endptr - buff); /* make relative to 's' */
|
||||
}
|
||||
return endptr;
|
||||
}
|
||||
|
||||
#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
|
||||
#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
|
||||
|
||||
static const char *l_str2int(const char *s, lua_Integer *result)
|
||||
{
|
||||
lua_Unsigned a = 0;
|
||||
int empty = 1;
|
||||
int neg;
|
||||
while (lisspace(cast_uchar(*s)))
|
||||
s++; /* skip initial spaces */
|
||||
neg = isneg(&s);
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { /* hex? */
|
||||
s += 2; /* skip '0x' */
|
||||
for (; lisxdigit(cast_uchar(*s)); s++) {
|
||||
a = a * 16 + luaO_hexavalue(*s);
|
||||
empty = 0;
|
||||
}
|
||||
} else { /* decimal */
|
||||
for (; lisdigit(cast_uchar(*s)); s++) {
|
||||
int d = *s - '0';
|
||||
if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
|
||||
return NULL; /* do not accept it (as integer) */
|
||||
a = a * 10 + d;
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
while (lisspace(cast_uchar(*s)))
|
||||
s++; /* skip trailing spaces */
|
||||
if (empty || *s != '\0')
|
||||
return NULL; /* something wrong in the numeral */
|
||||
else {
|
||||
*result = l_castU2S((neg) ? 0u - a : a);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
struct konst {
|
||||
uint8_t type;
|
||||
union {
|
||||
lua_Integer i;
|
||||
lua_Number n;
|
||||
};
|
||||
};
|
||||
|
||||
static size_t luaO_str2num(const char *s, struct konst *o)
|
||||
{
|
||||
lua_Integer i;
|
||||
lua_Number n;
|
||||
const char *e;
|
||||
if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
|
||||
o->i = i;
|
||||
o->type = 1;
|
||||
} else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
|
||||
o->n = n;
|
||||
o->type = 2;
|
||||
} else
|
||||
return 0; /* conversion failed */
|
||||
return (e - s) + 1; /* success; return string size */
|
||||
}
|
||||
|
||||
/* LUA_NUMBER */
|
||||
/*
|
||||
** this function is quite liberal in what it accepts, as 'luaO_str2num'
|
||||
** will reject ill-formed numerals.
|
||||
*/
|
||||
static int read_numeral(LexerState *ls, SemInfo *seminfo)
|
||||
{
|
||||
struct konst obj;
|
||||
const char *expo = "Ee";
|
||||
int first = ls->current;
|
||||
assert(lisdigit(ls->current));
|
||||
save_and_next(ls);
|
||||
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
|
||||
expo = "Pp";
|
||||
for (;;) {
|
||||
if (check_next2(ls, expo)) /* exponent part? */
|
||||
check_next2(ls, "-+"); /* optional exponent sign */
|
||||
if (lisxdigit(ls->current))
|
||||
save_and_next(ls);
|
||||
else if (ls->current == '.')
|
||||
save_and_next(ls);
|
||||
else
|
||||
break;
|
||||
}
|
||||
save(ls, '\0');
|
||||
if (luaO_str2num(raviX_buffer_data(ls->buff), &obj) == 0) /* format error? */
|
||||
lexerror(ls, "malformed number", TOK_FLT);
|
||||
if (obj.type == 1) {
|
||||
seminfo->i = obj.i;
|
||||
return TOK_INT;
|
||||
} else {
|
||||
assert(obj.type == 2);
|
||||
seminfo->r = obj.n;
|
||||
return TOK_FLT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return
|
||||
** its number of '='s; otherwise, return a negative number (-1 iff there
|
||||
** are no '='s after initial bracket)
|
||||
*/
|
||||
static int skip_sep(LexerState *ls)
|
||||
{
|
||||
int count = 0;
|
||||
int s = ls->current;
|
||||
assert(s == '[' || s == ']');
|
||||
save_and_next(ls);
|
||||
while (ls->current == '=') {
|
||||
save_and_next(ls);
|
||||
count++;
|
||||
}
|
||||
return (ls->current == s) ? count : (-count) - 1;
|
||||
}
|
||||
|
||||
static void read_long_string(LexerState *ls, SemInfo *seminfo, int sep)
|
||||
{
|
||||
int line = ls->linenumber; /* initial line (for error message) */
|
||||
(void)line;
|
||||
save_and_next(ls); /* skip 2nd '[' */
|
||||
if (currIsNewline(ls)) /* string starts with a newline? */
|
||||
inclinenumber(ls); /* skip it */
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case EOZ: { /* error */
|
||||
const char *what = (seminfo ? "string" : "comment");
|
||||
const char *msg = "";
|
||||
(void)what;
|
||||
// luaO_pushfstring(ls->L, "unfinished long %s (starting at line %d)",
|
||||
// what, line);
|
||||
lexerror(ls, msg, TOK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
}
|
||||
case ']': {
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd ']' */
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '\n':
|
||||
case '\r': {
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
if (!seminfo)
|
||||
raviX_buffer_reset(ls->buff); /* avoid wasting space */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (seminfo)
|
||||
save_and_next(ls);
|
||||
else
|
||||
next(ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
endloop:
|
||||
if (seminfo)
|
||||
seminfo->ts = luaX_newstring(ls, raviX_buffer_data(ls->buff) + (2 + sep),
|
||||
(uint32_t)(raviX_buffer_len(ls->buff) - 2 * (2 + sep)));
|
||||
}
|
||||
|
||||
static void esccheck(LexerState *ls, int c, const char *msg)
|
||||
{
|
||||
if (!c) {
|
||||
if (ls->current != EOZ)
|
||||
save_and_next(ls); /* add current to buffer for error message */
|
||||
lexerror(ls, msg, TOK_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
static int gethexa(LexerState *ls)
|
||||
{
|
||||
save_and_next(ls);
|
||||
esccheck(ls, lisxdigit(ls->current), "hexadecimal digit expected");
|
||||
return luaO_hexavalue(ls->current);
|
||||
}
|
||||
|
||||
static int readhexaesc(LexerState *ls)
|
||||
{
|
||||
int r = gethexa(ls);
|
||||
r = (r << 4) + gethexa(ls);
|
||||
raviX_buffer_remove(ls->buff, 2); /* remove saved chars from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
// static unsigned long readutf8esc (LexerState *ls) {
|
||||
// unsigned long r;
|
||||
// int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
|
||||
// save_and_next(ls); /* skip 'u' */
|
||||
// esccheck(ls, ls->current == '{', "missing '{'");
|
||||
// r = gethexa(ls); /* must have at least one digit */
|
||||
// while ((save_and_next(ls), lisxdigit(ls->current))) {
|
||||
// i++;
|
||||
// r = (r << 4) + luaO_hexavalue(ls->current);
|
||||
// esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large");
|
||||
// }
|
||||
// esccheck(ls, ls->current == '}', "missing '}'");
|
||||
// next(ls); /* skip '}' */
|
||||
// raviX_buffer_remove(ls->buff, i); /* remove saved chars from buffer */
|
||||
// return r;
|
||||
//}
|
||||
//
|
||||
//
|
||||
// static void utf8esc (LexerState *ls) {
|
||||
// char buff[UTF8BUFFSZ];
|
||||
// int n = luaO_utf8esc(buff, readutf8esc(ls));
|
||||
// for (; n > 0; n--) /* add 'buff' to string */
|
||||
// save(ls, buff[UTF8BUFFSZ - n]);
|
||||
//}
|
||||
|
||||
static int readdecesc(LexerState *ls)
|
||||
{
|
||||
int i;
|
||||
int r = 0; /* result accumulator */
|
||||
for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
|
||||
r = 10 * r + ls->current - '0';
|
||||
save_and_next(ls);
|
||||
}
|
||||
esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
|
||||
raviX_buffer_remove(ls->buff, i); /* remove read digits from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
static void read_string(LexerState *ls, int del, SemInfo *seminfo)
|
||||
{
|
||||
save_and_next(ls); /* keep delimiter (for error messages) */
|
||||
while (ls->current != del) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
lexerror(ls, "unfinished string", TOK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case '\n':
|
||||
case '\r':
|
||||
lexerror(ls, "unfinished string", TOK_STRING);
|
||||
break; /* to avoid warnings */
|
||||
case '\\': { /* escape sequences */
|
||||
int c; /* final character to be saved */
|
||||
save_and_next(ls); /* keep '\\' for error messages */
|
||||
switch (ls->current) {
|
||||
case 'a':
|
||||
c = '\a';
|
||||
goto read_save;
|
||||
case 'b':
|
||||
c = '\b';
|
||||
goto read_save;
|
||||
case 'f':
|
||||
c = '\f';
|
||||
goto read_save;
|
||||
case 'n':
|
||||
c = '\n';
|
||||
goto read_save;
|
||||
case 'r':
|
||||
c = '\r';
|
||||
goto read_save;
|
||||
case 't':
|
||||
c = '\t';
|
||||
goto read_save;
|
||||
case 'v':
|
||||
c = '\v';
|
||||
goto read_save;
|
||||
case 'x':
|
||||
c = readhexaesc(ls);
|
||||
goto read_save;
|
||||
// TODO - FIXME
|
||||
// case 'u': utf8esc(ls); goto no_save;
|
||||
case '\n':
|
||||
case '\r':
|
||||
inclinenumber(ls);
|
||||
c = '\n';
|
||||
goto only_save;
|
||||
case '\\':
|
||||
case '\"':
|
||||
case '\'':
|
||||
c = ls->current;
|
||||
goto read_save;
|
||||
case EOZ:
|
||||
goto no_save; /* will raise an error next loop */
|
||||
case 'z': { /* zap following span of spaces */
|
||||
raviX_buffer_remove(ls->buff, 1); /* remove '\\' */
|
||||
next(ls); /* skip the 'z' */
|
||||
while (lisspace(ls->current)) {
|
||||
if (currIsNewline(ls))
|
||||
inclinenumber(ls);
|
||||
else
|
||||
next(ls);
|
||||
}
|
||||
goto no_save;
|
||||
}
|
||||
default: {
|
||||
esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
|
||||
c = readdecesc(ls); /* digital escape '\ddd' */
|
||||
goto only_save;
|
||||
}
|
||||
}
|
||||
read_save:
|
||||
next(ls);
|
||||
/* go through */
|
||||
only_save:
|
||||
raviX_buffer_remove(ls->buff, 1); /* remove '\\' */
|
||||
save(ls, c);
|
||||
/* go through */
|
||||
no_save:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
}
|
||||
}
|
||||
save_and_next(ls); /* skip delimiter */
|
||||
seminfo->ts = luaX_newstring(ls, raviX_buffer_data(ls->buff) + 1, (uint32_t)(raviX_buffer_len(ls->buff) - 2));
|
||||
}
|
||||
|
||||
/*
|
||||
** RAVI extension: generate a token for the cast operators -
|
||||
** @number, @number[], @integer, @integer[], @table, @string, @closure
|
||||
*/
|
||||
static int casttoken(LexerState *ls, SemInfo *seminfo)
|
||||
{
|
||||
size_t n = raviX_buffer_len(ls->buff);
|
||||
const char *s = raviX_buffer_data(ls->buff);
|
||||
int tok;
|
||||
|
||||
/* @integer or @integer[] */
|
||||
if (strncmp(s, "@integer", n) == 0)
|
||||
tok = TOK_TO_INTEGER;
|
||||
else if (strncmp(s, "@integer[]", n) == 0)
|
||||
tok = TOK_TO_INTARRAY;
|
||||
/* @number or @number[] */
|
||||
else if (strncmp(s, "@number", n) == 0)
|
||||
tok = TOK_TO_NUMBER;
|
||||
else if (strncmp(s, "@number[]", n) == 0)
|
||||
tok = TOK_TO_NUMARRAY;
|
||||
/* @table */
|
||||
else if (strncmp(s, "@table", n) == 0)
|
||||
tok = TOK_TO_TABLE;
|
||||
else if (strncmp(s, "@string", n) == 0)
|
||||
tok = TOK_TO_STRING;
|
||||
else if (strncmp(s, "@closure", n) == 0)
|
||||
tok = TOK_TO_CLOSURE;
|
||||
else {
|
||||
seminfo->ts = luaX_newstring(ls, s + 1, (uint32_t)(n - 1)); /* omit @ */
|
||||
tok = '@';
|
||||
}
|
||||
raviX_buffer_remove(ls->buff, (int)n); /* rewind but buffer still holds the saved characters */
|
||||
return tok;
|
||||
}
|
||||
|
||||
static int llex(LexerState *ls, SemInfo *seminfo)
|
||||
{
|
||||
raviX_buffer_reset(ls->buff);
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case '\n':
|
||||
case '\r': { /* line breaks */
|
||||
inclinenumber(ls);
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case '\f':
|
||||
case '\t':
|
||||
case '\v': { /* spaces */
|
||||
next(ls);
|
||||
break;
|
||||
}
|
||||
case '-': { /* '-' or '--' (comment) */
|
||||
next(ls);
|
||||
if (ls->current != '-')
|
||||
return '-';
|
||||
/* else is a comment */
|
||||
next(ls);
|
||||
if (ls->current == '[') { /* long comment? */
|
||||
int sep = skip_sep(ls);
|
||||
raviX_buffer_reset(ls->buff); /* 'skip_sep' may dirty the buffer */
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, NULL, sep); /* skip long comment */
|
||||
raviX_buffer_reset(ls->buff); /* previous call may dirty the buff. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else short comment */
|
||||
while (!currIsNewline(ls) && ls->current != EOZ)
|
||||
next(ls); /* skip until end of line (or end of file) */
|
||||
break;
|
||||
}
|
||||
case '[': { /* long string or simply '[' */
|
||||
int sep = skip_sep(ls);
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, seminfo, sep);
|
||||
return TOK_STRING;
|
||||
} else if (sep != -1) /* '[=...' missing second bracket */
|
||||
lexerror(ls, "invalid long string delimiter", TOK_STRING);
|
||||
return '[';
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '='))
|
||||
return TOK_EQ;
|
||||
else
|
||||
return '=';
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '='))
|
||||
return TOK_LE;
|
||||
else if (check_next1(ls, '<'))
|
||||
return TOK_SHL;
|
||||
else
|
||||
return '<';
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '='))
|
||||
return TOK_GE;
|
||||
else if (check_next1(ls, '>'))
|
||||
return TOK_SHR;
|
||||
else
|
||||
return '>';
|
||||
}
|
||||
case '/': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '/'))
|
||||
return TOK_IDIV;
|
||||
else
|
||||
return '/';
|
||||
}
|
||||
case '~': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '='))
|
||||
return TOK_NE;
|
||||
else
|
||||
return '~';
|
||||
}
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (check_next1(ls, ':'))
|
||||
return TOK_DBCOLON;
|
||||
else
|
||||
return ':';
|
||||
}
|
||||
case '"':
|
||||
case '\'': { /* short literal strings */
|
||||
read_string(ls, ls->current, seminfo);
|
||||
return TOK_STRING;
|
||||
}
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (check_next1(ls, '.')) {
|
||||
if (check_next1(ls, '.'))
|
||||
return TOK_DOTS; /* '...' */
|
||||
else
|
||||
return TOK_CONCAT; /* '..' */
|
||||
} else if (!lisdigit(ls->current))
|
||||
return '.';
|
||||
else
|
||||
return read_numeral(ls, seminfo);
|
||||
}
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
return read_numeral(ls, seminfo);
|
||||
}
|
||||
case EOZ: {
|
||||
return TOK_EOS;
|
||||
}
|
||||
case '@': {
|
||||
/* RAVI change: @ introduces a type assertion operator */
|
||||
save_and_next(ls);
|
||||
while (lislalnum(ls->current)) {
|
||||
save_and_next(ls);
|
||||
}
|
||||
check_save_next1(ls, '[');
|
||||
check_save_next1(ls, ']');
|
||||
return casttoken(ls, seminfo);
|
||||
}
|
||||
default: {
|
||||
if (lislalpha(ls->current)) { /* identifier or reserved word? */
|
||||
const StringObject *ts;
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (lislalnum(ls->current));
|
||||
ts = raviX_create_string(ls->container, raviX_buffer_data(ls->buff),
|
||||
(int32_t)raviX_buffer_len(ls->buff));
|
||||
seminfo->ts = ts;
|
||||
int tok = is_reserved(ts);
|
||||
if (tok != -1) /* reserved word? */
|
||||
return tok + FIRST_RESERVED;
|
||||
else {
|
||||
return TOK_NAME;
|
||||
}
|
||||
} else { /* single-char tokens (+ - / ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_next(LexerState *ls)
|
||||
{
|
||||
ls->lastline = ls->linenumber;
|
||||
if (ls->lookahead.token != TOK_EOS) { /* is there a look-ahead token? */
|
||||
ls->t = ls->lookahead; /* use this one */
|
||||
ls->lookahead.token = TOK_EOS; /* and discharge it */
|
||||
} else
|
||||
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
|
||||
}
|
||||
|
||||
int raviX_lookahead(LexerState *ls)
|
||||
{
|
||||
assert(ls->lookahead.token == TOK_EOS);
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
||||
const char *raviX_get_last_error(CompilerState *container) { return container->error_message.buf; }
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,244 @@
|
||||
#ifndef ravicomp_LINEARIZER_H
|
||||
#define ravicomp_LINEARIZER_H
|
||||
|
||||
#include "ravi_compiler.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "parser.h"
|
||||
#include "allocate.h"
|
||||
#include "membuf.h"
|
||||
#include "ptrlist.h"
|
||||
|
||||
/*
|
||||
Linearizer component is responsible for translating the abstract syntax tree to
|
||||
a Linear intermediate representation (IR).
|
||||
*/
|
||||
typedef struct Instruction Instruction;
|
||||
typedef struct BasicBlock BasicBlock;
|
||||
typedef struct Proc Proc;
|
||||
typedef struct Constant Constant;
|
||||
typedef struct Graph Graph;
|
||||
|
||||
DECLARE_PTR_LIST(InstructionList, Instruction);
|
||||
DECLARE_PTR_LIST(PseudoList, Pseudo);
|
||||
DECLARE_PTR_LIST(ProcList, Proc);
|
||||
|
||||
#define container_of(ptr, type, member) ((type *)((char *)(ptr)-offsetof(type, member)))
|
||||
|
||||
/* order is important here ! */
|
||||
enum opcode {
|
||||
op_nop,
|
||||
op_ret,
|
||||
op_add,
|
||||
op_addff,
|
||||
op_addfi,
|
||||
op_addii,
|
||||
op_sub,
|
||||
op_subff,
|
||||
op_subfi,
|
||||
op_subif,
|
||||
op_subii,
|
||||
op_mul,
|
||||
op_mulff,
|
||||
op_mulfi,
|
||||
op_mulii,
|
||||
op_div,
|
||||
op_divff,
|
||||
op_divfi,
|
||||
op_divif,
|
||||
op_divii,
|
||||
op_idiv,
|
||||
op_band,
|
||||
op_bandii,
|
||||
op_bor,
|
||||
op_borii,
|
||||
op_bxor,
|
||||
op_bxorii,
|
||||
op_shl,
|
||||
op_shlii,
|
||||
op_shr,
|
||||
op_shrii,
|
||||
op_eq,
|
||||
op_eqii,
|
||||
op_eqff,
|
||||
op_lt,
|
||||
op_ltii,
|
||||
op_ltff,
|
||||
op_le,
|
||||
op_leii,
|
||||
op_leff,
|
||||
op_mod,
|
||||
op_pow,
|
||||
op_closure,
|
||||
op_unm,
|
||||
op_unmi,
|
||||
op_unmf,
|
||||
op_len,
|
||||
op_leni,
|
||||
op_toint,
|
||||
op_toflt,
|
||||
op_toclosure,
|
||||
op_tostring,
|
||||
op_toiarray,
|
||||
op_tofarray,
|
||||
op_totable,
|
||||
op_totype,
|
||||
op_not,
|
||||
op_bnot,
|
||||
op_loadglobal,
|
||||
op_newtable,
|
||||
op_newiarray,
|
||||
op_newfarray,
|
||||
op_put, /* target is any */
|
||||
op_put_ikey,
|
||||
op_put_skey,
|
||||
op_tput, /* target is table */
|
||||
op_tput_ikey,
|
||||
op_tput_skey,
|
||||
op_iaput, /* target is integer[]*/
|
||||
op_iaput_ival,
|
||||
op_faput, /* target is number[] */
|
||||
op_faput_fval,
|
||||
op_cbr,
|
||||
op_br,
|
||||
op_mov,
|
||||
op_movi,
|
||||
op_movif, /* int to float if compatible else error */
|
||||
op_movf,
|
||||
op_movfi, /* float to int if compatible else error */
|
||||
op_call,
|
||||
op_get,
|
||||
op_get_ikey,
|
||||
op_get_skey,
|
||||
op_tget,
|
||||
op_tget_ikey,
|
||||
op_tget_skey,
|
||||
op_iaget,
|
||||
op_iaget_ikey,
|
||||
op_faget,
|
||||
op_faget_ikey,
|
||||
op_storeglobal,
|
||||
op_close,
|
||||
op_string_concat
|
||||
};
|
||||
|
||||
/*
|
||||
* The IR instructions use operands and targets of type pseudo, which
|
||||
* is a way of referencing several different types of objects.
|
||||
*/
|
||||
enum PseudoType {
|
||||
PSEUDO_SYMBOL, /* An object of type lua_symbol representing local var or upvalue, always refers to Lua stack relative to 'base' */
|
||||
PSEUDO_TEMP_FLT, /* A floating point temp - may also be used for locals that don't escape - refers to C var */
|
||||
PSEUDO_TEMP_INT, /* An integer temp - may also be used for locals that don't escape - refers to C var */
|
||||
PSEUDO_TEMP_BOOL, /* An integer temp but restricted to 1 and 0 - refers to C var, shares the virtual C stack with PSEUDO_TEMP_INT */
|
||||
PSEUDO_TEMP_ANY, /* A temp of any type - will always be on Lua stack relative to 'base' */
|
||||
PSEUDO_CONSTANT, /* A literal value */
|
||||
PSEUDO_PROC, /* A proc / function */
|
||||
PSEUDO_NIL, /* Literal */
|
||||
PSEUDO_TRUE, /* Literal */
|
||||
PSEUDO_FALSE, /* Literal */
|
||||
PSEUDO_BLOCK, /* Points to a basic block, used as targets for jumps */
|
||||
PSEUDO_RANGE, /* Represents a range of registers from a certain starting register on Lua stack relative to 'base' */
|
||||
PSEUDO_RANGE_SELECT, /* Picks a certain register from a range, resolves to register on Lua stack, relative to 'base' */
|
||||
/* TODO we need a type for var args */
|
||||
PSEUDO_LUASTACK /* Specifies a Lua stack position - not used by linearizer - for use by codegen. This is relative to CI->func rather than 'base' */
|
||||
};
|
||||
|
||||
/* pseudo represents a pseudo (virtual) register */
|
||||
struct Pseudo {
|
||||
unsigned type : 4, regnum : 16, freed : 1;
|
||||
Instruction *insn; /* instruction that created this pseudo */
|
||||
union {
|
||||
LuaSymbol *symbol; /* PSEUDO_SYMBOL */
|
||||
const Constant *constant; /* PSEUDO_CONSTANT */
|
||||
LuaSymbol *temp_for_local; /* PSEUDO_TEMP - if the temp represents a local */
|
||||
Proc *proc; /* PSEUDO_PROC */
|
||||
BasicBlock *block; /* PSEUDO_BLOCK */
|
||||
Pseudo *range_pseudo; /* PSEUDO_RANGE_SELECT */
|
||||
int stackidx; /* PSEUDO_LUASTACK */
|
||||
};
|
||||
};
|
||||
|
||||
/* single instruction */
|
||||
struct Instruction {
|
||||
unsigned opcode : 8;
|
||||
PseudoList *operands;
|
||||
PseudoList *targets;
|
||||
BasicBlock *block; /* owning block */
|
||||
};
|
||||
|
||||
/* Basic block */
|
||||
struct BasicBlock {
|
||||
nodeId_t index; /* The index of the block is a key to enable retrieving the block from its container */
|
||||
InstructionList *insns; /* Note that if number of instructions is 0 then the block was logically deleted */
|
||||
};
|
||||
DECLARE_PTR_LIST(BasicBlockList, BasicBlock);
|
||||
|
||||
typedef struct PseudoGenerator {
|
||||
uint8_t next_reg; /* Next register if no free registers, initially 0 */
|
||||
int16_t free_pos; /* number of values in free_regs */
|
||||
uint8_t free_regs[256]; /* list of free registers */
|
||||
} PseudoGenerator;
|
||||
|
||||
struct Constant {
|
||||
uint8_t type; /* ravitype_t RAVI_TNUMINT, RAVI_TNUMFLT or RAVI_TSTRING */
|
||||
uint16_t index; /* index number starting from 0 assigned to each constant - acts like a reg num.
|
||||
* Each type will be assigned separate range */
|
||||
union {
|
||||
lua_Integer i;
|
||||
lua_Number n;
|
||||
const StringObject *s;
|
||||
};
|
||||
};
|
||||
|
||||
/* proc is a type of cfg */
|
||||
struct Proc {
|
||||
unsigned node_count;
|
||||
unsigned allocated;
|
||||
BasicBlock **nodes;
|
||||
uint32_t id; /* ID for the proc */
|
||||
LinearizerState *linearizer;
|
||||
ProcList *procs; /* procs defined in this proc */
|
||||
Proc *parent; /* enclosing proc */
|
||||
AstNode *function_expr; /* function ast that we are compiling */
|
||||
Scope *current_scope;
|
||||
BasicBlock *current_bb;
|
||||
BasicBlock *current_break_target; /* track the current break target, previous target must be saved /
|
||||
restored in stack discipline */
|
||||
Scope *current_break_scope; /* as above track the block scope */
|
||||
PseudoGenerator local_pseudos; /* locals */
|
||||
PseudoGenerator temp_int_pseudos; /* temporaries known to be integer type */
|
||||
PseudoGenerator temp_flt_pseudos; /* temporaries known to be number type */
|
||||
PseudoGenerator temp_pseudos; /* All other temporaries */
|
||||
Set *constants; /* constants used by this proc */
|
||||
uint16_t num_intconstants;
|
||||
uint16_t num_fltconstants;
|
||||
uint16_t num_strconstants;
|
||||
Graph *cfg; /* place holder for control flow graph; the linearizer does not create this */
|
||||
char funcname[30]; /* Each proc needs a name inside a module - name is a short string */
|
||||
void *userdata; /* For use by code generator */
|
||||
};
|
||||
|
||||
struct LinearizerState {
|
||||
Allocator instruction_allocator;
|
||||
Allocator pseudo_allocator;
|
||||
Allocator ptrlist_allocator;
|
||||
Allocator basic_block_allocator;
|
||||
Allocator proc_allocator;
|
||||
Allocator unsized_allocator;
|
||||
Allocator constant_allocator;
|
||||
CompilerState *ast_container;
|
||||
Proc *main_proc; /* The root of the compiled chunk of code */
|
||||
ProcList *all_procs; /* All procs allocated by the linearizer */
|
||||
Proc *current_proc; /* proc being compiled */
|
||||
uint32_t proc_id;
|
||||
};
|
||||
|
||||
void raviX_show_linearizer(LinearizerState *linearizer, TextBuffer *mb);
|
||||
void raviX_output_basic_block_as_table(Proc *proc, BasicBlock *bb, TextBuffer *mb);
|
||||
|
||||
Instruction *raviX_last_instruction(BasicBlock *block);
|
||||
Pseudo* raviX_allocate_stack_pseudo(Proc* proc, unsigned reg);
|
||||
const char *raviX_opcode_name(unsigned int opcode);
|
||||
|
||||
#endif
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright (C) 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include "membuf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void raviX_string_copy(char *buf, const char *src, size_t buflen)
|
||||
{
|
||||
if (buflen == 0)
|
||||
return;
|
||||
strncpy(buf, src, buflen);
|
||||
buf[buflen - 1] = 0;
|
||||
}
|
||||
|
||||
void raviX_buffer_init(TextBuffer *mb, size_t initial_size)
|
||||
{
|
||||
if (initial_size > 0) {
|
||||
mb->buf = (char *)calloc(1, initial_size);
|
||||
if (mb->buf == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
mb->buf = NULL;
|
||||
mb->pos = 0;
|
||||
mb->capacity = initial_size;
|
||||
}
|
||||
void raviX_buffer_resize(TextBuffer *mb, size_t new_size)
|
||||
{
|
||||
if (new_size <= mb->capacity)
|
||||
return;
|
||||
char *newmem = (char *)realloc(mb->buf, new_size);
|
||||
if (newmem == NULL) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
mb->buf = newmem;
|
||||
mb->capacity = new_size;
|
||||
}
|
||||
void raviX_buffer_reserve(TextBuffer *mb, size_t n)
|
||||
{
|
||||
if (mb->capacity < mb->pos + n) {
|
||||
size_t new_size = (((mb->pos + n) * 3 + 30) / 2) & ~15;
|
||||
raviX_buffer_resize(mb, new_size);
|
||||
assert(mb->capacity > mb->pos + n);
|
||||
}
|
||||
}
|
||||
void raviX_buffer_free(TextBuffer *mb) { free(mb->buf); }
|
||||
void raviX_buffer_add_bytes(TextBuffer *mb, const char *str, size_t len)
|
||||
{
|
||||
size_t required_size = mb->pos + len + 1; /* extra byte for NULL terminator */
|
||||
raviX_buffer_resize(mb, required_size);
|
||||
assert(mb->capacity - mb->pos > len);
|
||||
raviX_string_copy(&mb->buf[mb->pos], str, mb->capacity - mb->pos);
|
||||
mb->pos += len;
|
||||
}
|
||||
void raviX_buffer_add_string(TextBuffer *mb, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
raviX_buffer_add_bytes(mb, str, len);
|
||||
}
|
||||
|
||||
void raviX_buffer_add_fstring(TextBuffer *mb, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int estimated_size = 128;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
raviX_buffer_reserve(mb, estimated_size); // ensure we have at least estimated_size free space
|
||||
va_start(args, fmt);
|
||||
int n = vsnprintf(mb->buf + mb->pos, estimated_size, fmt, args);
|
||||
va_end(args);
|
||||
if (n > estimated_size) {
|
||||
estimated_size = n + 1; // allow for 0 byte
|
||||
} else if (n < 0) {
|
||||
fprintf(stderr, "Buffer conversion error\n");
|
||||
assert(false);
|
||||
break;
|
||||
} else {
|
||||
mb->pos += n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_buffer_add_bool(TextBuffer *mb, bool value)
|
||||
{
|
||||
if (value)
|
||||
raviX_buffer_add_string(mb, "true");
|
||||
else
|
||||
raviX_buffer_add_string(mb, "false");
|
||||
}
|
||||
void raviX_buffer_add_int(TextBuffer *mb, int value)
|
||||
{
|
||||
char temp[100];
|
||||
snprintf(temp, sizeof temp, "%d", value);
|
||||
raviX_buffer_add_string(mb, temp);
|
||||
}
|
||||
void raviX_buffer_add_longlong(TextBuffer *mb, int64_t value)
|
||||
{
|
||||
char temp[100];
|
||||
snprintf(temp, sizeof temp, "%" PRId64 "", value);
|
||||
raviX_buffer_add_string(mb, temp);
|
||||
}
|
||||
void raviX_buffer_add_char(TextBuffer *mb, char c)
|
||||
{
|
||||
char temp[2] = {c, '\0'};
|
||||
raviX_buffer_add_string(mb, temp);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#ifndef ravicomp_MEMBUF_H
|
||||
#define ravicomp_MEMBUF_H
|
||||
|
||||
#include "ravi_compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void raviX_buffer_add_bool(TextBuffer *mb, bool value);
|
||||
extern void raviX_buffer_add_int(TextBuffer *mb, int value);
|
||||
extern void raviX_buffer_add_longlong(TextBuffer *mb, int64_t value);
|
||||
extern void raviX_buffer_add_char(TextBuffer *mb, char c);
|
||||
|
||||
/* Following add and remove raw bytes */
|
||||
|
||||
/* Unchecked - user must first resize */
|
||||
static inline void raviX_buffer_addc(TextBuffer *mb, int c)
|
||||
{
|
||||
mb->buf[mb->pos++] = (char)c;
|
||||
assert(mb->pos < mb->capacity);
|
||||
}
|
||||
static inline void raviX_buffer_remove(TextBuffer *mb, int i) { mb->pos -= i; }
|
||||
|
||||
#endif
|
@ -0,0 +1,80 @@
|
||||
/* A pass over linearized code to eliminate unused code.
|
||||
* Initially only tackle unreachable basic blocks
|
||||
*/
|
||||
|
||||
#include "linearizer.h"
|
||||
#include "cfg.h"
|
||||
#include "graph.h"
|
||||
#include "allocate.h"
|
||||
#include "optimizer.h"
|
||||
|
||||
/**
|
||||
* Check if a basic block has 0 predecessors. If so we can remove it from the CFG.
|
||||
* We also remove all the instructions in the basic block
|
||||
*/
|
||||
static int process_block(LinearizerState *linearizer, Proc *proc, BasicBlock *bb)
|
||||
{
|
||||
GraphNode *node = raviX_graph_node(proc->cfg, bb->index);
|
||||
GraphNodeList *predecessors = raviX_predecessors(node);
|
||||
if (raviX_node_list_size(predecessors) != 0) {
|
||||
// Has predecessors so nothing to do
|
||||
return 0;
|
||||
}
|
||||
// No predecessor blocks, so we can remove this block
|
||||
GraphNodeList *successors = raviX_successors(node);
|
||||
uint32_t count = raviX_node_list_size(successors);
|
||||
if (count == 0) {
|
||||
// Nothing to do, but odd?
|
||||
// FIXME maybe assert?
|
||||
return 0;
|
||||
}
|
||||
// Make a copy of the successor node list as we need to change the CFG
|
||||
nodeId_t *nodes = raviX_realloc_array(NULL, sizeof(nodeId_t), 0, count);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
nodes[i] = raviX_node_list_at(successors, i);
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
// Remove edge from bb to the successor node
|
||||
raviX_delete_edge(proc->cfg, bb->index, nodes[i]);
|
||||
}
|
||||
free(nodes);
|
||||
assert(raviX_node_list_size(successors) == 0); // All should be gone
|
||||
// Now clear out this bb
|
||||
// FIXME deallocate instructions
|
||||
raviX_ptrlist_remove_all((struct ptr_list **)&bb->insns);
|
||||
// FIXME do we deallocate bb?
|
||||
return 1; // We changed something
|
||||
}
|
||||
|
||||
static int process_proc(LinearizerState *linearizer, Proc *proc)
|
||||
{
|
||||
if (proc->cfg == NULL) {
|
||||
if (raviX_construct_cfg(proc) != 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int changed = 1;
|
||||
while (changed) {
|
||||
changed = 0;
|
||||
BasicBlock *bb;
|
||||
for (int i = 0; i < (int)proc->node_count; i++) {
|
||||
bb = proc->nodes[i];
|
||||
if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
|
||||
continue;
|
||||
changed |= process_block(linearizer, proc, bb);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int raviX_remove_unreachable_blocks(LinearizerState *linearizer)
|
||||
{
|
||||
Proc *proc;
|
||||
FOR_EACH_PTR(linearizer->all_procs, proc)
|
||||
{
|
||||
if (process_proc(linearizer, proc) != 0)
|
||||
return 1;
|
||||
}
|
||||
END_FOR_EACH_PTR(proc)
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
#ifndef ravicomp_OPTIMIZER_H
|
||||
#define ravicomp_OPTIMIZER_H
|
||||
|
||||
/**
|
||||
* Remove blocks that are unreachable. Blocks ae logically deleted by removing
|
||||
* all instructions, rather than being physically removed.
|
||||
*/
|
||||
extern int raviX_remove_unreachable_blocks(LinearizerState *linearizer);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,376 @@
|
||||
#ifndef ravicomp_IMPLEMENTATION_H
|
||||
#define ravicomp_IMPLEMENTATION_H
|
||||
|
||||
/*
|
||||
* Internal header file for the implementation.
|
||||
* The data structures defined here are private.
|
||||
*/
|
||||
|
||||
#include "ravi_compiler.h"
|
||||
|
||||
#include "allocate.h"
|
||||
#include "membuf.h"
|
||||
#include "ptrlist.h"
|
||||
#include "set.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
enum { MAXVARS = 125 };
|
||||
#define LUA_ENV "_ENV"
|
||||
#define LUA_MAXINTEGER LLONG_MAX
|
||||
#define LUA_MININTEGER LLONG_MIN
|
||||
|
||||
typedef unsigned long long lua_Unsigned;
|
||||
typedef unsigned char lu_byte;
|
||||
|
||||
//////////////////////////
|
||||
|
||||
typedef struct AstNode AstNode;
|
||||
|
||||
/*
|
||||
* Encapsulate all the compiler state.
|
||||
* All memory is held by this object or sub-objects. Memory is freed when
|
||||
* the object is destroyed.
|
||||
*/
|
||||
struct CompilerState {
|
||||
Allocator ast_node_allocator;
|
||||
Allocator ptrlist_allocator;
|
||||
Allocator block_scope_allocator;
|
||||
Allocator symbol_allocator;
|
||||
Allocator string_allocator;
|
||||
Allocator string_object_allocator;
|
||||
Set *strings;
|
||||
AstNode *main_function;
|
||||
LinearizerState *linearizer;
|
||||
int (*error_handler)(const char *fmt, ...);
|
||||
TextBuffer buff; /* temp storage for literals, used by the lexer and parser */
|
||||
jmp_buf env; /* For error handling */
|
||||
TextBuffer error_message; /* For error handling, error message is saved here */
|
||||
bool killed; /* flag to check if this is already destroyed */
|
||||
const StringObject *_ENV; /* name of the env variable */
|
||||
};
|
||||
|
||||
/* number of reserved words */
|
||||
#define NUM_RESERVED ((int)(TOK_while - FIRST_RESERVED + 1))
|
||||
|
||||
/* state of the lexer plus state of the parser when shared by all
|
||||
functions */
|
||||
struct LexerState {
|
||||
int current; /* current character (charint) */
|
||||
int linenumber; /* input line counter */
|
||||
int lastline; /* line of last token 'consumed' */
|
||||
Token t; /* current token */
|
||||
Token lookahead; /* look ahead token */
|
||||
CompilerState *container;
|
||||
const char *buf;
|
||||
size_t bufsize;
|
||||
size_t n;
|
||||
const char *p;
|
||||
TextBuffer *buff; /* buffer for tokens, points to the buffer in compiler_state */
|
||||
const char *source; /* current source name */
|
||||
const char *envn; /* environment variable name */
|
||||
};
|
||||
void raviX_syntaxerror(LexerState *ls, const char *msg);
|
||||
|
||||
DECLARE_PTR_LIST(AstNodeList, AstNode);
|
||||
|
||||
/* RAVI: Following are the types we will use
|
||||
** use in parsing. The rationale for types is
|
||||
** performance - as of now these are the only types that
|
||||
** we care about from a performance point of view - if any
|
||||
** other types appear then they are all treated as ANY
|
||||
**/
|
||||
typedef enum {
|
||||
RAVI_TANY = 0, /* Lua dynamic type */
|
||||
RAVI_TNUMINT = 1, /* integer number */
|
||||
RAVI_TNUMFLT, /* floating point number */
|
||||
RAVI_TARRAYINT, /* array of ints */
|
||||
RAVI_TARRAYFLT, /* array of doubles */
|
||||
RAVI_TFUNCTION, /* Lua or C Function */
|
||||
RAVI_TTABLE, /* Lua table */
|
||||
RAVI_TSTRING, /* string */
|
||||
RAVI_TNIL, /* NIL */
|
||||
RAVI_TBOOLEAN, /* boolean */
|
||||
RAVI_TUSERDATA, /* userdata or lightuserdata */
|
||||
RAVI_TVARARGS /* Not a real type - represents ... */
|
||||
} ravitype_t;
|
||||
|
||||
/* Lua type info. We need to support user defined types too which are known by name */
|
||||
struct VariableType {
|
||||
ravitype_t type_code;
|
||||
/* type name for user defined types; used to lookup metatable in registry, only set when type_code is
|
||||
* RAVI_TUSERDATA */
|
||||
const StringObject *type_name;
|
||||
};
|
||||
|
||||
typedef struct Pseudo Pseudo;
|
||||
DECLARE_PTR_LIST(LuaSymbolList, LuaSymbol);
|
||||
|
||||
struct LuaVariableSymbol {
|
||||
VariableType value_type;
|
||||
const StringObject *var_name; /* name of the variable */
|
||||
Scope *block; /* NULL if global symbol, as globals are never added to a scope */
|
||||
LuaSymbol *env; /* Only applicable for global symbols - this should point to _ENV */
|
||||
unsigned escaped: 1, /* Has one or more up-value references */
|
||||
function_parameter: 1; /* Is a function parameter */
|
||||
Pseudo *pseudo; /* backend data for the symbol */
|
||||
};
|
||||
struct LuaLabelSymbol {
|
||||
const StringObject *label_name;
|
||||
Scope *block;
|
||||
Pseudo* pseudo; /* backend data for the symbol */
|
||||
};
|
||||
struct LuaUpvalueSymbol {
|
||||
VariableType value_type;
|
||||
LuaSymbol *target_variable; /* variable reference */
|
||||
AstNode *target_function; /* Where the upvalue lives */
|
||||
unsigned upvalue_index : 16, /* index of the upvalue in the function where this upvalue occurs */
|
||||
is_in_parent_stack : 1, /* 1 if yes - populated by code generator only */
|
||||
parent_upvalue_index : 15; /* if !is_in_parent_stack then upvalue index in parent - populated by code generator only */
|
||||
/*TODO add pseudo ?*/
|
||||
};
|
||||
/* A symbol is a name recognised in Ravi/Lua code*/
|
||||
struct LuaSymbol {
|
||||
enum SymbolType symbol_type;
|
||||
union {
|
||||
LuaVariableSymbol variable;
|
||||
LuaLabelSymbol label;
|
||||
LuaUpvalueSymbol upvalue;
|
||||
};
|
||||
};
|
||||
struct Scope {
|
||||
AstNode *function; /* function owning this block - of type FUNCTION_EXPR */
|
||||
Scope *parent; /* parent block, may belong to parent function */
|
||||
LuaSymbolList *symbol_list; /* symbols defined in this block */
|
||||
unsigned need_close: 1; /* When we exit scope of this block the upvalues need to be closed */
|
||||
};
|
||||
|
||||
/*STMT_RETURN */
|
||||
struct ReturnStatement {
|
||||
AstNodeList *expr_list;
|
||||
};
|
||||
/* STMT_LABEL */
|
||||
struct LabelStatement {
|
||||
LuaSymbol *symbol;
|
||||
};
|
||||
/* STMT_GOTO */
|
||||
struct GotoStatement {
|
||||
unsigned is_break : 1; /* is this a break statement */
|
||||
const StringObject *name; /* target label, used to resolve the goto destination */
|
||||
Scope* goto_scope; /* The scope of the goto statement */
|
||||
};
|
||||
/* STMT_LOCAL local variable declarations */
|
||||
struct LocalStatement {
|
||||
LuaSymbolList *var_list;
|
||||
AstNodeList *expr_list;
|
||||
};
|
||||
/* STMT_EXPR: Also covers assignments */
|
||||
struct ExpressionStatement {
|
||||
AstNodeList *var_expr_list; /* Optional var expressions, comma separated */
|
||||
AstNodeList *expr_list; /* Comma separated expressions */
|
||||
};
|
||||
struct FunctionStatement {
|
||||
AstNode *name; /* base symbol to be looked up - symbol_expression */
|
||||
AstNodeList *selectors; /* Optional list of index_expression(s) */
|
||||
AstNode *method_name; /* Optional - index_expression */
|
||||
AstNode *function_expr; /* Function's AST - function_expression */
|
||||
};
|
||||
struct DoStatement {
|
||||
Scope *scope; /* The do statement only creates a new scope */
|
||||
AstNodeList *do_statement_list; /* statements in this block */
|
||||
};
|
||||
/* Used internally in if_stmt, not an independent AST node */
|
||||
struct TestThenStatement {
|
||||
AstNode *condition;
|
||||
Scope *test_then_scope;
|
||||
AstNodeList *test_then_statement_list; /* statements in this block */
|
||||
};
|
||||
struct IfStatement {
|
||||
AstNodeList *if_condition_list; /* Actually a list of test_then_blocks */
|
||||
Scope *else_block;
|
||||
AstNodeList *else_statement_list; /* statements in this block */
|
||||
};
|
||||
struct WhileOrRepeatStatement {
|
||||
AstNode *condition;
|
||||
Scope *loop_scope;
|
||||
AstNodeList *loop_statement_list; /* statements in this block */
|
||||
};
|
||||
/* Used for both generic and numeric for loops */
|
||||
struct ForStatement {
|
||||
Scope* for_scope; /* encapsulates the entire for statement */
|
||||
LuaSymbolList *symbols;
|
||||
AstNodeList *expr_list;
|
||||
Scope *for_body;
|
||||
AstNodeList *for_statement_list; /* statements in this block */
|
||||
};
|
||||
/* To access the type field common to all expr objects */
|
||||
/* all expr types must be compatible with base_expression */
|
||||
|
||||
#define BASE_EXPRESSION_FIELDS VariableType type; unsigned truncate_results: 1
|
||||
|
||||
typedef struct BaseExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
} BaseExpression;
|
||||
|
||||
struct LiteralExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
SemInfo u;
|
||||
};
|
||||
/* primaryexp -> NAME | '(' expr ')', NAME is parsed as EXPR_SYMBOL */
|
||||
struct SymbolExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
LuaSymbol *var;
|
||||
};
|
||||
/* EXPR_Y_INDEX or EXPR_FIELD_SELECTOR */
|
||||
struct IndexExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
AstNode *expr; /* '[' expr ']' */
|
||||
};
|
||||
/* EXPR_UNARY */
|
||||
struct UnaryExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
UnaryOperatorType unary_op;
|
||||
AstNode *expr;
|
||||
};
|
||||
struct BinaryExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
BinaryOperatorType binary_op;
|
||||
AstNode *expr_left;
|
||||
AstNode *expr_right;
|
||||
};
|
||||
struct FunctionExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
unsigned is_vararg : 1;
|
||||
unsigned is_method : 1;
|
||||
unsigned need_close : 1;
|
||||
uint32_t proc_id; /* Backend allocated id */
|
||||
AstNode *parent_function; /* parent function or NULL if main chunk */
|
||||
Scope *main_block; /* the function's main block */
|
||||
AstNodeList *function_statement_list; /* statements in this block */
|
||||
LuaSymbolList
|
||||
*args; /* arguments, also must be part of the function block's symbol list */
|
||||
AstNodeList *child_functions; /* child functions declared in this function */
|
||||
LuaSymbolList *upvalues; /* List of upvalues */
|
||||
LuaSymbolList *locals; /* List of locals */
|
||||
};
|
||||
/* Assign values in table constructor */
|
||||
/* EXPR_TABLE_ELEMENT_ASSIGN - used in table constructor */
|
||||
struct TableElementAssignmentExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
AstNode *key_expr; /* If NULL means this is a list field with next available index,
|
||||
else specifies index expression */
|
||||
AstNode *value_expr;
|
||||
};
|
||||
/* constructor -> '{' [ field { sep field } [sep] ] '}' where sep -> ',' | ';' */
|
||||
/* table constructor expression EXPR_TABLE_LITERAL occurs in function call and simple expr */
|
||||
struct TableLiteralExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
AstNodeList *expr_list;
|
||||
};
|
||||
/* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
|
||||
/* suffix_list may have EXPR_FIELD_SELECTOR, EXPR_Y_INDEX, EXPR_FUNCTION_CALL */
|
||||
struct SuffixedExpression {
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
AstNode *primary_expr;
|
||||
AstNodeList *suffix_list;
|
||||
};
|
||||
struct FunctionCallExpression {
|
||||
/* Note that in Ravi the results from a function call must be type asserted during assignment to
|
||||
* variables. This is not explicit in the AST but is required to ensure that function return
|
||||
* values do not overwrite the type of the variables in an inconsistent way.
|
||||
*/
|
||||
BASE_EXPRESSION_FIELDS;
|
||||
const StringObject *method_name; /* Optional method_name */
|
||||
AstNodeList *arg_list; /* Call arguments */
|
||||
int num_results; /* How many results do we expect, -1 means all available results */
|
||||
};
|
||||
#undef BASE_EXPRESSION_FIELDS
|
||||
|
||||
/* ALL AST nodes start with following fields */
|
||||
#define BASE_AST_FIELDS enum AstNodeType type; int line_number
|
||||
/* Statement AST nodes have following common fields.
|
||||
*/
|
||||
struct Statement {
|
||||
BASE_AST_FIELDS;
|
||||
};
|
||||
/* Expression AST nodes have following common fields
|
||||
*/
|
||||
struct Expression {
|
||||
BASE_AST_FIELDS;
|
||||
BaseExpression common_expr;
|
||||
};
|
||||
|
||||
/* The parse tree is made up of ast_node objects. Some of the ast_nodes reference the appropriate block
|
||||
scopes but not all scopes may be referenced. The tree captures Lua syntax tree - i.e. statements such as
|
||||
while, repeat, and for are captured in the way user uses them and not the way Lua generates code. Potentially
|
||||
we can have a transformation step to convert to a tree that is more like the code generation
|
||||
|
||||
The ast_node must be aligned with Expression for expressions, and with Statement for statements.
|
||||
*/
|
||||
struct AstNode {
|
||||
BASE_AST_FIELDS;
|
||||
union {
|
||||
ReturnStatement return_stmt; /*STMT_RETURN */
|
||||
LabelStatement label_stmt; /* STMT_LABEL */
|
||||
GotoStatement goto_stmt; /* STMT_GOTO */
|
||||
LocalStatement local_stmt; /* STMT_LOCAL local variable declarations */
|
||||
ExpressionStatement expression_stmt;
|
||||
FunctionStatement function_stmt;
|
||||
DoStatement do_stmt;
|
||||
TestThenStatement test_then_block;
|
||||
IfStatement if_stmt;
|
||||
WhileOrRepeatStatement while_or_repeat_stmt;
|
||||
ForStatement for_stmt;
|
||||
BaseExpression common_expr;
|
||||
LiteralExpression literal_expr;
|
||||
SymbolExpression symbol_expr;
|
||||
IndexExpression index_expr;
|
||||
UnaryExpression unary_expr;
|
||||
BinaryExpression binary_expr;
|
||||
FunctionExpression function_expr; /* a literal expression whose result is a value of type function */
|
||||
TableElementAssignmentExpression table_elem_assign_expr;
|
||||
TableLiteralExpression table_expr;
|
||||
SuffixedExpression suffixed_expr;
|
||||
FunctionCallExpression function_call_expr;
|
||||
};
|
||||
};
|
||||
#undef BASE_AST_FIELDS
|
||||
|
||||
static inline void set_typecode(VariableType *vt, ravitype_t t) { vt->type_code = t; }
|
||||
static inline void set_type(VariableType *vt, ravitype_t t)
|
||||
{
|
||||
vt->type_code = t;
|
||||
vt->type_name = NULL;
|
||||
}
|
||||
static inline void set_typename(VariableType *vt, ravitype_t t, const StringObject *name)
|
||||
{
|
||||
vt->type_code = t;
|
||||
vt->type_name = name;
|
||||
}
|
||||
static inline void copy_type(VariableType *a, const VariableType *b)
|
||||
{
|
||||
a->type_code = b->type_code;
|
||||
a->type_name = b->type_name;
|
||||
}
|
||||
|
||||
struct parser_state {
|
||||
LexerState *ls;
|
||||
CompilerState *container;
|
||||
AstNode *current_function;
|
||||
Scope *current_scope;
|
||||
};
|
||||
|
||||
void raviX_print_ast_node(TextBuffer *buf, AstNode *node, int level); /* output the AST structure recursively */
|
||||
const char *raviX_get_type_name(ravitype_t tt);
|
||||
|
||||
int raviX_ast_simplify(CompilerState* container);
|
||||
|
||||
#endif
|
@ -0,0 +1,987 @@
|
||||
/*
|
||||
* ptrlist.c
|
||||
*
|
||||
* Pointer ptrlist_t manipulation
|
||||
*
|
||||
* (C) Copyright Linus Torvalds 2003-2005
|
||||
*/
|
||||
/*
|
||||
* This version is part of the dmr_c project.
|
||||
* Copyright (C) 2017 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#define PARANOIA 1
|
||||
|
||||
#include <ptrlist.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The ptr list */
|
||||
|
||||
/* For testing we change this */
|
||||
static int N_ = LIST_NODE_NR;
|
||||
|
||||
void raviX_ptrlist_split_node(struct ptr_list *head)
|
||||
{
|
||||
int old = head->nr_, nr = old / 2;
|
||||
Allocator *alloc = head->allocator_;
|
||||
assert(alloc);
|
||||
struct ptr_list *newlist = (struct ptr_list *)raviX_allocator_allocate(alloc, 0);
|
||||
struct ptr_list *next = head->next_;
|
||||
newlist->allocator_ = alloc;
|
||||
|
||||
old -= nr;
|
||||
head->nr_ = old;
|
||||
newlist->next_ = next;
|
||||
next->prev_ = newlist;
|
||||
newlist->prev_ = head;
|
||||
head->next_ = newlist;
|
||||
newlist->nr_ = nr;
|
||||
memcpy(newlist->list_, head->list_ + old, nr * sizeof(void *));
|
||||
memset(head->list_ + old, 0xf0, nr * sizeof(void *));
|
||||
}
|
||||
|
||||
PtrListIterator raviX_ptrlist_forward_iterator(struct ptr_list *head)
|
||||
{
|
||||
PtrListIterator iter;
|
||||
iter.__head = iter.__list = head;
|
||||
iter.__nr = -1;
|
||||
return iter;
|
||||
}
|
||||
|
||||
// Reverse iterator has to start from previous node not previous entry
|
||||
// in the given head
|
||||
PtrListIterator raviX_ptrlist_reverse_iterator(struct ptr_list *head)
|
||||
{
|
||||
PtrListIterator iter;
|
||||
iter.__head = iter.__list = head ? head->prev_ : NULL;
|
||||
iter.__nr = iter.__head ? iter.__head->nr_ : 0;
|
||||
return iter;
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_iter_next(PtrListIterator *self)
|
||||
{
|
||||
if (self->__head == NULL)
|
||||
return NULL;
|
||||
self->__nr++;
|
||||
Lretry:
|
||||
if (self->__nr < self->__list->nr_) {
|
||||
void *ptr = self->__list->list_[self->__nr];
|
||||
if (self->__list->rm_ && !ptr) {
|
||||
self->__nr++;
|
||||
goto Lretry;
|
||||
}
|
||||
return ptr;
|
||||
} else if (self->__list->next_ != self->__head) {
|
||||
self->__list = self->__list->next_;
|
||||
self->__nr = 0;
|
||||
goto Lretry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_nth_entry(struct ptr_list *list, unsigned int idx)
|
||||
{
|
||||
struct ptr_list *head = list;
|
||||
if (!head)
|
||||
return NULL;
|
||||
do {
|
||||
unsigned int nr = list->nr_;
|
||||
if (idx < nr)
|
||||
return list->list_[idx];
|
||||
else
|
||||
idx -= nr;
|
||||
} while ((list = list->next_) != head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_iter_prev(PtrListIterator *self)
|
||||
{
|
||||
if (self->__head == NULL)
|
||||
return NULL;
|
||||
self->__nr--;
|
||||
Lretry:
|
||||
if (self->__nr >= 0 && self->__nr < self->__list->nr_) {
|
||||
void *ptr = self->__list->list_[self->__nr];
|
||||
if (self->__list->rm_ && !ptr) {
|
||||
self->__nr--;
|
||||
goto Lretry;
|
||||
}
|
||||
return ptr;
|
||||
} else if (self->__list->prev_ != self->__head) {
|
||||
self->__list = self->__list->prev_;
|
||||
self->__nr = self->__list->nr_ - 1;
|
||||
goto Lretry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void raviX_ptrlist_iter_split_current(PtrListIterator *self)
|
||||
{
|
||||
if (self->__list->nr_ == N_) {
|
||||
/* full so split */
|
||||
raviX_ptrlist_split_node(self->__list);
|
||||
if (self->__nr >= self->__list->nr_) {
|
||||
self->__nr -= self->__list->nr_;
|
||||
self->__list = self->__list->next_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_ptrlist_iter_insert(PtrListIterator *self, void *newitem)
|
||||
{
|
||||
assert(self->__nr >= 0);
|
||||
raviX_ptrlist_iter_split_current(self);
|
||||
void **__this = self->__list->list_ + self->__nr;
|
||||
void **__last = self->__list->list_ + self->__list->nr_ - 1;
|
||||
while (__last >= __this) {
|
||||
__last[1] = __last[0];
|
||||
__last--;
|
||||
}
|
||||
*__this = newitem;
|
||||
self->__list->nr_++;
|
||||
}
|
||||
|
||||
void raviX_ptrlist_iter_remove(PtrListIterator *self)
|
||||
{
|
||||
assert(self->__nr >= 0);
|
||||
void **__this = self->__list->list_ + self->__nr;
|
||||
void **__last = self->__list->list_ + self->__list->nr_ - 1;
|
||||
while (__this < __last) {
|
||||
__this[0] = __this[1];
|
||||
__this++;
|
||||
}
|
||||
*__this = (void *)((uintptr_t)0xf0f0f0f0);
|
||||
self->__list->nr_--;
|
||||
self->__nr--;
|
||||
}
|
||||
|
||||
void raviX_ptrlist_iter_set(PtrListIterator *self, void *ptr)
|
||||
{
|
||||
assert(self->__list && self->__nr >= 0 && self->__nr < self->__list->nr_);
|
||||
self->__list->list_[self->__nr] = ptr;
|
||||
}
|
||||
|
||||
void raviX_ptrlist_iter_mark_deleted(PtrListIterator *self)
|
||||
{
|
||||
raviX_ptrlist_iter_set(self, NULL);
|
||||
self->__list->rm_++;
|
||||
}
|
||||
|
||||
int raviX_ptrlist_size(const struct ptr_list *self)
|
||||
{
|
||||
int nr = 0;
|
||||
if (self) {
|
||||
const struct ptr_list *list = self;
|
||||
do {
|
||||
nr += list->nr_ - list->rm_;
|
||||
} while ((list = list->next_) != self);
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
void **raviX_ptrlist_add(struct ptr_list **self, void *ptr, Allocator *ptr_list_allocator)
|
||||
{
|
||||
struct ptr_list *list = *self;
|
||||
struct ptr_list *last = NULL;
|
||||
void **ret;
|
||||
int nr;
|
||||
|
||||
if (!list || (nr = (last = list->prev_)->nr_) >= N_) {
|
||||
struct ptr_list *newlist = (struct ptr_list *)raviX_allocator_allocate(ptr_list_allocator, 0);
|
||||
newlist->allocator_ = ptr_list_allocator;
|
||||
if (!list) {
|
||||
newlist->next_ = newlist;
|
||||
newlist->prev_ = newlist;
|
||||
*self = newlist;
|
||||
} else {
|
||||
newlist->prev_ = last;
|
||||
newlist->next_ = list;
|
||||
list->prev_ = newlist;
|
||||
last->next_ = newlist;
|
||||
}
|
||||
last = newlist;
|
||||
nr = 0;
|
||||
}
|
||||
ret = last->list_ + nr;
|
||||
*ret = ptr;
|
||||
nr++;
|
||||
last->nr_ = nr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_first(struct ptr_list *list)
|
||||
{
|
||||
if (!list)
|
||||
return NULL;
|
||||
return list->list_[0];
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_last(struct ptr_list *list)
|
||||
{
|
||||
if (!list)
|
||||
return NULL;
|
||||
list = list->prev_;
|
||||
return list->list_[list->nr_ - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Linearize the entries of a list up to a total of 'max',
|
||||
* and return the nr of entries linearized.
|
||||
*
|
||||
* The array to linearize into (second argument) should really
|
||||
* be "void *x[]", but we want to let people fill in any kind
|
||||
* of pointer array, so let's just call it "void **".
|
||||
*/
|
||||
int raviX_ptrlist_linearize(struct ptr_list *head, void **arr, int max)
|
||||
{
|
||||
int nr = 0;
|
||||
if (head && max > 0) {
|
||||
struct ptr_list *list = head;
|
||||
|
||||
do {
|
||||
int i = list->nr_;
|
||||
if (i > max)
|
||||
i = max;
|
||||
memcpy(arr, list->list_, i * sizeof(void *));
|
||||
arr += i;
|
||||
nr += i;
|
||||
max -= i;
|
||||
if (!max)
|
||||
break;
|
||||
} while ((list = list->next_) != head);
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we've walked the list and deleted entries,
|
||||
* we may need to re-pack it so that we don't have
|
||||
* any empty blocks left (empty blocks upset the
|
||||
* walking code
|
||||
*/
|
||||
void raviX_ptrlist_pack(struct ptr_list **self)
|
||||
{
|
||||
struct ptr_list *head = *self;
|
||||
|
||||
if (head) {
|
||||
struct ptr_list *entry = head;
|
||||
do {
|
||||
struct ptr_list *next;
|
||||
restart:
|
||||
next = entry->next_;
|
||||
if (!entry->nr_) {
|
||||
struct ptr_list *prev;
|
||||
if (next == entry) {
|
||||
raviX_allocator_free(entry->allocator_, entry);
|
||||
*self = NULL;
|
||||
return;
|
||||
}
|
||||
prev = entry->prev_;
|
||||
prev->next_ = next;
|
||||
next->prev_ = prev;
|
||||
raviX_allocator_free(entry->allocator_, entry);
|
||||
if (entry == head) {
|
||||
*self = next;
|
||||
head = next;
|
||||
entry = next;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
entry = next;
|
||||
} while (entry != head);
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_ptrlist_remove_all(struct ptr_list **self)
|
||||
{
|
||||
struct ptr_list *tmp, *list = *self;
|
||||
if (!list)
|
||||
return;
|
||||
list->prev_->next_ = NULL;
|
||||
while (list) {
|
||||
tmp = list;
|
||||
list = list->next_;
|
||||
raviX_allocator_free(tmp->allocator_, tmp);
|
||||
}
|
||||
*self = NULL;
|
||||
}
|
||||
|
||||
int raviX_ptrlist_remove(struct ptr_list **self, void *entry, int count)
|
||||
{
|
||||
PtrListIterator iter = raviX_ptrlist_forward_iterator(*self);
|
||||
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
|
||||
if (ptr == entry) {
|
||||
raviX_ptrlist_iter_remove(&iter);
|
||||
if (!--count)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
assert(count <= 0);
|
||||
out:
|
||||
raviX_ptrlist_pack(self);
|
||||
return count;
|
||||
}
|
||||
|
||||
int raviX_ptrlist_replace(struct ptr_list **self, void *old_ptr, void *new_ptr, int count)
|
||||
{
|
||||
PtrListIterator iter = raviX_ptrlist_forward_iterator(*self);
|
||||
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
|
||||
if (ptr == old_ptr) {
|
||||
raviX_ptrlist_iter_set(&iter, new_ptr);
|
||||
if (!--count)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
assert(count <= 0);
|
||||
out:
|
||||
return count;
|
||||
}
|
||||
|
||||
/* This removes the last entry, but doesn't pack the ptr list */
|
||||
void *raviX_ptrlist_undo_last(struct ptr_list **self)
|
||||
{
|
||||
struct ptr_list *last, *first = *self;
|
||||
|
||||
if (!first)
|
||||
return NULL;
|
||||
last = first;
|
||||
do {
|
||||
last = last->prev_;
|
||||
if (last->nr_) {
|
||||
void *ptr;
|
||||
int nr = --last->nr_;
|
||||
ptr = last->list_[nr];
|
||||
last->list_[nr] = (void *)((intptr_t)0xf1f1f1f1);
|
||||
return ptr;
|
||||
}
|
||||
} while (last != first);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *raviX_ptrlist_delete_last(struct ptr_list **self)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
struct ptr_list *last, *first = *self;
|
||||
|
||||
if (!first)
|
||||
return NULL;
|
||||
last = first->prev_;
|
||||
if (last->nr_)
|
||||
ptr = last->list_[--last->nr_];
|
||||
if (last->nr_ <= 0) {
|
||||
first->prev_ = last->prev_;
|
||||
last->prev_->next_ = first;
|
||||
if (last == first)
|
||||
*self = NULL;
|
||||
raviX_allocator_free(last->allocator_, last);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void raviX_ptrlist_concat(struct ptr_list *a, struct ptr_list **self)
|
||||
{
|
||||
Allocator *alloc = NULL;
|
||||
PtrListIterator iter = raviX_ptrlist_forward_iterator(a);
|
||||
if (a)
|
||||
alloc = a->allocator_;
|
||||
else if (*self)
|
||||
alloc = (*self)->allocator_;
|
||||
else
|
||||
return;
|
||||
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
|
||||
raviX_ptrlist_add(self, ptr, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sort_list: a stable sort for lists.
|
||||
*
|
||||
* Time complexity: O(n*log n)
|
||||
* [assuming limited zero-element fragments]
|
||||
*
|
||||
* Space complexity: O(1).
|
||||
*
|
||||
* Stable: yes.
|
||||
*/
|
||||
|
||||
static void array_sort(void **ptr, int nr, void *userdata, int (*cmp)(void *, const void *, const void *))
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < nr; i++) {
|
||||
void *p = ptr[i];
|
||||
if (cmp(userdata, ptr[i - 1], p) > 0) {
|
||||
int j = i;
|
||||
do {
|
||||
ptr[j] = ptr[j - 1];
|
||||
if (!--j)
|
||||
break;
|
||||
} while (cmp(userdata, ptr[j - 1], p) > 0);
|
||||
ptr[j] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verify_sorted(struct ptr_list *l, int n, void *userdata, int (*cmp)(void *, const void *, const void *))
|
||||
{
|
||||
int i = 0;
|
||||
const void *a;
|
||||
struct ptr_list *head = l;
|
||||
|
||||
while (l->nr_ == 0) {
|
||||
l = l->next_;
|
||||
if (--n == 0)
|
||||
return;
|
||||
assert(l != head);
|
||||
}
|
||||
|
||||
a = l->list_[0];
|
||||
while (n > 0) {
|
||||
const void *b;
|
||||
if (++i >= l->nr_) {
|
||||
i = 0;
|
||||
l = l->next_;
|
||||
n--;
|
||||
assert(l != head || n == 0);
|
||||
continue;
|
||||
}
|
||||
b = l->list_[i];
|
||||
assert(cmp(userdata, a, b) <= 0);
|
||||
a = b;
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_to(struct ptr_list *b, void **buffer, int *nbuf)
|
||||
{
|
||||
int nr = b->nr_;
|
||||
assert(*nbuf >= nr);
|
||||
memcpy(b->list_, buffer, nr * sizeof(void *));
|
||||
*nbuf = *nbuf - nr;
|
||||
memmove(buffer, buffer + nr, *nbuf * sizeof(void *));
|
||||
}
|
||||
|
||||
static void dump_to(struct ptr_list *b, void **buffer, int nbuf)
|
||||
{
|
||||
assert(nbuf <= b->nr_);
|
||||
memcpy(b->list_, buffer, nbuf * sizeof(void *));
|
||||
}
|
||||
|
||||
// Merge two already-sorted sequences of blocks:
|
||||
// (b1_1, ..., b1_n) and (b2_1, ..., b2_m)
|
||||
// Since we may be moving blocks around, we return the new head
|
||||
// of the merged list.
|
||||
static struct ptr_list *merge_block_seqs(struct ptr_list *b1, int n, struct ptr_list *b2, int m, void *userdata,
|
||||
int (*cmp)(void *, const void *, const void *))
|
||||
{
|
||||
int i1 = 0, i2 = 0;
|
||||
void *buffer[2 * LIST_NODE_NR];
|
||||
int nbuf = 0;
|
||||
struct ptr_list *newhead = b1;
|
||||
|
||||
// printf ("Merging %d blocks at %p with %d blocks at %p\n", n, b1, m, b2);
|
||||
|
||||
// Skip empty blocks in b2.
|
||||
while (b2->nr_ == 0) {
|
||||
// BEEN_THERE('F');
|
||||
b2 = b2->next_;
|
||||
if (--m == 0) {
|
||||
// BEEN_THERE('G');
|
||||
return newhead;
|
||||
}
|
||||
}
|
||||
|
||||
// Do a quick skip in case entire blocks from b1 are
|
||||
// already less than smallest element in b2.
|
||||
while (b1->nr_ == 0 || cmp(userdata, PTR_ENTRY(b1, b1->nr_ - 1), PTR_ENTRY(b2, 0)) < 0) {
|
||||
// printf ("Skipping whole block.\n");
|
||||
// BEEN_THERE('H');
|
||||
b1 = b1->next_;
|
||||
if (--n == 0) {
|
||||
// BEEN_THERE('I');
|
||||
return newhead;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
void *d1 = PTR_ENTRY(b1, i1);
|
||||
void *d2 = PTR_ENTRY(b2, i2);
|
||||
|
||||
assert(i1 >= 0 && i1 < b1->nr_);
|
||||
assert(i2 >= 0 && i2 < b2->nr_);
|
||||
assert(b1 != b2);
|
||||
assert(n > 0);
|
||||
assert(m > 0);
|
||||
|
||||
if (cmp(userdata, d1, d2) <= 0) {
|
||||
// BEEN_THERE('J');
|
||||
buffer[nbuf++] = d1;
|
||||
// Element from b1 is smaller
|
||||
if (++i1 >= b1->nr_) {
|
||||
// BEEN_THERE('L');
|
||||
flush_to(b1, buffer, &nbuf);
|
||||
do {
|
||||
b1 = b1->next_;
|
||||
if (--n == 0) {
|
||||
// BEEN_THERE('O');
|
||||
while (b1 != b2) {
|
||||
// BEEN_THERE('P');
|
||||
flush_to(b1, buffer, &nbuf);
|
||||
b1 = b1->next_;
|
||||
}
|
||||
assert(nbuf == i2);
|
||||
dump_to(b2, buffer, nbuf);
|
||||
return newhead;
|
||||
}
|
||||
} while (b1->nr_ == 0);
|
||||
i1 = 0;
|
||||
}
|
||||
} else {
|
||||
// BEEN_THERE('K');
|
||||
// Element from b2 is smaller
|
||||
buffer[nbuf++] = d2;
|
||||
if (++i2 >= b2->nr_) {
|
||||
struct ptr_list *l = b2;
|
||||
// BEEN_THERE('M');
|
||||
// OK, we finished with b2. Pull it out
|
||||
// and plug it in before b1.
|
||||
|
||||
b2 = b2->next_;
|
||||
b2->prev_ = l->prev_;
|
||||
b2->prev_->next_ = b2;
|
||||
l->next_ = b1;
|
||||
l->prev_ = b1->prev_;
|
||||
l->next_->prev_ = l;
|
||||
l->prev_->next_ = l;
|
||||
|
||||
if (b1 == newhead) {
|
||||
// BEEN_THERE('N');
|
||||
newhead = l;
|
||||
}
|
||||
|
||||
flush_to(l, buffer, &nbuf);
|
||||
b2 = b2->prev_;
|
||||
do {
|
||||
b2 = b2->next_;
|
||||
if (--m == 0) {
|
||||
// BEEN_THERE('Q');
|
||||
assert(nbuf == i1);
|
||||
dump_to(b1, buffer, nbuf);
|
||||
return newhead;
|
||||
}
|
||||
} while (b2->nr_ == 0);
|
||||
i2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raviX_ptrlist_sort(struct ptr_list **plist, void *userdata, int (*cmp)(void *, const void *, const void *))
|
||||
{
|
||||
struct ptr_list *head = *plist, *list = head;
|
||||
int blocks = 1;
|
||||
|
||||
assert(N_ == LIST_NODE_NR);
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
// Sort all the sub-lists
|
||||
do {
|
||||
array_sort(list->list_, list->nr_, userdata, cmp);
|
||||
#ifdef PARANOIA
|
||||
verify_sorted(list, 1, userdata, cmp);
|
||||
#endif
|
||||
list = list->next_;
|
||||
} while (list != head);
|
||||
|
||||
// Merge the damn things together
|
||||
while (1) {
|
||||
struct ptr_list *block1 = head;
|
||||
|
||||
do {
|
||||
struct ptr_list *block2 = block1;
|
||||
struct ptr_list *next, *newhead;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < blocks; i++) {
|
||||
block2 = block2->next_;
|
||||
if (block2 == head) {
|
||||
if (block1 == head) {
|
||||
// BEEN_THERE('A');
|
||||
*plist = head;
|
||||
return;
|
||||
}
|
||||
// BEEN_THERE('B');
|
||||
goto next_pass;
|
||||
}
|
||||
}
|
||||
|
||||
next = block2;
|
||||
for (i = 0; i < blocks;) {
|
||||
next = next->next_;
|
||||
i++;
|
||||
if (next == head) {
|
||||
// BEEN_THERE('C');
|
||||
break;
|
||||
}
|
||||
// BEEN_THERE('D');
|
||||
}
|
||||
|
||||
newhead = merge_block_seqs(block1, blocks, block2, i, userdata, cmp);
|
||||
#ifdef PARANOIA
|
||||
verify_sorted(newhead, blocks + i, userdata, cmp);
|
||||
#endif
|
||||
if (block1 == head) {
|
||||
// BEEN_THERE('E');
|
||||
head = newhead;
|
||||
}
|
||||
block1 = next;
|
||||
} while (block1 != head);
|
||||
next_pass:
|
||||
blocks <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static int int_cmp(void *ud, const void *_a, const void *_b)
|
||||
{
|
||||
(void)ud;
|
||||
const int *a = (const int *)_a;
|
||||
const int *b = (const int *)_b;
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
#define MIN(_x, _y) ((_x) < (_y) ? (_x) : (_y))
|
||||
|
||||
static int test_sort()
|
||||
{
|
||||
int i, *e;
|
||||
const int N = 10000;
|
||||
|
||||
srand(N);
|
||||
for (i = 0; i < 1000; i++)
|
||||
(void)rand();
|
||||
|
||||
Allocator ptrlist_allocator;
|
||||
raviX_allocator_init(&ptrlist_allocator, "ptrlist_nodes", sizeof(struct ptr_list), __alignof__(struct ptr_list),
|
||||
CHUNK);
|
||||
Allocator int_allocator;
|
||||
raviX_allocator_init(&int_allocator, "ints", sizeof(int), __alignof__(int), CHUNK);
|
||||
struct ptr_list *int_list = NULL;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
e = (int *)raviX_allocator_allocate(&int_allocator, 0);
|
||||
*e = rand();
|
||||
raviX_ptrlist_add(&int_list, e, &ptrlist_allocator);
|
||||
}
|
||||
if (raviX_ptrlist_size(int_list) != N)
|
||||
return 1;
|
||||
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
|
||||
// Sort already sorted stuff.
|
||||
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
|
||||
|
||||
int *p = NULL;
|
||||
PtrListIterator iter = raviX_ptrlist_forward_iterator(int_list);
|
||||
int count = 0;
|
||||
for (int *k = (int *)raviX_ptrlist_iter_next(&iter); k != NULL; k = (int *)raviX_ptrlist_iter_next(&iter)) {
|
||||
if (p != NULL) {
|
||||
if (*k < *p)
|
||||
return 1;
|
||||
}
|
||||
p = k;
|
||||
count++;
|
||||
}
|
||||
if (count != N)
|
||||
return 1;
|
||||
|
||||
struct ptr_list *l = int_list, *l2;
|
||||
l2 = l;
|
||||
int expected_count = 0;
|
||||
do {
|
||||
l2->nr_ = MIN(l2->nr_, rand() % 3);
|
||||
for (i = 0; i < l2->nr_; i++) {
|
||||
*((int *)(l2->list_[i])) = rand();
|
||||
expected_count++;
|
||||
}
|
||||
l2 = l2->next_;
|
||||
} while (l2 != l);
|
||||
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
|
||||
|
||||
p = NULL;
|
||||
iter = raviX_ptrlist_forward_iterator(int_list);
|
||||
count = 0;
|
||||
for (int *k = (int *)raviX_ptrlist_iter_next(&iter); k != NULL; k = (int *)raviX_ptrlist_iter_next(&iter)) {
|
||||
if (p != NULL) {
|
||||
if (*k < *p)
|
||||
return 1;
|
||||
}
|
||||
p = k;
|
||||
count++;
|
||||
}
|
||||
if (count != expected_count)
|
||||
return 1;
|
||||
raviX_ptrlist_remove_all(&int_list);
|
||||
raviX_allocator_destroy(&int_allocator);
|
||||
raviX_allocator_destroy(&ptrlist_allocator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mystruct {
|
||||
int i;
|
||||
};
|
||||
|
||||
struct mytoken {
|
||||
const char *a;
|
||||
};
|
||||
|
||||
static int test_ptrlist_basics()
|
||||
{
|
||||
Allocator ptrlist_allocator;
|
||||
raviX_allocator_init(&ptrlist_allocator, "ptrlist_nodes", sizeof(struct ptr_list), __alignof__(struct ptr_list),
|
||||
CHUNK);
|
||||
Allocator token_allocator;
|
||||
raviX_allocator_init(&token_allocator, "ptr_list_tokens", sizeof(struct mytoken), __alignof__(struct mytoken),
|
||||
CHUNK);
|
||||
struct ptr_list *token_list = NULL;
|
||||
if (raviX_ptrlist_size(token_list) != 0)
|
||||
return 1;
|
||||
struct mytoken *tok1 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
struct mytoken **tok1p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok1, &ptrlist_allocator);
|
||||
if (raviX_ptrlist_size(token_list) != 1)
|
||||
return 1;
|
||||
if (tok1 != *tok1p)
|
||||
return 1;
|
||||
if (raviX_ptrlist_first(token_list) != tok1)
|
||||
return 1;
|
||||
if (raviX_ptrlist_last(token_list) != tok1)
|
||||
return 1;
|
||||
struct mytoken *tok2 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
struct mytoken **tok2p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok2, &ptrlist_allocator);
|
||||
if (raviX_ptrlist_size(token_list) != 2)
|
||||
return 1;
|
||||
struct mytoken *tok3 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
raviX_ptrlist_add(&token_list, tok3, &ptrlist_allocator);
|
||||
if (raviX_ptrlist_size(token_list) != 3)
|
||||
return 1;
|
||||
struct mytoken *tok4 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
raviX_ptrlist_add(&token_list, tok4, &ptrlist_allocator);
|
||||
if (raviX_ptrlist_size(token_list) != 4)
|
||||
return 1;
|
||||
struct mytoken *tok5 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
struct mytoken **tok5p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok5, &ptrlist_allocator);
|
||||
if (raviX_ptrlist_size(token_list) != 5)
|
||||
return 1;
|
||||
|
||||
if (tok2 != *tok2p)
|
||||
return 1;
|
||||
if (tok5 != *tok5p)
|
||||
return 1;
|
||||
if (raviX_ptrlist_first(token_list) != tok1)
|
||||
return 1;
|
||||
if (raviX_ptrlist_last(token_list) != tok5)
|
||||
return 1;
|
||||
struct mytoken *toks[5];
|
||||
int lin1 = raviX_ptrlist_linearize(token_list, (void **)toks, 5);
|
||||
if (lin1 != 5)
|
||||
return 1;
|
||||
if (toks[0] != tok1)
|
||||
return 1;
|
||||
if (toks[1] != tok2)
|
||||
return 1;
|
||||
if (toks[2] != tok3)
|
||||
return 1;
|
||||
if (toks[3] != tok4)
|
||||
return 1;
|
||||
if (toks[4] != tok5)
|
||||
return 1;
|
||||
if (raviX_ptrlist_size(token_list) != 5)
|
||||
return 1;
|
||||
raviX_ptrlist_pack(&token_list);
|
||||
if (raviX_ptrlist_size(token_list) != 5)
|
||||
return 1;
|
||||
|
||||
if (raviX_ptrlist_first(token_list) != tok1)
|
||||
return 1;
|
||||
if (raviX_ptrlist_last(token_list) != tok5)
|
||||
return 1;
|
||||
|
||||
const int X = 5 + 1;
|
||||
const int Y = X - 1;
|
||||
const int Z = Y - 1;
|
||||
PtrListIterator iter1 = raviX_ptrlist_forward_iterator(token_list);
|
||||
for (int i = 0; i < X; i++) {
|
||||
struct mytoken *tk = (struct mytoken *)raviX_ptrlist_iter_next(&iter1);
|
||||
if (tk == NULL) {
|
||||
if (i == Y)
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
if (tk != toks[i])
|
||||
return 1;
|
||||
}
|
||||
PtrListIterator iter2 = raviX_ptrlist_reverse_iterator(token_list);
|
||||
for (int i = 0; i < X; i++) {
|
||||
struct mytoken *tk = (struct mytoken *)raviX_ptrlist_iter_prev(&iter2);
|
||||
if (tk == NULL) {
|
||||
if (i == Y)
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
if (tk != toks[Z - i])
|
||||
return 1;
|
||||
}
|
||||
struct mytoken *tok0 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
|
||||
PtrListIterator iter3 = raviX_ptrlist_forward_iterator(token_list);
|
||||
if (!raviX_ptrlist_iter_next(&iter3))
|
||||
return 1;
|
||||
raviX_ptrlist_iter_insert(&iter3, tok0);
|
||||
if (raviX_ptrlist_size(token_list) != 6)
|
||||
return 1;
|
||||
if (raviX_ptrlist_first(token_list) != tok0)
|
||||
return 1;
|
||||
if (raviX_ptrlist_last(token_list) != tok5)
|
||||
return 1;
|
||||
|
||||
Allocator mystruct_allocator;
|
||||
raviX_allocator_init(&mystruct_allocator, "mystructs", sizeof(struct mystruct), __alignof__(struct mystruct),
|
||||
CHUNK);
|
||||
struct ptr_list *mystruct_list = NULL;
|
||||
|
||||
struct mystruct *s1 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s1->i = 1;
|
||||
struct mystruct *s2 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s2->i = 2;
|
||||
struct mystruct *s3 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s3->i = 3;
|
||||
struct mystruct *s4 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s4->i = 4;
|
||||
struct mystruct *s5 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s5->i = 5;
|
||||
struct mystruct *s6 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
|
||||
s6->i = 6;
|
||||
|
||||
raviX_ptrlist_add(&mystruct_list, s1, &ptrlist_allocator);
|
||||
raviX_ptrlist_add(&mystruct_list, s2, &ptrlist_allocator);
|
||||
raviX_ptrlist_add(&mystruct_list, s3, &ptrlist_allocator);
|
||||
raviX_ptrlist_add(&mystruct_list, s4, &ptrlist_allocator);
|
||||
raviX_ptrlist_add(&mystruct_list, s5, &ptrlist_allocator);
|
||||
raviX_ptrlist_add(&mystruct_list, s6, &ptrlist_allocator);
|
||||
|
||||
struct mystruct *serial1_expected[6] = {s1, s2, s3, s4, s5, s6};
|
||||
struct mystruct *serial1_got[6];
|
||||
raviX_ptrlist_linearize(mystruct_list, (void **)serial1_got, 6);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (serial1_expected[i] != serial1_got[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (raviX_ptrlist_remove(&mystruct_list, s3, 1) != 0)
|
||||
return 1;
|
||||
PtrListIterator iter4 = raviX_ptrlist_forward_iterator(mystruct_list);
|
||||
for (struct mystruct *p = (struct mystruct *)raviX_ptrlist_iter_next(&iter4); p != NULL;
|
||||
p = (struct mystruct *)raviX_ptrlist_iter_next(&iter4)) {
|
||||
if (p->i == 4)
|
||||
raviX_ptrlist_iter_remove(&iter4);
|
||||
}
|
||||
if (raviX_ptrlist_size(mystruct_list) != 4)
|
||||
return 1;
|
||||
|
||||
struct mystruct *serial3_expected[4] = {s1, s2, s5, s6};
|
||||
struct mystruct *serial3_got[4];
|
||||
int reverse_expected[2] = {2, 1};
|
||||
|
||||
int i = 0;
|
||||
struct mystruct *p;
|
||||
FOR_EACH_PTR(mystruct_list, p)
|
||||
{
|
||||
if (i == 4)
|
||||
return 1;
|
||||
serial3_got[i++] = p;
|
||||
if (i == 3) {
|
||||
struct mystruct *p2;
|
||||
int j = 0;
|
||||
RECURSE_PTR_REVERSE(p, p2)
|
||||
{
|
||||
if (j >= 2 || reverse_expected[j] != p2->i)
|
||||
return 1;
|
||||
j++;
|
||||
}
|
||||
END_FOR_EACH_PTR_REVERSE(p2);
|
||||
}
|
||||
}
|
||||
END_FOR_EACH_PTR(p);
|
||||
if (i != 4)
|
||||
return 1;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (serial3_expected[i] != serial3_got[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
PREPARE_PTR_LIST(mystruct_list, p);
|
||||
while (p != NULL) {
|
||||
if (i == 4)
|
||||
return 1;
|
||||
serial3_got[i++] = p;
|
||||
NEXT_PTR_LIST(p);
|
||||
}
|
||||
FINISH_PTR_LIST(p);
|
||||
if (i != 4)
|
||||
return 1;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (serial3_expected[i] != serial3_got[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
FOR_EACH_PTR_REVERSE(mystruct_list, p)
|
||||
{
|
||||
if (i == 4)
|
||||
return 1;
|
||||
serial3_got[i++] = p;
|
||||
if (i == 2) {
|
||||
struct mystruct *p3;
|
||||
int j = 0;
|
||||
RECURSE_PTR_REVERSE(p, p3)
|
||||
{
|
||||
if (j >= 2 || reverse_expected[j] != p3->i)
|
||||
return 1;
|
||||
j++;
|
||||
}
|
||||
END_FOR_EACH_PTR_REVERSE(p3);
|
||||
}
|
||||
}
|
||||
END_FOR_EACH_PTR_REVERSE(p);
|
||||
if (i != 4)
|
||||
return 1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (serial3_expected[3 - i] != serial3_got[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
raviX_ptrlist_remove_all(&token_list);
|
||||
raviX_ptrlist_remove_all(&mystruct_list);
|
||||
|
||||
raviX_allocator_destroy(&token_allocator);
|
||||
raviX_allocator_destroy(&mystruct_allocator);
|
||||
raviX_allocator_destroy(&ptrlist_allocator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_ptrlist()
|
||||
{
|
||||
if (test_sort() != 0)
|
||||
return 1;
|
||||
|
||||
/* For testing we set N_ temporarily */
|
||||
N_ = 2;
|
||||
int failure_count = test_ptrlist_basics();
|
||||
N_ = LIST_NODE_NR;
|
||||
|
||||
if (failure_count == 0)
|
||||
printf("ptrlist test okay\n");
|
||||
return failure_count;
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,149 @@
|
||||
#ifndef ravicomp_PTRLIST_H
|
||||
#define ravicomp_PTRLIST_H
|
||||
|
||||
/*
|
||||
* Generic pointer list manipulation code.
|
||||
*
|
||||
* (C) Copyright Linus Torvalds 2003-2005
|
||||
*/
|
||||
/*
|
||||
* This version is part of the dmr_c project.
|
||||
* Copyright (C) 2017 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include <allocate.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* The ptrlist data structure is copied from the Linux Sparse project.
|
||||
* It is essentially a dynamic array of pointers but the array is split up
|
||||
* into nodes that are linked together. Each node contains a small number of array entries.
|
||||
*
|
||||
* The ptr list data structure is like a train - with cars linked to each other.
|
||||
* Just as in a train each car has many seats, so in ptr list each "node" has
|
||||
* several entries. Unlike a train however, the ptr list is arranged as a ring,
|
||||
* i.e. the the front and back nodes are linked to each other. Hence there is no
|
||||
* such thing as a 'head' of the list - i.e. any node can be the head!
|
||||
*
|
||||
* The disadvantage of the ptrlist structure compared to a dynamic array is
|
||||
* that it consumes more memory to maintain the linked list data structure.
|
||||
*
|
||||
* The main advantage is that it is well suited to fixed sized memory
|
||||
* allocators as there is no resizing of memory already allocated, which happens
|
||||
* with dynamic arrays. The ptrlist is made up of fixed size nodes.
|
||||
*/
|
||||
|
||||
/* number of array entries per node */
|
||||
#ifndef LIST_NODE_NR
|
||||
#define LIST_NODE_NR (7)
|
||||
#endif
|
||||
|
||||
/* structure of a node */
|
||||
#define DECLARE_PTR_LIST(listname, type) \
|
||||
typedef struct listname { \
|
||||
int nr_ : 8; \
|
||||
int rm_ : 8; \
|
||||
struct listname *prev_; \
|
||||
struct listname *next_; \
|
||||
Allocator *allocator_; \
|
||||
type *list_[LIST_NODE_NR]; \
|
||||
} listname
|
||||
|
||||
/* Each node in the list */
|
||||
DECLARE_PTR_LIST(ptr_list, void);
|
||||
|
||||
/* The iterator strucure is used for looping */
|
||||
typedef struct PtrListIterator {
|
||||
struct ptr_list *__head;
|
||||
struct ptr_list *__list;
|
||||
int __nr;
|
||||
} PtrListIterator;
|
||||
|
||||
/* The ptr list */
|
||||
extern int raviX_ptrlist_size(const struct ptr_list *self);
|
||||
extern void **raviX_ptrlist_add(struct ptr_list **self, void *ptr, Allocator *ptr_list_allocator);
|
||||
extern void *raviX_ptrlist_nth_entry(struct ptr_list *list, unsigned int idx);
|
||||
extern void *raviX_ptrlist_first(struct ptr_list *list);
|
||||
extern void *raviX_ptrlist_last(struct ptr_list *list);
|
||||
extern int raviX_ptrlist_linearize(struct ptr_list *head, void **arr, int max);
|
||||
extern void raviX_ptrlist_split_node(struct ptr_list *head);
|
||||
extern void raviX_ptrlist_pack(struct ptr_list **self);
|
||||
extern void raviX_ptrlist_remove_all(struct ptr_list **self);
|
||||
extern int raviX_ptrlist_remove(struct ptr_list **self, void *entry, int count);
|
||||
extern int raviX_ptrlist_replace(struct ptr_list **self, void *old_ptr, void *new_ptr,
|
||||
int count);
|
||||
extern void *raviX_ptrlist_undo_last(struct ptr_list **self);
|
||||
extern void *raviX_ptrlist_delete_last(struct ptr_list **self);
|
||||
extern void raviX_ptrlist_concat(struct ptr_list *a, struct ptr_list **self);
|
||||
extern void raviX_ptrlist_sort(struct ptr_list **self, void *,
|
||||
int (*cmp)(void *, const void *, const void *));
|
||||
|
||||
/* iterator functions */
|
||||
extern PtrListIterator raviX_ptrlist_forward_iterator(struct ptr_list *self);
|
||||
extern PtrListIterator raviX_ptrlist_reverse_iterator(struct ptr_list *self);
|
||||
extern void *raviX_ptrlist_iter_next(PtrListIterator *self);
|
||||
extern void *raviX_ptrlist_iter_prev(PtrListIterator *self);
|
||||
extern void raviX_ptrlist_iter_split_current(PtrListIterator *self);
|
||||
extern void raviX_ptrlist_iter_insert(PtrListIterator *self, void *newitem);
|
||||
extern void raviX_ptrlist_iter_remove(PtrListIterator *self);
|
||||
extern void raviX_ptrlist_iter_set(PtrListIterator *self, void *ptr);
|
||||
extern void raviX_ptrlist_iter_mark_deleted(PtrListIterator *self);
|
||||
|
||||
static inline void **raviX_ptrlist_iter_this_address(PtrListIterator *self) {
|
||||
return &self->__list->list_[self->__nr];
|
||||
}
|
||||
#define ptr_list_empty(x) ((x) == NULL)
|
||||
#define PTR_ENTRY_NOTAG(h,i) ((h)->list_[i])
|
||||
#define PTR_ENTRY(h,i) (void *)(PTR_ENTRY_NOTAG(h,i))
|
||||
|
||||
#define FOR_EACH_PTR(list, var) \
|
||||
{ PtrListIterator var##iter__ = raviX_ptrlist_forward_iterator((struct ptr_list *)list); \
|
||||
for (var = raviX_ptrlist_iter_next(&var##iter__); var != NULL; var = raviX_ptrlist_iter_next(&var##iter__))
|
||||
#define END_FOR_EACH_PTR(var) }
|
||||
|
||||
#define FOR_EACH_PTR_REVERSE(list, var) \
|
||||
{ PtrListIterator var##iter__ = raviX_ptrlist_reverse_iterator((struct ptr_list *)list); \
|
||||
for (var = raviX_ptrlist_iter_prev(&var##iter__); var != NULL; var = raviX_ptrlist_iter_prev(&var##iter__))
|
||||
#define END_FOR_EACH_PTR_REVERSE(var) }
|
||||
|
||||
#define RECURSE_PTR_REVERSE(list, var) \
|
||||
{ PtrListIterator var##iter__ = list##iter__; \
|
||||
for (var = raviX_ptrlist_iter_prev(&var##iter__); var != NULL; var = raviX_ptrlist_iter_prev(&var##iter__))
|
||||
|
||||
#define PREPARE_PTR_LIST(list, var) \
|
||||
PtrListIterator var##iter__ = raviX_ptrlist_forward_iterator((struct ptr_list *)list); \
|
||||
var = raviX_ptrlist_iter_next(&var##iter__)
|
||||
|
||||
#define NEXT_PTR_LIST(var) \
|
||||
var = raviX_ptrlist_iter_next(&var##iter__)
|
||||
#define FINISH_PTR_LIST(var)
|
||||
|
||||
#define THIS_ADDRESS(type, var) \
|
||||
(type *)raviX_ptrlist_iter_this_address(&var##iter__)
|
||||
|
||||
#define DELETE_CURRENT_PTR(var) \
|
||||
raviX_ptrlist_iter_remove(&var##iter__)
|
||||
|
||||
#define REPLACE_CURRENT_PTR(type, var, replacement) \
|
||||
raviX_ptrlist_iter_set(&var##iter__, replacement)
|
||||
|
||||
#define INSERT_CURRENT(newval, var) \
|
||||
raviX_ptrlist_iter_insert(&var##iter__, newval)
|
||||
|
||||
#define MARK_CURRENT_DELETED(PTR_TYPE, var) \
|
||||
raviX_ptrlist_iter_mark_deleted(&var##iter__)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,66 @@
|
||||
/* This will contain Lua bindings */
|
||||
|
||||
#include "ravi_api.h"
|
||||
#include "ravi_compiler.h"
|
||||
|
||||
#include "cfg.h"
|
||||
#include "codegen.h"
|
||||
#include "optimizer.h"
|
||||
#include "parser.h"
|
||||
|
||||
int raviX_compile(struct Ravi_CompilerInterface *compiler_interface)
|
||||
{
|
||||
int rc = 0;
|
||||
int dump_ir = 0;
|
||||
if (compiler_interface->compiler_options != NULL) {
|
||||
dump_ir = strstr(compiler_interface->compiler_options, "--dump-ir") != NULL;
|
||||
}
|
||||
compiler_interface->generated_code = NULL;
|
||||
CompilerState *container = raviX_init_compiler();
|
||||
rc = raviX_parse(container, compiler_interface->source, compiler_interface->source_len,
|
||||
compiler_interface->source_name);
|
||||
if (rc != 0) {
|
||||
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
|
||||
goto L_exit;
|
||||
}
|
||||
rc = raviX_ast_typecheck(container);
|
||||
if (rc != 0) {
|
||||
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
|
||||
goto L_exit;
|
||||
}
|
||||
rc = raviX_ast_simplify(container);
|
||||
if (rc != 0) {
|
||||
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
|
||||
goto L_exit;
|
||||
}
|
||||
LinearizerState *linearizer = raviX_init_linearizer(container);
|
||||
rc = raviX_ast_linearize(linearizer);
|
||||
if (rc != 0) {
|
||||
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
|
||||
goto L_linend;
|
||||
}
|
||||
raviX_construct_cfg(linearizer->main_proc);
|
||||
raviX_remove_unreachable_blocks(linearizer);
|
||||
|
||||
TextBuffer buf;
|
||||
raviX_buffer_init(&buf, 4096);
|
||||
if (dump_ir) {
|
||||
raviX_buffer_add_string(&buf, "/* Following is an IR Dump from the compiler\n");
|
||||
raviX_show_linearizer(linearizer, &buf);
|
||||
raviX_buffer_add_string(&buf, "\nEnd of IR dump*/\n");
|
||||
}
|
||||
rc = raviX_generate_C(linearizer, &buf, compiler_interface);
|
||||
if (rc != 0) {
|
||||
raviX_buffer_free(&buf);
|
||||
} else {
|
||||
compiler_interface->generated_code = buf.buf;
|
||||
}
|
||||
|
||||
L_linend:
|
||||
raviX_destroy_linearizer(linearizer);
|
||||
|
||||
L_exit:
|
||||
raviX_destroy_compiler(container);
|
||||
|
||||
return rc;
|
||||
}
|
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
* Copyright © 1988-2004 Keith Packard and Bart Massey.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the names of the authors
|
||||
* or their institutions shall not be used in advertising or
|
||||
* otherwise to promote the sale, use or other dealings in this
|
||||
* Software without prior written authorization from the
|
||||
* authors.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Keith Packard <keithp@keithp.com>
|
||||
*/
|
||||
|
||||
#include "set.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
|
||||
|
||||
/*
|
||||
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
|
||||
* p and p-2 are both prime. These tables are sized to have an extra 10%
|
||||
* free to avoid exponential performance degradation as the hash table fills
|
||||
*/
|
||||
|
||||
static const uint32_t deleted_key_value;
|
||||
static const void *deleted_key = &deleted_key_value;
|
||||
|
||||
static const struct {
|
||||
uint32_t max_entries, size, rehash;
|
||||
} hash_sizes[] = {
|
||||
{ 2, 5, 3 },
|
||||
{ 4, 7, 5 },
|
||||
{ 8, 13, 11 },
|
||||
{ 16, 19, 17 },
|
||||
{ 32, 43, 41 },
|
||||
{ 64, 73, 71 },
|
||||
{ 128, 151, 149 },
|
||||
{ 256, 283, 281 },
|
||||
{ 512, 571, 569 },
|
||||
{ 1024, 1153, 1151 },
|
||||
{ 2048, 2269, 2267 },
|
||||
{ 4096, 4519, 4517 },
|
||||
{ 8192, 9013, 9011 },
|
||||
{ 16384, 18043, 18041 },
|
||||
{ 32768, 36109, 36107 },
|
||||
{ 65536, 72091, 72089 },
|
||||
{ 131072, 144409, 144407 },
|
||||
{ 262144, 288361, 288359 },
|
||||
{ 524288, 576883, 576881 },
|
||||
{ 1048576, 1153459, 1153457 },
|
||||
{ 2097152, 2307163, 2307161 },
|
||||
{ 4194304, 4613893, 4613891 },
|
||||
{ 8388608, 9227641, 9227639 },
|
||||
{ 16777216, 18455029, 18455027 },
|
||||
{ 33554432, 36911011, 36911009 },
|
||||
{ 67108864, 73819861, 73819859 },
|
||||
{ 134217728, 147639589, 147639587 },
|
||||
{ 268435456, 295279081, 295279079 },
|
||||
{ 536870912, 590559793, 590559791 },
|
||||
{ 1073741824, 1181116273, 1181116271},
|
||||
{ 2147483648ul, 2362232233ul, 2362232231ul}
|
||||
};
|
||||
|
||||
static int
|
||||
entry_is_free(const SetEntry *entry)
|
||||
{
|
||||
return entry->key == NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_deleted(const SetEntry *entry)
|
||||
{
|
||||
return entry->key == deleted_key;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_present(const SetEntry *entry)
|
||||
{
|
||||
return entry->key != NULL && entry->key != deleted_key;
|
||||
}
|
||||
|
||||
Set *raviX_set_create(uint32_t (*hash_function)(const void *key),
|
||||
int (*key_equals_function)(const void *a,
|
||||
const void *b))
|
||||
{
|
||||
Set *set;
|
||||
|
||||
set = malloc(sizeof(*set));
|
||||
if (set == NULL)
|
||||
return NULL;
|
||||
|
||||
set->size_index = 0;
|
||||
set->size = hash_sizes[set->size_index].size;
|
||||
set->rehash = hash_sizes[set->size_index].rehash;
|
||||
set->max_entries = hash_sizes[set->size_index].max_entries;
|
||||
set->hash_function = hash_function;
|
||||
set->key_equals_function = key_equals_function;
|
||||
set->table = calloc(set->size, sizeof(*set->table));
|
||||
set->entries = 0;
|
||||
set->deleted_entries = 0;
|
||||
|
||||
if (set->table == NULL) {
|
||||
free(set);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the given set.
|
||||
*
|
||||
* If delete_function is passed, it gets called on each entry present before
|
||||
* freeing.
|
||||
*/
|
||||
void raviX_set_destroy(Set *set, void (*delete_function)(SetEntry *entry))
|
||||
{
|
||||
if (!set)
|
||||
return;
|
||||
|
||||
if (delete_function) {
|
||||
SetEntry *entry;
|
||||
|
||||
set_foreach(set, entry) {
|
||||
delete_function(entry);
|
||||
}
|
||||
}
|
||||
free(set->table);
|
||||
free(set);
|
||||
}
|
||||
|
||||
/* Does the set contain an entry with the given key.
|
||||
*/
|
||||
bool raviX_set_contains(Set *set, const void *key)
|
||||
{
|
||||
SetEntry *entry;
|
||||
|
||||
entry = raviX_set_search(set, key);
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a set entry with the given key.
|
||||
*
|
||||
* Returns NULL if no entry is found.
|
||||
*/
|
||||
SetEntry *raviX_set_search(Set *set, const void *key)
|
||||
{
|
||||
uint32_t hash = set->hash_function(key);
|
||||
|
||||
return raviX_set_search_pre_hashed(set, hash, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a set entry with the given key and hash of that key.
|
||||
*
|
||||
* Returns NULL if no entry is found.
|
||||
*/
|
||||
SetEntry *raviX_set_search_pre_hashed(Set *set, uint32_t hash, const void *key)
|
||||
{
|
||||
uint32_t hash_address;
|
||||
|
||||
hash_address = hash % set->size;
|
||||
do {
|
||||
uint32_t double_hash;
|
||||
|
||||
SetEntry *entry = set->table + hash_address;
|
||||
|
||||
if (entry_is_free(entry)) {
|
||||
return NULL;
|
||||
} else if (entry_is_present(entry) && entry->hash == hash) {
|
||||
if (set->key_equals_function(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
double_hash = 1 + hash % set->rehash;
|
||||
|
||||
hash_address = (hash_address + double_hash) % set->size;
|
||||
} while (hash_address != hash % set->size);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
set_rehash(Set *set, int new_size_index)
|
||||
{
|
||||
Set old_set;
|
||||
SetEntry *table, *entry;
|
||||
|
||||
if (new_size_index >= ARRAY_SIZE(hash_sizes))
|
||||
return;
|
||||
|
||||
table = calloc(hash_sizes[new_size_index].size, sizeof(*set->table));
|
||||
if (table == NULL)
|
||||
return;
|
||||
|
||||
old_set = *set;
|
||||
|
||||
set->table = table;
|
||||
set->size_index = new_size_index;
|
||||
set->size = hash_sizes[set->size_index].size;
|
||||
set->rehash = hash_sizes[set->size_index].rehash;
|
||||
set->max_entries = hash_sizes[set->size_index].max_entries;
|
||||
set->entries = 0;
|
||||
set->deleted_entries = 0;
|
||||
|
||||
set_foreach(&old_set, entry) { raviX_set_add_pre_hashed(set, entry->hash, entry->key);
|
||||
}
|
||||
|
||||
free(old_set.table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the key into the set.
|
||||
*
|
||||
* Note that insertion may rearrange the set on a resize or rehash, so
|
||||
* previously found set_entry pointers are no longer valid after this
|
||||
* function.
|
||||
*/
|
||||
SetEntry *raviX_set_add(Set *set, const void *key)
|
||||
{
|
||||
uint32_t hash = set->hash_function(key);
|
||||
|
||||
/* Make sure nobody tries to add one of the magic values as a
|
||||
* key. If you need to do so, either do so in a wrapper, or
|
||||
* store keys with the magic values separately in the struct
|
||||
* set.
|
||||
*/
|
||||
assert(key != NULL);
|
||||
|
||||
return raviX_set_add_pre_hashed(set, hash, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the key with the given hash into the set.
|
||||
*
|
||||
* Note that insertion may rearrange the set on a resize or rehash, so
|
||||
* previously found set_entry pointers are no longer valid after this
|
||||
* function.
|
||||
*/
|
||||
SetEntry *raviX_set_add_pre_hashed(Set *set, uint32_t hash, const void *key)
|
||||
{
|
||||
uint32_t hash_address;
|
||||
SetEntry *available_entry = NULL;
|
||||
|
||||
if (set->entries >= set->max_entries) {
|
||||
set_rehash(set, set->size_index + 1);
|
||||
} else if (set->deleted_entries + set->entries >= set->max_entries) {
|
||||
set_rehash(set, set->size_index);
|
||||
}
|
||||
|
||||
hash_address = hash % set->size;
|
||||
do {
|
||||
SetEntry *entry = set->table + hash_address;
|
||||
uint32_t double_hash;
|
||||
|
||||
if (!entry_is_present(entry)) {
|
||||
/* Stash the first available entry we find */
|
||||
if (available_entry == NULL)
|
||||
available_entry = entry;
|
||||
if (entry_is_free(entry))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Implement replacement when another insert happens
|
||||
* with a matching key. This is a relatively common
|
||||
* feature of hash tables, with the alternative
|
||||
* generally being "insert the new value as well, and
|
||||
* return it first when the key is searched for".
|
||||
*
|
||||
* Note that the set doesn't have a delete callback.
|
||||
* If freeing of old keys is required to avoid memory leaks,
|
||||
* perform a search before inserting.
|
||||
*/
|
||||
if (!entry_is_deleted(entry) &&
|
||||
entry->hash == hash &&
|
||||
set->key_equals_function(key, entry->key)) {
|
||||
entry->key = key;
|
||||
return entry;
|
||||
}
|
||||
|
||||
double_hash = 1 + hash % set->rehash;
|
||||
|
||||
hash_address = (hash_address + double_hash) % set->size;
|
||||
} while (hash_address != hash % set->size);
|
||||
|
||||
if (available_entry) {
|
||||
if (entry_is_deleted(available_entry))
|
||||
set->deleted_entries--;
|
||||
available_entry->hash = hash;
|
||||
available_entry->key = key;
|
||||
set->entries++;
|
||||
return available_entry;
|
||||
}
|
||||
|
||||
/* We could hit here if a required resize failed. An unchecked-malloc
|
||||
* application could ignore this result.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function searches for, and removes an entry from the set.
|
||||
*
|
||||
* If the caller has previously found a SetEntry pointer,
|
||||
* (from calling raviX_set_search or remembering it from raviX_set_add), then
|
||||
* raviX_set_remove_entry can be called instead to avoid an extra search.
|
||||
*/
|
||||
void raviX_set_remove(Set *set, const void *key)
|
||||
{
|
||||
SetEntry *entry;
|
||||
|
||||
entry = raviX_set_search(set, key);
|
||||
|
||||
raviX_set_remove_entry(set, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function deletes the set given set entry.
|
||||
*
|
||||
* Note that deletion doesn't otherwise modify the set, so an
|
||||
* iteration over the set deleting entries is safe.
|
||||
*/
|
||||
void raviX_set_remove_entry(Set *set, SetEntry *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->key = deleted_key;
|
||||
set->entries--;
|
||||
set->deleted_entries++;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is an iterator over the set.
|
||||
*
|
||||
* Pass in NULL for the first entry, as in the start of a for loop.
|
||||
* Note that an iteration over the set is O(table_size) not
|
||||
* O(entries).
|
||||
*/
|
||||
SetEntry *raviX_set_next_entry(Set *set, SetEntry *entry)
|
||||
{
|
||||
if (entry == NULL)
|
||||
entry = set->table;
|
||||
else
|
||||
entry = entry + 1;
|
||||
|
||||
for (; entry != set->table + set->size; entry++) {
|
||||
if (entry_is_present(entry)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
SetEntry *raviX_set_random_entry(Set *set,
|
||||
int (*predicate)(SetEntry *entry))
|
||||
{
|
||||
SetEntry *entry;
|
||||
uint32_t i = random() % set->size;
|
||||
|
||||
if (set->entries == 0)
|
||||
return NULL;
|
||||
|
||||
for (entry = set->table + i; entry != set->table + set->size; entry++) {
|
||||
if (entry_is_present(entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = set->table; entry != set->table + i; entry++) {
|
||||
if (entry_is_present(entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ravicomp_SET_H
|
||||
#define ravicomp_SET_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct SetEntry {
|
||||
uint32_t hash;
|
||||
const void *key;
|
||||
} SetEntry;
|
||||
|
||||
typedef struct Set {
|
||||
SetEntry *table;
|
||||
uint32_t (*hash_function)(const void *key);
|
||||
int (*key_equals_function)(const void *a, const void *b);
|
||||
uint32_t size;
|
||||
uint32_t rehash;
|
||||
uint32_t max_entries;
|
||||
uint32_t size_index;
|
||||
uint32_t entries;
|
||||
uint32_t deleted_entries;
|
||||
} Set;
|
||||
|
||||
Set *raviX_set_create(uint32_t (*hash_function)(const void *key),
|
||||
int (*key_equals_function)(const void *a,
|
||||
const void *b));
|
||||
void raviX_set_destroy(Set *set,
|
||||
void (*delete_function)(SetEntry *entry));
|
||||
|
||||
SetEntry *raviX_set_add(Set *set, const void *key);
|
||||
|
||||
bool raviX_set_contains(Set *set, const void *key);
|
||||
|
||||
void raviX_set_remove(Set *set, const void *key);
|
||||
|
||||
SetEntry *raviX_set_search(Set *set, const void *key);
|
||||
|
||||
void raviX_set_remove_entry(Set *set, SetEntry *entry);
|
||||
|
||||
SetEntry *raviX_set_next_entry(Set *set, SetEntry *entry);
|
||||
|
||||
SetEntry *raviX_set_random_entry(Set *set,
|
||||
int (*predicate)(SetEntry *entry));
|
||||
|
||||
/**
|
||||
* This foreach function is safe against deletion (which just replaces
|
||||
* an entry's data with the deleted marker), but not against insertion
|
||||
* (which may rehash the table, making entry a dangling pointer).
|
||||
*/
|
||||
#define set_foreach(ht, entry) \
|
||||
for (entry = raviX_set_next_entry(ht, NULL); \
|
||||
entry != NULL; \
|
||||
entry = raviX_set_next_entry(ht, entry))
|
||||
|
||||
/* Alternate interfaces to reduce repeated calls to hash function. */
|
||||
SetEntry *raviX_set_search_pre_hashed(Set *set, uint32_t hash, const void *key);
|
||||
|
||||
SetEntry *raviX_set_add_pre_hashed(Set *set, uint32_t hash, const void *key);
|
||||
|
||||
#endif
|
@ -0,0 +1,522 @@
|
||||
/*
|
||||
Copyright (C) 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
#include <parser.h>
|
||||
|
||||
/*
|
||||
The Type checker walks through the AST and annotates nodes with type information.
|
||||
It also checks that the operations are valid.
|
||||
|
||||
There are following assumptions made about the code generator backend.
|
||||
|
||||
a) Function arguments that have type info must be asserted at runtime
|
||||
b) Local variable assignments in 'local' or expression statements must be asserted by the backend
|
||||
c) We allow assigning integer value to number and vice versa in the AST but the code generator must assert this is valid
|
||||
d) Any unassigned local vars that have type info must be set to valid initial values.
|
||||
|
||||
None of these operations are explicit in the AST.
|
||||
*/
|
||||
|
||||
|
||||
static bool is_type_same(const VariableType *a, const VariableType *b)
|
||||
{
|
||||
// String comparion of type_name relies upon strings being interned
|
||||
return a->type_code == b->type_code && a->type_name == b->type_name;
|
||||
}
|
||||
|
||||
static void handle_error(CompilerState *container, const char *msg)
|
||||
{
|
||||
// TODO source and line number
|
||||
raviX_buffer_add_string(&container->error_message, msg);
|
||||
longjmp(container->env, 1);
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_node(CompilerState *container, AstNode *function, AstNode *node);
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_list(CompilerState *container, AstNode *function, AstNodeList *list)
|
||||
{
|
||||
AstNode *node;
|
||||
FOR_EACH_PTR(list, node) { typecheck_ast_node(container, function, node); }
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_unary_operator(CompilerState *container, AstNode *function, AstNode *node)
|
||||
{
|
||||
UnaryOperatorType op = node->unary_expr.unary_op;
|
||||
typecheck_ast_node(container, function, node->unary_expr.expr);
|
||||
ravitype_t subexpr_type = node->unary_expr.expr->common_expr.type.type_code;
|
||||
switch (op) {
|
||||
case UNOPR_MINUS:
|
||||
if (subexpr_type == RAVI_TNUMINT) {
|
||||
set_type(&node->unary_expr.type, RAVI_TNUMINT);
|
||||
} else if (subexpr_type == RAVI_TNUMFLT) {
|
||||
set_type(&node->unary_expr.type, RAVI_TNUMFLT);
|
||||
}
|
||||
break;
|
||||
case UNOPR_LEN:
|
||||
if (subexpr_type == RAVI_TARRAYINT || subexpr_type == RAVI_TARRAYFLT) {
|
||||
set_type(&node->unary_expr.type, RAVI_TNUMINT);
|
||||
}
|
||||
break;
|
||||
case UNOPR_TO_INTEGER:
|
||||
set_type(&node->unary_expr.type, RAVI_TNUMINT);
|
||||
break;
|
||||
case UNOPR_TO_NUMBER:
|
||||
set_type(&node->unary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case UNOPR_TO_CLOSURE:
|
||||
set_type(&node->unary_expr.type, RAVI_TFUNCTION);
|
||||
break;
|
||||
case UNOPR_TO_STRING:
|
||||
set_type(&node->unary_expr.type, RAVI_TSTRING);
|
||||
break;
|
||||
case UNOPR_TO_INTARRAY:
|
||||
set_type(&node->unary_expr.type, RAVI_TARRAYINT);
|
||||
if (node->unary_expr.expr->type == EXPR_TABLE_LITERAL) {
|
||||
set_type(&node->unary_expr.expr->table_expr.type, RAVI_TARRAYINT);
|
||||
}
|
||||
break;
|
||||
case UNOPR_TO_NUMARRAY:
|
||||
set_type(&node->unary_expr.type, RAVI_TARRAYFLT);
|
||||
if (node->unary_expr.expr->type == EXPR_TABLE_LITERAL) {
|
||||
set_type(&node->unary_expr.expr->table_expr.type, RAVI_TARRAYFLT);
|
||||
}
|
||||
break;
|
||||
case UNOPR_TO_TABLE:
|
||||
set_type(&node->unary_expr.type, RAVI_TTABLE);
|
||||
break;
|
||||
case UNOPR_TO_TYPE:
|
||||
assert(node->unary_expr.type.type_name != NULL); // Should already be set by the parser
|
||||
set_typecode(&node->unary_expr.type, RAVI_TUSERDATA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_binary_operator(CompilerState *container, AstNode *function,
|
||||
AstNode *node)
|
||||
{
|
||||
BinaryOperatorType op = node->binary_expr.binary_op;
|
||||
AstNode *e1 = node->binary_expr.expr_left;
|
||||
AstNode *e2 = node->binary_expr.expr_right;
|
||||
typecheck_ast_node(container, function, e1);
|
||||
typecheck_ast_node(container, function, e2);
|
||||
switch (op) {
|
||||
case BINOPR_ADD:
|
||||
case BINOPR_SUB:
|
||||
case BINOPR_MUL:
|
||||
case BINOPR_DIV:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMFLT && e2->common_expr.type.type_code == RAVI_TNUMFLT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (e1->common_expr.type.type_code == RAVI_TNUMFLT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMFLT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (op != BINOPR_DIV && e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMINT);
|
||||
else if (op == BINOPR_DIV && e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case BINOPR_IDIV:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMINT);
|
||||
// FIXME missing cases
|
||||
break;
|
||||
case BINOPR_BAND:
|
||||
case BINOPR_BOR:
|
||||
case BINOPR_BXOR:
|
||||
case BINOPR_SHL:
|
||||
case BINOPR_SHR:
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMFLT ||
|
||||
e1->common_expr.type.type_code == RAVI_TNUMINT) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMINT);
|
||||
break;
|
||||
case BINOPR_EQ:
|
||||
case BINOPR_NE:
|
||||
case BINOPR_GE:
|
||||
case BINOPR_GT:
|
||||
case BINOPR_LE:
|
||||
case BINOPR_LT:
|
||||
/* This case is not handled in default parser - why? */
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMINT || e1->common_expr.type.type_code == RAVI_TNUMFLT ||
|
||||
e1->common_expr.type.type_code == RAVI_TBOOLEAN) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT ||
|
||||
e2->common_expr.type.type_code == RAVI_TBOOLEAN))
|
||||
set_typecode(&node->binary_expr.type, RAVI_TBOOLEAN);
|
||||
break;
|
||||
case BINOPR_POW:
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMFLT ||
|
||||
e1->common_expr.type.type_code == RAVI_TNUMINT) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case BINOPR_MOD:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMINT);
|
||||
else if ((e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMFLT) ||
|
||||
(e1->common_expr.type.type_code == RAVI_TNUMFLT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(&node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
default:
|
||||
set_typecode(&node->binary_expr.type, RAVI_TANY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_unindexable_type(VariableType *type)
|
||||
{
|
||||
switch (type->type_code) {
|
||||
case RAVI_TNUMFLT:
|
||||
case RAVI_TNUMINT:
|
||||
case RAVI_TBOOLEAN:
|
||||
case RAVI_TNIL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Suffixed expression examples:
|
||||
* f()[1]
|
||||
* x[1][2]
|
||||
* x.y[1]
|
||||
*/
|
||||
static void typecheck_suffixedexpr(CompilerState *container, AstNode *function, AstNode *node)
|
||||
{
|
||||
typecheck_ast_node(container, function, node->suffixed_expr.primary_expr);
|
||||
AstNode *prev_node = node->suffixed_expr.primary_expr;
|
||||
AstNode *this_node;
|
||||
FOR_EACH_PTR(node->suffixed_expr.suffix_list, this_node)
|
||||
{
|
||||
typecheck_ast_node(container, function, this_node);
|
||||
if (this_node->type == EXPR_Y_INDEX) {
|
||||
if (prev_node->common_expr.type.type_code == RAVI_TARRAYFLT) {
|
||||
if (this_node->index_expr.expr->common_expr.type.type_code == RAVI_TNUMINT) {
|
||||
set_typecode(&this_node->index_expr.type, RAVI_TNUMFLT);
|
||||
} else {
|
||||
handle_error(container, "invalid type in index");
|
||||
}
|
||||
} else if (prev_node->common_expr.type.type_code == RAVI_TARRAYINT) {
|
||||
if (this_node->index_expr.expr->common_expr.type.type_code == RAVI_TNUMINT) {
|
||||
set_typecode(&this_node->index_expr.type, RAVI_TNUMINT);
|
||||
} else {
|
||||
handle_error(container, "invalid type in index");
|
||||
}
|
||||
} else if (is_unindexable_type(&prev_node->common_expr.type)) {
|
||||
handle_error(container, "invalid type in index");
|
||||
}
|
||||
}
|
||||
prev_node = this_node;
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
copy_type(&node->suffixed_expr.type, &prev_node->common_expr.type);
|
||||
}
|
||||
|
||||
static void typecheck_var_assignment(CompilerState *container, VariableType *var_type, AstNode *expr,
|
||||
const StringObject *var_name)
|
||||
{
|
||||
if (var_type->type_code == RAVI_TANY)
|
||||
// Any value can be assigned to type ANY
|
||||
return;
|
||||
const char *variable_name = var_name ? var_name->str : "unknown-TODO";
|
||||
VariableType *expr_type = &expr->common_expr.type;
|
||||
|
||||
if (var_type->type_code == RAVI_TNUMINT) {
|
||||
/* if the expr is of type number or # operator then insert @integer operator */
|
||||
if (expr_type->type_code == RAVI_TNUMFLT ||
|
||||
(expr->type == EXPR_UNARY && expr->unary_expr.unary_op == UNOPR_LEN)) {
|
||||
/* Okay, but backend must do appropriate conversion */
|
||||
;
|
||||
} else if (expr_type->type_code != RAVI_TNUMINT) {
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", variable_name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (var_type->type_code == RAVI_TNUMFLT) {
|
||||
if (expr_type->type_code == RAVI_TNUMINT) {
|
||||
/* Okay, but backend must do appropriate conversion */
|
||||
;
|
||||
} else if (expr_type->type_code != RAVI_TNUMFLT) {
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", variable_name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// all other types must strictly match
|
||||
if (!is_type_same(var_type, expr_type)) { // We should probably check type convert-ability here
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", variable_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_local_statement(CompilerState *container, AstNode *function,
|
||||
AstNode *node)
|
||||
{
|
||||
// The local vars should already be annotated
|
||||
// We need to typecheck the expressions to the right of =
|
||||
// Then we need to ensure that the assignments are valid
|
||||
// We can perhaps insert type assertions where we have a mismatch?
|
||||
|
||||
typecheck_ast_list(container, function, node->local_stmt.expr_list);
|
||||
|
||||
LuaSymbol *var;
|
||||
AstNode *expr;
|
||||
PREPARE_PTR_LIST(node->local_stmt.var_list, var);
|
||||
PREPARE_PTR_LIST(node->local_stmt.expr_list, expr);
|
||||
|
||||
for (;;) {
|
||||
if (!var || !expr)
|
||||
break;
|
||||
|
||||
VariableType *var_type = &var->variable.value_type;
|
||||
const StringObject *var_name = var->variable.var_name;
|
||||
|
||||
typecheck_var_assignment(container, var_type, expr, var_name);
|
||||
|
||||
NEXT_PTR_LIST(var);
|
||||
NEXT_PTR_LIST(expr);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_expr_statement(CompilerState *container, AstNode *function, AstNode *node)
|
||||
{
|
||||
if (node->expression_stmt.var_expr_list)
|
||||
typecheck_ast_list(container, function, node->expression_stmt.var_expr_list);
|
||||
typecheck_ast_list(container, function, node->expression_stmt.expr_list);
|
||||
|
||||
if (!node->expression_stmt.var_expr_list)
|
||||
return;
|
||||
|
||||
AstNode *var;
|
||||
AstNode *expr;
|
||||
PREPARE_PTR_LIST(node->expression_stmt.var_expr_list, var);
|
||||
PREPARE_PTR_LIST(node->local_stmt.expr_list, expr);
|
||||
|
||||
for (;;) {
|
||||
if (!var || !expr)
|
||||
break;
|
||||
|
||||
VariableType *var_type = &var->common_expr.type;
|
||||
const StringObject *var_name = NULL; // FIXME how do we get this?
|
||||
|
||||
typecheck_var_assignment(container, var_type, expr, var_name);
|
||||
|
||||
NEXT_PTR_LIST(var);
|
||||
NEXT_PTR_LIST(expr);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_for_in_statment(CompilerState *container, AstNode *function,
|
||||
AstNode *node)
|
||||
{
|
||||
typecheck_ast_list(container, function, node->for_stmt.expr_list);
|
||||
typecheck_ast_list(container, function, node->for_stmt.for_statement_list);
|
||||
}
|
||||
|
||||
static void typecheck_for_num_statment(CompilerState *container, AstNode *function,
|
||||
AstNode *node)
|
||||
{
|
||||
typecheck_ast_list(container, function, node->for_stmt.expr_list);
|
||||
AstNode *expr;
|
||||
enum { I = 1, F = 2, A = 4 }; /* bits representing integer, number, any */
|
||||
int index_type = 0;
|
||||
FOR_EACH_PTR(node->for_stmt.expr_list, expr)
|
||||
{
|
||||
switch (expr->common_expr.type.type_code) {
|
||||
case RAVI_TNUMFLT:
|
||||
index_type |= F;
|
||||
break;
|
||||
case RAVI_TNUMINT:
|
||||
index_type |= I;
|
||||
break;
|
||||
default:
|
||||
index_type |= A;
|
||||
break;
|
||||
}
|
||||
if ((index_type & A) != 0)
|
||||
break;
|
||||
}
|
||||
END_FOR_EACH_PTR(expr);
|
||||
if ((index_type & A) == 0) { /* not any */
|
||||
/* for I+F we use F */
|
||||
ravitype_t symbol_type = index_type == I ? RAVI_TNUMINT : RAVI_TNUMFLT;
|
||||
LuaSymbolList *symbols = node->for_stmt.symbols;
|
||||
LuaSymbol *sym;
|
||||
/* actually there will be only index variable */
|
||||
FOR_EACH_PTR(symbols, sym)
|
||||
{
|
||||
if (sym->symbol_type == SYM_LOCAL) {
|
||||
set_typecode(&sym->variable.value_type, symbol_type);
|
||||
} else {
|
||||
assert(0); /* cannot happen */
|
||||
}
|
||||
}
|
||||
END_FOR_EACH_PTR(sym);
|
||||
}
|
||||
typecheck_ast_list(container, function, node->for_stmt.for_statement_list);
|
||||
}
|
||||
|
||||
static void typecheck_if_statement(CompilerState *container, AstNode *function, AstNode *node)
|
||||
{
|
||||
AstNode *test_then_block;
|
||||
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block)
|
||||
{
|
||||
typecheck_ast_node(container, function, test_then_block->test_then_block.condition);
|
||||
typecheck_ast_list(container, function, test_then_block->test_then_block.test_then_statement_list);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
if (node->if_stmt.else_statement_list) {
|
||||
typecheck_ast_list(container, function, node->if_stmt.else_statement_list);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_while_or_repeat_statement(CompilerState *container, AstNode *function,
|
||||
AstNode *node)
|
||||
{
|
||||
typecheck_ast_node(container, function, node->while_or_repeat_stmt.condition);
|
||||
if (node->while_or_repeat_stmt.loop_statement_list) {
|
||||
typecheck_ast_list(container, function, node->while_or_repeat_stmt.loop_statement_list);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_node(CompilerState *container, AstNode *function, AstNode *node)
|
||||
{
|
||||
switch (node->type) {
|
||||
case EXPR_FUNCTION: {
|
||||
/* args need type assertions but those have no ast - i.e. code gen should do it */
|
||||
typecheck_ast_list(container, function, node->function_expr.function_statement_list);
|
||||
break;
|
||||
}
|
||||
case AST_NONE: {
|
||||
break;
|
||||
}
|
||||
case STMT_RETURN: {
|
||||
typecheck_ast_list(container, function, node->return_stmt.expr_list);
|
||||
break;
|
||||
}
|
||||
case STMT_LOCAL: {
|
||||
typecheck_local_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case STMT_FUNCTION: {
|
||||
typecheck_ast_node(container, function, node->function_stmt.function_expr);
|
||||
break;
|
||||
}
|
||||
case STMT_LABEL: {
|
||||
break;
|
||||
}
|
||||
case STMT_GOTO: {
|
||||
break;
|
||||
}
|
||||
case STMT_DO: {
|
||||
break;
|
||||
}
|
||||
case STMT_EXPR: {
|
||||
typecheck_expr_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case STMT_IF: {
|
||||
typecheck_if_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case STMT_WHILE:
|
||||
case STMT_REPEAT: {
|
||||
typecheck_while_or_repeat_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case STMT_FOR_IN: {
|
||||
typecheck_for_in_statment(container, function, node);
|
||||
break;
|
||||
}
|
||||
case STMT_FOR_NUM: {
|
||||
typecheck_for_num_statment(container, function, node);
|
||||
break;
|
||||
}
|
||||
case EXPR_SUFFIXED: {
|
||||
typecheck_suffixedexpr(container, function, node);
|
||||
break;
|
||||
}
|
||||
case EXPR_FUNCTION_CALL: {
|
||||
if (node->function_call_expr.method_name) {
|
||||
} else {
|
||||
}
|
||||
typecheck_ast_list(container, function, node->function_call_expr.arg_list);
|
||||
break;
|
||||
}
|
||||
case EXPR_SYMBOL: {
|
||||
/* symbol type should have been set when symbol was created */
|
||||
if (node->symbol_expr.var->symbol_type != SYM_LABEL) {
|
||||
copy_type(&node->symbol_expr.type, &node->symbol_expr.var->variable.value_type);
|
||||
}
|
||||
else {
|
||||
// TODO can this happen?
|
||||
node->symbol_expr.type.type_code = RAVI_TANY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EXPR_BINARY: {
|
||||
typecheck_binary_operator(container, function, node);
|
||||
break;
|
||||
}
|
||||
case EXPR_UNARY: {
|
||||
typecheck_unary_operator(container, function, node);
|
||||
break;
|
||||
}
|
||||
case EXPR_LITERAL: {
|
||||
/* type set during parsing */
|
||||
break;
|
||||
}
|
||||
case EXPR_FIELD_SELECTOR: {
|
||||
typecheck_ast_node(container, function, node->index_expr.expr);
|
||||
break;
|
||||
}
|
||||
case EXPR_Y_INDEX: {
|
||||
typecheck_ast_node(container, function, node->index_expr.expr);
|
||||
break;
|
||||
}
|
||||
case EXPR_TABLE_ELEMENT_ASSIGN: {
|
||||
if (node->table_elem_assign_expr.key_expr) {
|
||||
typecheck_ast_node(container, function, node->table_elem_assign_expr.key_expr);
|
||||
}
|
||||
typecheck_ast_node(container, function, node->table_elem_assign_expr.value_expr);
|
||||
copy_type(&node->table_elem_assign_expr.type, &node->table_elem_assign_expr.value_expr->common_expr.type);
|
||||
break;
|
||||
}
|
||||
case EXPR_TABLE_LITERAL: {
|
||||
typecheck_ast_list(container, function, node->table_expr.expr_list);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_function(CompilerState *container, AstNode *func)
|
||||
{
|
||||
typecheck_ast_list(container, func, func->function_expr.function_statement_list);
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
int raviX_ast_typecheck(CompilerState *container)
|
||||
{
|
||||
AstNode *main_function = container->main_function;
|
||||
raviX_buffer_reset(&container->error_message);
|
||||
int rc = setjmp(container->env);
|
||||
if (rc == 0) {
|
||||
typecheck_function(container, main_function);
|
||||
}
|
||||
return rc;
|
||||
}
|
Loading…
Reference in new issue