kaon/
lib.rs

1//! # Kaon
2//!
3//! The Kaon programming language, including the parser, compiler and vm.
4
5pub mod common;
6pub mod compiler;
7pub mod core;
8pub mod error;
9pub mod runtime;
10
11pub use ahash; // remove later
12
13extern crate fnv;
14
15use common::{Function, KaonFile, Spanned, state::State};
16use compiler::{Resolver, Token, TypeChecker, AST};
17use error::{Error, Errors};
18use runtime::{Vm, VmSettings};
19
20use std::{fmt, fmt::Debug, fmt::Display, path::PathBuf, rc::Rc};
21
22pub use {common::Source, common::Value, compiler::Scope};
23
24pub type Result<T> = std::result::Result<T, KaonError>;
25
26pub enum KaonError {
27    ParserError(Error),
28    CompilerError(String),
29    RuntimeError(String),
30    InvalidScriptPath(String),
31    MultipleErrors(Errors),
32}
33
34impl Display for KaonError {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::CompilerError(error) => write!(f, "{}", error),
38            Self::ParserError(error) => write!(f, "{}", error),
39            Self::RuntimeError(error) => write!(f, "{}", error),
40            Self::InvalidScriptPath(path) => {
41                write!(f, "the path '{}' could not be found", path)
42            }
43            Self::MultipleErrors(errors) => write!(f, "{}", errors),
44        }
45    }
46}
47
48impl Debug for KaonError {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::CompilerError(error) => write!(f, "{error}"),
52            Self::ParserError(error) => write!(f, "{error}"),
53            Self::RuntimeError(error) => write!(f, "{error}"),
54            Self::InvalidScriptPath(path) => {
55                write!(f, "the path '{path}' could not be found")
56            }
57            Self::MultipleErrors(errors) => write!(f, "{errors}"),
58        }
59    }
60}
61
62pub struct KaonSettings {
63    pub stdin: Rc<dyn KaonFile>,
64    pub stdout: Rc<dyn KaonFile>,
65    pub stderr: Rc<dyn KaonFile>,
66}
67
68impl Default for KaonSettings {
69    fn default() -> Self {
70        let settings = VmSettings::default();
71        KaonSettings {
72            stdin: settings.stdin,
73            stdout: settings.stdout,
74            stderr: settings.stderr,
75        }
76    }
77}
78
79/// The main interface for the Kaon langauge.
80/// ## Examples
81/// ```rust
82/// use kaon::{Kaon, KaonError, Value};
83///
84/// fn main() -> Result<(), KaonError> {
85///     let mut kaon = Kaon::new();
86///
87///     let script = "io.println(1 + 2)";
88///     
89///     match kaon.run_from_script(script) {
90///         Ok(value) => println!("{}", value), // 3
91///         Err(err) => println!("{}", err),
92///     }
93///     
94///     Ok(())
95/// }
96/// ```
97pub struct Kaon {
98    pub vm: Vm,
99    pub state: State,
100    chunk: Function,
101}
102
103impl Default for Kaon {
104    fn default() -> Self {
105        Kaon::new()
106    }
107}
108
109impl Kaon {
110    pub fn new() -> Self {
111        Self::with_settings(KaonSettings::default())
112    }
113
114    pub fn with_settings(settings: KaonSettings) -> Self {
115        Kaon {
116            vm: Vm::with_settings(VmSettings {
117                stdin: settings.stdin,
118                stdout: settings.stdout,
119                stderr: settings.stderr,
120            }),
121            state: State::new(),
122            chunk: Function::script(),
123        }
124    }
125
126    /// Compile bytecode from a string.
127    pub fn compile(&mut self, script: &str) -> Result<Function> {
128        let source = Source::contents(script);
129        self.compile_from_source(source)
130    }
131
132    /// Compile bytecode from a source.
133    pub fn compile_from_source(&mut self, source: Rc<Source>) -> Result<Function> {
134        let tokens = self.tokenize(source)?;
135        let ast = self.parse(tokens)?;
136
137        let scope = self.type_check(&ast)?;
138
139        let mut compiler = compiler::Compiler::default();
140        let bytecode = compiler.run(&ast, scope);
141
142        match bytecode {
143            Ok(bytecode) => {
144                self.chunk = bytecode.clone();
145                Ok(bytecode)
146            }
147            Err(err) => panic!("{:?}", err),
148        }
149    }
150
151    /// Compile script with provided scope.
152    pub fn compile_with_scope(
153        &mut self,
154        scope: &mut Scope,
155        source: Rc<Source>,
156    ) -> Result<(Function, Scope)> {
157        let tokens = self.tokenize(source)?;
158        let ast = self.parse(tokens)?;
159
160        let mut resolver = Resolver::with_scope(scope);
161        resolver.resolve_ast(&ast);
162
163        let globals = resolver.global_scope();
164
165        if !resolver.errors.is_empty() {
166            return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
167        }
168
169        let mut compiler = compiler::Compiler::default();
170        let bytecode = compiler.run(&ast, resolver.global_scope());
171
172        match bytecode {
173            Ok(bytecode) => {
174                self.chunk = bytecode.clone();
175                Ok((bytecode, globals))
176            }
177            Err(err) => panic!("{:?}", err),
178        }
179    }
180
181    /// Compiles a [Function] from an [AST] with the supplied [Scope].
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use kaon::{Kaon, Source, Scope};
187    ///
188    /// let mut kaon = Kaon::new();
189    ///
190    /// let tokens = kaon.tokenize(Source::contents("1 + 2")).unwrap();
191    /// let ast = kaon.parse(tokens).unwrap();
192    ///
193    /// assert!(kaon.compile_ast(ast, &mut Scope::new()).is_ok());
194    /// ```
195    ///
196    /// # Errors
197    ///
198    /// This function will return an error if the [AST] is not semantically correct.
199    pub fn compile_ast(
200        &mut self,
201        ast: AST,
202        scope: &mut Scope,
203    ) -> Result<(Function, Scope)> {
204        let mut resolver = Resolver::with_scope(scope);
205        resolver.resolve_ast(&ast);
206
207        let globals = resolver.global_scope();
208
209        if !resolver.errors.is_empty() {
210            return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
211        }
212
213        let mut compiler = compiler::Compiler::default();
214        let bytecode = compiler.run(&ast, resolver.global_scope());
215
216        match bytecode {
217            Ok(bytecode) => {
218                self.chunk = bytecode.clone();
219                Ok((bytecode, globals))
220            }
221            Err(err) => Err(KaonError::CompilerError(err.0)),
222        }
223    }
224
225    /// Run a chunk of bytecode.
226    pub fn run(&mut self) -> Result<Value> {
227        let value = self.vm
228            .execute(Rc::new(self.chunk.clone()))
229            .map_err(KaonError::RuntimeError);
230        
231        self.vm.clear();
232
233        value
234    }
235
236    /// Compile and run from a script.
237    pub fn run_from_script(&mut self, script: &str) -> Result<Value> {
238        self.compile(script)?;
239        self.run()
240    }
241
242    /// Run from a [Source]
243    pub fn run_from_source(&mut self, source: Rc<Source>) -> Result<Value> {
244        self.compile_from_source(source)?;
245        self.run()
246    }
247
248    /// Compile and run a script with the provided scope.
249    pub fn run_with_scope(
250        &mut self,
251        scope: &mut Scope,
252        source: Rc<Source>,
253    ) -> Result<(Value, Scope)> {
254        let scope = self.compile_with_scope(scope, source)?.1;
255        let value = self.run()?;
256
257        Ok((value, scope))
258    }
259
260    /// Generate an [AST] from a script.
261    pub fn parse_from_script(&self, script: &str) -> Result<AST> {
262        let source = Source::contents(script);
263        let tokens = self.tokenize(source)?;
264        self.parse(tokens)
265    }
266
267    /// Parse a stream of [Token]s into an [AST].
268    pub fn parse(&self, tokens: Spanned<Vec<Token>>) -> Result<AST> {
269        let mut parser = compiler::Parser::new(tokens);
270        let ast = parser.parse();
271        match ast {
272            Ok(ast) => Ok(ast),
273            Err(err) => Err(KaonError::ParserError(err)),
274        }
275    }
276
277    /// Tokenize a script.
278    pub fn tokenize(&self, source: Rc<Source>) -> Result<Spanned<Vec<Token>>> {
279        let mut lexer = compiler::Lexer::new(source);
280        let tokens = lexer.tokenize();
281        match tokens {
282            Ok(token_stream) => Ok(token_stream),
283            Err(err) => Err(KaonError::ParserError(err)),
284        }
285    }
286
287    /// Type check an [AST].  
288    pub fn type_check(&mut self, ast: &AST) -> Result<Scope> {
289        let mut resolver = Resolver::default();
290        resolver.resolve_ast(ast);
291
292        if !resolver.errors.is_empty() {
293            return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
294        }
295
296        let mut typechecker = TypeChecker::new();
297        typechecker.check_ast(ast);
298
299        if !typechecker.errors.is_empty() {
300            return Err(KaonError::MultipleErrors(Errors::from(typechecker.errors)));
301        }
302
303        Ok(resolver.global_scope())
304    }
305
306    /// Read a file from provided path.
307    pub fn read_file(&self, path: PathBuf) -> Result<Rc<Source>> {
308        Source::from_file(path.to_str().unwrap())
309            .map_err(|_| KaonError::InvalidScriptPath(path.to_str().unwrap().to_string()))
310    }
311
312    /// Access the VM's prelude
313    pub fn prelude(&mut self) -> &mut State {
314        self.vm.prelude()
315    }
316
317    /// Gets a mutable reference to the global state.
318    pub fn globals(&mut self) -> &mut State {
319        &mut self.state
320    }
321}