New module header

Implements the new `module` header syntax as described in "module and package changes" [1]:

```
module [Request, Response, req]
```

The old syntax should still work fine, and is automatically upgraded to the new one
when running `roc format`.

[1] https://docs.google.com/document/d/1E_77fO-44BtoBtXoVeWyGh1xN2KRTWTu8q6i25RNNx0/edit
This commit is contained in:
Agus Zubiaga 2024-02-18 19:10:54 -03:00
parent 7754dd7ef7
commit 057a18573a
No known key found for this signature in database
92 changed files with 1445 additions and 1563 deletions

View File

@ -8,7 +8,6 @@ use roc_fmt::def::fmt_defs;
use roc_fmt::module::fmt_module; use roc_fmt::module::fmt_module;
use roc_fmt::spaces::RemoveSpaces; use roc_fmt::spaces::RemoveSpaces;
use roc_fmt::{Ast, Buf}; use roc_fmt::{Ast, Buf};
use roc_parse::ast::Defs;
use roc_parse::module::parse_module_defs; use roc_parse::module::parse_module_defs;
use roc_parse::{module, parser::SyntaxError, state::State}; use roc_parse::{module, parser::SyntaxError, state::State};
@ -226,7 +225,9 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result<Ast<'a>, SyntaxError<'
let (module, state) = module::parse_header(arena, State::new(src.as_bytes())) let (module, state) = module::parse_header(arena, State::new(src.as_bytes()))
.map_err(|e| SyntaxError::Header(e.problem))?; .map_err(|e| SyntaxError::Header(e.problem))?;
let defs = parse_module_defs(arena, state, Defs::default())?; let (module, defs) = module.upgrade_header_imports(arena);
let defs = parse_module_defs(arena, state, defs)?;
Ok(Ast { module, defs }) Ok(Ast { module, defs })
} }

View File

@ -1,6 +1,4 @@
interface Bool module [Bool, Eq, true, false, and, or, not, isEq, isNotEq]
exposes [Bool, Eq, true, false, and, or, not, isEq, isNotEq]
imports []
## Defines a type that can be compared for total equality. ## Defines a type that can be compared for total equality.
## ##

View File

@ -2,9 +2,7 @@
## - Holding unknown Roc types when developing [platforms](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform). ## - Holding unknown Roc types when developing [platforms](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform).
## - To improve performance in rare cases. ## - To improve performance in rare cases.
## ##
interface Box module [box, unbox]
exposes [box, unbox]
imports []
## Allocates a value on the heap. Boxing is an expensive process as it copies ## Allocates a value on the heap. Boxing is an expensive process as it copies
## the value from the stack to the heap. This may provide a performance ## the value from the stack to the heap. This may provide a performance

View File

@ -1,36 +1,34 @@
interface Decode module [
exposes [ DecodeError,
DecodeError, DecodeResult,
DecodeResult, Decoder,
Decoder, Decoding,
Decoding, DecoderFormatting,
DecoderFormatting, decoder,
decoder, u8,
u8, u16,
u16, u32,
u32, u64,
u64, u128,
u128, i8,
i8, i16,
i16, i32,
i32, i64,
i64, i128,
i128, f32,
f32, f64,
f64, dec,
dec, bool,
bool, string,
string, list,
list, record,
record, tuple,
tuple, custom,
custom, decodeWith,
decodeWith, fromBytesPartial,
fromBytesPartial, fromBytes,
fromBytes, mapResult,
mapResult, ]
]
imports []
import List import List
import Result exposing [Result] import Result exposing [Result]

View File

@ -1,35 +1,33 @@
interface Dict module [
exposes [ Dict,
Dict, empty,
empty, withCapacity,
withCapacity, single,
single, clear,
clear, capacity,
capacity, reserve,
reserve, releaseExcessCapacity,
releaseExcessCapacity, len,
len, isEmpty,
isEmpty, get,
get, contains,
contains, insert,
insert, remove,
remove, update,
update, walk,
walk, walkUntil,
walkUntil, keepIf,
keepIf, dropIf,
dropIf, toList,
toList, fromList,
fromList, keys,
keys, values,
values, insertAll,
insertAll, keepShared,
keepShared, removeAll,
removeAll, map,
map, joinMap,
joinMap, ]
]
imports []
import Bool exposing [Bool, Eq] import Bool exposing [Bool, Eq]
import Result exposing [Result] import Result exposing [Result]

View File

@ -1,34 +1,32 @@
interface Encode module [
exposes [ Encoder,
Encoder, Encoding,
Encoding, toEncoder,
toEncoder, EncoderFormatting,
EncoderFormatting, u8,
u8, u16,
u16, u32,
u32, u64,
u64, u128,
u128, i8,
i8, i16,
i16, i32,
i32, i64,
i64, i128,
i128, f32,
f32, f64,
f64, dec,
dec, bool,
bool, string,
string, list,
list, record,
record, tag,
tag, tuple,
tuple, custom,
custom, appendWith,
appendWith, append,
append, toBytes,
toBytes, ]
]
imports []
import Num exposing [ import Num exposing [
U8, U8,

View File

@ -1,26 +1,25 @@
interface Hash module [
exposes [ Hash,
Hash, Hasher,
Hasher, hash,
hash, addBytes,
addBytes, addU8,
addU8, addU16,
addU16, addU32,
addU32, addU64,
addU64, addU128,
addU128, hashBool,
hashBool, hashI8,
hashI8, hashI16,
hashI16, hashI32,
hashI32, hashI64,
hashI64, hashI128,
hashI128, hashDec,
hashDec, complete,
complete, hashStrBytes,
hashStrBytes, hashList,
hashList, hashUnordered,
hashUnordered, ]
] imports []
import Bool exposing [Bool] import Bool exposing [Bool]
import List import List

View File

@ -1,41 +1,39 @@
interface Inspect module [
exposes [ Inspect,
Inspect, Inspector,
Inspector, InspectFormatter,
InspectFormatter, ElemWalker,
ElemWalker, KeyValWalker,
KeyValWalker, inspect,
inspect, init,
init, list,
list, set,
set, dict,
dict, tag,
tag, tuple,
tuple, record,
record, bool,
bool, str,
str, function,
function, opaque,
opaque, u8,
u8, i8,
i8, u16,
u16, i16,
i16, u32,
u32, i32,
i32, u64,
u64, i64,
i64, u128,
u128, i128,
i128, f32,
f32, f64,
f64, dec,
dec, custom,
custom, apply,
apply, toInspector,
toInspector, toStr,
toStr, ]
]
imports []
import Bool exposing [Bool] import Bool exposing [Bool]
import Num exposing [U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec] import Num exposing [U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]

View File

@ -1,77 +1,75 @@
interface List module [
exposes [ isEmpty,
isEmpty, get,
get, set,
set, replace,
replace, update,
update, append,
append, appendIfOk,
appendIfOk, prepend,
prepend, prependIfOk,
prependIfOk, map,
map, len,
len, withCapacity,
withCapacity, walkBackwards,
walkBackwards, concat,
concat, first,
first, single,
single, repeat,
repeat, reverse,
reverse, join,
join, keepIf,
keepIf, contains,
contains, sum,
sum, walk,
walk, last,
last, keepOks,
keepOks, keepErrs,
keepErrs, mapWithIndex,
mapWithIndex, map2,
map2, map3,
map3, product,
product, walkWithIndex,
walkWithIndex, walkUntil,
walkUntil, walkWithIndexUntil,
walkWithIndexUntil, walkFrom,
walkFrom, walkFromUntil,
walkFromUntil, range,
range, sortWith,
sortWith, swap,
swap, dropAt,
dropAt, min,
min, max,
max, map4,
map4, mapTry,
mapTry, walkTry,
walkTry, joinMap,
joinMap, any,
any, takeFirst,
takeFirst, takeLast,
takeLast, dropFirst,
dropFirst, dropLast,
dropLast, findFirst,
findFirst, findLast,
findLast, findFirstIndex,
findFirstIndex, findLastIndex,
findLastIndex, sublist,
sublist, intersperse,
intersperse, split,
split, splitFirst,
splitFirst, splitLast,
splitLast, startsWith,
startsWith, endsWith,
endsWith, all,
all, dropIf,
dropIf, sortAsc,
sortAsc, sortDesc,
sortDesc, reserve,
reserve, releaseExcessCapacity,
releaseExcessCapacity, walkBackwardsUntil,
walkBackwardsUntil, countIf,
countIf, chunksOf,
chunksOf, ]
]
imports []
import Bool exposing [Bool, Eq] import Bool exposing [Bool, Eq]
import Result exposing [Result] import Result exposing [Result]

View File

@ -1,163 +1,161 @@
interface Num module [
exposes [ Num,
Num, Int,
Int, Frac,
Frac, Integer,
Integer, FloatingPoint,
FloatingPoint, I128,
I128, I64,
I64, I32,
I32, I16,
I16, I8,
I8, U128,
U128, U64,
U64, U32,
U32, U16,
U16, U8,
U8, Signed128,
Signed128, Signed64,
Signed64, Signed32,
Signed32, Signed16,
Signed16, Signed8,
Signed8, Unsigned128,
Unsigned128, Unsigned64,
Unsigned64, Unsigned32,
Unsigned32, Unsigned16,
Unsigned16, Unsigned8,
Unsigned8, Dec,
Dec, F64,
F64, F32,
F32, Decimal,
Decimal, Binary32,
Binary32, Binary64,
Binary64, e,
e, pi,
pi, tau,
tau, abs,
abs, absDiff,
absDiff, neg,
neg, add,
add, sub,
sub, mul,
mul, min,
min, max,
max, isLt,
isLt, isLte,
isLte, isGt,
isGt, isGte,
isGte, isApproxEq,
isApproxEq, sin,
sin, cos,
cos, tan,
tan, atan,
atan, acos,
acos, asin,
asin, isZero,
isZero, isEven,
isEven, isOdd,
isOdd, toFrac,
toFrac, isPositive,
isPositive, isNegative,
isNegative, isNaN,
isNaN, isInfinite,
isInfinite, isFinite,
isFinite, rem,
rem, remChecked,
remChecked, div,
div, divChecked,
divChecked, sqrt,
sqrt, sqrtChecked,
sqrtChecked, log,
log, logChecked,
logChecked, round,
round, ceiling,
ceiling, floor,
floor, compare,
compare, pow,
pow, powInt,
powInt, countLeadingZeroBits,
countLeadingZeroBits, countTrailingZeroBits,
countTrailingZeroBits, countOneBits,
countOneBits, addWrap,
addWrap, addChecked,
addChecked, addSaturated,
addSaturated, bitwiseAnd,
bitwiseAnd, bitwiseXor,
bitwiseXor, bitwiseOr,
bitwiseOr, bitwiseNot,
bitwiseNot, shiftLeftBy,
shiftLeftBy, shiftRightBy,
shiftRightBy, shiftRightZfBy,
shiftRightZfBy, subWrap,
subWrap, subChecked,
subChecked, subSaturated,
subSaturated, mulWrap,
mulWrap, mulSaturated,
mulSaturated, mulChecked,
mulChecked, intCast,
intCast, divCeil,
divCeil, divCeilChecked,
divCeilChecked, divTrunc,
divTrunc, divTruncChecked,
divTruncChecked, toStr,
toStr, isMultipleOf,
isMultipleOf, minI8,
minI8, maxI8,
maxI8, minU8,
minU8, maxU8,
maxU8, minI16,
minI16, maxI16,
maxI16, minU16,
minU16, maxU16,
maxU16, minI32,
minI32, maxI32,
maxI32, minU32,
minU32, maxU32,
maxU32, minI64,
minI64, maxI64,
maxI64, minU64,
minU64, maxU64,
maxU64, minI128,
minI128, maxI128,
maxI128, minU128,
minU128, maxU128,
maxU128, minF32,
minF32, maxF32,
maxF32, minF64,
minF64, maxF64,
maxF64, toI8,
toI8, toI8Checked,
toI8Checked, toI16,
toI16, toI16Checked,
toI16Checked, toI32,
toI32, toI32Checked,
toI32Checked, toI64,
toI64, toI64Checked,
toI64Checked, toI128,
toI128, toI128Checked,
toI128Checked, toU8,
toU8, toU8Checked,
toU8Checked, toU16,
toU16, toU16Checked,
toU16Checked, toU32,
toU32, toU32Checked,
toU32Checked, toU64,
toU64, toU64Checked,
toU64Checked, toU128,
toU128, toU128Checked,
toU128Checked, toF32,
toF32, toF32Checked,
toF32Checked, toF64,
toF64, toF64Checked,
toF64Checked, withoutDecimalPoint,
withoutDecimalPoint, withDecimalPoint,
withDecimalPoint, f32ToParts,
f32ToParts, f64ToParts,
f64ToParts, f32FromParts,
f32FromParts, f64FromParts,
f64FromParts, ]
]
imports []
import Bool exposing [Bool] import Bool exposing [Bool]
import Result exposing [Result] import Result exposing [Result]

View File

@ -1,6 +1,4 @@
interface Result module [Result, isOk, isErr, map, mapErr, try, onErr, withDefault]
exposes [Result, isOk, isErr, map, mapErr, try, onErr, withDefault]
imports []
import Bool exposing [Bool] import Bool exposing [Bool]

View File

@ -1,30 +1,28 @@
interface Set module [
exposes [ Set,
Set, empty,
empty, withCapacity,
withCapacity, reserve,
reserve, releaseExcessCapacity,
releaseExcessCapacity, single,
single, walk,
walk, walkUntil,
walkUntil, keepIf,
keepIf, dropIf,
dropIf, insert,
insert, len,
len, isEmpty,
isEmpty, capacity,
capacity, remove,
remove, contains,
contains, toList,
toList, fromList,
fromList, union,
union, intersection,
intersection, difference,
difference, map,
map, joinMap,
joinMap, ]
]
imports []
import List import List
import Bool exposing [Bool, Eq] import Bool exposing [Bool, Eq]

View File

@ -326,50 +326,48 @@
## If a situation like this comes up, a slice can be turned into a separate string by using [`Str.concat`](https://www.roc-lang.org/builtins/Str#concat) to concatenate the slice onto an empty string (or one created with [`Str.withCapacity`](https://www.roc-lang.org/builtins/Str#withCapacity)). ## If a situation like this comes up, a slice can be turned into a separate string by using [`Str.concat`](https://www.roc-lang.org/builtins/Str#concat) to concatenate the slice onto an empty string (or one created with [`Str.withCapacity`](https://www.roc-lang.org/builtins/Str#withCapacity)).
## ##
## Currently, the only way to get seamless slices of strings is by calling certain `Str` functions which return them. In general, `Str` functions which accept a string and return a subset of that string tend to do this. [`Str.trim`](https://www.roc-lang.org/builtins/Str#trim) is another example of a function which returns a seamless slice. ## Currently, the only way to get seamless slices of strings is by calling certain `Str` functions which return them. In general, `Str` functions which accept a string and return a subset of that string tend to do this. [`Str.trim`](https://www.roc-lang.org/builtins/Str#trim) is another example of a function which returns a seamless slice.
interface Str module [
exposes [ Utf8Problem,
Utf8Problem, Utf8ByteProblem,
Utf8ByteProblem, concat,
concat, isEmpty,
isEmpty, joinWith,
joinWith, split,
split, repeat,
repeat, countUtf8Bytes,
countUtf8Bytes, toUtf8,
toUtf8, fromUtf8,
fromUtf8, startsWith,
startsWith, endsWith,
endsWith, trim,
trim, trimStart,
trimStart, trimEnd,
trimEnd, toDec,
toDec, toF64,
toF64, toF32,
toF32, toU128,
toU128, toI128,
toI128, toU64,
toU64, toI64,
toI64, toU32,
toU32, toI32,
toI32, toU16,
toU16, toI16,
toI16, toU8,
toU8, toI8,
toI8, replaceEach,
replaceEach, replaceFirst,
replaceFirst, replaceLast,
replaceLast, splitFirst,
splitFirst, splitLast,
splitLast, walkUtf8,
walkUtf8, walkUtf8WithIndex,
walkUtf8WithIndex, reserve,
reserve, releaseExcessCapacity,
releaseExcessCapacity, withCapacity,
withCapacity, withPrefix,
withPrefix, contains,
contains, ]
]
imports []
import Bool exposing [Bool] import Bool exposing [Bool]
import Result exposing [Result] import Result exposing [Result]

View File

@ -1,12 +1,10 @@
## THIS MODULE IS DEPRECATED AND CURRENTLY IN THE PROCESS OF BEING REMOVED ## THIS MODULE IS DEPRECATED AND CURRENTLY IN THE PROCESS OF BEING REMOVED
## FROM STD LIBRARY ## FROM STD LIBRARY
interface TotallyNotJson module [
exposes [ Json,
Json, json,
json, jsonWithOptions,
jsonWithOptions, ]
]
imports []
import List import List
import Str import Str

View File

@ -506,7 +506,7 @@ fn canonicalize_claimed_ability_impl<'a>(
// OPTION-1: The implementation identifier is the only identifier of that name in the // OPTION-1: The implementation identifier is the only identifier of that name in the
// scope. For example, // scope. For example,
// //
// interface F imports [] exposes [] // module []
// //
// Hello := {} implements [Encoding.{ toEncoder }] // Hello := {} implements [Encoding.{ toEncoder }]
// //
@ -518,7 +518,9 @@ fn canonicalize_claimed_ability_impl<'a>(
// OPTION-2: The implementation identifier is a unique shadow of the ability member, // OPTION-2: The implementation identifier is a unique shadow of the ability member,
// which has also been explicitly imported. For example, // which has also been explicitly imported. For example,
// //
// interface F imports [Encoding.{ toEncoder }] exposes [] // module []
//
// import Encoding exposing [toEncoder]
// //
// Hello := {} implements [Encoding.{ toEncoder }] // Hello := {} implements [Encoding.{ toEncoder }]
// //

View File

@ -8,7 +8,7 @@ use bumpalo::Bump;
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces}; use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry, PackageHeader, ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires, PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
}; };
@ -18,8 +18,8 @@ use roc_region::all::Loc;
pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) { pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0); fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0);
match &module.header { match &module.header {
Header::Interface(header) => { Header::Module(header) => {
fmt_interface_header(buf, header); fmt_module_header(buf, header);
} }
Header::App(header) => { Header::App(header) => {
fmt_app_header(buf, header); fmt_app_header(buf, header);
@ -171,20 +171,19 @@ impl<'a, K: Formattable, V: Formattable> Formattable for KeywordItem<'a, K, V> {
} }
} }
pub fn fmt_interface_header<'a>(buf: &mut Buf, header: &'a InterfaceHeader<'a>) { pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) {
buf.indent(0); buf.indent(0);
buf.push_str("interface"); buf.push_str("module");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
// module name if header.before_exposes.iter().all(|c| c.is_newline()) {
buf.indent(indent); buf.spaces(1);
buf.push_str(header.name.value.as_str()); fmt_exposes(buf, header.exposes, 0);
} else {
let indent = INDENT;
header.exposes.keyword.format(buf, indent); fmt_default_spaces(buf, header.before_exposes, indent);
fmt_exposes(buf, header.exposes.item, indent); fmt_exposes(buf, header.exposes, indent);
header.imports.keyword.format(buf, indent); };
fmt_imports(buf, header.imports.item, indent);
} }
pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) { pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {

View File

@ -10,9 +10,9 @@ use roc_parse::{
StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
}, },
header::{ header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem, AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName,
ModuleName, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
ProvidesTo, To, TypedIdent, TypedIdent,
}, },
ident::{BadIdent, UppercaseIdent}, ident::{BadIdent, UppercaseIdent},
}; };
@ -283,11 +283,10 @@ impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
impl<'a> RemoveSpaces<'a> for Module<'a> { impl<'a> RemoveSpaces<'a> for Module<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
let header = match &self.header { let header = match &self.header {
Header::Interface(header) => Header::Interface(InterfaceHeader { Header::Module(header) => Header::Module(ModuleHeader {
before_name: &[], before_exposes: &[],
name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena), exposes: header.exposes.remove_spaces(arena),
imports: header.imports.remove_spaces(arena), interface_imports: header.interface_imports.remove_spaces(arena),
}), }),
Header::App(header) => Header::App(AppHeader { Header::App(header) => Header::App(AppHeader {
before_name: &[], before_name: &[],

View File

@ -6109,9 +6109,7 @@ In roc, functions are always written as a lambda, like{}
report_header_problem_as( report_header_problem_as(
indoc!( indoc!(
r" r"
interface Foobar module [main, @Foo]
exposes [main, @Foo]
imports [pf.Task, Base64]
" "
), ),
indoc!( indoc!(
@ -6120,39 +6118,12 @@ In roc, functions are always written as a lambda, like{}
I am partway through parsing an `exposes` list, but I got stuck here: I am partway through parsing an `exposes` list, but I got stuck here:
1 interface Foobar 1 module [main, @Foo]
2 exposes [main, @Foo] ^
^
I was expecting a type name, value name or function name next, like I was expecting a type name, value name or function name next, like
exposes [Animal, default, tame] [Animal, default, tame]
"
),
)
}
#[test]
fn invalid_module_name() {
report_header_problem_as(
indoc!(
r"
interface foobar
exposes [main, @Foo]
imports [pf.Task, Base64]
"
),
indoc!(
r"
WEIRD MODULE NAME in /code/proj/Main.roc
I am partway through parsing a header, but got stuck here:
1 interface foobar
^
I am expecting a module name next, like BigNum or Main. Module names
must start with an uppercase letter.
" "
), ),
) )

View File

@ -49,8 +49,8 @@ use roc_mono::{drop_specialization, inc_dec};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_parse::ast::{self, CommentOrNewline, ExtractSpaces, Spaced, ValueDef}; use roc_parse::ast::{self, CommentOrNewline, ExtractSpaces, Spaced, ValueDef};
use roc_parse::header::{ use roc_parse::header::{
ExposedName, HeaderType, ImportsKeywordItem, PackageEntry, PackageHeader, PlatformHeader, To, self, ExposedName, HeaderType, ImportsKeywordItem, PackageEntry, PackageHeader, PlatformHeader,
TypedIdent, To, TypedIdent,
}; };
use roc_parse::module::parse_module_defs; use roc_parse::module::parse_module_defs;
use roc_parse::parser::{FileError, SourceError, SyntaxError}; use roc_parse::parser::{FileError, SourceError, SyntaxError};
@ -644,7 +644,7 @@ struct CanAndCon {
enum PlatformPath<'a> { enum PlatformPath<'a> {
NotSpecified, NotSpecified,
Valid(To<'a>), Valid(To<'a>),
RootIsInterface, RootIsModule,
RootIsHosted, RootIsHosted,
RootIsPlatformModule, RootIsPlatformModule,
} }
@ -1204,8 +1204,9 @@ fn adjust_header_paths<'a>(
{ {
debug_assert_eq!(*header_id, header_output.module_id); debug_assert_eq!(*header_id, header_output.module_id);
if let HeaderType::Interface { name, .. } = header_type { if let HeaderType::Module { name, .. } = header_type {
// Interface modules can have names like Foo.Bar.Baz, // [modules-revamp] TODO: Privacy changes
// Modules can have names like Foo.Bar.Baz,
// in which case we need to adjust the src_dir to // in which case we need to adjust the src_dir to
// remove the "Bar/Baz" directories in order to correctly // remove the "Bar/Baz" directories in order to correctly
// resolve this interface module's imports! // resolve this interface module's imports!
@ -2313,13 +2314,13 @@ fn update<'a>(
state.exposed_modules = exposes_ids; state.exposed_modules = exposes_ids;
} }
} }
Builtin { .. } | Interface { .. } => { Builtin { .. } | Module { .. } => {
if header.is_root_module { if header.is_root_module {
debug_assert!(matches!( debug_assert!(matches!(
state.platform_path, state.platform_path,
PlatformPath::NotSpecified PlatformPath::NotSpecified
)); ));
state.platform_path = PlatformPath::RootIsInterface; state.platform_path = PlatformPath::RootIsModule;
} }
} }
Hosted { .. } => { Hosted { .. } => {
@ -2956,7 +2957,7 @@ fn update<'a>(
// This happens due to abilities. In detail, consider // This happens due to abilities. In detail, consider
// //
// # Default module // # Default module
// interface Default exposes [default, getDefault] // module [default, getDefault]
// //
// Default implements default : {} -> a where a implements Default // Default implements default : {} -> a where a implements Default
// //
@ -3198,7 +3199,7 @@ fn finish_specialization<'a>(
let module_id = state.root_id; let module_id = state.root_id;
let uses_prebuilt_platform = match platform_data { let uses_prebuilt_platform = match platform_data {
Some(data) => data.is_prebuilt, Some(data) => data.is_prebuilt,
// If there's no platform data (e.g. because we're building an interface module) // If there's no platform data (e.g. because we're building a module)
// then there's no prebuilt platform either! // then there's no prebuilt platform either!
None => false, None => false,
}; };
@ -3346,12 +3347,12 @@ fn load_package_from_disk<'a>(
match parsed { match parsed {
Ok(( Ok((
ast::Module { ast::Module {
header: ast::Header::Interface(header), header: ast::Header::Module(header),
.. ..
}, },
_parse_state, _parse_state,
)) => Err(LoadingProblem::UnexpectedHeader(format!( )) => Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got Interface with header\n{header:?}" "expected platform/package module, got Module with header\n{header:?}"
))), ))),
Ok(( Ok((
ast::Module { ast::Module {
@ -3477,23 +3478,25 @@ fn load_builtin_module_help<'a>(
match parsed { match parsed {
Ok(( Ok((
ast::Module { ast::Module {
header: ast::Header::Interface(header), header: ast::Header::Module(header),
comments, comments,
}, },
parse_state, parse_state,
)) => { )) => {
let name_stem = arena.alloc_str(filename.file_stem().unwrap().to_str().unwrap());
let info = HeaderInfo { let info = HeaderInfo {
filename, filename,
is_root_module, is_root_module,
opt_shorthand, opt_shorthand,
packages: &[], packages: &[],
header_type: HeaderType::Builtin { header_type: HeaderType::Builtin {
name: header.name.value, name: header::ModuleName::new(name_stem),
exposes: unspace(arena, header.exposes.item.items), exposes: unspace(arena, header.exposes.items),
generates_with: &[], generates_with: &[],
}, },
module_comments: comments, module_comments: comments,
header_imports: Some(header.imports), header_imports: header.interface_imports,
}; };
(info, parse_state) (info, parse_state)
@ -3693,45 +3696,6 @@ fn find_task<T>(local: &Worker<T>, global: &Injector<T>, stealers: &[Stealer<T>]
}) })
} }
fn verify_interface_matches_file_path<'a>(
interface_name: Loc<roc_parse::header::ModuleName<'a>>,
path: &Path,
state: &roc_parse::state::State<'a>,
) -> Result<(), LoadingProblem<'a>> {
let module_parts = interface_name.value.as_str().split(MODULE_SEPARATOR).rev();
let mut is_mismatched = false;
let mut opt_path = Some(path);
for part in module_parts {
match opt_path.and_then(|path| path.file_stem().map(|fi| (path, fi))) {
None => {
is_mismatched = true;
break;
}
Some((path, fi)) => {
if fi != part {
is_mismatched = true;
break;
}
opt_path = path.parent();
}
}
}
if !is_mismatched {
return Ok(());
}
use roc_parse::parser::EHeader;
let syntax_problem =
SyntaxError::Header(EHeader::InconsistentModuleName(interface_name.region));
let problem = LoadingProblem::ParsingFailed(FileError {
problem: SourceError::new(syntax_problem, state),
filename: path.to_path_buf(),
});
Err(problem)
}
#[derive(Debug)] #[derive(Debug)]
struct HeaderOutput<'a> { struct HeaderOutput<'a> {
module_id: ModuleId, module_id: ModuleId,
@ -3798,48 +3762,35 @@ fn parse_header<'a>(
match parsed { match parsed {
Ok(( Ok((
ast::Module { ast::Module {
header: ast::Header::Interface(header), header: ast::Header::Module(header),
comments, comments,
}, },
parse_state, parse_state,
)) => { )) => {
verify_interface_matches_file_path(header.name, &filename, &parse_state)?; let module_name = match opt_expected_module_name {
Some(pq_name) => arena.alloc_str(pq_name.as_inner().as_str()),
None => {
// [modules-revamp] [privacy-changes] TODO: Support test/check on nested modules
arena.alloc_str(filename.file_stem().unwrap().to_str().unwrap())
}
};
let header_name_region = header.name.region;
let info = HeaderInfo { let info = HeaderInfo {
filename, filename,
is_root_module, is_root_module,
opt_shorthand, opt_shorthand,
packages: &[], packages: &[],
header_type: HeaderType::Interface { header_type: HeaderType::Module {
name: header.name.value, name: roc_parse::header::ModuleName::new(module_name),
exposes: unspace(arena, header.exposes.item.items), exposes: unspace(arena, header.exposes.items),
}, },
module_comments: comments, module_comments: comments,
header_imports: Some(header.imports), header_imports: header.interface_imports,
}; };
let (module_id, module_name, header) = let (module_id, _, header) =
build_header(info, parse_state.clone(), module_ids, module_timing)?; build_header(info, parse_state.clone(), module_ids, module_timing)?;
if let Some(expected_module_name) = opt_expected_module_name {
if expected_module_name != module_name {
let problem = SourceError::new(
IncorrectModuleName {
module_id,
found: Loc::at(header_name_region, module_name),
expected: expected_module_name,
},
&parse_state,
);
let problem = LoadingProblem::IncorrectModuleName(FileError {
problem,
filename: header.module_path,
});
return Err(problem);
}
}
Ok(HeaderOutput { Ok(HeaderOutput {
module_id, module_id,
msg: Msg::Header(header), msg: Msg::Header(header),
@ -4217,14 +4168,9 @@ fn build_header<'a>(
// Package modules do not have names. // Package modules do not have names.
String::new().into() String::new().into()
} }
HeaderType::Interface { name, .. } HeaderType::Module { name, .. }
| HeaderType::Builtin { name, .. } | HeaderType::Builtin { name, .. }
| HeaderType::Hosted { name, .. } => { | HeaderType::Hosted { name, .. } => name.as_str().into(),
// TODO check to see if name is consistent with filename.
// If it isn't, report a problem!
name.as_str().into()
}
}; };
let (name, home) = { let (name, home) = {
@ -5172,7 +5118,7 @@ fn parse<'a>(
} }
HeaderType::App { .. } HeaderType::App { .. }
| HeaderType::Package { .. } | HeaderType::Package { .. }
| HeaderType::Interface { .. } | HeaderType::Module { .. }
| HeaderType::Builtin { .. } | HeaderType::Builtin { .. }
| HeaderType::Hosted { .. } => {} | HeaderType::Hosted { .. } => {}
}; };
@ -6408,12 +6354,12 @@ fn report_cannot_run(
severity: Severity::RuntimeError, severity: Severity::RuntimeError,
} }
} }
RootIsInterface => { RootIsModule => {
let doc = alloc.stack([ let doc = alloc.stack([
alloc.reflow( alloc.reflow(
r"The input file is an `interface` module, but only `app` modules can be run.", r"The input file is a module, but only `app` modules can be run.",
), ),
alloc.reflow(r"Tip: You can use `roc check` or `roc test` to verify an interface module like this one."), alloc.reflow(r"Tip: You can use `roc check` or `roc test` to verify a module like this one."),
]); ]);
Report { Report {

View File

@ -339,7 +339,7 @@ fn import_transitive_alias() {
"RBTree.roc", "RBTree.roc",
indoc!( indoc!(
r" r"
interface RBTree exposes [RedBlackTree, empty] imports [] module [RedBlackTree, empty]
# The color of a node. Leaves are considered Black. # The color of a node. Leaves are considered Black.
NodeColor : [Red, Black] NodeColor : [Red, Black]
@ -357,7 +357,7 @@ fn import_transitive_alias() {
"Other.roc", "Other.roc",
indoc!( indoc!(
r" r"
interface Other exposes [empty] imports [] module [empty]
import RBTree import RBTree
@ -372,9 +372,9 @@ fn import_transitive_alias() {
} }
#[test] #[test]
fn interface_with_deps() { fn module_with_deps() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let src_dir = fixtures_dir().join("interface_with_deps"); let src_dir = fixtures_dir().join("module_with_deps");
let filename = src_dir.join("Primary.roc"); let filename = src_dir.join("Primary.roc");
let arena = Bump::new(); let arena = Bump::new();
let loaded = load_and_typecheck( let loaded = load_and_typecheck(
@ -488,7 +488,7 @@ fn load_docs() {
#[test] #[test]
fn import_alias() { fn import_alias() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "ImportAlias", subs_by_module); let loaded_module = load_fixture("module_with_deps", "ImportAlias", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -501,7 +501,7 @@ fn import_alias() {
#[test] #[test]
fn import_inside_def() { fn import_inside_def() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "ImportInsideDef", subs_by_module); let loaded_module = load_fixture("module_with_deps", "ImportInsideDef", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -517,7 +517,7 @@ fn import_inside_def() {
fn exposed_used_outside_scope() { fn exposed_used_outside_scope() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
load_fixture( load_fixture(
"interface_with_deps", "module_with_deps",
"ExposedUsedOutsideScope", "ExposedUsedOutsideScope",
subs_by_module, subs_by_module,
); );
@ -527,17 +527,13 @@ fn exposed_used_outside_scope() {
#[should_panic(expected = "MODULE NOT IMPORTED")] #[should_panic(expected = "MODULE NOT IMPORTED")]
fn import_used_outside_scope() { fn import_used_outside_scope() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
load_fixture( load_fixture("module_with_deps", "ImportUsedOutsideScope", subs_by_module);
"interface_with_deps",
"ImportUsedOutsideScope",
subs_by_module,
);
} }
#[test] #[test]
fn test_load_and_typecheck() { fn test_load_and_typecheck() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "WithBuiltins", subs_by_module); let loaded_module = load_fixture("module_with_deps", "WithBuiltins", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -558,7 +554,7 @@ fn test_load_and_typecheck() {
#[test] #[test]
fn iface_quicksort() { fn iface_quicksort() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "Quicksort", subs_by_module); let loaded_module = load_fixture("module_with_deps", "Quicksort", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -574,7 +570,7 @@ fn iface_quicksort() {
#[test] #[test]
fn load_astar() { fn load_astar() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "AStar", subs_by_module); let loaded_module = load_fixture("module_with_deps", "AStar", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -606,7 +602,7 @@ fn load_principal_types() {
#[test] #[test]
fn iface_dep_types() { fn iface_dep_types() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "Primary", subs_by_module); let loaded_module = load_fixture("module_with_deps", "Primary", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -650,7 +646,7 @@ fn app_dep_types() {
#[test] #[test]
fn imported_dep_regression() { fn imported_dep_regression() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "OneDep", subs_by_module); let loaded_module = load_fixture("module_with_deps", "OneDep", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -663,7 +659,7 @@ fn imported_dep_regression() {
#[test] #[test]
fn ingested_file() { fn ingested_file() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "IngestedFile", subs_by_module); let loaded_module = load_fixture("module_with_deps", "IngestedFile", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -678,7 +674,7 @@ fn ingested_file() {
#[test] #[test]
fn ingested_file_bytes() { fn ingested_file_bytes() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "IngestedFileBytes", subs_by_module); let loaded_module = load_fixture("module_with_deps", "IngestedFileBytes", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -695,7 +691,7 @@ fn parse_problem() {
"Main.roc", "Main.roc",
indoc!( indoc!(
r" r"
interface Main exposes [main] imports [] module [main]
main = [ main = [
" "
@ -732,7 +728,7 @@ fn parse_problem() {
#[should_panic(expected = "FILE NOT FOUND")] #[should_panic(expected = "FILE NOT FOUND")]
fn file_not_found() { fn file_not_found() {
let subs_by_module = Default::default(); let subs_by_module = Default::default();
let loaded_module = load_fixture("interface_with_deps", "invalid$name", subs_by_module); let loaded_module = load_fixture("module_with_deps", "invalid$name", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -887,7 +883,7 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
"Age.roc", "Age.roc",
indoc!( indoc!(
r" r"
interface Age exposes [Age] imports [] module [Age]
Age := U32 Age := U32
" "
@ -897,7 +893,7 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
"Main.roc", "Main.roc",
indoc!( indoc!(
r" r"
interface Main exposes [twenty, readAge] imports [] module [twenty, readAge]
import Age exposing [Age] import Age exposing [Age]
@ -965,7 +961,8 @@ fn unused_imports() {
"Dep1.roc", "Dep1.roc",
indoc!( indoc!(
r#" r#"
interface Dep1 exposes [one] imports [] module [one]
one = 1 one = 1
"# "#
), ),
@ -974,7 +971,8 @@ fn unused_imports() {
"Dep2.roc", "Dep2.roc",
indoc!( indoc!(
r#" r#"
interface Dep2 exposes [two] imports [] module [two]
two = 2 two = 2
"# "#
), ),
@ -983,7 +981,7 @@ fn unused_imports() {
"Dep3.roc", "Dep3.roc",
indoc!( indoc!(
r#" r#"
interface Dep3 exposes [Three, three] imports [] module [Three, three]
Three : [Three] Three : [Three]
@ -995,7 +993,7 @@ fn unused_imports() {
"Main.roc", "Main.roc",
indoc!( indoc!(
r#" r#"
interface Main exposes [usedModule, unusedModule, unusedExposed, unusedWithAlias, usingThreeValue] imports [] module [usedModule, unusedModule, unusedExposed, usingThreeValue, unusedWithAlias]
import Dep1 import Dep1
import Dep3 exposing [Three] import Dep3 exposing [Three]
@ -1663,48 +1661,13 @@ fn import_builtin_in_platform_and_check_app() {
assert!(result.is_ok(), "should check"); assert!(result.is_ok(), "should check");
} }
#[test]
fn module_doesnt_match_file_path() {
let modules = vec![(
"Age.roc",
indoc!(
r"
interface NotAge exposes [Age] imports []
Age := U32
"
),
)];
let err = multiple_modules("module_doesnt_match_file_path", modules).unwrap_err();
assert_eq!(
err,
indoc!(
r"
WEIRD MODULE NAME in tmp/module_doesnt_match_file_path/Age.roc
This module name does not correspond with the file path it is defined
in:
1 interface NotAge exposes [Age] imports []
^^^^^^
Module names must correspond with the file paths they are defined in.
For example, I expect to see BigNum defined in BigNum.roc, or Math.Sin
defined in Math/Sin.roc."
),
"\n{}",
err
);
}
#[test] #[test]
fn module_cyclic_import_itself() { fn module_cyclic_import_itself() {
let modules = vec![( let modules = vec![(
"Age.roc", "Age.roc",
indoc!( indoc!(
r" r"
interface Age exposes [] imports [] module []
import Age import Age
" "
@ -1742,7 +1705,8 @@ fn module_cyclic_import_transitive() {
"Age.roc", "Age.roc",
indoc!( indoc!(
r" r"
interface Age exposes [] imports [] module []
import Person import Person
" "
), ),
@ -1751,7 +1715,8 @@ fn module_cyclic_import_transitive() {
"Person.roc", "Person.roc",
indoc!( indoc!(
r" r"
interface Person exposes [] imports [] module []
import Age import Age
" "
), ),

View File

@ -1,7 +1,7 @@
use std::fmt::Debug; use std::fmt::Debug;
use crate::header::{ use crate::header::{
self, AppHeader, HostedHeader, InterfaceHeader, ModuleName, PackageHeader, PlatformHeader, self, AppHeader, HostedHeader, ModuleHeader, ModuleName, PackageHeader, PlatformHeader,
}; };
use crate::ident::Accessor; use crate::ident::Accessor;
use crate::parser::ESingleQuote; use crate::parser::ESingleQuote;
@ -99,6 +99,21 @@ pub struct Module<'a> {
} }
impl<'a> Module<'a> { impl<'a> Module<'a> {
pub fn upgrade_header_imports(self, arena: &'a Bump) -> (Self, Defs<'a>) {
let (header, defs) = match self.header {
Header::Module(header) => (
Header::Module(ModuleHeader {
interface_imports: None,
..header
}),
Self::header_imports_to_defs(arena, header.interface_imports),
),
header => (header, Defs::default()),
};
(Module { header, ..self }, defs)
}
pub fn header_imports_to_defs( pub fn header_imports_to_defs(
arena: &'a Bump, arena: &'a Bump,
imports: Option< imports: Option<
@ -209,7 +224,7 @@ impl<'a> Module<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Header<'a> { pub enum Header<'a> {
Interface(InterfaceHeader<'a>), Module(ModuleHeader<'a>),
App(AppHeader<'a>), App(AppHeader<'a>),
Package(PackageHeader<'a>), Package(PackageHeader<'a>),
Platform(PlatformHeader<'a>), Platform(PlatformHeader<'a>),
@ -2272,7 +2287,7 @@ impl<'a> Malformed for Module<'a> {
impl<'a> Malformed for Header<'a> { impl<'a> Malformed for Header<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {
Header::Interface(header) => header.is_malformed(), Header::Module(header) => header.is_malformed(),
Header::App(header) => header.is_malformed(), Header::App(header) => header.is_malformed(),
Header::Package(header) => header.is_malformed(), Header::Package(header) => header.is_malformed(),
Header::Platform(header) => header.is_malformed(), Header::Platform(header) => header.is_malformed(),

View File

@ -19,7 +19,7 @@ impl<'a> HeaderType<'a> {
} }
| HeaderType::Hosted { exposes, .. } | HeaderType::Hosted { exposes, .. }
| HeaderType::Builtin { exposes, .. } | HeaderType::Builtin { exposes, .. }
| HeaderType::Interface { exposes, .. } => exposes, | HeaderType::Module { exposes, .. } => exposes,
HeaderType::Platform { .. } | HeaderType::Package { .. } => &[], HeaderType::Platform { .. } | HeaderType::Package { .. } => &[],
} }
} }
@ -30,7 +30,7 @@ impl<'a> HeaderType<'a> {
HeaderType::Builtin { .. } => "builtin", HeaderType::Builtin { .. } => "builtin",
HeaderType::Package { .. } => "package", HeaderType::Package { .. } => "package",
HeaderType::Platform { .. } => "platform", HeaderType::Platform { .. } => "platform",
HeaderType::Interface { .. } => "interface", HeaderType::Module { .. } => "module",
} }
} }
} }
@ -73,7 +73,7 @@ pub enum HeaderType<'a> {
/// usually `pf` /// usually `pf`
config_shorthand: &'a str, config_shorthand: &'a str,
}, },
Interface { Module {
name: ModuleName<'a>, name: ModuleName<'a>,
exposes: &'a [Loc<ExposedName<'a>>], exposes: &'a [Loc<ExposedName<'a>>],
}, },
@ -82,9 +82,9 @@ pub enum HeaderType<'a> {
impl<'a> HeaderType<'a> { impl<'a> HeaderType<'a> {
pub fn get_name(self) -> Option<&'a str> { pub fn get_name(self) -> Option<&'a str> {
match self { match self {
Self::Interface { name, .. } Self::Module { name, .. } | Self::Builtin { name, .. } | Self::Hosted { name, .. } => {
| Self::Builtin { name, .. } Some(name.into())
| Self::Hosted { name, .. } => Some(name.into()), }
Self::App { Self::App {
output_name: StrLiteral::PlainLine(name), output_name: StrLiteral::PlainLine(name),
.. ..
@ -106,13 +106,11 @@ impl<'a> HeaderType<'a> {
pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self { pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self {
match self { match self {
HeaderType::Interface { name, exposes } if module_id.is_builtin() => { HeaderType::Module { name, exposes } if module_id.is_builtin() => HeaderType::Builtin {
HeaderType::Builtin { name,
name, exposes,
exposes, generates_with: &[],
generates_with: &[], },
}
}
_ => self, _ => self,
} }
} }
@ -246,12 +244,12 @@ pub struct KeywordItem<'a, K, V> {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> { pub struct ModuleHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>], pub before_exposes: &'a [CommentOrNewline<'a>],
pub name: Loc<ModuleName<'a>>, pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>, // Keeping this so we can format old interface header into module headers
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>, pub interface_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
} }
pub type ImportsKeywordItem<'a> = KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>; pub type ImportsKeywordItem<'a> = KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>;
@ -430,7 +428,7 @@ where
} }
} }
impl<'a> Malformed for InterfaceHeader<'a> { impl<'a> Malformed for ModuleHeader<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
false false
} }

View File

@ -1,10 +1,11 @@
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces}; use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{ use crate::header::{
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, HostedHeader, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem,
PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader, PlatformRequires, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader,
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
}; };
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::parser::Progress::{self, *}; use crate::parser::Progress::{self, *};
@ -60,12 +61,20 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
record!(Module { record!(Module {
comments: space0_e(EHeader::IndentStart), comments: space0_e(EHeader::IndentStart),
header: one_of![ header: one_of![
map!(
skip_first!(
keyword("module", EHeader::Start),
increment_min_indent(module_header())
),
Header::Module
),
// Old headers
map!( map!(
skip_first!( skip_first!(
keyword("interface", EHeader::Start), keyword("interface", EHeader::Start),
increment_min_indent(interface_header()) increment_min_indent(interface_header())
), ),
Header::Interface Header::Module
), ),
map!( map!(
skip_first!( skip_first!(
@ -100,22 +109,65 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
} }
#[inline(always)] #[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
record!(InterfaceHeader { record!(ModuleHeader {
before_name: space0_e(EHeader::IndentStart), before_exposes: space0_e(EHeader::IndentStart),
name: loc!(module_name_help(EHeader::ModuleName)), exposes: specialize_err(EHeader::Exposes, exposes_list()),
exposes: specialize_err(EHeader::Exposes, exposes_values()), interface_imports: succeed!(None)
imports: specialize_err(EHeader::Imports, imports()), })
.trace("module_header")
}
/// Parse old interface headers so we can format them into module headers
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
use bumpalo::collections::Vec;
let before_exposes = map_with_arena!(
and!(
skip_second!(
space0_e(EHeader::IndentStart),
loc!(module_name_help(EHeader::ModuleName))
),
specialize_err(EHeader::Exposes, exposes_kw())
),
|arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
let mut combined: Vec<CommentOrNewline> =
Vec::with_capacity_in(before_name.len() + kw.before.len() + kw.after.len(), arena);
combined.extend(before_name);
combined.extend(kw.before);
combined.extend(kw.after);
arena.alloc(combined)
}
);
record!(ModuleHeader {
before_exposes: before_exposes,
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
interface_imports: map!(
specialize_err(EHeader::Imports, imports()),
imports_none_if_empty
)
.trace("imports"),
}) })
.trace("interface_header") .trace("interface_header")
} }
fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option<ImportsKeywordItem<'_>> {
if value.item.is_empty() {
None
} else {
Some(value)
}
}
#[inline(always)] #[inline(always)]
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> { fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
record!(HostedHeader { record!(HostedHeader {
before_name: space0_e(EHeader::IndentStart), before_name: space0_e(EHeader::IndentStart),
name: loc!(module_name_help(EHeader::ModuleName)), name: loc!(module_name_help(EHeader::ModuleName)),
exposes: specialize_err(EHeader::Exposes, exposes_values()), exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
imports: specialize_err(EHeader::Imports, imports()), imports: specialize_err(EHeader::Imports, imports()),
generates: specialize_err(EHeader::Generates, generates()), generates: specialize_err(EHeader::Generates, generates()),
generates_with: specialize_err(EHeader::GeneratesWith, generates_with()), generates_with: specialize_err(EHeader::GeneratesWith, generates_with()),
@ -388,28 +440,39 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
} }
#[inline(always)] #[inline(always)]
fn exposes_values<'a>() -> impl Parser< fn exposes_values_kw<'a>() -> impl Parser<
'a, 'a,
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>, KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
EExposes, EExposes,
> { > {
record!(KeywordItem { record!(KeywordItem {
keyword: spaces_around_keyword( keyword: exposes_kw(),
ExposesKeyword, item: exposes_list()
EExposes::Exposes,
EExposes::IndentExposes,
EExposes::IndentListStart
),
item: collection_trailing_sep_e!(
byte(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
)
}) })
} }
#[inline(always)]
fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> {
spaces_around_keyword(
ExposesKeyword,
EExposes::Exposes,
EExposes::IndentExposes,
EExposes::IndentListStart,
)
}
#[inline(always)]
fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, EExposes>
{
collection_trailing_sep_e!(
byte(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
)
}
pub fn spaces_around_keyword<'a, K: Keyword, E>( pub fn spaces_around_keyword<'a, K: Keyword, E>(
keyword_item: K, keyword_item: K,
expectation: fn(Position) -> E, expectation: fn(Position) -> E,

View File

@ -37,4 +37,4 @@
":=" ":="
":" ":"
"@" "@"
"->" "->"

View File

@ -1 +0,0 @@
interface Foo exposes [] imports []

View File

@ -1,27 +0,0 @@
Module {
comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-13 ModuleName(
"Foo",
),
exposes: KeywordItem {
keyword: Spaces {
before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
}

View File

@ -1 +0,0 @@
interface Foo exposes [] imports []

View File

@ -0,0 +1,10 @@
Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
exposes: [],
interface_imports: None,
},
),
}

View File

@ -1 +0,0 @@
interface T exposes [] imports []

View File

@ -1,27 +0,0 @@
Module {
comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-11 ModuleName(
"T",
),
exposes: KeywordItem {
keyword: Spaces {
before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
}

View File

@ -1 +0,0 @@
interface T exposes [] imports []

View File

@ -0,0 +1,25 @@
Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
exposes: [
@8-9 ExposedName(
"a",
),
@11-12 ExposedName(
"b",
),
@18-19 SpaceBefore(
ExposedName(
"c",
),
[
Newline,
],
),
],
interface_imports: None,
},
),
}

View File

@ -0,0 +1,2 @@
module [a, b,
c]

View File

@ -0,0 +1,10 @@
Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
exposes: [],
interface_imports: None,
},
),
}

View File

@ -1 +0,0 @@
interface Foo.Bar.Baz exposes [] imports []

View File

@ -1,27 +0,0 @@
Module {
comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-21 ModuleName(
"Foo.Bar.Baz",
),
exposes: KeywordItem {
keyword: Spaces {
before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
}

View File

@ -1 +0,0 @@
interface Foo.Bar.Baz exposes [] imports []

View File

@ -0,0 +1 @@
module [Foo, foo, bar]

View File

@ -0,0 +1,20 @@
Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
exposes: [
@23-26 ExposedName(
"Foo",
),
@28-31 ExposedName(
"foo",
),
@33-36 ExposedName(
"bar",
),
],
interface_imports: None,
},
),
}

View File

@ -0,0 +1 @@
interface Foo exposes [Foo, foo, bar] imports []

View File

@ -4768,10 +4768,10 @@ mod test_fmt {
// MODULES // MODULES
#[test] #[test]
fn single_line_interface() { fn single_line_module() {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo exposes [] imports []" module []"
)); ));
} }
@ -4781,12 +4781,14 @@ mod test_fmt {
module_formats_to( module_formats_to(
indoc!( indoc!(
r" r"
interface Foo exposes [] imports [] module []
a = 42 # Yay greetings" a = 42 # Yay greetings"
), ),
indoc!( indoc!(
r" r"
interface Foo exposes [] imports [] module []
a = 42 # Yay greetings a = 42 # Yay greetings
" "
), ),
@ -4794,49 +4796,25 @@ mod test_fmt {
} }
#[test] #[test]
fn multiline_interface() { fn module_exposing() {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo module [Bar, Baz, a, b]"
exposes []
imports []"
)); ));
} }
#[test] #[test]
fn interface_exposing() { fn module_exposing_multiline() {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo module [
exposes [Bar, Baz, a, b] Stuff,
imports []" Things,
)); somethingElse,
} ]
#[test] import Blah
fn interface_importing() { import Baz exposing [stuff, things]"
module_formats_same(indoc!(
r"
interface Foo
exposes [Bar, Baz, a, b]
imports [Blah, Thing.{ foo, bar }, Stuff]"
));
}
#[test]
fn multi_line_interface() {
module_formats_same(indoc!(
r"
interface Foo
exposes [
Stuff,
Things,
somethingElse,
]
imports [
Blah,
Baz.{ stuff, things },
]"
)); ));
} }
@ -4866,9 +4844,7 @@ mod test_fmt {
&format!( &format!(
indoc!( indoc!(
r#" r#"
interface Foo module []
exposes []
imports []
# comment 1{space} # comment 1{space}
def = "" # comment 2{space} def = "" # comment 2{space}
@ -4879,9 +4855,7 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
interface Foo module []
exposes []
imports []
# comment 1 # comment 1
def = "" # comment 2 def = "" # comment 2
@ -5700,7 +5674,7 @@ mod test_fmt {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo exposes [] imports [] module []
expect x == y expect x == y
@ -5727,7 +5701,7 @@ mod test_fmt {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo exposes [] imports [] module []
expect expect
foo bar foo bar
@ -5831,7 +5805,7 @@ mod test_fmt {
fn ability_member_doc_comments() { fn ability_member_doc_comments() {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
interface Foo exposes [] imports [] module []
A implements A implements
## This is member ab ## This is member ab
@ -5850,9 +5824,7 @@ mod test_fmt {
module_formats_same(indoc!( module_formats_same(indoc!(
r" r"
# hello world # hello world
interface Foo module []
exposes []
imports []
" "
)); ));
@ -5876,6 +5848,17 @@ mod test_fmt {
)); ));
} }
#[test]
fn comments_before_exposes_preserved() {
module_formats_same(indoc!(
r"
module
# comment
[a, b]
"
));
}
#[test] #[test]
fn clauses_with_multiple_abilities() { fn clauses_with_multiple_abilities() {
expr_formats_same(indoc!( expr_formats_same(indoc!(

View File

@ -301,8 +301,8 @@ mod test_snapshots {
pass/def_without_newline.expr, pass/def_without_newline.expr,
pass/destructure_tag_assignment.expr, pass/destructure_tag_assignment.expr,
pass/empty_app_header.header, pass/empty_app_header.header,
pass/empty_module_header.header,
pass/empty_hosted_header.header, pass/empty_hosted_header.header,
pass/empty_interface_header.header,
pass/empty_list.expr, pass/empty_list.expr,
pass/empty_package_header.header, pass/empty_package_header.header,
pass/empty_platform_header.header, pass/empty_platform_header.header,
@ -333,7 +333,7 @@ mod test_snapshots {
pass/inline_import.expr, pass/inline_import.expr,
pass/inline_ingested_file.expr, pass/inline_ingested_file.expr,
pass/int_with_underscore.expr, pass/int_with_underscore.expr,
pass/interface_with_newline.header, pass/module_with_newline.header,
pass/lambda_in_chain.expr, pass/lambda_in_chain.expr,
pass/lambda_indent.expr, pass/lambda_indent.expr,
pass/list_closing_indent_not_enough.expr, pass/list_closing_indent_not_enough.expr,
@ -348,6 +348,7 @@ mod test_snapshots {
pass/minus_twelve_minus_five.expr, pass/minus_twelve_minus_five.expr,
pass/mixed_docs.expr, pass/mixed_docs.expr,
pass/module_def_newline.moduledefs, pass/module_def_newline.moduledefs,
pass/module_multiline_exposes.header,
pass/multi_backpassing.expr, pass/multi_backpassing.expr,
pass/multi_backpassing_in_def.moduledefs, pass/multi_backpassing_in_def.moduledefs,
pass/multi_backpassing_with_apply.expr, pass/multi_backpassing_with_apply.expr,
@ -369,7 +370,6 @@ mod test_snapshots {
pass/nested_def_annotation.moduledefs, pass/nested_def_annotation.moduledefs,
pass/nested_def_without_newline.expr, pass/nested_def_without_newline.expr,
pass/nested_if.expr, pass/nested_if.expr,
pass/nested_module.header,
pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51 pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51
pass/newline_after_mul.expr, pass/newline_after_mul.expr,
pass/newline_after_paren.expr, pass/newline_after_paren.expr,
@ -409,6 +409,7 @@ mod test_snapshots {
pass/outdented_colon_in_record.expr, pass/outdented_colon_in_record.expr,
pass/outdented_list.expr, pass/outdented_list.expr,
pass/outdented_record.expr, pass/outdented_record.expr,
pass/old_interface_header.header,
pass/packed_singleton_list.expr, pass/packed_singleton_list.expr,
pass/parens_in_type_def_apply.expr, pass/parens_in_type_def_apply.expr,
pass/parens_in_value_def_annotation.expr, pass/parens_in_value_def_annotation.expr,

View File

@ -11,9 +11,8 @@ use roc_parse::{
WhenBranch, WhenBranch,
}, },
header::{ header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, AppHeader, ExposedName, HostedHeader, ImportsEntry, ModuleHeader, ModuleName, PackageEntry,
PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To, TypedIdent,
TypedIdent,
}, },
ident::{Accessor, UppercaseIdent}, ident::{Accessor, UppercaseIdent},
}; };
@ -202,7 +201,7 @@ impl IterTokens for Module<'_> {
impl IterTokens for Header<'_> { impl IterTokens for Header<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
match self { match self {
Header::Interface(ih) => ih.iter_tokens(arena), Header::Module(mh) => mh.iter_tokens(arena),
Header::App(app) => app.iter_tokens(arena), Header::App(app) => app.iter_tokens(arena),
Header::Package(pkg) => pkg.iter_tokens(arena), Header::Package(pkg) => pkg.iter_tokens(arena),
Header::Platform(pf) => pf.iter_tokens(arena), Header::Platform(pf) => pf.iter_tokens(arena),
@ -211,19 +210,15 @@ impl IterTokens for Header<'_> {
} }
} }
impl IterTokens for InterfaceHeader<'_> { impl IterTokens for ModuleHeader<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
let Self { let Self {
before_name: _, before_exposes: _,
name,
exposes, exposes,
imports, interface_imports: _,
} = self; } = self;
(name.iter_tokens(arena).into_iter()) exposes.iter_tokens(arena)
.chain(exposes.item.iter_tokens(arena))
.chain(imports.item.iter_tokens(arena))
.collect_in(arena)
} }
} }

View File

@ -128,7 +128,7 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
// TODO use this when finding .roc files by discovering them from the root module. // TODO use this when finding .roc files by discovering them from the root module.
// let other_modules: &[Module<'_>] = // let other_modules: &[Module<'_>] =
match read_header(&arena, &mut buf, path)?.header { match read_header(&arena, &mut buf, path)?.header {
Header::Interface(_) => { Header::Module(_) => {
todo!(); todo!();
// TODO report error // TODO report error
} }

View File

@ -3621,7 +3621,7 @@ fn to_exposes_report<'a>(
"I was expecting a type name, value name or function name next, like", "I was expecting a type name, value name or function name next, like",
)]), )]),
alloc alloc
.parser_suggestion("exposes [Animal, default, tame]") .parser_suggestion("[Animal, default, tame]")
.indent(4), .indent(4),
]); ]);
@ -3646,7 +3646,7 @@ fn to_exposes_report<'a>(
alloc.reflow(" keyword next, like"), alloc.reflow(" keyword next, like"),
]), ]),
alloc alloc
.parser_suggestion("exposes [Animal, default, tame]") .parser_suggestion("[Animal, default, tame]")
.indent(4), .indent(4),
]); ]);

View File

@ -102,7 +102,7 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
- [Unisonweb](https://www.unisonweb.org), definition based [editor](https://twitter.com/shojberg/status/1364666092598288385) as opposed to file based. - [Unisonweb](https://www.unisonweb.org), definition based [editor](https://twitter.com/shojberg/status/1364666092598288385) as opposed to file based.
- [Utopia](https://utopia.app/) integrated design and development environment for React. Design and code update each other, in real time. - [Utopia](https://utopia.app/) integrated design and development environment for React. Design and code update each other, in real time.
- [Paredit](https://calva.io/paredit/) structural clojure editing, navigation and selection. [Another overview](http://danmidwood.com/content/2014/11/21/animated-paredit.html) - [Paredit](https://calva.io/paredit/) structural clojure editing, navigation and selection. [Another overview](http://danmidwood.com/content/2014/11/21/animated-paredit.html)
- [tylr](https://tylr.fun/) projectional editor UX that helps you make it easier to do edits that are typically difficult with projectional editors but are easy with classic editors. - [tylr](https://tylr.fun/) projectional editor UX that helps you make it easier to do edits that are typically difficult with projectional editors but are easy with classic editors.
### Project exploration ### Project exploration

View File

@ -1,13 +1,11 @@
interface Community module [
exposes [ Community,
Community, empty,
empty, addPerson,
addPerson, addFriend,
addFriend, Person,
Person, walkFriendNames,
walkFriendNames, ]
]
imports []
## Datatype representing a community for demonstration purposes in inspect-gui.roc and inspect-logging.roc ## Datatype representing a community for demonstration purposes in inspect-gui.roc and inspect-logging.roc

View File

@ -1,9 +1,7 @@
interface GuiFormatter module [
exposes [ GuiFormatter,
GuiFormatter, toGui,
toGui, ]
]
imports []
## Creates GUI representations of Roc values, for use in inspect-gui.roc ## Creates GUI representations of Roc values, for use in inspect-gui.roc

View File

@ -1,6 +1,8 @@
interface Context module [Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope]
exposes [Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope]
imports [pf.File, pf.Task.{ Task }, Variable.{ Variable }] import pf.File
import pf.Task exposing [Task]
import Variable exposing [Variable]
Option a : [Some a, None] Option a : [Some a, None]

View File

@ -1,6 +1,4 @@
interface Variable module [Variable, fromUtf8, toIndex, totalCount, toStr]
exposes [Variable, fromUtf8, toIndex, totalCount, toStr]
imports []
# Variables in False can only be single letters. Thus, the valid variables are "a" to "z". # Variables in False can only be single letters. Thus, the valid variables are "a" to "z".
# This opaque type deals with ensure we always have valid variables. # This opaque type deals with ensure we always have valid variables.

View File

@ -1,6 +1,7 @@
interface File module [line, Handle, withOpen, chunk]
exposes [line, Handle, withOpen, chunk]
imports [pf.Effect, Task.{ Task }] import pf.Effect
import Task exposing [Task]
Handle := U64 Handle := U64

View File

@ -1,6 +1,7 @@
interface Stdin module [char]
exposes [char]
imports [pf.Effect, Task] import pf.Effect
import Task
# line : Task.Task Str * # line : Task.Task Str *
# line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice # line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice

View File

@ -1,6 +1,7 @@
interface Stdout module [line, raw]
exposes [line, raw]
imports [pf.Effect, Task.{ Task }] import pf.Effect
import Task exposing [Task]
line : Str -> Task {} * line : Str -> Task {} *
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {}) line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})

View File

@ -1,6 +1,6 @@
interface Task module [Task, succeed, fail, await, map, onFail, attempt, fromResult, loop]
exposes [Task, succeed, fail, await, map, onFail, attempt, fromResult, loop]
imports [pf.Effect] import pf.Effect
Task ok err : Effect.Effect (Result ok err) Task ok err : Effect.Effect (Result ok err)

View File

@ -1,6 +1,4 @@
interface Program module [Program]
exposes [Program]
imports []
Program model : { Program model : {
init : {} -> model, init : {} -> model,

View File

@ -1,6 +1,4 @@
interface Action module [Action, none, update, map]
exposes [Action, none, update, map]
imports []
Action state : [None, Update state] Action state : [None, Update state]

View File

@ -1,6 +1,6 @@
interface Elem module [Elem, PressEvent, row, col, text, button, none, translate, list]
exposes [Elem, PressEvent, row, col, text, button, none, translate, list]
imports [Action.{ Action }] import Action exposing [Action]
Elem state : [ Elem state : [
# PERFORMANCE NOTE: # PERFORMANCE NOTE:

View File

@ -1,6 +1,4 @@
interface Game module [Bounds, Elem, Event]
exposes [Bounds, Elem, Event]
imports []
Rgba : { r : F32, g : F32, b : F32, a : F32 } Rgba : { r : F32, g : F32, b : F32, a : F32 }

View File

@ -1,6 +1,4 @@
interface Action module [Action, none, update, map]
exposes [Action, none, update, map]
imports []
Action state : [None, Update state] Action state : [None, Update state]

View File

@ -1,6 +1,6 @@
interface Elem module [Elem, PressEvent, row, col, text, button, none, translate, list]
exposes [Elem, PressEvent, row, col, text, button, none, translate, list]
imports [Action.{ Action }] import Action exposing [Action]
Elem state : [ Elem state : [
# PERFORMANCE NOTE: # PERFORMANCE NOTE:

View File

@ -1,128 +1,128 @@
interface Html module [
exposes [ Node,
Node, Attribute,
Attribute, render,
render, renderWithoutDocType,
renderWithoutDocType, element,
element, unclosedElem,
unclosedElem, text,
text, attribute,
attribute, html,
html, base,
base, head,
head, link,
link, meta,
meta, style,
style, title,
title, body,
body, address,
address, article,
article, aside,
aside, footer,
footer, header,
header, h1,
h1, h2,
h2, h3,
h3, h4,
h4, h5,
h5, h6,
h6, main,
main, nav,
nav, section,
section, blockquote,
blockquote, dd,
dd, div,
div, dl,
dl, dt,
dt, figcaption,
figcaption, figure,
figure, hr,
hr, li,
li, menu,
menu, ol,
ol, p,
p, pre,
pre, ul,
ul, a,
a, abbr,
abbr, b,
b, bdi,
bdi, bdo,
bdo, br,
br, cite,
cite, code,
code, data,
data, dfn,
dfn, em,
em, i,
i, kbd,
kbd, mark,
mark, q,
q, rp,
rp, rt,
rt, ruby,
ruby, s,
s, samp,
samp, small,
small, span,
span, strong,
strong, sub,
sub, sup,
sup, time,
time, u,
u, var,
var, wbr,
wbr, area,
area, audio,
audio, img,
img, map,
map, track,
track, video,
video, embed,
embed, iframe,
iframe, object,
object, picture,
picture, portal,
portal, source,
source, svg,
svg, math,
math, canvas,
canvas, noscript,
noscript, script,
script, del,
del, ins,
ins, caption,
caption, col,
col, colgroup,
colgroup, table,
table, tbody,
tbody, td,
td, tfoot,
tfoot, th,
th, thead,
thead, tr,
tr, button,
button, datalist,
datalist, fieldset,
fieldset, form,
form, input,
input, label,
label, legend,
legend, meter,
meter, optgroup,
optgroup, option,
option, output,
output, progress,
progress, select,
select, textarea,
textarea, details,
details, dialog,
dialog, summary,
summary, slot,
slot, template,
template, ]
]
imports [Html.Attributes] import Html.Attributes
Node : [ Node : [
Text Str, Text Str,

View File

@ -1,144 +1,142 @@
interface Html.Attributes module [
exposes [ Attribute,
Attribute, attribute,
attribute, accept,
accept, acceptCharset,
acceptCharset, accesskey,
accesskey, action,
action, align,
align, allow,
allow, alt,
alt, ariaLabel,
ariaLabel, ariaLabelledBy,
ariaLabelledBy, ariaHidden,
ariaHidden, async,
async, autocapitalize,
autocapitalize, autocomplete,
autocomplete, autofocus,
autofocus, autoplay,
autoplay, background,
background, bgcolor,
bgcolor, border,
border, buffered,
buffered, capture,
capture, challenge,
challenge, charset,
charset, checked,
checked, cite,
cite, class,
class, code,
code, codebase,
codebase, color,
color, cols,
cols, colspan,
colspan, content,
content, contenteditable,
contenteditable, contextmenu,
contextmenu, controls,
controls, coords,
coords, crossorigin,
crossorigin, csp,
csp, data,
data, dataAttr,
dataAttr, datetime,
datetime, decoding,
decoding, default,
default, defer,
defer, dir,
dir, dirname,
dirname, disabled,
disabled, download,
download, draggable,
draggable, enctype,
enctype, enterkeyhint,
enterkeyhint, for,
for, form,
form, formaction,
formaction, formenctype,
formenctype, formmethod,
formmethod, formnovalidate,
formnovalidate, formtarget,
formtarget, headers,
headers, height,
height, hidden,
hidden, high,
high, href,
href, hreflang,
hreflang, httpEquiv,
httpEquiv, icon,
icon, id,
id, importance,
importance, integrity,
integrity, intrinsicsize,
intrinsicsize, inputmode,
inputmode, ismap,
ismap, itemprop,
itemprop, keytype,
keytype, kind,
kind, label,
label, lang,
lang, language,
language, loading,
loading, list,
list, loop,
loop, low,
low, manifest,
manifest, max,
max, maxlength,
maxlength, minlength,
minlength, media,
media, method,
method, min,
min, multiple,
multiple, muted,
muted, name,
name, novalidate,
novalidate, open,
open, optimum,
optimum, pattern,
pattern, ping,
ping, placeholder,
placeholder, poster,
poster, preload,
preload, radiogroup,
radiogroup, readonly,
readonly, referrerpolicy,
referrerpolicy, rel,
rel, required,
required, reversed,
reversed, role,
role, rows,
rows, rowspan,
rowspan, sandbox,
sandbox, scope,
scope, scoped,
scoped, selected,
selected, shape,
shape, size,
size, sizes,
sizes, slot,
slot, span,
span, spellcheck,
spellcheck, src,
src, srcdoc,
srcdoc, srclang,
srclang, srcset,
srcset, start,
start, step,
step, style,
style, summary,
summary, tabindex,
tabindex, target,
target, title,
title, translate,
translate, type,
type, usemap,
usemap, value,
value, width,
width, wrap,
wrap, ]
]
imports []
Attribute : [Attribute Str Str] Attribute : [Attribute Str Str]

View File

@ -1,8 +1,6 @@
interface ExampleApp module [exampleApp, State]
exposes [exampleApp, State]
imports [ import pf.Html exposing [App, Html, html, head, body, div, text, h1]
pf.Html.{ App, Html, html, head, body, div, text, h1 },
]
State : { State : {
answer : U32, answer : U32,

View File

@ -1,6 +1,4 @@
interface Action module [Action, none, update, map]
exposes [Action, none, update, map]
imports []
Action state : [None, Update state] Action state : [None, Update state]

View File

@ -1,129 +1,130 @@
interface Html module [
exposes [ App,
App, Html,
Html, Attribute,
Attribute, renderStatic,
renderStatic, renderStaticWithoutDocType,
renderStaticWithoutDocType, translate,
translate, translateStatic,
translateStatic, text,
text, none,
none, html,
html, base,
base, head,
head, link,
link, meta,
meta, style,
style, title,
title, body,
body, address,
address, article,
article, aside,
aside, footer,
footer, header,
header, h1,
h1, h2,
h2, h3,
h3, h4,
h4, h5,
h5, h6,
h6, main,
main, nav,
nav, section,
section, blockquote,
blockquote, dd,
dd, div,
div, dl,
dl, dt,
dt, figcaption,
figcaption, figure,
figure, hr,
hr, li,
li, menu,
menu, ol,
ol, p,
p, pre,
pre, ul,
ul, a,
a, abbr,
abbr, b,
b, bdi,
bdi, bdo,
bdo, br,
br, cite,
cite, code,
code, data,
data, dfn,
dfn, em,
em, i,
i, kbd,
kbd, mark,
mark, q,
q, rp,
rp, rt,
rt, ruby,
ruby, s,
s, samp,
samp, small,
small, span,
span, strong,
strong, sub,
sub, sup,
sup, time,
time, u,
u, var,
var, wbr,
wbr, area,
area, audio,
audio, img,
img, map,
map, track,
track, video,
video, embed,
embed, iframe,
iframe, object,
object, picture,
picture, portal,
portal, source,
source, svg,
svg, math,
math, canvas,
canvas, noscript,
noscript, script,
script, del,
del, ins,
ins, caption,
caption, col,
col, colgroup,
colgroup, table,
table, tbody,
tbody, td,
td, tfoot,
tfoot, th,
th, thead,
thead, tr,
tr, button,
button, datalist,
datalist, fieldset,
fieldset, form,
form, input,
input, label,
label, legend,
legend, meter,
meter, optgroup,
optgroup, option,
option, output,
output, progress,
progress, select,
select, textarea,
textarea, details,
details, dialog,
dialog, summary,
summary, slot,
slot, template,
template, ]
]
imports [Html.Internal.Shared, Html.Internal.Server] import Html.Internal.Shared
import Html.Internal.Server
App state initData : Html.Internal.Shared.App state initData App state initData : Html.Internal.Shared.App state initData
Html state : Html.Internal.Shared.Html state Html state : Html.Internal.Shared.Html state

View File

@ -1,140 +1,140 @@
interface Html.Attributes module [
exposes [ attribute,
attribute, accept,
accept, acceptCharset,
acceptCharset, accesskey,
accesskey, action,
action, align,
align, allow,
allow, alt,
alt, async,
async, autocapitalize,
autocapitalize, autocomplete,
autocomplete, autofocus,
autofocus, autoplay,
autoplay, background,
background, bgcolor,
bgcolor, border,
border, buffered,
buffered, capture,
capture, challenge,
challenge, charset,
charset, checked,
checked, cite,
cite, class,
class, code,
code, codebase,
codebase, color,
color, cols,
cols, colspan,
colspan, content,
content, contenteditable,
contenteditable, contextmenu,
contextmenu, controls,
controls, coords,
coords, crossorigin,
crossorigin, csp,
csp, data,
data, # dataAttr, TODO
# dataAttr, TODO datetime,
datetime, decoding,
decoding, default,
default, defer,
defer, dir,
dir, dirname,
dirname, disabled,
disabled, download,
download, draggable,
draggable, enctype,
enctype, enterkeyhint,
enterkeyhint, for,
for, form,
form, formaction,
formaction, formenctype,
formenctype, formmethod,
formmethod, formnovalidate,
formnovalidate, formtarget,
formtarget, headers,
headers, height,
height, hidden,
hidden, high,
high, href,
href, hreflang,
hreflang, httpEquiv,
httpEquiv, icon,
icon, id,
id, importance,
importance, integrity,
integrity, intrinsicsize,
intrinsicsize, inputmode,
inputmode, ismap,
ismap, itemprop,
itemprop, keytype,
keytype, kind,
kind, label,
label, lang,
lang, language,
language, loading,
loading, list,
list, loop,
loop, low,
low, manifest,
manifest, max,
max, maxlength,
maxlength, minlength,
minlength, media,
media, method,
method, min,
min, multiple,
multiple, muted,
muted, name,
name, novalidate,
novalidate, open,
open, optimum,
optimum, pattern,
pattern, ping,
ping, placeholder,
placeholder, poster,
poster, preload,
preload, radiogroup,
radiogroup, readonly,
readonly, referrerpolicy,
referrerpolicy, rel,
rel, required,
required, reversed,
reversed, role,
role, rows,
rows, rowspan,
rowspan, sandbox,
sandbox, scope,
scope, scoped,
scoped, selected,
selected, shape,
shape, size,
size, sizes,
sizes, slot,
slot, span,
span, spellcheck,
spellcheck, src,
src, srcdoc,
srcdoc, srclang,
srclang, srcset,
srcset, start,
start, step,
step, style,
style, summary,
summary, tabindex,
tabindex, target,
target, title,
title, translate,
translate, type,
type, usemap,
usemap, value,
value, width,
width, wrap,
wrap, ]
]
imports [Html.Internal.Shared.{ Attribute }] import Html.Internal.Shared exposing [Attribute]
attribute : Str -> (Str -> Attribute state) attribute : Str -> (Str -> Attribute state)
attribute = \attrType -> attribute = \attrType ->

View File

@ -1,27 +1,25 @@
interface Html.Event module [
exposes [ Handler,
Handler, CyclicStructureAccessor,
CyclicStructureAccessor, on,
on, custom,
custom, onClick,
onClick, onDoubleClick,
onDoubleClick, onMouseDown,
onMouseDown, onMouseUp,
onMouseUp, onMouseEnter,
onMouseEnter, onMouseLeave,
onMouseLeave, onMouseOver,
onMouseOver, onMouseOut,
onMouseOut, onCheck,
onCheck, onBlur,
onBlur, onFocus,
onFocus, onInput,
onInput, onSubmit,
onSubmit, ]
]
imports [ import Action exposing [Action]
Action.{ Action }, import Html.Internal.Shared exposing [Attribute]
Html.Internal.Shared.{ Attribute },
]
Handler state : Html.Internal.Shared.Handler state Handler state : Html.Internal.Shared.Handler state
CyclicStructureAccessor : Html.Internal.Shared.CyclicStructureAccessor CyclicStructureAccessor : Html.Internal.Shared.CyclicStructureAccessor

View File

@ -1,29 +1,27 @@
interface Html.Internal.Client module [
exposes [ PlatformState,
PlatformState, initClientApp,
initClientApp, dispatchEvent,
dispatchEvent, ]
]
imports [ import Effect exposing [
Effect.{ Effect,
Effect, NodeId,
NodeId, HandlerId,
HandlerId, TagName,
TagName, AttrType,
AttrType, EventType,
EventType, ]
}, import Html.Internal.Shared exposing [
Html.Internal.Shared.{ App,
App, Html,
Html, Attribute,
Attribute, CyclicStructureAccessor,
CyclicStructureAccessor, Handler,
Handler, translateStatic,
translateStatic, ]
}, import TotallyNotJson
TotallyNotJson, import Action
Action,
]
PlatformState state initData : { PlatformState state initData : {
app : App state initData, app : App state initData,

View File

@ -1,12 +1,10 @@
interface Html.Internal.Server module [
exposes [ appendRenderedStatic,
appendRenderedStatic, initServerApp,
initServerApp, ]
]
imports [ import Html.Internal.Shared exposing [Html, Attribute, App, translateStatic, text, element]
Html.Internal.Shared.{ Html, Attribute, App, translateStatic, text, element }, import TotallyNotJson
TotallyNotJson,
]
# ------------------------------- # -------------------------------
# STATIC HTML # STATIC HTML

View File

@ -1,21 +1,19 @@
interface Html.Internal.Shared module [
exposes [ App,
App, Html,
Html, Attribute,
Attribute, CyclicStructureAccessor,
CyclicStructureAccessor, Handler,
Handler, Size,
Size, element,
element, text,
text, none,
none, translate,
translate, translateStatic,
translateStatic, nodeSize,
nodeSize, ]
]
imports [ import Action exposing [Action]
Action.{ Action },
]
App state initData : { App state initData : {
init : DecodingResult initData -> state, init : DecodingResult initData -> state,