Make from __future__ imports a syntactic construct in the compiler

This commit is contained in:
Noah 2020-02-07 21:31:58 +00:00
parent 330fdce246
commit a499fb775a
2 changed files with 52 additions and 0 deletions

View file

@ -29,6 +29,7 @@ struct Compiler<O: OutputStream = BasicOutputStream> {
source_path: Option<String>,
current_source_location: ast::Location,
current_qualified_path: Option<String>,
done_with_future_stmts: bool,
ctx: CompileContext,
opts: CompileOpts,
}
@ -166,6 +167,7 @@ impl<O: OutputStream> Compiler<O> {
source_path: None,
current_source_location: ast::Location::default(),
current_qualified_path: None,
done_with_future_stmts: false,
ctx: CompileContext {
in_loop: false,
func: FunctionContext::NoFunction,
@ -334,6 +336,16 @@ impl<O: OutputStream> Compiler<O> {
self.set_source_location(statement.location);
use ast::StatementType::*;
match &statement.node {
// we do this here because `from __future__` still executes that `from` statement at runtime,
// we still need to compile the ImportFrom down below
ImportFrom { module, names, .. } if module.as_deref() == Some("__future__") => {
self.compile_future_features(&names)?
}
// if we find any other statement, stop accepting future statements
_ => self.done_with_future_stmts = true,
}
match &statement.node {
Import { names } => {
// import a, b, c as d
@ -2132,6 +2144,38 @@ impl<O: OutputStream> Compiler<O> {
Ok(())
}
fn compile_future_features(
&mut self,
features: &[ast::ImportSymbol],
) -> Result<(), CompileError> {
if self.done_with_future_stmts {
return Err(CompileError {
error: CompileErrorType::InvalidFuturePlacement,
location: self.current_source_location.clone(),
source_path: self.source_path.clone(),
statement: None,
});
}
for feature in features {
match &*feature.symbol {
// Python 3 features; we've already implemented them by default
"nested_scopes" | "generators" | "division" | "absolute_import"
| "with_statement" | "print_function" | "unicode_literals" => {}
// "generator_stop" => {}
// "annotations" => {}
other => {
return Err(CompileError {
error: CompileErrorType::InvalidFutureFeature(other.to_owned()),
location: self.current_source_location.clone(),
source_path: self.source_path.clone(),
statement: None,
})
}
}
}
Ok(())
}
// Scope helpers:
fn enter_scope(&mut self) {
// println!("Enter scope {:?}", self.symbol_table_stack);

View file

@ -58,6 +58,8 @@ pub enum CompileErrorType {
InvalidAwait,
AsyncYieldFrom,
AsyncReturnValue,
InvalidFuturePlacement,
InvalidFutureFeature(String),
}
impl CompileError {
@ -106,6 +108,12 @@ impl fmt::Display for CompileError {
CompileErrorType::AsyncReturnValue => {
"'return' with value inside async generator".to_owned()
}
CompileErrorType::InvalidFuturePlacement => {
"from __future__ imports must occur at the beginning of the file".to_owned()
}
CompileErrorType::InvalidFutureFeature(feat) => {
format!("future feature {} is not defined", feat)
}
};
if let Some(statement) = &self.statement {