<commit_before>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#![recursion_limit = "512"]

extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
    braced, parenthesized,
    parse::{Parse, ParseStream},
    parse_macro_input,
    punctuated::Punctuated,
    spanned::Spanned,
    token::Comma,
    ArgCaptured, Attribute, FnArg, Ident, Pat, ReturnType, Token, Visibility,
};

struct Service {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
    rpcs: Vec<RpcMethod>,
}

struct RpcMethod {
    attrs: Vec<Attribute>,
    ident: Ident,
    args: Punctuated<ArgCaptured, Comma>,
    output: ReturnType,
}

impl Parse for Service {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        let vis = input.parse()?;
        input.parse::<Token![trait]>()?;
        let ident = input.parse()?;
        let content;
        braced!(content in input);
        let mut rpcs = Vec::<RpcMethod>::new();
        while !content.is_empty() {
            rpcs.push(content.parse()?);
        }
        for rpc in &rpcs {
            if rpc.ident == "new" {
                return Err(syn::Error::new(
                    rpc.ident.span(),
                    format!("method name conflicts with generated fn `{}Client::new`", ident)
                ))
            }
            if rpc.ident == "serve" {
                return Err(syn::Error::new(
                    rpc.ident.span(),
                    format!("method name conflicts with generated fn `{}::serve`", ident)
                ))
            }
        }
        Ok(Service {
            attrs,
            vis,
            ident,
            rpcs,
        })
    }
}

impl Parse for RpcMethod {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        input.parse::<Token![async]>()?;
        input.parse::<Token![fn]>()?;
        let ident = input.parse()?;
        let content;
        parenthesized!(content in input);
        let args: Punctuated<FnArg, Comma> = content.parse_terminated(FnArg::parse)?;
        let args = args
            .into_iter()
            .map(|arg| match arg {
                FnArg::Captured(captured) => match captured.pat {
                    Pat::Ident(_) => Ok(captured),
                    _ => {
                        return Err(syn::Error::new(
                            captured.pat.span(),
                            "patterns aren't allowed in RPC args",
                        ))
                    }
                },
                FnArg::SelfRef(self_ref) => {
                    return Err(syn::Error::new(
                        self_ref.span(),
                        "method args cannot start with self",
                    ))
                }
                FnArg::SelfValue(self_val) => {
                    return Err(syn::Error::new(
                        self_val.span(),
                        "method args cannot start with self",
                    ))
                }
                arg => {
                    return Err(syn::Error::new(
                        arg.span(),
                        "method args must be explicitly typed patterns",
                    ))
                }
            })
            .collect::<Result<_, _>>()?;
        let output = input.parse()?;
        input.parse::<Token![;]>()?;

        Ok(RpcMethod {
            attrs,
            ident,
            args,
            output,
        })
    }
}

/// Generates:
/// - service trait
/// - serve fn
/// - client stub struct
/// - new_stub client factory fn
/// - Request and Response enums
/// - ResponseFut Future
#[proc_macro_attribute]
pub fn service(attr: TokenStream, input: TokenStream) -> TokenStream {
    struct EmptyArgs;
    impl Parse for EmptyArgs {
        fn parse(_: ParseStream) -> syn::Result<Self> {
            Ok(EmptyArgs)
        }
    }
    parse_macro_input!(attr as EmptyArgs);

    let Service {
        attrs,
        vis,
        ident,
        rpcs,
    } = parse_macro_input!(input as Service);
    let vis_repeated = std::iter::repeat(vis.clone());

    let camel_case_fn_names: Vec<String> = rpcs
        .iter()
        .map(|rpc| snake_to_camel(&rpc.ident.to_string()))
        .collect();
    let ref outputs: Vec<TokenStream2> = rpcs
        .iter()
        .map(|rpc| match rpc.output {
            ReturnType::Type(_, ref ty) => quote!(#ty),
            ReturnType::Default => quote!(()),
        })
        .collect();
    let future_types: Vec<Ident> = camel_case_fn_names
        .iter()
        .map(|name| Ident::new(&format!("{}Fut", name), ident.span()))
        .collect();
    let ref camel_case_idents: Vec<Ident> = rpcs
        .iter()
        .zip(camel_case_fn_names.iter())
        .map(|(rpc, name)| Ident::new(name, rpc.ident.span()))
        .collect();
    let camel_case_idents2 = camel_case_idents;

    let ref args: Vec<&Punctuated<ArgCaptured, Comma>> = rpcs.iter().map(|rpc| &rpc.args).collect();
    let ref arg_vars: Vec<Punctuated<&Pat, Comma>> = args
        .iter()
        .map(|args| args.iter().map(|arg| &arg.pat).collect())
        .collect();
    let arg_vars2 = arg_vars;
    let ref method_names: Vec<&Ident> = rpcs.iter().map(|rpc| &rpc.ident).collect();
    let method_attrs: Vec<_> = rpcs.iter().map(|rpc| &rpc.attrs).collect();

    let types_and_fns = rpcs
        .iter()
        .zip(future_types.iter())
        .zip(outputs.iter())
        .map(
            |(
                (
                    RpcMethod {
                        attrs, ident, args, ..
                    },
                    future_type,
                ),
                output,
            )| {
                let ty_doc = format!("The response future returned by {}.", ident);
                quote! {
                    #[doc = #ty_doc]
                    type #future_type: std::future::Future<Output = #output>;

                    #( #attrs )*
                    fn #ident(self, context: tarpc::context::Context, #args) -> Self::#future_type;
                }
            },
        );

    let service_name_repeated = std::iter::repeat(ident.clone());
    let service_name_repeated2 = service_name_repeated.clone();

    let client_ident = Ident::new(&format!("{}Client", ident), ident.span());
    let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
    let request_ident_repeated = std::iter::repeat(request_ident.clone());
    let request_ident_repeated2 = request_ident_repeated.clone();
    let response_ident = Ident::new(&format!("{}Response", ident), ident.span());
    let response_ident_repeated = std::iter::repeat(response_ident.clone());
    let response_ident_repeated2 = response_ident_repeated.clone();
    let response_fut_name = format!("{}ResponseFut", ident);
    let response_fut_ident = Ident::new(&response_fut_name, ident.span());
    let response_fut_ident_repeated = std::iter::repeat(response_fut_ident.clone());
    let response_fut_ident_repeated2 = response_fut_ident_repeated.clone();
    let server_ident = Ident::new(&format!("{}Server", ident), ident.span());

    #[cfg(feature = "serde1")]
    let derive_serialize = quote!(#[derive(serde::Serialize, serde::Deserialize)]);
    #[cfg(not(feature = "serde1"))]
    let derive_serialize = quote!();

    let tokens = quote! {
        #( #attrs )*
        #vis trait #ident: Clone + Send + 'static {
            #( #types_and_fns )*

            /// Returns a serving function to use with tarpc::server::Server.
            fn serve(self) -> #server_ident<Self> {
                #server_ident { service: self }
            }
        }

        #[derive(Clone)]
        #vis struct #server_ident<S> {
            service: S,
        }

        impl<S> tarpc::server::Serve<#request_ident> for #server_ident<S>
            where S: #ident
        {
            type Resp = #response_ident;
            type Fut = #response_fut_ident<S>;

            fn serve(self, ctx: tarpc::context::Context, req: #request_ident) -> Self::Fut {
                match req {
                    #(
                        #request_ident_repeated::#camel_case_idents{ #arg_vars } => {
                            #response_fut_ident_repeated2::#camel_case_idents2(
                                #service_name_repeated2::#method_names(
                                    self.service, ctx, #arg_vars2))
                        }
                    )*
                }
            }
        }

        /// The request sent over the wire from the client to the server.
        #[derive(Debug)]
        #derive_serialize
        #vis enum #request_ident {
            #( #camel_case_idents{ #args } ),*
        }

        /// The response sent over the wire from the server to the client.
        #[derive(Debug)]
        #derive_serialize
        #vis enum #response_ident {
            #( #camel_case_idents(#outputs) ),*
        }

        /// A future resolving to a server response.
        #vis enum #response_fut_ident<S: #ident> {
            #( #camel_case_idents(<S as #service_name_repeated>::#future_types) ),*
        }

        impl<S: #ident> std::fmt::Debug for #response_fut_ident<S> {
            fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
                fmt.debug_struct(#response_fut_name).finish()
            }
        }

        impl<S: #ident> std::future::Future for #response_fut_ident<S> {
            type Output = #response_ident;

            fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>)
                -> std::task::Poll<#response_ident>
            {
                unsafe {
                    match std::pin::Pin::get_unchecked_mut(self) {
                        #(
                            #response_fut_ident_repeated::#camel_case_idents(resp) =>
                                std::pin::Pin::new_unchecked(resp)
                                    .poll(cx)
                                    .map(#response_ident_repeated::#camel_case_idents2),
                        )*
                    }
                }
            }
        }

        #[allow(unused)]
        #[derive(Clone, Debug)]
        /// The client stub that makes RPC calls to the server. Exposes a Future interface.
        #vis struct #client_ident<C = tarpc::client::Channel<#request_ident, #response_ident>>(C);

        impl<C> From<C> for #client_ident<C>
            where for <'a> C: tarpc::Client<'a, #request_ident, Response = #response_ident>
        {
            fn from(client: C) -> Self {
                #client_ident(client)
            }
        }

        impl #client_ident {
            /// Returns a new client stub that sends requests over the given transport.
            #vis async fn new<T>(config: tarpc::client::Config, transport: T)
                -> std::io::Result<Self>
            where
                T: tarpc::Transport<tarpc::ClientMessage<#request_ident>, tarpc::Response<#response_ident>> + Send + 'static
            {
                Ok(#client_ident(tarpc::client::new(config, transport).await?))
            }

        }

        impl<C> #client_ident<C>
            where for<'a> C: tarpc::Client<'a, #request_ident, Response = #response_ident>
        {
            #(
                #[allow(unused)]
                #( #method_attrs )*
                #vis_repeated fn #method_names(&mut self, ctx: tarpc::context::Context, #args)
                    -> impl std::future::Future<Output = std::io::Result<#outputs>> + '_ {
                    let request = #request_ident_repeated2::#camel_case_idents { #arg_vars };
                    let resp = tarpc::Client::call(&mut self.0, ctx, request);
                    async move {
                        match resp.await? {
                            #response_ident_repeated2::#camel_case_idents2(msg) => std::result::Result::Ok(msg),
                            _ => unreachable!(),
                        }
                    }
                }
            )*
        }
    };

    tokens.into()
}

fn snake_to_camel(ident_str: &str) -> String {
    let mut camel_ty = String::new();
    let mut chars = ident_str.chars();

    let mut last_char_was_underscore = true;
    while let Some(c) = chars.next() {
        match c {
            '_' => last_char_was_underscore = true,
            c if last_char_was_underscore => {
                camel_ty.extend(c.to_uppercase());
                last_char_was_underscore = false;
            }
            c => camel_ty.extend(c.to_lowercase()),
        }
    }

    camel_ty
}

#[test]
fn snake_to_camel_basic() {
    assert_eq!(snake_to_camel("abc_def"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_suffix() {
    assert_eq!(snake_to_camel("abc_def_"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_prefix() {
    assert_eq!(snake_to_camel("_abc_def"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_consecutive() {
    assert_eq!(snake_to_camel("abc__def"), "AbcDef");
}

#[test]
fn snake_to_camel_capital_in_middle() {
    assert_eq!(snake_to_camel("aBc_dEf"), "AbcDef");
}
<commit_msg>Choose a slightly less obvious name for Serve impl.<commit_after>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#![recursion_limit = "512"]

extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
    braced, parenthesized,
    parse::{Parse, ParseStream},
    parse_macro_input,
    punctuated::Punctuated,
    spanned::Spanned,
    token::Comma,
    ArgCaptured, Attribute, FnArg, Ident, Pat, ReturnType, Token, Visibility,
};

struct Service {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
    rpcs: Vec<RpcMethod>,
}

struct RpcMethod {
    attrs: Vec<Attribute>,
    ident: Ident,
    args: Punctuated<ArgCaptured, Comma>,
    output: ReturnType,
}

impl Parse for Service {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        let vis = input.parse()?;
        input.parse::<Token![trait]>()?;
        let ident = input.parse()?;
        let content;
        braced!(content in input);
        let mut rpcs = Vec::<RpcMethod>::new();
        while !content.is_empty() {
            rpcs.push(content.parse()?);
        }
        for rpc in &rpcs {
            if rpc.ident == "new" {
                return Err(syn::Error::new(
                    rpc.ident.span(),
                    format!("method name conflicts with generated fn `{}Client::new`", ident)
                ))
            }
            if rpc.ident == "serve" {
                return Err(syn::Error::new(
                    rpc.ident.span(),
                    format!("method name conflicts with generated fn `{}::serve`", ident)
                ))
            }
        }
        Ok(Service {
            attrs,
            vis,
            ident,
            rpcs,
        })
    }
}

impl Parse for RpcMethod {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        input.parse::<Token![async]>()?;
        input.parse::<Token![fn]>()?;
        let ident = input.parse()?;
        let content;
        parenthesized!(content in input);
        let args: Punctuated<FnArg, Comma> = content.parse_terminated(FnArg::parse)?;
        let args = args
            .into_iter()
            .map(|arg| match arg {
                FnArg::Captured(captured) => match captured.pat {
                    Pat::Ident(_) => Ok(captured),
                    _ => {
                        return Err(syn::Error::new(
                            captured.pat.span(),
                            "patterns aren't allowed in RPC args",
                        ))
                    }
                },
                FnArg::SelfRef(self_ref) => {
                    return Err(syn::Error::new(
                        self_ref.span(),
                        "method args cannot start with self",
                    ))
                }
                FnArg::SelfValue(self_val) => {
                    return Err(syn::Error::new(
                        self_val.span(),
                        "method args cannot start with self",
                    ))
                }
                arg => {
                    return Err(syn::Error::new(
                        arg.span(),
                        "method args must be explicitly typed patterns",
                    ))
                }
            })
            .collect::<Result<_, _>>()?;
        let output = input.parse()?;
        input.parse::<Token![;]>()?;

        Ok(RpcMethod {
            attrs,
            ident,
            args,
            output,
        })
    }
}

/// Generates:
/// - service trait
/// - serve fn
/// - client stub struct
/// - new_stub client factory fn
/// - Request and Response enums
/// - ResponseFut Future
#[proc_macro_attribute]
pub fn service(attr: TokenStream, input: TokenStream) -> TokenStream {
    struct EmptyArgs;
    impl Parse for EmptyArgs {
        fn parse(_: ParseStream) -> syn::Result<Self> {
            Ok(EmptyArgs)
        }
    }
    parse_macro_input!(attr as EmptyArgs);

    let Service {
        attrs,
        vis,
        ident,
        rpcs,
    } = parse_macro_input!(input as Service);
    let vis_repeated = std::iter::repeat(vis.clone());

    let camel_case_fn_names: Vec<String> = rpcs
        .iter()
        .map(|rpc| snake_to_camel(&rpc.ident.to_string()))
        .collect();
    let ref outputs: Vec<TokenStream2> = rpcs
        .iter()
        .map(|rpc| match rpc.output {
            ReturnType::Type(_, ref ty) => quote!(#ty),
            ReturnType::Default => quote!(()),
        })
        .collect();
    let future_types: Vec<Ident> = camel_case_fn_names
        .iter()
        .map(|name| Ident::new(&format!("{}Fut", name), ident.span()))
        .collect();
    let ref camel_case_idents: Vec<Ident> = rpcs
        .iter()
        .zip(camel_case_fn_names.iter())
        .map(|(rpc, name)| Ident::new(name, rpc.ident.span()))
        .collect();
    let camel_case_idents2 = camel_case_idents;

    let ref args: Vec<&Punctuated<ArgCaptured, Comma>> = rpcs.iter().map(|rpc| &rpc.args).collect();
    let ref arg_vars: Vec<Punctuated<&Pat, Comma>> = args
        .iter()
        .map(|args| args.iter().map(|arg| &arg.pat).collect())
        .collect();
    let arg_vars2 = arg_vars;
    let ref method_names: Vec<&Ident> = rpcs.iter().map(|rpc| &rpc.ident).collect();
    let method_attrs: Vec<_> = rpcs.iter().map(|rpc| &rpc.attrs).collect();

    let types_and_fns = rpcs
        .iter()
        .zip(future_types.iter())
        .zip(outputs.iter())
        .map(
            |(
                (
                    RpcMethod {
                        attrs, ident, args, ..
                    },
                    future_type,
                ),
                output,
            )| {
                let ty_doc = format!("The response future returned by {}.", ident);
                quote! {
                    #[doc = #ty_doc]
                    type #future_type: std::future::Future<Output = #output>;

                    #( #attrs )*
                    fn #ident(self, context: tarpc::context::Context, #args) -> Self::#future_type;
                }
            },
        );

    let service_name_repeated = std::iter::repeat(ident.clone());
    let service_name_repeated2 = service_name_repeated.clone();

    let client_ident = Ident::new(&format!("{}Client", ident), ident.span());
    let request_ident = Ident::new(&format!("{}Request", ident), ident.span());
    let request_ident_repeated = std::iter::repeat(request_ident.clone());
    let request_ident_repeated2 = request_ident_repeated.clone();
    let response_ident = Ident::new(&format!("{}Response", ident), ident.span());
    let response_ident_repeated = std::iter::repeat(response_ident.clone());
    let response_ident_repeated2 = response_ident_repeated.clone();
    let response_fut_name = format!("{}ResponseFut", ident);
    let response_fut_ident = Ident::new(&response_fut_name, ident.span());
    let response_fut_ident_repeated = std::iter::repeat(response_fut_ident.clone());
    let response_fut_ident_repeated2 = response_fut_ident_repeated.clone();
    let server_ident = Ident::new(&format!("Serve{}", ident), ident.span());

    #[cfg(feature = "serde1")]
    let derive_serialize = quote!(#[derive(serde::Serialize, serde::Deserialize)]);
    #[cfg(not(feature = "serde1"))]
    let derive_serialize = quote!();

    let tokens = quote! {
        #( #attrs )*
        #vis trait #ident: Clone + Send + 'static {
            #( #types_and_fns )*

            /// Returns a serving function to use with tarpc::server::Server.
            fn serve(self) -> #server_ident<Self> {
                #server_ident { service: self }
            }
        }

        #[derive(Clone)]
        #vis struct #server_ident<S> {
            service: S,
        }

        impl<S> tarpc::server::Serve<#request_ident> for #server_ident<S>
            where S: #ident
        {
            type Resp = #response_ident;
            type Fut = #response_fut_ident<S>;

            fn serve(self, ctx: tarpc::context::Context, req: #request_ident) -> Self::Fut {
                match req {
                    #(
                        #request_ident_repeated::#camel_case_idents{ #arg_vars } => {
                            #response_fut_ident_repeated2::#camel_case_idents2(
                                #service_name_repeated2::#method_names(
                                    self.service, ctx, #arg_vars2))
                        }
                    )*
                }
            }
        }

        /// The request sent over the wire from the client to the server.
        #[derive(Debug)]
        #derive_serialize
        #vis enum #request_ident {
            #( #camel_case_idents{ #args } ),*
        }

        /// The response sent over the wire from the server to the client.
        #[derive(Debug)]
        #derive_serialize
        #vis enum #response_ident {
            #( #camel_case_idents(#outputs) ),*
        }

        /// A future resolving to a server response.
        #vis enum #response_fut_ident<S: #ident> {
            #( #camel_case_idents(<S as #service_name_repeated>::#future_types) ),*
        }

        impl<S: #ident> std::fmt::Debug for #response_fut_ident<S> {
            fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
                fmt.debug_struct(#response_fut_name).finish()
            }
        }

        impl<S: #ident> std::future::Future for #response_fut_ident<S> {
            type Output = #response_ident;

            fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>)
                -> std::task::Poll<#response_ident>
            {
                unsafe {
                    match std::pin::Pin::get_unchecked_mut(self) {
                        #(
                            #response_fut_ident_repeated::#camel_case_idents(resp) =>
                                std::pin::Pin::new_unchecked(resp)
                                    .poll(cx)
                                    .map(#response_ident_repeated::#camel_case_idents2),
                        )*
                    }
                }
            }
        }

        #[allow(unused)]
        #[derive(Clone, Debug)]
        /// The client stub that makes RPC calls to the server. Exposes a Future interface.
        #vis struct #client_ident<C = tarpc::client::Channel<#request_ident, #response_ident>>(C);

        impl<C> From<C> for #client_ident<C>
            where for <'a> C: tarpc::Client<'a, #request_ident, Response = #response_ident>
        {
            fn from(client: C) -> Self {
                #client_ident(client)
            }
        }

        impl #client_ident {
            /// Returns a new client stub that sends requests over the given transport.
            #vis async fn new<T>(config: tarpc::client::Config, transport: T)
                -> std::io::Result<Self>
            where
                T: tarpc::Transport<tarpc::ClientMessage<#request_ident>, tarpc::Response<#response_ident>> + Send + 'static
            {
                Ok(#client_ident(tarpc::client::new(config, transport).await?))
            }

        }

        impl<C> #client_ident<C>
            where for<'a> C: tarpc::Client<'a, #request_ident, Response = #response_ident>
        {
            #(
                #[allow(unused)]
                #( #method_attrs )*
                #vis_repeated fn #method_names(&mut self, ctx: tarpc::context::Context, #args)
                    -> impl std::future::Future<Output = std::io::Result<#outputs>> + '_ {
                    let request = #request_ident_repeated2::#camel_case_idents { #arg_vars };
                    let resp = tarpc::Client::call(&mut self.0, ctx, request);
                    async move {
                        match resp.await? {
                            #response_ident_repeated2::#camel_case_idents2(msg) => std::result::Result::Ok(msg),
                            _ => unreachable!(),
                        }
                    }
                }
            )*
        }
    };

    tokens.into()
}

fn snake_to_camel(ident_str: &str) -> String {
    let mut camel_ty = String::new();
    let mut chars = ident_str.chars();

    let mut last_char_was_underscore = true;
    while let Some(c) = chars.next() {
        match c {
            '_' => last_char_was_underscore = true,
            c if last_char_was_underscore => {
                camel_ty.extend(c.to_uppercase());
                last_char_was_underscore = false;
            }
            c => camel_ty.extend(c.to_lowercase()),
        }
    }

    camel_ty
}

#[test]
fn snake_to_camel_basic() {
    assert_eq!(snake_to_camel("abc_def"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_suffix() {
    assert_eq!(snake_to_camel("abc_def_"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_prefix() {
    assert_eq!(snake_to_camel("_abc_def"), "AbcDef");
}

#[test]
fn snake_to_camel_underscore_consecutive() {
    assert_eq!(snake_to_camel("abc__def"), "AbcDef");
}

#[test]
fn snake_to_camel_capital_in_middle() {
    assert_eq!(snake_to_camel("aBc_dEf"), "AbcDef");
}
<|endoftext|>


<commit_before>extern crate regex;

use std::cmp;
use std::option::Option;
use self::regex::Regex;

pub struct LexRule {
    priority: i32,
    token: String,
    expr: Regex
}

impl cmp::Eq for LexRule {}

impl cmp::PartialEq for LexRule {
    fn eq (&self, other:&Self) -> bool{
        (self.priority, &self.token, &self.expr) == (other.priority, &other.token, &other.expr)
    }
}

impl cmp::Ord for LexRule {
    fn cmp (&self, other: &Self) -> cmp::Ordering {
        self.priority.cmp(&other.priority)
    }
}

impl cmp::PartialOrd for LexRule {
    fn partial_cmp (&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.priority.cmp(&other.priority))
    }
}
<commit_msg>fixed imports<commit_after>use std::cmp;
use std::option::Option;
use regex::Regex;

pub struct LexRule {
    priority: i32,
    token: String,
    expr: Regex
}

impl cmp::Eq for LexRule {}

impl cmp::PartialEq for LexRule {
    fn eq (&self, other:&Self) -> bool{
        (self.priority, &self.token, &self.expr) == (other.priority, &other.token, &other.expr)
    }
}

impl cmp::Ord for LexRule {
    fn cmp (&self, other: &Self) -> cmp::Ordering {
        self.priority.cmp(&other.priority)
    }
}

impl cmp::PartialOrd for LexRule {
    fn partial_cmp (&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.priority.cmp(&other.priority))
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};
use hir::db::HirDatabase;
use ra_syntax::{
    algo::replace_children,
    ast::edit::IndentLevel,
    ast::make,
    ast::Block,
    ast::ContinueExpr,
    ast::IfExpr,
    ast::ReturnExpr,
    AstNode,
    SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
};
use std::ops::RangeInclusive;

pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let if_expr: IfExpr = ctx.node_at_offset()?;
    let expr = if_expr.condition()?.expr()?;
    let then_block = if_expr.then_branch()?.block()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(Block::cast)?;

    if parent_block.expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?.clone();
    if ReturnExpr::can_cast(first_in_then_block.kind())
        || ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ReturnExpr::can_cast(x.kind()) || ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?.parent()?;

    let early_expression = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => Some("continue;"),
        FN_DEF => Some("return;"),
        _ => None,
    }?;

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
    let cursor_position = ctx.frange.range.start();

    ctx.add_action(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
        let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
        let new_if_expr =
            if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
        let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
        let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
        let end_of_then =
            if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                end_of_then.prev_sibling_or_token().unwrap()
            } else {
                end_of_then
            };
        let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain(
            then_block_items
                .syntax()
                .children_with_tokens()
                .skip(1)
                .take_while(|i| *i != end_of_then),
        );
        let new_block = replace_children(
            &parent_block.syntax(),
            RangeInclusive::new(
                if_expr.clone().syntax().clone().into(),
                if_expr.syntax().clone().into(),
            ),
            &mut new_if_and_then_statements,
        );
        edit.target(if_expr.syntax().text_range());
        edit.replace_ast(parent_block, Block::cast(new_block).unwrap());
        edit.set_cursor(cursor_position);
    });
    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if<|> true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if<|> !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if<|> true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>minor cleanup<commit_after>//! Assist: `convert_to_guarded_return`
//!
//! Replace a large conditional with a guarded return.
//!
//! ```notrust
//! fn <|>main() {
//!     if cond {
//!         foo();
//!         bar();
//!     }
//! }
//! ```
//! ->
//! ```notrust
//! fn main() {
//!     if !cond {
//!         return;
//!     }
//!     foo();
//!     bar();
//! }
//! ```

use std::ops::RangeInclusive;

use hir::db::HirDatabase;
use ra_syntax::{
    algo::replace_children,
    ast::{self, edit::IndentLevel, make},
    AstNode,
    SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
};

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};

pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let if_expr: ast::IfExpr = ctx.node_at_offset()?;
    let expr = if_expr.condition()?.expr()?;
    let then_block = if_expr.then_branch()?.block()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?;

    if parent_block.expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?.clone();
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?.parent()?;

    let early_expression = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => Some("continue;"),
        FN_DEF => Some("return;"),
        _ => None,
    }?;

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
    let cursor_position = ctx.frange.range.start();

    ctx.add_action(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
        let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
        let new_if_expr =
            if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
        let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
        let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
        let end_of_then =
            if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                end_of_then.prev_sibling_or_token().unwrap()
            } else {
                end_of_then
            };
        let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain(
            then_block_items
                .syntax()
                .children_with_tokens()
                .skip(1)
                .take_while(|i| *i != end_of_then),
        );
        let new_block = replace_children(
            &parent_block.syntax(),
            RangeInclusive::new(
                if_expr.clone().syntax().clone().into(),
                if_expr.syntax().clone().into(),
            ),
            &mut new_if_and_then_statements,
        );
        edit.target(if_expr.syntax().text_range());
        edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
        edit.set_cursor(cursor_position);
    });
    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if<|> true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if<|> !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if<|> true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>use hyper::server::Request;
use std::sync::Mutex;
extern crate redis;
extern crate byteorder;

use common::*;
use self::byteorder::{BigEndian, WriteBytesExt};
use std::net::IpAddr;
use std::net::Ipv4Addr;
extern crate time;
use std::error::Error;

pub fn announce(req: &Request, redis_connection: &Mutex<redis::Connection>) -> Result<Vec<u8>, String>
{
    let query_hashmap = query_hashmap(&req.uri);

    // Check we have everything we need
    let info_hash = try!(query_hashmap.get("info_hash")
        .ok_or_else(|| "No info_hash specified".to_owned())
        .and_then(|i| parse_info_hash(i)));
    let peer_id = try!(query_hashmap.get("peer_id")
        .ok_or_else(|| "No peer_id specified".to_owned())
        .and_then(|i| parse_peer_id(i)));
    let port = try!(query_hashmap.get("port")
        .ok_or_else(|| "No port specified".to_owned())
        .and_then(|i| i.parse::<u16>().map_err(|_| "Invalid port specified".to_owned())));
    let left = try!(query_hashmap.get("left")
        .ok_or_else(|| "No left specified".to_owned())
        .and_then(|i| i.parse::<u64>().map_err(|_| "Invalid left specified".to_owned())));
    let compact = try!(query_hashmap.get("compact")
        .ok_or_else(|| "No compact specified".to_owned())
        .and_then(|i| i.parse::<u64>().map_err(|_| "Invalid compact specified".to_owned())));
    if compact != 1 {
        return Err("This tracker only supports compact responses".to_owned());
    }
    let ip = req.remote_addr.ip();

    // Generate compact peer entry
    let ip_v4 = match ip {
        IpAddr::V4(i) => i,
        _ => return Err("Ipv6 not implemented yet".to_owned()),
    };
    let peer_v4 = generate_peer_ipv4(ip_v4, port);

    // Get ready for redis queries
    let key_base = format!("torrent:{}", info_hash);
    let key_seeds = format!("{}:seeds", key_base);
    let key_peers = format!("{}:peers", key_base);
    let time_now = time::get_time().sec;
    let time_drop = time_now - 60;
    let mut pipe = redis::pipe();

    // Prune out old entries
    pipe.cmd("ZREMRANGEBYSCORE").arg(&*key_seeds).arg(0).arg(time_drop).ignore();
    pipe.cmd("ZREMRANGEBYSCORE").arg(&*key_peers).arg(0).arg(time_drop).ignore();

    // Get total count of peers/seeds
    pipe.cmd("ZCARD").arg(&*key_seeds);
    pipe.cmd("ZCARD").arg(&*key_peers);

    // Get peers and seeds
    let numwant = 50;
    pipe.cmd("ZRANGE").arg(&*key_seeds).arg(0).arg(numwant);
    pipe.cmd("ZRANGE").arg(&*key_peers).arg(0).arg(numwant);

    // Add
    if left == 0 {
        pipe.cmd("ZADD").arg(&*key_seeds).arg(time_now).arg(peer_v4).ignore();
    }
    else {
        pipe.cmd("ZADD").arg(&*key_peers).arg(time_now).arg(peer_v4).ignore();
    }

    // Unlock mutex and go!
    let results: (u32, u32, Vec<Vec<u8>>, Vec<Vec<u8>>);
    {
        let r = redis_connection.lock().unwrap();
        results = try!(pipe.query(&(*r))
            .map_err(|e| format!("Redis error: {}", e.description())));
    }

    let (total_seeds, total_peers, seeds, peers) = results;

    // Begin building output
    let mut response: Vec<u8> = Vec::new();
    response.extend(format!("d8:completei{}e10:incompletei{}e8:intervali{}e5:peers",
        total_seeds, total_peers, 60).bytes());

    // dont give seeds to seeds
    if left == 0 {
        response.extend(format!("{}:", 6*peers.len()).bytes());
        for i in peers {
            response.extend(i);
        }
    }
    else {
        response.extend(format!("{}:", 6*seeds.len() + 6*peers.len()).bytes());
        for i in seeds {
            response.extend(i);
        }
        for i in peers {
            response.extend(i);
        }
    }
    response.extend(b"e");

    return Ok(response);
}

fn generate_peer_ipv4(ip: Ipv4Addr, port: u16) -> Vec<u8> {
    let mut retval = Vec::with_capacity(6);
    retval.extend(ip.octets().iter());
    retval.write_u16::<BigEndian>(port).unwrap();
    return retval;
}

#[test]
fn generate_peer_ipv4_test() {
    assert_eq!(generate_peer_ipv4(Ipv4Addr::new(127, 0, 0, 1), 0x3039), &[127, 0, 0, 1, 0x30, 0x39]);
}
<commit_msg>announce: put interval and drop threshold as constants<commit_after>use hyper::server::Request;
use std::sync::Mutex;
extern crate redis;
extern crate byteorder;

use common::*;
use self::byteorder::{BigEndian, WriteBytesExt};
use std::net::IpAddr;
use std::net::Ipv4Addr;
extern crate time;
use std::error::Error;

const ANNOUNCE_INTERVAL: i64 = 60;
const DROP_THRESHOLD: i64 = 3*ANNOUNCE_INTERVAL;

pub fn announce(req: &Request, redis_connection: &Mutex<redis::Connection>) -> Result<Vec<u8>, String>
{
    let query_hashmap = query_hashmap(&req.uri);

    // Check we have everything we need
    let info_hash = try!(query_hashmap.get("info_hash")
        .ok_or_else(|| "No info_hash specified".to_owned())
        .and_then(|i| parse_info_hash(i)));
    let peer_id = try!(query_hashmap.get("peer_id")
        .ok_or_else(|| "No peer_id specified".to_owned())
        .and_then(|i| parse_peer_id(i)));
    let port = try!(query_hashmap.get("port")
        .ok_or_else(|| "No port specified".to_owned())
        .and_then(|i| i.parse::<u16>().map_err(|_| "Invalid port specified".to_owned())));
    let left = try!(query_hashmap.get("left")
        .ok_or_else(|| "No left specified".to_owned())
        .and_then(|i| i.parse::<u64>().map_err(|_| "Invalid left specified".to_owned())));
    let compact = try!(query_hashmap.get("compact")
        .ok_or_else(|| "No compact specified".to_owned())
        .and_then(|i| i.parse::<u64>().map_err(|_| "Invalid compact specified".to_owned())));
    if compact != 1 {
        return Err("This tracker only supports compact responses".to_owned());
    }
    let ip = req.remote_addr.ip();

    // Generate compact peer entry
    let ip_v4 = match ip {
        IpAddr::V4(i) => i,
        _ => return Err("Ipv6 not implemented yet".to_owned()),
    };
    let peer_v4 = generate_peer_ipv4(ip_v4, port);

    // Get ready for redis queries
    let key_base = format!("torrent:{}", info_hash);
    let key_seeds = format!("{}:seeds", key_base);
    let key_peers = format!("{}:peers", key_base);
    let time_now = time::get_time().sec;
    let time_drop = time_now - DROP_THRESHOLD;
    let mut pipe = redis::pipe();

    // Prune out old entries
    pipe.cmd("ZREMRANGEBYSCORE").arg(&*key_seeds).arg(0).arg(time_drop).ignore();
    pipe.cmd("ZREMRANGEBYSCORE").arg(&*key_peers).arg(0).arg(time_drop).ignore();

    // Get total count of peers/seeds
    pipe.cmd("ZCARD").arg(&*key_seeds);
    pipe.cmd("ZCARD").arg(&*key_peers);

    // Get peers and seeds
    let numwant = 50;
    pipe.cmd("ZRANGE").arg(&*key_seeds).arg(0).arg(numwant);
    pipe.cmd("ZRANGE").arg(&*key_peers).arg(0).arg(numwant);

    // Add
    if left == 0 {
        pipe.cmd("ZADD").arg(&*key_seeds).arg(time_now).arg(peer_v4).ignore();
    }
    else {
        pipe.cmd("ZADD").arg(&*key_peers).arg(time_now).arg(peer_v4).ignore();
    }

    // Unlock mutex and go!
    let results: (u32, u32, Vec<Vec<u8>>, Vec<Vec<u8>>);
    {
        let r = redis_connection.lock().unwrap();
        results = try!(pipe.query(&(*r))
            .map_err(|e| format!("Redis error: {}", e.description())));
    }

    let (total_seeds, total_peers, seeds, peers) = results;

    // Begin building output
    let mut response: Vec<u8> = Vec::new();
    response.extend(format!("d8:completei{}e10:incompletei{}e8:intervali{}e5:peers",
        total_seeds, total_peers, ANNOUNCE_INTERVAL).bytes());

    // dont give seeds to seeds
    if left == 0 {
        response.extend(format!("{}:", 6*peers.len()).bytes());
        for i in peers {
            response.extend(i);
        }
    }
    else {
        response.extend(format!("{}:", 6*seeds.len() + 6*peers.len()).bytes());
        for i in seeds {
            response.extend(i);
        }
        for i in peers {
            response.extend(i);
        }
    }
    response.extend(b"e");

    return Ok(response);
}

fn generate_peer_ipv4(ip: Ipv4Addr, port: u16) -> Vec<u8> {
    let mut retval = Vec::with_capacity(6);
    retval.extend(ip.octets().iter());
    retval.write_u16::<BigEndian>(port).unwrap();
    return retval;
}

#[test]
fn generate_peer_ipv4_test() {
    assert_eq!(generate_peer_ipv4(Ipv4Addr::new(127, 0, 0, 1), 0x3039), &[127, 0, 0, 1, 0x30, 0x39]);
}

#[test]
fn check_announce_interval_test() {
    assert!(ANNOUNCE_INTERVAL > 0);
}

#[test]
fn check_drop_threshold_test() {
    assert!(DROP_THRESHOLD > ANNOUNCE_INTERVAL);
}
<|endoftext|>


<commit_before>#[allow(unused)]

use std;
use hyper;
use hyper::method;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::Read;
use serde_json;
<commit_msg>remove unused file<commit_after><|endoftext|>


<commit_before>pub const SMALL: f64 = 1.0e-15;
<commit_msg>restore tests<commit_after>pub const SMALL: f64 = 1.0e-14;
<|endoftext|>


<commit_before>use std::iter::Peekable;
use { Token, TokenValue };
use environment::ParsingEnvironment;
use Result;
use error::{ Error, ErrorMessage };
use operator::OperatorOptions;

mod body;
mod expr;
mod module;

pub trait Parse<'c> {
    type Output;

    fn parse<'p, I>(parser: &mut Context<'p, I>)
        -> Result<Self::Output>
    where
        I: Iterator<Item=Result<Token<'c>>>;
}

pub struct Context<'p, I: 'p>
    where
        I: Iterator
{
    pub env: &'p ParsingEnvironment,
    pub tokens: Peekable<&'p mut I>,
}

impl<'p, I: 'p> Context<'p, I>
    where
        I: Iterator
{
    pub fn new<'r, 'c>(
        env: &'r ParsingEnvironment,
        tokens: &'r mut I
    ) -> Context<'r, I>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Context {
            env: env,
            tokens: tokens.peekable(),
        }
    }

    pub fn current<'r, 'c>(&'r mut self) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Ok(match self.tokens.peek() {
            Some(&Ok(ref t)) => t.clone(),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        })
    }

    pub fn maybe_current<'r, 'c>(&'r mut self) -> Result<Option<Token<'c>>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Ok(match self.tokens.peek() {
            Some(&Ok(ref t)) => Some(t.clone()),
            None => None,
            Some(&Err(ref e)) => return Err(e.clone()),
        })
    }

    pub fn next<'r, 'c>(&'r mut self) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let token = match self.tokens.peek() {
            Some(&Ok(ref t)) => t.clone(),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        };

        match self.tokens.next() {
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(Err(e)) => return Err(e),
            _ => (),
        };

        Ok(token)
    }

    pub fn skip_to_next_if<'r, 'c>(&'r mut self, expected: TokenValue<'c>) -> Result<bool>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let skip = match self.tokens.peek() {
            Some(&Ok(ref token)) if token.value == expected => true,
            _ => false,
        };
        if skip {
            match self.tokens.next() {
                Some(Ok(_)) => Ok(true),
                None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
                Some(Err(e)) => return Err(e),
            }
        } else {
            Ok(false)
        }
    }

    pub fn expect<'r, 'c>(&'r mut self, expected: TokenValue<'c>) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let token = match self.tokens.peek() {
            Some(&Ok(ref t)) if t.value == expected => t.clone(),
            Some(&Ok(ref t)) => return Err(Error::new_at(
                ErrorMessage::ExpectedOtherTokenValue((t.value.into(), expected.into())),
                t.line
            )),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        };
        try!(self.next());

        Ok(token)
    }

    pub fn get_operator_options<'r, 'c>(&'r self, op_str: &'c str) -> &'r OperatorOptions {
        self.env.operators
            .get(op_str)
            .expect("twig bug: operator that was lexed was not found when parsing")
    }
}
<commit_msg>Parser doc.<commit_after>use std::iter::Peekable;
use { Token, TokenValue };
use environment::ParsingEnvironment;
use Result;
use error::{ Error, ErrorMessage };
use operator::OperatorOptions;

mod body;
mod expr;
mod module;

pub trait Parse<'c> {
    type Output;

    fn parse<'p, I>(parser: &mut Context<'p, I>)
        -> Result<Self::Output>
    where
        I: Iterator<Item=Result<Token<'c>>>;
}

pub struct Context<'p, I: 'p>
    where
        I: Iterator
{
    pub env: &'p ParsingEnvironment,
    pub tokens: Peekable<&'p mut I>,
}

/// Helpers for manipulating and inspecting token iterators when creating AST.
///
/// Has methods to inspect state, like "current" token, and advance to next.
///
/// Current token is actually implemented as "peekable" next token. However,
/// in all parsing code this "peekable" becomes "current".
impl<'p, I: 'p> Context<'p, I>
    where
        I: Iterator
{
    pub fn new<'r, 'c>(
        env: &'r ParsingEnvironment,
        tokens: &'r mut I
    ) -> Context<'r, I>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Context {
            env: env,
            tokens: tokens.peekable(),
        }
    }

    /// Get current token or fail.
    ///
    /// Returns current token, does not modify iterator position.
    /// Expects current token to exist, and if it is not (the end of file), returns
    /// UnexpectedEndOfTemplate error.
    pub fn current<'r, 'c>(&'r mut self) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Ok(match self.tokens.peek() {
            Some(&Ok(ref t)) => t.clone(),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        })
    }

    /// Get current token or the end of stream.
    ///
    /// Returns current token, does not modify iterator position.
    /// If the end of stream, returns None.
    pub fn maybe_current<'r, 'c>(&'r mut self) -> Result<Option<Token<'c>>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        Ok(match self.tokens.peek() {
            Some(&Ok(ref t)) => Some(t.clone()),
            None => None,
            Some(&Err(ref e)) => return Err(e.clone()),
        })
    }

    /// Advances to the next token and returns previous.
    ///
    /// Expects the next token to exist. If it does not exist (the end of file), returns
    /// UnexpectedEndOfTemplate error.
    pub fn next<'r, 'c>(&'r mut self) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let token = match self.tokens.peek() {
            Some(&Ok(ref t)) => t.clone(),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        };

        match self.tokens.next() {
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(Err(e)) => return Err(e),
            _ => (),
        };

        Ok(token)
    }

    /// Advances to the next token if expected token value is the same as current and
    /// returns current.
    ///
    /// Expects these tokens to exist. If they do not exist (the end of file), returns
    /// UnexpectedEndOfTemplate error.
    pub fn skip_to_next_if<'r, 'c>(&'r mut self, expected: TokenValue<'c>) -> Result<bool>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let skip = match self.tokens.peek() {
            Some(&Ok(ref token)) if token.value == expected => true,
            _ => false,
        };
        if skip {
            match self.tokens.next() {
                Some(Ok(_)) => Ok(true),
                None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
                Some(Err(e)) => return Err(e),
            }
        } else {
            Ok(false)
        }
    }

    /// Expects the current token to match value and advances to next token.
    ///
    /// Expects these tokens to exist. If they do not exist (the end of file), returns
    /// UnexpectedEndOfTemplate error.
    pub fn expect<'r, 'c>(&'r mut self, expected: TokenValue<'c>) -> Result<Token<'c>>
        where I: Iterator<Item=Result<Token<'c>>>
    {
        let token = match self.tokens.peek() {
            Some(&Ok(ref t)) if t.value == expected => t.clone(),
            Some(&Ok(ref t)) => return Err(Error::new_at(
                ErrorMessage::ExpectedOtherTokenValue((t.value.into(), expected.into())),
                t.line
            )),
            None => return Err(Error::new(ErrorMessage::UnexpectedEndOfTemplate)),
            Some(&Err(ref e)) => return Err(e.clone()),
        };
        try!(self.next());

        Ok(token)
    }

    /// Returns options structure for specified operator.
    ///
    /// Operator must exist in environment, otherwise panics.
    pub fn get_operator_options<'r, 'c>(&'r self, op_str: &'c str) -> &'r OperatorOptions {
        self.env.operators
            .get(op_str)
            .expect("twig bug: operator that was lexed was not found when parsing")
    }
}
<|endoftext|>


<commit_before>
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]

// use std::io;
// use std::io::Write;
use std::ascii::AsciiExt;
use std::rc::Rc;

#[derive(Copy,Clone)]
enum TokenType{
  Terminal, Operator, Separator, Bracket, Bool, Int,
  String, Identifier, Keyword
}

#[derive(Copy,Clone)]
enum TokenValue{
  None, Plus, Minus, Ast, Div, Idiv, Mod, Pow, Lt, Gt, In, Is,
  And, Or, Amp, Vline, Not, Tilde, Svert, Assignment,
  PLeft, PRight, BLeft, BRight, CLeft, CRight, Newline,
  Assert, Begin, Break, Catch, Continue,
  Elif, Else, End, For, Global, Goto, Label,
  If, While, Do, Raise, Return, Sub, Table, Then, Try,
  Use, Yield, True, False, Null, Dot, Comma, Colon, Semicolon
}

pub struct Token {
  token_type: TokenType,
  value: TokenValue,
  line: usize,
  col: usize,
  s: Option<String>
}

struct KeywordsElement {
  s: &'static str,
  t: &'static TokenType,
  v: &'static TokenValue
}

static KEYWORDS: &'static [KeywordsElement] = &[
   KeywordsElement{s: "assert",  t: &TokenType::Keyword, v: &TokenValue::Assert},
   KeywordsElement{s: "and",     t: &TokenType::Operator,v: &TokenValue::And},
   KeywordsElement{s: "begin",   t: &TokenType::Keyword, v: &TokenValue::Begin},
   KeywordsElement{s: "catch",   t: &TokenType::Keyword, v: &TokenValue::Catch},
   KeywordsElement{s: "continue",t: &TokenType::Keyword, v: &TokenValue::Continue},
   KeywordsElement{s: "do",      t: &TokenType::Keyword, v: &TokenValue::Do},
   KeywordsElement{s: "elif",    t: &TokenType::Keyword, v: &TokenValue::Elif},
   KeywordsElement{s: "else",    t: &TokenType::Keyword, v: &TokenValue::Else},
   KeywordsElement{s: "end",     t: &TokenType::Keyword, v: &TokenValue::End},
   KeywordsElement{s: "false",   t: &TokenType::Bool,    v: &TokenValue::False},
   KeywordsElement{s: "for",     t: &TokenType::Keyword, v: &TokenValue::For},
   KeywordsElement{s: "global",  t: &TokenType::Keyword, v: &TokenValue::Global},
   KeywordsElement{s: "goto",    t: &TokenType::Keyword, v: &TokenValue::Goto},
   KeywordsElement{s: "label",   t: &TokenType::Keyword, v: &TokenValue::Label},
   KeywordsElement{s: "if",      t: &TokenType::Keyword, v: &TokenValue::If},
   KeywordsElement{s: "in",      t: &TokenType::Operator,v: &TokenValue::In},
   KeywordsElement{s: "is",      t: &TokenType::Operator,v: &TokenValue::Is},
   KeywordsElement{s: "not",     t: &TokenType::Operator,v: &TokenValue::Not},
   KeywordsElement{s: "null",    t: &TokenType::Keyword, v: &TokenValue::Null},
   KeywordsElement{s: "or",      t: &TokenType::Operator,v: &TokenValue::Or},
   KeywordsElement{s: "raise",   t: &TokenType::Keyword, v: &TokenValue::Raise},
   KeywordsElement{s: "return",  t: &TokenType::Keyword, v: &TokenValue::Return},
   KeywordsElement{s: "sub",     t: &TokenType::Keyword, v: &TokenValue::Sub},
   KeywordsElement{s: "table",   t: &TokenType::Keyword, v: &TokenValue::Table},
   KeywordsElement{s: "then",    t: &TokenType::Keyword, v: &TokenValue::Then},
   KeywordsElement{s: "true",    t: &TokenType::Bool,    v: &TokenValue::True},
   KeywordsElement{s: "try",     t: &TokenType::Keyword, v: &TokenValue::Try},
   KeywordsElement{s: "use",     t: &TokenType::Keyword, v: &TokenValue::Use},
   KeywordsElement{s: "while",   t: &TokenType::Keyword, v: &TokenValue::While},
   KeywordsElement{s: "yield",   t: &TokenType::Keyword, v: &TokenValue::Yield}
];

pub struct SyntaxError {
  line: usize, col: usize,
  s: String
}

pub fn print_syntax_error(e: SyntaxError){
  println!("Line {}, col {}:",e.line,e.col);
  println!("Syntax error: {}",e.s);
}

fn compiler_error() -> !{
  panic!("compiler error");
}

fn is_keyword(id: &String) -> Option<&'static KeywordsElement> {
  let mut i: usize;
  let n: usize = KEYWORDS.len();
  i=0;
  while i<n {
    if KEYWORDS[i].s==id  {return Some(&KEYWORDS[i]);}
    i+=1;
  }
  return None;
}

pub fn scan(s: &String) -> Result<Vec<Token>, SyntaxError>{
  let mut v: Vec<Token> = Vec::new();
  let mut line=1;
  let mut col=1;
  let a: Vec<char> = s.chars().collect();
  let mut i=0;
  let n = a.len();
  while i<n {
    let c = a[i];
    if c.is_digit(10) {
      let j=i;
      while i<n && a[i].is_digit(10) {
        i+=1; col+=1;
      }
      let number: &String = &a[j..i].iter().cloned().collect();
      v.push(Token{token_type: TokenType::Int,
        value: TokenValue::None, line: line, col: col, s: Some(number.clone())});
    }else if (c.is_alphabetic() && c.is_ascii()) || a[i]=='_' {
      let j=i;
      while i<n && (a[i].is_alphabetic() || a[i].is_digit(10) || a[i]=='_') {
        i+=1; col+=1;
      }
      let id: &String = &a[j..i].iter().cloned().collect();
      match is_keyword(id) {
        Some(x) => {
          v.push(Token{token_type: *x.t, value: *x.v,
            line: line, col: col, s: None});
        },
        None => {
          v.push(Token{token_type: TokenType::Identifier,
            value: TokenValue::None, line: line, col: col, s: Some(id.clone())});
        }
      }
    }else{
      match c {
        ' ' | '\t' => {
          i+=1; col+=1;
        },
        '\n' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Newline, line: line, col: col, s: None});
          i+=1; col=1; line+=1;
        },
        ',' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Comma, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ':' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Colon, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ';' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Semicolon, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '(' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::PLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ')' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::PRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '[' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::BLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ']' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::BRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '{' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::CLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '}' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::CRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '=' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Assignment, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '+' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Plus, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '-' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Minus, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '*' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Ast, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '/' => {
          if i+1<n && a[i+1]=='/' {
            v.push(Token{token_type: TokenType::Operator,
              value: TokenValue::Idiv, line: line, col: col, s: None});
            i+=2; col+=2;
          }else{
            v.push(Token{token_type: TokenType::Operator,
              value: TokenValue::Div, line: line, col: col, s: None});
            i+=1; col+=1;
          }
        },
        '%' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Mod, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '^' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Pow, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '.' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Dot, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '<' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Lt, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '>' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Gt, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '|' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Vline, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '&' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Amp, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '$' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Svert, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '~' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Tilde, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        _ => {
          return Err(SyntaxError{line: line, col: col,
            s: format!("unexpected character '{}'.", c)});
        }
      }
    }
  }
  v.push(Token{token_type: TokenType::Terminal,
    value: TokenValue::None, line: line, col: col, s: None});
  return Ok(v);
}

fn token_value_to_string(value: &TokenValue) -> &'static str {
  return match *value {
    TokenValue::Plus => "+",  TokenValue::Minus => "-",
    TokenValue::Ast  => "*",  TokenValue::Div => "/",
    TokenValue::Mod  => "%",  TokenValue::Pow => "^",
    TokenValue::Vline=> "|",  TokenValue::Amp => "&",
    TokenValue::Idiv => "//", TokenValue::Svert=> "$",
    TokenValue::In   => "in", TokenValue::Is => "is",
    TokenValue::And  => "and",TokenValue::Or => "or",
    TokenValue::Not  => "not",TokenValue::Tilde => "~",
    TokenValue::PLeft => "(", TokenValue::PRight => ")",
    TokenValue::BLeft => "[", TokenValue::BRight => "]",
    TokenValue::CLeft => "{", TokenValue::CRight => "}",
    TokenValue::Lt    => "<", TokenValue::Gt => ">",
    TokenValue::Dot   => ".", TokenValue::Comma => ",",
    TokenValue::Colon => ":", TokenValue::Semicolon => ";",
    TokenValue::Assignment => "=",
    TokenValue::Newline => "\\n",
    TokenValue::Assert => "assert",
    TokenValue::Begin => "begin",
    TokenValue::Break => "break",
    TokenValue::Catch => "catch",
    TokenValue::Continue => "continue",
    TokenValue::Elif => "elif",
    TokenValue::Else => "else",
    TokenValue::End => "end",
    TokenValue::False => "false",
    TokenValue::For => "for",
    TokenValue::Global => "global",
    TokenValue::Goto => "goto",
    TokenValue::If => "if",
    TokenValue::Null => "null",
    TokenValue::Raise => "raise",
    TokenValue::Return => "return",
    TokenValue::Sub => "sub",
    TokenValue::Table => "table",
    TokenValue::Then => "then",
    TokenValue::True => "true",
    TokenValue::Try => "try",
    TokenValue::Use => "use",
    TokenValue::While => "while",
    TokenValue::Yield => "yield",
    _ => "unknown token value"
  };
}

pub fn print_vtoken(v: &Vec<Token>){
  for x in v {
    match x.token_type {
      TokenType::String | TokenType::Int | TokenType::Identifier => {
        print!("[{}]",match x.s {Some(ref s) => s, None => compiler_error()});
      },
      TokenType::Operator | TokenType::Separator |
      TokenType::Bracket  | TokenType::Keyword | TokenType::Bool => {
        print!("[{}]",token_value_to_string(&x.value));
      },
      TokenType::Terminal => {
        print!("[Terminal]");
      }
    }
  }
  println!();
}

struct ASTNode{
  line: usize, col: usize,
  token_type: TokenType,
  value: TokenValue,
  s: Option<String>
}

pub struct Compilation{
  mode_cmd: bool,
  v: Vec<Token>,
  index: usize
}

impl Compilation{

fn ast(&mut self) -> Rc<ASTNode>{
  return Rc::new(ASTNode{line: 0, col: 0, token_type: TokenType::Terminal,
    value: TokenValue::Null, s: None});
}

pub fn compile(v: Vec<Token>, mode_cmd: bool){
  let mut compilation = Compilation{
    mode_cmd: mode_cmd,
    v: v, index: 0
  };
  compilation.index+=1;
}

}
<commit_msg>update<commit_after>
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]

// use std::io;
// use std::io::Write;
use std::ascii::AsciiExt;
use std::rc::Rc;

#[derive(Copy,Clone,PartialEq)]
enum TokenType{
  Terminal, Operator, Separator, Bracket, Bool, Int,
  String, Identifier, Keyword
}

#[derive(Copy,Clone,PartialEq)]
enum TokenValue{
  None, Plus, Minus, Ast, Div, Idiv, Mod, Pow, Lt, Gt, In, Is,
  And, Or, Amp, Vline, Not, Tilde, Svert, Assignment,
  PLeft, PRight, BLeft, BRight, CLeft, CRight, Newline,
  Assert, Begin, Break, Catch, Continue,
  Elif, Else, End, For, Global, Goto, Label,
  If, While, Do, Raise, Return, Sub, Table, Then, Try,
  Use, Yield, True, False, Null, Dot, Comma, Colon, Semicolon
}

pub struct Token {
  token_type: TokenType,
  value: TokenValue,
  line: usize,
  col: usize,
  s: Option<String>
}

struct KeywordsElement {
  s: &'static str,
  t: &'static TokenType,
  v: &'static TokenValue
}

static KEYWORDS: &'static [KeywordsElement] = &[
   KeywordsElement{s: "assert",  t: &TokenType::Keyword, v: &TokenValue::Assert},
   KeywordsElement{s: "and",     t: &TokenType::Operator,v: &TokenValue::And},
   KeywordsElement{s: "begin",   t: &TokenType::Keyword, v: &TokenValue::Begin},
   KeywordsElement{s: "catch",   t: &TokenType::Keyword, v: &TokenValue::Catch},
   KeywordsElement{s: "continue",t: &TokenType::Keyword, v: &TokenValue::Continue},
   KeywordsElement{s: "do",      t: &TokenType::Keyword, v: &TokenValue::Do},
   KeywordsElement{s: "elif",    t: &TokenType::Keyword, v: &TokenValue::Elif},
   KeywordsElement{s: "else",    t: &TokenType::Keyword, v: &TokenValue::Else},
   KeywordsElement{s: "end",     t: &TokenType::Keyword, v: &TokenValue::End},
   KeywordsElement{s: "false",   t: &TokenType::Bool,    v: &TokenValue::False},
   KeywordsElement{s: "for",     t: &TokenType::Keyword, v: &TokenValue::For},
   KeywordsElement{s: "global",  t: &TokenType::Keyword, v: &TokenValue::Global},
   KeywordsElement{s: "goto",    t: &TokenType::Keyword, v: &TokenValue::Goto},
   KeywordsElement{s: "label",   t: &TokenType::Keyword, v: &TokenValue::Label},
   KeywordsElement{s: "if",      t: &TokenType::Keyword, v: &TokenValue::If},
   KeywordsElement{s: "in",      t: &TokenType::Operator,v: &TokenValue::In},
   KeywordsElement{s: "is",      t: &TokenType::Operator,v: &TokenValue::Is},
   KeywordsElement{s: "not",     t: &TokenType::Operator,v: &TokenValue::Not},
   KeywordsElement{s: "null",    t: &TokenType::Keyword, v: &TokenValue::Null},
   KeywordsElement{s: "or",      t: &TokenType::Operator,v: &TokenValue::Or},
   KeywordsElement{s: "raise",   t: &TokenType::Keyword, v: &TokenValue::Raise},
   KeywordsElement{s: "return",  t: &TokenType::Keyword, v: &TokenValue::Return},
   KeywordsElement{s: "sub",     t: &TokenType::Keyword, v: &TokenValue::Sub},
   KeywordsElement{s: "table",   t: &TokenType::Keyword, v: &TokenValue::Table},
   KeywordsElement{s: "then",    t: &TokenType::Keyword, v: &TokenValue::Then},
   KeywordsElement{s: "true",    t: &TokenType::Bool,    v: &TokenValue::True},
   KeywordsElement{s: "try",     t: &TokenType::Keyword, v: &TokenValue::Try},
   KeywordsElement{s: "use",     t: &TokenType::Keyword, v: &TokenValue::Use},
   KeywordsElement{s: "while",   t: &TokenType::Keyword, v: &TokenValue::While},
   KeywordsElement{s: "yield",   t: &TokenType::Keyword, v: &TokenValue::Yield}
];

pub struct SyntaxError {
  line: usize, col: usize,
  s: String
}

pub fn print_syntax_error(e: SyntaxError){
  println!("Line {}, col {}:",e.line,e.col);
  println!("Syntax error: {}",e.s);
}

fn compiler_error() -> !{
  panic!("compiler error");
}

fn is_keyword(id: &String) -> Option<&'static KeywordsElement> {
  let mut i: usize;
  let n: usize = KEYWORDS.len();
  i=0;
  while i<n {
    if KEYWORDS[i].s==id  {return Some(&KEYWORDS[i]);}
    i+=1;
  }
  return None;
}

pub fn scan(s: &String) -> Result<Vec<Token>, SyntaxError>{
  let mut v: Vec<Token> = Vec::new();
  let mut line=1;
  let mut col=1;
  let a: Vec<char> = s.chars().collect();
  let mut i=0;
  let n = a.len();
  while i<n {
    let c = a[i];
    if c.is_digit(10) {
      let j=i;
      while i<n && a[i].is_digit(10) {
        i+=1; col+=1;
      }
      let number: &String = &a[j..i].iter().cloned().collect();
      v.push(Token{token_type: TokenType::Int,
        value: TokenValue::None, line: line, col: col, s: Some(number.clone())});
    }else if (c.is_alphabetic() && c.is_ascii()) || a[i]=='_' {
      let j=i;
      while i<n && (a[i].is_alphabetic() || a[i].is_digit(10) || a[i]=='_') {
        i+=1; col+=1;
      }
      let id: &String = &a[j..i].iter().cloned().collect();
      match is_keyword(id) {
        Some(x) => {
          v.push(Token{token_type: *x.t, value: *x.v,
            line: line, col: col, s: None});
        },
        None => {
          v.push(Token{token_type: TokenType::Identifier,
            value: TokenValue::None, line: line, col: col, s: Some(id.clone())});
        }
      }
    }else{
      match c {
        ' ' | '\t' => {
          i+=1; col+=1;
        },
        '\n' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Newline, line: line, col: col, s: None});
          i+=1; col=1; line+=1;
        },
        ',' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Comma, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ':' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Colon, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ';' => {
          v.push(Token{token_type: TokenType::Separator,
            value: TokenValue::Semicolon, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '(' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::PLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ')' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::PRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '[' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::BLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        ']' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::BRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '{' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::CLeft, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '}' => {
          v.push(Token{token_type: TokenType::Bracket,
            value: TokenValue::CRight, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '=' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Assignment, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '+' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Plus, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '-' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Minus, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '*' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Ast, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '/' => {
          if i+1<n && a[i+1]=='/' {
            v.push(Token{token_type: TokenType::Operator,
              value: TokenValue::Idiv, line: line, col: col, s: None});
            i+=2; col+=2;
          }else{
            v.push(Token{token_type: TokenType::Operator,
              value: TokenValue::Div, line: line, col: col, s: None});
            i+=1; col+=1;
          }
        },
        '%' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Mod, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '^' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Pow, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '.' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Dot, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '<' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Lt, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '>' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Gt, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '|' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Vline, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '&' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Amp, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '$' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Svert, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        '~' => {
          v.push(Token{token_type: TokenType::Operator,
            value: TokenValue::Tilde, line: line, col: col, s: None});
          i+=1; col+=1;
        },
        _ => {
          return Err(SyntaxError{line: line, col: col,
            s: format!("unexpected character '{}'.", c)});
        }
      }
    }
  }
  v.push(Token{token_type: TokenType::Terminal,
    value: TokenValue::None, line: line, col: col, s: None});
  return Ok(v);
}

fn token_value_to_string(value: &TokenValue) -> &'static str {
  return match *value {
    TokenValue::Plus => "+",  TokenValue::Minus => "-",
    TokenValue::Ast  => "*",  TokenValue::Div => "/",
    TokenValue::Mod  => "%",  TokenValue::Pow => "^",
    TokenValue::Vline=> "|",  TokenValue::Amp => "&",
    TokenValue::Idiv => "//", TokenValue::Svert=> "$",
    TokenValue::In   => "in", TokenValue::Is => "is",
    TokenValue::And  => "and",TokenValue::Or => "or",
    TokenValue::Not  => "not",TokenValue::Tilde => "~",
    TokenValue::PLeft => "(", TokenValue::PRight => ")",
    TokenValue::BLeft => "[", TokenValue::BRight => "]",
    TokenValue::CLeft => "{", TokenValue::CRight => "}",
    TokenValue::Lt    => "<", TokenValue::Gt => ">",
    TokenValue::Dot   => ".", TokenValue::Comma => ",",
    TokenValue::Colon => ":", TokenValue::Semicolon => ";",
    TokenValue::Assignment => "=",
    TokenValue::Newline => "\\n",
    TokenValue::Assert => "assert",
    TokenValue::Begin => "begin",
    TokenValue::Break => "break",
    TokenValue::Catch => "catch",
    TokenValue::Continue => "continue",
    TokenValue::Elif => "elif",
    TokenValue::Else => "else",
    TokenValue::End => "end",
    TokenValue::False => "false",
    TokenValue::For => "for",
    TokenValue::Global => "global",
    TokenValue::Goto => "goto",
    TokenValue::If => "if",
    TokenValue::Null => "null",
    TokenValue::Raise => "raise",
    TokenValue::Return => "return",
    TokenValue::Sub => "sub",
    TokenValue::Table => "table",
    TokenValue::Then => "then",
    TokenValue::True => "true",
    TokenValue::Try => "try",
    TokenValue::Use => "use",
    TokenValue::While => "while",
    TokenValue::Yield => "yield",
    _ => "unknown token value"
  };
}

pub fn print_vtoken(v: &Vec<Token>){
  for x in v {
    match x.token_type {
      TokenType::String | TokenType::Int | TokenType::Identifier => {
        print!("[{}]",match x.s {Some(ref s) => s, None => compiler_error()});
      },
      TokenType::Operator | TokenType::Separator |
      TokenType::Bracket  | TokenType::Keyword | TokenType::Bool => {
        print!("[{}]",token_value_to_string(&x.value));
      },
      TokenType::Terminal => {
        print!("[Terminal]");
      }
    }
  }
  println!();
}

struct ASTNode<'a>{
  line: usize, col: usize,
  token_type: TokenType,
  value: TokenValue,
  s: Option<String>,
  a: Option<&'a [Rc<ASTNode<'a>>]>
}

pub struct Compilation{
  mode_cmd: bool,
  index: usize
}

impl Compilation{

fn atom(&mut self, v: &Vec<Token>) -> Result<Rc<ASTNode>,SyntaxError>{
  let ref t = v[self.index];
  if t.token_type==TokenType::Identifier {
    self.index+=1;
    return Ok(Rc::new(ASTNode{line: t.line, col: t.col, token_type: TokenType::Identifier,
      value: TokenValue::Null, s: t.s.clone(), a: None}));
  }else{
    return Err(SyntaxError{line: t.line, col: t.col, s: String::from("expected identifier.")});
  }
}

fn ast(&mut self, v: &Vec<Token>) -> Result<Rc<ASTNode>,SyntaxError>{
  let x1 = match self.atom(v) {Ok(x) => x, Err(e) => {return Err(e);}};
  let ref t = v[self.index];
  if t.value==TokenValue::Ast {
    self.index+=1;
    let x2 = match self.atom(v) {Ok(x) => x, Err(e) => {return Err(e);}};
    return Ok(Rc::new(ASTNode{line: t.line, col: t.col, token_type: TokenType::Terminal,
      value: TokenValue::Null, s: None, a: None}));
  }else{
    return Err(SyntaxError{line: t.line, col: t.col, s: String::from("expected '*'.")});
  }
}

pub fn compile(v: &Vec<Token>, mode_cmd: bool){
  let mut compilation = Compilation{
    mode_cmd: mode_cmd, index: 0
  };
  let y = compilation.ast(v);
}

}
<|endoftext|>


<commit_before>//! Create graphene and other substrates for use in molecular dynamics simulations.

extern crate ansi_term;
extern crate dialoguer;
extern crate serde;
extern crate serde_json;
extern crate structopt;
#[macro_use] extern crate structopt_derive;

extern crate grafen;

mod error;
mod output;
mod ui;

use error::Result;

use grafen::database::{read_database, DataBase};

use std::process;
use std::path::PathBuf;
use structopt::StructOpt;

/// The program run configuration.
pub struct Config {
    /// Title of output system.
    pub title: String,
    /// Path to output file.
    pub output_path: PathBuf,
    /// Database of residue and substrate definitions.
    pub database: DataBase,
}

impl Config {
    /// Parse the input command line arguments and read the `DataBase`.
    ///
    /// # Errors
    /// Returns an error if the `DataBase` (if given as an input) could not be read.
    fn new() -> Result<Config> {
        let options = CliOptions::from_args();

        let output_path = PathBuf::from(options.output);
        let title = options.title;

        let database = match options.database {
            Some(path) => read_database(&path),
            None => Ok(DataBase::new()),
        }?;

        eprintln!("{} {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));

        Ok(Config { title, output_path, database })
    }
}

#[derive(StructOpt, Debug)]
/// Command line options
struct CliOptions {
    #[structopt(short = "t", long = "title", default_value = "System created by grafen")]
    /// Title of output system
    title: String,
    #[structopt(short = "o", long = "output", default_value = "conf.gro")]
    /// Output configuration file (Gromos87 format)
    output: String,
    #[structopt(short = "d", long = "database")]
    /// Path to residue and component database
    database: Option<String>,
}

fn main() {
    if let Err(err) = Config::new().and_then(|conf| ui::user_menu(conf)) {
        eprintln!("{}", err);
        process::exit(1);
    }
}
<commit_msg>Do not show the default title in the --help info<commit_after>//! Create graphene and other substrates for use in molecular dynamics simulations.

extern crate ansi_term;
extern crate dialoguer;
extern crate serde;
extern crate serde_json;
extern crate structopt;
#[macro_use] extern crate structopt_derive;

extern crate grafen;

mod error;
mod output;
mod ui;

use error::Result;

use grafen::database::{read_database, DataBase};

use std::process;
use std::path::PathBuf;
use structopt::StructOpt;

/// The program run configuration.
pub struct Config {
    /// Title of output system.
    pub title: String,
    /// Path to output file.
    pub output_path: PathBuf,
    /// Database of residue and substrate definitions.
    pub database: DataBase,
}

impl Config {
    /// Parse the input command line arguments and read the `DataBase`.
    ///
    /// # Errors
    /// Returns an error if the `DataBase` (if given as an input) could not be read.
    fn new() -> Result<Config> {
        let options = CliOptions::from_args();

        let output_path = PathBuf::from(options.output);
        let title = options.title.unwrap_or("System created by grafen".into());

        let database = match options.database {
            Some(path) => read_database(&path),
            None => Ok(DataBase::new()),
        }?;

        eprintln!("{} {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));

        Ok(Config { title, output_path, database })
    }
}

#[derive(StructOpt, Debug)]
/// Command line options
struct CliOptions {
    #[structopt(short = "t", long = "title")]
    /// Title of output system
    title: Option<String>,
    #[structopt(short = "o", long = "output", default_value = "conf.gro")]
    /// Output configuration file
    output: String,
    #[structopt(short = "d", long = "database")]
    /// Path to residue and component database
    database: Option<String>,
}

fn main() {
    if let Err(err) = Config::new().and_then(|conf| ui::user_menu(conf)) {
        eprintln!("{}", err);
        process::exit(1);
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

extern crate crowbook_text_processing;

use crowbook_text_processing::{FrenchFormatter, clean, escape};

use std::env;
use std::io;
use std::io::BufRead;
use std::process::exit;

const TOOLS: &'static[(&'static str, &'static str)] = &[
    ("escape_html", "escape text for HTML display"),
    ("escape_tex", "escape text for LaTeX display"),
    ("escape_nb_spaces", "escape non-breaking spaces using HTML entities"),
    ("escape_nb_spaces_tex", "escape non-breaking spaces using ‘~’ symbol"),
    ("clean_ellipsis", "use unicode character ‘…’ for ellipsis"),
    ("clean_quotes", "try to replace straight quotes with curly ones"),
    ("format_french", "try to apply french typographic rules"),
];

fn print_transformations() {
    for &(name, desc) in TOOLS {
        println!("    {name}: {desc}",
                 name = name,
                 desc = desc);
    }
}

fn main() {
    let args:Vec<_> = env::args()
        .collect();
    if args.len() == 1 {
        println!("\
{bin} {version}

USAGE: {bin} <TRANSFORMATIONS>

Read standard input, sequentially apply each TRANSFORMATION on the text, and print the
result on standard output.

Valid transformations are the following:",
                 bin = env!("CARGO_PKG_NAME"),
                 version = env!("CARGO_PKG_VERSION"));
        print_transformations();
        println!();
        println!("EXAMPLE: {bin} clean_quotes clean_ellipsis escape_html",
                 bin = env!("CARGO_PKG_NAME"));
    } else {
        let french = FrenchFormatter::new();

        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let line = line.expect("Error reading from standard input");
            let mut output = clean::whitespaces(line);
            for argument in &args[1..] {
                output = match argument.as_ref() {
                    "escape_html" => escape::html(output),
                    "escape_tex" => escape::tex(output),
                    "escape_nb_spaces" => escape::nb_spaces(output),
                    "escape_nb_spaces_tex" => escape::nb_spaces_tex(output),
                    "clean_quotes" => clean::quotes(output),
                    "clean_ellipsis" => clean::ellipsis(output),
                    "format_french" => french.format(output),
                    t => {
                        println!("Error: transformation “{}” not recognized.", t);
                        println!("Valid transformations are:");
                        print_transformations();
                        exit(0);
                    },
                }
            }
            println!("{}", output);
        }
    }
}
<commit_msg>Replace println!() with println!("")<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

extern crate crowbook_text_processing;

use crowbook_text_processing::{FrenchFormatter, clean, escape};

use std::env;
use std::io;
use std::io::BufRead;
use std::process::exit;

const TOOLS: &'static[(&'static str, &'static str)] = &[
    ("escape_html", "escape text for HTML display"),
    ("escape_tex", "escape text for LaTeX display"),
    ("escape_nb_spaces", "escape non-breaking spaces using HTML entities"),
    ("escape_nb_spaces_tex", "escape non-breaking spaces using ‘~’ symbol"),
    ("clean_ellipsis", "use unicode character ‘…’ for ellipsis"),
    ("clean_quotes", "try to replace straight quotes with curly ones"),
    ("format_french", "try to apply french typographic rules"),
];

fn print_transformations() {
    for &(name, desc) in TOOLS {
        println!("    {name}: {desc}",
                 name = name,
                 desc = desc);
    }
}

fn main() {
    let args:Vec<_> = env::args()
        .collect();
    if args.len() == 1 {
        println!("\
{bin} {version}

USAGE: {bin} <TRANSFORMATIONS>

Read standard input, sequentially apply each TRANSFORMATION on the text, and print the
result on standard output.

Valid transformations are the following:",
                 bin = env!("CARGO_PKG_NAME"),
                 version = env!("CARGO_PKG_VERSION"));
        print_transformations();
        println!("");
        println!("EXAMPLE: {bin} clean_quotes clean_ellipsis escape_html",
                 bin = env!("CARGO_PKG_NAME"));
    } else {
        let french = FrenchFormatter::new();

        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let line = line.expect("Error reading from standard input");
            let mut output = clean::whitespaces(line);
            for argument in &args[1..] {
                output = match argument.as_ref() {
                    "escape_html" => escape::html(output),
                    "escape_tex" => escape::tex(output),
                    "escape_nb_spaces" => escape::nb_spaces(output),
                    "escape_nb_spaces_tex" => escape::nb_spaces_tex(output),
                    "clean_quotes" => clean::quotes(output),
                    "clean_ellipsis" => clean::ellipsis(output),
                    "format_french" => french.format(output),
                    t => {
                        println!("Error: transformation “{}” not recognized.", t);
                        println!("Valid transformations are:");
                        print_transformations();
                        exit(0);
                    },
                }
            }
            println!("{}", output);
        }
    }
}
<|endoftext|>