aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 6b2bc5706ce9df46f9659000be1763c92086201d (plain)
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

use std::io;
use std::env;
use std::io::Write;
use std::path::Path;

mod sexpr;

fn repl(verbose: bool) {

    let stdin = io::stdin();
    let mut stdout = io::stdout();

    loop {
        let raw_input = &mut String::new();
        stdout.write(b"\ncasual> ").unwrap();
        stdout.flush().unwrap();
        stdin.read_line(raw_input).unwrap();
        let raw_input = raw_input; // mutable to immutable reference
        if raw_input.len() == 0 {
            // end-of-line, aka Ctrl-D. Blank line will still have newline char
            stdout.write(b"\nCiao!\n").unwrap();
            return;
        }
        // TODO: use debug or something instead of "verbose"?
        let tokens = match sexpr::sexpr_tokenize(&raw_input) {
            Ok(tokens) => {
                if verbose { println!("Tokens: {}", tokens.join(", ")); };
                tokens
            },
            Err(e) => {
                println!("couldn't tokenize: {}", e);
                continue;
            }
        };
        let ast = match sexpr::sexpr_parse(&tokens, 0) {
            Ok((mut ast_list, _)) => {
                if verbose {
                    for ast in &ast_list {
                        println!("AST: {}", sexpr::sexpr_repr(ast).unwrap());
                    };
                };
                // We're a REPL, so only one expression at a time
                if ast_list.len() > 1 {
                    println!("one expression at a time on the REPL, please!");
                    continue;
                } else if ast_list.len() == 0 {
                    sexpr::SExpr::SNull
                } else {
                    let ast = ast_list.pop().unwrap();
                    ast
                }
            },
            Err(e) => {
                println!("couldn't parse: {}", e);
                continue;
            }
        };
        println!("{}", sexpr::sexpr_repr(&ast).unwrap());
    }
}

fn usage() {
    println!("usage:\tcasual [-h] [-v] [--no-repl] [<files>]");
    println!("");
    println!("Files will be loaded in order, then drop to REPL (unless \"--no-repl\" is passed).");
    println!("Verbose flag (\"-v\") will result in lexed tokens and parsed AST \
        being dumped to stdout (when on REPL).");
}

fn main() {

    let mut verbose: bool = false;
    let mut no_repl: bool = false;

    let mut file_list = Vec::<String>::new();

    for arg in env::args().skip(1) {
        match &*arg {
            "-v" | "--verbose"  => { verbose = true; },
            "--no-repl"         => { no_repl = true; },
            "-h" | "--help"     => { usage(); return; },
            _ if arg.starts_with("-") => {
                println!("Unknown option: {}", arg);
                println!("");
                usage();
                return;
            },
            _ => {
                file_list.push(arg.clone());
            }
        }
    }

    for fname in file_list {
        let fpath = Path::new(&fname);
        if !fpath.is_file() {
            println!("File not found (or not file): {}", fname);
            return;
        }
        println!("Loading {}...", fname);
        match sexpr::sexpr_parse_file(&fpath) {
            Err(e) => {
                println!("Error loading file: {}\n    {}", fname, e);
                return;
            },
            Ok(_) => ()
        }
    }

    if !no_repl {
        repl(verbose);
    }
}