pub mod common;
pub mod compiler;
pub mod core;
pub mod error;
pub mod runtime;
pub use ahash;
extern crate fnv;
use common::{Function, KaonFile, Spanned, state::State};
use compiler::{Resolver, Token, TypeChecker, AST};
use error::{Error, Errors};
use runtime::{Vm, VmSettings};
use std::{fmt, fmt::Debug, fmt::Display, path::PathBuf, rc::Rc};
pub use {common::Source, common::Value, compiler::Scope};
pub type Result<T> = std::result::Result<T, KaonError>;
pub enum KaonError {
ParserError(Error),
CompilerError(String),
RuntimeError(String),
InvalidScriptPath(String),
MultipleErrors(Errors),
}
impl Display for KaonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CompilerError(error) => write!(f, "{}", error),
Self::ParserError(error) => write!(f, "{}", error),
Self::RuntimeError(error) => write!(f, "{}", error),
Self::InvalidScriptPath(path) => {
write!(f, "the path '{}' could not be found", path)
}
Self::MultipleErrors(errors) => write!(f, "{}", errors),
}
}
}
impl Debug for KaonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CompilerError(error) => write!(f, "{error}"),
Self::ParserError(error) => write!(f, "{error}"),
Self::RuntimeError(error) => write!(f, "{error}"),
Self::InvalidScriptPath(path) => {
write!(f, "the path '{path}' could not be found")
}
Self::MultipleErrors(errors) => write!(f, "{errors}"),
}
}
}
pub struct KaonSettings {
pub stdin: Rc<dyn KaonFile>,
pub stdout: Rc<dyn KaonFile>,
pub stderr: Rc<dyn KaonFile>,
}
impl Default for KaonSettings {
fn default() -> Self {
let settings = VmSettings::default();
KaonSettings {
stdin: settings.stdin,
stdout: settings.stdout,
stderr: settings.stderr,
}
}
}
pub struct Kaon {
pub vm: Vm,
pub state: State,
chunk: Function,
}
impl Default for Kaon {
fn default() -> Self {
Kaon::new()
}
}
impl Kaon {
pub fn new() -> Self {
Self::with_settings(KaonSettings::default())
}
pub fn with_settings(settings: KaonSettings) -> Self {
Kaon {
vm: Vm::with_settings(VmSettings {
stdin: settings.stdin,
stdout: settings.stdout,
stderr: settings.stderr,
}),
state: State::new(),
chunk: Function::script(),
}
}
pub fn compile(&mut self, script: &str) -> Result<Function> {
let source = Source::contents(script);
self.compile_from_source(source)
}
pub fn compile_from_source(&mut self, source: Rc<Source>) -> Result<Function> {
let tokens = self.tokenize(source)?;
let ast = self.parse(tokens)?;
let scope = self.type_check(&ast)?;
let mut compiler = compiler::Compiler::default();
let bytecode = compiler.run(&ast, scope);
match bytecode {
Ok(bytecode) => {
self.chunk = bytecode.clone();
Ok(bytecode)
}
Err(err) => panic!("{:?}", err),
}
}
pub fn compile_with_scope(
&mut self,
scope: &mut Scope,
source: Rc<Source>,
) -> Result<(Function, Scope)> {
let tokens = self.tokenize(source)?;
let ast = self.parse(tokens)?;
let mut resolver = Resolver::with_scope(scope);
resolver.resolve_ast(&ast);
let globals = resolver.global_scope();
if !resolver.errors.is_empty() {
return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
}
let mut compiler = compiler::Compiler::default();
let bytecode = compiler.run(&ast, resolver.global_scope());
match bytecode {
Ok(bytecode) => {
self.chunk = bytecode.clone();
Ok((bytecode, globals))
}
Err(err) => panic!("{:?}", err),
}
}
pub fn compile_ast(
&mut self,
ast: AST,
scope: &mut Scope,
) -> Result<(Function, Scope)> {
let mut resolver = Resolver::with_scope(scope);
resolver.resolve_ast(&ast);
let globals = resolver.global_scope();
if !resolver.errors.is_empty() {
return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
}
let mut compiler = compiler::Compiler::default();
let bytecode = compiler.run(&ast, resolver.global_scope());
match bytecode {
Ok(bytecode) => {
self.chunk = bytecode.clone();
Ok((bytecode, globals))
}
Err(err) => Err(KaonError::CompilerError(err.0)),
}
}
pub fn run(&mut self) -> Result<Value> {
let value = self.vm
.execute(Rc::new(self.chunk.clone()))
.map_err(KaonError::RuntimeError);
self.vm.clear();
value
}
pub fn run_from_script(&mut self, script: &str) -> Result<Value> {
self.compile(script)?;
self.run()
}
pub fn run_from_source(&mut self, source: Rc<Source>) -> Result<Value> {
self.compile_from_source(source)?;
self.run()
}
pub fn run_with_scope(
&mut self,
scope: &mut Scope,
source: Rc<Source>,
) -> Result<(Value, Scope)> {
let scope = self.compile_with_scope(scope, source)?.1;
let value = self.run()?;
Ok((value, scope))
}
pub fn parse_from_script(&self, script: &str) -> Result<AST> {
let source = Source::contents(script);
let tokens = self.tokenize(source)?;
self.parse(tokens)
}
pub fn parse(&self, tokens: Spanned<Vec<Token>>) -> Result<AST> {
let mut parser = compiler::Parser::new(tokens);
let ast = parser.parse();
match ast {
Ok(ast) => Ok(ast),
Err(err) => Err(KaonError::ParserError(err)),
}
}
pub fn tokenize(&self, source: Rc<Source>) -> Result<Spanned<Vec<Token>>> {
let mut lexer = compiler::Lexer::new(source);
let tokens = lexer.tokenize();
match tokens {
Ok(token_stream) => Ok(token_stream),
Err(err) => Err(KaonError::ParserError(err)),
}
}
pub fn type_check(&mut self, ast: &AST) -> Result<Scope> {
let mut resolver = Resolver::default();
resolver.resolve_ast(ast);
if !resolver.errors.is_empty() {
return Err(KaonError::MultipleErrors(Errors::from(resolver.errors)));
}
let mut typechecker = TypeChecker::new();
typechecker.check_ast(ast);
if !typechecker.errors.is_empty() {
return Err(KaonError::MultipleErrors(Errors::from(typechecker.errors)));
}
Ok(resolver.global_scope())
}
pub fn read_file(&self, path: PathBuf) -> Result<Rc<Source>> {
Source::from_file(path.to_str().unwrap())
.map_err(|_| KaonError::InvalidScriptPath(path.to_str().unwrap().to_string()))
}
pub fn prelude(&mut self) -> &mut State {
self.vm.prelude()
}
pub fn globals(&mut self) -> &mut State {
&mut self.state
}
}