Merge branch 'trunk' into parser

This commit is contained in:
Richard Feldman 2021-04-29 21:50:04 -04:00 committed by GitHub
commit bae11e4da5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1533 additions and 808 deletions

View file

@ -30,6 +30,26 @@ pub type SendSet<K> = im::hashset::HashSet<K, BuildHasher>;
pub type BumpMap<'a, K, V> = hashbrown::HashMap<K, V, BuildHasher, hashbrown::BumpWrapper<'a>>;
pub trait BumpMapDefault<'a> {
fn new_in(arena: &'a bumpalo::Bump) -> Self;
fn with_capacity_in(capacity: usize, arena: &'a bumpalo::Bump) -> Self;
}
impl<'a, K, V> BumpMapDefault<'a> for BumpMap<'a, K, V> {
fn new_in(arena: &'a bumpalo::Bump) -> Self {
hashbrown::HashMap::with_hasher_in(default_hasher(), hashbrown::BumpWrapper(arena))
}
fn with_capacity_in(capacity: usize, arena: &'a bumpalo::Bump) -> Self {
hashbrown::HashMap::with_capacity_and_hasher_in(
capacity,
default_hasher(),
hashbrown::BumpWrapper(arena),
)
}
}
pub fn arena_join<'a, I>(arena: &'a Bump, strings: &mut I, join_str: &str) -> String<'a>
where
I: Iterator<Item = &'a str>,

View file

@ -842,7 +842,7 @@ struct State<'a> {
/// pending specializations in the same thread.
pub needs_specialization: MutSet<ModuleId>,
pub all_pending_specializations: MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>,
pub all_pending_specializations: MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
pub specializations_in_flight: u32,
@ -3969,6 +3969,7 @@ fn add_def_to_module<'a>(
procs.insert_exposed(
symbol,
layout,
mono_env.arena,
mono_env.subs,
def.annotation,
annotation,
@ -4021,6 +4022,7 @@ fn add_def_to_module<'a>(
procs.insert_exposed(
symbol,
layout,
mono_env.arena,
mono_env.subs,
def.annotation,
annotation,

View file

@ -6,7 +6,7 @@ use crate::layout::{
};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap, MutSet};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
@ -77,31 +77,35 @@ impl<'a> CapturedSymbols<'a> {
}
#[derive(Clone, Debug, PartialEq)]
pub struct PendingSpecialization {
pub struct PendingSpecialization<'a> {
solved_type: SolvedType,
host_exposed_aliases: MutMap<Symbol, SolvedType>,
host_exposed_aliases: BumpMap<'a, Symbol, SolvedType>,
}
impl PendingSpecialization {
pub fn from_var(subs: &Subs, var: Variable) -> Self {
impl<'a> PendingSpecialization<'a> {
pub fn from_var(arena: &'a Bump, subs: &Subs, var: Variable) -> Self {
let solved_type = SolvedType::from_var(subs, var);
PendingSpecialization {
solved_type,
host_exposed_aliases: MutMap::default(),
host_exposed_aliases: BumpMap::new_in(arena),
}
}
pub fn from_var_host_exposed(
arena: &'a Bump,
subs: &Subs,
var: Variable,
host_exposed_aliases: &MutMap<Symbol, Variable>,
exposed: &MutMap<Symbol, Variable>,
) -> Self {
let solved_type = SolvedType::from_var(subs, var);
let host_exposed_aliases = host_exposed_aliases
let mut host_exposed_aliases = BumpMap::with_capacity_in(exposed.len(), arena);
host_exposed_aliases.extend(
exposed
.iter()
.map(|(symbol, variable)| (*symbol, SolvedType::from_var(subs, *variable)))
.collect();
.map(|(symbol, variable)| (*symbol, SolvedType::from_var(subs, *variable))),
);
PendingSpecialization {
solved_type,
@ -275,7 +279,8 @@ pub struct Procs<'a> {
pub partial_procs: MutMap<Symbol, PartialProc<'a>>,
pub imported_module_thunks: MutSet<Symbol>,
pub module_thunks: MutSet<Symbol>,
pub pending_specializations: Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>>,
pub pending_specializations:
Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>>,
pub specialized: MutMap<(Symbol, Layout<'a>), InProgressProc<'a>>,
pub call_by_pointer_wrappers: MutMap<Symbol, Symbol>,
pub runtime_errors: MutMap<Symbol, &'a str>,
@ -502,7 +507,7 @@ impl<'a> Procs<'a> {
// Changing it to use .entry() would necessarily make it incorrect.
#[allow(clippy::map_entry)]
if !already_specialized {
let pending = PendingSpecialization::from_var(env.subs, annotation);
let pending = PendingSpecialization::from_var(env.arena, env.subs, annotation);
let partial_proc;
if let Some(existing) = self.partial_procs.get(&symbol) {
@ -576,6 +581,7 @@ impl<'a> Procs<'a> {
&mut self,
name: Symbol,
layout: Layout<'a>,
arena: &'a Bump,
subs: &Subs,
opt_annotation: Option<roc_can::def::Annotation>,
fn_var: Variable,
@ -590,8 +596,9 @@ impl<'a> Procs<'a> {
// We're done with that tuple, so move layout back out to avoid cloning it.
let (name, layout) = tuple;
let pending = match opt_annotation {
None => PendingSpecialization::from_var(subs, fn_var),
None => PendingSpecialization::from_var(arena, subs, fn_var),
Some(annotation) => PendingSpecialization::from_var_host_exposed(
arena,
subs,
fn_var,
&annotation.introduced_variables.host_exposed_aliases,
@ -634,7 +641,7 @@ impl<'a> Procs<'a> {
// We're done with that tuple, so move layout back out to avoid cloning it.
let (name, layout) = tuple;
let pending = PendingSpecialization::from_var(env.subs, fn_var);
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// This should only be called when pending_specializations is Some.
// Otherwise, it's being called in the wrong pass!
@ -674,10 +681,10 @@ impl<'a> Procs<'a> {
}
fn add_pending<'a>(
pending_specializations: &mut MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>,
pending_specializations: &mut MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
symbol: Symbol,
layout: Layout<'a>,
pending: PendingSpecialization,
pending: PendingSpecialization<'a>,
) {
let all_pending = pending_specializations
.entry(symbol)
@ -1662,7 +1669,7 @@ pub fn specialize_all<'a>(
name,
layout_cache,
solved_type,
MutMap::default(),
BumpMap::new_in(env.arena),
partial_proc,
) {
Ok((proc, layout)) => {
@ -1833,8 +1840,7 @@ fn specialize_external<'a>(
let host_exposed_layouts = if host_exposed_variables.is_empty() {
HostExposedLayouts::NotHostExposed
} else {
let mut aliases =
hashbrown::HashMap::with_hasher_in(default_hasher(), hashbrown::BumpWrapper(env.arena));
let mut aliases = BumpMap::new_in(env.arena);
for (symbol, variable) in host_exposed_variables {
let layout = layout_cache
@ -2336,7 +2342,7 @@ fn specialize_solved_type<'a>(
proc_name: Symbol,
layout_cache: &mut LayoutCache<'a>,
solved_type: SolvedType,
host_exposed_aliases: MutMap<Symbol, SolvedType>,
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
partial_proc: PartialProc<'a>,
) -> Result<(Proc<'a>, Layout<'a>), SpecializeFailure<'a>> {
// add the specializations that other modules require of us
@ -4832,7 +4838,7 @@ fn from_can_when<'a>(
)
}
fn substitute(substitutions: &MutMap<Symbol, Symbol>, s: Symbol) -> Option<Symbol> {
fn substitute(substitutions: &BumpMap<Symbol, Symbol>, s: Symbol) -> Option<Symbol> {
match substitutions.get(&s) {
Some(new) => {
debug_assert!(!substitutions.contains_key(new));
@ -4843,7 +4849,7 @@ fn substitute(substitutions: &MutMap<Symbol, Symbol>, s: Symbol) -> Option<Symbo
}
fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, to: Symbol) {
let mut subs = MutMap::default();
let mut subs = BumpMap::with_capacity_in(1, arena);
subs.insert(from, to);
// TODO clean this up
@ -4856,7 +4862,7 @@ fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, t
fn substitute_in_stmt_help<'a>(
arena: &'a Bump,
stmt: &'a Stmt<'a>,
subs: &MutMap<Symbol, Symbol>,
subs: &BumpMap<Symbol, Symbol>,
) -> Option<&'a Stmt<'a>> {
use Stmt::*;
@ -5024,7 +5030,7 @@ fn substitute_in_stmt_help<'a>(
fn substitute_in_call<'a>(
arena: &'a Bump,
call: &'a Call<'a>,
subs: &MutMap<Symbol, Symbol>,
subs: &BumpMap<Symbol, Symbol>,
) -> Option<Call<'a>> {
let Call {
call_type,
@ -5087,7 +5093,7 @@ fn substitute_in_call<'a>(
fn substitute_in_expr<'a>(
arena: &'a Bump,
expr: &'a Expr<'a>,
subs: &MutMap<Symbol, Symbol>,
subs: &BumpMap<Symbol, Symbol>,
) -> Option<Expr<'a>> {
use Expr::*;
@ -6090,7 +6096,7 @@ fn call_by_name<'a>(
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
assign_to_symbols(env, procs, layout_cache, iter, result)
} else {
let pending = PendingSpecialization::from_var(env.subs, fn_var);
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// When requested (that is, when procs.pending_specializations is `Some`),
// store a pending specialization rather than specializing immediately.
@ -6927,7 +6933,7 @@ fn from_can_pattern_help<'a>(
// sorted fields based on the destruct
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
let destructs_by_label = env.arena.alloc(MutMap::default());
let mut destructs_by_label = BumpMap::with_capacity_in(destructs.len(), env.arena);
destructs_by_label.extend(destructs.iter().map(|x| (&x.value.label, x)));
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);

View file

@ -104,6 +104,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
* Suggested quick fixes should be directly visible and clickable. Not like in vs code where you put the caret on an error until a lightbulb appears in the margin which you have to click for the fixes to apppear, after which you click to apply the fix you want :( .
* Regex-like find and substitution based on plain english description and example (replacement). i.e. replace all `[` between double quotes with `{`. [Inspiration](https://alexmoltzau.medium.com/english-to-regex-thanks-to-gpt-3-13f03b68236e).
* Show productivity tips based on behavior. i.e. if the user is scrolling through the error bar and clicking on the next error several times, show a tip with "go to next error" shortcut.
* Command to "benchmark this function" or "benchmark this test" with flamegraph and execution time per line.
#### Autocomplete
- Use more space for autocomplete options:

View file

@ -130,6 +130,9 @@ pub enum EdError {
#[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmpyRecord."))]
RecordWithoutFields { backtrace: Backtrace },
#[snafu(display("StringParseError: {}", msg))]
StringParseError { msg: String, backtrace: Backtrace },
#[snafu(display("UIError: {}", msg))]
UIErrorBacktrace { msg: String, backtrace: Backtrace },
}

View file

@ -126,6 +126,10 @@ impl<'a> EdModel<'a> {
Ok(prev_id_opt)
}
pub fn node_exists_at_caret(&self) -> bool {
self.grid_node_map.node_exists_at_pos(self.get_caret())
}
}
#[derive(Debug)]

View file

@ -2,7 +2,6 @@
use crate::editor::code_lines::CodeLines;
use crate::editor::ed_error::from_ui_res;
use crate::editor::ed_error::print_ui_err;
use crate::editor::ed_error::EdResult;
use crate::editor::ed_error::MissingSelection;
use crate::editor::grid_node_map::GridNodeMap;
@ -12,6 +11,8 @@ use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::app_update::InputOutcome;
use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::ed_model::SelectedExpression;
use crate::editor::mvc::int_update::start_new_int;
use crate::editor::mvc::int_update::update_int;
use crate::editor::mvc::lookup_update::update_invalid_lookup;
use crate::editor::mvc::record_update::start_new_record;
use crate::editor::mvc::record_update::update_empty_record;
@ -39,6 +40,7 @@ use crate::ui::text::text_pos::TextPos;
use crate::ui::text::{lines, lines::Lines, lines::SelectableLines};
use crate::ui::ui_error::UIResult;
use crate::window::keyboard_input::Modifiers;
use bumpalo::Bump;
use roc_can::expected::Expected;
use roc_collections::all::MutMap;
use roc_module::ident::Lowercase;
@ -160,44 +162,13 @@ impl<'a> EdModel<'a> {
self.code_lines.del_at_line(line_nr, index)
}
// select all MarkupNodes that refer to specific ast node and its children.
pub fn select_expr(&mut self) -> EdResult<()> {
// include parent in selection if an `Expr2` was already selected
if let Some(selected_expr) = &self.selected_expr_opt {
let expr2_level_mark_node = self.markup_node_pool.get(selected_expr.mark_node_id);
if let Some(parent_id) = expr2_level_mark_node.get_parent_id_opt() {
let parent_mark_node = self.markup_node_pool.get(parent_id);
let ast_node_id = parent_mark_node.get_ast_node_id();
let (expr_start_pos, expr_end_pos) = self
.grid_node_map
.get_nested_start_end_pos(parent_id, self)?;
self.set_raw_sel(RawSelection {
start_pos: expr_start_pos,
end_pos: expr_end_pos,
})?;
self.set_caret(expr_start_pos);
//let type_str = self.expr2_to_type(ast_node_id);
self.selected_expr_opt = Some(SelectedExpression {
ast_node_id,
mark_node_id: parent_id,
type_str: PoolStr::new("{}", self.module.env.pool), // TODO get this PoolStr from type inference
});
self.dirty = true;
}
} else {
// select `Expr2` in which caret is currently positioned
let caret_pos = self.get_caret();
if self.grid_node_map.node_exists_at_pos(caret_pos) {
let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self
.grid_node_map
.get_expr_start_end_pos(self.get_caret(), &self)?;
pub fn set_selected_expr(
&mut self,
expr_start_pos: TextPos,
expr_end_pos: TextPos,
ast_node_id: NodeId<Expr2>,
mark_node_id: MarkNodeId,
) -> EdResult<()> {
self.set_raw_sel(RawSelection {
start_pos: expr_start_pos,
end_pos: expr_end_pos,
@ -214,6 +185,44 @@ impl<'a> EdModel<'a> {
});
self.dirty = true;
Ok(())
}
// select all MarkupNodes that refer to specific ast node and its children.
pub fn select_expr(&mut self) -> EdResult<()> {
// include parent in selection if an `Expr2` was already selected
if let Some(selected_expr) = &self.selected_expr_opt {
let expr2_level_mark_node = self.markup_node_pool.get(selected_expr.mark_node_id);
if let Some(parent_id) = expr2_level_mark_node.get_parent_id_opt() {
let parent_mark_node = self.markup_node_pool.get(parent_id);
let ast_node_id = parent_mark_node.get_ast_node_id();
let (expr_start_pos, expr_end_pos) = self
.grid_node_map
.get_nested_start_end_pos(parent_id, self)?;
self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, parent_id)?;
}
} else {
// select `Expr2` in which caret is currently positioned
let caret_pos = self.get_caret();
if self.grid_node_map.node_exists_at_pos(caret_pos) {
let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self
.grid_node_map
.get_expr_start_end_pos(self.get_caret(), &self)?;
self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?;
} else if self
.grid_node_map
.node_exists_at_pos(caret_pos.decrement_col())
{
let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self
.grid_node_map
.get_expr_start_end_pos(self.get_caret().decrement_col(), &self)?;
self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?;
}
}
@ -223,8 +232,10 @@ impl<'a> EdModel<'a> {
fn expr2_to_type(&mut self, expr2_id: NodeId<Expr2>) -> PoolStr {
let var = self.module.env.var_store.fresh();
let expr = self.module.env.pool.get(expr2_id);
let arena = Bump::new();
let constrained = constrain_expr(
&arena,
&mut self.module.env,
&expr,
Expected::NoExpectation(Type2::Variable(var)),
@ -542,11 +553,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
InputOutcome::Accepted
}
ch => {
let curr_mark_node_id_res = ed_model.get_curr_mark_node_id();
let outcome =
match curr_mark_node_id_res {
Ok(curr_mark_node_id) => {
if ed_model.node_exists_at_caret() {
let curr_mark_node_id = ed_model.get_curr_mark_node_id()?;
let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id);
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
@ -561,6 +570,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
'{' => {
start_new_record(ed_model)?
}
'0'..='9' => {
start_new_int(ed_model, ch)?
}
_ => InputOutcome::Ignored
}
} else if let Some(prev_mark_node_id) = prev_mark_node_id_opt{
@ -602,9 +614,18 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
InputOutcome::Ignored
}
}
Expr2::SmallInt{ .. } => {
update_int(ed_model, curr_mark_node_id, ch)?
}
_ => InputOutcome::Ignored
}
} else if ch.is_ascii_alphanumeric() { // prev_mark_node_id != curr_mark_node_id
match ast_node_ref {
Expr2::SmallInt{ .. } => {
update_int(ed_model, curr_mark_node_id, ch)?
}
_ => {
let prev_ast_node_id =
ed_model
.markup_node_pool
@ -647,26 +668,27 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
InputOutcome::Ignored
}
}
Expr2::SmallInt{ .. } => {
update_int(ed_model, prev_mark_node_id, ch)?
}
_ => {
match ast_node_ref {
Expr2::EmptyRecord => {
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
if ch.is_ascii_alphabetic() && ch.is_ascii_lowercase() {
update_empty_record(
&ch.to_string(),
prev_mark_node_id,
sibling_ids,
ed_model
)?
} else {
InputOutcome::Ignored
}
}
_ => InputOutcome::Ignored
}
}
}
}
}
} else if *ch == ':' {
let mark_parent_id_opt = curr_mark_node.get_parent_id_opt();
@ -682,16 +704,36 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
}
} else {
// Not supporting any Expr2 right now that would allow prepending at the start of a line
match ast_node_ref {
Expr2::SmallInt{ .. } => {
update_int(ed_model, curr_mark_node_id, ch)?
},
// only SmallInt currently allows prepending at the start
_ => InputOutcome::Ignored
}
}
} else { //no MarkupNode at the current position
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let prev_ast_node = ed_model.module.env.pool.get(prev_mark_node.get_ast_node_id());
match prev_ast_node {
Expr2::SmallInt{ .. } => {
update_int(ed_model, prev_mark_node_id, ch)?
},
_ => {
InputOutcome::Ignored
}
},
Err(e) => {
print_ui_err(&e);
}
} else {
InputOutcome::Ignored
}
};
if let InputOutcome::Accepted = outcome {
ed_model.set_sel_none();
}
@ -748,6 +790,10 @@ pub mod test_ed_update {
assert_insert_seq(pre_lines, expected_post_lines, &new_char.to_string())
}
pub fn assert_insert_ignore(lines: &[&str], new_char: char) -> Result<(), String> {
assert_insert_seq(lines, lines, &new_char.to_string())
}
// Create ed_model from pre_lines DSL, do handle_new_char() for every char in new_char_seq, check if modified ed_model has expected
// string representation of code, caret position and active selection.
pub fn assert_insert_seq(
@ -782,13 +828,69 @@ pub mod test_ed_update {
// space is added because Blank is inserted
assert_insert(&[""], &[""], 'a')?;
assert_insert(&[""], &[""], ';')?;
assert_insert(&[""], &[""], '5')?;
assert_insert(&[""], &[""], '-')?;
assert_insert(&[""], &[""], '_')?;
Ok(())
}
#[test]
fn test_int() -> Result<(), String> {
assert_insert(&[""], &["0┃"], '0')?;
assert_insert(&[""], &["1┃"], '1')?;
assert_insert(&[""], &["2┃"], '2')?;
assert_insert(&[""], &["3┃"], '3')?;
assert_insert(&[""], &["4┃"], '4')?;
assert_insert(&[""], &["5┃"], '5')?;
assert_insert(&[""], &["6┃"], '6')?;
assert_insert(&[""], &["7┃"], '7')?;
assert_insert(&[""], &["8┃"], '8')?;
assert_insert(&[""], &["9┃"], '9')?;
assert_insert(&["1┃"], &["19┃"], '9')?;
assert_insert(&["9876┃"], &["98769┃"], '9')?;
assert_insert(&["10┃"], &["103┃"], '3')?;
assert_insert(&["┃0"], &["1┃0"], '1')?;
assert_insert(&["10000┃"], &["100000┃"], '0')?;
assert_insert(&["┃1234"], &["5┃1234"], '5')?;
assert_insert(&["1┃234"], &["10┃234"], '0')?;
assert_insert(&["12┃34"], &["121┃34"], '1')?;
assert_insert(&["123┃4"], &["1232┃4"], '2')?;
Ok(())
}
#[test]
fn test_ignore_int() -> Result<(), String> {
assert_insert_seq_ignore(&["┃0"], "{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃7"], "{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["0┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["8┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["20┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["83┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["1┃0"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["8┃4"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃10"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃84"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["129┃96"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["97┃684"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_ignore(&["0┃"], '0')?;
assert_insert_ignore(&["0┃"], '9')?;
assert_insert_ignore(&["┃0"], '0')?;
assert_insert_ignore(&["┃1234"], '0')?;
assert_insert_ignore(&["┃100"], '0')?;
Ok(())
}
//TODO test_int arch bit limit
#[test]
fn test_string() -> Result<(), String> {
assert_insert(&[""], &["\"\""], '"')?;
@ -923,14 +1025,29 @@ pub mod test_ed_update {
assert_insert_seq(&["{ a┃ }"], &["{ a: \"\" }"], ":\"")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: \"\" }"], ":\"")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 0┃ }"], ":0")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 9┃ }"], ":9")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 1000┃ }"], ":1000")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 98761┃ }"], ":98761")?;
assert_insert(&["{ a: \"\" }"], &["{ a: \"a┃\" }"], 'a')?;
assert_insert(&["{ a: \"a┃\" }"], &["{ a: \"ab┃\" }"], 'b')?;
assert_insert(&["{ a: \"a┃b\" }"], &["{ a: \"az┃b\" }"], 'z')?;
assert_insert(&["{ a: \"┃ab\" }"], &["{ a: \"z┃ab\" }"], 'z')?;
assert_insert(&["{ a: 1┃ }"], &["{ a: 10┃ }"], '0')?;
assert_insert(&["{ a: 100┃ }"], &["{ a: 1004┃ }"], '4')?;
assert_insert(&["{ a: 9┃76 }"], &["{ a: 98┃76 }"], '8')?;
assert_insert(&["{ a: 4┃691 }"], &["{ a: 40┃691 }"], '0')?;
assert_insert(&["{ a: 469┃1 }"], &["{ a: 4699┃1 }"], '9')?;
assert_insert(&["{ camelCase: \"\" }"], &["{ camelCase: \"a┃\" }"], 'a')?;
assert_insert(&["{ camelCase: \"a┃\" }"], &["{ camelCase: \"ab┃\" }"], 'b')?;
assert_insert(&["{ camelCase: 3┃ }"], &["{ camelCase: 35┃ }"], '5')?;
assert_insert(&["{ camelCase: ┃2 }"], &["{ camelCase: 5┃2 }"], '5')?;
assert_insert(&["{ camelCase: 10┃2 }"], &["{ camelCase: 106┃2 }"], '6')?;
assert_insert(&["{ a┃: \"\" }"], &["{ ab┃: \"\" }"], 'b')?;
assert_insert(&["{ ┃a: \"\" }"], &["{ z┃a: \"\" }"], 'z')?;
assert_insert(&["{ ab┃: \"\" }"], &["{ abc┃: \"\" }"], 'c')?;
@ -951,7 +1068,16 @@ pub mod test_ed_update {
'z',
)?;
assert_insert(&["{ a┃: 0 }"], &["{ ab┃: 0 }"], 'b')?;
assert_insert(&["{ ┃a: 2100 }"], &["{ z┃a: 2100 }"], 'z')?;
assert_insert(&["{ ab┃: 9876 }"], &["{ abc┃: 9876 }"], 'c')?;
assert_insert(&["{ ┃ab: 102 }"], &["{ z┃ab: 102 }"], 'z')?;
assert_insert(&["{ camelCase┃: 99999 }"], &["{ camelCaseB┃: 99999 }"], 'B')?;
assert_insert(&["{ camel┃Case: 88156 }"], &["{ camelZ┃Case: 88156 }"], 'Z')?;
assert_insert(&["{ ┃camelCase: 1 }"], &["{ z┃camelCase: 1 }"], 'z')?;
assert_insert_seq(&[""], &["{ camelCase: \"hello┃\" }"], "{camelCase:\"hello")?;
assert_insert_seq(&[""], &["{ camelCase: 10009┃ }"], "{camelCase:10009")?;
Ok(())
}
@ -1013,6 +1139,25 @@ pub mod test_ed_update {
"ul",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: 1┃ } }"], ":1")?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: 0┃ } }"],
":0",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: 45┃ } }"],
":45",
)?;
assert_insert_seq(&["{ a: { zulu: ┃0 } }"], &["{ a: { zulu: 4┃0 } }"], "4")?;
assert_insert_seq(
&["{ a: { zulu: 10┃98 } }"],
&["{ a: { zulu: 1077┃98 } }"],
"77",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: { ┃ } } }"], ":{")?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
@ -1071,146 +1216,199 @@ pub mod test_ed_update {
Ok(())
}
const IGNORE_CHARS: &str = "{}()[]-><-_\"azAZ:@09";
const IGNORE_NO_LTR: &str = "{\"5";
const IGNORE_NO_NUM: &str = "a{\"";
#[test]
fn test_ignore_record() -> Result<(), String> {
assert_insert_seq_ignore(&["┃{ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ ┃}"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ ┃ }"], "{\"5")?;
assert_insert_seq_ignore(&["{ ┃a }"], "{\"5")?;
assert_insert_seq_ignore(&["{ ┃abc }"], "{\"5")?;
assert_insert_seq_ignore(&["{ ┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃abc }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ a }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ a }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a ┃}"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a15 }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a15 }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ a15 }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a15 ┃}"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ camelCase }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ camelCase }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ camelCase ┃}"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ a: \"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: ┃\"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"\"┃ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"\" }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ a: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"\" }"], "0")?;
assert_insert_seq_ignore(&["{ a: ┃\"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase: \"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ camelCase: \"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ camelCase: ┃\"\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ camelCase: \"\"┃ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ camelCase: \"\" }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ a: 1 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: 2 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃6 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 8┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 0 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"z\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ a: \"z\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: ┃\"z\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"z\"┃ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"z\" }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ camelCase: 1 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase: 7 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: ┃2 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCase: 4┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCase: 9 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{┃ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ }"], "a{\"5")?;
assert_insert_seq_ignore(&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃"], "a{\"5")?;
assert_insert_seq_ignore(&["┃{ camelCase: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: ┃\"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: \"\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: \"\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"z\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"z\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(
&["┃{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{┃ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(&["┃{ a: 915480 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: 915480 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃915480 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 915480┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 915480 }┃"], IGNORE_CHARS)?;
Ok(())
}
#[test]
fn test_ignore_nested_record() -> Result<(), String> {
assert_insert_seq_ignore(&["{ a: { ┃ } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: ┃{ } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: {┃ } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: { }┃ }"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: { } ┃}"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: { } }┃"], "{\"5")?;
assert_insert_seq_ignore(&["{ a:┃ { } }"], "{\"5")?;
assert_insert_seq_ignore(&["{┃ a: { } }"], "{\"5")?;
assert_insert_seq_ignore(&["┃{ a: { } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ a: { ┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: ┃{ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: {┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a:┃ { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ a: { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ a: { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a: { } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a ┃} }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a }┃ }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } ┃}"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }┃"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a } }"], "{\"5")?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a } }"], "{\"5")?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a ┃} }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\"┃ } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃\"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" ┃} }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" }┃ }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } ┃}"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } }┃"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: \"\" } }"], "{\"5")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\"┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃\"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" ┃} }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: \"\" } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: \"\" } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 0┃ } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃123 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ 999 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 80 ┃} }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: 99000 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: 12 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 7 }┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 98 } ┃}"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 4582 } }┃"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: 0 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: 44 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: 100123 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: 5 } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: 6 } }"], "1")?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a:┃ \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" ┃} }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: {┃ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: ┃{ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃ }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } ┃}"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }┃"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ camelCaseB1:┃ { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{┃ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["┃{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ ┃camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
@ -1223,35 +1421,35 @@ pub mod test_ed_update {
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase ┃} } } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } ┃} } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }┃"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } ┃} } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase } } } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase } } } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
"{\"5",
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
@ -1301,6 +1499,20 @@ pub mod test_ed_update {
Ok(())
}
#[test]
fn test_ctrl_shift_up_int() -> Result<(), String> {
assert_ctrl_shift_up(&["5┃"], &["5"])?;
assert_ctrl_shift_up_repeat(&["0┃"], &["0"], 3)?;
assert_ctrl_shift_up(&["12345┃"], &["12345"])?;
assert_ctrl_shift_up(&["┃12345"], &["12345"])?;
assert_ctrl_shift_up(&["1┃2345"], &["12345"])?;
assert_ctrl_shift_up(&["12┃345"], &["12345"])?;
assert_ctrl_shift_up(&["123┃45"], &["12345"])?;
assert_ctrl_shift_up(&["1234┃5"], &["12345"])?;
Ok(())
}
#[test]
fn test_ctrl_shift_up_string() -> Result<(), String> {
assert_ctrl_shift_up(&["\"\""], &["┃❮\"\""])?;
@ -1317,8 +1529,8 @@ pub mod test_ed_update {
&["┃❮\"hello, hello.0123456789ZXY{}[]-><-\""],
)?;
assert_ctrl_shift_up(&["\"\""], &["\"\""])?;
assert_ctrl_shift_up(&["\"abc\""], &["\"abc\""])?;
assert_ctrl_shift_up(&["\"\""], &["┃❮\"\""])?;
assert_ctrl_shift_up(&["\"abc\""], &["┃❮\"abc\""])?;
Ok(())
}
@ -1330,7 +1542,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["┃{ }"], &["┃❮{ }"])?;
assert_ctrl_shift_up(&["{ ┃}"], &["┃❮{ }"])?;
assert_ctrl_shift_up_repeat(&["{ ┃ }"], &["┃❮{ }"], 4)?;
assert_ctrl_shift_up(&["{ }┃"], &["{ }┃"])?;
assert_ctrl_shift_up(&["{ }┃"], &["┃❮{ }"])?;
assert_ctrl_shift_up(&["{ pear┃ }"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ pea┃r }"], &["┃❮{ pear }"])?;
@ -1340,7 +1552,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["┃{ pear }"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ pear ┃}"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up_repeat(&["{ pear┃ }"], &["┃❮{ pear }"], 3)?;
assert_ctrl_shift_up(&["{ pear }┃"], &["{ pear }┃"])?;
assert_ctrl_shift_up(&["{ pear }┃"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ camelCase123┃ }"], &["┃❮{ camelCase123 }"])?;
@ -1349,7 +1561,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["{ a: \"\"┃ }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up_repeat(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"], 3)?;
assert_ctrl_shift_up(&["{ a: \"\" }┃"], &["{ a: \"\" }┃"])?;
assert_ctrl_shift_up(&["{ a: \"\" }┃"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a:┃ \"\" }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a┃: \"\" }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ ┃a: \"\" }"], &["┃❮{ a: \"\" }"])?;
@ -1358,6 +1570,21 @@ pub mod test_ed_update {
assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 4)?;
assert_ctrl_shift_up(&["{ a: 1┃0 }"], &["{ a: ┃10 }"])?;
assert_ctrl_shift_up(&["{ a: ┃9 }"], &["{ a: ┃9 }"])?;
assert_ctrl_shift_up(&["{ a: 98┃89 }"], &["{ a: ┃9889 }"])?;
assert_ctrl_shift_up(&["{ a: 44┃ }"], &["┃❮{ a: 44 }"])?;
assert_ctrl_shift_up(&["{ a: 0 ┃}"], &["┃❮{ a: 0 }"])?;
assert_ctrl_shift_up_repeat(&["{ a: 123 ┃}"], &["┃❮{ a: 123 }"], 3)?;
assert_ctrl_shift_up(&["{ a: 96 }┃"], &["┃❮{ a: 96 }"])?;
assert_ctrl_shift_up(&["{ a:┃ 985600 }"], &["┃❮{ a: 985600 }"])?;
assert_ctrl_shift_up(&["{ a┃: 5648 }"], &["┃❮{ a: 5648 }"])?;
assert_ctrl_shift_up(&["{ ┃a: 1000000 }"], &["┃❮{ a: 1000000 }"])?;
assert_ctrl_shift_up(&["{┃ a: 1 }"], &["┃❮{ a: 1 }"])?;
assert_ctrl_shift_up(&["┃{ a: 900600 }"], &["┃❮{ a: 900600 }"])?;
assert_ctrl_shift_up_repeat(&["{ a: 10┃000 }"], &["┃❮{ a: 10000 }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ a: ┃45 }"], &["┃❮{ a: 45 }"], 4)?;
assert_ctrl_shift_up(&["{ abc: \"de┃\" }"], &["{ abc: ┃❮\"de\" }"])?;
assert_ctrl_shift_up(&["{ abc: \"d┃e\" }"], &["{ abc: ┃❮\"de\" }"])?;
assert_ctrl_shift_up(&["{ abc: \"┃de\" }"], &["{ abc: ┃❮\"de\" }"])?;
@ -1432,7 +1659,7 @@ pub mod test_ed_update {
)?;
assert_ctrl_shift_up(
&["{ abc: { de: \"f g\" } }┃"],
&["{ abc: { de: \"f g\" } }"],
&["┃❮{ abc: { de: \"f g\" } }"],
)?;
assert_ctrl_shift_up_repeat(
@ -1451,6 +1678,23 @@ pub mod test_ed_update {
4,
)?;
assert_ctrl_shift_up(&["{ abc: { de: ┃951 } }"], &["{ abc: { de: ┃951 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 11┃0 } }"], &["{ abc: { de: ┃110 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 444┃ } }"], &["{ abc: ┃❮{ de: 444 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de┃: 99 } }"], &["{ abc: ┃❮{ de: 99 } }"])?;
assert_ctrl_shift_up(&["{ abc: {┃ de: 0 } }"], &["{ abc: ┃❮{ de: 0 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 230 ┃} }"], &["{ abc: ┃❮{ de: 230 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 7 }┃ }"], &["┃❮{ abc: { de: 7 } }"])?;
assert_ctrl_shift_up(&["┃{ abc: { de: 1 } }"], &["┃❮{ abc: { de: 1 } }"])?;
assert_ctrl_shift_up(
&["{ abc: { de: 111111 } }┃"],
&["┃❮{ abc: { de: 111111 } }"],
)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: 1┃5 } }"], &["{ abc: ┃❮{ de: 15 } }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: ┃55 } }"], &["┃❮{ abc: { de: 55 } }"], 3)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: ┃400 } }"], &["┃❮{ abc: { de: 400 } }"], 4)?;
assert_ctrl_shift_up_repeat(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"],
&["{ g: { oi: { ng: { d: ┃❮{ e: { e: { p: { camelCase } } } } } } } }"],
@ -1597,6 +1841,15 @@ pub mod test_ed_update {
Ok(())
}
#[test]
fn test_ctrl_shift_up_move_int() -> Result<(), String> {
assert_ctrl_shift_single_up_move(&["┃0"], &["0┃"], move_down!())?;
assert_ctrl_shift_single_up_move(&["┃9654"], &["┃9654"], move_up!())?;
assert_ctrl_shift_single_up_move(&["┃100546"], &["100546┃"], move_end!())?;
Ok(())
}
#[test]
fn test_ctrl_shift_up_move_string() -> Result<(), String> {
assert_ctrl_shift_single_up_move(&["\"\""], &["\"\""], move_down!())?;
@ -1629,6 +1882,13 @@ pub mod test_ed_update {
move_down!(),
)?;
assert_ctrl_shift_up_move(
&["{ camelCase: { cC123: 9┃5 } }"],
&["{ camelCase: { cC123: 95 }┃ }"],
2,
move_down!(),
)?;
Ok(())
}
@ -1675,11 +1935,21 @@ pub mod test_ed_update {
Ok(())
}
#[test]
fn test_ctrl_shift_up_backspace_int() -> Result<(), String> {
// Blank is inserted when root is deleted
assert_ctrl_shift_single_up_backspace(&["95┃21"], &[""])?;
assert_ctrl_shift_single_up_backspace(&["0┃"], &[""])?;
assert_ctrl_shift_single_up_backspace(&["┃10000"], &[""])?;
Ok(())
}
#[test]
fn test_ctrl_shift_up_backspace_string() -> Result<(), String> {
// Blank is inserted when root is deleted
assert_ctrl_shift_single_up_backspace(&["\"\""], &[""])?;
assert_ctrl_shift_single_up_backspace(&["\"\""], &["\"\""])?;
assert_ctrl_shift_single_up_backspace(&["\"\""], &[" "])?;
assert_ctrl_shift_single_up_backspace(&["\"abc\""], &[""])?;
assert_ctrl_shift_single_up_backspace(
&["\"hello┃, hello.0123456789ZXY{}[]-><-\""],
@ -1699,6 +1969,7 @@ pub mod test_ed_update {
assert_ctrl_shift_single_up_backspace(&["{ a: ┃{ b } }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace(&["{ a: \"┃b cd\" }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace(&["{ a: ┃12 }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace(
&["{ g: { oi: { ng: { d: { ┃e: { e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: ┃ } } } }"],
@ -1709,6 +1980,11 @@ pub mod test_ed_update {
&["{ a: { b: ┃ } }"],
2,
)?;
assert_ctrl_shift_up_backspace(
&["{ a: { b: { c: 100┃000 } } }"],
&["{ a: { b: ┃ } }"],
2,
)?;
assert_ctrl_shift_up_backspace(&["{ a: { b: { c: {┃ } } } }"], &["{ a: { b: ┃ } }"], 2)?;
assert_ctrl_shift_up_backspace(
&["{ g: { oi: { ng: { d: { e: { e: { p┃: { camelCase } } } } } } } }"],

View file

@ -0,0 +1,156 @@
use crate::editor::ed_error::EdResult;
use crate::editor::ed_error::StringParseError;
use crate::editor::markup::attribute::Attributes;
use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::app_update::InputOutcome;
use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::ed_update::get_node_context;
use crate::editor::mvc::ed_update::NodeContext;
use crate::editor::slow_pool::MarkNodeId;
use crate::editor::syntax_highlight::HighlightStyle;
use crate::lang::ast::Expr2::SmallInt;
use crate::lang::ast::IntVal;
use crate::lang::ast::{IntStyle, IntVal::*};
use crate::lang::pool::PoolStr;
use crate::ui::text::lines::SelectableLines;
// digit_char should be verified to be a digit before calling this function
pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<InputOutcome> {
let NodeContext {
old_caret_pos,
curr_mark_node_id,
curr_mark_node,
parent_id_opt,
ast_node_id,
} = get_node_context(&ed_model)?;
let is_blank_node = curr_mark_node.is_blank();
let int_var = ed_model.module.env.var_store.fresh();
let digit_string = digit_char.to_string();
let expr2_node = SmallInt {
number: IntVal::U64(*digit_char as u64), // TODO determine if u64 on wordlength of current arch, perhaps introduce Unknown(i64)
var: int_var,
style: IntStyle::Decimal,
text: PoolStr::new(&digit_string, &mut ed_model.module.env.pool),
};
ed_model.module.env.pool.set(ast_node_id, expr2_node);
let int_node = MarkupNode::Text {
content: digit_string,
ast_node_id,
syn_high_style: HighlightStyle::Number,
attributes: Attributes::new(),
parent_id_opt,
};
if is_blank_node {
ed_model
.markup_node_pool
.replace_node(curr_mark_node_id, int_node);
// remove data corresponding to Blank node
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
let char_len = 1;
ed_model.simple_move_carets_right(char_len);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
&digit_char.to_string(),
curr_mark_node_id,
)?;
Ok(InputOutcome::Accepted)
} else {
Ok(InputOutcome::Ignored)
}
}
// TODO check if new int needs more than e.g. 64 bits
pub fn update_int(
ed_model: &mut EdModel,
int_mark_node_id: MarkNodeId,
ch: &char,
) -> EdResult<InputOutcome> {
if ch.is_ascii_digit() {
let old_caret_pos = ed_model.get_caret();
let node_caret_offset = ed_model
.grid_node_map
.get_offset_to_node_id(old_caret_pos, int_mark_node_id)?;
let int_mark_node = ed_model.markup_node_pool.get_mut(int_mark_node_id);
let int_ast_node_id = int_mark_node.get_ast_node_id();
let content_str_mut = int_mark_node.get_content_mut()?;
// 00, 01 are not valid ints
if (content_str_mut == "0" && (node_caret_offset == 1 || *ch == '0'))
|| (*ch == '0' && node_caret_offset == 0)
{
Ok(InputOutcome::Ignored)
} else {
content_str_mut.insert(node_caret_offset, *ch);
let content_str = int_mark_node.get_content()?;
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
&ch.to_string(),
int_mark_node_id,
)?;
// update ast
let new_pool_str = PoolStr::new(&content_str, ed_model.module.env.pool);
let int_ast_node = ed_model.module.env.pool.get_mut(int_ast_node_id);
match int_ast_node {
SmallInt { number, text, .. } => {
update_small_int_num(number, &content_str)?;
*text = new_pool_str;
}
_ => unimplemented!("TODO implement updating this type of Number"),
}
// update caret
ed_model.simple_move_carets_right(1);
Ok(InputOutcome::Accepted)
}
} else {
Ok(InputOutcome::Ignored)
}
}
fn update_small_int_num(number: &mut IntVal, updated_str: &str) -> EdResult<()> {
*number = match number {
I64(_) => I64(check_parse_res(updated_str.parse::<i64>())?),
U64(_) => U64(check_parse_res(updated_str.parse::<u64>())?),
I32(_) => I32(check_parse_res(updated_str.parse::<i32>())?),
U32(_) => U32(check_parse_res(updated_str.parse::<u32>())?),
I16(_) => I16(check_parse_res(updated_str.parse::<i16>())?),
U16(_) => U16(check_parse_res(updated_str.parse::<u16>())?),
I8(_) => I8(check_parse_res(updated_str.parse::<i8>())?),
U8(_) => U8(check_parse_res(updated_str.parse::<u8>())?),
};
Ok(())
}
fn check_parse_res<T, E: std::fmt::Debug>(parse_res: Result<T, E>) -> EdResult<T> {
match parse_res {
Ok(some_type) => Ok(some_type),
Err(parse_err) => StringParseError {
msg: format!("{:?}", parse_err),
}
.fail(),
}
}

View file

@ -3,6 +3,7 @@ pub mod app_update;
pub mod ed_model;
pub mod ed_update;
pub mod ed_view;
mod int_update;
mod lookup_update;
mod record_update;
mod string_update;

View file

@ -96,6 +96,11 @@ pub fn update_empty_record(
sibling_ids: Vec<MarkNodeId>,
ed_model: &mut EdModel,
) -> EdResult<InputOutcome> {
let mut input_chars = new_input.chars();
if input_chars.all(|ch| ch.is_ascii_alphabetic())
&& input_chars.all(|ch| ch.is_ascii_lowercase())
{
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let NodeContext {
@ -162,6 +167,9 @@ pub fn update_empty_record(
} else {
Ok(InputOutcome::Ignored)
}
} else {
Ok(InputOutcome::Ignored)
}
}
pub fn update_record_colon(
@ -173,10 +181,25 @@ pub fn update_record_colon(
curr_mark_node_id,
curr_mark_node,
parent_id_opt,
ast_node_id: _,
ast_node_id,
} = get_node_context(&ed_model)?;
if let Some(parent_id) = parent_id_opt {
let curr_ast_node = ed_model.module.env.pool.get(ast_node_id);
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let prev_ast_node = ed_model
.module
.env
.pool
.get(prev_mark_node.get_ast_node_id());
// current and prev node should always point to record when in valid position to add ':'
if matches!(prev_ast_node, Expr2::Record { .. })
&& matches!(curr_ast_node, Expr2::Record { .. })
{
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
@ -188,6 +211,11 @@ pub fn update_record_colon(
record_var: _,
fields,
} => {
if ed_model.node_exists_at_caret() {
let next_mark_node_id =
ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?;
let next_mark_node = ed_model.markup_node_pool.get(next_mark_node_id);
if next_mark_node.get_content()? == nodes::RIGHT_ACCOLADE {
// update AST node
let new_field_val = Expr2::Blank;
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
@ -214,7 +242,8 @@ pub fn update_record_colon(
parent_id_opt: Some(parent_id),
};
let record_colon_node_id = ed_model.markup_node_pool.add(record_colon_node);
let record_colon_node_id =
ed_model.markup_node_pool.add(record_colon_node);
ed_model
.markup_node_pool
.get_mut(parent_id)
@ -227,11 +256,15 @@ pub fn update_record_colon(
parent_id_opt: Some(parent_id),
};
let record_blank_node_id = ed_model.markup_node_pool.add(record_blank_node);
let record_blank_node_id =
ed_model.markup_node_pool.add(record_blank_node);
ed_model
.markup_node_pool
.get_mut(parent_id)
.add_child_at_index(new_child_index + 1, record_blank_node_id)?;
.add_child_at_index(
new_child_index + 1,
record_blank_node_id,
)?;
// update caret
ed_model.simple_move_carets_right(record_colon.len());
@ -252,8 +285,20 @@ pub fn update_record_colon(
)?;
Ok(InputOutcome::Accepted)
} else {
Ok(InputOutcome::Ignored)
}
other => unimplemented!("TODO implement updating of Expr2 {:?}.", other),
} else {
Ok(InputOutcome::Ignored)
}
}
_ => Ok(InputOutcome::Ignored),
}
} else {
Ok(InputOutcome::Ignored)
}
} else {
Ok(InputOutcome::Ignored)
}
} else {
MissingParent {

View file

@ -1,3 +1,3 @@
pub const NOTHING_OPENED: &str = "Execute `cargo run edit <filename>` to open a file.";
pub const START_TIP: &str =
"Start by typing '{' or '\"'.\nInput chars that would create parse errors will be ignored.";
"Start by typing '{', '\"' or a number.\nInput chars that would create parse errors will be ignored.";

View file

@ -437,6 +437,9 @@ fn expr2_to_string_helper(
Expr2::InvalidLookup(pool_str) => {
out_string.push_str(&format!("InvalidLookup({})", pool_str.as_str(pool)));
}
Expr2::SmallInt { text, .. } => {
out_string.push_str(&format!("SmallInt({})", text.as_str(pool)));
}
other => todo!("Implement for {:?}", other),
}

View file

@ -1,34 +1,91 @@
use crate::lang::pool::{Pool, PoolVec};
use crate::lang::{ast::Expr2, expr::Env, types::Type2};
use bumpalo::{collections::Vec as BumpVec, Bump};
use crate::lang::pool::{Pool, PoolStr, PoolVec, ShallowClone};
use crate::lang::{
ast::Expr2,
expr::Env,
types::{Type2, TypeId},
};
use roc_can::expected::Expected;
use roc_collections::all::SendMap;
use roc_module::symbol::Symbol;
use roc_region::all::Region;
use roc_types::types::Category;
use roc_region::all::{Located, Region};
use roc_types::{
subs::Variable,
types::{Category, Reason},
};
#[derive(Debug)]
pub enum Constraint {
pub enum Constraint<'a> {
Eq(Type2, Expected<Type2>, Category, Region),
// Store(Type, Variable, &'static str, u32),
// Lookup(Symbol, Expected<Type>, Region),
// Pattern(Region, PatternCategory, Type, PExpected<Type>),
True, // Used for things that always unify, e.g. blanks and runtime errors
And(BumpVec<'a, Constraint<'a>>),
Let(&'a LetConstraint<'a>),
// SaveTheEnvironment,
// Let(Box<LetConstraint>),
// And(Vec<Constraint>),
True, // Used for things that always unify, e.g. blanks and runtime errors
}
pub fn constrain_expr(
#[derive(Debug)]
pub struct LetConstraint<'a> {
pub rigid_vars: BumpVec<'a, Variable>,
pub flex_vars: BumpVec<'a, Variable>,
pub def_types: SendMap<Symbol, Located<Type2>>,
pub defs_constraint: Constraint<'a>,
pub ret_constraint: Constraint<'a>,
}
pub fn constrain_expr<'a>(
arena: &'a Bump,
env: &mut Env,
expr: &Expr2,
expected: Expected<Type2>,
region: Region,
) -> Constraint {
) -> Constraint<'a> {
use Constraint::*;
match expr {
Expr2::EmptyRecord => Eq(Type2::EmptyRec, expected, Category::Record, region),
Expr2::Str(_) => Eq(str_type(env.pool), expected, Category::Str, region),
Expr2::EmptyRecord => Eq(Type2::EmptyRec, expected, Category::Record, region),
Expr2::SmallInt { var, .. } => {
let mut flex_vars = BumpVec::with_capacity_in(1, arena);
let rigid_vars = BumpVec::new_in(arena);
let mut and_constraints = BumpVec::with_capacity_in(2, arena);
let num_type = Type2::Variable(*var);
flex_vars.push(*var);
let precision_var = env.var_store.fresh();
let range_type = Type2::Variable(precision_var);
let range_type_id = env.pool.add(range_type);
and_constraints.push(Eq(
num_type.shallow_clone(),
Expected::ForReason(Reason::IntLiteral, num_int(env.pool, range_type_id), region),
Category::Int,
region,
));
and_constraints.push(Eq(num_type, expected, Category::Int, region));
let defs_constraint = And(and_constraints);
let let_constraint = arena.alloc(LetConstraint {
rigid_vars,
flex_vars,
def_types: SendMap::default(),
defs_constraint,
ret_constraint: Constraint::True,
});
Let(let_constraint)
}
_ => todo!("implement constaints for {:?}", expr),
}
}
@ -36,3 +93,83 @@ pub fn constrain_expr(
fn str_type(pool: &mut Pool) -> Type2 {
Type2::Apply(Symbol::STR_STR, PoolVec::empty(pool))
}
fn num_int(pool: &mut Pool, range: TypeId) -> Type2 {
let num_integer_type = num_integer(pool, range);
let num_integer_id = pool.add(num_integer_type);
let num_num_type = num_num(pool, num_integer_id);
let num_num_id = pool.add(num_num_type);
Type2::Alias(
Symbol::NUM_INT,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
num_num_id,
)
}
fn _num_signed64(pool: &mut Pool) -> Type2 {
let alias_content = Type2::TagUnion(
PoolVec::new(
// TagName::Private(Symbol::NUM_AT_SIGNED64)
vec![(PoolStr::new("Num.@Signed64", pool), PoolVec::empty(pool))].into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_SIGNED64,
PoolVec::empty(pool),
pool.add(alias_content),
)
}
fn num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
let range_type = pool.get(range);
let alias_content = Type2::TagUnion(
PoolVec::new(
vec![(
// TagName::Private(Symbol::NUM_AT_INTEGER)
PoolStr::new("Num.@Integer", pool),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_INTEGER,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
pool.add(alias_content),
)
}
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
let range_type = pool.get(type_id);
let alias_content = Type2::TagUnion(
PoolVec::new(
vec![(
// TagName::Private(Symbol::NUM_AT_NUM)
PoolStr::new("Num.@Num", pool),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_NUM,
PoolVec::new(
vec![(PoolStr::new("range", pool), type_id)].into_iter(),
pool,
),
pool.add(alias_content),
)
}

View file

@ -4,7 +4,7 @@ use crate::lang::constrain::Constraint::{self, *};
use crate::lang::pool::Pool;
use crate::lang::types::Type2;
use roc_can::expected::{Expected, PExpected};
use roc_collections::all::MutMap;
use roc_collections::all::{ImMap, MutMap};
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_types::solved_types::Solved;
@ -177,7 +177,7 @@ pub fn run_in_place(
#[allow(clippy::too_many_arguments)]
fn solve(
mempool: &mut Pool,
_env: &Env,
env: &Env,
state: State,
rank: Rank,
pools: &mut Pools,
@ -235,7 +235,8 @@ fn solve(
state
}
}
} // Store(source, target, _filename, _linenr) => {
}
// Store(source, target, _filename, _linenr) => {
// // a special version of Eq that is used to store types in the AST.
// // IT DOES NOT REPORT ERRORS!
// let actual = type_to_var(subs, rank, pools, cached_aliases, source);
@ -332,24 +333,25 @@ fn solve(
// }
// }
// }
// And(sub_constraints) => {
// let mut state = state;
//
// for sub_constraint in sub_constraints.iter() {
// state = solve(
// env,
// state,
// rank,
// pools,
// problems,
// cached_aliases,
// subs,
// sub_constraint,
// );
// }
//
// state
// }
And(sub_constraints) => {
let mut state = state;
for sub_constraint in sub_constraints.iter() {
state = solve(
mempool,
env,
state,
rank,
pools,
problems,
cached_aliases,
subs,
sub_constraint,
);
}
state
}
// Pattern(region, category, typ, expectation) => {
// let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
// let expected = type_to_var(
@ -389,233 +391,251 @@ fn solve(
// }
// }
// }
// Let(let_con) => {
// match &let_con.ret_constraint {
// True if let_con.rigid_vars.is_empty() => {
// introduce(subs, rank, pools, &let_con.flex_vars);
//
// // If the return expression is guaranteed to solve,
// // solve the assignments themselves and move on.
// solve(
// &env,
// state,
// rank,
// pools,
// problems,
// cached_aliases,
// subs,
// &let_con.defs_constraint,
// )
// }
// ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => {
// let state = solve(
// env,
// state,
// rank,
// pools,
// problems,
// cached_aliases,
// subs,
// &let_con.defs_constraint,
// );
//
// // Add a variable for each def to new_vars_by_env.
// let mut local_def_vars = ImMap::default();
//
// for (symbol, loc_type) in let_con.def_types.iter() {
// let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value);
//
// local_def_vars.insert(
// *symbol,
// Located {
// value: var,
// region: loc_type.region,
// },
// );
// }
//
// let mut new_env = env.clone();
// for (symbol, loc_var) in local_def_vars.iter() {
// if !new_env.vars_by_symbol.contains_key(&symbol) {
// new_env.vars_by_symbol.insert(*symbol, loc_var.value);
// }
// }
//
// let new_state = solve(
// &new_env,
// state,
// rank,
// pools,
// problems,
// cached_aliases,
// subs,
// ret_con,
// );
//
// for (symbol, loc_var) in local_def_vars {
// check_for_infinite_type(subs, problems, symbol, loc_var);
// }
//
// new_state
// }
// ret_con => {
// let rigid_vars = &let_con.rigid_vars;
// let flex_vars = &let_con.flex_vars;
//
// // work in the next pool to localize header
// let next_rank = rank.next();
//
// // introduce variables
// for &var in rigid_vars.iter().chain(flex_vars.iter()) {
// subs.set_rank(var, next_rank);
// }
//
// // determine the next pool
// let next_pools;
// if next_rank.into_usize() < pools.len() {
// next_pools = pools
// } else {
// // we should be off by one at this point
// debug_assert_eq!(next_rank.into_usize(), 1 + pools.len());
// pools.extend_to(next_rank.into_usize());
// next_pools = pools;
// }
//
// let pool: &mut Vec<Variable> = next_pools.get_mut(next_rank);
//
// // Replace the contents of this pool with rigid_vars and flex_vars
// pool.clear();
// pool.reserve(rigid_vars.len() + flex_vars.len());
// pool.extend(rigid_vars.iter());
// pool.extend(flex_vars.iter());
//
// // run solver in next pool
//
// // Add a variable for each def to local_def_vars.
// let mut local_def_vars = ImMap::default();
//
// for (symbol, loc_type) in let_con.def_types.iter() {
// let def_type = &loc_type.value;
//
// let var =
// type_to_var(subs, next_rank, next_pools, cached_aliases, def_type);
//
// local_def_vars.insert(
// *symbol,
// Located {
// value: var,
// region: loc_type.region,
// },
// );
// }
//
// // Solve the assignments' constraints first.
// let State {
// env: saved_env,
// mark,
// } = solve(
// &env,
// state,
// next_rank,
// next_pools,
// problems,
// cached_aliases,
// subs,
// &let_con.defs_constraint,
// );
//
// let young_mark = mark;
// let visit_mark = young_mark.next();
// let final_mark = visit_mark.next();
//
// debug_assert_eq!(
// {
// let offenders = next_pools
// .get(next_rank)
// .iter()
// .filter(|var| {
// let current = subs.get_without_compacting(
// roc_types::subs::Variable::clone(var),
// );
//
// current.rank.into_usize() > next_rank.into_usize()
// })
// .collect::<Vec<_>>();
//
// let result = offenders.len();
//
// if result > 0 {
// dbg!(&subs, &offenders, &let_con.def_types);
// }
//
// result
// },
// 0
// );
//
// // pop pool
// generalize(subs, young_mark, visit_mark, next_rank, next_pools);
//
// next_pools.get_mut(next_rank).clear();
//
// // check that things went well
// debug_assert!({
// // NOTE the `subs.redundant` check is added for the uniqueness
// // inference, and does not come from elm. It's unclear whether this is
// // a bug with uniqueness inference (something is redundant that
// // shouldn't be) or that it just never came up in elm.
// let failing: Vec<_> = rigid_vars
// .iter()
// .filter(|&var| {
// !subs.redundant(*var)
// && subs.get_without_compacting(*var).rank != Rank::NONE
// })
// .collect();
//
// if !failing.is_empty() {
// println!("Rigids {:?}", &rigid_vars);
// println!("Failing {:?}", failing);
// }
//
// failing.is_empty()
// });
//
// let mut new_env = env.clone();
// for (symbol, loc_var) in local_def_vars.iter() {
// // when there are duplicates, keep the one from `env`
// if !new_env.vars_by_symbol.contains_key(&symbol) {
// new_env.vars_by_symbol.insert(*symbol, loc_var.value);
// }
// }
//
// // Note that this vars_by_symbol is the one returned by the
// // previous call to solve()
// let temp_state = State {
// env: saved_env,
// mark: final_mark,
// };
//
// // Now solve the body, using the new vars_by_symbol which includes
// // the assignments' name-to-variable mappings.
// let new_state = solve(
// &new_env,
// temp_state,
// rank,
// next_pools,
// problems,
// cached_aliases,
// subs,
// &ret_con,
// );
//
// for (symbol, loc_var) in local_def_vars {
// check_for_infinite_type(subs, problems, symbol, loc_var);
// }
//
// new_state
// }
// }
// }
Let(let_con) => {
match &let_con.ret_constraint {
True if let_con.rigid_vars.is_empty() => {
introduce(subs, rank, pools, &let_con.flex_vars);
// If the return expression is guaranteed to solve,
// solve the assignments themselves and move on.
solve(
mempool,
&env,
state,
rank,
pools,
problems,
cached_aliases,
subs,
&let_con.defs_constraint,
)
}
ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => {
let state = solve(
mempool,
env,
state,
rank,
pools,
problems,
cached_aliases,
subs,
&let_con.defs_constraint,
);
// Add a variable for each def to new_vars_by_env.
let mut local_def_vars = ImMap::default();
for (symbol, loc_type) in let_con.def_types.iter() {
let var = type_to_var(
mempool,
subs,
rank,
pools,
cached_aliases,
&loc_type.value,
);
local_def_vars.insert(
*symbol,
Located {
value: var,
region: loc_type.region,
},
);
}
let mut new_env = env.clone();
for (symbol, loc_var) in local_def_vars.iter() {
if !new_env.vars_by_symbol.contains_key(&symbol) {
new_env.vars_by_symbol.insert(*symbol, loc_var.value);
}
}
let new_state = solve(
mempool,
&new_env,
state,
rank,
pools,
problems,
cached_aliases,
subs,
ret_con,
);
for (symbol, loc_var) in local_def_vars {
check_for_infinite_type(subs, problems, symbol, loc_var);
}
new_state
}
ret_con => {
let rigid_vars = &let_con.rigid_vars;
let flex_vars = &let_con.flex_vars;
// work in the next pool to localize header
let next_rank = rank.next();
// introduce variables
for &var in rigid_vars.iter().chain(flex_vars.iter()) {
subs.set_rank(var, next_rank);
}
// determine the next pool
let next_pools;
if next_rank.into_usize() < pools.len() {
next_pools = pools
} else {
// we should be off by one at this point
debug_assert_eq!(next_rank.into_usize(), 1 + pools.len());
pools.extend_to(next_rank.into_usize());
next_pools = pools;
}
let pool: &mut Vec<Variable> = next_pools.get_mut(next_rank);
// Replace the contents of this pool with rigid_vars and flex_vars
pool.clear();
pool.reserve(rigid_vars.len() + flex_vars.len());
pool.extend(rigid_vars.iter());
pool.extend(flex_vars.iter());
// run solver in next pool
// Add a variable for each def to local_def_vars.
let mut local_def_vars = ImMap::default();
for (symbol, loc_type) in let_con.def_types.iter() {
let def_type = &loc_type.value;
let var = type_to_var(
mempool,
subs,
next_rank,
next_pools,
cached_aliases,
def_type,
);
local_def_vars.insert(
*symbol,
Located {
value: var,
region: loc_type.region,
},
);
}
// Solve the assignments' constraints first.
let State {
env: saved_env,
mark,
} = solve(
mempool,
&env,
state,
next_rank,
next_pools,
problems,
cached_aliases,
subs,
&let_con.defs_constraint,
);
let young_mark = mark;
let visit_mark = young_mark.next();
let final_mark = visit_mark.next();
debug_assert_eq!(
{
let offenders = next_pools
.get(next_rank)
.iter()
.filter(|var| {
let current = subs.get_without_compacting(
roc_types::subs::Variable::clone(var),
);
current.rank.into_usize() > next_rank.into_usize()
})
.collect::<Vec<_>>();
let result = offenders.len();
if result > 0 {
dbg!(&subs, &offenders, &let_con.def_types);
}
result
},
0
);
// pop pool
generalize(subs, young_mark, visit_mark, next_rank, next_pools);
next_pools.get_mut(next_rank).clear();
// check that things went well
debug_assert!({
// NOTE the `subs.redundant` check is added for the uniqueness
// inference, and does not come from elm. It's unclear whether this is
// a bug with uniqueness inference (something is redundant that
// shouldn't be) or that it just never came up in elm.
let failing: Vec<_> = rigid_vars
.iter()
.filter(|&var| {
!subs.redundant(*var)
&& subs.get_without_compacting(*var).rank != Rank::NONE
})
.collect();
if !failing.is_empty() {
println!("Rigids {:?}", &rigid_vars);
println!("Failing {:?}", failing);
}
failing.is_empty()
});
let mut new_env = env.clone();
for (symbol, loc_var) in local_def_vars.iter() {
// when there are duplicates, keep the one from `env`
if !new_env.vars_by_symbol.contains_key(&symbol) {
new_env.vars_by_symbol.insert(*symbol, loc_var.value);
}
}
// Note that this vars_by_symbol is the one returned by the
// previous call to solve()
let temp_state = State {
env: saved_env,
mark: final_mark,
};
// Now solve the body, using the new vars_by_symbol which includes
// the assignments' name-to-variable mappings.
let new_state = solve(
mempool,
&new_env,
temp_state,
rank,
next_pools,
problems,
cached_aliases,
subs,
&ret_con,
);
for (symbol, loc_var) in local_def_vars {
check_for_infinite_type(subs, problems, symbol, loc_var);
}
new_state
}
}
} // _ => todo!("implement {:?}", constraint),
}
}
@ -641,7 +661,7 @@ pub fn insert_type_into_subs(mempool: &mut Pool, subs: &mut Subs, typ: &Type2) -
}
fn type_to_variable(
mempool: &mut Pool,
mempool: &Pool,
subs: &mut Subs,
rank: Rank,
pools: &mut Pools,
@ -707,6 +727,95 @@ fn type_to_variable(
register(subs, rank, pools, content)
}
Alias(Symbol::BOOL_BOOL, _, _) => roc_types::subs::Variable::BOOL,
Alias(symbol, args, alias_type_id) => {
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// different variables (once for each occurence). The recursion restriction is required
// for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
// attribute in the body, when
//
// Peano : [ S Peano, Z ]
//
// becomes
//
// Peano : [ S (Attr u Peano), Z ]
//
// This `u` variable can be different between lists, so giving just one variable to
// this type is incorrect.
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
let alias_type = mempool.get(*alias_type_id);
let is_recursive = false; // alias_type.is_recursive();
let no_args = args.is_empty();
/*
if no_args && !is_recursive {
if let Some(var) = cached.get(symbol) {
return *var;
}
}
*/
let mut arg_vars = Vec::with_capacity(args.len());
let mut new_aliases = ImMap::default();
for (arg, arg_type_id) in args.iter(mempool) {
let arg_type = mempool.get(*arg_type_id);
let arg_var = type_to_variable(mempool, subs, rank, pools, cached, arg_type);
let arg_str = arg.as_str(mempool);
arg_vars.push((roc_module::ident::Lowercase::from(arg_str), arg_var));
new_aliases.insert(arg_str, arg_var);
}
let alias_var = type_to_variable(mempool, subs, rank, pools, cached, alias_type);
let content = Content::Alias(*symbol, arg_vars, alias_var);
let result = register(subs, rank, pools, content);
if no_args && !is_recursive {
// cached.insert(*symbol, result);
}
result
}
TagUnion(tags, ext_id) => {
let mut tag_vars = MutMap::default();
let ext = mempool.get(*ext_id);
for (_tag, tag_argument_types) in tags.iter(mempool) {
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
for arg_type in tag_argument_types.iter(mempool) {
tag_argument_vars.push(type_to_variable(
mempool, subs, rank, pools, cached, arg_type,
));
}
tag_vars.insert(
roc_module::ident::TagName::Private(Symbol::NUM_NUM),
tag_argument_vars,
);
}
let temp_ext_var = type_to_variable(mempool, subs, rank, pools, cached, ext);
let mut ext_tag_vec = Vec::new();
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
subs,
temp_ext_var,
&mut ext_tag_vec,
) {
Ok(()) => roc_types::subs::Variable::EMPTY_TAG_UNION,
Err((new, _)) => new,
};
tag_vars.extend(ext_tag_vec.into_iter());
let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
register(subs, rank, pools, content)
}
other => todo!("not implemented {:?}", &other),
//
// // This case is important for the rank of boolean variables
@ -723,35 +832,6 @@ fn type_to_variable(
//
// register(subs, rank, pools, content)
// }
// TagUnion(tags, ext) => {
// let mut tag_vars = MutMap::default();
//
// for (tag, tag_argument_types) in tags {
// let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
//
// for arg_type in tag_argument_types {
// tag_argument_vars.push(type_to_variable(subs, rank, pools, cached, arg_type));
// }
//
// tag_vars.insert(tag.clone(), tag_argument_vars);
// }
//
// let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
// let mut ext_tag_vec = Vec::new();
// let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
// subs,
// temp_ext_var,
// &mut ext_tag_vec,
// ) {
// Ok(()) => Variable::EMPTY_TAG_UNION,
// Err((new, _)) => new,
// };
// tag_vars.extend(ext_tag_vec.into_iter());
//
// let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
//
// register(subs, rank, pools, content)
// }
// RecursiveTagUnion(rec_var, tags, ext) => {
// let mut tag_vars = MutMap::default();
//
@ -792,54 +872,6 @@ fn type_to_variable(
//
// tag_union_var
// }
// Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
// Alias(symbol, args, alias_type) => {
// // TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// // Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// // different variables (once for each occurence). The recursion restriction is required
// // for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
// // attribute in the body, when
// //
// // Peano : [ S Peano, Z ]
// //
// // becomes
// //
// // Peano : [ S (Attr u Peano), Z ]
// //
// // This `u` variable can be different between lists, so giving just one variable to
// // this type is incorrect.
// // TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
// let is_recursive = alias_type.is_recursive();
// let no_args = args.is_empty();
// /*
// if no_args && !is_recursive {
// if let Some(var) = cached.get(symbol) {
// return *var;
// }
// }
// */
//
// let mut arg_vars = Vec::with_capacity(args.len());
// let mut new_aliases = ImMap::default();
//
// for (arg, arg_type) in args {
// let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
//
// arg_vars.push((arg.clone(), arg_var));
// new_aliases.insert(arg.clone(), arg_var);
// }
//
// let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
// let content = Content::Alias(*symbol, arg_vars, alias_var);
//
// let result = register(subs, rank, pools, content);
//
// if no_args && !is_recursive {
// // cached.insert(*symbol, result);
// }
//
// result
// }
// HostExposedAlias {
// name: symbol,
// arguments: args,

View file

@ -60,7 +60,13 @@ pub enum Problem2 {
impl ShallowClone for Type2 {
fn shallow_clone(&self) -> Self {
todo!()
match self {
Self::Variable(var) => Self::Variable(*var),
Self::Alias(symbol, pool_vec, type_id) => {
Self::Alias(*symbol, pool_vec.shallow_clone(), type_id.clone())
}
rest => todo!("{:?}", rest),
}
}
}

View file

@ -13,6 +13,19 @@ impl TextPos {
column: self.column + 1,
}
}
pub fn decrement_col(&self) -> TextPos {
let new_col = if self.column > 0 {
self.column - 1
} else {
self.column
};
TextPos {
line: self.line,
column: new_col,
}
}
}
impl Ord for TextPos {

View file

@ -16,6 +16,7 @@ use roc_editor::lang::{
types::Type2,
};
use roc_module::ident::Lowercase;
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_module::symbol::{IdentIds, ModuleIds};
use roc_region::all::Region;
@ -83,6 +84,7 @@ fn infer_eq(actual: &str, expected_str: &str) {
match expr2_result {
Ok((expr, _)) => {
let constraint = constrain_expr(
&code_arena,
&mut env,
&expr,
Expected::NoExpectation(Type2::Variable(var)),
@ -92,6 +94,7 @@ fn infer_eq(actual: &str, expected_str: &str) {
let Env {
pool,
var_store: ref_var_store,
dep_idents,
..
} = env;
@ -111,7 +114,11 @@ fn infer_eq(actual: &str, expected_str: &str) {
let content = subs.get(var).content;
let actual_str = content_to_string(content, &subs, mod_id, &Default::default());
let interns = Interns {
module_ids,
all_ident_ids: dep_idents,
};
let actual_str = content_to_string(content, &subs, mod_id, &interns);
assert_eq!(actual_str, expected_str);
}
@ -142,3 +149,15 @@ fn constrain_empty_record() {
"{}",
)
}
#[test]
fn constrain_small_int() {
infer_eq(
indoc!(
r#"
12
"#
),
"Int *",
)
}