How do I create a Rust macro to define a String variable with the value of its own identifier? - macros

I want to write a macro to define something like below:
let FOO: String = "FOO".to_string();
It is possible for me to have a macro:
macro_rules! my_macro {
($name: ident, $val: expr) => {
let $name: String = $val.to_string();
}
}
and use it as my_macro!(FOO, "FOO");
However, this is a bit redundant. I expect to have something like my_macro!(FOO), and it can expand and use the $name as identifier, but also in the string value.

You want stringify!:
macro_rules! str_var {
($name:ident) => {
let $name = String::from(stringify!($name));
};
}
fn main() {
str_var!(foo);
println!("foo: {:?}", foo);
}

Related

flutter evaluate if var is integer or string

i need to evaluate what type is a variable to make some switch,there are any way to evaluate a varible to get his type, like val() or something similar. i need to do something for integers and other for string.
i alreaedy try to using a switch, like this,
switch (selector) {
case int :
print('value is a integer');
break;
case String:
print('value is a String');
break;
}
but how i do this, if switch can allow compare mixed type of vars?
thank you
You can use the keyword is or switch over runtimeType :
dynamic foo = 42;
if (foo is int) {
print("Hello");
}
switch (foo.runtimeType) {
case int: {
print("World");
}
}
Consider using is instead of directly using runtimeType. As is works with subclasses. While using runtimeType is a strict comparison.
You can use something like :
if(selector.runtimeType == int) print("Hello")
It's very simple:
dynamic a = "hello";
if (a.runtimeType == int)
print("a is int");
else if (a.runtimeType == String)
print("a is String");
Create this extension:
extension EvaluateType on Object? {
bool get isInt => this is int;
bool get isString => this is String;
}
Usage:
void main() {
Object? foo;
foo = 0;
print(foo.isInt); // true
print(foo.isString); // false
}

"cannot find value `a` in this scope" in Rust macro

I created macro for printing, using proc-macro-hack.
Then this error occured though I already have defined a.
Following is the code.
On decl crate,
proc_macro_expr_decl! {
/// Function for printing to the standard output.
///
/// First argument can be literal or not literal.
gprint! => gprint_impl
}
On impl crate,
use syn::{Expr, ExprTuple, parse_str};
use quote::ToTokens;
fn _print_impl(input: &str, print_name: &str) -> String {
let mut input_with_parens = String::with_capacity(input.len() + 2);
input_with_parens.push('(');
input_with_parens.push_str(input);
input_with_parens.push(')');
let tuple = parse_str::<ExprTuple>(&input_with_parens)
.unwrap_or_else(|_| panic!("expected arguments is expressions separated by comma, found {}", input))
let mut arg_iter = tuple.elems.iter();
let first = arg_iter.next();
if first.is_none() {
return "()".to_string();
}
let first = first.unwrap();
let mut s = String::new();
if let &Expr::Lit(ref lit) = first {
s.push_str(print_name);
s.push('(');
s.push_str(&lit.into_tokens().to_string());
} else {
s.push_str(print_name);
s.push_str("(\"{}\", ");
s.push_str(&first.into_tokens().to_string());
}
for arg in arg_iter {
s.push_str(", ");
s.push_str(&arg.into_tokens().to_string());
}
s.push(')');
s
}
proc_macro_expr_impl! {
pub fn gprint_impl(input: &str) -> String {
_print_impl(input, "print!")
}
}
And tried using this macro,
fn main() {
let a = 0;
gprint!(a);
}
error occured:
error[E0425]: cannot find value `a` in this scope
Why?

What is the best way to get Haxe function parameter types using a macro?

I want to get the parameter types of a Haxe function using a macro and convert them to a shorthand string form, a bit like JNI/Java method signatures, but without a return type.
The motivation here is to provide access to the function parameter types, without having to slowly search through run-time type information at runtime. For example, say you want to construct a graphical widget for calling a function that takes parameters. You will need the type of each function parameter to create the correct spinbox, textbox, and select box widgets needed for tweaking the values that will be passed to the function.
So the question is, how can you save Haxe function parameter types with a macro?
Here is a macro that works for a few basic types, and any abstracts based on those types. It maps the function parameter types to strings. For example, function type String->Float->Int->String->Void maps to sfis, Float->Float->Int to ff etc:
package;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.ExprTools;
// Map some Haxe types to string ids
#:enum abstract TypeMapping(String) from (String) {
var BOOL = "b";
var FLOAT = "f";
var INT = "i";
var STRING = "s";
}
class Util
{
public macro static function getParameterTypes(f:Expr):ExprOf<String> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var t = type.getParameters()[0];
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var signature:String = "";
for (i in 0...args.length) {
switch(args[i].t) {
case TAbstract(t, p):
var underlyingTypeName = Std.string(t.get().type.getParameters()[0]);
switch(underlyingTypeName) {
case "Bool":
signature += TypeMapping.BOOL;
case "Float":
signature += TypeMapping.FLOAT;
case "Int":
signature += TypeMapping.INT;
case "String":
signature += TypeMapping.STRING;
default:
throw "Unhandled abstract function parameter type: " + underlyingTypeName;
}
case CString:
signature += TypeMapping.STRING;
default:
throw "Unhandled function parameter type: " + args[i];
}
}
return macro $v{signature};
}
}
A further problem is how to make this work for all types, rather than just ones you handle explicitly. To do that, you might populate an array of Strings with the type name/class name/path of each function parameter instead, and return that instead of a single String. Here's an attempt at that, note it doesn't work with function parameters (and probably other stuff) yet:
public macro static function getFullParameterTypes(f:Expr):ExprOf<Array<String>> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var pos = haxe.macro.Context.currentPos();
var signature:Array<Expr> = [];
for (i in 0...args.length) {
var argType:Type = args[i].t;
var s;
switch(argType) {
case TFun(t, r):
s = EConst(CString("Function"));
throw "Not working with function parameters yet";
case _:
s = EConst(CString(argType.getParameters()[0].toString()));
}
signature.push({expr: s, pos: pos});
}
return macro $a{signature};
}
A more up to date approach..
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames = extractFunction(type);
return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
paramNames.push(pName);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
Use it like this
using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate();
trace(args) // output: [name, greeting]
A problem you may face is how to collect the default value of a parameter, consider the example below.
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate();
trace(args) // output: [name, greeting]
Now let's account for default parameter values by slightly modifying the code:
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
final v = {name: pName, value: null}; // <= anticipate a value
paramNames.push(v);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames:Array<Dynamic> = extractFunction(type);
// extract default param values
switch fun.expr {
case EFunction(f, m):{
for(a in m.args){
for(p in paramNames){
if(p.name == a.name){
if(a.value != null){
switch (a.value.expr){
case EConst(c):{
switch(c){
case CString(v, _):{
p.value = v;
}
case CFloat(f): {
p.value = Std.parseFloat(f);
}
case CInt(i):{
p.value = Std.parseInt(i);
}
case _: throw "unsupported constant value for default parameter";
}
}
case _:
}
}
}
}
}
}
case _:
}
return macro $v{paramNames};
}
So we can now use it like this
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func);
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]

Macro to fill out field names, values, and switch statements of a custom #:enum abstract?

I have a bit of a unique situation. For various reasons, chiefly interoperating with a nullable stringly typed legacy system, as well as various other needs I won't go into at the moment, I've settled on a custom #:enum abstract that looks like this:
#:enum abstract MyEnum(Null<Int>) {
public var A = 0;
public var B = 1;
public var C = 2;
public var D = 3;
public var E = 4;
public var F = 5;
public var G = 6;
#:from private static function fromString (value:String):MyEnum {
return switch (value) {
case "a": A;
case "b": B;
case "c": C;
case "d": D;
case "e": E;
case "f": F;
case "g": G;
default: null;
}
}
#:to private static function toString (value:Int):String {
return switch (value) {
case A: "a";
case B: "b";
case C: "c";
case D: "d";
case E: "e";
case F: "f";
case G: "g";
default: null;
}
}
}
However, that's an annoyingly large amount of things to type, and when adding and removing members it's easy to make a manual error. Clearly, this follows a super predictable pattern and seems like a great thing to construct with a macro, but I am terrible at haxe macros.
Can someone explain how I could use a macro to build this enum in such a way that all I have to supply is a list of field names?
pseudocode:
#:enum abstract MyEnum = doTheMacroMagic(["A","B","C","D","E","F","G"]);
The logical steps would be:
Declare public vars from field names (upper case)
Declare fromString/toString values from field names (lower case)
Set public vars to 0-based integers and follow same order as field names are supplied
I think a simple practical example like this might make haxe macros finally "click" for me if I can see it in action.
Flixel handles a very similar use case in for classes like FlxKey with FlxMacroUtil.buildMap(). This expression macro looks for all uppercase, inline vars it finds in the abstract and generates a Map<String, EnumType> from it, with the keys being the field names and the values the field values (or the inverse of that if invert is true).
#:enum
abstract FlxKey(Int) from Int to Int
{
public static var fromStringMap(default, null):Map<String, FlxKey>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey");
public static var toStringMap(default, null):Map<FlxKey, String>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey", true);
var A = 65;
var B = 66;
// more keys...
#:from
public static inline function fromString(s:String)
{
s = s.toUpperCase();
return fromStringMap.exists(s) ? fromStringMap.get(s) : NONE;
}
#:to
public inline function toString():String
{
return toStringMap.get(this);
}
}
I'd imagine that's a good starting point. If you want to generate the entire abstract, you will need a #:build macro.
Answering the follow-up question, how to generate fields: This is actually quite straightforward with a build macro:
#:enum
#:build(Macro.createVariables(["A", "B", "C", "D", "E"]))
abstract Generated(Int)
{
}
Macro.hx (sensible to have in its own file to avoid having to deal with #if macro conditionals):
package;
import haxe.macro.Context;
import haxe.macro.Expr;
class Macro
{
public static macro function createVariables(varNames:Array<String>):Array<Field>
{
// get the current fields of the calling type (empty array in this case)
var fields = Context.getBuildFields();
for (i in 0...varNames.length)
// create a custom variable and add it to the fields
fields.push(createVariable(varNames[i], i));
return fields;
}
private static function createVariable(name:String, value:Int):Field
{
return {
name: name,
doc: null,
meta: [],
access: [Access.APublic, Access.AStatic, Access.AInline],
kind: FieldType.FVar(macro:Int, macro $v{value}),
pos: Context.currentPos()
}
}
}
You'll notice the fields showing up in auto-completion for Generated.. You can also see what's been generated by looking at the Generated_Impl.dump when doing an AST dump.

How to pass anonymous functions as parameters in Rust?

I've been playing around with Rust the past week. I can't seem to figure out how to pass a function that is defined as a parameter when calling the method, and haven't come across any documentation that shows them being used in that fashion.
Is it possible to define a function in the parameter list when calling a function in Rust?
This is what I've tried so far...
fn main() {
// This works
thing_to_do(able_to_pass);
// Does not work
thing_to_do(fn() {
println!("found fn in indent position");
});
// Not the same type
thing_to_do(|| {
println!("mismatched types: expected `fn()` but found `||`")
});
}
fn thing_to_do(execute: fn()) {
execute();
}
fn able_to_pass() {
println!("Hey, I worked!");
}
In Rust 1.0, the syntax for closure parameters is as follows:
fn main() {
thing_to_do(able_to_pass);
thing_to_do(|| {
println!("works!");
});
}
fn thing_to_do<F: FnOnce()>(func: F) {
func();
}
fn able_to_pass() {
println!("works!");
}
We define a generic type constrained to one of the closure traits: FnOnce, FnMut, or Fn.
Like elsewhere in Rust, you can use a where clause instead:
fn thing_to_do<F>(func: F)
where F: FnOnce(),
{
func();
}
You may also want to take a trait object instead:
fn main() {
thing_to_do(&able_to_pass);
thing_to_do(&|| {
println!("works!");
});
}
fn thing_to_do(func: &Fn()) {
func();
}
fn able_to_pass() {
println!("works!");
}