Re: 2 Pass Binder
- Posted by JJProg at CYBERBURY.NET Aug 18, 1999
- 405 views
EU>JJProg at CYBERBURY.NET wrote: EU>>EU> routine_name = commands[choice] EU>> EU>>EU> ? call_func (routine_id (routine_name), {operand}) EU>> EU>>EU>Now, how could I expect the binder to know to leave in the two EU>>EU>functions above? It HAS to leave both in the code, even though it EU>>EU>can't tell if either of them will ever be used. EU>[...] EU>>With this, though, a smart binder could do the following: EU>>1. see that commands is a constant EU>>2. see that the argument to routine_id is an element of commands EU>>3. know to leave in any routines named in commands EU>Well, that's theoretically true, and that sort of behavior would EU>be desireable. But just as another example of how tricky this could EU>get... before sending my email, I looked over the code and realized EU>that, silly me, I had left "commands" as a local variable. Since in EU>my example I knew it wasn't going to change, I made it a constant EU>"for efficiency purposes." EU>The problem is, leaving "commands" as a variable, even modifying it EU>later on (to add other commands once the user clicks on a checkbox, EU>etc.) is an completely valid approach. EU>Now I doubt *I'd* write a program like this, but if it's EU>syntactically and logically valid, why would it be wrong for someone EU>else to do so? That's when the problems begin. The only way to get EU>around this seems to be somehow limiting the programmer's ability to EU>write such code--probably by eliminating the ability to pass variables EU>to routine_id. Not nessicarily. If the binder couldn't be sure that a particular routine wasn't used, it would just have to include it anyway. It would still be able to get rid of routines that it was sure wasn't used. Also, a really smart interpreter could see where the variable is modified that stores the names of the routines, and try to figure out what is used from there. EU>Sure, you could try to make the binder smarter still, but even if EU>*this* case can be caught, the variations are nearly infinite... EU>you'll eventually reach a point at which the binder can't handle it. EU>Suppose your referenced routines consist of: EU> commandname_parametertype EU>and the name of the function to call is constructed at runtime EU>based on options the user selects? Or, what if you want to test EU>individual routines by directly inputing their names and EU>executing them? EU>"No one does any of that!" PROBABLY true. But it's valid, isn't it? EU>And if it's valid, then it's a situation that has to be handled. EU>The point is, there's no way to correctly evaluate all possible EU>contexes (?) when "routine_id (var)" is encountered. The binder EU>would have to back up through every place where "var" is set, EU>everywhere where a slice of "var" is set, every location where EU>a variable that var is set to gets set. In this miniscule example EU>alone, the binder would have to back up through: Actually, I have used dynamic construction of routine names to spcify the types of arguments. I did this to construct an Euphoria interpreter for my compiled Euphoria bytecodes (I've been working on a Euphoria assembly language and a compiler and interpreter for it). Still, a smart binder could figure it out because it would see: 1. I start out with a constant name 2. I add either 'l' or 'v' to the end through a for loop and if statements depending on the type of each argument. 3. The number of arguments is constant. I agree with you, though, that if the routine name was totally unpredictable (like using gets(0) to get a routine name), the binder would not be able to scrap _any_ routines _in the current scope_. For example: include somefile.e procedure a() ... end procedure procedure b() ... end procedure sequence name name = gets(0) call_proc(routine_id(name[1..length(name)-1]),{}) The binder couldn't get rid of a, b, or any global routines defined in somefile.e. It could get rid of any non-global routines in somefile.e that aren't referenced by any of the global routines. EU> routine_id (routine_name) --> EU> routine_name = commands[choice] --> EU> constant commands = {...} --> EU> BINDER: good, "commands" is a constant. Is "choice"? --> EU> choice = get_number (0) --> (assume get_number is built-in, EU> or things get worse...) EU> BINDER: no, "choice" is always undetermined. We need to EU> account for every element of "commands" --> EU> BINDER: adds routine named commands[1] to the table EU> ... EU> BINDER: adds routine named commands[length(commands)] to the table EU>I imagine that for larger programs, or use of expressions (&, etc.), EU>the complexity could increase quite rapidly. Even then, you're EU>still making some assumptions that may not be valid. And again, EU>heaven forbid that after backing through all assignments you wind EU>up not having enough data to construct all potential literals. EU>It seems clear that you'd be better off just leaving all routines EU>in rather than taking this approach. You'll either wind up limiting EU>routine_id, or only being able to do this on programs that have no EU>calls to routine_id, or leaving open the possibility of writing EU>programs that occasionally give "invalid routine name" errors at EU>runtime. EU>Rod Jeffrey Fielding JJProg at cyberbury.net http://members.tripod.com/~JJProg/