diff --git a/src/compile.rs b/src/compile.rs index 448524d..bceee87 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -29,6 +29,7 @@ struct Compiler { source_path: Option, current_source_location: ast::Location, current_qualified_path: Option, + done_with_future_stmts: bool, ctx: CompileContext, opts: CompileOpts, } @@ -166,6 +167,7 @@ impl Compiler { 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 Compiler { 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 Compiler { 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); diff --git a/src/error.rs b/src/error.rs index 6f28e3a..85bc9f4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 {