Merge branch 'main' into thelie-patch-1

This commit is contained in:
Thelie 2022-03-27 15:39:18 +02:00
commit 7890b86a64
6 changed files with 115 additions and 13 deletions

View file

@ -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"

View file

@ -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 <https://www.gnu.org/licenses/>.
use syn::custom_keyword;
custom_keyword!(ioe);

View file

@ -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 <https://www.gnu.org/licenses/>.
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<Fruit>;
/// }
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct Fruit {
/// fruit_type: String,
/// }
///
/// struct BananaTree;
///
/// #[sing_add_trait()]
/// impl FruitTree for BananaTree {
/// fn shake(&self, shakes: u32) -> Vec<Fruit>{
/// 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<NestedMeta>) -> 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<Self> {
let trait_obj = input.parse()?;
input.parse::<Token![,]>()?;
@ -500,10 +573,9 @@ impl Parse for LoopParams {
input.parse::<keyword::msg>()?;
input.parse::<Token![:]>()?;
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::<Token![:]>()?;
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()?;

View file

@ -27,6 +27,7 @@ pub struct CallObj {
data: Vec<String>,
}
/// Default message representation for the sing_loop! macro.
impl CallObj {
pub fn new((trait_string, fun_string, index): (Option<String>, String, usize), data: Vec<String>) -> Self {
Self{

View file

@ -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<String, Box<dyn Error>> {
Ok(o.to_string())
}
/// Default message deserialization function for the sing_loop macro.
pub fn callobj_from_string(s: String) -> Result<CallObj, Box<dyn Error>> {
// TODO: This should use a "?", but for some reason the error references s
Ok(fun_parser::CallParser::new().parse(&s).unwrap())

View file

@ -63,14 +63,23 @@ pub enum DeduplicatedFunctionProfile {
Multiple(Vec<String>),
}
/// 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<String>;
/// Returns the parameters (or arguments) of the function call.
fn get_params(&self) -> Vec<Self::Representation>;
/// Replaces the parameters currently stored in this TraitCallMessage object.
fn new_params(&mut self, p: Vec<Self::Representation>);
}