I don't think there's a way to directly get SmileBasic to recognize the expressions you want it to.
Parameters to functions can be arrays. Arrays can be of any length. So, with a single DEF GENERAL(INTARRAY, FLOATARRAY, STRINGARRAY), you could call that function with essentially one integer parameter, or 4 floating-point values and 8 strings, or any combination. One of the problems with this is, SmileBasic has no way of representing arrays as literals: an array must be declared, then populated, which takes at least 2 instructions (per data type) before you can call the function. This is much uglier than just putting the values in a list inside parentheses after the function name.
Another approach might be to have a preprocessor. Load the file to be 'executed' into slot 1. This will be code which consists of both regular SmileBasic and your extensions. Load the preprocessor into slot 0. RUN slot 0. The preprocessor examines what is in slot 1, line by line. If the line matches an 'extension' of yours, then translate it into something that SmileBasic can execute directly, and put that in slot 2 (e.g. this would be the step where you could convert eval(), friendly to the user, to eval1() or eval2() ... or eval22(), friendly to the system). If it doesn't match, then just copy the line directly into slot 2. When you have finished all of slot 1, EXEC 2.
Some drawbacks to this approach are: you'll probably have to make restrictions, such as, a line can only contain one 'extension', and cannot contain any other regular SmileBasic code on the same line (otherwise disentangling the two could be hellishly difficult), and the system may be very brittle with regard errors from misused 'extensions' (what should be regarded as a 'syntax error' could easily morph into very different types of errors and problems when you translate from one language to another).
Variable Length Function Parameters
Root / Programming Questions / [.]
kldck_hulCreated:
I'm currently working on an object-oriented language extension based around dictionaries.
I'm trying to find a way to make it possible to write:
eval(Ball("bounce"), arg1, arg2, ...)or
Ball("bounce", arg1, arg2, ...)I currently store functions as strings, and those references can be called with CALL <reference>. I can pass arguments directly to functions this way, and this works. Now enters THE STACK. To evaluate Ball.bounce, I store some variables to the stack. In a first-class language, I would then execute that function and restore the previous state of the stack. However, in smileBasic, I have not found a way to pass in an unknown number of arguments. This means I have to return a raw string that the user can then call with CALL. Now I have no way to intercept when the call actually takes place to restore the stack state. I'm currently thinking about two ways to fix this and neither is optimal:
- * Generate a range of eval functions, each taking a specific number of parameters - e.g. eval1() to eval22()
I could also make EVAL take a string argument that can be tokenized and passed to the appropriate evalN()
* Add syntactic sugar that replaces the END command of any exported DEF with a call to restore the stack, first.
This is much uglier than just putting the values in a list inside parentheses after the function name.Thats pretty much the conclusion I'm coming to talking this over in chat...
Another approach might be to have a preprocessor.I've actually already done this. The problem with your approach though, is that a main program can't load the library, the library has to load the main program. I'm trying to write everything in such a way, that the entry point is the main program, and the syntax is just basic. This avoids a lot of the tangled errors. EDIT: I think I'm going to have it be part of the source traversal, when it leaves a function, it'll call the stack restore code. Is there any way to force the user to call the function immediately? If they instead store the reference and call it later, the stack will be in an inconsistent state.
a main program can't load the libraryYou could just make it a requirement of your system that the first two instructions that get executed are LOAD "PRG1:EXTENSIONS":EXEC 1, and have a label @BEGIN where execution should begin. Then, have your program copy from slot 0 to slot 2, processing as it goes, and the last thing EXTENSIONS does is USE 2:GOTO "2:@BEGIN". That way, the source program loads the code for handling extensions, like you want.