Share your repls and programming experiences

← Back to all posts
🐍 I made my own version of Python in C! 🐍 + ⓒ
fuzzyastrocat (1297)

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):
      <actions>
    You do:
    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.
Commentshotnewtop
EpicGamer007 (801)

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

fuzzyastrocat (1297)

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

EpicGamer007 (801)

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

maxyang (139)

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

EpicGamer007 (801)

@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

maxyang (139)

@EpicGamer007 its supposedly really unlucky

EpicGamer007 (801)

@maxyang well its 667 now so ig im good

maxyang (139)

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

Coder100 (11140)

wait hold up do you not know that

#pragma once

is to help make you unsad?

DynamicSquid (4398)

@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

fuzzyastrocat (1297)

@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.

Coder100 (11140)

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

DynamicSquid (4398)

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

programmeruser (148)

@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.

Coder100 (11140)

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

ig the ifndef is better idk @programmeruser

xxpertHacker (555)

@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.

Coder100 (11140)

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

Coder100 (11140)

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

xxpertHacker (555)

@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)

xxpertHacker (555)

@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)

DynamicSquid (4398)

@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.

xxpertHacker (555)

@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.

DynamicSquid (4398)

@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?

xxpertHacker (555)

@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

xxpertHacker (555)

@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?

DynamicSquid (4398)

@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.

xxpertHacker (555)

@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.

xxpertHacker (555)

@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] (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.

DynamicSquid (4398)

@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();
xxpertHacker (555)

@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.

DynamicSquid (4398)

@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?

xxpertHacker (555)

@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.

DynamicSquid (4398)

@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?

xxpertHacker (555)

@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.

DynamicSquid (4398)

@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

xxpertHacker (555)

@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

DynamicSquid (4398)

@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?

Coder100 (11140)

garbage collection go brr @DynamicSquid

fuzzyastrocat (1297)

@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.

xxpertHacker (555)

@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.

DynamicSquid (4398)

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

xxpertHacker (555)

@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.

fuzzyastrocat (1297)

@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.)

xxpertHacker (555)

@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?

fuzzyastrocat (1297)

@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).

xxpertHacker (555)

@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.

fuzzyastrocat (1297)

@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)

xxpertHacker (555)

@fuzzyastrocat Maybe its bias, but

(And yes, FP is best)

And... congrats on 1k cycles.

fuzzyastrocat (1297)

@xxpertHacker I'm a big FP fan.

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

xxpertHacker (555)

@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.

fuzzyastrocat (1297)

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

RohilPatel (1428)

But python already exists! Lol!

fuzzyastrocat (1297)

@RohilPatel

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

RayhanADev (1002)

@fuzzyastrocat Ayyyy congrats you made it into the newsletter!

DynamicSquid (4398)

Ooh yay this was featured in the newsletter

hello4691 (32)

You could use exec() in python. It's a much better interpreter than eval().

fuzzyastrocat (1297)

@hello4691 sigh That's not the point. The point is that either option is a very low-effort "interpreter" which is really just a builtin metaprogramming feature of Python.

[deleted]

How long did it take u to make this awesome project?

fuzzyastrocat (1297)

@ilovebeef Took about a week, probably on average 2 hrs a day.

[deleted]

so about 14 [email protected]

fuzzyastrocat (1297)

@ilovebeef Uh maybe, I mean I'm not really sure since I never time my projects.

[deleted]

well, think about it there are 7 days in a week, and you worked on it 2 hours per day so that means the total amount would be 14 hours since you said maybe that would mean that it is about 14 hours of work. I am Big [email protected]

fuzzyastrocat (1297)

@ilovebeef No, I'm not doubting that what you said based on what I said is correct, I'm just saying that what I said might not have been correct. Ie, your calculation might be based on incorrect data.

[deleted]

what does le mean @fuzzyastrocat

[deleted]

so I'm correct if the numbers of days and hours you said were right but if the numbers of days and hours you said were wrong then I'm wrong [email protected]

fuzzyastrocat (1297)

@ilovebeef Ie = I.e. It's an abbreviation for some latin, basically means "as an example" or "in other words".

As for your second question, that's right. I doubt my numbers are correct since I don't time how long I work on things.

[deleted]

ok
OK is an English word denoting approval, acceptance, agreement, assent, acknowledgment, or a sign of indifference. OK is frequently used as a loanword in other languages. It has been described as the most frequently spoken or written word on the planet. The origins of the word are disputed.
@fuzzyastrocat

Code1Tech (88)

I'm just curious, is the "build" folder auto generated or did you write all of that?

Code1Tech (88)

Oh wow!
That is a lot of files.
Yeah, I would never be able to write that much.
@fuzzyastrocat

elburg (13)
╔══════[MSG_BOX]═══════[X]═╗
║                          ║
║   Does this look cool?   ║
║    [Yes]      [Yes]      ║
╚══════════════════════════╝
fuzzyastrocat (1297)

@elburg Uh... not sure how this is related to the post?

elburg (13)

@fuzzyastrocat i'm saying that the project is cool!

fuzzyastrocat (1297)

@elburg Ohh, by "does this look cool" I thought you meant that message box. Thanks! :D

dabombdgdzjr (120)

The second I saw this I lit just made a python repl that can make files mostly in those boxes. Its like a mini os sim @elburg

elburg (13)

@dabombdgdzjr cool! fun fact: these boxes were mainly used in Graphical UI applications in the old days of DOS

dabombdgdzjr (120)

Nice, Ive never used dos tho. Only had a laptop, which I dont have, and now I have a chromebook :( rip @elburg

DynamicSquid (4398)

Fuzzy, quick question about lang dev.

How am I supposed to detect negatives?

-3 # this is a negative 3
4 - 3 # this is a positive 3

I tried to detect them in the lexer but that didn't work. Do I have to do a linear scan of the expression and manually detect whether or not a number is negative or not?

fuzzyastrocat (1297)

@DynamicSquid There is no such thing as a negative number. - is a unary prefix operation — negation. So - 3 should parse as UnaryOp{-}(NumLiteral{3}). It's also a binary infix operation — subtraction. That's why 4 - 3 parses as BinaryOp{-}(NumLiteral{4}, NumLiteral{3}).

DynamicSquid (4398)

@fuzzyastrocat I was thinking of that, but how can I tell the difference between a unary - and a binary -?

fuzzyastrocat (1297)

@DynamicSquid If you're using a pratt parser, it should be easy — just add the unary parser function under the first column of the parse rules for - (I think, if not use whichever column is for unary parsing), add the binary parser function under the second (or whatever binary is) column of the parser rules for -.

If you're not, I can give you instructions for that too.

DynamicSquid (4398)

@fuzzyastrocat yay! I just finished the parser and it can evaluate expressions just fine now! I also cut down 87 lines and made the code super clean

I actually don't know if the math or boolean expressions produces the correct result since it's too complicated but it looks right

I now need to do type checking, and then the parser should be done, and then it's time to test the Interpreter

fuzzyastrocat (1297)

@DynamicSquid Imagine not having custom syntax highlighting set up :)

But yey, that's good that it's cleaner. Usually as clean as possible is good.

Lemme see here... -3 + -4 = -7, -that = 7, 7 * 5 = 35, 35 - 1 * 5 = 30, -30 / 10 = -3. Good on that!

!(blah) || true = true, so that works too.

Yay good job!

DynamicSquid (4398)

@fuzzyastrocat Hmm... custom syntax highlighting... never though of that before. I guess I could create a VSCode extension, not sure how I'll do it for Visual Studio though

fuzzyastrocat (1297)

@DynamicSquid If only you used Atom, it'd be a snap... I really like Atom's open-sourcey-ness

DynamicSquid (4398)

@fuzzyastrocat true yeah, but I've been using visual studio since day one so I don't think I can stop using it :)

DynamicSquid (4398)

@fuzzyastrocat Hmm... should I be lazy like Python and have input() automatically return a string so I don't have to type check it, or should I actually put in the effort to automatically turn input() into the correct type depending on the surrounding expression?

fuzzyastrocat (1297)

@DynamicSquid Depends entirely on the design of the language. If functions can have type parameters then input<int>() would be best. If your language is statically typed then inferring the surrounding expression is best. If your language is dynamically typed (which it is), then that's tricky — what should this be?

x = "hi"
if <condition>:
  x = 1

y = x + input()

If the condition is true, you'd want the input to be an int. If not, you'd want it to be a string. This means you'd have to build in automatic runtime type coercion, which does a few things:

  • It adds a lot of runtime overhead/complexity where it might not be needed (ie, if the input just needs to be a string anyway it has to do the runtime stuff. This could be mostly optimized but it's still a factor.)
  • It makes the language very weakly typed.
  • It may cause weirdness to happen when the auto-inference system does something you didn't expect it to. (For instance, if you can do num + num and num + str, what would num + input() do?)

So while this is a possibility, it'd have to be explored a lot and refined. If you don't think that's a good idea, then I'd definitely just have it return a string and then force the user to coerce type.

(And don't think that's a "cop-out" in any way — it's a useful behavior to have.)

DynamicSquid (4398)

@fuzzyastrocat Yup you've just convinced me that I need to return a string instead :)

fuzzyastrocat (1297)

@DynamicSquid Or you could go with the "type is a function which casts" thing and then have input take a callback parameter: input("Give me a number: ", int). Then input calls the callback function on the input string.

DynamicSquid (4398)

@fuzzyastrocat Oh that's interesting... good idea!

HarperframeInc (391)

TECHINCALLY You could make a python interpreter in python (have python lex and parse, then run functions based on that)

fuzzyastrocat (1297)

@HarperframeInc Sure, you can, but it'll be pretty bad. You can do langdev in any language, but if you make it in an interpreted language it will be slow, clunky, and you'll never be able to generate an executable for it.

DynamicSquid (4398)

Fuzzy, quick question on lang dev.

When making an AST for an expression, should I include brackets in there? Cause I'm having a little trouble with unary operators at the moment, and I think if I include brackets instead of treating them as an "abstract concept signaling higher operator precedence", then it might be a little easier.

fuzzyastrocat (1297)

@DynamicSquid By brackets I assume you mean parentheses. Here's the thing — parens don't have any meaning! They don't! They shouldn't be represented as an ast node. Here's why: ((((((1+1)))))) is the same as 1+1. So their AST should be the same. Basically, parentheses should only come into play in the parser. They should have no representation in the AST.

And for some reason I keep talking in little short sentences. Lots of short sentences. I dunno why. So many short sentences.

DynamicSquid (4398)

@fuzzyastrocat Yeah, that's what I'm doing right now. I guess I'll have to get crafty with unary operators and brackets.

Sorry for the late reply. Just realized I wrote 2000+ lines for Night v4 and I didn't even test it once.

Edit: print("Hello World!\n") works perfectly fine! only a couple hundred more features to test I want to drown

fuzzyastrocat (1297)

@DynamicSquid Not sure what you mean by "get crafty" since it shouldn't be much different from everything else.

Yeah, I usually add things feature-by-feature and then keep a "running test file" of each feature I add — that way I know if a new feature breaks a previously-added one. (source.fpy is a good example of this)

And you can't drown, you're a squid :D

DynamicSquid (4398)

@fuzzyastrocat Well, I'm having a little trouble with my parser when it comes to unary operators and brackets. Since I'm trying the pratt parser you suggested but I'm coming across some subtle bugs. But it's looking really good so far and I think I have it almost figured out

realTronsi (782)

@fuzzyastrocat yey now I upvote your reply so now no one else can cherish your accomplishment >:D

DynamicSquid (4398)

Wait, I've been thinking, why doesn't Python type check at compile time? Is there a downside to doing so?

fuzzyastrocat (1297)

@DynamicSquid There is no downside. It's just very difficult to do so — for instance, how would you typecheck this?

my_function(123 if <some condition> else "A string")

(Now, the solution is to consider the type being passed to the function the union of the types str and int. But evidently they haven't thought of that over at Python or they don't have the desire to implement it.)
Here's a more difficult example, but similar:

x = 1
if <cond 1>:
  x = "hi"
if <cond 2>:
  x = 12.3

my_fun(x)

x is now the union of int, str, and float. It's possible to determine that at compile time, but as you can see it takes some work because this example is obviously oversimplified.

DynamicSquid (4398)

@fuzzyastrocat Oh okay. I feel like that's actually a good thing for me because Night is practically Python with some very minor changes. So anything that can differentiate the two is good :)

fuzzyastrocat (1297)

@DynamicSquid Given that Python is beginning to take the route of gradual typing (optional static typing when the programmer so declares), I doubt they'll change that decision either.

xxpertHacker (555)

Bugs:
Floating-point literals cause lexing errors.
print(1.0)

Unknown token "."

Incorrect arithmetic.
print((1 / 0) / (1 / 0))

-nan

(should be +nan)

No semicolons.
print(0);

Unknown token ";"

Missing flooring division operator.
print(2 // 3)

Error at "/": Unexpected token!

No tuples.
print((0, 0))

Error at ",": Unexpected token!

Bitwise operators all cause lexing errors.
print(1 << 3)

Error at "<": Unexpected token!

No divmod.
print(divmod(1 3))

TypeError: Attempting to call non-callable!

Questions:
Why no % operator?

fuzzyastrocat (1297)

@xxpertHacker

  • I explicitly said no floats in the main post
  • Not sure about that, I'm using C's arithmetic on doubles so that's what I get
  • I'm not adding semicolons, I don't want them
  • Those aren't added yet either, I figured they would be covered in %, +=, -= etc.
  • Not added yet either.
  • Not added yet either.
  • No builtins like that have been added yet. (Also that's not how you call a function, which is why you're getting the "attempting to call non-callable", it's called like print (divmod 1, 3))

Answer:

  • Not implemented yet.
xxpertHacker (555)

@fuzzyastrocat

No True or False — use 1 and 0, and no float

Ahh, put that on it's own line, I totally didn't notice that.

I originally did

print(divmod(1, 3))

But it said error on token ,, so I tried that and it said non-callable, but okay.

fuzzyastrocat (1297)

@xxpertHacker That's because divmod (1, 3) would be passing the function divmod a single tuple, (1, 3), while divmod 1, 3 is passing the function divmod two integers, 1 and 3.

xxpertHacker (555)

@fuzzyastrocat Ohh, they're not optional, they're removed entirely, got it.

fuzzyastrocat (1297)

@xxpertHacker

Function calls don't have parens.

Because when tuples are added there'd be an ambiguity — is fun (1, 2) just calling with two ints or one tuple?

Maxscharlau (0)

it isn't working ha ha ha )(

DynamicSquid (4398)

Hey Fuzzy, I have a quick question. So remember when you taught me about the difference between values and tokens? Yeah, so I came across a slight issue:

struct Value
{
  std::string value; // what do I name this?
};

So the string that contains the actual value is called value, but that is very confuzzling.

Value value;
value.value = "5"; // value.value ???

So do you have any ideas of what to call the variable?

xxpertHacker (555)

@DynamicSquid That's when you realize you overused OOP. Too many structs.

Maybe this?

struct Value {
    std::string_view data;
};
DynamicSquid (4398)

@xxpertHacker But I didn't use OOP? That's just a struct to store data. No methods or any of that OOP stuff. I could be wrong, but I guess it's how you define OOP.

And yeah, I actually like data. Don't know why I didn't think of that before. I'll probably use that, thanks!

Also what's the difference between string_view and string? I've heard it was a new C++17 feature, but never had time to look into it

xxpertHacker (555)

@DynamicSquid I had presumed that you were using more OOP in the rest of the project.

That's just a struct to store data.

That's exactly why I thought of it.

string_view doesn't allocate any data, while providing most of the std::string methods.

It can be used for constexpr stuff too.

constexpr char s = "test"sv[2];
DynamicSquid (4398)

@xxpertHacker I just read up on string_view, it says to only use them for read-only strings, but I'll be doing a lot of modifying

xxpertHacker (555)

@DynamicSquid Oh, I tend to work with const/read-only data often. But I understand if you don't.

fuzzyastrocat (1297)

@DynamicSquid Are all values in your language strings? I have a feeling it isn't Bash, in which case the answer is no. So, if not all values are strings don't store them as strings. Check out value.h of this project and how I store values.

DynamicSquid (4398)

@fuzzyastrocat Hmm... I store all values as strings, and then just convert them into their respective types as needed, but I guess that's not the best way to do it?

HahaYes (1250)

Ahem why not glorious c++

fuzzyastrocat (1297)

@HahaYes Well, here's a few reasons:

  • I like C better. I don't have to deal with C++'s STL and needless features to make something I can do myself with C's tools.
  • C is more portable (in general).
  • CPython is written in C.
  • C is the goto (pun intended) language for interpreter implementation.
  • C is faster in certain cases (this isn't a major motivator for this project, but still something to be aware of)
RahulChoubey1 (62)

Custom file extensions?

wow

me noob

repl.it cool in that way

all the way to h6

idk what to put here
noiceness
RahulChoubey1 (62)

@fuzzyastrocat they should add this to repl.it

fuzzyastrocat (1297)

@RahulChoubey1 Lol, I wish they would, but there'd have to be a lot of support for it to be added

JBYT27 (530)

NOICE!

almost on your way to 1000 cycles!

LucasAllori (33)

This is great! Much better than my language.

Whippingdot (65)

Got a bug your program got scared of the number I have inputted and peed in its pants while running away

fuzzyastrocat (1297)

@Whippingdot Heh, you probably maxed out the double limit.

firefish (794)

@fuzzyastrocat how could you have maxed out a 308 digit number (looking at the photo your probably have)

fuzzyastrocat (1297)

@RahulChoubey1 How big of a number did you input?

RahulChoubey1 (62)

@fuzzyastrocat I just smashed my keyboard, maybe… around 10 or 12 characters? 324567898765 sure fails

fuzzyastrocat (1297)

@firefish It's calculating the factorial — it might be overflowing the stack, but if not then that factorial number is clearly over the double limit.

RahulChoubey1 (62)

@fuzzyastrocat also, looking for just the 100th Fibonacci number murders the code

fuzzyastrocat (1297)

@RahulChoubey1 Think about what you're doing there — calculating 324567898765 factorial. That's a massive number, so it's probably either maxing out the stack capacity or going above the double limit if not.

( @firefish) Additionally, about the 100th fibonacci number — try this in a python repl:

def fib(x):
  if x < 3:
    return 1
  return fib(x-1) + fib(x-2)
 
print(fib(100))

You'll notice it does the same thing.

fuzzyastrocat (1297)

@RahulChoubey1 @firefish I've added friendly callstack errors. You'll now see that your factorial example maxes out the callstack.

fuzzyastrocat (1297)

@RahulChoubey1

Additionally, about the 100th fibonacci number...

Whippingdot (65)

Hey, can't you use a large double. @fuzzyastrocat

fuzzyastrocat (1297)

@Whippingdot Sure, but that's really memory intensive and that wasn't actually the problem (it was stack recursion depth, just like CPython).

Whippingdot (65)

Ok even though I have no idea what stack recursion depth is @fuzzyastrocat

ZDev1 (695)

where did u learned how to lang dev, if you know where, give me the link im very interested in lang dev, especially in Node.js and C++ or C.

fuzzyastrocat (1297)

@ZDev1 A few things:

  • Don't do node.js langdev. Don't ever do langdev in an interpreted language.
  • C++ langdev is ok, but at that point you might as well use C.

As for where I learned this, it's basically been a lot of "dribs and drabs" from sites since langdev is such a broad subject. A place to start might be crafting interpreters.

ZDev1 (695)

@fuzzyastrocat oh really
cookeylang was made of Node.js, anyways, thanks!

ZDev1 (695)

@fuzzyastrocat also links is not working

fuzzyastrocat (1297)

@ZDev1 Try again, the site was down for a sec.

And cookeylang was made in Node.js because cookeylang was (no offense to Coder100) not made how a real language should be made (and how all popular/mainstream languages are made).

ZDev1 (695)

@fuzzyastrocat ohhhh ok, thanks for all!

elipie (307)

aww I really want to code a language in C++(def not excuse to learn more C++) but I am seeing so muchh proof that C is better to create a language. I don't know much of C and I dont want to learn it so how could I create a language?

fuzzyastrocat (1297)

@elipie Just learn C! Really, if you know C++ it won't be as hard as you might think. The syntax is already there, the basic semantics are already there, you just need to learn some C idioms (malloc management is the first thing that comes to mind).

elipie (307)

@fuzzyastrocat hmph, I am more comfartable with C++ because its more middle-level, but if i have to learn C i will

fuzzyastrocat (1297)

@elipie It's not like you can't make a language in C++, but I'd really encourage learning C. It's a valuable skill to have!

elipie (307)

@fuzzyastrocat yeah ik, but which one is easier, because lang dev is a hard thing, and I don't really want to go all out harder, im taking not on a bunch of people who say this, so yea. and the 'winner' i will make a language in.

fuzzyastrocat (1297)

@elipie Here's the deal:

  • C++ has a bunch of features (STL). Most of them you'll never touch for langdev.
  • C doesn't have those features. So while you might feel a little lacking sometimes, in general it'll be better because:
    • C gives you a better feel for what's happening "bare-metal", so you'll really understand how and why your language works
    • C is (most often) more portable
    • Knowing C is an incredibly valuable skill since so many resources/tutorials/things use it.

So while making it in C might be a little harder at first, in the long run I think it's the best option.

DynamicSquid (4398)

@fuzzyastrocat This reminds me, I really need to practice my C skills. Like I know a bit of C, but I've never made any real projects with it. Oh, is there a practical way I could use C and C++ for Night?

fuzzyastrocat (1297)

@DynamicSquid
1. Write a C header
2. Write a C implementation file
3. Plop this into your C++:

extern "C" {
#include "my_c_header.h"
}
  1. Boom
DynamicSquid (4398)

@fuzzyastrocat Well yes, but is there any practical reason why I would do so? Any advantages to writing low level C code?

fuzzyastrocat (1297)

@DynamicSquid Oh noes I deleted my comment accidentally. Here it is again:

(I forgot to talk about the linker in my original comment.) Ah yes. Well, I just prefer using C, so that's my reason. However, there are a few good valid reasons:

  • C will give you a better feel for what's really going on. It's more "bare-metal".
  • C is (in general) more portable.
  • C can be a little faster.
  • Learning C will make you pretty masterful at low-level things — it's a great stepping-stone to assembly.
  • A lot of resources/tutorials/things use C. It'll be a valuable skill to understand them.
DynamicSquid (4398)

@fuzzyastrocat Okay! Might start writing some C functions. So all I have to do is this:

// file.h

void CFunction();

// file.c

void CFunction() {}

// file.cpp

extern "C" {
#include "file.h"
}
Coder100 (11140)

more like:

#include <cstdio>
#include <iostream>

int main() {
  char savage[50];
  sprintf(savage, "oh no...");

  std::cout << savage << std::endl;

  return 0;
}

@DynamicSquid

Coder100 (11140)

oh and btw do any of you have discord @DynamicSquid

DynamicSquid (4398)

@Coder100 Wait I'm really confuzzled cause that's still C++ code with C++ linkage (I think that's what it's called?). But I'm wondering about C code with C linkage

Coder100 (11140)

all valid code in C is valid in C++ @DynamicSquid

fuzzyastrocat (1297)

@DynamicSquid [answering your question from forever ago] Well yes and no. Yes, in that you do need to do that. But no, in that you also need to mess with the linker. I forget exactly how to do it, but you've got to link file.c as well as file.cpp.

fuzzyastrocat (1297)

@Coder100 @DynamicSquid

Wait I'm really confuzzled cause that's still C++ code with C++ linkage (I think that's what it's called?). But I'm wondering about C code with C linkage

You're trying to link C code with C++ code.

all valid code in C is valid in C++

Not true at all.

oh and btw do any of you have discord

I don't.

Coder100 (11140)

Not true at all.

in what part is that not true? @fuzzyastrocat

fuzzyastrocat (1297)

@Coder100 VLA's aren't valid C++. Neither are flexible array members. No K&R function definition in C++. This doesn't work in C++, but does in C:

struct A { struct B { int a; } b; int c; };
struct B b;

This is valid C but not C++:

int class = 1;
int private = 2;
int public = 3;

There's more, but I think this proves the point.

DynamicSquid (4398)

@Coder100 I found this article that explains the difference

DynamicSquid (4398)

@fuzzyastrocat Funny story, I really wish the first example would work in C++. I had some trouble with nested unions, but, since this is C++ and C++ has a wrapper class for everything, I just used std::variant instead

fuzzyastrocat (1297)

@DynamicSquid One last thought — it might be easier just to full-switch to C if you're thinking about doing part-c part-cpp. The reason linking the two together is so cumbersome is because it's not a very common use case, since most often you either want to use C++ or you want to use C.

Coder100 (11140)

@fuzzyastrocat oh, reserved keywords lol

those are edge cases, you shouldn't use them anyways

fuzzyastrocat (1297)

@DynamicSquid Heh, all the more reason to use C :D

fuzzyastrocat (1297)

@Coder100 That's just the last example I gave, the others are more impactful. VLA's are SUPER useful, and flexible array members are pretty useful too.

DynamicSquid (4398)

@fuzzyastrocat lol, but I don't really feel like rewriting my entire lang in C lol. Also yes, I really wish C++ had VLAs. Now that I think about it C++ is weird when looking at it from a C perspective

DynamicSquid (4398)

@fuzzyastrocat But even still, I wish C had classes, templates, and exceptions. But I mean... more motivation to create my own lang :)

Coder100 (11140)

wait what are VLAs? I got a bit confused on that one, aren't they like:

const int size = 5;
int arr[size];

@fuzzyastrocat

fuzzyastrocat (1297)

@DynamicSquid I don't really agree on classes, I find classes useless. I semi-agree on templates, but instead I wish you could pass types to functions in C (at runtime) to have really general functions. Exceptions... eh, I'm not really a fan when it comes to a language like C, but I suppose.

DynamicSquid (4398)

@fuzzyastrocat Actually you're kind of right on this. I've never coded a project that needed classes (unless using external libraries), so I guess I could do with structs and functions.

I think I've tried making a small game using a more of a function style once, and honestly it was way better than using OOP.

fuzzyastrocat (1297)

@DynamicSquid For me structs and functions work really nicely. I like that style a lot better than class-based OOP.

@Coder100 Yes, that's the gist. The real power comes because you can do stuff like this:

void my_function(int x) {
  int a_vla[x];  // now a_vla is an array with length `x`
}
Coder100 (11140)

@fuzzyastrocat so for my_function, what is the purpose of that?

Coder100 (11140)

@fuzzyastrocat would it not just be the same as:

void my_function(int x) {
  int* a_vla = (int*)malloc(x); // still length x
}
fuzzyastrocat (1297)

@Coder100 Sure, but that allocates on the heap (so you have to free it and manage its memory). In general, vla's allocate on the stack (unless you've got a really weird implementation) so you don't have to manage their memory.

DynamicSquid (4398)

@Coder100 If becomes useful if you don't want to use the heap

Coder100 (11140)

ohhhh, and I'm guessing you can't access the stack in C++? @fuzzyastrocat

Coder100 (11140)

giv reading material thanks @fuzzyastrocat

fuzzyastrocat (1297)

@Coder100

ohhhh, and I'm guessing you can't access the stack in C++

When I say "the stack", I mean x86's stack. The place where variables like int x; are allocated, it's "static" allocation. That's why you don't have to free variables like that, because they get popped from the stack automatically. However, when you malloc(), you're allocating in a separate region (the heap) where it's not "managed" like the linear stack. It doesn't know when things won't be used anymore, so you have to manually say free when the time comes.

VLA's are nice because you don't have to manage them — they're allocated on the stack just like a variable int x[3]; would be. (I'm not saying they're free of all restrictions though — they behave like variables in that you can't return them from a function, for that exact same reason. In that case, you use malloc. But VLA's are a really nice tool to have.)

(Sorry I'm not sure of any reading material on this, some searches for "how does the C stack work" might turn something up)

DynamicSquid (4398)

@Coder100, @fuzzyastrocat Oh, this conversation reminds me, could I share a meme I saw a while back (it makes fun of high level programmers)?

fuzzyastrocat (1297)

@DynamicSquid Oh yes, let us relish in the delight of low-level programming

DynamicSquid (4398)

@Coder100 No. Data structures are completely different. SO has a good post on the stack and heap though

fuzzyastrocat (1297)

@Coder100 That's about how to make your own stack, I'm talking about C's allocation stack (the x86 stack). @DynamicSquid posted a great link. There's another (a bit wordy, but still good) here: https://www.i-programmer.info/ebooks/deep-c/363

Coder100 (11140)

words are of not importance when in the realm of ideas

ok thanks @fuzzyastrocat

DynamicSquid (4398)

@fuzzyastrocat your picture isn't loading :(

DynamicSquid (4398)

@fuzzyastrocat Can't, im using school wifi and its blocked

xxpertHacker (555)

@Coder100

ohhhh, and I'm guessing you can't access the stack in C++? @fuzzyastrocat

Naw, the C++ committee said, "VLAs bad," then the C devs asked, "but why?" and the C++ committee responded with, "because we said so."

There's no good reason for it, but yeah, it happened.
I think that it caused conflict with some of C++'s other features, but I'm unaware of which ones it could've interfered with.

RayhanADev (1002)

I updooted this, then I read everything, then I tried it (using what limited knowledge of Python I have), and then I actually realized how good this is. Amazing job as per usual @fuzzyastrocat !

fuzzyastrocat (1297)

@RayhanADev Thank you! (It's built just like real CPython is — complete with bytecode VM!)

RayhanADev (1002)

@fuzzyastrocat Amazing, this just amazing! I hope it gets recognition that it deserves xD. Meanwhile I made crappy 99 bottles of jooce in LOLCODE

fuzzyastrocat (1297)

@RayhanADev Lol thanks, we'll see how the repl community reacts.

just realized your original comment says "updooted"

RayhanADev (1002)

@fuzzyastrocat my bren hurts, LOLCODE causes physical pain

realTronsi (782)

@fuzzyastrocat LOLCODE is actually one of the easier esoteric languages because its basically equivalent to python

fuzzyastrocat (1297)

@realTronsi Yeah... I like Unlambda the best of all the esolangs I've seen

realTronsi (782)

@fuzzyastrocat Malbolge I like BrainF and I know that sounds super cliche but BrainF is basically just a Turing Machine and it's challenging but not so challenging to the point where it's not fun.

fuzzyastrocat (1297)

@realTronsi To me Unlambda is the same, except find & replace "a Turing Machine" with "Lambda Calculus"

realTronsi (782)

@fuzzyastrocat I never really played around with Unlambda, it uses Lambda Calculus??

RayhanADev (1002)

@fuzzyastrocat
me: sees Calculus
my brain: (´༎ຶོρ༎ຶོ`)

fuzzyastrocat (1297)

@realTronsi Unlambda is basically an extension of the SK combinator calculus, which is similar to the lambda calculus in its effect ("unlambda" is named so since the SK combinator calculus is almost like a "removal" of lambdas from the lambda calculus). It's built around 2 fundamental rules:

S x y z = x z (y z)
K x y = x

You can do partial application too — so SK is a valid expression, it's applying S to K and then leaving y and z unbound.

And that's it. Those two rules, together, are turing-complete. A "program" is just an expression consisting of applications — basically, just a sequential string of S's and K's with parentheses for grouping. The wikipedia article on it has good info if you'd like to know more.

And Unlambda is basically that, except it adds IO functions.

fuzzyastrocat (1297)

@RayhanADev Heh, "Calculus" (strictly speaking) just means "a method or system of calculation or reasoning". "Calculus" by itself now refers to the branch of mathematics regarding differentials, but "calculus" along with something else just means "a way of calculation". ("Lambda calculus" = "calculation using lambda abstractions")

realTronsi (782)

@fuzzyastrocat yes thank you I will definitely absorb this information

fuzzyastrocat (1297)

@realTronsi Yes, I keep many porcelain discs for placing food on top of in my place of dwelling.

Jokes aside, no I don't.