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);
}