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