summaryrefslogtreecommitdiff
path: root/src/parser.rs
blob: 9a139e4c64d05b7d15e2014fa6d29fff892a1fc9 (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
use crate::tokenizer::{self, If, Keyword::*, Loop};

pub type SplitWithMetadata<'a> = Vec<(&'a str, Option<Metadata>)>;

pub const EXPRESSION_DELIMITERS: &[char] = &[
    ';', '{', '}',
];

#[derive(Clone, Copy, Debug)]
pub enum Scope {
    If(If),
    Loop(Loop),
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Metadata {
    StartScope,
    EndScope,
}

#[derive(Debug, Clone, Copy)]
pub enum ExprT<'a> {
    StartOrStop(bool),
    Decision((&'a str, Scope)),
    IO(&'a str),
    Process(&'a str),
}

#[derive(Debug, Clone, Copy)]
pub struct Expr<'a> {
    pub expr: ExprT<'a>,
    pub meta: Option<Metadata>,
}

pub fn delimit(s: &str) -> SplitWithMetadata<'_> {
    s.split_inclusive(EXPRESSION_DELIMITERS)
        .fold(Vec::new(), |mut acc, part| {
            if let Some((i, ch)) = part
                .char_indices()
                .next_back()
            {
                acc.push((
                    if ch == ';' || ch == '{' || ch == '}' {
                        part[..i].trim()
                    } else {
                        part.trim()
                    },
                    None,
                ));
                match ch {
                    '{' => {
                        if let Some((_, meta)) = acc.last_mut() {
                            *meta = Some(Metadata::StartScope);
                        }
                    }
                    '}' => {
                        if let Some((_, meta)) = acc.last_mut() {
                            *meta = Some(Metadata::EndScope);
                        }
                    }
                    _ => {}
                }
            }
            acc
        })
}

pub fn process(strs: SplitWithMetadata<'_>) -> Vec<Expr<'_>> {
    let mut exprs = Vec::new();

    for (str, meta) in strs {
        let mut exp: Option<ExprT> = None;

        // TODO: fix one line if else as not delimited by {}
        for s in str.split_whitespace() {
            if let Some(token) = tokenizer::parse(if s.starts_with("println") {
                "println"
            } else if s.starts_with("throw") {
                "throw"
            } else {
                s
            }) {
                match token {
                    If(t) => {
                        if let Some(ExprT::Decision((_, Scope::If(If::Else)))) = exp {
                        } else {
                            exp = Some(ExprT::Decision((str, Scope::If(t.to_owned()))));
                        }
                    }
                    Loop(t) => exp = Some(ExprT::Decision((str, Scope::Loop(t.to_owned())))),
                    Throw => exp = Some(ExprT::IO(str)),
                    IO => exp = Some(ExprT::IO(str)),
                }
            }
        }

        match exp {
            Some(expr) => exprs.push(Expr {
                expr,
                meta,
            }),
            None => exprs.push(Expr {
                expr: ExprT::Process(str),
                meta,
            }),
        }
    }

    exprs
}

pub fn parse(str: &str) -> Vec<Expr<'_>> {
    process(delimit(str))
}