Skip to content

Commit

Permalink
Implement export star
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Jul 26, 2023
1 parent 960e5ef commit b683e47
Show file tree
Hide file tree
Showing 20 changed files with 204 additions and 25 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ not the subset of ValueScript that has actually been implemented.
- Enforcing `const`
- Temporal dead zones
- Local imports
- Including the many various import and export patterns
- Tree shaking
- Copy-on-write optimizations
- utf8 strings (_not_ JS's utf16 strings)
Expand Down Expand Up @@ -480,8 +481,6 @@ not the subset of ValueScript that has actually been implemented.
- Rest params
- Async functions
- TypeScript namespaces
- `export * from`
- (`export { name } from` _does_ work)
- `import.meta`
- Dynamic imports
- Unusual JS things like passing unintended types to standard functions
Expand Down
14 changes: 14 additions & 0 deletions inputs/passing/imports/exportStar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! test_output(["a","b (local)","this is the foo function","this is the bar function","baz",42])

import * as lotsOfThings from "./stuff/lotsOfThings.ts";

export default () => {
return [
lotsOfThings.a(),
lotsOfThings.b(),
lotsOfThings.foo(),
lotsOfThings.bar(),
lotsOfThings.baz(),
lotsOfThings.x,
];
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! test_output(["this is the bar function","this is the bar function"])

import { bar, barExported } from "./helpers/bar.ts";
import { bar, barExported } from "./stuff/bar.ts";

export default function () {
return [bar(), barExported()];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! test_output("this is the foo function")

import foo from "./helpers/foo.ts";
import foo from "./stuff/foo.ts";

export default function () {
return foo();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! test_output(["this is the foo function","this is the bar function"])

import { bar, foo } from "./helpers/fooAndBar.ts";
import { bar, foo } from "./stuff/fooAndBar.ts";

export default function () {
return [foo(), bar()];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! test_output(["this is the foo function","this is the bar function"])

import foobar from "./helpers/foobar.ts";
import foobar from "./stuff/foobar.ts";

export default function () {
return [foobar.foo(), foobar.bar()];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! test_output("done")

import { type Type } from "./helpers/Type.ts";
import { type Type } from "./stuff/Type.ts";

export default function main() {
const msg: Type = "done";
Expand Down
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions inputs/passing/imports/stuff/circularA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./circularB.ts";

export function a() {
return "a";
}
5 changes: 5 additions & 0 deletions inputs/passing/imports/stuff/circularB.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./circularA.ts";

export function b() {
return "b";
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions inputs/passing/imports/stuff/lotsOfThings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export * from "./fooAndBar.ts";
export * from "./circularA.ts";
export * from "./circularB.ts";

export const x = 42;

export function baz() {
return "baz";
}

// Conflicts with `b` in circularB.ts, but locals have precedence.
// (Conflicts between multiple export*s produce errors.)
export function b() {
return "b (local)";
}
1 change: 1 addition & 0 deletions valuescript_compiler/src/gather_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ where
};

let mut compiler_output = compile_module(&file_contents);
// println!("{}: {}", dependency.path, compiler_output.module);

gm.diagnostics
.entry(dependency.path.clone())
Expand Down
1 change: 1 addition & 0 deletions valuescript_compiler/src/import_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub struct ImportPattern {
pub kind: ImportKind,
}

#[derive(PartialEq, Eq)]
pub enum ImportKind {
Default,
Star,
Expand Down
151 changes: 138 additions & 13 deletions valuescript_compiler/src/link_module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::mem::swap;

use crate::asm::{Definition, DefinitionContent, ExportStar, FnLine, Instruction, Pointer, Value};
use crate::asm::{
Definition, DefinitionContent, ExportStar, FnLine, Instruction, Object, Pointer, Value,
};
use crate::gather_modules::PathAndModule;
use crate::import_pattern::{ImportKind, ImportPattern};
use crate::name_allocator::NameAllocator;
Expand Down Expand Up @@ -174,7 +177,14 @@ fn link_import_patterns(
included_modules: &HashMap<ResolvedPath, (Value, ExportStar)>,
diagnostics: &mut Vec<Diagnostic>,
) {
for definition in &mut module.definitions {
module.export_star = ExportStar {
includes: vec![],
local: flatten_export_star(&module.export_star, &*module, included_modules, diagnostics),
};

let mut new_definitions = HashMap::<Pointer, Definition>::new();

for definition in &module.definitions {
let import_pattern = match ImportPattern::decode(definition) {
Some(import_pattern) => import_pattern,
None => continue,
Expand All @@ -185,24 +195,21 @@ fn link_import_patterns(
path: import_pattern.path.clone(),
};

let (default, namespace) = match included_modules.get(&resolved_path) {
let (default, export_star) = match included_modules.get(&resolved_path) {
Some(el) => el,
None => continue,
};

let export_star = flatten_export_star(export_star, module, included_modules, diagnostics);

let new_definition = Definition {
pointer: import_pattern.pointer,
pointer: import_pattern.pointer.clone(),
content: match import_pattern.kind {
ImportKind::Default => DefinitionContent::Value(default.clone()),
ImportKind::Star => {
// TODO: namespace.includes
DefinitionContent::Value(Value::Object(Box::new(namespace.local.clone())))
}
ImportKind::Name(name) => match namespace.local.try_resolve_key(&name) {
ImportKind::Star => DefinitionContent::Value(Value::Object(Box::new(export_star.clone()))),
ImportKind::Name(name) => match export_star.try_resolve_key(&name) {
Some(value) => DefinitionContent::Value(value.clone()),
None => {
// TODO: namespace.includes

diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
message: format!(
Expand All @@ -218,6 +225,124 @@ fn link_import_patterns(
},
};

*definition = new_definition;
new_definitions.insert(import_pattern.pointer, new_definition);
}

for definition in &mut module.definitions {
if let Some(new_definition) = new_definitions.get_mut(&definition.pointer) {
swap(definition, new_definition);
}
}
}

fn flatten_export_star(
export_star: &ExportStar,
module: &Module,
included_modules: &HashMap<ResolvedPath, (Value, ExportStar)>,
diagnostics: &mut Vec<Diagnostic>,
) -> Object {
let mut include_pointers_to_process = export_star.includes.clone();
let mut include_map = BTreeMap::<String, Value>::new();
let mut processed_includes = HashSet::<ResolvedPath>::new();

let mut i = 0;

while i < include_pointers_to_process.len() {
let include_p = include_pointers_to_process[i].clone();
i += 1;
let mut matched = false;

for defn in &module.definitions {
if defn.pointer == include_p {
matched = true;

let ip = match ImportPattern::decode(defn) {
Some(ip) => ip,
None => {
diagnostics.push(Diagnostic::internal_error(
swc_common::DUMMY_SP,
"Expected import pattern",
));

break;
}
};

if ip.kind != ImportKind::Star {
diagnostics.push(Diagnostic::internal_error(
swc_common::DUMMY_SP,
"Expected import star pattern",
));

break;
}

let path = ResolvedPath::from(ip.path);

let inserted = processed_includes.insert(path.clone());

if !inserted {
break;
}

let matched_export_star = match included_modules.get(&path) {
Some((_, es)) => es,
None => {
diagnostics.push(Diagnostic::internal_error(
swc_common::DUMMY_SP,
"Missing module",
));

break;
}
};

for (k, v) in &matched_export_star.local.properties {
let k_string = match k {
Value::String(k_string) => k_string.clone(),
_ => {
diagnostics.push(Diagnostic::internal_error(
swc_common::DUMMY_SP,
"Expected exported name to be a string",
));

continue;
}
};

let old_value = include_map.insert(k_string.clone(), v.clone());

if old_value.is_some() {
diagnostics.push(Diagnostic::error(
swc_common::DUMMY_SP,
&format!("Conflicting export {}", k_string),
));
}
}

include_pointers_to_process.append(&mut matched_export_star.includes.clone());

break;
}
}

if !matched {
diagnostics.push(Diagnostic::internal_error(
swc_common::DUMMY_SP,
"Failed to match export* pointer",
));
}
}

let mut obj = Object::default();

for (key, value) in include_map {
obj.properties.push((Value::String(key), value));
}

obj
.properties
.append(&mut export_star.local.properties.clone());

obj
}
18 changes: 17 additions & 1 deletion valuescript_compiler/src/module_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl ModuleCompiler {
ExportNamed(en) => self.compile_named_export(en),
ExportDefaultDecl(edd) => self.compile_export_default_decl(edd),
ExportDefaultExpr(ede) => self.module.export_default = self.static_ec().expr(&ede.expr),
ExportAll(_) => self.todo(module_decl.span(), "ExportAll declaration"),
ExportAll(ea) => self.compile_export_all(ea),
TsImportEquals(_) => self.not_supported(module_decl.span(), "TsImportEquals declaration"),
TsExportAssignment(_) => {
self.not_supported(module_decl.span(), "TsExportAssignment declaration")
Expand Down Expand Up @@ -559,6 +559,22 @@ impl ModuleCompiler {
}
}

fn compile_export_all(&mut self, ea: &swc_ecma_ast::ExportAll) {
let defn = self.allocate_defn(&format!("{}_export_star", ident_from_str(&ea.src.value)));

self.module.definitions.push(Definition {
pointer: defn.clone(),
content: DefinitionContent::Lazy(Lazy {
body: vec![FnLine::Instruction(Instruction::ImportStar(
Value::String(ea.src.value.to_string()),
Register::return_(),
))],
}),
});

self.module.export_star.includes.push(defn);
}

fn compile_import(&mut self, import: &swc_ecma_ast::ImportDecl) {
if import.type_only {
return;
Expand Down
6 changes: 2 additions & 4 deletions website/src/playground/files/root/lib/mod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export { default as BinaryTree } from "./BinaryTree.ts";
export { type NotNullish } from "./util.ts";
export { factorize, factorizeAsPowers, isPrime, primes } from "./primes.ts";
export * from "./util.ts";
export * from "./primes.ts";
export { default as Range } from "./Range.ts";

// Note: `export *` would be used here, but it isn't implemented yet.

0 comments on commit b683e47

Please sign in to comment.