🤔 Add a type inference engine, along with typed literals. (#4)
The typed literal formatting mirrors that of Rust. If no type can be inferred for an untagged literal, the type inference engine will warn the user and then assume that they meant an unsigned 64-bit number. (This is slightly inconvenient, because there can be cases in which our Arbitrary instance may generate a unary negation, in which we should assume that it's a signed 64-bit number; we may want to revisit this later.) The type inference engine is a standard two phase one, in which we first generate a series of type constraints, and then we solve those constraints. In this particular implementation, we actually use a third phase to generate a final AST. Finally, to increase the amount of testing performed, I've removed the overflow checking in the evaluator. The only thing we now check for is division by zero. This does make things a trace slower in testing, but hopefully we get more coverage this way.
This commit was merged in pull request #4.
This commit is contained in:
77
src/repl.rs
77
src/repl.rs
@@ -1,6 +1,6 @@
|
||||
use crate::backend::{Backend, BackendError};
|
||||
use crate::ir::Program as IR;
|
||||
use crate::syntax::{Location, ParserError, Statement};
|
||||
use crate::syntax::{ConstantType, Location, ParserError, Statement};
|
||||
use crate::type_infer::TypeInferenceResult;
|
||||
use codespan_reporting::diagnostic::Diagnostic;
|
||||
use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term::{self, Config};
|
||||
@@ -129,17 +129,32 @@ impl REPL {
|
||||
.source();
|
||||
let syntax = Statement::parse(entry, source)?;
|
||||
|
||||
// if this is a variable binding, and we've never defined this variable before,
|
||||
// we should tell cranelift about it. this is optimistic; if we fail to compile,
|
||||
// then we won't use this definition until someone tries again.
|
||||
if let Statement::Binding(_, ref name, _) = syntax {
|
||||
if !self.variable_binding_sites.contains_key(name.as_str()) {
|
||||
self.jitter.define_string(name)?;
|
||||
self.jitter.define_variable(name.clone())?;
|
||||
let program = match syntax {
|
||||
Statement::Binding(loc, name, expr) => {
|
||||
// if this is a variable binding, and we've never defined this variable before,
|
||||
// we should tell cranelift about it. this is optimistic; if we fail to compile,
|
||||
// then we won't use this definition until someone tries again.
|
||||
if !self.variable_binding_sites.contains_key(&name.name) {
|
||||
self.jitter.define_string(&name.name)?;
|
||||
self.jitter
|
||||
.define_variable(name.to_string(), ConstantType::U64)?;
|
||||
}
|
||||
|
||||
crate::syntax::Program {
|
||||
statements: vec![
|
||||
Statement::Binding(loc.clone(), name.clone(), expr),
|
||||
Statement::Print(loc, name),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
nonbinding => crate::syntax::Program {
|
||||
statements: vec![nonbinding],
|
||||
},
|
||||
};
|
||||
|
||||
let (mut errors, mut warnings) = syntax.validate(&mut self.variable_binding_sites);
|
||||
let (mut errors, mut warnings) =
|
||||
program.validate_with_bindings(&mut self.variable_binding_sites);
|
||||
let stop = !errors.is_empty();
|
||||
let messages = errors
|
||||
.drain(..)
|
||||
@@ -154,13 +169,39 @@ impl REPL {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ir = IR::from(syntax);
|
||||
let name = format!("line{}", line_no);
|
||||
let function_id = self.jitter.compile_function(&name, ir)?;
|
||||
self.jitter.module.finalize_definitions()?;
|
||||
let compiled_bytes = self.jitter.bytes(function_id);
|
||||
let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) };
|
||||
compiled_function();
|
||||
Ok(())
|
||||
match program.type_infer() {
|
||||
TypeInferenceResult::Failure {
|
||||
mut errors,
|
||||
mut warnings,
|
||||
} => {
|
||||
let messages = errors
|
||||
.drain(..)
|
||||
.map(Into::into)
|
||||
.chain(warnings.drain(..).map(Into::into));
|
||||
|
||||
for message in messages {
|
||||
self.emit_diagnostic(message)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
TypeInferenceResult::Success {
|
||||
result,
|
||||
mut warnings,
|
||||
} => {
|
||||
for message in warnings.drain(..).map(Into::into) {
|
||||
self.emit_diagnostic(message)?;
|
||||
}
|
||||
let name = format!("line{}", line_no);
|
||||
let function_id = self.jitter.compile_function(&name, result)?;
|
||||
self.jitter.module.finalize_definitions()?;
|
||||
let compiled_bytes = self.jitter.bytes(function_id);
|
||||
let compiled_function =
|
||||
unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) };
|
||||
compiled_function();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user