rtoml/ast/
mod.rs

1use crate::bulletins::handle_value;
2use crate::lexer::Tokens;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone)]
6pub struct Context {
7    pub table_name: Option<String>,
8    pub key_value: HashMap<String, Node>,
9}
10
11impl Context {
12    pub fn get_table(&self) -> Option<&String> {
13        self.table_name.as_ref()
14    }
15    pub fn new(table_name: Option<String>) -> Self {
16        Context {
17            table_name,
18            key_value: HashMap::new(),
19        }
20    }
21    pub fn set_value(&mut self, key: &String, value: Node) {
22        self.key_value.insert(key.to_owned(), value);
23    }
24    pub fn has_context(&self) -> bool {
25        if self.table_name.is_some() {
26            if self.key_value.len() > 0 {
27                true
28            } else {
29                false
30            }
31        } else {
32            false
33        }
34    }
35    pub fn change_context(&mut self, table_name: String) {
36        self.table_name = Some(table_name);
37        self.key_value = HashMap::new();
38    }
39}
40
41#[derive(Debug, Clone)]
42pub enum TomlValue {
43    Int(i64),
44    Floating(f64),
45    String(String),
46    Array(Vec<TomlValue>),
47    Boolean(bool),
48}
49
50#[derive(Debug, Clone)]
51pub enum Node {
52    Value(TomlValue),
53    Table(String),
54}
55
56#[derive(Debug, PartialEq, Eq)]
57pub enum HeadOption<T> {
58    Some(T),
59    None,
60    Head,
61}
62
63impl<T> HeadOption<T> {
64    pub fn is_some(&self) -> bool {
65        if let HeadOption::None = self {
66            false
67        } else {
68            true
69        }
70    }
71}
72
73#[derive(Debug)]
74pub struct Ast {
75    pub child: Vec<Ast>,
76    pub item: HeadOption<Context>,
77}
78
79#[macro_export]
80macro_rules! comp_err {
81    ( $expected : expr , $found : expr) => {
82        panic!(
83            "Compiling error, expected {:?}, found {:?}",
84            $expected, $found
85        );
86    };
87    ( $message : expr) => {
88        panic!($message);
89    };
90}
91
92#[macro_export]
93macro_rules! assert_toml {
94    ( $left : expr , $right : expr) => {
95        if let Some(x) = $left {
96            if x == $right {
97                ()
98            } else {
99                comp_err!($right, x);
100            }
101        }
102    };
103}
104
105impl Node {
106    pub fn get_value(self) -> TomlValue {
107        if let Node::Value(x) = self {
108            x
109        } else {
110            comp_err!("Expected value, found Segment")
111        }
112    }
113    pub fn get_segment(self) -> String {
114        if let Node::Table(x) = self {
115            x
116        } else {
117            comp_err!("Expected table segment, found value")
118        }
119    }
120}
121
122impl Ast {
123    pub fn make(lexemes: Vec<Tokens>) -> Self {
124        let mut peekable = lexemes.into_iter().peekable();
125        let mut head = Ast::new_head();
126        let mut context = Context::new(None);
127        while peekable.peek().is_some() {
128            // Some declaration
129            if let Some(Tokens::Literal(_)) = peekable.peek() {
130                let identifier = peekable.next().unwrap().to_string();
131                if let Some(x) = identifier.chars().nth(0) {
132                    if x.is_numeric() {
133                        comp_err!("identifier names should not start with numbers");
134                    }
135                } else {
136                    comp_err!("Invalid identifier name");
137                }
138                // Validate for equal to sign
139                assert_toml!(peekable.next(), Tokens::Eq);
140                let value = handle_value(&mut peekable);
141                context.set_value(&identifier, value)
142            }
143            // comment
144            if let Some(Tokens::Hash) = peekable.peek() {
145                peekable.next();
146                while peekable.peek().is_some() {
147                    if let Some(Tokens::LineBreak) = peekable.next() {
148                        break;
149                    }
150                }
151            }
152            // table declaration
153            if let Some(Tokens::Sbo) = peekable.next() {
154                if let Some(Tokens::Literal(x)) = peekable.next() {
155                    if context.has_context() {
156                        head.child.push(Ast::new_context(context.clone()));
157                    }
158                    context.change_context(x);
159                }
160            }
161        }
162        head.child.push(Ast::new_context(context.clone()));
163        head
164    }
165    pub fn new_head() -> Self {
166        Ast {
167            child: Vec::new(),
168            item: HeadOption::Head,
169        }
170    }
171    pub fn new_segment(segment: &String) -> Self {
172        let context = Context::new(Some(segment.to_string()));
173        Ast {
174            child: Vec::new(),
175            item: HeadOption::Some(context),
176        }
177    }
178    pub fn new_context(context: Context) -> Self {
179        Ast {
180            child: Vec::new(),
181            item: HeadOption::Some(context),
182        }
183    }
184    pub fn has_token(&self) -> bool {
185        self.item.is_some()
186    }
187}