mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge pull request #784 from rtfeldman/pkg-config-module
Pkg config module
This commit is contained in:
commit
ab00e7a94e
33 changed files with 407 additions and 312 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,6 +3,9 @@ zig-cache
|
|||
.direnv
|
||||
*.rs.bk
|
||||
|
||||
# llvm human-readable output
|
||||
*.ll
|
||||
|
||||
#valgrind
|
||||
vgcore.*
|
||||
|
||||
|
|
|
@ -143,30 +143,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
fn run_multi_module() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
fn run_multi_module_optimized() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_module)]
|
||||
// TODO: Stop ignoring this test once we are correctly freeing the RocList even when in dev build.
|
||||
|
|
5
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
5
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
|
@ -1,4 +1,7 @@
|
|||
app "multi-dep-str" imports [ Dep1 ] provides [ main ] to "./platform"
|
||||
app "multi-dep-str"
|
||||
packages { base: "platform" }
|
||||
imports [ Dep1 ]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Str
|
||||
main = Dep1.str1
|
||||
|
|
|
@ -3,5 +3,8 @@ platform examples/multi-module
|
|||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
provides [ mainForHost ]
|
||||
effects Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
5
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
5
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
|
@ -1,4 +1,7 @@
|
|||
app "multi-dep-thunk" imports [ Dep1 ] provides [ main ] to "./platform"
|
||||
app "multi-dep-thunk"
|
||||
packages { base: "platform" }
|
||||
imports [ Dep1 ]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Str
|
||||
main = Dep1.value1 {}
|
||||
|
|
|
@ -3,5 +3,8 @@ platform examples/multi-dep-thunk
|
|||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
provides [ mainForHost ]
|
||||
effects Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -551,7 +551,7 @@ pub enum BuildProblem<'a> {
|
|||
#[derive(Debug)]
|
||||
struct ModuleHeader<'a> {
|
||||
module_id: ModuleId,
|
||||
module_name: AppOrInterfaceName<'a>,
|
||||
module_name: ModuleNameEnum<'a>,
|
||||
module_path: PathBuf,
|
||||
exposed_ident_ids: IdentIds,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
|
@ -559,11 +559,17 @@ struct ModuleHeader<'a> {
|
|||
imported_modules: MutSet<ModuleId>,
|
||||
exposes: Vec<Symbol>,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
to_platform: Option<To<'a>>,
|
||||
src: &'a [u8],
|
||||
module_timing: ModuleTiming,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum HeaderFor<'a> {
|
||||
App { to_platform: To<'a> },
|
||||
PkgConfig,
|
||||
Interface,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ConstrainedModule {
|
||||
module: Module,
|
||||
|
@ -620,7 +626,7 @@ pub struct VariablySizedLayouts<'a> {
|
|||
#[derive(Debug)]
|
||||
struct ParsedModule<'a> {
|
||||
module_id: ModuleId,
|
||||
module_name: AppOrInterfaceName<'a>,
|
||||
module_name: ModuleNameEnum<'a>,
|
||||
module_path: PathBuf,
|
||||
src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
|
@ -634,7 +640,7 @@ struct ParsedModule<'a> {
|
|||
#[derive(Debug)]
|
||||
enum Msg<'a> {
|
||||
Many(Vec<Msg<'a>>),
|
||||
Header(ModuleHeader<'a>),
|
||||
Header(ModuleHeader<'a>, HeaderFor<'a>),
|
||||
Parsed(ParsedModule<'a>),
|
||||
CanonicalizedAndConstrained {
|
||||
constrained_module: ConstrainedModule,
|
||||
|
@ -690,6 +696,7 @@ enum Msg<'a> {
|
|||
#[derive(Debug)]
|
||||
struct State<'a> {
|
||||
pub root_id: ModuleId,
|
||||
pub platform_id: Option<ModuleId>,
|
||||
pub goal_phase: Phase,
|
||||
pub stdlib: StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
|
@ -844,12 +851,6 @@ enum BuildTask<'a> {
|
|||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
},
|
||||
LoadPkgConfig {
|
||||
shorthand: &'a str,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
},
|
||||
Parse {
|
||||
header: ModuleHeader<'a>,
|
||||
},
|
||||
|
@ -1272,6 +1273,7 @@ where
|
|||
|
||||
let mut state = State {
|
||||
root_id,
|
||||
platform_id: None,
|
||||
goal_phase,
|
||||
stdlib,
|
||||
output_path: None,
|
||||
|
@ -1414,7 +1416,7 @@ fn update<'a>(
|
|||
|
||||
Ok(state)
|
||||
}
|
||||
Header(header) => {
|
||||
Header(header, header_extra) => {
|
||||
log!("loaded header for {:?}", header.module_id);
|
||||
let home = header.module_id;
|
||||
|
||||
|
@ -1426,7 +1428,20 @@ fn update<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
state.platform_path = state.platform_path.or_else(|| header.to_platform.clone());
|
||||
use HeaderFor::*;
|
||||
match header_extra {
|
||||
App { to_platform } => {
|
||||
debug_assert_eq!(state.platform_path, None);
|
||||
|
||||
state.platform_path = Some(to_platform.clone());
|
||||
}
|
||||
PkgConfig => {
|
||||
debug_assert_eq!(state.platform_id, None);
|
||||
|
||||
state.platform_id = Some(header.module_id);
|
||||
}
|
||||
Interface => {}
|
||||
}
|
||||
|
||||
// store an ID to name mapping, so we know the file to read when fetching dependencies' headers
|
||||
for (name, id) in header.deps_by_name.iter() {
|
||||
|
@ -1478,7 +1493,8 @@ fn update<'a>(
|
|||
//
|
||||
// e.g. for `app "blah"` we should generate an output file named "blah"
|
||||
match &parsed.module_name {
|
||||
AppOrInterfaceName::App(output_str) => match output_str {
|
||||
ModuleNameEnum::PkgConfig(_) => {}
|
||||
ModuleNameEnum::App(output_str) => match output_str {
|
||||
StrLiteral::PlainLine(path) => {
|
||||
state.output_path = Some(path);
|
||||
}
|
||||
|
@ -1486,7 +1502,7 @@ fn update<'a>(
|
|||
todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
|
||||
}
|
||||
},
|
||||
AppOrInterfaceName::Interface(_) => {}
|
||||
ModuleNameEnum::Interface(_) => {}
|
||||
}
|
||||
|
||||
let module_id = parsed.module_id;
|
||||
|
@ -1602,7 +1618,14 @@ fn update<'a>(
|
|||
|
||||
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
||||
|
||||
if module_id == state.root_id {
|
||||
// if there is a platform, the Pkg-Config module provides host-exposed,
|
||||
// otherwise the App module exposes host-exposed
|
||||
let is_host_exposed = match state.platform_id {
|
||||
None => module_id == state.root_id,
|
||||
Some(platform_id) => module_id == platform_id,
|
||||
};
|
||||
|
||||
if is_host_exposed {
|
||||
state
|
||||
.exposed_to_host
|
||||
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
|
||||
|
@ -1637,15 +1660,10 @@ fn update<'a>(
|
|||
// the originally requested module, we're all done!
|
||||
return Ok(state);
|
||||
} else {
|
||||
if module_id != state.root_id {
|
||||
state.exposed_types.insert(
|
||||
module_id,
|
||||
ExposedModuleTypes::Valid(
|
||||
solved_module.solved_types,
|
||||
solved_module.aliases,
|
||||
),
|
||||
);
|
||||
}
|
||||
state.exposed_types.insert(
|
||||
module_id,
|
||||
ExposedModuleTypes::Valid(solved_module.solved_types, solved_module.aliases),
|
||||
);
|
||||
|
||||
if state.goal_phase > Phase::SolveTypes {
|
||||
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
||||
|
@ -1913,6 +1931,7 @@ fn load_pkg_config<'a>(
|
|||
arena: &'a Bump,
|
||||
src_dir: &Path,
|
||||
shorthand: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
|
@ -1933,10 +1952,14 @@ fn load_pkg_config<'a>(
|
|||
let parse_header_duration = parse_start.elapsed().unwrap();
|
||||
|
||||
// Insert the first entries for this module's timings
|
||||
let mut module_timing = ModuleTiming::new(module_start_time);
|
||||
let mut pkg_module_timing = ModuleTiming::new(module_start_time);
|
||||
let mut effect_module_timing = ModuleTiming::new(module_start_time);
|
||||
|
||||
module_timing.read_roc_file = file_io_duration;
|
||||
module_timing.parse_header = parse_header_duration;
|
||||
pkg_module_timing.read_roc_file = file_io_duration;
|
||||
pkg_module_timing.parse_header = parse_header_duration;
|
||||
|
||||
effect_module_timing.read_roc_file = file_io_duration;
|
||||
effect_module_timing.parse_header = parse_header_duration;
|
||||
|
||||
match parsed {
|
||||
Ok((ast::Module::Interface { header }, _parse_state)) => {
|
||||
|
@ -1951,16 +1974,34 @@ fn load_pkg_config<'a>(
|
|||
header
|
||||
)))
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
||||
arena,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
header,
|
||||
module_timing,
|
||||
)
|
||||
.map(|x| x.1),
|
||||
Ok((ast::Module::Platform { header }, parser_state)) => {
|
||||
// make a Pkg-Config module that ultimately exposes `main` to the host
|
||||
let pkg_config_module_msg = fabricate_pkg_config_module(
|
||||
arena,
|
||||
shorthand,
|
||||
app_module_id,
|
||||
filename,
|
||||
parser_state,
|
||||
module_ids.clone(),
|
||||
ident_ids_by_module.clone(),
|
||||
&header,
|
||||
pkg_module_timing,
|
||||
)
|
||||
.map(|x| x.1)?;
|
||||
|
||||
let effects_module_msg = fabricate_effects_module(
|
||||
arena,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
header,
|
||||
effect_module_timing,
|
||||
)
|
||||
.map(|x| x.1)?;
|
||||
|
||||
Ok(Msg::Many(vec![effects_module_msg, pkg_config_module_msg]))
|
||||
}
|
||||
Err((fail, _)) => Err(LoadingProblem::ParsingFailed { filename, fail }),
|
||||
}
|
||||
}
|
||||
|
@ -2086,7 +2127,7 @@ fn parse_header<'a>(
|
|||
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: AppOrInterfaceName::Interface(header.name.value),
|
||||
value: ModuleNameEnum::Interface(header.name.value),
|
||||
},
|
||||
filename,
|
||||
opt_shorthand,
|
||||
|
@ -2108,7 +2149,7 @@ fn parse_header<'a>(
|
|||
let (module_id, app_module_header_msg) = send_header(
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: AppOrInterfaceName::App(header.name.value),
|
||||
value: ModuleNameEnum::App(header.name.value),
|
||||
},
|
||||
filename,
|
||||
opt_shorthand,
|
||||
|
@ -2160,6 +2201,7 @@ fn parse_header<'a>(
|
|||
arena,
|
||||
&pkg_config_roc,
|
||||
shorthand,
|
||||
module_id,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -2272,15 +2314,16 @@ fn load_from_str<'a>(
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AppOrInterfaceName<'a> {
|
||||
enum ModuleNameEnum<'a> {
|
||||
/// A filename
|
||||
App(StrLiteral<'a>),
|
||||
Interface(roc_parse::header::ModuleName<'a>),
|
||||
PkgConfig(&'a str),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header<'a>(
|
||||
loc_name: Located<AppOrInterfaceName<'a>>,
|
||||
loc_name: Located<ModuleNameEnum<'a>>,
|
||||
filename: PathBuf,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
|
@ -2292,9 +2335,10 @@ fn send_header<'a>(
|
|||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
use AppOrInterfaceName::*;
|
||||
use ModuleNameEnum::*;
|
||||
|
||||
let declared_name: ModuleName = match &loc_name.value {
|
||||
PkgConfig(_) => unreachable!(),
|
||||
App(_) => ModuleName::APP.into(),
|
||||
Interface(module_name) => {
|
||||
// TODO check to see if module_name is consistent with filename.
|
||||
|
@ -2442,22 +2486,228 @@ fn send_header<'a>(
|
|||
// We always need to send these, even if deps is empty,
|
||||
// because the coordinator thread needs to receive this message
|
||||
// to decrement its "pending" count.
|
||||
let extra = match to_platform {
|
||||
Some(to_platform) => HeaderFor::App { to_platform },
|
||||
None => HeaderFor::Interface,
|
||||
};
|
||||
|
||||
(
|
||||
home,
|
||||
Msg::Header(ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
to_platform,
|
||||
src: parse_state.bytes,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
}),
|
||||
Msg::Header(
|
||||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
src: parse_state.bytes,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
},
|
||||
extra,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO refactor so more logic is shared with `send_header`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
shorthand: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
requires: &'a [Located<TypedIdent<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
use inlinable_string::InlinableString;
|
||||
|
||||
let declared_name: InlinableString = "".into();
|
||||
|
||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||
Vec::with_capacity(imports.len());
|
||||
let mut imported_modules: MutSet<ModuleId> = MutSet::default();
|
||||
|
||||
let num_exposes = provides.len();
|
||||
let mut deps_by_name: MutMap<PQModuleName, ModuleId> =
|
||||
HashMap::with_capacity_and_hasher(num_exposes, default_hasher());
|
||||
|
||||
// add standard imports
|
||||
// TODO add Effect by default
|
||||
imported_modules.insert(app_module_id);
|
||||
deps_by_name.insert(
|
||||
PQModuleName::Unqualified(ModuleName::APP.into()),
|
||||
app_module_id,
|
||||
);
|
||||
|
||||
let mut scope_size = 0;
|
||||
|
||||
for loc_entry in imports {
|
||||
let (qualified_module_name, exposed) = exposed_from_import(&loc_entry.value);
|
||||
|
||||
scope_size += exposed.len();
|
||||
|
||||
imported.push((qualified_module_name, exposed, loc_entry.region));
|
||||
}
|
||||
|
||||
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
|
||||
|
||||
// Make sure the module_ids has ModuleIds for all our deps,
|
||||
// then record those ModuleIds in can_module_ids for later.
|
||||
let mut scope: MutMap<Ident, (Symbol, Region)> =
|
||||
HashMap::with_capacity_and_hasher(scope_size, default_hasher());
|
||||
let home: ModuleId;
|
||||
|
||||
let ident_ids = {
|
||||
// Lock just long enough to perform the minimal operations necessary.
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
|
||||
|
||||
let name = PQModuleName::Qualified(&shorthand, declared_name);
|
||||
home = module_ids.get_or_insert(&name);
|
||||
|
||||
// Ensure this module has an entry in the exposed_ident_ids map.
|
||||
ident_ids_by_module
|
||||
.entry(home)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
// For each of our imports, add an entry to deps_by_name
|
||||
//
|
||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
//
|
||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
let cloned_module_name = qualified_module_name.module.clone();
|
||||
let pq_module_name = match qualified_module_name.opt_package {
|
||||
None => PQModuleName::Qualified(shorthand, qualified_module_name.module.into()),
|
||||
Some(package) => {
|
||||
PQModuleName::Qualified(package, cloned_module_name.clone().into())
|
||||
}
|
||||
};
|
||||
|
||||
let module_id = module_ids.get_or_insert(&pq_module_name);
|
||||
imported_modules.insert(module_id);
|
||||
|
||||
deps_by_name.insert(pq_module_name, module_id);
|
||||
|
||||
// Add the new exposed idents to the dep module's IdentIds, so
|
||||
// once that module later gets loaded, its lookups will resolve
|
||||
// to the same symbols as the ones we're using here.
|
||||
let ident_ids = ident_ids_by_module
|
||||
.entry(module_id)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
for ident in exposed_idents {
|
||||
let ident_id = ident_ids.get_or_insert(ident.as_inline_str());
|
||||
let symbol = Symbol::new(module_id, ident_id);
|
||||
|
||||
// Since this value is exposed, add it to our module's default scope.
|
||||
debug_assert!(!scope.contains_key(&ident.clone()));
|
||||
|
||||
scope.insert(ident, (symbol, region));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let ident_ids = ident_ids_by_module
|
||||
.entry(app_module_id)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
for (loc_ident, _) in unpack_exposes_entries(arena, requires) {
|
||||
let ident: Ident = loc_ident.value.into();
|
||||
let ident_id = ident_ids.get_or_insert(ident.as_inline_str());
|
||||
let symbol = Symbol::new(app_module_id, ident_id);
|
||||
|
||||
// Since this value is exposed, add it to our module's default scope.
|
||||
debug_assert!(!scope.contains_key(&ident.clone()));
|
||||
|
||||
scope.insert(ident, (symbol, loc_ident.region));
|
||||
}
|
||||
}
|
||||
|
||||
let ident_ids = ident_ids_by_module.get_mut(&home).unwrap();
|
||||
|
||||
// Generate IdentIds entries for all values this module exposes.
|
||||
// This way, when we encounter them in Defs later, they already
|
||||
// have an IdentIds entry.
|
||||
//
|
||||
// We must *not* add them to scope yet, or else the Defs will
|
||||
// incorrectly think they're shadowing them!
|
||||
for loc_exposed in provides.iter() {
|
||||
// Use get_or_insert here because the ident_ids may already
|
||||
// created an IdentId for this, when it was imported exposed
|
||||
// in a dependent module.
|
||||
//
|
||||
// For example, if module A has [ B.{ foo } ], then
|
||||
// when we get here for B, `foo` will already have
|
||||
// an IdentId. We must reuse that!
|
||||
let ident_id = ident_ids.get_or_insert(&loc_exposed.value.as_str().into());
|
||||
let symbol = Symbol::new(home, ident_id);
|
||||
|
||||
exposed.push(symbol);
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
home.register_debug_idents(&ident_ids);
|
||||
}
|
||||
|
||||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = (&packages).iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value.clone());
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
//
|
||||
// We always need to send these, even if deps is empty,
|
||||
// because the coordinator thread needs to receive this message
|
||||
// to decrement its "pending" count.
|
||||
let module_name = ModuleNameEnum::PkgConfig(shorthand);
|
||||
|
||||
let extra = HeaderFor::PkgConfig;
|
||||
(
|
||||
home,
|
||||
Msg::Header(
|
||||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
src: parse_state.bytes,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
},
|
||||
extra,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2572,6 +2822,37 @@ fn run_solve<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fabricate_pkg_config_module<'a>(
|
||||
arena: &'a Bump,
|
||||
shorthand: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
filename: PathBuf,
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
header: &PlatformHeader<'a>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
||||
header.provides.clone().into_bump_slice();
|
||||
|
||||
Ok(send_header_two(
|
||||
arena,
|
||||
filename,
|
||||
shorthand,
|
||||
app_module_id,
|
||||
&[],
|
||||
provides,
|
||||
header.requires.clone().into_bump_slice(),
|
||||
header.imports.clone().into_bump_slice(),
|
||||
parse_state,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
module_timing,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fabricate_effects_module<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -2875,8 +3156,9 @@ fn canonicalize_and_constrain<'a>(
|
|||
// Generate documentation information
|
||||
// TODO: store timing information?
|
||||
let module_docs = match module_name {
|
||||
AppOrInterfaceName::App(_) => None,
|
||||
AppOrInterfaceName::Interface(name) => Some(crate::docs::generate_module_docs(
|
||||
ModuleNameEnum::PkgConfig(_) => None,
|
||||
ModuleNameEnum::App(_) => None,
|
||||
ModuleNameEnum::Interface(name) => Some(crate::docs::generate_module_docs(
|
||||
name.as_str().into(),
|
||||
&exposed_ident_ids,
|
||||
&parsed_defs,
|
||||
|
@ -3318,19 +3600,6 @@ fn run_task<'a>(
|
|||
mode,
|
||||
)
|
||||
.map(|(_, msg)| msg),
|
||||
LoadPkgConfig {
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
} => load_pkg_config(
|
||||
arena,
|
||||
src_dir,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
),
|
||||
Parse { header } => parse(arena, header),
|
||||
CanonicalizeAndConstrain {
|
||||
parsed,
|
||||
|
|
|
@ -6,7 +6,7 @@ app "effect-example"
|
|||
# TODO `main : Task {}` does not work
|
||||
# it will then think that the `Task` module is unused
|
||||
# (if we also don't use any of the other importd symbols)
|
||||
main : Task.Task {} as Fx
|
||||
main : Task.Task {}
|
||||
main =
|
||||
when if 1 == 1 then True 3 else False 3.14 is
|
||||
True n -> Task.putLine (Str.fromInt n)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
platform folkertdev/foo
|
||||
requires { main : Effect {} }
|
||||
exposes [ Task ]
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
imports [Effect]
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
putChar : I64 -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
mainForHost : Effect {} as Fx
|
||||
mainForHost : Effect.Effect {} as Fx
|
||||
mainForHost = main
|
||||
|
|
|
@ -7,16 +7,16 @@ use std::alloc::Layout;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__main_1_size"]
|
||||
#[link_name = "roc__mainForHost_1_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__main_1_Fx_caller"]
|
||||
#[link_name = "roc__mainForHost_1_Fx_caller"]
|
||||
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__main_1_Fx_size"]
|
||||
#[link_name = "roc__mainForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
app "hello-world" provides [ main ] to "./platform"
|
||||
app "hello-world"
|
||||
packages { base: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
|
|
|
@ -3,5 +3,8 @@ platform examples/hello-world
|
|||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
provides [ mainForHost ]
|
||||
effects Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
1
examples/multi-module/.gitignore
vendored
1
examples/multi-module/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
quicksort
|
|
@ -1,47 +0,0 @@
|
|||
app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform"
|
||||
|
||||
|
||||
quicksort : List I64 -> List I64
|
||||
quicksort = \originalList ->
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp (low - 1) low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair (newI + 1) (Utils.swap (newI + 1) high newList)
|
||||
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (Utils.swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
|
||||
|
||||
|
||||
n = List.len originalList
|
||||
quicksortHelp originalList 0 (n - 1)
|
|
@ -1,12 +0,0 @@
|
|||
interface Utils exposes [ swap ] imports []
|
||||
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
23
examples/multi-module/platform/Cargo.lock
generated
23
examples/multi-module/platform/Cargo.lock
generated
|
@ -1,23 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
|
||||
[workspace]
|
|
@ -1,7 +0,0 @@
|
|||
platform examples/multi-module
|
||||
requires { quicksort : List (Num a) -> List (Num a) }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
|
@ -1,8 +0,0 @@
|
|||
# Rebuilding the host from source
|
||||
|
||||
Run `build.sh` to manually rebuild this platform's host.
|
||||
|
||||
Note that the compiler currently has its own logic for rebuilding these hosts
|
||||
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
||||
hosts will be precompiled by platform authors and distributed in packages,
|
||||
at which point only package authors will need to think about rebuilding hosts.
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compile c_host.o and rust_host.o
|
||||
clang -c host.c -o c_host.o
|
||||
rustc host.rs -o rust_host.o
|
||||
|
||||
# link them together into host.o
|
||||
ld -r c_host.o rust_host.o -o host.o
|
||||
|
||||
# clean up
|
||||
rm -f c_host.o
|
||||
rm -f rust_host.o
|
|
@ -1,7 +0,0 @@
|
|||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
use roc_std::RocCallResult;
|
||||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 100;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let nums: RocList<i64> = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS);
|
||||
|
||||
for index in 0..nums.capacity() {
|
||||
let num = index as i64 % 12;
|
||||
|
||||
nums.push(num);
|
||||
}
|
||||
|
||||
RocList::from_slice(&nums)
|
||||
};
|
||||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut output = MaybeUninit::uninit();
|
||||
|
||||
quicksort(nums, &mut *output.as_mut_ptr());
|
||||
|
||||
match output.assume_init().into() {
|
||||
Ok(value) => value,
|
||||
Err(msg) => panic!("roc failed with message: {}", msg),
|
||||
}
|
||||
};
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc quicksort took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
&answer.as_slice()[0..20]
|
||||
);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
app "quicksort" provides [ quicksort ] to "./platform"
|
||||
app "quicksort"
|
||||
packages { base: "platform" }
|
||||
imports []
|
||||
provides [ quicksort ] to base
|
||||
|
||||
quicksort = \originalList ->
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
platform examples/quicksort
|
||||
requires { quicksort : List (Num a) -> List (Num a) }
|
||||
requires { quicksort : List I64 -> List I64 }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
provides [ mainForHost ]
|
||||
effects Effect {}
|
||||
|
||||
mainForHost : List I64 -> List I64
|
||||
mainForHost = \list -> quicksort list
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
platform examples/shared-quicksort
|
||||
requires { main : Effect {} }
|
||||
requires { quicksort : List I64 -> List I64 }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
putChar : I64 -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
mainForHost : List I64 -> List I64
|
||||
mainForHost = \list -> quicksort list
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_std::RocList;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,6 @@ app "effect-example"
|
|||
imports [ base.Task.{ Task, after } ]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task.Task {} I64 as Fx
|
||||
main : Task.Task {} I64
|
||||
main =
|
||||
Task.succeed {}
|
||||
|
|
|
@ -2,7 +2,7 @@ platform folkertdev/foo
|
|||
requires { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [ File ]
|
||||
imports [ Task ]
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
|
@ -10,5 +10,5 @@ platform folkertdev/foo
|
|||
readAllUtf8 : Str -> Effect { errno : I64, bytes : Str }
|
||||
}
|
||||
|
||||
mainForHost : Effect {} as Fx
|
||||
mainForHost : Task.Task {} I64 as Fx
|
||||
mainForHost = main
|
||||
|
|
|
@ -2,15 +2,15 @@ const std = @import("std");
|
|||
const str = @import("../../../compiler/builtins/bitcode/src/str.zig");
|
||||
const testing = std.testing;
|
||||
|
||||
extern fn roc__main_1_exposed([*]u8) void;
|
||||
extern fn roc__main_1_size() i64;
|
||||
extern fn roc__main_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
||||
extern fn roc__main_1_Fx_size() i64;
|
||||
extern fn roc__mainForHost_1_exposed([*]u8) void;
|
||||
extern fn roc__mainForHost_1_size() i64;
|
||||
extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
||||
extern fn roc__mainForHost_1_Fx_size() i64;
|
||||
|
||||
pub export fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
const size = @intCast(usize, roc__main_1_size());
|
||||
const size = @intCast(usize, roc__mainForHost_1_size());
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub export fn main() u8 {
|
|||
std.heap.c_allocator.free(raw_output);
|
||||
}
|
||||
|
||||
roc__main_1_exposed(output);
|
||||
roc__mainForHost_1_exposed(output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub export fn main() u8 {
|
|||
}
|
||||
|
||||
fn call_the_closure(function_pointer: *const u8, closure_data_pointer: [*]u8) void {
|
||||
const size = roc__main_1_Fx_size();
|
||||
const size = roc__mainForHost_1_Fx_size();
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
|
@ -46,7 +46,7 @@ fn call_the_closure(function_pointer: *const u8, closure_data_pointer: [*]u8) vo
|
|||
std.heap.c_allocator.free(raw_output);
|
||||
}
|
||||
|
||||
roc__main_1_Fx_caller(function_pointer, closure_data_pointer, output);
|
||||
roc__mainForHost_1_Fx_caller(function_pointer, closure_data_pointer, output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue