From 50369d18f3faf54946b8bdb80d10767e32d1b460 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 23 Jun 2023 15:41:16 +1000 Subject: [PATCH] Partial support for module constants --- valuescript_compiler/src/asm.rs | 11 +++ valuescript_compiler/src/link_module.rs | 15 +++- valuescript_compiler/src/module_compiler.rs | 82 ++++++++++++++++++++- valuescript_compiler/src/scope_analysis.rs | 7 +- 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/valuescript_compiler/src/asm.rs b/valuescript_compiler/src/asm.rs index ff8ff5ff..b52ab829 100644 --- a/valuescript_compiler/src/asm.rs +++ b/valuescript_compiler/src/asm.rs @@ -60,6 +60,17 @@ pub struct Definition { pub content: DefinitionContent, } +impl Default for Definition { + fn default() -> Self { + Definition { + pointer: Pointer { + name: "".to_string(), + }, + content: DefinitionContent::Value(Value::Void), + } + } +} + impl std::fmt::Display for Definition { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{} = {}", self.pointer, self.content) diff --git a/valuescript_compiler/src/link_module.rs b/valuescript_compiler/src/link_module.rs index eca80b3e..1f907723 100644 --- a/valuescript_compiler/src/link_module.rs +++ b/valuescript_compiler/src/link_module.rs @@ -453,6 +453,7 @@ fn shake_tree(module: &mut Module) { pointers_included.insert(pointer.clone()); if let Some(dependencies) = dependency_graph.get(pointer) { + // TODO: Avoid randomness caused by HashSet iteration for dependency in dependencies { if !pointers_included.contains(dependency) { pointers_to_include.push(dependency.clone()); @@ -462,11 +463,21 @@ fn shake_tree(module: &mut Module) { } let previous_definitions = std::mem::take(&mut module.definitions); - let mut new_definitions = Vec::::new(); + let mut new_definitions_map = HashMap::::new(); for definition in previous_definitions { if pointers_included.contains(&definition.pointer) { - new_definitions.push(definition); + new_definitions_map.insert(definition.pointer.clone(), definition); + } + } + + let mut new_definitions = Vec::::new(); + + for pointer in pointers_to_include { + let defn = take(new_definitions_map.get_mut(&pointer).unwrap()); + + if defn.pointer.name != "" { + new_definitions.push(defn); } } diff --git a/valuescript_compiler/src/module_compiler.rs b/valuescript_compiler/src/module_compiler.rs index afd2466c..5eec930c 100644 --- a/valuescript_compiler/src/module_compiler.rs +++ b/valuescript_compiler/src/module_compiler.rs @@ -231,7 +231,7 @@ impl ModuleCompiler { For(for_) => self.not_supported(for_.span, "module level For statement"), ForIn(for_in) => self.not_supported(for_in.span, "module level ForIn statement"), ForOf(for_of) => self.not_supported(for_of.span, "module level ForOf statement"), - Expr(expr) => self.todo(expr.span, "module level Expr statement"), + Expr(expr) => self.not_supported(expr.span, "module level Expr statement"), }; } @@ -244,8 +244,84 @@ impl ModuleCompiler { } Fn(fn_) => self.compile_fn_decl(false, fn_), Var(var_decl) => { - if !var_decl.declare { - self.todo(var_decl.span, "non-declare module level var declaration"); + if var_decl.declare { + // Uses the `declare` typescript keyword. Nothing needed to support this. + return; + } + + if var_decl.kind != swc_ecma_ast::VarDeclKind::Const { + // Only `const` variables in the global area. They cannot be mutated, so might as well + // insist they are `const` for clarity. + self.not_supported(var_decl.span, "non-const module level variable"); + } + + for decl in &var_decl.decls { + let ident = match &decl.name { + swc_ecma_ast::Pat::Ident(bi) => Some(&bi.id), + _ => { + self.todo(decl.name.span(), "Module level destructuring"); + None + } + }; + + let init = match &decl.init { + Some(_) => &decl.init, + _ => { + self.diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + message: format!("const variable without initializer"), + span: decl.init.span(), + }); + + &None + } + }; + + match (ident, init) { + (Some(ident), Some(init)) => { + let value = match static_eval_expr(init) { + Some(value) => value, + None => { + self.todo( + init.span(), + "Determine whether initializer can be statically evaluated", + ); + + continue; + } + }; + + let pointer = match self.scope_analysis.lookup(ident) { + Some(name) => match &name.value { + Value::Pointer(p) => p.clone(), + _ => { + self.diagnostics.push(Diagnostic { + level: DiagnosticLevel::InternalError, + message: "Expected pointer for module constant".to_string(), + span: ident.span(), + }); + + continue; + } + }, + None => { + self.diagnostics.push(Diagnostic { + level: DiagnosticLevel::InternalError, + message: "Failed to lookup name".to_string(), + span: ident.span(), + }); + + continue; + } + }; + + self.module.definitions.push(Definition { + pointer, + content: DefinitionContent::Value(value), + }); + } + _ => {} + } } } TsInterface(_) => {} diff --git a/valuescript_compiler/src/scope_analysis.rs b/valuescript_compiler/src/scope_analysis.rs index e3009d89..e7357a0d 100644 --- a/valuescript_compiler/src/scope_analysis.rs +++ b/valuescript_compiler/src/scope_analysis.rs @@ -705,7 +705,12 @@ impl ScopeAnalysis { for decl in &var_decl.decls { for ident in self.get_pat_idents(&decl.name) { - self.insert_reg_name(scope, name_type, &ident, Some(decl.span.hi)); + let owner_id = scope.borrow().owner_id.clone(); + + match owner_id { + OwnerId::Span(..) => self.insert_reg_name(scope, name_type, &ident, Some(decl.span.hi)), + OwnerId::Module => self.insert_pointer_name(scope, name_type, &ident), + } } } }