20
20
#include " rust-diagnostics.h"
21
21
#include " rust-expr.h"
22
22
#include " rust-session-manager.h"
23
+ #include " rust-macro-invoc-lexer.h"
24
+ #include " rust-lex.h"
25
+ #include " rust-parse.h"
23
26
24
27
namespace Rust {
25
28
namespace {
@@ -30,6 +33,107 @@ make_string (Location locus, std::string value)
30
33
new AST::LiteralExpr (value, AST::Literal::STRING,
31
34
PrimitiveCoreType::CORETYPE_STR, {}, locus));
32
35
}
36
+
37
+ /* Parse a single string literal from the given delimited token tree,
38
+ and return the LiteralExpr for it. Allow for an optional trailing comma,
39
+ but otherwise enforce that these are the only tokens. */
40
+
41
+ std::unique_ptr<AST::LiteralExpr>
42
+ parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
43
+ Location invoc_locus)
44
+ {
45
+ MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
46
+ Parser<MacroInvocLexer> parser (std::move (lex));
47
+
48
+ auto last_token_id = TokenId::RIGHT_CURLY;
49
+ switch (invoc_token_tree.get_delim_type ())
50
+ {
51
+ case AST::DelimType::PARENS:
52
+ last_token_id = TokenId::RIGHT_PAREN;
53
+ rust_assert (parser.skip_token (LEFT_PAREN));
54
+ break ;
55
+
56
+ case AST::DelimType::CURLY:
57
+ rust_assert (parser.skip_token (LEFT_CURLY));
58
+ break ;
59
+
60
+ case AST::DelimType::SQUARE:
61
+ last_token_id = TokenId::RIGHT_SQUARE;
62
+ rust_assert (parser.skip_token (LEFT_SQUARE));
63
+ break ;
64
+ }
65
+
66
+ std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr ;
67
+
68
+ if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
69
+ {
70
+ lit_expr = parser.parse_literal_expr ();
71
+ parser.maybe_skip_token (COMMA);
72
+ if (parser.peek_current_token ()->get_id () != last_token_id)
73
+ {
74
+ lit_expr = nullptr ;
75
+ rust_error_at (invoc_locus, " macro takes 1 argument" );
76
+ }
77
+ }
78
+ else if (parser.peek_current_token ()->get_id () == last_token_id)
79
+ rust_error_at (invoc_locus, " macro takes 1 argument" );
80
+ else
81
+ rust_error_at (invoc_locus, " argument must be a string literal" );
82
+
83
+ parser.skip_token (last_token_id);
84
+
85
+ return lit_expr;
86
+ }
87
+
88
+ /* Treat PATH as a path relative to the source file currently being
89
+ compiled, and return the absolute path for it. */
90
+
91
+ std::string
92
+ source_relative_path (std::string path, Location locus)
93
+ {
94
+ std::string compile_fname
95
+ = Session::get_instance ().linemap ->location_file (locus);
96
+
97
+ auto dir_separator_pos = compile_fname.rfind (file_separator);
98
+
99
+ /* If there is no file_separator in the path, use current dir ('.'). */
100
+ std::string dirname;
101
+ if (dir_separator_pos == std::string::npos)
102
+ dirname = std::string (" ." ) + file_separator;
103
+ else
104
+ dirname = compile_fname.substr (0 , dir_separator_pos) + file_separator;
105
+
106
+ return dirname + path;
107
+ }
108
+
109
+ /* Read the full contents of the file FILENAME and return them in a vector.
110
+ FIXME: platform specific. */
111
+
112
+ std::vector<uint8_t >
113
+ load_file_bytes (const char *filename)
114
+ {
115
+ RAIIFile file_wrap (filename);
116
+ if (file_wrap.get_raw () == nullptr )
117
+ {
118
+ rust_error_at (Location (), " cannot open filename %s: %m" , filename);
119
+ return std::vector<uint8_t > ();
120
+ }
121
+
122
+ FILE *f = file_wrap.get_raw ();
123
+ fseek (f, 0L , SEEK_END);
124
+ long fsize = ftell (f);
125
+ fseek (f, 0L , SEEK_SET);
126
+
127
+ std::vector<uint8_t > buf (fsize);
128
+
129
+ if (fread (&buf[0 ], fsize, 1 , f) != 1 )
130
+ {
131
+ rust_error_at (Location (), " error reading file %s: %m" , filename);
132
+ return std::vector<uint8_t > ();
133
+ }
134
+
135
+ return buf;
136
+ }
33
137
} // namespace
34
138
35
139
AST::ASTFragment
@@ -63,4 +167,73 @@ MacroBuiltin::column (Location invoc_locus, AST::MacroInvocData &invoc)
63
167
64
168
return AST::ASTFragment ({column_no});
65
169
}
170
+
171
+ /* Expand builtin macro include_bytes!("filename"), which includes the contents
172
+ of the given file as reference to a byte array. Yields an expression of type
173
+ &'static [u8; N]. */
174
+
175
+ AST::ASTFragment
176
+ MacroBuiltin::include_bytes (Location invoc_locus, AST::MacroInvocData &invoc)
177
+ {
178
+ /* Get target filename from the macro invocation, which is treated as a path
179
+ relative to the include!-ing file (currently being compiled). */
180
+ auto lit_expr
181
+ = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
182
+ if (lit_expr == nullptr )
183
+ return AST::ASTFragment::create_error ();
184
+
185
+ std::string target_filename
186
+ = source_relative_path (lit_expr->as_string (), invoc_locus);
187
+
188
+ std::vector<uint8_t > bytes = load_file_bytes (target_filename.c_str ());
189
+
190
+ /* Is there a more efficient way to do this? */
191
+ std::vector<std::unique_ptr<AST::Expr>> elts;
192
+ for (uint8_t b : bytes)
193
+ {
194
+ elts.emplace_back (
195
+ new AST::LiteralExpr (std::string (1 , (char ) b), AST::Literal::BYTE,
196
+ PrimitiveCoreType::CORETYPE_U8,
197
+ {} /* outer_attrs */ , invoc_locus));
198
+ }
199
+
200
+ auto elems = std::unique_ptr<AST::ArrayElems> (
201
+ new AST::ArrayElemsValues (std::move (elts), invoc_locus));
202
+
203
+ auto array = std::unique_ptr<AST::Expr> (
204
+ new AST::ArrayExpr (std::move (elems), {}, {}, invoc_locus));
205
+
206
+ auto borrow = std::unique_ptr<AST::Expr> (
207
+ new AST::BorrowExpr (std::move (array), false , false , {}, invoc_locus));
208
+
209
+ auto node = AST::SingleASTNode (std::move (borrow));
210
+ return AST::ASTFragment ({node});
211
+ }
212
+
213
+ /* Expand builtin macro include_str!("filename"), which includes the contents
214
+ of the given file as a string. The file must be UTF-8 encoded. Yields an
215
+ expression of type &'static str. */
216
+
217
+ AST::ASTFragment
218
+ MacroBuiltin::include_str (Location invoc_locus, AST::MacroInvocData &invoc)
219
+ {
220
+ /* Get target filename from the macro invocation, which is treated as a path
221
+ relative to the include!-ing file (currently being compiled). */
222
+ auto lit_expr
223
+ = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
224
+ if (lit_expr == nullptr )
225
+ return AST::ASTFragment::create_error ();
226
+
227
+ std::string target_filename
228
+ = source_relative_path (lit_expr->as_string (), invoc_locus);
229
+
230
+ std::vector<uint8_t > bytes = load_file_bytes (target_filename.c_str ());
231
+
232
+ /* FIXME: Enforce that the file contents are valid UTF-8. */
233
+ std::string str ((const char *) &bytes[0 ], bytes.size ());
234
+
235
+ auto node = AST::SingleASTNode (make_string (invoc_locus, str));
236
+ return AST::ASTFragment ({node});
237
+ }
238
+
66
239
} // namespace Rust
0 commit comments