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
use std::{fs::read_to_string, process::exit};

use ariadne::Source;
use std::path::PathBuf;

use crate::Module;
use walkdir::WalkDir;

use self::{error::ParserError, parse::parse_module, token::tokenise};

pub mod error;
pub mod parse;
pub mod token;

pub fn handle_parser_error(err: ParserError, file_name: String, content: String) {
    err.report_gen(file_name.clone())
        .eprint((file_name, Source::from(content)))
        .unwrap();
}

pub fn parse_script(main_script_name: impl Into<String>) -> Result<Module, ParserError> {
    let main_script_name = main_script_name.into();
    let input = read_to_string(main_script_name.clone())?;
    let tokens = tokenise(input.clone());
    let mut module = match parse_module(tokens) {
        Ok(m) => m,
        Err(e) => {
            handle_parser_error(e, main_script_name, input);
            exit(1)
        }
    };

    for i in module.links.clone() {
        let ext_path = match resolve_module_path(i) {
            Some(p) => p,
            None => {
                continue;
            }
        };
        let ext_input = read_to_string(ext_path)?;
        let ext_tokens = tokenise(ext_input);
        let ext_module = match parse_module(ext_tokens) {
            Ok(m) => m,
            Err(e) => {
                handle_parser_error(e, main_script_name, input);
                exit(1)
            }
        };
        module.link_module(ext_module);
    }

    Ok(module)
}

const FILE_EXTENSION: &str = ".august";

pub fn resolve_module_path(namespace: impl Into<String>) -> Option<PathBuf> {
    let namespace = namespace.into();

    // Check locals
    let local_dirs = WalkDir::new(".").into_iter();
    for entry in local_dirs {
        let entry = match entry {
            Ok(e) => e,
            Err(_) => {
                continue;
            }
        };

        if entry
            .file_name()
            .eq_ignore_ascii_case(format!("{}{}", namespace, FILE_EXTENSION))
        {
            return Some(entry.path().to_owned());
        }
    }

    let module_dir = {
        let mut home = match dirs::home_dir() {
            Some(d) => d,
            None => {
                return None;
            }
        };
        home.push(".august");
        home.push("modules");
        home
    };
    // Check globals
    let global_dirs = WalkDir::new(module_dir).into_iter();
    for entry in global_dirs {
        let entry = match entry {
            Ok(e) => e,
            Err(_) => {
                continue;
            }
        };
        if entry
            .file_name()
            .eq_ignore_ascii_case(format!("{}{}", namespace, FILE_EXTENSION))
        {
            return Some(entry.path().to_owned());
        }
    }

    None
}