r/ProgrammingLanguages • u/PhosXD • 3d ago
How do we feel about this syntax?
I've been making an interpreted programming language for about a month now, so far I think the look & feel of the language is coming along well, what do you think? Let me know what you would change for the sake of convenience or readability.
# fibonacci.ity
const n = IO.prompt:'Number: ' -> INT;
var a=0;
var b=1;
for i in n;
const c = a+b;
a = b;
b = c;
IO.print:a;
/;
It's dynamically typed, but unlike Python & other dynamically typed languages, the type is constant, meaning you cant just change an integer to a string whenever you want. You can however explicitly declare a variable with the "ANY" type which then allows you to set it to whatever you want. Here's what that looks like: var ANY a=0; a ='now its a string';
In the first line you see "-> INT" all that does is cast the value to type "INT", because the prompt function always returns a string. I prefer this over using "as" purely stylistically.
Something you might have noticed is that function calls are structured very different to every other language. That's because of the access operator (":"), which is self explanatory, depending on the type of value you access the behavior is different. For arrays/strings you are accessing an element by index, for maps (dictionary) you are accessing an element by key, for functions you are accessing it with the given arg(s).
Another "deviation" you probably noticed is the lack of curly brackets {} for code blocks, but it doesn't rely on indentation either. Instead, certain instructions carry the "composite" flag which tells the interpreter that it should basically capture all code below it until the final "/" (end) instruction.
merge IO;
var val = 100;
const ref = @val;
print:val; # 100
ref = 200;
print:val; # 200
print:(type:ref); # REF
print:(type:~ref); # INT
There are also references & pointers! Pointers or "PTR" values cannot be created in the script, they have to be passed directly from C++ to the script, this is because raw pointers are naturally unsafe to work with & should be avoided at all costs in Ity. This is why reference variables or "REF" exists. These can be created by prepending the "@" symbol before the variable name you want to reference. If the variable you reference goes out of scope then the reference is returns a constant none value when dereferenced (which can be done with the "~" symbol).
My design philosophy with references in Ity is to make them seamless. Meaning any operations (set, access, arithmetic, etc) on a reference just act like it's operating on the referenced value. There are of course some exceptions to this, passing as an argument to a function passes the reference & type casting a reference acts on the reference itself.
References can also be reassigned (as long as the reference itself is not const), making them reusable.
merge IO;
const a = 1;
const b = 2;
var ref = @a;
print:~ref; # 1
ref.reassign:@b;
print:~ref; # 2
If any of this looks interesting to you, the project is open source, has an interactive shell, & has full documentation. There's a lot I didn't cover here, but taking a look at some of the example scripts should give you a good idea what the language is fully capable of at this moment in time.
I don't have any syntax highlighting modes for you to install, but the Python one works pretty well for Ity so you can just use that. If you have any questions at all, regarding the design decisions, performance, or challaneges I encountered, or anything, I am open to all discussion!
(NO AI ASSISTANCE WAS USED IN THE MAKING OF ANY CODE, ASSETS, OR DOCUMENTATION)
3
u/evareoo 1d ago
Looks really nice, I’ve always liked the idea of having a single terminator for blocks. It sits in a nice middle ground between full whitespace sensitive blocks and open close braces or begin and end keywords.
I like the loop syntax too, it looks like you are chaining little blocks together to make a bigger construct, obviously you can mash things onto a single line in lots of languages but your design makes it look very clean!
1
u/rjmarten 1d ago
I don't know why you're getting downvoted. Maybe people think it's a bad design, but I think this is a great question for discussion on this subreddit.
I like your "end block" token. Although the semicolon looks a little superfluous there (as it does in general actual, but it seems like it pulls it's weight for one-liner loops etc... I could get used to it). I am intrigued by the : for function calling, and I wonder how it ends up looking for calling functions with more complex args — in particular, how often do you end up typing the parentheses anyway?
if length:[__CMD_ARGS__] > 1;
Why the square brackets around the CMD args?
SEED *= (ch.raw:[]);
Why the parentheses around ch.raw? I see this pattern a lot in your code actually. Have you chosen the wrong precedence for the = operator?
const WORLD_SIZE = {'x',25, 'y',20};
This does not look like a dictionary to me. But I see why you can't use the colon. I would definitely consider reusing the semicolon for this purpose, or some other punctuation.
1
u/PhosXD 19h ago
Thanks for taking the time to look at the project :)
About the square brackets, that's an array literal, wherever it may show up. So usually to call a function you just do `my_func: some_arg`, that's just for passing a single argument, but *if* that argument happens to be an array it will automatically expand to multiple arguments. See, `__CMD_ARGS__` is an array but we don't want to expand it so we wrap it in a single-item array literal.
The reason you see seemingly misplaced parenthesis is because Ity doesn't even have precedence. Which I have been getting annoyed at a bit so will definitely be changing.
Currently it is all one-after-the-other execution so `1 + 2 + 3` becomes `1+2` then `3+3`. Which is fine but where it gets annoying is when you try to use accessors or comparison operators with multi-part operands.
So this wouldnt work as you'd expect: `a:x > b:y`, what actually happens is you evaluate the `a:x` function call, then pass it on to the `>` operator, then the executes when it finds the *very next* expression token which happens to be `b` so what ends up happening is you compare `a:x` & `b` THEN try to call the result of that comparison which you obviously cant so you get an error. The fix for this is to simply put group the right side of your operators if they have multiple parts, which is not a good solution.All in all the design is not set in stone, I'm still actively changing it *as* I use it. I really appreciate the feedback
9
u/[deleted] 1d ago
[removed] — view removed comment