Skip to main content

clac_lang/
lib.rs

1mod builtins;
2pub mod types;
3
4use types::*;
5
6// resolve functions so we don't need to do a costly hashmap lookup
7fn resolve_funcmap(funcs: &mut FuncMap) {
8    for function in &mut funcs.functions {
9        if let Function::Clac(f) = function {
10            for token in f {
11                if let Token::FunctionCall(FunctionRef::Unresolved(name)) = token
12                    && let Some(resolved) = funcs.map.get(name)
13                {
14                    *token = Token::FunctionCall(FunctionRef::Resolved(*resolved));
15                }
16            }
17        }
18    }
19}
20
21fn parse(token: &str) -> Token {
22    use Token::*;
23
24    match token {
25        "print" => Print,
26        "quit" => Quit,
27
28        "drop" => Drop,
29        "swap" => Swap,
30        "rot" => Rot,
31        "pick" => Pick,
32
33        "if" => If,
34        "skip" => Skip,
35
36        ":" => Colon,
37        ";" => Semicolon,
38
39        // "syscall" => Syscall,
40        id => match id.parse() {
41            Ok(num) => Literal(num),
42            Err(_) => FunctionCall(FunctionRef::Unresolved(id.to_string())),
43        },
44    }
45}
46
47impl ClacState {
48    fn execute<'cs>(
49        functions: &'cs FuncMap,
50        stack: &mut ClacStack,
51        token: &Token,
52    ) -> Result<ExecRes<'cs>, ExecError> {
53        match (stack.as_mut_slice(), token) {
54            (_, Token::Literal(n)) => {
55                stack.push(*n);
56                Ok(ExecRes::Executed)
57            }
58            (_, Token::Quit) => Err(ExecError::Quit),
59            (_, Token::FunctionCall(state)) => {
60                let f = match state {
61                    FunctionRef::Resolved(x) => &functions.functions[*x],
62                    FunctionRef::Unresolved(name) => match functions.map.get(name) {
63                        Some(x) => &functions.functions[*x], // NOTE: we SHOULD be executing top level, because otherwise this token should have already been resolved.
64                        None => return Err(ExecError::UnknownFunction(name.to_string())),
65                    },
66                };
67
68                match f {
69                    Function::Clac(f) => Ok(ExecRes::RecursiveCall(f)),
70                    Function::Native(f) => {
71                        f(stack);
72                        Ok(ExecRes::Executed)
73                    }
74                    Function::ClacOp(f) => {
75                        let y = stack.pop().ok_or(ExecError::MissingArguments)?;
76                        let x = stack.pop().ok_or(ExecError::MissingArguments)?;
77
78                        stack.push(f(x, y));
79                        Ok(ExecRes::Executed)
80                    }
81                }
82            }
83
84            ([.., x], Token::Print) => {
85                println!("{x}");
86                stack.pop();
87                Ok(ExecRes::Executed)
88            }
89            ([.., _], Token::Drop) => {
90                stack.pop().expect("unreachable");
91                Ok(ExecRes::Executed)
92            }
93            ([.., x, y], Token::Swap) => {
94                std::mem::swap(x, y);
95                Ok(ExecRes::Executed)
96            }
97            ([.., x, y, z], Token::Rot) => {
98                (*x, *y, *z) = (*y, *z, *x);
99                Ok(ExecRes::Executed)
100            }
101            ([.., 0], Token::If) => {
102                stack.pop().unwrap();
103
104                Ok(ExecRes::Skip(3))
105            }
106            ([.., _], Token::If) => {
107                stack.pop().unwrap();
108
109                Ok(ExecRes::Executed)
110            }
111            ([.., n], Token::Skip) => {
112                let n = *n;
113                stack.pop();
114                Ok(ExecRes::Skip(
115                    n.try_into().map_err(|_| ExecError::InvalidSkip)?,
116                ))
117            }
118            ([.., n], Token::Pick) if (*n > 0) => {
119                let conv: usize = (*n).try_into().unwrap();
120                stack.pop();
121                let got = stack
122                    .get::<usize>(stack.len() - conv)
123                    .ok_or(ExecError::InvalidPick)?;
124
125                stack.push(*got);
126
127                Ok(ExecRes::Executed)
128            }
129            (
130                _,
131                Token::Swap
132                | Token::Print
133                | Token::Drop
134                | Token::Rot
135                | Token::If
136                | Token::Pick
137                | Token::Skip,
138            ) => Err(ExecError::MissingArguments),
139            (_, Token::Semicolon) => unreachable!(),
140            (_, Token::Colon) => unreachable!(),
141        }
142    }
143
144    // we have to split execute_line and this version, due to lifetime problems. When you call clac functions, it will be executing in this context, where the FunctionMap CANNOT be modified, since you cannot define functions within a function.
145    fn execute_line_nontop<'cs>(
146        funcs: &'cs FuncMap,
147        stack: &mut ClacStack,
148        mut callstack: CallStack<'cs>,
149    ) -> Result<(), ExecError> {
150        while let Some(line) = callstack.pop() {
151            // println!("cs = {callstack:?}");
152            let Some((token, xs)) = line.split_first() else {
153                continue;
154            };
155
156            let mut optimize_push = |vals: &[Token]| match vals {
157                [] => {}
158                [Token::Literal(n), Token::Skip, rest @ ..]
159                    if (*n >= 0 && ((*n as usize) == rest.len())) => {}
160                _ => {
161                    callstack.push(xs);
162                }
163            };
164
165            match Self::execute(funcs, stack, token)? {
166                ExecRes::Executed => {
167                    if !xs.is_empty() {
168                        callstack.push(xs);
169                    }
170                }
171                ExecRes::Skip(n) => match xs.split_at_checked(n) {
172                    Some((_, remain)) => {
173                        if !remain.is_empty() {
174                            callstack.push(remain);
175                        }
176                    }
177                    None => return Err(ExecError::InvalidSkip),
178                },
179                ExecRes::RecursiveCall(newfunc) => {
180                    // TODO: tailcall optimization
181                    optimize_push(xs);
182
183                    callstack.push(newfunc);
184                }
185            }
186        }
187
188        Ok(())
189    }
190
191    /// Execute a slice of [`Token`]s representing a line of Clac++ code.
192    pub fn execute_tokens(&mut self, mut line: &[Token]) -> Result<(), ExecError> {
193        let mut cur_func: Option<(&String, Code)> = None;
194
195        let funcs = &mut self.funcmap;
196        let stack = &mut self.stack;
197
198        loop {
199            (line, cur_func) = match (line, cur_func) {
200                (
201                    [
202                        Token::Colon,
203                        Token::FunctionCall(FunctionRef::Unresolved(name)),
204                        rem @ ..,
205                    ],
206                    None,
207                ) => (rem, Some((name, Vec::new()))),
208                ([Token::Semicolon, rem @ ..], Some((name, f))) => {
209                    let len = funcs.functions.len();
210
211                    // if we are re-defining a function, we should replace
212                    match funcs.map.get(name) {
213                        Some(idx) => {
214                            funcs.functions[*idx] = Function::Clac(f);
215                        }
216                        None => {
217                            funcs.functions.push(Function::Clac(f));
218                            funcs.map.insert(name.to_string(), len);
219                        }
220                    };
221
222                    // resolve function names to indices
223                    resolve_funcmap(funcs);
224
225                    (rem, None)
226                }
227                ([Token::Colon | Token::Semicolon, ..], _) => {
228                    return Err(ExecError::BadFunctionDefinition);
229                }
230                ([tok, rem @ ..], Some((nm, mut f))) => {
231                    f.push(tok.clone());
232                    (rem, Some((nm, f)))
233                }
234                ([tok, rem @ ..], None) => match Self::execute(funcs, stack, tok)? {
235                    ExecRes::Executed => (rem, None),
236                    ExecRes::Skip(n) => match rem.split_at_checked(n) {
237                        Some((_, rem2)) => (rem2, None),
238                        None => return Err(ExecError::InvalidSkip),
239                    },
240                    ExecRes::RecursiveCall(f) => {
241                        Self::execute_line_nontop(funcs, stack, vec![f])?;
242                        (rem, None)
243                    }
244                },
245                ([], Some(_)) => return Err(ExecError::BadFunctionDefinition),
246                ([], None) => return Ok(()),
247            };
248        }
249    }
250
251    /// Execute a line of Clac++ code in a string.
252    pub fn execute_str(&mut self, line: &str) -> Result<(), ExecError> {
253        let parsed: Vec<Token> = line.split_whitespace().map(parse).collect();
254
255        self.execute_tokens(&parsed)
256    }
257}
258
259fn name_func_pair_to_funcmap<const N: usize>(xs: [(&str, Function); N]) -> FuncMap {
260    FuncMap {
261        map: ahash::AHashMap::from_iter(
262            xs.iter()
263                .enumerate()
264                .map(|(i, (name, _))| (name.to_string(), i)),
265        ),
266        functions: Vec::from_iter(xs.into_iter().map(|(_, func)| func)),
267    }
268}
269
270impl Default for ClacState {
271    fn default() -> Self {
272        ClacState {
273            stack: Vec::new(),
274            funcmap: name_func_pair_to_funcmap(builtins::FUNCTIONS),
275        }
276    }
277}