mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into faster-str-eq
This commit is contained in:
commit
77d73f35eb
68 changed files with 1345 additions and 1098 deletions
|
@ -22,6 +22,7 @@ Earthly may temporarily use a lot of disk space, up to 90 GB. This disk space is
|
||||||
- Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
|
- Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
|
||||||
- It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review!
|
- It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review!
|
||||||
- Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security.
|
- Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security.
|
||||||
|
- You find good first issues [here](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
||||||
|
|
||||||
## Can we do better?
|
## Can we do better?
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ use roc_fmt::module::fmt_module;
|
||||||
use roc_fmt::Buf;
|
use roc_fmt::Buf;
|
||||||
use roc_module::called_via::{BinOp, UnaryOp};
|
use roc_module::called_via::{BinOp, UnaryOp};
|
||||||
use roc_parse::ast::{
|
use roc_parse::ast::{
|
||||||
AssignedField, Collection, Expr, Pattern, StrLiteral, StrSegment, Tag, TypeAnnotation,
|
AssignedField, Collection, Expr, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation,
|
||||||
WhenBranch,
|
WhenBranch,
|
||||||
};
|
};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||||
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
||||||
};
|
};
|
||||||
use roc_parse::{
|
use roc_parse::{
|
||||||
|
@ -228,16 +228,22 @@ impl<'a> RemoveSpaces<'a> for &'a str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for ExposesEntry<'a, T> {
|
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
match *self {
|
match *self {
|
||||||
ExposesEntry::Exposed(a) => ExposesEntry::Exposed(a.remove_spaces(arena)),
|
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
||||||
ExposesEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
|
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
ExposesEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
|
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
*self
|
*self
|
||||||
|
@ -261,18 +267,10 @@ impl<'a> RemoveSpaces<'a> for To<'a> {
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
match *self {
|
TypedIdent {
|
||||||
TypedIdent::Entry {
|
ident: self.ident.remove_spaces(arena),
|
||||||
ident,
|
spaces_before_colon: &[],
|
||||||
spaces_before_colon: _,
|
ann: self.ann.remove_spaces(arena),
|
||||||
ann,
|
|
||||||
} => TypedIdent::Entry {
|
|
||||||
ident: ident.remove_spaces(arena),
|
|
||||||
spaces_before_colon: &[],
|
|
||||||
ann: ann.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
TypedIdent::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
TypedIdent::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,29 +285,17 @@ impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PlatformRigid<'a> {
|
impl<'a> RemoveSpaces<'a> for PlatformRigid<'a> {
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
match *self {
|
*self
|
||||||
PlatformRigid::Entry { rigid, alias } => PlatformRigid::Entry { rigid, alias },
|
|
||||||
PlatformRigid::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
PlatformRigid::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
match *self {
|
PackageEntry {
|
||||||
PackageEntry::Entry {
|
shorthand: self.shorthand,
|
||||||
shorthand,
|
spaces_after_shorthand: &[],
|
||||||
spaces_after_shorthand: _,
|
package_or_path: self.package_or_path.remove_spaces(arena),
|
||||||
package_or_path,
|
|
||||||
} => PackageEntry::Entry {
|
|
||||||
shorthand,
|
|
||||||
spaces_after_shorthand: &[],
|
|
||||||
package_or_path: package_or_path.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
PackageEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
PackageEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,8 +314,6 @@ impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
||||||
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
||||||
ImportsEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
ImportsEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
4
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
|
@ -1,7 +1,7 @@
|
||||||
app "multi-dep-str"
|
app "multi-dep-str"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [ Dep1 ]
|
imports [ Dep1 ]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Str
|
main : Str
|
||||||
main = Dep1.str1
|
main = Dep1.str1
|
||||||
|
|
4
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
4
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
|
@ -1,7 +1,7 @@
|
||||||
app "multi-dep-thunk"
|
app "multi-dep-thunk"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [ Dep1 ]
|
imports [ Dep1 ]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Str
|
main : Str
|
||||||
main = Dep1.value1 {}
|
main = Dep1.value1 {}
|
||||||
|
|
10
cli_utils/Cargo.lock
generated
10
cli_utils/Cargo.lock
generated
|
@ -1310,13 +1310,13 @@ dependencies = [
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8)",
|
"inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f"
|
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"inkwell_internals",
|
"inkwell_internals",
|
||||||
|
@ -1324,13 +1324,12 @@ dependencies = [
|
||||||
"llvm-sys",
|
"llvm-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"regex",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell_internals"
|
name = "inkwell_internals"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f"
|
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2666,6 +2665,7 @@ dependencies = [
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
|
"roc_reporting",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
]
|
]
|
||||||
|
|
|
@ -16,7 +16,7 @@ const InPlace = enum(u8) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
||||||
const small_string_size = 2 * @sizeOf(usize);
|
const small_string_size = @sizeOf(RocStr);
|
||||||
const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size);
|
const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size);
|
||||||
|
|
||||||
fn init_blank_small_string(comptime n: usize) [n]u8 {
|
fn init_blank_small_string(comptime n: usize) [n]u8 {
|
||||||
|
@ -37,8 +37,9 @@ pub const RocStr = extern struct {
|
||||||
pub const alignment = @alignOf(usize);
|
pub const alignment = @alignOf(usize);
|
||||||
|
|
||||||
pub inline fn empty() RocStr {
|
pub inline fn empty() RocStr {
|
||||||
|
const small_str_flag: isize = std.math.minInt(isize);
|
||||||
return RocStr{
|
return RocStr{
|
||||||
.str_len = 0,
|
.str_len = @bitCast(usize, small_str_flag),
|
||||||
.str_bytes = null,
|
.str_bytes = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,7 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: RocStr) void {
|
pub fn deinit(self: RocStr) void {
|
||||||
if (!self.isSmallStr() and !self.isEmpty()) {
|
if (!self.isSmallStr()) {
|
||||||
utils.decref(self.str_bytes, self.str_len, RocStr.alignment);
|
utils.decref(self.str_bytes, self.str_len, RocStr.alignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +146,7 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone(in_place: InPlace, str: RocStr) RocStr {
|
pub fn clone(in_place: InPlace, str: RocStr) RocStr {
|
||||||
if (str.isSmallStr() or str.isEmpty()) {
|
if (str.isSmallStr()) {
|
||||||
// just return the bytes
|
// just return the bytes
|
||||||
return str;
|
return str;
|
||||||
} else {
|
} else {
|
||||||
|
@ -217,7 +218,8 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isEmpty(self: RocStr) bool {
|
pub fn isEmpty(self: RocStr) bool {
|
||||||
return (self.str_len << 1) == 0;
|
comptime const empty_len = RocStr.empty().str_len;
|
||||||
|
return self.str_len == empty_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a string happens to be null-terminated already, then we can pass its
|
// If a string happens to be null-terminated already, then we can pass its
|
||||||
|
@ -297,11 +299,6 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isUnique(self: RocStr) bool {
|
pub fn isUnique(self: RocStr) bool {
|
||||||
// the empty string is unique (in the sense that copying it will not leak memory)
|
|
||||||
if (self.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// small strings can be copied
|
// small strings can be copied
|
||||||
if (self.isSmallStr()) {
|
if (self.isSmallStr()) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -324,8 +321,8 @@ pub const RocStr = extern struct {
|
||||||
|
|
||||||
// Since this conditional would be prone to branch misprediction,
|
// Since this conditional would be prone to branch misprediction,
|
||||||
// make sure it will compile to a cmov.
|
// make sure it will compile to a cmov.
|
||||||
// return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
// return if (self.isSmallStr()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||||
if (self.isSmallStr() or self.isEmpty()) {
|
if (self.isSmallStr()) {
|
||||||
const as_int = @ptrToInt(&self);
|
const as_int = @ptrToInt(&self);
|
||||||
const as_ptr = @intToPtr([*]u8, as_int);
|
const as_ptr = @intToPtr([*]u8, as_int);
|
||||||
return as_ptr;
|
return as_ptr;
|
||||||
|
|
|
@ -446,9 +446,6 @@ drop : List elem, Nat -> List elem
|
||||||
## To replace the element at a given index, instead of dropping it, see [List.set].
|
## To replace the element at a given index, instead of dropping it, see [List.set].
|
||||||
dropAt : List elem, Nat -> List elem
|
dropAt : List elem, Nat -> List elem
|
||||||
|
|
||||||
## Drops the last element in a List.
|
|
||||||
dropLast : List elem -> List elem
|
|
||||||
|
|
||||||
## Adds a new element to the end of the list.
|
## Adds a new element to the end of the list.
|
||||||
##
|
##
|
||||||
## >>> List.append [ "a", "b" ] "c"
|
## >>> List.append [ "a", "b" ] "c"
|
||||||
|
@ -685,8 +682,6 @@ startsWith : List elem, List elem -> Bool
|
||||||
|
|
||||||
endsWith : List elem, List elem -> Bool
|
endsWith : List elem, List elem -> Bool
|
||||||
|
|
||||||
all : List elem, (elem -> Bool) -> Bool
|
|
||||||
|
|
||||||
## Run the given predicate on each element of the list, returning `True` if
|
## Run the given predicate on each element of the list, returning `True` if
|
||||||
## any of the elements satisfy it.
|
## any of the elements satisfy it.
|
||||||
any : List elem, (elem -> Bool) -> Bool
|
any : List elem, (elem -> Bool) -> Bool
|
||||||
|
@ -698,3 +693,11 @@ all : List elem, (elem -> Bool) -> Bool
|
||||||
## Returns the first element of the list satisfying a predicate function.
|
## Returns the first element of the list satisfying a predicate function.
|
||||||
## If no satisfying element is found, an `Err NotFound` is returned.
|
## If no satisfying element is found, an `Err NotFound` is returned.
|
||||||
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
||||||
|
|
||||||
|
## Apply a function that returns a Result on a list, only successful
|
||||||
|
## Results are kept and returned unwrapped.
|
||||||
|
keepOks : List before, (before -> Result after *) -> List after
|
||||||
|
|
||||||
|
## Apply a function that returns a Result on a list, only unsuccessful
|
||||||
|
## Results are kept and returned unwrapped.
|
||||||
|
keepErrs : List before, (before -> Result * after) -> List after
|
||||||
|
|
|
@ -621,13 +621,13 @@ toStr : Num * -> Str
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## In some countries (e.g. USA and UK), a comma is used to separate thousands:
|
## In some countries (e.g. USA and UK), a comma is used to separate thousands:
|
||||||
## >>> Num.format 1_000_000 { base: Decimal, wholeSep: { mark: ",", places: 3 } }
|
## >>> Num.format 1_000_000 { pf: Decimal, wholeSep: { mark: ",", places: 3 } }
|
||||||
##
|
##
|
||||||
## Sometimes when rendering bits, it's nice to group them into groups of 4:
|
## Sometimes when rendering bits, it's nice to group them into groups of 4:
|
||||||
## >>> Num.format 1_000_000 { base: Binary, wholeSep: { mark: " ", places: 4 } }
|
## >>> Num.format 1_000_000 { pf: Binary, wholeSep: { mark: " ", places: 4 } }
|
||||||
##
|
##
|
||||||
## It's also common to render hexadecimal in groups of 2:
|
## It's also common to render hexadecimal in groups of 2:
|
||||||
## >>> Num.format 1_000_000 { base: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
## >>> Num.format 1_000_000 { pf: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
||||||
format :
|
format :
|
||||||
Num *,
|
Num *,
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,8 @@ interface Result
|
||||||
exposes
|
exposes
|
||||||
[
|
[
|
||||||
Result,
|
Result,
|
||||||
|
isOk,
|
||||||
|
isErr,
|
||||||
map,
|
map,
|
||||||
mapErr,
|
mapErr,
|
||||||
withDefault,
|
withDefault,
|
||||||
|
@ -13,6 +15,16 @@ interface Result
|
||||||
## okay, or else there was an error of some sort.
|
## okay, or else there was an error of some sort.
|
||||||
Result ok err : [ @Result ok err ]
|
Result ok err : [ @Result ok err ]
|
||||||
|
|
||||||
|
## Return True if the result indicates a success, else return False
|
||||||
|
##
|
||||||
|
## >>> Result.isOk (Ok 5)
|
||||||
|
isOk : Result * * -> bool
|
||||||
|
|
||||||
|
## Return True if the result indicates a failure, else return False
|
||||||
|
##
|
||||||
|
## >>> Result.isErr (Err "uh oh")
|
||||||
|
isErr : Result * * -> bool
|
||||||
|
|
||||||
## If the result is `Ok`, return the value it holds. Otherwise, return
|
## If the result is `Ok`, return the value it holds. Otherwise, return
|
||||||
## the given default value.
|
## the given default value.
|
||||||
##
|
##
|
||||||
|
|
|
@ -29,6 +29,8 @@ isEmpty : Set * -> Bool
|
||||||
|
|
||||||
len : Set * -> Nat
|
len : Set * -> Nat
|
||||||
|
|
||||||
|
## Modify
|
||||||
|
|
||||||
# TODO: removed `'` from signature because parser does not support it yet
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
|
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
|
||||||
## Make sure never to add a *NaN* to a [Set]! Because *NaN* is defined to be
|
## Make sure never to add a *NaN* to a [Set]! Because *NaN* is defined to be
|
||||||
|
@ -41,6 +43,8 @@ add : Set elem, elem -> Set elem
|
||||||
# Original signature: `drop : Set 'elem, 'elem -> Set 'elem`
|
# Original signature: `drop : Set 'elem, 'elem -> Set 'elem`
|
||||||
drop : Set elem, elem -> Set elem
|
drop : Set elem, elem -> Set elem
|
||||||
|
|
||||||
|
## Transform
|
||||||
|
|
||||||
## Convert each element in the set to something new, by calling a conversion
|
## Convert each element in the set to something new, by calling a conversion
|
||||||
## function on each of them. Then return a new set of the converted values.
|
## function on each of them. Then return a new set of the converted values.
|
||||||
##
|
##
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::def::Def;
|
use crate::def::Def;
|
||||||
use crate::expr::{ClosureData, Expr::*};
|
use crate::expr::{self, ClosureData, Expr::*};
|
||||||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
use crate::expr::{Expr, Field, Recursive};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::SendMap;
|
||||||
use roc_module::called_via::CalledVia;
|
use roc_module::called_via::CalledVia;
|
||||||
|
@ -3091,7 +3091,7 @@ fn list_keep_errs(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListKeepErrs, var_store)
|
lowlevel_2(symbol, LowLevel::ListKeepErrs, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.keepErrs: List before, (before -> Result * after) -> List after
|
/// List.range: Int a, Int a -> List (Int a)
|
||||||
fn list_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListRange, var_store)
|
lowlevel_2(symbol, LowLevel::ListRange, var_store)
|
||||||
}
|
}
|
||||||
|
@ -4107,7 +4107,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4137,7 +4137,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4204,7 +4204,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4234,7 +4234,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4277,7 +4277,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![(ret_var, no_region(Pattern::Identifier(Symbol::ARG_3)))],
|
arguments: vec![(ret_var, no_region(Pattern::Identifier(Symbol::ARG_3)))],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(Var(Symbol::ARG_3)),
|
value: no_region(Var(Symbol::ARG_3)),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4297,7 +4297,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![(var_store.fresh(), no_region(Pattern::Underscore))],
|
arguments: vec![(var_store.fresh(), no_region(Pattern::Underscore))],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(Var(Symbol::ARG_2)),
|
value: no_region(Var(Symbol::ARG_2)),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4347,7 +4347,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(false_expr),
|
value: no_region(false_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4374,7 +4374,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(true_expr),
|
value: no_region(true_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4424,7 +4424,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(true_expr),
|
value: no_region(true_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4451,7 +4451,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(false_expr),
|
value: no_region(false_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4513,7 +4513,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
@ -4543,7 +4543,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)],
|
)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch = WhenBranch {
|
let branch = expr::WhenBranch {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
|
|
@ -40,12 +40,12 @@ pub enum Newlines {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Formattable<'a> {
|
pub trait Formattable {
|
||||||
fn is_multiline(&self) -> bool;
|
fn is_multiline(&self) -> bool;
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
_parens: Parens,
|
_parens: Parens,
|
||||||
_newlines: Newlines,
|
_newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -53,23 +53,23 @@ pub trait Formattable<'a> {
|
||||||
self.format(buf, indent);
|
self.format(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to a formattable value is also formattable
|
/// A reference to a formattable value is also formattable
|
||||||
impl<'a, T> Formattable<'a> for &'a T
|
impl<'a, T> Formattable for &'a T
|
||||||
where
|
where
|
||||||
T: Formattable<'a>,
|
T: Formattable,
|
||||||
{
|
{
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
(*self).is_multiline()
|
(*self).is_multiline()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -77,23 +77,23 @@ where
|
||||||
(*self).format_with_options(buf, parens, newlines, indent)
|
(*self).format_with_options(buf, parens, newlines, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
(*self).format(buf, indent)
|
(*self).format(buf, indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Located formattable value is also formattable
|
/// A Located formattable value is also formattable
|
||||||
impl<'a, T> Formattable<'a> for Located<T>
|
impl<T> Formattable for Located<T>
|
||||||
where
|
where
|
||||||
T: Formattable<'a>,
|
T: Formattable,
|
||||||
{
|
{
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
self.value.is_multiline()
|
self.value.is_multiline()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -102,12 +102,12 @@ where
|
||||||
.format_with_options(buf, parens, newlines, indent)
|
.format_with_options(buf, parens, newlines, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
self.value.format(buf, indent)
|
self.value.format(buf, indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use roc_parse::ast::TypeAnnotation::*;
|
use roc_parse::ast::TypeAnnotation::*;
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -275,14 +275,14 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||||
/// > term: { x: 100, y: True }
|
/// > term: { x: 100, y: True }
|
||||||
///
|
///
|
||||||
/// So we need two instances, each having the specific separator
|
/// So we need two instances, each having the specific separator
|
||||||
impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> {
|
impl<'a> Formattable for AssignedField<'a, TypeAnnotation<'a>> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
is_multiline_assigned_field_help(self)
|
is_multiline_assigned_field_help(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -292,14 +292,14 @@ impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> {
|
impl<'a> Formattable for AssignedField<'a, Expr<'a>> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
is_multiline_assigned_field_help(self)
|
is_multiline_assigned_field_help(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -309,7 +309,7 @@ impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_multiline_assigned_field_help<'a, T: Formattable<'a>>(afield: &AssignedField<'a, T>) -> bool {
|
fn is_multiline_assigned_field_help<T: Formattable>(afield: &AssignedField<'_, T>) -> bool {
|
||||||
use self::AssignedField::*;
|
use self::AssignedField::*;
|
||||||
|
|
||||||
match afield {
|
match afield {
|
||||||
|
@ -322,15 +322,15 @@ fn is_multiline_assigned_field_help<'a, T: Formattable<'a>>(afield: &AssignedFie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_assigned_field_help<'a, T>(
|
fn format_assigned_field_help<'a, 'buf, T>(
|
||||||
zelf: &AssignedField<'a, T>,
|
zelf: &AssignedField<'a, T>,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
separator_prefix: &str,
|
separator_prefix: &str,
|
||||||
is_multiline: bool,
|
is_multiline: bool,
|
||||||
) where
|
) where
|
||||||
T: Formattable<'a>,
|
T: Formattable,
|
||||||
{
|
{
|
||||||
use self::AssignedField::*;
|
use self::AssignedField::*;
|
||||||
|
|
||||||
|
@ -403,7 +403,7 @@ fn format_assigned_field_help<'a, T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for Tag<'a> {
|
impl<'a> Formattable for Tag<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use self::Tag::*;
|
use self::Tag::*;
|
||||||
|
|
||||||
|
@ -416,9 +416,9 @@ impl<'a> Formattable<'a> for Tag<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
_parens: Parens,
|
_parens: Parens,
|
||||||
_newlines: Newlines,
|
_newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
|
|
@ -6,15 +6,15 @@ use crate::{
|
||||||
Buf,
|
Buf,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fmt_collection<'a, 'b, T: ExtractSpaces<'a> + Formattable<'b>>(
|
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||||
buf: &mut Buf<'b>,
|
buf: &mut Buf<'buf>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
start: char,
|
start: char,
|
||||||
end: char,
|
end: char,
|
||||||
items: Collection<'a, T>,
|
items: Collection<'a, T>,
|
||||||
newline: Newlines,
|
newline: Newlines,
|
||||||
) where
|
) where
|
||||||
<T as ExtractSpaces<'a>>::Item: Formattable<'b>,
|
<T as ExtractSpaces<'a>>::Item: Formattable,
|
||||||
{
|
{
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
let is_multiline =
|
let is_multiline =
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_parse::ast::{Def, Expr, Pattern};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
|
||||||
/// A Located formattable value is also formattable
|
/// A Located formattable value is also formattable
|
||||||
impl<'a> Formattable<'a> for Def<'a> {
|
impl<'a> Formattable for Def<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use roc_parse::ast::Def::*;
|
use roc_parse::ast::Def::*;
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ impl<'a> Formattable<'a> for Def<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
_parens: Parens,
|
_parens: Parens,
|
||||||
_newlines: Newlines,
|
_newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -107,8 +107,8 @@ impl<'a> Formattable<'a> for Def<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_expect<'a>(
|
fn fmt_expect<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
condition: &'a Located<Expr<'a>>,
|
condition: &'a Located<Expr<'a>>,
|
||||||
is_multiline: bool,
|
is_multiline: bool,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -123,11 +123,16 @@ fn fmt_expect<'a>(
|
||||||
condition.format(buf, return_indent);
|
condition.format(buf, return_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_def<'a>(buf: &mut Buf<'a>, def: &Def<'a>, indent: u16) {
|
pub fn fmt_def<'a, 'buf>(buf: &mut Buf<'buf>, def: &Def<'a>, indent: u16) {
|
||||||
def.format(buf, indent);
|
def.format(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_body<'a>(buf: &mut Buf<'a>, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
|
pub fn fmt_body<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
pattern: &'a Pattern<'a>,
|
||||||
|
body: &'a Expr<'a>,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||||
buf.push_str(" =");
|
buf.push_str(" =");
|
||||||
if body.is_multiline() {
|
if body.is_multiline() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use roc_parse::ast::{
|
||||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for Expr<'a> {
|
impl<'a> Formattable for Expr<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use roc_parse::ast::Expr::*;
|
use roc_parse::ast::Expr::*;
|
||||||
// TODO cache these answers using a Map<Pointer, bool>, so
|
// TODO cache these answers using a Map<Pointer, bool>, so
|
||||||
|
@ -106,9 +106,9 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -289,7 +289,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut Buf<'a>, indent: u16) {
|
fn format_str_segment<'a, 'buf>(seg: &StrSegment<'a>, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
use StrSegment::*;
|
use StrSegment::*;
|
||||||
|
|
||||||
match seg {
|
match seg {
|
||||||
|
@ -344,7 +344,7 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u16) {
|
pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u16) {
|
||||||
use roc_parse::ast::StrLiteral::*;
|
use roc_parse::ast::StrLiteral::*;
|
||||||
|
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
@ -395,8 +395,8 @@ pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u
|
||||||
buf.push('"');
|
buf.push('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_bin_ops<'a>(
|
fn fmt_bin_ops<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
|
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
|
||||||
loc_right_side: &'a Located<Expr<'a>>,
|
loc_right_side: &'a Located<Expr<'a>>,
|
||||||
part_of_multi_line_bin_ops: bool,
|
part_of_multi_line_bin_ops: bool,
|
||||||
|
@ -454,8 +454,8 @@ fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_when<'a>(
|
fn fmt_when<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
loc_condition: &'a Located<Expr<'a>>,
|
loc_condition: &'a Located<Expr<'a>>,
|
||||||
branches: &[&'a WhenBranch<'a>],
|
branches: &[&'a WhenBranch<'a>],
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -569,8 +569,8 @@ fn fmt_when<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_expect<'a>(
|
fn fmt_expect<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
condition: &'a Located<Expr<'a>>,
|
condition: &'a Located<Expr<'a>>,
|
||||||
continuation: &'a Located<Expr<'a>>,
|
continuation: &'a Located<Expr<'a>>,
|
||||||
is_multiline: bool,
|
is_multiline: bool,
|
||||||
|
@ -588,8 +588,8 @@ fn fmt_expect<'a>(
|
||||||
continuation.format(buf, return_indent);
|
continuation.format(buf, return_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_if<'a>(
|
fn fmt_if<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
||||||
final_else: &'a Located<Expr<'a>>,
|
final_else: &'a Located<Expr<'a>>,
|
||||||
is_multiline: bool,
|
is_multiline: bool,
|
||||||
|
@ -709,8 +709,8 @@ fn fmt_if<'a>(
|
||||||
final_else.format(buf, return_indent);
|
final_else.format(buf, return_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_closure<'a>(
|
fn fmt_closure<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||||
loc_ret: &'a Located<Expr<'a>>,
|
loc_ret: &'a Located<Expr<'a>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -782,8 +782,8 @@ fn fmt_closure<'a>(
|
||||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_backpassing<'a>(
|
fn fmt_backpassing<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||||
loc_body: &'a Located<Expr<'a>>,
|
loc_body: &'a Located<Expr<'a>>,
|
||||||
loc_ret: &'a Located<Expr<'a>>,
|
loc_ret: &'a Located<Expr<'a>>,
|
||||||
|
@ -876,8 +876,8 @@ fn pattern_needs_parens_when_backpassing(pat: &Pattern) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_record<'a>(
|
fn fmt_record<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
update: Option<&'a Located<Expr<'a>>>,
|
update: Option<&'a Located<Expr<'a>>>,
|
||||||
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
|
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -945,13 +945,13 @@ fn fmt_record<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_field_multiline<'a, T>(
|
fn format_field_multiline<'a, 'buf, T>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
field: &AssignedField<'a, T>,
|
field: &AssignedField<'a, T>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
separator_prefix: &str,
|
separator_prefix: &str,
|
||||||
) where
|
) where
|
||||||
T: Formattable<'a>,
|
T: Formattable,
|
||||||
{
|
{
|
||||||
use self::AssignedField::*;
|
use self::AssignedField::*;
|
||||||
match field {
|
match field {
|
||||||
|
|
|
@ -3,14 +3,14 @@ use crate::collection::fmt_collection;
|
||||||
use crate::expr::fmt_str_literal;
|
use crate::expr::fmt_str_literal;
|
||||||
use crate::spaces::{fmt_default_spaces, fmt_spaces, INDENT};
|
use crate::spaces::{fmt_default_spaces, fmt_spaces, INDENT};
|
||||||
use crate::Buf;
|
use crate::Buf;
|
||||||
use roc_parse::ast::{Collection, Module};
|
use roc_parse::ast::{Collection, Module, Spaced};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||||
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
||||||
};
|
};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
|
||||||
pub fn fmt_module<'a>(buf: &mut Buf<'a>, module: &'a Module<'a>) {
|
pub fn fmt_module<'a, 'buf>(buf: &mut Buf<'buf>, module: &'a Module<'a>) {
|
||||||
match module {
|
match module {
|
||||||
Module::Interface { header } => {
|
Module::Interface { header } => {
|
||||||
fmt_interface_header(buf, header);
|
fmt_interface_header(buf, header);
|
||||||
|
@ -24,7 +24,7 @@ pub fn fmt_module<'a>(buf: &mut Buf<'a>, module: &'a Module<'a>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_interface_header<'a>(buf: &mut Buf<'a>, header: &'a InterfaceHeader<'a>) {
|
pub fn fmt_interface_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a InterfaceHeader<'a>) {
|
||||||
let indent = INDENT;
|
let indent = INDENT;
|
||||||
|
|
||||||
buf.indent(0);
|
buf.indent(0);
|
||||||
|
@ -49,7 +49,7 @@ pub fn fmt_interface_header<'a>(buf: &mut Buf<'a>, header: &'a InterfaceHeader<'
|
||||||
fmt_imports(buf, header.imports, indent);
|
fmt_imports(buf, header.imports, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_app_header<'a>(buf: &mut Buf<'a>, header: &'a AppHeader<'a>) {
|
pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>) {
|
||||||
let indent = INDENT;
|
let indent = INDENT;
|
||||||
buf.indent(0);
|
buf.indent(0);
|
||||||
buf.push_str("app");
|
buf.push_str("app");
|
||||||
|
@ -84,7 +84,7 @@ pub fn fmt_app_header<'a>(buf: &mut Buf<'a>, header: &'a AppHeader<'a>) {
|
||||||
fmt_to(buf, header.to.value, indent);
|
fmt_to(buf, header.to.value, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_platform_header<'a>(buf: &mut Buf<'a>, header: &'a PlatformHeader<'a>) {
|
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
|
||||||
let indent = INDENT;
|
let indent = INDENT;
|
||||||
|
|
||||||
buf.indent(0);
|
buf.indent(0);
|
||||||
|
@ -131,15 +131,15 @@ pub fn fmt_platform_header<'a>(buf: &mut Buf<'a>, header: &'a PlatformHeader<'a>
|
||||||
fmt_effects(buf, &header.effects, indent);
|
fmt_effects(buf, &header.effects, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_requires<'a>(buf: &mut Buf<'a>, requires: &PlatformRequires<'a>, indent: u16) {
|
fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>, indent: u16) {
|
||||||
fmt_collection(buf, indent, '{', '}', requires.rigids, Newlines::No);
|
fmt_collection(buf, indent, '{', '}', requires.rigids, Newlines::No);
|
||||||
|
|
||||||
buf.push_str(" { ");
|
buf.push_str(" { ");
|
||||||
fmt_typed_ident(buf, &requires.signature.value, indent);
|
requires.signature.value.format(buf, indent);
|
||||||
buf.push_str(" }");
|
buf.push_str(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_effects<'a>(buf: &mut Buf<'a>, effects: &Effects<'a>, indent: u16) {
|
fn fmt_effects<'a, 'buf>(buf: &mut Buf<'buf>, effects: &Effects<'a>, indent: u16) {
|
||||||
fmt_default_spaces(buf, effects.spaces_before_effects_keyword, " ", indent);
|
fmt_default_spaces(buf, effects.spaces_before_effects_keyword, " ", indent);
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str("effects");
|
buf.push_str("effects");
|
||||||
|
@ -155,95 +155,84 @@ fn fmt_effects<'a>(buf: &mut Buf<'a>, effects: &Effects<'a>, indent: u16) {
|
||||||
fmt_collection(buf, indent, '{', '}', effects.entries, Newlines::No)
|
fmt_collection(buf, indent, '{', '}', effects.entries, Newlines::No)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_typed_ident<'a>(buf: &mut Buf<'a>, entry: &TypedIdent<'a>, indent: u16) {
|
impl<'a> Formattable for TypedIdent<'a> {
|
||||||
use TypedIdent::*;
|
|
||||||
match entry {
|
|
||||||
Entry {
|
|
||||||
ident,
|
|
||||||
spaces_before_colon,
|
|
||||||
ann,
|
|
||||||
} => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str(ident.value);
|
|
||||||
fmt_default_spaces(buf, spaces_before_colon, " ", indent);
|
|
||||||
buf.push_str(": ");
|
|
||||||
ann.value.format(buf, indent);
|
|
||||||
}
|
|
||||||
SpaceBefore(sub_entry, spaces) => {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
fmt_typed_ident(buf, sub_entry, indent);
|
|
||||||
}
|
|
||||||
SpaceAfter(sub_entry, spaces) => {
|
|
||||||
fmt_typed_ident(buf, sub_entry, indent);
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for TypedIdent<'a> {
|
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
fmt_typed_ident(buf, self, indent);
|
buf.indent(indent);
|
||||||
|
buf.push_str(self.ident.value);
|
||||||
|
fmt_default_spaces(buf, self.spaces_before_colon, " ", indent);
|
||||||
|
buf.push_str(": ");
|
||||||
|
self.ann.value.format(buf, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for PlatformRigid<'a> {
|
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackageName) {
|
||||||
fn is_multiline(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
|
||||||
fmt_platform_rigid(buf, self, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_package_name<'a>(buf: &mut Buf<'a>, name: PackageName) {
|
|
||||||
buf.push_str(name.account);
|
buf.push_str(name.account);
|
||||||
buf.push('/');
|
buf.push('/');
|
||||||
buf.push_str(name.pkg);
|
buf.push_str(name.pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_platform_rigid<'a>(buf: &mut Buf<'a>, entry: &PlatformRigid<'a>, indent: u16) {
|
impl<'a, T: Formattable> Formattable for Spaced<'a, T> {
|
||||||
use roc_parse::header::PlatformRigid::*;
|
fn is_multiline(&self) -> bool {
|
||||||
|
// TODO
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
match entry {
|
fn format_with_options<'buf>(
|
||||||
Entry { rigid, alias } => {
|
&self,
|
||||||
buf.push_str(rigid);
|
buf: &mut Buf<'buf>,
|
||||||
buf.push_str("=>");
|
parens: crate::annotation::Parens,
|
||||||
buf.push_str(alias);
|
newlines: Newlines,
|
||||||
}
|
indent: u16,
|
||||||
|
) {
|
||||||
SpaceBefore(sub_entry, spaces) => {
|
match self {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
Spaced::Item(item) => {
|
||||||
fmt_platform_rigid(buf, sub_entry, indent);
|
item.format_with_options(buf, parens, newlines, indent);
|
||||||
}
|
}
|
||||||
SpaceAfter(sub_entry, spaces) => {
|
Spaced::SpaceBefore(item, spaces) => {
|
||||||
fmt_platform_rigid(buf, sub_entry, indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
item.format_with_options(buf, parens, newlines, indent);
|
||||||
|
}
|
||||||
|
Spaced::SpaceAfter(item, spaces) => {
|
||||||
|
item.format_with_options(buf, parens, newlines, indent);
|
||||||
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_imports<'a>(
|
impl<'a> Formattable for PlatformRigid<'a> {
|
||||||
buf: &mut Buf<'a>,
|
fn is_multiline(&self) -> bool {
|
||||||
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||||
|
buf.push_str(self.rigid);
|
||||||
|
buf.push_str("=>");
|
||||||
|
buf.push_str(self.alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_imports<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
loc_entries: Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_provides<'a>(
|
fn fmt_provides<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
loc_entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
loc_entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_to<'a>(buf: &mut Buf<'a>, to: To<'a>, indent: u16) {
|
fn fmt_to<'buf>(buf: &mut Buf<'buf>, to: To, indent: u16) {
|
||||||
match to {
|
match to {
|
||||||
To::ExistingPackage(name) => {
|
To::ExistingPackage(name) => {
|
||||||
buf.push_str(name);
|
buf.push_str(name);
|
||||||
|
@ -252,113 +241,95 @@ fn fmt_to<'a>(buf: &mut Buf<'a>, to: To<'a>, indent: u16) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_exposes<'a, N: FormatName + Copy + 'a>(
|
fn fmt_exposes<'buf, N: Formattable + Copy>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
loc_entries: Collection<'_, Located<ExposesEntry<'_, N>>>,
|
loc_entries: Collection<'_, Located<Spaced<'_, N>>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, N: FormatName> Formattable<'a> for ExposesEntry<'b, N> {
|
|
||||||
fn is_multiline(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
|
||||||
fmt_exposes_entry(buf, self, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FormatName {
|
pub trait FormatName {
|
||||||
fn format<'a>(&self, buf: &mut Buf<'a>);
|
fn format<'buf>(&self, buf: &mut Buf<'buf>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatName for &'a str {
|
impl<'a> FormatName for &'a str {
|
||||||
fn format<'b>(&self, buf: &mut Buf<'b>) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||||
buf.push_str(self)
|
buf.push_str(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatName for ModuleName<'a> {
|
impl<'a> FormatName for ModuleName<'a> {
|
||||||
fn format<'b>(&self, buf: &mut Buf<'b>) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||||
buf.push_str(self.as_str());
|
buf.push_str(self.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_exposes_entry<'a, 'b, N: FormatName>(
|
impl<'a> Formattable for ModuleName<'a> {
|
||||||
buf: &mut Buf<'a>,
|
fn is_multiline(&self) -> bool {
|
||||||
entry: &ExposesEntry<'b, N>,
|
false
|
||||||
indent: u16,
|
}
|
||||||
) {
|
|
||||||
use roc_parse::header::ExposesEntry::*;
|
|
||||||
|
|
||||||
match entry {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||||
Exposed(ident) => ident.format(buf),
|
buf.push_str(self.as_str());
|
||||||
|
|
||||||
SpaceBefore(sub_entry, spaces) => {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
fmt_exposes_entry(buf, sub_entry, indent);
|
|
||||||
}
|
|
||||||
SpaceAfter(sub_entry, spaces) => {
|
|
||||||
fmt_exposes_entry(buf, sub_entry, indent);
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_packages<'a>(
|
impl<'a> Formattable for ExposedName<'a> {
|
||||||
buf: &mut Buf<'a>,
|
fn is_multiline(&self) -> bool {
|
||||||
loc_entries: Collection<'a, Located<PackageEntry<'a>>>,
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||||
|
buf.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FormatName for ExposedName<'a> {
|
||||||
|
fn format<'buf>(&self, buf: &mut Buf<'buf>) {
|
||||||
|
buf.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_packages<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
loc_entries: Collection<'a, Located<Spaced<'a, PackageEntry<'a>>>>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
fmt_collection(buf, indent, '{', '}', loc_entries, Newlines::No)
|
fmt_collection(buf, indent, '{', '}', loc_entries, Newlines::No)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for PackageEntry<'a> {
|
impl<'a> Formattable for PackageEntry<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
fmt_packages_entry(buf, self, indent);
|
fmt_packages_entry(buf, self, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for ImportsEntry<'a> {
|
impl<'a> Formattable for ImportsEntry<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||||
fmt_imports_entry(buf, self, indent);
|
fmt_imports_entry(buf, self, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn fmt_packages_entry<'a>(buf: &mut Buf<'a>, entry: &PackageEntry<'a>, indent: u16) {
|
fn fmt_packages_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &PackageEntry<'a>, indent: u16) {
|
||||||
use PackageEntry::*;
|
buf.push_str(entry.shorthand);
|
||||||
match entry {
|
buf.push(':');
|
||||||
Entry {
|
fmt_default_spaces(buf, entry.spaces_after_shorthand, " ", indent);
|
||||||
shorthand,
|
fmt_package_or_path(buf, &entry.package_or_path.value, indent);
|
||||||
spaces_after_shorthand,
|
|
||||||
package_or_path,
|
|
||||||
} => {
|
|
||||||
buf.push_str(shorthand);
|
|
||||||
buf.push(':');
|
|
||||||
fmt_default_spaces(buf, spaces_after_shorthand, " ", indent);
|
|
||||||
fmt_package_or_path(buf, &package_or_path.value, indent);
|
|
||||||
}
|
|
||||||
SpaceBefore(sub_entry, spaces) => {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
fmt_packages_entry(buf, sub_entry, indent);
|
|
||||||
}
|
|
||||||
SpaceAfter(sub_entry, spaces) => {
|
|
||||||
fmt_packages_entry(buf, sub_entry, indent);
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_package_or_path<'a>(buf: &mut Buf<'a>, package_or_path: &PackageOrPath<'a>, indent: u16) {
|
fn fmt_package_or_path<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
package_or_path: &PackageOrPath<'a>,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
match package_or_path {
|
match package_or_path {
|
||||||
PackageOrPath::Package(_name, _version) => {
|
PackageOrPath::Package(_name, _version) => {
|
||||||
todo!("format package");
|
todo!("format package");
|
||||||
|
@ -367,7 +338,7 @@ fn fmt_package_or_path<'a>(buf: &mut Buf<'a>, package_or_path: &PackageOrPath<'a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_imports_entry<'a>(buf: &mut Buf<'a>, entry: &ImportsEntry<'a>, indent: u16) {
|
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
||||||
use roc_parse::header::ImportsEntry::*;
|
use roc_parse::header::ImportsEntry::*;
|
||||||
|
|
||||||
match entry {
|
match entry {
|
||||||
|
@ -392,14 +363,5 @@ fn fmt_imports_entry<'a>(buf: &mut Buf<'a>, entry: &ImportsEntry<'a>, indent: u1
|
||||||
fmt_collection(buf, indent, '{', '}', *entries, Newlines::No)
|
fmt_collection(buf, indent, '{', '}', *entries, Newlines::No)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceBefore(sub_entry, spaces) => {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
fmt_imports_entry(buf, sub_entry, indent);
|
|
||||||
}
|
|
||||||
SpaceAfter(sub_entry, spaces) => {
|
|
||||||
fmt_imports_entry(buf, sub_entry, indent);
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,16 @@ use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt};
|
||||||
use crate::Buf;
|
use crate::Buf;
|
||||||
use roc_parse::ast::{Base, Pattern};
|
use roc_parse::ast::{Base, Pattern};
|
||||||
|
|
||||||
pub fn fmt_pattern<'a>(buf: &mut Buf<'a>, pattern: &'a Pattern<'a>, indent: u16, parens: Parens) {
|
pub fn fmt_pattern<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
pattern: &'a Pattern<'a>,
|
||||||
|
indent: u16,
|
||||||
|
parens: Parens,
|
||||||
|
) {
|
||||||
pattern.format_with_options(buf, parens, Newlines::No, indent);
|
pattern.format_with_options(buf, parens, Newlines::No, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formattable<'a> for Pattern<'a> {
|
impl<'a> Formattable for Pattern<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
// Theory: a pattern should only be multiline when it contains a comment
|
// Theory: a pattern should only be multiline when it contains a comment
|
||||||
match self {
|
match self {
|
||||||
|
@ -37,9 +42,9 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(
|
fn format_with_options<'buf>(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
|
|
@ -18,8 +18,8 @@ pub fn add_spaces(buf: &mut String<'_>, spaces: u16) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_default_spaces<'a>(
|
pub fn fmt_default_spaces<'a, 'buf>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
spaces: &[CommentOrNewline<'a>],
|
spaces: &[CommentOrNewline<'a>],
|
||||||
default: &str,
|
default: &str,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
|
@ -31,9 +31,9 @@ pub fn fmt_default_spaces<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_spaces<'a, 'b, I>(buf: &mut Buf<'a>, spaces: I, indent: u16)
|
pub fn fmt_spaces<'a, 'buf, I>(buf: &mut Buf<'buf>, spaces: I, indent: u16)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||||
{
|
{
|
||||||
use self::CommentOrNewline::*;
|
use self::CommentOrNewline::*;
|
||||||
|
|
||||||
|
@ -84,13 +84,13 @@ pub enum NewlineAt {
|
||||||
/// The `new_line_at` argument describes how new lines should be inserted
|
/// The `new_line_at` argument describes how new lines should be inserted
|
||||||
/// at the beginning or at the end of the block
|
/// at the beginning or at the end of the block
|
||||||
/// in the case of there is some comment in the `spaces` argument.
|
/// in the case of there is some comment in the `spaces` argument.
|
||||||
pub fn fmt_comments_only<'a, 'b, I>(
|
pub fn fmt_comments_only<'a, 'buf, I>(
|
||||||
buf: &mut Buf<'a>,
|
buf: &mut Buf<'buf>,
|
||||||
spaces: I,
|
spaces: I,
|
||||||
new_line_at: NewlineAt,
|
new_line_at: NewlineAt,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) where
|
) where
|
||||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||||
{
|
{
|
||||||
use self::CommentOrNewline::*;
|
use self::CommentOrNewline::*;
|
||||||
use NewlineAt::*;
|
use NewlineAt::*;
|
||||||
|
@ -123,7 +123,7 @@ pub fn fmt_comments_only<'a, 'b, I>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_comment<'a>(buf: &mut Buf<'a>, comment: &str) {
|
fn fmt_comment<'buf>(buf: &mut Buf<'buf>, comment: &str) {
|
||||||
buf.push('#');
|
buf.push('#');
|
||||||
if !comment.starts_with(' ') {
|
if !comment.starts_with(' ') {
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
|
@ -131,7 +131,7 @@ fn fmt_comment<'a>(buf: &mut Buf<'a>, comment: &str) {
|
||||||
buf.push_str(comment);
|
buf.push_str(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_docs<'a>(buf: &mut Buf<'a>, docs: &str) {
|
fn fmt_docs<'buf>(buf: &mut Buf<'buf>, docs: &str) {
|
||||||
buf.push_str("##");
|
buf.push_str("##");
|
||||||
if !docs.starts_with(' ') {
|
if !docs.starts_with(' ') {
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod test_fmt {
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
use roc_test_utils::assert_multiline_str_eq;
|
||||||
|
|
||||||
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
|
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
|
||||||
fn expect_format_helper(input: &str, expected: &str) {
|
fn expect_format_expr_helper(input: &str, expected: &str) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) {
|
match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) {
|
||||||
Ok(actual) => {
|
Ok(actual) => {
|
||||||
|
@ -34,21 +34,20 @@ mod test_fmt {
|
||||||
let expected = expected.trim_end();
|
let expected = expected.trim_end();
|
||||||
|
|
||||||
// First check that input formats to the expected version
|
// First check that input formats to the expected version
|
||||||
expect_format_helper(input, expected);
|
expect_format_expr_helper(input, expected);
|
||||||
|
|
||||||
// Parse the expected result format it, asserting that it doesn't change
|
// Parse the expected result format it, asserting that it doesn't change
|
||||||
// It's important that formatting be stable / idempotent
|
// It's important that formatting be stable / idempotent
|
||||||
expect_format_helper(expected, expected);
|
expect_format_expr_helper(expected, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_formats_same(input: &str) {
|
fn expr_formats_same(input: &str) {
|
||||||
expr_formats_to(input, input);
|
expr_formats_to(input, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module_formats_to(src: &str, expected: &str) {
|
// Not intended to be used directly in tests; please use module_formats_to or module_formats_same
|
||||||
|
fn expect_format_module_helper(src: &str, expected: &str) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let src = src.trim_end();
|
|
||||||
|
|
||||||
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
||||||
Ok((actual, state)) => {
|
Ok((actual, state)) => {
|
||||||
let mut buf = Buf::new_in(&arena);
|
let mut buf = Buf::new_in(&arena);
|
||||||
|
@ -70,6 +69,17 @@ mod test_fmt {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn module_formats_to(input: &str, expected: &str) {
|
||||||
|
let input = input.trim_end();
|
||||||
|
|
||||||
|
// First check that input formats to the expected version
|
||||||
|
expect_format_module_helper(input, expected);
|
||||||
|
|
||||||
|
// Parse the expected result format it, asserting that it doesn't change
|
||||||
|
// It's important that formatting be stable / idempotent
|
||||||
|
expect_format_module_helper(expected, expected);
|
||||||
|
}
|
||||||
|
|
||||||
fn module_formats_same(input: &str) {
|
fn module_formats_same(input: &str) {
|
||||||
module_formats_to(input, input);
|
module_formats_to(input, input);
|
||||||
}
|
}
|
||||||
|
@ -2625,7 +2635,7 @@ mod test_fmt {
|
||||||
fn single_line_app() {
|
fn single_line_app() {
|
||||||
module_formats_same(indoc!(
|
module_formats_same(indoc!(
|
||||||
r#"
|
r#"
|
||||||
app "Foo" packages { base: "platform" } imports [] provides [ main ] to base"#
|
app "Foo" packages { pf: "platform" } imports [] provides [ main ] to pf"#
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
use roc_mono::code_gen_help::CodeGenHelp;
|
||||||
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_reporting::internal_error;
|
use roc_reporting::internal_error;
|
||||||
|
@ -256,8 +256,8 @@ pub struct Backend64Bit<
|
||||||
phantom_cc: PhantomData<CC>,
|
phantom_cc: PhantomData<CC>,
|
||||||
env: &'a Env<'a>,
|
env: &'a Env<'a>,
|
||||||
interns: &'a mut Interns,
|
interns: &'a mut Interns,
|
||||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
helper_proc_gen: CodeGenHelp<'a>,
|
||||||
refcount_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
helper_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||||
buf: Vec<'a, u8>,
|
buf: Vec<'a, u8>,
|
||||||
relocs: Vec<'a, Relocation>,
|
relocs: Vec<'a, Relocation>,
|
||||||
proc_name: Option<String>,
|
proc_name: Option<String>,
|
||||||
|
@ -308,8 +308,8 @@ pub fn new_backend_64bit<
|
||||||
phantom_cc: PhantomData,
|
phantom_cc: PhantomData,
|
||||||
env,
|
env,
|
||||||
interns,
|
interns,
|
||||||
refcount_proc_gen: RefcountProcGenerator::new(env.arena, IntWidth::I64, env.module_id),
|
helper_proc_gen: CodeGenHelp::new(env.arena, IntWidth::I64, env.module_id),
|
||||||
refcount_proc_symbols: bumpalo::vec![in env.arena],
|
helper_proc_symbols: bumpalo::vec![in env.arena],
|
||||||
proc_name: None,
|
proc_name: None,
|
||||||
is_self_recursive: None,
|
is_self_recursive: None,
|
||||||
buf: bumpalo::vec![in env.arena],
|
buf: bumpalo::vec![in env.arena],
|
||||||
|
@ -346,19 +346,17 @@ impl<
|
||||||
fn interns(&self) -> &Interns {
|
fn interns(&self) -> &Interns {
|
||||||
self.interns
|
self.interns
|
||||||
}
|
}
|
||||||
fn env_interns_refcount_mut(
|
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>) {
|
||||||
&mut self,
|
(self.env, self.interns, &mut self.helper_proc_gen)
|
||||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>) {
|
|
||||||
(self.env, self.interns, &mut self.refcount_proc_gen)
|
|
||||||
}
|
}
|
||||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a> {
|
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a> {
|
||||||
&mut self.refcount_proc_gen
|
&mut self.helper_proc_gen
|
||||||
}
|
}
|
||||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||||
&mut self.refcount_proc_symbols
|
&mut self.helper_proc_symbols
|
||||||
}
|
}
|
||||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||||
&self.refcount_proc_symbols
|
&self.helper_proc_symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
|
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
|
||||||
|
@ -383,7 +381,7 @@ impl<
|
||||||
self.float_used_regs.clear();
|
self.float_used_regs.clear();
|
||||||
self.float_free_regs
|
self.float_free_regs
|
||||||
.extend_from_slice(CC::FLOAT_DEFAULT_FREE_REGS);
|
.extend_from_slice(CC::FLOAT_DEFAULT_FREE_REGS);
|
||||||
self.refcount_proc_symbols.clear();
|
self.helper_proc_symbols.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const Layout<'a>)> {
|
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const Layout<'a>)> {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{ModuleName, TagName};
|
use roc_module::ident::{ModuleName, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
use roc_mono::code_gen_help::CodeGenHelp;
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||||
SelfRecursive, Stmt,
|
SelfRecursive, Stmt,
|
||||||
|
@ -62,9 +62,7 @@ trait Backend<'a> {
|
||||||
// This method is suboptimal, but it seems to be the only way to make rust understand
|
// This method is suboptimal, but it seems to be the only way to make rust understand
|
||||||
// that all of these values can be mutable at the same time. By returning them together,
|
// that all of these values can be mutable at the same time. By returning them together,
|
||||||
// rust understands that they are part of a single use of mutable self.
|
// rust understands that they are part of a single use of mutable self.
|
||||||
fn env_interns_refcount_mut(
|
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>);
|
||||||
&mut self,
|
|
||||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>);
|
|
||||||
|
|
||||||
fn symbol_to_string(&self, symbol: Symbol, layout_id: LayoutId) -> String {
|
fn symbol_to_string(&self, symbol: Symbol, layout_id: LayoutId) -> String {
|
||||||
layout_id.to_symbol_string(symbol, self.interns())
|
layout_id.to_symbol_string(symbol, self.interns())
|
||||||
|
@ -76,11 +74,11 @@ trait Backend<'a> {
|
||||||
.starts_with(ModuleName::APP)
|
.starts_with(ModuleName::APP)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a>;
|
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a>;
|
||||||
|
|
||||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||||
|
|
||||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||||
|
|
||||||
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
||||||
/// It also passes basic procedure information to the builder for setup of the next function.
|
/// It also passes basic procedure information to the builder for setup of the next function.
|
||||||
|
@ -116,17 +114,17 @@ trait Backend<'a> {
|
||||||
self.scan_ast(&proc.body);
|
self.scan_ast(&proc.body);
|
||||||
self.create_free_map();
|
self.create_free_map();
|
||||||
self.build_stmt(&proc.body, &proc.ret_layout);
|
self.build_stmt(&proc.body, &proc.ret_layout);
|
||||||
let mut rc_proc_names = bumpalo::vec![in self.env().arena];
|
let mut helper_proc_names = bumpalo::vec![in self.env().arena];
|
||||||
rc_proc_names.reserve(self.refcount_proc_symbols().len());
|
helper_proc_names.reserve(self.helper_proc_symbols().len());
|
||||||
for (rc_proc_sym, rc_proc_layout) in self.refcount_proc_symbols() {
|
for (rc_proc_sym, rc_proc_layout) in self.helper_proc_symbols() {
|
||||||
let name = layout_ids
|
let name = layout_ids
|
||||||
.get_toplevel(*rc_proc_sym, rc_proc_layout)
|
.get_toplevel(*rc_proc_sym, rc_proc_layout)
|
||||||
.to_symbol_string(*rc_proc_sym, self.interns());
|
.to_symbol_string(*rc_proc_sym, self.interns());
|
||||||
|
|
||||||
rc_proc_names.push((*rc_proc_sym, name));
|
helper_proc_names.push((*rc_proc_sym, name));
|
||||||
}
|
}
|
||||||
let (bytes, relocs) = self.finalize();
|
let (bytes, relocs) = self.finalize();
|
||||||
(bytes, relocs, rc_proc_names)
|
(bytes, relocs, helper_proc_names)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// build_stmt builds a statement and outputs at the end of the buffer.
|
/// build_stmt builds a statement and outputs at the end of the buffer.
|
||||||
|
@ -150,17 +148,16 @@ trait Backend<'a> {
|
||||||
// Expand the Refcounting statement into more detailed IR with a function call
|
// Expand the Refcounting statement into more detailed IR with a function call
|
||||||
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
||||||
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
||||||
let (rc_stmt, new_proc_info) = {
|
let (rc_stmt, new_specializations) = {
|
||||||
let (env, interns, rc_proc_gen) = self.env_interns_refcount_mut();
|
let (env, interns, rc_proc_gen) = self.env_interns_helpers_mut();
|
||||||
let module_id = env.module_id;
|
let module_id = env.module_id;
|
||||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||||
|
|
||||||
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
|
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((rc_proc_symbol, rc_proc_layout)) = new_proc_info {
|
for spec in new_specializations.into_iter() {
|
||||||
self.refcount_proc_symbols_mut()
|
self.helper_proc_symbols_mut().push(spec);
|
||||||
.push((rc_proc_symbol, rc_proc_layout));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.build_stmt(rc_stmt, ret_layout)
|
self.build_stmt(rc_stmt, ret_layout)
|
||||||
|
@ -538,7 +535,7 @@ trait Backend<'a> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
1,
|
1,
|
||||||
args.len(),
|
args.len(),
|
||||||
"RefCountGetPtr: expected to have exactly two argument"
|
"RefCountGetPtr: expected to have exactly one argument"
|
||||||
);
|
);
|
||||||
self.build_refcount_getptr(sym, &args[0])
|
self.build_refcount_getptr(sym, &args[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,38 +240,38 @@ fn build_object<'a, B: Backend<'a>>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let rc_procs = {
|
// Generate IR for specialized helper procs (refcounting & equality)
|
||||||
|
let helper_procs = {
|
||||||
let module_id = backend.env().module_id;
|
let module_id = backend.env().module_id;
|
||||||
|
|
||||||
let (env, interns, rc_proc_gen) = backend.env_interns_refcount_mut();
|
let (env, interns, helper_proc_gen) = backend.env_interns_helpers_mut();
|
||||||
|
|
||||||
// Generate IR for refcounting procedures
|
|
||||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||||
let rc_procs = rc_proc_gen.generate_refcount_procs(arena, ident_ids);
|
let helper_procs = helper_proc_gen.generate_procs(arena, ident_ids);
|
||||||
env.module_id.register_debug_idents(ident_ids);
|
env.module_id.register_debug_idents(ident_ids);
|
||||||
|
|
||||||
rc_procs
|
helper_procs
|
||||||
};
|
};
|
||||||
|
|
||||||
let empty = bumpalo::collections::Vec::new_in(arena);
|
let empty = bumpalo::collections::Vec::new_in(arena);
|
||||||
let rc_symbols_and_layouts = std::mem::replace(backend.refcount_proc_symbols_mut(), empty);
|
let helper_symbols_and_layouts = std::mem::replace(backend.helper_proc_symbols_mut(), empty);
|
||||||
let mut rc_names_symbols_procs = Vec::with_capacity_in(rc_procs.len(), arena);
|
let mut helper_names_symbols_procs = Vec::with_capacity_in(helper_procs.len(), arena);
|
||||||
|
|
||||||
// Names and linker data for refcounting procedures
|
// Names and linker data for helpers
|
||||||
for ((sym, layout), proc) in rc_symbols_and_layouts.into_iter().zip(rc_procs) {
|
for ((sym, layout), proc) in helper_symbols_and_layouts.into_iter().zip(helper_procs) {
|
||||||
let layout_id = layout_ids.get_toplevel(sym, &layout);
|
let layout_id = layout_ids.get_toplevel(sym, &layout);
|
||||||
let fn_name = backend.symbol_to_string(sym, layout_id);
|
let fn_name = backend.symbol_to_string(sym, layout_id);
|
||||||
if let Some(proc_id) = output.symbol_id(fn_name.as_bytes()) {
|
if let Some(proc_id) = output.symbol_id(fn_name.as_bytes()) {
|
||||||
if let SymbolSection::Section(section_id) = output.symbol(proc_id).section {
|
if let SymbolSection::Section(section_id) = output.symbol(proc_id).section {
|
||||||
rc_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
helper_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build refcounting procedures
|
// Build helpers
|
||||||
for (fn_name, section_id, proc_id, proc) in rc_names_symbols_procs {
|
for (fn_name, section_id, proc_id, proc) in helper_names_symbols_procs {
|
||||||
build_proc(
|
build_proc(
|
||||||
&mut output,
|
&mut output,
|
||||||
&mut backend,
|
&mut backend,
|
||||||
|
@ -285,7 +285,7 @@ fn build_object<'a, B: Backend<'a>>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relocations for all procedures (user code & refcounting)
|
// Relocations for all procedures (user code & helpers)
|
||||||
for (section_id, reloc) in relocations {
|
for (section_id, reloc) in relocations {
|
||||||
match output.add_relocation(section_id, reloc) {
|
match output.add_relocation(section_id, reloc) {
|
||||||
Ok(obj) => obj,
|
Ok(obj) => obj,
|
||||||
|
|
|
@ -15,8 +15,8 @@ use crate::llvm::build_list::{
|
||||||
list_single, list_sort_with, list_sublist, list_swap,
|
list_single, list_sort_with, list_sublist, list_swap,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
|
||||||
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
||||||
str_starts_with, str_starts_with_code_point, str_to_utf8, str_trim, str_trim_left,
|
str_starts_with, str_starts_with_code_point, str_to_utf8, str_trim, str_trim_left,
|
||||||
str_trim_right,
|
str_trim_right,
|
||||||
};
|
};
|
||||||
|
@ -779,118 +779,109 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||||
Str(str_literal) => {
|
Str(str_literal) => {
|
||||||
if str_literal.is_empty() {
|
let ctx = env.context;
|
||||||
empty_str(env)
|
let builder = env.builder;
|
||||||
} else {
|
let number_of_chars = str_literal.len() as u64;
|
||||||
let ctx = env.context;
|
|
||||||
let builder = env.builder;
|
|
||||||
let number_of_chars = str_literal.len() as u64;
|
|
||||||
|
|
||||||
let str_type = super::convert::zig_str_type(env);
|
let str_type = super::convert::zig_str_type(env);
|
||||||
|
|
||||||
if str_literal.len() < env.small_str_bytes() as usize {
|
if str_literal.len() < env.small_str_bytes() as usize {
|
||||||
// TODO support big endian systems
|
// TODO support big endian systems
|
||||||
|
|
||||||
let array_alloca = builder.build_array_alloca(
|
let array_alloca = builder.build_array_alloca(
|
||||||
ctx.i8_type(),
|
ctx.i8_type(),
|
||||||
ctx.i8_type().const_int(env.small_str_bytes() as u64, false),
|
ctx.i8_type().const_int(env.small_str_bytes() as u64, false),
|
||||||
"alloca_small_str",
|
"alloca_small_str",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Zero out all the bytes. If we don't do this, then
|
// Zero out all the bytes. If we don't do this, then
|
||||||
// small strings would have uninitialized bytes, which could
|
// small strings would have uninitialized bytes, which could
|
||||||
// cause string equality checks to fail randomly.
|
// cause string equality checks to fail randomly.
|
||||||
//
|
//
|
||||||
// We're running memset over *all* the bytes, even though
|
// We're running memset over *all* the bytes, even though
|
||||||
// the final one is about to be manually overridden, on
|
// the final one is about to be manually overridden, on
|
||||||
// the theory that LLVM will optimize the memset call
|
// the theory that LLVM will optimize the memset call
|
||||||
// into two instructions to move appropriately-sized
|
// into two instructions to move appropriately-sized
|
||||||
// zero integers into the appropriate locations instead
|
// zero integers into the appropriate locations instead
|
||||||
// of doing any iteration.
|
// of doing any iteration.
|
||||||
//
|
//
|
||||||
// TODO: look at the compiled output to verify this theory!
|
// TODO: look at the compiled output to verify this theory!
|
||||||
env.call_memset(
|
env.call_memset(
|
||||||
|
array_alloca,
|
||||||
|
ctx.i8_type().const_zero(),
|
||||||
|
env.ptr_int().const_int(env.small_str_bytes() as u64, false),
|
||||||
|
);
|
||||||
|
|
||||||
|
let final_byte = (str_literal.len() as u8) | 0b1000_0000;
|
||||||
|
|
||||||
|
let final_byte_ptr = unsafe {
|
||||||
|
builder.build_in_bounds_gep(
|
||||||
array_alloca,
|
array_alloca,
|
||||||
ctx.i8_type().const_zero(),
|
&[ctx
|
||||||
env.ptr_int().const_int(env.small_str_bytes() as u64, false),
|
|
||||||
);
|
|
||||||
|
|
||||||
let final_byte = (str_literal.len() as u8) | 0b1000_0000;
|
|
||||||
|
|
||||||
let final_byte_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(
|
|
||||||
array_alloca,
|
|
||||||
&[ctx
|
|
||||||
.i8_type()
|
|
||||||
.const_int(env.small_str_bytes() as u64 - 1, false)],
|
|
||||||
"str_literal_final_byte",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.build_store(
|
|
||||||
final_byte_ptr,
|
|
||||||
ctx.i8_type().const_int(final_byte as u64, false),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy the elements from the list literal into the array
|
|
||||||
for (index, character) in str_literal.as_bytes().iter().enumerate() {
|
|
||||||
let val = env
|
|
||||||
.context
|
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.const_int(*character as u64, false)
|
.const_int(env.small_str_bytes() as u64 - 1, false)],
|
||||||
.as_basic_value_enum();
|
"str_literal_final_byte",
|
||||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
|
||||||
let elem_ptr = unsafe {
|
|
||||||
builder.build_in_bounds_gep(array_alloca, &[index_val], "index")
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.build_store(elem_ptr, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.build_load(
|
|
||||||
builder
|
|
||||||
.build_bitcast(
|
|
||||||
array_alloca,
|
|
||||||
str_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"cast_collection",
|
|
||||||
)
|
|
||||||
.into_pointer_value(),
|
|
||||||
"small_str_array",
|
|
||||||
)
|
)
|
||||||
} else {
|
};
|
||||||
let ptr = define_global_str_literal_ptr(env, *str_literal);
|
|
||||||
let number_of_elements = env.ptr_int().const_int(number_of_chars, false);
|
|
||||||
|
|
||||||
let struct_type = str_type;
|
builder.build_store(
|
||||||
|
final_byte_ptr,
|
||||||
|
ctx.i8_type().const_int(final_byte as u64, false),
|
||||||
|
);
|
||||||
|
|
||||||
let mut struct_val;
|
// Copy the elements from the list literal into the array
|
||||||
|
for (index, character) in str_literal.as_bytes().iter().enumerate() {
|
||||||
|
let val = env
|
||||||
|
.context
|
||||||
|
.i8_type()
|
||||||
|
.const_int(*character as u64, false)
|
||||||
|
.as_basic_value_enum();
|
||||||
|
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||||
|
let elem_ptr =
|
||||||
|
unsafe { builder.build_in_bounds_gep(array_alloca, &[index_val], "index") };
|
||||||
|
|
||||||
// Store the pointer
|
builder.build_store(elem_ptr, val);
|
||||||
struct_val = builder
|
|
||||||
.build_insert_value(
|
|
||||||
struct_type.get_undef(),
|
|
||||||
ptr,
|
|
||||||
Builtin::WRAPPER_PTR,
|
|
||||||
"insert_ptr_str_literal",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Store the length
|
|
||||||
struct_val = builder
|
|
||||||
.build_insert_value(
|
|
||||||
struct_val,
|
|
||||||
number_of_elements,
|
|
||||||
Builtin::WRAPPER_LEN,
|
|
||||||
"insert_len",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
builder.build_bitcast(
|
|
||||||
struct_val.into_struct_value(),
|
|
||||||
str_type,
|
|
||||||
"cast_collection",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.build_load(
|
||||||
|
builder
|
||||||
|
.build_bitcast(
|
||||||
|
array_alloca,
|
||||||
|
str_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"cast_collection",
|
||||||
|
)
|
||||||
|
.into_pointer_value(),
|
||||||
|
"small_str_array",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let ptr = define_global_str_literal_ptr(env, *str_literal);
|
||||||
|
let number_of_elements = env.ptr_int().const_int(number_of_chars, false);
|
||||||
|
|
||||||
|
let struct_type = str_type;
|
||||||
|
|
||||||
|
let mut struct_val;
|
||||||
|
|
||||||
|
// Store the pointer
|
||||||
|
struct_val = builder
|
||||||
|
.build_insert_value(
|
||||||
|
struct_type.get_undef(),
|
||||||
|
ptr,
|
||||||
|
Builtin::WRAPPER_PTR,
|
||||||
|
"insert_ptr_str_literal",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Store the length
|
||||||
|
struct_val = builder
|
||||||
|
.build_insert_value(
|
||||||
|
struct_val,
|
||||||
|
number_of_elements,
|
||||||
|
Builtin::WRAPPER_LEN,
|
||||||
|
"insert_len",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
builder.build_bitcast(struct_val.into_struct_value(), str_type, "cast_collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -432,12 +432,3 @@ pub fn str_equal<'a, 'ctx, 'env>(
|
||||||
bitcode::STR_EQUAL,
|
bitcode::STR_EQUAL,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO investigate: does this cause problems when the layout is known? this value is now not refcounted!
|
|
||||||
pub fn empty_str<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
|
||||||
let struct_type = super::convert::zig_str_type(env);
|
|
||||||
|
|
||||||
// The pointer should be null (aka zero) and the length should be zero,
|
|
||||||
// so the whole struct should be a const_zero
|
|
||||||
BasicValueEnum::StructValue(struct_type.const_zero())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use bumpalo::{self, collections::Vec};
|
use bumpalo::{self, collections::Vec};
|
||||||
|
|
||||||
use code_builder::Align;
|
use code_builder::Align;
|
||||||
use roc_builtins::bitcode::IntWidth;
|
use roc_builtins::bitcode::{self, IntWidth};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::gen_refcount::{RefcountProcGenerator, REFCOUNT_MAX};
|
use roc_mono::code_gen_help::{CodeGenHelp, REFCOUNT_MAX};
|
||||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, ProcLayout, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||||
use roc_reporting::internal_error;
|
use roc_reporting::internal_error;
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ pub struct WasmBackend<'a> {
|
||||||
builtin_sym_index_map: MutMap<&'a str, usize>,
|
builtin_sym_index_map: MutMap<&'a str, usize>,
|
||||||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||||
linker_symbols: Vec<'a, SymInfo>,
|
linker_symbols: Vec<'a, SymInfo>,
|
||||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
helper_proc_gen: CodeGenHelp<'a>,
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
code_builder: CodeBuilder<'a>,
|
code_builder: CodeBuilder<'a>,
|
||||||
|
@ -71,7 +71,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||||
mut linker_symbols: Vec<'a, SymInfo>,
|
mut linker_symbols: Vec<'a, SymInfo>,
|
||||||
mut exports: Vec<'a, Export>,
|
mut exports: Vec<'a, Export>,
|
||||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
helper_proc_gen: CodeGenHelp<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
@ -144,7 +144,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
builtin_sym_index_map: MutMap::default(),
|
builtin_sym_index_map: MutMap::default(),
|
||||||
proc_symbols,
|
proc_symbols,
|
||||||
linker_symbols,
|
linker_symbols,
|
||||||
refcount_proc_gen,
|
helper_proc_gen,
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
|
@ -157,15 +157,34 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_refcount_procs(&mut self) -> Vec<'a, Proc<'a>> {
|
pub fn generate_helpers(&mut self) -> Vec<'a, Proc<'a>> {
|
||||||
let ident_ids = self
|
let ident_ids = self
|
||||||
.interns
|
.interns
|
||||||
.all_ident_ids
|
.all_ident_ids
|
||||||
.get_mut(&self.env.module_id)
|
.get_mut(&self.env.module_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.refcount_proc_gen
|
self.helper_proc_gen
|
||||||
.generate_refcount_procs(self.env.arena, ident_ids)
|
.generate_procs(self.env.arena, ident_ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_helper_proc(&mut self, new_proc_info: (Symbol, ProcLayout<'a>)) {
|
||||||
|
let (new_proc_sym, new_proc_layout) = new_proc_info;
|
||||||
|
let wasm_fn_index = self.proc_symbols.len() as u32;
|
||||||
|
let linker_sym_index = self.linker_symbols.len() as u32;
|
||||||
|
|
||||||
|
let name = self
|
||||||
|
.layout_ids
|
||||||
|
.get_toplevel(new_proc_sym, &new_proc_layout)
|
||||||
|
.to_symbol_string(new_proc_sym, self.interns);
|
||||||
|
|
||||||
|
self.proc_symbols.push((new_proc_sym, linker_sym_index));
|
||||||
|
self.linker_symbols
|
||||||
|
.push(SymInfo::Function(WasmObjectSymbol::Defined {
|
||||||
|
flags: 0,
|
||||||
|
index: wasm_fn_index,
|
||||||
|
name,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize_module(mut self) -> WasmModule<'a> {
|
pub fn finalize_module(mut self) -> WasmModule<'a> {
|
||||||
|
@ -500,8 +519,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
.get_mut(&self.env.module_id)
|
.get_mut(&self.env.module_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (rc_stmt, new_proc_info) = self
|
let (rc_stmt, new_specializations) = self
|
||||||
.refcount_proc_gen
|
.helper_proc_gen
|
||||||
.expand_refcount_stmt(ident_ids, *layout, modify, *following);
|
.expand_refcount_stmt(ident_ids, *layout, modify, *following);
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
|
@ -509,24 +528,9 @@ impl<'a> WasmBackend<'a> {
|
||||||
println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt);
|
println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're creating a new RC procedure, we need to store its symbol data,
|
// If any new specializations were created, register their symbol data
|
||||||
// so that we can correctly generate calls to it.
|
for spec in new_specializations.into_iter() {
|
||||||
if let Some((rc_proc_sym, rc_proc_layout)) = new_proc_info {
|
self.register_helper_proc(spec);
|
||||||
let wasm_fn_index = self.proc_symbols.len() as u32;
|
|
||||||
let linker_sym_index = self.linker_symbols.len() as u32;
|
|
||||||
|
|
||||||
let name = self
|
|
||||||
.layout_ids
|
|
||||||
.get_toplevel(rc_proc_sym, &rc_proc_layout)
|
|
||||||
.to_symbol_string(rc_proc_sym, self.interns);
|
|
||||||
|
|
||||||
self.proc_symbols.push((rc_proc_sym, linker_sym_index));
|
|
||||||
self.linker_symbols
|
|
||||||
.push(SymInfo::Function(WasmObjectSymbol::Defined {
|
|
||||||
flags: 0,
|
|
||||||
index: wasm_fn_index,
|
|
||||||
name,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.build_stmt(rc_stmt, ret_layout);
|
self.build_stmt(rc_stmt, ret_layout);
|
||||||
|
@ -560,7 +564,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
CallType::ByName { name: func_sym, .. } => {
|
CallType::ByName { name: func_sym, .. } => {
|
||||||
// If this function is just a lowlevel wrapper, then inline it
|
// If this function is just a lowlevel wrapper, then inline it
|
||||||
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
|
||||||
return self.build_low_level(lowlevel, arguments, *sym, wasm_layout);
|
return self.build_low_level(
|
||||||
|
lowlevel,
|
||||||
|
arguments,
|
||||||
|
*sym,
|
||||||
|
wasm_layout,
|
||||||
|
storage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||||
|
@ -596,7 +606,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
CallType::LowLevel { op: lowlevel, .. } => {
|
CallType::LowLevel { op: lowlevel, .. } => {
|
||||||
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout)
|
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout, storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
x => todo!("call type {:?}", x),
|
x => todo!("call type {:?}", x),
|
||||||
|
@ -925,6 +935,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
return_sym: Symbol,
|
return_sym: Symbol,
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
|
storage: &StoredValue,
|
||||||
) {
|
) {
|
||||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||||
self.env.arena,
|
self.env.arena,
|
||||||
|
@ -949,6 +960,37 @@ impl<'a> WasmBackend<'a> {
|
||||||
BuiltinCall(name) => {
|
BuiltinCall(name) => {
|
||||||
self.call_zig_builtin(name, param_types, ret_type);
|
self.call_zig_builtin(name, param_types, ret_type);
|
||||||
}
|
}
|
||||||
|
SpecializedEq | SpecializedNotEq => {
|
||||||
|
let layout = self.symbol_layouts[&arguments[0]];
|
||||||
|
|
||||||
|
if layout == Layout::Builtin(Builtin::Str) {
|
||||||
|
self.call_zig_builtin(bitcode::STR_EQUAL, param_types, ret_type);
|
||||||
|
} else {
|
||||||
|
let ident_ids = self
|
||||||
|
.interns
|
||||||
|
.all_ident_ids
|
||||||
|
.get_mut(&self.env.module_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (replacement_expr, new_specializations) = self
|
||||||
|
.helper_proc_gen
|
||||||
|
.specialize_equals(ident_ids, &layout, arguments);
|
||||||
|
|
||||||
|
// If any new specializations were created, register their symbol data
|
||||||
|
for spec in new_specializations.into_iter() {
|
||||||
|
self.register_helper_proc(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.build_expr(&return_sym, replacement_expr, &layout, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(build_result, SpecializedNotEq) {
|
||||||
|
self.code_builder.i32_eqz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpecializedHash => {
|
||||||
|
todo!("Specialized hash functions")
|
||||||
|
}
|
||||||
NotImplemented => {
|
NotImplemented => {
|
||||||
todo!("Low level operation {:?}", lowlevel)
|
todo!("Low level operation {:?}", lowlevel)
|
||||||
}
|
}
|
||||||
|
@ -977,14 +1019,11 @@ impl<'a> WasmBackend<'a> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredValue::StackMemory { location, .. } => match lit {
|
StoredValue::StackMemory { location, .. } => {
|
||||||
Literal::Decimal(decimal) => {
|
let mut write128 = |lower_bits, upper_bits| {
|
||||||
let (local_id, offset) =
|
let (local_id, offset) =
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||||
|
|
||||||
let lower_bits = decimal.0 as i64;
|
|
||||||
let upper_bits = (decimal.0 >> 64) as i64;
|
|
||||||
|
|
||||||
self.code_builder.get_local(local_id);
|
self.code_builder.get_local(local_id);
|
||||||
self.code_builder.i64_const(lower_bits);
|
self.code_builder.i64_const(lower_bits);
|
||||||
self.code_builder.i64_store(Align::Bytes8, offset);
|
self.code_builder.i64_store(Align::Bytes8, offset);
|
||||||
|
@ -992,39 +1031,56 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.code_builder.get_local(local_id);
|
self.code_builder.get_local(local_id);
|
||||||
self.code_builder.i64_const(upper_bits);
|
self.code_builder.i64_const(upper_bits);
|
||||||
self.code_builder.i64_store(Align::Bytes8, offset + 8);
|
self.code_builder.i64_store(Align::Bytes8, offset + 8);
|
||||||
|
};
|
||||||
|
|
||||||
|
match lit {
|
||||||
|
Literal::Decimal(decimal) => {
|
||||||
|
let lower_bits = (decimal.0 & 0xffff_ffff_ffff_ffff) as i64;
|
||||||
|
let upper_bits = (decimal.0 >> 64) as i64;
|
||||||
|
write128(lower_bits, upper_bits);
|
||||||
|
}
|
||||||
|
Literal::Int(x) => {
|
||||||
|
let lower_bits = (*x & 0xffff_ffff_ffff_ffff) as i64;
|
||||||
|
let upper_bits = (*x >> 64) as i64;
|
||||||
|
write128(lower_bits, upper_bits);
|
||||||
|
}
|
||||||
|
Literal::Float(_) => {
|
||||||
|
// Also not implemented in LLVM backend (nor in Rust!)
|
||||||
|
todo!("f128 type");
|
||||||
|
}
|
||||||
|
Literal::Str(string) => {
|
||||||
|
let (local_id, offset) =
|
||||||
|
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||||
|
|
||||||
|
let len = string.len();
|
||||||
|
if len < 8 {
|
||||||
|
let mut stack_mem_bytes = [0; 8];
|
||||||
|
stack_mem_bytes[0..len].clone_from_slice(string.as_bytes());
|
||||||
|
stack_mem_bytes[7] = 0x80 | (len as u8);
|
||||||
|
let str_as_int = i64::from_le_bytes(stack_mem_bytes);
|
||||||
|
|
||||||
|
// Write all 8 bytes at once using an i64
|
||||||
|
// Str is normally two i32's, but in this special case, we can get away with fewer instructions
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder.i64_const(str_as_int);
|
||||||
|
self.code_builder.i64_store(Align::Bytes4, offset);
|
||||||
|
} else {
|
||||||
|
let (linker_sym_index, elements_addr) =
|
||||||
|
self.lookup_string_constant(string, sym, layout);
|
||||||
|
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder
|
||||||
|
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
||||||
|
self.code_builder.i32_store(Align::Bytes4, offset);
|
||||||
|
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder.i32_const(string.len() as i32);
|
||||||
|
self.code_builder.i32_store(Align::Bytes4, offset + 4);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => not_supported_error(),
|
||||||
}
|
}
|
||||||
Literal::Str(string) => {
|
}
|
||||||
let (local_id, offset) =
|
|
||||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
|
||||||
|
|
||||||
let len = string.len();
|
|
||||||
if len < 8 {
|
|
||||||
let mut stack_mem_bytes = [0; 8];
|
|
||||||
stack_mem_bytes[0..len].clone_from_slice(string.as_bytes());
|
|
||||||
stack_mem_bytes[7] = 0x80 | (len as u8);
|
|
||||||
let str_as_int = i64::from_le_bytes(stack_mem_bytes);
|
|
||||||
|
|
||||||
// Write all 8 bytes at once using an i64
|
|
||||||
// Str is normally two i32's, but in this special case, we can get away with fewer instructions
|
|
||||||
self.code_builder.get_local(local_id);
|
|
||||||
self.code_builder.i64_const(str_as_int);
|
|
||||||
self.code_builder.i64_store(Align::Bytes4, offset);
|
|
||||||
} else {
|
|
||||||
let (linker_sym_index, elements_addr) =
|
|
||||||
self.lookup_string_constant(string, sym, layout);
|
|
||||||
|
|
||||||
self.code_builder.get_local(local_id);
|
|
||||||
self.code_builder
|
|
||||||
.i32_const_mem_addr(elements_addr, linker_sym_index);
|
|
||||||
self.code_builder.i32_store(Align::Bytes4, offset);
|
|
||||||
|
|
||||||
self.code_builder.get_local(local_id);
|
|
||||||
self.code_builder.i32_const(string.len() as i32);
|
|
||||||
self.code_builder.i32_store(Align::Bytes4, offset + 4);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => not_supported_error(),
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => not_supported_error(),
|
_ => not_supported_error(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ use roc_builtins::bitcode::IntWidth;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
use roc_mono::code_gen_help::CodeGenHelp;
|
||||||
use roc_mono::ir::{Proc, ProcLayout};
|
use roc_mono::ir::{Proc, ProcLayout};
|
||||||
use roc_mono::layout::LayoutIds;
|
use roc_mono::layout::LayoutIds;
|
||||||
use roc_reporting::internal_error;
|
use roc_reporting::internal_error;
|
||||||
|
@ -94,7 +94,7 @@ pub fn build_module_help<'a>(
|
||||||
proc_symbols,
|
proc_symbols,
|
||||||
linker_symbols,
|
linker_symbols,
|
||||||
exports,
|
exports,
|
||||||
RefcountProcGenerator::new(env.arena, IntWidth::I32, env.module_id),
|
CodeGenHelp::new(env.arena, IntWidth::I32, env.module_id),
|
||||||
);
|
);
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
|
@ -110,21 +110,21 @@ pub fn build_module_help<'a>(
|
||||||
backend.build_proc(proc);
|
backend.build_proc(proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate IR for refcounting procs
|
// Generate specialized helpers for refcounting & equality
|
||||||
let refcount_procs = backend.generate_refcount_procs();
|
let helper_procs = backend.generate_helpers();
|
||||||
|
|
||||||
backend.register_symbol_debug_names();
|
backend.register_symbol_debug_names();
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
println!("## refcount_procs");
|
println!("## helper_procs");
|
||||||
for proc in refcount_procs.iter() {
|
for proc in helper_procs.iter() {
|
||||||
println!("{}", proc.to_pretty(200));
|
println!("{}", proc.to_pretty(200));
|
||||||
println!("{:#?}", proc);
|
println!("{:#?}", proc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Wasm for refcounting procs
|
// Generate Wasm for refcounting procs
|
||||||
for proc in refcount_procs.iter() {
|
for proc in helper_procs.iter() {
|
||||||
backend.build_proc(proc);
|
backend.build_proc(proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,14 @@ use roc_reporting::internal_error;
|
||||||
|
|
||||||
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
||||||
use crate::storage::{Storage, StoredValue};
|
use crate::storage::{Storage, StoredValue};
|
||||||
use crate::wasm_module::{CodeBuilder, ValueType::*};
|
use crate::wasm_module::{Align, CodeBuilder, ValueType::*};
|
||||||
|
|
||||||
pub enum LowlevelBuildResult {
|
pub enum LowlevelBuildResult {
|
||||||
Done,
|
Done,
|
||||||
BuiltinCall(&'static str),
|
BuiltinCall(&'static str),
|
||||||
|
SpecializedEq,
|
||||||
|
SpecializedNotEq,
|
||||||
|
SpecializedHash,
|
||||||
NotImplemented,
|
NotImplemented,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +20,7 @@ pub fn decode_low_level<'a>(
|
||||||
code_builder: &mut CodeBuilder<'a>,
|
code_builder: &mut CodeBuilder<'a>,
|
||||||
storage: &mut Storage<'a>,
|
storage: &mut Storage<'a>,
|
||||||
lowlevel: LowLevel,
|
lowlevel: LowLevel,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
ret_layout: &WasmLayout,
|
ret_layout: &WasmLayout,
|
||||||
) -> LowlevelBuildResult {
|
) -> LowlevelBuildResult {
|
||||||
use LowlevelBuildResult::*;
|
use LowlevelBuildResult::*;
|
||||||
|
@ -81,8 +84,6 @@ pub fn decode_low_level<'a>(
|
||||||
WasmLayout::Primitive(value_type, size) => match value_type {
|
WasmLayout::Primitive(value_type, size) => match value_type {
|
||||||
I32 => {
|
I32 => {
|
||||||
code_builder.i32_add();
|
code_builder.i32_add();
|
||||||
// TODO: is *deliberate* wrapping really in the spirit of things?
|
|
||||||
// The point of choosing NumAddWrap is to go fast by skipping checks, but we're making it slower.
|
|
||||||
wrap_i32(code_builder, *size);
|
wrap_i32(code_builder, *size);
|
||||||
}
|
}
|
||||||
I64 => code_builder.i64_add(),
|
I64 => code_builder.i64_add(),
|
||||||
|
@ -347,27 +348,56 @@ pub fn decode_low_level<'a>(
|
||||||
},
|
},
|
||||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||||
},
|
},
|
||||||
NumIsFinite => match ret_layout {
|
NumIsFinite => {
|
||||||
WasmLayout::Primitive(value_type, _) => match value_type {
|
use StoredValue::*;
|
||||||
I32 => code_builder.i32_const(1),
|
match storage.get(&args[0]) {
|
||||||
I64 => code_builder.i32_const(1),
|
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||||
F32 => {
|
match value_type {
|
||||||
code_builder.i32_reinterpret_f32();
|
I32 | I64 => code_builder.i32_const(1), // always true for integers
|
||||||
code_builder.i32_const(0x7f800000);
|
F32 => {
|
||||||
code_builder.i32_and();
|
code_builder.i32_reinterpret_f32();
|
||||||
code_builder.i32_const(0x7f800000);
|
code_builder.i32_const(0x7f80_0000);
|
||||||
code_builder.i32_ne();
|
code_builder.i32_and();
|
||||||
|
code_builder.i32_const(0x7f80_0000);
|
||||||
|
code_builder.i32_ne();
|
||||||
|
}
|
||||||
|
F64 => {
|
||||||
|
code_builder.i64_reinterpret_f64();
|
||||||
|
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||||
|
code_builder.i64_and();
|
||||||
|
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||||
|
code_builder.i64_ne();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
F64 => {
|
StackMemory {
|
||||||
code_builder.i64_reinterpret_f64();
|
format, location, ..
|
||||||
code_builder.i64_const(0x7ff0000000000000);
|
} => {
|
||||||
code_builder.i64_and();
|
let (local_id, offset) = location.local_and_offset(storage.stack_frame_pointer);
|
||||||
code_builder.i64_const(0x7ff0000000000000);
|
|
||||||
code_builder.i64_ne();
|
match format {
|
||||||
|
Int128 => code_builder.i32_const(1),
|
||||||
|
Float128 => {
|
||||||
|
code_builder.get_local(local_id);
|
||||||
|
code_builder.i64_load(Align::Bytes4, offset + 8);
|
||||||
|
code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||||
|
code_builder.i64_and();
|
||||||
|
code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||||
|
code_builder.i64_ne();
|
||||||
|
}
|
||||||
|
Decimal => {
|
||||||
|
code_builder.get_local(local_id);
|
||||||
|
code_builder.i64_load(Align::Bytes4, offset + 8);
|
||||||
|
code_builder.i64_const(0x7100_0000_0000_0000);
|
||||||
|
code_builder.i64_and();
|
||||||
|
code_builder.i64_const(0x7100_0000_0000_0000);
|
||||||
|
code_builder.i64_ne();
|
||||||
|
}
|
||||||
|
DataStructure => return NotImplemented,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
}
|
||||||
},
|
|
||||||
NumAtan => {
|
NumAtan => {
|
||||||
let width = float_width_from_layout(ret_layout);
|
let width = float_width_from_layout(ret_layout);
|
||||||
return BuiltinCall(&bitcode::NUM_ATAN[width]);
|
return BuiltinCall(&bitcode::NUM_ATAN[width]);
|
||||||
|
@ -468,16 +498,80 @@ pub fn decode_low_level<'a>(
|
||||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Eq => match storage.get(&args[0]) {
|
Eq => {
|
||||||
StoredValue::VirtualMachineStack { value_type, .. }
|
use StoredValue::*;
|
||||||
| StoredValue::Local { value_type, .. } => match value_type {
|
match storage.get(&args[0]).to_owned() {
|
||||||
I32 => code_builder.i32_eq(),
|
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||||
I64 => code_builder.i64_eq(),
|
match value_type {
|
||||||
F32 => code_builder.f32_eq(),
|
I32 => code_builder.i32_eq(),
|
||||||
F64 => code_builder.f64_eq(),
|
I64 => code_builder.i64_eq(),
|
||||||
},
|
F32 => code_builder.f32_eq(),
|
||||||
StoredValue::StackMemory { .. } => return NotImplemented,
|
F64 => code_builder.f64_eq(),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
StackMemory {
|
||||||
|
format,
|
||||||
|
location: location0,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if let StackMemory {
|
||||||
|
location: location1,
|
||||||
|
..
|
||||||
|
} = storage.get(&args[1]).to_owned()
|
||||||
|
{
|
||||||
|
let stack_frame_pointer = storage.stack_frame_pointer;
|
||||||
|
let compare_bytes = |code_builder: &mut CodeBuilder| {
|
||||||
|
let (local0, offset0) = location0.local_and_offset(stack_frame_pointer);
|
||||||
|
let (local1, offset1) = location1.local_and_offset(stack_frame_pointer);
|
||||||
|
|
||||||
|
code_builder.get_local(local0);
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset0);
|
||||||
|
code_builder.get_local(local1);
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset1);
|
||||||
|
code_builder.i64_eq();
|
||||||
|
|
||||||
|
code_builder.get_local(local0);
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset0 + 8);
|
||||||
|
code_builder.get_local(local1);
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset1 + 8);
|
||||||
|
code_builder.i64_eq();
|
||||||
|
|
||||||
|
code_builder.i32_and();
|
||||||
|
};
|
||||||
|
|
||||||
|
match format {
|
||||||
|
Decimal => {
|
||||||
|
// Both args are finite
|
||||||
|
let first = [args[0]];
|
||||||
|
let second = [args[1]];
|
||||||
|
decode_low_level(
|
||||||
|
code_builder,
|
||||||
|
storage,
|
||||||
|
LowLevel::NumIsFinite,
|
||||||
|
&first,
|
||||||
|
ret_layout,
|
||||||
|
);
|
||||||
|
decode_low_level(
|
||||||
|
code_builder,
|
||||||
|
storage,
|
||||||
|
LowLevel::NumIsFinite,
|
||||||
|
&second,
|
||||||
|
ret_layout,
|
||||||
|
);
|
||||||
|
code_builder.i32_and();
|
||||||
|
|
||||||
|
// AND they have the same bytes
|
||||||
|
compare_bytes(code_builder);
|
||||||
|
code_builder.i32_and();
|
||||||
|
}
|
||||||
|
Int128 => compare_bytes(code_builder),
|
||||||
|
Float128 => return NotImplemented,
|
||||||
|
DataStructure => return SpecializedEq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
NotEq => match storage.get(&args[0]) {
|
NotEq => match storage.get(&args[0]) {
|
||||||
StoredValue::VirtualMachineStack { value_type, .. }
|
StoredValue::VirtualMachineStack { value_type, .. }
|
||||||
| StoredValue::Local { value_type, .. } => match value_type {
|
| StoredValue::Local { value_type, .. } => match value_type {
|
||||||
|
@ -486,12 +580,19 @@ pub fn decode_low_level<'a>(
|
||||||
F32 => code_builder.f32_ne(),
|
F32 => code_builder.f32_ne(),
|
||||||
F64 => code_builder.f64_ne(),
|
F64 => code_builder.f64_ne(),
|
||||||
},
|
},
|
||||||
StoredValue::StackMemory { .. } => return NotImplemented,
|
StoredValue::StackMemory { format, .. } => {
|
||||||
|
if matches!(format, DataStructure) {
|
||||||
|
return SpecializedNotEq;
|
||||||
|
} else {
|
||||||
|
decode_low_level(code_builder, storage, LowLevel::Eq, args, ret_layout);
|
||||||
|
code_builder.i32_eqz();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
And => code_builder.i32_and(),
|
And => code_builder.i32_and(),
|
||||||
Or => code_builder.i32_or(),
|
Or => code_builder.i32_or(),
|
||||||
Not => code_builder.i32_eqz(),
|
Not => code_builder.i32_eqz(),
|
||||||
Hash => return NotImplemented,
|
Hash => return SpecializedHash,
|
||||||
ExpectTrue => return NotImplemented,
|
ExpectTrue => return NotImplemented,
|
||||||
RefCountGetPtr => {
|
RefCountGetPtr => {
|
||||||
code_builder.i32_const(4);
|
code_builder.i32_const(4);
|
||||||
|
|
|
@ -23,14 +23,13 @@ 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, StrLiteral, TypeAnnotation};
|
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
ExposedName, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||||
};
|
};
|
||||||
use roc_parse::module::module_defs;
|
use roc_parse::module::module_defs;
|
||||||
use roc_parse::parser::{self, ParseProblem, Parser, SyntaxError};
|
use roc_parse::parser::{self, ParseProblem, Parser, SyntaxError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_reporting::internal_error;
|
|
||||||
use roc_solve::module::SolvedModule;
|
use roc_solve::module::SolvedModule;
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
|
@ -685,7 +684,7 @@ enum HeaderFor<'a> {
|
||||||
to_platform: To<'a>,
|
to_platform: To<'a>,
|
||||||
},
|
},
|
||||||
PkgConfig {
|
PkgConfig {
|
||||||
/// usually `base`
|
/// usually `pf`
|
||||||
config_shorthand: &'a str,
|
config_shorthand: &'a str,
|
||||||
/// the type scheme of the main function (required by the platform)
|
/// the type scheme of the main function (required by the platform)
|
||||||
/// (currently unused)
|
/// (currently unused)
|
||||||
|
@ -2554,8 +2553,8 @@ fn parse_header<'a>(
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
header_src,
|
header_src,
|
||||||
packages: &[],
|
packages: &[],
|
||||||
exposes: header.exposes.items,
|
exposes: unspace(arena, header.exposes.items),
|
||||||
imports: header.imports.items,
|
imports: unspace(arena, header.imports.items),
|
||||||
to_platform: None,
|
to_platform: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2576,7 +2575,7 @@ fn parse_header<'a>(
|
||||||
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
||||||
};
|
};
|
||||||
|
|
||||||
let packages = header.packages.items;
|
let packages = unspace(arena, header.packages.items);
|
||||||
|
|
||||||
let info = HeaderInfo {
|
let info = HeaderInfo {
|
||||||
loc_name: Located {
|
loc_name: Located {
|
||||||
|
@ -2588,8 +2587,8 @@ fn parse_header<'a>(
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
header_src,
|
header_src,
|
||||||
packages,
|
packages,
|
||||||
exposes: header.provides.items,
|
exposes: unspace(arena, header.provides.items),
|
||||||
imports: header.imports.items,
|
imports: unspace(arena, header.imports.items),
|
||||||
to_platform: Some(header.to.value),
|
to_platform: Some(header.to.value),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2603,62 +2602,56 @@ fn parse_header<'a>(
|
||||||
|
|
||||||
match header.to.value {
|
match header.to.value {
|
||||||
To::ExistingPackage(existing_package) => {
|
To::ExistingPackage(existing_package) => {
|
||||||
let opt_base_package = packages.iter().find(|loc_package_entry| {
|
let opt_base_package = packages.iter().find_map(|loc_package_entry| {
|
||||||
let Located { value, .. } = loc_package_entry;
|
let Located { value, .. } = loc_package_entry;
|
||||||
|
|
||||||
match value.extract_spaces().item {
|
if value.shorthand == existing_package {
|
||||||
PackageEntry::Entry { shorthand, .. } => shorthand == existing_package,
|
Some(value)
|
||||||
_ => internal_error!(),
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
match opt_base_package {
|
if let Some(PackageEntry {
|
||||||
Some(Located {
|
shorthand,
|
||||||
value:
|
package_or_path:
|
||||||
PackageEntry::Entry {
|
Located {
|
||||||
shorthand,
|
value: package_or_path,
|
||||||
package_or_path:
|
..
|
||||||
Located {
|
},
|
||||||
value: package_or_path,
|
..
|
||||||
..
|
}) = opt_base_package
|
||||||
},
|
{
|
||||||
..
|
match package_or_path {
|
||||||
},
|
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
||||||
..
|
// check whether we can find a Package-Config.roc file
|
||||||
}) => {
|
let mut pkg_config_roc = pkg_config_dir;
|
||||||
match package_or_path {
|
pkg_config_roc.push(package);
|
||||||
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
|
||||||
// check whether we can find a Package-Config.roc file
|
pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
|
||||||
let mut pkg_config_roc = pkg_config_dir;
|
|
||||||
pkg_config_roc.push(package);
|
|
||||||
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
|
|
||||||
pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
|
|
||||||
|
|
||||||
if pkg_config_roc.as_path().exists() {
|
if pkg_config_roc.as_path().exists() {
|
||||||
let load_pkg_config_msg = load_pkg_config(
|
let load_pkg_config_msg = load_pkg_config(
|
||||||
arena,
|
arena,
|
||||||
&pkg_config_roc,
|
&pkg_config_roc,
|
||||||
shorthand,
|
shorthand,
|
||||||
module_id,
|
module_id,
|
||||||
module_ids,
|
module_ids,
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
module_id,
|
module_id,
|
||||||
Msg::Many(vec![
|
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]),
|
||||||
app_module_header_msg,
|
))
|
||||||
load_pkg_config_msg,
|
} else {
|
||||||
]),
|
Ok((module_id, app_module_header_msg))
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok((module_id, app_module_header_msg))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
_ => panic!("could not find base"),
|
} else {
|
||||||
|
panic!("could not find base")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
To::NewPackage(package_or_path) => match package_or_path {
|
To::NewPackage(package_or_path) => match package_or_path {
|
||||||
|
@ -2766,7 +2759,7 @@ struct HeaderInfo<'a> {
|
||||||
opt_shorthand: Option<&'a str>,
|
opt_shorthand: Option<&'a str>,
|
||||||
header_src: &'a str,
|
header_src: &'a str,
|
||||||
packages: &'a [Located<PackageEntry<'a>>],
|
packages: &'a [Located<PackageEntry<'a>>],
|
||||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
exposes: &'a [Located<ExposedName<'a>>],
|
||||||
imports: &'a [Located<ImportsEntry<'a>>],
|
imports: &'a [Located<ImportsEntry<'a>>],
|
||||||
to_platform: Option<To<'a>>,
|
to_platform: Option<To<'a>>,
|
||||||
}
|
}
|
||||||
|
@ -2846,7 +2839,7 @@ fn send_header<'a>(
|
||||||
|
|
||||||
// For each of our imports, add an entry to deps_by_name
|
// For each of our imports, add an entry to deps_by_name
|
||||||
//
|
//
|
||||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
// e.g. for `imports [ pf.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||||
//
|
//
|
||||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||||
|
@ -2913,24 +2906,13 @@ fn send_header<'a>(
|
||||||
ident_ids.clone()
|
ident_ids.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
let package_entries = packages
|
||||||
let mut package_entries = MutMap::default();
|
.iter()
|
||||||
|
.map(|pkg| {
|
||||||
while let Some(parse_entry) = parse_entries.pop() {
|
let pkg = pkg.value;
|
||||||
use PackageEntry::*;
|
(pkg.shorthand, pkg.package_or_path.value)
|
||||||
match parse_entry {
|
})
|
||||||
Entry {
|
.collect::<MutMap<_, _>>();
|
||||||
shorthand,
|
|
||||||
package_or_path,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
package_entries.insert(*shorthand, package_or_path.value);
|
|
||||||
}
|
|
||||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
|
||||||
parse_entries.push(inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the deps to the coordinator thread for processing,
|
// Send the deps to the coordinator thread for processing,
|
||||||
// then continue on to parsing and canonicalizing defs.
|
// then continue on to parsing and canonicalizing defs.
|
||||||
|
@ -2989,7 +2971,7 @@ struct PlatformHeaderInfo<'a> {
|
||||||
header_src: &'a str,
|
header_src: &'a str,
|
||||||
app_module_id: ModuleId,
|
app_module_id: ModuleId,
|
||||||
packages: &'a [Located<PackageEntry<'a>>],
|
packages: &'a [Located<PackageEntry<'a>>],
|
||||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
provides: &'a [Located<ExposedName<'a>>],
|
||||||
requires: &'a [Located<TypedIdent<'a>>],
|
requires: &'a [Located<TypedIdent<'a>>],
|
||||||
imports: &'a [Located<ImportsEntry<'a>>],
|
imports: &'a [Located<ImportsEntry<'a>>],
|
||||||
}
|
}
|
||||||
|
@ -2997,7 +2979,6 @@ struct PlatformHeaderInfo<'a> {
|
||||||
// TODO refactor so more logic is shared with `send_header`
|
// TODO refactor so more logic is shared with `send_header`
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn send_header_two<'a>(
|
fn send_header_two<'a>(
|
||||||
arena: &'a Bump,
|
|
||||||
info: PlatformHeaderInfo<'a>,
|
info: PlatformHeaderInfo<'a>,
|
||||||
parse_state: parser::State<'a>,
|
parse_state: parser::State<'a>,
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
|
@ -3067,7 +3048,7 @@ fn send_header_two<'a>(
|
||||||
|
|
||||||
// For each of our imports, add an entry to deps_by_name
|
// For each of our imports, add an entry to deps_by_name
|
||||||
//
|
//
|
||||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
// e.g. for `imports [ pf.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||||
//
|
//
|
||||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||||
|
@ -3105,15 +3086,17 @@ fn send_header_two<'a>(
|
||||||
.entry(app_module_id)
|
.entry(app_module_id)
|
||||||
.or_insert_with(IdentIds::default);
|
.or_insert_with(IdentIds::default);
|
||||||
|
|
||||||
for (loc_ident, _) in unpack_exposes_entries(arena, requires) {
|
for entry in requires {
|
||||||
let ident: Ident = loc_ident.value.into();
|
let entry = entry.value;
|
||||||
|
|
||||||
|
let ident: Ident = entry.ident.value.into();
|
||||||
let ident_id = ident_ids.get_or_insert(&ident);
|
let ident_id = ident_ids.get_or_insert(&ident);
|
||||||
let symbol = Symbol::new(app_module_id, ident_id);
|
let symbol = Symbol::new(app_module_id, ident_id);
|
||||||
|
|
||||||
// Since this value is exposed, add it to our module's default scope.
|
// Since this value is exposed, add it to our module's default scope.
|
||||||
debug_assert!(!scope.contains_key(&ident.clone()));
|
debug_assert!(!scope.contains_key(&ident.clone()));
|
||||||
|
|
||||||
scope.insert(ident, (symbol, loc_ident.region));
|
scope.insert(ident, (symbol, entry.ident.region));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3146,24 +3129,10 @@ fn send_header_two<'a>(
|
||||||
ident_ids.clone()
|
ident_ids.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
let package_entries = packages
|
||||||
let mut package_entries = MutMap::default();
|
.iter()
|
||||||
|
.map(|pkg| (pkg.value.shorthand, pkg.value.package_or_path.value))
|
||||||
while let Some(parse_entry) = parse_entries.pop() {
|
.collect::<MutMap<_, _>>();
|
||||||
use PackageEntry::*;
|
|
||||||
match parse_entry {
|
|
||||||
Entry {
|
|
||||||
shorthand,
|
|
||||||
package_or_path,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
package_entries.insert(*shorthand, package_or_path.value);
|
|
||||||
}
|
|
||||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
|
||||||
parse_entries.push(inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the deps to the coordinator thread for processing,
|
// Send the deps to the coordinator thread for processing,
|
||||||
// then continue on to parsing and canonicalizing defs.
|
// then continue on to parsing and canonicalizing defs.
|
||||||
|
@ -3342,6 +3311,16 @@ fn run_solve<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unspace<'a, T: Copy>(arena: &'a Bump, items: &[Located<Spaced<'a, T>>]) -> &'a [Located<T>] {
|
||||||
|
bumpalo::collections::Vec::from_iter_in(
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.map(|item| Located::at(item.region, item.value.extract_spaces().item)),
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.into_bump_slice()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn fabricate_pkg_config_module<'a>(
|
fn fabricate_pkg_config_module<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -3355,8 +3334,6 @@ fn fabricate_pkg_config_module<'a>(
|
||||||
header_src: &'a str,
|
header_src: &'a str,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
) -> (ModuleId, Msg<'a>) {
|
) -> (ModuleId, Msg<'a>) {
|
||||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = header.provides.items;
|
|
||||||
|
|
||||||
let info = PlatformHeaderInfo {
|
let info = PlatformHeaderInfo {
|
||||||
filename,
|
filename,
|
||||||
is_root_module: false,
|
is_root_module: false,
|
||||||
|
@ -3364,13 +3341,15 @@ fn fabricate_pkg_config_module<'a>(
|
||||||
header_src,
|
header_src,
|
||||||
app_module_id,
|
app_module_id,
|
||||||
packages: &[],
|
packages: &[],
|
||||||
provides,
|
provides: unspace(arena, header.provides.items),
|
||||||
requires: arena.alloc([header.requires.signature]),
|
requires: &*arena.alloc([Located::at(
|
||||||
imports: header.imports.items,
|
header.requires.signature.region,
|
||||||
|
header.requires.signature.extract_spaces().item,
|
||||||
|
)]),
|
||||||
|
imports: unspace(arena, header.imports.items),
|
||||||
};
|
};
|
||||||
|
|
||||||
send_header_two(
|
send_header_two(
|
||||||
arena,
|
|
||||||
info,
|
info,
|
||||||
parse_state,
|
parse_state,
|
||||||
module_ids,
|
module_ids,
|
||||||
|
@ -3413,12 +3392,12 @@ fn fabricate_effects_module<'a>(
|
||||||
let mut module_ids = (*module_ids).lock();
|
let mut module_ids = (*module_ids).lock();
|
||||||
|
|
||||||
for exposed in header.exposes.iter() {
|
for exposed in header.exposes.iter() {
|
||||||
if let ExposesEntry::Exposed(module_name) = exposed.value {
|
let module_name = exposed.value.extract_spaces().item;
|
||||||
module_ids.get_or_insert(&PQModuleName::Qualified(
|
|
||||||
shorthand,
|
module_ids.get_or_insert(&PQModuleName::Qualified(
|
||||||
module_name.as_str().into(),
|
shorthand,
|
||||||
));
|
module_name.as_str().into(),
|
||||||
}
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3633,33 +3612,16 @@ fn fabricate_effects_module<'a>(
|
||||||
|
|
||||||
fn unpack_exposes_entries<'a>(
|
fn unpack_exposes_entries<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
entries: &'a [Located<TypedIdent<'a>>],
|
entries: &'a [Located<Spaced<'a, TypedIdent<'a>>>],
|
||||||
) -> bumpalo::collections::Vec<'a, (&'a Located<&'a str>, &'a Located<TypeAnnotation<'a>>)> {
|
) -> bumpalo::collections::Vec<'a, (Located<&'a str>, Located<TypeAnnotation<'a>>)> {
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
let mut stack: Vec<&TypedIdent> = Vec::with_capacity_in(entries.len(), arena);
|
let iter = entries.iter().map(|entry| {
|
||||||
let mut output = Vec::with_capacity_in(entries.len(), arena);
|
let entry: TypedIdent<'a> = entry.value.extract_spaces().item;
|
||||||
|
(entry.ident, entry.ann)
|
||||||
|
});
|
||||||
|
|
||||||
for entry in entries.iter() {
|
Vec::from_iter_in(iter, arena)
|
||||||
stack.push(&entry.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some(effects_entry) = stack.pop() {
|
|
||||||
match effects_entry {
|
|
||||||
TypedIdent::Entry {
|
|
||||||
ident,
|
|
||||||
spaces_before_colon: _,
|
|
||||||
ann,
|
|
||||||
} => {
|
|
||||||
output.push((ident, ann));
|
|
||||||
}
|
|
||||||
TypedIdent::SpaceAfter(nested, _) | TypedIdent::SpaceBefore(nested, _) => {
|
|
||||||
stack.push(nested);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -3849,21 +3811,11 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
|
||||||
|
|
||||||
(qualified_module_name, exposed)
|
(qualified_module_name, exposed)
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
|
||||||
// Ignore spaces.
|
|
||||||
exposed_from_import(*sub_entry)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident_from_exposed(entry: &ExposesEntry<'_, &str>) -> Ident {
|
fn ident_from_exposed(entry: &Spaced<'_, ExposedName<'_>>) -> Ident {
|
||||||
use roc_parse::header::ExposesEntry::*;
|
entry.extract_spaces().item.as_str().into()
|
||||||
|
|
||||||
match entry {
|
|
||||||
Exposed(ident) => (*ident).into(),
|
|
||||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -4429,7 +4381,7 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
||||||
alloc.reflow("I could not find a platform based on your input file."),
|
alloc.reflow("I could not find a platform based on your input file."),
|
||||||
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
|
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
|
||||||
alloc
|
alloc
|
||||||
.parser_suggestion(" packages { base: \"platform\" }")
|
.parser_suggestion(" packages { pf: \"platform\" }")
|
||||||
.indent(4),
|
.indent(4),
|
||||||
alloc.reflow("See also TODO."),
|
alloc.reflow("See also TODO."),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -359,7 +359,7 @@ impl fmt::Debug for ModuleId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// base.Task
|
/// pf.Task
|
||||||
/// 1. build mapping from short name to package
|
/// 1. build mapping from short name to package
|
||||||
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
|
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
|
||||||
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
|
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::ir::{
|
||||||
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc,
|
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc,
|
||||||
ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
||||||
};
|
};
|
||||||
use crate::layout::{Builtin, Layout};
|
use crate::layout::{Builtin, Layout, UnionLayout};
|
||||||
|
|
||||||
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
|
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
|
||||||
const LAYOUT_UNIT: Layout = Layout::Struct(&[]);
|
const LAYOUT_UNIT: Layout = Layout::Struct(&[]);
|
||||||
|
@ -21,71 +21,79 @@ const LAYOUT_U32: Layout = Layout::Builtin(Builtin::Int(IntWidth::U32));
|
||||||
pub const REFCOUNT_MAX: usize = 0;
|
pub const REFCOUNT_MAX: usize = 0;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum RefcountOp {
|
pub enum HelperOp {
|
||||||
Inc,
|
Inc,
|
||||||
Dec,
|
Dec,
|
||||||
DecRef,
|
DecRef,
|
||||||
|
Eq,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate specialized refcounting code in mono IR format
|
impl From<&ModifyRc> for HelperOp {
|
||||||
/// -------------------------------------------------------
|
fn from(modify: &ModifyRc) -> Self {
|
||||||
|
match modify {
|
||||||
|
ModifyRc::Inc(..) => Self::Inc,
|
||||||
|
ModifyRc::Dec(_) => Self::Dec,
|
||||||
|
ModifyRc::DecRef(_) => Self::DecRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate specialized helper procs for code gen
|
||||||
|
/// ----------------------------------------------
|
||||||
///
|
///
|
||||||
/// Any backend that wants to use this, needs a field of type `RefcountProcGenerator`.
|
/// Some low level operations need specialized helper procs to traverse data structures at runtime.
|
||||||
|
/// This includes refcounting, hashing, and equality checks.
|
||||||
///
|
///
|
||||||
/// Whenever the backend sees a `Stmt::Refcounting`, it calls
|
/// For example, when checking List equality, we need to visit each element and compare them.
|
||||||
/// `RefcountProcGenerator::expand_refcount_stmt()`, which returns IR statements
|
/// Depending on the type of the list elements, we may need to recurse deeper into each element.
|
||||||
/// to call a refcounting procedure. The backend can then generate target code
|
/// For tag unions, we may need branches for different tag IDs, etc.
|
||||||
/// for those IR statements instead of the original `Refcounting` statement.
|
|
||||||
///
|
///
|
||||||
/// Essentially we are expanding the `Refcounting` statement into a more detailed
|
/// This module creates specialized helper procs for all such operations and types used in the program.
|
||||||
/// form that's more suitable for code generation.
|
|
||||||
///
|
///
|
||||||
/// But so far, we've only mentioned _calls_ to the refcounting procedures.
|
/// The backend drives the process, in two steps:
|
||||||
/// The procedures themselves don't exist yet!
|
/// 1) When it sees the relevant node, it calls CodeGenHelp to get the replacement IR.
|
||||||
|
/// CodeGenHelp returns IR for a call to the helper proc, and remembers the specialization.
|
||||||
|
/// 2) After the backend has generated code for all user procs, it takes the IR for all of the
|
||||||
|
/// specialized helpers procs, and generates target code for them too.
|
||||||
///
|
///
|
||||||
/// So when the backend has finished with all the `Proc`s from user code,
|
pub struct CodeGenHelp<'a> {
|
||||||
/// it's time to call `RefcountProcGenerator::generate_refcount_procs()`,
|
|
||||||
/// which generates the `Procs` for refcounting helpers. The backend can
|
|
||||||
/// simply generate target code for these `Proc`s just like any other Proc.
|
|
||||||
///
|
|
||||||
pub struct RefcountProcGenerator<'a> {
|
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ptr_size: u32,
|
ptr_size: u32,
|
||||||
layout_isize: Layout<'a>,
|
layout_isize: Layout<'a>,
|
||||||
/// List of refcounting procs to generate, specialised by Layout and RefCountOp
|
/// Specializations to generate
|
||||||
/// Order of insertion is preserved, since it is important for Wasm backend
|
/// Order of insertion is preserved, since it is important for Wasm backend
|
||||||
procs_to_generate: Vec<'a, (Layout<'a>, RefcountOp, Symbol)>,
|
specs: Vec<'a, (Layout<'a>, HelperOp, Symbol)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RefcountProcGenerator<'a> {
|
impl<'a> CodeGenHelp<'a> {
|
||||||
pub fn new(arena: &'a Bump, intwidth_isize: IntWidth, home: ModuleId) -> Self {
|
pub fn new(arena: &'a Bump, intwidth_isize: IntWidth, home: ModuleId) -> Self {
|
||||||
RefcountProcGenerator {
|
CodeGenHelp {
|
||||||
arena,
|
arena,
|
||||||
home,
|
home,
|
||||||
ptr_size: intwidth_isize.stack_size(),
|
ptr_size: intwidth_isize.stack_size(),
|
||||||
layout_isize: Layout::Builtin(Builtin::Int(intwidth_isize)),
|
layout_isize: Layout::Builtin(Builtin::Int(intwidth_isize)),
|
||||||
procs_to_generate: Vec::with_capacity_in(16, arena),
|
specs: Vec::with_capacity_in(16, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands the IR node Stmt::Refcounting to a more detailed IR Stmt that calls a helper proc.
|
/// Expand a `Refcounting` node to a `Let` node that calls a specialized helper proc.
|
||||||
/// The helper procs themselves can be generated later by calling `generate_refcount_procs`
|
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||||
pub fn expand_refcount_stmt(
|
pub fn expand_refcount_stmt(
|
||||||
&mut self,
|
&mut self,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
layout: Layout<'a>,
|
layout: Layout<'a>,
|
||||||
modify: &ModifyRc,
|
modify: &ModifyRc,
|
||||||
following: &'a Stmt<'a>,
|
following: &'a Stmt<'a>,
|
||||||
) -> (&'a Stmt<'a>, Option<(Symbol, ProcLayout<'a>)>) {
|
) -> (&'a Stmt<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||||
if !Self::layout_is_supported(&layout) {
|
if !Self::is_rc_implemented_yet(&layout) {
|
||||||
// Just a warning, so we can decouple backend development from refcounting development.
|
// Just a warning, so we can decouple backend development from refcounting development.
|
||||||
// When we are closer to completion, we can change it to a panic.
|
// When we are closer to completion, we can change it to a panic.
|
||||||
println!(
|
println!(
|
||||||
"WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}",
|
"WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}",
|
||||||
layout
|
layout
|
||||||
);
|
);
|
||||||
return (following, None);
|
return (following, Vec::new_in(self.arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = self.arena;
|
let arena = self.arena;
|
||||||
|
@ -94,8 +102,8 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
ModifyRc::Inc(structure, amount) => {
|
ModifyRc::Inc(structure, amount) => {
|
||||||
let layout_isize = self.layout_isize;
|
let layout_isize = self.layout_isize;
|
||||||
|
|
||||||
let (is_existing, proc_name) =
|
let (proc_name, new_procs_info) =
|
||||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Inc);
|
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Inc);
|
||||||
|
|
||||||
// Define a constant for the amount to increment
|
// Define a constant for the amount to increment
|
||||||
let amount_sym = self.create_symbol(ident_ids, "amount");
|
let amount_sym = self.create_symbol(ident_ids, "amount");
|
||||||
|
@ -117,28 +125,14 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
||||||
let rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt)));
|
let rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt)));
|
||||||
|
|
||||||
// Create a linker symbol for the helper proc if this is the first usage
|
(rc_stmt, new_procs_info)
|
||||||
let new_proc_info = if is_existing {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((
|
|
||||||
proc_name,
|
|
||||||
ProcLayout {
|
|
||||||
arguments: arg_layouts,
|
|
||||||
result: LAYOUT_UNIT,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
(rc_stmt, new_proc_info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModifyRc::Dec(structure) => {
|
ModifyRc::Dec(structure) => {
|
||||||
let (is_existing, proc_name) =
|
let (proc_name, new_procs_info) =
|
||||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Dec);
|
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Dec);
|
||||||
|
|
||||||
// Call helper proc, passing the Roc structure
|
// Call helper proc, passing the Roc structure
|
||||||
let arg_layouts = arena.alloc([layout, self.layout_isize]);
|
|
||||||
let call_result_empty = self.create_symbol(ident_ids, "call_result_empty");
|
let call_result_empty = self.create_symbol(ident_ids, "call_result_empty");
|
||||||
let call_expr = Expr::Call(Call {
|
let call_expr = Expr::Call(Call {
|
||||||
call_type: CallType::ByName {
|
call_type: CallType::ByName {
|
||||||
|
@ -157,20 +151,7 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
following,
|
following,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create a linker symbol for the helper proc if this is the first usage
|
(rc_stmt, new_procs_info)
|
||||||
let new_proc_info = if is_existing {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((
|
|
||||||
proc_name,
|
|
||||||
ProcLayout {
|
|
||||||
arguments: arg_layouts,
|
|
||||||
result: LAYOUT_UNIT,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
(rc_stmt, new_proc_info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModifyRc::DecRef(structure) => {
|
ModifyRc::DecRef(structure) => {
|
||||||
|
@ -199,67 +180,202 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
let call_stmt = Stmt::Let(call_result_empty, call_expr, LAYOUT_UNIT, following);
|
||||||
let rc_stmt = arena.alloc(rc_ptr_stmt(arena.alloc(call_stmt)));
|
let rc_stmt = arena.alloc(rc_ptr_stmt(arena.alloc(call_stmt)));
|
||||||
|
|
||||||
(rc_stmt, None)
|
(rc_stmt, Vec::new_in(self.arena))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: consider refactoring so that we have just one place to define what's supported
|
/// Replace a generic `Lowlevel::Eq` call with a specialized helper proc.
|
||||||
// (Probably by generating procs on the fly instead of all at the end)
|
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||||
fn layout_is_supported(layout: &Layout) -> bool {
|
pub fn specialize_equals(
|
||||||
|
&mut self,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
arguments: &'a [Symbol],
|
||||||
|
) -> (&'a Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||||
|
// Record a specialization and get its name
|
||||||
|
let (proc_name, new_procs_info) =
|
||||||
|
self.get_or_create_proc_symbols_recursive(ident_ids, layout, HelperOp::Eq);
|
||||||
|
|
||||||
|
// Call the specialized helper
|
||||||
|
let arg_layouts = self.arena.alloc([*layout, *layout]);
|
||||||
|
let expr = self.arena.alloc(Expr::Call(Call {
|
||||||
|
call_type: CallType::ByName {
|
||||||
|
name: proc_name,
|
||||||
|
ret_layout: &LAYOUT_BOOL,
|
||||||
|
arg_layouts,
|
||||||
|
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||||
|
},
|
||||||
|
arguments,
|
||||||
|
}));
|
||||||
|
|
||||||
|
(expr, new_procs_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if refcounting is implemented yet. In the long term, this will be deleted.
|
||||||
|
// In the short term, it helps us to skip refcounting and let it leak, so we can make
|
||||||
|
// progress incrementally. Kept in sync with generate_procs using assertions.
|
||||||
|
fn is_rc_implemented_yet(layout: &Layout) -> bool {
|
||||||
matches!(layout, Layout::Builtin(Builtin::Str))
|
matches!(layout, Layout::Builtin(Builtin::Str))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate refcounting helper procs, each specialized to a particular Layout.
|
/// Generate refcounting helper procs, each specialized to a particular Layout.
|
||||||
/// For example `List (Result { a: Str, b: Int } Str)` would get its own helper
|
/// For example `List (Result { a: Str, b: Int } Str)` would get its own helper
|
||||||
/// to update the refcounts on the List, the Result and the strings.
|
/// to update the refcounts on the List, the Result and the strings.
|
||||||
pub fn generate_refcount_procs(
|
pub fn generate_procs(
|
||||||
&mut self,
|
&mut self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
) -> Vec<'a, Proc<'a>> {
|
) -> Vec<'a, Proc<'a>> {
|
||||||
// Move the vector out of self, so we can loop over it safely
|
use HelperOp::*;
|
||||||
let mut procs_to_generate =
|
|
||||||
std::mem::replace(&mut self.procs_to_generate, Vec::with_capacity_in(0, arena));
|
|
||||||
|
|
||||||
let procs_iter = procs_to_generate
|
// Move the vector out of self, so we can loop over it safely
|
||||||
.drain(0..)
|
let mut specs = std::mem::replace(&mut self.specs, Vec::with_capacity_in(0, arena));
|
||||||
.map(|(layout, op, proc_symbol)| {
|
|
||||||
debug_assert!(Self::layout_is_supported(&layout));
|
let procs_iter = specs.drain(0..).map(|(layout, op, proc_symbol)| match op {
|
||||||
|
Inc | Dec | DecRef => {
|
||||||
|
debug_assert!(Self::is_rc_implemented_yet(&layout));
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Str) => {
|
Layout::Builtin(Builtin::Str) => {
|
||||||
self.gen_modify_str(ident_ids, op, proc_symbol)
|
self.gen_modify_str(ident_ids, op, proc_symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => todo!("Please update layout_is_supported for {:?}", layout),
|
_ => todo!("Please update is_rc_implemented_yet for `{:?}`", layout),
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
Eq => match layout {
|
||||||
|
Layout::Builtin(
|
||||||
|
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
|
||||||
|
) => panic!(
|
||||||
|
"No generated helper proc. Use direct code gen for {:?}",
|
||||||
|
layout
|
||||||
|
),
|
||||||
|
|
||||||
|
Layout::Builtin(Builtin::Str) => {
|
||||||
|
panic!("No generated helper proc. Use Zig builtin for Str.")
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => todo!("Specialized equality check for `{:?}`", layout),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Vec::from_iter_in(procs_iter, arena)
|
Vec::from_iter_in(procs_iter, arena)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the Symbol of the procedure for this layout and refcount operation,
|
/// Find the Symbol of the procedure for this layout and operation
|
||||||
/// or create one if needed.
|
/// If any new helper procs are needed for this layout or its children,
|
||||||
fn get_proc_symbol(
|
/// return their details in a vector.
|
||||||
|
fn get_or_create_proc_symbols_recursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
layout: Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
op: RefcountOp,
|
op: HelperOp,
|
||||||
) -> (bool, Symbol) {
|
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||||
let found = self
|
let mut new_procs_info = Vec::new_in(self.arena);
|
||||||
.procs_to_generate
|
|
||||||
.iter()
|
let proc_symbol =
|
||||||
.find(|(l, o, _)| *l == layout && *o == op);
|
self.get_or_create_proc_symbols_visit(ident_ids, &mut new_procs_info, op, layout);
|
||||||
|
|
||||||
|
(proc_symbol, new_procs_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_create_proc_symbols_visit(
|
||||||
|
&mut self,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
new_procs_info: &mut Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||||
|
op: HelperOp,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) -> Symbol {
|
||||||
|
if let Layout::LambdaSet(lambda_set) = layout {
|
||||||
|
return self.get_or_create_proc_symbols_visit(
|
||||||
|
ident_ids,
|
||||||
|
new_procs_info,
|
||||||
|
op,
|
||||||
|
&lambda_set.runtime_representation(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (symbol, new_proc_layout) = self.get_or_create_proc_symbol(ident_ids, layout, op);
|
||||||
|
|
||||||
|
if let Some(proc_layout) = new_proc_layout {
|
||||||
|
new_procs_info.push((symbol, proc_layout));
|
||||||
|
|
||||||
|
let mut visit_child = |child| {
|
||||||
|
self.get_or_create_proc_symbols_visit(ident_ids, new_procs_info, op, child);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut visit_children = |children: &'a [Layout]| {
|
||||||
|
for child in children {
|
||||||
|
visit_child(child);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut visit_tags = |tags: &'a [&'a [Layout]]| {
|
||||||
|
for tag in tags {
|
||||||
|
visit_children(tag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Builtin::Dict(key, value) => {
|
||||||
|
visit_child(key);
|
||||||
|
visit_child(value);
|
||||||
|
}
|
||||||
|
Builtin::Set(element) | Builtin::List(element) => visit_child(element),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Layout::Struct(fields) => visit_children(fields),
|
||||||
|
Layout::Union(union_layout) => match union_layout {
|
||||||
|
UnionLayout::NonRecursive(tags) => visit_tags(tags),
|
||||||
|
UnionLayout::Recursive(tags) => visit_tags(tags),
|
||||||
|
UnionLayout::NonNullableUnwrapped(fields) => visit_children(fields),
|
||||||
|
UnionLayout::NullableWrapped { other_tags, .. } => visit_tags(other_tags),
|
||||||
|
UnionLayout::NullableUnwrapped { other_fields, .. } => {
|
||||||
|
visit_children(other_fields)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::LambdaSet(_) => unreachable!(),
|
||||||
|
Layout::RecursivePointer => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_create_proc_symbol(
|
||||||
|
&mut self,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
op: HelperOp,
|
||||||
|
) -> (Symbol, Option<ProcLayout<'a>>) {
|
||||||
|
let found = self.specs.iter().find(|(l, o, _)| l == layout && *o == op);
|
||||||
|
|
||||||
if let Some((_, _, existing_symbol)) = found {
|
if let Some((_, _, existing_symbol)) = found {
|
||||||
(true, *existing_symbol)
|
(*existing_symbol, None)
|
||||||
} else {
|
} else {
|
||||||
let layout_name = layout_debug_name(&layout);
|
let layout_name = layout_debug_name(layout);
|
||||||
let unique_idx = self.procs_to_generate.len();
|
let debug_name = format!("#help{:?}_{}", op, layout_name);
|
||||||
let debug_name = format!("#rc{:?}_{}_{}", op, layout_name, unique_idx);
|
|
||||||
let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name);
|
let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name);
|
||||||
self.procs_to_generate.push((layout, op, new_symbol));
|
self.specs.push((*layout, op, new_symbol));
|
||||||
(false, new_symbol)
|
|
||||||
|
let new_proc_layout = match op {
|
||||||
|
HelperOp::Inc => Some(ProcLayout {
|
||||||
|
arguments: self.arena.alloc([*layout, self.layout_isize]),
|
||||||
|
result: LAYOUT_UNIT,
|
||||||
|
}),
|
||||||
|
HelperOp::Dec => Some(ProcLayout {
|
||||||
|
arguments: self.arena.alloc([*layout]),
|
||||||
|
result: LAYOUT_UNIT,
|
||||||
|
}),
|
||||||
|
HelperOp::DecRef => None,
|
||||||
|
HelperOp::Eq => Some(ProcLayout {
|
||||||
|
arguments: self.arena.alloc([*layout, *layout]),
|
||||||
|
result: LAYOUT_BOOL,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
(new_symbol, new_proc_layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,14 +390,15 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
Stmt::Let(unit, Expr::Struct(&[]), LAYOUT_UNIT, ret_stmt)
|
Stmt::Let(unit, Expr::Struct(&[]), LAYOUT_UNIT, ret_stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_args(&self, op: RefcountOp, layout: Layout<'a>) -> &'a [(Layout<'a>, Symbol)] {
|
fn gen_args(&self, op: HelperOp, layout: Layout<'a>) -> &'a [(Layout<'a>, Symbol)] {
|
||||||
let roc_value = (layout, Symbol::ARG_1);
|
let roc_value = (layout, Symbol::ARG_1);
|
||||||
match op {
|
match op {
|
||||||
RefcountOp::Inc => {
|
HelperOp::Inc => {
|
||||||
let inc_amount = (self.layout_isize, Symbol::ARG_2);
|
let inc_amount = (self.layout_isize, Symbol::ARG_2);
|
||||||
self.arena.alloc([roc_value, inc_amount])
|
self.arena.alloc([roc_value, inc_amount])
|
||||||
}
|
}
|
||||||
RefcountOp::Dec | RefcountOp::DecRef => self.arena.alloc([roc_value]),
|
HelperOp::Dec | HelperOp::DecRef => self.arena.alloc([roc_value]),
|
||||||
|
HelperOp::Eq => self.arena.alloc([roc_value, (layout, Symbol::ARG_2)]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +406,7 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
fn gen_modify_str(
|
fn gen_modify_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
op: RefcountOp,
|
op: HelperOp,
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
) -> Proc<'a> {
|
) -> Proc<'a> {
|
||||||
let string = Symbol::ARG_1;
|
let string = Symbol::ARG_1;
|
||||||
|
@ -349,20 +466,21 @@ impl<'a> RefcountProcGenerator<'a> {
|
||||||
// Call the relevant Zig lowlevel to actually modify the refcount
|
// Call the relevant Zig lowlevel to actually modify the refcount
|
||||||
let zig_call_result = self.create_symbol(ident_ids, "zig_call_result");
|
let zig_call_result = self.create_symbol(ident_ids, "zig_call_result");
|
||||||
let zig_call_expr = match op {
|
let zig_call_expr = match op {
|
||||||
RefcountOp::Inc => Expr::Call(Call {
|
HelperOp::Inc => Expr::Call(Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
op: LowLevel::RefCountInc,
|
op: LowLevel::RefCountInc,
|
||||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
},
|
},
|
||||||
arguments: self.arena.alloc([rc_ptr, Symbol::ARG_2]),
|
arguments: self.arena.alloc([rc_ptr, Symbol::ARG_2]),
|
||||||
}),
|
}),
|
||||||
RefcountOp::Dec | RefcountOp::DecRef => Expr::Call(Call {
|
HelperOp::Dec | HelperOp::DecRef => Expr::Call(Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
op: LowLevel::RefCountDec,
|
op: LowLevel::RefCountDec,
|
||||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
},
|
},
|
||||||
arguments: self.arena.alloc([rc_ptr, alignment]),
|
arguments: self.arena.alloc([rc_ptr, alignment]),
|
||||||
}),
|
}),
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let zig_call_stmt = |next| Stmt::Let(zig_call_result, zig_call_expr, LAYOUT_UNIT, next);
|
let zig_call_stmt = |next| Stmt::Let(zig_call_result, zig_call_expr, LAYOUT_UNIT, next);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
pub mod alias_analysis;
|
pub mod alias_analysis;
|
||||||
pub mod borrow;
|
pub mod borrow;
|
||||||
pub mod gen_refcount;
|
pub mod code_gen_help;
|
||||||
pub mod inc_dec;
|
pub mod inc_dec;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::header::{
|
use crate::header::{AppHeader, InterfaceHeader, PlatformHeader};
|
||||||
AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PackageEntry, PlatformHeader,
|
|
||||||
PlatformRigid, TypedIdent,
|
|
||||||
};
|
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use bumpalo::collections::{String, Vec};
|
use bumpalo::collections::{String, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -17,6 +14,33 @@ pub struct Spaces<'a, T> {
|
||||||
pub after: &'a [CommentOrNewline<'a>],
|
pub after: &'a [CommentOrNewline<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
pub enum Spaced<'a, T> {
|
||||||
|
Item(T),
|
||||||
|
|
||||||
|
// Spaces
|
||||||
|
SpaceBefore(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||||
|
SpaceAfter(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Item(item) => item.fmt(f),
|
||||||
|
Self::SpaceBefore(item, space) => f
|
||||||
|
.debug_tuple("SpaceBefore")
|
||||||
|
.field(item)
|
||||||
|
.field(space)
|
||||||
|
.finish(),
|
||||||
|
Self::SpaceAfter(item, space) => f
|
||||||
|
.debug_tuple("SpaceAfter")
|
||||||
|
.field(item)
|
||||||
|
.field(space)
|
||||||
|
.finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ExtractSpaces<'a>: Sized + Copy {
|
pub trait ExtractSpaces<'a>: Sized + Copy {
|
||||||
type Item;
|
type Item;
|
||||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item>;
|
fn extract_spaces(&self) -> Spaces<'a, Self::Item>;
|
||||||
|
@ -674,6 +698,15 @@ pub trait Spaceable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Spaceable<'a> for Spaced<'a, T> {
|
||||||
|
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
|
Spaced::SpaceBefore(self, spaces)
|
||||||
|
}
|
||||||
|
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
|
Spaced::SpaceAfter(self, spaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for Expr<'a> {
|
impl<'a> Spaceable<'a> for Expr<'a> {
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
Expr::SpaceBefore(self, spaces)
|
Expr::SpaceBefore(self, spaces)
|
||||||
|
@ -701,24 +734,6 @@ impl<'a> Spaceable<'a> for TypeAnnotation<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for ImportsEntry<'a> {
|
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
ImportsEntry::SpaceBefore(self, spaces)
|
|
||||||
}
|
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
ImportsEntry::SpaceAfter(self, spaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for TypedIdent<'a> {
|
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
TypedIdent::SpaceBefore(self, spaces)
|
|
||||||
}
|
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
TypedIdent::SpaceAfter(self, spaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Val> Spaceable<'a> for AssignedField<'a, Val> {
|
impl<'a, Val> Spaceable<'a> for AssignedField<'a, Val> {
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
AssignedField::SpaceBefore(self, spaces)
|
AssignedField::SpaceBefore(self, spaces)
|
||||||
|
@ -823,8 +838,55 @@ macro_rules! impl_extract_spaces {
|
||||||
impl_extract_spaces!(Expr);
|
impl_extract_spaces!(Expr);
|
||||||
impl_extract_spaces!(Tag);
|
impl_extract_spaces!(Tag);
|
||||||
impl_extract_spaces!(AssignedField<T>);
|
impl_extract_spaces!(AssignedField<T>);
|
||||||
impl_extract_spaces!(PlatformRigid);
|
|
||||||
impl_extract_spaces!(TypedIdent);
|
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||||
impl_extract_spaces!(ImportsEntry);
|
type Item = T;
|
||||||
impl_extract_spaces!(ExposesEntry<T>);
|
|
||||||
impl_extract_spaces!(PackageEntry);
|
fn extract_spaces(&self) -> Spaces<'a, T> {
|
||||||
|
match self {
|
||||||
|
Spaced::SpaceBefore(item, before) => match item {
|
||||||
|
Spaced::SpaceBefore(_, _) => todo!(),
|
||||||
|
Spaced::SpaceAfter(item, after) => {
|
||||||
|
if let Spaced::Item(item) = item {
|
||||||
|
Spaces {
|
||||||
|
before,
|
||||||
|
item: *item,
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spaced::Item(item) => Spaces {
|
||||||
|
before,
|
||||||
|
item: *item,
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spaced::SpaceAfter(item, after) => match item {
|
||||||
|
Spaced::SpaceBefore(item, before) => {
|
||||||
|
if let Spaced::Item(item) = item {
|
||||||
|
Spaces {
|
||||||
|
before,
|
||||||
|
item: *item,
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spaced::SpaceAfter(_, _) => todo!(),
|
||||||
|
Spaced::Item(item) => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: *item,
|
||||||
|
after,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spaced::Item(item) => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: *item,
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ast::{Collection, CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation};
|
||||||
use crate::blankspace::space0_e;
|
use crate::blankspace::space0_e;
|
||||||
use crate::ident::lowercase_ident;
|
use crate::ident::lowercase_ident;
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
|
@ -57,11 +57,30 @@ impl<'a> ModuleName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct ExposedName<'a>(&'a str);
|
||||||
|
|
||||||
|
impl<'a> From<ExposedName<'a>> for &'a str {
|
||||||
|
fn from(name: ExposedName<'a>) -> Self {
|
||||||
|
name.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExposedName<'a> {
|
||||||
|
pub fn new(name: &'a str) -> Self {
|
||||||
|
ExposedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&'a self) -> &'a str {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct InterfaceHeader<'a> {
|
pub struct InterfaceHeader<'a> {
|
||||||
pub name: Loc<ModuleName<'a>>,
|
pub name: Loc<ModuleName<'a>>,
|
||||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
|
|
||||||
// Potential comments and newlines - these will typically all be empty.
|
// Potential comments and newlines - these will typically all be empty.
|
||||||
pub before_header: &'a [CommentOrNewline<'a>],
|
pub before_header: &'a [CommentOrNewline<'a>],
|
||||||
|
@ -81,9 +100,9 @@ pub enum To<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct AppHeader<'a> {
|
pub struct AppHeader<'a> {
|
||||||
pub name: Loc<StrLiteral<'a>>,
|
pub name: Loc<StrLiteral<'a>>,
|
||||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
pub to: Loc<To<'a>>,
|
pub to: Loc<To<'a>>,
|
||||||
|
|
||||||
// Potential comments and newlines - these will typically all be empty.
|
// Potential comments and newlines - these will typically all be empty.
|
||||||
|
@ -102,7 +121,7 @@ pub struct AppHeader<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PackageHeader<'a> {
|
pub struct PackageHeader<'a> {
|
||||||
pub name: Loc<PackageName<'a>>,
|
pub name: Loc<PackageName<'a>>,
|
||||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
|
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
|
||||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||||
|
|
||||||
|
@ -118,37 +137,25 @@ pub struct PackageHeader<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum PlatformRigid<'a> {
|
pub struct PlatformRigid<'a> {
|
||||||
Entry { rigid: &'a str, alias: &'a str },
|
pub rigid: &'a str,
|
||||||
|
pub alias: &'a str,
|
||||||
// Spaces
|
|
||||||
SpaceBefore(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
SpaceAfter(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for PlatformRigid<'a> {
|
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
PlatformRigid::SpaceBefore(self, spaces)
|
|
||||||
}
|
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
PlatformRigid::SpaceAfter(self, spaces)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PlatformRequires<'a> {
|
pub struct PlatformRequires<'a> {
|
||||||
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
|
pub rigids: Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>,
|
||||||
pub signature: Loc<TypedIdent<'a>>,
|
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PlatformHeader<'a> {
|
pub struct PlatformHeader<'a> {
|
||||||
pub name: Loc<PackageName<'a>>,
|
pub name: Loc<PackageName<'a>>,
|
||||||
pub requires: PlatformRequires<'a>,
|
pub requires: PlatformRequires<'a>,
|
||||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
pub effects: Effects<'a>,
|
pub effects: Effects<'a>,
|
||||||
|
|
||||||
// Potential comments and newlines - these will typically all be empty.
|
// Potential comments and newlines - these will typically all be empty.
|
||||||
|
@ -174,26 +181,7 @@ pub struct Effects<'a> {
|
||||||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||||
pub effect_shortname: &'a str,
|
pub effect_shortname: &'a str,
|
||||||
pub effect_type_name: &'a str,
|
pub effect_type_name: &'a str,
|
||||||
pub entries: Collection<'a, Loc<TypedIdent<'a>>>,
|
pub entries: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum ExposesEntry<'a, T> {
|
|
||||||
/// e.g. `Task`
|
|
||||||
Exposed(T),
|
|
||||||
|
|
||||||
// Spaces
|
|
||||||
SpaceBefore(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
|
||||||
SpaceAfter(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
|
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
ExposesEntry::SpaceBefore(self, spaces)
|
|
||||||
}
|
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
ExposesEntry::SpaceAfter(self, spaces)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -201,71 +189,35 @@ pub enum ImportsEntry<'a> {
|
||||||
/// e.g. `Task` or `Task.{ Task, after }`
|
/// e.g. `Task` or `Task.{ Task, after }`
|
||||||
Module(
|
Module(
|
||||||
ModuleName<'a>,
|
ModuleName<'a>,
|
||||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
),
|
),
|
||||||
|
|
||||||
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
|
/// e.g. `pf.Task` or `pf.Task.{ after }` or `pf.{ Task.{ Task, after } }`
|
||||||
Package(
|
Package(
|
||||||
&'a str,
|
&'a str,
|
||||||
ModuleName<'a>,
|
ModuleName<'a>,
|
||||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Spaces
|
|
||||||
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExposesEntry<'a, &'a str> {
|
/// e.g.
|
||||||
pub fn as_str(&'a self) -> &'a str {
|
///
|
||||||
use ExposesEntry::*;
|
/// printLine : Str -> Effect {}
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
match self {
|
pub struct TypedIdent<'a> {
|
||||||
Exposed(string) => string,
|
pub ident: Loc<&'a str>,
|
||||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
pub spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||||
}
|
pub ann: Loc<TypeAnnotation<'a>>,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum TypedIdent<'a> {
|
pub struct PackageEntry<'a> {
|
||||||
/// e.g.
|
pub shorthand: &'a str,
|
||||||
///
|
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||||
/// printLine : Str -> Effect {}
|
pub package_or_path: Loc<PackageOrPath<'a>>,
|
||||||
Entry {
|
|
||||||
ident: Loc<&'a str>,
|
|
||||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
|
||||||
ann: Loc<TypeAnnotation<'a>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Spaces
|
|
||||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||||
pub enum PackageEntry<'a> {
|
|
||||||
Entry {
|
|
||||||
shorthand: &'a str,
|
|
||||||
spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
|
||||||
package_or_path: Loc<PackageOrPath<'a>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Spaces
|
|
||||||
SpaceBefore(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
SpaceAfter(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for PackageEntry<'a> {
|
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
PackageEntry::SpaceBefore(self, spaces)
|
|
||||||
}
|
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
|
||||||
PackageEntry::SpaceAfter(self, spaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a>> {
|
|
||||||
move |arena, state| {
|
move |arena, state| {
|
||||||
// You may optionally have a package shorthand,
|
// You may optionally have a package shorthand,
|
||||||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||||
|
@ -293,19 +245,19 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
let entry = match opt_shorthand {
|
let entry = match opt_shorthand {
|
||||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||||
shorthand,
|
shorthand,
|
||||||
spaces_after_shorthand,
|
spaces_after_shorthand,
|
||||||
package_or_path,
|
package_or_path,
|
||||||
},
|
},
|
||||||
None => PackageEntry::Entry {
|
None => PackageEntry {
|
||||||
shorthand: "",
|
shorthand: "",
|
||||||
spaces_after_shorthand: &[],
|
spaces_after_shorthand: &[],
|
||||||
package_or_path,
|
package_or_path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((MadeProgress, entry, state))
|
Ok((MadeProgress, Spaced::Item(entry), state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ast::{Collection, CommentOrNewline, Def, Module};
|
use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced};
|
||||||
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, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
|
package_entry, package_name, package_or_path, AppHeader, Effects, ExposedName, ImportsEntry,
|
||||||
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To,
|
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To,
|
||||||
TypedIdent,
|
TypedIdent,
|
||||||
};
|
};
|
||||||
|
@ -220,7 +220,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
let opt_imports: Option<(
|
let opt_imports: Option<(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
)> = opt_imports;
|
)> = opt_imports;
|
||||||
|
|
||||||
let ((before_imports, after_imports), imports) =
|
let ((before_imports, after_imports), imports) =
|
||||||
|
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ProvidesTo<'a> {
|
struct ProvidesTo<'a> {
|
||||||
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||||
to: Located<To<'a>>,
|
to: Located<To<'a>>,
|
||||||
|
|
||||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||||
|
@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
(
|
(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||||
),
|
),
|
||||||
EProvides<'a>,
|
EProvides<'a>,
|
||||||
> {
|
> {
|
||||||
|
@ -385,14 +385,14 @@ fn provides_without_to<'a>() -> impl Parser<
|
||||||
EProvides::Open,
|
EProvides::Open,
|
||||||
EProvides::Space,
|
EProvides::Space,
|
||||||
EProvides::IndentListEnd,
|
EProvides::IndentListEnd,
|
||||||
ExposesEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exposes_entry<'a, F, E>(
|
fn exposes_entry<'a, F, E>(
|
||||||
to_expectation: F,
|
to_expectation: F,
|
||||||
) -> impl Parser<'a, Located<ExposesEntry<'a, &'a str>>, E>
|
) -> impl Parser<'a, Located<Spaced<'a, ExposedName<'a>>>, E>
|
||||||
where
|
where
|
||||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||||
F: Copy,
|
F: Copy,
|
||||||
|
@ -400,7 +400,7 @@ where
|
||||||
{
|
{
|
||||||
loc!(map!(
|
loc!(map!(
|
||||||
specialize(|_, r, c| to_expectation(r, c), unqualified_ident()),
|
specialize(|_, r, c| to_expectation(r, c), unqualified_ident()),
|
||||||
ExposesEntry::Exposed
|
|n| Spaced::Item(ExposedName::new(n))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn requires_rigids<'a>(
|
fn requires_rigids<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
|
) -> impl Parser<'a, Collection<'a, Located<Spaced<'a, PlatformRigid<'a>>>>, ERequires<'a>> {
|
||||||
collection_trailing_sep_e!(
|
collection_trailing_sep_e!(
|
||||||
word1(b'{', ERequires::ListStart),
|
word1(b'{', ERequires::ListStart),
|
||||||
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
||||||
|
@ -454,23 +454,24 @@ fn requires_rigids<'a>(
|
||||||
ERequires::Open,
|
ERequires::Open,
|
||||||
ERequires::Space,
|
ERequires::Space,
|
||||||
ERequires::IndentListEnd,
|
ERequires::IndentListEnd,
|
||||||
PlatformRigid::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn requires_rigid<'a>() -> impl Parser<'a, PlatformRigid<'a>, ()> {
|
fn requires_rigid<'a>() -> impl Parser<'a, Spaced<'a, PlatformRigid<'a>>, ()> {
|
||||||
map!(
|
map!(
|
||||||
and!(
|
and!(
|
||||||
lowercase_ident(),
|
lowercase_ident(),
|
||||||
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
|
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
|
||||||
),
|
),
|
||||||
|(rigid, alias)| PlatformRigid::Entry { rigid, alias }
|
|(rigid, alias)| Spaced::Item(PlatformRigid { rigid, alias })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<TypedIdent<'a>>, ERequires<'a>> {
|
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>>
|
||||||
|
{
|
||||||
skip_first!(
|
skip_first!(
|
||||||
word1(b'{', ERequires::ListStart),
|
word1(b'{', ERequires::ListStart),
|
||||||
skip_second!(
|
skip_second!(
|
||||||
|
@ -491,7 +492,7 @@ fn exposes_values<'a>() -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
(
|
(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||||
),
|
),
|
||||||
EExposes,
|
EExposes,
|
||||||
> {
|
> {
|
||||||
|
@ -515,7 +516,7 @@ fn exposes_values<'a>() -> impl Parser<
|
||||||
EExposes::Open,
|
EExposes::Open,
|
||||||
EExposes::Space,
|
EExposes::Space,
|
||||||
EExposes::IndentListEnd,
|
EExposes::IndentListEnd,
|
||||||
ExposesEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -545,7 +546,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
(
|
(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
Collection<'a, Located<Spaced<'a, ModuleName<'a>>>>,
|
||||||
),
|
),
|
||||||
EExposes,
|
EExposes,
|
||||||
> {
|
> {
|
||||||
|
@ -569,14 +570,14 @@ fn exposes_modules<'a>() -> impl Parser<
|
||||||
EExposes::Open,
|
EExposes::Open,
|
||||||
EExposes::Space,
|
EExposes::Space,
|
||||||
EExposes::IndentListEnd,
|
EExposes::IndentListEnd,
|
||||||
ExposesEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exposes_module<'a, F, E>(
|
fn exposes_module<'a, F, E>(
|
||||||
to_expectation: F,
|
to_expectation: F,
|
||||||
) -> impl Parser<'a, Located<ExposesEntry<'a, ModuleName<'a>>>, E>
|
) -> impl Parser<'a, Located<Spaced<'a, ModuleName<'a>>>, E>
|
||||||
where
|
where
|
||||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||||
F: Copy,
|
F: Copy,
|
||||||
|
@ -584,13 +585,13 @@ where
|
||||||
{
|
{
|
||||||
loc!(map!(
|
loc!(map!(
|
||||||
specialize(|_, r, c| to_expectation(r, c), module_name()),
|
specialize(|_, r, c| to_expectation(r, c), module_name()),
|
||||||
ExposesEntry::Exposed
|
Spaced::Item
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Packages<'a> {
|
struct Packages<'a> {
|
||||||
entries: Collection<'a, Located<PackageEntry<'a>>>,
|
entries: Collection<'a, Located<Spaced<'a, PackageEntry<'a>>>>,
|
||||||
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||||
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||||
}
|
}
|
||||||
|
@ -618,7 +619,7 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
|
||||||
EPackages::Open,
|
EPackages::Open,
|
||||||
EPackages::Space,
|
EPackages::Space,
|
||||||
EPackages::IndentListEnd,
|
EPackages::IndentListEnd,
|
||||||
PackageEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|((before_packages_keyword, after_packages_keyword), entries): (
|
|((before_packages_keyword, after_packages_keyword), entries): (
|
||||||
|
@ -639,7 +640,7 @@ fn imports<'a>() -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
(
|
(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||||
),
|
),
|
||||||
EImports,
|
EImports,
|
||||||
> {
|
> {
|
||||||
|
@ -663,7 +664,7 @@ fn imports<'a>() -> impl Parser<
|
||||||
EImports::Open,
|
EImports::Open,
|
||||||
EImports::Space,
|
EImports::Space,
|
||||||
EImports::IndentListEnd,
|
EImports::IndentListEnd,
|
||||||
ImportsEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -706,7 +707,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
||||||
EEffects::Open,
|
EEffects::Open,
|
||||||
EEffects::Space,
|
EEffects::Space,
|
||||||
EEffects::IndentListEnd,
|
EEffects::IndentListEnd,
|
||||||
TypedIdent::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
@ -726,7 +727,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||||
// e.g.
|
// e.g.
|
||||||
//
|
//
|
||||||
// printLine : Str -> Effect {}
|
// printLine : Str -> Effect {}
|
||||||
|
@ -752,11 +753,11 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|((ident, spaces_before_colon), ann)| {
|
|((ident, spaces_before_colon), ann)| {
|
||||||
TypedIdent::Entry {
|
Spaced::Item(TypedIdent {
|
||||||
ident,
|
ident,
|
||||||
spaces_before_colon,
|
spaces_before_colon,
|
||||||
ann,
|
ann,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -775,18 +776,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> {
|
||||||
let min_indent = 1;
|
let min_indent = 1;
|
||||||
|
|
||||||
type Temp<'a> = (
|
type Temp<'a> = (
|
||||||
(Option<&'a str>, ModuleName<'a>),
|
(Option<&'a str>, ModuleName<'a>),
|
||||||
Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
|
Option<Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
and!(
|
and!(
|
||||||
and!(
|
and!(
|
||||||
// e.g. `base.`
|
// e.g. `pf.`
|
||||||
maybe!(skip_second!(
|
maybe!(skip_second!(
|
||||||
shortname(),
|
shortname(),
|
||||||
word1(b'.', EImports::ShorthandDot)
|
word1(b'.', EImports::ShorthandDot)
|
||||||
|
@ -806,18 +807,20 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
||||||
EImports::Open,
|
EImports::Open,
|
||||||
EImports::Space,
|
EImports::Space,
|
||||||
EImports::IndentSetEnd,
|
EImports::IndentSetEnd,
|
||||||
ExposesEntry::SpaceBefore
|
Spaced::SpaceBefore
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||||
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
|
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
|
||||||
|
|
||||||
match opt_shortname {
|
let entry = match opt_shortname {
|
||||||
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
|
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
|
||||||
|
|
||||||
None => ImportsEntry::Module(module_name, exposed_values),
|
None => ImportsEntry::Module(module_name, exposed_values),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Spaced::Item(entry)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ Platform {
|
||||||
},
|
},
|
||||||
requires: PlatformRequires {
|
requires: PlatformRequires {
|
||||||
rigids: [],
|
rigids: [],
|
||||||
signature: |L 0-0, C 38-47| Entry {
|
signature: |L 0-0, C 38-47| TypedIdent {
|
||||||
ident: |L 0-0, C 38-42| "main",
|
ident: |L 0-0, C 38-42| "main",
|
||||||
spaces_before_colon: [],
|
spaces_before_colon: [],
|
||||||
ann: |L 0-0, C 45-47| Record {
|
ann: |L 0-0, C 45-47| Record {
|
||||||
|
|
|
@ -4,10 +4,10 @@ App {
|
||||||
"quicksort",
|
"quicksort",
|
||||||
),
|
),
|
||||||
packages: [
|
packages: [
|
||||||
|L 1-1, C 15-33| Entry {
|
|L 1-1, C 15-31| PackageEntry {
|
||||||
shorthand: "base",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_or_path: |L 1-1, C 21-33| Path(
|
package_or_path: |L 1-1, C 19-31| Path(
|
||||||
PlainLine(
|
PlainLine(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
|
@ -24,12 +24,12 @@ App {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
provides: [
|
provides: [
|
||||||
|L 3-3, C 15-24| Exposed(
|
|L 3-3, C 15-24| ExposedName(
|
||||||
"quicksort",
|
"quicksort",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
to: |L 3-3, C 30-34| ExistingPackage(
|
to: |L 3-3, C 30-32| ExistingPackage(
|
||||||
"base",
|
"pf",
|
||||||
),
|
),
|
||||||
before_header: [],
|
before_header: [],
|
||||||
after_app_keyword: [],
|
after_app_keyword: [],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
app "quicksort"
|
app "quicksort"
|
||||||
packages { base: "./platform" }
|
packages { pf: "./platform" }
|
||||||
imports [ foo.Bar.Baz ]
|
imports [ foo.Bar.Baz ]
|
||||||
provides [ quicksort ] to base
|
provides [ quicksort ] to pf
|
||||||
|
|
|
@ -4,10 +4,10 @@ App {
|
||||||
"quicksort",
|
"quicksort",
|
||||||
),
|
),
|
||||||
packages: [
|
packages: [
|
||||||
|L 1-1, C 15-33| Entry {
|
|L 1-1, C 15-31| PackageEntry {
|
||||||
shorthand: "base",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_or_path: |L 1-1, C 21-33| Path(
|
package_or_path: |L 1-1, C 19-31| Path(
|
||||||
PlainLine(
|
PlainLine(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
|
@ -23,7 +23,7 @@ App {
|
||||||
Collection {
|
Collection {
|
||||||
items: [
|
items: [
|
||||||
|L 3-3, C 8-11| SpaceBefore(
|
|L 3-3, C 8-11| SpaceBefore(
|
||||||
Exposed(
|
ExposedName(
|
||||||
"Baz",
|
"Baz",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
@ -31,7 +31,7 @@ App {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|L 4-4, C 8-16| SpaceBefore(
|
|L 4-4, C 8-16| SpaceBefore(
|
||||||
Exposed(
|
ExposedName(
|
||||||
"FortyTwo",
|
"FortyTwo",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
@ -49,12 +49,12 @@ App {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
provides: [
|
provides: [
|
||||||
|L 7-7, C 15-24| Exposed(
|
|L 7-7, C 15-24| ExposedName(
|
||||||
"quicksort",
|
"quicksort",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
to: |L 7-7, C 31-35| ExistingPackage(
|
to: |L 7-7, C 31-33| ExistingPackage(
|
||||||
"base",
|
"pf",
|
||||||
),
|
),
|
||||||
before_header: [],
|
before_header: [],
|
||||||
after_app_keyword: [],
|
after_app_keyword: [],
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
app "quicksort"
|
app "quicksort"
|
||||||
packages { base: "./platform", }
|
packages { pf: "./platform", }
|
||||||
imports [ foo.Bar.{
|
imports [ foo.Bar.{
|
||||||
Baz,
|
Baz,
|
||||||
FortyTwo,
|
FortyTwo,
|
||||||
# I'm a happy comment
|
# I'm a happy comment
|
||||||
} ]
|
} ]
|
||||||
provides [ quicksort, ] to base
|
provides [ quicksort, ] to pf
|
||||||
|
|
|
@ -6,12 +6,12 @@ Platform {
|
||||||
},
|
},
|
||||||
requires: PlatformRequires {
|
requires: PlatformRequires {
|
||||||
rigids: [
|
rigids: [
|
||||||
|L 1-1, C 14-26| Entry {
|
|L 1-1, C 14-26| PlatformRigid {
|
||||||
rigid: "model",
|
rigid: "model",
|
||||||
alias: "Model",
|
alias: "Model",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
signature: |L 1-1, C 30-39| Entry {
|
signature: |L 1-1, C 30-39| TypedIdent {
|
||||||
ident: |L 1-1, C 30-34| "main",
|
ident: |L 1-1, C 30-34| "main",
|
||||||
spaces_before_colon: [],
|
spaces_before_colon: [],
|
||||||
ann: |L 1-1, C 37-39| Record {
|
ann: |L 1-1, C 37-39| Record {
|
||||||
|
@ -22,7 +22,7 @@ Platform {
|
||||||
},
|
},
|
||||||
exposes: [],
|
exposes: [],
|
||||||
packages: [
|
packages: [
|
||||||
|L 3-3, C 15-27| Entry {
|
|L 3-3, C 15-27| PackageEntry {
|
||||||
shorthand: "foo",
|
shorthand: "foo",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_or_path: |L 3-3, C 20-27| Path(
|
package_or_path: |L 3-3, C 20-27| Path(
|
||||||
|
@ -34,7 +34,7 @@ Platform {
|
||||||
],
|
],
|
||||||
imports: [],
|
imports: [],
|
||||||
provides: [
|
provides: [
|
||||||
|L 5-5, C 15-26| Exposed(
|
|L 5-5, C 15-26| ExposedName(
|
||||||
"mainForHost",
|
"mainForHost",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -31,7 +31,7 @@ fn nat_alias() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn i128_signed_int_alias() {
|
fn i128_signed_int_alias() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -115,7 +115,7 @@ fn i8_signed_int_alias() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn i128_hex_int_alias() {
|
fn i128_hex_int_alias() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -741,7 +741,7 @@ fn gen_int_less_than() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn gen_dec_eq() {
|
fn gen_dec_eq() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -761,7 +761,7 @@ fn gen_dec_eq() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn gen_dec_neq() {
|
fn gen_dec_neq() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -864,7 +864,7 @@ fn gen_sub_i64() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn gen_mul_dec() {
|
fn gen_mul_dec() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1830,7 +1830,7 @@ fn shift_right_zf_by() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn max_i128() {
|
fn max_i128() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
|
@ -665,17 +665,17 @@ fn str_starts_with_false_small_str() {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_equality() {
|
fn str_equality() {
|
||||||
// assert_evals_to!(r#""a" == "a""#, true, bool);
|
assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||||
// true,
|
true,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// assert_evals_to!(r#""a" != "b""#, true, bool);
|
assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||||
// assert_evals_to!(r#""a" == "b""#, false, bool);
|
assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn nested_recursive_literal() {
|
// fn nested_recursive_literal() {
|
||||||
|
|
|
@ -58,9 +58,9 @@ mod insert_doc_syntax_highlighting {
|
||||||
|
|
||||||
pub const HELLO_WORLD: &str = r#"
|
pub const HELLO_WORLD: &str = r#"
|
||||||
app "test-app"
|
app "test-app"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main = "Hello, world!"
|
main = "Hello, world!"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
The editor is a work in progress, only a limited subset of Roc expressions are currently supported.
|
The editor is a work in progress, only a limited subset of Roc expressions are currently supported.
|
||||||
|
|
||||||
Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete and refactoring.
|
Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete, refactoring and never needing to format your code.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
@ -29,3 +29,46 @@ We thank the following open source projects in particular for inspiring us when
|
||||||
- [rgx](https://github.com/cloudhead/rgx)
|
- [rgx](https://github.com/cloudhead/rgx)
|
||||||
- [elm-editor](https://github.com/jxxcarlson/elm-editor)
|
- [elm-editor](https://github.com/jxxcarlson/elm-editor)
|
||||||
- [iced](https://github.com/hecrj/iced)
|
- [iced](https://github.com/hecrj/iced)
|
||||||
|
|
||||||
|
## How does it work?
|
||||||
|
|
||||||
|
To take a look behind the scenes, open the editor with `./roc edit` or `cargo run edit` and press F11.
|
||||||
|
This debug view shows important data structures that can be found in `editor/src/editor/mvc/ed_model.rs`.
|
||||||
|
Add or delete some code to see how these data structures are updated.
|
||||||
|
|
||||||
|
From roc to render:
|
||||||
|
- `./roc edit` or `cargo run edit` is first handled in `cli/src/main.rs`, from there the editor's launch function is called (`editor/src/editor/main.rs`).
|
||||||
|
- In `run_event_loop` we initialize the winit window, wgpu, the editor's model(`EdModel`) and start the rendering loop.
|
||||||
|
- The `ed_model` is filled in part with data obtained by loading and typechecking the roc file with the same function (`load_and_typecheck`) that is used by the compiler.
|
||||||
|
- `ed_model` also contains an `EdModule`, which holds the parsed abstract syntax tree (AST).
|
||||||
|
- In the `init_model` function:
|
||||||
|
+ The AST is converted into a tree of `MarkupNode`. The different types of `MarkupNode` are similar to the elements/nodes in HTML. A line of roc code is represented as a nested `MarkupNode` containing mostly text `MarkupNode`s. The line `foo = "bar"` is represented as
|
||||||
|
three text `MarkupNode` representing `foo`, ` = ` and `bar`. Multiple lines of roc code are represented as nested `MarkupNode` that contain other nested `MarkupNode`.
|
||||||
|
+ `CodeLines` holds a `Vec` of `String`, each line of code is a `String`. When saving the file, the content of `CodeLines` is written to disk.
|
||||||
|
+ `GridNodeMap` maps every position of a char of roc code to a `MarkNodeId`, for easy interaction with the caret.
|
||||||
|
- Back in `editor/src/editor/main.rs` we convert the `EdModel` to `RenderedWgpu` by calling `model_to_wgpu`.
|
||||||
|
- The `RenderedWgpu` is passed to the `glyph_brush` to draw the characters(glyphs) on the screen.
|
||||||
|
|
||||||
|
|
||||||
|
### Important files
|
||||||
|
|
||||||
|
To understand how the editor works it is useful to know the most important files:
|
||||||
|
- editor/src/editor/main.rs
|
||||||
|
- editor/src/editor/mvc/ed_update.rs
|
||||||
|
- editor/src/editor/mvc/ed_model.rs
|
||||||
|
- editor/src/editor/mvc/ed_view.rs
|
||||||
|
- editor/src/editor/render_ast.rs
|
||||||
|
- editor/src/editor/render_debug.rs
|
||||||
|
|
||||||
|
Important folders/files outside the editor folder:
|
||||||
|
- code_markup/src/markup/convert
|
||||||
|
- code_markup/src/markup/nodes.rs
|
||||||
|
- ast/src/lang/core/def
|
||||||
|
- ast/src/lang/core/expr
|
||||||
|
- ast/src/lang/core/ast.rs
|
||||||
|
- ast/src/lang/env.rs
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome new contributors :heart: and are happy to help you get started.
|
||||||
|
Check [CONTRIBUTING.md](../CONTRIBUTING.md) for more info.
|
||||||
|
|
|
@ -17,9 +17,9 @@ For convenience and consistency, there is only one way to format roc.
|
||||||
|
|
||||||
pub const HELLO_WORLD: &str = r#"
|
pub const HELLO_WORLD: &str = r#"
|
||||||
app "test-app"
|
app "test-app"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main = "Hello, world!"
|
main = "Hello, world!"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "cfold"
|
app "cfold"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
|
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "closure"
|
app "closure"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
# see https://github.com/rtfeldman/roc/issues/985
|
# see https://github.com/rtfeldman/roc/issues/985
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "deriv"
|
app "deriv"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
|
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "nqueens"
|
app "nqueens"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "quicksortapp"
|
app "quicksortapp"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task, Quicksort]
|
imports [pf.Task, Quicksort]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "rbtree-ck"
|
app "rbtree-ck"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
|
|
||||||
Color : [ Red, Black ]
|
Color : [ Red, Black ]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "rbtree-del"
|
app "rbtree-del"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
|
|
||||||
Color : [ Red, Black ]
|
Color : [ Red, Black ]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "rbtree-insert"
|
app "rbtree-insert"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task]
|
imports [pf.Task]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "test-astar"
|
app "test-astar"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task, AStar]
|
imports [pf.Task, AStar]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "test-base64"
|
app "test-base64"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task, Base64 ]
|
imports [pf.Task, Base64 ]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
IO a : Task.Task a []
|
IO a : Task.Task a []
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env roc
|
#!/usr/bin/env roc
|
||||||
|
|
||||||
app "echo"
|
app "echo"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
|
imports [ pf.Task.{ Task, await }, pf.Stdout, pf.Stdin ]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Task {} *
|
main : Task {} *
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "effect-example"
|
app "effect-example"
|
||||||
packages { base: "thing/platform-dir" }
|
packages { pf: "thing/platform-dir" }
|
||||||
imports [fx.Effect]
|
imports [fx.Effect]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main : Effect.Effect {}
|
main : Effect.Effect {}
|
||||||
main =
|
main =
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
interface Context
|
interface Context
|
||||||
exposes [ Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope ]
|
exposes [ Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope ]
|
||||||
imports [ base.File, base.Task.{ Task }, Variable.{ Variable } ]
|
imports [ pf.File, pf.Task.{ Task }, Variable.{ Variable } ]
|
||||||
|
|
||||||
Option a : [ Some a, None ]
|
Option a : [ Some a, None ]
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env roc
|
#!/usr/bin/env roc
|
||||||
app "false"
|
app "false"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [ base.Task.{ Task }, base.Stdout, base.Stdin, Context.{ Context }, Variable.{ Variable } ]
|
imports [ pf.Task.{ Task }, pf.Stdout, pf.Stdin, Context.{ Context }, Variable.{ Variable } ]
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
# An interpreter for the False programming language: https://strlen.com/false-language/
|
# An interpreter for the False programming language: https://strlen.com/false-language/
|
||||||
# This is just a silly example to test this variety of program.
|
# This is just a silly example to test this variety of program.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "fib"
|
app "fib"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main = \n -> fib n 0 1
|
main = \n -> fib n 0 1
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "hello-rust"
|
app "hello-rust"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
greeting =
|
greeting =
|
||||||
hi = "Hello"
|
hi = "Hello"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "hello-swift"
|
app "hello-swift"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main =
|
main =
|
||||||
host = "Swift"
|
host = "Swift"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "hello-web"
|
app "hello-web"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
greeting =
|
greeting =
|
||||||
hi = "Hello"
|
hi = "Hello"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
app "hello-world"
|
app "hello-world"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
main = "Hello, World!\n"
|
main = "Hello, World!\n"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "hello-world"
|
app "hello-world"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to pf
|
||||||
|
|
||||||
greeting =
|
greeting =
|
||||||
hi = "Hello"
|
hi = "Hello"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
app "quicksort"
|
app "quicksort"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports []
|
imports []
|
||||||
provides [ quicksort ] to base
|
provides [ quicksort ] to pf
|
||||||
|
|
||||||
quicksort = \originalList ->
|
quicksort = \originalList ->
|
||||||
n = List.len originalList
|
n = List.len originalList
|
||||||
|
|
|
@ -6048,9 +6048,9 @@ I need all branches in an `if` to have the same type!
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
app "test-base64"
|
app "test-base64"
|
||||||
packages { base: "platform" }
|
packages { pf: "platform" }
|
||||||
imports [base.Task, Base64 ]
|
imports [pf.Task, Base64 ]
|
||||||
provides [ main, @Foo ] to base
|
provides [ main, @Foo ] to pf
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -6059,8 +6059,8 @@ I need all branches in an `if` to have the same type!
|
||||||
|
|
||||||
I am partway through parsing a provides list, but I got stuck here:
|
I am partway through parsing a provides list, but I got stuck here:
|
||||||
|
|
||||||
3│ imports [base.Task, Base64 ]
|
3│ imports [pf.Task, Base64 ]
|
||||||
4│ provides [ main, @Foo ] to base
|
4│ provides [ main, @Foo ] to pf
|
||||||
^
|
^
|
||||||
|
|
||||||
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
|
||||||
|
@ -6116,7 +6116,7 @@ I need all branches in an `if` to have the same type!
|
||||||
r#"
|
r#"
|
||||||
interface Foobar
|
interface Foobar
|
||||||
exposes [ main, @Foo ]
|
exposes [ main, @Foo ]
|
||||||
imports [base.Task, Base64 ]
|
imports [pf.Task, Base64 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -6144,7 +6144,7 @@ I need all branches in an `if` to have the same type!
|
||||||
r#"
|
r#"
|
||||||
interface foobar
|
interface foobar
|
||||||
exposes [ main, @Foo ]
|
exposes [ main, @Foo ]
|
||||||
imports [base.Task, Base64 ]
|
imports [pf.Task, Base64 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -6170,7 +6170,7 @@ I need all branches in an `if` to have the same type!
|
||||||
r#"
|
r#"
|
||||||
app foobar
|
app foobar
|
||||||
exposes [ main, @Foo ]
|
exposes [ main, @Foo ]
|
||||||
imports [base.Task, Base64 ]
|
imports [pf.Task, Base64 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
|
@ -507,7 +507,7 @@ impl RocStr {
|
||||||
pub fn storage(&self) -> Option<Storage> {
|
pub fn storage(&self) -> Option<Storage> {
|
||||||
use core::cmp::Ordering::*;
|
use core::cmp::Ordering::*;
|
||||||
|
|
||||||
if self.is_small_str() || self.length == 0 {
|
if self.is_small_str() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,7 +660,7 @@ impl RocStr {
|
||||||
impl Default for RocStr {
|
impl Default for RocStr {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
length: 0,
|
length: isize::MIN as usize,
|
||||||
elements: core::ptr::null_mut(),
|
elements: core::ptr::null_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -693,7 +693,7 @@ impl Eq for RocStr {}
|
||||||
|
|
||||||
impl Clone for RocStr {
|
impl Clone for RocStr {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
if self.is_small_str() || self.is_empty() {
|
if self.is_small_str() {
|
||||||
Self {
|
Self {
|
||||||
elements: self.elements,
|
elements: self.elements,
|
||||||
length: self.length,
|
length: self.length,
|
||||||
|
@ -730,7 +730,7 @@ impl Clone for RocStr {
|
||||||
|
|
||||||
impl Drop for RocStr {
|
impl Drop for RocStr {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.is_small_str() && !self.is_empty() {
|
if !self.is_small_str() {
|
||||||
let storage_ptr = self.get_storage_ptr_mut();
|
let storage_ptr = self.get_storage_ptr_mut();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue