Skip to content
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

Add default literals for optional arguments #283

Merged
merged 14 commits into from
Sep 21, 2020

Conversation

jhugman
Copy link
Contributor

@jhugman jhugman commented Sep 16, 2020

Fixes #263.

This PR fleshes out the parsing and output of Literals, and used in default arguments and defaulted values for dictionaries. This corresponds to the WebIDL keyword optional.

Copy link
Contributor Author

@jhugman jhugman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this here for feedback before a rebase.

Literal::EmptyMap => "{}".into(),

_ => panic!("Literal unsupported by python"),
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd do more with numbers, but the test_rondpoint.py is quite far behind the kotlin and swift versions right now.

Copy link
Collaborator

@rfk rfk Sep 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my list of "things I want to do before revisiting this python patch" is back down to zero, fingers crossed...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this to the list of things to do after #214 has landed.

Comment on lines +152 to +172
match radix {
Radix::Octal => format!("0o{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
)?,
Literal::UInt(i, radix, type_) => typed_number(
type_,
match radix {
Radix::Octal => format!("0o{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This level of duplication felt acceptable.

Using the 8 byte u64, i64 and f64 as internal representations for number literals was the compromise between lots of duplication, and flexibility of representation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a fine idea! This reads very cleanly, and I think the duplication trade-off is more than okay.

@@ -358,7 +358,7 @@ extension Dictionary: ViaFfiUsingByteBuffer, ViaFfi, Serializable where Key == S
let len: UInt32 = try buf.readInt()
var dict = [String: Value]()
dict.reserveCapacity(Int(len))
for _ in 1...len {
for _ in 0..<len {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests found that empty lists crashes the swift bindings.

};

let trimmer = Regex::new("^(-?)(0x)?").unwrap();
let string = trimmer.replace_all(string, "$1").to_lowercase();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible improvement/optimization: putting this in a lazy_static! macro.

Comment on lines 1026 to 1170
(weedle::literal::DefaultValue::String(s), Type::Enum(_)) => {
Literal::Enum(s.0.to_string(), type_.clone())
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was very pleased to work out we could default enums.

@jhugman jhugman requested review from linabutler and eoger September 16, 2020 17:33
@jhugman jhugman self-assigned this Sep 16, 2020
@jhugman jhugman force-pushed the 263-default-arguments branch from a477d03 to b2ce9ee Compare September 16, 2020 18:10
Copy link
Collaborator

@rfk rfk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't dig too much into the nitty-gritty parsing details, but did an initial high-level pass and left some comments. Overall, seems to be coming together nicely 👍

@@ -168,4 +168,93 @@ impl Stringifier {
}
}

#[derive(Debug, Clone)]
struct Optionneur;
impl Optionneur {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rondpoint example continues to be a thing of great beauty 👍

Literal::EmptyMap => "{}".into(),

_ => panic!("Literal unsupported by python"),
})
Copy link
Collaborator

@rfk rfk Sep 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my list of "things I want to do before revisiting this python patch" is back down to zero, fingers crossed...

})
}
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: as of latest main, you'll need to remove Serialize and Deserialize from here.

let src_radix = radix as u32;
// This radix tells the backends how to represent the number in the output languages.
let dest_radix = if string == "0" || string.starts_with('-') {
// 1. weedle parses "0" as an octal literal, but we most likely want to treat this as a decimal.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow; octal zero I hope 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D Yeah. Thought that'd be a bit surprising to our API consumers finding 0o0 in the API.

Decimal = 10,
Octal = 8,
Hexadecimal = 16,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is preserving the radix necessary for correctness in some cases, or is it entirely about readability of the generated code? (I'm not opposed to doing it for readability reasons, just want to make sure I understand).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserving the radix is only for readabilty/documentation for the generated interfaces.

There are cases when we can't preserve radix (octal numbers, anyone?), or our choice of representation mean that it's not possible (negative hex and octal numbers); in both these cases we use decimal because correctness beats readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment about radices.

weedle::literal::DefaultValue::Boolean(b) => Literal::Boolean(b.0),
weedle::literal::DefaultValue::String(s) => Literal::String(s.0.to_string()),
_ => bail!("no support for {:?} literal yet", self),
impl TypedAPIConverter<Literal> for weedle::literal::DefaultValue<'_> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you only impl this for a single type, I wonder if it would be simpler to have convert_default_value as a standalone function to begin with, rather than making it a trait. Or, do you anticipate adding more impls for this shortly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not anticipating any additional implementations. I've made the change to a standalone function as suggested.

// TODO: more types of literal
UInt(u64, Radix, Type),
Int(i64, Radix, Type),
Float(String, Type),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naively, I was expecting to see separate UInt8, UInt16 etc variants on this enum. I don't object to this more succinct representation if it makes the code cleaner, but it may be worth a short comment here to explain the choice since it stands out as being a bit different from other type-related enums.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added extra comments around these representations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the extra comments! I also like your approach of using the widest representation possible, and truncating to the actual type in the binding backends, to simplify the code.

@jhugman jhugman force-pushed the 263-default-arguments branch from b2ce9ee to 330588d Compare September 21, 2020 13:46
Copy link
Contributor

@linabutler linabutler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is fantastic, @jhugman! Everything looks solid, and the battery of tests is great.

It would be super helpful to have for the JS and C++ backends, too, which already have some machinery for returning a dummy default value for methods that throw a JS exception—all that code can piggyback on your default literals now.

Ship it! :shipit:

u64 sinon_u64_dec(optional u64 value = 42);
i64 sinon_i64_dec(optional i64 value = 42);

// Hexadecimal, including negatgives.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Hexadecimal, including negatgives.
// Hexadecimal, including negatives.


Ok(match literal {
Literal::Boolean(v) => format!("{}", v),
Literal::String(s) => format!("\"{}\"", s),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to worry about escaping internal quotes (maybe more of an issue for Python single-quoted strings), or handling binary or Unicode escape sequences? It's definitely an edge case, especially for Nimbus and our other Rust components, so "no" is a totally acceptable answer—but it came to mind reading this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😷

It appears weedle doesn't let us escape quotes,

    /// Represents a string value
    ///
    /// Follow `/"[^"]*"/`
    #[derive(Copy)]
    struct StringLit<'a>(
        &'a str = ws!(do_parse!(
            char!('"') >>
            s: take_while!(|c| c != '"') >>
            char!('"') >>
            (s)
        )),
    )

I added some comments at the frontend, and changed the quotes in the python backend.

pub fn literal_kt(literal: &Literal) -> Result<String, askama::Error> {
fn typed_number(type_: &Type, num_str: String) -> Result<String, askama::Error> {
Ok(match type_ {
// special case Int32.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, did this comment come from the Swift backend?

Comment on lines +152 to +172
match radix {
Radix::Octal => format!("0o{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
)?,
Literal::UInt(i, radix, type_) => typed_number(
type_,
match radix {
Radix::Octal => format!("0o{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a fine idea! This reads very cleanly, and I think the duplication trade-off is more than okay.

let src_radix = radix as u32;
// This radix tells the backends how to represent the number in the output languages.
let dest_radix = if string == "0" || string.starts_with('-') {
// 1. weedle parses "0" as an octal literal, but we most likely want to treat this as a decimal.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😬 at "parses '0' as an octal literal"

radix
};

let trimmer = Regex::new("^(-?)(0x)?").unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too worried about this, since we're still prototyping and might eventually use more complex regexes elsewhere—but bringing in regex can bump up compile times. For prefix-stripping like this, I wonder if we can use plain-ol' string functions, maybe something like this:

let string = if string.starts_with('-') {
    ("-".to_string() + string[1..].strip_prefix("0x").unwrap_or(&string[1..])).to_lowercase()
} else {
    string.strip_prefix("0x").unwrap_or(&string).to_lowercase()
};

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, magic.

Not since Java 1.3 have I had to think about not having regular expressions.

// TODO: more types of literal
UInt(u64, Radix, Type),
Int(i64, Radix, Type),
Float(String, Type),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the extra comments! I also like your approach of using the widest representation possible, and truncating to the actual type in the binding backends, to simplify the code.

@jhugman jhugman force-pushed the 263-default-arguments branch from 330588d to eed96d0 Compare September 21, 2020 22:28
@jhugman jhugman merged commit 3913c61 into mozilla:main Sep 21, 2020
@jhugman jhugman deleted the 263-default-arguments branch September 21, 2020 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Figure out optional arguments with defaults
3 participants