mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +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
|
.direnv
|
||||||
*.rs.bk
|
*.rs.bk
|
||||||
|
|
||||||
|
# llvm human-readable output
|
||||||
|
*.ll
|
||||||
|
|
||||||
#valgrind
|
#valgrind
|
||||||
vgcore.*
|
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]
|
#[test]
|
||||||
#[serial(multi_module)]
|
#[serial(multi_module)]
|
||||||
// TODO: Stop ignoring this test once we are correctly freeing the RocList even when in dev build.
|
// 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 : Str
|
||||||
main = Dep1.str1
|
main = Dep1.str1
|
||||||
|
|
|
@ -3,5 +3,8 @@ platform examples/multi-module
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports []
|
||||||
provides [ main ]
|
provides [ mainForHost ]
|
||||||
effects Effect {}
|
effects Effect {}
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__main_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
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 : Str
|
||||||
main = Dep1.value1 {}
|
main = Dep1.value1 {}
|
||||||
|
|
|
@ -3,5 +3,8 @@ platform examples/multi-dep-thunk
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports []
|
||||||
provides [ main ]
|
provides [ mainForHost ]
|
||||||
effects Effect {}
|
effects Effect {}
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__main_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -551,7 +551,7 @@ pub enum BuildProblem<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ModuleHeader<'a> {
|
struct ModuleHeader<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
module_name: AppOrInterfaceName<'a>,
|
module_name: ModuleNameEnum<'a>,
|
||||||
module_path: PathBuf,
|
module_path: PathBuf,
|
||||||
exposed_ident_ids: IdentIds,
|
exposed_ident_ids: IdentIds,
|
||||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||||
|
@ -559,11 +559,17 @@ struct ModuleHeader<'a> {
|
||||||
imported_modules: MutSet<ModuleId>,
|
imported_modules: MutSet<ModuleId>,
|
||||||
exposes: Vec<Symbol>,
|
exposes: Vec<Symbol>,
|
||||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||||
to_platform: Option<To<'a>>,
|
|
||||||
src: &'a [u8],
|
src: &'a [u8],
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum HeaderFor<'a> {
|
||||||
|
App { to_platform: To<'a> },
|
||||||
|
PkgConfig,
|
||||||
|
Interface,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ConstrainedModule {
|
struct ConstrainedModule {
|
||||||
module: Module,
|
module: Module,
|
||||||
|
@ -620,7 +626,7 @@ pub struct VariablySizedLayouts<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ParsedModule<'a> {
|
struct ParsedModule<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
module_name: AppOrInterfaceName<'a>,
|
module_name: ModuleNameEnum<'a>,
|
||||||
module_path: PathBuf,
|
module_path: PathBuf,
|
||||||
src: &'a str,
|
src: &'a str,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
@ -634,7 +640,7 @@ struct ParsedModule<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Msg<'a> {
|
enum Msg<'a> {
|
||||||
Many(Vec<Msg<'a>>),
|
Many(Vec<Msg<'a>>),
|
||||||
Header(ModuleHeader<'a>),
|
Header(ModuleHeader<'a>, HeaderFor<'a>),
|
||||||
Parsed(ParsedModule<'a>),
|
Parsed(ParsedModule<'a>),
|
||||||
CanonicalizedAndConstrained {
|
CanonicalizedAndConstrained {
|
||||||
constrained_module: ConstrainedModule,
|
constrained_module: ConstrainedModule,
|
||||||
|
@ -690,6 +696,7 @@ enum Msg<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct State<'a> {
|
struct State<'a> {
|
||||||
pub root_id: ModuleId,
|
pub root_id: ModuleId,
|
||||||
|
pub platform_id: Option<ModuleId>,
|
||||||
pub goal_phase: Phase,
|
pub goal_phase: Phase,
|
||||||
pub stdlib: StdLib,
|
pub stdlib: StdLib,
|
||||||
pub exposed_types: SubsByModule,
|
pub exposed_types: SubsByModule,
|
||||||
|
@ -844,12 +851,6 @@ enum BuildTask<'a> {
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
},
|
},
|
||||||
LoadPkgConfig {
|
|
||||||
shorthand: &'a str,
|
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
|
||||||
mode: Mode,
|
|
||||||
},
|
|
||||||
Parse {
|
Parse {
|
||||||
header: ModuleHeader<'a>,
|
header: ModuleHeader<'a>,
|
||||||
},
|
},
|
||||||
|
@ -1272,6 +1273,7 @@ where
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
root_id,
|
root_id,
|
||||||
|
platform_id: None,
|
||||||
goal_phase,
|
goal_phase,
|
||||||
stdlib,
|
stdlib,
|
||||||
output_path: None,
|
output_path: None,
|
||||||
|
@ -1414,7 +1416,7 @@ fn update<'a>(
|
||||||
|
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
Header(header) => {
|
Header(header, header_extra) => {
|
||||||
log!("loaded header for {:?}", header.module_id);
|
log!("loaded header for {:?}", header.module_id);
|
||||||
let home = 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
|
// 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() {
|
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"
|
// e.g. for `app "blah"` we should generate an output file named "blah"
|
||||||
match &parsed.module_name {
|
match &parsed.module_name {
|
||||||
AppOrInterfaceName::App(output_str) => match output_str {
|
ModuleNameEnum::PkgConfig(_) => {}
|
||||||
|
ModuleNameEnum::App(output_str) => match output_str {
|
||||||
StrLiteral::PlainLine(path) => {
|
StrLiteral::PlainLine(path) => {
|
||||||
state.output_path = Some(path);
|
state.output_path = Some(path);
|
||||||
}
|
}
|
||||||
|
@ -1486,7 +1502,7 @@ fn update<'a>(
|
||||||
todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
|
todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
AppOrInterfaceName::Interface(_) => {}
|
ModuleNameEnum::Interface(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_id = parsed.module_id;
|
let module_id = parsed.module_id;
|
||||||
|
@ -1602,7 +1618,14 @@ fn update<'a>(
|
||||||
|
|
||||||
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
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
|
state
|
||||||
.exposed_to_host
|
.exposed_to_host
|
||||||
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
|
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
|
||||||
|
@ -1637,15 +1660,10 @@ fn update<'a>(
|
||||||
// the originally requested module, we're all done!
|
// the originally requested module, we're all done!
|
||||||
return Ok(state);
|
return Ok(state);
|
||||||
} else {
|
} else {
|
||||||
if module_id != state.root_id {
|
state.exposed_types.insert(
|
||||||
state.exposed_types.insert(
|
module_id,
|
||||||
module_id,
|
ExposedModuleTypes::Valid(solved_module.solved_types, solved_module.aliases),
|
||||||
ExposedModuleTypes::Valid(
|
);
|
||||||
solved_module.solved_types,
|
|
||||||
solved_module.aliases,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.goal_phase > Phase::SolveTypes {
|
if state.goal_phase > Phase::SolveTypes {
|
||||||
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
||||||
|
@ -1913,6 +1931,7 @@ fn load_pkg_config<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
src_dir: &Path,
|
src_dir: &Path,
|
||||||
shorthand: &'a str,
|
shorthand: &'a str,
|
||||||
|
app_module_id: ModuleId,
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
@ -1933,10 +1952,14 @@ fn load_pkg_config<'a>(
|
||||||
let parse_header_duration = parse_start.elapsed().unwrap();
|
let parse_header_duration = parse_start.elapsed().unwrap();
|
||||||
|
|
||||||
// Insert the first entries for this module's timings
|
// 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;
|
pkg_module_timing.read_roc_file = file_io_duration;
|
||||||
module_timing.parse_header = parse_header_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 {
|
match parsed {
|
||||||
Ok((ast::Module::Interface { header }, _parse_state)) => {
|
Ok((ast::Module::Interface { header }, _parse_state)) => {
|
||||||
|
@ -1951,16 +1974,34 @@ fn load_pkg_config<'a>(
|
||||||
header
|
header
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
Ok((ast::Module::Platform { header }, parser_state)) => {
|
||||||
arena,
|
// make a Pkg-Config module that ultimately exposes `main` to the host
|
||||||
shorthand,
|
let pkg_config_module_msg = fabricate_pkg_config_module(
|
||||||
module_ids,
|
arena,
|
||||||
ident_ids_by_module,
|
shorthand,
|
||||||
mode,
|
app_module_id,
|
||||||
header,
|
filename,
|
||||||
module_timing,
|
parser_state,
|
||||||
)
|
module_ids.clone(),
|
||||||
.map(|x| x.1),
|
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 }),
|
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(
|
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||||
Located {
|
Located {
|
||||||
region: header.name.region,
|
region: header.name.region,
|
||||||
value: AppOrInterfaceName::Interface(header.name.value),
|
value: ModuleNameEnum::Interface(header.name.value),
|
||||||
},
|
},
|
||||||
filename,
|
filename,
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
|
@ -2108,7 +2149,7 @@ fn parse_header<'a>(
|
||||||
let (module_id, app_module_header_msg) = send_header(
|
let (module_id, app_module_header_msg) = send_header(
|
||||||
Located {
|
Located {
|
||||||
region: header.name.region,
|
region: header.name.region,
|
||||||
value: AppOrInterfaceName::App(header.name.value),
|
value: ModuleNameEnum::App(header.name.value),
|
||||||
},
|
},
|
||||||
filename,
|
filename,
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
|
@ -2160,6 +2201,7 @@ fn parse_header<'a>(
|
||||||
arena,
|
arena,
|
||||||
&pkg_config_roc,
|
&pkg_config_roc,
|
||||||
shorthand,
|
shorthand,
|
||||||
|
module_id,
|
||||||
module_ids,
|
module_ids,
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
mode,
|
mode,
|
||||||
|
@ -2272,15 +2314,16 @@ fn load_from_str<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum AppOrInterfaceName<'a> {
|
enum ModuleNameEnum<'a> {
|
||||||
/// A filename
|
/// A filename
|
||||||
App(StrLiteral<'a>),
|
App(StrLiteral<'a>),
|
||||||
Interface(roc_parse::header::ModuleName<'a>),
|
Interface(roc_parse::header::ModuleName<'a>),
|
||||||
|
PkgConfig(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn send_header<'a>(
|
fn send_header<'a>(
|
||||||
loc_name: Located<AppOrInterfaceName<'a>>,
|
loc_name: Located<ModuleNameEnum<'a>>,
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
opt_shorthand: Option<&'a str>,
|
opt_shorthand: Option<&'a str>,
|
||||||
packages: &'a [Located<PackageEntry<'a>>],
|
packages: &'a [Located<PackageEntry<'a>>],
|
||||||
|
@ -2292,9 +2335,10 @@ fn send_header<'a>(
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
) -> (ModuleId, Msg<'a>) {
|
) -> (ModuleId, Msg<'a>) {
|
||||||
use AppOrInterfaceName::*;
|
use ModuleNameEnum::*;
|
||||||
|
|
||||||
let declared_name: ModuleName = match &loc_name.value {
|
let declared_name: ModuleName = match &loc_name.value {
|
||||||
|
PkgConfig(_) => unreachable!(),
|
||||||
App(_) => ModuleName::APP.into(),
|
App(_) => ModuleName::APP.into(),
|
||||||
Interface(module_name) => {
|
Interface(module_name) => {
|
||||||
// TODO check to see if module_name is consistent with filename.
|
// 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,
|
// We always need to send these, even if deps is empty,
|
||||||
// because the coordinator thread needs to receive this message
|
// because the coordinator thread needs to receive this message
|
||||||
// to decrement its "pending" count.
|
// to decrement its "pending" count.
|
||||||
|
let extra = match to_platform {
|
||||||
|
Some(to_platform) => HeaderFor::App { to_platform },
|
||||||
|
None => HeaderFor::Interface,
|
||||||
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
home,
|
home,
|
||||||
Msg::Header(ModuleHeader {
|
Msg::Header(
|
||||||
module_id: home,
|
ModuleHeader {
|
||||||
module_path: filename,
|
module_id: home,
|
||||||
exposed_ident_ids: ident_ids,
|
module_path: filename,
|
||||||
module_name: loc_name.value,
|
exposed_ident_ids: ident_ids,
|
||||||
packages: package_entries,
|
module_name: loc_name.value,
|
||||||
imported_modules,
|
packages: package_entries,
|
||||||
deps_by_name,
|
imported_modules,
|
||||||
exposes: exposed,
|
deps_by_name,
|
||||||
to_platform,
|
exposes: exposed,
|
||||||
src: parse_state.bytes,
|
src: parse_state.bytes,
|
||||||
exposed_imports: scope,
|
exposed_imports: scope,
|
||||||
module_timing,
|
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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn fabricate_effects_module<'a>(
|
fn fabricate_effects_module<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -2875,8 +3156,9 @@ fn canonicalize_and_constrain<'a>(
|
||||||
// Generate documentation information
|
// Generate documentation information
|
||||||
// TODO: store timing information?
|
// TODO: store timing information?
|
||||||
let module_docs = match module_name {
|
let module_docs = match module_name {
|
||||||
AppOrInterfaceName::App(_) => None,
|
ModuleNameEnum::PkgConfig(_) => None,
|
||||||
AppOrInterfaceName::Interface(name) => Some(crate::docs::generate_module_docs(
|
ModuleNameEnum::App(_) => None,
|
||||||
|
ModuleNameEnum::Interface(name) => Some(crate::docs::generate_module_docs(
|
||||||
name.as_str().into(),
|
name.as_str().into(),
|
||||||
&exposed_ident_ids,
|
&exposed_ident_ids,
|
||||||
&parsed_defs,
|
&parsed_defs,
|
||||||
|
@ -3318,19 +3600,6 @@ fn run_task<'a>(
|
||||||
mode,
|
mode,
|
||||||
)
|
)
|
||||||
.map(|(_, msg)| msg),
|
.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),
|
Parse { header } => parse(arena, header),
|
||||||
CanonicalizeAndConstrain {
|
CanonicalizeAndConstrain {
|
||||||
parsed,
|
parsed,
|
||||||
|
|
|
@ -6,7 +6,7 @@ app "effect-example"
|
||||||
# TODO `main : Task {}` does not work
|
# TODO `main : Task {}` does not work
|
||||||
# it will then think that the `Task` module is unused
|
# it will then think that the `Task` module is unused
|
||||||
# (if we also don't use any of the other importd symbols)
|
# (if we also don't use any of the other importd symbols)
|
||||||
main : Task.Task {} as Fx
|
main : Task.Task {}
|
||||||
main =
|
main =
|
||||||
when if 1 == 1 then True 3 else False 3.14 is
|
when if 1 == 1 then True 3 else False 3.14 is
|
||||||
True n -> Task.putLine (Str.fromInt n)
|
True n -> Task.putLine (Str.fromInt n)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
platform folkertdev/foo
|
platform folkertdev/foo
|
||||||
requires { main : Effect {} }
|
requires { main : Effect {} }
|
||||||
exposes [ Task ]
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports [Effect]
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects Effect
|
effects Effect
|
||||||
{
|
{
|
||||||
putChar : Int -> Effect {},
|
putChar : I64 -> Effect {},
|
||||||
putLine : Str -> Effect {},
|
putLine : Str -> Effect {},
|
||||||
getLine : Effect Str
|
getLine : Effect Str
|
||||||
}
|
}
|
||||||
|
|
||||||
mainForHost : Effect {} as Fx
|
mainForHost : Effect.Effect {} as Fx
|
||||||
mainForHost = main
|
mainForHost = main
|
||||||
|
|
|
@ -7,16 +7,16 @@ use std::alloc::Layout;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__main_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn roc_main(output: *mut u8) -> ();
|
fn roc_main(output: *mut u8) -> ();
|
||||||
|
|
||||||
#[link_name = "roc__main_1_size"]
|
#[link_name = "roc__mainForHost_1_size"]
|
||||||
fn roc_main_size() -> i64;
|
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) -> ();
|
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;
|
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 =
|
greeting =
|
||||||
hi = "Hello"
|
hi = "Hello"
|
||||||
|
|
|
@ -3,5 +3,8 @@ platform examples/hello-world
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports []
|
||||||
provides [ main ]
|
provides [ mainForHost ]
|
||||||
effects Effect {}
|
effects Effect {}
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__main_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
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 ->
|
quicksort = \originalList ->
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
platform examples/quicksort
|
platform examples/quicksort
|
||||||
requires { quicksort : List (Num a) -> List (Num a) }
|
requires { quicksort : List I64 -> List I64 }
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports []
|
||||||
provides [ main ]
|
provides [ mainForHost ]
|
||||||
effects Effect {}
|
effects Effect {}
|
||||||
|
|
||||||
|
mainForHost : List I64 -> List I64
|
||||||
|
mainForHost = \list -> quicksort list
|
||||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__quicksort_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
platform examples/shared-quicksort
|
platform examples/shared-quicksort
|
||||||
requires { main : Effect {} }
|
requires { quicksort : List I64 -> List I64 }
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports []
|
imports []
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects Effect
|
effects Effect
|
||||||
{
|
{
|
||||||
putChar : Int -> Effect {},
|
putChar : I64 -> Effect {},
|
||||||
putLine : Str -> Effect {},
|
putLine : Str -> Effect {},
|
||||||
getLine : Effect Str
|
getLine : Effect Str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainForHost : List I64 -> List I64
|
||||||
|
mainForHost = \list -> quicksort list
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use roc_std::RocList;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__quicksort_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,6 @@ app "effect-example"
|
||||||
imports [ base.Task.{ Task, after } ]
|
imports [ base.Task.{ Task, after } ]
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
main : Task.Task {} I64 as Fx
|
main : Task.Task {} I64
|
||||||
main =
|
main =
|
||||||
Task.succeed {}
|
Task.succeed {}
|
||||||
|
|
|
@ -2,7 +2,7 @@ platform folkertdev/foo
|
||||||
requires { main : Effect {} }
|
requires { main : Effect {} }
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports [ File ]
|
imports [ Task ]
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects Effect
|
effects Effect
|
||||||
{
|
{
|
||||||
|
@ -10,5 +10,5 @@ platform folkertdev/foo
|
||||||
readAllUtf8 : Str -> Effect { errno : I64, bytes : Str }
|
readAllUtf8 : Str -> Effect { errno : I64, bytes : Str }
|
||||||
}
|
}
|
||||||
|
|
||||||
mainForHost : Effect {} as Fx
|
mainForHost : Task.Task {} I64 as Fx
|
||||||
mainForHost = main
|
mainForHost = main
|
||||||
|
|
|
@ -2,15 +2,15 @@ const std = @import("std");
|
||||||
const str = @import("../../../compiler/builtins/bitcode/src/str.zig");
|
const str = @import("../../../compiler/builtins/bitcode/src/str.zig");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
extern fn roc__main_1_exposed([*]u8) void;
|
extern fn roc__mainForHost_1_exposed([*]u8) void;
|
||||||
extern fn roc__main_1_size() i64;
|
extern fn roc__mainForHost_1_size() i64;
|
||||||
extern fn roc__main_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
||||||
extern fn roc__main_1_Fx_size() i64;
|
extern fn roc__mainForHost_1_Fx_size() i64;
|
||||||
|
|
||||||
pub export fn main() u8 {
|
pub export fn main() u8 {
|
||||||
const stdout = std.io.getStdOut().writer();
|
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;
|
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||||
var output = @ptrCast([*]u8, raw_output);
|
var output = @ptrCast([*]u8, raw_output);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub export fn main() u8 {
|
||||||
std.heap.c_allocator.free(raw_output);
|
std.heap.c_allocator.free(raw_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
roc__main_1_exposed(output);
|
roc__mainForHost_1_exposed(output);
|
||||||
|
|
||||||
const elements = @ptrCast([*]u64, @alignCast(8, 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 {
|
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;
|
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable;
|
||||||
var output = @ptrCast([*]u8, raw_output);
|
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);
|
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));
|
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue