Sign in to follow this  

Structure of how this language should work

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've tried endlessly for the past few days to figure out how all this would work, but it's just not knocking in. The problem is basically working with classes and functions etc. If I have a very simple program:
class myclass
{

   void mymethod()
   {
      char boo[4] = "hello";
      print boo;
   }

}

void main()
{
   myclass myobj;
   myobj.mymethod();
}
The problem is, I have got to close to getting this to work, except for a few things: - Getting everything only in functions to work. I was thinking of perhaps making "frames" or something for this. - Maing an object ¬.¬ I figure it has to be in memory before you can make an object, let alone call any vars of functions. Problem is, the entry point is "main", you have just skipped out all the class code, so how do you make an object then. If the entry point isn't "main", how do you stop everything in the class (and it's functions) being executed? Thanks for your help :)

Share this post


Link to post
Share on other sites
C++ implementations face a similar problem. The OS doesn't call main() directly, rather, it (usually) calls start() which does environment setup code (such as constructors), finishing with a call to main().

If I'm understanding you right, you want to do the same thing. At compile-time, generate a list of classes and store them with the script somehow. Then, just before starting your script with a call to main(), the execution engine can initialize all that class data.

Share this post


Link to post
Share on other sites
The start functon (which is the actual entry point your program) sets up your environment for you. So it will do things like initialise various things the standard library will need and call the constructors for any global classes (at least I think it's done there, it's certainly done before main).

However in your sample code the only instance you have of myclass is on the stack and is local to main. Hence the constructor wouldn't be called before main. What would happen is at the beginning of main space would be allocated on the stack for myclass, and then the myclass constructor would be called for that newly allocated instance.

Share this post


Link to post
Share on other sites
Global classes are initialized via constructors, too. The start function does all of that before actually calling main. The environment is set up by allocating stack space, collecting command-line arguments/environment variables, etc. Because you're designing a scripting language, you won't need to actually have a start function, just have the engine set it up before executing the script.

Share this post


Link to post
Share on other sites
The stack is a fundamental concept and understanding it will help you - it will also help you see why you probably don't need one for as many things as C++ does. Stacks are dynamic in nature, and they manage their own 'space'; that's what they're for.

If you haven't got functions working yet, or variables, forget classes. Work on one problem at a time.

What do you mean by "Getting everything only in functions to work"? Functions compile down to code, like your main function does. Calling a function is typically a case of:
- remembering the current position in the program by pushing it onto a call stack
- moving the current position to the start of the function you're calling
- creating storage for any local variables within this function
- initialising any function arguments as local variables

Then you execute the function, and at the end you pop the return value off the call stack to see where to send execution to next.

As for creating objects - whether classes, or any other type - you just allocate space locally of the appropriate size, initialise that space accordingly, and then have your instructions refer to the relevant parts of that space. In machine compiled code this is typically done by allocating space on the stack, and having all variable accesses resolve to accessing stack memory. In bytecode compilation or any other high level language though, you could have some sort of name -> object lookup, where an 'object' is a polymorphic/generic variable type.

The entry point isn't all that relevant. Either you compile all code before execution, so that it's always there when you need it, or you execute the code as you go along, so that you'd just have to search for the definition before creating the object. Or you have some hybrid scheme in the middle. Having an arbitrary entry point doesn't mean ignoring everything before it in the file.

Creating a class is much like creating any other object, except initialising the space is typically a case of calling some sort of constructor.

Share this post


Link to post
Share on other sites
Well what I meant about the functions, was that I only want code within functions to work, and not anything outside functions unless it's creating classes or global objects/variables etc.

I was also sorta worrying about making classes mainly because I thought if I didn't get them done now, I would encounter problems in the future with how the rest of my code works.

I'll keep reading :)

[edit]

Oh also, as for the local variables bit, I have never understood them (obviously I know what they are etc), I understand turning parameters into variables, but having an array for local variables seems kinda of pointless, seeing as the braces {} will clear all the variables by the time the function has finished anyway.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zotoaster
Hmm, but how do you allocate stack space when ugh, the stack is totally dynamic?I think this might be getting too confusing :]


That's what needs to be done for compiled programs, since you have total control of the runtime environment of your scripts, you can do whatever setup is necessary.

Quote:
Original post by Zotoaster
Well what I meant about the functions, was that I only want code within functions to work, and not anything outside functions unless it's creating classes or global objects/variables etc.

That's an easy one. When parsing outside of a function, refuse to parse any executable statements, just declarations and whatnot.

Share this post


Link to post
Share on other sites
It seems to me that your problem is that you have no separation between parsing and execution, when in fact these are two distinct actions.

IMO, scripts should be transformed into a form more easily executed either at load time (once when the script is loaded) or Just-In-Time (once, the first time a function is encountered) rather than run-time interpretted. Expression trees or an assembly-like stack-based language are usually pretty good options. Even globals could be JIT'ed, so long as they are completely initialized before their first use.


The basic approach I would use is this:

Scan the file at load time for symbols (class/function names, class definitions). Place them in a symbol table with an offset to the beginning of the definition in the file, but don't translate them yet.

Begin execution at your entry point, when a symbol is encountered check the symbol table to see if it has been translated, if not, use the offset to find it again and translate it. In the case of a function/method Push a return location and any parameters and jump to it's exectutable form. In the case of a class definition, check the symbol table to see if the class itself has been translated, if not, translate it. Minimally, the data layout and appropriate constructor must be translated (other methods can be translated now, or JIT'ed as encountered if you wish.) then push a return value and any parameters and jump to the appropriate constructor.

For simple built-in types which are global, I'd just instantiate them once at load time as they're encountered.

Also, if you were to impliment the JIT approach, you must watch out for dependancies. If a class takes another class or another class's member as a parameter, that class must be translated/instantiated first; so-on and so-forth all the way down until no dependancies exist. If you instantiate all globals at load time, you'll have a much easier time since you can instantiate the data as encountered, without trying to walk back up the dependancy chain.

Share this post


Link to post
Share on other sites
Quote: It seems to me that your problem is that you have no separation between parsing and execution

Actually, I am working in a strange order just now. I am starting off with the virtual machine, so I can know that the bytecode can work well. I have a descent parser in another file, but I'm not using that just now. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Zotoaster
Oh also, as for the local variables bit, I have never understood them (obviously I know what they are etc), I understand turning parameters into variables, but having an array for local variables seems kinda of pointless, seeing as the braces {} will clear all the variables by the time the function has finished anyway.


That makes no sense. That's like saying "allocating memory with 'new' is pointless, as it'll just get deleted." Local variables have to exist somewhere while you use them. How you handle them is up to you. And I never used the word 'array'.

Share this post


Link to post
Share on other sites
Quote: "And I never used the word 'array'."

Heh, sorry, I just figured that's what you meant.

But really what I meant was that, atleast in my scripting (not sure about anything else), when a variable is created, a pointer to it is added to the stack, and obviously when it reaches a } it clears the stack till it finds a {, so to me atleast, there is no difference between local and global variables, so actually treating what turns out to be "local variables" as a different type of variable seemed pretty pointless, heheh.

But anyway, I have figured out functions, and just working on my virtual machine. I think once I get a clear understanding of them, I will try to implement classes.

Thanks everyone :)

Share this post


Link to post
Share on other sites
*Bump*

I have encountered another problem. It's about parameters. I figured, if I made a variable in a function, and set it as a parameter, it would just pop the top stack item into it. There's a problem though, when the program moves the the start of the function, it adds the { to the stack for scoping purposes.

Should I just work around this by only popping any actual values into the variable? I don't like "work arounds", they annoy me. There must be something better I can do.

Share this post


Link to post
Share on other sites
Thanks, everything seems to be working out good. I can succesfully compile code like this:


void foo()
{
int bar[3+2];
}

void main()
{

foo();
int hi;

}


But that's about as complex as the compiler can handle. Obviously the virtual machine is capable of alot more, but soon enough it'll work fine :)

Share this post


Link to post
Share on other sites

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this