]> git.lizzy.rs Git - nothing.git/blob - src/game/level/script.c
Merge pull request #1046 from tsoding/704
[nothing.git] / src / game / level / script.c
1 #include "system/stacktrace.h"
2 #include "ebisp/gc.h"
3 #include "ebisp/interpreter.h"
4 #include "ebisp/parser.h"
5 #include "ebisp/scope.h"
6 #include "ebisp/std.h"
7 #include "game/level.h"
8 #include "script.h"
9 #include "system/str.h"
10 #include "system/line_stream.h"
11 #include "system/log.h"
12 #include "system/log_script.h"
13 #include "system/lt.h"
14 #include "system/nth_alloc.h"
15 #include "ui/console.h"
16 #include "broadcast.h"
17
18 struct Script
19 {
20     Lt *lt;
21     Gc *gc;
22     struct Scope scope;
23 };
24
25 static Script *create_script(Broadcast *broadcast, const char *source_code)
26 {
27     trace_assert(source_code);
28
29     Lt *lt = create_lt();
30
31     Script *script = PUSH_LT(lt, nth_calloc(1, sizeof(Script)), free);
32     if (script == NULL) {
33         RETURN_LT(lt, NULL);
34     }
35     script->lt = lt;
36
37     script->gc = PUSH_LT(lt, create_gc(), destroy_gc);
38     if (script->gc == NULL) {
39         RETURN_LT(lt, NULL);
40     }
41
42     script->scope = create_scope(script->gc);
43     load_std_library(script->gc, &script->scope);
44     load_log_library(script->gc, &script->scope);
45     broadcast_load_library(broadcast, script->gc, &script->scope);
46
47     struct ParseResult parse_result =
48         read_all_exprs_from_string(
49             script->gc,
50             source_code);
51     if (parse_result.is_error) {
52         log_fail("Parsing error: %s\n", parse_result.error_message);
53         RETURN_LT(lt, NULL);
54     }
55
56     struct EvalResult eval_result = eval(
57         script->gc,
58         &script->scope,
59         CONS(script->gc,
60              SYMBOL(script->gc, "begin"),
61              parse_result.expr));
62     if (eval_result.is_error) {
63         print_expr_as_sexpr(stderr, eval_result.expr);
64         log_fail("\n");
65         RETURN_LT(lt, NULL);
66     }
67
68     gc_collect(script->gc, script->scope.expr);
69
70     return script;
71 }
72
73 Script *create_script_from_string(Broadcast *broadcast, const char *source_code)
74 {
75     return create_script(broadcast, string_duplicate(source_code, NULL));
76 }
77
78 Script *create_script_from_line_stream(LineStream *line_stream, Broadcast *broadcast)
79 {
80     trace_assert(line_stream);
81
82     const char *source_code = line_stream_collect_until_end(line_stream);
83     if (source_code == NULL) {
84         return NULL;
85     }
86
87     return create_script(broadcast, source_code);
88 }
89
90 void destroy_script(Script *script)
91 {
92     trace_assert(script);
93     RETURN_LT0(script->lt);
94 }
95
96 int script_eval(Script *script, struct Expr expr)
97 {
98     trace_assert(script);
99
100     struct EvalResult eval_result = eval(
101         script->gc,
102         &script->scope,
103         expr);
104     if (eval_result.is_error) {
105         log_fail("Evaluation error: ");
106         /* TODO(#521): Evalation error is prepended with `[FAIL]` at the end of the message */
107         /* TODO(#486): print_expr_as_sexpr could not be easily integrated with log_fail */
108         print_expr_as_sexpr(stderr, eval_result.expr);
109         log_fail("\n");
110         return -1;
111     }
112
113     gc_collect(script->gc, script->scope.expr);
114
115     return 0;
116 }
117
118 bool script_has_scope_value(const Script *script, const char *name)
119 {
120     return !nil_p(
121         get_scope_value(
122             &script->scope,
123             SYMBOL(script->gc, name)));
124 }
125
126 Gc *script_gc(const Script *script)
127 {
128     return script->gc;
129 }