Skip to content

Conversation

jneem
Copy link
Member

@jneem jneem commented Aug 22, 2025

Factors out the parser into a separate crate, nickel-lang-parser. This is mostly just moving things around, but there were a few other changes.

  • The implementations of CloneTo for some type aliases in nickel_lang_core::typecheck (like UnifEnumRow) broke the orphan rule. I worked around these by adding some more generic implementations of CloneTo in the parser, so that those type aliases already implement CloneTo from the implementations in nickel_lang_parser
  • Some of the Display impls in nickel_lang_core::typ (like EnumRow) broke the orphan rule. I couldn't re-use the same trick from the last point, because of differences in the two different Allocator structs. So I used a newtype wrapper instead.
  • I ported the parser tests to the new ast.

I didn't pay too much attention to exactly where the API boundary is. The main goal was to get it working, and then we can refine it later.

Fixes #2265

Copy link
Contributor

github-actions bot commented Aug 25, 2025

@jneem jneem marked this pull request as ready for review August 25, 2025 17:55
@jneem jneem requested a review from yannham August 25, 2025 18:01
@yannham
Copy link
Member

yannham commented Aug 26, 2025

Some of the Display impls in nickel_lang_core::typ (like EnumRow) broke the orphan rule. I couldn't re-use the same trick from the last point, because of differences in the two different Allocator structs. So I used a newtype wrapper instead.

Where is this code/newtype located? I'm trying to focus the review a bit.

/// Concrete, recursive definition for an enum row.
pub type EnumRow = EnumRowF<Box<Type>>;
#[derive(Clone, PartialEq, Debug)]
pub struct EnumRow(pub EnumRowF<Box<Type>>);
Copy link
Member Author

Choose a reason for hiding this comment

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

Here (and RecordRow just below) is the newtype wrapper to allow us to implement Display.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I feared this would be quite painful, but it doesn't impact the typechecker, only the runtime Type representation. Maybe we should add a comment as to why this one only get the newtype treatment. Another possibility would be to use only the newtype wrapper when we actually need to pretty-print/display, and keep the naked type alias in the actual definition. Though if the newtype wrapper didn't cause much diff,maybe that's not very useful...

impl CloneTo for typ::EnumRows<'_> {
type Data<'ast> = typ::EnumRows<'ast>;
impl<Ty: CloneTo, ERows: CloneTo> CloneTo for typ::EnumRowsF<Ty, ERows> {
type Data<'ast> = typ::EnumRowsF<Ty::Data<'ast>, ERows::Data<'ast>>;
Copy link
Member Author

Choose a reason for hiding this comment

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

Here's the more generic impl of CloneTo for EnumRowsF so that it covers the type alias in nickel_lang_core

Copy link
Member

Choose a reason for hiding this comment

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

Thanks!

Copy link
Member

@yannham yannham left a comment

Choose a reason for hiding this comment

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

I'm requesting changes as a lock to wait for #2302 to land first, but otherwise looks good as a first step.

@@ -0,0 +1,322 @@
//! An environment for storing variables with scopes.
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to have environment here? It's a bit strange. Maybe another solution would be, middle term, to separate it entirely into its own helper crate.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's used in fix_type_vars in the uniterm module.

/// Concrete, recursive definition for an enum row.
pub type EnumRow = EnumRowF<Box<Type>>;
#[derive(Clone, PartialEq, Debug)]
pub struct EnumRow(pub EnumRowF<Box<Type>>);
Copy link
Member

Choose a reason for hiding this comment

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

Ah, I feared this would be quite painful, but it doesn't impact the typechecker, only the runtime Type representation. Maybe we should add a comment as to why this one only get the newtype treatment. Another possibility would be to use only the newtype wrapper when we actually need to pretty-print/display, and keep the naked type alias in the actual definition. Though if the newtype wrapper didn't cause much diff,maybe that's not very useful...

impl CloneTo for typ::EnumRows<'_> {
type Data<'ast> = typ::EnumRows<'ast>;
impl<Ty: CloneTo, ERows: CloneTo> CloneTo for typ::EnumRowsF<Ty, ERows> {
type Data<'ast> = typ::EnumRowsF<Ty::Data<'ast>, ERows::Data<'ast>>;
Copy link
Member

Choose a reason for hiding this comment

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

Thanks!

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.

Move the parser into its own crate
2 participants