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