<commit_before>#![cfg(not(syn_disable_nightly_tests))]
#![cfg(not(miri))]
#![recursion_limit = "1024"]
#![feature(rustc_private)]
#![allow(
    clippy::explicit_deref_methods,
    clippy::manual_assert,
    clippy::match_wildcard_for_single_variants,
    clippy::too_many_lines
)]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `librustc`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `librustc_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `librustc`.
//! 4. Respan all of the expressions, replacing the spans with the default
//!    spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

extern crate rustc_ast;
extern crate rustc_data_structures;
extern crate rustc_span;

use crate::common::eq::SpanlessEq;
use crate::common::parse;
use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use regex::Regex;
use rustc_ast::ast;
use rustc_ast::ptr::P;
use rustc_span::edition::Edition;
use std::fs;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use walkdir::{DirEntry, WalkDir};

#[macro_use]
mod macros;

#[allow(dead_code)]
mod common;

mod repo;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
        "{ for i in unsafe { 20 } { } }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(Edition::Edition2018, vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    common::rayon_init();
    repo::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let passed = AtomicUsize::new(0);
    let failed = AtomicUsize::new(0);

    // 2018 edition is hard
    let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();

    WalkDir::new("tests/rust")
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
        .unwrap()
        .into_par_iter()
        .for_each(|entry| {
            let path = entry.path();
            if path.is_dir() {
                return;
            }

            let content = fs::read_to_string(path).unwrap();
            let content = edition_regex.replace_all(&content, "_$0");

            let (l_passed, l_failed) = match syn::parse_file(&content) {
                Ok(file) => {
                    let edition = repo::edition(path).parse().unwrap();
                    let exprs = collect_exprs(file);
                    test_expressions(edition, exprs)
                }
                Err(msg) => {
                    errorf!("syn failed to parse\n{:?}\n", msg);
                    (0, 1)
                }
            };

            errorf!(
                "=== {}: {} passed | {} failed\n",
                path.display(),
                l_passed,
                l_failed
            );

            passed.fetch_add(l_passed, Ordering::Relaxed);
            let prev_failed = failed.fetch_add(l_failed, Ordering::Relaxed);

            if prev_failed + l_failed >= abort_after {
                process::exit(1);
            }
        });

    let passed = passed.load(Ordering::Relaxed);
    let failed = failed.load(Ordering::Relaxed);

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) {
    let mut passed = 0;
    let mut failed = 0;

    rustc_span::create_session_if_not_set_then(edition, |_| {
        for expr in exprs {
            let raw = quote!(#expr).to_string();

            let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&raw) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - librustc failed to parse raw\n");
                continue;
            };

            let syn_expr = syn_brackets(expr);
            let syn_ast = if let Some(e) = parse::librustc_expr(&quote!(#syn_expr).to_string()) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - librustc failed to parse bracketed\n");
                continue;
            };

            if SpanlessEq::eq(&syn_ast, &librustc_ast) {
                passed += 1;
            } else {
                failed += 1;
                errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, librustc_ast);
            }
        }
    });

    (passed, failed)
}

fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::librustc_expr(input).and_then(librustc_brackets)
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
///
/// This method operates on librustc objects.
fn librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use rustc_ast::ast::{
        Attribute, Block, BorrowKind, Expr, ExprField, ExprKind, GenericArg, Local, LocalKind, Pat,
        Stmt, StmtKind, StructExpr, StructRest, Ty,
    };
    use rustc_ast::mut_visit::{noop_visit_generic_arg, noop_visit_local, MutVisitor};
    use rustc_data_structures::map_in_place::MapInPlace;
    use rustc_data_structures::thin_vec::ThinVec;
    use rustc_span::DUMMY_SP;
    use std::mem;
    use std::ops::DerefMut;

    struct BracketsVisitor {
        failed: bool,
    }

    fn flat_map_field<T: MutVisitor>(mut f: ExprField, vis: &mut T) -> Vec<ExprField> {
        if f.is_shorthand {
            noop_visit_expr(&mut f.expr, vis);
        } else {
            vis.visit_expr(&mut f.expr);
        }
        vec![f]
    }

    fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
        let kind = match stmt.kind {
            // Don't wrap toplevel expressions in statements.
            StmtKind::Expr(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Expr(e)
            }
            StmtKind::Semi(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Semi(e)
            }
            s => s,
        };

        vec![Stmt { kind, ..stmt }]
    }

    fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
        use rustc_ast::mut_visit::{noop_visit_expr, visit_thin_attrs};
        match &mut e.kind {
            ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
            ExprKind::Struct(expr) => {
                let StructExpr {
                    qself,
                    path,
                    fields,
                    rest,
                } = expr.deref_mut();
                vis.visit_qself(qself);
                vis.visit_path(path);
                fields.flat_map_in_place(|field| flat_map_field(field, vis));
                if let StructRest::Base(rest) = rest {
                    vis.visit_expr(rest);
                }
                vis.visit_id(&mut e.id);
                vis.visit_span(&mut e.span);
                visit_thin_attrs(&mut e.attrs, vis);
            }
            _ => noop_visit_expr(e, vis),
        }
    }

    impl MutVisitor for BracketsVisitor {
        fn visit_expr(&mut self, e: &mut P<Expr>) {
            match e.kind {
                ExprKind::ConstBlock(..) => {}
                _ => noop_visit_expr(e, self),
            }
            match e.kind {
                ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
                _ => {
                    let inner = mem::replace(
                        e,
                        P(Expr {
                            id: ast::DUMMY_NODE_ID,
                            kind: ExprKind::Err,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                            tokens: None,
                        }),
                    );
                    e.kind = ExprKind::Paren(inner);
                }
            }
        }

        fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
            match arg {
                // Don't wrap unbraced const generic arg as that's invalid syntax.
                GenericArg::Const(anon_const) => {
                    if let ExprKind::Block(..) = &mut anon_const.value.kind {
                        noop_visit_expr(&mut anon_const.value, self);
                    }
                }
                _ => noop_visit_generic_arg(arg, self),
            }
        }

        fn visit_block(&mut self, block: &mut P<Block>) {
            self.visit_id(&mut block.id);
            block
                .stmts
                .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
            self.visit_span(&mut block.span);
        }

        fn visit_local(&mut self, local: &mut P<Local>) {
            match local.kind {
                LocalKind::InitElse(..) => {}
                _ => noop_visit_local(local, self),
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn visit_pat(&mut self, pat: &mut P<Pat>) {
            let _ = pat;
        }

        fn visit_ty(&mut self, ty: &mut P<Ty>) {
            let _ = ty;
        }

        fn visit_attribute(&mut self, attr: &mut Attribute) {
            let _ = attr;
        }
    }

    let mut folder = BracketsVisitor { failed: false };
    folder.visit_expr(&mut librustc_expr);
    if folder.failed {
        None
    } else {
        Some(librustc_expr)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precedence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::fold::{fold_expr, fold_generic_argument, fold_generic_method_argument, Fold};
    use syn::{token, Expr, ExprParen, GenericArgument, GenericMethodArgument, Pat, Stmt, Type};

    struct ParenthesizeEveryExpr;
    impl Fold for ParenthesizeEveryExpr {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Group(_) => unreachable!(),
                Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
                    fold_expr(self, expr)
                }
                _ => Expr::Paren(ExprParen {
                    attrs: Vec::new(),
                    expr: Box::new(fold_expr(self, expr)),
                    paren_token: token::Paren::default(),
                }),
            }
        }

        fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument {
            match arg {
                GenericArgument::Const(arg) => GenericArgument::Const(match arg {
                    Expr::Block(_) => fold_expr(self, arg),
                    // Don't wrap unbraced const generic arg as that's invalid syntax.
                    _ => arg,
                }),
                _ => fold_generic_argument(self, arg),
            }
        }

        fn fold_generic_method_argument(
            &mut self,
            arg: GenericMethodArgument,
        ) -> GenericMethodArgument {
            match arg {
                GenericMethodArgument::Const(arg) => GenericMethodArgument::Const(match arg {
                    Expr::Block(_) => fold_expr(self, arg),
                    // Don't wrap unbraced const generic arg as that's invalid syntax.
                    _ => arg,
                }),
                _ => fold_generic_method_argument(self, arg),
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
                Stmt::Semi(e, semi) => {
                    if let Expr::Verbatim(_) = e {
                        Stmt::Semi(e, semi)
                    } else {
                        Stmt::Semi(fold_expr(self, e), semi)
                    }
                }
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_type(&mut self, ty: Type) -> Type {
            ty
        }
    }

    let mut folder = ParenthesizeEveryExpr;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use syn::fold::Fold;
    use syn::punctuated::Punctuated;
    use syn::{token, Expr, ExprTuple, Path};

    struct CollectExprs(Vec<Expr>);
    impl Fold for CollectExprs {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Verbatim(_) => {}
                _ => self.0.push(expr),
            }

            Expr::Tuple(ExprTuple {
                attrs: vec![],
                elems: Punctuated::new(),
                paren_token: token::Paren::default(),
            })
        }

        fn fold_path(&mut self, path: Path) -> Path {
            // Skip traversing into const generic path arguments
            path
        }
    }

    let mut folder = CollectExprs(vec![]);
    folder.fold_file(file);
    folder.0
}
<commit_msg>Fix typo<commit_after>#![cfg(not(syn_disable_nightly_tests))]
#![cfg(not(miri))]
#![recursion_limit = "1024"]
#![feature(rustc_private)]
#![allow(
    clippy::explicit_deref_methods,
    clippy::manual_assert,
    clippy::match_wildcard_for_single_variants,
    clippy::too_many_lines
)]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `librustc`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `librustc_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `librustc`.
//! 4. Respan all of the expressions, replacing the spans with the default
//!    spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

extern crate rustc_ast;
extern crate rustc_data_structures;
extern crate rustc_span;

use crate::common::eq::SpanlessEq;
use crate::common::parse;
use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use regex::Regex;
use rustc_ast::ast;
use rustc_ast::ptr::P;
use rustc_span::edition::Edition;
use std::fs;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use walkdir::{DirEntry, WalkDir};

#[macro_use]
mod macros;

#[allow(dead_code)]
mod common;

mod repo;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
        "{ for i in unsafe { 20 } { } }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(Edition::Edition2018, vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    common::rayon_init();
    repo::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let passed = AtomicUsize::new(0);
    let failed = AtomicUsize::new(0);

    // 2018 edition is hard
    let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();

    WalkDir::new("tests/rust")
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
        .unwrap()
        .into_par_iter()
        .for_each(|entry| {
            let path = entry.path();
            if path.is_dir() {
                return;
            }

            let content = fs::read_to_string(path).unwrap();
            let content = edition_regex.replace_all(&content, "_$0");

            let (l_passed, l_failed) = match syn::parse_file(&content) {
                Ok(file) => {
                    let edition = repo::edition(path).parse().unwrap();
                    let exprs = collect_exprs(file);
                    test_expressions(edition, exprs)
                }
                Err(msg) => {
                    errorf!("syn failed to parse\n{:?}\n", msg);
                    (0, 1)
                }
            };

            errorf!(
                "=== {}: {} passed | {} failed\n",
                path.display(),
                l_passed,
                l_failed
            );

            passed.fetch_add(l_passed, Ordering::Relaxed);
            let prev_failed = failed.fetch_add(l_failed, Ordering::Relaxed);

            if prev_failed + l_failed >= abort_after {
                process::exit(1);
            }
        });

    let passed = passed.load(Ordering::Relaxed);
    let failed = failed.load(Ordering::Relaxed);

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) {
    let mut passed = 0;
    let mut failed = 0;

    rustc_span::create_session_if_not_set_then(edition, |_| {
        for expr in exprs {
            let raw = quote!(#expr).to_string();

            let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&raw) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - librustc failed to parse raw\n");
                continue;
            };

            let syn_expr = syn_brackets(expr);
            let syn_ast = if let Some(e) = parse::librustc_expr(&quote!(#syn_expr).to_string()) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - librustc failed to parse bracketed\n");
                continue;
            };

            if SpanlessEq::eq(&syn_ast, &librustc_ast) {
                passed += 1;
            } else {
                failed += 1;
                errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, librustc_ast);
            }
        }
    });

    (passed, failed)
}

fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::librustc_expr(input).and_then(librustc_brackets)
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precedence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
///
/// This method operates on librustc objects.
fn librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use rustc_ast::ast::{
        Attribute, Block, BorrowKind, Expr, ExprField, ExprKind, GenericArg, Local, LocalKind, Pat,
        Stmt, StmtKind, StructExpr, StructRest, Ty,
    };
    use rustc_ast::mut_visit::{noop_visit_generic_arg, noop_visit_local, MutVisitor};
    use rustc_data_structures::map_in_place::MapInPlace;
    use rustc_data_structures::thin_vec::ThinVec;
    use rustc_span::DUMMY_SP;
    use std::mem;
    use std::ops::DerefMut;

    struct BracketsVisitor {
        failed: bool,
    }

    fn flat_map_field<T: MutVisitor>(mut f: ExprField, vis: &mut T) -> Vec<ExprField> {
        if f.is_shorthand {
            noop_visit_expr(&mut f.expr, vis);
        } else {
            vis.visit_expr(&mut f.expr);
        }
        vec![f]
    }

    fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
        let kind = match stmt.kind {
            // Don't wrap toplevel expressions in statements.
            StmtKind::Expr(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Expr(e)
            }
            StmtKind::Semi(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Semi(e)
            }
            s => s,
        };

        vec![Stmt { kind, ..stmt }]
    }

    fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
        use rustc_ast::mut_visit::{noop_visit_expr, visit_thin_attrs};
        match &mut e.kind {
            ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
            ExprKind::Struct(expr) => {
                let StructExpr {
                    qself,
                    path,
                    fields,
                    rest,
                } = expr.deref_mut();
                vis.visit_qself(qself);
                vis.visit_path(path);
                fields.flat_map_in_place(|field| flat_map_field(field, vis));
                if let StructRest::Base(rest) = rest {
                    vis.visit_expr(rest);
                }
                vis.visit_id(&mut e.id);
                vis.visit_span(&mut e.span);
                visit_thin_attrs(&mut e.attrs, vis);
            }
            _ => noop_visit_expr(e, vis),
        }
    }

    impl MutVisitor for BracketsVisitor {
        fn visit_expr(&mut self, e: &mut P<Expr>) {
            match e.kind {
                ExprKind::ConstBlock(..) => {}
                _ => noop_visit_expr(e, self),
            }
            match e.kind {
                ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
                _ => {
                    let inner = mem::replace(
                        e,
                        P(Expr {
                            id: ast::DUMMY_NODE_ID,
                            kind: ExprKind::Err,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                            tokens: None,
                        }),
                    );
                    e.kind = ExprKind::Paren(inner);
                }
            }
        }

        fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
            match arg {
                // Don't wrap unbraced const generic arg as that's invalid syntax.
                GenericArg::Const(anon_const) => {
                    if let ExprKind::Block(..) = &mut anon_const.value.kind {
                        noop_visit_expr(&mut anon_const.value, self);
                    }
                }
                _ => noop_visit_generic_arg(arg, self),
            }
        }

        fn visit_block(&mut self, block: &mut P<Block>) {
            self.visit_id(&mut block.id);
            block
                .stmts
                .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
            self.visit_span(&mut block.span);
        }

        fn visit_local(&mut self, local: &mut P<Local>) {
            match local.kind {
                LocalKind::InitElse(..) => {}
                _ => noop_visit_local(local, self),
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn visit_pat(&mut self, pat: &mut P<Pat>) {
            let _ = pat;
        }

        fn visit_ty(&mut self, ty: &mut P<Ty>) {
            let _ = ty;
        }

        fn visit_attribute(&mut self, attr: &mut Attribute) {
            let _ = attr;
        }
    }

    let mut folder = BracketsVisitor { failed: false };
    folder.visit_expr(&mut librustc_expr);
    if folder.failed {
        None
    } else {
        Some(librustc_expr)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precedence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::fold::{fold_expr, fold_generic_argument, fold_generic_method_argument, Fold};
    use syn::{token, Expr, ExprParen, GenericArgument, GenericMethodArgument, Pat, Stmt, Type};

    struct ParenthesizeEveryExpr;
    impl Fold for ParenthesizeEveryExpr {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Group(_) => unreachable!(),
                Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
                    fold_expr(self, expr)
                }
                _ => Expr::Paren(ExprParen {
                    attrs: Vec::new(),
                    expr: Box::new(fold_expr(self, expr)),
                    paren_token: token::Paren::default(),
                }),
            }
        }

        fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument {
            match arg {
                GenericArgument::Const(arg) => GenericArgument::Const(match arg {
                    Expr::Block(_) => fold_expr(self, arg),
                    // Don't wrap unbraced const generic arg as that's invalid syntax.
                    _ => arg,
                }),
                _ => fold_generic_argument(self, arg),
            }
        }

        fn fold_generic_method_argument(
            &mut self,
            arg: GenericMethodArgument,
        ) -> GenericMethodArgument {
            match arg {
                GenericMethodArgument::Const(arg) => GenericMethodArgument::Const(match arg {
                    Expr::Block(_) => fold_expr(self, arg),
                    // Don't wrap unbraced const generic arg as that's invalid syntax.
                    _ => arg,
                }),
                _ => fold_generic_method_argument(self, arg),
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
                Stmt::Semi(e, semi) => {
                    if let Expr::Verbatim(_) = e {
                        Stmt::Semi(e, semi)
                    } else {
                        Stmt::Semi(fold_expr(self, e), semi)
                    }
                }
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_type(&mut self, ty: Type) -> Type {
            ty
        }
    }

    let mut folder = ParenthesizeEveryExpr;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use syn::fold::Fold;
    use syn::punctuated::Punctuated;
    use syn::{token, Expr, ExprTuple, Path};

    struct CollectExprs(Vec<Expr>);
    impl Fold for CollectExprs {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Verbatim(_) => {}
                _ => self.0.push(expr),
            }

            Expr::Tuple(ExprTuple {
                attrs: vec![],
                elems: Punctuated::new(),
                paren_token: token::Paren::default(),
            })
        }

        fn fold_path(&mut self, path: Path) -> Path {
            // Skip traversing into const generic path arguments
            path
        }
    }

    let mut folder = CollectExprs(vec![]);
    folder.fold_file(file);
    folder.0
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use parser::ast::*;
use scope::context::*;
use processing::structs::*;

type FormalProp<'a> = (&'a str);
type FormalPropVec<'a> = Vec<FormalProp<'a>>;

type PropKeyRef = (String, String);
type PropKeyRefVec = Vec<PropKeyRef>;

type PropValue<'a> = (&'a str, Option<&'a ExprValue>);
type PropValueVec<'a> = Vec<PropValue<'a>>;

#[derive(Debug, Clone, Default)]
pub struct CompDefProcessorOutput {
    ty: Option<String>,
    ops: Option<OpsVec>
}

impl Into<Component> for CompDefProcessorOutput {
    fn into(self) -> Component {
        Component::new_with_vec(self.ty.unwrap(), self.ops)
    }
}

#[derive(Debug)]
pub struct CompDefProcessor<'out> {
    output: &'out mut CompDefProcessorOutput
}

impl<'out> CompDefProcessor<'out> {
    pub fn with_output(output: &'out mut CompDefProcessorOutput) -> Self {
        CompDefProcessor { output: output }
    }

    fn add_op(&mut self, op: ElementOp) {
        if let Some(ref mut ops) = self.output.ops {
            ops.push(op);
        } else {
            self.output.ops = Some(vec![op]);
        }
    }

    pub fn push_component_definition_scope<'a, I>(&mut self, ctx: &mut Context,component_ty: &str, formals: I)
        where I: IntoIterator<Item = &'a FormalProp<'a>>
    {
        if self.output.ty.is_some() {
            panic!("Already have component defintion in this output.");
        }
        self.output.ty = Some(component_ty.to_owned());

        ctx.push_child_scope();
        for formal in formals {
            ctx.add_unbound_formal_param(formal);
        }
    }

    pub fn push_element_parameter_definition_scope<'a, I>(&mut self, ctx: &mut Context, element_id: &str, element_ty: &str, props: Option<I>)
        where I: IntoIterator<Item = &'a PropKeyRef> + Clone
    {
        // let props = props.map(|props| props.clone().into_iter());
        let element_props = props.as_ref().map(|props| props.clone().into_iter().map(|prop| {
            let key = &prop.0;
            let ref_key = &prop.1;
            let value_sym = ctx.resolve_sym(ref_key);
            if let Some(value_sym) = value_sym {
                return (key.to_owned(), Some(ExprValue::SymbolReference(value_sym)));
            };
            (key.to_owned(), None)
        }).collect());

        let op = ElementOp::ElementVoid(element_ty.to_owned(), Some(element_id.to_owned()), element_props, None, None);
        self.add_op(op);

        let parent_scope_id = ctx.scope().id().to_owned();
        ctx.push_child_scope();
        if let Some(props) = props {
            for prop in props {
                ctx.add_element_prop_ref(&prop.0, &prop.1, Some(&parent_scope_id));
            }
        }
    }

    pub fn push_element_scope(&mut self, ctx: &mut Context, element_id: &str, _element_ty: &str) {
        ctx.push_child_scope();
        ctx.append_path_str(element_id);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::*;
    use parser::ast::*;
    use scope::context::*;

    trait OpsWriter {
        fn write_op(&mut self, op: &ElementOp);
    }

    #[derive(Debug, Default)]
    struct TestOpsWriter {
    }

    impl OpsWriter for TestOpsWriter {
        fn write_op(&mut self, op: &ElementOp) {

        }
    }

    #[derive(Debug, Default)]
    struct ComponentWriter {
        ctx: Context
    }

    type PropValue<'a> = (&'a str, Option<&'a ExprValue>);

    #[derive(Debug)]
    struct SymbolResolver<'a, I: Iterator<Item = PropValue<'a>>> {
        ctx: &'a mut Context,
        iter: I
    }

    // impl<'a, I> SymbolResolver<'a, I>
    // //   where I: Iterator<Item = &'a PropValue<'a>>
    // {
    //     pub fn new(ctx: &'a mut Context, iter: Self::I) -> Self {
    //         SymbolResolver { ctx: ctx, iter: iter }
    //     }
    // }

    // impl<'a, I> Iterator for SymbolResolver<'a, I>
    //   where I: Iterator<Item = &'a PropValue<'a>>
    impl<'a, I: Iterator<Item = PropValue<'a>>> Iterator for SymbolResolver<'a, I>
    {
        type Item = Prop;

        fn next(&mut self) -> Option<Self::Item> {
            if let Some(prop) = self.iter.next() {
                let key = prop.0.to_owned();
                let expr = &prop.1;
                let resolved_sym = self.ctx.resolve_sym(&key);
                if let Some(resolved_sym) = resolved_sym {
                    // let key = prop.0.to_owned();
                    let expr = ExprValue::SymbolReference(resolved_sym);
                    return Some((key, Some(expr)));
                };
                return Some((key, expr.map(|p| p.clone())));
                // return Some((key.to_owned(), prop.1.map(|p| p.to_owned())));
            };
            None

            // self.iter.next().map(|prop| {
            //     let resolved_sym = self.ctx.resolve_sym(&prop.0);
            //     if let Some(resolved_sym) = resolved_sym {
            //         let key = prop.0.to_owned();
            //         let expr = ExprValue::SymbolReference(resolved_sym);
            //         return (key, Some(expr));
            //     }

            //     return (prop.0.to_owned(), prop.1.map(|p| p.to_owned()))
            // })
        }
    }

    // impl<B, I: ExactSizeIterator, F> ExactSizeIterator for SymbolResolver<I>
    // {
    //     fn len(&self) -> usize {
    //         self.iter.len()
    //     }

    //     fn is_empty(&self) -> bool {
    //         self.iter.is_empty()
    //     }
    // }

    impl<'a> ComponentWriter {
        // fn resolve_props<'props, I>(&'a mut self, props: I) -> SymbolResolver<'a, I>
        //   where I: Iterator<Item = PropValue<'props>>
        // {
        //     SymbolResolver::new(&mut self.ctx, props)
        // }

        fn write_op_to<W>(&mut self, ops_writer: &mut W, comp_op: &ElementOp)
          where W: OpsWriter
        {
            match comp_op {
                &ElementOp::ElementVoid(ref element_ty, ref element_id, ref props, ref event_handlers, ref value_bindings) => {
                    if let &Some(ref props) = props {
                        // let v: Vec<PropValue<'a>> = vec![];
                        // let v: PropVec = vec![];
                        // let mut prop_iter = v.iter().map(|prop| (prop.0.as_ref(), prop.1.as_ref()));

                        let mut prop_iter = props.iter().map(|prop| (prop.0.as_str(), prop.1.as_ref()));
                        let mut iter = SymbolResolver { ctx: &mut self.ctx, iter: prop_iter };
                        let resolved_props: Vec<Prop> = iter.collect();

                        let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), Some(resolved_props), event_handlers.to_owned(), value_bindings.to_owned());
                        ops_writer.write_op(&op);
                    };

                    // let resolved_props = props.map(|props| {
                    //     let mut props_iter = props.iter().map(|prop| (prop.0.as_ref(), prop.1.map(|p| &p)));
                    //     let mut iter = SymbolResolver { ctx: &mut self.ctx, iter: props_iter };

                    //     // let mut iter = self.resolve_props(
                    //     //     props.iter().map(|prop| (prop.0.as_ref(), prop.1.map(|p| &p)))
                    //     // );
                    //     let resolved_prop = iter.next().unwrap();
                    //     // let resolved_props: PropVec = iter.collect();

                    //     // resolved_props
                    //     vec![]
                    // });
                    // let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), resolved_props, event_handlers.to_owned(), value_bindings.to_owned());
                    // ops_writer.write_op(&op);
                },
                _ => { ops_writer.write_op(comp_op); }
            };
        }

        pub fn write_component<W>(&mut self, ops_writer: &mut W, comp: &Component)
          where W: OpsWriter
        {
            if let Some(ops_iter) = comp.ops_iter() {
                for op in ops_iter {
                    ops_writer.write_op(op);
                }
            };
        }
    }

    #[test]
    pub fn test_process_comp_def1() {
        let mut output = CompDefProcessorOutput::default();

        let comp_definition_scope_id: String;
        // let element_param_defs_scope_id: String;
        {
            let mut ctx = Context::default();
            let mut processor = CompDefProcessor::with_output(&mut output);

            // component Component(todo)
            // Create new component context with unbound formal prop (todo)
            {
                let formals: FormalPropVec = vec![("todo")];
                processor.push_component_definition_scope(&mut ctx, "Component", formals.iter());
            }
            comp_definition_scope_id = ctx.scope().id().to_owned();

            // within Component definition
            {
                // The local (todo) should be an unbound formal prop (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    // Some(Symbol::ref_prop_in_scope("todo", "todo", Some(&lm_element_scope_id)))
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
            // element_param_defs_scope_id = ctx.scope().id().to_owned();

            // element Pq invocation
            {
                let props: PropKeyRefVec = vec![
                    ("value".into(), "todo".into())
                ];
                processor.push_element_parameter_definition_scope(&mut ctx, "Pq", "input", Some(props.iter()));
            }
            // let element_pq_invocation_scope_id = ctx.scope().id().to_owned();

            // element Pq scope
            {
                processor.push_element_scope(&mut ctx, "Pq", "input");

                // The local (todo) should still be an unbound formal param (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
        }

        // Verify output
        let comp: Component = output.into();
        assert_eq!(comp.ty(), "Component");
        assert_eq!(
            comp.ops_iter().map(|v| v.into_iter().cloned().collect()),
            Some(vec![
                ElementOp::ElementVoid("input".into(), Some("Pq".into()), Some(vec![(
                    "value".into(),
                    Some(ExprValue::SymbolReference(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)) ))
                )]), None, None)
            ])
        );

        // Test writer
        let mut ops_writer = TestOpsWriter::default();
        let mut comp_writer = ComponentWriter::default();

        comp_writer.write_component(&mut ops_writer, &comp);
    }
}<commit_msg>Add test for output ops.<commit_after>#![allow(dead_code)]

use parser::ast::*;
use scope::context::*;
use processing::structs::*;

type FormalProp<'a> = (&'a str);
type FormalPropVec<'a> = Vec<FormalProp<'a>>;

type PropKeyRef = (String, String);
type PropKeyRefVec = Vec<PropKeyRef>;

type PropValue<'a> = (&'a str, Option<&'a ExprValue>);
type PropValueVec<'a> = Vec<PropValue<'a>>;

#[derive(Debug, Clone, Default)]
pub struct CompDefProcessorOutput {
    ty: Option<String>,
    ops: Option<OpsVec>
}

impl Into<Component> for CompDefProcessorOutput {
    fn into(self) -> Component {
        Component::new_with_vec(self.ty.unwrap(), self.ops)
    }
}

#[derive(Debug)]
pub struct CompDefProcessor<'out> {
    output: &'out mut CompDefProcessorOutput
}

impl<'out> CompDefProcessor<'out> {
    pub fn with_output(output: &'out mut CompDefProcessorOutput) -> Self {
        CompDefProcessor { output: output }
    }

    fn add_op(&mut self, op: ElementOp) {
        if let Some(ref mut ops) = self.output.ops {
            ops.push(op);
        } else {
            self.output.ops = Some(vec![op]);
        }
    }

    pub fn push_component_definition_scope<'a, I>(&mut self, ctx: &mut Context,component_ty: &str, formals: I)
        where I: IntoIterator<Item = &'a FormalProp<'a>>
    {
        if self.output.ty.is_some() {
            panic!("Already have component defintion in this output.");
        }
        self.output.ty = Some(component_ty.to_owned());

        ctx.push_child_scope();
        for formal in formals {
            ctx.add_unbound_formal_param(formal);
        }
    }

    pub fn push_element_parameter_definition_scope<'a, I>(&mut self, ctx: &mut Context, element_id: &str, element_ty: &str, props: Option<I>)
        where I: IntoIterator<Item = &'a PropKeyRef> + Clone
    {
        // let props = props.map(|props| props.clone().into_iter());
        let element_props = props.as_ref().map(|props| props.clone().into_iter().map(|prop| {
            let key = &prop.0;
            let ref_key = &prop.1;
            let value_sym = ctx.resolve_sym(ref_key);
            if let Some(value_sym) = value_sym {
                return (key.to_owned(), Some(ExprValue::SymbolReference(value_sym)));
            };
            (key.to_owned(), None)
        }).collect());

        let op = ElementOp::ElementVoid(element_ty.to_owned(), Some(element_id.to_owned()), element_props, None, None);
        self.add_op(op);

        let parent_scope_id = ctx.scope().id().to_owned();
        ctx.push_child_scope();
        if let Some(props) = props {
            for prop in props {
                ctx.add_element_prop_ref(&prop.0, &prop.1, Some(&parent_scope_id));
            }
        }
    }

    pub fn push_element_scope(&mut self, ctx: &mut Context, element_id: &str, _element_ty: &str) {
        ctx.push_child_scope();
        ctx.append_path_str(element_id);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::*;
    use parser::ast::*;
    use scope::context::*;

    trait OpsWriter {
        fn write_op(&mut self, op: &ElementOp);
    }

    #[derive(Debug, Default)]
    struct TestOpsWriter {
        output_ops: Vec<ElementOp>
    }

    impl<'a> TestOpsWriter {
        pub fn ops_iter(&'a self) -> impl Iterator<Item = &'a ElementOp> {
            self.output_ops.iter()
        }
    }

    impl OpsWriter for TestOpsWriter {
        fn write_op(&mut self, op: &ElementOp) {
            self.output_ops.push(op.to_owned());
        }
    }

    #[derive(Debug, Default)]
    struct ComponentWriter {
        ctx: Context
    }

    type PropValue<'a> = (&'a str, Option<&'a ExprValue>);

    #[derive(Debug)]
    struct SymbolResolver<'a, I: Iterator<Item = PropValue<'a>>> {
        ctx: &'a mut Context,
        iter: I
    }

    impl<'a, I: Iterator<Item = PropValue<'a>>> SymbolResolver<'a, I>
    {
        pub fn new(ctx: &'a mut Context, iter: I) -> Self {
            SymbolResolver { ctx: ctx, iter: iter }
        }
    }

    impl<'a, I: Iterator<Item = PropValue<'a>>> Iterator for SymbolResolver<'a, I>
    {
        type Item = Prop;

        fn next(&mut self) -> Option<Self::Item> {
            if let Some(prop) = self.iter.next() {
                let key = prop.0.to_owned();
                let expr = &prop.1;
                let resolved_sym = self.ctx.resolve_sym(&key);
                if let Some(resolved_sym) = resolved_sym {
                    let expr = ExprValue::SymbolReference(resolved_sym);
                    return Some((key, Some(expr)));
                };
                return Some((key, expr.map(|p| p.clone())));
            };
            None
        }
    }

    impl<'a> ComponentWriter {
        fn resolve_props<I>(&'a mut self, props: I) -> SymbolResolver<'a, I>
          where I: Iterator<Item = PropValue<'a>>
        {
            SymbolResolver::new(&mut self.ctx, props)
        }

        fn write_op_to<W>(&mut self, ops_writer: &mut W, comp_op: &ElementOp)
          where W: OpsWriter
        {
            match comp_op {
                &ElementOp::ElementVoid(ref element_ty, ref element_id, ref props, ref event_handlers, ref value_bindings) => {
                    if let &Some(ref props) = props {
                        let prop_iter = props.iter().map(|prop| (prop.0.as_str(), prop.1.as_ref()));
                        let iter = self.resolve_props(prop_iter);
                        let resolved_props: Vec<Prop> = iter.collect();

                        let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), Some(resolved_props), event_handlers.to_owned(), value_bindings.to_owned());
                        ops_writer.write_op(&op);
                    };
                },
                _ => { ops_writer.write_op(comp_op); }
            };
        }

        pub fn write_component<W>(&mut self, ops_writer: &mut W, comp: &Component)
          where W: OpsWriter
        {
            if let Some(ops_iter) = comp.ops_iter() {
                for op in ops_iter {
                    ops_writer.write_op(op);
                }
            };
        }
    }

    #[test]
    pub fn test_process_comp_def1() {
        let mut output = CompDefProcessorOutput::default();

        let comp_definition_scope_id: String;
        // let element_param_defs_scope_id: String;
        {
            let mut ctx = Context::default();
            let mut processor = CompDefProcessor::with_output(&mut output);

            // component Component(todo)
            // Create new component context with unbound formal prop (todo)
            {
                let formals: FormalPropVec = vec![("todo")];
                processor.push_component_definition_scope(&mut ctx, "Component", formals.iter());
            }
            comp_definition_scope_id = ctx.scope().id().to_owned();

            // within Component definition
            {
                // The local (todo) should be an unbound formal prop (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    // Some(Symbol::ref_prop_in_scope("todo", "todo", Some(&lm_element_scope_id)))
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
            // element_param_defs_scope_id = ctx.scope().id().to_owned();

            // element Pq invocation
            {
                let props: PropKeyRefVec = vec![
                    ("value".into(), "todo".into())
                ];
                processor.push_element_parameter_definition_scope(&mut ctx, "Pq", "input", Some(props.iter()));
            }
            // let element_pq_invocation_scope_id = ctx.scope().id().to_owned();

            // element Pq scope
            {
                processor.push_element_scope(&mut ctx, "Pq", "input");

                // The local (todo) should still be an unbound formal param (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
        }

        // Verify output
        let comp: Component = output.into();
        assert_eq!(comp.ty(), "Component");
        assert_eq!(
            comp.ops_iter().map(|v| v.into_iter().cloned().collect()),
            Some(vec![
                ElementOp::ElementVoid("input".into(), Some("Pq".into()), Some(vec![(
                    "value".into(),
                    Some(ExprValue::SymbolReference(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)) ))
                )]), None, None)
            ])
        );

        // Test writer
        let mut ops_writer = TestOpsWriter::default();
        let mut comp_writer = ComponentWriter::default();

        comp_writer.write_component(&mut ops_writer, &comp);

        assert_eq!(ops_writer.ops_iter().cloned().collect::<OpsVec>(), vec![
                ElementOp::ElementVoid("input".into(), Some("Pq".into()), Some(vec![(
                    "value".into(),
                    Some(ExprValue::SymbolReference(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)) ))
                )]), None, None)
        ]);
    }
}<|endoftext|>


<commit_before>// A unit struct
#[derive(Debug)]
struct Nil;

// A tuple struct
#[derive(Debug)]
struct Pair(i32, f32);

// A struct with two fields
#[derive(Debug)]
struct Point {
    x: f32,
    y: f32,
}

// Structs can be reused as fields of another struct
#[allow(dead_code)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

pub fn main() {
    // Instantiate a `Point`
    let point: Point = Point { x: 0.3, y: 0.4 };

    // Access the fields of the point
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Destructure the point using a `let` binding
    let Point { x: my_x, y: my_y } = point;
    println!("point coordinates: ({}, {})", my_x, my_y);

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };
    //println!("Rectangle: {:?}", _rectangle);

    // Instantiate a unit struct
    let _nil = Nil;
    // Instantiate a tuple struct
    let pair = Pair(1, 0.1);

    println!("Unit struct {:?}", _nil);

    // Access the fields of a tuple struct
    println!("pair contains {:?} and {:?}", pair.0, pair.1);
    println!("pair struct {:?}", pair);

    // Destructure a tuple struct
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);

    // Calculates the area of a rectangle
    let rect: Rectangle = Rectangle{
        p1: Point{x:-2.0, y: 3.2},
        p2: Point{x: 3.2, y:-1.8},
    };
    println!("Rectangle Area: {:?}", rect_area(rect));
}

// Activity
// Calculates the area of a rectangle
fn rect_area(rect: Rectangle) -> f32 {
    1.0
}

// Activity
// returns a Rectangle with its lower left corner on the point
fn square(p: Point, w: f32) -> Rectangle {
    let point = &p;
    return Rectangle{
        p1: *point,
        p2: Point{x: p.x + w, y: p.y + w},
    };
}<commit_msg>Rust By Examples [RBE] - 3.1 Structures -  rect_area - fix<commit_after>// A unit struct
#[derive(Debug)]
struct Nil;

// A tuple struct
#[derive(Debug)]
struct Pair(i32, f32);

// A struct with two fields
#[derive(Debug)]
struct Point {
    x: f32,
    y: f32,
}

// Structs can be reused as fields of another struct
#[derive(Debug)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

pub fn main() {
    // Instantiate a `Point`
    let point: Point = Point { x: 0.3, y: 0.4 };

    // Access the fields of the point
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Destructure the point using a `let` binding
    let Point { x: my_x, y: my_y } = point;
    println!("point coordinates: ({}, {})", my_x, my_y);

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };
    //println!("Rectangle: {:?}", _rectangle);

    // Instantiate a unit struct
    let _nil = Nil;
    // Instantiate a tuple struct
    let pair = Pair(1, 0.1);

    println!("Unit struct {:?}", _nil);

    // Access the fields of a tuple struct
    println!("pair contains {:?} and {:?}", pair.0, pair.1);
    println!("pair struct {:?}", pair);

    // Destructure a tuple struct
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);

    // Calculates the area of a rectangle
    let rect: Rectangle = Rectangle{
        p1: Point{x:-2.0, y: 3.2},
        p2: Point{x: 3.2, y:-1.8},
    };
    println!("Rectangle Area: {:?}", rect_area(&rect));
    //println!("Rectangle Square: {:?}", square(&point, 3.4));
}

// Activity
// Calculates the area of a rectangle
fn rect_area(rect: &Rectangle) -> f32 {
    let mut width_x = rect.p1.x - rect.p2.x;
    if rect.p1.x < rect.p2.x {
        width_x = -width_x;
    }

    let mut width_y = rect.p1.y - rect.p2.y;
    if rect.p1.y < rect.p2.y {
        width_y = -width_y;
    }

    width_x * width_y
}

// Activity
// returns a Rectangle with its lower left corner on the point
fn square(p: &Point, w: f32) -> Rectangle {
    return Rectangle{
        p1: Point{x: 2.0 + w, y: 1.0 + w},
        p2: Point{x: 2.0 + w, y: 1.0 + w},
    };
}<|endoftext|>


<commit_before>use crate::server::ServerData;
use crate::Database;
use actix_web::{error::ResponseError, get, http::StatusCode, web, HttpRequest, HttpResponse};
use cemu_smm::proto::SMMCourse::{
    SMMCourse_AutoScroll, SMMCourse_CourseTheme, SMMCourse_GameStyle,
};
use mongodb::{oid::ObjectId, ordered::OrderedDocument, Bson};
use serde::Deserialize;
use serde_qs::actix::QsQuery;
use std::sync::MutexGuard;

#[get("/getcourses")]
pub fn get_courses(
    data: web::Data<ServerData>,
    query: QsQuery<GetCourses>,
    req: HttpRequest,
) -> Result<String, GetCoursesError> {
    data.get_courses(query.into_inner())
}

#[derive(Deserialize, Debug)]
pub struct GetCourses {
    #[serde(default)]
    limit: Limit,
    skip: Option<u32>,
    id: Option<String>,
    ids: Option<Vec<String>>,
    title: Option<String>,
    maker: Option<String>,
    owner: Option<String>,
    uploader: Option<String>,
    game_style: Option<Vec<SMMCourse_GameStyle>>,
    course_theme: Option<Vec<SMMCourse_CourseTheme>>,
    course_theme_sub: Option<Vec<SMMCourse_CourseTheme>>,
    auto_scroll: Option<Vec<SMMCourse_AutoScroll>>,
    auto_scroll_sub: Option<Vec<SMMCourse_AutoScroll>>,
    width_from: Option<i32>,
    width_to: Option<i32>,
    width_sub_from: Option<i32>,
    width_sub_to: Option<i32>,
    nintendoid: Option<String>,
    difficulty_from: Option<u32>,
    difficulty_to: Option<u32>,
    videoid: Option<String>,
    lastmodified_from: Option<u32>,
    lastmodified_to: Option<u32>,
    uploaded_from: Option<u32>,
    uploaded_to: Option<u32>,
    stars_from: Option<u32>,
    stars_to: Option<u32>,
}

impl GetCourses {
    pub fn into_ordered_document(
        self,
        database: &MutexGuard<Database>,
    ) -> Result<Vec<OrderedDocument>, GetCoursesError> {
        let mut pipeline = vec![];

        if let Some(pipeline_match) = self.get_match(database)? {
            pipeline.push(doc! { "$match" => pipeline_match });
        }

        let limit = self.get_limit()?;
        pipeline.push(doc! {
            "$limit" => limit
        });

        if let Some(skip) = self.skip {
            pipeline.push(doc! {
                "$skip" => skip
            });
        }

        Ok(pipeline)
    }

    fn get_match(
        &self,
        database: &MutexGuard<Database>,
    ) -> Result<Option<OrderedDocument>, GetCoursesError> {
        let mut res = doc! {};
        if let Some(id) = &self.id {
            res.insert_bson(
                "_id".to_string(),
                Bson::ObjectId(
                    ObjectId::with_string(id)
                        .map_err(|_| GetCoursesError::DeserializeError("id".to_string()))?,
                ),
            );
        }

        if let Some(ids) = self.ids.clone() {
            let ids: Vec<Bson> = ids
                .iter()
                .map(|id| -> Result<Bson, GetCoursesError> {
                    let object_id = ObjectId::with_string(id)
                        .map_err(|_| GetCoursesError::DeserializeError("ids".to_string()))?;
                    Ok(Bson::ObjectId(object_id))
                })
                .filter_map(Result::ok)
                .collect();
            res.insert_bson(
                "_id".to_string(),
                Bson::Document(doc! {
                    "$in" => ids
                }),
            );
        }

        if let Some(title) = self.title.clone() {
            res.insert_bson(
                "title".to_string(),
                Bson::RegExp(format!(".*{}.*", title), "i".to_string()),
            );
        }

        if let Some(maker) = self.maker.clone() {
            res.insert_bson(
                "maker".to_string(),
                Bson::RegExp(format!(".*{}.*", maker), "i".to_string()),
            );
        }

        if let Some(owner) = &self.owner {
            res.insert_bson(
                "owner".to_string(),
                Bson::ObjectId(
                    ObjectId::with_string(owner)
                        .map_err(|_| GetCoursesError::DeserializeError("owner".to_string()))?,
                ),
            );
        }

        if let Some(uploader) = &self.uploader {
            let filter = doc! {
                "username" => Bson::RegExp(format!("^{}$", uploader), "i".to_string())
            };
            match database.find_account(filter) {
                Some(account) => {
                    res.insert_bson("owner".to_string(), Bson::ObjectId(account.get_id()));
                }
                None => return Err(GetCoursesError::UploaderUnknown(uploader.clone())),
            };
        }

        match res.is_empty() {
            true => Ok(None),
            false => Ok(Some(res)),
        }
    }

    fn get_limit(&self) -> Result<u32, GetCoursesError> {
        let limit = self.limit.0;
        if limit <= 0 {
            return Err(GetCoursesError::LimitTooLow);
        }
        if limit > 120 {
            return Err(GetCoursesError::LimitTooHigh);
        }
        Ok(limit + self.skip.unwrap_or_default())
    }
}

#[derive(Deserialize, Debug)]
struct Limit(u32);

impl Default for Limit {
    fn default() -> Limit {
        Limit(120)
    }
}

#[derive(Fail, Debug)]
pub enum GetCoursesError {
    #[fail(display = "limit must be at least 1")]
    LimitTooLow,
    #[fail(display = "limit must be at most 120")]
    LimitTooHigh,
    #[fail(display = "could not deserialize {} fom hex string", _0)]
    DeserializeError(String),
    #[fail(display = "uploader with name {} unknown", _0)]
    UploaderUnknown(String),
}

impl ResponseError for GetCoursesError {
    fn error_response(&self) -> HttpResponse {
        match *self {
            GetCoursesError::LimitTooLow => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::LimitTooHigh => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::DeserializeError(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::UploaderUnknown(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
        }
    }
}
<commit_msg>add game style and course theme filter<commit_after>use crate::server::ServerData;
use crate::Database;
use actix_web::{error::ResponseError, get, http::StatusCode, web, HttpRequest, HttpResponse};
use cemu_smm::proto::SMMCourse::{
    SMMCourse_AutoScroll, SMMCourse_CourseTheme, SMMCourse_GameStyle,
};
use mongodb::{oid::ObjectId, ordered::OrderedDocument, Bson};
use protobuf::ProtobufEnum;
use serde::Deserialize;
use serde_qs::actix::QsQuery;
use std::sync::MutexGuard;

#[get("/getcourses")]
pub fn get_courses(
    data: web::Data<ServerData>,
    query: QsQuery<GetCourses>,
    req: HttpRequest,
) -> Result<String, GetCoursesError> {
    data.get_courses(query.into_inner())
}

#[derive(Deserialize, Debug)]
pub struct GetCourses {
    #[serde(default)]
    limit: Limit,
    skip: Option<u32>,
    id: Option<String>,
    ids: Option<Vec<String>>,
    title: Option<String>,
    maker: Option<String>,
    owner: Option<String>,
    uploader: Option<String>,
    game_style: Option<Vec<SMMCourse_GameStyle>>,
    course_theme: Option<Vec<SMMCourse_CourseTheme>>,
    course_theme_sub: Option<Vec<SMMCourse_CourseTheme>>,
    auto_scroll: Option<Vec<SMMCourse_AutoScroll>>,
    auto_scroll_sub: Option<Vec<SMMCourse_AutoScroll>>,
    width_from: Option<i32>,
    width_to: Option<i32>,
    width_sub_from: Option<i32>,
    width_sub_to: Option<i32>,
    nintendoid: Option<String>,
    difficulty_from: Option<u32>,
    difficulty_to: Option<u32>,
    videoid: Option<String>,
    lastmodified_from: Option<u32>,
    lastmodified_to: Option<u32>,
    uploaded_from: Option<u32>,
    uploaded_to: Option<u32>,
    stars_from: Option<u32>,
    stars_to: Option<u32>,
}

impl GetCourses {
    pub fn into_ordered_document(
        self,
        database: &MutexGuard<Database>,
    ) -> Result<Vec<OrderedDocument>, GetCoursesError> {
        let mut pipeline = vec![];

        if let Some(pipeline_match) = self.get_match(database)? {
            pipeline.push(doc! { "$match" => pipeline_match });
        }

        let limit = self.get_limit()?;
        pipeline.push(doc! {
            "$limit" => limit
        });

        if let Some(skip) = self.skip {
            pipeline.push(doc! {
                "$skip" => skip
            });
        }

        Ok(pipeline)
    }

    fn get_match(
        &self,
        database: &MutexGuard<Database>,
    ) -> Result<Option<OrderedDocument>, GetCoursesError> {
        let mut res = doc! {};
        if let Some(id) = &self.id {
            res.insert_bson(
                "_id".to_string(),
                Bson::ObjectId(
                    ObjectId::with_string(id)
                        .map_err(|_| GetCoursesError::DeserializeError("id".to_string()))?,
                ),
            );
        }

        if let Some(ids) = self.ids.clone() {
            let ids: Vec<Bson> = ids
                .iter()
                .map(|id| -> Result<Bson, GetCoursesError> {
                    let object_id = ObjectId::with_string(id)
                        .map_err(|_| GetCoursesError::DeserializeError("ids".to_string()))?;
                    Ok(Bson::ObjectId(object_id))
                })
                .filter_map(Result::ok)
                .collect();
            res.insert_bson(
                "_id".to_string(),
                Bson::Document(doc! {
                    "$in" => ids
                }),
            );
        }

        if let Some(title) = self.title.clone() {
            res.insert_bson(
                "title".to_string(),
                Bson::RegExp(format!(".*{}.*", title), "i".to_string()),
            );
        }

        if let Some(maker) = self.maker.clone() {
            res.insert_bson(
                "maker".to_string(),
                Bson::RegExp(format!(".*{}.*", maker), "i".to_string()),
            );
        }

        if let Some(owner) = &self.owner {
            res.insert_bson(
                "owner".to_string(),
                Bson::ObjectId(
                    ObjectId::with_string(owner)
                        .map_err(|_| GetCoursesError::DeserializeError("owner".to_string()))?,
                ),
            );
        }

        if let Some(uploader) = &self.uploader {
            let filter = doc! {
                "username" => Bson::RegExp(format!("^{}$", uploader), "i".to_string())
            };
            match database.find_account(filter) {
                Some(account) => {
                    res.insert_bson("owner".to_string(), Bson::ObjectId(account.get_id()));
                }
                None => return Err(GetCoursesError::UploaderUnknown(uploader.clone())),
            };
        }

        if let Some(game_styles) = &self.game_style {
            let game_styles: Vec<Bson> = game_styles
                .iter()
                .map(|game_style| Bson::I32(game_style.value()))
                .collect();
            res.insert_bson(
                "gameStyle".to_string(),
                Bson::Document(doc! {
                    "$in" => game_styles
                }),
            );
        }

        if let Some(course_themes) = &self.course_theme {
            let course_themes: Vec<Bson> = course_themes
                .iter()
                .map(|course_theme| Bson::I32(course_theme.value()))
                .collect();
            res.insert_bson(
                "courseTheme".to_string(),
                Bson::Document(doc! {
                    "$in" => course_themes
                }),
            );
        }

        if let Some(course_themes) = &self.course_theme_sub {
            let course_themes: Vec<Bson> = course_themes
                .iter()
                .map(|course_theme| Bson::I32(course_theme.value()))
                .collect();
            res.insert_bson(
                "courseThemeSub".to_string(),
                Bson::Document(doc! {
                    "$in" => course_themes
                }),
            );
        }

        match res.is_empty() {
            true => Ok(None),
            false => Ok(Some(res)),
        }
    }

    fn get_limit(&self) -> Result<u32, GetCoursesError> {
        let limit = self.limit.0;
        if limit <= 0 {
            return Err(GetCoursesError::LimitTooLow);
        }
        if limit > 120 {
            return Err(GetCoursesError::LimitTooHigh);
        }
        Ok(limit + self.skip.unwrap_or_default())
    }
}

#[derive(Deserialize, Debug)]
struct Limit(u32);

impl Default for Limit {
    fn default() -> Limit {
        Limit(120)
    }
}

#[derive(Fail, Debug)]
pub enum GetCoursesError {
    #[fail(display = "limit must be at least 1")]
    LimitTooLow,
    #[fail(display = "limit must be at most 120")]
    LimitTooHigh,
    #[fail(display = "could not deserialize {} fom hex string", _0)]
    DeserializeError(String),
    #[fail(display = "uploader with name {} unknown", _0)]
    UploaderUnknown(String),
}

impl ResponseError for GetCoursesError {
    fn error_response(&self) -> HttpResponse {
        match *self {
            GetCoursesError::LimitTooLow => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::LimitTooHigh => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::DeserializeError(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCoursesError::UploaderUnknown(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
        }
    }
}
<|endoftext|>


<commit_before>extern crate piston_truetype;

use std::ptr::{null_mut};
use piston_truetype::*;

fn expect_glyph(letter: char, expected: String) {
    unsafe {
        let bs = include_bytes!("Tuffy_Bold.ttf");
        let s = 20.0;

        let mut w = 0;
        let mut h = 0;

        let offset = get_font_offset_for_index(bs.as_ptr(),0) as usize;

        let font = FontInfo::new_with_offset(&bs[..], offset).ok().expect("Failed to load font");
        let scale = font.scale_for_pixel_height(s);
        let bitmap = get_codepoint_bitmap(&font, 0.0,scale, letter as isize, &mut w, &mut h, null_mut(),null_mut());

        let mut result = String::new();
        for j in 0..h {
            for i in 0..w {
                result.push([' ', '.', ':', 'i', 'o', 'V', 'M', '@'][((*bitmap.offset(j*w+i))>>5) as usize]);
            }
            result.push('\n');
        }

        if result != expected {
            println!("\n{:?}", expected);
            println!("{:?}", result);
            panic!("The `A` is malformed.\n\n\nExpected:\n{}|\n\nGot:\n{}|", result, expected);
        }
    }
}

#[test]
fn draw_capital_a() {
    expect_glyph('A', String::new() +
        "    VMM     \n" +
        "    @@@i    \n" +
        "   i@@@M    \n" +
        "   M@o@@.   \n" +
        "  .@@.V@o   \n" +
        "  o@M i@@   \n" +
        "  M@i .@@.  \n" +
        " .@@@@@@@o  \n" +
        " o@@@@@@@@  \n" +
        " @@o   .@@: \n" +
        ":@@.    M@V \n" +
        "V@M     i@@ \n" );
}

#[test]
fn draw_capital_g() {
    expect_glyph('G', String::new() +
        "     .     \n" +
        "   o@@@V.  \n" +
        "  M@@M@@@  \n" +
        " i@@:  Vo. \n" +
        " @@o       \n" +
        ".@@.       \n" +
        ".@@  .oooo:\n" +
        ".@@  .@@@@i\n" +
        " @@:  iiM@i\n" +
        " M@o    @@.\n" +
        " i@@:  i@@ \n" +
        "  M@@MM@@: \n" +
        "   o@@@@:  \n" +
        "     .     \n" );
}
<commit_msg>Fix up error message when glyph-printing fails<commit_after>extern crate piston_truetype;

use std::ptr::{null_mut};
use piston_truetype::*;

fn expect_glyph(letter: char, expected: String) {
    unsafe {
        let bs = include_bytes!("Tuffy_Bold.ttf");
        let s = 20.0;

        let mut w = 0;
        let mut h = 0;

        let offset = get_font_offset_for_index(bs.as_ptr(),0) as usize;

        let font = FontInfo::new_with_offset(&bs[..], offset).ok().expect("Failed to load font");
        let scale = font.scale_for_pixel_height(s);
        let bitmap = get_codepoint_bitmap(&font, 0.0,scale, letter as isize, &mut w, &mut h, null_mut(),null_mut());

        let mut result = String::new();
        for j in 0..h {
            for i in 0..w {
                result.push([' ', '.', ':', 'i', 'o', 'V', 'M', '@'][((*bitmap.offset(j*w+i))>>5) as usize]);
            }
            result.push('\n');
        }

        if result != expected {
            println!("\n{:?}", expected);
            println!("{:?}", result);
            panic!("The `{}` is malformed.\n\n\nExpected:\n{}\n\nGot:\n{}", letter, expected, result);
        }
    }
}

#[test]
fn draw_capital_a() {
    expect_glyph('A', String::new() +
        "    VMM     \n" +
        "    @@@i    \n" +
        "   i@@@M    \n" +
        "   M@o@@.   \n" +
        "  .@@.V@o   \n" +
        "  o@M i@@   \n" +
        "  M@i .@@.  \n" +
        " .@@@@@@@o  \n" +
        " o@@@@@@@@  \n" +
        " @@o   .@@: \n" +
        ":@@.    M@V \n" +
        "V@M     i@@ \n" );
}

#[test]
fn draw_capital_g() {
    expect_glyph('G', String::new() +
        "     .     \n" +
        "   o@@@V.  \n" +
        "  M@@M@@@  \n" +
        " i@@:  Vo. \n" +
        " @@o       \n" +
        ".@@.       \n" +
        ".@@  .oooo:\n" +
        ".@@  .@@@@i\n" +
        " @@:  iiM@i\n" +
        " M@o    @@.\n" +
        " i@@:  i@@ \n" +
        "  M@@MM@@: \n" +
        "   o@@@@:  \n" +
        "     .     \n" );
}
<|endoftext|>


<commit_before>extern crate otpauth;
extern crate time;

#[test]
fn test_hotp() {
    let auth = otpauth::HOTP::new("python");
    let code = auth.generate(4);
    assert!(auth.verify(code, 0, 100));
    assert!(!auth.verify(123456, 0, 100));
}

#[test]
fn test_totp() {
    let auth = otpauth::TOTP::new("python");
    let timestamp1 = time::now().to_timespec().sec as usize;
    let code = auth.generate(30, timestamp1);
    let timestamp2 = time::now().to_timespec().sec as usize;
    assert!(auth.verify(code, 30, timestamp2));
    assert!(!auth.verify(123456, 30, timestamp2));
}

#[test]
fn test_to_uri_hotp() {
    let auth = otpauth::HOTP::new("python");
    let expect = "otpauth://hotp/python?secret=OB4XI2DPNY&issuer=python&counter=4";
    assert_eq!(expect, auth.to_uri("python", "python", 4));
}

#[test]
fn test_to_uri_totp() {
    let auth = otpauth::TOTP::new("python");
    let expect = "otpauth://totp/python?secret=OB4XI2DPNY&issuer=python";
    assert_eq!(expect, auth.to_uri("python", "python"));
}
<commit_msg>Better code coverage<commit_after>extern crate otpauth;
extern crate time;

#[test]
fn test_hotp() {
    let auth = otpauth::HOTP::new("python");
    let code = auth.generate(4);
    assert!(auth.verify(code, 0, 100));
    assert!(!auth.verify(123456, 0, 100));
    assert!(!auth.verify(1234567, 0, 100));
}

#[test]
fn test_totp() {
    let auth = otpauth::TOTP::new("python");
    let timestamp1 = time::now().to_timespec().sec as usize;
    let code = auth.generate(30, timestamp1);
    let timestamp2 = time::now().to_timespec().sec as usize;
    assert!(auth.verify(code, 30, timestamp2));
    assert!(!auth.verify(123456, 30, timestamp2));
    assert!(!auth.verify(1234567, 30, timestamp2));
}

#[test]
fn test_to_uri_hotp() {
    let auth = otpauth::HOTP::new("python");
    let expect = "otpauth://hotp/python?secret=OB4XI2DPNY&issuer=python&counter=4";
    assert_eq!(expect, auth.to_uri("python", "python", 4));
}

#[test]
fn test_to_uri_totp() {
    let auth = otpauth::TOTP::new("python");
    let expect = "otpauth://totp/python?secret=OB4XI2DPNY&issuer=python";
    assert_eq!(expect, auth.to_uri("python", "python"));
}
<|endoftext|>


<commit_before>use parser::ast::ctxt::Context;

use parser::ast::*;
use parser::ast::Expr::*;
use parser::ast::visit::*;

pub fn generate<'a>(ctxt: &Context<'a, 'a>) {
    CodeGenInfo::new(ctxt).visit_ast(ctxt.ast);
}

struct CodeGenInfo<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    current_fct: Option<NodeId>,
}

impl<'a, 'ast> CodeGenInfo<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>) -> CodeGenInfo<'a, 'ast> {
        CodeGenInfo {
            ctxt: ctxt,
            current_fct: None,
        }
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_fct(&mut self, f: &'ast Function) {
        self.current_fct = Some(f.id);

        self.ctxt.function(f.id, |fct| {
            fct.stacksize = 0;
            fct.contains_fct_invocation = false;
        });

        visit::walk_stmt(self, &f.block);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            println!("update me");
            self.ctxt.function(self.current_fct.unwrap(), |fct| {
                fct.contains_fct_invocation = true;
            });
        }

        visit::walk_expr(self, e);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F) where F: FnOnce(&Context) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        generate(&ctxt);

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            let fct1 = ctxt.ast.elements[0].to_function().unwrap();
            let fct2 = ctxt.ast.elements[1].to_function().unwrap();

            assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));
            assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }
}
<commit_msg>commit<commit_after>use parser::ast::ctxt::Context;

use parser::ast::*;
use parser::ast::Expr::*;
use parser::ast::visit::*;

pub fn generate<'a, 'ast>(ctxt: &Context<'a, 'ast>, fct: &'ast Function) {
    CodeGenInfo::new(ctxt, fct).generate();
}

struct CodeGenInfo<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,
}

impl<'a, 'ast> CodeGenInfo<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> CodeGenInfo<'a, 'ast> {
        CodeGenInfo {
            ctxt: ctxt,
            fct: fct,
        }
    }

    fn generate(&mut self) {
        self.ctxt.function(self.fct.id, |fct| {
            fct.stacksize = 0;
            fct.contains_fct_invocation = false;
        });

        self.visit_stmt(&self.fct.block);
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            self.ctxt.function(self.fct.id, |fct| {
                fct.contains_fct_invocation = true;
            });
        }

        visit::walk_expr(self, e);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F)
            where F: for<'a, 'ast> FnOnce(&Context<'a, 'ast>) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            generate(ctxt, ctxt.ast.elements[0].to_function().unwrap());
            // assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));

            // let fct2 = ctxt.ast.elements[1].to_function().unwrap();
            // generate(ctxt, fct2);
            // assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }
}
<|endoftext|>


<commit_before>// memory debug window
extern crate minifb;
use minifb::*;
use std::io::Write;
use c64;
use utils;
mod font;

const DEBUG_W: usize = 640;
const DEBUG_H: usize = 416;

pub struct Debugger
{
    debug_window: minifb::Window,
    font: font::SysFont,
    window_buffer: Vec<u32>,
    mempage_offset: u16,
}

impl Debugger
{
    pub fn new() -> Debugger
    {
        Debugger {
            debug_window: Window::new("Debug window", DEBUG_W, DEBUG_H, Scale::X2, Vsync::No).unwrap(),
            font: font::SysFont::new(DEBUG_W, DEBUG_H),
            window_buffer: vec![0; DEBUG_W * DEBUG_H],
            mempage_offset: 0,
        }
    }

    pub fn render(&mut self, memory: &mut c64::memory::MemShared)
    {
        if self.debug_window.is_key_pressed(Key::PageUp, KeyRepeat::Yes)
        {
            self.mempage_offset += 0x400;
            if self.mempage_offset > 0xFC00 { self.mempage_offset = 0; }
        }

        if self.debug_window.is_key_pressed(Key::PageDown, KeyRepeat::Yes)
        {
            if self.mempage_offset == 0x0000 { self.mempage_offset = 0x10000; }
            self.mempage_offset -= 0x400;
        }

        // dump memory page to screen
        let mut start = 0x0000 + self.mempage_offset;

        let mut title = Vec::new();
        let _ = write!(&mut title, "Page ${:04x}-${:04x}", start, start + 0x3FF);
        self.font.draw_text(&mut self.window_buffer, 0, 0, &String::from_utf8(title).unwrap().to_owned()[..], 0x0A);
        let mut hex_offset_x = 0;
        let mut hex_offset_y = 27;
        for y in 0..25
        {
            for x in 0..40
            {
                let byte = memory.borrow_mut().get_ram_bank(c64::memory::MemType::RAM).read(start);
                self.font.draw_char(&mut self.window_buffer, 8*x as usize, 8 + 8*y as usize, byte, 0x05);

                self.draw_hex(hex_offset_x + x as usize, hex_offset_y + y as usize, byte);
                hex_offset_x += 1;
                start += 1;
            }

            hex_offset_x = 0;
        }

        self.draw_registers(memory);

        self.debug_window.update(&self.window_buffer);
    }

    fn draw_hex(&mut self, x_pos: usize, y_pos: usize, byte: u8 )
    {
        let mut hex_value = Vec::new();
        let _ = write!(&mut hex_value, "{:02X}", byte);

        let mut base_color = utils::fetch_c64_color_rgba(byte >> 4);
        if base_color == 0 { base_color = 0x00333333; }
        //self.set_saturation(&mut base_color, (byte >> 4) as f64 / 15.0);

        // all black? make it at least somewhat visible
        if byte == 0 { base_color = 0x00101010; }

        self.font.draw_text_rgb(&mut self.window_buffer, x_pos, y_pos, &String::from_utf8(hex_value).unwrap().to_owned()[..], base_color);
    }

    fn draw_registers(&mut self, memory: &mut c64::memory::MemShared)
    {
        let d018 = memory.borrow_mut().get_ram_bank(c64::memory::MemType::IO).read(0xD018);
        let dd00 = memory.borrow_mut().get_ram_bank(c64::memory::MemType::IO).read(0xDD00);

        let mut vmatrix_txt = Vec::new();
        let mut char_txt = Vec::new();
        let mut bmp_txt = Vec::new();
        let mut bank_txt = Vec::new();
        let _ = write!(&mut vmatrix_txt, "  Screen: ${:04X}", (((d018 >> 4) & 0xF) as u16) * 0x400);
        let _ = write!(&mut char_txt, "    Char: ${:04X}", (((d018 >> 1) & 0x07) as u16) * 0x800);
        let _ = write!(&mut bmp_txt, "  Bitmap: ${:04X}", (((d018 >> 3) & 0x01) as u16) * 0x2000);
        let _ = write!(&mut bank_txt, "VIC bank: ${:04X}", dd00);
        self.font.draw_text(&mut self.window_buffer, 41, 1, &String::from_utf8(vmatrix_txt).unwrap().to_owned()[..], 0x0A);
        self.font.draw_text(&mut self.window_buffer, 41, 2, &String::from_utf8(char_txt).unwrap().to_owned()[..], 0x0A);
        self.font.draw_text(&mut self.window_buffer, 41, 3, &String::from_utf8(bmp_txt).unwrap().to_owned()[..], 0x0A);
        self.font.draw_text(&mut self.window_buffer, 41, 4, &String::from_utf8(bank_txt).unwrap().to_owned()[..], 0x0A);
    }

    fn set_saturation(&self, color: &mut u32, change: f64)
    {
        let Pr = 0.299;
        let Pg = 0.587;
        let Pb = 0.114;
        let mut r = ((*color >> 16) & 0xFF) as f64;
        let mut g = ((*color >> 8) & 0xFF) as f64;
        let mut b = (*color & 0xFF) as f64;

        let P = ((r*r*Pr + g*g*Pg + b*b*Pb) as f64).sqrt();

        r = P + (r - P) * change;
        g = P + (g - P) * change;
        b = P + (b - P) * change;

        *color = (r as u32) << 16;
        *color |= (g as u32) << 8;
        *color |= b as u32;
    }
}
<commit_msg>[debug] added more diagnostic info<commit_after>// memory debug window
extern crate minifb;
use minifb::*;
use std::io::Write;
use c64;
use utils;
mod font;

const DEBUG_W: usize = 640;
const DEBUG_H: usize = 416;

pub struct Debugger
{
    debug_window: minifb::Window,
    font: font::SysFont,
    window_buffer: Vec<u32>,
    mempage_offset: u32,
}

impl Debugger
{
    pub fn new() -> Debugger
    {
        Debugger {
            debug_window: Window::new("Debug window", DEBUG_W, DEBUG_H, Scale::X2, Vsync::No).unwrap(),
            font: font::SysFont::new(DEBUG_W, DEBUG_H),
            window_buffer: vec![0; DEBUG_W * DEBUG_H],
            mempage_offset: 0,
        }
    }

    pub fn render(&mut self, memory: &mut c64::memory::MemShared)
    {
        if self.debug_window.is_key_pressed(Key::PageUp, KeyRepeat::Yes)
        {
            self.mempage_offset += 0x400;
            if self.mempage_offset > 0xFC00 { self.mempage_offset = 0; }
        }

        if self.debug_window.is_key_pressed(Key::PageDown, KeyRepeat::Yes)
        {
            if self.mempage_offset == 0x0000 { self.mempage_offset = 0x10000; }
            self.mempage_offset -= 0x400;
        }

        self.draw_border();

        // dump memory page to screen
        let mut start = 0x0000 + self.mempage_offset as u16;

        let mut title = Vec::new();
        let _ = write!(&mut title, "Memory page ${:04x}-${:04x}", start, start + 0x3FF);
        self.font.draw_text(&mut self.window_buffer, 0, 0, &String::from_utf8(title).unwrap().to_owned()[..], 0x0A);
        let mut hex_offset_x = 0;

        for y in 0..25
        {
            for x in 0..40
            {
                let byte = memory.borrow_mut().get_ram_bank(c64::memory::MemType::RAM).read(start);
                self.font.draw_char(&mut self.window_buffer, 8*x as usize, 8 + 8*y as usize, byte, 0x05);

                self.draw_hex(hex_offset_x + x as usize, 27 + y as usize, byte);
                hex_offset_x += 1;
                start += 1;
            }

            hex_offset_x = 0;
        }

        self.draw_gfx_mode(memory);
        self.draw_data(memory);

        self.debug_window.update(&self.window_buffer);
    }

    fn draw_hex(&mut self, x_pos: usize, y_pos: usize, byte: u8 )
    {
        let mut hex_value = Vec::new();
        let _ = write!(&mut hex_value, "{:02X}", byte);

        let mut base_color = utils::fetch_c64_color_rgba(byte >> 4);
        if base_color == 0 { base_color = 0x00333333; }
        //self.set_saturation(&mut base_color, (byte >> 4) as f64 / 15.0);

        // all black? make it at least somewhat visible
        if byte == 0 { base_color = 0x00101010; }

        self.font.draw_text_rgb(&mut self.window_buffer, x_pos, y_pos, &String::from_utf8(hex_value).unwrap().to_owned()[..], base_color);
    }

    fn draw_data(&mut self, memory: &mut c64::memory::MemShared)
    {
        let d018 = memory.borrow_mut().get_ram_bank(c64::memory::MemType::IO).read(0xD018);
        let dd00 = memory.borrow_mut().get_ram_bank(c64::memory::MemType::IO).read(0xDD00);

        let mut vmatrix_txt = Vec::new();
        let mut char_txt = Vec::new();
        let mut bmp_txt = Vec::new();
        let mut bank_txt = Vec::new();
        let _ = write!(&mut vmatrix_txt, "${:04X}", (((d018 >> 4) & 0xF) as u16) * 0x400);
        let _ = write!(&mut char_txt, "${:04X}", (((d018 >> 1) & 0x07) as u16) * 0x800);
        let _ = write!(&mut bmp_txt, "${:04X}", (((d018 >> 3) & 0x01) as u16) * 0x2000);
        let _ = write!(&mut bank_txt, "${:04X}", dd00);
        self.font.draw_text(&mut self.window_buffer, 43, 3, "Screen: ", 0x0F);
        self.font.draw_text(&mut self.window_buffer, 51, 3, &String::from_utf8(vmatrix_txt).unwrap().to_owned()[..], 0x0E);
        self.font.draw_text(&mut self.window_buffer, 45, 4, "Char: ", 0x0F);
        self.font.draw_text(&mut self.window_buffer, 51, 4, &String::from_utf8(char_txt).unwrap().to_owned()[..], 0x0E);
        self.font.draw_text(&mut self.window_buffer, 43, 5, "Bitmap: ", 0x0F);
        self.font.draw_text(&mut self.window_buffer, 51, 5, &String::from_utf8(bmp_txt).unwrap().to_owned()[..], 0x0E);
        self.font.draw_text(&mut self.window_buffer, 41, 6, "VIC Bank: ", 0x0F);
        self.font.draw_text(&mut self.window_buffer, 51, 6, &String::from_utf8(bank_txt).unwrap().to_owned()[..], 0x0E);
    }

    fn draw_gfx_mode(&mut self, memory: &mut c64::memory::MemShared)
    {
        self.font.draw_text(&mut self.window_buffer, 52, 1, "ECM", 0x0B);
        self.font.draw_text(&mut self.window_buffer, 57, 1, "CHR", 0x0B);
        self.font.draw_text(&mut self.window_buffer, 62, 1, "BMP", 0x0B);
        self.font.draw_text(&mut self.window_buffer, 67, 1, "MCM", 0x0B);
    }

    fn draw_border(&mut self)
    {
        for x in 0..80
        {
            self.font.draw_char(&mut self.window_buffer, 8*x as usize, 0, 64, 0x0B);
            self.font.draw_char(&mut self.window_buffer, 8*x as usize, 8*26, 64, 0x0B);
        }

        for y in 1..26
        {
            self.font.draw_char(&mut self.window_buffer, 8*40, 8*y as usize, 66, 0x0B);
        }

        self.font.draw_char(&mut self.window_buffer, 8*40, 0, 114, 0x0B);
        self.font.draw_char(&mut self.window_buffer, 8*40, 8*26, 113, 0x0B);
    }

   /* fn set_saturation(&self, color: &mut u32, change: f64)
    {
        let Pr = 0.299;
        let Pg = 0.587;
        let Pb = 0.114;
        let mut r = ((*color >> 16) & 0xFF) as f64;
        let mut g = ((*color >> 8) & 0xFF) as f64;
        let mut b = (*color & 0xFF) as f64;

        let P = ((r*r*Pr + g*g*Pg + b*b*Pb) as f64).sqrt();

        r = P + (r - P) * change;
        g = P + (g - P) * change;
        b = P + (b - P) * change;

        *color = (r as u32) << 16;
        *color |= (g as u32) << 8;
        *color |= b as u32;
    }*/
}
<|endoftext|>