Advertisement

Scripting Language Syntax Proposal

Started by April 13, 2004 10:38 AM
118 comments, last by irbrian 20 years, 9 months ago
quote:
Original post by irbrian
These questions and others like them are questions I''ve been pondering that may have a direct impact on the final syntax.


It''s really up to you. The old matra is that you should chose a programming language that has a level of abstraction suitable to your program domain. The closer it is, the easier your task will be.
quote:
Original post by irbrian
Also, at what point does one seperate engine logic from surface logic? Where is the surface? Should the scripting language somehow be connected with the State Machine? Should the scripts derive from a centralized point, kind of like a Script Main() function/method, or should scripts simply be extensions of engine-defined modules?

These questions and others like them are questions I''ve been pondering that may have a direct impact on the final syntax.


I''ve gone down this road too. At the one end, you give the user a very high-level poking/peeking interface into the engine, but you may limit the flexibility. At the other end, you give the user complete control over the engine, but this requires a great deal of hooks into your code, a great deal of effort to make your engine script-able and a great deal of scripting on your own part (to make the engine do something useful). At the extreme, the "scripter" might as well be an engine coder. And there''s a performance trade-off that happens if you let the scripting get too low-level as already mentioned.

Agreed that there is a big trade-off here, but you have to decide a) how much power you want to give scripters b) how much effort do you want to spend on making your engine flexible through scripting and c) how much confusion you want to generate for your users. I would suggest taking a look at some commercial examples and then pick a level of implementation that you feel will be satisfactory.

And ask yourself this question: Would you like to create a game engine or would you like to create a game?

There is no such thing as a "do-all" scripting language, that''s why there are so many scripting languages out there. You need to choose a domain/problem space and figure that one out. i.e. Stop spinning around in the theoretical stratosphere and choose an implementation that you can actually accomplish in a reasonable timeframe.

Regards,
Jeff


[ CodeDread ]
Advertisement
I think it is better to keep the compiler simple and have a powerful dynamic environment. Keeping the compiler simple means it will be fast. The byte-code interpreter is mostly there because interpreting line-by-line is awefully slow. Let the script language be totally dynamic and allow the user tools to debug and inspect things and change them on the fly.

Static typing is a bad idea. For one thing, implementation details like if something is an int or float should be hidden from the interface. All the user needs to think of are objects like numbers, strings, etc.

It depends on what you want to do but from my experience most game script languages are really data initialization languages. They are used mostly to set and control the underlying engine functionality. They are not used for main algorithms. It is a bad idea to offload your AI or 3D graphics into a script language as you are never going to get an interpreter to run much faster than Python or Lua without a JIT.

If you want to JIT then static type hints are necessary. If you want to translate to C they are necessary. In interpreted mode these hints should just be ignored but stored in the symbol table.

The fastest and easiest way I found was to seperate operator logic by type. There are only 2 types: numbers and strings. User defined objects can be operated on by aliases to function calls.
You can create an operator ''+'' that switches on tags but it is slow. Instead seperate operators between number and strings, and objects are accessible via function call. It is ugly, but it runs fast and allows the compiler to be extremely simple.

Otherwise why not look at a C or C++ interpreter? There are a few of them out there on Google. Also, Pascal, Modula, etc.
Here''s another idea.. what about having three types: string, number, and object?

They''re pretty self explanatory -- anything value of zero or more characters is a string; anything that can be mathematically manipulated on paper is a number. An object is any user-defined class.

Regarding JIT and such, I don''t remember if I''ve mentioned this but I think maybe a derivative JIT is best. Here''s what I''ve been thinking:

Once a scripter has written a script, it is somehow determined (either by the scripter or the engine) as having been added or modified. Scripts that have not been compiled are compiled to bytecode when the engine is initialized. This process may slow down engine initialization, of course, but after this first compilation, the bytecode file is cached. From then on it is simply interpreted as necessary straight from the bytecode cache. As long as the source script doesn''t change, there is no additional performance hit due to script compilation, no matter how many times the engine is initialized.
---------------------------Brian Lacy"I create. Therefore I am."
@irbrian:
I''d recommend at least subdividing number into integer and real, simply because of the different methods required to store a float of any magnitude and an integer of any worth (you don''t want round-off errors in your integers!)

I''d also recommend providing collection types. You don''t need to distinguish between an array and a list, as your language can provide an intermediate ArrayList type that yields acceptable performance in both cases. Tuples are nice, though not necessary if an ArrayList is a first-class type. Associative arrays (hash tables or dictionaries) are also a good idea.
I never said that dynamic types are bad. I was just pointing out that you could still have the option to use static types if you want, which wouldn''t be that hard to implement and would give you a really nice feature - allow more experienced scripters to make fast code, which in turn allows them to modify core functions of the game. Look at any game mods and levels with scripts out there, and you''ll see that hardcore scripters are constantly pushing these systems to the limit to get cool effects. Do you really want to keep them from doing that for just a little more effort? And my notion of static types is more like irbrian, you surely won''t need to have distinct doubles, floats, etc
Advertisement
quote:
Original post by Oluseyi
@irbrian:
I''d recommend at least subdividing number into integer and real, simply because of the different methods required to store a float of any magnitude and an integer of any worth (you don''t want round-off errors in your integers!)
I had an idea about that actually, but I''m not sure how it would affect performance and such. The idea is that the Numeric type would be made up of two variables internally, a 32-bit integer and a 32-bit float. Anytime the number value comes out to be an integral, the floating point portion is zero''d out, and any integer arithmetic (subtraction, multiplication, etc, involving another integral value), the floating point portion would be ignored. If the operation were division, etc, or one of the values were a non-integral real, the floating point portion would kick in and pick up the decimal remainder. That way there would never be any risk of truncation (if you handle everything carefully in the first place).

Alternatively, you could just make sure in the engine that the number is converted to an integer or floating point as necessary without any truncation -- taking care to only convert to an integer if there were no non-0 digits after the decimal place.

OT: If you guys haven''t already, check out the thread on the new Squirrel language; I''m interested to hear your viewpoints.
---------------------------Brian Lacy"I create. Therefore I am."
Aha, type migration. You might want to consider using a union, just brainstorming though (don''t have the experience). It seems to me that it may be useful to provide integer division too. Perl does this with a pragma* ''use integer'':
sub foo {  use integer;  $_[0] /= 3; # integer division  foreach my $thing(@_) {    no integer;    $thing /= 3; # FP division  }} 


* probably not correct terminology, technically at least.
Squirrel looked rather interesting, lot of cool ideas put into it.
I have not looked at the source code much yet though or tried it.
I have been playing with Tiny Scheme alot lately :-)

The only drawback to scheme is that it is very hard to read, I think it is even harder to read then Perl scripts. It is actually a super simple language though at the foundation.
quote:
Original post by PeterTarkus
Squirrel looked rather interesting, lot of cool ideas put into it.
I have not looked at the source code much yet though or tried it.
I have been playing with Tiny Scheme alot lately :-)

The only drawback to scheme is that it is very hard to read, I think it is even harder to read then Perl scripts. It is actually a super simple language though at the foundation.



Scheme/Lisp hard to read? Yes and no. It's much like any language, I remember learning BASIC years ago. Looked like greek to me then, then C; "what's all those '*' and '{' for?", and Lisp; "woah, so many brackets!", Perl looked familiar and yet everyone had there own way of writing it, and Python; "huh, were are all the brackets!?".

In the end, so long as it's not completely brain dead (Cobol?), and persist, you grok it. Most people though are used to Algol styled languages, so functional ones (try OCaml, Haskell) freak people out more!

I think it might be wise to not freak people out in the case of this new proposed scripting language, unless of course that's the point of it?

Me, I like Scheme/Lisp just as much as C/C++/Java. However I seem to write much more of the latter than the former. Perhaps
what we need is a little crash course...

[Lisp: Crash Course]

Write numbers as numbers, strings surrounded by double quotes, symbols as just names, lists as '(' then zero or more things seperated by spaces then ')'. To evaluate something, numbers and strings are themselves, and symbols are their current value, but lists of the form (function arg1 arg2 ... argN) mean evaluate arg1 through argN in turn, then pass them to function, which gives you the result. Exceptions; if something is preceed by a quote, then you don't evaluate it. If the function is "special" then you don't evaluate any of it's arguments. You now need to understand all the provided functions, and if they are "special" or not.

---

So given that you could start reading Lisp. Here's something familiar in nature, but not syntax (clue: defun is short for define function, the second argument is the name of the function, thirdly is a list of symbols standing for the arguments, and the rest is how to evaluate that function);

(defun factorial (n)  (if (== n 1)      1    (* n (factorial (1- n))))) 


Notice anything? No types... it's dynamic! It's also using recursion. Scary stuff if you've not seen that sort of thing before. Fortunately Lisp has always had side effects, so you can use iteration and do I/O. Some even let you specify types and have OO! It's also pretty easy to write an interpreter for, I have one somewhere which I wrote in under a day, sub 1000 lines that does pretty much everything you need for a scripting language. Of course nobody in their right mind would use it as a scripting language right?

Anyways, I need to stop this and some more code in Emacs, finish some textures I was working on in Gimp. Perhaps I'll just complete that last level on Jak and Daxter... :-)


--
Harvey Thompson (aka Viper)
Software Engineer/Games Researcher, UK.

[edited by - Viper3369 on April 22, 2004 8:27:39 AM]

[edited by - Viper3369 on April 22, 2004 8:29:27 AM]

This topic is closed to new replies.

Advertisement