Scripting Language Genesis

Started by
116 comments, last by cmp 19 years, 4 months ago
Here's a test of a script compiler and vm I wrote about one year ago. Note how explicit everything is. Call is used to branch to subroutines and Let is used to do assignments. I planned on making calls and assignments implicit, but I really liked how simple the parser was and decided to leave it be. Also notice how the Else comes after an End. This is mostly because I'm lazy. I never got around to adding a string concatenation operator either, hence the calls to the Concat function.

Script RunTests()	Call TestBoolExpressions	Call TestWhile	Call TestIfElse	Call TestFunctionCallsEndScript TestBoolExpressions()	Send "TestBoolExpressions"	If 5 == 3 + 1		Send("5 == 4?")	End	Else		Send("5 <> 4.")	EndEnd# SuccessScript TestWhile()	Var i	Send "TestWhile"		Let i = 10	While i		Send i		Let i = i - 1	EndEnd# This script tested successfullyScript TestIfElse()	Send "TestIfElse"	If Input("Enter 0 or 1")		If Input("Enter 0 or 1")			Send "A"		End		Else			Send "B"		End	End	Else		If Input("Enter 0 or 1")			Send "C"		End	End	Send "Out"End# This script tested successfullyScript TestFunctionCalls()	Var First	Var Middle	Var Last	Var Full	Send "TestFunctionCalls"		Let First = Input("Please enter your first name")	Let Middle = Input("Please enter your middle name")	Let Last = Input("Please enter your last name")		Let Full = CombineNames(First, Middle, Last)	Send Concat("Your full name is: ", Full)EndScript CombineNames(F, M, L)	Return Concat(Concat(Concat(Concat(F, " "), M), " "), L)End
Advertisement
Quote:Original post by smr
Here's a test of a script compiler and vm I wrote about one year ago.
...

Nice, very nice.

Did you write your own parser or use a tool (bison etc.)?

I am thinking about making this language not need the end statements, just work off the tabs. What do you think? Do you always tab your code?

I know that it is going to be hard to use bison and flex to parse it (going to have to do some tricky stuff with the lexer - tokens etc) but I also know that those tools make parsing so much easier.
I coded the parser myself.

Python doesn't use end statements. It uses indentation to indicate blocks too. I love python. My initial reaction was that it stunk and what kind of crappy language forces you into a coding convention, but once you grasp the wicked awesomeness that is python you get over it in a hurry.
I've been doing a little bit of thinking about this.....

First on two new keywords "When" and "Forever"

Forever call get_track when currtrack = null, endoftrack


Forever takes three arguments:
The one of the left, is executed when the one in the middle is true. it is taken of the forever list when the last is true.

The forevers are checked every time it could possible fire (i know its slow, but if your only referencing 3 different variables, you only need to check it everytime one of those variables change). When it is removed from the list, it ceases to exist.

The final argument is what is seperated from the first two by a comma. the 1st and second are seperated by the keyword when.

When also exists by itself, it is only a form of if.

so:
if x = 0 then
call thefunction
end if

gets turned into
call thefunction when x = 0
or
call thefunction when not x

These would be very helpfull for some tasks...
For egsample, if you want to mkae sure that myval never goes over 255, and its changed and has a whole lot of things done to it over a few hundred lines, you could just do:
myval = 255 when myval > 255

At the beginning of the function.

also,
Objects would be "black boxes" within themselves.
They can reveal variables, forevers, whens, and methods to either the rest of the program, a specific group of objects, or the rest of the program and not a specific group of objects.

This would be more oop.

Also, for arrays:
How would you do multi-dimentional arrays?
i = [3,3][0,2,3:1,2,3:2,2,3]
This would be a way... but is there something better?

From,
Nice coder
Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.
Quote:Original post by smr
I coded the parser myself.

Python doesn't use end statements. It uses indentation to indicate blocks too. I love python. My initial reaction was that it stunk and what kind of crappy language forces you into a coding convention, but once you grasp the wicked awesomeness that is python you get over it in a hurry.

Wicked awesomeness? Sounds like devotion.

What makes python cool? Can I do it in my language?
Quote:Original post by Nice Coder
First on two new keywords "When" and "Forever"

Similar to my idea of triggers. An object asks to be notified when a certain situation / expression is true - a method gets called. I like the idea that there is a object and a method involved - nice and oop. I don't really like the overhead involved, but it is the only way.

Quote:Objects would be "black boxes" within themselves.
They can reveal variables, forevers, whens, and methods to either the rest of the program, a specific group of objects, or the rest of the program and not a specific group of objects.

This would be more oop.

How would the syntax of this work?
I know public, private, protected and package. Something like public <class_name>?

Good idea though. Might be a bit pedantic though.

Wouldn't that mean that the object needs to know about the other object - making it less oop? Part of oop is less dependancy - you can reuse objects because they are not linked to other objects.

Quote:How would you do multi-dimentional arrays?
i = [3,3][0,2,3:1,2,3:2,2,3]
This would be a way... but is there something better?

If you wrote your parser right this would automatically work:
for an array like
1 2 34 5 67 8 9

can be written as:
integer[][] i = [[1, 2, 3],                 [4, 5, 6],                 [7, 8, 9]]

This would also work:
integer[][] i = [[1, 2, 3],                 [4, 5],                 [7, 8, 9]]

just i[1][2] would not exist.

Edit:

The coolest idea is the 'I don't care it is null' dot notation from groovy (Safenavigation).
foo = nullbar = foo->something->method()assert bar == null


[Edited by - umbrae on November 7, 2004 2:54:32 PM]
Similar, it would just be easier. and you could have your forevers In one function only! (scoped forevers, from objects to however low you want).

Forevers would be like events, it is just that forevers would be made within the code, while events should be caused by something external (like a keypress). Nothing to stop both from working in the same project, however... (events also have to have names... and you can't remove an event.)

Thought:
Events - Keypress, mousemove, ect. caused by other objects, and the system (which is an object).

Forevers - Microevents, like forever call drawframe when time - lastframetime > somevalue. Would be limited to objects at maximum scope.


I like whens... its like the iif()'s from vb, just a whole lot easier to use (and for n00bs to understand).

For objects, i was thinking of something around these lines...

Object Obj1:
Functions:Function1, Function2, Fucntion3.
Import Functions: Obj2::Myfunc, obj3::Otherstuff.
Export Functions: Myfunc.
Variables:Var1, Var2, Var3.
Include: IO, System, Time, other_header_stuff.
Show variables: Var1(RW), Var2(R), Var3(R).
Get Variables: Renderer::Currscreen
Recieve Interfaces: Render::Renderme, Rerder::settings, Script::Doscript, onject::Interfacefunction.
Give interfaces: Function1, Myfunc.

Differences between importing/exporting functions and interfaces.
Importing a function, is like including that function from another object into your own. It saves you having to type it, or keep a constant copy on another object.
The interfaces, are functions which you can call from other objects. Any function other then an inferface, you cannot call.

Interfaces with variables are a bit different.
You show, or get variables.
When you show a variable, you expose it for the world to see. you can show it with r, or rw. with r, others can read it, but not change it.
With rw, others can read and change the variable.

With only allowing things to change according to object, i'm not sure on how to figure that one out... although it would be nice if you could... (like give your renderscreen variable to your shader object, and give it rw, but have it r to your showtoscreen object).

Maybe something like
GIve variables: Var1 (Everyone:R, Obj1:RW), Var2 (everyone:RW, obj2:R).

It just seems wierd to me.

I like how the language is coming along...
From,
Nice coder
Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.
Quote:Original post by Nice Coder
Similar, it would just be easier. and you could have your forevers In one function only! (scoped forevers, from objects to however low you want).

Does that mean that the forever only lasts as long as the method (if it was a method forever)? or the object (if an object forever)?
Quote:Forevers would be like events, it is just that forevers would be made within the code, while events should be caused by something external (like a keypress). Nothing to stop both from working in the same project, however... (events also have to have names... and you can't remove an event.)

Can they be the same thing? I really like simplicity of single objects. What I was thinking was that you could attach a trigger to a method.
unit_group g = goblin_army.alloc().init()trigger t = g.go_hostlie.trigger(player.distance_to(g) < 10 && g.awake)

t will now 'fire' if the condition is true. I suppose different types of triggers could be created (fire once, keep firing, fire when the condition changes from false to true etc.) t also can be deleted or stored somewhere, or temporally be disabled etc. The problem, and the same problem with closures, is scope. What variables can you reference in the trigger expression? If you can reference local ones then if the trigger is tested when that method is not running - the stack will be different. I suggest that the scope is relative to the object with the method that the trigger calls. This is a big weird because it means that if you declare a trigger in another class the scope within the expression is different.
Quote:Forevers - Microevents, like forever call drawframe when time - lastframetime > somevalue. Would be limited to objects at maximum scope.

What do you mean limited to objects at maximum scope? Until the object goes out of scope?
Quote:I like whens... its like the iif()'s from vb, just a whole lot easier to use (and for n00bs to understand).

Your whens are like my triggers.

Your forevers are interesting, the way I would do it is to have another trigger turn the first and itself off.

both fire and outofammo are methods

In player.init()
shoot_trigger = fire.trigger(system.keydown(system.spacekey)outofammo_trigger = outofammo.trigger(ammo_count == 0)

In player.outofammo()
shoot_trigger.disable()outofammo_trigger.disable()


Although this example could probably be implemented better, that just gives you an idea.

Behind the scenes the trigger super class (I really need a better word for that - master? single? owner?) would have a list of active triggers and inactive ones. They get added and removed from the list as they are enabled / disabled.
Quote:For objects, i was thinking of something around these lines...

It's a little hard to read, the quoted text had spacing - need to wrap your code with code or pre tags - forum , html &lt; pre &gt;. Even with the spaces it's a bit of a jumble, but I get the idea. Could use block formatting like:<br><!--STARTSCRIPT--><!--source lang="cpp"--><div class="source"><pre>Object Obj1:<br> Functions:<br> Function1,<br> Function2,<br> Fucntion3.<br> Import Functions:<br> Obj2::Myfunc,<br> obj3::Otherstuff.<br> Export Functions:<br> Myfunc.<br> Variables:<br> Var1,<br> Var2,<br> Var3.<br> Include:<br> IO,<br> System,<br> Time,<br> other_header_stuff.<br> Show variables:<br> Var1(RW),<br> Var2(R),<br> Var3(R).<br> Get Variables:<br> Renderer::Currscreen<br> Recieve Interfaces:<br> Render::Renderme,<br> Rerder::settings,<br> Script::Doscript,<br> onject::Interfacefunction.<br> Give interfaces:<br> Function1,<br> Myfunc.<br><br></pre></div><!--ENDSCRIPT--><br><!--QUOTE--><BLOCKQUOTE><span class="smallfont">Quote:</span><table border=0 cellpadding=4 cellspacing=0 width="95%"><tr><td class=quote><!--/QUOTE--><!--STARTQUOTE-->Differences between importing/exporting functions and interfaces.<br>Importing a function, is like including that function from another object into your own. It saves you having to type it, or keep a constant copy &#111;n another object.<br>The interfaces, are functions which you can call from other objects. Any function other then an inferface, you cannot call.<!--QUOTE--></td></tr></table></BLOCKQUOTE><!--/QUOTE--><!--ENDQUOTE--><br>You are saying you can import any (public) functions from anywhere? Whatever happened to nice old subclassing or the (java) interfaces? That is not oop in my book.<br><br>That's like selective subclassing and implementing interfaces, but &#111;n the method level. I don't really like the idea - spagetti all over the place. How do you make a variable (container) for a class that supports a certain method? In java it would be the simple case of defining a variable as a (java) interface. In your language how would you put any other object in it's place? You would have to do a careful comparison and make sure that every (method) interface was in both classes - but even still the variable would be defined as &#111;ne type of object so it wouldn't work. How do you say "I want a variable that supports these methods"?<br><br>Also, how do you override methods?<br><!--QUOTE--><BLOCKQUOTE><span class="smallfont">Quote:</span><table border=0 cellpadding=4 cellspacing=0 width="95%"><tr><td class=quote><!--/QUOTE--><!--STARTQUOTE-->Interfaces with variables are a bit different.<br>You show, or get variables.<br>When you show a variable, you expose it for the world to see. you can show it with r, or rw. with r, others can read it, but not change it.<br>With rw, others can read and change the variable.<br>...<br>It just seems wierd to me.<!--QUOTE--></td></tr></table></BLOCKQUOTE><!--/QUOTE--><!--ENDQUOTE--><br>Yeah, I'll argue the necessity of selective access. To me there is &#111;nly really the 3 basic &#111;nes, public, private and protected. &#79;nce you start building dependancies I believe coding would start to get cramped.<br><!--QUOTE--><BLOCKQUOTE><span class="smallfont">Quote:</span><table border=0 cellpadding=4 cellspacing=0 width="95%"><tr><td class=quote><!--/QUOTE--><!--STARTQUOTE-->I like how the language is coming along...<!--QUOTE--></td></tr></table></BLOCKQUOTE><!--/QUOTE--><!--ENDQUOTE--><br>Your's or mine or cmp's? :P<br><br>All ideas at the moment.<br><br>I just want to hear ideas for language features - everyone is welcome to share their ideas and use the concepts in their own language. I will criticise and comment. Would also like to hear your criticisms and comments. I can't guarantee that I will use the features in a language but I will consider them all.<br><br>Again, you are welcome to build up your own language from these ideas.
damm i really should get a new keyboard, i had written a reply and then the z key got stuck an messed everthing up, so have to write everthing again.

Quote:Original post by umbrae
The class would still be flagged as remote though? The compiler would have to know.

the compiler knows with new remote type(), that type may be remote, so it creates a proxy object.

Quote:The cool thing is that .alloc() and .async() are actually both methods. They are both implemented in C++ although they are not 'normal' methods. There are a few methods in my language that are 'special', mostly the base ones in the object object, the type object, the method object and the class object.
The method object can have methods! but the compiler will only allow 1 level deep. Say you want a particular method async, just go like this:
unit_group g = goblins.spawn(world).init()// normal callg.moveto(waypoint)// async callg.moveto.async(waypoint)

The .async() method on the method takes the same parameters as the method - but it will execute asynchronously. I think this is better than a keyword - the functionality is linked to the method, not a global keyword. And it can be called either sync or async.

as thought about i really liked the idea of methods beeing objects with a () operator - at least i would explain it this way from my c++ background.
but when you write goblin.init(), it looks to me like goblin is a global var, wich should really be avoided in oop languages, you could of course say that every superclass is contained in the base class (i think you call it object), but this would mean that the baseclass is changed everytime you add a new class to your project, something wich does not look right to me - and you can't implement the baseclass in your own language.
additional you are blurring the lines between object-instances and classes: when you write integer a = integer.init() integer is a class and an instance - or init is static, but then you can't say integer.async().init() because what should async() return? it can't return classes it would have to be an instance, this way integer is an instance of class and a class itself at the same time, wich looks like a design error to me.
Quote:
I think I can explain it a bit better if given another go, if you are hazy or don't quite understand I will try again :).

that isn't necessary, i think i got the idea - interesting aproach - like i said the whole method as object stuff, etc are aspects i didn't even considerd when planning my language - simply because i didn't came up with idea ;).

Quote:
Yeah, I want a type of generic (template) support, but I'm not sure if it should be in the language or not. The only thing I think it is really useful is data structures, and if I can supply the structures (lists etc) then there won't be a need for templates. There will be support for them in the vm so I can always add support later.

but sometime the user will implement structures himself and now he either has to rewrite it everytime or use boxig - i think this is the term microsoft used for object o = (object)integer_var.

Quote:
Quote:
actually i had one parser written in c++ (with flex and bison), but then i realised that i would be really cool to write the parser in my language, so currently i will write everthing as xml, so i can recompile it later to c++ and my langauge.

So let me get this straight... you are writing your parser in the same language that you are parsing for. Now I want to do that (seriously).

I think I will go away and write a vm, then write the parser in my own language.

the only problem is that you have to supply some boostraping code, since it is like the chicken and the egg. i use xslt as a boostraping language, since it is fairly easy to write and very flexible when transforming xml code.

tag:    other_tag    | tag other_tag

For 0 or more
tag:    /* nothing */    | tag other_tag


from what i have got you should never write sth. like tag : /* nothing */, because the parser then get stuck, because he can't distinguish between tag other_tag and nothing.

and from what i remeber you should rahter use left recursion with yacc, so you should write: tag: tag other_tag | other_tag.

Quote:
Also it is a good idea to implement what order your operators have - I copied the structure from a C parser I found.

i had done the same, but since i'm now writing everything in xml, there is no problem with oeprator precedence.

Quote:So are you going to write your parser in your own language?
Also if you are interested, this is the bnf of my original language.

i think i will still use lex for parser generation but everything beyond this will be implemented in my language. so generated the tree is done with lex and the transformation of the tree to the xml file is done in my language.

Quote:i = [3,3][0,2,3:1,2,3:2,2,3]

btw. this is the same way my calculator does define multi-dim-arrays ;)
but it has the problem that you are restricted to 2 dimension with this syntax.
and with [[1,2,3][1,2,3][1,2,3]] is the problem that you won't have a multidemensional array, but an array of arrays.

Quote:Forever call get_track when currtrack = null, endoftrack

nice idea - i like it. since i don't want to introduce another keyword in my language i would do this the following way:
condition : boolean = trueend : boolean = falsesubsrciption : Kernel.Subcription = voidsubscription = condition.on_change(lambda { if(condition == true) { track_list.get_track() }})end.subsrcipe(lambda { if(end == true) { condition.remove(subscription) }})

btw: lamdda expression creates a lambda expression, so only expressions may be used
but lambda { } creates a real function so statements may be used.
Firstly:
The forevers are like variables, they are removed as they go out of scope (as would variables).

A method is a scope, an object is a scope.

What you would have, is a list of currently active forevers. You remove forevers as they leave scops, and add them to the list as you see them. (or they go into scope).

I would say the destinction between forevers and triggers are:
Triggers are triggerred from outside (the game, the user(s), whatever). Forevers are triggered from inside (variables changed, function called, something happening or not happening, timer expiring)...

Also, for the includes, your including functions from other objects. Thats like going (inside your object)

#include <stdio>
...
Just with functions, like
#include Getch()
Ect.

Maybe something like header files?
In the header of the main source file, it would have things like commonly used function lists, and an alias. use that alias in the includes, and all the functions get included.

Whens are not like triggers...
I origionally thought of them like shortaned ifs as one expression...
How about these things being Micro-triggers, or micro-forevers?
They execute once, then die.

How about some includes?
Object obj:	Functions: Function1, Function2, Function2, Fucntion4.        Imported functions: Obj2:Function1, Someobject:Func.	Give variables: Obj2:Var, Obj:foobar.	Show variables: Var1, Var2.	Variables:Var,Va2,Va3. 	Import from C1.cpp, C2.cpp, C3.cpp, Othertuff.cpp.

Heres an idea, the full stops in the declerations are like ;'s in c++. You can put whatever whitespace you want inside them (comments too), and then, on the . you recognise an end-of-statement.

Look at the import from,
When compiling, it takes all the functions from those files, and shoves it into one big file, within the object. Then it is all compiled together.


Perhaps the triggeres variables which it references, are the variables that are currently running at the time?

So something like
c = 3d = 65t = trigger(c < 2, Gui::Msgbox("Look at me! i'm a msgbox!/n" & d))d = 2c = 1

Now it would pop up a msgbox which would say
"Look at me! i'm a msgbox
2"

So it takes the value of d, when it is triggered. Makes sence? (note to self: Do not code while tired. Also do not post while tired)

Also, if d went out of scope, the trigger could be deleated. (unless it was d = null, special case).

For overridden methods, maybe an override flag?

Inline Override Int method1End method

?

Thought:
In main.cpp you have your:
Header
and
object declarations.

You now have your source code in other files, which are seperate and can be reused.

In the other files, you can have other object declerations. these are assumed to have been derived from its parent object (which specified it). they can also specify other parent objects.

I was referring to voodoo, but all of the languages mentioned are coming along great.
From,
Nice coder
Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.

This topic is closed to new replies.

Advertisement