2021-02-07 15:40:00 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Thakee Nathees
|
|
|
|
* Licensed under: MIT License
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "compiler.h"
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
#include "core.h"
|
2021-02-07 15:40:00 +08:00
|
|
|
#include "types/name_table.h"
|
|
|
|
#include "types/gen/byte_buffer.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "vm.h"
|
|
|
|
|
2021-02-18 02:27:24 +08:00
|
|
|
#if DEBUG_DUMP_COMPILED_CODE
|
|
|
|
#include "debug.h"
|
|
|
|
#endif
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// The maximum number of variables (or global if compiling top level script)
|
|
|
|
// to lookup from the compiling context. Also it's limited by it's opcode
|
2021-02-07 15:40:00 +08:00
|
|
|
// which is using a single byte value to identify the local.
|
|
|
|
#define MAX_VARIABLES 256
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
// The maximum number of constant literal a script can contain. Also it's
|
|
|
|
// limited by it's opcode which is using a short value to identify.
|
|
|
|
#define MAX_CONSTANTS (1 << 16)
|
|
|
|
|
|
|
|
// The maximum address possible to jump. Similar limitation as above.
|
|
|
|
#define MAX_JUMP (1 << 16)
|
|
|
|
|
|
|
|
// Max number of break statement in a loop statement to patch.
|
|
|
|
#define MAX_BREAK_PATCH 256
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// The size of the compiler time error message buffer excluding the file path,
|
|
|
|
// line number, and function name. Used for `vsprintf` and `vsnprintf` is not
|
|
|
|
// available in C++98.
|
|
|
|
#define ERROR_MESSAGE_SIZE 256
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
typedef enum {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
TK_ERROR = 0,
|
|
|
|
TK_EOF,
|
|
|
|
TK_LINE,
|
|
|
|
|
|
|
|
// symbols
|
|
|
|
TK_DOT, // .
|
|
|
|
TK_DOTDOT, // ..
|
|
|
|
TK_COMMA, // ,
|
|
|
|
TK_COLLON, // :
|
|
|
|
TK_SEMICOLLON, // ;
|
|
|
|
TK_HASH, // #
|
|
|
|
TK_LPARAN, // (
|
|
|
|
TK_RPARAN, // )
|
|
|
|
TK_LBRACKET, // [
|
|
|
|
TK_RBRACKET, // ]
|
|
|
|
TK_LBRACE, // {
|
|
|
|
TK_RBRACE, // }
|
|
|
|
TK_PERCENT, // %
|
|
|
|
|
|
|
|
TK_TILD, // ~
|
|
|
|
TK_AMP, // &
|
|
|
|
TK_PIPE, // |
|
|
|
|
TK_CARET, // ^
|
|
|
|
|
|
|
|
TK_PLUS, // +
|
|
|
|
TK_MINUS, // -
|
|
|
|
TK_STAR, // *
|
|
|
|
TK_FSLASH, // /
|
|
|
|
TK_BSLASH, // \.
|
|
|
|
TK_EQ, // =
|
|
|
|
TK_GT, // >
|
|
|
|
TK_LT, // <
|
|
|
|
|
|
|
|
TK_EQEQ, // ==
|
|
|
|
TK_NOTEQ, // !=
|
|
|
|
TK_GTEQ, // >=
|
|
|
|
TK_LTEQ, // <=
|
|
|
|
|
|
|
|
TK_PLUSEQ, // +=
|
|
|
|
TK_MINUSEQ, // -=
|
|
|
|
TK_STAREQ, // *=
|
|
|
|
TK_DIVEQ, // /=
|
|
|
|
TK_SRIGHT, // >>
|
|
|
|
TK_SLEFT, // <<
|
|
|
|
|
|
|
|
//TODO:
|
2021-02-17 02:28:03 +08:00
|
|
|
//TK_SRIGHTEQ // >>=
|
|
|
|
//TK_SLEFTEQ // <<=
|
|
|
|
//TK_MODEQ, // %=
|
|
|
|
//TK_XOREQ, // ^=
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
// Keywords.
|
|
|
|
TK_DEF, // def
|
2021-02-13 21:57:59 +08:00
|
|
|
TK_NATIVE, // native (C function declaration)
|
|
|
|
TK_FUNCTION, // function (literal function)
|
2021-02-12 01:35:43 +08:00
|
|
|
TK_END, // end
|
|
|
|
|
|
|
|
TK_NULL, // null
|
|
|
|
TK_SELF, // self
|
|
|
|
TK_IN, // in
|
|
|
|
TK_AND, // and
|
|
|
|
TK_OR, // or
|
2021-02-17 02:28:03 +08:00
|
|
|
TK_NOT, // not / !
|
2021-02-12 01:35:43 +08:00
|
|
|
TK_TRUE, // true
|
|
|
|
TK_FALSE, // false
|
|
|
|
|
|
|
|
TK_DO, // do
|
|
|
|
TK_WHILE, // while
|
|
|
|
TK_FOR, // for
|
|
|
|
TK_IF, // if
|
|
|
|
TK_ELIF, // elif
|
|
|
|
TK_ELSE, // else
|
|
|
|
TK_BREAK, // break
|
|
|
|
TK_CONTINUE, // continue
|
|
|
|
TK_RETURN, // return
|
|
|
|
|
|
|
|
TK_NAME, // identifier
|
|
|
|
|
|
|
|
TK_NUMBER, // number literal
|
|
|
|
TK_STRING, // string literal
|
|
|
|
|
|
|
|
/* String interpolation (reference wren-lang)
|
|
|
|
* but it doesn't support recursive ex: "a \(b + "\(c)")"
|
|
|
|
* "a \(b) c \(d) e"
|
|
|
|
* tokenized as:
|
|
|
|
* TK_STR_INTERP "a "
|
|
|
|
* TK_NAME b
|
|
|
|
* TK_STR_INTERP " c "
|
|
|
|
* TK_NAME d
|
|
|
|
* TK_STRING " e" */
|
|
|
|
// TK_STR_INTERP, //< not yet.
|
2021-02-07 15:40:00 +08:00
|
|
|
|
|
|
|
} TokenType;
|
|
|
|
|
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType type;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* start; //< Begining of the token in the source.
|
|
|
|
int length; //< Number of chars of the token.
|
|
|
|
int line; //< Line number of the token (1 based).
|
|
|
|
Var value; //< Literal value of the token.
|
2021-02-07 15:40:00 +08:00
|
|
|
} Token;
|
|
|
|
|
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* identifier;
|
|
|
|
int length;
|
|
|
|
TokenType tk_type;
|
2021-02-07 15:40:00 +08:00
|
|
|
} _Keyword;
|
|
|
|
|
|
|
|
// List of keywords mapped into their identifiers.
|
|
|
|
static _Keyword _keywords[] = {
|
2021-02-12 01:35:43 +08:00
|
|
|
{ "def", 3, TK_DEF },
|
|
|
|
{ "native", 6, TK_NATIVE },
|
2021-02-13 21:57:59 +08:00
|
|
|
{ "function", 8, TK_FUNCTION },
|
2021-02-12 01:35:43 +08:00
|
|
|
{ "end", 3, TK_END },
|
|
|
|
{ "null", 4, TK_NULL },
|
|
|
|
{ "self", 4, TK_SELF },
|
|
|
|
{ "in", 2, TK_IN },
|
|
|
|
{ "and", 3, TK_AND },
|
|
|
|
{ "or", 2, TK_OR },
|
|
|
|
{ "not", 3, TK_NOT },
|
|
|
|
{ "true", 4, TK_TRUE },
|
|
|
|
{ "false", 5, TK_FALSE },
|
|
|
|
{ "do", 2, TK_DO },
|
|
|
|
{ "while", 5, TK_WHILE },
|
|
|
|
{ "for", 3, TK_FOR },
|
|
|
|
{ "if", 2, TK_IF },
|
|
|
|
{ "elif", 4, TK_ELIF },
|
|
|
|
{ "else", 4, TK_ELSE },
|
|
|
|
{ "break", 5, TK_BREAK },
|
|
|
|
{ "continue", 8, TK_CONTINUE },
|
|
|
|
{ "return", 6, TK_RETURN },
|
|
|
|
|
2021-04-26 17:34:30 +08:00
|
|
|
{ NULL, 0, (TokenType)(0) }, // Sentinal to mark the end of the array
|
2021-02-07 15:40:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
MSVM* vm; //< Owner of the parser (for reporting errors, etc).
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* source; //< Currently compiled source.
|
|
|
|
const char* path; //< Path of the source.
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* token_start; //< Start of the currently parsed token.
|
|
|
|
const char* current_char; //< Current char position in the source.
|
|
|
|
int current_line; //< Line number of the current char.
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
Token previous, current, next; //< Currently parsed tokens.
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
bool has_errors; //< True if any syntex error occured at compile time.
|
2021-02-07 15:40:00 +08:00
|
|
|
} Parser;
|
|
|
|
|
|
|
|
// Compiler Types ////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Precedence parsing references:
|
|
|
|
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
|
|
|
|
|
|
|
typedef enum {
|
2021-02-12 01:35:43 +08:00
|
|
|
PREC_NONE,
|
|
|
|
PREC_LOWEST,
|
|
|
|
PREC_LOGICAL_OR, // or
|
|
|
|
PREC_LOGICAL_AND, // and
|
|
|
|
PREC_LOGICAL_NOT, // not
|
|
|
|
PREC_EQUALITY, // == !=
|
|
|
|
PREC_IN, // in
|
|
|
|
PREC_IS, // is
|
|
|
|
PREC_COMPARISION, // < > <= >=
|
|
|
|
PREC_BITWISE_OR, // |
|
|
|
|
PREC_BITWISE_XOR, // ^
|
|
|
|
PREC_BITWISE_AND, // &
|
|
|
|
PREC_BITWISE_SHIFT, // << >>
|
|
|
|
PREC_RANGE, // ..
|
|
|
|
PREC_TERM, // + -
|
|
|
|
PREC_FACTOR, // * / %
|
|
|
|
PREC_UNARY, // - ! ~
|
|
|
|
PREC_CALL, // ()
|
|
|
|
PREC_SUBSCRIPT, // []
|
|
|
|
PREC_ATTRIB, // .index
|
|
|
|
PREC_PRIMARY,
|
2021-02-07 15:40:00 +08:00
|
|
|
} Precedence;
|
|
|
|
|
|
|
|
typedef void (*GrammarFn)(Compiler* compiler, bool can_assign);
|
|
|
|
|
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
GrammarFn prefix;
|
|
|
|
GrammarFn infix;
|
|
|
|
Precedence precedence;
|
2021-02-07 15:40:00 +08:00
|
|
|
} GrammarRule;
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
typedef enum {
|
|
|
|
DEPTH_SCRIPT = -2, //< Only used for script body function's depth.
|
|
|
|
DEPTH_GLOBAL = -1, //< Global variables.
|
|
|
|
DEPTH_LOCAL, //< Local scope. Increase with inner scope.
|
|
|
|
} Depth;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
FN_NATIVE, //< Native C function.
|
|
|
|
FN_SCRIPT, //< Script level functions defined with 'def'.
|
|
|
|
FN_LITERAL, //< Literal functions defined with 'function(){...}'
|
|
|
|
} FuncType;
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* name; //< Directly points into the source string.
|
|
|
|
int length; //< Length of the name.
|
2021-02-13 21:57:59 +08:00
|
|
|
int depth; //< The depth the local is defined in.
|
2021-02-12 01:35:43 +08:00
|
|
|
int line; //< The line variable declared for debugging.
|
2021-02-07 15:40:00 +08:00
|
|
|
} Variable;
|
|
|
|
|
|
|
|
typedef struct sLoop {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Index of the loop's start instruction where the execution will jump
|
|
|
|
// back to once it reach the loop end or continue used.
|
|
|
|
int start;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Index of the jump out address instruction to patch it's value once done
|
|
|
|
// compiling the loop.
|
|
|
|
int exit_jump;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Array of address indexes to patch break address.
|
|
|
|
int patches[MAX_BREAK_PATCH];
|
|
|
|
int patch_count;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// The outer loop of the current loop used to set and reset the compiler's
|
|
|
|
// current loop context.
|
|
|
|
struct sLoop* outer_loop;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
|
|
|
} Loop;
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
typedef struct sFunc {
|
|
|
|
|
|
|
|
// Scope of the function. -2 for script body, -1 for top level function and
|
|
|
|
// literal functions will have the scope where it declared.
|
|
|
|
int depth;
|
|
|
|
|
|
|
|
// The actual function pointer which is being compiled.
|
|
|
|
Function* ptr;
|
|
|
|
|
|
|
|
// If outer function of a literal or the script body function of a script
|
|
|
|
// function. Null for script body function.
|
|
|
|
struct sFunc* outer_func;
|
|
|
|
|
|
|
|
} Func;
|
|
|
|
|
|
|
|
// A convinent macro to get the current function.
|
|
|
|
#define _FN (compiler->func->ptr->fn)
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
struct Compiler {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
MSVM* vm;
|
|
|
|
Parser parser;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Current depth the compiler in (-1 means top level) 0 means function
|
|
|
|
// level and > 0 is inner scope.
|
|
|
|
int scope_depth;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
Variable variables[MAX_VARIABLES]; //< Variables in the current context.
|
|
|
|
int var_count; //< Number of locals in [variables].
|
|
|
|
int global_count; //< Number of globals in [variables].
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-04-26 17:34:30 +08:00
|
|
|
int stack_size; //< Current size including locals ind temps.=
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
Script* script; //< Current script.
|
|
|
|
Loop* loop; //< Current loop.
|
|
|
|
Func* func; //< Current function.
|
2021-02-25 17:03:06 +08:00
|
|
|
|
|
|
|
// True if the last statement is a new local variable assignment. Because
|
|
|
|
// the assignment is different than reqular assignment and use this boolean
|
|
|
|
// to tell the compiler that dont pop it's assigned value because the value
|
|
|
|
// itself is the local.
|
|
|
|
bool new_local;
|
2021-02-09 16:21:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
2021-02-12 01:35:43 +08:00
|
|
|
int params;
|
|
|
|
int stack;
|
2021-02-09 16:21:10 +08:00
|
|
|
} OpInfo;
|
|
|
|
|
|
|
|
static OpInfo opcode_info[] = {
|
2021-02-12 01:35:43 +08:00
|
|
|
#define OPCODE(name, params, stack) { params, stack },
|
|
|
|
#include "opcodes.h"
|
|
|
|
#undef OPCODE
|
2021-02-07 15:40:00 +08:00
|
|
|
};
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
/*****************************************************************************
|
|
|
|
* ERROR HANDLERS *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
static void reportError(Parser* parser, const char* file, int line,
|
|
|
|
const char* fmt, va_list args) {
|
2021-02-12 01:35:43 +08:00
|
|
|
MSVM* vm = parser->vm;
|
|
|
|
parser->has_errors = true;
|
|
|
|
char message[ERROR_MESSAGE_SIZE];
|
|
|
|
int length = vsprintf(message, fmt, args);
|
|
|
|
ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed "
|
|
|
|
"the buffer");
|
|
|
|
vm->config.error_fn(vm, MS_ERROR_COMPILE, file, line, message);
|
2021-02-08 02:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Error caused at the middle of lexing (and TK_ERROR will be lexed insted).
|
|
|
|
static void lexError(Parser* parser, const char* fmt, ...) {
|
2021-02-12 01:35:43 +08:00
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
reportError(parser, parser->path, parser->current_line, fmt, args);
|
|
|
|
va_end(args);
|
2021-02-08 02:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Error caused when parsing. The associated token assumed to be last consumed
|
|
|
|
// which is [parser->previous].
|
|
|
|
static void parseError(Parser* parser, const char* fmt, ...) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
Token* token = &parser->previous;
|
2021-02-08 02:30:29 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Lex errors would repored earlier by lexError and lexed a TK_ERROR token.
|
|
|
|
if (token->type == TK_ERROR) return;
|
2021-02-08 02:30:29 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
reportError(parser, parser->path, token->line, fmt, args);
|
|
|
|
va_end(args);
|
2021-02-08 02:30:29 +08:00
|
|
|
}
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
/*****************************************************************************
|
|
|
|
* LEXING *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// Forward declaration of lexer methods.
|
|
|
|
|
|
|
|
static char eatChar(Parser* parser);
|
|
|
|
static void setNextValueToken(Parser* parser, TokenType type, Var value);
|
|
|
|
static void setNextToken(Parser* parser, TokenType type);
|
|
|
|
static bool matchChar(Parser* parser, char c);
|
|
|
|
static bool matchLine(Parser* parser);
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
static void eatString(Parser* parser, bool single_quote) {
|
2021-02-12 01:35:43 +08:00
|
|
|
ByteBuffer buff;
|
|
|
|
byteBufferInit(&buff);
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
char quote = (single_quote) ? '\'' : '"';
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
while (true) {
|
|
|
|
char c = eatChar(parser);
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
if (c == quote) break;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
if (c == '\0') {
|
|
|
|
lexError(parser, "Non terminated string.");
|
|
|
|
|
|
|
|
// Null byte is required by TK_EOF.
|
|
|
|
parser->current_char--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == '\\') {
|
|
|
|
switch (eatChar(parser)) {
|
|
|
|
case '"': byteBufferWrite(&buff, parser->vm, '"'); break;
|
2021-02-13 01:40:19 +08:00
|
|
|
case '\'': byteBufferWrite(&buff, parser->vm, '\''); break;
|
2021-02-12 01:35:43 +08:00
|
|
|
case '\\': byteBufferWrite(&buff, parser->vm, '\\'); break;
|
|
|
|
case 'n': byteBufferWrite(&buff, parser->vm, '\n'); break;
|
|
|
|
case 'r': byteBufferWrite(&buff, parser->vm, '\r'); break;
|
|
|
|
case 't': byteBufferWrite(&buff, parser->vm, '\t'); break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
lexError(parser, "Error: invalid escape character");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
byteBufferWrite(&buff, parser->vm, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// '\0' will be added by varNewSring();
|
|
|
|
Var string = VAR_OBJ(&newString(parser->vm, (const char*)buff.data,
|
|
|
|
(uint32_t)buff.count)->_super);
|
|
|
|
|
|
|
|
byteBufferClear(&buff, parser->vm);
|
|
|
|
|
|
|
|
setNextValueToken(parser, TK_STRING, string);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the current char of the parser on.
|
|
|
|
static char peekChar(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return *parser->current_char;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the next char of the parser on.
|
|
|
|
static char peekNextChar(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (peekChar(parser) == '\0') return '\0';
|
|
|
|
return *(parser->current_char + 1);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Advance the parser by 1 char.
|
|
|
|
static char eatChar(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
char c = peekChar(parser);
|
|
|
|
parser->current_char++;
|
|
|
|
if (c == '\n') parser->current_line++;
|
|
|
|
return c;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Complete lexing an identifier name.
|
|
|
|
static void eatName(Parser* parser) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
char c = peekChar(parser);
|
|
|
|
while (utilIsName(c) || utilIsDigit(c)) {
|
|
|
|
eatChar(parser);
|
|
|
|
c = peekChar(parser);
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* name_start = parser->token_start;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType type = TK_NAME;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
int length = (int)(parser->current_char - name_start);
|
|
|
|
for (int i = 0; _keywords[i].identifier != NULL; i++) {
|
|
|
|
if (_keywords[i].length == length &&
|
|
|
|
strncmp(name_start, _keywords[i].identifier, length) == 0) {
|
|
|
|
type = _keywords[i].tk_type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
setNextToken(parser, type);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Complete lexing a number literal.
|
|
|
|
static void eatNumber(Parser* parser) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// TODO: hex, binary and scientific literals.
|
|
|
|
|
|
|
|
while (utilIsDigit(peekChar(parser)))
|
|
|
|
eatChar(parser);
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
|
|
|
|
matchChar(parser, '.');
|
2021-02-12 01:35:43 +08:00
|
|
|
while (utilIsDigit(peekChar(parser)))
|
|
|
|
eatChar(parser);
|
|
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
Var value = VAR_NUM(strtod(parser->token_start, NULL));
|
|
|
|
if (errno == ERANGE) {
|
|
|
|
const char* start = parser->token_start;
|
|
|
|
int len = (int)(parser->current_char - start);
|
|
|
|
lexError(parser, "Literal is too large (%.*s)", len, start);
|
|
|
|
value = VAR_NUM(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
setNextValueToken(parser, TK_NUMBER, value);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read and ignore chars till it reach new line or EOF.
|
|
|
|
static void skipLineComment(Parser* parser) {
|
2021-02-15 20:49:19 +08:00
|
|
|
char c;
|
|
|
|
while ((c = peekChar(parser)) != '\0') {
|
|
|
|
eatChar(parser);
|
|
|
|
if (c == '\n') return;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Will skip multiple new lines.
|
|
|
|
static void skipNewLines(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
matchLine(parser);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the current char is [c] consume it and advance char by 1 and returns
|
|
|
|
// true otherwise returns false.
|
|
|
|
static bool matchChar(Parser* parser, char c) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (peekChar(parser) != c) return false;
|
|
|
|
eatChar(parser);
|
|
|
|
return true;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the current char is [c] eat the char and add token two otherwise eat
|
|
|
|
// append token one.
|
|
|
|
static void setNextTwoCharToken(Parser* parser, char c, TokenType one,
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType two) {
|
|
|
|
if (matchChar(parser, c)) {
|
|
|
|
setNextToken(parser, two);
|
|
|
|
} else {
|
|
|
|
setNextToken(parser, one);
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the next token as the type.
|
|
|
|
static void setNextToken(Parser* parser, TokenType type) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parser->next.type = type;
|
|
|
|
parser->next.start = parser->token_start;
|
|
|
|
parser->next.length = (int)(parser->current_char - parser->token_start);
|
|
|
|
parser->next.line = parser->current_line - ((type == TK_LINE) ? 1 : 0);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the next token as the type and assign the value.
|
|
|
|
static void setNextValueToken(Parser* parser, TokenType type, Var value) {
|
2021-02-12 01:35:43 +08:00
|
|
|
setNextToken(parser, type);
|
|
|
|
parser->next.value = value;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lex the next token and set it as the next token.
|
|
|
|
static void lexToken(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parser->previous = parser->current;
|
|
|
|
parser->current = parser->next;
|
|
|
|
|
|
|
|
if (parser->current.type == TK_EOF) return;
|
|
|
|
|
|
|
|
while (peekChar(parser) != '\0') {
|
|
|
|
parser->token_start = parser->current_char;
|
|
|
|
char c = eatChar(parser);
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case ',': setNextToken(parser, TK_COMMA); return;
|
|
|
|
case ':': setNextToken(parser, TK_COLLON); return;
|
|
|
|
case ';': setNextToken(parser, TK_SEMICOLLON); return;
|
2021-02-13 21:57:59 +08:00
|
|
|
case '#': skipLineComment(parser); break;
|
2021-02-12 01:35:43 +08:00
|
|
|
case '(': setNextToken(parser, TK_LPARAN); return;
|
|
|
|
case ')': setNextToken(parser, TK_RPARAN); return;
|
|
|
|
case '[': setNextToken(parser, TK_LBRACKET); return;
|
|
|
|
case ']': setNextToken(parser, TK_RBRACKET); return;
|
|
|
|
case '{': setNextToken(parser, TK_LBRACE); return;
|
|
|
|
case '}': setNextToken(parser, TK_RBRACE); return;
|
|
|
|
case '%': setNextToken(parser, TK_PERCENT); return;
|
|
|
|
|
|
|
|
case '~': setNextToken(parser, TK_TILD); return;
|
|
|
|
case '&': setNextToken(parser, TK_AMP); return;
|
|
|
|
case '|': setNextToken(parser, TK_PIPE); return;
|
|
|
|
case '^': setNextToken(parser, TK_CARET); return;
|
|
|
|
|
|
|
|
case '\n': setNextToken(parser, TK_LINE); return;
|
|
|
|
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\r': {
|
|
|
|
char c = peekChar(parser);
|
|
|
|
while (c == ' ' || c == '\t' || c == '\r') {
|
|
|
|
eatChar(parser);
|
|
|
|
c = peekChar(parser);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '.': // TODO: ".5" should be a valid number.
|
|
|
|
setNextTwoCharToken(parser, '.', TK_DOT, TK_DOTDOT);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '=':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_EQ, TK_EQEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_NOT, TK_NOTEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '>':
|
|
|
|
if (matchChar(parser, '>'))
|
|
|
|
setNextToken(parser, TK_SRIGHT);
|
|
|
|
else
|
|
|
|
setNextTwoCharToken(parser, '=', TK_GT, TK_GTEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
if (matchChar(parser, '<'))
|
|
|
|
setNextToken(parser, TK_SLEFT);
|
|
|
|
else
|
|
|
|
setNextTwoCharToken(parser, '=', TK_LT, TK_LTEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_PLUS, TK_PLUSEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_MINUS, TK_MINUSEQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '*':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_STAR, TK_STAREQ);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case '/':
|
|
|
|
setNextTwoCharToken(parser, '=', TK_FSLASH, TK_DIVEQ);
|
|
|
|
return;
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
case '"': eatString(parser, false); return;
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
case '\'': eatString(parser, true); return;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
default: {
|
|
|
|
|
|
|
|
if (utilIsDigit(c)) {
|
|
|
|
eatNumber(parser);
|
|
|
|
} else if (utilIsName(c)) {
|
|
|
|
eatName(parser);
|
|
|
|
} else {
|
|
|
|
if (c >= 32 && c <= 126) {
|
|
|
|
lexError(parser, "Invalid character %c", c);
|
|
|
|
} else {
|
|
|
|
lexError(parser, "Invalid byte 0x%x", (uint8_t)c);
|
|
|
|
}
|
|
|
|
setNextToken(parser, TK_ERROR);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setNextToken(parser, TK_EOF);
|
|
|
|
parser->next.start = parser->current_char;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* PARSING *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// Initialize the parser.
|
2021-02-08 02:30:29 +08:00
|
|
|
static void parserInit(Parser* self, MSVM* vm, const char* source,
|
|
|
|
const char* path) {
|
2021-02-12 01:35:43 +08:00
|
|
|
self->vm = vm;
|
|
|
|
self->source = source;
|
|
|
|
self->path = path;
|
|
|
|
self->token_start = source;
|
|
|
|
self->current_char = source;
|
|
|
|
self->current_line = 1;
|
|
|
|
self->has_errors = false;
|
|
|
|
|
|
|
|
self->next.type = TK_ERROR;
|
|
|
|
self->next.start = NULL;
|
|
|
|
self->next.length = 0;
|
|
|
|
self->next.line = 1;
|
|
|
|
self->next.value = VAR_UNDEFINED;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns current token type.
|
|
|
|
static TokenType peek(Parser* self) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return self->current.type;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns next token type.
|
|
|
|
static TokenType peekNext(Parser* self) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return self->next.type;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Consume the current token if it's expected and lex for the next token
|
2021-02-12 01:35:43 +08:00
|
|
|
// and return true otherwise reutrn false.
|
2021-02-07 15:40:00 +08:00
|
|
|
static bool match(Parser* self, TokenType expected) {
|
2021-02-12 01:35:43 +08:00
|
|
|
//ASSERT(expected != TK_LINE, "Can't match TK_LINE.");
|
|
|
|
//matchLine(self);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
if (peek(self) != expected) return false;
|
|
|
|
lexToken(self);
|
|
|
|
return true;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
// Consume the the current token and if it's not [expected] emits error log
|
|
|
|
// and continue parsing for more error logs.
|
|
|
|
static void consume(Parser* self, TokenType expected, const char* err_msg) {
|
|
|
|
//ASSERT(expected != TK_LINE, "Can't match TK_LINE.");
|
|
|
|
//matchLine(self);
|
|
|
|
|
|
|
|
lexToken(self);
|
|
|
|
if (self->previous.type != expected) {
|
|
|
|
parseError(self, "%s", err_msg);
|
|
|
|
|
|
|
|
// If the next token is expected discard the current to minimize
|
|
|
|
// cascaded errors and continue parsing.
|
|
|
|
if (peek(self) == expected) {
|
|
|
|
lexToken(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
// Match one or more lines and return true if there any.
|
|
|
|
static bool matchLine(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (peek(parser) != TK_LINE) return false;
|
|
|
|
while (peek(parser) == TK_LINE)
|
|
|
|
lexToken(parser);
|
|
|
|
return true;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
// Match semi collon, multiple new lines or peek 'end' keyword.
|
|
|
|
static bool matchEndStatement(Parser* parser) {
|
|
|
|
if (match(parser, TK_SEMICOLLON)) {
|
|
|
|
skipNewLines(parser);
|
|
|
|
return true;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-15 20:49:19 +08:00
|
|
|
|
|
|
|
if (matchLine(parser) || peek(parser) == TK_END || peek(parser) == TK_EOF)
|
|
|
|
return true;
|
2021-02-16 02:51:00 +08:00
|
|
|
return false;
|
2021-02-15 20:49:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Consume semi collon, multiple new lines or peek 'end' keyword.
|
|
|
|
static void consumeEndStatement(Parser* parser) {
|
|
|
|
if (!matchEndStatement(parser)) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parseError(parser, "Expected statement end with newline or ';'.");
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match optional "do" keyword and new lines.
|
2021-02-09 16:21:10 +08:00
|
|
|
static void consumeStartBlock(Parser* parser) {
|
2021-02-12 01:35:43 +08:00
|
|
|
bool consumed = false;
|
|
|
|
|
|
|
|
// Match optional "do".
|
|
|
|
if (match(parser, TK_DO))
|
|
|
|
consumed = true;
|
|
|
|
|
|
|
|
if (matchLine(parser))
|
|
|
|
consumed = true;
|
|
|
|
|
|
|
|
if (!consumed) {
|
|
|
|
parseError(parser, "Expected enter block with newline or 'do'.");
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
// Returns a optional compound assignment.
|
|
|
|
static bool matchAssignment(Parser* parser) {
|
|
|
|
if (match(parser, TK_EQ)) return true;
|
|
|
|
if (match(parser, TK_PLUSEQ)) return true;
|
|
|
|
if (match(parser, TK_MINUSEQ)) return true;
|
|
|
|
if (match(parser, TK_STAREQ)) return true;
|
|
|
|
if (match(parser, TK_DIVEQ)) return true;
|
|
|
|
return false;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
/*****************************************************************************
|
|
|
|
* NAME SEARCH *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// Result type for an identifier definition.
|
|
|
|
typedef enum {
|
2021-02-12 01:35:43 +08:00
|
|
|
NAME_NOT_DEFINED,
|
|
|
|
NAME_LOCAL_VAR, //< Including parameter.
|
|
|
|
NAME_GLOBAL_VAR,
|
|
|
|
NAME_FUNCTION,
|
|
|
|
NAME_BUILTIN, //< Native builtin function.
|
2021-02-11 01:23:48 +08:00
|
|
|
} NameDefnType;
|
|
|
|
|
|
|
|
// Identifier search result.
|
|
|
|
typedef struct {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
NameDefnType type;
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Index in the variable/function buffer/array.
|
|
|
|
int index;
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// The line it declared.
|
|
|
|
int line;
|
2021-02-11 01:23:48 +08:00
|
|
|
|
|
|
|
} NameSearchResult;
|
|
|
|
|
|
|
|
// Will check if the name already defined.
|
|
|
|
static NameSearchResult compilerSearchName(Compiler* compiler,
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* name, int length) {
|
|
|
|
|
|
|
|
NameSearchResult result;
|
|
|
|
result.type = NAME_NOT_DEFINED;
|
2021-02-13 01:40:19 +08:00
|
|
|
int index;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
// Search through local and global valriables.
|
|
|
|
NameDefnType type = NAME_LOCAL_VAR; //< Will change to local.
|
2021-02-13 21:57:59 +08:00
|
|
|
|
|
|
|
// [index] will points to the ith local or ith global (will update).
|
2021-02-12 01:35:43 +08:00
|
|
|
index = compiler->var_count - compiler->global_count - 1;
|
|
|
|
|
|
|
|
for (int i = compiler->var_count - 1; i >= 0; i--) {
|
|
|
|
Variable* variable = &compiler->variables[i];
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
// Literal functions are not closures and ignore it's outer function's
|
|
|
|
// local variables.
|
|
|
|
if (variable->depth != DEPTH_GLOBAL &&
|
|
|
|
compiler->func->depth >= variable->depth) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == NAME_LOCAL_VAR && variable->depth == DEPTH_GLOBAL) {
|
2021-02-12 01:35:43 +08:00
|
|
|
type = NAME_GLOBAL_VAR;
|
|
|
|
index = compiler->global_count - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length == variable->length) {
|
|
|
|
if (strncmp(variable->name, name, length) == 0) {
|
|
|
|
result.type = type;
|
|
|
|
result.index = index;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search through functions.
|
|
|
|
index = nameTableFind(&compiler->script->function_names, name, length);
|
|
|
|
if (index != -1) {
|
|
|
|
result.type = NAME_FUNCTION;
|
|
|
|
result.index = index;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
// Search through builtin functions.
|
|
|
|
index = findBuiltinFunction(name, length);
|
|
|
|
if (index != -1) {
|
|
|
|
result.type = NAME_BUILTIN;
|
|
|
|
result.index = index;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
return result;
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
/*****************************************************************************
|
|
|
|
* PARSING GRAMMAR *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
// Forward declaration of codegen functions.
|
|
|
|
static void emitOpcode(Compiler* compiler, Opcode opcode);
|
|
|
|
static int emitByte(Compiler* compiler, int byte);
|
|
|
|
static int emitShort(Compiler* compiler, int arg);
|
|
|
|
static int compilerAddConstant(Compiler* compiler, Var value);
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
static int compilerAddVariable(Compiler* compiler, const char* name,
|
2021-02-12 01:35:43 +08:00
|
|
|
int length, int line);
|
2021-02-11 01:23:48 +08:00
|
|
|
static int compilerAddConstant(Compiler* compiler, Var value);
|
2021-02-13 21:57:59 +08:00
|
|
|
static int compileFunction(Compiler* compiler, FuncType fn_type);
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
// Forward declaration of grammar functions.
|
2021-02-09 16:21:10 +08:00
|
|
|
static void parsePrecedence(Compiler* compiler, Precedence precedence);
|
|
|
|
static void compileExpression(Compiler* compiler);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
|
|
|
static void exprLiteral(Compiler* compiler, bool can_assign);
|
2021-02-13 21:57:59 +08:00
|
|
|
static void exprFunc(Compiler* compiler, bool can_assign);
|
2021-02-07 15:40:00 +08:00
|
|
|
static void exprName(Compiler* compiler, bool can_assign);
|
|
|
|
|
|
|
|
static void exprBinaryOp(Compiler* compiler, bool can_assign);
|
|
|
|
static void exprUnaryOp(Compiler* compiler, bool can_assign);
|
|
|
|
|
|
|
|
static void exprGrouping(Compiler* compiler, bool can_assign);
|
2021-02-13 01:40:19 +08:00
|
|
|
static void exprList(Compiler* compiler, bool can_assign);
|
2021-02-07 15:40:00 +08:00
|
|
|
static void exprMap(Compiler* compiler, bool can_assign);
|
|
|
|
|
|
|
|
static void exprCall(Compiler* compiler, bool can_assign);
|
|
|
|
static void exprAttrib(Compiler* compiler, bool can_assign);
|
|
|
|
static void exprSubscript(Compiler* compiler, bool can_assign);
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
// true, false, null, self.
|
|
|
|
static void exprValue(Compiler* compiler, bool can_assign);
|
|
|
|
|
2021-02-07 15:40:00 +08:00
|
|
|
#define NO_RULE { NULL, NULL, PREC_NONE }
|
|
|
|
#define NO_INFIX PREC_NONE
|
|
|
|
|
|
|
|
GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
2021-02-12 01:35:43 +08:00
|
|
|
/* TK_ERROR */ NO_RULE,
|
|
|
|
/* TK_EOF */ NO_RULE,
|
|
|
|
/* TK_LINE */ NO_RULE,
|
2021-02-16 02:51:00 +08:00
|
|
|
/* TK_DOT */ { NULL, exprAttrib, PREC_ATTRIB },
|
2021-02-12 01:35:43 +08:00
|
|
|
/* TK_DOTDOT */ { NULL, exprBinaryOp, PREC_RANGE },
|
|
|
|
/* TK_COMMA */ NO_RULE,
|
|
|
|
/* TK_COLLON */ NO_RULE,
|
|
|
|
/* TK_SEMICOLLON */ NO_RULE,
|
|
|
|
/* TK_HASH */ NO_RULE,
|
|
|
|
/* TK_LPARAN */ { exprGrouping, exprCall, PREC_CALL },
|
|
|
|
/* TK_RPARAN */ NO_RULE,
|
2021-02-13 01:40:19 +08:00
|
|
|
/* TK_LBRACKET */ { exprList, exprSubscript, PREC_SUBSCRIPT },
|
2021-02-12 01:35:43 +08:00
|
|
|
/* TK_RBRACKET */ NO_RULE,
|
|
|
|
/* TK_LBRACE */ { exprMap, NULL, NO_INFIX },
|
|
|
|
/* TK_RBRACE */ NO_RULE,
|
|
|
|
/* TK_PERCENT */ { NULL, exprBinaryOp, PREC_FACTOR },
|
|
|
|
/* TK_TILD */ { exprUnaryOp, NULL, NO_INFIX },
|
|
|
|
/* TK_AMP */ { NULL, exprBinaryOp, PREC_BITWISE_AND },
|
|
|
|
/* TK_PIPE */ { NULL, exprBinaryOp, PREC_BITWISE_OR },
|
|
|
|
/* TK_CARET */ { NULL, exprBinaryOp, PREC_BITWISE_XOR },
|
|
|
|
/* TK_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
|
|
|
|
/* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
|
|
|
|
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
|
|
|
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
|
|
|
/* TK_BSLASH */ NO_RULE,
|
|
|
|
/* TK_EQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
|
|
|
/* TK_GT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
|
|
|
/* TK_LT */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
|
|
|
/* TK_EQEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
|
|
|
/* TK_NOTEQ */ { NULL, exprBinaryOp, PREC_EQUALITY },
|
|
|
|
/* TK_GTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
|
|
|
/* TK_LTEQ */ { NULL, exprBinaryOp, PREC_COMPARISION },
|
|
|
|
/* TK_PLUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
|
|
|
/* TK_MINUSEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
|
|
|
/* TK_STAREQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
|
|
|
/* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
|
|
|
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
|
|
|
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
|
|
|
/* TK_DEF */ NO_RULE,
|
|
|
|
/* TK_EXTERN */ NO_RULE,
|
2021-02-13 21:57:59 +08:00
|
|
|
/* TK_FUNCTION */ { exprFunc, NULL, NO_INFIX },
|
2021-02-12 01:35:43 +08:00
|
|
|
/* TK_END */ NO_RULE,
|
|
|
|
/* TK_NULL */ { exprValue, NULL, NO_INFIX },
|
|
|
|
/* TK_SELF */ { exprValue, NULL, NO_INFIX },
|
|
|
|
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
|
|
|
|
/* TK_AND */ { NULL, exprBinaryOp, PREC_LOGICAL_AND },
|
|
|
|
/* TK_OR */ { NULL, exprBinaryOp, PREC_LOGICAL_OR },
|
|
|
|
/* TK_NOT */ { exprUnaryOp, NULL, PREC_LOGICAL_NOT },
|
|
|
|
/* TK_TRUE */ { exprValue, NULL, NO_INFIX },
|
|
|
|
/* TK_FALSE */ { exprValue, NULL, NO_INFIX },
|
|
|
|
/* TK_DO */ NO_RULE,
|
|
|
|
/* TK_WHILE */ NO_RULE,
|
|
|
|
/* TK_FOR */ NO_RULE,
|
|
|
|
/* TK_IF */ NO_RULE,
|
|
|
|
/* TK_ELIF */ NO_RULE,
|
|
|
|
/* TK_ELSE */ NO_RULE,
|
|
|
|
/* TK_BREAK */ NO_RULE,
|
|
|
|
/* TK_CONTINUE */ NO_RULE,
|
|
|
|
/* TK_RETURN */ NO_RULE,
|
|
|
|
/* TK_NAME */ { exprName, NULL, NO_INFIX },
|
|
|
|
/* TK_NUMBER */ { exprLiteral, NULL, NO_INFIX },
|
|
|
|
/* TK_STRING */ { exprLiteral, NULL, NO_INFIX },
|
2021-02-07 15:40:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static GrammarRule* getRule(TokenType type) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return &(rules[(int)type]);
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
// Emit variable store.
|
2021-02-15 20:49:19 +08:00
|
|
|
static void emitStoreVariable(Compiler* compiler, int index, bool global) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (global) {
|
|
|
|
emitOpcode(compiler, OP_STORE_GLOBAL);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (index < 9) { //< 0..8 locals have single opcode.
|
|
|
|
emitOpcode(compiler, (Opcode)(OP_STORE_LOCAL_0 + index));
|
|
|
|
} else {
|
|
|
|
emitOpcode(compiler, OP_STORE_LOCAL_N);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
}
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
static void emitPushVariable(Compiler* compiler, int index, bool global) {
|
2021-02-12 01:35:43 +08:00
|
|
|
if (global) {
|
|
|
|
emitOpcode(compiler, OP_PUSH_GLOBAL);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (index < 9) { //< 0..8 locals have single opcode.
|
|
|
|
emitOpcode(compiler, (Opcode)(OP_PUSH_LOCAL_0 + index));
|
|
|
|
} else {
|
|
|
|
emitOpcode(compiler, OP_PUSH_LOCAL_N);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
}
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
|
|
|
|
static void exprLiteral(Compiler* compiler, bool can_assign) {
|
2021-02-12 01:35:43 +08:00
|
|
|
Token* value = &compiler->parser.previous;
|
|
|
|
int index = compilerAddConstant(compiler, value->value);
|
|
|
|
emitOpcode(compiler, OP_CONSTANT);
|
|
|
|
emitShort(compiler, index);
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
static void exprFunc(Compiler* compiler, bool can_assign) {
|
|
|
|
int fn_index = compileFunction(compiler, FN_LITERAL);
|
|
|
|
emitOpcode(compiler, OP_PUSH_FN);
|
|
|
|
emitShort(compiler, fn_index);
|
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
// Local/global variables, script/native/builtin functions name.
|
|
|
|
static void exprName(Compiler* compiler, bool can_assign) {
|
2021-02-15 20:49:19 +08:00
|
|
|
|
|
|
|
Parser* parser = &compiler->parser;
|
|
|
|
|
|
|
|
const char* name_start = parser->previous.start;
|
|
|
|
int name_len = parser->previous.length;
|
|
|
|
int name_line = parser->previous.line;
|
2021-02-12 01:35:43 +08:00
|
|
|
NameSearchResult result = compilerSearchName(compiler, name_start, name_len);
|
|
|
|
|
|
|
|
if (result.type == NAME_NOT_DEFINED) {
|
2021-02-15 20:49:19 +08:00
|
|
|
if (can_assign && match(parser, TK_EQ)) {
|
2021-02-12 01:35:43 +08:00
|
|
|
int index = compilerAddVariable(compiler, name_start, name_len,
|
|
|
|
name_line);
|
|
|
|
compileExpression(compiler);
|
2021-02-25 17:03:06 +08:00
|
|
|
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
|
|
|
emitStoreVariable(compiler, index, true);
|
|
|
|
} else {
|
|
|
|
// This will prevent the assignment from poped out from the stack
|
|
|
|
// since the assigned value itself is the local and not a temp.
|
|
|
|
compiler->new_local = true;
|
|
|
|
}
|
2021-02-12 01:35:43 +08:00
|
|
|
} else {
|
2021-05-05 12:55:27 +08:00
|
|
|
// TODO: The name could be a function which hasn't been defined at this point.
|
|
|
|
// Implement opcode to push a named variable.
|
2021-02-15 20:49:19 +08:00
|
|
|
parseError(parser, "Name \"%.*s\" is not defined.", name_len, name_start);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-18 02:27:24 +08:00
|
|
|
return;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (result.type) {
|
|
|
|
case NAME_LOCAL_VAR:
|
|
|
|
case NAME_GLOBAL_VAR:
|
2021-02-15 20:49:19 +08:00
|
|
|
|
|
|
|
if (can_assign && matchAssignment(parser)) {
|
|
|
|
TokenType assignment = parser->previous.type;
|
|
|
|
if (assignment != TK_EQ) {
|
2021-04-26 17:34:30 +08:00
|
|
|
emitPushVariable(compiler, result.index,
|
|
|
|
result.type == NAME_GLOBAL_VAR);
|
2021-02-15 20:49:19 +08:00
|
|
|
compileExpression(compiler);
|
|
|
|
|
|
|
|
switch (assignment) {
|
|
|
|
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
|
|
|
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
|
|
|
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
|
|
|
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
2021-05-05 12:55:27 +08:00
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
} else {
|
|
|
|
compileExpression(compiler);
|
|
|
|
}
|
|
|
|
|
|
|
|
emitStoreVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} else {
|
2021-02-15 20:49:19 +08:00
|
|
|
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NAME_FUNCTION:
|
2021-02-12 14:53:52 +08:00
|
|
|
emitOpcode(compiler, OP_PUSH_FN);
|
|
|
|
emitShort(compiler, result.index);
|
2021-02-12 01:35:43 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
case NAME_BUILTIN:
|
|
|
|
emitOpcode(compiler, OP_PUSH_BUILTIN_FN);
|
|
|
|
emitShort(compiler, result.index);
|
|
|
|
return;
|
2021-02-17 02:28:03 +08:00
|
|
|
|
|
|
|
case NAME_NOT_DEFINED:
|
|
|
|
UNREACHABLE(); // Case already handled.
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
|
|
|
|
static void exprBinaryOp(Compiler* compiler, bool can_assign) {
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType op = compiler->parser.previous.type;
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
parsePrecedence(compiler, (Precedence)(getRule(op)->precedence + 1));
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break;
|
|
|
|
case TK_PERCENT: emitOpcode(compiler, OP_MOD); break;
|
|
|
|
case TK_AMP: emitOpcode(compiler, OP_BIT_AND); break;
|
|
|
|
case TK_PIPE: emitOpcode(compiler, OP_BIT_OR); break;
|
|
|
|
case TK_CARET: emitOpcode(compiler, OP_BIT_XOR); break;
|
|
|
|
case TK_PLUS: emitOpcode(compiler, OP_ADD); break;
|
|
|
|
case TK_MINUS: emitOpcode(compiler, OP_SUBTRACT); break;
|
|
|
|
case TK_STAR: emitOpcode(compiler, OP_MULTIPLY); break;
|
|
|
|
case TK_FSLASH: emitOpcode(compiler, OP_DIVIDE); break;
|
|
|
|
case TK_GT: emitOpcode(compiler, OP_GT); break;
|
|
|
|
case TK_LT: emitOpcode(compiler, OP_LT); break;
|
|
|
|
case TK_EQEQ: emitOpcode(compiler, OP_EQEQ); break;
|
|
|
|
case TK_NOTEQ: emitOpcode(compiler, OP_NOTEQ); break;
|
|
|
|
case TK_GTEQ: emitOpcode(compiler, OP_GTEQ); break;
|
|
|
|
case TK_LTEQ: emitOpcode(compiler, OP_LTEQ); break;
|
|
|
|
case TK_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
|
|
|
|
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
|
|
|
|
case TK_IN: emitOpcode(compiler, OP_IN); break;
|
|
|
|
case TK_AND: emitOpcode(compiler, OP_AND); break;
|
|
|
|
case TK_OR: emitOpcode(compiler, OP_OR); break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void exprUnaryOp(Compiler* compiler, bool can_assign) {
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType op = compiler->parser.previous.type;
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
parsePrecedence(compiler, (Precedence)(PREC_UNARY + 1));
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case TK_TILD: emitOpcode(compiler, OP_BIT_NOT); break;
|
|
|
|
case TK_MINUS: emitOpcode(compiler, OP_NEGATIVE); break;
|
|
|
|
case TK_NOT: emitOpcode(compiler, OP_NOT); break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
static void exprGrouping(Compiler* compiler, bool can_assign) {
|
2021-02-12 01:35:43 +08:00
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
compileExpression(compiler);
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
consume(&compiler->parser, TK_RPARAN, "Expected ')' after expression ");
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
static void exprList(Compiler* compiler, bool can_assign) {
|
|
|
|
|
|
|
|
emitOpcode(compiler, OP_PUSH_LIST);
|
|
|
|
int size_index = emitShort(compiler, 0);
|
|
|
|
|
|
|
|
int size = 0;
|
|
|
|
do {
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
if (peek(&compiler->parser) == TK_COMMA) break;
|
|
|
|
|
|
|
|
compileExpression(compiler);
|
|
|
|
emitOpcode(compiler, OP_LIST_APPEND);
|
|
|
|
size++;
|
|
|
|
|
|
|
|
} while (match(&compiler->parser, TK_COMMA));
|
|
|
|
|
2021-02-23 04:41:05 +08:00
|
|
|
skipNewLines(&compiler->parser);
|
2021-02-13 01:40:19 +08:00
|
|
|
consume(&compiler->parser, TK_RBRACKET, "Expected ']' after list elements.");
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
_FN->opcodes.data[size_index] = (size >> 8) & 0xff;
|
|
|
|
_FN->opcodes.data[size_index + 1] = size & 0xff;
|
2021-02-13 01:40:19 +08:00
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
static void exprMap(Compiler* compiler, bool can_assign) { TODO; }
|
|
|
|
|
|
|
|
static void exprCall(Compiler* compiler, bool can_assign) {
|
|
|
|
|
|
|
|
// Compile parameters.
|
|
|
|
int argc = 0;
|
|
|
|
if (!match(&compiler->parser, TK_RPARAN)) {
|
|
|
|
do {
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
compileExpression(compiler);
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
argc++;
|
|
|
|
} while (match(&compiler->parser, TK_COMMA));
|
|
|
|
consume(&compiler->parser, TK_RPARAN, "Expected ')' after parameter list.");
|
|
|
|
}
|
|
|
|
|
|
|
|
emitOpcode(compiler, OP_CALL);
|
|
|
|
emitShort(compiler, argc);
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
|
|
|
|
static void exprAttrib(Compiler* compiler, bool can_assign) {
|
2021-02-16 02:51:00 +08:00
|
|
|
Parser* parser = &compiler->parser;
|
|
|
|
consume(parser, TK_NAME, "Expected an attribute name after '.'.");
|
|
|
|
const char* name = parser->previous.start;
|
|
|
|
int length = parser->previous.length;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
// Store the name in script's names.
|
|
|
|
String* string = newString(compiler->vm, name, length);
|
|
|
|
vmPushTempRef(compiler->vm, &string->_super);
|
|
|
|
stringBufferWrite(&compiler->script->names, compiler->vm, string);
|
|
|
|
vmPopTempRef(compiler->vm);
|
|
|
|
|
|
|
|
int index = (int)compiler->script->names.count - 1;
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
if (can_assign && matchAssignment(parser)) {
|
|
|
|
|
|
|
|
TokenType assignment = parser->previous.type;
|
|
|
|
if (assignment != TK_EQ) {
|
|
|
|
emitOpcode(compiler, OP_GET_ATTRIB_AOP);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
compileExpression(compiler);
|
|
|
|
|
|
|
|
switch (assignment) {
|
|
|
|
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
|
|
|
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
|
|
|
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
|
|
|
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
compileExpression(compiler);
|
|
|
|
}
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
emitOpcode(compiler, OP_SET_ATTRIB);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
emitOpcode(compiler, OP_GET_ATTRIB);
|
|
|
|
emitShort(compiler, index);
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
static void exprSubscript(Compiler* compiler, bool can_assign) {
|
|
|
|
Parser* parser = &compiler->parser;
|
|
|
|
compileExpression(compiler);
|
|
|
|
consume(parser, TK_RBRACKET, "Expected ']' after subscription ends.");
|
|
|
|
|
|
|
|
if (can_assign && matchAssignment(parser)) {
|
|
|
|
|
|
|
|
TokenType assignment = parser->previous.type;
|
|
|
|
if (assignment != TK_EQ) {
|
|
|
|
emitOpcode(compiler, OP_GET_SUBSCRIPT_AOP);
|
|
|
|
compileExpression(compiler);
|
|
|
|
|
|
|
|
switch (assignment) {
|
|
|
|
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
|
|
|
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
|
|
|
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
|
|
|
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
compileExpression(compiler);
|
|
|
|
}
|
|
|
|
|
|
|
|
emitOpcode(compiler, OP_SET_SUBSCRIPT);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
emitOpcode(compiler, OP_GET_SUBSCRIPT);
|
|
|
|
}
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
static void exprValue(Compiler* compiler, bool can_assign) {
|
2021-02-12 01:35:43 +08:00
|
|
|
TokenType op = compiler->parser.previous.type;
|
|
|
|
switch (op) {
|
|
|
|
case TK_NULL: emitOpcode(compiler, OP_PUSH_NULL); return;
|
|
|
|
case TK_SELF: emitOpcode(compiler, OP_PUSH_SELF); return;
|
|
|
|
case TK_TRUE: emitOpcode(compiler, OP_PUSH_TRUE); return;
|
|
|
|
case TK_FALSE: emitOpcode(compiler, OP_PUSH_FALSE); return;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
static void parsePrecedence(Compiler* compiler, Precedence precedence) {
|
2021-02-12 01:35:43 +08:00
|
|
|
lexToken(&compiler->parser);
|
|
|
|
GrammarFn prefix = getRule(compiler->parser.previous.type)->prefix;
|
|
|
|
|
|
|
|
if (prefix == NULL) {
|
|
|
|
parseError(&compiler->parser, "Expected an expression.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool can_assign = precedence <= PREC_LOWEST;
|
|
|
|
prefix(compiler, can_assign);
|
|
|
|
|
|
|
|
while (getRule(compiler->parser.current.type)->precedence >= precedence) {
|
|
|
|
lexToken(&compiler->parser);
|
|
|
|
GrammarFn infix = getRule(compiler->parser.previous.type)->infix;
|
|
|
|
infix(compiler, can_assign);
|
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* COMPILING *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2021-02-08 02:30:29 +08:00
|
|
|
static void compilerInit(Compiler* compiler, MSVM* vm, const char* source,
|
|
|
|
const char* path) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parserInit(&compiler->parser, vm, source, path);
|
|
|
|
compiler->vm = vm;
|
|
|
|
vm->compiler = compiler;
|
2021-02-13 21:57:59 +08:00
|
|
|
compiler->scope_depth = DEPTH_GLOBAL;
|
2021-02-12 01:35:43 +08:00
|
|
|
compiler->var_count = 0;
|
|
|
|
compiler->global_count = 0;
|
|
|
|
compiler->stack_size = 0;
|
2021-02-25 17:03:06 +08:00
|
|
|
compiler->loop = NULL;
|
|
|
|
compiler->func = NULL;
|
|
|
|
compiler->script = NULL;
|
|
|
|
compiler->new_local = false;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add a variable and return it's index to the context. Assumes that the
|
|
|
|
// variable name is unique and not defined before in the current scope.
|
|
|
|
static int compilerAddVariable(Compiler* compiler, const char* name,
|
2021-02-08 02:30:29 +08:00
|
|
|
int length, int line) {
|
2021-02-12 01:35:43 +08:00
|
|
|
Variable* variable = &compiler->variables[compiler->var_count];
|
|
|
|
variable->name = name;
|
|
|
|
variable->length = length;
|
|
|
|
variable->depth = compiler->scope_depth;
|
|
|
|
variable->line = line;
|
2021-02-13 21:57:59 +08:00
|
|
|
if (variable->depth == DEPTH_GLOBAL) compiler->global_count++;
|
2021-02-12 01:35:43 +08:00
|
|
|
return compiler->var_count++;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
// Add a literal constant to scripts literals and return it's index.
|
|
|
|
static int compilerAddConstant(Compiler* compiler, Var value) {
|
2021-02-12 01:35:43 +08:00
|
|
|
VarBuffer* literals = &compiler->script->literals;
|
|
|
|
|
2021-05-04 18:24:26 +08:00
|
|
|
for (uint32_t i = 0; i < literals->count; i++) {
|
|
|
|
if (isValuesSame(literals->data[i], value)) {
|
2021-02-12 01:35:43 +08:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add new constant to script.
|
|
|
|
if (literals->count < MAX_CONSTANTS) {
|
|
|
|
varBufferWrite(literals, compiler->vm, value);
|
|
|
|
} else {
|
|
|
|
parseError(&compiler->parser, "A script should contain at most %d "
|
|
|
|
"unique constants.", MAX_CONSTANTS);
|
|
|
|
}
|
|
|
|
return (int)literals->count - 1;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enters inside a block.
|
|
|
|
static void compilerEnterBlock(Compiler* compiler) {
|
2021-02-12 01:35:43 +08:00
|
|
|
compiler->scope_depth++;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Exits a block.
|
|
|
|
static void compilerExitBlock(Compiler* compiler) {
|
2021-02-13 21:57:59 +08:00
|
|
|
ASSERT(compiler->scope_depth > (int)DEPTH_GLOBAL, "Cannot exit toplevel.");
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
while (compiler->var_count > 0 && compiler->variables[
|
|
|
|
compiler->var_count - 1].depth >= compiler->scope_depth) {
|
|
|
|
compiler->var_count--;
|
|
|
|
compiler->stack_size--;
|
|
|
|
}
|
|
|
|
compiler->scope_depth--;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* COMPILING (EMIT BYTECODE) *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
// Emit a single byte and return it's index.
|
|
|
|
static int emitByte(Compiler* compiler, int byte) {
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
byteBufferWrite(&_FN->opcodes, compiler->vm,
|
2021-02-09 16:21:10 +08:00
|
|
|
(uint8_t)byte);
|
2021-02-13 21:57:59 +08:00
|
|
|
intBufferWrite(&_FN->oplines, compiler->vm,
|
2021-02-09 16:21:10 +08:00
|
|
|
compiler->parser.previous.line);
|
2021-02-13 21:57:59 +08:00
|
|
|
return (int)_FN->opcodes.count - 1;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Emit 2 bytes argument as big indian. return it's starting index.
|
|
|
|
static int emitShort(Compiler* compiler, int arg) {
|
2021-02-12 01:35:43 +08:00
|
|
|
emitByte(compiler, (arg >> 8) & 0xff);
|
|
|
|
return emitByte(compiler, arg & 0xff) - 1;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Emits an instruction and update stack size (variable stack size opcodes
|
|
|
|
// should be handled).
|
|
|
|
static void emitOpcode(Compiler* compiler, Opcode opcode) {
|
2021-02-12 01:35:43 +08:00
|
|
|
emitByte(compiler, (int)opcode);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compiler->stack_size += opcode_info[opcode].stack;
|
2021-02-13 21:57:59 +08:00
|
|
|
if (compiler->stack_size > _FN->stack_size) {
|
|
|
|
_FN->stack_size = compiler->stack_size;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
// Emits a constant value if it doesn't exists on the current script it'll
|
|
|
|
// make one.
|
2021-02-09 16:21:10 +08:00
|
|
|
static void emitConstant(Compiler* compiler, Var value) {
|
2021-02-12 01:35:43 +08:00
|
|
|
int index = compilerAddConstant(compiler, value);
|
|
|
|
emitOpcode(compiler, OP_CONSTANT);
|
|
|
|
emitShort(compiler, index);
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
// Update the jump offset.
|
2021-02-09 16:21:10 +08:00
|
|
|
static void patchJump(Compiler* compiler, int addr_index) {
|
2021-02-13 21:57:59 +08:00
|
|
|
int jump_to = (int)_FN->opcodes.count - addr_index - 2;
|
2021-02-13 01:40:19 +08:00
|
|
|
ASSERT(jump_to < MAX_JUMP, "Too large address offset to jump to.");
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
_FN->opcodes.data[addr_index] = (jump_to >> 8) & 0xff;
|
|
|
|
_FN->opcodes.data[addr_index + 1] = jump_to & 0xff;
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
// Jump back to the start of the loop.
|
|
|
|
static void emitLoopJump(Compiler* compiler) {
|
|
|
|
emitOpcode(compiler, OP_LOOP);
|
2021-02-13 21:57:59 +08:00
|
|
|
int offset = (int)_FN->opcodes.count - compiler->loop->start + 2;
|
2021-02-13 01:40:19 +08:00
|
|
|
emitShort(compiler, offset);
|
|
|
|
}
|
|
|
|
|
2021-02-11 01:23:48 +08:00
|
|
|
/****************************************************************************
|
|
|
|
* COMPILING (PARSE TOPLEVEL) *
|
|
|
|
****************************************************************************/
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
typedef enum {
|
|
|
|
BLOCK_FUNC,
|
|
|
|
BLOCK_LOOP,
|
|
|
|
BLOCK_IF,
|
|
|
|
BLOCK_ELIF,
|
|
|
|
BLOCK_ELSE,
|
|
|
|
} BlockType;
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
static void compileStatement(Compiler* compiler);
|
2021-02-16 02:51:00 +08:00
|
|
|
static void compileBlockBody(Compiler* compiler, BlockType type);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
// Compile a function and return it's index in the script's function buffer.
|
|
|
|
static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
Parser* parser = &compiler->parser;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
const char* name;
|
2021-02-16 02:51:00 +08:00
|
|
|
int name_length;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
if (fn_type != FN_LITERAL) {
|
|
|
|
consume(parser, TK_NAME, "Expected a function name.");
|
2021-02-16 02:51:00 +08:00
|
|
|
name = parser->previous.start;
|
|
|
|
name_length = parser->previous.length;
|
|
|
|
NameSearchResult result = compilerSearchName(compiler, name,
|
2021-02-13 21:57:59 +08:00
|
|
|
name_length);
|
|
|
|
if (result.type != NAME_NOT_DEFINED) {
|
|
|
|
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
|
2021-02-16 02:51:00 +08:00
|
|
|
name);
|
2021-02-13 21:57:59 +08:00
|
|
|
// Not returning here as to complete for skip cascaded errors.
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
name = "[FunctionLiteral]";
|
2021-02-16 02:51:00 +08:00
|
|
|
name_length = (int)strlen(name);
|
2021-02-13 21:57:59 +08:00
|
|
|
}
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
Function* func = newFunction(compiler->vm, name, name_length,
|
2021-02-13 21:57:59 +08:00
|
|
|
compiler->script, fn_type == FN_NATIVE);
|
2021-02-15 20:49:19 +08:00
|
|
|
int fn_index = (int)compiler->script->functions.count - 1;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
Func curr_func;
|
|
|
|
curr_func.outer_func = compiler->func;
|
|
|
|
curr_func.ptr = func;
|
|
|
|
curr_func.depth = compiler->scope_depth;
|
|
|
|
|
|
|
|
compiler->func = &curr_func;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
int argc = 0;
|
|
|
|
compilerEnterBlock(compiler); // Parameter depth.
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Parameter list is optional.
|
|
|
|
if (match(parser, TK_LPARAN) && !match(parser, TK_RPARAN)) {
|
|
|
|
do {
|
|
|
|
skipNewLines(parser);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
consume(parser, TK_NAME, "Expected a parameter name.");
|
|
|
|
argc++;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
const char* param_name = parser->previous.start;
|
|
|
|
int param_len = parser->previous.length;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
bool predefined = false;
|
|
|
|
for (int i = compiler->var_count - 1; i >= 0; i--) {
|
|
|
|
Variable* variable = &compiler->variables[i];
|
|
|
|
if (compiler->scope_depth != variable->depth) break;
|
|
|
|
if (variable->length == param_len &&
|
|
|
|
strncmp(variable->name, param_name, param_len) == 0) {
|
|
|
|
predefined = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (predefined)
|
|
|
|
parseError(parser, "Multiple definition of a parameter");
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compilerAddVariable(compiler, param_name, param_len,
|
|
|
|
compiler->parser.previous.line);
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} while (match(parser, TK_COMMA));
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
consume(parser, TK_RPARAN, "Expected ')' after parameter list.");
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
func->arity = argc;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
if (fn_type != FN_NATIVE) {
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_FUNC);
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
consume(parser, TK_END, "Expected 'end' after function definition end.");
|
|
|
|
|
2021-02-12 14:53:52 +08:00
|
|
|
emitOpcode(compiler, OP_PUSH_NULL);
|
|
|
|
emitOpcode(compiler, OP_RETURN);
|
|
|
|
emitOpcode(compiler, OP_END);
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compilerExitBlock(compiler); // Parameter depth.
|
2021-02-18 02:27:24 +08:00
|
|
|
|
|
|
|
#if DEBUG_DUMP_COMPILED_CODE
|
|
|
|
dumpInstructions(compiler->vm, compiler->func->ptr);
|
|
|
|
#endif
|
2021-02-13 21:57:59 +08:00
|
|
|
compiler->func = compiler->func->outer_func;
|
|
|
|
|
|
|
|
return fn_index;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 16:21:10 +08:00
|
|
|
// Finish a block body.
|
2021-02-16 02:51:00 +08:00
|
|
|
static void compileBlockBody(Compiler* compiler, BlockType type) {
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compilerEnterBlock(compiler);
|
2021-02-16 02:51:00 +08:00
|
|
|
|
2021-02-25 17:03:06 +08:00
|
|
|
if (type != BLOCK_ELSE && type != BLOCK_ELIF) {
|
2021-02-16 02:51:00 +08:00
|
|
|
consumeStartBlock(&compiler->parser);
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool if_body = (type == BLOCK_IF) || (type == BLOCK_ELIF);
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
TokenType next = peek(&compiler->parser);
|
|
|
|
while (!(next == TK_END || next == TK_EOF || (
|
|
|
|
if_body && (next == TK_ELSE || next == TK_ELIF)))) {
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compileStatement(compiler);
|
|
|
|
skipNewLines(&compiler->parser);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
next = peek(&compiler->parser);
|
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compilerExitBlock(compiler);
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compiles an expression. An expression will result a value on top of the
|
|
|
|
// stack.
|
|
|
|
static void compileExpression(Compiler* compiler) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parsePrecedence(compiler, PREC_LOWEST);
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void compileIfStatement(Compiler* compiler) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
compileExpression(compiler); //< Condition.
|
|
|
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
|
|
|
int ifpatch = emitShort(compiler, 0xffff); //< Will be patched.
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_IF);
|
|
|
|
|
|
|
|
// Elif statement's don't consume 'end' after they end since it's treated as
|
|
|
|
// else and if they require 2 'end' statements. But we're omitting the 'end'
|
|
|
|
// for the 'else' since it'll consumed by the 'if'.
|
|
|
|
bool elif = false;
|
|
|
|
|
|
|
|
if (peek(&compiler->parser) == TK_ELIF) {
|
|
|
|
elif = true;
|
|
|
|
// Override the elif to if so that it'll be parsed as a new if statement
|
|
|
|
// and that's why we're not consuming it here.
|
|
|
|
compiler->parser.current.type = TK_IF;
|
|
|
|
|
|
|
|
// Jump pass else.
|
|
|
|
emitOpcode(compiler, OP_JUMP);
|
|
|
|
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
patchJump(compiler, ifpatch);
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_ELIF);
|
|
|
|
patchJump(compiler, exit_jump);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} else if (match(&compiler->parser, TK_ELSE)) {
|
2021-02-16 02:51:00 +08:00
|
|
|
|
|
|
|
// Jump pass else.
|
|
|
|
emitOpcode(compiler, OP_JUMP);
|
|
|
|
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
patchJump(compiler, ifpatch);
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_ELSE);
|
|
|
|
patchJump(compiler, exit_jump);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} else {
|
|
|
|
patchJump(compiler, ifpatch);
|
|
|
|
}
|
2021-02-11 01:23:48 +08:00
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
if (!elif) {
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
|
|
|
}
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void compileWhileStatement(Compiler* compiler) {
|
2021-02-12 01:35:43 +08:00
|
|
|
Loop loop;
|
2021-02-13 21:57:59 +08:00
|
|
|
loop.start = (int)_FN->opcodes.count;
|
2021-02-12 01:35:43 +08:00
|
|
|
loop.patch_count = 0;
|
|
|
|
loop.outer_loop = compiler->loop;
|
|
|
|
compiler->loop = &loop;
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
compileExpression(compiler); //< Condition.
|
|
|
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
2021-02-18 02:27:24 +08:00
|
|
|
int whilepatch = emitShort(compiler, 0xffff); //< Will be patched.
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_LOOP);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-13 01:40:19 +08:00
|
|
|
emitLoopJump(compiler);
|
2021-02-12 01:35:43 +08:00
|
|
|
patchJump(compiler, whilepatch);
|
2021-02-09 16:21:10 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Patch break statement.
|
|
|
|
for (int i = 0; i < compiler->loop->patch_count; i++) {
|
|
|
|
patchJump(compiler, compiler->loop->patches[i]);
|
|
|
|
}
|
|
|
|
compiler->loop = loop.outer_loop;
|
2021-02-13 01:40:19 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void compileForStatement(Compiler* compiler) {
|
2021-02-13 01:40:19 +08:00
|
|
|
compilerEnterBlock(compiler);
|
|
|
|
|
|
|
|
Parser* parser = &compiler->parser;
|
|
|
|
consume(parser, TK_NAME, "Expected an iterator name.");
|
|
|
|
|
|
|
|
// Unlike functions local variable could shadow a name.
|
|
|
|
const char* iter_name = parser->previous.start;
|
|
|
|
int iter_len = parser->previous.length;
|
|
|
|
int iter_line = parser->previous.line;
|
|
|
|
|
|
|
|
consume(parser, TK_IN, "Expected 'in' after iterator name.");
|
|
|
|
|
|
|
|
// Compile and store container.
|
|
|
|
int container = compilerAddVariable(compiler, "@container", 10, iter_line);
|
|
|
|
compileExpression(compiler);
|
|
|
|
|
|
|
|
// Add iterator to locals. It would initially be null and once the loop
|
|
|
|
// started it'll be an increasing integer indicating that the current
|
|
|
|
// loop is nth.
|
|
|
|
int iterator = compilerAddVariable(compiler, "@iterator", 9, iter_line);
|
|
|
|
emitOpcode(compiler, OP_PUSH_NULL);
|
|
|
|
|
|
|
|
// Add the iteration value. It'll be updated to each element in an array of
|
|
|
|
// each character in a string etc.
|
|
|
|
int iter_value = compilerAddVariable(compiler, iter_name, iter_len,
|
|
|
|
iter_line);
|
|
|
|
emitOpcode(compiler, OP_PUSH_NULL);
|
|
|
|
|
|
|
|
Loop loop;
|
2021-02-13 21:57:59 +08:00
|
|
|
loop.start = (int)_FN->opcodes.count;
|
2021-02-13 01:40:19 +08:00
|
|
|
loop.patch_count = 0;
|
|
|
|
loop.outer_loop = compiler->loop;
|
|
|
|
compiler->loop = &loop;
|
|
|
|
|
|
|
|
// Compile next iteration.
|
|
|
|
emitOpcode(compiler, OP_ITER);
|
|
|
|
int forpatch = emitShort(compiler, 0xffff);
|
|
|
|
|
2021-02-16 02:51:00 +08:00
|
|
|
compileBlockBody(compiler, BLOCK_LOOP);
|
2021-02-13 01:40:19 +08:00
|
|
|
|
|
|
|
emitLoopJump(compiler);
|
|
|
|
patchJump(compiler, forpatch);
|
|
|
|
|
|
|
|
// Patch break statement.
|
|
|
|
for (int i = 0; i < compiler->loop->patch_count; i++) {
|
|
|
|
patchJump(compiler, compiler->loop->patches[i]);
|
|
|
|
}
|
|
|
|
compiler->loop = loop.outer_loop;
|
|
|
|
|
|
|
|
skipNewLines(&compiler->parser);
|
|
|
|
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
|
|
|
compilerExitBlock(compiler); //< Iterator scope.
|
2021-02-09 16:21:10 +08:00
|
|
|
}
|
2021-02-08 02:30:29 +08:00
|
|
|
|
|
|
|
// Compiles a statement. Assignment could be an assignment statement or a new
|
|
|
|
// variable declaration, which will be handled.
|
|
|
|
static void compileStatement(Compiler* compiler) {
|
2021-02-12 01:35:43 +08:00
|
|
|
Parser* parser = &compiler->parser;
|
|
|
|
|
|
|
|
if (match(parser, TK_BREAK)) {
|
|
|
|
if (compiler->loop == NULL) {
|
|
|
|
parseError(parser, "Cannot use 'break' outside a loop.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(compiler->loop->patch_count < MAX_BREAK_PATCH,
|
|
|
|
"Too many break statements (" STRINGIFY(MAX_BREAK_PATCH) ")." );
|
|
|
|
|
|
|
|
consumeEndStatement(parser);
|
|
|
|
emitOpcode(compiler, OP_JUMP);
|
|
|
|
int patch = emitByte(compiler, 0xffff); //< Will be patched.
|
|
|
|
compiler->loop->patches[compiler->loop->patch_count++] = patch;
|
|
|
|
|
|
|
|
} else if (match(parser, TK_CONTINUE)) {
|
|
|
|
if (compiler->loop == NULL) {
|
|
|
|
parseError(parser, "Cannot use 'continue' outside a loop.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
consumeEndStatement(parser);
|
2021-02-13 01:40:19 +08:00
|
|
|
emitLoopJump(compiler);
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
} else if (match(parser, TK_RETURN)) {
|
|
|
|
|
2021-02-13 21:57:59 +08:00
|
|
|
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
2021-02-12 01:35:43 +08:00
|
|
|
parseError(parser, "Invalid 'return' outside a function.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:49:19 +08:00
|
|
|
if (matchEndStatement(parser)) {
|
2021-02-12 01:35:43 +08:00
|
|
|
emitOpcode(compiler, OP_PUSH_NULL);
|
|
|
|
emitOpcode(compiler, OP_RETURN);
|
|
|
|
} else {
|
|
|
|
compileExpression(compiler); //< Return value is at stack top.
|
|
|
|
consumeEndStatement(parser);
|
|
|
|
emitOpcode(compiler, OP_RETURN);
|
|
|
|
}
|
|
|
|
} else if (match(parser, TK_IF)) {
|
|
|
|
compileIfStatement(compiler);
|
|
|
|
|
|
|
|
} else if (match(parser, TK_WHILE)) {
|
|
|
|
compileWhileStatement(compiler);
|
|
|
|
|
|
|
|
} else if (match(parser, TK_FOR)) {
|
|
|
|
compileForStatement(compiler);
|
|
|
|
|
|
|
|
} else {
|
2021-02-25 17:03:06 +08:00
|
|
|
compiler->new_local = false;
|
2021-02-12 01:35:43 +08:00
|
|
|
compileExpression(compiler);
|
|
|
|
consumeEndStatement(parser);
|
2021-02-25 17:03:06 +08:00
|
|
|
if (!compiler->new_local) {
|
|
|
|
emitOpcode(compiler, OP_POP);
|
|
|
|
}
|
|
|
|
compiler->new_local = false;
|
2021-02-12 01:35:43 +08:00
|
|
|
}
|
2021-02-08 02:30:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Script* compileSource(MSVM* vm, const char* path) {
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
MSLoadScriptResult res = vm->config.load_script_fn(vm, path);
|
|
|
|
if (res.is_failed) // FIXME:
|
|
|
|
vm->config.error_fn(vm, MS_ERROR_COMPILE, NULL, -1,
|
|
|
|
"file load source failed.");
|
|
|
|
const char* source = res.source;
|
|
|
|
|
|
|
|
// Skip utf8 BOM if there is any.
|
|
|
|
if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
|
|
|
|
|
|
|
Compiler compiler;
|
|
|
|
compilerInit(&compiler, vm, source, path);
|
|
|
|
|
|
|
|
Script* script = newScript(vm);
|
|
|
|
compiler.script = script;
|
2021-02-13 21:57:59 +08:00
|
|
|
|
|
|
|
Func curr_fn;
|
|
|
|
curr_fn.depth = DEPTH_SCRIPT;
|
|
|
|
curr_fn.ptr = script->body;
|
|
|
|
curr_fn.outer_func = NULL;
|
|
|
|
compiler.func = &curr_fn;
|
2021-02-12 01:35:43 +08:00
|
|
|
|
|
|
|
// Parser pointer for quick access.
|
|
|
|
Parser* parser = &compiler.parser;
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Lex initial tokens. current <-- next.
|
|
|
|
lexToken(parser);
|
|
|
|
lexToken(parser);
|
|
|
|
skipNewLines(parser);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
while (!match(parser, TK_EOF)) {
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
if (match(parser, TK_NATIVE)) {
|
2021-02-13 21:57:59 +08:00
|
|
|
compileFunction(&compiler, FN_NATIVE);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} else if (match(parser, TK_DEF)) {
|
2021-02-13 21:57:59 +08:00
|
|
|
compileFunction(&compiler, FN_SCRIPT);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
} else {
|
|
|
|
compileStatement(&compiler);
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
skipNewLines(parser);
|
|
|
|
}
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
emitOpcode(&compiler, OP_PUSH_NULL);
|
|
|
|
emitOpcode(&compiler, OP_RETURN);
|
|
|
|
emitOpcode(&compiler, OP_END);
|
2021-02-07 15:40:00 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Source done callback.
|
|
|
|
if (vm->config.load_script_done_fn != NULL)
|
|
|
|
vm->config.load_script_done_fn(vm, path, res.user_data);
|
2021-02-08 02:30:29 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
// Create script globals.
|
|
|
|
for (int i = 0; i < compiler.var_count; i++) {
|
2021-02-17 02:28:03 +08:00
|
|
|
ASSERT(compiler.variables[i].depth == (int)DEPTH_GLOBAL, OOPS);
|
2021-02-12 01:35:43 +08:00
|
|
|
varBufferWrite(&script->globals, vm, VAR_NULL);
|
|
|
|
}
|
2021-02-08 02:30:29 +08:00
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
vm->compiler = NULL;
|
2021-02-08 02:30:29 +08:00
|
|
|
|
2021-02-18 02:27:24 +08:00
|
|
|
#if DEBUG_DUMP_COMPILED_CODE
|
|
|
|
dumpInstructions(vm, script->body);
|
|
|
|
#endif
|
|
|
|
|
2021-02-12 01:35:43 +08:00
|
|
|
if (compiler.parser.has_errors) return NULL;
|
|
|
|
return script;
|
2021-02-07 15:40:00 +08:00
|
|
|
}
|
2021-04-26 17:34:30 +08:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void compilerMarkObjects(Compiler* compiler, MSVM* vm) {
|
|
|
|
|
|
|
|
// Mark the script which is currently being compiled.
|
2021-05-01 18:13:39 +08:00
|
|
|
grayObject(&compiler->script->_super, vm);
|
2021-04-26 17:34:30 +08:00
|
|
|
|
|
|
|
// Mark the string literals (they haven't added to the script's literal
|
|
|
|
// buffer yet).
|
2021-05-01 18:13:39 +08:00
|
|
|
grayValue(compiler->parser.current.value, vm);
|
|
|
|
grayValue(compiler->parser.previous.value, vm);
|
|
|
|
grayValue(compiler->parser.next.value, vm);
|
2021-04-26 17:34:30 +08:00
|
|
|
}
|