Add support for global variables without type annotation #548

Merged
sb10q merged 4 commits from enhance/global-var into master 2024-10-08 09:05:38 +08:00
4 changed files with 87 additions and 73 deletions
Showing only changes of commit 65a12d9ab3 - Show all commits

View File

@ -1,6 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use nac3parser::ast::{fold::Fold, ExprKind}; use nac3parser::ast::{fold::Fold, ExprKind, Ident};
use super::*; use super::*;
use crate::{ use crate::{
@ -386,28 +386,49 @@ impl TopLevelComposer {
let ExprKind::Name { id: name, .. } = target.node else { let ExprKind::Name { id: name, .. } = target.node else {
return Err(format!( return Err(format!(
"global variable declaration must be an identifier (at {})", "global variable declaration must be an identifier (at {})",
ast.location target.location
)); ));
}; };
if self.keyword_list.contains(&name) { self.register_top_level_var(
return Err(format!(
"cannot use keyword `{}` as a class name (at {})",
name, name,
ast.location Some(annotation.as_ref().clone()),
)); resolver,
mod_path,
target.location,
)
} }
let global_var_name = if mod_path.is_empty() { _ => Err(format!(
name.to_string() "registrations of constructs other than top level classes/functions/variables are not supported (at {})",
} else {
format!("{mod_path}.{name}")
};
if !defined_names.insert(global_var_name.clone()) {
return Err(format!(
"global variable `{}` defined twice (at {})",
global_var_name,
ast.location ast.location
)),
}
}
/// Registers a top-level variable with the given `name` into the composer.
///
/// `annotation` - The type annotation of the top-level variable, or [`None`] if no type
/// annotation is provided.
/// `location` - The location of the top-level variable.
pub fn register_top_level_var(
&mut self,
name: Ident,
annotation: Option<Expr>,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
mod_path: &str,
location: Location,
) -> Result<(StrRef, DefinitionId, Option<Type>), String> {
if self.keyword_list.contains(&name) {
return Err(format!("cannot use keyword `{name}` as a class name (at {location})"));
}
let global_var_name =
if mod_path.is_empty() { name.to_string() } else { format!("{mod_path}.{name}") };
if !self.defined_names.insert(global_var_name.clone()) {
return Err(format!(
"global variable `{global_var_name}` defined twice (at {location})"
)); ));
} }
@ -418,25 +439,15 @@ impl TopLevelComposer {
name, name,
// dummy here, unify with correct type later, // dummy here, unify with correct type later,
ty_to_be_unified, ty_to_be_unified,
*(annotation.clone()), annotation,
resolver, resolver,
Some(ast.location), Some(location),
)).into(), ))
.into(),
None, None,
)); ));
Ok(( Ok((name, DefinitionId(self.definition_ast_list.len() - 1), Some(ty_to_be_unified)))
name,
DefinitionId(self.definition_ast_list.len() - 1),
Some(ty_to_be_unified),
))
}
_ => Err(format!(
"registrations of constructs other than top level classes/functions/variables are not supported (at {})",
ast.location
)),
}
} }
pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> { pub fn start_analysis(&mut self, inference: bool) -> Result<(), HashSet<String>> {
@ -2249,9 +2260,8 @@ impl TopLevelComposer {
let primitives_store = &self.primitives_ty; let primitives_store = &self.primitives_ty;
let mut analyze = |variable_def: &Arc<RwLock<TopLevelDef>>| -> Result<_, HashSet<String>> { let mut analyze = |variable_def: &Arc<RwLock<TopLevelDef>>| -> Result<_, HashSet<String>> {
let variable_def = &mut *variable_def.write(); let TopLevelDef::Variable { ty: dummy_ty, ty_decl, resolver, loc, .. } =
&*variable_def.read()
let TopLevelDef::Variable { ty: dummy_ty, ty_decl, resolver, loc, .. } = variable_def
else { else {
// not top level variable def, skip // not top level variable def, skip
return Ok(()); return Ok(());
@ -2259,6 +2269,7 @@ impl TopLevelComposer {
let resolver = &**resolver.as_ref().unwrap(); let resolver = &**resolver.as_ref().unwrap();
if let Some(ty_decl) = ty_decl {
let ty_annotation = parse_ast_to_type_annotation_kinds( let ty_annotation = parse_ast_to_type_annotation_kinds(
resolver, resolver,
&temp_def_list, &temp_def_list,
@ -2278,6 +2289,8 @@ impl TopLevelComposer {
unifier.unify(*dummy_ty, ty_from_ty_annotation).map_err(|e| { unifier.unify(*dummy_ty, ty_from_ty_annotation).map_err(|e| {
HashSet::from([e.at(Some(loc.unwrap())).to_display(unifier).to_string()]) HashSet::from([e.at(Some(loc.unwrap())).to_display(unifier).to_string()])
})?; })?;
}
Ok(()) Ok(())
}; };

View File

@ -600,7 +600,7 @@ impl TopLevelComposer {
name: String, name: String,
simple_name: StrRef, simple_name: StrRef,
ty: Type, ty: Type,
ty_decl: Expr, ty_decl: Option<Expr>,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>, resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
loc: Option<Location>, loc: Option<Location>,
) -> TopLevelDef { ) -> TopLevelDef {

View File

@ -158,8 +158,8 @@ pub enum TopLevelDef {
/// Type of the global variable. /// Type of the global variable.
ty: Type, ty: Type,
/// The declared type of the global variable. /// The declared type of the global variable, or [`None`] if no type annotation is provided.
ty_decl: Expr, ty_decl: Option<Expr>,
/// Symbol resolver of the module defined the class. /// Symbol resolver of the module defined the class.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>, resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,

View File

@ -248,8 +248,9 @@ fn handle_assignment_pattern(
fn handle_global_var( fn handle_global_var(
target: &Expr, target: &Expr,
value: Option<&Expr>, value: Option<&Expr>,
resolver: &(dyn SymbolResolver + Send + Sync), resolver: &Arc<dyn SymbolResolver + Send + Sync>,
internal_resolver: &ResolverInternal, internal_resolver: &ResolverInternal,
composer: &mut TopLevelComposer,
) -> Result<(), String> { ) -> Result<(), String> {
let ExprKind::Name { id, .. } = target.node else { let ExprKind::Name { id, .. } = target.node else {
return Err(format!( return Err(format!(
@ -262,8 +263,12 @@ fn handle_global_var(
return Err(format!("global variable `{id}` must be initialized in its definition")); return Err(format!("global variable `{id}` must be initialized in its definition"));
}; };
if let Ok(val) = parse_parameter_default_value(value, resolver) { if let Ok(val) = parse_parameter_default_value(value, &**resolver) {
internal_resolver.add_module_global(id, val); internal_resolver.add_module_global(id, val);
let (name, def_id, _) = composer
.register_top_level_var(id, None, Some(resolver.clone()), "__main__", target.location)
.unwrap();
internal_resolver.add_id_def(name, def_id);
Ok(()) Ok(())
} else { } else {
Err(format!( Err(format!(
@ -375,16 +380,12 @@ fn main() {
if let Err(err) = handle_global_var( if let Err(err) = handle_global_var(
target, target,
value.as_ref().map(Box::as_ref), value.as_ref().map(Box::as_ref),
resolver.as_ref(), &resolver,
internal_resolver.as_ref(), internal_resolver.as_ref(),
&mut composer,
) { ) {
panic!("{err}"); panic!("{err}");
} }
let (name, def_id, _) = composer
.register_top_level(stmt, Some(resolver.clone()), "__main__", true)
.unwrap();
internal_resolver.add_id_def(name, def_id);
} }
// allow (and ignore) "from __future__ import annotations" // allow (and ignore) "from __future__ import annotations"