Devlog: Giving Flower Real Module Support
v1.2.x was the point where Flower’s impors had to stop being “you can import stuff.. and there’s a fake layer of security” to a proper (get it?) module system.
Before this work, imports and aliases mostly behaved like naming conveniences. You could write:
import "math.flo" as math
math.add(1, 2)
but the compiler was still too loose about what that really meant. Alias names were in emitted C symbol names, top-level declarations were not private, and prop existed more as syntax than as a real feature.
That was fine for early bootstrap work, but it was not strong enough for the next stage of the language.
So the main goal of v1.2.x became:
- make imported aliases act like real namespaces
- make
propdefine the public interface of a module - make top-level declarations private by default
- lay groundwork for field visibility rules like
hiddenandreadonly
Module Semantics Instead of Alias Tricks
One of the biggest changes was separating Flower semantics from C lowering.
Previously, imported aliases were drifting toward becoming part of emitted symbol identity. A local alias should just be a source-level name bound to a module, not the backend symbol.
So now each module has its own lowered symbol prefix, and alias lookup happens semantically during compilation. That means:
import "math.flo" as math
math.add(1, 2)
is resolved as “look up exported add in module math.flo,” not “invent a C symbol from the local alias text as math_add().”
That change also forced a cleanup of emitted names so they stopped leaking absolute filesystem paths. Generated symbols now use project-relative module prefixes instead of machine-specific paths.
prop Became Real
Another major part of this was making prop actually do something.
Instead of treating it as a thin wrapper around only functions, the compiler now recognizes prop as a real top-level export marker. That means the module’s public interface is defined by what it explicitly exports, while non-prop declarations remain internal.
This moved Flower closer to the model I wanted all along: explicit modules, explicit public APIs, no heavy object system (OOP), no fake privacy delegated to the C backend.
Dot Access, Pointers, and the Annoying Middle Part
This work also exposed a weakness in the compiler’s dot-access resolution.
Flower source always uses ., while the C backend must decide whether a given access becomes . or ->. That mostly worked already, but nested pointer-heavy field chains exposed cases where the type tracker was not propagating enough information, which caused bad fallback behavior during self-hosting.
So before field visibility could be trusted, I had to harden nested dot-access resolution. Not fun, but necessary. As it turns out, I was missing several AST kinds which caused the logic to fall through. Very tedious work, but it was well-worth it in the end!
Field Flags Groundwork
With module ownership and access resolution in better shape, I added the first field visibility modifiers:
hiddenreadonlyfrozen
Originally I debated having these as bitwise operations, but I decided it’d be best to stay within the current update’s scope, so sometime in the future I’ll refactor.
Right now, hidden and readonly are the meaningful additions:
-
hiddenblocks external field access -
readonlyblocks external assignment
frozen is groundwork for later. It is parsed, stored, and carried through the compiler, but full assignment-once enforcement is still deferred. Unsure how I’ll go about it, but that’s an issue for future me :p
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.