implemented selfdocumenting fstrings and tests from cpython- current handling of quotes is too simple

This commit is contained in:
TheAnyKey committed 2020-04-29 15:37:47 -04:00
1 parent 94a7b48560
commit 34478d3db7
2 files changed
+142 -7

No files matched your search

+62 -6
View File
@@ -27,9 +27,36 @@ impl<'a> FStringParser<'a> {
let mut delims = Vec::new();
let mut conversion = None;
let mut pred_expression_text = String::new();
let mut trailing_seq=String::new();
while let Some(ch) = self.chars.next() {
match ch {
// can be integrated better with the remainign code, but as a starting point ok
// in general I would do here a tokenizing of the fstrings to omit this peeking.
'!' if self.chars.peek() == Some(&'=') => {
expression.push('!');
expression.push('=');
self.chars.next();
}
'=' if self.chars.peek() == Some(&'=') => {
expression.push('=');
expression.push('=');
self.chars.next();
}
'>' if self.chars.peek() == Some(&'=') => {
expression.push('>');
expression.push('=');
self.chars.next();
}
'<' if self.chars.peek() == Some(&'=') => {
expression.push('<');
expression.push('=');
self.chars.next();
}
'!' if delims.is_empty() && self.chars.peek() != Some(&'=') => {
if expression.trim().is_empty() {
return Err(EmptyExpression);
@@ -54,8 +81,9 @@ impl<'a> FStringParser<'a> {
// match a python 3.8 self documenting expression
// format '{' PYTHON_EXPRESSION '=' FORMAT_SPECIFIER? '}'
'=' if self.chars.peek() != Some(&'=') => {
pred_expression_text = expression.trim().to_string(); // use expression and remove whitespace
'=' if self.chars.peek() != Some(&'=') => { // check for delims empty?
pred_expression_text = expression.to_string(); // safe expression before = to print it
// pred_expression_text contains trailing spaces, which are trimmed somewhere later before printing, this needs to be prevented orovercome here
}
':' if delims.is_empty() => {
@@ -143,7 +171,11 @@ impl<'a> FStringParser<'a> {
return Ok(Joined{
values:vec![
Constant{
value:pred_expression_text.to_owned()
value:pred_expression_text.to_owned()+"="
},
Constant {
value:trailing_seq.to_owned()
},
FormattedValue {
@@ -167,6 +199,11 @@ impl<'a> FStringParser<'a> {
}
}
}
' ' if !pred_expression_text.is_empty() => {
trailing_seq.push(ch);
}
_ => {
expression.push(ch);
}
@@ -370,9 +407,7 @@ mod tests {
assert_eq!(parse_fstring("{a:{b}"), Err(UnclosedLbrace));
assert_eq!(parse_fstring("{"), Err(UnclosedLbrace));
// TODO: which err?
//assert_eq!(parse_fstring("{}"), Err()); // which one?
assert!(parse_fstring("{}").is_err()); // take this for the moment
assert_eq!(parse_fstring("{}"), Err(EmptyExpression));
// TODO: check for InvalidExpression enum?
assert!(parse_fstring("{class}").is_err());
@@ -384,4 +419,25 @@ mod tests {
let parse_ast = parse_fstring(&source);
assert!(parse_ast.is_ok());
}
#[test]
fn test_parse_fstring_equals() {
let source = String::from("{42 == 42}");
let parse_ast = parse_fstring(&source);
assert!(parse_ast.is_ok());
}
#[test]
fn test_parse_fstring_selfdoc_prec_space() {
let source = String::from("{x =}");
let parse_ast = parse_fstring(&source);
assert!(parse_ast.is_ok());
}
#[test]
fn test_parse_fstring_selfdoc_trailing_space() {
let source = String::from("{x= }");
let parse_ast = parse_fstring(&source);
assert!(parse_ast.is_ok());
}
}
+80 -1
View File
@@ -1,4 +1,4 @@
from testutils import assert_raises
#from testutils import assert_raises
foo = 'bar'
assert f"{''}" == ''
@@ -17,6 +17,13 @@ assert fr'x={4*10}\n' == 'x=40\\n'
assert f'{16:0>+#10x}' == '00000+0x10'
assert f"{{{(lambda x: f'hello, {x}')('world}')}" == '{hello, world}'
assert f'{foo=}' == 'foo=bar'
num=42
assert f'{num=}' == 'num=42'
assert f'{num=:>10}' == 'num= 42'
spec = "0>+#10x"
assert f"{16:{spec}}{foo}" == '00000+0x10bar'
@@ -61,3 +68,75 @@ assert f'>{v}' == '>\u262e'
assert f'>{v!r}' == ">'\u262e'"
assert f'>{v!s}' == '>\u262e'
assert f'>{v!a}' == r">'\u262e'"
### Tests for fstring selfdocumenting form CPython
class C:
def assertEqual(self, a,b):
assert a==b, "{0} == {1}".format(a,b)
self=C()
x = 'A string'
self.assertEqual(f'{10=}', '10=10')
self.assertEqual(f'{x=}', 'x=' + x )#repr(x)) # TODO: add ' when printing strings
#self.assertEqual(f'{x =}', 'x =' + x )# + repr(x)) # TODO: remove trim in impl
# self.assertEqual(f'{x=!s}', 'x=' + str(x)) # TODO : implement format specs properly
# self.assertEqual(f'{x=!r}', 'x=' + x) #repr(x))
#self.assertEqual(f'{x=!a}', 'x=' + ascii(x))
x = 2.71828
self.assertEqual(f'{x=:.2f}', 'x=' + format(x, '.2f'))
self.assertEqual(f'{x=:}', 'x=' + format(x, ''))
# self.assertEqual(f'{x=!r:^20}', 'x=' + format(repr(x), '^20'))
# self.assertEqual(f'{x=!s:^20}', 'x=' + format(str(x), '^20'))
# self.assertEqual(f'{x=!a:^20}', 'x=' + format(ascii(x), '^20'))
x = 9
self.assertEqual(f'{3*x+15=}', '3*x+15=42')
# There is code in ast.c that deals with non-ascii expression values. So,
# use a unicode identifier to trigger that.
tenπ = 31.4
self.assertEqual(f'{tenπ=:.2f}', 'tenπ=31.40')
# Also test with Unicode in non-identifiers.
#self.assertEqual(f'{"Σ"=}', '"Σ"=\'Σ\'') ' TODO ' missing
# Make sure nested fstrings still work.
self.assertEqual(f'{f"{3.1415=:.1f}":*^20}', '*****3.1415=3.1*****')
# Make sure text before and after an expression with = works
# correctly.
pi = 'π'
#self.assertEqual(f'alpha α {pi=} ω omega', "alpha α pi='π' ω omega") # ' missing around pi
# Check multi-line expressions.
#self.assertEqual(f'''{3=}''', '\n3\n=3') # TODO: multiline f strings not supported, seems to be an rustpython issue
# Since = is handled specially, make sure all existing uses of
# it still work.
self.assertEqual(f'{0==1}', 'False')
self.assertEqual(f'{0!=1}', 'True')
self.assertEqual(f'{0<=1}', 'True')
self.assertEqual(f'{0>=1}', 'False')
# Make sure leading and following text works.
x = 'foo'
#self.assertEqual(f'X{x=}Y', 'Xx='+repr(x)+'Y') # TODO '
self.assertEqual(f'X{x=}Y', 'Xx='+x+'Y') # just for the moment
# Make sure whitespace around the = works.
# self.assertEqual(f'X{x =}Y', 'Xx ='+repr(x)+'Y') # TODO '
# self.assertEqual(f'X{x= }Y', 'Xx= '+repr(x)+'Y') # TODO '
# self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y') # TODO '
# TODO remove trim
self.assertEqual(f'X{x =}Y', 'Xx ='+x+'Y')
self.assertEqual(f'X{x= }Y', 'Xx= '+x+'Y')
self.assertEqual(f'X{x = }Y', 'Xx = '+x+'Y')