mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Start single-quote literal parser work
This commit is contained in:
parent
b25862a93b
commit
dc2016dc0d
5 changed files with 141 additions and 0 deletions
|
@ -91,6 +91,8 @@ pub enum Expr<'a> {
|
|||
Access(&'a Expr<'a>, &'a str),
|
||||
/// e.g. `.foo`
|
||||
AccessorFunction(&'a str),
|
||||
/// eg 'b'
|
||||
SingleQuote(char),
|
||||
|
||||
// Collection Literals
|
||||
List {
|
||||
|
@ -358,6 +360,7 @@ pub enum Pattern<'a> {
|
|||
FloatLiteral(&'a str),
|
||||
StrLiteral(StrLiteral<'a>),
|
||||
Underscore(&'a str),
|
||||
SingleQuote(char),
|
||||
|
||||
// Space
|
||||
SpaceBefore(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]),
|
||||
|
|
|
@ -203,6 +203,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()),
|
||||
|
@ -225,6 +226,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)),
|
||||
|
@ -2340,6 +2342,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>, Number> {
|
||||
map!(
|
||||
crate::number_literal::positive_number_literal(),
|
||||
|
|
|
@ -419,6 +419,7 @@ pub enum EExpr<'a> {
|
|||
InParens(EInParens<'a>, Row, Col),
|
||||
Record(ERecord<'a>, Row, Col),
|
||||
Str(EString<'a>, Row, Col),
|
||||
SingleQuote(char, Row, Col),
|
||||
Number(Number, Row, Col),
|
||||
List(List<'a>, Row, Col),
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ pub fn loc_pattern_help<'a>(
|
|||
)),
|
||||
loc!(number_pattern_help()),
|
||||
loc!(string_pattern_help()),
|
||||
loc!(single_quote_pattern_help()),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -112,6 +113,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)
|
||||
|
@ -164,6 +166,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(
|
||||
|_, r, c| EPattern::Start(r, c),
|
||||
map!(
|
||||
crate::string_literal::parse_single_quote(),
|
||||
Pattern::SingleQuote
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_ident_pattern_help<'a>(
|
||||
min_indent: u16,
|
||||
can_have_arguments: bool,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use crate::ast::{EscapedChar, StrLiteral, StrSegment};
|
||||
use crate::expr;
|
||||
use crate::parser::Progress::*;
|
||||
|
@ -46,6 +48,120 @@ macro_rules! advance_state {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn parse_single_quote<'a>() -> impl Parser<'a, char, 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.line, state.column), 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'\\' {
|
||||
if state.bytes.first() == Some(&b'\'') {
|
||||
state = advance_state!(state, 1)?;
|
||||
// since we checked the current char between the single quotes we
|
||||
// know they are valid UTF-8, allowing us to use 'from_u32_unchecked'
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
unsafe { char::from_u32_unchecked(ch as u32) },
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
// invalid error, backslah escaping something we do not recognize
|
||||
return Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
}
|
||||
None => {
|
||||
// no close quote found
|
||||
return Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
// do nothing for other characters, handled below
|
||||
}
|
||||
None => {
|
||||
return Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let mut bytes = state.bytes.iter();
|
||||
let mut end_index = 1;
|
||||
|
||||
loop {
|
||||
match bytes.next() {
|
||||
Some(b'\'') => {
|
||||
break;
|
||||
}
|
||||
Some(_) => end_index += 1,
|
||||
None => {
|
||||
return Err((NoProgress, EString::Open(state.line, state.column), 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.line, state.column), state));
|
||||
}
|
||||
|
||||
if end_index > (std::mem::size_of::<u32>() + 1) {
|
||||
// bad case: too big to fit into u32
|
||||
return Err((NoProgress, EString::Open(state.line, state.column), 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 bytes_array: [u8; 4] = match state.bytes[1..end_index].try_into() {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
return Err((NoProgress, EString::Open(state.line, state.column), state));
|
||||
}
|
||||
};
|
||||
|
||||
state = advance_state!(state, end_index)?;
|
||||
match char::from_u32(u32::from_ne_bytes(bytes_array)) {
|
||||
Some(ch) => {
|
||||
return Ok((MadeProgress, ch, state));
|
||||
}
|
||||
None => {
|
||||
// invalid UTF-8
|
||||
return Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
||||
use StrLiteral::*;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue