An EleetScript update.
Following the previous blogpost about my ventures into interpreters for an existing esoteric language and my plans to jump right into hosted scripting language development I thought I'd delve further into ideas and implementations for EleetScript (formerly EleetScript).
My desire when building this language is very specific to my needs; however, that hasn't prevented the addition of features that would make the language usable for other needs. The core concept behind building this scripting language is ease of hosting it on top of a Ruby application (a MUD server) as well as keeping it's core features to a minimum so that security was not a major concern. That meaning, don't worry about sandboxing the code, only open the portions of Ruby (if any) you want to add to the features the language has already.
Now that I mentioned the security and the lack of major core features I'll discuss what I wanted to leave out by default and include. I've decided that access to the process (the ability to halt the application directly or indirectly) should not be allowed. So the language is very tolerant of errors (this will open a slew of logic errors, I'm aware, but will prevent disgruntled scripters from causing the system harm). Errors that normally halt program execution of interpreted languages (like undefined functions) simply return nil which of course will evaluate to false. I also left out features such as reading files from the system, reserving that access to the application itself. Regardless of what I've not included by default, being a hosted language, these features can be plugged in as simply as
engine = ES::Engine.new; engine.add_class("File", File) and this will link the Ruby File class to the constant "File" inside the engine.
Just because possibly unsafe aspects of a language are being excluded or worked around doesn't mean the core power of the language will be nonexistent. The majority of the default Ruby number and string capabilities will be present in EleetScript and arrays will function almost exactly the same. I'm doing away with the difference between Arrays and Hashes to take a more PHP oriented approach by making arrays with non-auto keys a "Hash" for all intensive purposes. The reason I'm doing this is simplicity of language grammar. Another concept behind EleetScript is to open up scripting with the language to people who may not be expert programmers and the less they have to learn and memorize the better. This will cause some readability confusion, I for see, but I think it's worth that hit to bring in a wider audience of scripters.
The Ruby linking features aren't implemented yet as the base interpreter is still under development but I plan to make linking Ruby classes and objects easy. Any work done executing or using EleetScript will begin with a EleetScript engine:
engine = EleetScript::Engine.new engine.load(script)
This load function will interpret the script and persist the state of the program after initial interpretation (method definitions, class definitions, etc...) for use when necessary. Most often this loaded state will be used to call methods defined in EleetScript script:
result = engine.call(:player_entered, @player) # call "player_entered" with one argument
Or allow for instantiation of objects, given this EleetScript script:
class Greeter init do |@greeting| end greet do |name| print("%@greeting, %name") end end
You could create instances of Greeter inside of Ruby:
greeter = engine.new("Greeter", "Hello") greeter.greet("World") # => "Hello, World"
This demonstrates that EleetScript will link with Ruby in both directions. Ruby objects can be added to a EleetScript engine instance for use by the loaded script and use EleetScript defined objects inside of Ruby if necessary.
The biggest hurdle to conquer thus far in the development of EleetScript (which speed is not being addressed yet, once the interpreter and Ruby integration are complete I will address speed if it becomes a concern) has been planning the "memory" side of the runtime so that it would be completely stand alone. The needs require many scripts loaded simultaneously having them all share the same memory space would cause clashes. For example, all Rooms (or regions) in the MUD will respond to an event when players enter the room, a "player_entered event. So a room script written to do something special with a player when they enter the room looks like:
on_fire = Buff.create do apply(:on_tick) duration("10s") tick("2s") damage(Damage.new(10, DamageType.FIRE)) end player_entered do |player| player.debuff(on_fire) end
This would allow the room to set the player on fire upon entering the area, and this should apply to any room. Nor should another room loading it's script replace this definition of player_entered. Solving the stand alone memory issue wasn't difficult or impressive but thus far has been the greatest hurdle in getting EleetScript up and running.