1 // Copyright 2014 Wouter van Oortmerssen. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "lobster/stdafx.h"
16
17 #include "lobster/compiler.h"
18 #include "lobster/disasm.h"
19 #include "lobster/tonative.h"
20
21 #if LOBSTER_ENGINE
22 #include "lobster/engine.h"
23 #endif
24
25 #include "lobster/unicode.h"
26
27 #if LOBSTER_ENGINE
28 // FIXME: This makes SDL not modular, but without it it will miss the SDLMain indirection.
29 #include "lobster/sdlincludes.h"
30 #include "lobster/sdlinterface.h"
31 #endif
32
33 using namespace lobster;
34
unit_test_all(bool full)35 void unit_test_all(bool full) {
36 // We don't really have unit tests, but let's collect some that always
37 // run in debug mode:
38 #ifdef NDEBUG
39 return;
40 #endif
41 unit_test_tools();
42 unit_test_unicode();
43 unit_test_wasm(full);
44 }
45
main(int argc,char * argv[])46 int main(int argc, char* argv[]) {
47 #ifdef _WIN32
48 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
49 InitUnhandledExceptionFilter(argc, argv);
50 #endif
51 LOG_INFO("Lobster running...");
52 bool wait = false;
53 bool from_bundle =
54 #ifdef __IOS__
55 true;
56 #else
57 false;
58 #endif
59 #ifdef USE_EXCEPTION_HANDLING
60 try
61 #endif
62 {
63 bool parsedump = false;
64 bool disasm = false;
65 bool to_cpp = false;
66 bool to_wasm = false;
67 bool dump_builtins = false;
68 bool dump_names = false;
69 bool compile_only = false;
70 bool compile_bench = false;
71 bool full_unit_test = false;
72 int runtime_checks = RUNTIME_ASSERT;
73 const char *default_lpak = "default.lpak";
74 const char *lpak = nullptr;
75 const char *fn = nullptr;
76 vector<string> program_args;
77 auto trace = TraceMode::OFF;
78 string helptext = "\nUsage:\n"
79 "lobster [ OPTIONS ] [ FILE ] [ -- ARGS ]\n"
80 "Compile & run FILE, or omit FILE to load default.lpak\n"
81 "--pak Compile to pakfile, don't run.\n"
82 "--cpp Compile to C++ code, don't run.\n"
83 "--wasm Compile to WASM code, don't run.\n"
84 "--parsedump Also dump parse tree.\n"
85 "--disasm Also dump bytecode disassembly.\n"
86 "--verbose Output additional informational text.\n"
87 "--debug Output compiler internal logging.\n"
88 "--silent Only output errors.\n"
89 "--runtime-shipping Compile with asserts off.\n"
90 "--runtime-asserts Compile with asserts on (default).\n"
91 "--runtime-verbose Compile with asserts on + additional debug.\n"
92 "--noconsole Close console window (Windows).\n"
93 "--gen-builtins-html Write builtin commands help file.\n"
94 "--gen-builtins-names Write builtin commands - just names.\n"
95 #if LOBSTER_ENGINE
96 "--non-interactive-test Quit after running 1 frame.\n"
97 #endif
98 "--trace Log bytecode instructions (SLOW).\n"
99 "--trace-tail Show last 50 bytecode instructions on error.\n"
100 "--wait Wait for input before exiting.\n";
101 int arg = 1;
102 for (; arg < argc; arg++) {
103 if (argv[arg][0] == '-') {
104 string a = argv[arg];
105 if (a == "--wait") { wait = true; }
106 else if (a == "--pak") { lpak = default_lpak; }
107 else if (a == "--cpp") { to_cpp = true; }
108 else if (a == "--wasm") { to_wasm = true; }
109 else if (a == "--parsedump") { parsedump = true; }
110 else if (a == "--disasm") { disasm = true; }
111 else if (a == "--verbose") { min_output_level = OUTPUT_INFO; }
112 else if (a == "--debug") { min_output_level = OUTPUT_DEBUG; }
113 else if (a == "--silent") { min_output_level = OUTPUT_ERROR; }
114 else if (a == "--runtime-shipping") { runtime_checks = RUNTIME_NO_ASSERT; }
115 else if (a == "--runtime-asserts") { runtime_checks = RUNTIME_ASSERT; }
116 else if (a == "--runtime-verbose") { runtime_checks = RUNTIME_ASSERT_PLUS; }
117 else if (a == "--noconsole") { SetConsole(false); }
118 else if (a == "--gen-builtins-html") { dump_builtins = true; }
119 else if (a == "--gen-builtins-names") { dump_names = true; }
120 else if (a == "--compile-only") { compile_only = true; }
121 else if (a == "--compile-bench") { compile_bench = true; }
122 #if LOBSTER_ENGINE
123 else if (a == "--non-interactive-test") { SDLTestMode(); }
124 #endif
125 else if (a == "--trace") { trace = TraceMode::ON; }
126 else if (a == "--trace-tail") { trace = TraceMode::TAIL; }
127 else if (a == "--full-unit-test") { full_unit_test = true; }
128 else if (a == "--") { arg++; break; }
129 // process identifier supplied by OS X
130 else if (a.substr(0, 5) == "-psn_") { from_bundle = true; }
131 else THROW_OR_ABORT("unknown command line argument: " + (argv[arg] + helptext));
132 } else {
133 if (fn) THROW_OR_ABORT("more than one file specified" + helptext);
134 fn = argv[arg];
135 }
136 }
137 for (; arg < argc; arg++) { program_args.push_back(argv[arg]); }
138
139 unit_test_all(full_unit_test);
140
141 #ifdef __IOS__
142 //fn = "totslike.lobster"; // FIXME: temp solution
143 #endif
144
145 if (!InitPlatform(GetMainDirFromExePath(argv[0]), fn ? fn : default_lpak, from_bundle,
146 #if LOBSTER_ENGINE
147 SDLLoadFile
148 #else
149 DefaultLoadFile
150 #endif
151 ))
152 THROW_OR_ABORT(string("cannot find location to read/write data on this platform!"));
153
154 NativeRegistry nfr;
155 #if LOBSTER_ENGINE
156 RegisterCoreEngineBuiltins(nfr);
157 #else
158 RegisterCoreLanguageBuiltins(nfr);
159 #endif
160
161 if (fn) fn = StripDirPart(fn);
162
163 auto vmargs = VMArgs { nfr, fn ? fn : "" };
164 vmargs.program_args = std::move(program_args);
165 vmargs.trace = trace;
166
167 if (!fn) {
168 if (!LoadPakDir(default_lpak))
169 THROW_OR_ABORT("Lobster programming language compiler/runtime (version " __DATE__
170 ")\nno arguments given - cannot load " + (default_lpak + helptext));
171 // This will now come from the pakfile.
172 if (!LoadByteCode(vmargs.bytecode_buffer))
173 THROW_OR_ABORT(string("Cannot load bytecode from pakfile!"));
174 } else {
175 LOG_INFO("compiling...");
176 string dump;
177 string pakfile;
178 auto start_time = SecondsSinceStart();
179 size_t bench_iters = 1000;
180 for (size_t i = 0; i < (compile_bench ? bench_iters : 1); i++) {
181 dump.clear();
182 pakfile.clear();
183 vmargs.bytecode_buffer.clear();
184 Compile(nfr, StripDirPart(fn), {}, vmargs.bytecode_buffer,
185 parsedump ? &dump : nullptr, lpak ? &pakfile : nullptr, dump_builtins,
186 dump_names, false, runtime_checks);
187 }
188 if (compile_bench) {
189 auto compile_time = (SecondsSinceStart() - start_time);
190 LOG_PROGRAM("time to compile ", bench_iters, "x (seconds): ",
191 compile_time);
192 }
193 if (parsedump) {
194 WriteFile("parsedump.txt", false, dump);
195 }
196 if (lpak) {
197 WriteFile(lpak, true, pakfile);
198 return 0;
199 }
200 }
201 if (disasm) {
202 ostringstream ss;
203 DisAsm(nfr, ss, vmargs.bytecode_buffer);
204 WriteFile("disasm.txt", false, ss.str());
205 }
206 if (to_cpp) {
207 ostringstream ss;
208 auto err = ToCPP(nfr, ss, vmargs.bytecode_buffer);
209 if (!err.empty()) THROW_OR_ABORT(err);
210 // FIXME: make less hard-coded.
211 auto out = "dev/compiled_lobster/src/compiled_lobster.cpp";
212 FILE *f = fopen((MainDir() + out).c_str(), "w");
213 if (f) {
214 fputs(ss.str().c_str(), f);
215 fclose(f);
216 } else {
217 THROW_OR_ABORT(cat("cannot write: ", out));
218 }
219 } else if (to_wasm) {
220 vector<uint8_t> buf;
221 auto err = ToWASM(nfr, buf, vmargs.bytecode_buffer);
222 if (!err.empty()) THROW_OR_ABORT(err);
223 // FIXME: make less hard-coded.
224 auto out = "dev/emscripten/compiled_lobster_wasm.o";
225 FILE *f = fopen((MainDir() + out).c_str(), "wb");
226 if (f) {
227 fwrite(buf.data(), buf.size(), 1, f);
228 fclose(f);
229 }
230 else {
231 THROW_OR_ABORT(cat("cannot write: ", out));
232 }
233 } else if (!compile_only) {
234 #if LOBSTER_ENGINE
235 EngineRunByteCode(std::move(vmargs));
236 #else
237 VM vm(std::move(vmargs));
238 vm.EvalProgram();
239 #endif
240 }
241 }
242 #ifdef USE_EXCEPTION_HANDLING
243 catch (string &s) {
244 LOG_ERROR(s);
245 #if LOBSTER_ENGINE
246 if (from_bundle) SDLMessageBox("Lobster", s.c_str());
247 #endif
248 if (wait) {
249 LOG_PROGRAM("press <ENTER> to continue:\n");
250 getchar();
251 }
252 #ifdef _WIN32
253 _CrtSetDbgFlag(0); // Don't bother with memory leaks when there was an error.
254 #endif
255 #if LOBSTER_ENGINE
256 EngineExit(1);
257 #endif
258 }
259 #endif
260 #if LOBSTER_ENGINE
261 EngineExit(0);
262 #endif
263 return 0;
264 }
265
266