Merge branch 'main' into thelie-patch-1
This commit is contained in:
commit
7890b86a64
6 changed files with 115 additions and 13 deletions
|
@ -10,6 +10,7 @@ proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sing_util = { path = "../sing_util" }
|
sing_util = { path = "../sing_util" }
|
||||||
|
sing_parse = {path = "../sing_parse" }
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
ron = "0.7"
|
ron = "0.7"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
|
|
@ -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;
|
use syn::custom_keyword;
|
||||||
|
|
||||||
custom_keyword!(ioe);
|
custom_keyword!(ioe);
|
||||||
|
|
|
@ -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_macro::TokenStream;
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
use quote::{quote, spanned::Spanned, ToTokens};
|
use quote::{quote, spanned::Spanned, ToTokens};
|
||||||
|
@ -10,21 +27,49 @@ use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
};
|
};
|
||||||
use syn::{
|
use syn::{
|
||||||
bracketed, parenthesized,
|
bracketed,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
parse2, parse_macro_input, Arm, AttributeArgs, FnArg, Ident, ImplItem, ItemImpl, LitStr, Meta,
|
parse2, parse_macro_input, Arm, AttributeArgs, FnArg, Ident, ImplItem, ItemImpl, LitStr, Meta,
|
||||||
NestedMeta, Path, Signature, Stmt, Token,
|
NestedMeta, Path, Signature, Stmt, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern crate sing_parse;
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate sing_util;
|
extern crate sing_util;
|
||||||
|
|
||||||
mod keyword;
|
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.
|
/// The impl block needs to contain all functions as usual.
|
||||||
/// After adding a trait, it can be used by the sing_loop macro.
|
/// 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]
|
#[proc_macro_attribute]
|
||||||
pub fn sing_add_trait(annotated_item: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn sing_add_trait(annotated_item: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as ItemImpl);
|
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),
|
/// Creates a main loop that parses stdin (or the given input stream),
|
||||||
/// evaluates the called function and returns the result.
|
/// 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]
|
#[proc_macro]
|
||||||
pub fn sing_loop(input: TokenStream) -> TokenStream {
|
pub fn sing_loop(input: TokenStream) -> TokenStream {
|
||||||
let span = TokenStream2::from(input.clone()).__span();
|
let span = TokenStream2::from(input.clone()).__span();
|
||||||
|
@ -443,9 +519,6 @@ struct LoopParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for 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> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let trait_obj = input.parse()?;
|
let trait_obj = input.parse()?;
|
||||||
input.parse::<Token![,]>()?;
|
input.parse::<Token![,]>()?;
|
||||||
|
@ -500,10 +573,9 @@ impl Parse for LoopParams {
|
||||||
input.parse::<keyword::msg>()?;
|
input.parse::<keyword::msg>()?;
|
||||||
input.parse::<Token![:]>()?;
|
input.parse::<Token![:]>()?;
|
||||||
|
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
parenthesized!(content in input);
|
bracketed!(content in input);
|
||||||
let m_a_type_err_generic = "The msg keyword expects two variables in the following order: (MessageType, ArgumentType),";
|
let m_a_type_err_generic = "The msg keyword expects two variables in the following order: [MessageType, ArgumentType],";
|
||||||
|
|
||||||
message_type = match content.parse() {
|
message_type = match content.parse() {
|
||||||
Ok(m) => Some(m),
|
Ok(m) => Some(m),
|
||||||
|
@ -542,9 +614,9 @@ impl Parse for LoopParams {
|
||||||
input.parse::<Token![:]>()?;
|
input.parse::<Token![:]>()?;
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
parenthesized!(content in input);
|
bracketed!(content in input);
|
||||||
let lh_inner = content.lookahead1();
|
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) {
|
if lh_inner.peek(Ident) {
|
||||||
let r = content.parse()?;
|
let r = content.parse()?;
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub struct CallObj {
|
||||||
data: Vec<String>,
|
data: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default message representation for the sing_loop! macro.
|
||||||
impl CallObj {
|
impl CallObj {
|
||||||
pub fn new((trait_string, fun_string, index): (Option<String>, String, usize), data: Vec<String>) -> Self {
|
pub fn new((trait_string, fun_string, index): (Option<String>, String, usize), data: Vec<String>) -> Self {
|
||||||
Self{
|
Self{
|
||||||
|
|
|
@ -26,10 +26,12 @@ mod callobj;
|
||||||
|
|
||||||
lalrpop_mod!(fun_parser);
|
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>> {
|
pub fn callobj_to_string(o: CallObj) -> Result<String, Box<dyn Error>> {
|
||||||
Ok(o.to_string())
|
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>> {
|
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
|
// TODO: This should use a "?", but for some reason the error references s
|
||||||
Ok(fun_parser::CallParser::new().parse(&s).unwrap())
|
Ok(fun_parser::CallParser::new().parse(&s).unwrap())
|
||||||
|
|
|
@ -63,14 +63,23 @@ pub enum DeduplicatedFunctionProfile {
|
||||||
Multiple(Vec<String>),
|
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
|
/// function calls that are sent through a sing I/O stream
|
||||||
pub trait TraitCallMessage {
|
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;
|
type Representation;
|
||||||
|
|
||||||
|
/// Returns the called functions name.
|
||||||
fn get_fun_name(&self) -> String;
|
fn get_fun_name(&self) -> String;
|
||||||
|
|
||||||
|
/// Returns the name of the trait the function is called from.
|
||||||
fn get_trait_name(&self) -> Option<String>;
|
fn get_trait_name(&self) -> Option<String>;
|
||||||
|
|
||||||
|
/// Returns the parameters (or arguments) of the function call.
|
||||||
fn get_params(&self) -> Vec<Self::Representation>;
|
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>);
|
fn new_params(&mut self, p: Vec<Self::Representation>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue