1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#[macro_use]
extern crate log;

extern crate regex;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;
use regex::Regex;
use std::io::Write;

mod ast;
mod context;
mod error;
mod expression_evaluator;
mod sass;
mod operator;
mod operator_offset;
mod operator_or_token;
mod optimizer;
mod parser;
mod token;
mod token_offset;
mod tokenizer;

use context::Context;
use error::Result;
use tokenizer::Tokenizer;
use parser::Parser;
use sass::output_style::{SassOutputStyle, Nested, Compressed, Expanded,
                         Compact, Debug};

fn resolve_imports(inputpath: &PathBuf) -> Result<String> {
    let mut file = try!(File::open(&inputpath));
    let mut sass = String::new();

    try!(file.read_to_string(&mut sass));

    let mut imports_resolved = String::new();
    for line in sass.split("\n") {
        let re = Regex::new("@import \"([^\"]*)\";").unwrap();

        match re.captures(line) {
            Some(caps) => {
                let imported = try!(resolve_imports(&inputpath.with_file_name(caps.at(1).unwrap())));
                imports_resolved.push_str(&imported);
            },
            None => {
                imports_resolved.push_str(line);
            },
        }
        imports_resolved.push_str("\n");
    }
    Ok(imports_resolved)
}

pub fn compile(input_filename: &str, output: &mut Write, style: &str) -> Result<()> {
    let input_path = PathBuf::from(input_filename);
    let imports_resolved = try!(resolve_imports(&input_path));

    match style {
        "tokens" => {
            let mut tokenizer = Tokenizer::new(&imports_resolved);
            while let Some(token) = tokenizer.next() {
                try!(writeln!(output, "{:?}", token));
            }
        },
        "ast" => {
            let mut parser = Parser::new(&imports_resolved);
            while let Some(root) = parser.next() {
                try!(writeln!(output, "{:#?}", root));
            }
        },
        other => {
            let style: Box<SassOutputStyle> = get_style(other);
            let mut parser  = Parser::new(&imports_resolved);
            let mut context = Context::new();
            while let Some(Ok(ast_root)) = parser.next() {
                let evaluated = ast_root.evaluate(&mut context);
                if let Some(root) = evaluated {
                    let optimized = optimizer::optimize(root);
                    for r in optimized.into_iter() {
                        try!(r.stream(output, &*style));
                    }
                }
            }
        },
    }
    Ok(())
}

fn get_style(style: &str) -> Box<SassOutputStyle> {
    match style {
        "nested"     => Box::new(Nested {}),
        "compressed" => Box::new(Compressed {}),
        "expanded"   => Box::new(Expanded {}),
        "compact"    => Box::new(Compact {}),
        "debug"      => Box::new(Debug {}),
        style        => panic!("Unknown output style {:?}. Please specify one of nested, compressed, expanded, or compact.", style),
    }
}