diff --git a/sing_macros/Cargo.toml b/sing_macros/Cargo.toml index 638644f..abfae0a 100644 --- a/sing_macros/Cargo.toml +++ b/sing_macros/Cargo.toml @@ -10,6 +10,7 @@ proc-macro = true [dependencies] sing_util = { path = "../sing_util" } +sing_parse = {path = "../sing_parse" } proc-macro2 = "1.0" ron = "0.7" rand = "0.7" diff --git a/sing_macros/src/keyword.rs b/sing_macros/src/keyword.rs index 6724b4c..a2d0584 100644 --- a/sing_macros/src/keyword.rs +++ b/sing_macros/src/keyword.rs @@ -1,3 +1,20 @@ +// Copyright 2022 Daniel Mowitz +// +// This file is part of sing. +// +// sing is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License +// as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// sing is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with sing. If not, see . + use syn::custom_keyword; custom_keyword!(ioe); diff --git a/sing_macros/src/lib.rs b/sing_macros/src/lib.rs index c0047b1..abf2481 100644 --- a/sing_macros/src/lib.rs +++ b/sing_macros/src/lib.rs @@ -1,3 +1,20 @@ +// Copyright 2022 Daniel Mowitz +// +// This file is part of sing. +// +// sing is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License +// as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// sing is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with sing. If not, see . + use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, spanned::Spanned, ToTokens}; @@ -10,21 +27,49 @@ use std::{ io::{Read, Write}, }; use syn::{ - bracketed, parenthesized, + bracketed, parse::{Parse, ParseStream}, parse2, parse_macro_input, Arm, AttributeArgs, FnArg, Ident, ImplItem, ItemImpl, LitStr, Meta, NestedMeta, Path, Signature, Stmt, Token, }; +extern crate sing_parse; extern crate proc_macro; extern crate sing_util; mod keyword; -/// Add the trait in this ipml block to sings trait store. +/// Add the trait in this impl block to sings trait store. /// /// The impl block needs to contain all functions as usual. /// After adding a trait, it can be used by the sing_loop macro. +/// +/// ## Example +/// ``` +/// trait FruitTree { +/// fn shake(&self, shakes: u32) -> Vec; +/// } +/// +/// #[derive(Debug, Serialize, Deserialize)] +/// struct Fruit { +/// fruit_type: String, +/// } +/// +/// struct BananaTree; +/// +/// #[sing_add_trait()] +/// impl FruitTree for BananaTree { +/// fn shake(&self, shakes: u32) -> Vec{ +/// let mut out = vec!(); +/// +/// for _ in 0..shakes { +/// out.push(Fruit{fruit_type: String::from("Banana")}); +/// } +/// +/// out +/// } +/// } +/// ``` #[proc_macro_attribute] pub fn sing_add_trait(annotated_item: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemImpl); @@ -100,7 +145,38 @@ fn add_trait_inner(input: ItemImpl, annotated_item: Vec) -> TokenStr /// Creates a main loop that parses stdin (or the given input stream), /// evaluates the called function and returns the result. /// -/// TODO: document syntax +/// ## Usage +/// +/// A call of this macro looks like: +/// ```sing_loop!(trait_object, ser_de <, msg: [msg_type, arg_type]> <, ioe: [input, output, error]>)``` +/// where all items in angle brackets are optional. +/// +/// with the arguments: +/// - trait_object: An instance of a struct that implements all desired traits. +/// - argument_ser_de: Serialization and deserialization functions. It either looks like `[equal, ser_fun, de_fun]` in which case both messages and arguments are serialized an deserialized by the same functions or `[different, arg_ser_fun, arg_de_fun <, msg_ser_fun, msg_de_fun>` in which case different functions are used. +/// - msg_type: A struct that implements [`sing_util::TraitCallMessage`] and should be used as the message type. +/// - arg_type: The type to which the arguments of function calls should be serialized to. +/// - input: The input Stream. Needs to implement [`std::io::BufRead`]. +/// - output: The output Stream. Needs to implement [`std::io::Write`]. +/// - error: The error output Stream. Needs to implement [`std::io::Write`]. +/// +/// `msg`, `ioe`, `equal` and `different` are keywords and need to be used literally in the call. +/// +/// The optional parameters have the following defaults: +/// - msg_ser_fun: [`sing_parse::callobj_to_string`] +/// - msg_de_fun: [`sing_parse::callobj_to_string`] +/// - msg_type: [`sing_parse::CallObj`] +/// - arg_type: [`String`] +/// - input: [`std::io::StdinLock`] +/// - output: [`std::io::StdoutLock`] +/// - error: [`std::io::StderrLock`] +/// +/// ## Example +/// ``` +/// let tree = BananaTree {}; +/// +/// sing_loop!(tree, [different, ron::to_string, ron::from_str],); +/// ``` #[proc_macro] pub fn sing_loop(input: TokenStream) -> TokenStream { let span = TokenStream2::from(input.clone()).__span(); @@ -443,9 +519,6 @@ struct LoopParams { } impl Parse for LoopParams { - /// Parses a given input into a Loopparams object - /// according to the syntax outlined in the documentation - /// of the sing_loop macro fn parse(input: ParseStream) -> syn::Result { let trait_obj = input.parse()?; input.parse::()?; @@ -500,10 +573,9 @@ impl Parse for LoopParams { input.parse::()?; input.parse::()?; - let content; - parenthesized!(content in input); - let m_a_type_err_generic = "The msg keyword expects two variables in the following order: (MessageType, ArgumentType),"; + bracketed!(content in input); + let m_a_type_err_generic = "The msg keyword expects two variables in the following order: [MessageType, ArgumentType],"; message_type = match content.parse() { Ok(m) => Some(m), @@ -542,9 +614,9 @@ impl Parse for LoopParams { input.parse::()?; let content; - parenthesized!(content in input); + bracketed!(content in input); let lh_inner = content.lookahead1(); - let ioe_err_generic = "The ioe keyword expects three variables in the following order: (Input, Output, Error),"; + let ioe_err_generic = "The ioe keyword expects three variables in the following order: [Input, Output, Error],"; if lh_inner.peek(Ident) { let r = content.parse()?; diff --git a/sing_parse/src/callobj.rs b/sing_parse/src/callobj.rs index a91fc84..751a2ca 100644 --- a/sing_parse/src/callobj.rs +++ b/sing_parse/src/callobj.rs @@ -27,6 +27,7 @@ pub struct CallObj { data: Vec, } +/// Default message representation for the sing_loop! macro. impl CallObj { pub fn new((trait_string, fun_string, index): (Option, String, usize), data: Vec) -> Self { Self{ diff --git a/sing_parse/src/lib.rs b/sing_parse/src/lib.rs index dbd243f..00237ac 100644 --- a/sing_parse/src/lib.rs +++ b/sing_parse/src/lib.rs @@ -26,10 +26,12 @@ mod callobj; lalrpop_mod!(fun_parser); +/// Default message serialization function for the sing_loop macro. pub fn callobj_to_string(o: CallObj) -> Result> { Ok(o.to_string()) } +/// Default message deserialization function for the sing_loop macro. pub fn callobj_from_string(s: String) -> Result> { // TODO: This should use a "?", but for some reason the error references s Ok(fun_parser::CallParser::new().parse(&s).unwrap()) diff --git a/sing_util/src/lib.rs b/sing_util/src/lib.rs index 5204850..b394140 100644 --- a/sing_util/src/lib.rs +++ b/sing_util/src/lib.rs @@ -63,14 +63,23 @@ pub enum DeduplicatedFunctionProfile { Multiple(Vec), } -/// Needs to be implemented by all structs that are used as +/// Needs to be implemented by all structs that are used to represent /// function calls that are sent through a sing I/O stream pub trait TraitCallMessage { + /// The representation of rust data structures that is used internally to store the parameters. + /// Usually also the type used by the I/O stream. type Representation; - + + /// Returns the called functions name. fn get_fun_name(&self) -> String; + + /// Returns the name of the trait the function is called from. fn get_trait_name(&self) -> Option; + + /// Returns the parameters (or arguments) of the function call. fn get_params(&self) -> Vec; + + /// Replaces the parameters currently stored in this TraitCallMessage object. fn new_params(&mut self, p: Vec); }