1 //
2 //  main.cpp
3 //  GravityCpp
4 //
5 //  Created by Marco Bambini on 26/12/2018.
6 //  Copyright © 2018 Creolabs. All rights reserved.
7 //
8 
9 #include "gravity_compiler.h"
10 #include "gravity_core.h"
11 #include "gravity_vm.h"
12 #include "gravity_macros.h"
13 #include "gravity_vmmacros.h"
14 #include "gravity_opcodes.h"
15 #include <iostream>
16 using namespace std;
17 
18 // MARK: - C++ code -
19 
20 class Rectangle {
21 public:
22     double length;
23     double height;
24 
25     // Constructor
Rectangle(double l=2.0,double h=2.0)26     Rectangle(double l = 2.0, double h = 2.0) {
27         cout << "Rectangle constructor called." << endl;
28         length = l;
29         height = h;
30     }
31 
~Rectangle()32     virtual ~Rectangle() {
33         cout << "Rectangle destructor called." << endl;
34     }
35 
36     // Methods
Area()37     double Area() {
38         return length * height;
39     }
40 
Test(double p1,int32_t p2,string p3)41     void Test(double p1, int32_t p2, string p3) {
42         cout << "Rectangle test: " << p1 << p2 << p3 << endl;
43     }
44 };
45 
46 // MARK: - Gravity Bridge -
47 
rect_create(gravity_vm * vm,gravity_value_t * args,uint16_t nargs,uint32_t rindex)48 static bool rect_create (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
49     // check for optional parameters here (if you need to process a more complex constructor)
50 
51     // self parameter is the rect_class create in register_cpp_classes
52     gravity_class_t *c = (gravity_class_t *)GET_VALUE(0).p;
53 
54     // create Gravity instance and set its class to c
55     gravity_instance_t *instance = gravity_instance_new(vm, c);
56 
57     // allocate a cpp instance of the Rectangle class on the heap
58     Rectangle *r = new Rectangle();
59 
60     // set cpp instance and xdata of the gravity instance (for later used in the rect_area and rect_test functions)
61     gravity_instance_setxdata(instance, r);
62 
63     // return instance
64     RETURN_VALUE(VALUE_FROM_OBJECT(instance), rindex);
65 }
66 
rect_area(gravity_vm * vm,gravity_value_t * args,uint16_t nargs,uint32_t rindex)67 static bool rect_area (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
68     // get self object which is the instance created in rect_create function
69     gravity_instance_t *instance = (gravity_instance_t *)GET_VALUE(0).p;
70 
71     // get xdata (which is a cpp Rectangle instance)
72     Rectangle *r = (Rectangle *)instance->xdata;
73 
74     // invoke the Area method
75     double d = r->Area();
76 
77     RETURN_VALUE(VALUE_FROM_FLOAT(d), rindex);
78 }
79 
rect_test(gravity_vm * vm,gravity_value_t * args,uint16_t nargs,uint32_t rindex)80 static bool rect_test (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
81     // get self object which is the instance created in rect_create function
82     gravity_instance_t *instance = (gravity_instance_t *)GET_VALUE(0).p;
83 
84     // get xdata (which is a cpp Rectangle instance)
85     Rectangle *r = (Rectangle *)instance->xdata;
86 
87     // invoke the Test method with dummy parameters
88     // the right way to proceed here would be to check for nargs first
89     // and then check if each parameter is of the right type
90     // and then bind parameters to the cpp call (using the std::bind method)
91     r->Test(3.0, 89, "rect_test");
92 
93     RETURN_NOVALUE();
94 }
95 
96 // MARK: -
97 
length_get(gravity_vm * vm,gravity_value_t * args,uint16_t nargs,uint32_t rindex)98 static bool length_get (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
99     // get self object which is the instance created in rect_create function
100     gravity_instance_t *instance = (gravity_instance_t *)GET_VALUE(0).p;
101 
102     // get xdata (which is a cpp Rectangle instance)
103     Rectangle *r = (Rectangle *)instance->xdata;
104 
105     RETURN_VALUE(VALUE_FROM_FLOAT(r->length), rindex);
106 }
107 
length_set(gravity_vm * vm,gravity_value_t * args,uint16_t nargs,uint32_t rindex)108 static bool length_set (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
109     // get self object which is the instance created in rect_create function
110     gravity_instance_t *instance = (gravity_instance_t *)GET_VALUE(0).p;
111 
112     // get xdata (which is a cpp Rectangle instance)
113     Rectangle *r = (Rectangle *)instance->xdata;
114 
115     // read user value
116     gravity_value_t value = GET_VALUE(1);
117 
118     // decode value
119     double d = 0.0f;
120     if (VALUE_ISA_FLOAT(value)) d = VALUE_AS_FLOAT(value);
121     else if (VALUE_ISA_INT(value)) d = double(VALUE_AS_INT(value));
122     // more cases here, for example VALUE_ISA_STRING
123 
124     r->length = d;
125 
126     RETURN_NOVALUE();
127 }
128 
129 // MARK: -
130 
object_free(gravity_vm * vm,gravity_object_t * obj)131 static void object_free (gravity_vm *vm, gravity_object_t *obj) {
132     gravity_instance_t *instance = (gravity_instance_t *)obj;
133 
134     // get xdata (which is a cpp Rectangle instance)
135     Rectangle *r = (Rectangle *)instance->xdata;
136 
137     // explicitly free memory
138     delete r;
139 }
140 
register_cpp_classes(gravity_vm * vm)141 void register_cpp_classes (gravity_vm *vm) {
142     // create Rectangle class
143     gravity_class_t *rect_class = gravity_class_new_pair(vm, "Rectangle", NULL, 0, 0);
144     gravity_class_t *rect_class_meta = gravity_class_get_meta(rect_class);
145 
146     gravity_class_bind(rect_class_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(rect_create));
147     gravity_class_bind(rect_class, "area", NEW_CLOSURE_VALUE(rect_area));
148     gravity_class_bind(rect_class, "test", NEW_CLOSURE_VALUE(rect_test));
149     gravity_class_bind(rect_class, "length", VALUE_FROM_OBJECT(computed_property_create(NULL, NEW_FUNCTION(length_get), NEW_FUNCTION(length_set))));
150 
151     // register Rectangle class inside VM
152     gravity_vm_setvalue(vm, "Rectangle", VALUE_FROM_OBJECT(rect_class));
153 }
154 
155 // MARK: - Main -
156 
report_error(gravity_vm * vm,error_type_t error_type,const char * message,error_desc_t error_desc,void * xdata)157 void report_error (gravity_vm *vm, error_type_t error_type, const char *message, error_desc_t error_desc, void *xdata) {
158     #pragma unused(vm, xdata)
159 
160     const char *type = "N/A";
161     switch (error_type) {
162         case GRAVITY_ERROR_NONE: type = "NONE"; break;
163         case GRAVITY_ERROR_SYNTAX: type = "SYNTAX"; break;
164         case GRAVITY_ERROR_SEMANTIC: type = "SEMANTIC"; break;
165         case GRAVITY_ERROR_RUNTIME: type = "RUNTIME"; break;
166         case GRAVITY_WARNING: type = "WARNING"; break;
167         case GRAVITY_ERROR_IO: type = "I/O"; break;
168     }
169 
170     if (error_type == GRAVITY_ERROR_RUNTIME) printf("RUNTIME ERROR: ");
171     else printf("%s ERROR on %d (%d,%d): ", type, error_desc.fileid, error_desc.lineno, error_desc.colno);
172     printf("%s\n", message);
173 }
174 
main(int argc,const char * argv[])175 int main(int argc, const char * argv[]) {
176     cout << "Gravity version " << GRAVITY_VERSION << endl << endl;
177 
178     const char source_code[] =  "extern var Rectangle;"
179                                 "extern var Triangle;"
180                                 "func main() {"
181                                 "var r = Rectangle();"
182                                 "System.print(r.area());"
183                                 "r.test(1.0, 32, \"Hello\");"
184                                 "return 1;"
185                                 "}";
186 
187     // setup delegate
188     gravity_delegate_t delegate = {
189         .error_callback = report_error,
190         .bridge_free = object_free
191     };
192 
193     // setup compiler
194     gravity_compiler_t *compiler = gravity_compiler_create(&delegate);
195 
196     // compile source code
197     gravity_closure_t *closure = gravity_compiler_run(compiler, source_code, strlen(source_code), 0, true, true);
198     if (!closure) return -1; // syntax/semantic error
199 
200     // setup Gravity VM
201     gravity_vm *vm = gravity_vm_new(&delegate);
202 
203     // transfer memory from compiler to VM
204     gravity_compiler_transfer(compiler, vm);
205 
206     // cleanup compiler
207     gravity_compiler_free(compiler);
208 
209     // register my C++ classes inside Gravity VM
210     register_cpp_classes(vm);
211 
212     // run main func
213     if (gravity_vm_runmain(vm, closure)) {
214         // print result to stdout
215         gravity_value_t result = gravity_vm_result(vm);
216         gravity_value_dump(vm, result, NULL, 0);
217     }
218 
219     // cleanup
220     gravity_vm_free(vm);
221     gravity_core_free();
222 
223     return 0;
224 }
225