├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── src ├── common.h ├── environment.c ├── environment.h ├── error.c ├── error.h ├── expr.c ├── expr.h ├── garbage_collector.c ├── garbage_collector.h ├── interpreter.c ├── interpreter.h ├── lox_callable.c ├── lox_callable.h ├── lox_class.c ├── lox_class.h ├── lox_function.c ├── lox_function.h ├── lox_instance.c ├── lox_instance.h ├── main.c ├── memory.c ├── memory.h ├── memory_pool.c ├── memory_pool.h ├── objects.c ├── objects.h ├── parser.c ├── parser.h ├── resolver.c ├── resolver.h ├── return.c ├── return.h ├── scanner.c ├── scanner.h ├── stmt.c ├── stmt.h ├── string.c ├── string.h ├── token.c ├── token.h ├── utility.c └── utility.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marco Caldarelli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile to build loxi, a Lox interpreter 2 | # 3 | # targets: debug release clean 4 | # 5 | # More configuration options in src/common.h 6 | 7 | TARGET := loxi 8 | CC := clang 9 | CFLAGS := -std=c11 -Wall -pedantic -Wextra -Wno-unused-parameter 10 | SRC_DIR := ./src 11 | 12 | ifeq ($(MAKECMDGOALS),debug) 13 | BUILD_DIR := build/debug 14 | CFLAGS += -g -O0 -DDEBUG=1 15 | else 16 | BUILD_DIR := build/release 17 | CFLAGS += -Wno-unused-parameter -O3 -DRELEASE=1 18 | endif 19 | 20 | HEADERS := $(wildcard $(SRC_DIR)/*.h) 21 | SOURCES := $(wildcard $(SRC_DIR)/*.c) 22 | OBJECTS := $(addprefix $(BUILD_DIR)/, $(notdir $(SOURCES:.c=.o))) 23 | 24 | release: $(TARGET) 25 | 26 | debug: $(TARGET) 27 | 28 | all: default 29 | 30 | $(TARGET): $(OBJECTS) 31 | $(CC) $(CFLAGS) $^ -o $@ 32 | 33 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HEADERS) 34 | mkdir -p $(BUILD_DIR) 35 | $(CC) $(CFLAGS) -c -o $@ $< 36 | 37 | .PHONY: clean 38 | 39 | clean: 40 | $(RM) build/debug/*.o build/release/*.o 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Loxi - a Lox Interpreter written in C 2 | 3 | **Lox** is a dynamically typed, object oriented programming language, that supports first class functions and single inheritance. 4 | It is described in the *[Crafting Interpreters][]* book by [Bob Nystrom], in which he builds a full Lox interpreter [jlox][] written in Java. 5 | 6 | **Loxi** is a complete **C** implementation of the Lox interpreter. 7 | 8 | 9 | [Crafting interpreters]: http://www.craftinginterpreters.com 10 | [Bob Nystrom]: https://github.com/munificent 11 | [jlox]: https://github.com/munificent/craftinginterpreters 12 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // common.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #ifndef common_h 9 | #define common_h 10 | 11 | /* Language */ 12 | 13 | // In standar Lox, if a variable was defined but not assigned a 14 | // value, we return nil.When LOX_ACCESSING_UNINIT_VAR_ERROR is 15 | // defined, an error is thrown instead of returning nil. 16 | #undef LOX_ACCESSING_UNINIT_VAR_ERROR 17 | 18 | /* Limits */ 19 | 20 | // Maximum input line length in the repl 21 | #define REPL_MAX_INPUT_LENGTH 1024 22 | 23 | // Maximum number of variables that can be stored in a local environment 24 | #define LOX_MAX_LOCAL_VARIABLES 255 25 | 26 | // Maximum number of methods that can be defined in a class 27 | #define LOX_METHODS_MAX_COUNT 32 28 | 29 | // Maximum number of fields that can be stored in an instance 30 | #define LOX_INSTANCE_MAX_FIELDS 256 31 | 32 | // Maximum number of environments that can be simultaneously 33 | #define LOX_MAX_ENVIRONMENTS 31*1024 34 | 35 | // Maximum number of function arguments 36 | #define LOX_MAX_ARG_COUNT 8 37 | 38 | // Store global variables in a hash table 39 | #define ENV_GLOBALS_USE_HASH 1 40 | 41 | #ifdef ENV_GLOBALS_USE_HASH 42 | // NOTE: Size of the globals hash table 43 | #define ENV_GLOBAL_HASH_SIZE 512 44 | #define ENV_GLOBAL_HASH_MASK (ENV_GLOBAL_HASH_SIZE - 1) 45 | #endif 46 | 47 | /* Time */ 48 | 49 | // If defined, clock() uses the mach absolute time instead of using 50 | // the clock function of the standard C library. 51 | //#define USE_MACH_TIME 1 52 | 53 | //#if (defined (__APPLE__) && defined (__MACH__)) 54 | 55 | /* Strings */ 56 | 57 | // If defined, two pools of strings are created, one for small size 58 | // strings and one for medium size strings. If the needed capacity 59 | // exceeds the latter, usual allocate/free takes place. 60 | #define STR_USE_MEMORY_POOLS 1 61 | 62 | // Maximum capacities of small and medium size strings. 63 | #ifdef STR_USE_MEMORY_POOLS 64 | #define STR_SMALL_SIZE 32 65 | #define STR_MEDIUM_SIZE 256 66 | #endif 67 | 68 | // If defined, the string stores its hash value when calculated. 69 | #define STR_STORE_HASH 1 70 | 71 | /* Garbage collector */ 72 | 73 | #define GC_INITIAL_ENVIRONMENTS_THRESHOLD 32 74 | #define GC_LOCKS_STACK_SIZE 4096 75 | 76 | // If defined, triggers a garbage collection at every place where 77 | // a garbage collection could be triggered. 78 | //#define GC_DEBUG 1 79 | 80 | /* Memory */ 81 | 82 | // If defined, the interpreter frees all allocated memory before exiting. 83 | // This option is turned on automatically if MEMORY_DEBUG is defined. 84 | //#define MEMORY_FREE_ON_EXIT 1 85 | 86 | // If defined, tracks all memory allocations and checks that there are no leaks. 87 | //#define MEMORY_DEBUG 1 88 | 89 | // If defined, (some of the) unused memory is set to a special value. 90 | //#define DEBUG_SCRAMBLE_MEMORY 1 91 | 92 | // If MEMORY_VERBOSE is defined, all allocations/frees are logged on the standard output 93 | //#define MEMORY_VERBOSE 1 94 | 95 | // If defined, some extra debugging info is displayed 96 | //#define DEBUG_VERBOSE 1 97 | 98 | /* Resolver */ 99 | 100 | // If defined, the resolved variable depth and index corresponding to expressions 101 | // are stored in a hash map instead of a simple array. 102 | #define USE_LOCALS_HASH_MAP 1 103 | 104 | // Size of the resolver hash table (must be >= LOX_MAX_LOCAL_VARIABLES) 105 | #define RESOLVER_HASH_TABLE_SIZE 255 106 | 107 | // If defined, the debuggers prints debugging information 108 | //#define RESOLVER_VERBOSE 1 109 | 110 | /* Exit codes */ 111 | 112 | #define LOX_EXIT_CODE_OK 0 113 | #define LOX_EXIT_CODE_HAD_ERROR 65 114 | #define LOX_EXIT_CODE_HAD_RUNTIME_ERROR 70 115 | #define LOX_EXIT_CODE_FATAL_ERROR -1 116 | 117 | /**********************************************/ 118 | 119 | #ifdef MEMORY_VERBOSE 120 | #define MEMORY_DEBUG 1 121 | #endif 122 | 123 | // NOTE: to check for memory leaks, we need to the interpreter to do a full clean up before exiting. 124 | #if defined(MEMORY_DEBUG) && !defined(MEMORY_FREE_ON_EXIT) 125 | #define MEMORY_FREE_ON_EXIT 1 126 | #endif 127 | 128 | // NOTE: to check for memory leaks of strings with MEMORY_DEBUG, we need to disable the memory pools first. 129 | #if defined(MEMORY_DEBUG) && defined(STR_USE_MEMORY_POOLS) 130 | #undef STR_USE_MEMORY_POOLS 131 | #endif 132 | 133 | // NOTE: disable asserts in release build 134 | #ifdef RELEASE 135 | #define NDEBUG 1 136 | #endif 137 | 138 | #include 139 | #include // NOTE: ISO C99 140 | #include // Defines NULL 141 | #include 142 | 143 | #include "memory.h" 144 | 145 | 146 | #ifndef PAGE_SIZE 147 | #define PAGE_SIZE 4096 148 | #endif 149 | 150 | #ifdef DEBUG_SCRAMBLE_MEMORY 151 | #define DEBUG_SCRAMBLE_VALUE (void *)0xbababebedadadede 152 | #endif 153 | 154 | #define max(a,b) (((a)<(b))?(b):(a)) 155 | 156 | #define global 157 | 158 | #ifdef DEBUG 159 | #define FATAL_ERROR assert(false); 160 | #else 161 | #define FATAL_ERROR exit(-1) 162 | #endif 163 | 164 | #define INVALID_PATH FATAL_ERROR 165 | #define INVALID_CASE { FATAL_ERROR; } break 166 | #define INVALID_DEFAULT_CASE default: { FATAL_ERROR; } break 167 | 168 | #define ARRAY_COUNT(array) (sizeof array / sizeof array[0]) 169 | 170 | #define XSTR(a) STR(a) 171 | #define STR(a) #a 172 | 173 | #endif /* common_h */ 174 | -------------------------------------------------------------------------------- /src/environment.c: -------------------------------------------------------------------------------- 1 | // 2 | // environment.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #include "environment.h" 9 | 10 | #include "garbage_collector.h" 11 | #include "lox_function.h" 12 | #include "memory.h" 13 | #include "string.h" 14 | #include "token.h" 15 | 16 | extern inline bool env_isGlobal(const Environment *environment); 17 | extern inline void env_release(Environment *environment); 18 | extern inline Error * env_defineLocal(const Token *var, Object *value, Environment *environment); 19 | extern inline void env_defineThis(Object *value, Environment *environment); 20 | extern inline void env_defineSuper(Object *value, Environment *environment); 21 | 22 | // Initializes and returns a new environment. 23 | // Returns NULL and sets error if there was a stack overflow. 24 | Environment * env_init(Environment *enclosing, Error **error, GarbageCollector *collector) 25 | { 26 | Environment *environment = gcGetEnvironment(collector); 27 | if (environment == NULL) 28 | { 29 | *error = initError(NULL, "Stack overflow."); 30 | return NULL; 31 | } 32 | environment->enclosing = enclosing; 33 | environment->slotsUsed = 0; 34 | environment->isActive = true; 35 | 36 | #ifdef DEBUG 37 | static int32_t debugID = 1; 38 | if(enclosing == NULL) 39 | { 40 | // NOTE: global environment has id 0. 41 | environment->debugID = 0; 42 | } 43 | else 44 | { 45 | environment->debugID = debugID++; 46 | } 47 | #endif 48 | return environment; 49 | } 50 | 51 | // Initializes and returns the global environment. 52 | Environment * env_initGlobal(GarbageCollector *collector) 53 | { 54 | EnvironmentGlobal *globals = lox_alloc(EnvironmentGlobal); 55 | Environment *environment = &globals->environment; 56 | environment->enclosing = NULL; 57 | environment->slotsUsed = 0; 58 | environment->isActive = true; 59 | 60 | #ifdef DEBUG 61 | // NOTE: global environment has always id 0. 62 | environment->debugID = 0; 63 | #endif 64 | 65 | #ifdef ENV_GLOBALS_USE_HASH 66 | for(int32_t index = 0; index < ENV_GLOBAL_HASH_SIZE; ++index) 67 | #else 68 | for(int32_t index = 0; index < ENV_MAX_CAPACITY; ++index) 69 | #endif 70 | { 71 | GLOBALS_NAME(globals, index) = NULL; 72 | } 73 | 74 | gcSetGlobalEnvironment(environment, collector); 75 | 76 | return environment; 77 | } 78 | 79 | // Frees the environment and its contents. 80 | // NOTE: The garbage collector takes care of freeing the values. 81 | void env_free(Environment *environment) 82 | { 83 | if (env_isGlobal(environment)) 84 | { 85 | #ifdef ENV_GLOBALS_USE_HASH 86 | for(int32_t index = 0; index < ENV_GLOBAL_HASH_SIZE; ++index) 87 | { 88 | if (GLOBALS_NAME(environment, index) != NULL) 89 | { 90 | str_free(GLOBALS_NAME(environment, index)); 91 | } 92 | } 93 | #else 94 | for(int32_t index = 0; index < environment->slotsUsed; ++index) 95 | { 96 | str_free(GLOBALS_NAME(environment, index)); 97 | } 98 | #endif 99 | } 100 | lox_free(environment); 101 | } 102 | 103 | // Returns the index of the variable `name` in the global environment. 104 | // If a variable with that name is not yet defined, returns the index 105 | // of the next free slot. 106 | static int32_t env_indexOf(const char *name, Environment *globals) 107 | { 108 | assert(env_isGlobal(globals)); 109 | 110 | #ifdef ENV_GLOBALS_USE_HASH 111 | unsigned long hash = str_hash(name); 112 | // int32_t startIndex = (int32_t)(hash % ENV_GLOBAL_HASH_SIZE); 113 | int32_t startIndex = (int32_t)(hash & ENV_GLOBAL_HASH_MASK); 114 | int32_t index = startIndex; 115 | while ((GLOBALS_NAME(globals, index) != NULL) && 116 | (!str_isEqual(GLOBALS_NAME(globals, index), name))) 117 | { 118 | ++index; 119 | if (index > ENV_GLOBAL_HASH_SIZE) 120 | { 121 | index = 0; 122 | } 123 | } 124 | #else 125 | int32_t index = 0; 126 | while(index < globals->slotsUsed) 127 | { 128 | if (str_isEqual(GLOBALS_NAME(globals, index), name)) 129 | { 130 | break; 131 | } 132 | ++index; 133 | } 134 | #endif 135 | 136 | return index; 137 | } 138 | 139 | // Defines in `environment` a new variable. Its name is in the 140 | // identifier token `var`, its value is carried by the object `value`. 141 | // Returns NULL 142 | Error * env_define(const Token *var, Object *value, Environment *environment) 143 | { 144 | Error *error; 145 | if (env_isGlobal(environment)) 146 | { 147 | error = env_defineGlobal(var, value, environment); 148 | } 149 | else 150 | { 151 | error = env_defineLocal(var, value, environment); 152 | } 153 | return error; 154 | } 155 | 156 | // Defines a new variable in the global environment. 157 | // NOTE: we allow redefinitions of global variables 158 | Error * env_defineGlobal(const Token *var, Object *value, Environment *globals) 159 | { 160 | assert(env_isGlobal(globals)); 161 | 162 | const char *name = get_identifier_name(var); 163 | #ifdef ENV_GLOBALS_USE_HASH 164 | int32_t hashIndex = env_indexOf(name, globals); 165 | if (GLOBALS_NAME(globals, hashIndex) == NULL) 166 | { 167 | if (globals->slotsUsed == ENV_MAX_CAPACITY) 168 | { 169 | return initError(var, "Too many constants in one chunk."); 170 | } 171 | GLOBALS_NAME(globals, hashIndex) = str_dup(name); 172 | GLOBALS_INDEX(globals, hashIndex) = globals->slotsUsed; 173 | globals->slotsUsed++; 174 | } 175 | int32_t index = GLOBALS_INDEX(globals, hashIndex); 176 | #else 177 | int32_t index = env_indexOf(name, globals); 178 | if (index == globals->slotsUsed) 179 | { 180 | // NOTE: This test could be directly handled by the resolver. 181 | if (globals->slotsUsed == ENV_MAX_CAPACITY) 182 | { 183 | return initError(var, "Too many constants in one chunk."); 184 | } 185 | GLOBALS_NAME(globals, globals->slotsUsed) = str_dup(name); 186 | globals->slotsUsed++; 187 | } 188 | #endif 189 | globals->values[index] = value; 190 | 191 | return NULL; 192 | } 193 | 194 | // Defines a native function in the global environment. 195 | void env_defineNative(const char *name, Object *value, Environment *globals) 196 | { 197 | assert(env_isGlobal(globals)); 198 | assert(globals->slotsUsed < ENV_MAX_CAPACITY); 199 | #ifdef ENV_GLOBALS_USE_HASH 200 | char *nameStr = str_fromLiteral(name); 201 | int32_t hashIndex = env_indexOf(nameStr, globals); 202 | assert(GLOBALS_NAME((EnvironmentGlobal *)globals, hashIndex) == NULL); 203 | GLOBALS_NAME(globals, hashIndex) = nameStr; 204 | GLOBALS_INDEX(globals, hashIndex) = globals->slotsUsed; 205 | #else 206 | GLOBALS_NAME(globals, globals->slotsUsed) = str_fromLiteral(name); 207 | #endif 208 | globals->values[globals->slotsUsed] = value; 209 | globals->slotsUsed++; 210 | } 211 | 212 | static inline Environment * env_ancestor(int32_t distance, Environment *environment) 213 | { 214 | for (int32_t i = 0; i < distance; i++) 215 | { 216 | environment = environment->enclosing; 217 | assert(environment != NULL); 218 | } 219 | return environment; 220 | } 221 | 222 | // Looks up the variable `identifier` in `environment` and returns 223 | // a duplicate object carrying its value if it finds it, or NULL 224 | // in which case `error` is set to an appropriate value. 225 | // The variable location is determined by `distance` and `index`, as 226 | // calculated by the resolver. 227 | Object * env_getAt(const Token *identifier, int32_t distance, int32_t index, Environment *environment, Error **error, GarbageCollector *collector) 228 | { 229 | assert(!env_isGlobal(environment)); 230 | Environment *env = env_ancestor(distance, environment); 231 | assert(index >= 0 && index < env->slotsUsed); 232 | Object *value = env->values[index]; 233 | if(value == NULL) 234 | { 235 | // NOTE: the variable was defined but not assigned a value. 236 | #ifdef LOX_ACCESSING_UNINIT_VAR_ERROR 237 | *error = initErrorIdentifier("Accessing uninitialized variable '", identifier, "'."); 238 | #else 239 | value = obj_newNil(collector); 240 | #endif 241 | } 242 | else 243 | { 244 | value = obj_dup(value, collector); 245 | } 246 | 247 | return value; 248 | } 249 | 250 | // Assigns a new value to the object at (`distance`, `index`) relative to `environment`. 251 | void env_assignAt(Object *value, int32_t distance, int32_t index, Environment *environment, GarbageCollector *collector) 252 | { 253 | assert(value != NULL); 254 | Environment *env = env_ancestor(distance, environment); 255 | 256 | assert(index >= 0 && index < env->slotsUsed); 257 | env->values[index] = value; 258 | } 259 | 260 | // Looks up the variable in the global environment, using a hash table. 261 | // If it is defined, returns a duplicate of its value, otherwise NULL and 262 | // error is set to an appropriate value. 263 | Object * env_getGlobal(const Token *identifier, Environment *globals, Error **error, GarbageCollector *collector) 264 | { 265 | assert(env_isGlobal(globals)); 266 | Object *value; 267 | #ifdef ENV_GLOBALS_USE_HASH 268 | int32_t hashIndex = env_indexOf(get_identifier_name(identifier), globals); 269 | if(GLOBALS_NAME(globals, hashIndex) != NULL) 270 | { 271 | int32_t index = GLOBALS_INDEX(globals, hashIndex); 272 | #else 273 | int32_t index = env_indexOf(get_identifier_name(identifier), globals); 274 | if(index < globals->slotsUsed) 275 | { 276 | #endif 277 | value = globals->values[index]; 278 | if(value == NULL) 279 | { 280 | // NOTE: the variable was defined but not assigned a value. 281 | #ifdef LOX_ACCESSING_UNINIT_VAR_ERROR 282 | *error = initErrorIdentifier("Accessing uninitialized variable '", identifier, "'."); 283 | #else 284 | value = obj_newNil(collector); 285 | #endif 286 | } 287 | else 288 | { 289 | value = obj_dup(value, collector); 290 | } 291 | } 292 | else 293 | { 294 | value = NULL; 295 | *error = initErrorIdentifier("Undefined variable '", identifier, "'."); 296 | } 297 | return value; 298 | } 299 | 300 | Error *env_assignGlobal(const Token *identifier, Object *value, Environment *globals, GarbageCollector *collector) 301 | { 302 | assert(env_isGlobal(globals)); 303 | assert(value != NULL); 304 | 305 | Error *error = NULL; 306 | const char *name = get_identifier_name(identifier); 307 | #ifdef ENV_GLOBALS_USE_HASH 308 | int32_t hashIndex = env_indexOf(name, globals); 309 | if ( GLOBALS_NAME(globals, hashIndex) != NULL) 310 | { 311 | int32_t index = GLOBALS_INDEX(globals, hashIndex); 312 | #else 313 | int32_t index = env_indexOf(name, globals); 314 | if(index < globals->slotsUsed) 315 | { 316 | #endif 317 | globals->values[index] = value; 318 | } 319 | else 320 | { 321 | error = initErrorIdentifier("Undefined variable '", identifier, "'."); 322 | } 323 | return error; 324 | } 325 | 326 | /***********************/ 327 | 328 | static int32_t env_debugID(const Environment *env) 329 | { 330 | if (env) 331 | { 332 | #ifdef DEBUG 333 | return env->debugID; 334 | #else 335 | return (int32_t)env; 336 | #endif 337 | } 338 | return -1; 339 | } 340 | 341 | static int32_t env_enclosingDebugID(const Environment *env) 342 | { 343 | if (env) 344 | { 345 | #ifdef DEBUG 346 | return env_debugID(env->enclosing); 347 | #else 348 | return (int32_t)env->enclosing; 349 | #endif 350 | } 351 | return -1; 352 | } 353 | 354 | static void env_printGlobal(const Environment *globals) 355 | { 356 | assert(env_isGlobal(globals)); 357 | printf("Global environment - id: %d, %d symbols defined\n", env_debugID(globals), globals->slotsUsed); 358 | #ifdef ENV_GLOBALS_USE_HASH 359 | for(int32_t index = 0; index < ENV_GLOBAL_HASH_SIZE; ++index) 360 | { 361 | if (GLOBALS_NAME(globals, index) != NULL) 362 | { 363 | char *value = obj_description(globals->values[GLOBALS_INDEX(globals, index)]); 364 | printf(" %d. %s: %s\n", GLOBALS_INDEX(globals, index), GLOBALS_NAME(globals, index), value); 365 | str_free(value); 366 | } 367 | } 368 | #else 369 | for(int32_t index = 0; index < globals->slotsUsed; ++index) 370 | { 371 | char *value = obj_description(globals->values[index]); 372 | printf(" %d. %s: %s\n", index, GLOBALS_NAME(globals, index), value); 373 | str_free(value); 374 | } 375 | #endif 376 | } 377 | 378 | static void env_printLocal(const Environment *environment) 379 | { 380 | assert(!env_isGlobal(environment)); 381 | printf("Environment id: %d, %d symbols defined, (enclosing id: %d)\n", env_debugID(environment), environment->slotsUsed, env_enclosingDebugID(environment)); 382 | for(int32_t index = 0; index < environment->slotsUsed; ++index) 383 | { 384 | char *value = obj_description(environment->values[index]); 385 | printf(" %d. %s\n", index, value); 386 | str_free(value); 387 | } 388 | } 389 | 390 | void env_printReport(const Environment *environment) 391 | { 392 | if (env_isGlobal(environment)) 393 | { 394 | env_printGlobal(environment); 395 | } 396 | else 397 | { 398 | env_printLocal(environment); 399 | } 400 | } 401 | 402 | void env_printReportAll(const Environment *environment) 403 | { 404 | printf("\n--- Environment Report -------\n"); 405 | while (environment != NULL) 406 | { 407 | env_printReport(environment); 408 | environment = environment->enclosing; 409 | } 410 | printf("--- Environment Report end ---\n"); 411 | } 412 | 413 | -------------------------------------------------------------------------------- /src/environment.h: -------------------------------------------------------------------------------- 1 | // 2 | // environment.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #ifndef environment_h 9 | #define environment_h 10 | 11 | #include 12 | #include "error.h" 13 | #include "objects.h" 14 | #include "token.h" 15 | #include "common.h" 16 | 17 | /* Local environments */ 18 | 19 | #define ENV_MAX_CAPACITY (LOX_MAX_LOCAL_VARIABLES + 1) 20 | 21 | typedef struct Environment_tag 22 | { 23 | struct Environment_tag *enclosing; 24 | Object *values[ENV_MAX_CAPACITY]; 25 | int32_t slotsUsed; 26 | 27 | /* Members used by the garbage collector */ 28 | int32_t marked; 29 | // NOTE: next environment in the list of environments kept by the GC 30 | struct Environment_tag *next; 31 | // NOTE: if true, the environment and all the values it contains are 32 | // marked by the garbage collector 33 | bool isActive; 34 | 35 | #ifdef DEBUG 36 | int32_t debugID; 37 | #endif 38 | } Environment; 39 | 40 | #ifdef ENV_GLOBALS_USE_HASH 41 | typedef struct 42 | { 43 | char *name; 44 | uint32_t index; 45 | } EnvHashEntry; 46 | #endif 47 | 48 | /* Global environment */ 49 | 50 | typedef struct 51 | { 52 | Environment environment; 53 | // NOTE: This is the hash table for the global variables. 54 | // If names[i] == NULL, the i-th slot is available; if not 55 | // index[i] yields the index in the values array where the 56 | // object assigned to names[i] is stored. 57 | #ifdef ENV_GLOBALS_USE_HASH 58 | EnvHashEntry table[ENV_GLOBAL_HASH_SIZE]; 59 | #else 60 | char *names[ENV_MAX_CAPACITY]; 61 | #endif 62 | } EnvironmentGlobal; 63 | 64 | #ifdef ENV_GLOBALS_USE_HASH 65 | #define GLOBALS_NAME(env, i) ((EnvironmentGlobal *)env)->table[i].name 66 | #define GLOBALS_INDEX(env, i) ((EnvironmentGlobal *)env)->table[i].index 67 | #else 68 | #define GLOBALS_NAME(env, i) ((EnvironmentGlobal *)env)->names[i] 69 | #endif 70 | 71 | Environment * env_init(Environment *enclosing, Error **error, GarbageCollector *collector); 72 | Environment * env_initGlobal(GarbageCollector *collector); 73 | void env_free(Environment *environment); 74 | void env_freeObjects(Environment *environment); 75 | 76 | Error * env_define(const Token *var, Object *value, Environment *environment); 77 | Error * env_defineGlobal(const Token *var, Object *value, Environment *globals); 78 | void env_defineNative(const char *name, Object *value, Environment *globals); 79 | Object * env_getAt(const Token *identifier, int32_t distance, int32_t index, Environment *environment, Error **error, GarbageCollector *collector); 80 | Error * env_assign(const char *name, Object *value, Environment *environment); 81 | void env_assignAt(Object *value, int32_t distance, int32_t index, Environment *environment, GarbageCollector *collector); 82 | 83 | Object * env_getGlobal(const Token *identifier, Environment *globals, Error **error, GarbageCollector *collector); 84 | Error * env_assignGlobal(const Token *identifier, Object *value, Environment *globals, GarbageCollector *collector); 85 | 86 | void env_printReport(const Environment *environment); 87 | void env_printReportAll(const Environment *environment); 88 | 89 | // Returns true iff `environment` is the global environment 90 | inline bool env_isGlobal(const Environment *environment) 91 | { 92 | return environment->enclosing == NULL; 93 | } 94 | 95 | // Tell the garbage collector that it shouldn't automatically 96 | // retain all objects stored in `environment`. 97 | inline void env_release(Environment *environment) 98 | { 99 | environment->isActive = false; 100 | } 101 | 102 | // Defines a new variable in a local environment environment. Its value 103 | // is retained by the environment: the object is *not* duplicated. 104 | inline Error * env_defineLocal(const Token *var, Object *value, Environment *environment) 105 | { 106 | assert(!env_isGlobal(environment)); 107 | Error *error = NULL; 108 | 109 | // NOTE: This test could be directly handled by the resolver. 110 | if(environment->slotsUsed < ENV_MAX_CAPACITY) 111 | { 112 | environment->values[environment->slotsUsed++] = value; 113 | } 114 | else 115 | { 116 | error = initError(var, "Too many constants in one chunk."); 117 | } 118 | 119 | return error; 120 | } 121 | 122 | // Defines "this" in `environment`. `value` must be an instance object. 123 | inline void env_defineThis(Object *value, Environment *environment) 124 | { 125 | assert(!env_isGlobal(environment)); 126 | assert(value->type == OT_INSTANCE); 127 | environment->values[environment->slotsUsed++] = value; 128 | } 129 | 130 | // Defines "super" in `environment`. `value` must be an class object. 131 | inline void env_defineSuper(Object *value, Environment *environment) 132 | { 133 | assert(!env_isGlobal(environment)); 134 | assert(value->type == OT_CLASS); 135 | environment->values[environment->slotsUsed++] = value; 136 | } 137 | 138 | #endif /* environment_h */ 139 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | // 2 | // error.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #include "error.h" 9 | #include "common.h" 10 | #include "token.h" 11 | #include "string.h" 12 | 13 | Error * initErrorString(const Token *token, char *message) 14 | { 15 | Error *error = lox_alloc(Error); 16 | if(error == NULL) 17 | { 18 | fprintf(stderr, "%s\n[line %d]\n", message, token->lexeme.line + 1); 19 | fatal_outOfMemory(); 20 | } 21 | error->message = message; 22 | error->token = token; 23 | return error; 24 | } 25 | 26 | Error * initError(const Token *token, const char *message) 27 | { 28 | Error *error = initErrorString(token, str_fromLiteral(message)); 29 | return error; 30 | } 31 | 32 | static char * error_buildMessage(const char *prefixLiteral, const Token *identifier, const char *suffixLiteral) 33 | { 34 | char *message = str_fromLiteral(prefixLiteral); 35 | str_append(message, get_identifier_name(identifier)); 36 | str_appendLiteral(message, suffixLiteral); 37 | return message; 38 | } 39 | 40 | Error * initErrorIdentifier(const char *prefixLiteral, const Token *identifier, const char *suffixLiteral) 41 | { 42 | Error *error = lox_alloc(Error); 43 | if(error == NULL) 44 | { 45 | fprintf(stderr, "%s%s%s\n[line %d]\n", prefixLiteral, identifier->literal, suffixLiteral, identifier->lexeme.line + 1); 46 | fatal_outOfMemory(); 47 | } 48 | error->message = error_buildMessage(prefixLiteral, identifier, suffixLiteral); 49 | error->token = identifier; 50 | 51 | return error; 52 | } 53 | 54 | void freeError(Error *error) 55 | { 56 | assert(error != NULL); 57 | assert(error->message != NULL); 58 | str_free(error->message); 59 | lox_free(error); 60 | } 61 | 62 | /******************/ 63 | /* Error handling */ 64 | /******************/ 65 | 66 | extern bool lox_hadError_; 67 | extern bool lox_hadRuntimeError_; 68 | 69 | static void lox_report(int line, const char *location, const char *message) 70 | { 71 | fprintf(stderr, "[line %d] Error%s: %s\n", line+1, location, message); 72 | lox_hadError_ = true; 73 | } 74 | 75 | void lox_token_error(const char *source, const Token *token, const char *message) 76 | { 77 | if (token->type == TT_EOF) 78 | { 79 | lox_report(token->lexeme.line, " at end", message); 80 | } 81 | else 82 | { 83 | char *lexeme = str_substring(source, token->lexeme.index); 84 | char location[256]; 85 | snprintf(location, 256, " at '%s'", lexeme); 86 | lox_report(token->lexeme.line, location, message); 87 | str_free(lexeme); 88 | } 89 | } 90 | 91 | void lox_error(int line, const char *message) 92 | { 93 | lox_report(line, "", message); 94 | } 95 | 96 | void lox_runtimeError(Error *error) 97 | { 98 | fprintf(stderr, "%s\n[line %d]\n", error->message, error->token->lexeme.line + 1); 99 | lox_hadRuntimeError_ = true; 100 | } 101 | 102 | __attribute__((__noreturn__)) 103 | void fatal_outOfMemory() 104 | { 105 | fprintf(stderr, "Fatal error: out of memory"); 106 | exit(LOX_EXIT_CODE_FATAL_ERROR); 107 | } 108 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | // 2 | // error.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #ifndef error_h 9 | #define error_h 10 | 11 | struct Token_tag; 12 | typedef struct Token_tag Token; 13 | 14 | typedef struct 15 | { 16 | const Token *token; 17 | char *message; 18 | } Error; 19 | 20 | Error * initError(const Token *token, const char *message); 21 | Error * initErrorString(const Token *token, char *message); 22 | Error * initErrorIdentifier(const char *prefixLiteral, const Token *identifier, const char *suffixLiteral); 23 | void freeError(Error *error); 24 | void lox_token_error(const char *source, const Token *token, const char *message); 25 | void lox_error(int line, const char *message); 26 | void lox_runtimeError(Error *error); 27 | __attribute__((__noreturn__)) void fatal_outOfMemory(void); 28 | 29 | #endif /* error_h */ 30 | -------------------------------------------------------------------------------- /src/expr.c: -------------------------------------------------------------------------------- 1 | // 2 | // expr.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #include "expr.h" 9 | #include "common.h" 10 | 11 | /* Expressions */ 12 | 13 | Expr * init_assign(Token *name, Expr *value) 14 | { 15 | Assign *assign = lox_alloc(Assign); 16 | assign->expr.type = EXPR_Assign; 17 | assign->expr.next = NULL; 18 | assign->name = name; 19 | assign->value = value; 20 | return AS_EXPR(assign); 21 | } 22 | 23 | Expr * init_binary(Expr *left, Token *operator, Expr *right) 24 | { 25 | Binary *binary = lox_alloc(Binary); 26 | binary->expr.type = EXPR_Binary; 27 | binary->expr.next = NULL; 28 | binary->left = left; 29 | binary->operator = operator; 30 | binary->right = right; 31 | return AS_EXPR(binary); 32 | } 33 | 34 | Expr * init_call(Expr *callee, Token *paren, Expr *arguments) 35 | { 36 | Call *call = lox_alloc(Call); 37 | call->expr.type = EXPR_Call; 38 | call->expr.next = NULL; 39 | call->callee = callee; 40 | call->paren = paren; 41 | call->arguments = arguments; 42 | return AS_EXPR(call); 43 | } 44 | 45 | Expr * init_get(Expr *object, Token *name) 46 | { 47 | Get *get = lox_alloc(Get); 48 | get->expr.type = EXPR_Get; 49 | get->expr.next = NULL; 50 | get->object = object; 51 | get->name = name; 52 | return AS_EXPR(get); 53 | } 54 | 55 | Expr * init_grouping(Expr *expression) 56 | { 57 | Grouping *grouping = lox_alloc(Grouping); 58 | grouping->expr.type = EXPR_Grouping; 59 | grouping->expr.next = NULL; 60 | grouping->expression = expression; 61 | return AS_EXPR(grouping); 62 | } 63 | 64 | Expr * init_literal(Token *value) 65 | { 66 | Literal *literal = lox_alloc(Literal); 67 | literal->expr.type = EXPR_Literal; 68 | literal->expr.next = NULL; 69 | literal->value = *value; 70 | return AS_EXPR(literal); 71 | } 72 | 73 | Expr * init_logical(Expr *left, Token *operator, Expr *right) 74 | { 75 | Logical *logical = lox_alloc(Logical); 76 | logical->expr.type = EXPR_Logical; 77 | logical->expr.next = NULL; 78 | logical->left = left; 79 | logical->operator = operator; 80 | logical->right = right; 81 | return AS_EXPR(logical); 82 | } 83 | 84 | Expr * init_set(Expr *object, Token *name, Expr *value) 85 | { 86 | Set *set = lox_alloc(Set); 87 | set->expr.type = EXPR_Set; 88 | set->expr.next = NULL; 89 | set->object = object; 90 | set->name = name; 91 | set->value = value; 92 | return AS_EXPR(set); 93 | } 94 | 95 | Expr * init_super(Token *keyword, Token *method) 96 | { 97 | Super *super = lox_alloc(Super); 98 | super->expr.type = EXPR_Super; 99 | super->expr.next = NULL; 100 | super->keyword = keyword; 101 | super->method = method; 102 | return AS_EXPR(super); 103 | } 104 | 105 | 106 | Expr * init_this(Token *keyword) 107 | { 108 | This *this = lox_alloc(This); 109 | this->expr.type = EXPR_This; 110 | this->expr.next = NULL; 111 | this->keyword = keyword; 112 | return AS_EXPR(this); 113 | } 114 | 115 | Expr * init_unary(Token *operator, Expr *right) 116 | { 117 | Unary *unary = lox_alloc(Unary); 118 | unary->expr.type = EXPR_Unary; 119 | unary->expr.next = NULL; 120 | unary->operator = operator; 121 | unary->right = right; 122 | return AS_EXPR(unary); 123 | } 124 | 125 | Expr * init_variable(Token *name) 126 | { 127 | Variable *variable = lox_alloc(Variable); 128 | variable->expr.type = EXPR_Variable; 129 | variable->expr.next = NULL; 130 | variable->name = name; 131 | return AS_EXPR(variable); 132 | } 133 | 134 | Expr * init_bool_literal(bool value) 135 | { 136 | Literal *literal = lox_alloc(Literal); 137 | literal->expr.type = EXPR_Literal; 138 | literal->expr.next = NULL; 139 | literal->value.type = value ? TT_TRUE : TT_FALSE; 140 | literal->value.literal = NULL; 141 | literal->value.nextInScan = NULL; 142 | return AS_EXPR(literal); 143 | } 144 | 145 | Expr * init_nil_literal() 146 | { 147 | Literal *literal = lox_alloc(Literal); 148 | literal->expr.type = EXPR_Literal; 149 | literal->expr.next = NULL; 150 | literal->value.type = TT_NIL; 151 | literal->value.literal = NULL; 152 | literal->value.nextInScan = NULL; 153 | return AS_EXPR(literal); 154 | } 155 | 156 | Expr * init_number_literal(double value) 157 | { 158 | Literal *literal = lox_alloc(Literal); 159 | literal->expr.type = EXPR_Literal; 160 | literal->expr.next = NULL; 161 | literal->value.type = TT_NUMBER; 162 | literal->value.number = value; 163 | literal->value.literal = NULL; 164 | literal->value.nextInScan = NULL; 165 | return AS_EXPR(literal); 166 | } 167 | 168 | // NOTE: The tokens are owned by the token list in the scanner, 169 | // and must not be freed here. 170 | void free_expr(Expr *expr) 171 | { 172 | while(expr) 173 | { 174 | switch (expr->type) { 175 | case EXPR_Assign: { 176 | Assign *assign = (Assign *)expr; 177 | free_expr(assign->value); 178 | } break; 179 | case EXPR_Binary: { 180 | Binary *binary = (Binary *)expr; 181 | free_expr(binary->left); 182 | free_expr(binary->right); 183 | } break; 184 | case EXPR_Call: { 185 | Call *call = (Call *)expr; 186 | free_expr(call->callee); 187 | free_expr(call->arguments); 188 | } break; 189 | case EXPR_Get: { 190 | Get *get = (Get *)expr; 191 | free_expr(get->object); 192 | } break; 193 | case EXPR_Grouping: { 194 | Grouping *grouping = (Grouping *)expr; 195 | free_expr(grouping->expression); 196 | } break; 197 | case EXPR_Literal: { 198 | // do nothing 199 | } break; 200 | case EXPR_Logical: { 201 | Logical *logical = (Logical *)expr; 202 | free_expr(logical->left); 203 | free_expr(logical->right); 204 | } break; 205 | case EXPR_Set: { 206 | Set *set = (Set *)expr; 207 | free_expr(set->object); 208 | free_expr(set->value); 209 | } break; 210 | case EXPR_Super: { 211 | // do nothing 212 | } break; 213 | case EXPR_This: { 214 | // do nothing 215 | } break; 216 | case EXPR_Unary: { 217 | Unary *unary = (Unary *)expr; 218 | free_expr(unary->right); 219 | } break; 220 | case EXPR_Variable: { 221 | // do nothing 222 | } break; 223 | } 224 | Expr *next = expr->next; 225 | lox_free(expr); 226 | expr = next; 227 | } 228 | } 229 | 230 | // Returns the count of element in the list elements 231 | int32_t expr_count(Expr *elements) 232 | { 233 | int32_t count = 0; 234 | while(elements != NULL) 235 | { 236 | count++; 237 | elements = elements->next; 238 | } 239 | return count; 240 | } 241 | 242 | // Returns the last statement in a non-empty chain of statements. 243 | static inline Expr * expr_last(Expr *expressions) 244 | { 245 | assert(expressions != NULL); 246 | 247 | Expr *last = expressions; 248 | while(last->next) 249 | { 250 | last = last->next; 251 | } 252 | return last; 253 | } 254 | 255 | Expr * expr_appendTo(Expr *head, Expr *tail) 256 | { 257 | if (head == NULL) 258 | { 259 | return tail; 260 | } 261 | Expr *last = expr_last(head); 262 | last->next = tail; 263 | return head; 264 | } 265 | 266 | Expr * make_test_expr() 267 | { 268 | Expr *expr = init_binary(init_unary(token_atomic(TT_MINUS, (Lexeme){{0, 0, 0}}), init_number_literal(123)), 269 | token_atomic(TT_STAR, (Lexeme){{0, 0, 0}}), 270 | init_grouping(init_number_literal(45.67))); 271 | return expr; 272 | } 273 | 274 | void * expr_accept_visitor(Expr *expr, ExprVisitor *visitor, void *context) 275 | { 276 | void *result = NULL; 277 | switch(expr->type) 278 | { 279 | #define DEFINE_VISIT_CASE(type) \ 280 | case EXPR_##type: { \ 281 | result = visitor->visit##type((type *)expr, context); \ 282 | } break; 283 | 284 | FOREACH_AST_NODE(DEFINE_VISIT_CASE) 285 | 286 | #undef DEFINE_VISIT_CASE 287 | } 288 | return result; 289 | } 290 | -------------------------------------------------------------------------------- /src/expr.h: -------------------------------------------------------------------------------- 1 | // 2 | // expr.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #ifndef expr_h 9 | #define expr_h 10 | 11 | #include "token.h" 12 | 13 | /* 14 | "Assign : Token name, Expr value", 15 | "Binary : Expr left, Token operator, Expr right", 16 | "Call : Expr callee, Token paren, List arguments", 17 | "Get : Expr object, Token name", 18 | "Grouping : Expr expression", 19 | "Literal : Object value", 20 | "Logical : Expr left, Token operator, Expr right", 21 | "Set : Expr object, Token name, Expr value", 22 | "Super : Token keyword, Token method", 23 | "This : Token keyword", 24 | "Unary : Token operator, Expr right", 25 | "Variable : Token name" 26 | */ 27 | 28 | /* 29 | The Expr structure is used for the nodes of the abstract syntax tree. 30 | */ 31 | 32 | #define FOREACH_AST_NODE(code) \ 33 | code(Assign) code(Binary) code(Call) \ 34 | code(Get) code(Grouping) code(Unary) \ 35 | code(Literal) code(Logical) code(This) \ 36 | code(Set) code(Super) code(Variable) 37 | 38 | typedef enum 39 | { 40 | #define DEFINE_ENUM_TYPE(type) EXPR_##type, 41 | FOREACH_AST_NODE(DEFINE_ENUM_TYPE) 42 | #undef DEFINE_ENUM_TYPE 43 | } ExprType; 44 | 45 | 46 | typedef struct Expr_tag 47 | { 48 | ExprType type; 49 | struct Expr_tag *next; 50 | } Expr; 51 | 52 | #define AS_EXPR(expr) (Expr *)expr 53 | 54 | Expr * init_assign(Token *name, Expr *value); 55 | Expr * init_binary(Expr *left, Token *operator, Expr *right); 56 | Expr * init_call(Expr *callee, Token *paren, Expr *arguments); 57 | Expr * init_get(Expr *object, Token *name); 58 | Expr * init_grouping(Expr *expression); 59 | Expr * init_literal(Token *value); 60 | Expr * init_logical(Expr *left, Token *operator, Expr *right); 61 | Expr * init_set(Expr *object, Token *name, Expr *value); 62 | Expr * init_super(Token *keyword, Token *method); 63 | Expr * init_this(Token *keyword); 64 | Expr * init_unary(Token *operator, Expr *right); 65 | Expr * init_variable(Token *name); 66 | 67 | Expr * init_bool_literal(bool value); 68 | Expr * init_nil_literal(void); 69 | Expr * init_number_literal(double value); 70 | 71 | void free_expr(Expr *expr); 72 | 73 | int32_t expr_count(Expr *elements); 74 | Expr * expr_appendTo(Expr *head, Expr *tail); 75 | 76 | Expr * make_test_expr(void); 77 | 78 | // NOTE: "Assign : Token name, Expr value", 79 | typedef struct 80 | { 81 | Expr expr; 82 | Token *name; 83 | Expr *value; 84 | } Assign; 85 | 86 | // NOTE: "Binary : Expr left, Token operator, Expr right", 87 | typedef struct 88 | { 89 | Expr expr; 90 | Expr *left; 91 | Token *operator; 92 | Expr *right; 93 | } Binary; 94 | 95 | // NOTE: "Call : Expr callee, Token paren, List arguments", 96 | typedef struct 97 | { 98 | Expr expr; 99 | Expr *callee; 100 | Token *paren; // token for the closing parenthesis; its location is used when reporting a runtime error 101 | Expr *arguments; // List of expressions 102 | } Call; 103 | 104 | // NOTE: "Get : Expr object, Token name", 105 | typedef struct 106 | { 107 | Expr expr; 108 | Expr *object; 109 | Token *name; 110 | } Get; 111 | 112 | // NOTE: "Grouping : Expr expression", 113 | typedef struct 114 | { 115 | Expr expr; 116 | Expr *expression; 117 | } Grouping; 118 | 119 | // NOTE: "Literal : Object value", 120 | typedef struct 121 | { 122 | Expr expr; 123 | Token value; 124 | } Literal; 125 | 126 | // NOTE: "Logical : Expr left, Token operator, Expr right", 127 | typedef struct 128 | { 129 | Expr expr; 130 | Expr *left; 131 | Token *operator; 132 | Expr *right; 133 | } Logical; 134 | 135 | // NOTE: "Set : Expr object, Token name, Expr value", 136 | typedef struct 137 | { 138 | Expr expr; 139 | Expr *object; 140 | Token *name; 141 | Expr *value; 142 | } Set; 143 | 144 | // NOTE: "Super : Token keyword, Token method", 145 | typedef struct 146 | { 147 | Expr expr; 148 | Token *keyword; 149 | Token *method; 150 | } Super; 151 | 152 | // NOTE: "This : Token keyword", 153 | typedef struct 154 | { 155 | Expr expr; 156 | Token *keyword; 157 | } This; 158 | 159 | // NOTE: "Unary : Token operator, Expr right", 160 | typedef struct 161 | { 162 | Expr expr; 163 | Token *operator; 164 | Expr *right; 165 | } Unary; 166 | 167 | // NOTE: "Variable : Token name" 168 | typedef struct 169 | { 170 | Expr expr; 171 | Token *name; 172 | } Variable; 173 | 174 | /***********/ 175 | /* Visitor */ 176 | /***********/ 177 | 178 | typedef struct 179 | { 180 | #define DEFINE_VISIT(type) \ 181 | void *(*visit##type)(type *, void *context); 182 | 183 | FOREACH_AST_NODE(DEFINE_VISIT) 184 | #undef DEFINE_VISIT 185 | } ExprVisitor; 186 | 187 | void * expr_accept_visitor(Expr *expr, ExprVisitor *visitor, void *context); 188 | 189 | #endif /* expr_h */ 190 | -------------------------------------------------------------------------------- /src/garbage_collector.c: -------------------------------------------------------------------------------- 1 | // 2 | // garbage_collector.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 20/11/2017. 6 | // 7 | 8 | #include "garbage_collector.h" 9 | 10 | #include 11 | 12 | #include "common.h" 13 | #include "lox_class.h" 14 | #include "lox_function.h" 15 | #include "lox_instance.h" 16 | #include "objects.h" 17 | #include "string.h" 18 | 19 | extern inline bool gcLock(Object *object, GarbageCollector *collector); 20 | extern inline void gcPopLock(GarbageCollector *collector); 21 | extern inline void gcPopLockn(int32_t n, GarbageCollector *collector); 22 | extern inline void gcClearLocks(GarbageCollector *collector); 23 | 24 | 25 | #define GC_OBJECT_SIZE (sizeof(Object)) 26 | #define GC_OBJECTS_PER_PAGE ((PAGE_SIZE - sizeof(MemoryPage)) / GC_OBJECT_SIZE) 27 | 28 | static void gcAllocObjects(GarbageCollector *collector) 29 | { 30 | MemoryPage *page = (MemoryPage *)lox_allocn(uint8_t, PAGE_SIZE); 31 | if(page == NULL) 32 | { 33 | fatal_outOfMemory(); 34 | } 35 | page->next = collector->memoryPages; 36 | collector->memoryPages = page; 37 | 38 | Object *firstObject = (Object *)(page + 1); 39 | Object *object = firstObject; 40 | int32_t count = GC_OBJECTS_PER_PAGE; 41 | while (count > 0) 42 | { 43 | Object *next = object + 1; 44 | object->type = OT_UNUSED; 45 | object->next = next; 46 | object = next; 47 | --count; 48 | } 49 | assert((uint8_t *)object < (uint8_t *)page + PAGE_SIZE); 50 | assert((uint8_t *)(object + 1) >= (uint8_t *)page + PAGE_SIZE); 51 | 52 | (object - 1)->next = collector->firstUnused; 53 | collector->firstUnused = (Object *)firstObject; 54 | collector->objectsCount += GC_OBJECTS_PER_PAGE; 55 | 56 | #ifdef GC_KEEPS_STATS 57 | collector->unusedObjectsCount += GC_OBJECTS_PER_PAGE; 58 | #endif 59 | 60 | if (collector->maxObjects < collector->objectsCount) 61 | { 62 | collector->maxObjects = collector->objectsCount; 63 | } 64 | } 65 | 66 | GarbageCollector *gcInit() 67 | { 68 | GarbageCollector *collector = lox_alloc(GarbageCollector); 69 | collector->firstObject = NULL; 70 | collector->laundryList = NULL; 71 | collector->firstUnused = NULL; 72 | 73 | collector->objectsCount = 0; 74 | collector->maxObjects = 0; 75 | 76 | collector->firstEnvironment = NULL; 77 | collector->firstUnusedEnvironment = NULL; 78 | 79 | collector->environmentsCount = 0; 80 | collector->maxEnvironments = GC_INITIAL_ENVIRONMENTS_THRESHOLD; 81 | 82 | collector->visitedMark = 0; 83 | collector->recycledMark = 1; 84 | collector->lockedCount = 0; 85 | 86 | collector->memoryPages = NULL; 87 | 88 | collector->activeObjectsCount = 0; 89 | collector->activeEnvironmentsCount = 0; 90 | 91 | #ifdef GC_KEEPS_STATS 92 | collector->unusedObjectsCount = 0; 93 | collector->unusedEnvironmentsCount = 0; 94 | collector->debug_recycledObjCount = 0; 95 | collector->debug_recycledEnvCount = 0; 96 | #endif 97 | 98 | gcAllocObjects(collector); 99 | gcAllocObjects(collector); 100 | 101 | return collector; 102 | } 103 | 104 | static inline void objFree(Object *object) 105 | { 106 | assert(object->type == OT_UNUSED); 107 | #ifdef DEBUG_SCRAMBLE_MEMORY 108 | assert(object->klass == DEBUG_SCRAMBLE_VALUE); 109 | #endif 110 | } 111 | 112 | Object * gcGetObject(GarbageCollector *collector) 113 | { 114 | #ifdef GC_DEBUG 115 | gcCollect(collector); 116 | #endif 117 | #ifdef GC_KEEPS_STATS 118 | assert(collector->activeObjectsCount + collector->unusedObjectsCount == collector->objectsCount); 119 | #endif 120 | if (collector->firstUnused == NULL) 121 | { 122 | #ifdef GC_KEEPS_STATS 123 | assert(collector->unusedObjectsCount == 0); 124 | #endif 125 | if (collector->objectsCount >= collector->maxObjects) 126 | { 127 | gcCollect(collector); 128 | } 129 | if (collector->firstUnused == NULL) 130 | { 131 | gcAllocObjects(collector); 132 | } 133 | } 134 | 135 | Object *object = collector->firstUnused; 136 | collector->firstUnused = object->next; 137 | 138 | object->next = collector->firstObject; 139 | collector->firstObject = object; 140 | ++collector->activeObjectsCount; 141 | 142 | #ifdef GC_KEEPS_STATS 143 | --collector->unusedObjectsCount; 144 | #endif 145 | 146 | object->marked = GC_CLEAR; 147 | 148 | return object; 149 | } 150 | 151 | // Returns a new environment, or NULL if it could not be allocated. 152 | Environment * gcGetEnvironment(GarbageCollector *collector) 153 | { 154 | #ifdef GC_DEBUG 155 | gcCollect(collector); 156 | #endif 157 | if(collector->firstUnusedEnvironment == NULL) 158 | { 159 | if (collector->environmentsCount >= collector->maxEnvironments) 160 | { 161 | gcCollect(collector); 162 | } 163 | } 164 | 165 | Environment *environment; 166 | if (collector->firstUnusedEnvironment) 167 | { 168 | environment = collector->firstUnusedEnvironment; 169 | collector->firstUnusedEnvironment = environment->next; 170 | #ifdef GC_KEEPS_STATS 171 | --collector->unusedEnvironmentsCount; 172 | #endif 173 | } 174 | else 175 | { // Alloc a bunch of them at a time? 176 | if(collector->environmentsCount >= LOX_MAX_ENVIRONMENTS) 177 | { 178 | return NULL; 179 | } 180 | environment = lox_alloc(Environment); 181 | if(environment == NULL) 182 | { 183 | fatal_outOfMemory(); 184 | } 185 | ++collector->environmentsCount; 186 | } 187 | environment->next = collector->firstEnvironment; 188 | collector->firstEnvironment = environment; 189 | environment->marked = GC_CLEAR; 190 | ++collector->activeEnvironmentsCount; 191 | 192 | return environment; 193 | } 194 | 195 | 196 | void gcSetGlobalEnvironment(Environment *globals, GarbageCollector *collector) 197 | { 198 | ++collector->environmentsCount; 199 | globals->next = collector->firstEnvironment; 200 | collector->firstEnvironment = globals; 201 | globals->marked = GC_CLEAR; 202 | ++collector->activeEnvironmentsCount; 203 | } 204 | 205 | static void gcMarkEnvironment(Environment *environment, GarbageCollector *collector); 206 | static void gcMarkObject(Object *object, GarbageCollector *collector); 207 | 208 | static inline void gcMarkArguments(LoxArguments *args, GarbageCollector *collector) 209 | { 210 | for (int32_t i = 0; i < args->count; ++i) 211 | { 212 | gcMarkObject(args->values[i], collector); 213 | } 214 | } 215 | 216 | static inline void gcMarkClass(LoxClass *klass, GarbageCollector *collector) 217 | { 218 | klass->marked = collector->visitedMark; 219 | for(int32_t index = 0; index < klass->methodsCount; ++index) 220 | { 221 | gcMarkEnvironment(klass->methods[index].function->closure, collector); 222 | } 223 | if ((klass->superClass) && (klass->superClass->marked != collector->visitedMark)) 224 | { 225 | gcMarkClass(klass->superClass, collector); 226 | } 227 | } 228 | 229 | static inline void gcMarkFunction(LoxFunction *function, GarbageCollector *collector) 230 | { 231 | function->marked = collector->visitedMark; 232 | gcMarkEnvironment(function->closure, collector); 233 | } 234 | 235 | static inline void gcMarkInstance(LoxInstance *instance, GarbageCollector *collector) 236 | { 237 | instance->marked = collector->visitedMark; 238 | for (int32_t index = 0; index < instance->fieldsCount; ++index) 239 | { 240 | gcMarkObject(instance->fields[index].value, collector); 241 | } 242 | gcMarkClass(instance->klass, collector); 243 | } 244 | 245 | static void gcMarkObject(Object *object, GarbageCollector *collector) 246 | { 247 | if (object == NULL || object->marked == collector->visitedMark) 248 | { 249 | return; 250 | } 251 | object->marked = collector->visitedMark; 252 | switch (object->type) 253 | { 254 | case OT_ARGUMENTS: { 255 | gcMarkArguments(obj_unwrapArguments(object), collector); 256 | } break; 257 | case OT_CLASS: { 258 | gcMarkClass(obj_unwrapClass(object), collector); 259 | } break; 260 | case OT_FUNCTION: { 261 | gcMarkFunction(object->function, collector); 262 | } break; 263 | case OT_INSTANCE: { 264 | gcMarkInstance(obj_unwrapInstance(object), collector); 265 | } break; 266 | case OT_BOOLEAN: 267 | case OT_CALLABLE: 268 | case OT_NIL: 269 | case OT_NUMBER: 270 | case OT_STRING: 271 | break; 272 | case OT_UNUSED: 273 | INVALID_CASE; 274 | } 275 | } 276 | 277 | static void gcMarkEnvironment(Environment *environment, GarbageCollector *collector) 278 | { 279 | if(environment == NULL || environment->marked == collector->visitedMark) 280 | { 281 | return; 282 | } 283 | environment->marked = collector->visitedMark; 284 | 285 | for (int32_t index = 0; index < environment->slotsUsed; ++index) 286 | { 287 | gcMarkObject(environment->values[index], collector); 288 | } 289 | gcMarkEnvironment(environment->enclosing, collector); 290 | } 291 | 292 | // Releases an object. The Object structure is moved to the unused objects list so 293 | // that it can be reused. If its value cannot be shared it's freed if appropriate. 294 | // If more objects can retain the same value (class, function, or instance) the object 295 | // is sent to the laundry list the first time that value is met; otherwise it is moved 296 | // to the unused object list as usual. The values in the laundry list will be freed in 297 | // a subsequent step; and they are marked with recycledMark so that we know we already 298 | // visited them. 299 | static void gcRelease(Object *object, GarbageCollector *collector) 300 | { 301 | assert(object->marked != collector->visitedMark && object->marked != collector->recycledMark); 302 | switch (object->type) 303 | { 304 | case OT_ARGUMENTS: 305 | { 306 | argumentsFree(object->arguments); 307 | } break; 308 | case OT_CALLABLE: 309 | { 310 | callableFree(object->callable); 311 | } break; 312 | case OT_CLASS: 313 | { 314 | LoxClass *klass = obj_unwrapClass(object); 315 | if((klass->marked != collector->visitedMark) && (klass->marked != collector->recycledMark)) 316 | { 317 | klass->marked = collector->recycledMark; 318 | object->next = collector->laundryList; 319 | collector->laundryList = object; 320 | return; 321 | } 322 | } break; 323 | case OT_FUNCTION: 324 | { 325 | LoxFunction *function = obj_unwrapFunction(object); 326 | if ((function->marked != collector->visitedMark) && 327 | (function->marked != collector->recycledMark)) 328 | { 329 | function->marked = collector->recycledMark; 330 | object->next = collector->laundryList; 331 | collector->laundryList = object; 332 | return; 333 | } 334 | } break; 335 | case OT_INSTANCE: 336 | { 337 | LoxInstance *instance = obj_unwrapInstance(object); 338 | if((instance->marked != collector->visitedMark) && (instance->marked != collector->recycledMark)) 339 | { 340 | instance->marked = collector->recycledMark; 341 | object->next = collector->laundryList; 342 | collector->laundryList = object; 343 | return; 344 | } 345 | } break; 346 | case OT_STRING: 347 | { 348 | str_free(object->string); 349 | } break; 350 | case OT_BOOLEAN: 351 | case OT_NIL: 352 | case OT_NUMBER: 353 | { 354 | // do nothing 355 | } break; 356 | case OT_UNUSED: 357 | INVALID_PATH; 358 | } 359 | object->type = OT_UNUSED; 360 | #ifdef DEBUG_SCRAMBLE_MEMORY 361 | object->klass = DEBUG_SCRAMBLE_VALUE; 362 | #endif 363 | object->next = collector->firstUnused; 364 | collector->firstUnused = object; 365 | #ifdef GC_KEEPS_STATS 366 | ++collector->unusedObjectsCount; 367 | ++collector->debug_recycledObjCount; 368 | #endif 369 | } 370 | 371 | // Sweep step of the mark & sweep garbage collector 372 | static void gcSweep(GarbageCollector *collector) 373 | { 374 | // NOTE: Visits all active objects and releases the ones 375 | // that were not marked. 376 | Object **object = &collector->firstObject; 377 | while (*object) 378 | { 379 | assert((*object)->marked != collector->recycledMark); 380 | if ((*object)->marked == collector->visitedMark) 381 | { 382 | object = &(*object)->next; 383 | } 384 | else 385 | { 386 | Object *released = *object; 387 | *object = released->next; 388 | gcRelease(released, collector); 389 | --collector->activeObjectsCount; 390 | } 391 | } 392 | 393 | // NOTE: Free the objects in the laundry list 394 | while(collector->laundryList != NULL) 395 | { 396 | Object *object = collector->laundryList; 397 | switch (object->type) 398 | { 399 | case OT_CLASS: { 400 | assert(object->klass->marked == collector->recycledMark); 401 | classFree(object->klass); 402 | } break; 403 | case OT_CALLABLE: { 404 | callableFree(object->callable); 405 | } break; 406 | case OT_FUNCTION: { 407 | assert(object->function->marked == collector->recycledMark); 408 | function_free(object->function); 409 | } break; 410 | case OT_INSTANCE: { 411 | instanceFree(object->instance); 412 | } break; 413 | INVALID_DEFAULT_CASE; 414 | } 415 | collector->laundryList = object->next; 416 | 417 | object->type = OT_UNUSED; 418 | #ifdef DEBUG_SCRAMBLE_MEMORY 419 | object->klass = DEBUG_SCRAMBLE_VALUE; 420 | #endif 421 | object->next = collector->firstUnused; 422 | collector->firstUnused = object; 423 | #ifdef GC_KEEPS_STATS 424 | ++collector->unusedObjectsCount; 425 | ++collector->debug_recycledObjCount; 426 | #endif 427 | } 428 | 429 | // NOTE: Recycle the environments that were not marked. 430 | Environment **env = &collector->firstEnvironment; 431 | while (*env) 432 | { 433 | assert((*env)->marked != collector->recycledMark); 434 | if ((*env)->marked == collector->visitedMark) 435 | { 436 | env = &(*env)->next; 437 | } 438 | else 439 | { 440 | Environment *released = *env; 441 | *env = released->next; 442 | released->next = collector->firstUnusedEnvironment; 443 | collector->firstUnusedEnvironment = released; 444 | --collector->activeEnvironmentsCount; 445 | #ifdef GC_KEEPS_STATS 446 | ++collector->unusedEnvironmentsCount; 447 | ++collector->debug_recycledEnvCount; 448 | #endif 449 | } 450 | } 451 | } 452 | 453 | // Run the mark & sweep garbage collector 454 | void gcCollect(GarbageCollector *collector) 455 | { 456 | #ifdef GC_KEEPS_STATS 457 | assert(collector->activeObjectsCount + collector->unusedObjectsCount == collector->objectsCount); 458 | #endif 459 | // NOTE: First we mark all objects that have been locked 460 | for(int32_t index = 0; index < collector->lockedCount; ++index) 461 | { 462 | gcMarkObject(collector->locked[index], collector); 463 | } 464 | 465 | // NOTE: Next we mark all objects stored in active environments 466 | for(Environment *env = collector->firstEnvironment; 467 | env != NULL; 468 | env = env->next) 469 | { 470 | if(env->isActive) 471 | { 472 | gcMarkEnvironment(env, collector); 473 | } 474 | } 475 | 476 | // NOTE: Finally we perform the sweep step 477 | gcSweep(collector); 478 | 479 | // NOTE: Update the thresholds for the next garbage collection 480 | collector->maxObjects = max(2*collector->activeObjectsCount, collector->objectsCount); 481 | collector->maxEnvironments = max(2*collector->activeEnvironmentsCount, collector->objectsCount); 482 | 483 | // NOTE: define new values for the marks, so we do not reset the marks of all remaining objects 484 | collector->visitedMark += 2; 485 | collector->recycledMark += 2; 486 | if(collector->visitedMark == (1 << 30)) 487 | { 488 | collector->visitedMark = 0; 489 | collector->recycledMark = 1; 490 | } 491 | } 492 | 493 | // Releases all objects. 494 | static void gcClean(GarbageCollector *collector) 495 | { 496 | #ifdef GC_KEEPS_STATS 497 | assert(collector->activeObjectsCount + collector->unusedObjectsCount == collector->objectsCount); 498 | #endif 499 | assert(collector->lockedCount == 0); 500 | // NOTE: At this point everything is unmarked, so the sweep step releases everything. 501 | gcSweep(collector); 502 | 503 | #ifdef GC_KEEPS_STATS 504 | printf("MAX ACTIVE obj: %d env: %d - RECYCLED obj: %d env: %d\n", collector->objectsCount, collector->environmentsCount, collector->debug_recycledObjCount - collector->unusedObjectsCount, collector->debug_recycledEnvCount - collector->unusedEnvironmentsCount); 505 | #endif 506 | } 507 | 508 | // Releases everything and frees the garbage collector. 509 | void gcFree(GarbageCollector *collector) 510 | { 511 | gcClean(collector); 512 | 513 | assert(collector->firstEnvironment == NULL); 514 | assert(collector->firstObject == NULL); 515 | assert(collector->activeEnvironmentsCount == 0); 516 | 517 | Object *object = collector->firstUnused; 518 | while(object) 519 | { 520 | Object *next = object->next; 521 | objFree(object); 522 | --collector->objectsCount; 523 | #ifdef GC_KEEPS_STATS 524 | --collector->unusedObjectsCount; 525 | #endif 526 | object = next; 527 | } 528 | assert(collector->objectsCount == 0); 529 | 530 | MemoryPage *page = collector->memoryPages; 531 | while(page) 532 | { 533 | MemoryPage *next = page->next; 534 | lox_free(page); 535 | page = next; 536 | } 537 | 538 | Environment *environment = collector->firstUnusedEnvironment; 539 | while(environment) 540 | { 541 | Environment *next = environment->next; 542 | env_free(environment); 543 | --collector->environmentsCount; 544 | environment = next; 545 | #ifdef GC_KEEPS_STATS 546 | --collector->unusedEnvironmentsCount; 547 | #endif 548 | } 549 | assert(collector->environmentsCount == 0); 550 | 551 | lox_free(collector); 552 | } 553 | -------------------------------------------------------------------------------- /src/garbage_collector.h: -------------------------------------------------------------------------------- 1 | // 2 | // garbage_collector.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 20/11/2017. 6 | // 7 | 8 | #ifndef garbage_collector_h 9 | #define garbage_collector_h 10 | 11 | #include "environment.h" 12 | #include "memory.h" 13 | 14 | // NOTE: objects with mark set to GC_CLEAR are considered unmarked. 15 | #define GC_CLEAR -1 16 | 17 | typedef struct MemoryPage_tag 18 | { 19 | struct MemoryPage_tag *next; 20 | } MemoryPage; 21 | 22 | typedef struct GarbageCollector_tag 23 | { 24 | Object *firstObject; 25 | Object *laundryList; 26 | Object *firstUnused; 27 | int32_t objectsCount; 28 | int32_t maxObjects; 29 | 30 | // NOTE: current mark values given to visited and recicled objects 31 | int32_t visitedMark; 32 | int32_t recycledMark; 33 | 34 | Environment *firstEnvironment; 35 | Environment *firstUnusedEnvironment; 36 | int32_t environmentsCount; 37 | int32_t maxEnvironments; 38 | 39 | Object *locked[GC_LOCKS_STACK_SIZE]; 40 | int32_t lockedCount; 41 | 42 | MemoryPage *memoryPages; 43 | 44 | int32_t activeEnvironmentsCount; 45 | int32_t activeObjectsCount; 46 | 47 | // NOTE: This is just to take some stats 48 | #ifdef GC_KEEPS_STATS 49 | int32_t unusedObjectsCount; 50 | int32_t unusedEnvironmentsCount; 51 | 52 | int32_t debug_recycledObjCount; 53 | int32_t debug_recycledEnvCount; 54 | #endif 55 | } GarbageCollector; 56 | 57 | GarbageCollector * gcInit(void); 58 | void gcFree(GarbageCollector *collector); 59 | Object * gcGetObject(GarbageCollector *collector); 60 | void gcSetGlobalEnvironment(Environment *globals, GarbageCollector *collector); 61 | Environment * gcGetEnvironment(GarbageCollector *collector); 62 | void gcCollect(GarbageCollector *collector); 63 | 64 | inline bool gcLock(Object *object, GarbageCollector *collector) 65 | { 66 | if (collector->lockedCount == ARRAY_COUNT(collector->locked)) 67 | { 68 | // TODO: At the moment, a stack overflow exception is thrown here. 69 | // This is fine since GC_LOCKS_STACK_SIZE is chose to be large. 70 | // Should we keep it smaller and dynamically grow the stack instead? 71 | return false; 72 | } 73 | collector->locked[collector->lockedCount++] = object; 74 | return true; 75 | } 76 | 77 | inline void gcPopLock(GarbageCollector *collector) 78 | { 79 | assert(collector->lockedCount > 0); 80 | --collector->lockedCount; 81 | #ifdef DEBUG_SCRAMBLE_MEMORY 82 | collector->locked[collector->lockedCount] = DEBUG_SCRAMBLE_VALUE; 83 | #endif 84 | } 85 | 86 | inline void gcPopLockn(int32_t n, GarbageCollector *collector) 87 | { 88 | collector->lockedCount -= n; 89 | #ifdef DEBUG_SCRAMBLE_MEMORY 90 | for (int32_t index = 0; index < n; ++index) 91 | { 92 | collector->locked[collector->lockedCount + index] = DEBUG_SCRAMBLE_VALUE; 93 | } 94 | #endif 95 | assert(collector->lockedCount >= 0); 96 | } 97 | 98 | inline void gcClearLocks(GarbageCollector *collector) 99 | { 100 | gcPopLockn(collector->lockedCount, collector); 101 | } 102 | 103 | #endif /* garbage_collector_h */ 104 | -------------------------------------------------------------------------------- /src/interpreter.h: -------------------------------------------------------------------------------- 1 | // 2 | // interpreter.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 17/10/2017. 6 | // 7 | 8 | #ifndef interpreter_h 9 | #define interpreter_h 10 | 11 | #include "environment.h" 12 | #include "error.h" 13 | #include "expr.h" 14 | #include "garbage_collector.h" 15 | #include "return.h" 16 | #include "stmt.h" 17 | #include "utility.h" 18 | 19 | #include 20 | 21 | // TODO: The size of the locals hash table limits the maximum size of 22 | // the program that can be run. We need to allow it to grow dynamically. 23 | // NOTE: Must be a power of 2 24 | #define LOCALS_HASH_MAP_SIZE 1024 25 | 26 | typedef struct 27 | { 28 | Expr *expr; 29 | int32_t depth; 30 | int32_t index; 31 | } LocalEntry; 32 | 33 | typedef struct Interpreter_tag 34 | { 35 | ExprVisitor exprVisitor; 36 | StmtVisitor stmtVisitor; 37 | 38 | Environment *globals; 39 | Environment *environment; 40 | LocalEntry locals[LOCALS_HASH_MAP_SIZE]; 41 | int32_t localsCount; 42 | 43 | GarbageCollector *collector; 44 | 45 | Error *runtimeError; 46 | const char *source; 47 | 48 | Timer timer; 49 | 50 | struct timespec time_start; 51 | time_t clock_start; 52 | 53 | // exception handler 54 | jmp_buf catchLocation; 55 | 56 | // NOTE: for repl use only 57 | bool isREPL; 58 | bool exitREPL; 59 | } Interpreter; 60 | 61 | typedef struct 62 | { 63 | Interpreter *interpreter; 64 | char *message; 65 | void (*callback)(Token *token, Interpreter *interpreter); 66 | } ErrorCallback; 67 | 68 | Interpreter * interpreter_init(bool isREPL); 69 | void interpreter_free(Interpreter *interpreter); 70 | void interpret(Stmt *statements, Interpreter *interpreter); 71 | void interpreter_clearRuntimeError(Interpreter *interpreter); 72 | Return * interpreter_executeBlock(Stmt *statements, Environment *environment, Interpreter *interpreter); 73 | void interpreter_resolve(Expr *expr, int32_t depth, int32_t index, Interpreter *interpreter); 74 | 75 | __attribute__((__noreturn__)) 76 | void interpreter_throwExit(Interpreter *interpreter); 77 | 78 | #endif /* interpreter_h */ 79 | -------------------------------------------------------------------------------- /src/lox_callable.c: -------------------------------------------------------------------------------- 1 | // 2 | // lox_callable.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 21/10/2017. 6 | // 7 | 8 | #include "lox_callable.h" 9 | #include "common.h" 10 | #include "garbage_collector.h" 11 | 12 | extern inline int32_t callableArity(const LoxCallable *f); 13 | extern inline bool isLoxCallable(const Object *callee); 14 | 15 | LoxCallable * callableInit(lox_callable_function *f, int32_t arity) 16 | { 17 | assert(arity >= 0 && arity <= LOX_MAX_ARG_COUNT); 18 | 19 | LoxCallable* callable = lox_alloc(LoxCallable); 20 | if(callable == NULL) 21 | { 22 | fatal_outOfMemory(); 23 | } 24 | callable->function = f; 25 | callable->arity = arity; 26 | 27 | return callable; 28 | } 29 | 30 | void callableFree(LoxCallable *callable) 31 | { 32 | lox_free(callable); 33 | } 34 | 35 | /* LOX native functions */ 36 | 37 | #include "interpreter.h" 38 | #include 39 | 40 | // clock() returns the time elapsed in milliseconds since a reference time 41 | LOX_CALLABLE(lox_clock) 42 | { 43 | Interpreter *interpreter = (Interpreter *)context; 44 | double elapsedSec = timer_elapsedSec(&interpreter->timer); 45 | 46 | Object *result = obj_newNumber(elapsedSec*1000, interpreter->collector); 47 | return result; 48 | } 49 | 50 | // env() prints all objects defined in the current environment 51 | LOX_CALLABLE(lox_env) 52 | { 53 | Interpreter *interpreter = (Interpreter *)context; 54 | env_printReportAll(interpreter->environment); 55 | 56 | Object *result = obj_newNil(interpreter->collector); 57 | return result; 58 | } 59 | 60 | // quit() exits the interpreter 61 | LOX_CALLABLE(lox_quit) 62 | { 63 | Interpreter *interpreter = (Interpreter *)context; 64 | if (interpreter->isREPL) 65 | { 66 | interpreter_throwExit(interpreter); 67 | } 68 | else 69 | { 70 | exit(0); 71 | } 72 | } 73 | 74 | // help() prints a some help in the interpreter 75 | LOX_CALLABLE(lox_help) 76 | { 77 | printf("\nLoxi is an interpreter for the Lox language, as described on\nhttp://www.craftinginterpreters.com/the-lox-language.html\n\n"); 78 | printf("Native functions:\n"); 79 | printf(" clock() - returns the time (in msec) elapsed since the start\n"); 80 | printf(" env() - prints objects defined in current environment\n"); 81 | printf(" help() - prints this help\n"); 82 | printf(" quit() - exits the interpreter\n"); 83 | printf("\n"); 84 | return NULL; 85 | } 86 | -------------------------------------------------------------------------------- /src/lox_callable.h: -------------------------------------------------------------------------------- 1 | // 2 | // lox_callable.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 21/10/2017. 6 | // 7 | 8 | #ifndef lox_callable_h 9 | #define lox_callable_h 10 | 11 | #include 12 | 13 | struct LoxArguments_tag; 14 | #define LOX_CALLABLE(name) struct Object_tag *name(struct LoxArguments_tag *args, void *context) 15 | typedef LOX_CALLABLE(lox_callable_function); 16 | 17 | typedef struct LoxCallable_tag 18 | { 19 | lox_callable_function *function; 20 | int32_t arity; 21 | } LoxCallable; 22 | 23 | LOX_CALLABLE(lox_clock); 24 | LOX_CALLABLE(lox_help); 25 | LOX_CALLABLE(lox_env); 26 | LOX_CALLABLE(lox_quit); 27 | 28 | LoxCallable * callableInit(lox_callable_function *f, int32_t arity); 29 | void callableFree(LoxCallable *callable); 30 | 31 | inline int32_t callableArity(const LoxCallable *f) 32 | { 33 | return f->arity; 34 | } 35 | 36 | #include "objects.h" 37 | 38 | inline bool isLoxCallable(const Object *callee) 39 | { 40 | return callee->type == OT_CALLABLE; 41 | } 42 | 43 | #endif /* lox_callable_h */ 44 | -------------------------------------------------------------------------------- /src/lox_class.c: -------------------------------------------------------------------------------- 1 | // 2 | // lox_class.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 08/11/2017. 6 | // 7 | 8 | #include "garbage_collector.h" 9 | #include "interpreter.h" 10 | #include "lox_class.h" 11 | #include "lox_function.h" 12 | #include "lox_instance.h" 13 | 14 | extern inline bool isLoxClass(const Object *klass); 15 | 16 | LOX_CALLABLE(class_call) 17 | { 18 | struct LoxClassInitializerContext *initializerContext = (struct LoxClassInitializerContext *)context; 19 | Interpreter *interpreter = initializerContext->interpreter; 20 | LoxClass *klass = initializerContext->klass; 21 | 22 | LoxInstance *instance = instanceInit(klass); 23 | LoxFunction *initializer = findMethod(instance, klass, "init", &initializerContext->error, interpreter->collector); 24 | if(initializerContext->error) 25 | { 26 | return NULL; 27 | } 28 | Object *result; 29 | if (initializer != NULL) 30 | { 31 | result = function_call(initializer, args, &initializerContext->error, interpreter); 32 | 33 | env_release(initializer->closure); 34 | lox_free(initializer); 35 | 36 | if(initializerContext->error) 37 | { 38 | return NULL; 39 | } 40 | } 41 | else 42 | { 43 | result = obj_wrapInstance(instance, interpreter->collector); 44 | } 45 | return result; 46 | } 47 | 48 | const LoxFunction * findClassMethod(const LoxClass *klass, const char *name) 49 | { 50 | for (int32_t index = 0; index < klass->methodsCount; ++index) 51 | { 52 | if(str_isEqual(name, klass->methods[index].name)) 53 | { 54 | const LoxFunction *method = klass->methods[index].function; 55 | return method; 56 | } 57 | } 58 | return NULL; 59 | } 60 | 61 | int32_t class_arity(const LoxClass *klass) 62 | { 63 | const LoxFunction *initializer = findClassMethod(klass, "init"); 64 | if (initializer == NULL) 65 | { 66 | return 0; 67 | } 68 | int32_t arity = initializer->declaration->arity; 69 | return arity; 70 | } 71 | 72 | LoxClass * classInit(const char *name, LoxClass *superClass, MethodEntry *methods, int32_t methodsCount) 73 | { 74 | LoxClass* klass = lox_alloc(LoxClass); 75 | if(klass == NULL) 76 | { 77 | fatal_outOfMemory(); 78 | } 79 | assert(klass != NULL); 80 | klass->marked = GC_CLEAR; 81 | klass->name = name; 82 | klass->superClass = superClass; 83 | klass->methods = methods; 84 | klass->methodsCount = methodsCount; 85 | int32_t arity = class_arity(klass); 86 | klass->callable = callableInit(class_call, arity); 87 | return klass; 88 | } 89 | 90 | static void classFreeMethods(LoxClass *klass) 91 | { 92 | for (int32_t index = 0; index < klass->methodsCount; ++index) 93 | { 94 | function_free(klass->methods[index].function); 95 | } 96 | lox_free(klass->methods); 97 | } 98 | 99 | void classFree(LoxClass *klass) 100 | { 101 | classFreeMethods(klass); 102 | callableFree(klass->callable); 103 | lox_free(klass); 104 | } 105 | 106 | char * classToString(const LoxClass *klass) 107 | { 108 | char *str = str_dup(klass->name); 109 | str_appendLiteral(str, " class"); 110 | return str; 111 | } 112 | 113 | LoxFunction * findMethod(LoxInstance *instance, LoxClass *klass, const char *name, Error **error, GarbageCollector *collector) 114 | { 115 | for (int32_t index = 0; index < klass->methodsCount; ++index) 116 | { 117 | if(str_isEqual(name, klass->methods[index].name)) 118 | { 119 | const LoxFunction *function = klass->methods[index].function; 120 | LoxFunction *result = function_bind(function, instance, error, collector); 121 | return result; 122 | } 123 | } 124 | 125 | if (klass->superClass != NULL) 126 | { 127 | return findMethod(instance, klass->superClass, name, error, collector); 128 | } 129 | 130 | return NULL; 131 | } 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/lox_class.h: -------------------------------------------------------------------------------- 1 | // 2 | // lox_class.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 08/11/2017. 6 | // 7 | 8 | #ifndef lox_class_h 9 | #define lox_class_h 10 | 11 | #include "lox_callable.h" 12 | #include "memory.h" 13 | #include "objects.h" 14 | #include "string.h" 15 | 16 | struct Interpreter_tag; 17 | typedef struct Interpreter_tag Interpreter; 18 | 19 | typedef struct 20 | { 21 | const char *name; 22 | LoxFunction *function; 23 | } MethodEntry; 24 | 25 | typedef struct LoxClass_tag 26 | { 27 | LoxCallable *callable; 28 | const char *name; 29 | LoxClass *superClass; 30 | // TODO: Use a hash table instead of a simple array to store the methods? 31 | MethodEntry *methods; 32 | int32_t methodsCount; 33 | int32_t marked; 34 | } LoxClass; 35 | 36 | struct LoxClassInitializerContext 37 | { 38 | Interpreter *interpreter; 39 | LoxClass *klass; 40 | Error *error; 41 | }; 42 | 43 | LoxClass * classInit(const char *name, LoxClass *superClass, MethodEntry *methods, int32_t methodsCount); 44 | void classFree(LoxClass *klass); 45 | char * classToString(const LoxClass *klass); 46 | LoxFunction * findMethod(LoxInstance *instance, LoxClass *klass, const char *name, Error **error, GarbageCollector *collector); 47 | 48 | inline bool isLoxClass(const Object* klass) 49 | { 50 | return klass->type == OT_CLASS; 51 | } 52 | 53 | #endif /* lox_class_h */ 54 | -------------------------------------------------------------------------------- /src/lox_function.c: -------------------------------------------------------------------------------- 1 | // 2 | // lox_function.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 31/10/2017. 6 | // 7 | 8 | #include "lox_function.h" 9 | 10 | #include "garbage_collector.h" 11 | #include "lox_instance.h" 12 | #include "interpreter.h" 13 | #include "return.h" 14 | 15 | extern inline bool isLoxFunction(const Object *function); 16 | 17 | LoxFunction * function_init(FunctionStmt *declaration, Environment *closure, bool isInitializer) 18 | { 19 | LoxFunction *function = lox_alloc(LoxFunction); 20 | if(function == NULL) 21 | { 22 | fatal_outOfMemory(); 23 | } 24 | function->declaration = declaration; 25 | function->closure = closure; 26 | function->isInitializer = isInitializer; 27 | function->marked = GC_CLEAR; 28 | return function; 29 | } 30 | 31 | void function_free(LoxFunction *function) 32 | { 33 | lox_free(function); 34 | } 35 | 36 | Object * function_call(const LoxFunction *function, LoxArguments *args, Error **error, Interpreter *interpreter) 37 | { 38 | assert(args->count == function->declaration->arity); 39 | 40 | // NOTE: we dynamically create a new local environment for 41 | // the function to allow for recursion. 42 | Environment *environment = env_init(function->closure, error, interpreter->collector); 43 | if (*error) 44 | { 45 | return NULL; 46 | } 47 | assert(environment != NULL); 48 | 49 | for(int32_t i = 0; i < function->declaration->arity; i++) 50 | { 51 | Token *parameter = function->declaration->parameters[i]; 52 | *error = env_defineLocal(parameter, args->values[i], environment); 53 | assert(*error == NULL); 54 | } 55 | 56 | Return *ret = interpreter_executeBlock(function->declaration->body, environment, interpreter); 57 | 58 | env_release(environment); 59 | 60 | Object *result; 61 | if (function->isInitializer) 62 | { 63 | result = env_getAt(/*"this"*/NULL, 0, 0, function->closure, error, interpreter->collector); 64 | if(*error) 65 | { 66 | return NULL; 67 | } 68 | } 69 | else if(ret != NULL) 70 | { 71 | result = return_unwrap(ret); 72 | } 73 | else 74 | { 75 | result = obj_newNil(interpreter->collector); 76 | } 77 | assert(result != NULL); 78 | 79 | return result; 80 | } 81 | 82 | int32_t function_arity(LoxFunction *function) 83 | { 84 | int32_t arity = function->declaration->arity; 85 | return arity; 86 | } 87 | 88 | char * function_toString(LoxFunction *function, Interpreter *interpreter) 89 | { 90 | char *str = str_fromLiteral("declaration->name, interpreter->source); 93 | str_append(str, tok); 94 | str_free(tok); 95 | } 96 | str_appendLiteral(str, ">"); 97 | return str; 98 | } 99 | 100 | LoxFunction * function_bind(const LoxFunction *function, LoxInstance *instance, Error **error, GarbageCollector *collector) 101 | { 102 | Environment *environment = env_init(function->closure, error, collector); 103 | if (*error) 104 | { 105 | return NULL; 106 | } 107 | assert(environment != NULL); 108 | 109 | env_defineThis(obj_wrapInstance(instance, collector), environment); 110 | 111 | LoxFunction *result = function_init(function->declaration, environment, function->isInitializer); 112 | return result; 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /src/lox_function.h: -------------------------------------------------------------------------------- 1 | // 2 | // lox_function.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 31/10/2017. 6 | // 7 | 8 | #ifndef lox_function_h 9 | #define lox_function_h 10 | 11 | #include "environment.h" 12 | #include "lox_callable.h" 13 | #include "stmt.h" 14 | 15 | struct Interpreter_tag; 16 | typedef struct Interpreter_tag Interpreter; 17 | 18 | #define LOX_FUNCTION(name) Object *name(Object **args, uint32_t argsCount, void *context) 19 | typedef LOX_FUNCTION(lox_function); 20 | 21 | typedef struct LoxFunction_tag 22 | { 23 | FunctionStmt *declaration; 24 | Environment *closure; 25 | bool isInitializer; 26 | int32_t marked; 27 | } LoxFunction; 28 | 29 | typedef struct LoxArguments_tag 30 | { 31 | Object * values[LOX_MAX_ARG_COUNT]; 32 | int32_t count; 33 | } LoxArguments; 34 | 35 | static inline LoxArguments *argumentsInit() 36 | { 37 | LoxArguments *arguments = lox_alloc(LoxArguments); 38 | return arguments; 39 | } 40 | 41 | 42 | static inline void argumentsFree(LoxArguments *arguments) 43 | { 44 | lox_free(arguments); 45 | } 46 | 47 | LoxFunction * function_init(FunctionStmt *declaration, Environment *closure, bool isInitializer); 48 | void function_free(LoxFunction *function); 49 | Object * function_call(const LoxFunction *function, LoxArguments *args, Error **error, Interpreter *interpreter); 50 | int32_t function_arity(LoxFunction *function); 51 | char * function_toString(LoxFunction *function, Interpreter *interpreter); 52 | LoxFunction * function_bind(const LoxFunction *function, LoxInstance *instance, Error **error, GarbageCollector *collector); 53 | 54 | inline bool isLoxFunction(const Object *function) 55 | { 56 | return function->type == OT_FUNCTION; 57 | } 58 | 59 | #endif /* lox_function_h */ 60 | -------------------------------------------------------------------------------- /src/lox_instance.c: -------------------------------------------------------------------------------- 1 | // 2 | // lox_instance.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 08/11/2017. 6 | // 7 | 8 | #include "garbage_collector.h" 9 | #include "lox_function.h" 10 | #include "lox_instance.h" 11 | #include "memory.h" 12 | #include "string.h" 13 | 14 | extern inline bool isLoxInstance(const Object *function); 15 | 16 | LoxInstance * instanceInit(LoxClass *klass) 17 | { 18 | LoxInstance *instance = lox_alloc(LoxInstance); 19 | if(instance == NULL) 20 | { 21 | fatal_outOfMemory(); 22 | } 23 | instance->marked = GC_CLEAR; 24 | instance->klass = klass; 25 | instance->fieldsCount = 0; 26 | return instance; 27 | } 28 | 29 | void instanceFree(LoxInstance *instance) 30 | { 31 | // NOTE: the fields are objects, and they're taken 32 | // care of by the garbage collector. 33 | lox_free(instance); 34 | } 35 | 36 | char * instanceToString(const LoxInstance *instance) 37 | { 38 | char *str = str_dup(instance->klass->name); 39 | str_appendLiteral(str, " instance"); 40 | return str; 41 | } 42 | 43 | // Returns the index of the field named `name`. 44 | // If such a field does not exist, returns -1. 45 | static int32_t indexOf(const char *name, const FieldEntry *fields, int32_t fieldsCount) 46 | { 47 | for (int32_t index = 0; index < fieldsCount; ++index) 48 | { 49 | if (str_isEqual(name, fields[index].name)) 50 | { 51 | return index; 52 | } 53 | } 54 | return -1; 55 | } 56 | 57 | // NOTE: We first look for a field, and if not found for a method. 58 | // This implies that fields shadow methods. 59 | Object * instanceGet(LoxInstance *instance, const Token *property, Error **error, GarbageCollector *collector) 60 | { 61 | const char *name = get_identifier_name(property); 62 | 63 | int32_t index = indexOf(name, instance->fields, instance->fieldsCount); 64 | if (index != -1) 65 | { 66 | Object *result = obj_dup(instance->fields[index].value, collector); 67 | return result; 68 | } 69 | 70 | LoxFunction *method = findMethod(instance, instance->klass, name, error, collector); 71 | if (*error) 72 | { 73 | return NULL; 74 | } 75 | if (method != NULL) 76 | { 77 | Object *result = obj_wrapFunction(method, collector); 78 | env_release(method->closure); 79 | return result; 80 | } 81 | 82 | *error = initErrorIdentifier("Undefined property '", property, "'."); 83 | return NULL; 84 | } 85 | 86 | // Stores a duplicate of value in the field `property` of instance. 87 | // NOTE: the value is duplicated since the interpreter uses it as return value too. 88 | void instanceSet(LoxInstance *instance, const Token *property, Object *value) 89 | { 90 | const char *name = get_identifier_name(property); 91 | int32_t index = indexOf(name, instance->fields, instance->fieldsCount); 92 | if (index == -1) 93 | { 94 | assert(instance->fieldsCount < LOX_INSTANCE_MAX_FIELDS); 95 | index = instance->fieldsCount; 96 | instance->fields[index].name = name; 97 | instance->fieldsCount++; 98 | } 99 | else 100 | { 101 | assert(instance->fields[index].value != NULL); 102 | } 103 | instance->fields[index].value = value; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/lox_instance.h: -------------------------------------------------------------------------------- 1 | // 2 | // lox_instance.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 08/11/2017. 6 | // 7 | 8 | #ifndef lox_instance_h 9 | #define lox_instance_h 10 | 11 | #include "error.h" 12 | #include "lox_class.h" 13 | #include "token.h" 14 | #include "common.h" 15 | 16 | typedef struct 17 | { 18 | const char *name; 19 | Object *value; 20 | } FieldEntry; 21 | 22 | typedef struct LoxInstance_tag 23 | { 24 | LoxClass *klass; 25 | // TODO: Use a hash table instead of a simple array to store the fields? 26 | FieldEntry fields[LOX_INSTANCE_MAX_FIELDS]; 27 | int32_t fieldsCount; 28 | int32_t marked; 29 | } LoxInstance; 30 | 31 | LoxInstance * instanceInit(LoxClass *klass); 32 | void instanceFree(LoxInstance *instance); 33 | char * instanceToString(const LoxInstance *instance); 34 | Object * instanceGet(LoxInstance *instance, const Token *property, Error **error, GarbageCollector *collector); 35 | void instanceSet(LoxInstance *instance, const Token *property, Object *value); 36 | 37 | inline bool isLoxInstance(const Object *function) 38 | { 39 | return function->type == OT_INSTANCE; 40 | } 41 | 42 | #endif /* lox_instance_h */ 43 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 12/10/2017. 6 | // 7 | 8 | #include 9 | 10 | #include "common.h" 11 | #include "interpreter.h" 12 | #include "parser.h" 13 | #include "resolver.h" 14 | #include "scanner.h" 15 | #include "utility.h" 16 | 17 | global bool lox_hadError_ = false; 18 | global bool lox_hadRuntimeError_ = false; 19 | 20 | static void lox_clearError() 21 | { 22 | lox_hadError_ = false; 23 | } 24 | 25 | static void run(const char *source, Interpreter *interpreter) 26 | { 27 | Token *tokens = scan(source); 28 | Stmt *statements = parse(tokens, source); 29 | 30 | // NOTE: Stop if there was a syntax error. 31 | if (lox_hadError_) 32 | { 33 | return; 34 | } 35 | 36 | interpreter->source = source; 37 | resolve(statements, interpreter); 38 | 39 | // NOTE: Stop if there was a resolution error. 40 | if (lox_hadError_) 41 | { 42 | return; 43 | } 44 | 45 | interpret(statements, interpreter); 46 | 47 | while(tokens) 48 | { 49 | Token *next = tokens->nextInScan; 50 | token_free(tokens); 51 | tokens = next; 52 | } 53 | freeStmt(statements); 54 | } 55 | 56 | void runFile(const char *filename) 57 | { 58 | char *source = readFile(filename); 59 | if(source == NULL) 60 | { 61 | exit(LOX_EXIT_CODE_OK); 62 | } 63 | 64 | Interpreter *interpreter = interpreter_init(false); 65 | if (interpreter == NULL) 66 | { 67 | fprintf(stderr, "Fatal error: could not start the interpreter."); 68 | exit(LOX_EXIT_CODE_FATAL_ERROR); 69 | } 70 | 71 | run(source, interpreter); 72 | 73 | if (lox_hadError_) 74 | { 75 | exit(LOX_EXIT_CODE_HAD_ERROR); 76 | } 77 | if (lox_hadRuntimeError_) 78 | { 79 | exit(LOX_EXIT_CODE_HAD_RUNTIME_ERROR); 80 | } 81 | 82 | #ifdef MEMORY_FREE_ON_EXIT 83 | interpreter_free(interpreter); 84 | str_free(source); 85 | #endif 86 | } 87 | 88 | typedef struct Line_tag 89 | { 90 | char *source; 91 | Token *tokens; 92 | Stmt *statements; 93 | int32_t line; 94 | struct Line_tag *next; 95 | } Line; 96 | 97 | Line *lineInit(char *source) 98 | { 99 | Line *input = lox_alloc(Line); 100 | if(input == NULL) 101 | { 102 | fatal_outOfMemory(); 103 | } 104 | input->source = str_fromLiteral(source); 105 | return input; 106 | } 107 | 108 | static void repl() 109 | { 110 | printf("Welcome to LOXI, the Lox Interpreter\n"); 111 | printf("Type 'help();' for help or 'quit();' to exit.\n"); 112 | 113 | Line *lines = NULL; 114 | char input[REPL_MAX_INPUT_LENGTH]; 115 | 116 | Interpreter *interpreter = interpreter_init(true); 117 | if (interpreter == NULL) 118 | { 119 | fprintf(stderr, "Fatal error: could not start the interpreter."); 120 | exit(LOX_EXIT_CODE_FATAL_ERROR); 121 | } 122 | 123 | int32_t lineNumber = 1; 124 | 125 | do { 126 | printf("%d> ", lineNumber); 127 | if (!fgets(input, REPL_MAX_INPUT_LENGTH, stdin)) 128 | { 129 | printf("\n"); 130 | break; 131 | } 132 | input[strlen(input) - 1] = '\0'; 133 | Line *currentLine = lineInit(input); 134 | currentLine->line = lineNumber++; 135 | currentLine->next = lines; 136 | lines = currentLine; 137 | 138 | currentLine->tokens = scanLine(currentLine->source, currentLine->line); 139 | currentLine->statements = parse(currentLine->tokens, currentLine->source); 140 | 141 | // NOTE: Stop if there was a syntax error. 142 | if (!lox_hadError_) 143 | { 144 | 145 | interpreter->source = currentLine->source; 146 | resolve(currentLine->statements, interpreter); 147 | 148 | if (!lox_hadError_) 149 | { 150 | interpret(currentLine->statements, interpreter); 151 | } 152 | } 153 | 154 | // NOTE: We don't interrupt interactive session if an error happened 155 | lox_clearError(); 156 | interpreter_clearRuntimeError(interpreter); 157 | 158 | gcCollect(interpreter->collector); 159 | } while (interpreter->exitREPL != true); 160 | 161 | #ifdef MEMORY_FREE_ON_EXIT 162 | // NOTE: Free all lines 163 | Line *line = lines; 164 | while(line) 165 | { 166 | Token *token = line->tokens; 167 | while(token) 168 | { 169 | Token *next = token->nextInScan; 170 | token_free(token); 171 | token = next; 172 | } 173 | freeStmt(line->statements); 174 | str_free(line->source); 175 | 176 | Line *next = line->next; 177 | lox_free(line); 178 | line = next; 179 | } 180 | 181 | interpreter_free(interpreter); 182 | #endif 183 | } 184 | 185 | int main(int argc, const char * argv[]) 186 | { 187 | lox_alloc_init(); 188 | str_initPools(); 189 | lox_clearError(); 190 | 191 | if(argc == 1) { 192 | repl(); 193 | } else if (argc == 2) { 194 | runFile(argv[1]); 195 | } else { 196 | fprintf(stderr, "Usage: clox [path]\n"); 197 | exit(LOX_EXIT_CODE_FATAL_ERROR); 198 | } 199 | 200 | #ifdef MEMORY_FREE_ON_EXIT 201 | str_freePools(); 202 | #endif 203 | 204 | #ifdef MEMORY_DEBUG 205 | alloc_printActiveDB(); 206 | printLeakedPointers(); 207 | #endif 208 | 209 | return LOX_EXIT_CODE_OK; 210 | } 211 | -------------------------------------------------------------------------------- /src/memory.c: -------------------------------------------------------------------------------- 1 | // 2 | // memory.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 01/11/2017. 6 | // 7 | 8 | #include "memory.h" 9 | 10 | unsigned long str_hashLiteral(const char *str); 11 | unsigned long str_hash(const char *str); 12 | 13 | #ifdef MEMORY_DEBUG 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | typedef uint16_t memory_size; 21 | #define MEMORY_MAX_SIZE ((memory_size)-1) 22 | 23 | typedef struct 24 | { 25 | uint32_t hash; 26 | memory_size count; 27 | memory_size capacity; 28 | } AllocHeader; 29 | 30 | typedef struct 31 | { 32 | uint32_t hash; 33 | } EndMarker; 34 | 35 | #define ALLOC_OFFSET (sizeof(AllocHeader)) 36 | #define ALLOC_ENDMARKER_SIZE (sizeof(EndMarker)) 37 | #define ALLOC_TYPE_NAME_SIZE 32 38 | 39 | #define ALLOC_POINTER_FROM_HEADER(header) (void *)((char *)header + ALLOC_OFFSET) 40 | #define ALLOC_HEADER_FROM_POINTER(ptr) (AllocHeader *)((char *)ptr - ALLOC_OFFSET) 41 | #define ALLOC_MARKER_FROM_HEADER(header, capacity) (EndMarker *)((char *)header + ALLOC_OFFSET + capacity); 42 | #define ALLOC_MARKER_FROM_POINTER(ptr, capacity) (EndMarker *)((char *)ptr + capacity) 43 | 44 | typedef struct 45 | { 46 | uint32_t hash; 47 | int32_t count; 48 | int64_t size; 49 | uint32_t alloc_count; 50 | uint32_t realloc_count; 51 | uint32_t free_count; 52 | char type[ALLOC_TYPE_NAME_SIZE]; 53 | } AllocEntry; 54 | 55 | // NOTE: ALLOC_DB_SIZE must be a power of two 56 | #define ALLOC_DB_SIZE (1 << 9) 57 | #define ALLOC_DB_MASK ((ALLOC_DB_SIZE) - 1) 58 | 59 | static AllocEntry alloc_db[ALLOC_DB_SIZE]; 60 | 61 | static void alloc_initDB() 62 | { 63 | for (int32_t i = 0; i < ALLOC_DB_SIZE; ++i) 64 | { 65 | alloc_db[i].hash = 0; 66 | alloc_db[i].count = 0; 67 | alloc_db[i].size = 0; 68 | alloc_db[i].alloc_count = 0; 69 | alloc_db[i].realloc_count = 0; 70 | alloc_db[i].free_count = 0; 71 | alloc_db[i].type[0] = '0'; 72 | } 73 | } 74 | 75 | // NOTE: We need to align the marker on a 4 bytes boundary since it is an uint32_t 76 | #define MARKER_ALIGNMENT 4 77 | 78 | static void printPointerInfo(AllocHeader *header, const char *prefix, const char *filename, int line); 79 | 80 | void lox_alloc_init() 81 | { 82 | alloc_initDB(); 83 | } 84 | 85 | static void printAllocEntry(const AllocEntry *entry) 86 | { 87 | printf("%s (#%x) count %d (%lld bytes) - %d alloc %d free %d realloc.\n", entry->type, entry->hash, entry->count, entry->size, entry->alloc_count, entry->free_count, entry->realloc_count); 88 | } 89 | 90 | static const char * hashToType(uint32_t hash) 91 | { 92 | uint32_t index = hash & ALLOC_DB_MASK; 93 | AllocEntry *entry = alloc_db + index; 94 | 95 | assert(hash == entry->hash); 96 | 97 | return entry->type; 98 | } 99 | 100 | /* allocatedPointers is a table where we store all pointers that are * 101 | * allocated, and allows us to check whether memory has been leaked. */ 102 | 103 | // NOTE: Maximum number of pointers that can be tracked 104 | #define MAX_ALLOCATED_POINTERS 256*1024 105 | 106 | static AllocHeader * allocatedPointers[MAX_ALLOCATED_POINTERS]; 107 | static int allocatedPointersCount = 0; 108 | 109 | static void storePointer(AllocHeader *pointer) 110 | { 111 | assert(allocatedPointersCount < MAX_ALLOCATED_POINTERS); 112 | allocatedPointers[allocatedPointersCount++] = pointer; 113 | } 114 | 115 | static void removePointer(AllocHeader *pointer) 116 | { 117 | for(uint32_t i = 0; i < allocatedPointersCount; ++i) 118 | { 119 | if(allocatedPointers[i] == pointer) 120 | { 121 | allocatedPointers[i] = allocatedPointers[--allocatedPointersCount]; 122 | return; 123 | } 124 | } 125 | INVALID_PATH; 126 | } 127 | 128 | void alloc_printDB() 129 | { 130 | printf("\n--- Alloc DB -----------\n"); 131 | for (int32_t i = 0; i < ALLOC_DB_SIZE; ++i) 132 | { 133 | if(alloc_db[i].hash != 0) 134 | { 135 | printAllocEntry(alloc_db + i); 136 | } 137 | } 138 | printf("--- Alloc DB end -------\n"); 139 | 140 | printLeakedPointers(); 141 | } 142 | 143 | static inline bool alloc_entryIsFree(AllocEntry *entry) 144 | { 145 | bool isFree = (entry->count == 0) && (entry->size == 0); 146 | return isFree; 147 | } 148 | 149 | int64_t alloc_active() 150 | { 151 | int64_t totalBytes = 0; 152 | for (int32_t i = 0; i < ALLOC_DB_SIZE; ++i) 153 | { 154 | AllocEntry *entry = alloc_db + i; 155 | if(entry->hash != 0) 156 | { 157 | if (!alloc_entryIsFree(entry)) 158 | { 159 | totalBytes += entry->size; 160 | } 161 | } 162 | } 163 | return totalBytes; 164 | } 165 | 166 | void alloc_printActiveDB() 167 | { 168 | int64_t totalBytes = alloc_active(); 169 | if(totalBytes == 0) 170 | { 171 | return; 172 | } 173 | printf("\n--- Alloc Active -------\n"); 174 | for (int32_t i = 0; i < ALLOC_DB_SIZE; ++i) 175 | { 176 | AllocEntry *entry = alloc_db + i; 177 | if(entry->hash != 0) 178 | { 179 | if (!alloc_entryIsFree(entry)) 180 | { 181 | printAllocEntry(entry); 182 | } 183 | } 184 | } 185 | printf("--- Alloc Active end ---\n"); 186 | printf("%lld bytes leaked.\n", totalBytes); 187 | } 188 | 189 | static void alloc_recordAlloc(uint32_t hash, memory_size count, memory_size capacity, const char *type) 190 | { 191 | uint32_t index = hash & ALLOC_DB_MASK; 192 | AllocEntry *entry = alloc_db + index; 193 | 194 | if(entry->hash == 0) 195 | { 196 | // first entry 197 | entry->hash = hash; 198 | snprintf(entry->type, ALLOC_TYPE_NAME_SIZE, "%s", type); 199 | } 200 | else 201 | { 202 | // NOTE: we don't want clashes in our table 203 | assert(entry->hash == hash); 204 | } 205 | entry->count += (int32_t)count; 206 | entry->size += capacity; 207 | entry->alloc_count++; 208 | } 209 | 210 | static void alloc_recordRealloc(uint32_t hash, memory_size count, memory_size capacity, memory_size oldCount, memory_size oldCapacity) 211 | { 212 | uint32_t index = hash & ALLOC_DB_MASK; 213 | AllocEntry *entry = alloc_db + index; 214 | 215 | assert(entry->hash != 0); 216 | assert(entry->hash == hash); 217 | 218 | entry->count += (int32_t)count - (int32_t)oldCount; 219 | entry->size += (int64_t)capacity - (int64_t)oldCapacity; 220 | entry->realloc_count++; 221 | } 222 | 223 | static void alloc_recordFree(uint32_t hash, memory_size count, memory_size capacity) 224 | { 225 | uint32_t index = hash & ALLOC_DB_MASK; 226 | AllocEntry *entry = alloc_db + index; 227 | 228 | assert(entry->hash == hash); 229 | entry->count -= (int32_t)count; 230 | entry->size -= capacity; 231 | entry->free_count++; 232 | 233 | assert(alloc_db->count >= 0); 234 | assert(alloc_db->size >= 0); 235 | } 236 | 237 | #ifdef MEMORY_VERBOSE 238 | static const char * filenameFromPath(const char *path) 239 | { 240 | const char *name = path; 241 | while(*path != '\0') 242 | { 243 | if(*path == '/') 244 | { 245 | name = path+1; 246 | } 247 | path++; 248 | } 249 | return name; 250 | } 251 | #endif 252 | 253 | // Returns a pointer to `count` contiguous blocks of memory, 254 | // each one of size `capacity`. 255 | // If the allocation was unsuccssful, returns NULL. 256 | void * lox_alloc_(size_t capacity, int32_t count, const char *file, int line, const char *type) 257 | { 258 | assert(count < MEMORY_MAX_SIZE); 259 | assert(capacity < MEMORY_MAX_SIZE / count); 260 | size_t totalCapacity = capacity*count; 261 | assert(totalCapacity < MEMORY_MAX_SIZE - ALLOC_OFFSET - ALLOC_ENDMARKER_SIZE - MARKER_ALIGNMENT ); 262 | 263 | uint32_t hash = (uint32_t)str_hashLiteral(type); 264 | 265 | totalCapacity = (totalCapacity + MARKER_ALIGNMENT) & ~(MARKER_ALIGNMENT-1); 266 | size_t size = totalCapacity + ALLOC_OFFSET + ALLOC_ENDMARKER_SIZE; 267 | AllocHeader *header = (AllocHeader *)malloc(size); 268 | storePointer(header); 269 | 270 | header->capacity = (memory_size)totalCapacity; 271 | header->count = (memory_size)count; 272 | header->hash = hash; 273 | 274 | EndMarker *marker = ALLOC_MARKER_FROM_HEADER(header, totalCapacity); 275 | marker->hash = hash; 276 | 277 | void *pointer = ALLOC_POINTER_FROM_HEADER(header); 278 | 279 | alloc_recordAlloc(hash, (memory_size)count, (memory_size)totalCapacity, type); 280 | 281 | #ifdef MEMORY_VERBOSE 282 | const char *name = filenameFromPath(file); 283 | memset(pointer, 0, totalCapacity); 284 | printPointerInfo(header, "ALLOC ", name, line); 285 | #endif 286 | return pointer; 287 | } 288 | 289 | void lox_free_(void *ptr, const char *file, int line) 290 | { 291 | AllocHeader *header = ALLOC_HEADER_FROM_POINTER(ptr); 292 | #ifdef MEMORY_VERBOSE 293 | const char *name = filenameFromPath(file); 294 | printPointerInfo(header, "FREE ", name, line); 295 | #endif 296 | 297 | memory_size capacity = header->capacity; 298 | EndMarker *marker = ALLOC_MARKER_FROM_POINTER(ptr, capacity); 299 | assert(header->hash == marker->hash); 300 | 301 | alloc_recordFree(header->hash, header->count, capacity); 302 | 303 | // NOTE: Invalidate the end marker 304 | marker->hash = 0xabecedaf; 305 | // memset(ptr, 0, capacity); 306 | 307 | removePointer(header); 308 | free(header); 309 | } 310 | 311 | void * lox_realloc_(void *ptr, int32_t count, const char *file, int line) 312 | { 313 | assert(count < MEMORY_MAX_SIZE); 314 | AllocHeader *header = ALLOC_HEADER_FROM_POINTER(ptr); 315 | uint32_t hash = header->hash; 316 | memory_size oldCapacity = header->capacity; 317 | memory_size oldCount = header->count; 318 | 319 | EndMarker *marker = ALLOC_MARKER_FROM_POINTER(ptr, oldCapacity); 320 | assert(header->hash == marker->hash); 321 | 322 | // NOTE: Invalidate the end marker 323 | marker->hash = 0xabecedaf; 324 | // memset(ptr, 0, oldCapacity); 325 | 326 | memory_size elementSize = oldCapacity / oldCount; 327 | size_t capacity = elementSize*count; 328 | capacity = (capacity + MARKER_ALIGNMENT) & ~(MARKER_ALIGNMENT-1); 329 | 330 | size_t size = capacity + ALLOC_OFFSET + ALLOC_ENDMARKER_SIZE; 331 | assert(size < MEMORY_MAX_SIZE); 332 | 333 | removePointer(header); 334 | header = (AllocHeader *)realloc(header, size); 335 | storePointer(header); 336 | 337 | header->capacity = (memory_size)capacity; 338 | header->count = (memory_size)count; 339 | header->hash = hash; 340 | 341 | marker = ALLOC_MARKER_FROM_HEADER(header, capacity); 342 | marker->hash = hash; 343 | 344 | ptr = ALLOC_POINTER_FROM_HEADER(header); 345 | 346 | alloc_recordRealloc(hash, (memory_size)count, (memory_size)capacity, oldCount, oldCapacity); 347 | 348 | #ifdef MEMORY_VERBOSE 349 | const char *name = filenameFromPath(file); 350 | printf("$ Realloc pointer at %p -- %s l.%d\n", ptr, name, line); 351 | #endif 352 | 353 | return ptr; 354 | } 355 | 356 | #include "objects.h" 357 | #include "string.h" 358 | 359 | static void printPointerInfo(AllocHeader *header, const char *prefix, const char *filename, int line) 360 | { 361 | printf("%s0x%p ", prefix, (void *)header); 362 | uint32_t hash = header->hash; 363 | printf("%-17s count: %d cap: %d", hashToType(hash), header->count, header->capacity); 364 | void *ptr = ALLOC_POINTER_FROM_HEADER(header); 365 | if (!strcmp(hashToType(hash), "char")) 366 | { 367 | printf(" -> '%s'", STR_FROM_HEADER(ptr)); 368 | } 369 | else if (!strcmp(hashToType(hash), "Object *")) 370 | { 371 | printf(" -> type: %s", obj_typeLiteral(((Object *)ptr)->type)); 372 | } 373 | if (filename) 374 | { 375 | printf(" -- %s l.%d\n", filename, line); 376 | } 377 | else 378 | { 379 | printf("\n"); 380 | } 381 | } 382 | 383 | void printLeakedPointers() 384 | { 385 | if (allocatedPointersCount) 386 | { 387 | printf("\n=== Leaked pointers: ===\n"); 388 | for (int32_t index = 0; index < allocatedPointersCount; index++) 389 | { 390 | AllocHeader *header = allocatedPointers[index]; 391 | printPointerInfo(header, "", NULL, 0); 392 | } 393 | printf("=== End leaked pointers ===\n"); 394 | } 395 | } 396 | 397 | #endif 398 | 399 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | // 2 | // memory.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 01/11/2017. 6 | // 7 | 8 | #ifndef memory_h 9 | #define memory_h 10 | 11 | #include "common.h" 12 | 13 | #include 14 | 15 | #ifdef MEMORY_DEBUG 16 | 17 | void lox_alloc_init(void); 18 | void printLeakedPointers(void); 19 | void alloc_printDB(void); 20 | void alloc_printActiveDB(void); 21 | void alloc_pointerInfo(void *ptr); 22 | 23 | void * lox_alloc_(size_t capacity, int32_t count, const char *file, int line, const char *type); 24 | void * lox_realloc_(void *ptr, int32_t count, const char *file, int line); 25 | void lox_free_(void *ptr, const char *file, int line); 26 | 27 | #define ALLOC_STR(x) #x 28 | #define lox_alloc(type) lox_allocn(type, 1) 29 | #define lox_allocn(type, count) (type *)lox_alloc_(sizeof(type), count, __FILE__, __LINE__, ALLOC_STR(type)) 30 | #define lox_realloc(ptr, count) lox_realloc_(ptr, count, __FILE__, __LINE__) 31 | #define lox_free(ptr) lox_free_(ptr, __FILE__, __LINE__) 32 | 33 | #else 34 | 35 | #include 36 | #define lox_alloc(type) lox_allocn(type, 1) 37 | #define lox_allocn(type, count) (type *)malloc(count*sizeof(type)) 38 | // NOTE: -IMPORTANT- This realloc does not keep track of the size 39 | // of the type, and thus works for chars only at the moment 40 | #define lox_realloc(ptr, count) realloc(ptr, count) 41 | #define lox_free(ptr) free(ptr) 42 | 43 | #define lox_alloc_init(void) 44 | #define alloc_printDB(void) 45 | #define alloc_printActiveDB(void) 46 | 47 | #endif 48 | 49 | #endif /* memory_h */ 50 | -------------------------------------------------------------------------------- /src/memory_pool.c: -------------------------------------------------------------------------------- 1 | // 2 | // memory_pool.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 14/12/2017. 6 | // 7 | 8 | #include "memory_pool.h" 9 | #include "common.h" 10 | #include "error.h" 11 | 12 | extern inline void poolReleaseObject(void *object, struct MemoryPool *pool); 13 | 14 | static void poolAlloc(int32_t numPages, struct MemoryPool *pool) 15 | { 16 | ChunkSize totalSize = PAGE_SIZE*numPages; 17 | PoolChunk *chunk = (PoolChunk *)lox_allocn(uint8_t, totalSize); 18 | if(chunk == NULL) 19 | { 20 | fatal_outOfMemory(); 21 | } 22 | chunk->memory = (void *)(chunk + 1); 23 | chunk->memorySize = totalSize - sizeof(PoolChunk); 24 | chunk->used = 0; 25 | chunk->nextChunk = pool->firstChunk; 26 | pool->firstChunk = chunk; 27 | } 28 | 29 | struct MemoryPool * poolInit(ChunkSize objectSize) 30 | { 31 | struct MemoryPool *pool = lox_alloc(struct MemoryPool); 32 | if(pool == NULL) 33 | { 34 | fatal_outOfMemory(); 35 | } 36 | pool->firstChunk = NULL; 37 | pool->firstUnused = NULL; 38 | pool->chunksCount = 0; 39 | pool->objectSize = objectSize; 40 | poolAlloc(1, pool); 41 | return pool; 42 | } 43 | 44 | void poolFree(struct MemoryPool *pool) 45 | { 46 | while(pool->firstChunk) 47 | { 48 | PoolChunk *chunk = pool->firstChunk; 49 | pool->firstChunk = chunk->nextChunk; 50 | lox_free(chunk); 51 | } 52 | lox_free(pool); 53 | } 54 | 55 | void * poolGetObject(struct MemoryPool *pool) 56 | { 57 | PoolChunk *chunk = pool->firstChunk; 58 | 59 | if (pool->firstUnused) 60 | { 61 | PoolUnusedObject *object = pool->firstUnused; 62 | pool->firstUnused = object->next; 63 | return (void *)object; 64 | } 65 | 66 | if (chunk->used + pool->objectSize > chunk->memorySize) 67 | { 68 | poolAlloc(1, pool); 69 | chunk = pool->firstChunk; 70 | } 71 | void *object = (uint8_t *)chunk->memory + chunk->used; 72 | chunk->used += pool->objectSize; 73 | 74 | return object; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/memory_pool.h: -------------------------------------------------------------------------------- 1 | // 2 | // memory_pool.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 14/12/2017. 6 | // 7 | 8 | #ifndef memory_pool_h 9 | #define memory_pool_h 10 | 11 | #include 12 | 13 | typedef uint32_t ChunkSize; 14 | 15 | typedef struct PoolChunk_tag 16 | { 17 | struct PoolChunk_tag *nextChunk; 18 | void *memory; 19 | ChunkSize used; 20 | ChunkSize memorySize; 21 | } PoolChunk; 22 | 23 | typedef struct PoolUnusedObject_tag 24 | { 25 | struct PoolUnusedObject_tag *next; 26 | } PoolUnusedObject; 27 | 28 | struct MemoryPool 29 | { 30 | PoolChunk *firstChunk; 31 | PoolUnusedObject *firstUnused; 32 | int32_t chunksCount; 33 | ChunkSize objectSize; 34 | }; 35 | 36 | struct MemoryPool* poolInit(ChunkSize objectSize); 37 | void poolFree(struct MemoryPool *pool); 38 | void * poolGetObject(struct MemoryPool *pool); 39 | 40 | inline void poolReleaseObject(void* object, struct MemoryPool *pool) 41 | { 42 | ((PoolUnusedObject *)object)->next = pool->firstUnused; 43 | pool->firstUnused = object; 44 | } 45 | 46 | #endif /* memory_pool_h */ 47 | -------------------------------------------------------------------------------- /src/objects.c: -------------------------------------------------------------------------------- 1 | // 2 | // objects.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 17/10/2017. 6 | // 7 | 8 | #include "garbage_collector.h" 9 | #include "lox_callable.h" 10 | #include "lox_class.h" 11 | #include "lox_function.h" 12 | #include "lox_instance.h" 13 | #include "memory.h" 14 | #include "objects.h" 15 | #include "string.h" 16 | 17 | #include 18 | 19 | extern inline Object * objNew(ObjectType type, GarbageCollector *collector); 20 | extern inline Object * obj_newBoolean(bool value, GarbageCollector *collector); 21 | extern inline Object * obj_wrapCallable(LoxCallable *callable, GarbageCollector *collector); 22 | extern inline Object * obj_newCallable(LoxCallable *callable, GarbageCollector *collector); 23 | extern inline Object * obj_wrapClass(LoxClass *klass, GarbageCollector *collector); 24 | extern inline Object * obj_wrapFunction(LoxFunction *function, GarbageCollector *collector); 25 | extern inline Object * obj_wrapInstance(LoxInstance *instance, GarbageCollector *collector); 26 | extern inline Object * obj_newNil(GarbageCollector *collector); 27 | extern inline Object * obj_newNumber(double value, GarbageCollector *collector); 28 | extern inline Object * obj_newString(const char *str, GarbageCollector *collector); 29 | extern inline Object * obj_wrapString(char *str, GarbageCollector *collector); 30 | extern inline Object * obj_wrapArguments(LoxArguments *arguments, GarbageCollector *collector); 31 | 32 | extern inline bool obj_unwrapBoolean(const Object *obj); 33 | extern inline const LoxCallable * obj_unwrapCallable(const Object *obj); 34 | extern inline LoxClass * obj_unwrapClass(const Object *obj); 35 | extern inline LoxFunction * obj_unwrapFunction(const Object *obj); 36 | extern inline LoxInstance * obj_unwrapInstance(const Object *obj); 37 | extern inline void * obj_unwrapNil(const Object *obj); 38 | extern inline double obj_unwrapNumber(const Object *obj); 39 | extern inline const char * obj_unwrapString(const Object *obj); 40 | extern inline LoxArguments * obj_unwrapArguments(const Object *obj); 41 | 42 | #if DEBUG 43 | // NOTE: Here we store the last debug ID that was assigned 44 | int32_t obj_debugID = 0; 45 | #endif 46 | 47 | // Array of the object type names 48 | static const char * const object_type_string[] = 49 | { 50 | #define DEFINE_TYPE_STRING(type) XSTR(type), 51 | FOREACH_OBJECT(DEFINE_TYPE_STRING) 52 | #undef DEFINE_TYPE_STRING 53 | }; 54 | 55 | // Returns a string literal with the name of the object type 56 | const char * obj_typeLiteral(ObjectType type) 57 | { 58 | return object_type_string[type]; 59 | } 60 | 61 | Object * obj_dup(const Object *obj, GarbageCollector *collector) 62 | { 63 | switch (obj->type) { 64 | case OT_BOOLEAN: 65 | return obj_newBoolean(obj->boolean, collector); 66 | case OT_CALLABLE: 67 | return obj_newCallable(obj->callable, collector); 68 | case OT_CLASS: 69 | return obj_wrapClass(obj->klass, collector); 70 | case OT_INSTANCE: 71 | return obj_wrapInstance(obj->instance, collector); 72 | case OT_FUNCTION: 73 | return obj_wrapFunction(obj->function, collector); 74 | case OT_NIL: 75 | return obj_newNil(collector); 76 | case OT_NUMBER: 77 | return obj_newNumber(obj->number, collector); 78 | case OT_STRING: 79 | return obj_newString(obj->string, collector); 80 | case OT_ARGUMENTS: 81 | // NOTE: we never duplicate an Arguments object. 82 | INVALID_CASE; 83 | case OT_UNUSED: 84 | INVALID_CASE; 85 | } 86 | } 87 | 88 | bool isTruthy(Object *object) 89 | { 90 | if (object->type == OT_NIL) 91 | { 92 | return false; 93 | } 94 | if (object->type == OT_BOOLEAN) 95 | { 96 | return obj_unwrapBoolean(object); 97 | } 98 | return true; 99 | } 100 | 101 | bool isEqual(Object *a, Object *b) 102 | { 103 | if (a->type == OT_NIL && b->type == OT_NIL) 104 | { 105 | return true; 106 | } 107 | if (a->type == OT_BOOLEAN && b->type == OT_BOOLEAN) 108 | { 109 | return obj_unwrapBoolean(a) == obj_unwrapBoolean(b); 110 | } 111 | if (a->type == OT_CALLABLE && b->type == OT_CALLABLE) 112 | { 113 | const LoxCallable *callableA = obj_unwrapCallable(a); 114 | const LoxCallable *callableB = obj_unwrapCallable(b); 115 | return callableA->function == callableB->function; 116 | } 117 | if (a->type == OT_FUNCTION && b->type == OT_FUNCTION) 118 | { 119 | const LoxFunction *funcA = obj_unwrapFunction(a); 120 | const LoxFunction *funcB = obj_unwrapFunction(b); 121 | return ((funcA->declaration == funcB->declaration) && (funcA->closure == funcB->closure)); 122 | } 123 | if (a->type == OT_NUMBER && b->type == OT_NUMBER) 124 | { 125 | return obj_unwrapNumber(a) == obj_unwrapNumber(b); 126 | } 127 | if (a->type == OT_STRING && b->type == OT_STRING) 128 | { 129 | return str_isEqual(obj_unwrapString(a), obj_unwrapString(b)); 130 | } 131 | return false; 132 | } 133 | 134 | // Returns a string that represents the object `obj`. 135 | char * obj_stringify(const Object *object) 136 | { 137 | char *string; 138 | if (object == NULL) 139 | { 140 | return str_fromLiteral("nil"); 141 | } 142 | 143 | switch(object->type) 144 | { 145 | case OT_BOOLEAN: 146 | { 147 | bool value = obj_unwrapBoolean(object); 148 | string = str_fromLiteral(value ? "true" : "false"); 149 | } break; 150 | 151 | case OT_CALLABLE: 152 | { 153 | // TODO: include the name of the native function. 154 | string = str_fromLiteral(""); 156 | } break; 157 | 158 | case OT_CLASS: 159 | { 160 | const LoxClass *klass = obj_unwrapClass(object); 161 | string = str_dup(klass->name); 162 | } break; 163 | 164 | case OT_INSTANCE: 165 | { 166 | const LoxInstance *instance = obj_unwrapInstance(object); 167 | string = instanceToString(instance); 168 | } break; 169 | 170 | case OT_FUNCTION: 171 | { 172 | const LoxFunction *function = obj_unwrapFunction(object); 173 | string = str_fromLiteral("declaration->name)); 175 | str_appendLiteral(string, ">"); 176 | } break; 177 | 178 | case OT_NIL: 179 | { 180 | string = str_fromLiteral("nil"); 181 | } break; 182 | 183 | case OT_NUMBER: 184 | { 185 | double value = obj_unwrapNumber(object); 186 | if (value == 0) 187 | { 188 | if (signbit(value)) 189 | { 190 | string = str_fromLiteral("-0"); 191 | } 192 | else 193 | { 194 | string = str_fromLiteral("0"); 195 | } 196 | } 197 | else if(value == (double)(int)value) 198 | { 199 | // Note: The number is an integer! 200 | string = str_fromInt64((int64_t)value); 201 | } 202 | else 203 | { 204 | string = str_fromDouble(value); 205 | } 206 | } break; 207 | 208 | case OT_STRING: 209 | { 210 | string = str_dup(obj_unwrapString(object)); 211 | } break; 212 | 213 | case OT_ARGUMENTS: 214 | { 215 | string = str_fromLiteral("[args]"); 216 | } break; 217 | 218 | case OT_UNUSED: 219 | INVALID_CASE; 220 | } 221 | return string; 222 | } 223 | 224 | // Returns a description of the object. 225 | char * obj_description(const Object *object) 226 | { 227 | char *string; 228 | if (object == NULL) 229 | { 230 | return str_fromLiteral("nil"); 231 | } 232 | 233 | switch(object->type) 234 | { 235 | case OT_BOOLEAN: 236 | { 237 | bool value = obj_unwrapBoolean(object); 238 | string = str_fromLiteral(value ? "true" : "false"); 239 | } break; 240 | 241 | case OT_CALLABLE: 242 | { 243 | const LoxCallable *callable = obj_unwrapCallable(object); 244 | string = str_fromLiteral("native function ("); 245 | str_append(string, str_fromInt64(callable->arity)); 246 | str_appendLiteral(string, " parameters)"); 247 | } break; 248 | 249 | case OT_CLASS: 250 | { 251 | const LoxClass *klass = obj_unwrapClass(object); 252 | string = classToString(klass); 253 | } break; 254 | 255 | case OT_INSTANCE: 256 | { 257 | const LoxInstance *instance = obj_unwrapInstance(object); 258 | string = instanceToString(instance); 259 | } break; 260 | 261 | case OT_FUNCTION: 262 | { 263 | const LoxFunction *function = obj_unwrapFunction(object); 264 | string = str_fromLiteral("function ("); 265 | str_append(string, str_fromInt64(function->declaration->arity)); 266 | str_appendLiteral(string, " parameters)"); 267 | if (function->isInitializer) 268 | { 269 | str_appendLiteral(string, " - class initializer"); 270 | } 271 | } break; 272 | 273 | case OT_NIL: 274 | { 275 | string = str_fromLiteral("nil"); 276 | } break; 277 | 278 | case OT_NUMBER: 279 | { 280 | double value = obj_unwrapNumber(object); 281 | if (value == 0) 282 | { 283 | if (signbit(value)) 284 | { 285 | string = str_fromLiteral("-0"); 286 | } 287 | else 288 | { 289 | string = str_fromLiteral("0"); 290 | } 291 | } 292 | else if(value == (double)(int)value) 293 | { 294 | // Note: The number is an integer! 295 | string = str_fromInt64((int64_t)value); 296 | } 297 | else 298 | { 299 | string = str_fromDouble(value); 300 | } 301 | } break; 302 | 303 | case OT_STRING: 304 | { 305 | string = str_fromLiteral("\""); 306 | str_append(string, obj_unwrapString(object)); 307 | str_appendLiteral(string, "\""); 308 | } break; 309 | 310 | case OT_ARGUMENTS: 311 | { 312 | string = str_fromLiteral("[args]"); 313 | } break; 314 | 315 | case OT_UNUSED: 316 | INVALID_CASE; 317 | } 318 | return string; 319 | } 320 | 321 | void obj_print(Object *obj) 322 | { 323 | char *str = obj_stringify(obj); 324 | printf("%s\n", str); 325 | str_free(str); 326 | } 327 | -------------------------------------------------------------------------------- /src/objects.h: -------------------------------------------------------------------------------- 1 | // 2 | // objects.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 17/10/2017. 6 | // 7 | 8 | #ifndef objects_h 9 | #define objects_h 10 | 11 | #include "common.h" 12 | 13 | #include 14 | 15 | // NOTE: definition of the object types 16 | 17 | #define FOREACH_OBJECT(obj) \ 18 | obj(NIL) obj(BOOLEAN) obj(CALLABLE) obj(CLASS) \ 19 | obj(FUNCTION) obj(INSTANCE) obj(NUMBER) obj(STRING) \ 20 | obj(ARGUMENTS) obj(UNUSED) 21 | 22 | typedef enum ObjectType 23 | { 24 | #define DEFINE_ENUM_TYPE(type) OT_##type, 25 | FOREACH_OBJECT(DEFINE_ENUM_TYPE) 26 | #undef DEFINE_ENUM_TYPE 27 | } ObjectType; 28 | 29 | #define OBJECT_VALUE_OFFSET sizeof(ObjectType) 30 | 31 | struct LoxArguments_tag; 32 | typedef struct LoxArguments_tag LoxArguments; 33 | 34 | struct LoxCallable_tag; 35 | typedef struct LoxCallable_tag LoxCallable; 36 | 37 | struct LoxClass_tag; 38 | typedef struct LoxClass_tag LoxClass; 39 | 40 | struct LoxFunction_tag; 41 | typedef struct LoxFunction_tag LoxFunction; 42 | 43 | struct LoxInstance_tag; 44 | typedef struct LoxInstance_tag LoxInstance; 45 | 46 | struct GarbageCollector_tag; 47 | typedef struct GarbageCollector_tag GarbageCollector; 48 | 49 | typedef struct Object_tag 50 | { 51 | union { 52 | double number; 53 | bool boolean; 54 | char *string; 55 | LoxArguments *arguments; 56 | LoxCallable *callable; 57 | LoxClass *klass; 58 | LoxFunction *function; 59 | LoxInstance *instance; 60 | void *value; 61 | }; 62 | struct Object_tag *next; 63 | ObjectType type; 64 | int32_t marked; 65 | #ifdef DEBUG 66 | int32_t debugID; 67 | #endif 68 | } Object; 69 | 70 | #include "lox_callable.h" 71 | 72 | const char * obj_typeLiteral(ObjectType type); 73 | Object * obj_dup(const Object *obj, GarbageCollector *collector); 74 | bool isTruthy(Object *object); 75 | bool isEqual(Object *a, Object *b); 76 | char * obj_stringify(const Object *object); 77 | char * obj_description(const Object *object); 78 | void obj_print(Object *obj); 79 | 80 | #include "string.h" 81 | #include "lox_callable.h" 82 | 83 | #if DEBUG 84 | extern int32_t obj_debugID; 85 | #endif 86 | 87 | struct GarbageCollector_tag; 88 | typedef struct GarbageCollector_tag GarbageCollector; 89 | Object * gcGetObject(GarbageCollector *collector); 90 | 91 | inline Object * objNew(ObjectType type, GarbageCollector *collector) 92 | { 93 | Object *object = gcGetObject(collector); 94 | object->type = type; 95 | #if DEBUG 96 | object->debugID = obj_debugID++; 97 | #endif 98 | return object; 99 | } 100 | 101 | inline Object * obj_newBoolean(bool value, GarbageCollector *collector) 102 | { 103 | Object *object = objNew(OT_BOOLEAN, collector); 104 | object->boolean = value; 105 | return object; 106 | } 107 | 108 | inline Object * obj_wrapCallable(LoxCallable *callable, GarbageCollector *collector) 109 | { 110 | Object *object = objNew(OT_CALLABLE, collector); 111 | object->callable = callable; 112 | return object; 113 | } 114 | 115 | inline Object * obj_newCallable(LoxCallable *callable, GarbageCollector *collector) 116 | { 117 | Object *object = objNew(OT_CALLABLE, collector); 118 | object->callable = callableInit(callable->function, callable->arity); 119 | return object; 120 | } 121 | 122 | inline Object * obj_wrapClass(LoxClass *klass, GarbageCollector *collector) 123 | { 124 | Object *object = objNew(OT_CLASS, collector); 125 | object->klass = klass; 126 | return object; 127 | } 128 | 129 | inline Object * obj_wrapFunction(LoxFunction *function, GarbageCollector *collector) 130 | { 131 | Object *object = objNew(OT_FUNCTION, collector); 132 | object->function = function; 133 | return object; 134 | } 135 | 136 | inline Object * obj_wrapInstance(LoxInstance *instance, GarbageCollector *collector) 137 | { 138 | Object *object = objNew(OT_INSTANCE, collector); 139 | object->instance = instance; 140 | return object; 141 | } 142 | 143 | inline Object * obj_newNil(GarbageCollector *collector) 144 | { 145 | Object *object = objNew(OT_NIL, collector); 146 | return object; 147 | } 148 | 149 | inline Object * obj_newNumber(double value, GarbageCollector *collector) 150 | { 151 | Object *object = objNew(OT_NUMBER, collector); 152 | object->number = value; 153 | return object; 154 | } 155 | 156 | inline Object * obj_newString(const char *str, GarbageCollector *collector) 157 | { 158 | Object *object = objNew(OT_STRING, collector); 159 | object->string = str_dup(str); 160 | return object; 161 | } 162 | 163 | inline Object * obj_wrapString(char *str, GarbageCollector *collector) 164 | { 165 | Object *object = objNew(OT_STRING, collector); 166 | object->string = str; 167 | return object; 168 | } 169 | 170 | inline Object * obj_wrapArguments(LoxArguments *arguments, GarbageCollector *collector) 171 | { 172 | Object *object = objNew(OT_ARGUMENTS, collector); 173 | object->arguments = arguments; 174 | return object; 175 | } 176 | 177 | inline bool obj_unwrapBoolean(const Object *obj) 178 | { 179 | assert(obj->type == OT_BOOLEAN); 180 | return obj->boolean; 181 | } 182 | 183 | inline const LoxCallable * obj_unwrapCallable(const Object *obj) 184 | { 185 | assert(obj->type == OT_CALLABLE); 186 | return obj->callable; 187 | } 188 | 189 | inline LoxFunction * obj_unwrapFunction(const Object *obj) 190 | { 191 | assert(obj->type == OT_FUNCTION); 192 | return obj->function; 193 | } 194 | 195 | inline LoxClass * obj_unwrapClass(const Object *obj) 196 | { 197 | if (obj == NULL) 198 | { 199 | return NULL; 200 | } 201 | assert(obj->type == OT_CLASS); 202 | return obj->klass; 203 | } 204 | 205 | inline LoxInstance * obj_unwrapInstance(const Object *obj) 206 | { 207 | assert(obj->type == OT_INSTANCE); 208 | return obj->instance; 209 | } 210 | 211 | inline void * obj_unwrapNil(const Object *obj) 212 | { 213 | assert(obj->type == OT_NIL); 214 | return NULL; 215 | } 216 | 217 | inline double obj_unwrapNumber(const Object *obj) 218 | { 219 | assert(obj->type == OT_NUMBER); 220 | return obj->number; 221 | } 222 | 223 | inline const char * obj_unwrapString(const Object *obj) 224 | { 225 | assert(obj->type == OT_STRING); 226 | return obj->string; 227 | } 228 | 229 | inline LoxArguments * obj_unwrapArguments(const Object *obj) 230 | { 231 | assert(obj->type == OT_ARGUMENTS); 232 | return obj->arguments; 233 | } 234 | 235 | #endif /* objects_h */ 236 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // parser.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | /* 9 | Note: This is a recursive descent parser that currently 10 | implements the following grammar: 11 | 12 | program → declaration* eof ; 13 | declaration → classDecl 14 | | funDecl 15 | | varDecl 16 | | statement ; 17 | classDecl → "class" IDENTIFIER "{" function* "}" ; 18 | funDecl → "fun" function ; 19 | function → IDENTIFIER "(" parameters? ")" block ; 20 | parameters → IDENTIFIER ( "," IDENTIFIER )* ; 21 | varDecl → "var" IDENTIFIER ( "=" expression )? ";" ; 22 | statement → exprStmt 23 | | forStmt 24 | | ifStmt 25 | | printStmt 26 | | returnStmt 27 | | whileStmt 28 | | block ; 29 | exprStmt → expression ";" ; 30 | forStmt → "for" "(" ( varDecl | exprStmt | ";" ) expression? ";" expression? ")" statement ; 31 | ifStmt → "if" "(" expression ")" statement ( "else" statement )? ; 32 | printStmt → "print" expression ";" ; 33 | returnStmt → "return" expression? ";" ; 34 | whileStmt → "while" "(" expression ")" statement ; 35 | block → "{" declaration* "}" ; 36 | 37 | expression → assignment ; 38 | assignment → ( call "." )? IDENTIFIER "=" assignment 39 | | logic_or; 40 | logic_or → logic_and ( "or" logic_and )* ; 41 | logic_and → equality ( "and" equality )* ; 42 | equality → comparison ( ( "!=" | "==" ) comparison )* ; 43 | comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ; 44 | addition → multiplication ( ( "-" | "+" ) multiplication )* ; 45 | multiplication → unary ( ( "/" | "*" ) unary )* ; 46 | unary → ( "!" | "-" ) unary | call ; 47 | call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ; 48 | arguments → expression ( "," expression )* ; 49 | primary → "true" | "false" | "null" | "this" 50 | | NUMBER | STRING | IDENTIFIER | "(" expression ")" 51 | | "super" "." IDENTIFIER ; 52 | */ 53 | 54 | #ifndef parser_h 55 | #define parser_h 56 | 57 | #include "token.h" 58 | #include "stmt.h" 59 | 60 | Stmt * parse(Token *tokens, const char *source); 61 | 62 | #endif /* parser_h */ 63 | -------------------------------------------------------------------------------- /src/resolver.c: -------------------------------------------------------------------------------- 1 | // 2 | // resolver.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 03/11/2017. 6 | // 7 | 8 | #include "resolver.h" 9 | 10 | #include "common.h" 11 | #include "error.h" 12 | #include "expr.h" 13 | #include "memory.h" 14 | #include "string.h" 15 | 16 | typedef struct 17 | { 18 | const char *name; 19 | int32_t index; // NOTE: unique id 20 | bool isDefined; 21 | } ResolverEntry; 22 | 23 | typedef struct ResolverHashTable 24 | { 25 | ResolverEntry entries[RESOLVER_HASH_TABLE_SIZE]; 26 | int32_t entriesCount; 27 | struct ResolverHashTable *nextInStack; 28 | } ResolverHashTable; 29 | 30 | typedef struct 31 | { 32 | ResolverHashTable *top; 33 | } ResolverStack; 34 | 35 | typedef enum 36 | { 37 | FT_NONE, 38 | FT_FUNCTION, 39 | FT_INITIALIZER, 40 | FT_METHOD, 41 | } FunctionType; 42 | 43 | typedef enum 44 | { 45 | CT_NONE, 46 | CT_CLASS, 47 | CT_SUBCLASS, 48 | } ClassType; 49 | 50 | typedef struct 51 | { 52 | ExprVisitor exprVisitor; 53 | StmtVisitor stmtVisitor; 54 | Interpreter *interpreter; 55 | ResolverStack scopes; 56 | FunctionType currentFunction; 57 | ClassType currentClass; 58 | Error *error; 59 | 60 | char *thisString; 61 | char *superString; 62 | } Resolver; 63 | 64 | 65 | static inline Error * resolver_throwError(Token *token, const char *message, Resolver *resolver) 66 | { 67 | lox_token_error(resolver->interpreter->source, token, message); 68 | 69 | // NOTE: we only keep track of the last error that occurred for now. 70 | if(resolver->error) 71 | { 72 | freeError(resolver->error); 73 | } 74 | resolver->error = initError(token, message); 75 | 76 | return resolver->error; 77 | } 78 | 79 | // Stack 80 | 81 | static inline void 82 | push(ResolverHashTable *table, ResolverStack *stack) 83 | { 84 | table->nextInStack = stack->top; 85 | stack->top = table; 86 | } 87 | 88 | static inline ResolverHashTable * 89 | pop(ResolverStack *stack) 90 | { 91 | ResolverHashTable *table = stack->top; 92 | stack->top = table->nextInStack; 93 | return table; 94 | } 95 | 96 | static inline ResolverHashTable * 97 | peek(ResolverStack *stack) 98 | { 99 | ResolverHashTable *table = stack->top; 100 | return table; 101 | } 102 | 103 | static inline bool 104 | stackIsEmpty(ResolverStack *stack) 105 | { 106 | bool result = (stack->top == NULL); 107 | return result; 108 | } 109 | 110 | // HashMap 111 | 112 | static inline ResolverHashTable* 113 | table_init() 114 | { 115 | ResolverHashTable *table = lox_alloc(ResolverHashTable); 116 | if(table == NULL) 117 | { 118 | fatal_outOfMemory(); 119 | } 120 | 121 | for(int32_t i = 0; i < RESOLVER_HASH_TABLE_SIZE; i++) 122 | { 123 | table->entries[i].name = NULL; 124 | } 125 | table->entriesCount = 0; 126 | 127 | return table; 128 | } 129 | 130 | static inline void 131 | table_free(ResolverHashTable *table) 132 | { 133 | lox_free(table); 134 | } 135 | 136 | static bool 137 | table_insert(const char *name, bool isDefined, ResolverHashTable *table) 138 | { 139 | static_assert(LOX_MAX_LOCAL_VARIABLES <= RESOLVER_HASH_TABLE_SIZE, "Resolver hash table size not large enough to store all local variables."); 140 | if (table->entriesCount == LOX_MAX_LOCAL_VARIABLES) 141 | { 142 | // NOTE: Too many local variables 143 | return false; 144 | } 145 | 146 | int32_t tableIndex = (str_hash(name) % RESOLVER_HASH_TABLE_SIZE); 147 | while (table->entries[tableIndex].name != NULL) 148 | { 149 | tableIndex++; 150 | if (tableIndex == RESOLVER_HASH_TABLE_SIZE) 151 | { 152 | tableIndex = 0; 153 | } 154 | } 155 | 156 | ResolverEntry *entry = table->entries + tableIndex; 157 | 158 | entry->name = name; 159 | entry->index = table->entriesCount; 160 | entry->isDefined = isDefined; 161 | 162 | #ifdef RESOLVER_VERBOSE 163 | printf("R table_ins(%d) '%s' isDefined: %d\n", table->entriesCount, name, isDefined); 164 | #endif 165 | 166 | table->entriesCount++; 167 | 168 | return true; 169 | } 170 | 171 | static ResolverEntry * 172 | table_get(const char *name, ResolverHashTable *table) 173 | { 174 | int32_t tableIndex = (str_hash(name) % RESOLVER_HASH_TABLE_SIZE); 175 | 176 | int32_t startIndex = tableIndex; 177 | while(table->entries[tableIndex].name != NULL) 178 | { 179 | if (str_isEqual(name, table->entries[tableIndex].name)) 180 | { 181 | ResolverEntry *entry = table->entries + tableIndex; 182 | return entry; 183 | } 184 | tableIndex++; 185 | if (tableIndex == RESOLVER_HASH_TABLE_SIZE) 186 | { 187 | tableIndex = 0; 188 | } 189 | if (tableIndex == startIndex) 190 | { 191 | break; 192 | } 193 | } 194 | return NULL; 195 | } 196 | 197 | static bool 198 | table_containsKey(const char *name, ResolverHashTable *table) 199 | { 200 | int32_t tableIndex = (str_hash(name) % RESOLVER_HASH_TABLE_SIZE); 201 | int32_t startIndex = tableIndex; 202 | while(table->entries[tableIndex].name != NULL) 203 | { 204 | if (str_isEqual(name, table->entries[tableIndex].name)) 205 | { 206 | return true; 207 | } 208 | tableIndex++; 209 | if (tableIndex == RESOLVER_HASH_TABLE_SIZE) 210 | { 211 | tableIndex = 0; 212 | } 213 | if (tableIndex == startIndex) 214 | { 215 | break; 216 | } 217 | } 218 | return false; 219 | } 220 | 221 | static void 222 | resolveStmt(Stmt *stmt, Resolver *resolver) 223 | { 224 | stmt_accept_visitor(stmt, &resolver->stmtVisitor, resolver); 225 | } 226 | 227 | static void 228 | resolveExpr(Expr *expr, Resolver *resolver) 229 | { 230 | expr_accept_visitor(expr, &resolver->exprVisitor, resolver); 231 | } 232 | 233 | static void 234 | resolveStmtList(Stmt *statements, Resolver *resolver) 235 | { 236 | Stmt *statement = statements; 237 | while(statement) 238 | { 239 | resolveStmt(statement, resolver); 240 | statement = statement->next; 241 | } 242 | } 243 | 244 | static void 245 | beginScope(Resolver *resolver) 246 | { 247 | ResolverHashTable *table = table_init(); 248 | push(table, &resolver->scopes); 249 | } 250 | 251 | static void 252 | endScope(Resolver *resolver) 253 | { 254 | ResolverHashTable *table = pop(&resolver->scopes); 255 | table_free(table); 256 | } 257 | 258 | static Error * 259 | declare(Token *name, Resolver *resolver) 260 | { 261 | Error *error = NULL; 262 | if (!stackIsEmpty(&resolver->scopes)) 263 | { 264 | ResolverHashTable *scope = peek(&resolver->scopes); 265 | const char *nameStr = get_identifier_name(name); 266 | if (table_containsKey(nameStr, scope)) 267 | { 268 | error = resolver_throwError(name, "Variable with this name already declared in this scope.", resolver); 269 | } 270 | bool success = table_insert(nameStr, false, scope); 271 | if (!success) 272 | { 273 | error = resolver_throwError(name, "Too many local variables in function.", resolver); 274 | } 275 | } 276 | else 277 | { 278 | // global variable 279 | } 280 | return error; 281 | } 282 | 283 | static void 284 | define(Token *name, Resolver *resolver) 285 | { 286 | if (!stackIsEmpty(&resolver->scopes)) 287 | { 288 | ResolverHashTable *scope = peek(&resolver->scopes); 289 | const char *nameStr = get_identifier_name(name); 290 | ResolverEntry *entry = table_get(nameStr, scope); 291 | assert(entry != NULL); 292 | entry->isDefined = true; 293 | } 294 | else 295 | { 296 | // global variable 297 | } 298 | } 299 | 300 | static void 301 | resolveLocal(Expr *expr, const char *name, Resolver *resolver) 302 | { 303 | ResolverHashTable *scope = peek(&resolver->scopes); 304 | // Note: Number of scopes between the current innermost 305 | // scope and the scope where the variable was found. 306 | int32_t depth = 0; 307 | while(scope) 308 | { 309 | ResolverEntry *entry = table_get(name, scope); 310 | if (entry != NULL) 311 | { 312 | #ifdef RESOLVER_VERBOSE 313 | printf("R Name '%s' resolved at depth %d (expr %p type %d)\n", name, depth, (void *)expr, expr->type); 314 | #endif 315 | interpreter_resolve(expr, depth, entry->index, resolver->interpreter); 316 | return; 317 | } 318 | ++depth; 319 | scope = scope->nextInStack; 320 | } 321 | 322 | // NOTE: Not found. Assume it is global. 323 | } 324 | 325 | static void 326 | resolveFunction(FunctionStmt *function, FunctionType type, Resolver *resolver) 327 | { 328 | FunctionType enclosingFunction = resolver->currentFunction; 329 | resolver->currentFunction = type; 330 | 331 | beginScope(resolver); 332 | for(int32_t i = 0; i < function->arity; ++i) 333 | { 334 | Token *param = function->parameters[i]; 335 | Error *error = declare(param, resolver); 336 | if(error) 337 | { 338 | break; 339 | } 340 | define(param, resolver); 341 | } 342 | resolveStmtList(function->body, resolver); 343 | endScope(resolver); 344 | 345 | resolver->currentFunction = enclosingFunction; 346 | } 347 | 348 | static void * 349 | resolver_visitBlockStmt(BlockStmt *stmt, void *context) 350 | { 351 | Resolver *resolver = (Resolver *)context; 352 | beginScope(resolver); 353 | resolveStmtList(stmt->statements, resolver); 354 | endScope(resolver); 355 | return NULL; 356 | } 357 | 358 | static void * 359 | resolver_visitClassStmt(ClassStmt *stmt, void *context) 360 | { 361 | Resolver *resolver = (Resolver *)context; 362 | declare(stmt->name, resolver); 363 | define(stmt->name, resolver); 364 | 365 | ClassType enclosingClass = resolver->currentClass; 366 | resolver->currentClass = CT_CLASS; 367 | 368 | if (stmt->superClass != NULL) 369 | { 370 | resolver->currentClass = CT_SUBCLASS; 371 | resolveExpr(stmt->superClass, resolver); 372 | beginScope(resolver); 373 | ResolverHashTable *scope = peek(&resolver->scopes); 374 | table_insert(resolver->superString, true, scope); 375 | } 376 | 377 | beginScope(resolver); 378 | ResolverHashTable *scope = peek(&resolver->scopes); 379 | table_insert(resolver->thisString, true, scope); 380 | 381 | FunctionStmt *method = stmt->methods; 382 | while (method != NULL) 383 | { 384 | assert(method->stmt.type == STMT_Function); 385 | FunctionType declaration = FT_METHOD; 386 | if (str_isEqual(get_identifier_name(method->name), "init")) 387 | { 388 | declaration = FT_INITIALIZER; 389 | } 390 | resolveFunction(method, declaration, resolver); 391 | method = (FunctionStmt *)method->stmt.next; 392 | } 393 | endScope(resolver); 394 | 395 | if (stmt->superClass != NULL) 396 | { 397 | endScope(resolver); 398 | } 399 | 400 | resolveLocal((void *)stmt, get_identifier_name(stmt->name), resolver); 401 | resolver->currentClass = enclosingClass; 402 | return NULL; 403 | } 404 | 405 | static void * 406 | resolver_visitVarStmt(VarStmt *stmt, void *context) 407 | { 408 | Resolver *resolver = (Resolver *)context; 409 | Error *error = declare(stmt->name, resolver); 410 | if (error) 411 | { 412 | return error; 413 | } 414 | if (stmt->initializer != NULL) 415 | { 416 | resolveExpr(stmt->initializer, resolver); 417 | } 418 | define(stmt->name, resolver); 419 | 420 | return NULL; 421 | } 422 | 423 | static void * 424 | resolver_visitVariableExpr(Variable *expr, void *context) 425 | { 426 | Resolver *resolver = (Resolver *)context; 427 | 428 | const char *name = get_identifier_name(expr->name); 429 | if (!stackIsEmpty(&resolver->scopes)) 430 | { 431 | ResolverHashTable *scope = peek(&resolver->scopes); 432 | ResolverEntry *entry = table_get(name, scope); 433 | if ((entry != NULL) && (entry->isDefined == false)) 434 | { 435 | Error *error = resolver_throwError(expr->name, "Cannot read local variable in its own initializer.", resolver); 436 | return error; 437 | } 438 | } 439 | 440 | resolveLocal((Expr *)expr, name, resolver); 441 | 442 | return NULL; 443 | } 444 | 445 | static void * 446 | resolver_visitAssignExpr(Assign *expr, void *context) 447 | { 448 | Resolver *resolver = (Resolver *)context; 449 | 450 | resolveExpr(expr->value, resolver); 451 | const char *name = get_identifier_name(expr->name); 452 | resolveLocal((Expr *)expr, name, resolver); 453 | 454 | return NULL; 455 | } 456 | 457 | static void * 458 | resolver_visitFunctionStmt(FunctionStmt *stmt, void *context) 459 | { 460 | Resolver *resolver = (Resolver *)context; 461 | declare(stmt->name, resolver); 462 | define(stmt->name, resolver); 463 | resolveFunction(stmt, FT_FUNCTION, resolver); 464 | 465 | return NULL; 466 | } 467 | 468 | static void * 469 | resolver_visitExpressionStmt(ExpressionStmt *stmt, void *context) 470 | { 471 | Resolver *resolver = (Resolver *)context; 472 | resolveExpr(stmt->expression, resolver); 473 | return NULL; 474 | } 475 | 476 | static void * 477 | resolver_visitIfStmt(IfStmt *stmt, void *context) 478 | { 479 | Resolver *resolver = (Resolver *)context; 480 | resolveExpr(stmt->condition, resolver); 481 | resolveStmt(stmt->thenBranch, resolver); 482 | if (stmt->elseBranch != NULL) 483 | { 484 | resolveStmt(stmt->elseBranch, resolver); 485 | } 486 | 487 | return NULL; 488 | } 489 | 490 | static void * 491 | resolver_visitPrintStmt(PrintStmt *stmt, void *context) 492 | { 493 | Resolver *resolver = (Resolver *)context; 494 | resolveExpr(stmt->expression, resolver); 495 | return NULL; 496 | } 497 | 498 | static void * 499 | resolver_visitReturnStmt(ReturnStmt *stmt, void *context) 500 | { 501 | Resolver *resolver = (Resolver *)context; 502 | 503 | if (resolver->currentFunction == FT_NONE) { 504 | Error *error = resolver_throwError(stmt->keyword, "Cannot return from top-level code.", resolver); 505 | return error; 506 | } 507 | 508 | if (stmt->value != NULL) 509 | { 510 | if (resolver->currentFunction == FT_INITIALIZER) 511 | { 512 | Error *error = resolver_throwError(stmt->keyword, "Cannot return a value from an initializer.", resolver); 513 | return error; 514 | } 515 | resolveExpr(stmt->value, resolver); 516 | } 517 | 518 | return NULL; 519 | } 520 | 521 | static void * 522 | resolver_visitWhileStmt(WhileStmt *stmt, void *context) 523 | { 524 | Resolver *resolver = (Resolver *)context; 525 | resolveExpr(stmt->condition, resolver); 526 | resolveStmt(stmt->body, resolver); 527 | 528 | return NULL; 529 | } 530 | 531 | static void * 532 | resolver_visitBinaryExpr(Binary *expr, void *context) 533 | { 534 | Resolver *resolver = (Resolver *)context; 535 | resolveExpr(expr->left, resolver); 536 | resolveExpr(expr->right, resolver); 537 | 538 | return NULL; 539 | } 540 | 541 | static void * 542 | resolver_visitCallExpr(Call *expr, void *context) 543 | { 544 | Resolver *resolver = (Resolver *)context; 545 | resolveExpr(expr->callee, resolver); 546 | 547 | Expr *arg = expr->arguments; 548 | while (arg != NULL) 549 | { 550 | resolveExpr(arg, resolver); 551 | arg = arg->next; 552 | } 553 | 554 | return NULL; 555 | } 556 | 557 | static void * 558 | resolver_visitGetExpr(Get *expr, void *context) 559 | { 560 | Resolver *resolver = (Resolver *)context; 561 | resolveExpr(expr->object, resolver); 562 | return NULL; 563 | } 564 | 565 | static void * 566 | resolver_visitGroupingExpr(Grouping *expr, void *context) 567 | { 568 | Resolver *resolver = (Resolver *)context; 569 | resolveExpr(expr->expression, resolver); 570 | 571 | return NULL; 572 | } 573 | 574 | static void * 575 | resolver_visitLiteralExpr(Literal *expr, void *context) 576 | { 577 | return NULL; 578 | } 579 | 580 | static void * 581 | resolver_visitLogicalExpr(Logical *expr, void *context) 582 | { 583 | Resolver *resolver = (Resolver *)context; 584 | resolveExpr(expr->left, resolver); 585 | resolveExpr(expr->right, resolver); 586 | return NULL; 587 | } 588 | 589 | static void * 590 | resolver_visitSetExpr(Set *expr, void *context) 591 | { 592 | Resolver *resolver = (Resolver *)context; 593 | resolveExpr(expr->value, resolver); 594 | resolveExpr(expr->object, resolver); 595 | return NULL; 596 | } 597 | 598 | static void * 599 | resolver_visitSuperExpr(Super *expr, void *context) 600 | { 601 | Resolver *resolver = (Resolver *)context; 602 | if (resolver->currentClass == CT_NONE) 603 | { 604 | Error *error = resolver_throwError(expr->keyword, "Cannot use 'super' outside of a class.", resolver); 605 | return error; 606 | } 607 | else if (resolver->currentClass != CT_SUBCLASS) 608 | { 609 | Error *error = resolver_throwError(expr->keyword, "Cannot use 'super' in a class with no superclass.", resolver); 610 | return error; 611 | } 612 | resolveLocal((Expr *)expr, resolver->superString, resolver); 613 | return NULL; 614 | } 615 | 616 | static void * 617 | resolver_visitThisExpr(This *expr, void *context) 618 | { 619 | Resolver *resolver = (Resolver *)context; 620 | 621 | if (resolver->currentClass == CT_NONE) 622 | { 623 | Error *error = resolver_throwError(expr->keyword, "Cannot use 'this' outside of a class.", resolver); 624 | return error; 625 | } 626 | 627 | assert(expr->keyword->type == TT_THIS); 628 | resolveLocal((Expr *)expr, resolver->thisString, resolver); 629 | return NULL; 630 | } 631 | static void * 632 | resolver_visitUnaryExpr(Unary *expr, void *context) 633 | { 634 | Resolver *resolver = (Resolver *)context; 635 | resolveExpr(expr->right, resolver); 636 | 637 | return NULL; 638 | } 639 | 640 | static Resolver * 641 | resolver_init(Interpreter *interpreter) 642 | { 643 | Resolver *resolver = lox_alloc(Resolver); 644 | if(resolver == NULL) 645 | { 646 | fatal_outOfMemory(); 647 | } 648 | resolver->interpreter = interpreter; 649 | 650 | // Initializes the stack of scopes 651 | resolver->scopes.top = NULL; 652 | 653 | resolver->currentFunction = FT_NONE; 654 | resolver->currentClass = CT_NONE; 655 | resolver->error = NULL; 656 | 657 | resolver->thisString = str_fromLiteral("this"); 658 | resolver->superString = str_fromLiteral("super"); 659 | 660 | // Initialize expression visitor 661 | { 662 | ExprVisitor *visitor = &resolver->exprVisitor; 663 | #define DEFINE_VISITOR(type) \ 664 | visitor->visit##type = resolver_visit##type##Expr; 665 | FOREACH_AST_NODE(DEFINE_VISITOR) 666 | #undef DEFINE_VISITOR 667 | } 668 | 669 | // Initialize statement visitor 670 | { 671 | StmtVisitor *visitor = &resolver->stmtVisitor; 672 | #define DEFINE_VISITOR(type) \ 673 | visitor->visit##type = resolver_visit##type##Stmt; 674 | 675 | FOREACH_STMT_NODE(DEFINE_VISITOR) 676 | #undef DEFINE_VISITOR 677 | } 678 | 679 | return resolver; 680 | } 681 | 682 | static void resolver_free(Resolver *resolver) 683 | { 684 | if(resolver->error) 685 | { 686 | freeError(resolver->error); 687 | } 688 | str_free(resolver->thisString); 689 | str_free(resolver->superString); 690 | assert(stackIsEmpty(&resolver->scopes)); 691 | lox_free(resolver); 692 | } 693 | 694 | void resolve(Stmt* statements, Interpreter *interpreter) 695 | { 696 | Resolver *resolver = resolver_init(interpreter); 697 | resolveStmtList(statements, resolver); 698 | resolver_free(resolver); 699 | } 700 | 701 | -------------------------------------------------------------------------------- /src/resolver.h: -------------------------------------------------------------------------------- 1 | // 2 | // resolver.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 03/11/2017. 6 | // 7 | 8 | #ifndef resolver_h 9 | #define resolver_h 10 | 11 | #include "interpreter.h" 12 | #include "stmt.h" 13 | 14 | void resolve(Stmt* statements, Interpreter *interpreter); 15 | 16 | #endif /* resolver_h */ 17 | -------------------------------------------------------------------------------- /src/return.c: -------------------------------------------------------------------------------- 1 | // 2 | // return.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 02/11/2017. 6 | // 7 | 8 | #include "return.h" 9 | 10 | extern inline Return * return_init(Object *value, GarbageCollector *collector); 11 | extern inline Object * return_unwrap(Return *ret); 12 | -------------------------------------------------------------------------------- /src/return.h: -------------------------------------------------------------------------------- 1 | // 2 | // return.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 02/11/2017. 6 | // 7 | 8 | #ifndef return_h 9 | #define return_h 10 | 11 | #include "objects.h" 12 | 13 | typedef struct 14 | { 15 | Object value; 16 | } Return; 17 | 18 | #include "garbage_collector.h" 19 | #include "memory.h" 20 | 21 | inline Return * return_init(Object *value, GarbageCollector *collector) 22 | { 23 | Return *ret = (Return *)value; 24 | if(ret == NULL) 25 | { 26 | ret = (Return *)obj_newNil(collector); 27 | } 28 | return ret; 29 | } 30 | 31 | inline Object * return_unwrap(Return *ret) 32 | { 33 | Object *value = (Object *)ret; 34 | return value; 35 | } 36 | 37 | #endif /* return_h */ 38 | -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | // 2 | // scanner.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #include "scanner.h" 9 | 10 | #include "error.h" 11 | #include "utility.h" 12 | 13 | typedef struct Scanner 14 | { 15 | const char *source; 16 | 17 | Token *first_token; 18 | Token *last_token; 19 | 20 | str_size start; 21 | str_size current; 22 | str_size end; 23 | 24 | int line; 25 | } Scanner; 26 | 27 | static Scanner * scanner_init(const char *source, int32_t firstLineNum) 28 | { 29 | Scanner *scanner = lox_alloc(Scanner); 30 | if(scanner == NULL) 31 | { 32 | fatal_outOfMemory(); 33 | } 34 | scanner->source = source; 35 | 36 | scanner->first_token = NULL; 37 | scanner->last_token = NULL; 38 | 39 | scanner->start = 0; 40 | scanner->current = 0; 41 | assert(str_calculateLength(source) == str_length(source)); 42 | scanner->end = str_length(source); 43 | 44 | // NOTE: internally the first line is num 0. 45 | scanner->line = firstLineNum - 1; 46 | 47 | return scanner; 48 | } 49 | 50 | static inline bool scanner_is_at_end(const Scanner *scanner) 51 | { 52 | return scanner->current >= scanner->end; 53 | } 54 | 55 | static inline Lexeme scanner_current_lexeme(const Scanner *scanner) 56 | { 57 | Lexeme result; 58 | result.index = substringStartEnd(scanner->start, scanner->current); 59 | result.line = scanner->line; 60 | return result; 61 | } 62 | 63 | static inline char scanner_advance(Scanner *scanner) 64 | { 65 | char result = scanner->source[scanner->current]; 66 | scanner->current++; 67 | 68 | return result; 69 | } 70 | 71 | static inline bool scanner_match(Scanner *scanner, char expected) 72 | { 73 | if(scanner_is_at_end(scanner) || scanner->source[scanner->current] != expected) 74 | { 75 | return false; 76 | } 77 | scanner->current++; 78 | return true; 79 | } 80 | 81 | // NOTE: returns 0 if we are past the end of the source. 82 | static inline char scanner_peek(const Scanner *scanner) 83 | { 84 | if(scanner_is_at_end(scanner)) 85 | { 86 | return '\0'; 87 | } 88 | return scanner->source[scanner->current]; 89 | } 90 | 91 | static inline char scanner_peek_next(const Scanner *scanner) 92 | { 93 | size_t index_next = scanner->current + 1; 94 | if(index_next >= scanner->end) 95 | { 96 | return '\0'; 97 | } 98 | return scanner->source[index_next]; 99 | } 100 | 101 | static void scanner_add_token(Scanner *scanner, Token *token) 102 | { 103 | if(scanner->first_token == NULL) 104 | { 105 | scanner->first_token = token; 106 | scanner->last_token = token; 107 | } 108 | else 109 | { 110 | scanner->last_token->nextInScan = token; 111 | scanner->last_token = token; 112 | } 113 | } 114 | 115 | static void scanner_atomic(Scanner *scanner, TokenType type) 116 | { 117 | Token *token = token_atomic(type, scanner_current_lexeme(scanner)); 118 | scanner_add_token(scanner, token); 119 | } 120 | 121 | static void scanner_number(Scanner *scanner) 122 | { 123 | while(is_digit(scanner_peek(scanner))) 124 | { 125 | scanner_advance(scanner); 126 | } 127 | 128 | // Look for a fractional part. 129 | if (scanner_peek(scanner) == '.' && is_digit(scanner_peek_next(scanner))) 130 | { 131 | // Consume the '.' 132 | scanner_advance(scanner); 133 | 134 | while (is_digit(scanner_peek(scanner))) 135 | { 136 | scanner_advance(scanner); 137 | } 138 | } 139 | 140 | char *text = str_substring(scanner->source, substringStartEnd(scanner->start, scanner->current)); 141 | double value = atof(text); 142 | str_free(text); 143 | 144 | Token *token = token_number_literal(value, scanner_current_lexeme(scanner)); 145 | scanner_add_token(scanner, token); 146 | } 147 | 148 | static void scanner_string(Scanner *scanner) 149 | { 150 | while (scanner_peek(scanner) != '\"' && !scanner_is_at_end(scanner)) 151 | { 152 | if (scanner_peek(scanner) == '\n') 153 | { 154 | scanner->line++; 155 | } 156 | scanner_advance(scanner); 157 | } 158 | 159 | // Unterminated string. 160 | if (scanner_is_at_end(scanner)) 161 | { 162 | lox_error(scanner->line, "Unterminated string."); 163 | } 164 | else 165 | { 166 | // The closing '"' 167 | scanner_advance(scanner); 168 | 169 | Lexeme lexeme = scanner_current_lexeme(scanner); 170 | // Trim the surrounding quotes. 171 | SubstringIndex index = substring_trimmed(lexeme.index); 172 | char *value = str_substring(scanner->source, index); 173 | 174 | Token *token = token_string_literal(value, lexeme); 175 | str_free(value); 176 | 177 | scanner_add_token(scanner, token); 178 | } 179 | } 180 | 181 | static void scanner_identifier(Scanner *scanner) 182 | { 183 | while(is_alphanumeric(scanner_peek(scanner))) 184 | { 185 | scanner_advance(scanner); 186 | } 187 | 188 | // See if the identifier is a reserved word. 189 | Lexeme lexeme = scanner_current_lexeme(scanner); 190 | char *str = str_substring(scanner->source, lexeme.index); 191 | KeywordEntry *entry = lookup_keyword(str); 192 | 193 | if(keyword_is_valid(entry)) 194 | { 195 | scanner_atomic(scanner, entry->type); 196 | } 197 | else 198 | { 199 | Token *token = token_identifier(str, lexeme); 200 | scanner_add_token(scanner, token); 201 | } 202 | str_free(str); 203 | } 204 | 205 | static void scanner_scanToken(Scanner *scanner) 206 | { 207 | char c = scanner_advance(scanner); 208 | 209 | switch (c) { 210 | case '(': { scanner_atomic(scanner, TT_LEFT_PAREN); } break; 211 | case ')': { scanner_atomic(scanner, TT_RIGHT_PAREN); } break; 212 | case '{': { scanner_atomic(scanner, TT_LEFT_BRACE); } break; 213 | case '}': { scanner_atomic(scanner, TT_RIGHT_BRACE); } break; 214 | case ',': { scanner_atomic(scanner, TT_COMMA); } break; 215 | case '.': { scanner_atomic(scanner, TT_DOT); } break; 216 | case '-': { scanner_atomic(scanner, TT_MINUS); } break; 217 | case '+': { scanner_atomic(scanner, TT_PLUS); } break; 218 | case ';': { scanner_atomic(scanner, TT_SEMICOLON); } break; 219 | case '*': { scanner_atomic(scanner, TT_STAR); } break; 220 | case '!': { 221 | scanner_atomic(scanner, 222 | scanner_match(scanner, '=') ? TT_BANG_EQUAL : TT_BANG); 223 | } break; 224 | case '=': { 225 | scanner_atomic(scanner, 226 | scanner_match(scanner, '=') ? TT_EQUAL_EQUAL : TT_EQUAL); 227 | } break; 228 | case '<': { 229 | scanner_atomic(scanner, 230 | scanner_match(scanner, '=') ? TT_LESS_EQUAL : TT_LESS); 231 | } break; 232 | case '>': { 233 | scanner_atomic(scanner, 234 | scanner_match(scanner, '=') ? TT_GREATER_EQUAL : TT_GREATER); 235 | } break; 236 | 237 | case '/': 238 | { 239 | if (scanner_match(scanner, '/')) 240 | { 241 | // A comment goes until the end of the line. 242 | while ((scanner_peek(scanner) != '\n') && !scanner_is_at_end(scanner)) 243 | { 244 | scanner_advance(scanner); 245 | } 246 | } 247 | else if(scanner_match(scanner, '*')) 248 | { 249 | // Start c-like comment 250 | int level = 1; 251 | while ((level > 0) && !scanner_is_at_end(scanner)) 252 | { 253 | if((scanner_peek(scanner) == '*') && (scanner_peek_next(scanner) == '/')) 254 | { 255 | level--; 256 | scanner_advance(scanner); 257 | } 258 | else if ((scanner_peek(scanner) == '/') && (scanner_peek_next(scanner) == '*')) 259 | { 260 | level++; 261 | scanner_advance(scanner); 262 | } 263 | scanner_advance(scanner); 264 | } 265 | if(level > 0) 266 | { 267 | lox_error(scanner->line, "Unterminated /* comment."); 268 | } 269 | } 270 | else 271 | { 272 | scanner_atomic(scanner, TT_SLASH); 273 | } 274 | } break; 275 | 276 | case ' ': 277 | case '\r': 278 | case '\t': 279 | { 280 | // Ignore whitespace. 281 | } break; 282 | 283 | case '\n': 284 | { 285 | scanner->line++; 286 | } break; 287 | 288 | case '\"': 289 | { 290 | scanner_string(scanner); 291 | } break; 292 | 293 | default: 294 | { 295 | if(is_digit(c)) 296 | { 297 | scanner_number(scanner); 298 | } 299 | else if(is_alpha(c)) 300 | { 301 | scanner_identifier(scanner); 302 | } 303 | else 304 | { 305 | lox_error(scanner->line, "Unexpected character."); 306 | } 307 | } 308 | } 309 | } 310 | 311 | static Token * scan_tokens(Scanner *scanner) 312 | { 313 | while (!scanner_is_at_end(scanner)) 314 | { 315 | // We are the beginning of the next lexeme. 316 | scanner->start = scanner->current; 317 | scanner_scanToken(scanner); 318 | } 319 | 320 | Token *eof = token_atomic(TT_EOF, scanner_current_lexeme(scanner)); 321 | scanner_add_token(scanner, eof); 322 | 323 | return scanner->first_token; 324 | } 325 | 326 | Token * scanLine(const char *source, int32_t lineNumber) 327 | { 328 | Scanner *scanner = scanner_init(source, lineNumber); 329 | Token *tokens = scan_tokens(scanner); 330 | lox_free(scanner); 331 | return tokens; 332 | } 333 | 334 | Token * scan(const char *source) 335 | { 336 | return scanLine(source, 1); 337 | } 338 | 339 | -------------------------------------------------------------------------------- /src/scanner.h: -------------------------------------------------------------------------------- 1 | // 2 | // scanner.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #ifndef scanner_h 9 | #define scanner_h 10 | 11 | #include "token.h" 12 | 13 | Token * scanLine(const char *source, int32_t lineNumber); 14 | Token * scan(const char *source); 15 | 16 | #endif /* scanner_h */ 17 | -------------------------------------------------------------------------------- /src/stmt.c: -------------------------------------------------------------------------------- 1 | // 2 | // stmt.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #include "stmt.h" 9 | 10 | Stmt * initBlock(Stmt *statements) 11 | { 12 | BlockStmt *stmt = lox_alloc(BlockStmt); 13 | stmt->stmt.type = STMT_Block; 14 | stmt->stmt.next = NULL; 15 | stmt->statements = statements; 16 | return AS_STMT(stmt); 17 | } 18 | 19 | Stmt * initExpression(Expr *expr) 20 | { 21 | ExpressionStmt *stmt = lox_alloc(ExpressionStmt); 22 | stmt->stmt.type = STMT_Expression; 23 | stmt->stmt.next = NULL; 24 | stmt->expression = expr; 25 | return AS_STMT(stmt); 26 | } 27 | 28 | Stmt * initClass(Token *name, Expr *superClass, FunctionStmt *methods) 29 | { 30 | ClassStmt *stmt = lox_alloc(ClassStmt); 31 | stmt->stmt.type = STMT_Class; 32 | stmt->stmt.next = NULL; 33 | stmt->name = name; 34 | stmt->superClass = superClass; 35 | stmt->methods = methods; 36 | return AS_STMT(stmt); 37 | } 38 | 39 | Stmt * initFunction(Token *name, Token **parameters, int32_t parametersCount, Stmt *body) 40 | { 41 | FunctionStmt *stmt = lox_alloc(FunctionStmt); 42 | stmt->stmt.type = STMT_Function; 43 | stmt->stmt.next = NULL; 44 | stmt->name = name; 45 | stmt->parameters = parameters; 46 | stmt->arity = parametersCount; 47 | stmt->body = body; 48 | 49 | return AS_STMT(stmt); 50 | } 51 | 52 | Stmt * initIf(Expr *condition, Stmt *thenBranch, Stmt *elseBranch) 53 | { 54 | IfStmt *stmt = lox_alloc(IfStmt); 55 | stmt->stmt.type = STMT_If; 56 | stmt->stmt.next = NULL; 57 | stmt->condition = condition; 58 | stmt->thenBranch = thenBranch; 59 | stmt->elseBranch = elseBranch; 60 | return AS_STMT(stmt); 61 | } 62 | 63 | Stmt * initPrint(Expr *expr) 64 | { 65 | PrintStmt *stmt = lox_alloc(PrintStmt); 66 | stmt->stmt.type = STMT_Print; 67 | stmt->stmt.next = NULL; 68 | stmt->expression = expr; 69 | return AS_STMT(stmt); 70 | } 71 | 72 | Stmt * initReturn(Token *keyword, Expr *value) 73 | { 74 | ReturnStmt *stmt = lox_alloc(ReturnStmt); 75 | stmt->stmt.type = STMT_Return; 76 | stmt->stmt.next = NULL; 77 | stmt->keyword = keyword; 78 | stmt->value = value; 79 | return AS_STMT(stmt); 80 | } 81 | 82 | Stmt * initVar(Token *name, Expr *initializer) 83 | { 84 | VarStmt *stmt = lox_alloc(VarStmt); 85 | stmt->stmt.type = STMT_Var; 86 | stmt->stmt.next = NULL; 87 | stmt->name = name; 88 | stmt->initializer = initializer; 89 | return AS_STMT(stmt); 90 | } 91 | 92 | Stmt * initWhile(Expr *condition, Stmt *body) 93 | { 94 | WhileStmt *stmt = lox_alloc(WhileStmt); 95 | stmt->stmt.type = STMT_While; 96 | stmt->stmt.next = NULL; 97 | stmt->condition = condition; 98 | stmt->body = body; 99 | return AS_STMT(stmt); 100 | } 101 | 102 | // Free the list of statements stmt. 103 | // If stmt is NULL the function does nothing. 104 | void freeStmt(Stmt *stmt) 105 | { 106 | while(stmt) 107 | { 108 | switch (stmt->type) { 109 | case STMT_Block: { 110 | BlockStmt *block = (BlockStmt *)stmt; 111 | freeStmt(block->statements); 112 | } break; 113 | case STMT_Class: { 114 | ClassStmt *classStmt = (ClassStmt *)stmt; 115 | freeStmt(AS_STMT(classStmt->methods)); 116 | free_expr(classStmt->superClass); 117 | } break; 118 | case STMT_Expression: { 119 | ExpressionStmt *expression = (ExpressionStmt *)stmt; 120 | free_expr(expression->expression); 121 | } break; 122 | case STMT_Function: { 123 | FunctionStmt *fun = (FunctionStmt *)stmt; 124 | freeStmt(fun->body); 125 | lox_free(fun->parameters); 126 | } break; 127 | case STMT_If: { 128 | IfStmt *ifStmt = (IfStmt *)stmt; 129 | free_expr(ifStmt->condition); 130 | freeStmt(ifStmt->thenBranch); 131 | freeStmt(ifStmt->elseBranch); 132 | } break; 133 | case STMT_Print: { 134 | PrintStmt *print = (PrintStmt *)stmt; 135 | free_expr(print->expression); 136 | } break; 137 | case STMT_Return: { 138 | ReturnStmt *ret = (ReturnStmt *)stmt; 139 | free_expr(ret->value); 140 | } break; 141 | case STMT_Var: { 142 | VarStmt *var = (VarStmt *)stmt; 143 | free_expr(var->initializer); 144 | } break; 145 | case STMT_While: { 146 | WhileStmt *whileStmt = (WhileStmt *)stmt; 147 | free_expr(whileStmt->condition); 148 | freeStmt(whileStmt->body); 149 | } break; 150 | } 151 | Stmt *next = stmt->next; 152 | lox_free(stmt); 153 | stmt = next; 154 | } 155 | } 156 | 157 | // Returns the last statement in a non-empty chain of statements. 158 | Stmt * stmt_last(Stmt *statements) 159 | { 160 | assert(statements != NULL); 161 | 162 | Stmt *last = statements; 163 | while(last->next) 164 | { 165 | last = last->next; 166 | } 167 | return last; 168 | } 169 | 170 | Stmt * stmt_appendTo(Stmt *head, Stmt *tail) 171 | { 172 | if (head == NULL) 173 | { 174 | return tail; 175 | } 176 | Stmt *last = stmt_last(head); 177 | last->next = tail; 178 | return head; 179 | } 180 | 181 | void * stmt_accept_visitor(Stmt *stmt, StmtVisitor *visitor, void *context) 182 | { 183 | void *result = NULL; 184 | switch(stmt->type) 185 | { 186 | #define DEFINE_VISIT_CASE(type) \ 187 | case STMT_##type: { \ 188 | result = visitor->visit##type((type##Stmt *)stmt, context); \ 189 | } break; 190 | 191 | FOREACH_STMT_NODE(DEFINE_VISIT_CASE) 192 | 193 | #undef DEFINE_VISIT_CASE 194 | } 195 | return result; 196 | } 197 | -------------------------------------------------------------------------------- /src/stmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // stmt.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 18/10/2017. 6 | // 7 | 8 | #ifndef stmt_h 9 | #define stmt_h 10 | 11 | #include "expr.h" 12 | 13 | /* 14 | "Block : List statements", 15 | "Class : Token name, Expr superclass," + 16 | " List methods", 17 | "Expression : Expr expression", 18 | "Function : Token name, List parameters, List body", 19 | "If : Expr condition, Stmt thenBranch, Stmt elseBranch", 20 | "Print : Expr expression", 21 | "Return : Token keyword, Expr value", 22 | "Var : Token name, Expr initializer", 23 | "While : Expr condition, Stmt body" 24 | */ 25 | 26 | #define FOREACH_STMT_NODE(code) \ 27 | code(Block) code(Class) code(Expression) code(Function) \ 28 | code(If) code(Print) code(Return) code(Var) \ 29 | code(While) 30 | 31 | typedef enum StmtType 32 | { 33 | #define DEFINE_ENUM_TYPE(type) STMT_##type, 34 | FOREACH_STMT_NODE(DEFINE_ENUM_TYPE) 35 | #undef DEFINE_ENUM_TYPE 36 | } StmtType; 37 | 38 | typedef struct Stmt 39 | { 40 | StmtType type; 41 | struct Stmt *next; 42 | } Stmt; 43 | 44 | #define AS_STMT(stmt) (Stmt *)stmt 45 | 46 | // Block : List statements 47 | typedef struct 48 | { 49 | Stmt stmt; 50 | Stmt *statements; 51 | } BlockStmt; 52 | 53 | // Expression : Expr expression 54 | typedef struct 55 | { 56 | Stmt stmt; 57 | Expr *expression; 58 | } ExpressionStmt; 59 | 60 | // "Function : Token name, List parameters, List body", 61 | typedef struct 62 | { 63 | Stmt stmt; 64 | Token *name; 65 | Token **parameters; 66 | int32_t arity; 67 | Stmt *body; 68 | } FunctionStmt; 69 | 70 | // Class : Token name, Expr superclass, List methods 71 | typedef struct 72 | { 73 | Stmt stmt; 74 | Token *name; 75 | Expr *superClass; 76 | FunctionStmt *methods; 77 | } ClassStmt; 78 | 79 | // If : Expr condition, Stmt thenBranch, Stmt elseBranch 80 | typedef struct 81 | { 82 | Stmt stmt; 83 | Expr *condition; 84 | Stmt *thenBranch; 85 | Stmt *elseBranch; 86 | } IfStmt; 87 | 88 | // Print : Expr expression 89 | typedef struct 90 | { 91 | Stmt stmt; 92 | Expr *expression; 93 | } PrintStmt; 94 | 95 | // Return : Token keyword, Expr value 96 | typedef struct 97 | { 98 | Stmt stmt; 99 | Token *keyword; 100 | Expr *value; 101 | } ReturnStmt; 102 | 103 | // Var : Token name, Expr initializer 104 | typedef struct 105 | { 106 | Stmt stmt; 107 | Token *name; 108 | Expr *initializer; 109 | } VarStmt; 110 | 111 | // While : Expr condition, Stmt body 112 | typedef struct 113 | { 114 | Stmt stmt; 115 | Expr *condition; 116 | Stmt *body; 117 | } WhileStmt; 118 | 119 | Stmt * initBlock(Stmt *statements); 120 | Stmt * initClass(Token *name, Expr *superClass, FunctionStmt *methods); 121 | Stmt * initExpression(Expr *expr); 122 | Stmt * initFunction(Token *name, Token **parameters, int32_t parametersCount, Stmt *body); 123 | Stmt * initIf(Expr *condition, Stmt *thenBranch, Stmt *elseBranch); 124 | Stmt * initPrint(Expr *expr); 125 | Stmt * initReturn(Token *keyword, Expr *value); 126 | Stmt * initVar(Token *name, Expr *initializer); 127 | Stmt * initWhile(Expr *condition, Stmt *body); 128 | void freeStmt(Stmt *stmt); 129 | Stmt * stmt_last(Stmt *statements); 130 | Stmt * stmt_appendTo(Stmt *head, Stmt *tail); 131 | 132 | /* Visitor */ 133 | 134 | typedef struct 135 | { 136 | #define DEFINE_VISIT(type) \ 137 | void *(*visit##type)(type##Stmt *, void *context); 138 | 139 | FOREACH_STMT_NODE(DEFINE_VISIT) 140 | #undef DEFINE_VISIT 141 | } StmtVisitor; 142 | 143 | void * stmt_accept_visitor(Stmt *stmt, StmtVisitor *visitor, void *context); 144 | 145 | #endif /* stmt_h */ 146 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | // 2 | // string.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #include "string.h" 9 | #include "error.h" 10 | #include 11 | #include 12 | 13 | extern inline void str_free(char *str); 14 | extern inline char *str_clear(char *str); 15 | extern inline str_size str_calculateLength(const char *str); 16 | extern inline str_size str_length(const char *str); 17 | 18 | extern inline SubstringIndex substring(str_size start, str_size count); 19 | extern inline SubstringIndex substringStartEnd(str_size start, str_size onePastLast); 20 | extern inline SubstringIndex substring_trimmed(SubstringIndex index); 21 | 22 | struct MemoryPool *str_smallPool; 23 | struct MemoryPool *str_mediumPool; 24 | 25 | void str_initPools() 26 | { 27 | #ifdef STR_USE_MEMORY_POOLS 28 | #ifdef DEBUG_VERBOSE 29 | printf("Initializing string pools.\n"); 30 | #endif 31 | str_smallPool = poolInit(STR_SMALL_SIZE); 32 | str_mediumPool = poolInit(STR_MEDIUM_SIZE); 33 | #endif 34 | } 35 | 36 | void str_freePools() 37 | { 38 | #ifdef STR_USE_MEMORY_POOLS 39 | #ifdef DEBUG_VERBOSE 40 | printf("Freeing string pools.\n"); 41 | #endif 42 | poolFree(str_smallPool); 43 | poolFree(str_mediumPool); 44 | #endif 45 | } 46 | 47 | char * str_alloc(str_size capacity) 48 | { 49 | StringHeader *header; 50 | #ifdef STR_USE_MEMORY_POOLS 51 | if (capacity <= STR_SMALL_CAPACITY) 52 | { 53 | header = poolGetObject(str_smallPool); 54 | header->capacity = STR_SMALL_CAPACITY; 55 | } 56 | else if (capacity <= STR_MEDIUM_CAPACITY) 57 | { 58 | header = poolGetObject(str_mediumPool); 59 | header->capacity = STR_MEDIUM_CAPACITY; 60 | } 61 | else 62 | #endif 63 | { 64 | assert(capacity < STR_SIZE_MAX - STR_HEADER_SIZE - 1); 65 | header = (StringHeader *)lox_allocn(char, STR_HEADER_SIZE + capacity + 1); 66 | if(header == NULL) 67 | { 68 | fatal_outOfMemory(); 69 | } 70 | header->capacity = capacity; 71 | } 72 | 73 | header->length = 0; 74 | char *str = STR_FROM_HEADER(header); 75 | str[0] = '\0'; 76 | #ifdef STR_STORE_HASH 77 | header->hash = STR_HASH_UNAVAILABLE; 78 | #endif 79 | 80 | return str; 81 | } 82 | 83 | char *str_realloc(char *str, str_size newCapacity) 84 | { 85 | char *newString; 86 | StringHeader *header = STR_HEADER(str); 87 | #ifdef STR_USE_MEMORY_POOLS 88 | if (header->capacity == STR_SMALL_CAPACITY) 89 | { 90 | newString = str_alloc(newCapacity); 91 | memcpy(newString, STR_FROM_HEADER(header), header->length + 1); 92 | STR_LENGTH(newString) = header->length; 93 | poolReleaseObject(header, str_smallPool); 94 | } 95 | else if (header->capacity == STR_MEDIUM_CAPACITY) 96 | { 97 | newString = str_alloc(newCapacity); 98 | memcpy(newString, STR_FROM_HEADER(header), header->length + 1); 99 | STR_LENGTH(newString) = header->length; 100 | poolReleaseObject(header, str_mediumPool); 101 | } 102 | else 103 | #endif 104 | { 105 | StringHeader *newHeader = (StringHeader *)lox_realloc(header, STR_HEADER_SIZE + newCapacity + 1); 106 | 107 | if(newHeader == NULL) 108 | { 109 | fatal_outOfMemory(); 110 | } 111 | newHeader->capacity = newCapacity; 112 | newString = STR_FROM_HEADER(newHeader); 113 | } 114 | 115 | return newString; 116 | } 117 | 118 | char * str_grow_(char *str, str_size newCapacity) 119 | { 120 | if (STR_CAPACITY(str) < newCapacity) 121 | { 122 | str = str_realloc(str, newCapacity); 123 | } 124 | return str; 125 | } 126 | 127 | void str_setLength(char *str) 128 | { 129 | STR_LENGTH(str) = str_calculateLength(str); 130 | } 131 | 132 | char * str_fromLiteral(const char *source) 133 | { 134 | str_size len = str_calculateLength(source); 135 | char *result = str_alloc(len); 136 | memcpy(result, source, len + 1); 137 | assert(result[len] == '\0'); 138 | STR_LENGTH(result) = len; 139 | return result; 140 | } 141 | 142 | char * str_dup(const char *source) 143 | { 144 | str_size len = str_length(source); 145 | char *result = str_alloc(len); 146 | memcpy(result, source, len + 1); 147 | assert(result[len] == '\0'); 148 | STR_LENGTH(result) = len; 149 | return result; 150 | } 151 | 152 | char * str_concat(const char *str1, const char *str2) 153 | { 154 | str_size len1 = str_length(str1); 155 | str_size len2 = str_length(str2); 156 | char *result = str_alloc(len1 + len2); 157 | memcpy(result, str1, len1); 158 | memcpy(result + len1, str2, len2 + 1); 159 | str_size totalLen = len1 + len2; 160 | assert(result[totalLen] == '\0'); 161 | STR_LENGTH(result) = totalLen; 162 | return result; 163 | } 164 | 165 | char * str_append_(char *str, const char *suffix) 166 | { 167 | str_size len = str_length(str); 168 | str_size suffixLen = str_length(suffix); 169 | str_size totalLen = len + suffixLen; 170 | 171 | str_grow(str, totalLen); 172 | memcpy(str+len, suffix, suffixLen + 1); 173 | STR_LENGTH(str) = totalLen; 174 | #ifdef STR_STORE_HASH 175 | STR_HASH(str) = STR_HASH_UNAVAILABLE; 176 | #endif 177 | 178 | return str; 179 | } 180 | 181 | char * str_appendLiteral_(char *str, const char *suffix) 182 | { 183 | str_size len = str_length(str); 184 | str_size suffixLen = str_calculateLength(suffix); 185 | str_size totalLen = len + suffixLen; 186 | 187 | str_grow(str, totalLen); 188 | memcpy(str+len, suffix, suffixLen + 1); 189 | STR_LENGTH(str) = totalLen; 190 | #ifdef STR_STORE_HASH 191 | STR_HASH(str) = STR_HASH_UNAVAILABLE; 192 | #endif 193 | 194 | return str; 195 | } 196 | 197 | char * str_fromDouble(double value) 198 | { 199 | char *result = str_alloc(64); 200 | sprintf(result, "%.*g", DBL_DIG, value); 201 | STR_LENGTH(result) = str_calculateLength(result); 202 | return result; 203 | } 204 | 205 | char * str_fromInt64(int64_t value) 206 | { 207 | char *result = str_alloc(64); 208 | sprintf(result, "%lld", value); 209 | STR_LENGTH(result) = str_calculateLength(result); 210 | return result; 211 | } 212 | 213 | // Returns a new string containing the substring of `str` defined 214 | // by `index`. The returned string must be freed with str_free(). 215 | char * str_substring(const char * const str, SubstringIndex index) 216 | { 217 | char *substring = str_alloc(index.count); 218 | 219 | char *dest = substring; 220 | const char *source = str + index.start; 221 | str_size len = index.count; 222 | STR_LENGTH(substring) = len; 223 | while(len > 0) 224 | { 225 | *dest++ = *source++; 226 | len--; 227 | } 228 | *dest = '\0'; 229 | 230 | assert(str_length(substring) == str_calculateLength(substring)); 231 | 232 | return substring; 233 | } 234 | 235 | bool str_isEqual(const char *s1, const char *s2) 236 | { 237 | while(*s1 == *s2) 238 | { 239 | if(*s1 == '\0') 240 | { 241 | return true; 242 | } 243 | s1++; 244 | s2++; 245 | } 246 | return false; 247 | } 248 | 249 | // TODO: Test and tune the hash function 250 | // NOTE: This is the djb2 hash function ( http://www.cse.yorku.ca/~oz/hash.html ) 251 | unsigned long 252 | str_hashLiteral(const char *str) 253 | { 254 | const unsigned char *bytes = (const unsigned char *)str; 255 | unsigned long hash = 5381; 256 | int c; 257 | 258 | while ((c = *bytes++)) 259 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 260 | 261 | return hash; 262 | } 263 | 264 | unsigned long 265 | str_hash(const char *str) 266 | { 267 | #ifdef STR_STORE_HASH 268 | if (STR_HASH(str) != STR_HASH_UNAVAILABLE) 269 | { 270 | return STR_HASH(str); 271 | } 272 | #endif 273 | const unsigned char *bytes = (const unsigned char *)str; 274 | unsigned long hash = 5381; 275 | int c; 276 | 277 | while ((c = *bytes++)) 278 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 279 | 280 | #ifdef STR_STORE_HASH 281 | STR_HASH(str) = (uint32_t)hash; 282 | #endif 283 | return (uint32_t)hash; 284 | } 285 | -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | // 2 | // string.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #ifndef string_h 9 | #define string_h 10 | 11 | #include "common.h" 12 | 13 | #ifdef STR_STORE_HASH 14 | // NOTE: When the hash field has this value, the hash is recalculated 15 | #define STR_HASH_UNAVAILABLE 0 16 | #endif 17 | 18 | typedef uint32_t str_size; 19 | #define STR_SIZE_MAX ((uint32_t)-1) 20 | 21 | typedef struct SubstringIndex 22 | { 23 | str_size start; // start index of the substring 24 | str_size count; // number of characters in the substring 25 | } SubstringIndex; 26 | 27 | void str_initPools(void); 28 | void str_freePools(void); 29 | 30 | char * str_alloc(str_size capacity); 31 | void str_setLength(char *str); 32 | char * str_fromLiteral(const char *source); 33 | char * str_dup(const char *source); 34 | char * str_concat(const char *str1, const char *str2); 35 | char * str_fromDouble(double value); 36 | char * str_fromInt64(int64_t value); 37 | bool str_isEqual(const char *s1, const char *s2); 38 | 39 | #define str_grow(str, newCapacity) do { str = str_grow_(str, newCapacity); } while(0); 40 | #define str_append(str, suffix) do { str = str_append_(str, suffix); } while(0); 41 | #define str_appendLiteral(str, suffix) do { str = str_appendLiteral_(str, suffix); } while(0); 42 | 43 | char * str_append_(char* str, const char* suffix); 44 | char * str_appendLiteral_(char* str, const char* suffix); 45 | 46 | char* str_substring(const char* const str, SubstringIndex index); 47 | 48 | unsigned long str_hashLiteral(const char *str); 49 | unsigned long str_hash(const char *str); 50 | 51 | 52 | inline SubstringIndex substring(str_size start, str_size count) 53 | { 54 | SubstringIndex index = {start, count}; 55 | return index; 56 | } 57 | 58 | inline SubstringIndex substringStartEnd(str_size start, str_size onePastLast) 59 | { 60 | assert(onePastLast >= start); 61 | str_size count = onePastLast - start; 62 | SubstringIndex index = substring(start, count); 63 | return index; 64 | } 65 | 66 | inline SubstringIndex substring_trimmed(SubstringIndex index) 67 | { 68 | SubstringIndex trimmed = (index.count >= 2) ? (SubstringIndex){index.start + 1, index.count - 2} 69 | : (SubstringIndex){index.start, 0}; 70 | return trimmed; 71 | } 72 | 73 | typedef struct 74 | { 75 | str_size capacity; 76 | str_size length; 77 | #ifdef STR_STORE_HASH 78 | uint32_t hash; 79 | #endif 80 | } StringHeader; 81 | 82 | #define STR_HEADER_SIZE sizeof(StringHeader) 83 | 84 | #define STR_HEADER(string) ((StringHeader *)(string - STR_HEADER_SIZE)) 85 | #define STR_FROM_HEADER(header) ((char *)header + STR_HEADER_SIZE) 86 | #define STR_HEADER_SIZE sizeof(StringHeader) 87 | #define STR_CAPACITY(string) (STR_HEADER(string))->capacity 88 | #define STR_LENGTH(string) (STR_HEADER(string))->length 89 | #ifdef STR_STORE_HASH 90 | #define STR_HASH(string) (STR_HEADER(string))->hash 91 | #endif 92 | 93 | #define STR_SMALL_CAPACITY (STR_SMALL_SIZE - 1 - STR_HEADER_SIZE) 94 | #define STR_MEDIUM_CAPACITY (STR_MEDIUM_SIZE - 1 - STR_HEADER_SIZE) 95 | 96 | 97 | #include "memory_pool.h" 98 | 99 | extern struct MemoryPool *str_smallPool; 100 | extern struct MemoryPool *str_mediumPool; 101 | 102 | inline void str_free(char *str) 103 | { 104 | StringHeader *header = STR_HEADER(str); 105 | #ifdef STR_USE_MEMORY_POOLS 106 | if (header->capacity == STR_SMALL_CAPACITY) 107 | { 108 | poolReleaseObject(header, str_smallPool); 109 | } 110 | else if (header->capacity == STR_MEDIUM_CAPACITY) 111 | { 112 | poolReleaseObject(header, str_mediumPool); 113 | } 114 | else 115 | #endif 116 | { 117 | lox_free(header); 118 | } 119 | } 120 | 121 | inline char *str_clear(char *str) 122 | { 123 | str[0] = '\0'; 124 | STR_LENGTH(str) = 0; 125 | #ifdef STR_STORE_HASH 126 | STR_HASH(str) = STR_HASH_UNAVAILABLE; 127 | #endif 128 | return str; 129 | } 130 | 131 | inline str_size str_calculateLength(const char *str) 132 | { 133 | str_size len = 0; 134 | while(str[len] != '\0') 135 | { 136 | len++; 137 | } 138 | return len; 139 | } 140 | 141 | inline str_size str_length(const char *str) 142 | { 143 | str_size len = STR_LENGTH(str); 144 | return len; 145 | } 146 | 147 | #endif /* string_h */ 148 | 149 | -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | // 2 | // token.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #include "token.h" 9 | #include "error.h" 10 | 11 | /* Keywords */ 12 | 13 | extern inline bool keyword_is_valid(KeywordEntry *entry); 14 | 15 | KeywordEntry * lookup_keyword(const char *keyword) 16 | { 17 | KeywordEntry *entry = keywords; 18 | while(keyword_is_valid(entry)) 19 | { 20 | if(str_isEqual(entry->keyword, keyword)) 21 | { 22 | break; 23 | } 24 | entry++; 25 | } 26 | return entry; 27 | } 28 | 29 | /* Tokens */ 30 | 31 | extern inline const char * get_identifier_name(const Token *token); 32 | extern inline const char * get_string_value(const Token *token); 33 | extern inline double get_number_value(const Token *token); 34 | 35 | // Returns a new string with the value of a token literal. 36 | // The returned string must be freed with str_free(). 37 | char *string_from_token_literal(const Token *token) 38 | { 39 | char *result; 40 | switch(token->type) { 41 | case TT_NUMBER: 42 | { 43 | result = str_fromDouble(get_number_value(token)); 44 | } break; 45 | case TT_STRING: 46 | { 47 | result = str_dup(get_string_value(token)); 48 | } break; 49 | case TT_IDENTIFIER: 50 | { 51 | result = str_dup(get_identifier_name(token)); 52 | } break; 53 | case TT_TRUE: 54 | { 55 | result = str_fromLiteral("true"); 56 | } break; 57 | case TT_FALSE: 58 | { 59 | result = str_fromLiteral("false"); 60 | } break; 61 | INVALID_DEFAULT_CASE; 62 | } 63 | return result; 64 | } 65 | 66 | // Returns a new string containing a description of `token`. 67 | // The returned string must be freed with str_free(). 68 | char * token_to_string(const Token * const token, const char * const source) 69 | { 70 | char *lexeme = str_substring(source, token->lexeme.index); 71 | char *str = str_fromLiteral(tt_name_string[token->type]); 72 | str_appendLiteral(str, " '"); 73 | str_append(str, lexeme); 74 | str_appendLiteral(str, "'"); 75 | switch(token->type) 76 | { 77 | case TT_NUMBER: 78 | { 79 | str_appendLiteral(str, " - value: "); 80 | char *number = str_fromDouble(get_number_value(token)); 81 | str_append(str, number); 82 | str_free(number); 83 | } break; 84 | 85 | case TT_STRING: 86 | { 87 | str_appendLiteral(str, " - value: "); 88 | str_append(str, get_string_value(token)); 89 | } break; 90 | 91 | case TT_IDENTIFIER: 92 | { 93 | str_appendLiteral(str, " - value: "); 94 | str_append(str, get_identifier_name(token)); 95 | } break; 96 | 97 | default: 98 | { 99 | // do nothing 100 | } break; 101 | } 102 | str_free(lexeme); 103 | 104 | return str; 105 | } 106 | 107 | static Token * get_token() 108 | { 109 | Token *token = lox_alloc(Token); 110 | if(token == NULL) 111 | { 112 | fatal_outOfMemory(); 113 | } 114 | token->literal = NULL; 115 | token->nextInScan = NULL; 116 | return token; 117 | } 118 | 119 | static inline void token_set_lexeme(Token * token, Lexeme lexeme) 120 | { 121 | token->lexeme = lexeme; 122 | } 123 | 124 | Token *token_number_literal(double value, Lexeme lexeme) 125 | { 126 | Token *result = get_token(); 127 | result->type = TT_NUMBER; 128 | result->number = value; 129 | token_set_lexeme(result, lexeme); 130 | 131 | return result; 132 | } 133 | 134 | Token * token_string_literal(const char * const str, Lexeme lexeme) 135 | { 136 | Token *result = get_token(); 137 | 138 | result->type = TT_STRING; 139 | result->literal = str_dup(str); 140 | token_set_lexeme(result, lexeme); 141 | 142 | return result; 143 | } 144 | 145 | Token * token_identifier(const char *str, Lexeme lexeme) 146 | { 147 | Token *result = get_token(); 148 | 149 | result->type = TT_IDENTIFIER; 150 | result->literal = str_dup(str); 151 | token_set_lexeme(result, lexeme); 152 | 153 | return result; 154 | } 155 | 156 | Token * token_atomic(TokenType type, Lexeme lexeme) 157 | { 158 | assert(type != TT_STRING && type != TT_NUMBER); 159 | Token *result = get_token(); 160 | result->type = type; 161 | token_set_lexeme(result, lexeme); 162 | 163 | return result; 164 | } 165 | 166 | void token_free(Token *token) 167 | { 168 | #ifdef TOKEN_UNION 169 | if(token->type == TT_IDENTIFIER || token->type == TT_STRING) 170 | #else 171 | if(token->literal) 172 | #endif 173 | { 174 | assert(token->type == TT_IDENTIFIER || token->type == TT_STRING); 175 | str_free(token->literal); 176 | } 177 | lox_free(token); 178 | } 179 | 180 | // Returns the number of elements in the list `elements` 181 | int32_t token_count(Token *elements) 182 | { 183 | int32_t count = 0; 184 | while(elements != NULL) 185 | { 186 | count++; 187 | elements = elements->nextInScan; 188 | } 189 | return count; 190 | } 191 | 192 | // Returns the last token in a non-empty chain of tokens. 193 | Token * token_last(Token *expressions) 194 | { 195 | assert(expressions != NULL); 196 | 197 | Token *last = expressions; 198 | while(last->nextInScan) 199 | { 200 | last = last->nextInScan; 201 | } 202 | return last; 203 | } 204 | 205 | Token * token_appendTo(Token *head, Token *tail) 206 | { 207 | if (head == NULL) 208 | { 209 | return tail; 210 | } 211 | Token *last = token_last(head); 212 | last->nextInScan = tail; 213 | return head; 214 | } 215 | -------------------------------------------------------------------------------- /src/token.h: -------------------------------------------------------------------------------- 1 | // 2 | // token.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 16/10/2017. 6 | // 7 | 8 | #ifndef token_h 9 | #define token_h 10 | 11 | #include "string.h" 12 | 13 | /* Token types */ 14 | 15 | // NOTE: we use the preprocessor to generate all enum cases and an array with the corresponding name strings. 16 | #define TOKEN_LIST(token, key) \ 17 | /* Single-character tokens */ \ 18 | token(LEFT_PAREN, "(") token(RIGHT_PAREN, ")") token(LEFT_BRACE, "{") \ 19 | token(RIGHT_BRACE, "}") token(COMMA, ",") token(DOT, ".") \ 20 | token(MINUS, "-") token(PLUS, "+") token(SEMICOLON, ";") \ 21 | token(SLASH, "/") token(STAR, "*") \ 22 | /* One or two character tokens */ \ 23 | token(BANG, "!") token(BANG_EQUAL, "!=") \ 24 | token(EQUAL, "=") token(EQUAL_EQUAL, "==") \ 25 | token(GREATER, ">") token(GREATER_EQUAL, ">=") \ 26 | token(LESS, "<") token(LESS_EQUAL, "<=") \ 27 | /* Literals */ \ 28 | token(IDENTIFIER, NULL) token(STRING, NULL) token(NUMBER, NULL) \ 29 | /* Keywords */ \ 30 | key(AND, "and") key(CLASS, "class") key(ELSE, "else") \ 31 | key(FALSE, "false") key(FUN, "fun") key(FOR, "for") \ 32 | key(IF, "if") key(NIL, "nil") key(OR, "or") \ 33 | key(PRINT, "print") key(RETURN, "return") key(SUPER, "super") \ 34 | key(THIS, "this") key(TRUE, "true") key(VAR, "var") \ 35 | key(WHILE, "while") key(EOF, "eof") 36 | 37 | #define IGNORE(type, string) 38 | #define FOREACH_TOKEN(code) TOKEN_LIST(code, code) 39 | #define FOREACH_SYMBOL(code) TOKEN_LIST(code, IGNORE) 40 | #define FOREACH_KEYWORD(code) TOKEN_LIST(IGNORE, code) 41 | 42 | typedef enum TokenType 43 | { 44 | #define DEFINE_ENUM_TYPE(type, string) TT_##type, 45 | FOREACH_TOKEN(DEFINE_ENUM_TYPE) 46 | #undef DEFINE_ENUM_TYPE 47 | } TokenType; 48 | 49 | static const char * const tt_name_string[] = 50 | { 51 | #define DEFINE_TYPE_NAME(type, string) #type, 52 | FOREACH_TOKEN(DEFINE_TYPE_NAME) 53 | #undef DEFINE_TYPE_NAME 54 | }; 55 | 56 | static const char * const tt_symbol_string[] = 57 | { 58 | #define DEFINE_TYPE_STRING(type, string) string, 59 | FOREACH_TOKEN(DEFINE_TYPE_STRING) 60 | #undef DEFINE_TYPE_STRING 61 | }; 62 | 63 | /* Keywords */ 64 | 65 | typedef struct KeywordEntry { 66 | char *keyword; 67 | TokenType type; 68 | } KeywordEntry; 69 | 70 | static KeywordEntry keywords[] = { 71 | #define DEFINE_KEYWORD_ENTRY(name, string) \ 72 | {string, TT_##name}, 73 | FOREACH_KEYWORD(DEFINE_KEYWORD_ENTRY) 74 | #undef DEFINE_KEYWORD_ENTRY 75 | // NOTE: The following marks the end of the array and must come last. 76 | {"", 0} 77 | }; 78 | 79 | typedef union 80 | { 81 | struct 82 | { 83 | str_size start; // start index of lexeme in source 84 | str_size count; // number of characters in the lexeme 85 | int32_t line; // source line where the lexeme appears 86 | }; 87 | struct 88 | { 89 | SubstringIndex index; 90 | int32_t line_; 91 | }; 92 | } Lexeme; 93 | 94 | /* Tokens */ 95 | 96 | typedef struct Token_tag 97 | { 98 | double number; 99 | char *literal; 100 | 101 | // Note: used by the scanner to link the tokens as they appear in the source code in a list 102 | struct Token_tag *nextInScan; 103 | 104 | TokenType type; 105 | Lexeme lexeme; 106 | } Token; 107 | 108 | void token_free(Token *token); 109 | Token * token_atomic(TokenType type, Lexeme lexeme); 110 | Token * token_identifier(const char *str, Lexeme lexeme); 111 | Token * token_string_literal(const char * const str, Lexeme lexeme); 112 | Token * token_number_literal(double value, Lexeme lexeme); 113 | char * token_to_string(const Token * const token, const char * const source); 114 | char * string_from_token_literal(const Token *token); 115 | 116 | int32_t token_count(Token *elements); 117 | Token * token_last(Token *expressions); 118 | Token * token_appendTo(Token *head, Token *tail); 119 | 120 | KeywordEntry * lookup_keyword(const char *keyword); 121 | 122 | inline bool keyword_is_valid(KeywordEntry *entry) 123 | { 124 | assert(entry != NULL); 125 | return entry->keyword[0] != '\0'; 126 | } 127 | 128 | inline double get_number_value(const Token *token) 129 | { 130 | assert(token->type == TT_NUMBER); 131 | return token->number; 132 | } 133 | 134 | inline const char * get_string_value(const Token *token) 135 | { 136 | assert(token->type == TT_STRING); 137 | return token->literal; 138 | } 139 | 140 | inline const char * get_identifier_name(const Token *token) 141 | { 142 | assert(token->type == TT_IDENTIFIER); 143 | return token->literal; 144 | } 145 | 146 | #endif /* token_h */ 147 | -------------------------------------------------------------------------------- /src/utility.c: -------------------------------------------------------------------------------- 1 | // 2 | // utility.c 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 23/10/2017. 6 | // 7 | 8 | #include "utility.h" 9 | #include "common.h" 10 | #include "string.h" 11 | 12 | #include 13 | #include 14 | 15 | /* File I/O */ 16 | 17 | char * readFile(const char *filename) 18 | { 19 | char *buffer = NULL; 20 | 21 | FILE *fp = fopen(filename, "r"); 22 | if(fp != NULL) 23 | { 24 | fseek(fp, 0L, SEEK_END); 25 | size_t size = ftell(fp); 26 | if(size > UINT32_MAX) 27 | { 28 | fprintf(stderr, "File %s too large (2GB max).\n", filename); 29 | } 30 | else if (size == 0) 31 | { 32 | buffer = str_alloc((uint32_t)size); 33 | } 34 | else 35 | { 36 | buffer = str_alloc((uint32_t)size); 37 | rewind(fp); 38 | 39 | size_t result = fread(buffer, size, 1, fp); 40 | buffer[size] = '\0'; 41 | str_setLength(buffer); 42 | 43 | if(result != 1) 44 | { 45 | fprintf(stderr, "Read file %s failed.\n", filename); 46 | str_free(buffer); 47 | buffer = NULL; 48 | } 49 | } 50 | fclose(fp); 51 | } 52 | else 53 | { 54 | fprintf(stderr, "Could not open file %s: %s\n", filename, strerror(errno)); 55 | } 56 | 57 | return buffer; 58 | } 59 | 60 | 61 | /* Char utilities */ 62 | 63 | extern inline bool is_alpha(char c); 64 | extern inline bool is_digit(char c); 65 | extern inline bool is_alphanumeric(char c); 66 | 67 | 68 | /* Time utils */ 69 | 70 | #include 71 | #include 72 | 73 | extern inline double timer_elapsedSec(Timer *timer); 74 | 75 | Timer timer_init() 76 | { 77 | Timer timer; 78 | 79 | #ifdef USE_MACH_TIME 80 | // NOTE: Retrieve conversion factors for performance timers 81 | kern_return_t timebaseError = mach_timebase_info(&timer.timebaseInfo); 82 | if (timebaseError != KERN_SUCCESS) 83 | { 84 | fprintf(stderr, "Could not retrieve mach timebase from the system."); 85 | assert(false); 86 | } 87 | timer.absoluteStartTime = mach_absolute_time(); 88 | #else 89 | timer.start = clock(); 90 | #endif 91 | 92 | return timer; 93 | } 94 | 95 | void timer_reset(Timer *timer) 96 | { 97 | #ifdef USE_MACH_TIME 98 | timer->absoluteStartTime = mach_absolute_time(); 99 | #else 100 | timer->start = clock(); 101 | #endif 102 | } 103 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // utility.h 3 | // loxi - a Lox interpreter 4 | // 5 | // Created by Marco Caldarelli on 23/10/2017. 6 | // 7 | 8 | #ifndef utility_h 9 | #define utility_h 10 | 11 | #include "common.h" 12 | #include 13 | 14 | /* File I/O */ 15 | 16 | char * readFile(const char *filename); 17 | 18 | 19 | /* Char utils */ 20 | 21 | inline bool is_alpha(char c) 22 | { 23 | return ((c >= 'a' && c <= 'z') || 24 | (c >= 'A' && c <= 'Z') || 25 | c == '_'); 26 | } 27 | 28 | inline bool is_digit(char c) 29 | { 30 | return (c >= '0' && c <= '9'); 31 | } 32 | 33 | inline bool is_alphanumeric(char c) 34 | { 35 | return (is_alpha(c) || is_digit(c)); 36 | } 37 | 38 | 39 | /* Time utils */ 40 | 41 | #ifdef USE_MACH_TIME 42 | #include 43 | #define NANOSEC_PER_SEC 1000000000 44 | #else 45 | #include 46 | #endif 47 | 48 | 49 | typedef struct Timer 50 | { 51 | #ifdef USE_MACH_TIME 52 | mach_timebase_info_data_t timebaseInfo; 53 | uint64_t absoluteStartTime; 54 | #else 55 | clock_t start; 56 | #endif 57 | } Timer; 58 | 59 | Timer timer_init(void); 60 | void timer_reset(Timer *timer); 61 | 62 | inline double timer_elapsedSec(Timer *timer) 63 | { 64 | double elapsedSec; 65 | 66 | #ifdef USE_MACH_TIME 67 | uint64_t absoluteTime = mach_absolute_time(); 68 | uint64_t elapsedNanoSec = (absoluteTime - timer->absoluteStartTime) * timer->timebaseInfo.numer / timer->timebaseInfo.denom; 69 | elapsedSec = (double)elapsedNanoSec / NANOSEC_PER_SEC; 70 | #else 71 | clock_t time_now = clock(); 72 | elapsedSec = difftime(time_now, timer->start) / CLOCKS_PER_SEC; 73 | #endif 74 | 75 | return elapsedSec; 76 | } 77 | 78 | #endif /* utility_h */ 79 | --------------------------------------------------------------------------------