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