]> git.lizzy.rs Git - nothing.git/blob - src/game/level/script.c
Add TODO(#498)
[nothing.git] / src / game / level / script.c
1 #include <assert.h>
2
3 #include "ebisp/gc.h"
4 #include "ebisp/interpreter.h"
5 #include "ebisp/parser.h"
6 #include "ebisp/scope.h"
7 #include "script.h"
8 #include "str.h"
9 #include "system/error.h"
10 #include "system/line_stream.h"
11 #include "system/log.h"
12 #include "system/lt.h"
13 #include "system/nth_alloc.h"
14 #include "ui/console.h"
15
16 struct Script
17 {
18     Lt *lt;
19     Gc *gc;
20     struct Scope scope;
21 };
22
23 static struct EvalResult
24 hide_goal(void *param, Gc *gc, struct Scope *scope, struct Expr args)
25 {
26     assert(param);
27     assert(gc);
28     assert(scope);
29     (void) args;
30
31     /* TODO(#497): hide_goal is not implemented */
32
33     return eval_success(NIL(gc));
34 }
35
36 static struct EvalResult
37 show_goal(void *param, Gc *gc, struct Scope *scope, struct Expr args)
38 {
39     assert(param);
40     assert(gc);
41     assert(scope);
42     (void) args;
43
44     /* TODO(#498): show_goal is not implemented */
45
46     return eval_success(NIL(gc));
47 }
48
49 Script *create_script_from_line_stream(LineStream *line_stream, Level *level)
50 {
51     assert(line_stream);
52
53     Lt *lt = create_lt();
54     if (lt == NULL) {
55         return NULL;
56     }
57
58     Script *script = PUSH_LT(lt, nth_alloc(sizeof(Script)), free);
59     if (script == NULL) {
60         throw_error(ERROR_TYPE_LIBC);
61         RETURN_LT(lt, NULL);
62     }
63     script->lt = lt;
64
65     script->gc = PUSH_LT(lt, create_gc(), destroy_gc);
66     if (script->gc == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     script->scope = create_scope(script->gc);
71
72     size_t n = 0;
73     sscanf(line_stream_next(line_stream), "%lu", &n);
74
75     char *source_code = NULL;
76     for (size_t i = 0; i < n; ++i) {
77         /* TODO(#466): maybe source_code should be constantly replaced in the Lt */
78         source_code = string_append(
79             source_code,
80             line_stream_next(line_stream));
81     }
82     PUSH_LT(lt, source_code, free);
83
84     set_scope_value(
85         script->gc,
86         &script->scope,
87         SYMBOL(script->gc, "rect-apply-force"),
88         NATIVE(script->gc, rect_apply_force, level));
89     set_scope_value(
90         script->gc,
91         &script->scope,
92         SYMBOL(script->gc, "hide-goal"),
93         NATIVE(script->gc, hide_goal, level));
94     set_scope_value(
95         script->gc,
96         &script->scope,
97         SYMBOL(script->gc, "show-goal"),
98         NATIVE(script->gc, show_goal, level));
99
100     struct ParseResult parse_result =
101         read_all_exprs_from_string(
102             script->gc,
103             source_code);
104     if (parse_result.is_error) {
105         log_fail("Parsing error: %s\n", parse_result.error_message);
106         RETURN_LT(lt, NULL);
107     }
108
109     struct EvalResult eval_result = eval(
110         script->gc,
111         &script->scope,
112         CONS(script->gc,
113              SYMBOL(script->gc, "begin"),
114              parse_result.expr));
115     if (eval_result.is_error) {
116         print_expr_as_sexpr(stderr, eval_result.expr);
117         log_fail("\n");
118         RETURN_LT(lt, NULL);
119     }
120
121     gc_collect(script->gc, script->scope.expr);
122
123     free(RELEASE_LT(lt, source_code));
124
125     return script;
126 }
127
128 void destroy_script(Script *script)
129 {
130     assert(script);
131     RETURN_LT0(script->lt);
132 }
133
134 int script_eval(Script *script, const char *source_code)
135 {
136     assert(script);
137     assert(source_code);
138
139     struct ParseResult parse_result = read_expr_from_string(
140         script->gc,
141         source_code);
142     if (parse_result.is_error) {
143         log_fail("Parsing error: %s\n", parse_result.error_message);
144         return -1;
145     }
146
147     struct EvalResult eval_result = eval(
148         script->gc,
149         &script->scope,
150         parse_result.expr);
151     if (eval_result.is_error) {
152         log_fail("Evaluation error: ");
153         /* TODO(#486): print_expr_as_sexpr could not be easily integrated with log_fail */
154         print_expr_as_sexpr(stderr, eval_result.expr);
155         log_fail("\n");
156         return -1;
157     }
158
159     gc_collect(script->gc, script->scope.expr);
160
161     return 0;
162 }
163
164 bool script_has_scope_value(const Script *script, const char *name)
165 {
166     return !nil_p(
167         get_scope_value(
168             &script->scope,
169             SYMBOL(script->gc, name)));
170 }