use super::*;
use crate::error::Error;
use crate::reader::*;
use crate::value;
use crate::value::RefValue;
use crate::vm::*;
use env_logger;
use indexmap::{indexset, IndexMap, IndexSet};
use log;
use std::cell::RefCell;
pub struct Compiler {
parser: Option<parser::Parser>, pub debug: u8, pub(super) restrict: bool, pub(super) statics: RefCell<IndexSet<RefValue>>,
main: ImlParseletModel, constants: IndexMap<String, ImlValue>, }
impl Compiler {
pub fn new() -> Self {
let statics = indexset![
value!(void),
value!(null),
value!(true),
value!(false),
value!(0),
value!(1),
];
let mut compiler = Self {
parser: None,
debug: 0,
restrict: true,
statics: RefCell::new(statics),
main: ImlParseletModel::new(None),
constants: IndexMap::new(),
};
compiler.load_prelude();
compiler.debug = if let Ok(level) = std::env::var("TOKAY_DEBUG") {
level.parse::<u8>().unwrap_or_default()
} else {
0
};
compiler
}
pub(super) fn compile_from_ast(
&mut self,
ast: &RefValue,
name: Option<String>,
) -> Result<Option<Program>, Vec<Error>> {
log::trace!("compile_from_ast");
let main_parselet = ImlRefParselet::new(ImlParselet::new(
Some(self.main.clone()),
None,
None,
Some(name.unwrap_or("__main__".to_string())),
5,
false,
));
self.constants = {
let global_scope = Scope::new(self, ScopeLevel::Parselet(main_parselet.clone()), None);
global_scope
.constants
.borrow_mut()
.extend(self.constants.clone());
ast::traverse(&global_scope, &ast);
global_scope.resolve_usages();
for usage in global_scope.usages.borrow_mut().drain(..) {
global_scope
.push_error(usage.offset(), format!("Use of undefined name '{}'", usage));
}
if !global_scope.errors.borrow().is_empty() {
return Err(global_scope.errors.borrow_mut().drain(..).collect());
}
global_scope.constants.take()
};
self.main = main_parselet.borrow().model.borrow().clone();
self.main.body = ImlOp::Nop;
self.main.begin = ImlOp::Nop;
self.main.end = ImlOp::Nop;
if self.debug > 1 {
println!("--- Intermediate main ---\n{:#?}", main_parselet);
}
let program = ImlProgram::new(ImlValue::from(main_parselet));
match program.compile() {
Ok(program) => {
if self.debug > 1 {
println!("--- Finalized program ---");
program.dump();
}
Ok(Some(program))
}
Err(errors) => Err(errors),
}
}
pub fn compile(&mut self, reader: Reader) -> Result<Option<Program>, Vec<Error>> {
log::trace!("compile");
if self.parser.is_none() {
self.parser = Some(Parser::new());
}
let parser = self.parser.as_ref().unwrap();
let ast = match parser.parse(reader) {
Ok(ast) => ast,
Err(error) => {
return Err(vec![error]);
}
};
if self.debug > 0 {
ast::print(&ast);
}
if let Ok(log) = std::env::var("TOKAY_LOG") {
std::env::set_var("RUST_LOG", log.clone());
env_logger::init();
}
self.compile_from_ast(&ast, None)
}
pub fn compile_from_str(&mut self, src: &str) -> Result<Option<Program>, Vec<Error>> {
self.compile(Reader::new(
None,
Box::new(std::io::Cursor::new(src.to_owned())),
))
}
pub(super) fn register_static(&self, value: RefValue) -> ImlValue {
log::trace!("register_static value = {:?}", value);
let mut statics = self.statics.borrow_mut();
if let Some(value) = statics.get(&value) {
log::trace!("value already known");
ImlValue::Value(value.clone())
} else {
statics.insert(value.clone());
log::trace!("value added to registry");
ImlValue::Value(value)
}
}
}