make Str + Result work

This commit is contained in:
Folkert 2022-02-26 17:52:24 +01:00
parent d1d7cfef44
commit c0d3543d5a
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
12 changed files with 401 additions and 142 deletions

View file

@ -12,7 +12,7 @@ use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable}; use roc_types::subs::{VarStore, Variable};
use roc_types::types::{AliasKind, Type}; use roc_types::types::{AliasKind, Type};
#[derive(Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub(crate) struct HostedGeneratedFunctions { pub(crate) struct HostedGeneratedFunctions {
pub(crate) after: bool, pub(crate) after: bool,
pub(crate) map: bool, pub(crate) map: bool,

View file

@ -65,6 +65,96 @@ fn validate_generate_with<'a>(
(functions, unknown) (functions, unknown)
} }
#[derive(Debug)]
enum GeneratedInfo<'a> {
Hosted {
effect_symbol: Symbol,
generated_functions: HostedGeneratedFunctions,
},
Builtin {
generated_functions: &'a [Symbol],
},
NotSpecial,
}
impl<'a> GeneratedInfo<'a> {
fn from_header_for(
env: &mut Env,
scope: &mut Scope,
var_store: &mut VarStore,
header_for: &HeaderFor<'a>,
) -> Self {
match header_for {
HeaderFor::Hosted {
generates,
generates_with,
} => {
let name: &str = generates.into();
let (generated_functions, unknown_generated) =
validate_generate_with(generates_with);
for unknown in unknown_generated {
env.problem(Problem::UnknownGeneratesWith(unknown));
}
let effect_symbol = scope
.introduce(
name.into(),
&env.exposed_ident_ids,
&mut env.ident_ids,
Region::zero(),
)
.unwrap();
let effect_tag_name = TagName::Private(effect_symbol);
{
let a_var = var_store.fresh();
let actual = crate::effect_module::build_effect_actual(
effect_tag_name,
Type::Variable(a_var),
var_store,
);
scope.add_alias(
effect_symbol,
Region::zero(),
vec![Loc::at_zero(("a".into(), a_var))],
actual,
AliasKind::Structural,
);
}
GeneratedInfo::Hosted {
effect_symbol,
generated_functions,
}
}
HeaderFor::Builtin { generates_with } => GeneratedInfo::Builtin {
generated_functions: generates_with,
},
_ => GeneratedInfo::NotSpecial,
}
}
}
fn has_no_implementation(expr: &Expr) -> bool {
match expr {
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
Expr::Closure(closure_data)
if matches!(
closure_data.loc_body.value,
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. })
) =>
{
true
}
_ => false,
}
}
// TODO trim these down // TODO trim these down
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a>( pub fn canonicalize_module_defs<'a>(
@ -95,59 +185,8 @@ pub fn canonicalize_module_defs<'a>(
); );
} }
struct Hosted { let generated_info =
effect_symbol: Symbol, GeneratedInfo::from_header_for(&mut env, &mut scope, var_store, header_for);
generated_functions: HostedGeneratedFunctions,
}
let hosted_info = if let HeaderFor::Hosted {
generates,
generates_with,
} = header_for
{
let name: &str = generates.into();
let (generated_functions, unknown_generated) = validate_generate_with(generates_with);
for unknown in unknown_generated {
env.problem(Problem::UnknownGeneratesWith(unknown));
}
let effect_symbol = scope
.introduce(
name.into(),
&env.exposed_ident_ids,
&mut env.ident_ids,
Region::zero(),
)
.unwrap();
let effect_tag_name = TagName::Private(effect_symbol);
{
let a_var = var_store.fresh();
let actual = crate::effect_module::build_effect_actual(
effect_tag_name,
Type::Variable(a_var),
var_store,
);
scope.add_alias(
effect_symbol,
Region::zero(),
vec![Loc::at_zero(("a".into(), a_var))],
actual,
AliasKind::Structural,
);
}
Some(Hosted {
effect_symbol,
generated_functions,
})
} else {
None
};
// Desugar operators (convert them to Apply calls, taking into account // Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization. // operator precedence and associativity rules), before doing other canonicalization.
@ -206,11 +245,13 @@ pub fn canonicalize_module_defs<'a>(
} else { } else {
// This is a type alias // This is a type alias
dbg!(scope.aliases.keys().collect::<Vec<_>>());
// the symbol should already be added to the scope when this module is canonicalized // the symbol should already be added to the scope when this module is canonicalized
debug_assert!( debug_assert!(
scope.contains_alias(symbol), scope.contains_alias(symbol),
"apparently, {:?} is not actually a type alias", "The {:?} is not a type alias known in {:?}",
symbol symbol,
home
); );
// but now we know this symbol by a different identifier, so we still need to add it to // but now we know this symbol by a different identifier, so we still need to add it to
@ -291,10 +332,10 @@ pub fn canonicalize_module_defs<'a>(
(Ok(mut declarations), output) => { (Ok(mut declarations), output) => {
use crate::def::Declaration::*; use crate::def::Declaration::*;
if let Some(Hosted { if let GeneratedInfo::Hosted {
effect_symbol, effect_symbol,
generated_functions, generated_functions,
}) = hosted_info } = generated_info
{ {
let mut exposed_symbols = MutSet::default(); let mut exposed_symbols = MutSet::default();
@ -327,9 +368,23 @@ pub fn canonicalize_module_defs<'a>(
// Temporary hack: we don't know exactly what symbols are hosted symbols, // Temporary hack: we don't know exactly what symbols are hosted symbols,
// and which are meant to be normal definitions without a body. So for now // and which are meant to be normal definitions without a body. So for now
// we just assume they are hosted functions (meant to be provided by the platform) // we just assume they are hosted functions (meant to be provided by the platform)
if let Some(Hosted { effect_symbol, .. }) = hosted_info { if has_no_implementation(&def.loc_expr.value) {
macro_rules! make_hosted_def { match generated_info {
() => { GeneratedInfo::Builtin {
generated_functions,
} => {
let symbol = def.pattern_vars.iter().next().unwrap().0;
match crate::builtins::builtin_defs_map(*symbol, var_store) {
None => {
panic!("A builtin module contains a signature without implementation")
}
Some(mut replacement_def) => {
replacement_def.annotation = def.annotation.take();
*def = replacement_def;
}
}
}
GeneratedInfo::Hosted { effect_symbol, .. } => {
let symbol = def.pattern_vars.iter().next().unwrap().0; let symbol = def.pattern_vars.iter().next().unwrap().0;
let ident_id = symbol.ident_id(); let ident_id = symbol.ident_id();
let ident = let ident =
@ -353,27 +408,8 @@ pub fn canonicalize_module_defs<'a>(
); );
*def = hosted_def; *def = hosted_def;
};
}
match &def.loc_expr.value {
Expr::RuntimeError(RuntimeError::NoImplementationNamed {
..
}) => {
make_hosted_def!();
} }
Expr::Closure(closure_data) _ => (),
if matches!(
closure_data.loc_body.value,
Expr::RuntimeError(
RuntimeError::NoImplementationNamed { .. }
)
) =>
{
make_hosted_def!();
}
_ => {}
} }
} }
} }
@ -408,7 +444,7 @@ pub fn canonicalize_module_defs<'a>(
let mut aliases = MutMap::default(); let mut aliases = MutMap::default();
if let Some(Hosted { effect_symbol, .. }) = hosted_info { if let GeneratedInfo::Hosted { effect_symbol, .. } = generated_info {
// Remove this from exposed_symbols, // Remove this from exposed_symbols,
// so that at the end of the process, // so that at the end of the process,
// we can see if there were any // we can see if there were any

View file

@ -17,7 +17,7 @@ pub struct Scope {
symbols: SendMap<Symbol, Region>, symbols: SendMap<Symbol, Region>,
/// The type aliases currently in scope /// The type aliases currently in scope
aliases: SendMap<Symbol, Alias>, pub aliases: SendMap<Symbol, Alias>,
/// The current module being processed. This will be used to turn /// The current module being processed. This will be used to turn
/// unqualified idents into Symbols. /// unqualified idents into Symbols.
@ -88,13 +88,17 @@ impl Scope {
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> { pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
match self.idents.get(ident) { match self.idents.get(ident) {
Some((symbol, _)) => Ok(*symbol), Some((symbol, _)) => Ok(*symbol),
None => Err(RuntimeError::LookupNotInScope( None => {
Loc { let error = RuntimeError::LookupNotInScope(
region, Loc {
value: ident.clone(), region,
}, value: ident.clone(),
self.idents.keys().map(|v| v.as_ref().into()).collect(), },
)), self.idents.keys().map(|v| v.as_ref().into()).collect(),
);
Err(error)
}
} }
} }

View file

@ -144,7 +144,7 @@ pub fn pre_constrain_imports(
// We used this module, so clearly it is not unused! // We used this module, so clearly it is not unused!
unused_imports.remove(&module_id); unused_imports.remove(&module_id);
if module_id.is_builtin() { if module_id.is_builtin() && module_id != ModuleId::STR {
// For builtin modules, we create imports from the // For builtin modules, we create imports from the
// hardcoded builtin map. // hardcoded builtin map.
match stdlib.types.get(&symbol) { match stdlib.types.get(&symbol) {

View file

@ -23,7 +23,7 @@ use roc_mono::ir::{
UpdateModeIds, UpdateModeIds,
}; };
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem}; use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral}; use roc_parse::ast::{self, Collection, ExtractSpaces, Spaced, StrLiteral};
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName}; use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
@ -65,7 +65,7 @@ const PKG_CONFIG_FILE_NAME: &str = "Package-Config";
/// The . in between module names like Foo.Bar.Baz /// The . in between module names like Foo.Bar.Baz
const MODULE_SEPARATOR: char = '.'; const MODULE_SEPARATOR: char = '.';
const SHOW_MESSAGE_LOG: bool = false; const SHOW_MESSAGE_LOG: bool = true;
const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024; const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
@ -77,7 +77,7 @@ macro_rules! log {
const BUILTIN_MODULES: &[(ModuleId, &[ModuleId])] = &[(ModuleId::RESULT, &[])]; const BUILTIN_MODULES: &[(ModuleId, &[ModuleId])] = &[(ModuleId::RESULT, &[])];
/// Struct storing various intermediate stages by their ModuleId /// Struct storing various intermediate stages by their ModuleId
#[derive(Debug, Default)] #[derive(Debug)]
struct ModuleCache<'a> { struct ModuleCache<'a> {
module_names: MutMap<ModuleId, PQModuleName<'a>>, module_names: MutMap<ModuleId, PQModuleName<'a>>,
@ -101,6 +101,40 @@ struct ModuleCache<'a> {
sources: MutMap<ModuleId, (PathBuf, &'a str)>, sources: MutMap<ModuleId, (PathBuf, &'a str)>,
} }
impl Default for ModuleCache<'_> {
fn default() -> Self {
let mut module_names = MutMap::default();
module_names.insert(
ModuleId::RESULT,
PQModuleName::Unqualified(ModuleName::from(ModuleName::RESULT)),
);
module_names.insert(
ModuleId::STR,
PQModuleName::Unqualified(ModuleName::from(ModuleName::STR)),
);
Self {
module_names,
headers: Default::default(),
parsed: Default::default(),
aliases: Default::default(),
constrained: Default::default(),
typechecked: Default::default(),
found_specializations: Default::default(),
external_specializations_requested: Default::default(),
imports: Default::default(),
top_level_thunks: Default::default(),
documentation: Default::default(),
can_problems: Default::default(),
type_problems: Default::default(),
mono_problems: Default::default(),
sources: Default::default(),
}
}
}
fn start_phase<'a>( fn start_phase<'a>(
module_id: ModuleId, module_id: ModuleId,
phase: Phase, phase: Phase,
@ -130,20 +164,24 @@ fn start_phase<'a>(
let task = { let task = {
match phase { match phase {
Phase::LoadHeader => { Phase::LoadHeader => {
let dep_name = state let opt_dep_name = state.module_cache.module_names.get(&module_id);
.module_cache
.module_names
.get(&module_id)
.expect("module id is present")
.clone();
BuildTask::LoadModule { match opt_dep_name {
module_name: dep_name, None => {
// Provide mutexes of ModuleIds and IdentIds by module, panic!("Module {:?} is not in module_cache.module_names", module_id)
// so other modules can populate them as they load. }
module_ids: Arc::clone(&state.arc_modules), Some(dep_name) => {
shorthands: Arc::clone(&state.arc_shorthands), let module_name = dep_name.clone();
ident_ids_by_module: Arc::clone(&state.ident_ids_by_module),
BuildTask::LoadModule {
module_name,
// Provide mutexes of ModuleIds and IdentIds by module,
// so other modules can populate them as they load.
module_ids: Arc::clone(&state.arc_modules),
shorthands: Arc::clone(&state.arc_shorthands),
ident_ids_by_module: Arc::clone(&state.ident_ids_by_module),
}
}
} }
} }
Phase::Parse => { Phase::Parse => {
@ -215,6 +253,8 @@ fn start_phase<'a>(
} }
} }
dbg!(module_id, &parsed.imported_modules);
BuildTask::CanonicalizeAndConstrain { BuildTask::CanonicalizeAndConstrain {
parsed, parsed,
dep_idents, dep_idents,
@ -1037,7 +1077,7 @@ fn load<'a>(
) -> Result<LoadResult<'a>, LoadingProblem<'a>> { ) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
// When compiling to wasm, we cannot spawn extra threads // When compiling to wasm, we cannot spawn extra threads
// so we have a single-threaded implementation // so we have a single-threaded implementation
if cfg!(target_family = "wasm") { if true || cfg!(target_family = "wasm") {
load_single_threaded( load_single_threaded(
arena, arena,
load_start, load_start,
@ -1613,7 +1653,7 @@ fn update<'a>(
state.platform_path = PlatformPath::RootIsPkgConfig; state.platform_path = PlatformPath::RootIsPkgConfig;
} }
} }
Interface => { Builtin { .. } | Interface => {
if header.is_root_module { if header.is_root_module {
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
state.platform_path = PlatformPath::RootIsInterface; state.platform_path = PlatformPath::RootIsInterface;
@ -1649,6 +1689,37 @@ fn update<'a>(
.exposed_symbols_by_module .exposed_symbols_by_module
.insert(home, exposed_symbols); .insert(home, exposed_symbols);
// add the prelude
let mut header = header;
// let mut imports = header.package_qualified_imported_modules.clone();
if header.module_id != ModuleId::RESULT {
header
.package_qualified_imported_modules
.insert(PackageQualified::Unqualified(ModuleId::RESULT));
header
.imported_modules
.insert(ModuleId::RESULT, Region::zero());
header.exposed_imports.insert(
Ident::from("Result"),
(Symbol::RESULT_RESULT, Region::zero()),
);
}
if !header.module_id.is_builtin() {
header
.package_qualified_imported_modules
.insert(PackageQualified::Unqualified(ModuleId::STR));
header
.imported_modules
.insert(ModuleId::STR, Region::zero());
}
dbg!(header.module_id, &header.exposed_imports);
state state
.module_cache .module_cache
.imports .imports
@ -1770,7 +1841,9 @@ fn update<'a>(
}; };
for (unused, region) in unused_imports.drain() { for (unused, region) in unused_imports.drain() {
existing.push(roc_problem::can::Problem::UnusedImport(unused, region)); if !unused.is_builtin() {
existing.push(roc_problem::can::Problem::UnusedImport(unused, region));
}
} }
let work = state.dependencies.notify(module_id, Phase::SolveTypes); let work = state.dependencies.notify(module_id, Phase::SolveTypes);
@ -2209,6 +2282,12 @@ fn load_pkg_config<'a>(
header header
))) )))
} }
Ok((ast::Module::Hosted { header }, _parse_state)) => {
Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got Hosted module with header\n{:?}",
header
)))
}
Ok((ast::Module::App { header }, _parse_state)) => { Ok((ast::Module::App { header }, _parse_state)) => {
Err(LoadingProblem::UnexpectedHeader(format!( Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got App with header\n{:?}", "expected platform/package module, got App with header\n{:?}",
@ -2232,12 +2311,6 @@ fn load_pkg_config<'a>(
Ok(pkg_config_module_msg) Ok(pkg_config_module_msg)
} }
Ok((ast::Module::Hosted { header }, _parse_state)) => {
Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got Hosted module with header\n{:?}",
header
)))
}
Err(fail) => Err(LoadingProblem::ParsingFailed( Err(fail) => Err(LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header) fail.map_problem(SyntaxError::Header)
.into_file_error(filename), .into_file_error(filename),
@ -2261,12 +2334,8 @@ fn load_module<'a>(
arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageName<'a>>>>, arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageName<'a>>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> { ) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
use PackageQualified::*;
let module_start_time = SystemTime::now(); let module_start_time = SystemTime::now();
dbg!(&module_name);
match module_name.as_inner().as_str() { match module_name.as_inner().as_str() {
"Result" => { "Result" => {
let parse_start = SystemTime::now(); let parse_start = SystemTime::now();
@ -2302,7 +2371,9 @@ fn load_module<'a>(
packages: &[], packages: &[],
exposes: &EXPOSES, exposes: &EXPOSES,
imports, imports,
extra: HeaderFor::Interface, extra: HeaderFor::Builtin {
generates_with: &[],
},
}; };
let src_bytes = r#" let src_bytes = r#"
@ -2355,6 +2426,136 @@ fn load_module<'a>(
module_timing, module_timing,
)); ));
} }
"Str" => {
let parse_start = SystemTime::now();
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);
module_timing.read_roc_file = Default::default();
module_timing.parse_header = parse_header_duration;
let filename = PathBuf::from("Str.roc");
const EXPOSES: &[Loc<ExposedName>] = &[
Loc::at_zero(ExposedName::new("Utf8Problem")),
Loc::at_zero(ExposedName::new("Utf8ByteProblem")),
Loc::at_zero(ExposedName::new("isEmpty")),
Loc::at_zero(ExposedName::new("concat")),
Loc::at_zero(ExposedName::new("joinWith")),
Loc::at_zero(ExposedName::new("split")),
Loc::at_zero(ExposedName::new("repeat")),
Loc::at_zero(ExposedName::new("countGraphemes")),
Loc::at_zero(ExposedName::new("startsWithCodePt")),
Loc::at_zero(ExposedName::new("toUtf8")),
Loc::at_zero(ExposedName::new("fromUtf8")),
Loc::at_zero(ExposedName::new("fromUtf8Range")),
Loc::at_zero(ExposedName::new("startsWith")),
Loc::at_zero(ExposedName::new("endsWith")),
Loc::at_zero(ExposedName::new("trim")),
Loc::at_zero(ExposedName::new("trimLeft")),
Loc::at_zero(ExposedName::new("trimRight")),
Loc::at_zero(ExposedName::new("toDec")),
Loc::at_zero(ExposedName::new("toF64")),
Loc::at_zero(ExposedName::new("toF32")),
Loc::at_zero(ExposedName::new("toNat")),
Loc::at_zero(ExposedName::new("toU128")),
Loc::at_zero(ExposedName::new("toI128")),
Loc::at_zero(ExposedName::new("toU64")),
Loc::at_zero(ExposedName::new("toI64")),
Loc::at_zero(ExposedName::new("toU32")),
Loc::at_zero(ExposedName::new("toI32")),
Loc::at_zero(ExposedName::new("toU16")),
Loc::at_zero(ExposedName::new("toI16")),
Loc::at_zero(ExposedName::new("toU8")),
Loc::at_zero(ExposedName::new("toI8")),
];
const IMPORTS: &[Loc<ImportsEntry>] = &[Loc::at_zero(ImportsEntry::Module(
roc_parse::header::ModuleName::new("Result"),
Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("Result")))]),
))];
const GENERATES_WITH: &[Symbol] = &[Symbol::STR_IS_EMPTY, Symbol::STR_CONCAT];
let info = HeaderInfo {
loc_name: Loc {
region: Region::zero(),
value: ModuleNameEnum::Interface(roc_parse::header::ModuleName::new("Str")),
},
filename,
is_root_module: false,
opt_shorthand: None,
packages: &[],
exposes: &EXPOSES,
imports: &IMPORTS,
extra: HeaderFor::Builtin {
generates_with: GENERATES_WITH,
},
};
let src_bytes = r#"
Utf8ByteProblem :
[
InvalidStartByte,
UnexpectedEndOfSequence,
ExpectedContinuation,
OverlongEncoding,
CodepointTooLarge,
EncodesSurrogateHalf,
]
Utf8Problem : { byteIndex : Nat, problem : Utf8ByteProblem }
isEmpty : Str -> Bool
concat : Str, Str -> Str
joinWith : List Str, Str -> Str
split : Str, Str -> List Str
repeat : Str, Nat -> Str
countGraphemes : Str -> Nat
startsWithCodePt : Str, U32 -> Bool
toUtf8 : Str -> List U8
# fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
# fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8Problem, OutOfBounds ]*
fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8ByteProblem Nat ]*
fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8ByteProblem Nat, OutOfBounds ]*
startsWith : Str, Str -> Bool
endsWith : Str, Str -> Bool
trim : Str -> Str
trimLeft : Str -> Str
trimRight : Str -> Str
toDec : Str -> Result Dec [ InvalidNumStr ]*
toF64 : Str -> Result F64 [ InvalidNumStr ]*
toF32 : Str -> Result F32 [ InvalidNumStr ]*
toNat : Str -> Result Nat [ InvalidNumStr ]*
toU128 : Str -> Result U128 [ InvalidNumStr ]*
toI128 : Str -> Result I128 [ InvalidNumStr ]*
toU64 : Str -> Result U64 [ InvalidNumStr ]*
toI64 : Str -> Result I64 [ InvalidNumStr ]*
toU32 : Str -> Result U32 [ InvalidNumStr ]*
toI32 : Str -> Result I32 [ InvalidNumStr ]*
toU16 : Str -> Result U16 [ InvalidNumStr ]*
toI16 : Str -> Result I16 [ InvalidNumStr ]*
toU8 : Str -> Result U8 [ InvalidNumStr ]*
toI8 : Str -> Result I8 [ InvalidNumStr ]*
"#;
let parse_state = roc_parse::state::State::new(src_bytes.as_bytes());
return Ok(send_header(
info,
parse_state,
module_ids,
ident_ids_by_module,
module_timing,
));
}
_ => { _ => {
// fall through // fall through
} }

View file

@ -1048,7 +1048,7 @@ define_builtins! {
0 STR_STR: "Str" imported // the Str.Str type alias 0 STR_STR: "Str" imported // the Str.Str type alias
1 STR_AT_STR: "@Str" // the Str.@Str private tag 1 STR_AT_STR: "@Str" // the Str.@Str private tag
2 STR_IS_EMPTY: "isEmpty" 2 STR_IS_EMPTY: "isEmpty"
3 STR_APPEND: "append" 3 STR_APPEND: "#append" // unused
4 STR_CONCAT: "concat" 4 STR_CONCAT: "concat"
5 STR_JOIN_WITH: "joinWith" 5 STR_JOIN_WITH: "joinWith"
6 STR_SPLIT: "split" 6 STR_SPLIT: "split"

View file

@ -6589,7 +6589,19 @@ fn can_reuse_symbol<'a>(
if let roc_can::expr::Expr::Var(symbol) = expr { if let roc_can::expr::Expr::Var(symbol) = expr {
let symbol = *symbol; let symbol = *symbol;
if env.is_imported_symbol(symbol) { let arguments = [
Symbol::ARG_1,
Symbol::ARG_2,
Symbol::ARG_3,
Symbol::ARG_4,
Symbol::ARG_5,
Symbol::ARG_6,
Symbol::ARG_7,
];
if arguments.contains(&symbol) {
Value(symbol)
} else if env.is_imported_symbol(symbol) {
Imported(symbol) Imported(symbol)
} else if procs.partial_procs.contains_key(symbol) { } else if procs.partial_procs.contains_key(symbol) {
LocalFunction(symbol) LocalFunction(symbol)

View file

@ -620,7 +620,7 @@ impl<'a, T> Collection<'a, T> {
} }
} }
pub fn with_items(items: &'a [T]) -> Collection<'a, T> { pub const fn with_items(items: &'a [T]) -> Collection<'a, T> {
Collection { Collection {
items, items,
final_comments: None, final_comments: None,

View file

@ -6,6 +6,7 @@ use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
use crate::state::State; use crate::state::State;
use crate::string_literal; use crate::string_literal;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_module::symbol::Symbol;
use roc_region::all::Loc; use roc_region::all::Loc;
#[derive(Debug)] #[derive(Debug)]
@ -17,6 +18,10 @@ pub enum HeaderFor<'a> {
generates: UppercaseIdent<'a>, generates: UppercaseIdent<'a>,
generates_with: &'a [Loc<ExposedName<'a>>], generates_with: &'a [Loc<ExposedName<'a>>],
}, },
/// Only created during canonicalization, never actually parsed from source
Builtin {
generates_with: &'a [Symbol],
},
PkgConfig { PkgConfig {
/// usually `pf` /// usually `pf`
config_shorthand: &'a str, config_shorthand: &'a str,
@ -60,11 +65,11 @@ impl<'a> From<ModuleName<'a>> for &'a str {
} }
impl<'a> ModuleName<'a> { impl<'a> ModuleName<'a> {
pub fn new(name: &'a str) -> Self { pub const fn new(name: &'a str) -> Self {
ModuleName(name) ModuleName(name)
} }
pub fn as_str(&'a self) -> &'a str { pub const fn as_str(&'a self) -> &'a str {
self.0 self.0
} }
} }

View file

@ -126,8 +126,15 @@ mod solve_expr {
} }
fn promote_expr_to_module(src: &str) -> String { fn promote_expr_to_module(src: &str) -> String {
let mut buffer = let mut buffer = String::from(indoc!(
String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n"); r#"
app "test"
imports [ Result.{ Result } ]
provides [ main ] to "./platform"
main =
"#
));
for line in src.lines() { for line in src.lines() {
// indent the body! // indent the body!
@ -3503,7 +3510,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
app "test" provides [ main ] to "./platform" app "test" imports [ Result.{ Result } ] provides [ main ] to "./platform"
boom = \_ -> boom {} boom = \_ -> boom {}
@ -4728,7 +4735,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
canIGo : _ -> Result _ _ canIGo : _ -> Result.Result _ _
canIGo = \color -> canIGo = \color ->
when color is when color is
"green" -> Ok "go!" "green" -> Ok "go!"

View file

@ -5,13 +5,4 @@ app "effect-example"
main : Effect.Effect {} main : Effect.Effect {}
main = main =
Effect.after (Effect.putLine (Str.concat "It is known" "foo"))
(Effect.getLine)
\line ->
Effect.after
(Effect.putLine "You entered: \(line)")
\{ } ->
Effect.after
(Effect.putLine "It is known")
\{ } ->
Effect.always {}

View file

@ -509,8 +509,11 @@ impl<'a> RocDocAllocator<'a> {
// debug_assert!(region.contains(&sub_region)); // debug_assert!(region.contains(&sub_region));
// If the outer region takes more than 1 full screen (~60 lines), only show the inner region // If the outer region takes more than 1 full screen (~60 lines), only show the inner region
if region.end().line - region.start().line > 60 { match region.end().line.checked_sub(region.start().line) {
return self.region_with_subregion(sub_region, sub_region); Some(v) if v > 60 => {
return self.region_with_subregion(sub_region, sub_region);
}
_ => {}
} }
// if true, the final line of the snippet will be some ^^^ that point to the region where // if true, the final line of the snippet will be some ^^^ that point to the region where