@@ -306,4 +306,74 @@ MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
306306 return AST::ASTFragment ({node});
307307}
308308
309+ /* Expand builtin macro env!(), which inspects an environment variable at
310+ compile time. */
311+
312+ AST::ASTFragment
313+ MacroBuiltin::env (Location invoc_locus, AST::MacroInvocData &invoc)
314+ {
315+ auto invoc_token_tree = invoc.get_delim_tok_tree ();
316+ MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
317+ Parser<MacroInvocLexer> parser (std::move (lex));
318+
319+ auto last_token_id = macro_end_token (invoc_token_tree, parser);
320+
321+ if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
322+ {
323+ if (parser.peek_current_token ()->get_id () == last_token_id)
324+ rust_error_at (invoc_locus, " env! takes 1 or 2 arguments" );
325+ else
326+ rust_error_at (parser.peek_current_token ()->get_locus (),
327+ " argument must be a string literal" );
328+ return AST::ASTFragment::create_error ();
329+ }
330+
331+ auto lit_expr = parser.parse_literal_expr ();
332+ auto comma_skipped = parser.maybe_skip_token (COMMA);
333+
334+ std::unique_ptr<AST::LiteralExpr> error_expr = nullptr ;
335+
336+ if (parser.peek_current_token ()->get_id () != last_token_id)
337+ {
338+ if (!comma_skipped)
339+ {
340+ rust_error_at (parser.peek_current_token ()->get_locus (),
341+ " expected token: %<,%>" );
342+ return AST::ASTFragment::create_error ();
343+ }
344+ if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
345+ {
346+ rust_error_at (parser.peek_current_token ()->get_locus (),
347+ " argument must be a string literal" );
348+ return AST::ASTFragment::create_error ();
349+ }
350+
351+ error_expr = parser.parse_literal_expr ();
352+ parser.maybe_skip_token (COMMA);
353+ }
354+
355+ if (parser.peek_current_token ()->get_id () != last_token_id)
356+ {
357+ rust_error_at (invoc_locus, " env! takes 1 or 2 arguments" );
358+ return AST::ASTFragment::create_error ();
359+ }
360+
361+ parser.skip_token (last_token_id);
362+
363+ auto env_value = getenv (lit_expr->as_string ().c_str ());
364+
365+ if (env_value == nullptr )
366+ {
367+ if (error_expr == nullptr )
368+ rust_error_at (invoc_locus, " environment variable %qs not defined" ,
369+ lit_expr->as_string ().c_str ());
370+ else
371+ rust_error_at (invoc_locus, " %s" , error_expr->as_string ().c_str ());
372+ return AST::ASTFragment::create_error ();
373+ }
374+
375+ auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
376+ return AST::ASTFragment ({node});
377+ }
378+
309379} // namespace Rust
0 commit comments