Thursday, February 26, 2015

Better (non-int) return values


On my list of annoyances in the code that I've been writing so far has been declaring all my functions as returning ints, because that was what worked. I just dug in and figured out what was keeping void functions from working correctly - turns out, all I needed to do was to do a ret_void() at the right places, and everything worked great.

I also thought that returning floats would require extra work, since I didn't have float support in the language until recently, and I figured there'd be something that'd have to pay attention to whether I was returning an int or a float, but the above code worked just great on the first time. Well, the second time, if you count the compiler catching a legitimate bug in the convert_rad_to_deg source code. But even that's a success.


In the "note to self" category, or maybe for people who are following these breadcrumbs, learning about the LLVM experience, when I was implementing const variable declarations, I banged my head against parsing errors - I was having a hard time parsing "const int x = 8;", for reasons that weren't making sense to me. I was thinking that there was some precedence issue, and the LALR parser was maybe not sufficient to parse the ambiguous grammar that I had built.

I ended up (after many other stabs in the dark) turning on the bit that prints out the output of the tokenizer, and I noticed that "const" was being interpreted as an identifier, not as the keyword. I moved "const" up in my list of tokens, nope, same thing. I added 'const' into my list of reserved words, and then things worked right.


Another note: as I was implementing "const", I began putting in support for function static variables - variables whose visibility is restricted to a function, but whose lifetime is for the length of the application. I haven't finished that, and it's not a priority, but I did notice that LLVM variables have a flag, so maybe it's as simple as detecting that qualifier on the declaration and setting the flag.


Next up: structures / classes / objects. This will make a significant improvement to the architecture of my Pong game, in a few ways.

  • data modularity - I can have a 2d vector representing velocity, and refer to the velocity as a higher-level object, moving it around as a unit, rather than having to remember to move pairs of vx,vy.
  • API cleanliness - the events coming back from SDL for keyboard events or mouse actions are complex things. They have an event type (e.g. keyboard event) and usually one or more pieces of data (e.g. what button is pressed, maybe modifier keys that are also pressed, like 'shift'). 

Being able to clean up the structure of the Pong game will make it substantially less gross for me to look at, which is really the point of that game - show me what I can't stand, and I fix it.

Wednesday, February 25, 2015

Dear Constants

If you read that title and start humming "Dear Prudence" by the Beatles, you can skip the rest of this post.

I've added a few things, like being able to initialize variables on the same line as their declaration, and declaring that variables are constants. Except, if they're "constant" they don't vary. I'm still going to call them "variables".

I've also begun tidying stuff up to make a public repository. Things are rather messier than I'd like for real distribution, but getting stuff pushed to a public repository is going to force my hand some to make things cleaner as a habit.


As I was implementing constants, I was surprised to find that global variables in LLVM have a flag for being constants, but I didn't see the same thing on locals. So, I just made a separate dictionary for them in my frame objects. Nothing too fancy.

I also made parsing rules initially for constant declarations without initializers. Then, I stuck some code into my code generation to flag that as an error, and then I realized that I could catch it at parse time. As early as possible seems the best for that sort of thing.


No screenshots, as constants and variable declarations with initializers aren't pretty to look at.


Tuesday, February 24, 2015

Local Variable Progress

Evidence of new local variable behavior:

I added a stack of frames for local variables, and am looking up variables on that stack. Now, variables are local to the curly-brace block where they were defined (which has a name, which may aid debugging), they're looked up by most local to least local, and then in the global namespace (which is separate).

One way that this test had been failing was that the two different 'a' variables were getting confused, and the assignment "a=7" was stepping on the toplevel a, which should still have been 1. Now, everything's working correctly, and the assertions are happy.

Next up: being able to initialize variables at the time of declaration like "int x = 8;"

Monday, February 23, 2015

I've been working on my Compiler

I've been working on my compiler, a little bit, in the half year since I've written here. This isn't going to be a complete summary of everything that's been going on, but a few notes, as much for myself as anyone. I'll get chattier in later posts.

There are a few goals of my language project. These aren't in any priority order, just in me jotting down ideas as they come to me:
  • lightweight language, with a feel like C, with only a little bit of C++'s object-oriented features, without C++'s bulk.
  • targeting platforms ranging from desktops (Linux, Mac, Windows) to mobile (Android, iOS) to the web (HTML5 / JS) to maybe crazy unreasonable stuff like the Apple ][
  • primarily for writing games (interactive, graphical), but maybe command-line stuff is OK (server support?)
  • should be fun to program in
  • should be performant, but this isn't the highest priority. If I can achieve performance through tools, rather than by burdening the developer with decisions, that's nice.
I'm currently using a few tools so that I'm not building everything from scratch:
As I go, I've been building test apps that exercise specific parts of the language and compiler. Many of these are command-line Linux binaries, because that's what makes sense for early work. One larger test is a Pong game. Still not a huge app, but pulling in several pieces that a real game will have to deal with.

WIP, hopefully still around by the time you read this: http://bigdicegames.com/BDGRT/Pong/pong.html

One nice thing about the Pong test is that it's showing me what features I can live without, and what features are painful in their absence. I only had integer variables, and they were all global. This worked, but it was gross, so now I've been adding local variables and float support to the language.

As of this morning, I've got a little sample program that does a Fahrenheit to Celsius conversion using locally allocated float variables. There's a few pieces that I expect from local variables:
  • declared in the function where they're used
  • or, declared in a block (a chunk of code delimited by curly braces, contrasted with a basic block, which is something a little different inside the guts of the compiler)
  • has a well-defined lifetime (it goes away when the allocating block is exited)
  • initialized to something well-defined, by default
  • can be initialized as part of declaration (float x=8.0; #remember, Mallory, x=8)
  • scopes can nest, so a more-specific scope will have priority over a less-specific scope.
So far, I've got the first one of those working. Allocating space on the stack, via LLVM's alloca instruction. Works great.

Also working is reading and writing local variables. I didn't list those, above. Not really variables if you can't assign to and read from them.

The rest of that remains to be done, and I'll post here as I get it working.