Transforming attributes into identifiers on proc macro derive - parsing

I'm writing my first proc macro and despite trying to read through the source for thiserror, structopt and derive_more I can't seem to find exactly what I'm looking for. I want to transform this:
#[derive(Attach)]
#[attach(foo(SomeType, OtherType))]
#[attach(bar(OtherType))]
struct Plugin {}
into this:
impl Attach for Plugin {
fn attach(self, srv: &mut Server) {
let this = Arc::new(self);
srv.foo_order(this.clone(), &[TypeId::of::<SomeType>(), TypeId::of::<OtherType>()]);
srv.bar_order(this, &[TypeId::of::<OtherType>()]);
}
}
I've started writing a proc macro but got bungled up trying to parse the attributes:
extern crate proc_macro;
use proc_macro::{Span, TokenStream};
use quote::quote;
use std::any::TypeId;
use syn::{
parse::ParseStream, parse_macro_input, Attribute, AttributeArgs, DeriveInput, Ident, Result,
};
#[proc_macro_derive(Attach, attributes(attach))]
pub fn register_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
impl_register(input)
}
fn impl_register(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let attrs = &input.attrs;
for attr in attrs {
if attr.path.is_ident("attach") {
parse_attach_attribute(&attr);
}
}
println!("{:#?}", input);
TokenStream::from(quote! {
impl ::corten::Attach for #name {
fn attach(self, srv: &mut ::corten::Server) {
}
}
})
}
fn parse_attach_attribute(attr: &Attribute) -> Result<Dependency> {
let list: syn::MetaList = attr.parse_args()?;
// println!("{:#?}", list);
let ident = list.path.get_ident().expect("expected identifier");
let method = Ident::new(&format!("{}_order", ident), ident.span());
println!("{:#?}", method);
let dependencies = list
.nested
.into_pairs()
.map(|pair| pair.into_value())
.collect::<Vec<_>>();
// How does one get the identifiers out of a NestedMeta?
println!("{:#?}", dependencies);
// attr.parse_args_with(|input: ParseStream| {
// let ident = input.p
// let method = Ident::new(&format!("{}_order", ident), Span::call_site());
// let dep = Dependency {
// method,
// }
// })
unimplemented!()
}
struct Dependency {
method: Ident,
dependencies: Vec<Ident>,
}
the difficulty I'm having is how to actually get the attributes list into a usable form? As far as I can tell, I need to get the "foo" and "bar" parsed from &[Attribute] so I can construct the method identifier, and also the "SomeType" & "OtherType" identifiers, which I'll all eventually feed into quote!. If I print the TokenStream in the console, all of the information is there:
[
Attribute {
pound_token: Pound,
style: Outer,
bracket_token: Bracket,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "attach",
span: #0 bytes(103..109),
},
arguments: None,
},
],
},
tokens: TokenStream [
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "predecode",
span: #0 bytes(110..119),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "SomeType",
span: #0 bytes(120..128),
},
Punct {
ch: ',',
spacing: Alone,
span: #0 bytes(128..129),
},
Ident {
ident: "OtherType",
span: #0 bytes(130..139),
},
],
span: #0 bytes(119..140),
},
],
span: #0 bytes(109..141),
},
],
},
Attribute {
pound_token: Pound,
style: Outer,
bracket_token: Bracket,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "attach",
span: #0 bytes(145..151),
},
arguments: None,
},
],
},
tokens: TokenStream [
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "preresolve",
span: #0 bytes(152..162),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "OtherType",
span: #0 bytes(163..172),
},
],
span: #0 bytes(162..173),
},
],
span: #0 bytes(151..174),
},
],
},
]
But I don't have a way to actually get at it. How do I get to tokens[0].stream.ident?

After much messing around I think I have something that works, although I'm happy to accept other answers that are better as I feel this is a bit messy:
extern crate proc_macro;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, Attribute, DeriveInput, Ident, Result};
#[proc_macro_derive(Attach, attributes(attach))]
pub fn register_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
proc_macro::TokenStream::from(impl_register(input))
}
fn impl_register(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let attrs = &input.attrs;
// println!("{:#?}", input);
let attrs = attrs
.iter()
.filter(|attr| attr.path.is_ident("attach"))
.map(|attr| parse_attach_attribute(&attr).expect("parse failed"))
.map(|dep| {
let method: Ident = dep.method;
let dependencies = dep.dependencies.iter().map(|ident: &Ident| {
quote! {
std::any::TypeId::of::<#ident>()
}
});
quote! {
srv.#method::<#name, _>(Arc::clone(&this), &[ #(#dependencies),* ]);
}
});
quote! {
impl corten::Attach for #name {
fn attach(self, srv: &mut corten::Server) {
let this = std::sync::Arc::new(self);
#(#attrs)*
}
}
}
}
fn parse_attach_attribute(attr: &Attribute) -> Result<Dependency> {
let list: syn::MetaList = attr.parse_args()?;
// println!("{:#?}", list);
let ident = list.path.get_ident().expect("expected identifier");
let method = Ident::new(&format!("{}_order", ident), Span::call_site());
println!("{:#?}", method);
let dependencies = list
.nested
.into_pairs()
.map(|pair| pair.into_value())
.filter_map(|pair| match pair {
syn::NestedMeta::Meta(meta) => match meta {
syn::Meta::Path(path) => path.get_ident().cloned(),
_ => panic!("only path meta supported"),
},
_ => panic!("lit not supported"),
})
.collect();
println!("{:#?}", dependencies);
Ok(Dependency {
method,
dependencies,
})
}
struct Dependency {
method: Ident,
dependencies: Vec<Ident>,
}

Related

Toggle a boolean within a struct in zig, possible?

To toggle* a boolean I normally use boolean = !boolean like this:
var boolean: bool = true;
boolean = !boolean;
std.debug.print("My bool is: {}\n", .{boolean}); //My bool is: false
But how do I achieve this for booleans within a struct? Or is it not possible?
const std = #import("std");
pub fn main() void {
//Struct with default values:
const animal = struct {
tail: bool = true,
wings: bool = false,
horns: bool = false,
paws: bool = true,
};
//Struct instances:
var has = animal{};
//This works alright:
//var hasno = animal{.tail = false, .wings = true, .horns = true, .paws = false};
//FAILS: error: expected type 'bool', found '#TypeOf(.enum_literal)'
var hasno = animal{ .tail = !.tail }; //, .wings = !.wings, .horns = !.horns, .paws = !.paws };
//Debug printing:
std.debug.print("Animal has tail: {}, wings: {}, horns: {}, paws: {}\n", .{ has.tail, has.wings, has.horns, has.paws });
std.debug.print("Animal has no tail: {}, wings: {}, horns: {}, paws: {}\n", .{ hasno.tail, hasno.wings, hasno.horns, hasno.paws });
}
Test code for yourself online in zig playground:
https://zig-play.dev
*give it the opposite value of what it was, without knowing what it was.
Like if (boolean == true) boolean = false; else boolean = true; But I'm wondering if it is possible with the (bang) operator for booleans within struct.
const std = #import("std");
const Animal = struct {
tail: bool,
};
pub fn main() void {
var animal = Animal { .tail = true };
std.debug.print("{}\n", .{ animal });
animal.tail = !animal.tail;
std.debug.print("{}\n", .{ animal });
}
Prints:
main.Animal{ .tail = true }
main.Animal{ .tail = false }

failed to send transaction: Cross-program invocation with unauthorized signer or writable account

I am trying to build a web3 based e-commerce site using Anchor.
I've just started learning about PDAs and there's a error I've been getting for hours, like the one in the title.
My contract:
#[program]
pub mod dailsap_store_contract {
use super::*;
pub fn create_collection(
ctx: Context<CreateCollection>,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let collection: &mut Account<Collection> = &mut ctx.accounts.collection;
let authority: &Signer = &ctx.accounts.authority;
let clock: Clock = Clock::get().unwrap();
let bump = *ctx.bumps.get("collection").unwrap();
if name.chars().count() > 50 {
return Err(ErrorCode::CollectionNameTooLong.into());
}
if description.chars().count() > 250 {
return Err(ErrorCode::CollectionDescriptionTooLong.into());
}
if image_uri.chars().count() > 60 {
return Err(ErrorCode::CollectionImageUrlTooLong.into());
}
collection.authority = *authority.key;
collection.timestamp = clock.unix_timestamp;
collection.name = name;
collection.description = description;
collection.image = image_uri;
collection.bump = bump;
Ok(())
}
pub fn update_collection(
ctx: Context<UpdateCollection>,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let base_collection: &mut Account<Collection> = &mut ctx.accounts.collection_account;
base_collection.name = name;
base_collection.description = description;
base_collection.image = image_uri;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateCollection<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", collection.key().as_ref()], bump)]
pub collection: Account<'info, Collection>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
}
#[account]
pub struct Collection {
authority: Pubkey,
timestamp: i64,
name: String,
description: String,
image: String,
bump: u8,
}
Frontend:
const collection = anchor.web3.Keypair.generate();
const [collectionPDA, _] = await anchor.web3.PublicKey.findProgramAddress(
[
anchor.utils.bytes.utf8.encode("collection"),
collection.publicKey.toBuffer(),
],
program.programId
);
await program.methods
.createCollection(
"This is collection name",
"This is collection description",
"Hello World"
)
.accounts({
collection: collectionPDA,
authority: anchor.AnchorProvider.env().publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
The problem should be here: seeds=[b"collection", collection.key().as_ref()]
The source from which I received help: https://book.anchor-lang.com/anchor_in_depth/PDAs.html
But I'm getting errors in a way I don't understand
can you help?
To be honest, I'm surprised the program got so far! The program-derived address is meant to be derived from a set of seeds and the program id, and in your case, the collection address is using itself as a seed, which would normally mean infinite recursion. But in this case, it only goes down one level and fails to derive itself.
Try any other seeds on your collection address, even something like:
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", authority.key().as_ref()], bump)]
pub collection: Account<'info, Collection>,
I came up with a solution like this, I'm not sure how logical it is.
...
#[program]
pub mod dailsap_store_contract {
use super::*;
pub fn create_collection(
ctx: Context<CreateCollection>,
id: Pubkey,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let collection: &mut Account<Collection> = &mut ctx.accounts.collection;
let authority: &Signer = &ctx.accounts.authority;
let clock: Clock = Clock::get().unwrap();
let bump = *ctx.bumps.get("collection").unwrap();
if name.chars().count() > 50 {
return Err(ErrorCode::CollectionNameTooLong.into());
}
...
#[derive(Accounts)]
#[instruction(id: Pubkey)]
pub struct CreateCollection<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", id.as_ref()], bump)]
pub collection: Account<'info, Collection>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
}
#[account]
pub struct Collection {
id: Pubkey,
authority: Pubkey,
timestamp: i64,
name: String,
description: String,
image: String,
bump: u8,
}
...
Frontend:
const collection = anchor.web3.Keypair.generate();
const [collectionPDA, _] = await anchor.web3.PublicKey.findProgramAddress(
[
Buffer.from(anchor.utils.bytes.utf8.encode("collection")),
collection.publicKey.toBuffer(),
],
program.programId
);
await program.methods
.createCollection(
collection.publicKey,
"This is collection name",
"This is collection description",
"Hello World"
)
.accounts({
collection: collectionPDA,
authority: anchor.AnchorProvider.env().publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();

Parsing command line arguments in Rust

I am working on a command line program where I need to parse the cli arguments. My problem is that there is an error when I try to parse elements from a vector of Strings
I have a function called ìnto_num_vec() which takes a vector of Strings and I should parse it into a new vector of integers.
Code from lib.rs
pub fn affirm_args(input: Vec<String>) {
if input.len() < 2 {
panic!("To few arguments");
} else {
let numbers = into_num_vec(input);
print_numbers(numbers);
}
}
fn into_num_vec(input: Vec<String>) -> Vec<i32> {
let mut collection: Vec<i32> = Vec::new();
for i in input {
match i.trim().parse() {
Ok(n) => collection.push(n),
Err(_) => panic!("Error parsing")
}
}
collection
}
pub fn print_numbers(input: Vec<i32>) {
for i in input {
println!("{}", i);
}
}
The function is panicking and I'am getting the custom panic msg "Error parsing".
Code in main.rs
use sort_program::*;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
affirm_args(args);
}
The first argument to a program traditionally is the executable name. You should skip it:
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
affirm_args(args);
}

Have short error message using nom with nom_locate

I am trying to parse using nom with nom_locate, following this tutorial. I only want output in the format:
Error: line 5, column 1
But currently I get:
Error: Parsing Failure: ParseError { span: LocatedSpan { offset: 37, line: 5, fragment: "{{G]\nK(1)key [J]\n", extra: () }, message: "Error: line 5, column 1" }
Relevant code:
pub type Span<'a> = LocatedSpan<&'a str>;
pub type IResult<'a, O> = nom::IResult<Span<'a>, O, ParseError<'a>>;
#[derive(Debug, PartialEq)]
pub struct ParseError<'a> {
span: Span<'a>,
message: String,
}
impl<'a> ParseError<'a> {
pub fn new(message: String, span: Span<'a>) -> Self {
Self {
span,
message,
}
}
}
impl<'a> nom::error::ParseError<Span<'a>> for ParseError<'a> {
fn from_error_kind(input: Span<'a>, _: nom::error::ErrorKind) -> Self {
Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
}
fn append(input: Span<'a>, _: nom::error::ErrorKind, _: Self) -> Self {
Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
}
fn from_char(input: Span<'a>, _: char) -> Self {
Self::new(format!("Error: line {}, column {}", input.location_line(), input.location_line()), input)
}
}
pub fn job(s: Span) -> IResult<Vec<entities::Step>> {
let (s, steps) = many1(job_lines)(s)?;
Ok((s, steps))
}
fn job_lines(s: Span) -> IResult<entities::Step> {
let (s, name) = match step_name(s) {
Ok((s, name)) => (s, name),
Err(nom::Err::Error(_)) => {
let line = s.location_line();
let column = s.get_column();
return Err(nom::Err::Failure(ParseError::new(
format!("Error: line {}, column {}", line, column),
s,
)));
},
Err(e) => return Err(e),
};
Ok((s, name))
}
Relevant error code in main:
let steps = match input::parsers::job(input::parsers::Span::new(&input)) {
Ok((_, steps)) => steps,
Err(error) => {
eprintln!("{}", error);
std::process::exit(1)
}
};
What do I need to do to get the short error message instead of the extensive one?
EDIT:
I deleted the span property from the custom ParseError and then the Error output becomes:
Parsing Failure: ParseError { message: "Error: line 5, column 1" }
Much better, but question remains: can I get of everything except of the message itself?
As you surmised, error is a nom::Err::Err which is an enum, so you will need to match on that to get at the inner error:
let steps = match input::parsers::job(input::parsers::Span::new(&input)) {
Ok((_, steps)) => steps,
Err(nom::Err::Failure (error)) => {
eprintln!("{}", error.message);
std::process::exit(1)
},
Err(error) => {
eprintln!("Unknown error: {}", error);
std::process::exit(1)
},
};

Basic parsing Flow with Babel not working

I cannot seem to be able to parse Flow using Babel, its not recognising types or "declare" and is coming up with errors on them :-
const fs = require("fs");
const babel = require("#babel/core");
const parser = require('#babel/parser');
const generate = require('#babel/generator').default;
if (process.argv.length == 3) {
const filename = process.argv[2];
const sourceCode = fs.readFileSync(filename).toString();
console.log("sourceCode = ", sourceCode);
var options = {
"sourceType": "module", // parse in strict mode and allow module declarations
"presets": ["#babel/preset-flow"]
};
const parsedAst = parser.parse(sourceCode, options);
console.log("parsedAst = ", parsedAst)
const { codeOutput, map, ast } = babel.transformFromAstSync(parsedAst, sourceCode, { ast: true } );
console.log("ast = ", JSON.stringify(ast, 2, 2))
const output = generate(ast, { /* options */ }, sourceCode);
console.log("codeOutput = ", codeOutput);
console.log("output = ", output);
};
given the following code :-
// #flow strict
const MAX_SUGGESTIONS = 5;
/**
* Given [ A, B, C ] return ' Did you mean A, B, or C?'.
*/
declare function didYouMean(suggestions: $ReadOnlyArray<string>): string;
// eslint-disable-next-line no-redeclare
declare function didYouMean(
subMessage: string,
suggestions: $ReadOnlyArray<string>,
): string;
// eslint-disable-next-line no-redeclare
export default function didYouMean(firstArg, secondArg?) { ... }
I am getting errors on declare also on types :-
C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:730
throw err;
^
SyntaxError: Unexpected token, expected ";" (8:8)
at Parser._raise (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:723:17)
at Parser.raiseWithData (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:716:17)
at Parser.raise (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:710:17)
at Parser.unexpected (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:8610:16)
at Parser.semicolon (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:8592:40)
at Parser.parseExpressionStatement (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:11449:10)
at Parser.parseStatementContent (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:11050:19)
at Parser.parseStatement (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:10916:17)
at Parser.parseBlockOrModuleBlockBody (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:11490:25)
at Parser.parseBlockBody (C:\Users\aaron\Tests\Babel\generator-test\node_modules\#babel\parser\lib\index.js:11477:10) {
loc: Position { line: 8, column: 8 },
pos: 118
}
Github :-
https://github.com/AaronNGray/babel-flow-parser-test
Tag v0 is without package-lock.json
Need to add some extra details which I cannot think of ???
The following works :-
const fs = require('fs');
const babel = require('#babel/core');
if (process.argv.length == 3) {
const filename = process.argv[2];
const source = fs.readFileSync(filename).toString();
const ast = babel.parseSync(source, {
babelrc: false,
configFile: false,
ast: true,
parserOpts: {
plugins: ['flow', 'jsx'],
},
filename,
});
console.log("ast = ", JSON.stringify(ast, null, 2))
}
I would still like to know if the original will work with modification as its the near enough what is specified in the Babel documentation.

Resources