Question about implementing functions calls in an intrepreter - interpreter

Consider the pseudo code:
function test()
print ('First Method')
test()
When the interpreter reads this code it enters the symbol 'test' into the symbol table then compiles the test() function into bytecode
When it comes to test(), it first looks up test in the symbol table, finds it, and emits code to push that symbol onto the VM stack. It then sees () and emits a 'call' bytecode. We end up with this sequence of calls
push test
call
What happens if we also have a second test declared in the same scripts such as:
function test()
print ('First Method')
test()
function test()
print ('Second Method')
Byte code:
push test
call
push test <- but which test?
When the interpreter encounters the second function, it has the same name as the first function, what do we do? If we replace the symbol table entry with the new test function we lose the entry that the test() call was relying on. I noticed that interpreters such as R and Python preserve the call to the original test function.
My question is how is this done? The only way I can think of is that there is some kind of scoping going on. Thus the first function and the call to test() all are in one scope with its own symbol table and the second function is in a separate scope and has its own symbol table. The second scoping would also have to be inside the first scope. For example
function test()
print ('First Method')
test()
x = "This is in the first scope"
function test()
print ('Second Method')
print (x)
The print (x) would first search for the symbol x in the local scope, then in the enclosing scope.

Related

Passing arguments from Visio ShapeSheet to procedure in VBA

I have been working with Visio VBA for a couple of years. I pass arguments from the ShapeSheet of a shape to several procedures in VBA using either RUNMACRO() or CALLTHIS() Functions.
I either pass pass plain strings or the value of different formulas in the ShapeSheet. The most common argument that I pass to my procedures is the ID() of the shape.
After a patch update of Windows: https://support.microsoft.com/en-us/topic/march-15-2021-kb5001566-os-build-18363-1441-out-of-band-23c4c824-8638-43e9-a381-ff58213ae6fe, I am no longer able to pass arguments from the ShapeSheet of a Shape to my procedures in VBA. Whenever I use RUNMACRO or CALLTHIS, all the arguments that I pass, even if they are plain strings, in my procedure side everything that I get is "", blank information.
Is there a way to know if this is a bug or if this is on purpose, maybe as a security measure?
Here is a simple example procedure:
Public Function HelloWorld(Number As Integer)
If Number = 1 Then
MsgBox ("Hello World 1")
End If
If Number = 2 Then
MsgBox ("Hello World 2")
End If
End Function
And this procedure is called in the EventDblClick of a Shape:
RUNMACRO("HelloWorld(1)","Test")
When the EventDblClick is triggered the value of Number stays as null, ignoring the argument sent in the RUNMACRO function, which is a 1. Since the argument of Number in the HelloWorld() function is not optional, it triggers a Compile error message
"Argument not optional"
My programs were also recently affected by this update. Previously, I was able to structure the ‘macroname’ string in RUNMACRO (macroname [,projname_opt]) to include arguments to my functions and subroutines (as in Alexis’ HelloWorld example). However, that suddenly seems to be no longer possible. It is almost as if RUNMACRO is now modifying the ‘macroname’ string to exclude any arguments prior to making the call to the Function or Subroutine.
One workaround that I have found is to use CALLTHIS and restructure the VBA Function or Subroutine slightly as shown below. That said, it is going to be a huge hassle for me to execute this change in every cell, in every shape, in every file using the RUNMACRO formula. I’m going to have to write a separate macro just to execute this change.
Workaround:
CALLTHIS(HelloWorld,,1)
[NOTE: The two commas are intentional.]
Public Function HelloWorld(callingShape as Visio.Shape, number as Integer)
‘Insert Code Here
End Function

Change color of created plane [duplicate]

I am getting the 800A0414 error in lines 7 and 12 of this script:
Module Module1
Dim p
Sub Main()
CreateObject("Wscript.Shell").Run("program.bat", 0, True)
p = Process.GetProcessesByName("program")
If p.Count > 0 Then
WScript.Sleep(300000)
Else
CreateObject("Wscript.Shell").Run("program clean up.bat", 0, True)
End If
End Sub
Private Function WScript() As Object
Throw New NotImplementedException
End Function
End Module
I am trying to run a batch script, that starts a process, then wait until the process terminates, then run another batch script. I also do not want any command boxes being shown. If their is a easier way please let me know.
Thanks for your help
When you enclose a procedure's argument list in parentheses, you must use the Call keyword:
Call CreateObject("WScript.Shell").Run("program.bat", 0, True)
If you omit the Call keyword, you must also drop parentheses:
CreateObject("WScript.Shell").Run "program.bat", 0, True
To complete what's been said before:
When Call keyword is used to call a procedure (i.e. sub or function) the arguments must be enclosed in parentheses, except when the procedure has no arguments in which case the parentheses are optional. For example all the statements:
Call test()
Call test
Call test(1,2)
are valid, but not this one:
Call test 1
When calling a procedure without using the Call keyword, the parentheses can only be used when either the procedure has zero or one argument or the procedure has a return value (i.e. is a function) and its value is used in the same statement. For example all the statements:
test()
test(1)
test(1,2)
a = test
a = test(1,2)
a = test(test(1,2),2)
are valid, except the third one which has more than one argument. In case it's not clear, the inner call of "test" in the last statement is valid because its return value is used as an argument to another call.
Note that whenever parentheses is used in this text, it is meant to imply the possible comma-separated values as well.
Seems to me this is a VB.NET, not VBScript code.
You have Shell function in VB.NET (and other methods).
Anyway, Run returns any error code returned by the program, and if you
store that result in a variable, you can use parentheses in this case.
Dim lResult As Long
lResult = CreateObject("Wscript.Shell").Run("program.bat", 0, True)
The rest was answered by #Helen.

How to call a sub routine of one form from other form?

i'm a new in vb.net, i would like to ask if how can I call a sub routine of the main form from another form.
I tried putting the prefix of the main form( main.subroutinename() ), it doesn't show an error when compiled but it doesn't work either.
As Microsoft says:
Calling syntax
You invoke a Sub procedure explicitly with a stand-alone calling statement. You cannot call it by using its name in an expression. You must provide values for all arguments that are not optional, and you must enclose the argument list in parentheses. If no arguments are supplied, you can optionally omit the parentheses. The use of the Call keyword is optional but not recommended.
The syntax for a call to a Sub procedure is as follows:
[Call] SubName[(argumentlist)]
You can call a Sub method from outside the class that defines it. First, you have to use the New keyword to create an instance of the class, or call a method that returns an instance of the class. For more information, see New Operator. Then, you can use the following syntax to call the Sub method on the instance object:
Or you can create a Sub Routine in a Module making it visible (Friend or Public, etc) then calling it inside entire the Namespace that contain your Module
object.MethodName[(argumentList)]
Illustration of declaration and call
The following Sub procedure tells the computer operator which task the application is about to perform, and also displays a time stamp. Instead of duplicating this code at the start of every task, the application just calls tellOperator from various locations. Each call passes a string in the task argument that identifies the task being started.
Sub tellOperator(ByVal task As String)
Dim stamp As Date
stamp = TimeOfDay()
MsgBox("Starting " & task & " at " & CStr(stamp))
End Sub
The following example shows a typical call to tellOperator.
tellOperator("file update")

String Function with No Paramaters Invoked with Int Should Fail Build

We have a VB.NET project which I am doing some refactoring in. A lot of current functions require a userId to be passed in as a parameter. For example:
Private Function Foo(userId As Integer) As String
Return "Foo"
End Function
But now, I have made this userId paramater no longer needed in much of our code.
I was thinking I would just remove all the userId parameters from all of the functions that don't need them anymore, and try re-building the project to see what calling code needs to change. To my surprise though, the code built successfully.
I learned that you can call a function which returns a string and doesn't take any parameters, but still pass in an Integer value. VB.NET builds/executes this code as if you were trying to invoke the function and then get the character from the String at the specified index. For example:
Imports System
Public Module MyModule
Private Function Foo() As String
Return "Foo"
End Function
Public Sub Main()
Console.WriteLine(Foo(0)) ' prints 'F'
Console.WriteLine(Foo(1)) ' prints 'o'
End Sub
End Module
(.NET Fiddle)
I want my build to fail in this case. I have tried using Option Strict On and Option Explicit On but neither seem to change this behaviour.
Is there anyway to make this kind of function invocation invalid in my VB.NET project?
Create an overload with no parameters which is a copy of the original function in every other respect.
Public Function Foo() As String
Return "Foo"
End Function
Now change the type of userId to something else (not Object or anything numeric etc.)
Public Function Foo(userId As DateTime) As String
Return "Foo"
End Function
Since there is an overload with a parameter, the compiler thinks you mean to call that, instead of indexing the chararray. So you have compile errors. Fix the errors
Then delete the original function.

How do I pass a range obj variable to a sub in Excel VBA (2016) [duplicate]

This question already has an answer here:
Array argument must be ByRef
(1 answer)
Closed 6 years ago.
Given the following code:
I can not seem to successfully pass a Range Object Variable from one sub-function to another. I spent an entire day researching, and experimenting before I swallowed pride and came here.
Please read the comments below, and reply with any ideas you have regarding why the LAST two lines will not behave.
Public Sub doSomethingToRows(ROI As Range)
*'do Something with the cell values within the supplied range*
End Sub
'
Public Sub testDoAltRows()
Dim RegionOfInterest As Range 'is this an object or not?
'*The following yields: Class doesn't support Automation (Error 430)*
'*Set RegionOfInterest = New Worksheet 'this just gives an error*
Set RegionOfInterest = Worksheets("Sheet1").Range("A1")
RegionOfInterest.Value = 1234.56 '*okay, updates cell A1*
Set RegionOfInterest = Worksheets("Sheet1").Range("B5:D15")
RegionOfInterest.Columns(2).Value = "~~~~~~" '*okay*
'doSomethingToRows (RegionOfInterest) 'why do I get "OBJECT IS REQUIRED" error?
doSomethingToRows (Worksheets("Sheet1").Range("B5:C15")) 'but this executes okay
End Sub
From the msdn documentation of the Call keyword statement,
Remarks
You are not required to use the Call keyword when calling a procedure.
However, if you use the Call keyword to call a procedure that requires
arguments, argumentlist must be enclosed in parentheses. If you omit
the Call keyword, you also must omit the parentheses around
argumentlist. If you use either Call syntax to call any intrinsic or
user-defined function, the function's return value is discarded.
To pass a whole array to a procedure, use the array name followed by
empty parentheses.
From a practical standpoint, even though Subs can be called with or without the "Call" keyword, it makes sense to pick one way and stick with it as part of your coding style. I agree with Comintern - it is my opinion, based on observation of modern VBA code, that using the "Call" keyword should be considered deprecated. Instead, invoke Subs without parenthesis around the argument list.
And now the answer to the important question:
Why does your code throw an error?
Take for example the following Subroutine:
Public Sub ShowSum(arg1 As Long, arg2 As Long)
MsgBox arg1 + arg2
End Sub
We have established that, if not using the Call keyword, Subs must be invoked like so:
ShowSum 45, 37
What happens if it were instead called like ShowSum(45, 37)? Well, you wouldn't even be able to compile as VBA immediately complains "Expected =". This is because the VBA parser sees the parenthesis and decides that this must be a Function call, and it therefore expects you to be handling the return value with an "=" assignment statement.
What about a Sub with only one argument? For example:
Public Sub ShowNum(arg1 As Long)
MsgBox arg1
End Sub
The correct way to call this Sub is ShowNum 45. But what if you typed this into the VBA IDE: ShowNum(45)? As soon as you move the cursor off of the line, you'll notice that VBA adds a space between the Sub name and the opening parenthesis, giving you a crucial clue as to how the line of code is actually being interpreted:
ShowNum (45)
VBA is not treating those parenthesis as if they surrounded the argument list - it is instead treating them as grouping parenthesis. MOST of the time, this wouldn't matter, but it does in the case of Objects which have a default member.
To see the problem this causes, try running the following:
Dim v As Variant
Set v = Range("A1")
Set v = (Range("A1")) '<--- type mismatch here
Notice that you get a "Type Mismatch" on the marked line. Now add those two statements to the watch window and look at the "Type" column:
+-------------+-----+--------------+
| Expression |Value| Type |
+-------------+-----+--------------+
|Range("A1") | |Object/Range |
|(Range("A1"))| |Variant/String|
+-------------+-----+--------------+
When you surround an Object with grouping parenthesis, its default property is evaluated - in the case of the Range object, it is the Value property.
So it's really just a coincidence that VBA allowed you to get away with "putting parenthesis around the argumentlist" - really, VBA just interprets this as grouping parenthesis and evaluates the value accordingly. You can see by trying the same thing on a Sub with multiple parameters that it is invalid in VBA to invoke a Sub with parenthesis around the argument list.
#PaulG
Try this:
Public Sub Main()
Debug.Print TypeName(Range("A1"))
Debug.Print TypeName((Range("A1")))
End Sub
okay, I knew after I posted this question I'd be struck by lighting and receive an answer.
When passing an object VARIABLE to a sub-function and wishing to use parentheses "()", one must use CALL! Thus the correction to my code sample is:
**CALL doSomethingToRows(RegionOfInterest)**
Thank you!
Maybe we're talking about different things, but here's an example to make it a bit clearer what I mean.
Option Explicit
Sub TestDisplay()
Dim r As Range
'Create some range object
Set r = Range("A1")
'Invoke with Call.
Call DisplaySomething(r)
'Invoke without Call.
DisplaySomething r
End Sub
Sub DisplaySomething(ByVal Data As Range)
Debug.Print "Hi my type is " & TypeName(Data)
End Sub
Both calls work perfectly. One with Call and the other without.
Edit:
#Conintern. Thanks for explaining that. I see what is meant now.
However, I still respectively disagree.
If I declare the following:
Function DisplaySomething(ByVal Data As String)
DisplaySomething = "Hi my type is " & TypeName(Data)
End Function
and invoke it:
Debug.print DisplaySomething(Range("A1"))
I believe that Excel has been clever and converted to a string. It can do that by invoking the Default Parameter and can convert to a string.
However, as in the original parameter example, If I declare the following:
Function DisplaySomething(ByVal Data As Range)
DisplaySomething = "Hi my type is " & TypeName(Data)
End Function
There is no call on the Default Parameter, however it is called, because Excel was able to resolve it to that type.
Function DisplaySomething(ByVal Data As Double)
DisplaySomething = "Hi my type is " & TypeName(Data)
End Function
will return a double because it was able to coerce to a double.
Indeed in those examples the Default was called.
But in this example we are defining as Range. No Default called there however it is invoked - brackets or no brackets.
I believe this is more to do with Excel and data coercion.
Similar to the following:
Public Function Test(ByVal i As String) As Integer
Test = i
End Function
and invoking with:
Debug.print Test("1")
BTW, yes I know this isn't an object without a Default parmeter. Im pointing out data coercion. Excel does its best to resolve it.
Could be wrong mind you...