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