Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/compiler/gravity_compiler.c
1214 views
1
//
2
// gravity_compiler.c
3
// gravity
4
//
5
// Created by Marco Bambini on 29/08/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
#include "gravity_compiler.h"
10
#include "gravity_parser.h"
11
#include "gravity_token.h"
12
#include "gravity_utils.h"
13
#include "gravity_semacheck1.h"
14
#include "gravity_semacheck2.h"
15
#include "gravity_optimizer.h"
16
#include "gravity_codegen.h"
17
#include "gravity_array.h"
18
#include "gravity_hash.h"
19
#include "gravity_core.h"
20
21
struct gravity_compiler_t {
22
gravity_parser_t *parser;
23
gravity_delegate_t *delegate;
24
cstring_r *storage;
25
gravity_vm *vm;
26
gnode_t *ast;
27
void_r *objects;
28
};
29
30
static void internal_vm_transfer (gravity_vm *vm, gravity_object_t *obj) {
31
gravity_compiler_t *compiler = (gravity_compiler_t *)gravity_vm_getdata(vm);
32
marray_push(void*, *compiler->objects, obj);
33
}
34
35
static void internal_free_class (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
36
#pragma unused (hashtable, data)
37
38
// sanity checks
39
if (!VALUE_ISA_FUNCTION(value)) return;
40
if (!VALUE_ISA_STRING(key)) return;
41
42
// check for special function
43
gravity_function_t *f = VALUE_AS_FUNCTION(value);
44
if (f->tag == EXEC_TYPE_SPECIAL) {
45
if (f->special[0]) gravity_function_free(NULL, (gravity_function_t *)f->special[0]);
46
if (f->special[1]) gravity_function_free(NULL, (gravity_function_t *)f->special[1]);
47
}
48
49
// a super special init constructor is a string that begins with $init AND it is longer than strlen($init)
50
gravity_string_t *s = VALUE_AS_STRING(key);
51
bool is_super_function = ((s->len > 5) && (string_casencmp(s->s, CLASS_INTERNAL_INIT_NAME, 5) == 0));
52
if (!is_super_function) gravity_function_free(NULL, VALUE_AS_FUNCTION(value));
53
}
54
55
static void internal_vm_cleanup (gravity_vm *vm) {
56
gravity_compiler_t *compiler = (gravity_compiler_t *)gravity_vm_getdata(vm);
57
size_t count = marray_size(*compiler->objects);
58
for (size_t i=0; i<count; ++i) {
59
gravity_object_t *obj = marray_pop(*compiler->objects);
60
if (OBJECT_ISA_CLASS(obj)) {
61
gravity_class_t *c = (gravity_class_t *)obj;
62
gravity_hash_iterate(c->htable, internal_free_class, NULL);
63
}
64
gravity_object_free(vm, obj);
65
}
66
}
67
68
// MARK: -
69
70
gravity_compiler_t *gravity_compiler_create (gravity_delegate_t *delegate) {
71
gravity_compiler_t *compiler = mem_alloc(sizeof(gravity_compiler_t));
72
if (!compiler) return NULL;
73
74
compiler->ast = NULL;
75
compiler->objects = void_array_create();
76
compiler->delegate = delegate;
77
return compiler;
78
}
79
80
static void gravity_compiler_reset (gravity_compiler_t *compiler, bool free_core) {
81
// free memory for array of strings storage
82
if (compiler->storage) {
83
cstring_array_each(compiler->storage, {mem_free((void *)val);});
84
gnode_array_free(compiler->storage);
85
}
86
87
// first ast then parser, don't change the release order
88
if (compiler->ast) gnode_free(compiler->ast);
89
if (compiler->parser) gravity_parser_free(compiler->parser);
90
91
// at the end free mini VM and objects array
92
if (compiler->vm) gravity_vm_free(compiler->vm);
93
if (compiler->objects) {
94
marray_destroy(*compiler->objects);
95
mem_free((void*)compiler->objects);
96
}
97
98
// feel free to free core if someone requires it
99
if (free_core) gravity_core_free();
100
101
// reset internal pointers
102
compiler->vm = NULL;
103
compiler->ast = NULL;
104
compiler->parser = NULL;
105
compiler->objects = NULL;
106
compiler->storage = NULL;
107
}
108
109
void gravity_compiler_free (gravity_compiler_t *compiler) {
110
gravity_compiler_reset(compiler, true);
111
mem_free(compiler);
112
}
113
114
gnode_t *gravity_compiler_ast (gravity_compiler_t *compiler) {
115
return compiler->ast;
116
}
117
118
void gravity_compiler_transfer(gravity_compiler_t *compiler, gravity_vm *vm) {
119
if (!compiler->objects) return;
120
121
// transfer each object from compiler mini VM to exec VM
122
gravity_gc_setenabled(vm, false);
123
size_t count = marray_size(*compiler->objects);
124
for (size_t i=0; i<count; ++i) {
125
gravity_object_t *obj = marray_pop(*compiler->objects);
126
gravity_vm_transfer(vm, obj);
127
if (!OBJECT_ISA_CLOSURE(obj)) continue;
128
129
// $moduleinit closure needs to be explicitly initialized
130
gravity_closure_t *closure = (gravity_closure_t *)obj;
131
if ((closure->f->identifier) && strcmp(closure->f->identifier, INITMODULE_NAME) == 0) {
132
// code is here because it does not make sense to add this overhead (that needs to be executed only once)
133
// inside the gravity_vm_transfer callback which is called for each allocated object inside the VM
134
gravity_vm_initmodule(vm, closure->f);
135
}
136
}
137
138
gravity_gc_setenabled(vm, true);
139
}
140
141
// MARK: -
142
143
gravity_closure_t *gravity_compiler_run (gravity_compiler_t *compiler, const char *source, size_t len, uint32_t fileid, bool is_static) {
144
if ((source == NULL) || (len == 0)) return NULL;
145
146
// CHECK cleanup first
147
if (compiler->ast) gnode_free(compiler->ast);
148
if (!compiler->objects) compiler->objects = void_array_create();
149
150
// CODEGEN requires a mini vm in order to be able to handle garbage collector
151
compiler->vm = gravity_vm_newmini();
152
gravity_vm_setdata(compiler->vm, (void *)compiler);
153
gravity_vm_set_callbacks(compiler->vm, internal_vm_transfer, internal_vm_cleanup);
154
gravity_core_register(compiler->vm);
155
156
// STEP 0: CREATE PARSER
157
compiler->parser = gravity_parser_create(source, len, fileid, is_static);
158
if (!compiler->parser) return NULL;
159
160
// STEP 1: SYNTAX CHECK
161
compiler->ast = gravity_parser_run(compiler->parser, compiler->delegate);
162
if (!compiler->ast) goto abort_compilation;
163
gravity_parser_free(compiler->parser);
164
compiler->parser = NULL;
165
166
// STEP 2a: SEMANTIC CHECK (NON-LOCAL DECLARATIONS)
167
bool b1 = gravity_semacheck1(compiler->ast, compiler->delegate);
168
if (!b1) goto abort_compilation;
169
170
// STEP 2b: SEMANTIC CHECK (LOCAL DECLARATIONS)
171
bool b2 = gravity_semacheck2(compiler->ast, compiler->delegate);
172
if (!b2) goto abort_compilation;
173
174
// STEP 3: INTERMEDIATE CODE GENERATION (stack based VM)
175
gravity_function_t *f = gravity_codegen(compiler->ast, compiler->delegate, compiler->vm);
176
if (!f) goto abort_compilation;
177
178
// STEP 4: CODE GENERATION (register based VM)
179
f = gravity_optimizer(f);
180
if (f) return gravity_closure_new(compiler->vm, f);
181
182
abort_compilation:
183
gravity_compiler_reset(compiler, false);
184
return NULL;
185
}
186
187
json_t *gravity_compiler_serialize (gravity_compiler_t *compiler, gravity_closure_t *closure) {
188
#pragma unused(compiler)
189
190
json_t *json = json_new();
191
json_begin_object(json, NULL);
192
193
gravity_function_serialize(closure->f, json);
194
195
json_end_object(json);
196
return json;
197
}
198
199
bool gravity_compiler_serialize_infile (gravity_compiler_t *compiler, gravity_closure_t *closure, const char *path) {
200
if (!closure) return false;
201
json_t *json = gravity_compiler_serialize(compiler, closure);
202
if (!json) return false;
203
json_write_file(json, path);
204
json_free(json);
205
return true;
206
}
207
208