Just wrapped up the last final push of Polaris!
This included many tiny changes, but the most major changes were:
I wrote an AR library that projects 2 lines (denoted by equators along the coordinates of the object’s position) to cross at the point of the star! Also created a small page for this, along with media-syncing after the photo was taken
Me and my friend created a demo video showcasing this project!
wait what? this isn’t ending. this is just the beginning
Hihihi I’m so excited to share the awesome things I’ve been working on! Check out the following new additions:
struct Vec { x: num, y: num }
proc subVec(a: Vec, b: Vec) -> Vec {
return Vec( x = a.x + b.x, y = a.y + b.y);
}
Katnip has so much ahead of it… star the repo and follow along!!
The editor is turning out beautifully. Like I love it so far. Tons and tons of improvements to mainly the editor, but a few pivotal improvements to the compiler itself.
Finally, after a long time of putting this off, Katnip finally has syntax to import sounds and assets to use for your projects!
stage {
costume "./sky.svg";
events.onFlag() {
looks.switchBackdrop("sky");
}
}
sprite Cat {
costume "./cat.svg" as idle;
costume "./cat-blink.svg" as blink;
sound "./meow.wav";
events.onFlag() {
motion.goTo(-100, 0);
looks.switchCostume("idle");
looks.say("costumes + sounds work", 2);
forever {
wait(0.5);
looks.nextCostume();
}
}
}
Compiler changes
costume and sound keywordsstage keyword for stage override (previously auto-gen’d)forever keyword (cuz I forgot to add it somehow)Website changes
costume and sound required fs callbacksNot sure how the forever block and rename button took this long to add, both seem like something I could’ve adressed earlier, but—better late than never :P
Finally got a working prototype! Check it out at https://katnip.org
This was honestly a pretty hard part of the process. I am using tw-scaffolding, the core vm from TurboWarp. But it has no types, no docs, and the docs that exist are outdated. So I resorted to having Claude make a .d.ts file for me so I could fuzz my way through instead.
This process wasn’t that bad once the functions were clear, and I hooked it up to my compiler—intentionally designed to output Uint8Array.
This was fun since I don’t know svelte yet that well. I had a state originally stored in my state.svelte.ts, but I figure out I could refactor and not need it.
Then I just did some vm tomfoolery to ensure the threads stay open on pausing, and it was good to go.
This is a super rudamentry approach. I plan to have an input method at some point, but we will see. Hopefully I implement a better technique, currently I just have a string array storing the logs. Makes it easy to make it stateful in svelte ig
Ahhhh im so excited.
I’m building the gui for Katnip right now—built off a small foundation done by a friend.
Currently repo is private, but I’ll open it once my computer’s not about to die and I’m on stable internet.
Worked on the following things:
I have moved this to Macondo!
https://macondo.hackclub.com/projects/16810
Some progress below:
So this is a step I didn’t really want to take.
I had to take away a part of the language that I always wanted to exist.
The existence of the temp keyword.
I mean this thing was literally covering my demos and other scripts in the docs and whatnot. But alas, it was not properly implemented, and I have no forseeable good solution to working it out.
So its removed for the time being.
On the flipside, I did finalize and fully implement zip, enumerate, and range!
These functions now properly parse the list arguments and let tuple unpacking do its amazing goodness.
Great new features. Genuinely super cool.
This new bout of progress that led to end-to-end compilation has given me so much motivation.
So, this already existed. But I realized that they are so much more than just a way to create states to check against, or store values in. Ever since I allowed scalar values to be the represenation of enum members, this has become more powerful than I realized.
The key was implementing the type interpretation of an enum. I already was kind of doing this, but when you have an input param for a function, you can assign a type of enum FlowerType for instance. So the input only accepts FlowerType.x where x is the type.
But thats long. Look at a function call:
sniff(FlowerType.DAFFODIL, backup = FlowerType.ROSE);
Instead, I like what typescript does with its discriminant types, allowing a type to represent a set of possible values. So, it was born.
sniff("daffodil", backup = "rose");
In order to use this, just assign values to your enum:
enum FlowerType {
DAFFODIL = "daffodil",
ROSE = "rose",
...
}
Prior, a lot of stuff including type casting was a mess. I just recently got this up and running, so it was hard to even try to think about this.
Scratch only has a few types: round and angled and block. Thats it. And types really only apply to round/angled.
So I implemented small blocks that allow scratch’s oddities and allow you to tell the compiler to “shut up”.
Want to get the string of a bool? No problem.
looks.say( Str(var == 2) + "apple")
And many many other use cases.
Insane progress over the past few days has occurred, in large part due to my travels on train, and my apparent ability to lock in when I start seeing things.
Probably the most recognizable part of the project, Katnip can now generate scratch code. Blocks are being made, and complex patterns written in the IR are finally revealing themselves.
It is so important that you look at the pictures, as the improvements made here are hard to put into words.
Writing this part was arguably the most fun part of the entire project. This part was more scratch-like than any other part so far (“well duh, its the closest stage” you might be saying. And you’d be right.)
Writing a majority did not come without its need for the IR to step up its game. I was quite fed up with 0 visual progress, so I jumped to codegen to boost my morale for the improvements made to the IR.
This includes dict and list slicing, and a proper handling of booleans.
I can see it… (what is it?) … the light at the end of the tunnel… (what are you talking about?) .. its almost here
Though the project isn’t done, I will admit I am nearing the closing moments of the IR stage, with only 1 last hurdle to overcome before I can transpile projects beginning -> end.
Here’s a small overview of what’s been done in the last two weeks (nah who am I kidding its all last night, I haven’t touched this in a bit)
Katnip is scratch. Kinda. It transpiles to scratch. So it needs procedures.
I took a giant bite out of the huge problem that is procedures. It is very complex to try and emulate a return system in a program that just doesn’t support it.
Having taken a DSA class, this was a problem that I took to paper, and got the following solution:
FiLo, or first-in, last-out. This is super important for returning from procedures, when considering the fact that a single variable will not be enough for systems when/if the function recurses. So this way, you push and pop on the stack to keep the order, and all return values are kept track of nicely!A smaller sections, but still cool.
A for loop in scratch is a hidden–but supported–block. You can only iterate a variable, lets call it x, from 1..y inclusive, where y is the max range.
So for lists, this means lowering this:
for (x, myList) {
looks.say(x);
}
into this:
for (x, listLength(myList)) {
x = myList[x];
looks.say(x);
}
Additionally, if doing a range function like this:
for (x, range(4, 10, 2)) {
looks.say(x);
}
it lowers into this:
for (x, 3) {
x = x * 2 + 4;
looks.say(x);
}
Finally got around to adding and implementing sites into Polaris! No longer are you land-locked to your current position, you can now plan ahead for other locations and times.
You can now have multiple sites, switch between them, and add them. Fully fleshed out menu
I moved to a 3-tab layout.
After messing around in my other projects with regex, I came to the conclusion that regex is hard to read, and hrad to write. Lets fix that shall we?
I want the language to feel simple, and ideally be simple under the hood. simple == !regex. For the whole run down on all of my keywords and symbols, check out the Token definition file.
But we both know you’re lazy, so heres a tl;dr rundown:
def dotted(part) = { part repeat { "." part } }
let buildId = repeat 1.. { num}
capture kward and use that output directly by importing the .grex file using a typescript import (yes really, no nrn, this lang is a WIP)Don’t trust me? Adament on writing your own regex? Tell me what this regex means in 10 seconds:
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)(?!.*(?i:password))\S{12,}$
..
..
Yea no, thats near impossible. Now try this:
/** Secret must contain each class, avoid the obvious, and be 12+ non-space. */
pattern StrongSecret {
start
ahead { repeat { any } digit }
ahead { repeat { any } ['a'..'z'] }
ahead { repeat { any } ['A'..'Z'] }
ahead { repeat { any } not word }
notAhead { repeat { any } ignoreCase { "password" } }
repeat 12.. { not space }
end
}
You may be saying: “you cheated, you used a comment!”. Well, exactly. Regex doesn’t have comments. G-Rex does.
Jokes aside, this was one of the more fun and easy coding sessions I’ve had. I built most of the functions I needed, wrapping the astronomy library I use.
So it was just a culmination of cobbling together functions + iterating over them to form coherent graphs. They still look a bit off to me, but I will clean things up later. My focus is on finishing the demo, then going back.
tonight/upcoming sections for sky diagnosisMy friend, a large astronomy enthusiast, pointed out that many apps have an entirely red mode to block out blue light, as it distorts vision when its most critical to seeing stars in the night sky or through a telescope. So I designed the entire app in shades of black and gray, with the intention of supporting a red-scale version with a very plug-and-play approach. Gray-scale ensured the app wouldn’t look horrendous when I switched to red.
This is my first project with a database. I opted to use sqlite and learn all about how it works. Though much of the commands are based on stack overflow threads, and my understanding is still lacking, I plan to further and devlop this skill as I work on storage for users and pictures. This is definently an important part of an app like this.
This doesn’t really deserve its own section, but I thought it was interesting.
The cleanest apps imo are ones that use the system colors and design. So react-native was an obvious choice. However, liquid glass is an experimental feature, and furthermore requires packaging and building the react-native in a way that makes it near impossible to hot-reload using expo-go. For this reason, I opted to make my own liquid glass elements, pulling in the native ui library to apply it to elements of my own.
This is the core portion of the app. The main idea of the app is to engage users, and provide a constant influx of ideas and targets to see. It is hard for some to constantly have to look up what would be fun to see, with their equipment and location, and current weather.
Our app solves this by crunching all the data for this and displaying it in an easy-to-read format. We first calculate the different weather and astrological conditions at the time for the user. This involves calculating a multitude of values, but it all collapses into a single equation: score: reasons.length === 0 ? 0.5 + quality / 2 : (quality / 2) * 0.5 where quality is based off headroom and moonroom, and the other weather conditions (pulled from a local weather api)
Retrospectively, this probably should’ve been multiple seperate posts. But I forgot. Again.
For compiling to scratch, a variety of different appracohes have been done. Personally, these fall under a few distinct approaches: variable and list. Variable is simply setting a variable to the output, whereas a list is just a stack following LiFo rules.
A variable should be used for single value, non-recursive functions. It’s pretty naive actually. The better alternative way is to use a stack, in which you push and pop from it. This way, you know the exact offset that correlates to the return value in recursive functions.
The stack also allows for easy nesting of functions next to each other, like foo(1, 2) + foo(3, 4), which is only possible in variable form with temporary vars.
Less said here, but this was actually pretty simple. I just ran some regex to get all the identifiers, and then seperated them by type. Pretty rudamentry, but nice.
This was a huge first step in the IR design. Basically, I need a good way of storing metadata. Inventing whole new katnip syntax for this would:
a. Look bad, but also
b. Be horrible to implement
So I wrote a typescript file that takes care of the json metadata, allowing only rudamentry tags and opcode to be stored in Katnip portion.
This was a fun portion to tackle. Because I am targeting 3 deployments: vsc, cli, and web, I need a design that covers all 3. A friend suggested callbacks. This allows me to assume the user will define a function that allows filesystem interactions, without me touching a thing.
So all I did was bring in the code from the imported file, and ran compilation over it too. I pulled it in during the semantic analysis phase as that was the correct timing; after verifying the import statement; before the codegen.
Lots of deliberation occured here. It was very crucial to implement this, personally, because I use this type of feature (self-implemented, each time) in scratch. It always felt like a thing that I needed.
I was torn between strided and parallel list storage for the pieces of it. Strided was cool, and was more efficient with space and project list bloat, but in the end parallel lists were just SO much cleaner.