<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn build_query(q: &str,
               match_type: &str,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    let boost_addr = rs_q::build_term("_type", "addr").with_boost(1000).build();
    let boost_match_query = rs_q::build_multi_match(vec![match_type.to_string(),
                                                         "zip_codes.prefix".to_string()],
                                                    q.to_string())
        .with_boost(100)
        .build();

    let mut should_query = vec![boost_addr, boost_match_query];
    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    let mut must = vec![rs_q::build_multi_match(vec![match_type.to_string(),
        "zip_codes.prefix".to_string()], q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(90f64)).build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_multi_match(vec!["house_number".to_string(),
                                                       "zip_codes".to_string()],
                                                  q.to_string())
                              .build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn query(q: &str,
         cnx: &str,
         match_type: &str,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&["munin"])
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

fn query_prefix(q: &str,
                cnx: &str,
                offset: u64,
                limit: u64,
                coord: &Option<model::Coord>,
                shape: Option<Vec<rs_es::units::Location>>)
                -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    query(&q, cnx, "label.prefix", offset, limit, coord, shape)
}

fn query_ngram(q: &str,
               cnx: &str,
               offset: u64,
               limit: u64,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    query(&q, cnx, "label.ngram", offset, limit, coord, shape)
}

pub fn autocomplete(q: &str,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    // First search with match = "name.prefix".
    // If no result then another search with match = "name.ngram"
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    let results = try!(query_prefix(&q, cnx, offset, limit, &coord, make_shape(&shape)));
    if results.is_empty() {
        query_ngram(&q, cnx, offset, limit, &coord, make_shape(&shape))
    } else {
        Ok(results)
    }
}
<commit_msg>house number handling<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn build_query(q: &str,
               match_type: &str,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    let boost_addr = rs_q::build_term("_type", "addr").with_boost(1000).build();
    let boost_match_query = rs_q::build_multi_match(vec![match_type.to_string(),
                                                         "zip_codes.prefix".to_string()],
                                                    q.to_string())
        .with_boost(100)
        .build();

    let mut should_query = vec![boost_addr, boost_match_query];
    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    let mut must = vec![rs_q::build_multi_match(vec![match_type.to_string(),
        "zip_codes.prefix".to_string()], q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(90f64)).build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn query(q: &str,
         cnx: &str,
         match_type: &str,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&["munin"])
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

fn query_prefix(q: &str,
                cnx: &str,
                offset: u64,
                limit: u64,
                coord: &Option<model::Coord>,
                shape: Option<Vec<rs_es::units::Location>>)
                -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    query(&q, cnx, "label.prefix", offset, limit, coord, shape)
}

fn query_ngram(q: &str,
               cnx: &str,
               offset: u64,
               limit: u64,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    query(&q, cnx, "label.ngram", offset, limit, coord, shape)
}

pub fn autocomplete(q: &str,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    // First search with match = "name.prefix".
    // If no result then another search with match = "name.ngram"
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    let results = try!(query_prefix(&q, cnx, offset, limit, &coord, make_shape(&shape)));
    if results.is_empty() {
        query_ngram(&q, cnx, offset, limit, &coord, make_shape(&shape))
    } else {
        Ok(results)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(syntax)]
extern crate fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!");
    assert_eq!(val, 0xC0FFEE21u32);
    let little_val = fourcc!("foo ", little);
    assert_eq!(little_val, 0x21EEFFC0u32);
}
 ```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#[crate_id = "fourcc#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];

#[feature(macro_registrar, managed_boxes)];

extern crate syntax;

use syntax::ast;
use syntax::ast::Name;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::InternedString;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("fourcc"),
        NormalTT(~BasicMacroExpander {
            expander: expand_syntax_ext,
            span: None,
        },
        None));
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if s.get().char_len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
        }
    };

    let mut val = 0u32;
    for codepoint in s.get().chars().take(4) {
        let byte = if codepoint as u32 > 0xFF {
            cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
            0u8
        } else {
            codepoint as u8
        };

        val = if little {
            (val >> 8) | ((byte as u32) << 24)
        } else {
            (val << 8) | (byte as u32)
        };
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MRExpr(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
    let p = &mut parse::new_parser_from_tts(cx.parse_sess(),
                                            cx.cfg(),
                                            tts.iter()
                                               .map(|x| (*x).clone())
                                               .collect());
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg().as_slice(), meta)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<commit_msg>Add rustdoc html crate info<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(syntax)]
extern crate fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!");
    assert_eq!(val, 0xC0FFEE21u32);
    let little_val = fourcc!("foo ", little);
    assert_eq!(little_val, 0x21EEFFC0u32);
}
 ```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#[crate_id = "fourcc#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];
#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
      html_root_url = "http://static.rust-lang.org/doc/master")];

#[feature(macro_registrar, managed_boxes)];

extern crate syntax;

use syntax::ast;
use syntax::ast::Name;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::InternedString;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("fourcc"),
        NormalTT(~BasicMacroExpander {
            expander: expand_syntax_ext,
            span: None,
        },
        None));
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if s.get().char_len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
        }
    };

    let mut val = 0u32;
    for codepoint in s.get().chars().take(4) {
        let byte = if codepoint as u32 > 0xFF {
            cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
            0u8
        } else {
            codepoint as u8
        };

        val = if little {
            (val >> 8) | ((byte as u32) << 24)
        } else {
            (val << 8) | (byte as u32)
        };
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MRExpr(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
    let p = &mut parse::new_parser_from_tts(cx.parse_sess(),
                                            cx.cfg(),
                                            tts.iter()
                                               .map(|x| (*x).clone())
                                               .collect());
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg().as_slice(), meta)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<|endoftext|>


<commit_before>use ra_syntax::{
    algo::visit::{visitor, Visitor},
    AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*, SyntaxNodeRef,
};

use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};

fn keyword(kw: &str, snippet: &str) -> CompletionItem {
    CompletionItem::new(CompletionKind::Keyword, kw)
        .kind(CompletionItemKind::Keyword)
        .snippet(snippet)
        .build()
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_trivial_path {
        return;
    }

    // complete keyword "crate" in use stmt
    if let (Some(use_item), None) = (&ctx.use_item_syntax, &ctx.path_prefix) {
        let mut complete_crate = false;
        let use_tree = use_item.use_tree();
        if let Some(use_tree) = use_tree {
            let tree_str = use_tree.syntax().text().to_string();
            if tree_str != "crate" && "crate".starts_with(&tree_str) {
                complete_crate = true;
            }
        } else {
            complete_crate = true;
        }
        if complete_crate {
            CompletionItem::new(CompletionKind::Keyword, "crate")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("crate")
                .add_to(acc);
        }
    }

    let fn_def = match ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add(keyword("if", "if $0 {}"));
    acc.add(keyword("match", "match $0 {}"));
    acc.add(keyword("while", "while $0 {}"));
    acc.add(keyword("loop", "loop {$0}"));

    if ctx.after_if {
        acc.add(keyword("else", "else {$0}"));
        acc.add(keyword("else if", "else if $0 {}"));
    }
    if is_in_loop_body(ctx.leaf) {
        if ctx.can_be_stmt {
            acc.add(keyword("continue", "continue;"));
            acc.add(keyword("break", "break;"));
        } else {
            acc.add(keyword("continue", "continue"));
            acc.add(keyword("break", "break"));
        }
    }
    acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
}

fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
    for node in leaf.ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if leaf.range().is_subrange(&body.syntax().range()) {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_keyword_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Keyword);
    }

    #[test]
    fn completes_various_keywords_in_function() {
        check_keyword_completion(
            r"
            fn quux() {
                <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn completes_else_after_if() {
        check_keyword_completion(
            r"
            fn quux() {
                if true {
                    ()
                } <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            else "else {$0}"
            else if "else if $0 {}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn test_completion_return_value() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn dont_add_semi_after_return_if_not_a_statement() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                match () {
                    () => <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0"
            "#,
        );
    }

    #[test]
    fn last_return_in_block_has_semi() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
                let x = 92;
                x
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn completes_break_and_continue_in_loops() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { <|> }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue;"
            break "break;"
            return "return $0;"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { || { <|> } }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn no_semi_after_break_continue_in_expr() {
        check_keyword_completion(
            r"
            fn f() {
                loop {
                    match () {
                        () => br<|>
                    }
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue"
            break "break"
            return "return"
            "#,
        )
    }

    #[test]
    fn completes_crate_in_use_stmt() {
        check_keyword_completion(
            r"
            use <|>
            ",
            r#"
            crate "crate"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            use a<|>
            ",
            r#"
            "#,
        );
    }
}
<commit_msg>complete "self" and "super"<commit_after>use ra_syntax::{
    algo::visit::{visitor, Visitor},
    AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*, SyntaxNodeRef,
};

use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};

fn keyword(kw: &str, snippet: &str) -> CompletionItem {
    CompletionItem::new(CompletionKind::Keyword, kw)
        .kind(CompletionItemKind::Keyword)
        .snippet(snippet)
        .build()
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    // complete keyword "crate" in use stmt
    match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
        (Some(_), None) => {
            CompletionItem::new(CompletionKind::Keyword, "crate")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("crate")
                .snippet("crate::")
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, "self")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("self")
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, "super")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("super")
                .add_to(acc);
        }
        (Some(_), Some(_)) => {
            CompletionItem::new(CompletionKind::Keyword, "self")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("self")
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, "super")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("super")
                .add_to(acc);
        }
        _ => {}
    }

    if !ctx.is_trivial_path {
        return;
    }

    let fn_def = match ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add(keyword("if", "if $0 {}"));
    acc.add(keyword("match", "match $0 {}"));
    acc.add(keyword("while", "while $0 {}"));
    acc.add(keyword("loop", "loop {$0}"));

    if ctx.after_if {
        acc.add(keyword("else", "else {$0}"));
        acc.add(keyword("else if", "else if $0 {}"));
    }
    if is_in_loop_body(ctx.leaf) {
        if ctx.can_be_stmt {
            acc.add(keyword("continue", "continue;"));
            acc.add(keyword("break", "break;"));
        } else {
            acc.add(keyword("continue", "continue"));
            acc.add(keyword("break", "break"));
        }
    }
    acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
}

fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
    for node in leaf.ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if leaf.range().is_subrange(&body.syntax().range()) {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_keyword_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Keyword);
    }

    #[test]
    fn completes_various_keywords_in_function() {
        check_keyword_completion(
            r"
            fn quux() {
                <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn completes_else_after_if() {
        check_keyword_completion(
            r"
            fn quux() {
                if true {
                    ()
                } <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            else "else {$0}"
            else if "else if $0 {}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn test_completion_return_value() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn dont_add_semi_after_return_if_not_a_statement() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                match () {
                    () => <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0"
            "#,
        );
    }

    #[test]
    fn last_return_in_block_has_semi() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
                let x = 92;
                x
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn completes_break_and_continue_in_loops() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { <|> }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue;"
            break "break;"
            return "return $0;"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { || { <|> } }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn no_semi_after_break_continue_in_expr() {
        check_keyword_completion(
            r"
            fn f() {
                loop {
                    match () {
                        () => br<|>
                    }
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue"
            break "break"
            return "return"
            "#,
        )
    }

    #[test]
    fn completes_keywords_in_use_stmt() {
        check_keyword_completion(
            r"
            use <|>
            ",
            r#"
            crate "crate" "crate::"
            self "self"
            super "super"
            "#,
        );

        check_keyword_completion(
            r"
            use a::<|>
            ",
            r#"
            self "self"
            super "super"
            "#,
        );

        check_keyword_completion(
            r"
            use a::{b, <|>}
            ",
            r#"
            self "self"
            super "super"
            "#,
        );
    }
}
<|endoftext|>


<commit_before>use codegen::RustWriter;
use std::io;
use std::str;
mod codegen;

struct Grammar {
	rules: ~[~Rule]
}

enum Item {
	Rule
}

struct Rule {
	name: ~str,
	expr: ~Expr,
	ret_type: ~str,
}

enum Attr {
	AttrDefineParser(~str),
	AttrDefineType(~str)
}

struct CharSetCase {
	start: char,
	end: char
}

struct TaggedExpr {
	name: Option<~str>,
	expr: ~Expr
}

enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	ZeroOrMore(~Expr),
	OneOrMore(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
	ActionExpr(~[TaggedExpr], ~str),
}

fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w);

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter) {
	w.write("// Generated by rust-peg. Do not edit.
use std::str::{CharRange};

#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<(uint, ()), uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok((pos+l, ()))
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<(uint, ()), uint> {
    if input.len() > pos {
        Ok((input.char_range_at(pos).next, ()))
    } else {
        Err(pos)
    }
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	do w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<(uint, " + rule.ret_type + ") , uint>") {
		compile_expr(w, rule.expr);
	}
}

fn compile_match_and_then(w: &RustWriter, e: &Expr, value_name: &str, then: &fn()) {
	do w.let_block("seq_res") {
		compile_expr(w, e);
	}
	do w.match_block("seq_res") {
		w.match_inline_case("Err(pos)", "Err(pos)");
		do w.match_case("Ok((pos, "+value_name+"))") {
			then();
		}
	}
}

fn compile_zero_or_more(w: &RustWriter, e: &Expr) {
	w.let_mut_stmt("repeat_pos", "pos");
	do w.loop_block {
		do w.let_block("step_res") {
			w.let_stmt("pos", "repeat_pos");
			compile_expr(w, e);
		}
		do w.match_block("step_res") {
			do w.match_case("Ok((newpos, value))") {
				w.line("repeat_pos = newpos;");
			}
			do w.match_case("Err(*)") {
				w.line("break;");
			}
		}
	}
	w.line("Ok((repeat_pos,()))");
}

fn compile_expr(w: &RustWriter, e: &Expr) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+*s+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					do w.match_block("ch") {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start)+"'");
							} else {
								w.write("'"+str::from_char(case.start)+"'..'"+str::from_char(case.end)+"'");
							}
						}
						w.write(" => { Ok((next, ())) }\n");
						w.match_inline_case("_", "Err(pos)");
					}
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {
			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do compile_match_and_then(w, exprs[0], "_") {
						write_seq(w, exprs.tail());
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {
			fn write_choice(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do w.let_block("choice_res") {
						compile_expr(w, exprs[0]);
					}
					do w.match_block("choice_res") {
						w.match_inline_case("Ok((pos, value))", "Ok((pos, value))");
						do w.match_case("Err(*)") {
							write_choice(w, exprs.tail());
						}
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs);
			}
		}

		OptionalExpr(ref e) => {
			do w.let_block("optional_res") {
				compile_expr(w, *e);
			}
			do w.match_block("optional_res") {
				w.match_inline_case("Ok((newpos, _))", "Ok((newpos, ()))");
				w.match_inline_case("Err(*)", "Ok((pos, ()))");
			}
		}

		ZeroOrMore(ref e) => {
			compile_zero_or_more(w, *e);
		}

		OneOrMore(ref e) => {
			do compile_match_and_then(w, *e, "_") {
				compile_zero_or_more(w, *e);
			}
		}

		DelimitedExpr(ref e, _) => fail!("not implemented"),

		  StringifyExpr(*)
		| PosAssertExpr(*)
		| NegAssertExpr(*) => fail!("not implemented"),

		ActionExpr(ref exprs, ref code) => {
			w.let_stmt("start_pos", "pos");
			fn write_seq(w: &RustWriter, exprs: &[TaggedExpr], code: &str) {
				if (exprs.len() > 0) {
					let name = match exprs.head().name {
						Some(ref s) => s.as_slice(),
						None => "_"
					};
					do compile_match_and_then(w, exprs.head().expr, name) {
						write_seq(w, exprs.tail(), code);
					}
				} else {
					w.write_indent();
					w.write("Ok((pos, (|| {");
					w.write(code);
					w.write("})()))\n");
				}
			}

			write_seq(w, *exprs, *code);
		}
	}
}

fn main() {
	let grammar = include!("grammar_def.rs");
	let w = RustWriter::new(io::stdout());
	compile_grammar(&w, grammar);
}<commit_msg>Generate public functions for parse rules<commit_after>use codegen::RustWriter;
use std::io;
use std::str;
mod codegen;

struct Grammar {
	rules: ~[~Rule]
}

enum Item {
	Rule
}

struct Rule {
	name: ~str,
	expr: ~Expr,
	ret_type: ~str,
}

enum Attr {
	AttrDefineParser(~str),
	AttrDefineType(~str)
}

struct CharSetCase {
	start: char,
	end: char
}

struct TaggedExpr {
	name: Option<~str>,
	expr: ~Expr
}

enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	ZeroOrMore(~Expr),
	OneOrMore(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
	ActionExpr(~[TaggedExpr], ~str),
}

fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w);

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter) {
	w.write("// Generated by rust-peg. Do not edit.
extern mod std;
use std::str::{CharRange};

#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<(uint, ()), uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok((pos+l, ()))
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<(uint, ()), uint> {
    if input.len() > pos {
        Ok((input.char_range_at(pos).next, ()))
    } else {
        Err(pos)
    }
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	do w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<(uint, " + rule.ret_type + ") , uint>") {
		compile_expr(w, rule.expr);
	}

	do w.def_fn(true, rule.name, "input: &str", "Result<"+rule.ret_type+", ~str>") {
		do w.match_block("parse_"+rule.name+"(input, 0)") {
			do w.match_case("Ok((pos, value))") {
				w.if_else("pos == input.len()",
					|| { w.line("Ok(value)"); },
					|| { w.line("Err(~\"Unexpected characters at end of input\")"); }
				)
			}
			w.match_inline_case("Err(pos)", "Err(\"Error at \"+ pos.to_str())");
		}
	}
}

fn compile_match_and_then(w: &RustWriter, e: &Expr, value_name: &str, then: &fn()) {
	do w.let_block("seq_res") {
		compile_expr(w, e);
	}
	do w.match_block("seq_res") {
		w.match_inline_case("Err(pos)", "Err(pos)");
		do w.match_case("Ok((pos, "+value_name+"))") {
			then();
		}
	}
}

fn compile_zero_or_more(w: &RustWriter, e: &Expr) {
	w.let_mut_stmt("repeat_pos", "pos");
	do w.loop_block {
		do w.let_block("step_res") {
			w.let_stmt("pos", "repeat_pos");
			compile_expr(w, e);
		}
		do w.match_block("step_res") {
			do w.match_case("Ok((newpos, value))") {
				w.line("repeat_pos = newpos;");
			}
			do w.match_case("Err(*)") {
				w.line("break;");
			}
		}
	}
	w.line("Ok((repeat_pos,()))");
}

fn compile_expr(w: &RustWriter, e: &Expr) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+*s+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					do w.match_block("ch") {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start)+"'");
							} else {
								w.write("'"+str::from_char(case.start)+"'..'"+str::from_char(case.end)+"'");
							}
						}
						w.write(" => { Ok((next, ())) }\n");
						w.match_inline_case("_", "Err(pos)");
					}
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {
			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do compile_match_and_then(w, exprs[0], "_") {
						write_seq(w, exprs.tail());
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {
			fn write_choice(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do w.let_block("choice_res") {
						compile_expr(w, exprs[0]);
					}
					do w.match_block("choice_res") {
						w.match_inline_case("Ok((pos, value))", "Ok((pos, value))");
						do w.match_case("Err(*)") {
							write_choice(w, exprs.tail());
						}
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs);
			}
		}

		OptionalExpr(ref e) => {
			do w.let_block("optional_res") {
				compile_expr(w, *e);
			}
			do w.match_block("optional_res") {
				w.match_inline_case("Ok((newpos, _))", "Ok((newpos, ()))");
				w.match_inline_case("Err(*)", "Ok((pos, ()))");
			}
		}

		ZeroOrMore(ref e) => {
			compile_zero_or_more(w, *e);
		}

		OneOrMore(ref e) => {
			do compile_match_and_then(w, *e, "_") {
				compile_zero_or_more(w, *e);
			}
		}

		DelimitedExpr(ref e, _) => fail!("not implemented"),

		  StringifyExpr(*)
		| PosAssertExpr(*)
		| NegAssertExpr(*) => fail!("not implemented"),

		ActionExpr(ref exprs, ref code) => {
			w.let_stmt("start_pos", "pos");
			fn write_seq(w: &RustWriter, exprs: &[TaggedExpr], code: &str) {
				if (exprs.len() > 0) {
					let name = match exprs.head().name {
						Some(ref s) => s.as_slice(),
						None => "_"
					};
					do compile_match_and_then(w, exprs.head().expr, name) {
						write_seq(w, exprs.tail(), code);
					}
				} else {
					w.write_indent();
					w.write("Ok((pos, (|| {");
					w.write(code);
					w.write("})()))\n");
				}
			}

			write_seq(w, *exprs, *code);
		}
	}
}

fn main() {
	let grammar = include!("grammar_def.rs");
	let w = RustWriter::new(io::stdout());
	compile_grammar(&w, grammar);
}<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::process::Command;
use std::io::Error as IOError;

use tempfile::NamedTempFile;

pub fn edit_in_tmpfile_with_command(mut cmd: Command, s: &mut String) -> Result<bool, IOError> {
    let file      = try!(NamedTempFile::new());
    let file_path = file.path();
    let mut file  = try!(file.reopen());

    try!(file.write_all(&s.clone().into_bytes()[..]));
    try!(file.sync_data());

    cmd.arg(file_path)
        .status()
        .and_then(|status| {
            if status.success() {
                file.sync_data()
                    .and_then(|_| file.seek(SeekFrom::Start(0)))
                    .and_then(|_| {
                        let mut new_s = String::new();
                        let res = file.read_to_string(&mut new_s);
                        *s = new_s;
                        res
                    })
                    .map(|_| true)
            } else {
                Ok(false)
            }
        })
}

<commit_msg>Don't re-open temporary file.<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::process::Command;
use std::io::Error as IOError;

use tempfile::NamedTempFile;

pub fn edit_in_tmpfile_with_command(mut cmd: Command, s: &mut String) -> Result<bool, IOError> {
    let mut file  = &try!(NamedTempFile::new());
    let file_path = file.path();

    try!(file.write_all(&s.clone().into_bytes()[..]));
    try!(file.sync_data());

    cmd.arg(file_path)
        .status()
        .and_then(|status| {
            if status.success() {
                file.sync_data()
                    .and_then(|_| file.seek(SeekFrom::Start(0)))
                    .and_then(|_| {
                        let mut new_s = String::new();
                        let res = file.read_to_string(&mut new_s);
                        *s = new_s;
                        res
                    })
                    .map(|_| true)
            } else {
                Ok(false)
            }
        })
}

<|endoftext|>