]> git.lizzy.rs Git - nothing.git/blob - src/game/level/script.c
(#802) TODO
[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 Script *create_script_from_line_stream(LineStream *line_stream, Broadcast *broadcast)
26 {
27     trace_assert(line_stream);
28
29     Lt *lt = create_lt();
30     if (lt == NULL) {
31         return NULL;
32     }
33
34     Script *script = PUSH_LT(lt, nth_alloc(sizeof(Script)), free);
35     if (script == NULL) {
36         RETURN_LT(lt, NULL);
37     }
38     script->lt = lt;
39
40     script->gc = PUSH_LT(lt, create_gc(), destroy_gc);
41     if (script->gc == NULL) {
42         RETURN_LT(lt, NULL);
43     }
44
45     script->scope = create_scope(script->gc);
46
47     load_std_library(script->gc, &script->scope);
48     load_log_library(script->gc, &script->scope);
49     struct EvalResult eval_result = broadcast_load_library(broadcast, script->gc, &script->scope);
50     if (eval_result.is_error) {
51         print_expr_as_sexpr(stderr, eval_result.expr);
52         log_fail("\n");
53         RETURN_LT(lt, NULL);
54     }
55
56     size_t n = 0;
57     sscanf(line_stream_next(line_stream), "%lu", &n);
58
59     char *source_code = NULL;
60     for (size_t i = 0; i < n; ++i) {
61         /* TODO(#466): maybe source_code should be constantly replaced in the Lt */
62         source_code = string_append(
63             source_code,
64             line_stream_next(line_stream));
65     }
66     PUSH_LT(lt, source_code, free);
67
68     struct ParseResult parse_result =
69         read_all_exprs_from_string(
70             script->gc,
71             source_code);
72     if (parse_result.is_error) {
73         log_fail("Parsing error: %s\n", parse_result.error_message);
74         RETURN_LT(lt, NULL);
75     }
76
77     eval_result = eval(
78         script->gc,
79         &script->scope,
80         CONS(script->gc,
81              SYMBOL(script->gc, "begin"),
82              parse_result.expr));
83     if (eval_result.is_error) {
84         print_expr_as_sexpr(stderr, eval_result.expr);
85         log_fail("\n");
86         RETURN_LT(lt, NULL);
87     }
88
89     gc_collect(script->gc, script->scope.expr);
90
91     free(RELEASE_LT(lt, source_code));
92
93     return script;
94 }
95
96 void destroy_script(Script *script)
97 {
98     trace_assert(script);
99     RETURN_LT0(script->lt);
100 }
101
102 int script_eval(Script *script, const char *source_code)
103 {
104     trace_assert(script);
105     trace_assert(source_code);
106
107     struct ParseResult parse_result = read_expr_from_string(
108         script->gc,
109         source_code);
110     if (parse_result.is_error) {
111         log_fail("Parsing error: %s\n", parse_result.error_message);
112         return -1;
113     }
114
115     struct EvalResult eval_result = eval(
116         script->gc,
117         &script->scope,
118         parse_result.expr);
119     if (eval_result.is_error) {
120         log_fail("Evaluation error: ");
121         /* TODO(#521): Evalation error is prepended with `[FAIL]` at the end of the message */
122         /* TODO(#486): print_expr_as_sexpr could not be easily integrated with log_fail */
123         print_expr_as_sexpr(stderr, eval_result.expr);
124         log_fail("\n");
125         return -1;
126     }
127
128     gc_collect(script->gc, script->scope.expr);
129
130     return 0;
131 }
132
133 bool script_has_scope_value(const Script *script, const char *name)
134 {
135     return !nil_p(
136         get_scope_value(
137             &script->scope,
138             SYMBOL(script->gc, name)));
139 }