Skip to content
Sign upLog in
← Back to Community
🐍 I made my own version of Python in C! 🐍 + β“’
Profile icon
fuzzyastrocat

You know those "Python interpreters" which are really just eval(input()) (or exec(input()))? Well, I decided to make the polar opposite of that β€” a ground-up custom implementation of Python in C with nothing more than standard libc. 1191 semicolons and 2252 lines later, I've gotten my interpreter to a usable state.

A quick disclaimer: this isn't finished yet! I'm sharing it now since it's in a usable state, but it doesn't (yet) implement things like OOP, dictionaries or arrays. I'll share updates when I make them!

Changes

Of course, what fun would an exact remake be? To shake things up a bit, I've made some changes:

  • Function calls don't have parens. So function 1, 2, 3 is like function (1, 2, 3) in normal Python. This means that something like (int x + 1) is saying int (x + 1), so be careful with your parentheses! (If you intend that to not happen, do (int x) + 1.)
  • For a function call with no arguments, use () as the argument. (() is actually a synonym for None.)
  • Arguments are handled with my. This means that instead of doing:

def myfun(x, y, z):

You do: ```Python def myfun: my x my y my z <actions>

Now, what point does this have you ask? Well, you can put the my statement anywhere. Inside loops, conditionals, you name it. So you could do:

def myfun: i = 1 while i < 10: my x print "The #" + (str i) + " argument is: " + (str x) i = i + 1

Limitations

  • No for loops. For loops require iterators, which don't exist yet.
  • No % or +=, -=, etc.
  • No elif β€”Β use else & if, and no break/continue
  • No True or False β€” use 1 and 0
  • No float

So yeah, it's limited as of now, but it works!

Demo

Here's a demo (look in source.fpy in the repl for more):

def fib: my x if x < 3: return 1 return (fib x-1) + (fib x-2) def factorial: my x if x == 1: return x return x * (factorial x-1) userExiting = 0 while userExiting == 0: print "Welcome to the calculator! What would you like to do?\n\ 1 - Exit\n\ 2 - Calculate factorial\n\ 3 - Calculate fibonacci" userInput = input ">> " if userInput != "1" and userInput != "2" and userInput != "3": print "Invalid option! Try again." else: userInput = int userInput if userInput == 1: userExiting = 1 if userInput == 2: userInput = int (input "What number? ") print "Factorial of " + (str userInput) + " is " + (str (factorial userInput)) if userInput == 3: userInput = int (input "What number? ") print "The #" + (str userInput) + " fibonacci number is " + (str (fib userInput))

How can I get started?

Just fork the repl and edit source.fpy (a demo program is preloaded)! Hit run and you'll be good to go!

I hope you enjoy! There's probably numerous bugs (if you find one please report it!) but so far it's worked fairly well!

Note: It's been asked if the build folder was auto-generated. The answer is no β€”Β I wrote all the code in this project! I simply put code in the build folder to make the repl easier to navigate.
Voters
Profile icon
thelonecodist
Profile icon
ArnabSarkar
Profile icon
AbrahamJ
Profile icon
ANDREWVOSS
Profile icon
darkdarcool
Profile icon
vtu17496
Profile icon
chillcafe
Profile icon
Wilke000
Profile icon
MDClay
Profile icon
XCanG
Comments
hotnewtop
Profile icon
EpicGamer007

Bruh, how did u get so good. All your projects are frickin amazing

Profile icon
fuzzyastrocat

@EpicGamer007
Lol thanks, the answer is a lot of practice

Profile icon
EpicGamer007

@fuzzyastrocat
ok i see...

Profile icon
FlaminHotValdez

@EpicGamer007
666 upvotes...

Profile icon
EpicGamer007

@maxyang
have u seen coder, hes about to hit 10000

Profile icon
FlaminHotValdez

@EpicGamer007
Do you not know the significance of the number 666?!

Profile icon
EpicGamer007
Profile icon
EpicGamer007

@maxyang
oh wait i see... im not Christian so I wouldn't know, if you care that much gimme an upvote to make it 667(lol jk u dont have to).

Wait its 667 now XD

Profile icon
FlaminHotValdez

@EpicGamer007
its supposedly really unlucky

Profile icon
EpicGamer007

@maxyang
well its 667 now so ig im good

Profile icon
FlaminHotValdez

@EpicGamer007
yea, I'm not christian either but I've just heard it's unlucky

Profile icon
Coder100

wait hold up do you not know that

#pragma once

is to help make you unsad?

Profile icon
DynamicSquid

@Coder100
#pragam once isn't in the standard because of some weird file semantics. I think one problem is when files contain the same name but are in different directories, pragma can't tell which is which

Profile icon
fuzzyastrocat

@Coder100
I like doing it #ifndef thefile_h-style because it's standard and guaranteed to work. And it's really not that big of a difficulty compared to #pragma once.

Profile icon
Coder100

yeah it is determined by if your compiler supports it or not, but I do know the latest versions of GCC, Clang, and MSVC work.

@DynamicSquid

Profile icon
DynamicSquid

@Coder100
No, compilers supporting it aren't the issue, it's the problems related to #pragma

Profile icon
Coder100

oic

hmm well, ig

@DynamicSquid

Profile icon
programmeruser

@Coder100
#pragma once is useless, I only used it in the past since I was stupid and thought that I would have to do checks for each of the stdlib headers.

Profile icon
Coder100
Profile icon
Coder100

the only reason I am using it is because that was how I was taught

ig the ifndef is better idk

@programmeruser

Profile icon
xxpertHacker

@DynamicSquid
Na, it's literally just because it's not standard. They can tell the difference. Usually, pragma once is more performant than the alternative guards.

If it were standard, I doubt anyone would willingly not use it, unless you were trying to support an old compiler or a compiler that didn't support it.

Profile icon
Coder100

@xxpertHacker
well it's standard on gcc and msvc, so what more to say?

Profile icon
Coder100

and the only reason I would be using an older compiler is if I wanted to hurt myself lol

@xxpertHacker

Profile icon
xxpertHacker

@Coder100
No, it's not standard, it's suppported. That's the key difference.
The code won't work outside of the compilers that support it.

(btw, Rust don't have these problems)

Profile icon
xxpertHacker

@Coder100

and the only reason I would be using an older compiler is if I wanted to hurt myself lol

100% agree there, I would never use an older compiler than a C++20 compiler. I hate how Repl still has 2017 compilers.
I think I have a /feedback or /bugs report requesting an update; they didn't notice it.

(Legit the reason I dislike Repl, it gets outdated fast, and they don't update anything, ever)

Profile icon
DynamicSquid

@xxpertHacker
Wait but aren't many features of C++20 still experimental and not fully supported yet?

I also though that pragma has many problems with it.

Profile icon
xxpertHacker

@DynamicSquid
In a modern compiler, it's almost entirely supported, except for modules :(.

I recently checked out compiler support, just this week, but I don't remember where it is right now; I could probably look it up real fast.

As for pragma, it's just as I had said, it's simply non-standard, but supported.

Profile icon
DynamicSquid

@xxpertHacker
The 3rd, 4th, and 5th answer point on some bugs when using pragma though. Although the 4th answer suggests using both, but I think that's just too ugly. But yeah, I guess you're right, the bugs are supper rare.

:( C++ is adding too many features. I guess modules are nice, coroutines sound very interesting actually (heard they're really good for networking and server requests), but concepts? I don't really feel the need for more template metaprogramming. Aggregate initialization using () doesn't make and sense... and polymorphic allocator? What's that?

Profile icon
xxpertHacker

@DynamicSquid

Finally got back, I was checking out the GNU stdlib implementation, and I happened across their C++20 support.
https://gcc.gnu.org/projects/cxx-status.html
And the other compilers have good support too, but they didn't support modules yet, but some others already do.
https://en.cppreference.com/w/cpp/compiler_support/20

Profile icon
xxpertHacker

@DynamicSquid

But yeah, I guess you're right, the bugs are super rare.

Umm... I never suggested that?
I was saying that #pragma once is non-standard, but supported in certain compilers. That's it.
If you use it, just know that it might not work (as you expect it to), and that it might not be portable.

But among the compiler vendors who do support it, it may be more optimized than manually adding your own macro guards.

Concepts are similar to Rust traits; they provide constraints to generic code. I haven't gotten my hands on them yet though, but I'm not too sure I'll care until I have them.

Now coroutines and co_await, oh, now that is going to be fun.

polymorphic allocator

I frankly have no clue what that could mean, but then again, I'm not about to try to disambiguate it either.
I have a feeling that it allows for more performant code in specific situations since that's why plenty of ad-hoc allocators are used, but maybe not.

If modules are as good as they claim to be, I'll come back to C++. (I really hope they are)

Wait no, I need Repl.it to update their dang compiler!

We're almost in 2021, why am I forced to use a buggy 2017 compiler?

Profile icon
DynamicSquid

@xxpertHacker
Oh that's interesting. And lol, 2017 isn't that outdated. The only C++17 feature I have every used in a major project was std::variant. I guess I'm more of a minimalist and only use features if I have to. I probably should start doing what you're doing though and use some of the newer features.

Profile icon
xxpertHacker

@DynamicSquid
Honestly, I don't even know what was added in C++17, I only realize that it's missing when I downgrade to an older compiler.

But, I have wanted modules forever.

Profile icon
xxpertHacker

@DynamicSquid
Just checked https://en.wikipedia.org/wiki/C%2B%2B17

I've used over half of what C++17 offers.

Allow typename (as an alternative to class) in a template template parameter

I only use typename, never class.

Nested namespace definitions, e.g., namespace X::Y { … } instead of namespace X { namespace Y { … } }

Used it earlier this month.

New standard attributes [[fallthrough]], [[maybe_unused]] and [[nodiscard]]

Ha, I think you saw me use one.

UTF-8 (u8) character literals[14][17] (UTF-8 string literals have existed since C++11; C++17 adds the corresponding character literals for consistency, though as they are restricted to a single byte they can only store ASCII)

I tried it out, it was pretty lame :)

Hexadecimal floating-point literals

I think C++ is the only language that I've used that allows that, I'ma put that to use soon.

Use of auto as the type for a non-type template parameter

Check.

Constant evaluation for all non-type template arguments

Check.

Fold expressions, for variadic templates

Probably used it... somewhere?

A compile-time static if with the form if constexpr(expression)

Check.

Structured binding declarations, allowing auto [a, b] = getTwoReturnValues();

Caused what I belive to be undefined behavior in Clang-7 with that ^, still liked it.

Initializers in if and switch statements

I love this feature right here ^

Class template argument deduction (CTAD), introducing constructor deduction guides, eg. allowing std::pair(5.0, false) instead of requiring explicit constructor arguments types std::pair<double, bool>(5.0, false) or an additional helper template function std::make_pair(5.0, false)

Used a deduction guide this year, I think I used more than one.
Actually, that reminds me... I have to go help someone out with this.

Inline variables, which allows the definition of variables in header files without violating the one definition rule. The rules are effectively the same as inline functions
__has_include, allowing the availability of a header to be checked by preprocessor directives

Check.

Exception specifications were made part of the function type

Check.

std::string_view

Check. Oh yeah, they allow creating 0 allocation slices too!

Okay, I have used way more than even what has been listed so far, but still.
Generally, I tend to prefer bleeding-edge technology.
Give me a new compiler with new features, I'm going to try everything out, what's good, I'll use, what's useless, I'll discard, what I don't understand yet, or don't have need for yet, I'll reserve for later.

Profile icon
DynamicSquid

@xxpertHacker
Oh wow, I actually used more than I though from that list!

Allow typename (as an alternative to class) in a template template parameter

Yay! They finally fixed that! Now I don't have to worry about the class keyword in templates

New standard attributes [[fallthrough]], [[maybe_unused]] and [[nodiscard]]

Not really a big fan of attributes :( I feel like good code doesn't need these. Comments are better. But I guess if you can't suppress compiler warnings than these could be pretty useful.

UTF-8 (u8) character literals[14] (UTF-8 string literals have existed since C++11; C++17 adds the corresponding character literals for consistency, though as they are restricted to a single byte they can only store ASCII)

I think I used that when I was making some stuff with console graphics before

Fold expressions, for variadic templates

Okay now that just gets confusing

Initializers in if and switch statements; A compile-time static if with the form if constexpr(expression)

And I thought if statements were as simple as it can get lol

Class template argument deduction (CTAD), introducing constructor deduction guides, eg. allowing std::pair(5.0, false) instead of requiring explicit constructor arguments types std::pair<double, bool>(5.0, false) or an additional helper template function std::make_pair(5.0, false)

I used that a lot without even realizing it. Java has this feature so I guess I got used to it

I bet you C++50 will look like this:

std::make_language(); std::make_website(); std::make_google(); std::make_falcon_9_rocket();
Profile icon
xxpertHacker

@DynamicSquid
Na, C++ will die eventually. (It better)

Ohh, you know what I haven't done? constexpr if + initialization in if.

constexpr if (constexpr bool x = false; x);

That sounds both, terrible, and good, at the same time.

That could be abused so badly.

Now, as for attributes, they're not for other programmers, they're for giving extra meta info to the compiler.

You can have 100 comments telling other people how the code should work, but not one of them will be read by the compiler at all.
Attributes help generate optimal code, without creating a whole new syntax within the language.

Go lookup C++20's likely and unlikely attributes, and you'll understand their purpose better.

Profile icon
DynamicSquid

@xxpertHacker
Oh good point about the compiler. Also yeah, C++ will die soon. Really sucks cause I can't seem to find an imperative statically typed language with the low-levelness of C, but also with the medium-levelness of templates, OOP, etc.

I tried Rust (made a language in it actually) but didn't like the whole memory transfer thing.

Crystal seems really good (I saw the website and instantly got hooked) but I haven't tried it yet. But this weekend actually I might create a language with it.

Welp, I guess I have to make my own, or you have any suggestions?

Profile icon
xxpertHacker

@DynamicSquid
If you can make your own language, then, most likely, it will have what you want, and what you value, so you should be the most pleased individual with your own language, as you made it for yourself.

But if you can't, then you'll have to keep searching.

(I can't make my own lang, langdev is hard, and I suck at naming stuff)

Btw I checked out Mirror's source some time ago.

Profile icon
DynamicSquid

@xxpertHacker
Yeah, I'm really into language dev. Currently working on Night, but it's interpreted and dynamically typed. Once I get that to a good point, I want to start work on a compiled statically typed language. (thinking of naming it Syntax, is that a good name?)

What are you currently working on?

Profile icon
xxpertHacker

@DynamicSquid
I'm bad with naming stuff, so I'd have no clue.

Right now, I'm helping update a text editor to properly support encodings: https://encoding-pr.xxperthacker.repl.co.

I posted it in "share," but I think you were the only one to upvote it.

It was okay until today, when I realized something terrible was happening.

It reads a file correctly... but mauls the encoding when writing.
So, if you use it, it just mauls your file system, one file at a time, without you even realizing it :).

There are also some edge cases that cause data loss.

I need to fix it soon.

Beyond that, I was thinking of getting back into language development, but I still don't know how the fundamentals would work, I still need an answer to this Q: Why do references exist? Should we only have pass-by-value?.

And I don't want to use C++ till I understand how this went wrong, lol.

Profile icon
DynamicSquid

@xxpertHacker
There's some good answers here. Also you should try Java. Java's all pass by value. Also all the problems with pointers you're having won't (in theory) be a problem in Java

Profile icon
xxpertHacker

@DynamicSquid
Eh, that question asks "pointer vs reference," I'm looking at "pass by value vs pass by reference," subtle difference.

Also you should try Java. Java's all pass by value.

Java is also a garbage-collected language. (the language wouldn't need a GC if the language weren't garbage)

Also all the problems with pointers you're having won't be a problem in Java

Oh, I wouldn't be getting segfaults, I'd just be getting range errors for accessing out of bounds data, really, it's no different, the logic error remains

Profile icon
DynamicSquid

@xxpertHacker
> the language wouldn't need a GC if the language weren't garbage

Absolutely agree lol.

Hmm... I'm not sure about your problem. Have you tried using asserts?

Profile icon
Coder100

garbage collection go brr

@DynamicSquid

Profile icon
fuzzyastrocat

@xxpertHacker
@DynamicSquid

the language wouldn't need a GC if the language weren't garbage

LOL so true

And yes, I hope C++ dies soon. It feels really... bloated. They're trying to add too much and it's just getting messy.

As for Concepts, those would actually be kinda cool, but I don't know how much practical value they have. As xxpertHacker said, they're like Rust's traits or Haskell's typeclasses, but I feel like C++ doesn't really need that.

Profile icon
xxpertHacker

@DynamicSquid

@fuzzyastrocat

I'm honestly surprised that neither of you hated on me for being too harsh towards Java, but okay, cool, we're all on the same page.

C++ is a pretty neat language, but it's weighing itself down, this is why I fully advocate using Rust instead, it's a might lighter-weight language, built in the modern-day and age, for the modern-day and age, today, not 30-40 years ago.

I wish someone could just cast off what C remains in C++, but instead it'll stay :(.

I want the same thing for all languages that have survived.
Why use Python 2 when you have Python 3? Python 3 made backwards incompatible changes, and I like that.

I hate seeing how JavaScript still has so much old stuff, just remove the old parts and it's actually a great scripting language, like seriously, why are there still both, var and let!? One of them needs to go, and I'm all for removing the older one.

If C++ actually chose to do the same, it wouldn't be as bad, but it would require a major overhaul.

Eventually the same may happen to all great languages (*cough, Rust), and that'll be a sad day indeed.

Profile icon
DynamicSquid

@xxpertHacker
Which parts of C++ need to be removed in your opinion?

Profile icon
xxpertHacker

@DynamicSquid
It would take at least a year to give a well thought out decision on that, C++ has a lot.
And then there's the stdlib, that's even larger.

And I doubt that I could do it without introducing my own bias' either.

Profile icon
DynamicSquid

@xxpertHacker
Fair enough lol

Profile icon
fuzzyastrocat

@xxpertHacker
I agree with you about Rust vs C++ β€” if I had to choose which one of those to use, I'd never pick C++.

(Though your anecdote about var and let β€” those two keywords serve different purposes, and I use both purposes often.)

Profile icon
xxpertHacker

@fuzzyastrocat
var & let are both used to declare reassignamble variables.
Let enforces lexical scoping, var doesn't, and it's hoisted.

Var is deprecated, you should never use it unless you're working with a transpiler+minifier.

Why do you use it?

Profile icon
fuzzyastrocat

@xxpertHacker
The use cases are rare, but I find it useful. Consider this:

function do_some_stuffs(){ do { var my_var = ...; // some code involving my_var } while (/* some condition involving my_var */); }

I know it's inside a function, so var doesn't leak out of scope. And yes, I could do it with let, but that would mean an extra line of code and now when I'm re-reading it I'm wondering where I assign to my_var (since this example is small that might not be apparent, but in larger code examples it makes more sense).

Profile icon
xxpertHacker

@fuzzyastrocat

function do_some_stuffs() { do { var my_var = ...; // some code involving my_var } while (/* some condition involving my_var */); return my_var; // edited in }

The way I read this, when I get to the return, I would check the variables outside of the loop's scope, and if there aren't any, then I'd check the arguments. If there aren't any, then I would scan the entire function and global scope for this variable that appears from nowhere.
This only happens to those who are used to let/const though.

But in reality, recursion is the proper way to loop over anything.

Profile icon
fuzzyastrocat

@xxpertHacker
Ah yes, but I deliberately did not include a return in that example. If my_var is included in anything outside the loop then yes, I agree that it should be declared with let.

(And yes, FP is best)

Profile icon
xxpertHacker

@fuzzyastrocat
Maybe its bias, but

(And yes, FP is best)

And... congrats on 1k cycles.

Profile icon
fuzzyastrocat

@xxpertHacker
I'm a big FP fan.

(Oh wow thanks, didn't realize I had reached it exactly!)

Profile icon
xxpertHacker

@fuzzyastrocat
I gave you the upvote to reach 1k, solely because of the comment supporting FP. :)

Btw, I'm thinking of allowing features that somewhat stand in between normal imperative, mutable code, and FP.

Think of a simple for loop, they're often used to reassign variables with every iteration, until a specific value is reached, or a condition is met.
Recursion does the exact same thing, but instead of reassignment, it passes the current iteration's values back to itself.

I'm thinking of something else, how about, instead of recursion, which is inefficient (without optimization), or loops reading and writing to registers (which is against FP, and could be inefficient), the author just pushes and pops values off of the stack, repeatedly, until a condition is met.

It would act like inline recursion, acting somewhere in between both of the opposing ideas.

I'm thinking that it would require some sort of variables, I'm probably going to call them "loop-locals."

An example, with a not planned out syntax:

// on entering the loop, the loop-locals are assigned these values let sum = loop(i = 0, counter = 1) { if (i < 10) { continue(i + 1, counter * 10); // leaves both values on the stack } else { break(null, counter); // leaves only counter on the stack } }

When the bottom of the loop is reached, a goto/jmp is done, moving control flow back to the top, but leaving the values on the stack.

But the problem: that syntax is god-awful, any suggestions?

Does this sound like a good idea to implement in a human-readable language? (this sounds more like Asm tbh)

If done right, this would be very expression-orientated, more so than Rust's loops.

Profile icon
fuzzyastrocat

@xxpertHacker
About your recursion alternative β€”Β yes, that's interesting. It'd have to be explored more but it could be interesting.

Profile icon
RohilPatel

But python already exists! Lol!

Profile icon
fuzzyastrocat

@RohilPatel

Of course, what fun would an exact remake be? To shake things up a bit, I've made some changes:

Profile icon
OldWizard209

This is GREAT NGL

@fuzzyastrocat
:)

Profile icon
fuzzyastrocat

@abdullahrajput9
Thank you! I'll be continuing to work on it and give it more features! Eventually it'll be just like a real version of Python! (with my own twists of course :D)

Profile icon
OldWizard209

That would be rilly cool

@fuzzyastrocat
I would love to see it

Profile icon
RayhanADev

@fuzzyastrocat
Ayyyy congrats you made it into the newsletter!

Profile icon
fuzzyastrocat
Profile icon
DynamicSquid