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.
|
||||
- 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.
|
||||
- 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?
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ use roc_fmt::module::fmt_module;
|
|||
use roc_fmt::Buf;
|
||||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, Expr, Pattern, StrLiteral, StrSegment, Tag, TypeAnnotation,
|
||||
AssignedField, Collection, Expr, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation,
|
||||
WhenBranch,
|
||||
};
|
||||
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,
|
||||
};
|
||||
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 {
|
||||
match *self {
|
||||
ExposesEntry::Exposed(a) => ExposesEntry::Exposed(a.remove_spaces(arena)),
|
||||
ExposesEntry::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
ExposesEntry::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
||||
Spaced::SpaceBefore(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> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
|
@ -261,18 +267,10 @@ impl<'a> RemoveSpaces<'a> for To<'a> {
|
|||
|
||||
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
TypedIdent::Entry {
|
||||
ident,
|
||||
spaces_before_colon: _,
|
||||
ann,
|
||||
} => TypedIdent::Entry {
|
||||
ident: ident.remove_spaces(arena),
|
||||
TypedIdent {
|
||||
ident: self.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),
|
||||
ann: self.ann.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,29 +285,17 @@ impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
|||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PlatformRigid<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
PlatformRigid::Entry { rigid, alias } => PlatformRigid::Entry { rigid, alias },
|
||||
PlatformRigid::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
PlatformRigid::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
PackageEntry::Entry {
|
||||
shorthand,
|
||||
spaces_after_shorthand: _,
|
||||
package_or_path,
|
||||
} => PackageEntry::Entry {
|
||||
shorthand,
|
||||
PackageEntry {
|
||||
shorthand: self.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),
|
||||
package_or_path: self.package_or_path.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -328,8 +314,6 @@ impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
|||
match *self {
|
||||
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::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"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports [ Dep1 ]
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Str
|
||||
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"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports [ Dep1 ]
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Str
|
||||
main = Dep1.value1 {}
|
||||
|
|
10
cli_utils/Cargo.lock
generated
10
cli_utils/Cargo.lock
generated
|
@ -1310,13 +1310,13 @@ dependencies = [
|
|||
name = "inkwell"
|
||||
version = "0.1.0"
|
||||
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]]
|
||||
name = "inkwell"
|
||||
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 = [
|
||||
"either",
|
||||
"inkwell_internals",
|
||||
|
@ -1324,13 +1324,12 @@ dependencies = [
|
|||
"llvm-sys",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inkwell_internals"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm12-0.release8#14b78d96d2dbc95694e181be66e4cd53df3fc02f"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2666,6 +2665,7 @@ dependencies = [
|
|||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
|
|
@ -16,7 +16,7 @@ const InPlace = enum(u8) {
|
|||
};
|
||||
|
||||
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);
|
||||
|
||||
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 inline fn empty() RocStr {
|
||||
const small_str_flag: isize = std.math.minInt(isize);
|
||||
return RocStr{
|
||||
.str_len = 0,
|
||||
.str_len = @bitCast(usize, small_str_flag),
|
||||
.str_bytes = null,
|
||||
};
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn clone(in_place: InPlace, str: RocStr) RocStr {
|
||||
if (str.isSmallStr() or str.isEmpty()) {
|
||||
if (str.isSmallStr()) {
|
||||
// just return the bytes
|
||||
return str;
|
||||
} else {
|
||||
|
@ -217,7 +218,8 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -297,11 +299,6 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
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
|
||||
if (self.isSmallStr()) {
|
||||
return true;
|
||||
|
@ -324,8 +321,8 @@ pub const RocStr = extern struct {
|
|||
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// 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));
|
||||
if (self.isSmallStr() or self.isEmpty()) {
|
||||
// return if (self.isSmallStr()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
if (self.isSmallStr()) {
|
||||
const as_int = @ptrToInt(&self);
|
||||
const as_ptr = @intToPtr([*]u8, as_int);
|
||||
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].
|
||||
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.
|
||||
##
|
||||
## >>> List.append [ "a", "b" ] "c"
|
||||
|
@ -685,8 +682,6 @@ startsWith : 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
|
||||
## any of the elements satisfy it.
|
||||
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.
|
||||
## If no satisfying element is found, an `Err NotFound` is returned.
|
||||
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:
|
||||
##
|
||||
## 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:
|
||||
## >>> 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:
|
||||
## >>> Num.format 1_000_000 { base: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
||||
## >>> Num.format 1_000_000 { pf: Hexadecimal, wholeSep: { mark: " ", places: 2 } }
|
||||
format :
|
||||
Num *,
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@ interface Result
|
|||
exposes
|
||||
[
|
||||
Result,
|
||||
isOk,
|
||||
isErr,
|
||||
map,
|
||||
mapErr,
|
||||
withDefault,
|
||||
|
@ -13,6 +15,16 @@ interface Result
|
|||
## okay, or else there was an error of some sort.
|
||||
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
|
||||
## the given default value.
|
||||
##
|
||||
|
|
|
@ -29,6 +29,8 @@ isEmpty : Set * -> Bool
|
|||
|
||||
len : Set * -> Nat
|
||||
|
||||
## Modify
|
||||
|
||||
# TODO: removed `'` from signature because parser does not support it yet
|
||||
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
|
||||
## 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`
|
||||
drop : Set elem, elem -> Set elem
|
||||
|
||||
## Transform
|
||||
|
||||
## 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.
|
||||
##
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{ClosureData, Expr::*};
|
||||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||
use crate::expr::{self, ClosureData, Expr::*};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::SendMap;
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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)],
|
||||
value: no_region(ok),
|
||||
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)],
|
||||
value: no_region(err),
|
||||
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)],
|
||||
value: no_region(ok),
|
||||
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)],
|
||||
value: no_region(err),
|
||||
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)))],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_3)),
|
||||
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))],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_2)),
|
||||
guard: None,
|
||||
|
@ -4347,7 +4347,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
guard: None,
|
||||
|
@ -4374,7 +4374,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
|
@ -4424,7 +4424,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
|
@ -4451,7 +4451,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![],
|
||||
};
|
||||
|
||||
let branch = WhenBranch {
|
||||
let branch = expr::WhenBranch {
|
||||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
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)],
|
||||
value: no_region(ok),
|
||||
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)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
|
|
|
@ -40,12 +40,12 @@ pub enum Newlines {
|
|||
No,
|
||||
}
|
||||
|
||||
pub trait Formattable<'a> {
|
||||
pub trait Formattable {
|
||||
fn is_multiline(&self) -> bool;
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -53,23 +53,23 @@ pub trait Formattable<'a> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
fn is_multiline(&self) -> bool {
|
||||
(*self).is_multiline()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -77,23 +77,23 @@ where
|
|||
(*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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
impl<'a, T> Formattable<'a> for Located<T>
|
||||
impl<T> Formattable for Located<T>
|
||||
where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
fn is_multiline(&self) -> bool {
|
||||
self.value.is_multiline()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -102,12 +102,12 @@ where
|
|||
.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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
||||
impl<'a> Formattable for TypeAnnotation<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
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,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -275,14 +275,14 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> {
|
|||
/// > term: { x: 100, y: True }
|
||||
///
|
||||
/// 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 {
|
||||
is_multiline_assigned_field_help(self)
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
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 {
|
||||
is_multiline_assigned_field_help(self)
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
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::*;
|
||||
|
||||
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>,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
indent: u16,
|
||||
separator_prefix: &str,
|
||||
is_multiline: bool,
|
||||
) where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
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 {
|
||||
use self::Tag::*;
|
||||
|
||||
|
@ -416,9 +416,9 @@ impl<'a> Formattable<'a> for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
|
|
@ -6,15 +6,15 @@ use crate::{
|
|||
Buf,
|
||||
};
|
||||
|
||||
pub fn fmt_collection<'a, 'b, T: ExtractSpaces<'a> + Formattable<'b>>(
|
||||
buf: &mut Buf<'b>,
|
||||
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||
buf: &mut Buf<'buf>,
|
||||
indent: u16,
|
||||
start: char,
|
||||
end: char,
|
||||
items: Collection<'a, T>,
|
||||
newline: Newlines,
|
||||
) where
|
||||
<T as ExtractSpaces<'a>>::Item: Formattable<'b>,
|
||||
<T as ExtractSpaces<'a>>::Item: Formattable,
|
||||
{
|
||||
buf.indent(indent);
|
||||
let is_multiline =
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_parse::ast::{Def, Expr, Pattern};
|
|||
use roc_region::all::Located;
|
||||
|
||||
/// 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 {
|
||||
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,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
|
@ -107,8 +107,8 @@ impl<'a> Formattable<'a> for Def<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_expect<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
condition: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
|
@ -123,11 +123,16 @@ fn fmt_expect<'a>(
|
|||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
buf.push_str(" =");
|
||||
if body.is_multiline() {
|
||||
|
|
|
@ -11,7 +11,7 @@ use roc_parse::ast::{
|
|||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_region::all::Located;
|
||||
|
||||
impl<'a> Formattable<'a> for Expr<'a> {
|
||||
impl<'a> Formattable for Expr<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::Expr::*;
|
||||
// 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,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
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::*;
|
||||
|
||||
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::*;
|
||||
|
||||
buf.indent(indent);
|
||||
|
@ -395,8 +395,8 @@ pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u
|
|||
buf.push('"');
|
||||
}
|
||||
|
||||
fn fmt_bin_ops<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_bin_ops<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
|
||||
loc_right_side: &'a Located<Expr<'a>>,
|
||||
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>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_when<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_condition: &'a Located<Expr<'a>>,
|
||||
branches: &[&'a WhenBranch<'a>],
|
||||
indent: u16,
|
||||
|
@ -569,8 +569,8 @@ fn fmt_when<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_expect<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
condition: &'a Located<Expr<'a>>,
|
||||
continuation: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
|
@ -588,8 +588,8 @@ fn fmt_expect<'a>(
|
|||
continuation.format(buf, return_indent);
|
||||
}
|
||||
|
||||
fn fmt_if<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_if<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
||||
final_else: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
|
@ -709,8 +709,8 @@ fn fmt_if<'a>(
|
|||
final_else.format(buf, return_indent);
|
||||
}
|
||||
|
||||
fn fmt_closure<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_closure<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||
loc_ret: &'a Located<Expr<'a>>,
|
||||
indent: u16,
|
||||
|
@ -782,8 +782,8 @@ fn fmt_closure<'a>(
|
|||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
}
|
||||
|
||||
fn fmt_backpassing<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_backpassing<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_patterns: &'a [Located<Pattern<'a>>],
|
||||
loc_body: &'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>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn fmt_record<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
update: Option<&'a Located<Expr<'a>>>,
|
||||
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
|
||||
indent: u16,
|
||||
|
@ -945,13 +945,13 @@ fn fmt_record<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn format_field_multiline<'a, T>(
|
||||
buf: &mut Buf<'a>,
|
||||
fn format_field_multiline<'a, 'buf, T>(
|
||||
buf: &mut Buf<'buf>,
|
||||
field: &AssignedField<'a, T>,
|
||||
indent: u16,
|
||||
separator_prefix: &str,
|
||||
) where
|
||||
T: Formattable<'a>,
|
||||
T: Formattable,
|
||||
{
|
||||
use self::AssignedField::*;
|
||||
match field {
|
||||
|
|
|
@ -3,14 +3,14 @@ use crate::collection::fmt_collection;
|
|||
use crate::expr::fmt_str_literal;
|
||||
use crate::spaces::{fmt_default_spaces, fmt_spaces, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{Collection, Module};
|
||||
use roc_parse::ast::{Collection, Module, Spaced};
|
||||
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,
|
||||
};
|
||||
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 {
|
||||
Module::Interface { 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
buf.indent(0);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
buf.push_str(" { ");
|
||||
fmt_typed_ident(buf, &requires.signature.value, indent);
|
||||
requires.signature.value.format(buf, indent);
|
||||
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);
|
||||
buf.indent(indent);
|
||||
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)
|
||||
}
|
||||
|
||||
fn fmt_typed_ident<'a>(buf: &mut Buf<'a>, entry: &TypedIdent<'a>, indent: u16) {
|
||||
use TypedIdent::*;
|
||||
match entry {
|
||||
Entry {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
ann,
|
||||
} => {
|
||||
impl<'a> Formattable for TypedIdent<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(ident.value);
|
||||
fmt_default_spaces(buf, spaces_before_colon, " ", indent);
|
||||
buf.push_str(self.ident.value);
|
||||
fmt_default_spaces(buf, self.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 {
|
||||
false
|
||||
}
|
||||
|
||||
fn format(&self, buf: &mut Buf<'a>, indent: u16) {
|
||||
fmt_typed_ident(buf, self, indent);
|
||||
self.ann.value.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for PlatformRigid<'a> {
|
||||
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) {
|
||||
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackageName) {
|
||||
buf.push_str(name.account);
|
||||
buf.push('/');
|
||||
buf.push_str(name.pkg);
|
||||
}
|
||||
|
||||
fn fmt_platform_rigid<'a>(buf: &mut Buf<'a>, entry: &PlatformRigid<'a>, indent: u16) {
|
||||
use roc_parse::header::PlatformRigid::*;
|
||||
impl<'a, T: Formattable> Formattable for Spaced<'a, T> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// TODO
|
||||
false
|
||||
}
|
||||
|
||||
match entry {
|
||||
Entry { rigid, alias } => {
|
||||
buf.push_str(rigid);
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: crate::annotation::Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
match self {
|
||||
Spaced::Item(item) => {
|
||||
item.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
Spaced::SpaceBefore(item, spaces) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for PlatformRigid<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||
buf.push_str(self.rigid);
|
||||
buf.push_str("=>");
|
||||
buf.push_str(alias);
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_platform_rigid(buf, sub_entry, indent);
|
||||
}
|
||||
SpaceAfter(sub_entry, spaces) => {
|
||||
fmt_platform_rigid(buf, sub_entry, indent);
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
buf.push_str(self.alias);
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_imports<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
fn fmt_imports<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_collection(buf, indent, '[', ']', loc_entries, Newlines::No)
|
||||
}
|
||||
|
||||
fn fmt_provides<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
fn fmt_provides<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
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 {
|
||||
To::ExistingPackage(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>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'_, Located<ExposesEntry<'_, N>>>,
|
||||
fn fmt_exposes<'buf, N: Formattable + Copy>(
|
||||
buf: &mut Buf<'buf>,
|
||||
loc_entries: Collection<'_, Located<Spaced<'_, N>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
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 {
|
||||
fn format<'a>(&self, buf: &mut Buf<'a>);
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>);
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_exposes_entry<'a, 'b, N: FormatName>(
|
||||
buf: &mut Buf<'a>,
|
||||
entry: &ExposesEntry<'b, N>,
|
||||
indent: u16,
|
||||
) {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
|
||||
match entry {
|
||||
Exposed(ident) => ident.format(buf),
|
||||
|
||||
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);
|
||||
impl<'a> Formattable for ModuleName<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_packages<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
loc_entries: Collection<'a, Located<PackageEntry<'a>>>,
|
||||
impl<'a> Formattable for ExposedName<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
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,
|
||||
) {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for ImportsEntry<'a> {
|
||||
impl<'a> Formattable for ImportsEntry<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
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);
|
||||
}
|
||||
}
|
||||
fn fmt_packages_entry<'a>(buf: &mut Buf<'a>, entry: &PackageEntry<'a>, indent: u16) {
|
||||
use PackageEntry::*;
|
||||
match entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
} => {
|
||||
buf.push_str(shorthand);
|
||||
fn fmt_packages_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &PackageEntry<'a>, indent: u16) {
|
||||
buf.push_str(entry.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);
|
||||
}
|
||||
}
|
||||
fmt_default_spaces(buf, entry.spaces_after_shorthand, " ", indent);
|
||||
fmt_package_or_path(buf, &entry.package_or_path.value, 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 {
|
||||
PackageOrPath::Package(_name, _version) => {
|
||||
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::*;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
impl<'a> Formattable<'a> for Pattern<'a> {
|
||||
impl<'a> Formattable for Pattern<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// Theory: a pattern should only be multiline when it contains a comment
|
||||
match self {
|
||||
|
@ -37,9 +42,9 @@ impl<'a> Formattable<'a> for Pattern<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'a>,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
|
|
|
@ -18,8 +18,8 @@ pub fn add_spaces(buf: &mut String<'_>, spaces: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_default_spaces<'a>(
|
||||
buf: &mut Buf<'a>,
|
||||
pub fn fmt_default_spaces<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
spaces: &[CommentOrNewline<'a>],
|
||||
default: &str,
|
||||
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
|
||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||
{
|
||||
use self::CommentOrNewline::*;
|
||||
|
||||
|
@ -84,13 +84,13 @@ pub enum NewlineAt {
|
|||
/// The `new_line_at` argument describes how new lines should be inserted
|
||||
/// at the beginning or at the end of the block
|
||||
/// in the case of there is some comment in the `spaces` argument.
|
||||
pub fn fmt_comments_only<'a, 'b, I>(
|
||||
buf: &mut Buf<'a>,
|
||||
pub fn fmt_comments_only<'a, 'buf, I>(
|
||||
buf: &mut Buf<'buf>,
|
||||
spaces: I,
|
||||
new_line_at: NewlineAt,
|
||||
indent: u16,
|
||||
) where
|
||||
I: Iterator<Item = &'b CommentOrNewline<'b>>,
|
||||
I: Iterator<Item = &'a CommentOrNewline<'a>>,
|
||||
{
|
||||
use self::CommentOrNewline::*;
|
||||
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('#');
|
||||
if !comment.starts_with(' ') {
|
||||
buf.push(' ');
|
||||
|
@ -131,7 +131,7 @@ fn fmt_comment<'a>(buf: &mut Buf<'a>, comment: &str) {
|
|||
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("##");
|
||||
if !docs.starts_with(' ') {
|
||||
buf.push(' ');
|
||||
|
|
|
@ -15,7 +15,7 @@ mod test_fmt {
|
|||
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
|
||||
fn expect_format_helper(input: &str, expected: &str) {
|
||||
fn expect_format_expr_helper(input: &str, expected: &str) {
|
||||
let arena = Bump::new();
|
||||
match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) {
|
||||
Ok(actual) => {
|
||||
|
@ -34,21 +34,20 @@ mod test_fmt {
|
|||
let expected = expected.trim_end();
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
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 src = src.trim_end();
|
||||
|
||||
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
||||
Ok((actual, state)) => {
|
||||
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) {
|
||||
module_formats_to(input, input);
|
||||
}
|
||||
|
@ -2625,7 +2635,7 @@ mod test_fmt {
|
|||
fn single_line_app() {
|
||||
module_formats_same(indoc!(
|
||||
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_collections::all::{MutMap, MutSet};
|
||||
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::layout::{Builtin, Layout};
|
||||
use roc_reporting::internal_error;
|
||||
|
@ -256,8 +256,8 @@ pub struct Backend64Bit<
|
|||
phantom_cc: PhantomData<CC>,
|
||||
env: &'a Env<'a>,
|
||||
interns: &'a mut Interns,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
refcount_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
helper_proc_symbols: Vec<'a, (Symbol, ProcLayout<'a>)>,
|
||||
buf: Vec<'a, u8>,
|
||||
relocs: Vec<'a, Relocation>,
|
||||
proc_name: Option<String>,
|
||||
|
@ -308,8 +308,8 @@ pub fn new_backend_64bit<
|
|||
phantom_cc: PhantomData,
|
||||
env,
|
||||
interns,
|
||||
refcount_proc_gen: RefcountProcGenerator::new(env.arena, IntWidth::I64, env.module_id),
|
||||
refcount_proc_symbols: bumpalo::vec![in env.arena],
|
||||
helper_proc_gen: CodeGenHelp::new(env.arena, IntWidth::I64, env.module_id),
|
||||
helper_proc_symbols: bumpalo::vec![in env.arena],
|
||||
proc_name: None,
|
||||
is_self_recursive: None,
|
||||
buf: bumpalo::vec![in env.arena],
|
||||
|
@ -346,19 +346,17 @@ impl<
|
|||
fn interns(&self) -> &Interns {
|
||||
self.interns
|
||||
}
|
||||
fn env_interns_refcount_mut(
|
||||
&mut self,
|
||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>) {
|
||||
(self.env, self.interns, &mut self.refcount_proc_gen)
|
||||
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>) {
|
||||
(self.env, self.interns, &mut self.helper_proc_gen)
|
||||
}
|
||||
fn refcount_proc_gen_mut(&mut self) -> &mut RefcountProcGenerator<'a> {
|
||||
&mut self.refcount_proc_gen
|
||||
fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a> {
|
||||
&mut self.helper_proc_gen
|
||||
}
|
||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&mut self.refcount_proc_symbols
|
||||
fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&mut self.helper_proc_symbols
|
||||
}
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&self.refcount_proc_symbols
|
||||
fn helper_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&self.helper_proc_symbols
|
||||
}
|
||||
|
||||
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
|
||||
|
@ -383,7 +381,7 @@ impl<
|
|||
self.float_used_regs.clear();
|
||||
self.float_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>)> {
|
||||
|
|
|
@ -8,7 +8,7 @@ use roc_collections::all::{MutMap, MutSet};
|
|||
use roc_module::ident::{ModuleName, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::gen_refcount::RefcountProcGenerator;
|
||||
use roc_mono::code_gen_help::CodeGenHelp;
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||
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
|
||||
// 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.
|
||||
fn env_interns_refcount_mut(
|
||||
&mut self,
|
||||
) -> (&Env<'a>, &mut Interns, &mut RefcountProcGenerator<'a>);
|
||||
fn env_interns_helpers_mut(&mut self) -> (&Env<'a>, &mut Interns, &mut CodeGenHelp<'a>);
|
||||
|
||||
fn symbol_to_string(&self, symbol: Symbol, layout_id: LayoutId) -> String {
|
||||
layout_id.to_symbol_string(symbol, self.interns())
|
||||
|
@ -76,11 +74,11 @@ trait Backend<'a> {
|
|||
.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.
|
||||
/// 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.create_free_map();
|
||||
self.build_stmt(&proc.body, &proc.ret_layout);
|
||||
let mut rc_proc_names = bumpalo::vec![in self.env().arena];
|
||||
rc_proc_names.reserve(self.refcount_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.refcount_proc_symbols() {
|
||||
let mut helper_proc_names = bumpalo::vec![in self.env().arena];
|
||||
helper_proc_names.reserve(self.helper_proc_symbols().len());
|
||||
for (rc_proc_sym, rc_proc_layout) in self.helper_proc_symbols() {
|
||||
let name = layout_ids
|
||||
.get_toplevel(*rc_proc_sym, rc_proc_layout)
|
||||
.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();
|
||||
(bytes, relocs, rc_proc_names)
|
||||
(bytes, relocs, helper_proc_names)
|
||||
}
|
||||
|
||||
/// 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
|
||||
// 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.
|
||||
let (rc_stmt, new_proc_info) = {
|
||||
let (env, interns, rc_proc_gen) = self.env_interns_refcount_mut();
|
||||
let (rc_stmt, new_specializations) = {
|
||||
let (env, interns, rc_proc_gen) = self.env_interns_helpers_mut();
|
||||
let module_id = env.module_id;
|
||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||
|
||||
rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following)
|
||||
};
|
||||
|
||||
if let Some((rc_proc_symbol, rc_proc_layout)) = new_proc_info {
|
||||
self.refcount_proc_symbols_mut()
|
||||
.push((rc_proc_symbol, rc_proc_layout));
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.helper_proc_symbols_mut().push(spec);
|
||||
}
|
||||
|
||||
self.build_stmt(rc_stmt, ret_layout)
|
||||
|
@ -538,7 +535,7 @@ trait Backend<'a> {
|
|||
debug_assert_eq!(
|
||||
1,
|
||||
args.len(),
|
||||
"RefCountGetPtr: expected to have exactly two argument"
|
||||
"RefCountGetPtr: expected to have exactly one argument"
|
||||
);
|
||||
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 (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 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);
|
||||
|
||||
rc_procs
|
||||
helper_procs
|
||||
};
|
||||
|
||||
let empty = bumpalo::collections::Vec::new_in(arena);
|
||||
let rc_symbols_and_layouts = std::mem::replace(backend.refcount_proc_symbols_mut(), empty);
|
||||
let mut rc_names_symbols_procs = Vec::with_capacity_in(rc_procs.len(), arena);
|
||||
let helper_symbols_and_layouts = std::mem::replace(backend.helper_proc_symbols_mut(), empty);
|
||||
let mut helper_names_symbols_procs = Vec::with_capacity_in(helper_procs.len(), arena);
|
||||
|
||||
// Names and linker data for refcounting procedures
|
||||
for ((sym, layout), proc) in rc_symbols_and_layouts.into_iter().zip(rc_procs) {
|
||||
// Names and linker data for helpers
|
||||
for ((sym, layout), proc) in helper_symbols_and_layouts.into_iter().zip(helper_procs) {
|
||||
let layout_id = layout_ids.get_toplevel(sym, &layout);
|
||||
let fn_name = backend.symbol_to_string(sym, layout_id);
|
||||
if let Some(proc_id) = output.symbol_id(fn_name.as_bytes()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
||||
}
|
||||
|
||||
// Build refcounting procedures
|
||||
for (fn_name, section_id, proc_id, proc) in rc_names_symbols_procs {
|
||||
// Build helpers
|
||||
for (fn_name, section_id, proc_id, proc) in helper_names_symbols_procs {
|
||||
build_proc(
|
||||
&mut output,
|
||||
&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 {
|
||||
match output.add_relocation(section_id, reloc) {
|
||||
Ok(obj) => obj,
|
||||
|
|
|
@ -15,8 +15,8 @@ use crate::llvm::build_list::{
|
|||
list_single, list_sort_with, list_sublist, list_swap,
|
||||
};
|
||||
use crate::llvm::build_str::{
|
||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_repeat, str_split,
|
||||
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
|
||||
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_trim_right,
|
||||
};
|
||||
|
@ -779,9 +779,6 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
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(),
|
||||
Str(str_literal) => {
|
||||
if str_literal.is_empty() {
|
||||
empty_str(env)
|
||||
} else {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
let number_of_chars = str_literal.len() as u64;
|
||||
|
@ -840,9 +837,8 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
.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")
|
||||
};
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(array_alloca, &[index_val], "index") };
|
||||
|
||||
builder.build_store(elem_ptr, val);
|
||||
}
|
||||
|
@ -885,12 +881,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
str_type,
|
||||
"cast_collection",
|
||||
)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
// 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 code_builder::Align;
|
||||
use roc_builtins::bitcode::IntWidth;
|
||||
use roc_builtins::bitcode::{self, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::gen_refcount::{RefcountProcGenerator, REFCOUNT_MAX};
|
||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||
use roc_mono::code_gen_help::{CodeGenHelp, REFCOUNT_MAX};
|
||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, ProcLayout, Stmt};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||
use roc_reporting::internal_error;
|
||||
|
||||
|
@ -49,7 +49,7 @@ pub struct WasmBackend<'a> {
|
|||
builtin_sym_index_map: MutMap<&'a str, usize>,
|
||||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||
linker_symbols: Vec<'a, SymInfo>,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
|
||||
// Function-level data
|
||||
code_builder: CodeBuilder<'a>,
|
||||
|
@ -71,7 +71,7 @@ impl<'a> WasmBackend<'a> {
|
|||
proc_symbols: Vec<'a, (Symbol, u32)>,
|
||||
mut linker_symbols: Vec<'a, SymInfo>,
|
||||
mut exports: Vec<'a, Export>,
|
||||
refcount_proc_gen: RefcountProcGenerator<'a>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
) -> Self {
|
||||
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
||||
let arena = env.arena;
|
||||
|
@ -144,7 +144,7 @@ impl<'a> WasmBackend<'a> {
|
|||
builtin_sym_index_map: MutMap::default(),
|
||||
proc_symbols,
|
||||
linker_symbols,
|
||||
refcount_proc_gen,
|
||||
helper_proc_gen,
|
||||
|
||||
// Function-level data
|
||||
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
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
self.refcount_proc_gen
|
||||
.generate_refcount_procs(self.env.arena, ident_ids)
|
||||
self.helper_proc_gen
|
||||
.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> {
|
||||
|
@ -500,8 +519,8 @@ impl<'a> WasmBackend<'a> {
|
|||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (rc_stmt, new_proc_info) = self
|
||||
.refcount_proc_gen
|
||||
let (rc_stmt, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.expand_refcount_stmt(ident_ids, *layout, modify, *following);
|
||||
|
||||
if false {
|
||||
|
@ -509,24 +528,9 @@ impl<'a> WasmBackend<'a> {
|
|||
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,
|
||||
// so that we can correctly generate calls to it.
|
||||
if let Some((rc_proc_sym, rc_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(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,
|
||||
}));
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
}
|
||||
|
||||
self.build_stmt(rc_stmt, ret_layout);
|
||||
|
@ -560,7 +564,13 @@ impl<'a> WasmBackend<'a> {
|
|||
CallType::ByName { name: func_sym, .. } => {
|
||||
// If this function is just a lowlevel wrapper, then inline it
|
||||
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(
|
||||
|
@ -596,7 +606,7 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
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),
|
||||
|
@ -925,6 +935,7 @@ impl<'a> WasmBackend<'a> {
|
|||
arguments: &'a [Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: WasmLayout,
|
||||
storage: &StoredValue,
|
||||
) {
|
||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||
self.env.arena,
|
||||
|
@ -949,6 +960,37 @@ impl<'a> WasmBackend<'a> {
|
|||
BuiltinCall(name) => {
|
||||
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 => {
|
||||
todo!("Low level operation {:?}", lowlevel)
|
||||
}
|
||||
|
@ -977,14 +1019,11 @@ impl<'a> WasmBackend<'a> {
|
|||
};
|
||||
}
|
||||
|
||||
StoredValue::StackMemory { location, .. } => match lit {
|
||||
Literal::Decimal(decimal) => {
|
||||
StoredValue::StackMemory { location, .. } => {
|
||||
let mut write128 = |lower_bits, upper_bits| {
|
||||
let (local_id, offset) =
|
||||
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.i64_const(lower_bits);
|
||||
self.code_builder.i64_store(Align::Bytes8, offset);
|
||||
|
@ -992,6 +1031,22 @@ impl<'a> WasmBackend<'a> {
|
|||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i64_const(upper_bits);
|
||||
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) =
|
||||
|
@ -1024,7 +1079,8 @@ impl<'a> WasmBackend<'a> {
|
|||
};
|
||||
}
|
||||
_ => not_supported_error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
_ => not_supported_error(),
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_builtins::bitcode::IntWidth;
|
|||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
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::layout::LayoutIds;
|
||||
use roc_reporting::internal_error;
|
||||
|
@ -94,7 +94,7 @@ pub fn build_module_help<'a>(
|
|||
proc_symbols,
|
||||
linker_symbols,
|
||||
exports,
|
||||
RefcountProcGenerator::new(env.arena, IntWidth::I32, env.module_id),
|
||||
CodeGenHelp::new(env.arena, IntWidth::I32, env.module_id),
|
||||
);
|
||||
|
||||
if false {
|
||||
|
@ -110,21 +110,21 @@ pub fn build_module_help<'a>(
|
|||
backend.build_proc(proc);
|
||||
}
|
||||
|
||||
// Generate IR for refcounting procs
|
||||
let refcount_procs = backend.generate_refcount_procs();
|
||||
// Generate specialized helpers for refcounting & equality
|
||||
let helper_procs = backend.generate_helpers();
|
||||
|
||||
backend.register_symbol_debug_names();
|
||||
|
||||
if false {
|
||||
println!("## refcount_procs");
|
||||
for proc in refcount_procs.iter() {
|
||||
println!("## helper_procs");
|
||||
for proc in helper_procs.iter() {
|
||||
println!("{}", proc.to_pretty(200));
|
||||
println!("{:#?}", proc);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Wasm for refcounting procs
|
||||
for proc in refcount_procs.iter() {
|
||||
for proc in helper_procs.iter() {
|
||||
backend.build_proc(proc);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,14 @@ use roc_reporting::internal_error;
|
|||
|
||||
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
||||
use crate::storage::{Storage, StoredValue};
|
||||
use crate::wasm_module::{CodeBuilder, ValueType::*};
|
||||
use crate::wasm_module::{Align, CodeBuilder, ValueType::*};
|
||||
|
||||
pub enum LowlevelBuildResult {
|
||||
Done,
|
||||
BuiltinCall(&'static str),
|
||||
SpecializedEq,
|
||||
SpecializedNotEq,
|
||||
SpecializedHash,
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
|
@ -17,7 +20,7 @@ pub fn decode_low_level<'a>(
|
|||
code_builder: &mut CodeBuilder<'a>,
|
||||
storage: &mut Storage<'a>,
|
||||
lowlevel: LowLevel,
|
||||
args: &'a [Symbol],
|
||||
args: &[Symbol],
|
||||
ret_layout: &WasmLayout,
|
||||
) -> LowlevelBuildResult {
|
||||
use LowlevelBuildResult::*;
|
||||
|
@ -81,8 +84,6 @@ pub fn decode_low_level<'a>(
|
|||
WasmLayout::Primitive(value_type, size) => match value_type {
|
||||
I32 => {
|
||||
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);
|
||||
}
|
||||
I64 => code_builder.i64_add(),
|
||||
|
@ -347,27 +348,56 @@ pub fn decode_low_level<'a>(
|
|||
},
|
||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
NumIsFinite => match ret_layout {
|
||||
WasmLayout::Primitive(value_type, _) => match value_type {
|
||||
I32 => code_builder.i32_const(1),
|
||||
I64 => code_builder.i32_const(1),
|
||||
NumIsFinite => {
|
||||
use StoredValue::*;
|
||||
match storage.get(&args[0]) {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
match value_type {
|
||||
I32 | I64 => code_builder.i32_const(1), // always true for integers
|
||||
F32 => {
|
||||
code_builder.i32_reinterpret_f32();
|
||||
code_builder.i32_const(0x7f800000);
|
||||
code_builder.i32_const(0x7f80_0000);
|
||||
code_builder.i32_and();
|
||||
code_builder.i32_const(0x7f800000);
|
||||
code_builder.i32_const(0x7f80_0000);
|
||||
code_builder.i32_ne();
|
||||
}
|
||||
F64 => {
|
||||
code_builder.i64_reinterpret_f64();
|
||||
code_builder.i64_const(0x7ff0000000000000);
|
||||
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
code_builder.i64_and();
|
||||
code_builder.i64_const(0x7ff0000000000000);
|
||||
code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
code_builder.i64_ne();
|
||||
}
|
||||
},
|
||||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
}
|
||||
}
|
||||
StackMemory {
|
||||
format, location, ..
|
||||
} => {
|
||||
let (local_id, offset) = location.local_and_offset(storage.stack_frame_pointer);
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NumAtan => {
|
||||
let width = float_width_from_layout(ret_layout);
|
||||
return BuiltinCall(&bitcode::NUM_ATAN[width]);
|
||||
|
@ -468,16 +498,80 @@ pub fn decode_low_level<'a>(
|
|||
WasmLayout::StackMemory { .. } => return NotImplemented,
|
||||
}
|
||||
}
|
||||
Eq => match storage.get(&args[0]) {
|
||||
StoredValue::VirtualMachineStack { value_type, .. }
|
||||
| StoredValue::Local { value_type, .. } => match value_type {
|
||||
Eq => {
|
||||
use StoredValue::*;
|
||||
match storage.get(&args[0]).to_owned() {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
match value_type {
|
||||
I32 => code_builder.i32_eq(),
|
||||
I64 => code_builder.i64_eq(),
|
||||
F32 => code_builder.f32_eq(),
|
||||
F64 => code_builder.f64_eq(),
|
||||
},
|
||||
StoredValue::StackMemory { .. } => return NotImplemented,
|
||||
},
|
||||
}
|
||||
}
|
||||
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]) {
|
||||
StoredValue::VirtualMachineStack { value_type, .. }
|
||||
| StoredValue::Local { value_type, .. } => match value_type {
|
||||
|
@ -486,12 +580,19 @@ pub fn decode_low_level<'a>(
|
|||
F32 => code_builder.f32_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(),
|
||||
Or => code_builder.i32_or(),
|
||||
Not => code_builder.i32_eqz(),
|
||||
Hash => return NotImplemented,
|
||||
Hash => return SpecializedHash,
|
||||
ExpectTrue => return NotImplemented,
|
||||
RefCountGetPtr => {
|
||||
code_builder.i32_const(4);
|
||||
|
|
|
@ -23,14 +23,13 @@ use roc_mono::ir::{
|
|||
UpdateModeIds,
|
||||
};
|
||||
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::{
|
||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
ExposedName, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{self, ParseProblem, Parser, SyntaxError};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_reporting::internal_error;
|
||||
use roc_solve::module::SolvedModule;
|
||||
use roc_solve::solve;
|
||||
use roc_types::solved_types::Solved;
|
||||
|
@ -685,7 +684,7 @@ enum HeaderFor<'a> {
|
|||
to_platform: To<'a>,
|
||||
},
|
||||
PkgConfig {
|
||||
/// usually `base`
|
||||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
/// the type scheme of the main function (required by the platform)
|
||||
/// (currently unused)
|
||||
|
@ -2554,8 +2553,8 @@ fn parse_header<'a>(
|
|||
opt_shorthand,
|
||||
header_src,
|
||||
packages: &[],
|
||||
exposes: header.exposes.items,
|
||||
imports: header.imports.items,
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: None,
|
||||
};
|
||||
|
||||
|
@ -2576,7 +2575,7 @@ fn parse_header<'a>(
|
|||
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
||||
};
|
||||
|
||||
let packages = header.packages.items;
|
||||
let packages = unspace(arena, header.packages.items);
|
||||
|
||||
let info = HeaderInfo {
|
||||
loc_name: Located {
|
||||
|
@ -2588,8 +2587,8 @@ fn parse_header<'a>(
|
|||
opt_shorthand,
|
||||
header_src,
|
||||
packages,
|
||||
exposes: header.provides.items,
|
||||
imports: header.imports.items,
|
||||
exposes: unspace(arena, header.provides.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: Some(header.to.value),
|
||||
};
|
||||
|
||||
|
@ -2603,19 +2602,17 @@ fn parse_header<'a>(
|
|||
|
||||
match header.to.value {
|
||||
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;
|
||||
|
||||
match value.extract_spaces().item {
|
||||
PackageEntry::Entry { shorthand, .. } => shorthand == existing_package,
|
||||
_ => internal_error!(),
|
||||
if value.shorthand == existing_package {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
match opt_base_package {
|
||||
Some(Located {
|
||||
value:
|
||||
PackageEntry::Entry {
|
||||
if let Some(PackageEntry {
|
||||
shorthand,
|
||||
package_or_path:
|
||||
Located {
|
||||
|
@ -2623,9 +2620,8 @@ fn parse_header<'a>(
|
|||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
}) = opt_base_package
|
||||
{
|
||||
match package_or_path {
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
||||
// check whether we can find a Package-Config.roc file
|
||||
|
@ -2646,10 +2642,7 @@ fn parse_header<'a>(
|
|||
|
||||
Ok((
|
||||
module_id,
|
||||
Msg::Many(vec![
|
||||
app_module_header_msg,
|
||||
load_pkg_config_msg,
|
||||
]),
|
||||
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]),
|
||||
))
|
||||
} else {
|
||||
Ok((module_id, app_module_header_msg))
|
||||
|
@ -2657,8 +2650,8 @@ fn parse_header<'a>(
|
|||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => panic!("could not find base"),
|
||||
} else {
|
||||
panic!("could not find base")
|
||||
}
|
||||
}
|
||||
To::NewPackage(package_or_path) => match package_or_path {
|
||||
|
@ -2766,7 +2759,7 @@ struct HeaderInfo<'a> {
|
|||
opt_shorthand: Option<&'a str>,
|
||||
header_src: &'a str,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
exposes: &'a [Located<ExposedName<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'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
|
||||
//
|
||||
// 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.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
|
@ -2913,24 +2906,13 @@ fn send_header<'a>(
|
|||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value);
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
let package_entries = packages
|
||||
.iter()
|
||||
.map(|pkg| {
|
||||
let pkg = pkg.value;
|
||||
(pkg.shorthand, pkg.package_or_path.value)
|
||||
})
|
||||
.collect::<MutMap<_, _>>();
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
|
@ -2989,7 +2971,7 @@ struct PlatformHeaderInfo<'a> {
|
|||
header_src: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
provides: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
provides: &'a [Located<ExposedName<'a>>],
|
||||
requires: &'a [Located<TypedIdent<'a>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
}
|
||||
|
@ -2997,7 +2979,6 @@ struct PlatformHeaderInfo<'a> {
|
|||
// TODO refactor so more logic is shared with `send_header`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
info: PlatformHeaderInfo<'a>,
|
||||
parse_state: parser::State<'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
|
||||
//
|
||||
// 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.)
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
|
@ -3105,15 +3086,17 @@ fn send_header_two<'a>(
|
|||
.entry(app_module_id)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
for (loc_ident, _) in unpack_exposes_entries(arena, requires) {
|
||||
let ident: Ident = loc_ident.value.into();
|
||||
for entry in requires {
|
||||
let entry = entry.value;
|
||||
|
||||
let ident: Ident = entry.ident.value.into();
|
||||
let ident_id = ident_ids.get_or_insert(&ident);
|
||||
let symbol = Symbol::new(app_module_id, ident_id);
|
||||
|
||||
// Since this value is exposed, add it to our module's default scope.
|
||||
debug_assert!(!scope.contains_key(&ident.clone()));
|
||||
|
||||
scope.insert(ident, (symbol, loc_ident.region));
|
||||
scope.insert(ident, (symbol, entry.ident.region));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3146,24 +3129,10 @@ fn send_header_two<'a>(
|
|||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = packages.iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value);
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
let package_entries = packages
|
||||
.iter()
|
||||
.map(|pkg| (pkg.value.shorthand, pkg.value.package_or_path.value))
|
||||
.collect::<MutMap<_, _>>();
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// 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)]
|
||||
fn fabricate_pkg_config_module<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -3355,8 +3334,6 @@ fn fabricate_pkg_config_module<'a>(
|
|||
header_src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] = header.provides.items;
|
||||
|
||||
let info = PlatformHeaderInfo {
|
||||
filename,
|
||||
is_root_module: false,
|
||||
|
@ -3364,13 +3341,15 @@ fn fabricate_pkg_config_module<'a>(
|
|||
header_src,
|
||||
app_module_id,
|
||||
packages: &[],
|
||||
provides,
|
||||
requires: arena.alloc([header.requires.signature]),
|
||||
imports: header.imports.items,
|
||||
provides: unspace(arena, header.provides.items),
|
||||
requires: &*arena.alloc([Located::at(
|
||||
header.requires.signature.region,
|
||||
header.requires.signature.extract_spaces().item,
|
||||
)]),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
};
|
||||
|
||||
send_header_two(
|
||||
arena,
|
||||
info,
|
||||
parse_state,
|
||||
module_ids,
|
||||
|
@ -3413,14 +3392,14 @@ fn fabricate_effects_module<'a>(
|
|||
let mut module_ids = (*module_ids).lock();
|
||||
|
||||
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_name.as_str().into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let exposed_ident_ids = {
|
||||
// Lock just long enough to perform the minimal operations necessary.
|
||||
|
@ -3633,33 +3612,16 @@ fn fabricate_effects_module<'a>(
|
|||
|
||||
fn unpack_exposes_entries<'a>(
|
||||
arena: &'a Bump,
|
||||
entries: &'a [Located<TypedIdent<'a>>],
|
||||
) -> bumpalo::collections::Vec<'a, (&'a Located<&'a str>, &'a Located<TypeAnnotation<'a>>)> {
|
||||
entries: &'a [Located<Spaced<'a, TypedIdent<'a>>>],
|
||||
) -> bumpalo::collections::Vec<'a, (Located<&'a str>, Located<TypeAnnotation<'a>>)> {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
let mut stack: Vec<&TypedIdent> = Vec::with_capacity_in(entries.len(), arena);
|
||||
let mut output = Vec::with_capacity_in(entries.len(), arena);
|
||||
let iter = entries.iter().map(|entry| {
|
||||
let entry: TypedIdent<'a> = entry.value.extract_spaces().item;
|
||||
(entry.ident, entry.ann)
|
||||
});
|
||||
|
||||
for entry in entries.iter() {
|
||||
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
|
||||
Vec::from_iter_in(iter, arena)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -3849,21 +3811,11 @@ fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>
|
|||
|
||||
(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 {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
|
||||
match entry {
|
||||
Exposed(ident) => (*ident).into(),
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry),
|
||||
}
|
||||
fn ident_from_exposed(entry: &Spaced<'_, ExposedName<'_>>) -> Ident {
|
||||
entry.extract_spaces().item.as_str().into()
|
||||
}
|
||||
|
||||
#[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(r"Does the module header contain an entry that looks like this:"),
|
||||
alloc
|
||||
.parser_suggestion(" packages { base: \"platform\" }")
|
||||
.parser_suggestion(" packages { pf: \"platform\" }")
|
||||
.indent(4),
|
||||
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
|
||||
/// 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
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::ir::{
|
|||
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, Literal, ModifyRc, Proc,
|
||||
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_UNIT: Layout = Layout::Struct(&[]);
|
||||
|
@ -21,71 +21,79 @@ const LAYOUT_U32: Layout = Layout::Builtin(Builtin::Int(IntWidth::U32));
|
|||
pub const REFCOUNT_MAX: usize = 0;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RefcountOp {
|
||||
pub enum HelperOp {
|
||||
Inc,
|
||||
Dec,
|
||||
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
|
||||
/// `RefcountProcGenerator::expand_refcount_stmt()`, which returns IR statements
|
||||
/// to call a refcounting procedure. The backend can then generate target code
|
||||
/// for those IR statements instead of the original `Refcounting` statement.
|
||||
/// For example, when checking List equality, we need to visit each element and compare them.
|
||||
/// Depending on the type of the list elements, we may need to recurse deeper into each element.
|
||||
/// For tag unions, we may need branches for different tag IDs, etc.
|
||||
///
|
||||
/// Essentially we are expanding the `Refcounting` statement into a more detailed
|
||||
/// form that's more suitable for code generation.
|
||||
/// This module creates specialized helper procs for all such operations and types used in the program.
|
||||
///
|
||||
/// But so far, we've only mentioned _calls_ to the refcounting procedures.
|
||||
/// The procedures themselves don't exist yet!
|
||||
/// The backend drives the process, in two steps:
|
||||
/// 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,
|
||||
/// 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> {
|
||||
pub struct CodeGenHelp<'a> {
|
||||
arena: &'a Bump,
|
||||
home: ModuleId,
|
||||
ptr_size: u32,
|
||||
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
|
||||
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 {
|
||||
RefcountProcGenerator {
|
||||
CodeGenHelp {
|
||||
arena,
|
||||
home,
|
||||
ptr_size: intwidth_isize.stack_size(),
|
||||
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.
|
||||
/// The helper procs themselves can be generated later by calling `generate_refcount_procs`
|
||||
/// Expand a `Refcounting` node to a `Let` node that calls a specialized helper proc.
|
||||
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||
pub fn expand_refcount_stmt(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
modify: &ModifyRc,
|
||||
following: &'a Stmt<'a>,
|
||||
) -> (&'a Stmt<'a>, Option<(Symbol, ProcLayout<'a>)>) {
|
||||
if !Self::layout_is_supported(&layout) {
|
||||
) -> (&'a Stmt<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
if !Self::is_rc_implemented_yet(&layout) {
|
||||
// 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.
|
||||
println!(
|
||||
"WARNING! MEMORY LEAK! Refcounting not yet implemented for Layout {:?}",
|
||||
layout
|
||||
);
|
||||
return (following, None);
|
||||
return (following, Vec::new_in(self.arena));
|
||||
}
|
||||
|
||||
let arena = self.arena;
|
||||
|
@ -94,8 +102,8 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
ModifyRc::Inc(structure, amount) => {
|
||||
let layout_isize = self.layout_isize;
|
||||
|
||||
let (is_existing, proc_name) =
|
||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Inc);
|
||||
let (proc_name, new_procs_info) =
|
||||
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Inc);
|
||||
|
||||
// Define a constant for the amount to increment
|
||||
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 rc_stmt = arena.alloc(amount_stmt(arena.alloc(call_stmt)));
|
||||
|
||||
// Create a linker symbol for the helper proc if this is the first usage
|
||||
let new_proc_info = if is_existing {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
proc_name,
|
||||
ProcLayout {
|
||||
arguments: arg_layouts,
|
||||
result: LAYOUT_UNIT,
|
||||
},
|
||||
))
|
||||
};
|
||||
|
||||
(rc_stmt, new_proc_info)
|
||||
(rc_stmt, new_procs_info)
|
||||
}
|
||||
|
||||
ModifyRc::Dec(structure) => {
|
||||
let (is_existing, proc_name) =
|
||||
self.get_proc_symbol(ident_ids, layout, RefcountOp::Dec);
|
||||
let (proc_name, new_procs_info) =
|
||||
self.get_or_create_proc_symbols_recursive(ident_ids, &layout, HelperOp::Dec);
|
||||
|
||||
// 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_expr = Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
|
@ -157,20 +151,7 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
following,
|
||||
));
|
||||
|
||||
// Create a linker symbol for the helper proc if this is the first usage
|
||||
let new_proc_info = if is_existing {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
proc_name,
|
||||
ProcLayout {
|
||||
arguments: arg_layouts,
|
||||
result: LAYOUT_UNIT,
|
||||
},
|
||||
))
|
||||
};
|
||||
|
||||
(rc_stmt, new_proc_info)
|
||||
(rc_stmt, new_procs_info)
|
||||
}
|
||||
|
||||
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 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
|
||||
// (Probably by generating procs on the fly instead of all at the end)
|
||||
fn layout_is_supported(layout: &Layout) -> bool {
|
||||
/// Replace a generic `Lowlevel::Eq` call with a specialized helper proc.
|
||||
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||
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))
|
||||
}
|
||||
|
||||
/// Generate refcounting helper procs, each specialized to a particular Layout.
|
||||
/// 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.
|
||||
pub fn generate_refcount_procs(
|
||||
pub fn generate_procs(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
ident_ids: &mut IdentIds,
|
||||
) -> Vec<'a, Proc<'a>> {
|
||||
// Move the vector out of self, so we can loop over it safely
|
||||
let mut procs_to_generate =
|
||||
std::mem::replace(&mut self.procs_to_generate, Vec::with_capacity_in(0, arena));
|
||||
use HelperOp::*;
|
||||
|
||||
let procs_iter = procs_to_generate
|
||||
.drain(0..)
|
||||
.map(|(layout, op, proc_symbol)| {
|
||||
debug_assert!(Self::layout_is_supported(&layout));
|
||||
// Move the vector out of self, so we can loop over it safely
|
||||
let mut specs = std::mem::replace(&mut self.specs, Vec::with_capacity_in(0, arena));
|
||||
|
||||
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 {
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Find the Symbol of the procedure for this layout and refcount operation,
|
||||
/// or create one if needed.
|
||||
fn get_proc_symbol(
|
||||
/// Find the Symbol of the procedure for this layout and operation
|
||||
/// If any new helper procs are needed for this layout or its children,
|
||||
/// return their details in a vector.
|
||||
fn get_or_create_proc_symbols_recursive(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
op: RefcountOp,
|
||||
) -> (bool, Symbol) {
|
||||
let found = self
|
||||
.procs_to_generate
|
||||
.iter()
|
||||
.find(|(l, o, _)| *l == layout && *o == op);
|
||||
layout: &Layout<'a>,
|
||||
op: HelperOp,
|
||||
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut new_procs_info = Vec::new_in(self.arena);
|
||||
|
||||
let proc_symbol =
|
||||
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 {
|
||||
(true, *existing_symbol)
|
||||
(*existing_symbol, None)
|
||||
} else {
|
||||
let layout_name = layout_debug_name(&layout);
|
||||
let unique_idx = self.procs_to_generate.len();
|
||||
let debug_name = format!("#rc{:?}_{}_{}", op, layout_name, unique_idx);
|
||||
let layout_name = layout_debug_name(layout);
|
||||
let debug_name = format!("#help{:?}_{}", op, layout_name);
|
||||
let new_symbol: Symbol = self.create_symbol(ident_ids, &debug_name);
|
||||
self.procs_to_generate.push((layout, op, new_symbol));
|
||||
(false, new_symbol)
|
||||
self.specs.push((*layout, op, 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)
|
||||
}
|
||||
|
||||
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);
|
||||
match op {
|
||||
RefcountOp::Inc => {
|
||||
HelperOp::Inc => {
|
||||
let inc_amount = (self.layout_isize, Symbol::ARG_2);
|
||||
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(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
op: RefcountOp,
|
||||
op: HelperOp,
|
||||
proc_name: Symbol,
|
||||
) -> Proc<'a> {
|
||||
let string = Symbol::ARG_1;
|
||||
|
@ -349,20 +466,21 @@ impl<'a> RefcountProcGenerator<'a> {
|
|||
// 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_expr = match op {
|
||||
RefcountOp::Inc => Expr::Call(Call {
|
||||
HelperOp::Inc => Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::RefCountInc,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
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 {
|
||||
op: LowLevel::RefCountDec,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: self.arena.alloc([rc_ptr, alignment]),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
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 borrow;
|
||||
pub mod gen_refcount;
|
||||
pub mod code_gen_help;
|
||||
pub mod inc_dec;
|
||||
pub mod ir;
|
||||
pub mod layout;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::header::{
|
||||
AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PackageEntry, PlatformHeader,
|
||||
PlatformRigid, TypedIdent,
|
||||
};
|
||||
use crate::header::{AppHeader, InterfaceHeader, PlatformHeader};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -17,6 +14,33 @@ pub struct Spaces<'a, T> {
|
|||
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 {
|
||||
type 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> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
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> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
AssignedField::SpaceBefore(self, spaces)
|
||||
|
@ -823,8 +838,55 @@ macro_rules! impl_extract_spaces {
|
|||
impl_extract_spaces!(Expr);
|
||||
impl_extract_spaces!(Tag);
|
||||
impl_extract_spaces!(AssignedField<T>);
|
||||
impl_extract_spaces!(PlatformRigid);
|
||||
impl_extract_spaces!(TypedIdent);
|
||||
impl_extract_spaces!(ImportsEntry);
|
||||
impl_extract_spaces!(ExposesEntry<T>);
|
||||
impl_extract_spaces!(PackageEntry);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
||||
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::ident::lowercase_ident;
|
||||
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)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
|
@ -81,9 +100,9 @@ pub enum To<'a> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -102,7 +121,7 @@ pub struct AppHeader<'a> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'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 imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
|
@ -118,37 +137,25 @@ pub struct PackageHeader<'a> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PlatformRigid<'a> {
|
||||
Entry { rigid: &'a str, 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)
|
||||
}
|
||||
pub struct PlatformRigid<'a> {
|
||||
pub rigid: &'a str,
|
||||
pub alias: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformRequires<'a> {
|
||||
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
|
||||
pub signature: Loc<TypedIdent<'a>>,
|
||||
pub rigids: Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>,
|
||||
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: PlatformRequires<'a>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// 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 effect_shortname: &'a str,
|
||||
pub effect_type_name: &'a str,
|
||||
pub entries: Collection<'a, Loc<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)
|
||||
}
|
||||
pub entries: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -201,71 +189,35 @@ pub enum ImportsEntry<'a> {
|
|||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(
|
||||
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(
|
||||
&'a str,
|
||||
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> {
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
use ExposesEntry::*;
|
||||
|
||||
match self {
|
||||
Exposed(string) => string,
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
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 struct TypedIdent<'a> {
|
||||
pub ident: Loc<&'a str>,
|
||||
pub spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
pub ann: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
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>]),
|
||||
pub struct PackageEntry<'a> {
|
||||
pub shorthand: &'a str,
|
||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
pub package_or_path: Loc<PackageOrPath<'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>> {
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||
move |arena, state| {
|
||||
// You may optionally have a package shorthand,
|
||||
// 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)?;
|
||||
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
},
|
||||
None => PackageEntry::Entry {
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
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::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,
|
||||
TypedIdent,
|
||||
};
|
||||
|
@ -220,7 +220,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
|||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
|
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
entries: Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
to: Located<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
|
@ -362,7 +362,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EProvides<'a>,
|
||||
> {
|
||||
|
@ -385,14 +385,14 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
EProvides::Open,
|
||||
EProvides::Space,
|
||||
EProvides::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_entry<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, &'a str>>, E>
|
||||
) -> impl Parser<'a, Located<Spaced<'a, ExposedName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Copy,
|
||||
|
@ -400,7 +400,7 @@ where
|
|||
{
|
||||
loc!(map!(
|
||||
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)]
|
||||
fn requires_rigids<'a>(
|
||||
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!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
||||
|
@ -454,23 +454,24 @@ fn requires_rigids<'a>(
|
|||
ERequires::Open,
|
||||
ERequires::Space,
|
||||
ERequires::IndentListEnd,
|
||||
PlatformRigid::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, PlatformRigid<'a>, ()> {
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, Spaced<'a, PlatformRigid<'a>>, ()> {
|
||||
map!(
|
||||
and!(
|
||||
lowercase_ident(),
|
||||
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
|
||||
),
|
||||
|(rigid, alias)| PlatformRigid::Entry { rigid, alias }
|
||||
|(rigid, alias)| Spaced::Item(PlatformRigid { rigid, alias })
|
||||
)
|
||||
}
|
||||
|
||||
#[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!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
skip_second!(
|
||||
|
@ -491,7 +492,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Located<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -515,7 +516,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -545,7 +546,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
Collection<'a, Located<Spaced<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -569,14 +570,14 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_module<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, ModuleName<'a>>>, E>
|
||||
) -> impl Parser<'a, Located<Spaced<'a, ModuleName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Copy,
|
||||
|
@ -584,13 +585,13 @@ where
|
|||
{
|
||||
loc!(map!(
|
||||
specialize(|_, r, c| to_expectation(r, c), module_name()),
|
||||
ExposesEntry::Exposed
|
||||
Spaced::Item
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Packages<'a> {
|
||||
entries: Collection<'a, Located<PackageEntry<'a>>>,
|
||||
entries: Collection<'a, Located<Spaced<'a, PackageEntry<'a>>>>,
|
||||
before_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::Space,
|
||||
EPackages::IndentListEnd,
|
||||
PackageEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
),
|
||||
|((before_packages_keyword, after_packages_keyword), entries): (
|
||||
|
@ -639,7 +640,7 @@ fn imports<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Located<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
),
|
||||
EImports,
|
||||
> {
|
||||
|
@ -663,7 +664,7 @@ fn imports<'a>() -> impl Parser<
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentListEnd,
|
||||
ImportsEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -706,7 +707,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
EEffects::Open,
|
||||
EEffects::Space,
|
||||
EEffects::IndentListEnd,
|
||||
TypedIdent::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
@ -726,7 +727,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
}
|
||||
|
||||
#[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.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
|
@ -752,11 +753,11 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
|||
)
|
||||
),
|
||||
|((ident, spaces_before_colon), ann)| {
|
||||
TypedIdent::Entry {
|
||||
Spaced::Item(TypedIdent {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
ann,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -775,18 +776,18 @@ where
|
|||
}
|
||||
|
||||
#[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;
|
||||
|
||||
type Temp<'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!(
|
||||
and!(
|
||||
and!(
|
||||
// e.g. `base.`
|
||||
// e.g. `pf.`
|
||||
maybe!(skip_second!(
|
||||
shortname(),
|
||||
word1(b'.', EImports::ShorthandDot)
|
||||
|
@ -806,18 +807,20 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentSetEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
))
|
||||
),
|
||||
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||
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),
|
||||
|
||||
None => ImportsEntry::Module(module_name, exposed_values),
|
||||
}
|
||||
};
|
||||
|
||||
Spaced::Item(entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ Platform {
|
|||
},
|
||||
requires: PlatformRequires {
|
||||
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",
|
||||
spaces_before_colon: [],
|
||||
ann: |L 0-0, C 45-47| Record {
|
||||
|
|
|
@ -4,10 +4,10 @@ App {
|
|||
"quicksort",
|
||||
),
|
||||
packages: [
|
||||
|L 1-1, C 15-33| Entry {
|
||||
shorthand: "base",
|
||||
|L 1-1, C 15-31| PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 1-1, C 21-33| Path(
|
||||
package_or_path: |L 1-1, C 19-31| Path(
|
||||
PlainLine(
|
||||
"./platform",
|
||||
),
|
||||
|
@ -24,12 +24,12 @@ App {
|
|||
),
|
||||
],
|
||||
provides: [
|
||||
|L 3-3, C 15-24| Exposed(
|
||||
|L 3-3, C 15-24| ExposedName(
|
||||
"quicksort",
|
||||
),
|
||||
],
|
||||
to: |L 3-3, C 30-34| ExistingPackage(
|
||||
"base",
|
||||
to: |L 3-3, C 30-32| ExistingPackage(
|
||||
"pf",
|
||||
),
|
||||
before_header: [],
|
||||
after_app_keyword: [],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app "quicksort"
|
||||
packages { base: "./platform" }
|
||||
packages { pf: "./platform" }
|
||||
imports [ foo.Bar.Baz ]
|
||||
provides [ quicksort ] to base
|
||||
provides [ quicksort ] to pf
|
||||
|
|
|
@ -4,10 +4,10 @@ App {
|
|||
"quicksort",
|
||||
),
|
||||
packages: [
|
||||
|L 1-1, C 15-33| Entry {
|
||||
shorthand: "base",
|
||||
|L 1-1, C 15-31| PackageEntry {
|
||||
shorthand: "pf",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 1-1, C 21-33| Path(
|
||||
package_or_path: |L 1-1, C 19-31| Path(
|
||||
PlainLine(
|
||||
"./platform",
|
||||
),
|
||||
|
@ -23,7 +23,7 @@ App {
|
|||
Collection {
|
||||
items: [
|
||||
|L 3-3, C 8-11| SpaceBefore(
|
||||
Exposed(
|
||||
ExposedName(
|
||||
"Baz",
|
||||
),
|
||||
[
|
||||
|
@ -31,7 +31,7 @@ App {
|
|||
],
|
||||
),
|
||||
|L 4-4, C 8-16| SpaceBefore(
|
||||
Exposed(
|
||||
ExposedName(
|
||||
"FortyTwo",
|
||||
),
|
||||
[
|
||||
|
@ -49,12 +49,12 @@ App {
|
|||
),
|
||||
],
|
||||
provides: [
|
||||
|L 7-7, C 15-24| Exposed(
|
||||
|L 7-7, C 15-24| ExposedName(
|
||||
"quicksort",
|
||||
),
|
||||
],
|
||||
to: |L 7-7, C 31-35| ExistingPackage(
|
||||
"base",
|
||||
to: |L 7-7, C 31-33| ExistingPackage(
|
||||
"pf",
|
||||
),
|
||||
before_header: [],
|
||||
after_app_keyword: [],
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
app "quicksort"
|
||||
packages { base: "./platform", }
|
||||
packages { pf: "./platform", }
|
||||
imports [ foo.Bar.{
|
||||
Baz,
|
||||
FortyTwo,
|
||||
# I'm a happy comment
|
||||
} ]
|
||||
provides [ quicksort, ] to base
|
||||
provides [ quicksort, ] to pf
|
||||
|
|
|
@ -6,12 +6,12 @@ Platform {
|
|||
},
|
||||
requires: PlatformRequires {
|
||||
rigids: [
|
||||
|L 1-1, C 14-26| Entry {
|
||||
|L 1-1, C 14-26| PlatformRigid {
|
||||
rigid: "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",
|
||||
spaces_before_colon: [],
|
||||
ann: |L 1-1, C 37-39| Record {
|
||||
|
@ -22,7 +22,7 @@ Platform {
|
|||
},
|
||||
exposes: [],
|
||||
packages: [
|
||||
|L 3-3, C 15-27| Entry {
|
||||
|L 3-3, C 15-27| PackageEntry {
|
||||
shorthand: "foo",
|
||||
spaces_after_shorthand: [],
|
||||
package_or_path: |L 3-3, C 20-27| Path(
|
||||
|
@ -34,7 +34,7 @@ Platform {
|
|||
],
|
||||
imports: [],
|
||||
provides: [
|
||||
|L 5-5, C 15-26| Exposed(
|
||||
|L 5-5, C 15-26| ExposedName(
|
||||
"mainForHost",
|
||||
),
|
||||
],
|
||||
|
|
|
@ -31,7 +31,7 @@ fn nat_alias() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn i128_signed_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -115,7 +115,7 @@ fn i8_signed_int_alias() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn i128_hex_int_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -741,7 +741,7 @@ fn gen_int_less_than() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_dec_eq() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -761,7 +761,7 @@ fn gen_dec_eq() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_dec_neq() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -864,7 +864,7 @@ fn gen_sub_i64() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_mul_dec() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1830,7 +1830,7 @@ fn shift_right_zf_by() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn max_i128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -665,17 +665,17 @@ fn str_starts_with_false_small_str() {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_equality() {
|
||||
// assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
// assert_evals_to!(
|
||||
// r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
// assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn str_equality() {
|
||||
assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
assert_evals_to!(
|
||||
r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
true,
|
||||
bool
|
||||
);
|
||||
assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn nested_recursive_literal() {
|
||||
|
|
|
@ -58,9 +58,9 @@ mod insert_doc_syntax_highlighting {
|
|||
|
||||
pub const HELLO_WORLD: &str = r#"
|
||||
app "test-app"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main = "Hello, world!"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
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
|
||||
|
||||
|
@ -29,3 +29,46 @@ We thank the following open source projects in particular for inspiring us when
|
|||
- [rgx](https://github.com/cloudhead/rgx)
|
||||
- [elm-editor](https://github.com/jxxcarlson/elm-editor)
|
||||
- [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#"
|
||||
app "test-app"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main = "Hello, world!"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "cfold"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "closure"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
# see https://github.com/rtfeldman/roc/issues/985
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "deriv"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "nqueens"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "quicksortapp"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task, Quicksort]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task, Quicksort]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "rbtree-ck"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
|
||||
Color : [ Red, Black ]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "rbtree-del"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
|
||||
Color : [ Red, Black ]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "rbtree-insert"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "test-astar"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task, AStar]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task, AStar]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "test-base64"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task, Base64 ]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task, Base64 ]
|
||||
provides [ main ] to pf
|
||||
|
||||
IO a : Task.Task a []
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env roc
|
||||
|
||||
app "echo"
|
||||
packages { base: "platform" }
|
||||
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [ pf.Task.{ Task, await }, pf.Stdout, pf.Stdin ]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Task {} *
|
||||
main =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "effect-example"
|
||||
packages { base: "thing/platform-dir" }
|
||||
packages { pf: "thing/platform-dir" }
|
||||
imports [fx.Effect]
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Effect.Effect {}
|
||||
main =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Context
|
||||
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 ]
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env roc
|
||||
app "false"
|
||||
packages { base: "platform" }
|
||||
imports [ base.Task.{ Task }, base.Stdout, base.Stdin, Context.{ Context }, Variable.{ Variable } ]
|
||||
provides [ main ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [ pf.Task.{ Task }, pf.Stdout, pf.Stdin, Context.{ Context }, Variable.{ Variable } ]
|
||||
provides [ main ] to pf
|
||||
|
||||
# An interpreter for the False programming language: https://strlen.com/false-language/
|
||||
# This is just a silly example to test this variety of program.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "fib"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main = \n -> fib n 0 1
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "hello-rust"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "hello-swift"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main =
|
||||
host = "Swift"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "hello-web"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
app "hello-world"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
main = "Hello, World!\n"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "hello-world"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
provides [ main ] to pf
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "quicksort"
|
||||
packages { base: "platform" }
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ quicksort ] to base
|
||||
provides [ quicksort ] to pf
|
||||
|
||||
quicksort = \originalList ->
|
||||
n = List.len originalList
|
||||
|
|
|
@ -6048,9 +6048,9 @@ I need all branches in an `if` to have the same type!
|
|||
indoc!(
|
||||
r#"
|
||||
app "test-base64"
|
||||
packages { base: "platform" }
|
||||
imports [base.Task, Base64 ]
|
||||
provides [ main, @Foo ] to base
|
||||
packages { pf: "platform" }
|
||||
imports [pf.Task, Base64 ]
|
||||
provides [ main, @Foo ] to pf
|
||||
"#
|
||||
),
|
||||
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:
|
||||
|
||||
3│ imports [base.Task, Base64 ]
|
||||
4│ provides [ main, @Foo ] to base
|
||||
3│ imports [pf.Task, Base64 ]
|
||||
4│ provides [ main, @Foo ] to pf
|
||||
^
|
||||
|
||||
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#"
|
||||
interface Foobar
|
||||
exposes [ main, @Foo ]
|
||||
imports [base.Task, Base64 ]
|
||||
imports [pf.Task, Base64 ]
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
|
@ -6144,7 +6144,7 @@ I need all branches in an `if` to have the same type!
|
|||
r#"
|
||||
interface foobar
|
||||
exposes [ main, @Foo ]
|
||||
imports [base.Task, Base64 ]
|
||||
imports [pf.Task, Base64 ]
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
|
@ -6170,7 +6170,7 @@ I need all branches in an `if` to have the same type!
|
|||
r#"
|
||||
app foobar
|
||||
exposes [ main, @Foo ]
|
||||
imports [base.Task, Base64 ]
|
||||
imports [pf.Task, Base64 ]
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
|
|
|
@ -507,7 +507,7 @@ impl RocStr {
|
|||
pub fn storage(&self) -> Option<Storage> {
|
||||
use core::cmp::Ordering::*;
|
||||
|
||||
if self.is_small_str() || self.length == 0 {
|
||||
if self.is_small_str() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -660,7 +660,7 @@ impl RocStr {
|
|||
impl Default for RocStr {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
length: 0,
|
||||
length: isize::MIN as usize,
|
||||
elements: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
@ -693,7 +693,7 @@ impl Eq for RocStr {}
|
|||
|
||||
impl Clone for RocStr {
|
||||
fn clone(&self) -> Self {
|
||||
if self.is_small_str() || self.is_empty() {
|
||||
if self.is_small_str() {
|
||||
Self {
|
||||
elements: self.elements,
|
||||
length: self.length,
|
||||
|
@ -730,7 +730,7 @@ impl Clone for RocStr {
|
|||
|
||||
impl Drop for RocStr {
|
||||
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();
|
||||
|
||||
unsafe {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue