Merge pull request #1591 from rtfeldman/single-quote-literal

Single Quote literal
This commit is contained in:
Richard Feldman 2022-02-26 23:27:46 -05:00 committed by GitHub
commit d3acf34415
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 404 additions and 18 deletions

View File

@ -1474,6 +1474,15 @@ pub fn constrain_pattern<'a>(
));
}
CharacterLiteral(_) => {
state.constraints.push(Constraint::Pattern(
region,
PatternCategory::Character,
num_unsigned32(env.pool),
expected,
));
}
RecordDestructure {
whole_var,
ext_var,
@ -1927,6 +1936,26 @@ fn _num_signed64(pool: &mut Pool) -> Type2 {
)
}
#[inline(always)]
fn num_unsigned32(pool: &mut Pool) -> Type2 {
let alias_content = Type2::TagUnion(
PoolVec::new(
std::iter::once((
TagName::Private(Symbol::NUM_UNSIGNED32),
PoolVec::empty(pool),
)),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_UNSIGNED32,
PoolVec::empty(pool),
pool.add(alias_content),
)
}
#[inline(always)]
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
let range_type = pool.get(range);

View File

@ -39,6 +39,7 @@ pub enum Pattern2 {
IntLiteral(IntVal), // 16B
FloatLiteral(FloatVal), // 16B
StrLiteral(PoolStr), // 8B
CharacterLiteral(char), // 4B
Underscore, // 0B
GlobalTag {
whole_var: Variable, // 4B
@ -249,6 +250,26 @@ pub fn to_pattern2<'a>(
ptype => unsupported_pattern(env, ptype, region),
},
SingleQuote(string) => match pattern_type {
WhenBranch => {
let mut it = string.chars().peekable();
if let Some(char) = it.next() {
if it.peek().is_none() {
Pattern2::CharacterLiteral(char)
} else {
// multiple chars is found
let problem = MalformedPatternProblem::MultipleCharsInSingleQuote;
malformed_pattern(env, problem, region)
}
} else {
// no characters found
let problem = MalformedPatternProblem::EmptySingleQuote;
malformed_pattern(env, problem, region)
}
}
ptype => unsupported_pattern(env, ptype, region),
},
GlobalTag(name) => {
// Canonicalize the tag's name.
Pattern2::GlobalTag {
@ -506,6 +527,7 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
| IntLiteral(_)
| FloatLiteral(_)
| StrLiteral(_)
| CharacterLiteral(_)
| Underscore
| MalformedPattern(_, _)
| Shadowed { .. }
@ -566,6 +588,7 @@ pub fn symbols_and_variables_from_pattern(
| IntLiteral(_)
| FloatLiteral(_)
| StrLiteral(_)
| CharacterLiteral(_)
| Underscore
| MalformedPattern(_, _)
| Shadowed { .. }

View File

@ -607,6 +607,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
Expr::SingleQuote(a) => Expr::Num(a),
}
}
}
@ -649,6 +650,7 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
}
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
Pattern::SingleQuote(a) => Pattern::NumLiteral(a),
}
}
}

1
cli_utils/Cargo.lock generated
View File

@ -2696,6 +2696,7 @@ dependencies = [
"roc_mono",
"roc_problem",
"roc_region",
"roc_reporting",
"roc_solve",
"roc_target",
"roc_types",

View File

@ -854,6 +854,7 @@ fn pattern_to_vars_by_symbol(
| IntLiteral(..)
| FloatLiteral(..)
| StrLiteral(_)
| SingleQuote(_)
| Underscore
| MalformedPattern(_, _)
| UnsupportedPattern(_)

View File

@ -73,6 +73,7 @@ pub enum Expr {
Int(Variable, Variable, Box<str>, IntValue, IntBound),
Float(Variable, Variable, Box<str>, f64, FloatBound),
Str(Box<str>),
SingleQuote(char),
List {
elem_var: Variable,
loc_elems: Vec<Loc<Expr>>,
@ -323,6 +324,28 @@ pub fn canonicalize_expr<'a>(
}
}
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
ast::Expr::SingleQuote(string) => {
let mut it = string.chars().peekable();
if let Some(char) = it.next() {
if it.peek().is_none() {
(Expr::SingleQuote(char), Output::default())
} else {
// multiple chars is found
let error = roc_problem::can::RuntimeError::MultipleCharsInSingleQuote(region);
let answer = Expr::RuntimeError(error);
(answer, Output::default())
}
} else {
// no characters found
let error = roc_problem::can::RuntimeError::EmptySingleQuote(region);
let answer = Expr::RuntimeError(error);
(answer, Output::default())
}
}
ast::Expr::List(loc_elems) => {
if loc_elems.is_empty() {
(
@ -1267,6 +1290,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
| other @ Int(..)
| other @ Float(..)
| other @ Str { .. }
| other @ SingleQuote(_)
| other @ RuntimeError(_)
| other @ EmptyRecord
| other @ Accessor { .. }

View File

@ -572,6 +572,7 @@ fn fix_values_captured_in_closure_pattern(
| IntLiteral(..)
| FloatLiteral(..)
| StrLiteral(_)
| SingleQuote(_)
| Underscore
| Shadowed(..)
| MalformedPattern(_, _)
@ -629,6 +630,7 @@ fn fix_values_captured_in_closure_expr(
| Int(..)
| Float(..)
| Str(_)
| SingleQuote(_)
| Var(_)
| EmptyRecord
| RuntimeError(_)

View File

@ -126,6 +126,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| Num(..)
| NonBase10Int { .. }
| Str(_)
| SingleQuote(_)
| AccessorFunction(_)
| Var { .. }
| Underscore { .. }

View File

@ -39,6 +39,7 @@ pub enum Pattern {
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
StrLiteral(Box<str>),
SingleQuote(char),
Underscore,
// Runtime Exceptions
@ -108,6 +109,7 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
| IntLiteral(..)
| FloatLiteral(..)
| StrLiteral(_)
| SingleQuote(_)
| Underscore
| MalformedPattern(_, _)
| UnsupportedPattern(_)
@ -309,6 +311,23 @@ pub fn canonicalize_pattern<'a>(
ptype => unsupported_pattern(env, ptype, region),
},
SingleQuote(string) => {
let mut it = string.chars().peekable();
if let Some(char) = it.next() {
if it.peek().is_none() {
Pattern::SingleQuote(char)
} else {
// multiple chars is found
let problem = MalformedPatternProblem::MultipleCharsInSingleQuote;
malformed_pattern(env, problem, region)
}
} else {
// no characters found
let problem = MalformedPatternProblem::EmptySingleQuote;
malformed_pattern(env, problem, region)
}
}
SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => {
return canonicalize_pattern(env, var_store, scope, pattern_type, sub_pattern, region)
}
@ -560,6 +579,7 @@ fn add_bindings_from_patterns(
| IntLiteral(..)
| FloatLiteral(..)
| StrLiteral(_)
| SingleQuote(_)
| Underscore
| MalformedPattern(_, _)
| UnsupportedPattern(_)

View File

@ -193,6 +193,21 @@ pub fn num_floatingpoint(range: Type) -> Type {
)
}
#[inline(always)]
pub fn num_u32() -> Type {
builtin_alias(Symbol::NUM_U32, vec![], Box::new(num_int(num_unsigned32())))
}
#[inline(always)]
fn num_unsigned32() -> Type {
let alias_content = Type::TagUnion(
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
Box::new(Type::EmptyTagUnion),
);
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
}
#[inline(always)]
pub fn num_binary64() -> Type {
let alias_content = Type::TagUnion(

View File

@ -1,5 +1,5 @@
use crate::builtins::{
empty_list_type, float_literal, int_literal, list_type, num_literal, str_type,
empty_list_type, float_literal, int_literal, list_type, num_literal, num_u32, str_type,
};
use crate::pattern::{constrain_pattern, PatternState};
use roc_can::annotation::IntroducedVariables;
@ -213,6 +213,7 @@ pub fn constrain_expr(
exists(vars, And(cons))
}
Str(_) => Eq(str_type(), expected, Category::Str, region),
SingleQuote(_) => Eq(num_u32(), expected, Category::Character, region),
List {
elem_var,
loc_elems,

View File

@ -60,6 +60,7 @@ fn headers_from_annotation_help(
| NumLiteral(..)
| IntLiteral(..)
| FloatLiteral(..)
| SingleQuote(_)
| StrLiteral(_) => true,
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
@ -252,6 +253,15 @@ pub fn constrain_pattern(
));
}
SingleQuote(_) => {
state.constraints.push(Constraint::Pattern(
region,
PatternCategory::Character,
builtins::num_u32(),
expected,
));
}
RecordDestructure {
whole_var,
ext_var,

View File

@ -30,6 +30,7 @@ impl<'a> Formattable for Expr<'a> {
Float(..)
| Num(..)
| NonBase10Int { .. }
| SingleQuote(_)
| Access(_, _)
| AccessorFunction(_)
| Var { .. }
@ -209,6 +210,11 @@ impl<'a> Formattable for Expr<'a> {
buf.indent(indent);
buf.push_str(string)
}
SingleQuote(string) => {
buf.push('\'');
buf.push_str(string);
buf.push('\'');
}
&NonBase10Int {
base,
string,

View File

@ -36,6 +36,7 @@ impl<'a> Formattable for Pattern<'a> {
| Pattern::NonBase10Literal { .. }
| Pattern::FloatLiteral(..)
| Pattern::StrLiteral(_)
| Pattern::SingleQuote(_)
| Pattern::Underscore(_)
| Pattern::Malformed(_)
| Pattern::MalformedIdent(_, _)
@ -147,6 +148,11 @@ impl<'a> Formattable for Pattern<'a> {
StrLiteral(literal) => {
todo!("Format string literal: {:?}", literal);
}
SingleQuote(string) => {
buf.push('\'');
buf.push_str(string);
buf.push('\'');
}
Underscore(name) => {
buf.indent(indent);
buf.push('_');

View File

@ -2040,8 +2040,11 @@ fn pattern_to_when<'a>(
}
UnwrappedOpaque { .. } => todo_opaques!(),
IntLiteral(..) | NumLiteral(..) | FloatLiteral(..) | StrLiteral(_) => {
IntLiteral(..)
| NumLiteral(..)
| FloatLiteral(..)
| StrLiteral(..)
| roc_can::pattern::Pattern::SingleQuote(..) => {
// These patters are refutable, and thus should never occur outside a `when` expression
// They should have been replaced with `UnsupportedPattern` during canonicalization
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
@ -3147,6 +3150,13 @@ pub fn with_hole<'a>(
hole,
),
SingleQuote(character) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(character as _)),
Layout::int_width(IntWidth::I32),
hole,
),
Num(var, num_str, num, _bound) => {
// first figure out what kind of number this is
match num_argument_to_int_or_float(env.subs, env.target_info, var, false) {
@ -7746,6 +7756,7 @@ fn from_can_pattern_help<'a>(
}
}
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
SingleQuote(c) => Ok(Pattern::IntLiteral(*c as _, IntWidth::I32)),
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
original_region: *region,
shadow: ident.clone(),

View File

@ -152,6 +152,8 @@ pub enum Expr<'a> {
Access(&'a Expr<'a>, &'a str),
/// e.g. `.foo`
AccessorFunction(&'a str),
/// eg 'b'
SingleQuote(&'a str),
// Collection Literals
List(Collection<'a, &'a Loc<Expr<'a>>>),
@ -462,6 +464,7 @@ pub enum Pattern<'a> {
FloatLiteral(&'a str),
StrLiteral(StrLiteral<'a>),
Underscore(&'a str),
SingleQuote(&'a str),
// Space
SpaceBefore(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]),

View File

@ -195,6 +195,7 @@ fn parse_loc_term_or_underscore<'a>(
one_of!(
loc_expr_in_parens_etc_help(min_indent),
loc!(specialize(EExpr::Str, string_literal_help())),
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
loc!(specialize(EExpr::Number, positive_number_literal_help())),
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
loc!(underscore_expression()),
@ -217,6 +218,7 @@ fn parse_loc_term<'a>(
one_of!(
loc_expr_in_parens_etc_help(min_indent),
loc!(specialize(EExpr::Str, string_literal_help())),
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
loc!(specialize(EExpr::Number, positive_number_literal_help())),
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
loc!(record_literal_help(min_indent)),
@ -1534,6 +1536,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::UnaryOp(_, _) => Err(()),
Expr::Str(string) => Ok(Pattern::StrLiteral(*string)),
Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(*string)),
Expr::MalformedIdent(string, _problem) => Ok(Pattern::Malformed(string)),
}
}
@ -2352,6 +2355,13 @@ fn string_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map!(crate::string_literal::parse(), Expr::Str)
}
fn single_quote_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map!(
crate::string_literal::parse_single_quote(),
Expr::SingleQuote
)
}
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(
crate::number_literal::positive_number_literal(),

View File

@ -355,6 +355,7 @@ pub enum EExpr<'a> {
InParens(EInParens<'a>, Position),
Record(ERecord<'a>, Position),
Str(EString<'a>, Position),
SingleQuote(EString<'a>, Position),
Number(ENumber, Position),
List(EList<'a>, Position),

View File

@ -61,6 +61,7 @@ pub fn loc_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
)),
loc!(number_pattern_help()),
loc!(string_pattern_help()),
loc!(single_quote_pattern_help()),
)
}
@ -108,6 +109,7 @@ fn loc_parse_tag_pattern_arg<'a>(
crate::pattern::record_pattern_help(min_indent)
)),
loc!(string_pattern_help()),
loc!(single_quote_pattern_help()),
loc!(number_pattern_help())
)
.parse(arena, state)
@ -159,6 +161,16 @@ fn string_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
)
}
fn single_quote_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
specialize(
|_, pos| EPattern::Start(pos),
map!(
crate::string_literal::parse_single_quote(),
Pattern::SingleQuote
),
)
}
fn loc_ident_pattern_help<'a>(
min_indent: u32,
can_have_arguments: bool,

View File

@ -35,6 +35,100 @@ macro_rules! advance_state {
};
}
pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
move |arena: &'a Bump, mut state: State<'a>| {
if state.bytes().starts_with(b"\'") {
// we will be parsing a single-quote-string
} else {
return Err((NoProgress, EString::Open(state.pos()), state));
}
// early return did not hit, just advance one byte
state = advance_state!(state, 1)?;
// Handle back slaches in byte literal
// - starts with a backslash and used as an escape character. ex: '\n', '\t'
// - single quote floating (un closed single quote) should be an error
match state.bytes().first() {
Some(b'\\') => {
state = advance_state!(state, 1)?;
match state.bytes().first() {
Some(&ch) => {
state = advance_state!(state, 1)?;
if (ch == b'n' || ch == b'r' || ch == b't' || ch == b'\'' || ch == b'\\')
&& (state.bytes().first() == Some(&b'\''))
{
state = advance_state!(state, 1)?;
let test = match ch {
b'n' => '\n',
b't' => '\t',
b'r' => '\r',
// since we checked the current char between the single quotes we
// know they are valid UTF-8, allowing us to use 'from_u32_unchecked'
_ => unsafe { char::from_u32_unchecked(ch as u32) },
};
return Ok((MadeProgress, &*arena.alloc_str(&test.to_string()), state));
}
// invalid error, backslah escaping something we do not recognize
return Err((NoProgress, EString::CodePtEnd(state.pos()), state));
}
None => {
// no close quote found
return Err((NoProgress, EString::CodePtEnd(state.pos()), state));
}
}
}
Some(_) => {
// do nothing for other characters, handled below
}
None => return Err((NoProgress, EString::CodePtEnd(state.pos()), state)),
}
let mut bytes = state.bytes().iter();
let mut end_index = 1;
// Copy paste problem in mono
loop {
match bytes.next() {
Some(b'\'') => {
break;
}
Some(_) => end_index += 1,
None => {
return Err((NoProgress, EString::Open(state.pos()), state));
}
}
}
if end_index == 1 {
// no progress was made
// this case is a double single quote, ex: ''
// not supporting empty single quotes
return Err((NoProgress, EString::Open(state.pos()), state));
}
if end_index > (std::mem::size_of::<u32>() + 1) {
// bad case: too big to fit into u32
return Err((NoProgress, EString::Open(state.pos()), state));
}
// happy case -> we have some bytes that will fit into a u32
// ending up w/ a slice of bytes that we want to convert into an integer
let raw_bytes = &state.bytes()[0..end_index - 1];
state = advance_state!(state, end_index)?;
match std::str::from_utf8(raw_bytes) {
Ok(string) => Ok((MadeProgress, string, state)),
Err(_) => {
// invalid UTF-8
return Err((NoProgress, EString::CodePtEnd(state.pos()), state));
}
}
}
}
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
use StrLiteral::*;

View File

@ -507,19 +507,28 @@ mod test_parse {
}
#[quickcheck]
fn all_f64_values_parse(num: f64) {
let string = num.to_string();
if string.contains('.') {
assert_parses_to(&string, Float(&string));
} else if num.is_nan() {
assert_parses_to(&string, Expr::GlobalTag(&string));
} else if num.is_finite() {
// These are whole numbers. Add the `.0` back to make float.
let float_string = format!("{}.0", string);
assert_parses_to(&float_string, Float(&float_string));
fn all_f64_values_parse(mut num: f64) {
// NaN, Infinity, -Infinity (these would all parse as tags in Roc)
if !num.is_finite() {
num = 0.0;
}
// These can potentially be whole numbers. `Display` omits the decimal point for those,
// causing them to no longer be parsed as fractional numbers by Roc.
// Using `Debug` instead of `Display` ensures they always have a decimal point.
let float_string = format!("{:?}", num);
assert_parses_to(float_string.as_str(), Float(float_string.as_str()));
}
// SINGLE QUOTE LITERAL
#[test]
fn single_quote() {
assert_parses_to("'b'", Expr::SingleQuote("b"));
}
// RECORD LITERALS
// #[test]
// fn type_signature_def() {
// let arena = Bump::new();

View File

@ -230,6 +230,11 @@ pub enum RuntimeError {
VoidValue,
ExposedButNotDefined(Symbol),
/// where ''
EmptySingleQuote(Region),
/// where 'aa'
MultipleCharsInSingleQuote(Region),
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -240,4 +245,6 @@ pub enum MalformedPatternProblem {
Unknown,
QualifiedIdentifier,
BadIdent(roc_parse::ident::BadIdent),
EmptySingleQuote,
MultipleCharsInSingleQuote,
}

View File

@ -357,6 +357,66 @@ fn u8_hex_int_alias() {
);
}
#[test]
fn character_literal() {
assert_evals_to!(
indoc!(
r#"
x = 'A'
x
"#
),
65,
u32
);
}
#[test]
fn character_literal_back_slash() {
assert_evals_to!(
indoc!(
r#"
x = '\\'
x
"#
),
92,
u32
);
}
#[test]
fn character_literal_single_quote() {
assert_evals_to!(
indoc!(
r#"
x = '\''
x
"#
),
39,
u32
);
}
#[test]
fn character_literal_new_line() {
assert_evals_to!(
indoc!(
r#"
x = '\n'
x
"#
),
10,
u32
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn dec_float_alias() {

View File

@ -1304,6 +1304,7 @@ pub enum Category {
Num,
List,
Str,
Character,
// records
Record,
@ -1325,6 +1326,7 @@ pub enum PatternCategory {
Num,
Int,
Float,
Character,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]

View File

@ -945,13 +945,17 @@ fn pretty_runtime_error<'b>(
}
Unknown => " ",
QualifiedIdentifier => " qualified ",
EmptySingleQuote => " empty character literal ",
MultipleCharsInSingleQuote => " overfull literal ",
};
let tip = match problem {
MalformedInt | MalformedFloat | MalformedBase(_) => alloc
.tip()
.append(alloc.reflow("Learn more about number literals at TODO")),
Unknown | BadIdent(_) => alloc.nil(),
EmptySingleQuote | MultipleCharsInSingleQuote | Unknown | BadIdent(_) => {
alloc.nil()
}
QualifiedIdentifier => alloc.tip().append(
alloc.reflow("In patterns, only private and global tags can be qualified"),
),
@ -1341,6 +1345,37 @@ fn pretty_runtime_error<'b>(
title = MISSING_DEFINITION;
}
RuntimeError::EmptySingleQuote(region) => {
let tip = alloc
.tip()
.append(alloc.reflow("Learn more about character literals at TODO"));
doc = alloc.stack(vec![
alloc.concat(vec![alloc.reflow("This character literal is empty.")]),
alloc.region(lines.convert_region(region)),
tip,
]);
title = SYNTAX_PROBLEM;
}
RuntimeError::MultipleCharsInSingleQuote(region) => {
let tip = alloc
.tip()
.append(alloc.reflow("Learn more about character literals at TODO"));
doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This character literal contains more than one code point.")
]),
alloc.region(lines.convert_region(region)),
alloc.concat(vec![
alloc.reflow("Character literals can only contain one code point.")
]),
tip,
]);
title = SYNTAX_PROBLEM;
}
RuntimeError::OpaqueNotDefined {
usage:
Loc {

View File

@ -1100,7 +1100,6 @@ fn format_category<'b>(
]),
alloc.text(" produces:"),
),
List => (
alloc.concat(vec![this_is, alloc.text(" a list")]),
alloc.text(" of type:"),
@ -1128,17 +1127,18 @@ fn format_category<'b>(
]),
alloc.text(" which was of type:"),
),
Character => (
alloc.concat(vec![this_is, alloc.text(" a character")]),
alloc.text(" of type:"),
),
Lambda => (
alloc.concat(vec![this_is, alloc.text(" an anonymous function")]),
alloc.text(" of type:"),
),
ClosureSize => (
alloc.concat(vec![this_is, alloc.text(" the closure size of a function")]),
alloc.text(" of type:"),
),
TagApply {
tag_name: TagName::Global(name),
args_count: 0,
@ -1472,6 +1472,7 @@ fn add_pattern_category<'b>(
Num => alloc.reflow(" numbers:"),
Int => alloc.reflow(" integers:"),
Float => alloc.reflow(" floats:"),
Character => alloc.reflow(" characters:"),
};
alloc.concat(vec![i_am_trying_to_match, rest])