Skip to content

Implement Py3.9 LCM and GCD #1907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bed0577
Merge pull request #2 from RustPython/master
TheAnyKey Apr 28, 2020
94a7b48
added initial implementation of self defining expressions to fstring …
TheAnyKey Apr 28, 2020
34478d3
implemented selfdocumenting fstrings and tests from cpython- current …
TheAnyKey Apr 29, 2020
c25353c
minor cleanup
TheAnyKey Apr 29, 2020
e7ec972
now conversion flags can be followed by a format specifier and added …
TheAnyKey Apr 29, 2020
972503c
fixed qute handling in tests
TheAnyKey Apr 29, 2020
8e8e839
activated further test, removed todos
TheAnyKey Apr 29, 2020
3ccb117
fixed fmt
TheAnyKey Apr 29, 2020
68bc6fb
Update ci.yaml
TheAnyKey Apr 30, 2020
3e8cde7
Removed incompatibilty with CPython, fixed parser error handling
TheAnyKey Apr 30, 2020
80a9710
fixed clippy; tests fail explicitly for python 3.7 and below
TheAnyKey Apr 30, 2020
f823af1
skipping inkompatible test for the moment
TheAnyKey Apr 30, 2020
620115d
recommit
TheAnyKey May 1, 2020
87dff70
Merge pull request #3 from TheAnyKey/TheAnyKey/p38_self_documenting_f…
TheAnyKey May 1, 2020
90123df
Initial implementation of Py3.9 dict union and python-level tests
TheAnyKey May 2, 2020
155f3ca
first complete? implementation. TODO: Cleanup, remove warnings, clipp…
TheAnyKey May 2, 2020
9a8b8e7
fixed clippy, fmt, warnings, etc.. Improved tests and extended testut…
TheAnyKey May 2, 2020
f6f9754
changed reference python version to 3.9 - lets see what happens
TheAnyKey May 2, 2020
a285474
fixed issued in testutils.skip_if_unsupported and testutils.fail_if_u…
TheAnyKey May 2, 2020
41849ab
fixed: returned wrong error when union opration is invoked with other…
TheAnyKey May 3, 2020
1697ce0
reverted toolchain settings to python version 3.8 as 3.9 is so far no…
TheAnyKey May 3, 2020
0faf968
fixed typo
TheAnyKey May 3, 2020
be41a7b
Merge pull request #4 from TheAnyKey/TheAnyKey/p39_dict_union
TheAnyKey May 3, 2020
87ae8d8
added lcm with var args and changed to gcd to var args
TheAnyKey May 4, 2020
ed69270
cleaned up
TheAnyKey May 4, 2020
9d238bd
cleaned up
TheAnyKey May 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
on:
push:
branches: [master, release]

pull_request:

name: CI
Expand Down
129 changes: 120 additions & 9 deletions parser/src/fstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,37 @@ impl<'a> FStringParser<'a> {
let mut spec = None;
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);
Expand All @@ -46,10 +74,19 @@ impl<'a> FStringParser<'a> {
}
});

if self.chars.peek() != Some(&'}') {
let peek = self.chars.peek();
if peek != Some(&'}') && peek != Some(&':') {
return Err(ExpectedRbrace);
}
}

// match a python 3.8 self documenting expression
// format '{' PYTHON_EXPRESSION '=' FORMAT_SPECIFIER? '}'
'=' if self.chars.peek() != Some(&'=') => {
// check for delims empty?
pred_expression_text = expression.to_string(); // safe expression before = to print it
}

':' if delims.is_empty() => {
let mut nested = false;
let mut in_nested = false;
Expand Down Expand Up @@ -121,14 +158,35 @@ impl<'a> FStringParser<'a> {
if expression.is_empty() {
return Err(EmptyExpression);
}
return Ok(FormattedValue {
value: Box::new(
parse_expression(expression.trim())
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
),
conversion,
spec,
});
if pred_expression_text.is_empty() {
return Ok(FormattedValue {
value: Box::new(
parse_expression(expression.trim())
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
),
conversion,
spec,
});
} else {
return Ok(Joined {
values: vec![
Constant {
value: pred_expression_text + "=",
},
Constant {
value: trailing_seq,
},
FormattedValue {
value: Box::new(
parse_expression(expression.trim())
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
),
conversion,
spec,
},
],
});
}
}
'"' | '\'' => {
expression.push(ch);
Expand All @@ -139,6 +197,11 @@ impl<'a> FStringParser<'a> {
}
}
}

' ' if !pred_expression_text.is_empty() => {
trailing_seq.push(ch);
}

_ => {
expression.push(ch);
}
Expand Down Expand Up @@ -298,9 +361,34 @@ mod tests {
);
}

#[test]
fn test_fstring_parse_selfdocumenting_base() {
let src = String::from("{user=}");
let parse_ast = parse_fstring(&src);

assert!(parse_ast.is_ok());
}

#[test]
fn test_fstring_parse_selfdocumenting_base_more() {
let src = String::from("mix {user=} with text and {second=}");
let parse_ast = parse_fstring(&src);

assert!(parse_ast.is_ok());
}

#[test]
fn test_fstring_parse_selfdocumenting_format() {
let src = String::from("{user=:>10}");
let parse_ast = parse_fstring(&src);

assert!(parse_ast.is_ok());
}

#[test]
fn test_parse_invalid_fstring() {
assert_eq!(parse_fstring("{5!a"), Err(ExpectedRbrace));

assert_eq!(parse_fstring("{5!a1}"), Err(ExpectedRbrace));
assert_eq!(parse_fstring("{5!"), Err(ExpectedRbrace));

Expand All @@ -318,6 +406,8 @@ mod tests {
assert_eq!(parse_fstring("{a:{b}"), Err(UnclosedLbrace));
assert_eq!(parse_fstring("{"), Err(UnclosedLbrace));

assert_eq!(parse_fstring("{}"), Err(EmptyExpression));

// TODO: check for InvalidExpression enum?
assert!(parse_fstring("{class}").is_err());
}
Expand All @@ -328,4 +418,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());
}
}
83 changes: 83 additions & 0 deletions tests/snippets/dict_union.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

import testutils

def test_dunion_ior0():
a={1:2,2:3}
b={3:4,5:6}
a|=b

assert a == {1:2,2:3,3:4,5:6}, f"wrong value assigned {a=}"
assert b == {3:4,5:6}, f"right hand side modified, {b=}"

def test_dunion_or0():
a={1:2,2:3}
b={3:4,5:6}
c=a|b

assert a == {1:2,2:3}, f"left hand side of non-assignment operator modified {a=}"
assert b == {3:4,5:6}, f"right hand side of non-assignment operator modified, {b=}"
assert c == {1:2,2:3, 3:4, 5:6}, f"unexpected result of dict union {c=}"


def test_dunion_or1():
a={1:2,2:3}
b={3:4,5:6}
c=a.__or__(b)

assert a == {1:2,2:3}, f"left hand side of non-assignment operator modified {a=}"
assert b == {3:4,5:6}, f"right hand side of non-assignment operator modified, {b=}"
assert c == {1:2,2:3, 3:4, 5:6}, f"unexpected result of dict union {c=}"


def test_dunion_ror0():
a={1:2,2:3}
b={3:4,5:6}
c=b.__ror__(a)

assert a == {1:2,2:3}, f"left hand side of non-assignment operator modified {a=}"
assert b == {3:4,5:6}, f"right hand side of non-assignment operator modified, {b=}"
assert c == {1:2,2:3, 3:4, 5:6}, f"unexpected result of dict union {c=}"


def test_dunion_other_types():
def perf_test_or(other_obj):
d={1:2}
try:
d.__or__(other_obj)
except:
return True
return False

def perf_test_ior(other_obj):
d={1:2}
try:
d.__ior__(other_obj)
except:
return True
return False

def perf_test_ror(other_obj):
d={1:2}
try:
d.__ror__(other_obj)
except:
return True
return False

test_fct={'__or__':perf_test_or, '__ror__':perf_test_ror, '__ior__':perf_test_ior}
others=['FooBar', 42, [36], set([19]), ['aa'], None]
for tfn,tf in test_fct.items():
for other in others:
assert tf(other), f"Failed: dict {tfn}, accepted {other}"




testutils.skip_if_unsupported(3,9,test_dunion_ior0)
testutils.skip_if_unsupported(3,9,test_dunion_or0)
testutils.skip_if_unsupported(3,9,test_dunion_or1)
testutils.skip_if_unsupported(3,9,test_dunion_ror0)
testutils.skip_if_unsupported(3,9,test_dunion_other_types)



Loading