From 0f8806ed9fc30ac26dd215f8e873655b343a28c2 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 15:17:56 +0200 Subject: [PATCH 01/10] Document If-Then-Else, Select-Case, While-Wend, With and Stop. --- docs/Reference/Core/Exit.md | 5 +- docs/Reference/Core/If-Then-Else.md | 102 ++++++++++++++++++++++++++++ docs/Reference/Core/Select-Case.md | 86 +++++++++++++++++++++++ docs/Reference/Core/Stop.md | 36 ++++++++++ docs/Reference/Core/While-Wend.md | 56 +++++++++++++++ docs/Reference/Core/With.md | 59 ++++++++++++++++ docs/Reference/Core/todo.md | 5 -- docs/Reference/Statements.md | 10 +-- 8 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 docs/Reference/Core/If-Then-Else.md create mode 100644 docs/Reference/Core/Select-Case.md create mode 100644 docs/Reference/Core/Stop.md create mode 100644 docs/Reference/Core/While-Wend.md create mode 100644 docs/Reference/Core/With.md diff --git a/docs/Reference/Core/Exit.md b/docs/Reference/Core/Exit.md index 9ef912f..615c4cf 100644 --- a/docs/Reference/Core/Exit.md +++ b/docs/Reference/Core/Exit.md @@ -6,7 +6,7 @@ permalink: /tB/Core/Exit # Exit {: .no_toc } -Exits a block of **Do…Loop**, **For…Next**, **Function**, **Sub**, or **Property** code. +Exits a block of **Do…Loop**, **For…Next**, **While...Wend**, **Function**, **Sub**, or **Property** code. Syntax: @@ -16,6 +16,9 @@ Syntax: - **Exit For** Provides a way to exit a **For** loop. It can be used only in a **[For...Next](For-Next)** or **[For Each...Next](For-Next)** loop. **Exit For** transfers control to the statement following the **Next** statement. When used within nested **For** loops, **Exit For** transfers control to the loop that is one nested level above the loop where **Exit For** occurs. +- **Exit While** + Provides a way to exit a **[While...Wend](While-Wend)** loop. It can be used only inside a **While...Wend** statement. **Exit While** transfers control to the statement following the **Wend** statement. When used within nested **While...Wend** statements, **Exit While** transfers control to the loop that is one nested level above the loop where **Exit While** occurs. **Exit While** is a twinBASIC extension; classic VBA has no early-exit form for **While...Wend**. + - **Exit Function** Immediately exits the **[Function](Function)** procedure in which it appears. Execution continues with the statement following the statement that called the **Function**. diff --git a/docs/Reference/Core/If-Then-Else.md b/docs/Reference/Core/If-Then-Else.md new file mode 100644 index 0000000..bd9d18a --- /dev/null +++ b/docs/Reference/Core/If-Then-Else.md @@ -0,0 +1,102 @@ +--- +title: If...Then...Else +parent: Statements +permalink: /tB/Core/If-Then-Else +--- + +# If...Then...Else +{: .no_toc } + +Conditionally executes a group of statements, depending on the value of an expression. + +Syntax: + +- > **If** *condition* **Then** [ *statements* ] [ **Else** *elsestatements* ] +- > **If** *condition* **Then** + >     [ *statements* ] + > [ **ElseIf** *condition-n* **Then** + >     [ *elseifstatements* ] ] + > [ **Else** + >     [ *elsestatements* ] ] + > **End If** + +*condition* +: One or more of the following two types of expressions: + - A numeric expression or string expression that evaluates to **True** or **False**. If *condition* is Null, *condition* is treated as **False**. + - An expression of the form **TypeOf** *objectname* **Is** *objecttype*. *objectname* is any object reference, and *objecttype* is any valid object type. The expression is **True** if *objectname* is of the object type specified by *objecttype*; otherwise it is **False**. + +*statements* +: Optional in block form; required in single-line form that has no **Else** clause. One or more statements separated by colons; executed if *condition* is **True**. + +*condition-n* +: *optional* Same as *condition*. + +*elseifstatements* +: *optional* One or more statements executed if the associated *condition-n* is **True**. + +*elsestatements* +: *optional* One or more statements executed if no previous *condition* or *condition-n* expression is **True**. + +Use the single-line form (first syntax) for short, simple tests. The block form (second syntax) provides more structure and flexibility than the single-line form and is usually easier to read, maintain, and debug. + +> [!NOTE] +> With the single-line form, it is possible to have multiple statements executed as the result of an **If...Then** decision. All statements must be on the same line and separated by colons, as in the following statement: +> +> ```tb +> If A > 10 Then A = A + 1 : B = B + A : C = C + B +> ``` + +A block form **If** statement must be the first statement on a line. The **Else**, **ElseIf**, and **End If** parts of the statement can have only a line number or line label preceding them. The block **If** must end with an **End If** statement. + +To determine whether or not a statement is a block **If**, examine what follows the **Then** keyword. If anything other than a comment appears after **Then** on the same line, the statement is treated as a single-line **If** statement. + +The **Else** and **ElseIf** clauses are both optional. You can have as many **ElseIf** clauses as you want in a block **If**, but none can appear after an **Else** clause. Block **If** statements can be nested; that is, contained within one another. + +When executing a block **If** (second syntax), *condition* is tested. If *condition* is **True**, the statements following **Then** are executed. If *condition* is **False**, each **ElseIf** condition (if any) is evaluated in turn. When a **True** condition is found, the statements immediately following the associated **Then** are executed. If none of the **ElseIf** conditions are **True** (or if there are no **ElseIf** clauses), the statements following **Else** are executed. After executing the statements following **Then** or **Else**, execution continues with the statement following **End If**. + +> [!TIP] +> [**Select Case**](Select-Case) may be more useful when evaluating a single expression that has several possible actions. However, the **TypeOf** *objectname* **Is** *objecttype* clause can't be used with the **Select Case** statement. + +> [!NOTE] +> **TypeOf** cannot be used with hard data types such as **Long**, **Integer**, and so forth other than **Object**. + +### Example + +This example shows both the block and single-line forms of the **If...Then...Else** statement. It also illustrates the use of **If TypeOf...Then...Else**. + +```tb +Dim Number, Digits, MyString +Number = 53 ' Initialize variable. +If Number < 10 Then + Digits = 1 +ElseIf Number < 100 Then + ' Condition evaluates to True so the next statement is executed. + Digits = 2 +Else + Digits = 3 +End If + +' Assign a value using the single-line form of syntax. +If Digits = 1 Then MyString = "One" Else MyString = "More than one" +``` + +Use the **If TypeOf** construct to determine whether the Control passed into a procedure is a particular kind of control. + +```tb +Sub ControlProcessor(MyControl As Control) + If TypeOf MyControl Is CommandButton Then + Debug.Print "You passed in a " & TypeName(MyControl) + ElseIf TypeOf MyControl Is CheckBox Then + Debug.Print "You passed in a " & TypeName(MyControl) + ElseIf TypeOf MyControl Is TextBox Then + Debug.Print "You passed in a " & TypeName(MyControl) + End If +End Sub +``` + +### See Also + +- [**Select Case** statement](Select-Case) +- [**#If...Then...Else** directive](Topic-Preprocessor) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Select-Case.md b/docs/Reference/Core/Select-Case.md new file mode 100644 index 0000000..93a4ef6 --- /dev/null +++ b/docs/Reference/Core/Select-Case.md @@ -0,0 +1,86 @@ +--- +title: Select Case +parent: Statements +permalink: /tB/Core/Select-Case +--- + +# Select Case +{: .no_toc } + +Executes one of several groups of statements, depending on the value of an expression. + +Syntax: + +> **Select Case** *testexpression* +>     [ **Case** *expressionlist-n* +>         [ *statements-n* ] ] ... +>     [ **Case Else** +>         [ *elsestatements* ] ] +> **End Select** + +*testexpression* +: Any numeric expression or string expression. + +*expressionlist-n* +: Required if a **Case** appears. A delimited list of one or more of the following forms: + - *expression* + - *expression* **To** *expression* + - **Is** *comparisonoperator* *expression* + + The **To** keyword specifies a range of values. If you use the **To** keyword, the smaller value must appear before **To**. + + Use the **Is** keyword with comparison operators (except **Is** and **Like**) to specify a range of values. If not supplied, the **Is** keyword is automatically inserted. + +*statements-n* +: *optional* One or more statements executed if *testexpression* matches any part of *expressionlist-n*. + +*elsestatements* +: *optional* One or more statements executed if *testexpression* doesn't match any of the **Case** clauses. + +If *testexpression* matches any **Case** *expressionlist* expression, the *statements* following that **Case** clause are executed up to the next **Case** clause, or, for the last clause, up to **End Select**. Control then passes to the statement following **End Select**. If *testexpression* matches an *expressionlist* expression in more than one **Case** clause, only the statements following the first match are executed. + +The **Case Else** clause is used to indicate the *elsestatements* to be executed if no match is found between the *testexpression* and an *expressionlist* in any of the other **Case** selections. Although not required, it is a good idea to have a **Case Else** statement in your **Select Case** block to handle unforeseen *testexpression* values. If no **Case** *expressionlist* matches *testexpression* and there is no **Case Else** statement, execution continues at the statement following **End Select**. + +You can use multiple expressions or ranges in each **Case** clause. For example, the following line is valid: + +```tb +Case 1 To 4, 7 To 9, 11, 13, Is > MaxNumber +``` + +> [!NOTE] +> The **Is** comparison operator is not the same as the **Is** keyword used in the **Select Case** statement. + +You can also specify ranges and multiple expressions for character strings. In the following example, **Case** matches strings that are exactly equal to `everything`, strings that fall between `nuts` and `soup` in alphabetic order, and the current value of `TestItem`: + +```tb +Case "everything", "nuts" To "soup", TestItem +``` + +**Select Case** statements can be nested. Each nested **Select Case** statement must have a matching **End Select** statement. + +### Example + +This example uses the **Select Case** statement to evaluate the value of a variable. The second **Case** clause contains the value of the variable being evaluated, and therefore only the statement associated with it is executed. + +```tb +Dim Number +Number = 8 ' Initialize variable. +Select Case Number ' Evaluate Number. + Case 1 To 5 ' Number between 1 and 5, inclusive. + Debug.Print "Between 1 and 5" + ' The following is the only Case clause that evaluates to True. + Case 6, 7, 8 ' Number between 6 and 8. + Debug.Print "Between 6 and 8" + Case 9 To 10 ' Number is 9 or 10. + Debug.Print "Greater than 8" + Case Else ' Other values. + Debug.Print "Not between 1 and 10" +End Select +``` + +### See Also + +- [**If...Then...Else** statement](If-Then-Else) +- [**Do...Loop** statement](Do-Loop) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Stop.md b/docs/Reference/Core/Stop.md new file mode 100644 index 0000000..9d4f195 --- /dev/null +++ b/docs/Reference/Core/Stop.md @@ -0,0 +1,36 @@ +--- +title: Stop +parent: Statements +permalink: /tB/Core/Stop +--- + +# Stop +{: .no_toc } + +Suspends execution. + +Syntax: + +> **Stop** + +You can place **Stop** statements anywhere in procedures to suspend execution. Using the **Stop** statement is similar to setting a breakpoint in the code. + +The **Stop** statement suspends execution, but unlike [**End**](End), it doesn't close any files or clear variables, unless it is in a compiled executable (.exe) file. + +### Example + +This example uses the **Stop** statement to suspend execution for each iteration through the **For...Next** loop. + +```tb +Dim i As Long +For i = 1 To 10 ' Start For...Next loop. + Debug.Print i ' Print i to the Immediate window. + Stop ' Stop during each iteration. +Next i +``` + +### See Also + +- [**End** statement](End) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/While-Wend.md b/docs/Reference/Core/While-Wend.md new file mode 100644 index 0000000..3e0cb8c --- /dev/null +++ b/docs/Reference/Core/While-Wend.md @@ -0,0 +1,56 @@ +--- +title: While...Wend +parent: Statements +permalink: /tB/Core/While-Wend +--- + +# While...Wend +{: .no_toc } + +Executes a series of statements as long as a given condition is **True**. + +Syntax: + +> **While** *condition* +>     [ *statements* ] +> **Wend** + +*condition* +: Numeric expression or string expression that evaluates to **True** or **False**. If *condition* is Null, *condition* is treated as **False**. + +*statements* +: *optional* One or more statements executed while *condition* is **True**. + +If *condition* is **True**, all *statements* are executed until the **Wend** statement is encountered. Control then returns to the **While** statement and *condition* is again checked. If *condition* is still **True**, the process is repeated. If it is not **True**, execution resumes with the statement following the **Wend** statement. + +**While...Wend** loops can be nested to any level. Each **Wend** matches the most recent **While**. + +Any number of [**Exit While**](Exit) statements may be placed anywhere in the **While...Wend** loop as a way to exit early. **Exit While** is often used after evaluating some condition, in which case it transfers control to the statement immediately following the **Wend**. + +Any number of [**Continue While**](Continue) statements may be placed anywhere in the **While...Wend** loop to skip the rest of the statements and proceed with a new iteration. + +> [!NOTE] +> **Exit While** and **Continue While** are twinBASIC extensions. Classic VBA has no early-exit or skip-iteration form for **While...Wend** loops. + +> [!TIP] +> The [**Do...Loop**](Do-Loop) statement provides a more structured and flexible way to perform looping. + +### Example + +This example uses the **While...Wend** statement to increment a counter variable. The statements in the loop are executed as long as the condition evaluates to **True**. + +```tb +Dim Counter +Counter = 0 ' Initialize variable. +While Counter < 20 ' Test value of Counter. + Counter = Counter + 1 ' Increment Counter. +Wend ' End While loop when Counter > 19. +Debug.Print Counter ' Prints 20 in the Immediate window. +``` + +### See Also + +- [**Do...Loop** statement](Do-Loop) +- [**For...Next** statement](For-Next) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/With.md b/docs/Reference/Core/With.md new file mode 100644 index 0000000..cf7b851 --- /dev/null +++ b/docs/Reference/Core/With.md @@ -0,0 +1,59 @@ +--- +title: With +parent: Statements +permalink: /tB/Core/With +--- + +# With +{: .no_toc } + +Executes a series of statements on a single object or a user-defined type. + +Syntax: + +> **With** *object* +>     [ *statements* ] +> **End With** + +*object* +: Name of an object or a user-defined type. + +*statements* +: *optional* One or more statements to be executed on *object*. + +The **With** statement allows you to perform a series of statements on a specified object without requalifying the name of the object. For example, to change a number of different properties on a single object, place the property assignment statements within the **With** control structure, referring to the object once instead of referring to it with each property assignment. + +The following example illustrates use of the **With** statement to assign values to several properties of the same object. + +```tb +With MyLabel + .Height = 2000 + .Width = 2000 + .Caption = "This is MyLabel" +End With +``` + +> [!NOTE] +> Once a **With** block is entered, *object* can't be changed. As a result, you can't use a single **With** statement to affect a number of different objects. + +You can nest **With** statements by placing one **With** block within another. However, because members of outer **With** blocks are masked within the inner **With** blocks, you must provide a fully qualified object reference in an inner **With** block to any member of an object in an outer **With** block. + +> [!NOTE] +> In general, it's recommended that you don't jump into or out of **With** blocks. If statements in a **With** block are executed, but either the **With** or **End With** statement is not executed, a temporary variable containing a reference to the object remains in memory until you exit the procedure. + +### Example + +This example uses the **With** statement to execute a series of statements on a single object. The object and its properties are generic names used for illustration purposes only. + +```tb +With MyObject + .Height = 100 ' Same as MyObject.Height = 100. + .Caption = "Hello World" ' Same as MyObject.Caption = "Hello World". + With .Font + .Color = Red ' Same as MyObject.Font.Color = Red. + .Bold = True ' Same as MyObject.Font.Bold = True. + End With +End With +``` + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index 5541b9d..2ce0caf 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -8,7 +8,6 @@ redirect_from: - /tB/Core/Get - /tB/Core/GoSub-Return - /tB/Core/GoTo - - /tB/Core/If-Then-Else - /tB/Core/Implements - /tB/Core/Input - /tB/Core/Interface @@ -41,16 +40,12 @@ redirect_from: - /tB/Core/Return - /tB/Core/RSet - /tB/Core/SavePicture - - /tB/Core/Select-Case - /tB/Core/Set - /tB/Core/Static - /tB/Core/Sub - - /tB/Core/Stop - /tB/Core/Type - /tB/Core/Unload - /tB/Core/Unlock - - /tB/Core/While-Wend - - /tB/Core/With - /tB/Core/Write --- diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 5eb4ecc..c9a0fb6 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -55,7 +55,7 @@ These statements are built into the language itself. They are understood by the * [GoTo](../tB/Core/GoTo) -* [If ... Then ... Else](../tB/Core/If-Then-Else) +* [If ... Then ... Else](../tB/Core/If-Then-Else) -- conditionally executes a group of statements, depending on the value of an expression * [Input](../tB/Core/Input) @@ -109,21 +109,21 @@ These statements are built into the language itself. They are understood by the * [Seek](../tB/Core/Seek) -* [Select Case](../tB/Core/Select-Case) +* [Select Case](../tB/Core/Select-Case) -- executes one of several groups of statements, depending on the value of an expression * [Set](../tB/Core/Set) * [Static](../tB/Core/Static) -* [Stop](../tB/Core/Stop) +* [Stop](../tB/Core/Stop) -- suspends execution * [Sub](../tB/Core/Sub) * [Unlock](../tB/Core/Unlock) -* [While ... Wend](../tB/Core/While-Wend) +* [While ... Wend](../tB/Core/While-Wend) -- executes a series of statements as long as a given condition is **True** -* [With](../tB/Core/With) +* [With](../tB/Core/With) -- executes a series of statements on a single object or a user-defined type * [Write](../tB/Core/Write) From 9eeb311effcf7c2cfa0bfbc854aeea190b44e4c3 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 15:55:27 +0200 Subject: [PATCH 02/10] Document Sub, Property, Public, Private, Static and ParamArray. --- docs/Reference/Core/ParamArray.md | 71 ++++++++++++ docs/Reference/Core/Private.md | 74 ++++++++++++ docs/Reference/Core/Property.md | 181 ++++++++++++++++++++++++++++++ docs/Reference/Core/Public.md | 74 ++++++++++++ docs/Reference/Core/Static.md | 84 ++++++++++++++ docs/Reference/Core/Sub.md | 130 +++++++++++++++++++++ docs/Reference/Core/todo.md | 6 - docs/Reference/Statements.md | 12 +- 8 files changed, 621 insertions(+), 11 deletions(-) create mode 100644 docs/Reference/Core/ParamArray.md create mode 100644 docs/Reference/Core/Private.md create mode 100644 docs/Reference/Core/Property.md create mode 100644 docs/Reference/Core/Public.md create mode 100644 docs/Reference/Core/Static.md create mode 100644 docs/Reference/Core/Sub.md diff --git a/docs/Reference/Core/ParamArray.md b/docs/Reference/Core/ParamArray.md new file mode 100644 index 0000000..a47d3a2 --- /dev/null +++ b/docs/Reference/Core/ParamArray.md @@ -0,0 +1,71 @@ +--- +title: ParamArray +parent: Statements +permalink: /tB/Core/ParamArray +--- +# ParamArray +{: .no_toc } + +Used in the argument list of a [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) procedure to indicate that the final parameter is an open-ended list of arguments. The **ParamArray** keyword allows the procedure to accept an arbitrary number of arguments at the call site. + +Syntax: +> [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Sub** \| **Function** \| **Property Get** \| **Property Let** \| **Property Set** *name* **(** [ *arglist*, ] **ParamArray** *varname*[ **()** ] [ **As** *type* ] **)** + +*varname* +: Name of the variable representing the **ParamArray**; follows standard variable naming conventions. + +*type* +: *optional* Must be **Variant** (explicitly or by default). Each argument supplied at the call site can be of a different data type, so **ParamArray** must always be an array of **Variant** elements. + +**ParamArray** must be the last parameter in the argument list of a **Sub**, **Function**, or **Property Get** procedure. In a **Property Let** or **Property Set** procedure it must precede the *value*/*reference* parameter and so cannot be the only parameter. + +**ParamArray** cannot be combined with **Optional**, **ByVal**, or **ByRef** on the same parameter — arguments supplied to a **ParamArray** are always passed by reference as elements of a **Variant** array. + +When the procedure is called, each argument supplied in the call becomes a corresponding element of the **Variant** array. If no arguments are supplied for the **ParamArray** position, the array is empty. + +> [!NOTE] +> A procedure that defines a **ParamArray** parameter cannot be called using named-argument syntax. All arguments to such a procedure must be positional. To omit individual elements within the **ParamArray** position at a call site, leave the position blank between commas. + +### Example + +This example defines a function that sums an arbitrary number of numeric arguments by using **ParamArray**. + +```tb +Function CalcSum(ParamArray Args() As Variant) As Double + Dim Total As Double, i As Long + For i = LBound(Args) To UBound(Args) + Total = Total + CDbl(Args(i)) + Next i + CalcSum = Total +End Function + +' Calls of varying arity: +Debug.Print CalcSum() ' 0 +Debug.Print CalcSum(1) ' 1 +Debug.Print CalcSum(1, 2, 3, 4) ' 10 +Debug.Print CalcSum(1.5, 2.5, 3#) ' 7 +``` + +A **ParamArray** can follow ordinary positional parameters; only those that come after a fixed leading list participate in the variadic tail. + +```tb +Function Concat(ByVal Separator As String, ParamArray Parts() As Variant) As String + Dim i As Long, s As String + For i = LBound(Parts) To UBound(Parts) + If i > LBound(Parts) Then s = s & Separator + s = s & CStr(Parts(i)) + Next i + Concat = s +End Function + +Debug.Print Concat(", ", "one", "two", "three") ' "one, two, three" +``` + +### See Also + +- [**Sub** statement](Sub) +- [**Function** statement](Function) +- [**Property** statement](Property) +- [**Call** statement](Call) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Private.md b/docs/Reference/Core/Private.md new file mode 100644 index 0000000..75ad711 --- /dev/null +++ b/docs/Reference/Core/Private.md @@ -0,0 +1,74 @@ +--- +title: Private +parent: Statements +permalink: /tB/Core/Private +--- +# Private +{: .no_toc } + +Used at the module level to declare private variables and allocate storage space. + +Syntax: +> **Private** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **,** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ]] **. . .** + +**WithEvents** +: *optional* Keyword that specifies that *varname* is an object variable used to respond to events triggered by an ActiveX object. **WithEvents** is valid only in class modules. You can declare as many individual variables as you like by using **WithEvents**, but you can't create arrays with **WithEvents**, nor can you use **New** with **WithEvents**. + +*varname* +: Name of the variable; follows standard variable naming conventions. + +*subscripts* +: *optional* Dimensions of an array variable; up to 60 multiple dimensions may be declared. The *subscripts* argument uses the following syntax: [ *lower* **To** ] *upper* [ , [ *lower* **To** ] *upper* ] **. . .**. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. + +**New** +: *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects, and it can't be used with **WithEvents**. + +*type* +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. + +**Private** variables are available only to the module in which they are declared. + +Use the **Private** statement to declare the data type of a variable. For example, the following statement declares a variable as an **Integer**: + +```tb +Private NumberOfEmployees As Integer +``` + +You can also use a **Private** statement to declare the object type of a variable. The following statement declares a variable for a new instance of a worksheet: + +```tb +Private X As New Worksheet +``` + +If the **New** keyword isn't used when declaring an object variable, the variable that refers to the object must be assigned an existing object by using the **Set** statement before it can be used. Until it is assigned an object, the declared object variable has the special value **Nothing**, which indicates that it doesn't refer to any particular instance of an object. + +If you don't specify a data type or object type, and there is no [**Deftype**](Deftype) statement in the module, the variable is **Variant** by default. + +You can also use the **Private** statement with empty parentheses to declare a dynamic array. After declaring a dynamic array, use the **[ReDim](ReDim)** statement within a procedure to define the number of dimensions and elements in the array. If you try to redeclare a dimension for an array variable whose size was explicitly specified in a **Private**, [**Public**](Public), or [**Dim**](Dim) statement, an error occurs. + +When variables are initialized, a numeric variable is initialized to 0, a variable-length string is initialized to a zero-length string (""), and a fixed-length string is filled with zeros. **Variant** variables are initialized to **Empty**. Each element of a user-defined type variable is initialized as if it were a separate variable. + +> [!NOTE] +> The **Private** statement cannot be used inside a procedure; use the **[Dim](Dim)** statement to declare local variables. + +The **Private** keyword is also used as a procedure modifier on **[Sub](Sub)**, **[Function](Function)**, and **[Property](Property)** declarations to make those procedures accessible only within the module in which they are declared. + +### Example + +This example shows the **Private** statement being used at the module level to declare variables as private; that is, they are available only to the module in which they are declared. + +```tb +Private Number As Integer ' Private Integer variable. +Private NameArray(1 To 5) As String ' Private array variable. +' Multiple declarations, two Variants and one Integer, all Private. +Private MyVar, YourVar, ThisVar As Integer +``` + +### See Also + +- [**Dim** statement](Dim) +- [**Public** statement](Public) +- [**Static** statement](Static) +- [**ReDim** statement](ReDim) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Property.md b/docs/Reference/Core/Property.md new file mode 100644 index 0000000..71335d1 --- /dev/null +++ b/docs/Reference/Core/Property.md @@ -0,0 +1,181 @@ +--- +title: Property +parent: Statements +permalink: /tB/Core/Property +--- +# Property +{: .no_toc } + +Declares the name, arguments, and code that form the body of a **Property** procedure: a procedure that gets the value of a property, assigns a value to a property, or sets a reference to an object property. + +A property is exposed to callers through up to three property procedures, all sharing the same *name* in the same module: + +- A **Property Get** procedure returns the property's value (or object reference). +- A **Property Let** procedure assigns a non-object value to the property. +- A **Property Set** procedure assigns an object reference to the property. + +Syntax: + +- > [ *attributes* ] + > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Get** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] [ **As** *type* ] + >      [ *statements* ] ... + >      [ [ **Let** ] *name* **=** *expression* ] ... + >      [ **Set** *name* **=** *expression* ] ... + >      [ **Return** *expression* ] ... + >      [ **Exit Property** \| **Return** ] ... + >      [ *statements* ] ... + > **End Property** + +- > [ *attributes* ] + > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Let** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *value* **)** + >      [ *statements* ] ... + >      [ **Exit Property** \| **Return** ] ... + >      [ *statements* ] ... + > **End Property** + +- > [ *attributes* ] + > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Set** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *reference* **)** + >      [ *statements* ] ... + >      [ **Exit Property** \| **Return** ] ... + >      [ *statements* ] ... + > **End Property** + +*attributes* +: *optional* One or more of the [supported attributes](Attributes) for procedures. + +**Public** +: *optional* Indicates that the **Property** procedure is accessible to all other procedures in all modules. If used in a module that contains an **Option Private** statement, the procedure is not available outside the project. + +**Private** +: *optional* Indicates that the **Property** procedure is accessible only to other procedures in the module where it is declared. + +**Friend** +: *optional* Used only in a class module. Indicates that the **Property** procedure is visible throughout the project, but not visible to a controller of an instance of an object. + +**[Static](Static)** +: *optional* Indicates that the **Property** procedure's local variables are preserved between calls. The **Static** attribute doesn't affect variables that are declared outside the **Property** procedure, even if they are used in the procedure. + +*name* +: Name of the **Property** procedure; follows standard variable naming conventions, except that the same name is shared by the matching **Property Get**, **Property Let**, and **Property Set** procedures in the same module. + +**Of** *typevars* +: *optional* One or more type variable names, following standard variable naming conventions. The names are separated by commas. Causes the procedure to be a generic **Property** procedure. The matching **Property Get**, **Property Let**, and **Property Set** procedures must declare the same generic parameters. + +*arglist* +: List of variables representing arguments that are passed to the **Property** procedure when it is called. Multiple arguments are separated by commas. The name and data type of each argument in a **Property Let** or **Property Set** procedure must be the same as the corresponding argument in the matching **Property Get** procedure. See [*arglist*](#arglist) below for syntax. *arglist* is optional for **Property Get**; for **Property Let** and **Property Set** at least the *value*/*reference* parameter is required. + +**As** *type* +: *optional* Data type of the value returned by the **Property Get** procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (except fixed length), **Object**, **Variant**, a user-defined type, or an array. The return *type* of a **Property Get** procedure must be the same data type as the *value* parameter of the corresponding **Property Let** procedure (if one exists), or compatible with the *reference* parameter of the corresponding **Property Set** procedure. + +*statements* +: *optional* Any group of statements to be executed within the body of the **Property** procedure. + +*expression* +: *optional* In **Property Get**, the value (or reference, when assigned with **Set**) returned by the procedure. + +*value* +: In **Property Let**, the variable that contains the value to be assigned to the property. When the procedure is called, this argument appears on the right side of the calling expression. The data type of *value* must be the same as the return type of the corresponding **Property Get** procedure. *value* cannot be **Optional** or a **ParamArray**. + +*reference* +: In **Property Set**, the variable containing the object reference used on the right side of the object reference assignment. *reference* cannot be **Optional**. + +**[Exit Property](Exit)** +: *optional* Immediately returns from the **Property** procedure. + +**[Return](Return)** +: *optional* Immediately returns from the **Property** procedure. In **Property Get**, an *expression* may be supplied as the return value. + +### *arglist* + +Syntax: One or more of +[ **Optional** ] [ **ByVal** \| **ByRef** ] [ **[ParamArray](ParamArray)** ] *varname* [ **()** ] [ **As** *type* ] [ **=** *defaultvalue* ] + +**Optional** +: *optional* Indicates that an argument is not required. If used, all subsequent arguments in *arglist* must also be optional and declared by using the **Optional** keyword. The right side of a **Property Let** or **Property Set** call (the *value* or *reference* parameter) cannot be **Optional**. + +**ByVal** +: *optional* Indicates that the argument is passed by value. + +**ByRef** +: *optional* Indicates that the argument is passed by reference. **ByRef** is the default unlike in Visual Basic .NET. + +**[ParamArray](ParamArray)** +: *optional* Indicates that the argument is an **Optional** array of **Variant** elements. The **ParamArray** keyword allows you to provide an arbitrary number of arguments. It may not be used with **ByVal**, **ByRef**, or **Optional**, and it may not be the *value*/*reference* parameter of a **Property Let** or **Property Set** procedure. + +*varname* +: Name of the variable representing the argument; follows standard variable naming conventions. + +*type* +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. +If the name of a generic type parameter is used, it becomes bound to the concrete type of the argument passed to the procedure. The name binding has the scope of the body of the procedure. + +*defaultvalue* +: *optional* Any constant or constant expression. Valid for **Optional** parameters only. If the type is an **Object**, an explicit default value can only be **Nothing**. + +If not explicitly specified by using **Public**, **Private**, or **Friend**, **Property** procedures are public by default. If **Static** isn't used, the value of local variables is not preserved between calls. + +The **Friend** keyword can only be used in class modules. However, **Friend** procedures can be accessed by procedures in any module of a project. A **Friend** procedure doesn't appear in the type library of its parent class, nor can a **Friend** procedure be late bound. + +All executable code must be in procedures. You can't define a **Property** procedure inside another **[Property](Property)**, **[Sub](Sub)**, or **[Function](Function)** procedure. + +The **[Exit Property](Exit)** and **[Return](Return)** statements cause an immediate exit from a **Property** procedure. Program execution continues with the statement following the statement that called the **Property** procedure. Any number of **Exit Property** and **Return** statements can appear anywhere in a **Property** procedure. + +Like **Sub** and **Function** procedures, a **Property** procedure is a separate procedure that can take arguments, perform a series of statements, and change the values of its arguments. A **Property Get** procedure can be used on the right side of an expression in the same way as a **Function** or a property name. A **Property Let** procedure can only be used on the left side of a property assignment expression or [**Let**](Let) statement. A **Property Set** procedure can only be used on the left side of an object reference assignment or **[Set](Set)** statement. + +### Example + +This example uses the **Property** statements to define a `PenColor` property: a **Property Let** that accepts a color name as a string and stores a numeric code, and a **Property Get** that returns the color name from the stored numeric code. + +```tb +Dim CurrentColor As Integer +Const BLACK = 0, RED = 1, GREEN = 2, BLUE = 3 + +' Set the pen color property for a Drawing package. +' The module-level variable CurrentColor is set to +' a numeric value that identifies the color used for drawing. +Property Let PenColor(ColorName As String) + Select Case ColorName ' Check color name string. + Case "Red" + CurrentColor = RED ' Assign value for Red. + Case "Green" + CurrentColor = GREEN ' Assign value for Green. + Case "Blue" + CurrentColor = BLUE ' Assign value for Blue. + Case Else + CurrentColor = BLACK ' Assign default value. + End Select +End Property + +' Returns the current color of the pen as a string. +Property Get PenColor() As String + Select Case CurrentColor + Case RED: PenColor = "Red" + Case GREEN: PenColor = "Green" + Case BLUE: PenColor = "Blue" + End Select +End Property + +' Calling code: +PenColor = "Red" ' Calls Property Let. +ColorName = PenColor ' Calls Property Get. +``` + +A **Property Set** procedure assigns an object reference, in much the same way as **Property Let** assigns a value: + +```tb +' The Pen property may be set to different Pen implementations. +Property Set Pen(P As Object) + Set CurrentPen = P ' Assign Pen to object. +End Property +``` + +### See Also + +- [**Sub** statement](Sub) +- [**Function** statement](Function) +- [**Let** statement](Let) +- [**Set** statement](Set) +- [**Exit** statement](Exit) +- [**Return** statement](Return) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Public.md b/docs/Reference/Core/Public.md new file mode 100644 index 0000000..28fe5c4 --- /dev/null +++ b/docs/Reference/Core/Public.md @@ -0,0 +1,74 @@ +--- +title: Public +parent: Statements +permalink: /tB/Core/Public +--- +# Public +{: .no_toc } + +Used at the module level to declare public variables and allocate storage space. + +Syntax: +> **Public** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **,** [ **WithEvents** ] *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ]] **. . .** + +**WithEvents** +: *optional* Keyword specifying that *varname* is an object variable used to respond to events triggered by an ActiveX object. **WithEvents** is valid only in class modules. You can declare as many individual variables as you like by using **WithEvents**, but you can't create arrays with **WithEvents**, nor can you use **New** with **WithEvents**. + +*varname* +: Name of the variable; follows standard naming conventions. + +*subscripts* +: *optional* Dimensions of an array variable; up to 60 multiple dimensions may be declared. The *subscripts* argument uses the following syntax: [ *lower* **To** ] *upper* [ , [ *lower* **To** ] *upper* ] **. . .**. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. + +**New** +: *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects, and it can't be used with **WithEvents**. + +*type* +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. + +Variables declared by using the **Public** statement are available to all procedures in all modules in all applications, unless **[Option Private Module](Option)** is in effect; in which case, the variables are public only within the project in which they reside. + +The **Public** statement can't be used in a class module to declare a fixed-length string variable. + +Use the **Public** statement to declare the data type of a variable. For example, the following statement declares a variable as an **Integer**: + +```tb +Public NumberOfEmployees As Integer +``` + +Also use a **Public** statement to declare the object type of a variable. The following statement declares a variable for a new instance of a worksheet: + +```tb +Public X As New Worksheet +``` + +If the **New** keyword is not used when declaring an object variable, the variable that refers to the object must be assigned an existing object by using the **Set** statement before it can be used. Until it is assigned an object, the declared object variable has the special value **Nothing**, which indicates that it doesn't refer to any particular instance of an object. + +You can also use the **Public** statement with empty parentheses to declare a dynamic array. After declaring a dynamic array, use the **[ReDim](ReDim)** statement within a procedure to define the number of dimensions and elements in the array. If you try to redeclare a dimension for an array variable whose size was explicitly specified in a [**Private**](Private), **Public**, or [**Dim**](Dim) statement, an error occurs. + +If you don't specify a data type or object type, and there is no [**Deftype**](Deftype) statement in the module, the variable is **Variant** by default. + +When variables are initialized, a numeric variable is initialized to 0, a variable-length string is initialized to a zero-length string (""), and a fixed-length string is filled with zeros. **Variant** variables are initialized to **Empty**. Each element of a user-defined type variable is initialized as if it were a separate variable. + +The **Public** keyword is also used as a procedure modifier on **[Sub](Sub)**, **[Function](Function)**, and **[Property](Property)** declarations to make those procedures accessible to all other procedures in all modules. + +### Example + +This example uses the **Public** statement at the module level (General section) of a standard module to explicitly declare variables as public; that is, they are available to all procedures in all modules in all applications unless **Option Private Module** is in effect. + +```tb +Public Number As Integer ' Public Integer variable. +Public NameArray(1 To 5) As String ' Public array variable. +' Multiple declarations, two Variants and one Integer, all Public. +Public MyVar, YourVar, ThisVar As Integer +``` + +### See Also + +- [**Dim** statement](Dim) +- [**Private** statement](Private) +- [**Static** statement](Static) +- [**Option** statement](Option) +- [**ReDim** statement](ReDim) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Static.md b/docs/Reference/Core/Static.md new file mode 100644 index 0000000..799a7ba --- /dev/null +++ b/docs/Reference/Core/Static.md @@ -0,0 +1,84 @@ +--- +title: Static +parent: Statements +permalink: /tB/Core/Static +--- +# Static +{: .no_toc } + +Used at the procedure level to declare variables and allocate storage space. Variables declared with the **Static** statement retain their values as long as the code is running. + +Syntax: +> **Static** *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **,** *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ]] **. . .** + +*varname* +: Name of the variable; follows standard variable naming conventions. + +*subscripts* +: *optional* Dimensions of an array variable; up to 60 multiple dimensions may be declared. The *subscripts* argument uses the following syntax: [ *lower* **To** ] *upper* [ , [ *lower* **To** ] *upper* ] **. . .**. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. + +**New** +: *optional* Keyword that enables implicit creation of an object. If you use **New** when declaring the object variable, a new instance of the object is created on first reference to it, so you don't have to use the **[Set](Set)** statement to assign the object reference. The **New** keyword can't be used to declare variables of any intrinsic data type or to declare instances of dependent objects. + +*type* +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. Use a separate **As** *type* clause for each variable being defined. + +After module code is running, variables declared with the **Static** statement retain their value until the module is reset or restarted. In class modules, variables declared with the **Static** statement retain their value in each class instance until that instance is destroyed. In form modules, static variables retain their value until the form is closed. + +Use the **Static** statement in nonstatic procedures to explicitly declare variables that are visible only within the procedure, but whose lifetime is the same as the module in which the procedure is defined. + +Use a **Static** statement within a procedure to declare the data type of a variable that retains its value between procedure calls. For example, the following statement declares a fixed-size array of integers: + +```tb +Static EmployeeNumber(200) As Integer +``` + +The following statement declares a variable for a new instance of a worksheet: + +```tb +Static X As New Worksheet +``` + +If the **New** keyword isn't used when declaring an object variable, the variable that refers to the object must be assigned an existing object by using the **Set** statement before it can be used. Until it is assigned an object, the declared object variable has the special value **Nothing**, which indicates that it doesn't refer to any particular instance of an object. When you use the **New** keyword in the declaration, an instance of the object is created on the first reference to the object. + +If you don't specify a data type or object type, and there is no [**Deftype**](Deftype) statement in the module, the variable is **Variant** by default. + +> [!NOTE] +> The **Static** statement and the **Static** keyword are similar, but used for different effects. If you declare a procedure by using the **Static** keyword (as in `Static Sub CountSales()`), the storage space for all local variables within the procedure is allocated once, and the value of the variables is preserved for the entire time the program is running. For nonstatic procedures, storage space for variables is allocated each time the procedure is called and released when the procedure is exited. The **Static** statement is used to declare specific variables within nonstatic procedures to preserve their value for as long as the program is running. + +When variables are initialized, a numeric variable is initialized to 0, a variable-length string is initialized to a zero-length string (""), and a fixed-length string is filled with zeros. **Variant** variables are initialized to **Empty**. Each element of a user-defined type variable is initialized as if it were a separate variable. + +> [!NOTE] +> When you use **Static** statements within a procedure, put them at the beginning of the procedure with other declarative statements such as **[Dim](Dim)**. + +### Example + +This example uses the **Static** statement to retain the value of a variable for as long as the module code is running. + +```tb +' Function definition. +Function KeepTotal(Number) + ' Only the variable Accumulate preserves its value between calls. + Static Accumulate + Accumulate = Accumulate + Number + KeepTotal = Accumulate +End Function + +' Static function definition. +Static Function MyFunction(Arg1, Arg2, Arg3) + ' All local variables preserve value between function calls. + Accumulate = Arg1 + Arg2 + Arg3 + Half = Accumulate / 2 + MyFunction = Half +End Function +``` + +### See Also + +- [**Dim** statement](Dim) +- [**Private** statement](Private) +- [**Public** statement](Public) +- [**Sub** statement](Sub) +- [**Function** statement](Function) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Sub.md b/docs/Reference/Core/Sub.md new file mode 100644 index 0000000..776b361 --- /dev/null +++ b/docs/Reference/Core/Sub.md @@ -0,0 +1,130 @@ +--- +title: Sub +parent: Statements +permalink: /tB/Core/Sub +--- +# Sub +{: .no_toc } + +Declares the name, arguments, and code that form the body of a **Sub** procedure. + +Syntax: +> [ *attributes* ] +> [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Sub** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] +>      [ *statements* ] ... +>      [ **Exit Sub** \| **Return** ] ... +>      [ *statements* ] ... +> **End Sub** + +*attributes* +: *optional* One or more of the [supported attributes](Attributes) for procedures. + +**Public** +: *optional* Indicates that the **Sub** procedure is accessible to all other procedures in all modules. If used in a module that contains an **Option Private**, the procedure is not available outside the project. + +**Private** +: *optional* Indicates that the **Sub** procedure is accessible only to other procedures in the module where it is declared. + +**Friend** +: *optional* Used only in a class module. Indicates that the **Sub** procedure is visible throughout the project, but not visible to a controller of an instance of an object. + +**[Static](Static)** +: *optional* Indicates that the **Sub** procedure's local variables are preserved between calls. The **Static** attribute doesn't affect variables that are declared outside the **Sub**, even if they are used in the procedure. + +*name* +: Name of the **Sub**; follows standard variable naming conventions. + +**Of** *typevars* +: *optional* One or more type variable names, following standard variable naming conventions. The names are separated by commas. Causes the procedure to be a generic **Sub**. + +*arglist* +: *optional* List of variables representing arguments that are passed to the **Sub** procedure when it is called. Multiple variables are separated by commas. See [*arglist*](#arglist) below for syntax. + +*statements* +: *optional* Any group of statements to be executed within the **Sub** procedure. + +**[Exit Sub](Exit)** +: *optional* Immediately returns from the **Sub** procedure. + +**[Return](Return)** +: *optional* Immediately returns from the **Sub** procedure. + +### *arglist* + +Syntax: One or more of +[ **Optional** ] [ **ByVal** \| **ByRef** ] [ **[ParamArray](ParamArray)** ] *varname* [ **()** ] [ **As** *type* ] [ **=** *defaultvalue* ] + +**Optional** +: *optional* Indicates that an argument is not required. If used, all subsequent arguments in *arglist* must also be optional and declared by using the **Optional** keyword. **Optional** can't be used for any argument if **ParamArray** is used. + +**ByVal** +: *optional* Indicates that the argument is passed by value. + +**ByRef** +: *optional* Indicates that the argument is passed by reference. **ByRef** is the default unlike in Visual Basic .NET. + +**[ParamArray](ParamArray)** +: *optional* Used only as the last argument in *arglist* to indicate that the final argument is an **Optional** array of **Variant** elements. The **ParamArray** keyword allows you to provide an arbitrary number of arguments. It may not be used with **ByVal**, **ByRef**, or **Optional**. + +*varname* +: Name of the variable representing the argument; follows standard variable naming conventions. + +*type* +: *optional* Data type of the argument passed to the procedure; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (variable length only), **Object**, **Variant**, a specific object type, or the name of a generic type argument. If the parameter is not **Optional**, a user-defined type may also be specified. +If the name of a generic type parameter is used, it becomes bound to the concrete type of the argument passed to the procedure. The name binding has the scope of the body of the procedure. + +*defaultvalue* +: *optional* Any constant or constant expression. Valid for **Optional** parameters only. If the type is an **Object**, an explicit default value can only be **Nothing**. + +If not explicitly specified by using **Public**, **Private**, or **Friend**, **Sub** procedures are public by default. + +If **Static** isn't used, the value of local variables is not preserved between calls. + +The **Friend** keyword can only be used in class modules. However, **Friend** procedures can be accessed by procedures in any module of a project. A **Friend** procedure does not appear in the type library of its parent class, nor can a **Friend** procedure be late bound. + +**Sub** procedures can be recursive; that is, they can call themselves to perform a given task. However, recursion can lead to stack overflow. The **Static** keyword usually is not used with recursive **Sub** procedures. + +All executable code must be in procedures. You can't define a **Sub** procedure inside another **[Sub](Sub)**, **[Function](Function)**, or **[Property](Property)** procedure. + +The **[Exit Sub](Exit)** and **[Return](Return)** statements cause an immediate exit from a **Sub** procedure. Program execution continues with the statement following the statement that called the **Sub** procedure. Any number of **Exit Sub** and **Return** statements can appear anywhere in a **Sub** procedure. + +Like a **Function** procedure, a **Sub** procedure is a separate procedure that can take arguments, perform a series of statements, and change the value of its arguments. However, unlike a **Function** procedure, which returns a value, a **Sub** procedure can't be used in an expression. + +You call a **Sub** procedure by using the procedure name followed by the argument list. See the **[Call](Call)** statement for specific information about how to call **Sub** procedures. + +Variables used in **Sub** procedures fall into two categories: those that are explicitly declared within the procedure and those that are not. Variables that are explicitly declared in a procedure (using **Dim** or the equivalent) are always local to the procedure. Variables that are used but not explicitly declared in a procedure are also local unless they are explicitly declared at some higher level outside the procedure. + +A procedure can use a variable that is not explicitly declared in the procedure, but a naming conflict can occur if anything you defined at the module level has the same name. If your procedure refers to an undeclared variable that has the same name as another procedure, constant, or variable, it is assumed that your procedure is referring to that module-level name. To avoid this kind of conflict, explicitly declare variables. Use an **[Option Explicit](Option#Explicit)** statement to force explicit declaration of variables. + +> [!NOTE] +> You can't use **GoSub**, **GoTo**, or **Return** to enter or exit a **Sub** procedure. (Inside a **Sub**, **Return** ends the procedure.) + +### Example + +This example uses the **Sub** statement to define the name, arguments, and code that form the body of a **Sub** procedure. + +```tb +' Sub procedure definition. +' Sub procedure with two arguments. +Sub SubComputeArea(Length As Double, TheWidth As Double) + Dim Area As Double ' Declare local variable. + + If Length = 0 Or TheWidth = 0 Then + ' If either argument = 0. + Exit Sub ' Exit Sub immediately. + End If + + Area = Length * TheWidth ' Calculate area of rectangle. + Debug.Print Area ' Print Area to Debug window. +End Sub +``` + +### See Also + +- [**Call** statement](Call) +- [**Function** statement](Function) +- [**Property** statement](Property) +- [**Exit** statement](Exit) +- [**Return** statement](Return) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index 2ce0caf..43cf285 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -27,12 +27,8 @@ redirect_from: - /tB/Core/On-GoSub - /tB/Core/On-GoTo - /tB/Core/Open - - /tB/Core/ParamArray - /tB/Core/Print - - /tB/Core/Private - - /tB/Core/Property - /tB/Core/Protected - - /tB/Core/Public - /tB/Core/Put - /tB/Core/RaiseEvent - /tB/Core/ReDim @@ -41,8 +37,6 @@ redirect_from: - /tB/Core/RSet - /tB/Core/SavePicture - /tB/Core/Set - - /tB/Core/Static - - /tB/Core/Sub - /tB/Core/Type - /tB/Core/Unload - /tB/Core/Unlock diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index c9a0fb6..e87dd10 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -85,13 +85,15 @@ These statements are built into the language itself. They are understood by the * [Option](../tB/Core/Option) -- configure a compiler option +* [ParamArray](../tB/Core/ParamArray) -- declares the final parameter of a procedure as an arbitrary-arity list of arguments + * [Print](../tB/Core/Print) -* [Private](../tB/Core/Private) +* [Private](../tB/Core/Private) -- declares module-level variables accessible only within the declaring module -* [Property](../tB/Core/Property) +* [Property](../tB/Core/Property) -- declares the **Get**, **Let**, or **Set** procedures that form the body of a property -* [Public](../tB/Core/Public) +* [Public](../tB/Core/Public) -- declares module-level variables accessible to all procedures in all modules * [Put](../tB/Core/Put) @@ -113,11 +115,11 @@ These statements are built into the language itself. They are understood by the * [Set](../tB/Core/Set) -* [Static](../tB/Core/Static) +* [Static](../tB/Core/Static) -- declares procedure-local variables whose values are preserved between calls * [Stop](../tB/Core/Stop) -- suspends execution -* [Sub](../tB/Core/Sub) +* [Sub](../tB/Core/Sub) -- declares the name, arguments, and code that form the body of a **Sub** procedure * [Unlock](../tB/Core/Unlock) From e5bca3584cf631713708324e947ce4a41edc9dc8 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 16:05:31 +0200 Subject: [PATCH 03/10] Document Is, IsNot, Let, New, ReDim, Set, and Type. --- docs/Reference/Core/Is.md | 75 ++++++++++++++++++++++++++++++ docs/Reference/Core/IsNot.md | 54 ++++++++++++++++++++++ docs/Reference/Core/Let.md | 56 +++++++++++++++++++++++ docs/Reference/Core/New.md | 60 ++++++++++++++++++++++++ docs/Reference/Core/ReDim.md | 89 ++++++++++++++++++++++++++++++++++++ docs/Reference/Core/Set.md | 64 ++++++++++++++++++++++++++ docs/Reference/Core/Type.md | 88 +++++++++++++++++++++++++++++++++++ docs/Reference/Core/todo.md | 6 --- docs/Reference/Statements.md | 14 ++++-- 9 files changed, 497 insertions(+), 9 deletions(-) create mode 100644 docs/Reference/Core/Is.md create mode 100644 docs/Reference/Core/IsNot.md create mode 100644 docs/Reference/Core/Let.md create mode 100644 docs/Reference/Core/New.md create mode 100644 docs/Reference/Core/ReDim.md create mode 100644 docs/Reference/Core/Set.md create mode 100644 docs/Reference/Core/Type.md diff --git a/docs/Reference/Core/Is.md b/docs/Reference/Core/Is.md new file mode 100644 index 0000000..4a2dc90 --- /dev/null +++ b/docs/Reference/Core/Is.md @@ -0,0 +1,75 @@ +--- +title: Is +parent: Statements +permalink: /tB/Core/Is +--- +# Is +{: .no_toc } + +Used to compare two object references for identity. + +Syntax: +> *result* **=** *object1* **Is** *object2* + +*result* +: Any **Boolean** or numeric variable. + +*object1*, *object2* +: Any object references. + +If *object1* and *object2* both refer to the same object, *result* is **True**; if they don't, *result* is **False**. **Is** does not compare values inside the objects — it compares whether the two references point to the same instance. + +Two variables can be made to refer to the same object in several ways. In the following example, A has been set to refer to the same object as B: + +```tb +Set A = B +``` + +The following example makes A and B refer to the same object as C: + +```tb +Set A = C +Set B = C +``` + +A reference compared against **Nothing** with **Is** tells whether the reference is unassigned: + +```tb +If MyObject Is Nothing Then + Debug.Print "MyObject has not been assigned." +End If +``` + +For the negation of an identity test, twinBASIC also provides the [**IsNot**](IsNot) operator: `If MyObject IsNot Nothing Then` reads more naturally than `If Not (MyObject Is Nothing) Then`. + +> [!NOTE] +> The **Is** keyword has two unrelated uses elsewhere in the language: +> +> - In an **[If...Then...Else](If-Then-Else)** condition of the form **TypeOf** *objectname* **Is** *objecttype*, **Is** introduces a runtime type test. +> - In a **[Select Case](Select-Case)** clause of the form **Is** *comparisonoperator* *expression*, **Is** introduces a comparison against the **Select Case** test expression. +> +> In both of those constructs the surrounding statement provides the meaning; **Is** there is not the object-identity operator described on this page. + +### Example + +This example uses the **Is** operator to compare two object references. + +```tb +Dim MyObject, YourObject, ThisObject, OtherObject, ThatObject, MyCheck +Set YourObject = MyObject ' Assign object references. +Set ThisObject = MyObject +Set ThatObject = OtherObject +MyCheck = YourObject Is ThisObject ' Returns True. +MyCheck = ThatObject Is ThisObject ' Returns False. +' Assume MyObject <> OtherObject. +MyCheck = MyObject Is ThatObject ' Returns False. +``` + +### See Also + +- [**IsNot** operator](IsNot) +- [**Set** statement](Set) +- [**If...Then...Else** statement](If-Then-Else) +- [**Select Case** statement](Select-Case) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/IsNot.md b/docs/Reference/Core/IsNot.md new file mode 100644 index 0000000..380c814 --- /dev/null +++ b/docs/Reference/Core/IsNot.md @@ -0,0 +1,54 @@ +--- +title: IsNot +parent: Statements +permalink: /tB/Core/IsNot +--- +# IsNot +{: .no_toc } + +Used to compare two object references for non-identity. The logical inverse of the [**Is**](Is) operator. + +Syntax: +> *result* **=** *object1* **IsNot** *object2* + +*result* +: Any **Boolean** or numeric variable. + +*object1*, *object2* +: Any object references. + +If *object1* and *object2* refer to *different* objects (or one of them is **Nothing** while the other is not), *result* is **True**; if they refer to the same object, *result* is **False**. Like **Is**, the comparison is on the references themselves, not on the values inside the objects. + +> [!NOTE] +> **IsNot** is a twinBASIC extension. Classic VBA has no **IsNot** operator; the equivalent is `Not (a Is b)`. + +The most common use is testing that an object reference has been assigned: + +```tb +If MyObject IsNot Nothing Then + ' Use MyObject. +End If +``` + +This reads more naturally than the equivalent `If Not (MyObject Is Nothing) Then` or the older `If (MyObject Is Nothing) = False Then`. + +### Example + +```tb +Dim A As Object, B As Object, C As Object +Set A = New Collection +Set B = A ' B refers to the same object as A. +Set C = New Collection + +Debug.Print A IsNot B ' False - same object. +Debug.Print A IsNot C ' True - different objects. +Debug.Print A IsNot Nothing ' True - A is assigned. + +Set A = Nothing +Debug.Print A IsNot Nothing ' False - A is now unassigned. +``` + +### See Also + +- [**Is** operator](Is) +- [**Set** statement](Set) diff --git a/docs/Reference/Core/Let.md b/docs/Reference/Core/Let.md new file mode 100644 index 0000000..d796df6 --- /dev/null +++ b/docs/Reference/Core/Let.md @@ -0,0 +1,56 @@ +--- +title: Let +parent: Statements +permalink: /tB/Core/Let +--- +# Let +{: .no_toc } + +Assigns the value of an expression to a variable or property. + +Syntax: +> [ **Let** ] *varname* **=** *expression* + +**Let** +: *optional* Explicit use of the **Let** keyword is a matter of style; it is usually omitted. + +*varname* +: Name of the variable or property; follows standard variable naming conventions. + +*expression* +: Value assigned to the variable or property. + +A value expression can be assigned to a variable or property only if it is of a data type that is compatible with the variable. You can't assign string expressions to numeric variables, and you can't assign numeric expressions to string variables. If you do, an error occurs at compile time. + +**Variant** variables can be assigned to either string or numeric expressions. However, the reverse is not always true. Any **Variant** except a **Null** can be assigned to a string variable, but only a **Variant** whose value can be interpreted as a number can be assigned to a numeric variable. Use the **IsNumeric** function to determine if the **Variant** can be converted to a number. + +Assigning an expression of one numeric type to a variable of a different numeric type coerces the value of the expression into the numeric type of the resulting variable. + +**Let** statements can be used to assign one record variable to another only when both variables are of the same user-defined type. Use the **LSet** statement to assign record variables of different user-defined types. Use the [**Set**](Set) statement to assign object references to variables. + +### Example + +This example assigns the values of expressions to variables by using the explicit **Let** statement. + +```tb +Dim MyStr, MyInt +' The following variable assignments use the Let statement. +Let MyStr = "Hello World" +Let MyInt = 5 +``` + +The following are the same assignments without the **Let** statement. + +```tb +Dim MyStr, MyInt +MyStr = "Hello World" +MyInt = 5 +``` + +### See Also + +- [**Set** statement](Set) +- [**LSet** statement](LSet) +- [**Property** statement](Property) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/New.md b/docs/Reference/Core/New.md new file mode 100644 index 0000000..1e629af --- /dev/null +++ b/docs/Reference/Core/New.md @@ -0,0 +1,60 @@ +--- +title: New +parent: Statements +permalink: /tB/Core/New +--- +# New +{: .no_toc } + +Creates a new instance of a class. + +The **New** keyword is used in two contexts: + +- In a declaration ([**Dim**](Dim), [**Private**](Private), [**Public**](Public), or [**Static**](Static)) to enable *implicit* object creation: a new instance is created on the first reference to the variable. +- In a [**Set**](Set) statement, to create a new instance of a class and assign the reference to a variable or property. + +Syntax: + +- > [ **Dim** \| **Private** \| **Public** \| **Static** ] *varname* **As** **New** *type* +- > **Set** *objectvar* **=** **New** *type* + +*varname*, *objectvar* +: Name of the variable or property receiving the new object reference. + +*type* +: A class name or other creatable object type. **New** can't be used to create new instances of any intrinsic data type (such as **Long** or **String**), and can't be used to create dependent objects. + +When **New** is used in a declaration, no instance is created at the point of declaration. Instead, an instance is created automatically the first time the variable is referenced after declaration. Each time the variable is set to **Nothing** and then referenced again, a new instance is created. + +When **New** is used with **Set**, an instance is created immediately, and the reference is assigned to *objectvar*. If *objectvar* previously held a reference to another object, that reference is released when the new one is assigned. + +> [!NOTE] +> **New** cannot be used together with **WithEvents** in a declaration. To wire up an event-aware object reference, declare the variable with **WithEvents** and assign it later with **Set**. + +### Example + +Implicit creation via **New** in a declaration. The instance of `Worksheet` is created on first use, not at the **Dim** line. + +```tb +Dim X As New Worksheet +' No instance exists yet. +X.Activate ' First reference - instance is created here. +``` + +Explicit creation via **Set ... = New**. The instance is created at the **Set** line. This is the more common form, since the moment of construction is visible at the call site. + +```tb +Dim Forms(1 To 4) As Form1 +Dim i As Long +For i = 1 To 4 + Set Forms(i) = New Form1 +Next i +``` + +### See Also + +- [**Set** statement](Set) +- [**Dim** statement](Dim) +- [**Class** statement](Class) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/ReDim.md b/docs/Reference/Core/ReDim.md new file mode 100644 index 0000000..a563d91 --- /dev/null +++ b/docs/Reference/Core/ReDim.md @@ -0,0 +1,89 @@ +--- +title: ReDim +parent: Statements +permalink: /tB/Core/ReDim +--- +# ReDim +{: .no_toc } + +Used at the procedure level to reallocate storage space for dynamic array variables. + +Syntax: +> **ReDim** [ **Preserve** ] *varname* **(** *subscripts* **)** [ **As** *type* ] [ **,** *varname* **(** *subscripts* **)** [ **As** *type* ] ] **. . .** + +**Preserve** +: *optional* Keyword used to preserve the data in an existing array when you change the size of the last dimension. + +*varname* +: Name of the variable; follows standard variable naming conventions. + +*subscripts* +: Dimensions of an array variable; up to 60 multiple dimensions may be declared. The *subscripts* argument uses the following syntax: [ *lower* **To** ] *upper* [ , [ *lower* **To** ] *upper* ] **. . .**. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. + +*type* +: *optional* Data type of the variable; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, a user-defined type, or an object type. +Use a separate **As** *type* clause for each variable being defined. For a **Variant** containing an array, *type* describes the type of each element of the array, but doesn't change the **Variant** to some other type. + +The **ReDim** statement is used to size or resize a dynamic array that has already been formally declared by using a [**Private**](Private), [**Public**](Public), or [**Dim**](Dim) statement with empty parentheses (without dimension subscripts). + +Use the **ReDim** statement repeatedly to change the number of elements and dimensions in an array. However, you can't declare an array of one data type and later use **ReDim** to change the array to another data type, unless the array is contained in a **Variant**. If the array is contained in a **Variant**, the type of the elements can be changed by using an **As** *type* clause, unless you are using the **Preserve** keyword, in which case no changes of data type are permitted. + +If you use the **Preserve** keyword, you can resize only the last array dimension and you can't change the number of dimensions at all. For example, if your array has only one dimension, you can resize that dimension because it is the last and only dimension. However, if your array has two or more dimensions, you can change the size of only the last dimension and still preserve the contents of the array. + +The following example shows how you can increase the size of the last dimension of a dynamic array without erasing any existing data contained in the array. + +```tb +ReDim X(10, 10, 10) +. . . +ReDim Preserve X(10, 10, 15) +``` + +Similarly, when you use **Preserve**, you can change the size of the array only by changing the upper bound; changing the lower bound causes an error. + +If you make an array smaller than it was, data in the eliminated elements will be lost. + +When variables are initialized, a numeric variable is initialized to 0, a variable-length string is initialized to a zero-length string (""), and a fixed-length string is filled with zeros. **Variant** variables are initialized to **Empty**. Each element of a user-defined type variable is initialized as if it were a separate variable. + +A variable that refers to an object must be assigned an existing object by using the [**Set**](Set) statement before it can be used. Until it is assigned an object, the declared object variable has the special value **Nothing**, which indicates that it doesn't refer to any particular instance of an object. + +The **ReDim** statement acts as a declarative statement if the variable it declares doesn't exist at the module level or procedure level. If another variable with the same name is created later, even in a wider scope, **ReDim** will refer to the later variable and won't necessarily cause a compilation error, even if **Option Explicit** is in effect. To avoid such conflicts, **ReDim** should not be used as a declarative statement, but simply for redimensioning arrays. + +> [!NOTE] +> To resize an array contained in a **Variant**, you must explicitly declare the **Variant** variable before attempting to resize its array. + +### Example + +This example uses the **ReDim** statement to allocate and reallocate storage space for dynamic-array variables. It assumes the **Option Base** is **1**. + +```tb +Dim MyArray() As Integer ' Declare dynamic array. +ReDim MyArray(5) ' Allocate 5 elements. +For I = 1 To 5 ' Loop 5 times. + MyArray(I) = I ' Initialize array. +Next I +``` + +The next statement resizes the array and erases the elements. + +```tb +ReDim MyArray(10) ' Resize to 10 elements. +For I = 1 To 10 ' Loop 10 times. + MyArray(I) = I ' Initialize array. +Next I +``` + +The following statement resizes the array but does not erase elements. + +```tb +ReDim Preserve MyArray(15) ' Resize to 15 elements. +``` + +### See Also + +- [**Dim** statement](Dim) +- [**Private** statement](Private) +- [**Public** statement](Public) +- [**Static** statement](Static) +- [**Erase** statement](Erase) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Set.md b/docs/Reference/Core/Set.md new file mode 100644 index 0000000..8fd064a --- /dev/null +++ b/docs/Reference/Core/Set.md @@ -0,0 +1,64 @@ +--- +title: Set +parent: Statements +permalink: /tB/Core/Set +--- +# Set +{: .no_toc } + +Assigns an object reference to a variable or property. + +Syntax: +> **Set** *objectvar* **=** { [ **New** ] *objectexpression* \| **Nothing** } + +*objectvar* +: Name of the variable or property; follows standard variable naming conventions. + +**[New](New)** +: *optional* **New** is usually used during declaration to enable implicit object creation. When **New** is used with **Set**, it creates a new instance of the class. If *objectvar* contained a reference to an object, that reference is released when the new one is assigned. The **New** keyword can't be used to create new instances of any intrinsic data type and can't be used to create dependent objects. + +*objectexpression* +: Expression consisting of the name of an object, another declared variable of the same object type, or a function or method that returns an object of the same object type. + +**Nothing** +: *optional* Discontinues association of *objectvar* with any specific object. Assigning **Nothing** to *objectvar* releases all the system and memory resources associated with the previously referenced object when no other variable refers to it. + +To be valid, *objectvar* must be an object type consistent with the object being assigned to it. + +The [**Dim**](Dim), [**Private**](Private), [**Public**](Public), [**ReDim**](ReDim), and [**Static**](Static) statements only declare a variable that refers to an object. No actual object is referred to until you use the **Set** statement to assign a specific object. + +The following example illustrates how **Dim** is used to declare an array with the type `Form1`. No instance of `Form1` actually exists. **Set** then assigns references to new instances of `Form1` to the `myChildForms` variable. Such code might be used to create child forms in an MDI application. + +```tb +Dim myChildForms(1 To 4) As Form1 +Set myChildForms(1) = New Form1 +Set myChildForms(2) = New Form1 +Set myChildForms(3) = New Form1 +Set myChildForms(4) = New Form1 +``` + +Generally, when you use **Set** to assign an object reference to a variable, no copy of the object is created for that variable. Instead, a reference to the object is created. More than one object variable can refer to the same object. Because such variables are references to the object rather than copies of the object, any change in the object is reflected in all variables that refer to it. However, when you use the **New** keyword in the **Set** statement, you are actually creating an instance of the object. + +### Example + +This example uses the **Set** statement to assign object references to variables. *YourObject* is assumed to be a valid object with a **Text** property. + +```tb +Dim YourObject, MyObject, MyStr +Set MyObject = YourObject ' Assign object reference. +' MyObject and YourObject refer to the same object. +YourObject.Text = "Hello World" ' Initialize property. +MyStr = MyObject.Text ' Returns "Hello World". + +' Discontinue association. MyObject no longer refers to YourObject. +Set MyObject = Nothing ' Release the object. +``` + +### See Also + +- [**Let** statement](Let) +- [**New** keyword](New) +- [**Dim** statement](Dim) +- [**Property** statement](Property) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Type.md b/docs/Reference/Core/Type.md new file mode 100644 index 0000000..824ce6b --- /dev/null +++ b/docs/Reference/Core/Type.md @@ -0,0 +1,88 @@ +--- +title: Type +parent: Statements +permalink: /tB/Core/Type +--- +# Type +{: .no_toc } + +Used at the module level to define a user-defined data type containing one or more elements. + +Syntax: +> [ **Private** \| **Public** ] **Type** *varname* +>      *elementname* [ **(** [ *subscripts* ] **)** ] **As** *type* +>      [ *elementname* [ **(** [ *subscripts* ] **)** ] **As** *type* ] +>      **. . .** +> **End Type** + +**Public** +: *optional* Used to declare user-defined types that are available to all procedures in all modules in all projects. + +**Private** +: *optional* Used to declare user-defined types that are available only within the module where the declaration is made. + +*varname* +: Name of the user-defined type; follows standard variable naming conventions. + +*elementname* +: Name of an element of the user-defined type. Element names also follow standard variable naming conventions, except that keywords can be used. + +*subscripts* +: *optional* Dimensions of an array element. When not explicitly stated in *lower*, the lower bound of an array is controlled by the [**Option Base**](Option#Base) statement. The lower bound is zero if no **Option Base** statement is present. + +*type* +: Data type of the element; may be **Byte**, **Boolean**, **Integer**, **Long**, **Currency**, **Single**, **Double**, **Decimal** (not currently supported), **Date**, **String** (for variable-length strings), **String** *length* (for fixed-length strings), **Object**, **Variant**, another user-defined type, or an object type. + +The **Type** statement can be used only at the module level. After you have declared a user-defined type by using the **Type** statement, you can declare a variable of that type anywhere within the scope of the declaration. Use [**Dim**](Dim), [**Private**](Private), [**Public**](Public), [**ReDim**](ReDim), or [**Static**](Static) to declare a variable of a user-defined type. + +In standard modules and class modules, user-defined types are public by default. This visibility can be changed by using the **Private** keyword. + +Line numbers and line labels aren't allowed in **Type...End Type** blocks. + +User-defined types are often used with data records, which frequently consist of a number of related elements of different data types. + +The following example shows the use of fixed-size arrays in a user-defined type: + +```tb +Type StateData + CityCode (1 To 100) As Integer ' Declare a static array. + County As String * 30 +End Type + +Dim Washington(1 To 100) As StateData +``` + +In the preceding example, `StateData` includes the `CityCode` static array, and the record `Washington` has the same structure as `StateData`. + +When you declare a fixed-size array within a user-defined type, its dimensions must be declared with numeric literals or constants rather than variables. + +### Example + +This example uses the **Type** statement to define a user-defined data type. The **Type** statement is used at the module level only. If it appears in a class module, a **Type** statement must be preceded by the keyword **Private**. + +```tb +Type EmployeeRecord ' Create user-defined type. + ID As Integer ' Define elements of data type. + Name As String * 20 + Address As String * 30 + Phone As Long + HireDate As Date +End Type + +Sub CreateRecord() + Dim MyRecord As EmployeeRecord ' Declare variable. + + ' Assignment to EmployeeRecord variable must occur in a procedure. + MyRecord.ID = 12003 ' Assign a value to an element. +End Sub +``` + +### See Also + +- [**Dim** statement](Dim) +- [**Private** statement](Private) +- [**Public** statement](Public) +- [**Enum** statement](Enum) +- [**Class** statement](Class) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index 43cf285..05234d5 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -11,9 +11,7 @@ redirect_from: - /tB/Core/Implements - /tB/Core/Input - /tB/Core/Interface - - /tB/Core/Is - - /tB/Core/Let - /tB/Core/Line-Input - /tB/Core/Load - /tB/Core/Lock @@ -22,7 +20,6 @@ redirect_from: - /tB/Core/MidB-equals - /tB/Core/Module - /tB/Core/Name - - /tB/Core/New - /tB/Core/On-Error - /tB/Core/On-GoSub - /tB/Core/On-GoTo @@ -31,13 +28,10 @@ redirect_from: - /tB/Core/Protected - /tB/Core/Put - /tB/Core/RaiseEvent - - /tB/Core/ReDim - /tB/Core/Resume - /tB/Core/Return - /tB/Core/RSet - /tB/Core/SavePicture - - /tB/Core/Set - - /tB/Core/Type - /tB/Core/Unload - /tB/Core/Unlock - /tB/Core/Write diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index e87dd10..1532439 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -61,9 +61,13 @@ These statements are built into the language itself. They are understood by the * [Implements](../tB/Core/Implements) +* [Is](../tB/Core/Is) -- compares two object references for identity + +* [IsNot](../tB/Core/IsNot) -- the logical inverse of **Is**; compares two object references for non-identity + * [Kill](../tB/Core/Kill) -- deletes files from a disk -* [Let](../tB/Core/Let) +* [Let](../tB/Core/Let) -- assigns the value of an expression to a variable or property * [Line Input](../tB/Core/Line-Input) @@ -77,6 +81,8 @@ These statements are built into the language itself. They are understood by the * [Module](../tB/Core/Module) +* [New](../tB/Core/New) -- creates a new instance of a class + * [On Error](../tB/Core/On-Error) * [On ... GoTo](../tB/Core/On-GoTo), [On .. GoSub](../tB/Core/On-GoSub) @@ -101,7 +107,7 @@ These statements are built into the language itself. They are understood by the * [Randomize](../tB/Modules/Math/Randomize) -- initializes the random-number generator -* [ReDim](../tB/Core/ReDim) +* [ReDim](../tB/Core/ReDim) -- reallocates storage space for a dynamic array * [Resume](../tB/Core/Resume) @@ -113,7 +119,7 @@ These statements are built into the language itself. They are understood by the * [Select Case](../tB/Core/Select-Case) -- executes one of several groups of statements, depending on the value of an expression -* [Set](../tB/Core/Set) +* [Set](../tB/Core/Set) -- assigns an object reference to a variable or property * [Static](../tB/Core/Static) -- declares procedure-local variables whose values are preserved between calls @@ -121,6 +127,8 @@ These statements are built into the language itself. They are understood by the * [Sub](../tB/Core/Sub) -- declares the name, arguments, and code that form the body of a **Sub** procedure +* [Type](../tB/Core/Type) -- defines a user-defined data type containing one or more elements + * [Unlock](../tB/Core/Unlock) * [While ... Wend](../tB/Core/While-Wend) -- executes a series of statements as long as a given condition is **True** From 042210e26c57c7f9c9a2ed17d17312b9b5d74966 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 16:15:58 +0200 Subject: [PATCH 04/10] Document Get, [Line-]Input, [Un]Lock, LSet, Mid[B]=, Name, Open, Print, Put, RSet, Write. --- docs/Reference/Core/Get.md | 98 ++++++++++++++++++++++++ docs/Reference/Core/Input.md | 71 +++++++++++++++++ docs/Reference/Core/LSet.md | 50 ++++++++++++ docs/Reference/Core/Line-Input.md | 47 ++++++++++++ docs/Reference/Core/Lock.md | 73 ++++++++++++++++++ docs/Reference/Core/Mid-equals.md | 54 +++++++++++++ docs/Reference/Core/MidB-equals.md | 37 +++++++++ docs/Reference/Core/Name.md | 44 +++++++++++ docs/Reference/Core/Open.md | 118 +++++++++++++++++++++++++++++ docs/Reference/Core/Print.md | 93 +++++++++++++++++++++++ docs/Reference/Core/Put.md | 99 ++++++++++++++++++++++++ docs/Reference/Core/RSet.md | 41 ++++++++++ docs/Reference/Core/Unlock.md | 27 +++++++ docs/Reference/Core/Write.md | 69 +++++++++++++++++ docs/Reference/Core/todo.md | 14 ---- docs/Reference/Statements.md | 28 +++---- 16 files changed, 935 insertions(+), 28 deletions(-) create mode 100644 docs/Reference/Core/Get.md create mode 100644 docs/Reference/Core/Input.md create mode 100644 docs/Reference/Core/LSet.md create mode 100644 docs/Reference/Core/Line-Input.md create mode 100644 docs/Reference/Core/Lock.md create mode 100644 docs/Reference/Core/Mid-equals.md create mode 100644 docs/Reference/Core/MidB-equals.md create mode 100644 docs/Reference/Core/Name.md create mode 100644 docs/Reference/Core/Open.md create mode 100644 docs/Reference/Core/Print.md create mode 100644 docs/Reference/Core/Put.md create mode 100644 docs/Reference/Core/RSet.md create mode 100644 docs/Reference/Core/Unlock.md create mode 100644 docs/Reference/Core/Write.md diff --git a/docs/Reference/Core/Get.md b/docs/Reference/Core/Get.md new file mode 100644 index 0000000..d82569b --- /dev/null +++ b/docs/Reference/Core/Get.md @@ -0,0 +1,98 @@ +--- +title: Get +parent: Statements +permalink: /tB/Core/Get +--- +# Get +{: .no_toc } + +Reads data from an open disk file into a variable. + +> [!NOTE] +> This page documents the **Get** *statement* (file I/O). The unrelated **[Property Get](Property)** procedure form is a different use of the keyword. + +Syntax: +> **Get** [ **#** ] *filenumber* **,** [ *recnumber* ] **,** *varname* + +*filenumber* +: Any valid file number. + +*recnumber* +: *optional* **Variant** (**Long**). Record number (**Random** mode files) or byte number (**Binary** mode files) at which reading begins. + +*varname* +: Valid variable name into which data is read. + +Data read with **Get** is usually written to a file with [**Put**](Put). The first record or byte in a file is at position 1, the second record or byte is at position 2, and so on. If you omit *recnumber*, the next record or byte following the last **Get** or **Put** statement (or pointed to by the last [**Seek**](../Modules/FileSystem/Seek) function) is read. You must include the delimiting commas: + +```tb +Get #4, , FileBuffer +``` + +For files opened in **Random** mode, the following rules apply: + +- If the length of the data being read is less than the length specified in the **Len** clause of the [**Open**](Open) statement, **Get** reads subsequent records on record-length boundaries. The space between the end of one record and the beginning of the next record is padded with the existing contents of the file buffer. Because the amount of padding data can't be determined with any certainty, it is generally a good idea to have the record length match the length of the data being read. + +- If the variable being read into is a variable-length string, **Get** reads a 2-byte descriptor containing the string length and then reads the data that goes into the variable. Therefore, the record length specified by the **Len** clause in the **Open** statement must be at least 2 bytes greater than the actual length of the string. + +- If the variable being read into is a **Variant** of numeric type, **Get** reads 2 bytes identifying the **VarType** of the **Variant** and then the data that goes into the variable. For example, when reading a **Variant** of **VarType** 3, **Get** reads 6 bytes: 2 bytes identifying the **Variant** as **VarType** 3 (**Long**) and 4 bytes containing the **Long** data. The record length specified by the **Len** clause in the **Open** statement must be at least 2 bytes greater than the actual number of bytes required to store the variable. + + > [!NOTE] + > Use the **Get** statement to read a **Variant** array from disk, but you can't use **Get** to read a scalar **Variant** containing an array. You also can't use **Get** to read objects from disk. + +- If the variable being read into is a **Variant** of **VarType** 8 (**String**), **Get** reads 2 bytes identifying the **VarType**, 2 bytes indicating the length of the string, and then reads the string data. The record length specified by the **Len** clause in the **Open** statement must be at least 4 bytes greater than the actual length of the string. + +- If the variable being read into is a dynamic array, **Get** reads a descriptor whose length equals 2 plus 8 times the number of dimensions, that is, `2 + 8 * NumberOfDimensions`. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the sum of all the bytes required to read the array data and the array descriptor. For example, the following array declaration requires 118 bytes when the array is written to disk. + + ```tb + Dim MyArray(1 To 5, 1 To 10) As Integer + ``` + + The 118 bytes are distributed as follows: 18 bytes for the descriptor (`2 + 8 * 2`), and 100 bytes for the data (`5 * 10 * 2`). + +- If the variable being read into is a fixed-size array, **Get** reads only the data. No descriptor is read. + +- If the variable being read into is any other type of variable (not a variable-length string or a **Variant**), **Get** reads only the variable data. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the length of the data being read. + +- **Get** reads elements of user-defined types as if each were being read individually, except that there is no padding between elements. On disk, a dynamic array in a user-defined type (written with **Put**) is prefixed by a descriptor whose length equals `2 + 8 * NumberOfDimensions`. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the sum of all the bytes required to read the individual elements, including any arrays and their descriptors. + +For files opened in **Binary** mode, all of the **Random** rules apply, except: + +- The **Len** clause in the **Open** statement has no effect. **Get** reads all variables from disk contiguously; that is, with no padding between records. + +- For any array other than an array in a user-defined type, **Get** reads only the data. No descriptor is read. + +- **Get** reads variable-length strings that aren't elements of user-defined types without expecting the 2-byte length descriptor. The number of bytes read equals the number of characters already in the string. For example, the following statements read 10 bytes from file number 1: + + ```tb + VarString = String(10, " ") + Get #1, , VarString + ``` + +### Example + +This example uses the **Get** statement to read data from a file into a variable. This example assumes that `TESTFILE` is a file containing five records of the user-defined type `Record`. + +```tb +Type Record ' Define user-defined type. + ID As Integer + Name As String * 20 +End Type + +Dim MyRecord As Record, Position ' Declare variables. +' Open sample file for random access. +Open "TESTFILE" For Random As #1 Len = Len(MyRecord) +' Read the sample file using the Get statement. +Position = 3 ' Define record number. +Get #1, Position, MyRecord ' Read third record. +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Put** statement](Put) +- [**Seek** function](../Modules/FileSystem/Seek) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Input.md b/docs/Reference/Core/Input.md new file mode 100644 index 0000000..7589d3e --- /dev/null +++ b/docs/Reference/Core/Input.md @@ -0,0 +1,71 @@ +--- +title: Input # +parent: Statements +permalink: /tB/Core/Input +--- +# Input # statement +{: .no_toc } + +Reads data from an open sequential file and assigns the data to variables. + +> [!NOTE] +> This page documents the **Input #** *statement*. The unrelated [**Input** function](../Modules/HiddenModule/Input) reads a fixed number of characters from any open file. + +Syntax: +> **Input** **#** *filenumber* **,** *varlist* + +*filenumber* +: Any valid file number. + +*varlist* +: Comma-delimited list of variables that are assigned values read from the file. *varlist* can't contain an array variable or an object variable. However, variables that describe an element of an array or user-defined type may be used. + +Data read with **Input #** is usually written to a file with [**Write #**](Write). Use this statement only with files opened in **Input** or **Binary** mode. When read, standard string or numeric data is assigned to variables without modification. + +The following table illustrates how other input data is treated: + +| Data | Value assigned to variable | +|:-----|:-----| +| Delimiting comma or blank line | **Empty** | +| `#NULL#` | **Null** | +| `#TRUE#` or `#FALSE#` | **True** or **False** | +| `#`*yyyy-mm-dd hh:mm:ss*`#` | The date and/or time represented by the expression | +| `#ERROR `*errornumber*`#` | *errornumber* (variable is a **Variant** tagged as an error) | + +Double quotation marks (`"`) within input data are ignored. + +> [!NOTE] +> You should not write strings that contain embedded quotation marks (for example, `"1,2""X"`) for use with the **Input #** statement; **Input #** parses this string as two complete and separate strings. + +Data items in a file must appear in the same order as the variables in *varlist* and match variables of the same data type. If a variable is numeric and the data is not numeric, a value of zero is assigned to the variable. + +If you reach the end of the file while you are inputting a data item, the input is terminated and an error occurs. + +> [!NOTE] +> To be able to correctly read data from a file into variables by using **Input #**, use the [**Write #**](Write) statement instead of the [**Print #**](Print) statement to write the data to the files. Using **Write #** ensures that each separate data field is properly delimited. + +### Example + +This example uses the **Input #** statement to read data from a file into two variables. This example assumes that `TESTFILE` is a file with a few lines of data written to it by using the **Write #** statement; that is, each line contains a string in quotations and a number separated by a comma, for example, `"Hello", 234`. + +```tb +Dim MyString, MyNumber +Open "TESTFILE" For Input As #1 ' Open file for input. +Do While Not EOF(1) ' Loop until end of file. + Input #1, MyString, MyNumber ' Read data into two variables. + Debug.Print MyString, MyNumber ' Print data to the Immediate window. +Loop +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Line Input #** statement](Line-Input) +- [**Write #** statement](Write) +- [**Print #** statement](Print) +- [**Input** function](../Modules/HiddenModule/Input) +- [**EOF** function](../Modules/FileSystem/EOF) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/LSet.md b/docs/Reference/Core/LSet.md new file mode 100644 index 0000000..47b5a00 --- /dev/null +++ b/docs/Reference/Core/LSet.md @@ -0,0 +1,50 @@ +--- +title: LSet +parent: Statements +permalink: /tB/Core/LSet +--- +# LSet +{: .no_toc } + +Left-aligns a string within a string variable, or copies a variable of one user-defined type to another variable of a different user-defined type. + +Syntax: +- > **LSet** *stringvar* **=** *string* +- > **LSet** *varname1* **=** *varname2* + +*stringvar* +: Name of a string variable. + +*string* +: String expression to be left-aligned within *stringvar*. + +*varname1* +: Variable name of the user-defined type being copied to. + +*varname2* +: Variable name of the user-defined type being copied from. + +**LSet** replaces any leftover characters in *stringvar* with spaces. + +If *string* is longer than *stringvar*, **LSet** places only the leftmost characters, up to the length of the *stringvar*, in *stringvar*. + +> [!WARNING] +> Using **LSet** to copy a variable of one user-defined type into a variable of a different user-defined type is not recommended. Copying data of one data type into space reserved for a different data type can cause unpredictable results. When you copy a variable from one user-defined type to another, the binary data from one variable is copied into the memory space of the other, without regard for the data types specified for the elements. + +### Example + +This example uses the **LSet** statement to left-align a string within a string variable. Although **LSet** can also be used to copy a variable of one user-defined type to another variable of a different but compatible user-defined type, this practice is not recommended; due to the varying implementations of data structures among platforms, such a use of **LSet** can't be guaranteed to be portable. + +```tb +Dim MyString +MyString = "0123456789" ' Initialize string. +LSet MyString = "<-Left" ' MyString contains "<-Left ". +``` + +### See Also + +- [**RSet** statement](RSet) +- [**Mid =** statement](Mid-equals) +- [**Let** statement](Let) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Line-Input.md b/docs/Reference/Core/Line-Input.md new file mode 100644 index 0000000..d930e08 --- /dev/null +++ b/docs/Reference/Core/Line-Input.md @@ -0,0 +1,47 @@ +--- +title: Line Input # +parent: Statements +permalink: /tB/Core/Line-Input +--- +# Line Input # statement +{: .no_toc } + +Reads a single line from an open sequential file and assigns it to a **String** variable. + +Syntax: +> **Line Input** **#** *filenumber* **,** *varname* + +*filenumber* +: Any valid file number. + +*varname* +: Valid **Variant** or **String** variable name. + +Data read with **Line Input #** is usually written to a file with [**Print #**](Print). + +The **Line Input #** statement reads from a file one character at a time until it encounters a carriage return (**Chr**(13)) or carriage return-linefeed (**Chr**(13) + **Chr**(10)) sequence. Carriage return-linefeed sequences are skipped rather than appended to the character string. + +### Example + +This example uses the **Line Input #** statement to read a line from a sequential file and assign it to a variable. This example assumes that `TESTFILE` is a text file with a few lines of sample data. + +```tb +Dim TextLine +Open "TESTFILE" For Input As #1 ' Open file. +Do While Not EOF(1) ' Loop until end of file. + Line Input #1, TextLine ' Read line into variable. + Debug.Print TextLine ' Print to the Immediate window. +Loop +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Input #** statement](Input) +- [**Print #** statement](Print) +- [**Write #** statement](Write) +- [**EOF** function](../Modules/FileSystem/EOF) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Lock.md b/docs/Reference/Core/Lock.md new file mode 100644 index 0000000..004251c --- /dev/null +++ b/docs/Reference/Core/Lock.md @@ -0,0 +1,73 @@ +--- +title: Lock, Unlock +parent: Statements +permalink: /tB/Core/Lock +--- +# Lock, Unlock +{: .no_toc } + +Controls access by other processes to all or part of a file opened with the [**Open**](Open) statement. + +Syntax: +- > **Lock** [ **#** ] *filenumber* **,** [ *recordrange* ] +- > **Unlock** [ **#** ] *filenumber* **,** [ *recordrange* ] + +*filenumber* +: Any valid file number. + +*recordrange* +: *optional* The range of records to lock or unlock. The *recordrange* settings are: + + > *recnumber* \| [ *start* ] **To** *end* + + *recnumber* + : Record number (**Random** mode files) or byte number (**Binary** mode files) at which locking or unlocking begins. + + *start* + : Number of the first record or byte to lock or unlock. + + *end* + : Number of the last record or byte to lock or unlock. + +The **Lock** and **Unlock** statements are used in environments where several processes might need access to the same file. + +**Lock** and **Unlock** statements are always used in pairs. The arguments to **Lock** and **Unlock** must match exactly. + +The first record or byte in a file is at position 1, the second record or byte is at position 2, and so on. If you specify just one record, only that record is locked or unlocked. If you specify a range of records and omit a starting record (*start*), all records from the first record to the end of the range (*end*) are locked or unlocked. Using **Lock** without *recnumber* locks the entire file; using **Unlock** without *recnumber* unlocks the entire file. + +If the file has been opened for sequential input or output, **Lock** and **Unlock** affect the entire file, regardless of the range specified by *start* and *end*. + +> [!IMPORTANT] +> Be sure to remove all locks with an **Unlock** statement before closing a file or quitting your program. Failure to remove locks produces unpredictable results. + +### Example + +This example illustrates the use of the **Lock** and **Unlock** statements. While a record is being modified, access by other processes to the record is denied. This example assumes that `TESTFILE` is a file containing five records of the user-defined type `Record`. + +```tb +Type Record ' Define user-defined type. + ID As Integer + Name As String * 20 +End Type + +Dim MyRecord As Record, RecordNumber ' Declare variables. +' Open sample file for random access. +Open "TESTFILE" For Random Shared As #1 Len = Len(MyRecord) +RecordNumber = 4 ' Define record number. +Lock #1, RecordNumber ' Lock record. +Get #1, RecordNumber, MyRecord ' Read record. +MyRecord.ID = 234 ' Modify record. +MyRecord.Name = "John Smith" +Put #1, RecordNumber, MyRecord ' Write modified record. +Unlock #1, RecordNumber ' Unlock current record. +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Get** statement](Get) +- [**Put** statement](Put) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Mid-equals.md b/docs/Reference/Core/Mid-equals.md new file mode 100644 index 0000000..41d6b9b --- /dev/null +++ b/docs/Reference/Core/Mid-equals.md @@ -0,0 +1,54 @@ +--- +title: Mid = +parent: Statements +permalink: /tB/Core/Mid-equals +--- +# Mid = statement +{: .no_toc } + +Replaces a specified number of characters in a **Variant** (**String**) variable with characters from another string. + +> [!NOTE] +> This page documents the **Mid =** *statement* (string mutation). The unrelated [**Mid** function](../Modules/Strings/Mid) returns a substring without modifying its argument. + +Syntax: +> **Mid(** *stringvar* **,** *start* [ **,** *length* ] **) =** *string* + +*stringvar* +: Name of the string variable to modify. + +*start* +: **Variant** (**Long**). Character position in *stringvar* where the replacement of text begins. + +*length* +: *optional* **Variant** (**Long**). Number of characters to replace. If omitted, all of *string* is used. + +*string* +: String expression that replaces part of *stringvar*. + +The number of characters replaced is always less than or equal to the number of characters in *stringvar*. + +> [!NOTE] +> Use the [**MidB =**](MidB-equals) statement with byte data contained in a string. In the **MidB =** statement, *start* specifies the byte position within *stringvar* where replacement begins, and *length* specifies the number of bytes to replace. + +### Example + +This example uses the **Mid =** statement to replace a specified number of characters in a string variable with characters from another string. + +```tb +Dim MyString +MyString = "The dog jumps" ' Initialize string. +Mid(MyString, 5, 3) = "fox" ' MyString = "The fox jumps". +Mid(MyString, 5) = "cow" ' MyString = "The cow jumps". +Mid(MyString, 5) = "cow jumped over" ' MyString = "The cow jumpe". +Mid(MyString, 5, 3) = "duck" ' MyString = "The duc jumpe". +``` + +### See Also + +- [**MidB =** statement](MidB-equals) +- [**Mid** function](../Modules/Strings/Mid) +- [**LSet** statement](LSet) +- [**RSet** statement](RSet) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/MidB-equals.md b/docs/Reference/Core/MidB-equals.md new file mode 100644 index 0000000..53ca780 --- /dev/null +++ b/docs/Reference/Core/MidB-equals.md @@ -0,0 +1,37 @@ +--- +title: MidB = +parent: Statements +permalink: /tB/Core/MidB-equals +--- +# MidB = statement +{: .no_toc } + +Replaces a specified number of bytes in a **Variant** (**String**) variable with bytes from another string. The byte-mode counterpart of the [**Mid =**](Mid-equals) statement. + +Syntax: +> **MidB(** *stringvar* **,** *start* [ **,** *length* ] **) =** *string* + +*stringvar* +: Name of the string variable to modify. + +*start* +: **Variant** (**Long**). Byte position in *stringvar* where the replacement of bytes begins. + +*length* +: *optional* **Variant** (**Long**). Number of bytes to replace. If omitted, all of *string* is used. + +*string* +: String expression whose bytes replace part of *stringvar*. + +The number of bytes replaced is always less than or equal to the number of bytes in *stringvar*. + +**MidB =** is the byte-positioned form of [**Mid =**](Mid-equals): in this form, *start* and *length* count bytes of the underlying buffer rather than characters. This matters in double-byte character set languages where one character may occupy two bytes. + +### See Also + +- [**Mid =** statement](Mid-equals) +- [**MidB** function](../Modules/Strings/Mid) +- [**LSet** statement](LSet) +- [**RSet** statement](RSet) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Name.md b/docs/Reference/Core/Name.md new file mode 100644 index 0000000..eb4b6d3 --- /dev/null +++ b/docs/Reference/Core/Name.md @@ -0,0 +1,44 @@ +--- +title: Name +parent: Statements +permalink: /tB/Core/Name +--- +# Name +{: .no_toc } + +Renames a disk file, directory, or folder. + +Syntax: +> **Name** *oldpathname* **As** *newpathname* + +*oldpathname* +: String expression that specifies the existing file name and location; may include directory or folder, and drive. + +*newpathname* +: String expression that specifies the new file name and location; may include directory or folder, and drive. The file name specified by *newpathname* can't already exist. + +The **Name** statement renames a file and moves it to a different directory or folder, if necessary. **Name** can move a file across drives, but it can only rename an existing directory or folder when both *newpathname* and *oldpathname* are located on the same drive. **Name** cannot create a new file, directory, or folder. + +Using **Name** on an open file produces an error. You must close an open file before renaming it. **Name** arguments cannot include multiple-character (`*`) and single-character (`?`) wildcards. + +### Example + +This example uses the **Name** statement to rename a file. For purposes of this example, assume that the directories or folders that are specified already exist. + +```tb +Dim oldName, newName +oldName = "OLDFILE": newName = "NEWFILE" ' Define file names. +Name oldName As newName ' Rename file. + +oldName = "C:\MYDIR\OLDFILE": newName = "C:\YOURDIR\NEWFILE" +Name oldName As newName ' Move and rename file. +``` + +### See Also + +- [**Kill** statement](../Modules/FileSystem/Kill) +- [**FileCopy** procedure](../Modules/FileSystem/FileCopy) +- [**MkDir** statement](../Modules/FileSystem/MkDir) +- [**RmDir** statement](../Modules/FileSystem/RmDir) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Open.md b/docs/Reference/Core/Open.md new file mode 100644 index 0000000..a159522 --- /dev/null +++ b/docs/Reference/Core/Open.md @@ -0,0 +1,118 @@ +--- +title: Open +parent: Statements +permalink: /tB/Core/Open +--- +# Open +{: .no_toc } + +Enables input/output (I/O) to a file. + +Syntax: +> **Open** *pathname* **For** *mode* [ **Access** *access* ] [ *lock* ] [ **Encoding** *encoding* ] **As** [ **#** ] *filenumber* [ **Len** **=** *reclength* ] + +*pathname* +: String expression that specifies a file name; may include directory or folder, and drive. + +*mode* +: Keyword specifying the file mode: **Append**, **Binary**, **Input**, **Output**, or **Random**. If unspecified, the file is opened for **Random** access. + +*access* +: *optional* Keyword specifying the operations permitted on the open file: **Read**, **Write**, or **Read Write**. + +*lock* +: *optional* Keyword specifying the operations restricted on the open file by other processes: **Shared**, **Lock Read**, **Lock Write**, or **Lock Read Write**. + +*encoding* +: *optional* An encoding identifier from the [**TextEncodingConstants**](../Modules/TextEncodingConstants/) module — for example **utf_8**, **utf_16**, **windows_1252_western**, or **default_system_ansi**. The IDE surfaces these constants automatically in IntelliSense after the **Encoding** keyword. Other system-supported encodings with similar identifier strings are also accepted at runtime. The **Encoding** clause applies to text-mode I/O (**Input**, **Output**, **Append**); it has no effect on **Binary** or **Random** mode files. + +*filenumber* +: A valid file number in the range 1 to 511, inclusive. Use the [**FreeFile**](../Modules/FileSystem/FreeFile) function to obtain the next available file number. + +*reclength* +: *optional* Number less than or equal to 32,767 (bytes). For files opened for random access, this value is the record length. For sequential files, this value is the number of characters buffered. + +You must open a file before any I/O operation can be performed on it. **Open** allocates a buffer for I/O to the file and determines the mode of access to use with the buffer. + +If the file specified by *pathname* doesn't exist, it is created when a file is opened for **Append**, **Binary**, **Output**, or **Random** modes. + +If the file is already opened by another process, and the specified type of access is not allowed, the **Open** operation fails and an error occurs. + +The **Len** clause is ignored if *mode* is **Binary**. + +> [!IMPORTANT] +> In **Binary**, **Input**, and **Random** modes, you can open a file by using a different file number without first closing the file. In **Append** and **Output** modes, you must close a file before opening it with a different file number. + +> [!NOTE] +> The **Encoding** clause is a twinBASIC extension. Classic VBA has no equivalent and reads or writes text using the system ANSI code page only. + +### Example + +This example illustrates various uses of the **Open** statement to enable input and output to a file. + +The following code opens the file in sequential-input mode. + +```tb +Open "TESTFILE" For Input As #1 +' Close before reopening in another mode. +Close #1 +``` + +This example opens the file in **Binary** mode for writing operations only. + +```tb +Open "TESTFILE" For Binary Access Write As #1 +' Close before reopening in another mode. +Close #1 +``` + +The following example opens the file in **Random** mode. The file contains records of the user-defined type. + +```tb +Type Record ' Define user-defined type. + ID As Integer + Name As String * 20 +End Type + +Dim MyRecord As Record ' Declare variable. +Open "TESTFILE" For Random As #1 Len = Len(MyRecord) +' Close before reopening in another mode. +Close #1 +``` + +This code example opens the file for sequential output; any process can read or write to the file. + +```tb +Open "TESTFILE" For Output Shared As #1 +' Close before reopening in another mode. +Close #1 +``` + +This code example opens the file in **Binary** mode for reading; other processes can't read the file. + +```tb +Open "TESTFILE" For Binary Access Read Lock Read As #1 +``` + +This example reads a UTF-8 text file. The **Encoding** clause names the [TextEncodingConstants](../Modules/TextEncodingConstants/) constant for UTF-8. + +```tb +Open "C:\MyFile.txt" For Input Encoding utf_8 As #1 +' Subsequent Line Input #, Input #, etc. interpret bytes as UTF-8. +Close #1 +``` + +### See Also + +- [**Close** statement](Close) +- [**Get** statement](Get) +- [**Put** statement](Put) +- [**Input #** statement](Input) +- [**Line Input #** statement](Line-Input) +- [**Print #** statement](Print) +- [**Write #** statement](Write) +- [**Lock** / **Unlock** statements](Lock) +- [**FreeFile** function](../Modules/FileSystem/FreeFile) +- [**TextEncodingConstants** module](../Modules/TextEncodingConstants/) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Print.md b/docs/Reference/Core/Print.md new file mode 100644 index 0000000..4a35050 --- /dev/null +++ b/docs/Reference/Core/Print.md @@ -0,0 +1,93 @@ +--- +title: Print # +parent: Statements +permalink: /tB/Core/Print +--- +# Print # statement +{: .no_toc } + +Writes display-formatted data to a sequential file. + +> [!NOTE] +> This page documents the **Print #** *statement* (file I/O). The unrelated `Debug.Print` statement writes to the **Immediate** window during debugging. + +Syntax: +> **Print** **#** *filenumber* **,** [ *outputlist* ] + +*filenumber* +: Any valid file number. + +*outputlist* +: *optional* Expression or list of expressions to print. The *outputlist* settings are: + + > [ { **Spc(***n***)** \| **Tab** [ **(***n***)** ] } ] [ *expression* ] [ *charpos* ] + + **Spc(***n***)** + : Used to insert space characters in the output, where *n* is the number of space characters to insert. + + **Tab(***n***)** + : Used to position the insertion point to an absolute column number, where *n* is the column number. Use **Tab** with no argument to position the insertion point at the beginning of the next print zone. + + *expression* + : Numeric expressions or string expressions to print. + + *charpos* + : Specifies the insertion point for the next character. Use a semicolon to position the insertion point immediately after the last character displayed. Use **Tab(***n***)** to position the insertion point to an absolute column number. Use **Tab** with no argument to position the insertion point at the beginning of the next print zone. If *charpos* is omitted, the next character is printed on the next line. + +Data written with **Print #** is usually read from a file with [**Line Input #**](Line-Input) or [**Input #**](Input). + +If you omit *outputlist* and include only a list separator after *filenumber*, a blank line is printed to the file. + +Multiple expressions can be separated with either a space or a semicolon. A space has the same effect as a semicolon. + +For **Boolean** data, either `True` or `False` is printed. The **True** and **False** keywords are not translated, regardless of the locale. + +**Date** data is written to the file by using the standard short date format recognized by your system. When either the date or the time component is missing or zero, only the part provided gets written to the file. + +Nothing is written to the file if *outputlist* data is **Empty**. However, if *outputlist* data is **Null**, `Null` is written to the file. + +For **Error** data, the output appears as `Error `*errorcode*. The **Error** keyword is not translated regardless of the locale. + +All data written to the file by using **Print #** is internationally-aware; that is, the data is properly formatted by using the appropriate decimal separator. + +Because **Print #** writes an image of the data to the file, you must delimit the data so that it prints correctly. If you use **Tab** with no arguments to move the print position to the next print zone, **Print #** also writes the spaces between print fields to the file. + +> [!NOTE] +> If, at some future time, you want to read the data from a file by using the **Input #** statement, use the [**Write #**](Write) statement instead of the **Print #** statement to write the data to the file. Using **Write #** ensures the integrity of each separate data field by properly delimiting it, so that it can be read back in by using **Input #**. Using **Write #** also ensures that it can be correctly read in any locale. + +### Example + +This example uses the **Print #** statement to write data to a file. + +```tb +Open "TESTFILE" For Output As #1 ' Open file for output. +Print #1, "This is a test" ' Print text to file. +Print #1, ' Print blank line to file. +Print #1, "Zone 1"; Tab; "Zone 2" ' Print in two print zones. +Print #1, "Hello"; " "; "World" ' Separate strings with space. +Print #1, Spc(5); "5 leading spaces " ' Print five leading spaces. +Print #1, Tab(10); "Hello" ' Print word at column 10. + +' Assign Boolean, Date, Null and Error values. +Dim MyBool, MyDate, MyNull, MyError +MyBool = False : MyDate = #February 12, 1969# : MyNull = Null +MyError = CVErr(32767) +' True, False, Null, and Error are translated using locale settings of +' your system. Date literals are written using standard short date +' format. +Print #1, MyBool; " is a Boolean value" +Print #1, MyDate; " is a date" +Print #1, MyNull; " is a null value" +Print #1, MyError; " is an error value" +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Write #** statement](Write) +- [**Input #** statement](Input) +- [**Line Input #** statement](Line-Input) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Put.md b/docs/Reference/Core/Put.md new file mode 100644 index 0000000..476a2d0 --- /dev/null +++ b/docs/Reference/Core/Put.md @@ -0,0 +1,99 @@ +--- +title: Put +parent: Statements +permalink: /tB/Core/Put +--- +# Put +{: .no_toc } + +Writes data from a variable to a disk file. + +Syntax: +> **Put** [ **#** ] *filenumber* **,** [ *recnumber* ] **,** *varname* + +*filenumber* +: Any valid file number. + +*recnumber* +: *optional* **Variant** (**Long**). Record number (**Random** mode files) or byte number (**Binary** mode files) at which writing begins. + +*varname* +: Name of the variable containing data to be written to disk. + +Data written with **Put** is usually read from a file with [**Get**](Get). + +The first record or byte in a file is at position 1, the second record or byte is at position 2, and so on. If you omit *recnumber*, the next record or byte after the last **Get** or **Put** statement, or pointed to by the last [**Seek**](../Modules/FileSystem/Seek) function, is written. You must include the delimiting commas: + +```tb +Put #4, , FileBuffer +``` + +For files opened in **Random** mode, the following rules apply: + +- If the length of the data being written is less than the length specified in the **Len** clause of the [**Open**](Open) statement, **Put** writes subsequent records on record-length boundaries. The space between the end of one record and the beginning of the next record is padded with the existing contents of the file buffer. Because the amount of padding data can't be determined with any certainty, it is generally a good idea to have the record length match the length of the data being written. If the length of the data being written is greater than the length specified in the **Len** clause of the **Open** statement, an error occurs. + +- If the variable being written is a variable-length string, **Put** writes a 2-byte descriptor containing the string length and then the variable. The record length specified by the **Len** clause in the **Open** statement must be at least 2 bytes greater than the actual length of the string. + +- If the variable being written is a **Variant** of a numeric type, **Put** writes 2 bytes identifying the **VarType** of the **Variant** and then writes the variable. For example, when writing a **Variant** of **VarType** 3, **Put** writes 6 bytes: 2 bytes identifying the **Variant** as **VarType** 3 (**Long**) and 4 bytes containing the **Long** data. The record length specified by the **Len** clause in the **Open** statement must be at least 2 bytes greater than the actual number of bytes required to store the variable. + + > [!NOTE] + > Use the **Put** statement to write a **Variant** array to disk, but you can't use **Put** to write a scalar **Variant** containing an array to disk. You also can't use **Put** to write objects to disk. + +- If the variable being written is a **Variant** of **VarType** 8 (**String**), **Put** writes 2 bytes identifying the **VarType**, 2 bytes indicating the length of the string, and then writes the string data. The record length specified by the **Len** clause in the **Open** statement must be at least 4 bytes greater than the actual length of the string. + +- If the variable being written is a dynamic array, **Put** writes a descriptor whose length equals `2 + 8 * NumberOfDimensions`. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the sum of all the bytes required to write the array data and the array descriptor. For example, the following array declaration requires 118 bytes when the array is written to disk. + + ```tb + Dim MyArray(1 To 5, 1 To 10) As Integer + ``` + + The 118 bytes are distributed as follows: 18 bytes for the descriptor (`2 + 8 * 2`), and 100 bytes for the data (`5 * 10 * 2`). + +- If the variable being written is a fixed-size array, **Put** writes only the data. No descriptor is written to disk. + +- If the variable being written is any other type of variable (not a variable-length string or a **Variant**), **Put** writes only the variable data. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the length of the data being written. + +- **Put** writes elements of user-defined types as if each were written individually, except that there is no padding between elements. On disk, a dynamic array in a user-defined type written with **Put** is prefixed by a descriptor whose length equals `2 + 8 * NumberOfDimensions`. The record length specified by the **Len** clause in the **Open** statement must be greater than or equal to the sum of all the bytes required to write the individual elements, including any arrays and their descriptors. + +For files opened in **Binary** mode, all of the **Random** rules apply, except: + +- The **Len** clause in the **Open** statement has no effect. **Put** writes all variables to disk contiguously; that is, with no padding between records. + +- For any array other than an array in a user-defined type, **Put** writes only the data. No descriptor is written. + +- **Put** writes variable-length strings that are not elements of user-defined types without the 2-byte length descriptor. The number of bytes written equals the number of characters in the string. For example, the following statements write 10 bytes to file number 1: + + ```tb + VarString$ = String$(10, " ") + Put #1, , VarString$ + ``` + +### Example + +This example uses the **Put** statement to write data to a file. Five records of the user-defined type are written to the file. + +```tb +Type Record ' Define user-defined type. + ID As Integer + Name As String * 20 +End Type + +Dim MyRecord As Record, RecordNumber ' Declare variables. +' Open file for random access. +Open "TESTFILE" For Random As #1 Len = Len(MyRecord) +For RecordNumber = 1 To 5 ' Loop 5 times. + MyRecord.ID = RecordNumber ' Define ID. + MyRecord.Name = "My Name" & RecordNumber ' Create a string. + Put #1, RecordNumber, MyRecord ' Write record to file. +Next RecordNumber +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Get** statement](Get) +- [**Seek** function](../Modules/FileSystem/Seek) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/RSet.md b/docs/Reference/Core/RSet.md new file mode 100644 index 0000000..fde0cda --- /dev/null +++ b/docs/Reference/Core/RSet.md @@ -0,0 +1,41 @@ +--- +title: RSet +parent: Statements +permalink: /tB/Core/RSet +--- +# RSet +{: .no_toc } + +Right-aligns a string within a string variable. + +Syntax: +> **RSet** *stringvar* **=** *string* + +*stringvar* +: Name of a string variable. + +*string* +: String expression to be right-aligned within *stringvar*. + +If *stringvar* is longer than *string*, **RSet** replaces any leftover characters in *stringvar* with spaces, back to its beginning. + +> [!NOTE] +> **RSet** can't be used with user-defined types. + +### Example + +This example uses the **RSet** statement to right-align a string within a string variable. + +```tb +Dim MyString +MyString = "0123456789" ' Initialize string. +RSet MyString = "Right->" ' MyString contains " Right->". +``` + +### See Also + +- [**LSet** statement](LSet) +- [**Mid =** statement](Mid-equals) +- [**Let** statement](Let) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Unlock.md b/docs/Reference/Core/Unlock.md new file mode 100644 index 0000000..932c0f1 --- /dev/null +++ b/docs/Reference/Core/Unlock.md @@ -0,0 +1,27 @@ +--- +title: Unlock +parent: Statements +permalink: /tB/Core/Unlock +--- +# Unlock +{: .no_toc } + +Releases a lock acquired with the [**Lock**](Lock) statement, restoring access by other processes to the previously locked region of an open file. + +The **Unlock** statement is documented together with **Lock** on the [**Lock, Unlock**](Lock) page. + +Syntax: +> **Unlock** [ **#** ] *filenumber* **,** [ *recordrange* ] + +The arguments to **Unlock** must match exactly the arguments of the corresponding **Lock** statement. See [**Lock, Unlock**](Lock) for full details. + +> [!IMPORTANT] +> Be sure to remove all locks with an **Unlock** statement before closing a file or quitting your program. Failure to remove locks produces unpredictable results. + +### See Also + +- [**Lock** statement](Lock) +- [**Open** statement](Open) +- [**Close** statement](Close) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Write.md b/docs/Reference/Core/Write.md new file mode 100644 index 0000000..c8f4119 --- /dev/null +++ b/docs/Reference/Core/Write.md @@ -0,0 +1,69 @@ +--- +title: Write # +parent: Statements +permalink: /tB/Core/Write +--- +# Write # statement +{: .no_toc } + +Writes data to a sequential file. + +Syntax: +> **Write** **#** *filenumber* **,** [ *outputlist* ] + +*filenumber* +: Any valid file number. + +*outputlist* +: *optional* One or more comma-delimited numeric expressions or string expressions to write to a file. + +Data written with **Write #** is usually read from a file with [**Input #**](Input). + +If you omit *outputlist* and include a comma after *filenumber*, a blank line is printed to the file. Multiple expressions can be separated with a space, a semicolon, or a comma. A space has the same effect as a semicolon. + +When **Write #** is used to write data to a file, several universal assumptions are followed so that the data can always be read and correctly interpreted by using **Input #**, regardless of locale: + +- Numeric data is always written by using the period as the decimal separator. +- For **Boolean** data, either `#TRUE#` or `#FALSE#` is printed. The **True** and **False** keywords are not translated, regardless of locale. +- **Date** data is written to the file by using the universal date format. When either the date or the time component is missing or zero, only the part provided gets written to the file. +- Nothing is written to the file if *outputlist* data is **Empty**. However, for **Null** data, `#NULL#` is written. +- For **Error** data, the output appears as `#ERROR `*errorcode*`#`. The **Error** keyword is not translated, regardless of locale. + +Unlike the [**Print #**](Print) statement, the **Write #** statement inserts commas between items and quotation marks around strings as they are written to the file. You don't have to put explicit delimiters in the list. **Write #** inserts a newline character — that is, a carriage return-linefeed (**Chr**(13) + **Chr**(10)) — after it has written the final character in *outputlist* to the file. + +> [!NOTE] +> You should not write strings that contain embedded quotation marks (for example, `"1,2""X"`) for use with the **Input #** statement; **Input #** parses this string as two complete and separate strings. + +### Example + +This example uses the **Write #** statement to write raw data to a sequential file. + +```tb +Open "TESTFILE" For Output As #1 ' Open file for output. +Write #1, "Hello World", 234 ' Write comma-delimited data. +Write #1, ' Write blank line. + +Dim MyBool, MyDate, MyNull, MyError +' Assign Boolean, Date, Null, and Error values. +MyBool = False : MyDate = #February 12, 1969# : MyNull = Null +MyError = CVErr(32767) +' Boolean data is written as #TRUE# or #FALSE#. Date literals are +' written in universal date format, for example, #1994-07-13# +' represents July 13, 1994. Null data is written as #NULL#. +' Error data is written as #ERROR errorcode#. +Write #1, MyBool; " is a Boolean value" +Write #1, MyDate; " is a date" +Write #1, MyNull; " is a null value" +Write #1, MyError; " is an error value" +Close #1 ' Close file. +``` + +### See Also + +- [**Open** statement](Open) +- [**Close** statement](Close) +- [**Input #** statement](Input) +- [**Line Input #** statement](Line-Input) +- [**Print #** statement](Print) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index 05234d5..cc6cebb 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -5,36 +5,22 @@ redirect_from: - /tB/Core/Topic-Preprocessor - /tB/Core/CoClass - /tB/Core/Deftype - - /tB/Core/Get - /tB/Core/GoSub-Return - /tB/Core/GoTo - /tB/Core/Implements - - /tB/Core/Input - /tB/Core/Interface - - /tB/Core/Line-Input - /tB/Core/Load - - /tB/Core/Lock - - /tB/Core/LSet - - /tB/Core/Mid-equals - - /tB/Core/MidB-equals - /tB/Core/Module - - /tB/Core/Name - /tB/Core/On-Error - /tB/Core/On-GoSub - /tB/Core/On-GoTo - - /tB/Core/Open - - /tB/Core/Print - /tB/Core/Protected - - /tB/Core/Put - /tB/Core/RaiseEvent - /tB/Core/Resume - /tB/Core/Return - - /tB/Core/RSet - /tB/Core/SavePicture - /tB/Core/Unload - - /tB/Core/Unlock - - /tB/Core/Write --- > [!WARNING] diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 1532439..425243e 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -49,7 +49,7 @@ These statements are built into the language itself. They are understood by the * [Function](../tB/Core/Function) -- declares the name, arguments, and code that form the body of a **Function** procedure -* [Get](../tB/Core/Get) +* [Get](../tB/Core/Get) -- reads data from an open disk file into a variable * [GoSub ... Return](../tB/Core/GoSub-Return) @@ -57,7 +57,7 @@ These statements are built into the language itself. They are understood by the * [If ... Then ... Else](../tB/Core/If-Then-Else) -- conditionally executes a group of statements, depending on the value of an expression -* [Input](../tB/Core/Input) +* [Input #](../tB/Core/Input) -- reads data from an open sequential file and assigns it to variables * [Implements](../tB/Core/Implements) @@ -69,31 +69,33 @@ These statements are built into the language itself. They are understood by the * [Let](../tB/Core/Let) -- assigns the value of an expression to a variable or property -* [Line Input](../tB/Core/Line-Input) +* [Line Input #](../tB/Core/Line-Input) -- reads a single line from an open sequential file into a string variable -* [Lock](../tB/Core/Lock) +* [Lock](../tB/Core/Lock), [Unlock](../tB/Core/Unlock) -- control access by other processes to all or part of an open file -* [LSet](../tB/Core/LSet) +* [LSet](../tB/Core/LSet) -- left-aligns a string within a string variable, or copies one user-defined-type variable into another -* [Mid =](../tB/Core/Mid-equals) +* [Mid =](../tB/Core/Mid-equals) -- replaces a specified number of characters within a string variable -* [MidB =](../tB/Core/MidB-equals) +* [MidB =](../tB/Core/MidB-equals) -- byte-positioned form of **Mid =** * [Module](../tB/Core/Module) +* [Name](../tB/Core/Name) -- renames a disk file, directory, or folder + * [New](../tB/Core/New) -- creates a new instance of a class * [On Error](../tB/Core/On-Error) * [On ... GoTo](../tB/Core/On-GoTo), [On .. GoSub](../tB/Core/On-GoSub) -* [Open](../tB/Core/Open) +* [Open](../tB/Core/Open) -- enables input/output (I/O) to a file * [Option](../tB/Core/Option) -- configure a compiler option * [ParamArray](../tB/Core/ParamArray) -- declares the final parameter of a procedure as an arbitrary-arity list of arguments -* [Print](../tB/Core/Print) +* [Print #](../tB/Core/Print) -- writes display-formatted data to a sequential file * [Private](../tB/Core/Private) -- declares module-level variables accessible only within the declaring module @@ -101,7 +103,7 @@ These statements are built into the language itself. They are understood by the * [Public](../tB/Core/Public) -- declares module-level variables accessible to all procedures in all modules -* [Put](../tB/Core/Put) +* [Put](../tB/Core/Put) -- writes data from a variable to a disk file * [RaiseEvent](../tB/Core/RaiseEvent) @@ -113,7 +115,7 @@ These statements are built into the language itself. They are understood by the * [Return](../tB/Core/Return) -* [RSet](../tB/Core/RSet) +* [RSet](../tB/Core/RSet) -- right-aligns a string within a string variable * [Seek](../tB/Core/Seek) @@ -129,13 +131,11 @@ These statements are built into the language itself. They are understood by the * [Type](../tB/Core/Type) -- defines a user-defined data type containing one or more elements -* [Unlock](../tB/Core/Unlock) - * [While ... Wend](../tB/Core/While-Wend) -- executes a series of statements as long as a given condition is **True** * [With](../tB/Core/With) -- executes a series of statements on a single object or a user-defined type -* [Write](../tB/Core/Write) +* [Write #](../tB/Core/Write) -- writes raw, delimited data to a sequential file (paired with [**Input #**](../tB/Core/Input)) * [#If ... Then ... Else](../tB/Core/Topic-Preprocessor) From f50207792c315f00c9a81ff5fc85188a7925204a Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 16:20:45 +0200 Subject: [PATCH 05/10] Fix example of Encoding. --- docs/Features/Standard-Library/File-IO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Features/Standard-Library/File-IO.md b/docs/Features/Standard-Library/File-IO.md index 5c333fc..9f61f29 100644 --- a/docs/Features/Standard-Library/File-IO.md +++ b/docs/Features/Standard-Library/File-IO.md @@ -11,7 +11,7 @@ The `Open` statement supports Unicode through the use of a new `Encoding` keywor ## Usage Example ```tb -Open "C:\MyFile.txt" For Input Encoding utf-8 As #1 +Open "C:\MyFile.txt" For Input Encoding utf_8 As #1 ``` ## Supported Encodings From fe5407faa3f95513e2339ed053220cf6a588211e Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 16:27:02 +0200 Subject: [PATCH 06/10] Document [On-]Error, [On-]GoSub, [On-]GoTo, GoSub-Return, Resume, and Return. --- docs/Reference/Core/Function.md | 10 +-- docs/Reference/Core/GoSub-Return.md | 55 ++++++++++++++ docs/Reference/Core/GoTo.md | 51 +++++++++++++ docs/Reference/Core/On-Error.md | 110 ++++++++++++++++++++++++++++ docs/Reference/Core/On-GoSub.md | 24 ++++++ docs/Reference/Core/On-GoTo.md | 65 ++++++++++++++++ docs/Reference/Core/Property.md | 14 ++-- docs/Reference/Core/Resume.md | 54 ++++++++++++++ docs/Reference/Core/Return.md | 70 ++++++++++++++++++ docs/Reference/Core/Sub.md | 11 +-- docs/Reference/Core/todo.md | 7 -- docs/Reference/Statements.md | 12 +-- 12 files changed, 451 insertions(+), 32 deletions(-) create mode 100644 docs/Reference/Core/GoSub-Return.md create mode 100644 docs/Reference/Core/GoTo.md create mode 100644 docs/Reference/Core/On-Error.md create mode 100644 docs/Reference/Core/On-GoSub.md create mode 100644 docs/Reference/Core/On-GoTo.md create mode 100644 docs/Reference/Core/Resume.md create mode 100644 docs/Reference/Core/Return.md diff --git a/docs/Reference/Core/Function.md b/docs/Reference/Core/Function.md index 407871c..75d5ff7 100644 --- a/docs/Reference/Core/Function.md +++ b/docs/Reference/Core/Function.md @@ -15,7 +15,7 @@ Syntax: >      [ [ **Let** ] *name* **=** *expression* ] ... >      [ **Set** *name* **=** *expression* ] ... >      [ **Return** *expression* ] ... ->      [ **Exit Function** | **Return** ] ... +>      [ **Exit Function** ] ... >      [ *statements* ] ... > **End Function** @@ -56,11 +56,11 @@ Syntax: **[Set](Set)** : *optional* Assigns an object-type return value of the **Function** without exiting the function. -**[Return](Return)** -: *optional* Immediately returns from the function. If an *expression* is provided, its value is used as the return value of the **Function**. +**[Return](Return)** *expression* +: *optional* Immediately returns from the function with *expression* as the return value. The *expression* is required in this form; a bare **Return** is reserved for the [**GoSub...Return**](GoSub-Return) construct and does not exit a **Function**. **[Exit Function](Exit)** -: *optional* Immediately returns from the function. +: *optional* Immediately returns from the function without setting a return value. Use this to leave a function early when no value needs to be returned (the function will yield its default return value: 0 for numeric types, `""` for strings, **Empty** for **Variant**, **Nothing** for object references). *expression* : *optional* Return value of the **Function**. @@ -102,7 +102,7 @@ The **Friend** keyword can only be used in class modules. However, **Friend** pr All executable code must be in procedures. You can't define a **Function** procedure inside another **Function**, **[Sub](Sub)**, or **[Property](Property)** procedure. -The **[Exit Function](Exit)** and **[Return](Return)** statements cause an immediate exit from a **Function** procedure. Program execution continues with the statement following the statement that called the **Function** procedure. Any number of **Exit Function** and **Return** statements can appear anywhere in a **Function** procedure. +The **[Exit Function](Exit)** statement and the **[Return](Return)** *expression* statement both cause an immediate exit from a **Function** procedure. Program execution continues with the statement following the statement that called the **Function** procedure. Any number of these statements can appear anywhere in a **Function** procedure. Use **Exit Function** when you've already assigned the return value (or want the default), and **Return** *expression* when you want to set the return value and exit in a single step. Like a **Sub** procedure, a **Function** procedure is a separate procedure that can take arguments, perform a series of statements, and change the values of its arguments. However, unlike a **Sub** procedure, you can use a **Function** procedure on the right side of an expression in the same way you use any intrinsic function, such as **Sqr**, **Cos**, or **Chr**, when you want to use the value returned by the function. diff --git a/docs/Reference/Core/GoSub-Return.md b/docs/Reference/Core/GoSub-Return.md new file mode 100644 index 0000000..2a03830 --- /dev/null +++ b/docs/Reference/Core/GoSub-Return.md @@ -0,0 +1,55 @@ +--- +title: GoSub ... Return +parent: Statements +permalink: /tB/Core/GoSub-Return +--- +# GoSub ... Return +{: .no_toc } + +Branches to and returns from a subroutine within a procedure. + +Syntax: +> **GoSub** *line* +>      ... +> *line* +>      ... +>      **Return** + +*line* +: Any line label or line number. + +Use **GoSub** and **Return** anywhere in a procedure, but **GoSub** and the corresponding **Return** statement must be in the same procedure. A subroutine can contain more than one **Return** statement, but the first **Return** statement encountered causes the flow of execution to branch back to the statement immediately following the most recently executed **GoSub** statement. + +> [!NOTE] +> You can't enter or exit [**Sub**](Sub) procedures with **GoSub...Return**. + +> [!TIP] +> Creating separate procedures that you can call may provide a more structured alternative to using **GoSub...Return**. + +### Example + +This example uses **GoSub** to call a subroutine within a **Sub** procedure. The **Return** statement causes the execution to resume at the statement immediately following the **GoSub** statement. The [**Exit Sub**](Exit) statement is used to prevent control from accidentally flowing into the subroutine. + +```tb +Sub GosubDemo() + Dim Num + ' Solicit a number from the user. + Num = InputBox("Enter a positive number to be divided by 2.") + ' Only use routine if user enters a positive number. + If Num > 0 Then GoSub MyRoutine + Debug.Print Num + Exit Sub ' Use Exit to prevent an error. +MyRoutine: + Num = Num / 2 ' Perform the division. + Return ' Return control to statement following the GoSub statement. +End Sub +``` + +### See Also + +- [**Return** statement](Return) +- [**GoTo** statement](GoTo) +- [**On...GoSub** statement](On-GoSub) +- [**Sub** statement](Sub) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/GoTo.md b/docs/Reference/Core/GoTo.md new file mode 100644 index 0000000..66b7880 --- /dev/null +++ b/docs/Reference/Core/GoTo.md @@ -0,0 +1,51 @@ +--- +title: GoTo +parent: Statements +permalink: /tB/Core/GoTo +--- +# GoTo +{: .no_toc } + +Branches unconditionally to a specified line within a procedure. + +Syntax: +> **GoTo** *line* + +*line* +: Any line label or line number. + +**GoTo** can branch only to lines within the procedure where it appears. + +> [!NOTE] +> Too many **GoTo** statements can make code difficult to read and debug. Use structured control statements ([**Do...Loop**](Do-Loop), [**For...Next**](For-Next), [**If...Then...Else**](If-Then-Else), [**Select Case**](Select-Case)) whenever possible. + +### Example + +This example uses the **GoTo** statement to branch to line labels within a procedure. + +```tb +Sub GotoStatementDemo() + Dim Number, MyString + Number = 1 ' Initialize variable. + ' Evaluate Number and branch to appropriate label. + If Number = 1 Then GoTo Line1 Else GoTo Line2 + +Line1: + MyString = "Number equals 1" + GoTo LastLine ' Go to LastLine. +Line2: + ' The following statement never gets executed. + MyString = "Number equals 2" +LastLine: + Debug.Print MyString ' Print "Number equals 1" in the Immediate window. +End Sub +``` + +### See Also + +- [**On...GoTo** statement](On-GoTo) +- [**GoSub...Return** statement](GoSub-Return) +- [**On Error** statement](On-Error) +- [**Select Case** statement](Select-Case) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/On-Error.md b/docs/Reference/Core/On-Error.md new file mode 100644 index 0000000..b612ad5 --- /dev/null +++ b/docs/Reference/Core/On-Error.md @@ -0,0 +1,110 @@ +--- +title: On Error +parent: Statements +permalink: /tB/Core/On-Error +--- +# On Error +{: .no_toc } + +Enables an error-handling routine and specifies the location of the routine within a procedure; can also be used to disable an error-handling routine. + +Syntax: +- > **On Error GoTo** *line* +- > **On Error Resume Next** +- > **On Error GoTo 0** + +**On Error GoTo** *line* +: Enables the error-handling routine that starts at *line*. The *line* argument is any line label or line number. If a run-time error occurs, control branches to *line*, making the error handler active. The specified *line* must be in the same procedure as the **On Error** statement; otherwise, a compile-time error occurs. + +**On Error Resume Next** +: Specifies that when a run-time error occurs, control goes to the statement immediately following the statement where the error occurred and execution continues. Use this form rather than **On Error GoTo** when accessing objects. + +**On Error GoTo 0** +: Disables any enabled error handler in the current procedure. + +If you don't use an **On Error** statement, any run-time error that occurs is fatal; that is, an error message is displayed and execution stops. + +An "enabled" error handler is one that is turned on by an **On Error** statement; an "active" error handler is an enabled handler that is in the process of handling an error. If an error occurs while an error handler is active (between the occurrence of the error and a [**Resume**](Resume), [**Exit Sub**](Exit), **Exit Function**, or **Exit Property** statement), the current procedure's error handler can't handle the error. Control returns to the calling procedure. + +If the calling procedure has an enabled error handler, it is activated to handle the error. If the calling procedure's error handler is also active, control passes back through previous calling procedures until an enabled, but inactive, error handler is found. If no inactive, enabled error handler is found, the error is fatal at the point at which it actually occurred. + +Each time the error handler passes control back to a calling procedure, that procedure becomes the current procedure. After an error is handled by an error handler in any procedure, execution resumes in the current procedure at the point designated by the **Resume** statement. + +> [!NOTE] +> An error-handling routine is not a [**Sub**](Sub) procedure or [**Function**](Function) procedure. It's a section of code marked by a line label or line number. + +Error-handling routines rely on the value in the **Number** property of the **Err** object to determine the cause of the error. The error-handling routine should test or save relevant property values in the **Err** object before any other error can occur or before a procedure that might cause an error is called. The property values in the **Err** object reflect only the most recent error. The error message associated with **Err.Number** is contained in **Err.Description**. + +**On Error Resume Next** causes execution to continue with the statement immediately following the statement that caused the run-time error, or with the statement immediately following the most recent call out of the procedure containing the **On Error Resume Next** statement. This statement allows execution to continue despite a run-time error. You can place the error-handling routine where the error would occur, rather than transferring control to another location within the procedure. An **On Error Resume Next** statement becomes inactive when another procedure is called, so you should execute an **On Error Resume Next** statement in each called routine if you want inline error handling within that routine. + +> [!NOTE] +> The **On Error Resume Next** construct may be preferable to **On Error GoTo** when handling errors generated during access to other objects. Checking **Err** after each interaction with an object removes ambiguity about which object was accessed by the code. You can be sure which object placed the error code in **Err.Number**, as well as which object originally generated the error (the object specified in **Err.Source**). + +**On Error GoTo 0** disables error handling in the current procedure. It doesn't specify line 0 as the start of the error-handling code, even if the procedure contains a line numbered 0. Without an **On Error GoTo 0** statement, an error handler is automatically disabled when a procedure is exited. + +To prevent error-handling code from running when no error has occurred, place an [**Exit Sub**](Exit), **Exit Function**, or **Exit Property** statement immediately before the error-handling routine, as in the following fragment: + +```tb +Sub InitializeMatrix(Var1, Var2, Var3, Var4) + On Error GoTo ErrorHandler + . . . + Exit Sub +ErrorHandler: + . . . + Resume Next +End Sub +``` + +Here, the error-handling code follows the **Exit Sub** statement and precedes the [**End Sub**](End) statement to separate it from the procedure flow. Error-handling code can be placed anywhere in a procedure. + +If you create an object that accesses other objects, you should try to handle errors passed back from them unhandled. If you cannot handle such errors, map the error code in **Err.Number** to one of your own errors, and then pass them back to the caller of your object. You should specify your error by adding your error code to the **vbObjectError** constant. For example, if your error code is 1052, assign it as follows: + +```tb +Err.Number = vbObjectError + 1052 +``` + +> [!NOTE] +> System errors during calls to Windows dynamic-link libraries (DLLs) don't raise exceptions and cannot be trapped with twinBASIC error trapping. When calling DLL functions, you should check each return value for success or failure (according to the API specifications), and in the event of a failure, check the value in the **Err** object's **LastDLLError** property. + +### Example + +This example first uses the **On Error GoTo** statement to specify the location of an error-handling routine within a procedure. In the example, an attempt to delete an open file generates error number 55. The error is handled in the error-handling routine, and control is then returned to the statement that caused the error. The **On Error GoTo 0** statement turns off error trapping. + +The **On Error Resume Next** statement is then used to defer error trapping so that the context for the error generated by the next statement can be known for certain. Note that **Err.Clear** is used to clear the **Err** object's properties after the error is handled. + +```tb +Sub OnErrorStatementDemo() + On Error GoTo ErrorHandler ' Enable error-handling routine. + Open "TESTFILE" For Output As #1 ' Open file for output. + Kill "TESTFILE" ' Attempt to delete open file. + On Error GoTo 0 ' Turn off error trapping. + On Error Resume Next ' Defer error trapping. + ObjectRef = GetObject("MyWord.Basic") ' Try to start nonexistent object. + + ' Check for likely Automation errors. + If Err.Number = 440 Or Err.Number = 432 Then + ' Tell user what happened. Then clear the Err object. + Msg = "There was an error attempting to open the Automation object!" + MsgBox Msg, , "Deferred Error Test" + Err.Clear ' Clear Err object fields. + End If + Exit Sub ' Exit to avoid handler. +ErrorHandler: ' Error-handling routine. + Select Case Err.Number ' Evaluate error number. + Case 55 ' "File already open" error. + Close #1 ' Close open file. + Case Else + ' Handle other situations here... + End Select + Resume ' Resume execution at same line that caused the error. +End Sub +``` + +### See Also + +- [**Resume** statement](Resume) +- [**Error** statement](Error) +- [**Exit** statement](Exit) +- [**GoTo** statement](GoTo) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/On-GoSub.md b/docs/Reference/Core/On-GoSub.md new file mode 100644 index 0000000..53c12f3 --- /dev/null +++ b/docs/Reference/Core/On-GoSub.md @@ -0,0 +1,24 @@ +--- +title: On...GoSub +parent: Statements +permalink: /tB/Core/On-GoSub +--- +# On...GoSub +{: .no_toc } + +Branches to one of several specified subroutine lines, depending on the value of an expression. + +The **On...GoSub** statement is documented together with **On...GoTo** on the [**On...GoTo, On...GoSub**](On-GoTo) page. + +Syntax: +> **On** *expression* **GoSub** *destinationlist* + +When *expression* evaluates to *n*, control transfers to the *n*-th label in *destinationlist*, just as if a [**GoSub**](GoSub-Return) had been executed against that label. A subsequent [**Return**](Return) within the called subroutine resumes execution at the statement following the **On...GoSub**. See [**On...GoTo, On...GoSub**](On-GoTo) for the full description of out-of-range values, the 0-255 constraint on *expression*, and worked examples. + +### See Also + +- [**On...GoTo** statement](On-GoTo) +- [**GoSub...Return** statement](GoSub-Return) +- [**Select Case** statement](Select-Case) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/On-GoTo.md b/docs/Reference/Core/On-GoTo.md new file mode 100644 index 0000000..664b8dc --- /dev/null +++ b/docs/Reference/Core/On-GoTo.md @@ -0,0 +1,65 @@ +--- +title: On...GoTo, On...GoSub +parent: Statements +permalink: /tB/Core/On-GoTo +--- +# On...GoTo, On...GoSub +{: .no_toc } + +Branch to one of several specified lines, depending on the value of an expression. + +Syntax: +- > **On** *expression* **GoTo** *destinationlist* +- > **On** *expression* **GoSub** *destinationlist* + +*expression* +: Any numeric expression that evaluates to a whole number between 0 and 255, inclusive. If *expression* is any number other than a whole number, it is rounded before it is evaluated. + +*destinationlist* +: List of line numbers or line labels separated by commas. + +The value of *expression* determines which line is branched to in *destinationlist*. If the value of *expression* is less than 1 or greater than the number of items in the list, one of the following results occurs: + +| If *expression* is | Then | +|:-----|:-----| +| Equal to 0 | Control drops to the statement following **On...GoSub** or **On...GoTo**. | +| Greater than the number of items in the list | Control drops to the statement following **On...GoSub** or **On...GoTo**. | +| Negative | An error occurs. | +| Greater than 255 | An error occurs. | + +You can mix line numbers and line labels in the same list. Use as many line labels and line numbers as you like with **On...GoSub** and **On...GoTo**. However, if you use more labels or numbers than fit on a single line, you must use the line-continuation character to continue the logical line onto the next physical line. + +> [!TIP] +> [**Select Case**](Select-Case) provides a more structured and flexible way to perform multiple branching. + +### Example + +This example uses the **On...GoSub** and **On...GoTo** statements to branch to subroutines and line labels, respectively. + +```tb +Sub OnGosubGotoDemo() + Dim Number, MyString + Number = 2 ' Initialize variable. + ' Branch to Sub2. + On Number GoSub Sub1, Sub2 ' Execution resumes here after On...GoSub. + On Number GoTo Line1, Line2 ' Branch to Line2. + ' Execution does not resume here after On...GoTo. + Exit Sub +Sub1: + MyString = "In Sub1" : Return +Sub2: + MyString = "In Sub2" : Return +Line1: + MyString = "In Line1" +Line2: + MyString = "In Line2" +End Sub +``` + +### See Also + +- [**GoTo** statement](GoTo) +- [**GoSub...Return** statement](GoSub-Return) +- [**Select Case** statement](Select-Case) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Property.md b/docs/Reference/Core/Property.md index 71335d1..b53bd00 100644 --- a/docs/Reference/Core/Property.md +++ b/docs/Reference/Core/Property.md @@ -22,21 +22,21 @@ Syntax: >      [ [ **Let** ] *name* **=** *expression* ] ... >      [ **Set** *name* **=** *expression* ] ... >      [ **Return** *expression* ] ... - >      [ **Exit Property** \| **Return** ] ... + >      [ **Exit Property** ] ... >      [ *statements* ] ... > **End Property** - > [ *attributes* ] > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Let** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *value* **)** >      [ *statements* ] ... - >      [ **Exit Property** \| **Return** ] ... + >      [ **Exit Property** ] ... >      [ *statements* ] ... > **End Property** - > [ *attributes* ] > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Property Set** *name* [ **(** **Of** *typevars* **)** ] **(** [ *arglist* **,** ] *reference* **)** >      [ *statements* ] ... - >      [ **Exit Property** \| **Return** ] ... + >      [ **Exit Property** ] ... >      [ *statements* ] ... > **End Property** @@ -80,10 +80,10 @@ Syntax: : In **Property Set**, the variable containing the object reference used on the right side of the object reference assignment. *reference* cannot be **Optional**. **[Exit Property](Exit)** -: *optional* Immediately returns from the **Property** procedure. +: *optional* Immediately returns from the **Property** procedure without setting a return value. Valid in **Property Get**, **Property Let**, and **Property Set**. -**[Return](Return)** -: *optional* Immediately returns from the **Property** procedure. In **Property Get**, an *expression* may be supplied as the return value. +**[Return](Return)** *expression* +: *optional* Valid only in a **Property Get** procedure. Immediately returns from the procedure with *expression* as the property's value. The *expression* is required in this form; a bare **Return** is reserved for the [**GoSub...Return**](GoSub-Return) construct and does not exit a **Property** procedure. ### *arglist* @@ -118,7 +118,7 @@ The **Friend** keyword can only be used in class modules. However, **Friend** pr All executable code must be in procedures. You can't define a **Property** procedure inside another **[Property](Property)**, **[Sub](Sub)**, or **[Function](Function)** procedure. -The **[Exit Property](Exit)** and **[Return](Return)** statements cause an immediate exit from a **Property** procedure. Program execution continues with the statement following the statement that called the **Property** procedure. Any number of **Exit Property** and **Return** statements can appear anywhere in a **Property** procedure. +The **[Exit Property](Exit)** statement, and the **[Return](Return)** *expression* statement (in **Property Get** only), cause an immediate exit from a **Property** procedure. Program execution continues with the statement following the statement that called the **Property** procedure. Any number of these statements can appear anywhere in a **Property** procedure. Like **Sub** and **Function** procedures, a **Property** procedure is a separate procedure that can take arguments, perform a series of statements, and change the values of its arguments. A **Property Get** procedure can be used on the right side of an expression in the same way as a **Function** or a property name. A **Property Let** procedure can only be used on the left side of a property assignment expression or [**Let**](Let) statement. A **Property Set** procedure can only be used on the left side of an object reference assignment or **[Set](Set)** statement. diff --git a/docs/Reference/Core/Resume.md b/docs/Reference/Core/Resume.md new file mode 100644 index 0000000..bea3a1b --- /dev/null +++ b/docs/Reference/Core/Resume.md @@ -0,0 +1,54 @@ +--- +title: Resume +parent: Statements +permalink: /tB/Core/Resume +--- +# Resume +{: .no_toc } + +Resumes execution after an error-handling routine is finished. + +Syntax: +- > **Resume** [ **0** ] +- > **Resume Next** +- > **Resume** *line* + +**Resume** +: If the error occurred in the same procedure as the error handler, execution resumes with the statement that caused the error. If the error occurred in a called procedure, execution resumes at the statement that last called out of the procedure containing the error-handling routine. **Resume** and **Resume 0** are equivalent. + +**Resume Next** +: If the error occurred in the same procedure as the error handler, execution resumes with the statement immediately following the statement that caused the error. If the error occurred in a called procedure, execution resumes with the statement immediately following the statement that last called out of the procedure containing the error-handling routine (or the [**On Error Resume Next**](On-Error) statement). + +**Resume** *line* +: Execution resumes at the *line* specified. The *line* argument is a line label or line number, and must be in the same procedure as the error handler. + +If you use a **Resume** statement anywhere except in an error-handling routine, an error occurs. + +### Example + +This example uses the **Resume** statement to end error handling in a procedure, and then resume execution with the statement that caused the error. Error number 55 is generated to illustrate using the **Resume** statement. + +```tb +Sub ResumeStatementDemo() + On Error GoTo ErrorHandler ' Enable error-handling routine. + Open "TESTFILE" For Output As #1 ' Open file for output. + Kill "TESTFILE" ' Attempt to delete open file. + Exit Sub ' Exit Sub to avoid error handler. +ErrorHandler: ' Error-handling routine. + Select Case Err.Number ' Evaluate error number. + Case 55 ' "File already open" error. + Close #1 ' Close open file. + Case Else + ' Handle other situations here.... + End Select + Resume ' Resume execution at same line that caused the error. +End Sub +``` + +### See Also + +- [**On Error** statement](On-Error) +- [**Error** statement](Error) +- [**GoTo** statement](GoTo) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Return.md b/docs/Reference/Core/Return.md new file mode 100644 index 0000000..479c5a4 --- /dev/null +++ b/docs/Reference/Core/Return.md @@ -0,0 +1,70 @@ +--- +title: Return +parent: Statements +permalink: /tB/Core/Return +--- +# Return +{: .no_toc } + +Returns control from the current point of execution to its caller. The **Return** keyword has two distinct uses, distinguished by form and context: + +- **Bare `Return`** — valid only as the companion to [**GoSub**](GoSub-Return). Inside a **GoSub** subroutine, **Return** branches back to the statement immediately following the most recently executed **GoSub**. +- **`Return` *expression*** — valid only inside a [**Function**](Function) or [**Property Get**](Property) procedure (twinBASIC extension). It exits the procedure with *expression* as the return value. + +To exit a [**Sub**](Sub), [**Function**](Function), [**Property Get**](Property), [**Property Let**](Property), or [**Property Set**](Property) procedure *without* returning a value, use [**Exit Sub**](Exit), **Exit Function**, or **Exit Property** — there is no bare-`Return` procedure-exit form. + +Syntax: +- > **Return** +- > **Return** *expression* + +*expression* +: In a [**Function**](Function) or **Property Get** procedure, the value to return to the caller. The type of *expression* must be compatible with the procedure's declared return type. *expression* must be present in this form — it cannot be omitted. + +> [!NOTE] +> The **Return** *expression* form is a twinBASIC extension. Classic VBA reserves **Return** exclusively for the [**GoSub...Return**](GoSub-Return) construct, and the only way to set a function's return value is to assign to the function name (e.g., `MyFunc = expr`). twinBASIC supports both styles. + +### Example + +Returning a value from a **Function** by using **Return** *expression*: + +```tb +Function Square(N As Double) As Double + Return N * N +End Function + +Debug.Print Square(7) ' 49 +``` + +Exiting a **Sub** early — note the use of **Exit Sub**, not bare **Return**: + +```tb +Sub LogIfEnabled(ByVal Message As String) + If Not LoggingEnabled Then Exit Sub + Debug.Print Now & ": " & Message +End Sub +``` + +Returning from a **GoSub** subroutine — bare **Return** here resumes execution at the statement following the **GoSub**. See [**GoSub...Return**](GoSub-Return) for the full pattern. + +```tb +Sub GosubDemo() + Dim Num As Double + Num = 10 + GoSub Halve + Debug.Print Num ' 5 + Exit Sub +Halve: + Num = Num / 2 + Return ' Return to the GoSub call site. +End Sub +``` + +### See Also + +- [**GoSub...Return** statement](GoSub-Return) +- [**Exit** statement](Exit) +- [**Sub** statement](Sub) +- [**Function** statement](Function) +- [**Property** statement](Property) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Sub.md b/docs/Reference/Core/Sub.md index 776b361..6ada7b7 100644 --- a/docs/Reference/Core/Sub.md +++ b/docs/Reference/Core/Sub.md @@ -12,7 +12,7 @@ Syntax: > [ *attributes* ] > [ **Public** \| **Private** \| **Friend** ] [ **Static** ] **Sub** *name* [ **(** **Of** *typevars* **)** ] [ **(** *arglist* **)** ] >      [ *statements* ] ... ->      [ **Exit Sub** \| **Return** ] ... +>      [ **Exit Sub** ] ... >      [ *statements* ] ... > **End Sub** @@ -44,10 +44,7 @@ Syntax: : *optional* Any group of statements to be executed within the **Sub** procedure. **[Exit Sub](Exit)** -: *optional* Immediately returns from the **Sub** procedure. - -**[Return](Return)** -: *optional* Immediately returns from the **Sub** procedure. +: *optional* Immediately returns from the **Sub** procedure. (A bare [**Return**](Return) statement does *not* exit a **Sub** — it is reserved for the [**GoSub...Return**](GoSub-Return) construct.) ### *arglist* @@ -86,7 +83,7 @@ The **Friend** keyword can only be used in class modules. However, **Friend** pr All executable code must be in procedures. You can't define a **Sub** procedure inside another **[Sub](Sub)**, **[Function](Function)**, or **[Property](Property)** procedure. -The **[Exit Sub](Exit)** and **[Return](Return)** statements cause an immediate exit from a **Sub** procedure. Program execution continues with the statement following the statement that called the **Sub** procedure. Any number of **Exit Sub** and **Return** statements can appear anywhere in a **Sub** procedure. +The **[Exit Sub](Exit)** statement causes an immediate exit from a **Sub** procedure. Program execution continues with the statement following the statement that called the **Sub** procedure. Any number of **Exit Sub** statements can appear anywhere in a **Sub** procedure. Like a **Function** procedure, a **Sub** procedure is a separate procedure that can take arguments, perform a series of statements, and change the value of its arguments. However, unlike a **Function** procedure, which returns a value, a **Sub** procedure can't be used in an expression. @@ -97,7 +94,7 @@ Variables used in **Sub** procedures fall into two categories: those that are ex A procedure can use a variable that is not explicitly declared in the procedure, but a naming conflict can occur if anything you defined at the module level has the same name. If your procedure refers to an undeclared variable that has the same name as another procedure, constant, or variable, it is assumed that your procedure is referring to that module-level name. To avoid this kind of conflict, explicitly declare variables. Use an **[Option Explicit](Option#Explicit)** statement to force explicit declaration of variables. > [!NOTE] -> You can't use **GoSub**, **GoTo**, or **Return** to enter or exit a **Sub** procedure. (Inside a **Sub**, **Return** ends the procedure.) +> You can't use **GoSub**, **GoTo**, or **Return** to enter or exit a **Sub** procedure. Use [**Exit Sub**](Exit) to leave a **Sub** early. ### Example diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index cc6cebb..ccdc1b9 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -5,20 +5,13 @@ redirect_from: - /tB/Core/Topic-Preprocessor - /tB/Core/CoClass - /tB/Core/Deftype - - /tB/Core/GoSub-Return - - /tB/Core/GoTo - /tB/Core/Implements - /tB/Core/Interface - /tB/Core/Load - /tB/Core/Module - - /tB/Core/On-Error - - /tB/Core/On-GoSub - - /tB/Core/On-GoTo - /tB/Core/Protected - /tB/Core/RaiseEvent - - /tB/Core/Resume - - /tB/Core/Return - /tB/Core/SavePicture - /tB/Core/Unload --- diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 425243e..625b68f 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -51,9 +51,9 @@ These statements are built into the language itself. They are understood by the * [Get](../tB/Core/Get) -- reads data from an open disk file into a variable -* [GoSub ... Return](../tB/Core/GoSub-Return) +* [GoSub ... Return](../tB/Core/GoSub-Return) -- branches to and returns from a subroutine within a procedure -* [GoTo](../tB/Core/GoTo) +* [GoTo](../tB/Core/GoTo) -- branches unconditionally to a specified line within a procedure * [If ... Then ... Else](../tB/Core/If-Then-Else) -- conditionally executes a group of statements, depending on the value of an expression @@ -85,9 +85,9 @@ These statements are built into the language itself. They are understood by the * [New](../tB/Core/New) -- creates a new instance of a class -* [On Error](../tB/Core/On-Error) +* [On Error](../tB/Core/On-Error) -- enables an error-handling routine and specifies its location, or disables error handling -* [On ... GoTo](../tB/Core/On-GoTo), [On .. GoSub](../tB/Core/On-GoSub) +* [On ... GoTo](../tB/Core/On-GoTo), [On ... GoSub](../tB/Core/On-GoSub) -- branch to one of several lines based on the value of an expression * [Open](../tB/Core/Open) -- enables input/output (I/O) to a file @@ -111,9 +111,9 @@ These statements are built into the language itself. They are understood by the * [ReDim](../tB/Core/ReDim) -- reallocates storage space for a dynamic array -* [Resume](../tB/Core/Resume) +* [Resume](../tB/Core/Resume) -- resumes execution after an error-handling routine is finished -* [Return](../tB/Core/Return) +* [Return](../tB/Core/Return) -- returns from a **GoSub** subroutine, or (twinBASIC) exits a procedure with an optional value * [RSet](../tB/Core/RSet) -- right-aligns a string within a string variable From 518c44fb04248db7da23dc7f3cfd9179cb74e839 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 17:11:05 +0200 Subject: [PATCH 07/10] Document CoClass, Implements, Interface, Module, Protected and RaiseEvent. --- docs/Reference/Core/CoClass.md | 117 ++++++++++++++++++++++++++++++ docs/Reference/Core/Implements.md | 106 +++++++++++++++++++++++++++ docs/Reference/Core/Interface.md | 94 ++++++++++++++++++++++++ docs/Reference/Core/Module.md | 75 +++++++++++++++++++ docs/Reference/Core/Protected.md | 80 ++++++++++++++++++++ docs/Reference/Core/RaiseEvent.md | 98 +++++++++++++++++++++++++ docs/Reference/Core/todo.md | 6 -- docs/Reference/Statements.md | 12 ++- 8 files changed, 579 insertions(+), 9 deletions(-) create mode 100644 docs/Reference/Core/CoClass.md create mode 100644 docs/Reference/Core/Implements.md create mode 100644 docs/Reference/Core/Interface.md create mode 100644 docs/Reference/Core/Module.md create mode 100644 docs/Reference/Core/Protected.md create mode 100644 docs/Reference/Core/RaiseEvent.md diff --git a/docs/Reference/Core/CoClass.md b/docs/Reference/Core/CoClass.md new file mode 100644 index 0000000..50578af --- /dev/null +++ b/docs/Reference/Core/CoClass.md @@ -0,0 +1,117 @@ +--- +title: CoClass +parent: Statements +permalink: /tB/Core/CoClass +--- +# CoClass +{: .no_toc } + +Defines a creatable COM class as a contract: a public name and identity, paired with one or more [**Interface**](Interface) blocks that the class will expose. The actual implementation lives in a separate [**Class**](Class) (typically `Private`) that uses [**Implements**](Implements). + +> [!NOTE] +> The **CoClass** block is a twinBASIC extension. In classic VBA, coclasses could only be defined indirectly via a referenced type library (IDL/C++). + +Syntax: +> [ *attributes* ] +> [ **Public** \| **Private** ] **CoClass** *name* +>      [ *member-attributes* ] **Interface** *interfacename* +>      ... +> **End CoClass** + +*attributes* +: *optional* Coclass-level attributes. See [Available attributes](#available-attributes) below. + +*name* +: The identifier naming the coclass. + +*interfacename* +: An [**Interface**](Interface) defined in the project (or imported from a referenced type library) that the coclass exposes. A coclass must list at least one interface and may list several. + +*member-attributes* +: *optional* Per-interface markers, principally: + + - `[Default]` — marks an interface as the default interface of the coclass. It is conventional and highly recommended to mark exactly one interface as `[Default]`. + - `[Source]` — marks an interface as a source interface (an outgoing/event interface). Combine with `[Default]` (`[Default, Source]`) to mark the default event interface. + +**CoClass** blocks are valid only in `.twin` source files (not legacy `.bas` or `.cls` files), and must appear *before* the [**Class**](Class) or [**Module**](Module) statement in the file. + +### Available attributes + +- `[CoClassId("...")]` — fixes the CLSID for the coclass (a string GUID). Set this on any public/exported coclass so consumers in other projects bind to a stable identity. +- `[Description("text")]` — exposed as the `helpstring` in the type library. +- `[ComCreatable(True/False)]` — indicates whether the coclass can be created with **New**. `True` by default. +- `[AppObject]` — marks the class as part of the global namespace. Use only when you fully understand the implication. +- `[Hidden]` — hides the coclass from IntelliSense and similar lists. +- `[CoClassCustomConstructor("ModuleName.FunctionName")]` — names a factory function (returning `HRESULT` and producing the new instance via an out parameter) used in place of the default `New` behavior. The factory may construct any private class that implements the coclass's interfaces. + +### Example + +A simple coclass exposing two interfaces, with `IFoo` marked as the default: + +```tb +[CoClassId("52112FA1-FBE4-11CA-B5DD-0020AFE7292D")] +CoClass Foo + [Default] Interface IFoo + Interface IBar +End CoClass +``` + +A more complete example showing a custom-constructor coclass paired with a private implementing class. The coclass `Foo` is what consumers see and instantiate; the actual implementation `FooImpl` is hidden: + +```tb +[InterfaceId("016BC30A-A8E0-4AAF-93AE-13BD838A149E")] +Public Interface IFoo + Sub Foo() +End Interface + +[InterfaceId("2A20E655-30A4-4534-86BC-6A7E281C425D")] +Public Interface IBar + Sub Bar() +End Interface + +[CoClassId("7980D953-10BF-478C-93BB-DD0093315D96")] +[CoClassCustomConstructor("FooFactory.CreateFoo")] +[ComCreatable(True)] +Public CoClass Foo + [Default] Interface IFoo + Interface IBar +End CoClass + +' The implementation does not have to be exposed. +Private Class FooImpl + Implements IFoo + Implements IBar + + Public Sub Foo() Implements IFoo.Foo + Debug.Print "Foo ran" + End Sub + + Public Sub Bar() Implements IBar.Bar + Debug.Print "Bar ran" + End Sub +End Class + +Public Module FooFactory + ' The signature must be "preserved", returning an HRESULT + ' and the new instance via the "out" parameter. + Public Function CreateFoo(ByRef RHS As Foo) As Long + Set RHS = New FooImpl + Return 0 ' S_OK + End Function +End Module + +Public Module Test + Public Sub DoIt() + Dim MyFoo As Foo + Set MyFoo = New Foo ' Implicitly calls FooFactory.CreateFoo. + MyFoo.Foo + End Sub +End Module +``` + +### See Also + +- [**Interface** statement](Interface) +- [**Implements** statement](Implements) +- [**Class** statement](Class) +- [Interfaces and CoClasses](../../Features/Language/Interfaces-CoClasses) diff --git a/docs/Reference/Core/Implements.md b/docs/Reference/Core/Implements.md new file mode 100644 index 0000000..63266aa --- /dev/null +++ b/docs/Reference/Core/Implements.md @@ -0,0 +1,106 @@ +--- +title: Implements +parent: Statements +permalink: /tB/Core/Implements +--- +# Implements +{: .no_toc } + +Specifies an interface or class that will be implemented in the [class](Class) in which it appears. + +Syntax: +> **Implements** *InterfaceName* \| *ClassName* + +*InterfaceName* +: The name of an interface — either an [**Interface**](Interface) block defined in twinBASIC, or an interface in a referenced type library — whose members will be implemented by the corresponding members in the class. + +*ClassName* +: The name of a class whose default interface will be implemented. + +An *interface* is a collection of prototypes representing the members (methods and properties) that the interface encapsulates; that is, it contains only the declarations for the member procedures. A *class* provides an implementation of all the methods and properties of one or more interfaces. Classes provide the code used when each function is called by a controller of the class. All classes implement at least one interface, which is considered the default interface of the class. Any member that isn't explicitly a member of an implemented interface is implicitly a member of the default interface. + +When a class implements an interface, the class provides its own versions of all the **Public** procedures specified in the interface. In addition to providing a mapping between the interface prototypes and your procedures, the **Implements** statement causes the class to accept COM `QueryInterface` calls for the specified interface ID. + +When you implement an interface or class, you must include all the **Public** procedures involved. A missing member in an implementation of an interface or class causes an error. If you don't place code in one of the procedures in a class you are implementing, you can raise the appropriate error (`Const E_NOTIMPL = &H80004001`) so a user of the implementation understands that a member is not implemented. + +The **Implements** statement can't appear in a standard module — it is valid only in a [**Class**](Class) block. + +### twinBASIC enhancements + +twinBASIC extends classic VBA's **Implements** in several ways. See [Inheritance](../../Features/Language/Inheritance) for the full discussion; the headline differences: + +- **Inherited interfaces** — `Implements` works directly on a derived interface (e.g. `Implements IFoo2` where `Interface IFoo2 Extends IFoo`). The class need not name `IFoo` separately; `QueryInterface` for the base is satisfied automatically. Classic VBA does not support implementing derived interfaces. +- **Multiple-implementation form** — a single member can implement methods on several interfaces at once via `Implements ., ., …` after the procedure header. This is useful when several interfaces declare the same member and you want one body to satisfy all of them. +- **`As Any` parameters** — interfaces declared with `As Any` parameters can be implemented (substituting `As LongPtr` for `As Any` in the implementing class). Classic VBA rejects this. + +> [!NOTE] +> Use `Private` (or `Friend`) on the implementing procedures so that the interface methods don't also become part of the implementing class's *default* interface. The conventional naming pattern is `_`. + +### Example + +The following example shows how to use the **Implements** statement to make a set of declarations available to multiple classes. By sharing the declarations through the **Implements** statement, neither class has to make any declarations itself. The example also shows how use of an interface allows abstraction: a strongly-typed variable can be declared by using the interface type. It can then be assigned objects of different class types that implement the interface. + +The interface declarations are in a class called `PersonalData`: + +```tb +Public Name As String +Public Address As String +``` + +The code supporting the customer data is in a class module called `Customer`. Note that the `PersonalData` interface is implemented with members that are named with the interface name `PersonalData_` as a prefix. + +```tb +Implements PersonalData + +' For PersonalData implementation +Private m_name As String +Private m_address As String + +' Customer-specific +Public CustomerAgentId As Long + +' PersonalData implementation +Private Property Let PersonalData_Name(ByVal RHS As String) + m_name = RHS +End Property + +Private Property Get PersonalData_Name() As String + PersonalData_Name = m_name +End Property + +Private Property Let PersonalData_Address(ByVal RHS As String) + m_address = RHS +End Property + +Private Property Get PersonalData_Address() As String + PersonalData_Address = m_address +End Property + +Private Sub Class_Initialize() + m_name = "[customer name]" + m_address = "[customer address]" + CustomerAgentId = 0 +End Sub +``` + +A second class `Supplier` implements the same interface independently, with its own state and `Class_Initialize`. Code that needs name/address access can declare a variable as the interface type and accept either: + +```tb +Private m_pd As PersonalData + +Public Property Set PD(Data As PersonalData) + Set m_pd = Data +End Property +``` + +`m_pd` can only access the members of `PersonalData`. Customer-specific or Supplier-specific members are not visible through it — assigning an object to a variable declared by interface type provides polymorphic behavior. + +### See Also + +- [**Interface** statement](Interface) +- [**CoClass** statement](CoClass) +- [**Class** statement](Class) +- [Inheritance](../../Features/Language/Inheritance) +- [Interfaces and CoClasses](../../Features/Language/Interfaces-CoClasses) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Interface.md b/docs/Reference/Core/Interface.md new file mode 100644 index 0000000..a33608d --- /dev/null +++ b/docs/Reference/Core/Interface.md @@ -0,0 +1,94 @@ +--- +title: Interface +parent: Statements +permalink: /tB/Core/Interface +--- +# Interface +{: .no_toc } + +Defines a COM interface using twinBASIC syntax. An interface is a contract: a named set of method and property prototypes, with no implementation. Classes provide implementations of interfaces by using the [**Implements**](Implements) statement. + +> [!NOTE] +> The **Interface** block is a twinBASIC extension. In classic VBA there is no interface keyword — interfaces could only be defined indirectly via a referenced type library (IDL/C++) or by using a class with no implementation. + +Syntax: +> [ *attributes* ] +> [ **Public** \| **Private** ] **Interface** *name* [ **Extends** *baseinterface* [ **,** *baseinterface* ] ... ] +>      [ *attributes* ] +>      *member-prototype* +>      ... +> **End Interface** + +*attributes* +: *optional* Interface- and member-level attributes. See [Available attributes](#available-attributes) below. + +*name* +: The identifier naming the interface. By convention an interface name begins with an uppercase `I` (`IFoo`, `ICalculator`, ...). + +*baseinterface* +: *optional* One or more interfaces that *name* extends. An implementing class is required to provide bodies for the inherited methods as well; in twinBASIC, you can `Implements` *name* and rely on the inherited interfaces being satisfied automatically. + +*member-prototype* +: A header-only declaration. May be a [**Sub**](Sub), [**Function**](Function), [**Property Get**](Property), [**Property Let**](Property), or [**Property Set**](Property) signature, with arguments and return type. **Public**/**Private**/**Friend** modifiers are *not* allowed on members. There is no `End Sub` / `End Function` / `End Property` — the prototype ends at end of line. + +**Interface** blocks are valid only in `.twin` source files (not legacy `.bas` or `.cls` files), and must appear *before* the [**Class**](Class) or [**Module**](Module) statement in the file. Interfaces always have project-wide scope. + +### Available attributes + +Interface-level attributes: + +- `[InterfaceId("...")]` — fixes the IID for the interface (a string GUID). Set this on any public/exported interface so consumers in other projects bind to a stable identity. +- `[Description("text")]` — exposed as the `helpstring` in the type library. +- `[Hidden]` — hides the interface from IntelliSense and similar lists. +- `[Restricted]` — restricts the interface methods from being called in most contexts. +- `[OleAutomation(True/False)]` — controls whether the attribute is applied in the type library. `True` by default. +- `[ComImport]` — declares the interface as an import from an external COM library (e.g., the Windows shell). +- `[ComExtensible(True/False)]` — controls whether dynamically-added members can be invoked through `IDispatch`. `False` by default. + +Member-level attributes: + +- `[Description("text")]` +- `[PreserveSig]` — keeps the raw COM signature (returning `HRESULT`) instead of having the runtime translate negative results into errors. Use this when you need the literal return value, or when negative values mean *acceptable failure* (e.g. an enumerator running out of items). +- `[DispId(number)]` — fixes the dispatch ID associated with the member. + +### Example + +```tb +[InterfaceId("E7064791-0E4A-425B-8C8F-08802AAFEE61")] +[Description("Defines the IFoo interface")] +[OleAutomation(False)] +Interface IFoo Extends IUnknown + Sub MySub(Arg1 As Long) + Function Clone() As IFoo + [PreserveSig] + Function MyFunc([TypeHint(MyEnum)] Arg1 As Variant) As Boolean +End Interface +``` + +A class that implements `IFoo` provides bodies for every member: + +```tb +Class FooImpl + Implements IFoo + + Private Sub IFoo_MySub(Arg1 As Long) Implements IFoo.MySub + Debug.Print "MySub called with"; Arg1 + End Sub + + Private Function IFoo_Clone() As IFoo Implements IFoo.Clone + Set IFoo_Clone = New FooImpl + End Function + + Private Function IFoo_MyFunc(Arg1 As Variant) As Boolean Implements IFoo.MyFunc + IFoo_MyFunc = True + End Function +End Class +``` + +### See Also + +- [**Implements** statement](Implements) +- [**CoClass** statement](CoClass) +- [**Class** statement](Class) +- [Interfaces and CoClasses](../../Features/Language/Interfaces-CoClasses) +- [Inheritance](../../Features/Language/Inheritance) diff --git a/docs/Reference/Core/Module.md b/docs/Reference/Core/Module.md new file mode 100644 index 0000000..88e5c53 --- /dev/null +++ b/docs/Reference/Core/Module.md @@ -0,0 +1,75 @@ +--- +title: Module +parent: Statements +permalink: /tB/Core/Module +--- +# Module +{: .no_toc } + +Defines a module — a non-instantiable container for procedures, constants, types, enums, and module-level variables. A module's members are accessed through the module name (or, for **Public** members in a non-private module, directly). + +> [!NOTE] +> The explicit **Module** ... **End Module** block is a twinBASIC extension. Classic VBA distinguishes "standard modules" from "class modules" purely by file type (`.bas` vs. `.cls`); the source has no enclosing keyword. In `.twin` files twinBASIC requires (and supports) the explicit block, which lets you put a class and a module in the same file and apply attributes to the module as a whole. + +Syntax: +> [ *attributes* ] +> [ **Public** \| **Private** ] **Module** *name* +>      [ *modulemember* ] +>      ... +> **End Module** + +*attributes* +: *optional* One or more attributes applicable to a module. + +**Public** +: *optional* In an ActiveX project, marks the module as exported into the type library so that consumers in other projects can see its **Public** members. + +**Private** +: *optional* In an ActiveX project, withholds the module from the type library: its members remain usable within the project, but are not exported. Equivalent to placing [**Option Private Module**](Option) at the top of a classic standard module. + +*name* +: The identifier naming the module. + +*modulemember* +: *optional* Any of the following: + + - constants defined using [**Const**](Const) + - module-level variables defined using [**Public**](Public), [**Private**](Private), or [**Dim**](Dim) (modules don't take part in inheritance, so [**Protected**](Protected) is not allowed) + - procedures defined using [**Sub**](Sub), [**Function**](Function), or [**Property**](Property) + - user-defined types defined using [**Type**](Type) or [**Enum**](Enum) + - external procedure declarations using [**Declare**](Declare) + +Modules cannot be instantiated and have no `New` constructor. Their **Public** members behave as project-wide globals (subject to the **Public**/**Private** module modifier above). + +### Example + +```tb +Public Module StringHelpers + Public Function Reverse(ByVal s As String) As String + Dim i As Long, r As String + For i = Len(s) To 1 Step -1 + r = r & Mid$(s, i, 1) + Next i + Reverse = r + End Function + + Public Function StartsWith(ByVal s As String, ByVal prefix As String) As Boolean + StartsWith = (Left$(s, Len(prefix)) = prefix) + End Function +End Module +``` + +Callers reach the members either through the module name or directly: + +```tb +Debug.Print StringHelpers.Reverse("hello") ' "olleh" +Debug.Print StartsWith("hello world", "hi") ' False +``` + +### See Also + +- [**Class** statement](Class) +- [**Public** statement](Public) +- [**Private** statement](Private) +- [**Option** statement](Option) +- [Class and Module Enhancements](../../Features/Advanced/Classes-and-Modules) diff --git a/docs/Reference/Core/Protected.md b/docs/Reference/Core/Protected.md new file mode 100644 index 0000000..452db94 --- /dev/null +++ b/docs/Reference/Core/Protected.md @@ -0,0 +1,80 @@ +--- +title: Protected +parent: Statements +permalink: /tB/Core/Protected +--- +# Protected +{: .no_toc } + +Declares a class member (variable, procedure, or property) that is accessible from inside the declaring class and from classes that derive from it via [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop), but not from outside callers. + +> [!NOTE] +> The **Protected** access modifier is a twinBASIC extension over classic VBA, which has only **Public**, **Private**, and **Friend**. **Protected** is meaningful only inside an [**Inherits**](../../Features/Language/Inheritance#inherits-for-complete-oop) hierarchy; in a class without inheritance, it behaves like [**Private**](Private). + +Syntax: + +- > **Protected** *varname* [ **(** [ *subscripts* ] **)** ] [ **As** [ **New** ] *type* ] [ **,** *varname* ... ] +- > **Protected** [ **Overridable** ] **Sub** \| **Function** \| **Property Get** \| **Property Let** \| **Property Set** *name* ... + +In a variable declaration, **Protected** has the same form as [**Private**](Private)/[**Public**](Public): a comma-separated list of names with optional `WithEvents`/`New` and `As`-clause. **Protected** is valid only at class scope; it cannot be used inside a procedure (use [**Dim**](Dim) or [**Static**](Static)), and it cannot be used in a [**Module**](Module) (modules have no notion of derived types). + +In a procedure declaration, **Protected** replaces the **Public**/**Private**/**Friend** modifier. Combined with the **Overridable** keyword, it declares an inheritance hook — a method or property that derived classes are allowed to override. + +### Visibility summary + +| Modifier | Same class | Derived class (via **Inherits**) | Other code | +|:---|:---:|:---:|:---:| +| **Public** | yes | yes | yes | +| **Friend** | yes | yes | within the project only | +| **Protected** | yes | yes | no | +| **Private** | yes | no | no | + +### Example + +The following pattern uses **Protected** state plus an **Overridable** **Protected** method as an inheritance hook. The base `Animal` class exposes a public `Speak` that delegates to `GetSound`, which derived classes override: + +```tb +Private Class Animal + Protected _name As String + Protected _dob As Date ' date of birth + + Public Sub New(name As String, dob As Date) + _name = name + _dob = dob + End Sub + + Public Property Get Name() As String + Name = _name + End Property + + Public Sub Speak() + Debug.Print _name & " says: " & GetSound() + End Sub + + ' Overridable hook for derived classes. + Protected Overridable Function GetSound() As String + GetSound = "" + End Function +End Class + +Private Class Dog + Inherits Animal + + Public Sub New(name As String, dob As Date) + Animal.New(name, dob) ' explicit base constructor call + End Sub + + Protected Function GetSound() As String Overrides Animal.GetSound + GetSound = "woof" + End Function +End Class +``` + +`_name` and `_dob` are visible inside `Dog` (because `Dog` inherits from `Animal`), but not visible to any code that holds an `Animal` or `Dog` reference from outside the hierarchy. + +### See Also + +- [**Public** statement](Public) +- [**Private** statement](Private) +- [**Class** statement](Class) +- [Inheritance](../../Features/Language/Inheritance) diff --git a/docs/Reference/Core/RaiseEvent.md b/docs/Reference/Core/RaiseEvent.md new file mode 100644 index 0000000..7cdd7b7 --- /dev/null +++ b/docs/Reference/Core/RaiseEvent.md @@ -0,0 +1,98 @@ +--- +title: RaiseEvent +parent: Statements +permalink: /tB/Core/RaiseEvent +--- +# RaiseEvent +{: .no_toc } + +Fires an event declared at the module level within a class, form, or document. + +Syntax: +> **RaiseEvent** *eventname* [ **(** *argumentlist* **)** ] + +*eventname* +: Name of the event to fire. Must be the name of an [**Event**](Event) declared in the same module. + +*argumentlist* +: *optional* Comma-delimited list of variables, arrays, or expressions to pass as event arguments. The *argumentlist* must be enclosed by parentheses. If the event has no arguments, the parentheses must be omitted. + +If the event has not been declared within the module in which it is raised, an error occurs. If the event has no arguments, including empty parentheses in the **RaiseEvent** invocation causes an error. + +You can't use **RaiseEvent** to fire events that are not explicitly declared in the module. For example, if a form has a built-in **Click** event, you can't fire its **Click** event by using **RaiseEvent**. If you declare a **Click** event in the form module, it shadows the form's own **Click** event. + +Event firing is done in the order that connections were established. Because events can have **ByRef** parameters, a process that connects late may receive parameters that have been changed by an earlier event handler. + +### Example + +The following fragment declares an event at the module level of a class module and raises it from a procedure: + +```tb +' In a class module: +Public Event LogonCompleted(UserName As String) + +Sub Demo() + RaiseEvent LogonCompleted("AntoineJan") +End Sub +``` + +A more complete example showing event source/sink wiring. The class that raises an event is the event source, and the classes that handle the event are the sinks. An event source can have multiple sinks; when the source raises the event, every sink receives it. + +The source class declares two events and raises them from a worker procedure: + +```tb +Class TimerState + Public Event UpdateElapsedTime(ByVal elapsedTime As Double) + Public Event DisplayFinalTime() + Private Const delta As Double = 0.01 + + Public Sub TimerTask(ByVal duration As Double) + Dim startTime As Double, timeElapsedSoFar As Double + startTime = Timer + timeElapsedSoFar = startTime + + Do While Timer < startTime + duration + If Timer - timeElapsedSoFar >= delta Then + timeElapsedSoFar = timeElapsedSoFar + delta + RaiseEvent UpdateElapsedTime(Timer - startTime) + DoEvents + End If + Loop + + RaiseEvent DisplayFinalTime + End Sub +End Class +``` + +A sink subscribes by using a `WithEvents` field and supplying handler procedures named `_`: + +```tb +Class Form1 + Private WithEvents ts As TimerState + + Private Sub UserForm_Initialize() + Set ts = New TimerState + End Sub + + Private Sub Command1_Click() + ts.TimerTask 9.58 + End Sub + + Private Sub ts_UpdateElapsedTime(ByVal elapsedTime As Double) + Text2.Text = Format(elapsedTime, "0.00") + End Sub + + Private Sub ts_DisplayFinalTime() + Text1.Text = "Until Now" + Text2.Text = "9.58" + End Sub +End Class +``` + +### See Also + +- [**Event** statement](Event) +- [**Class** statement](Class) +- [**Implements** statement](Implements) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index ccdc1b9..3510fc6 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -3,15 +3,9 @@ title: TODO List for /tb/Core nav_exclude: true redirect_from: - /tB/Core/Topic-Preprocessor - - /tB/Core/CoClass - /tB/Core/Deftype - - /tB/Core/Implements - - /tB/Core/Interface - /tB/Core/Load - - /tB/Core/Module - - /tB/Core/Protected - - /tB/Core/RaiseEvent - /tB/Core/SavePicture - /tB/Core/Unload --- diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 625b68f..7445cf7 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -19,6 +19,8 @@ These statements are built into the language itself. They are understood by the * [Class](../tB/Core/Class) -- define a class +* [CoClass](../tB/Core/CoClass) -- (twinBASIC) defines a creatable COM class as the contract for one or more **Interface** blocks + * [Close](../tB/Core/Close) -- concludes input/output (I/O) to a file opened using the **Open** statement * [Const](../tB/Core/Const) -- declares constants for use in place of literal values @@ -59,7 +61,9 @@ These statements are built into the language itself. They are understood by the * [Input #](../tB/Core/Input) -- reads data from an open sequential file and assigns it to variables -* [Implements](../tB/Core/Implements) +* [Implements](../tB/Core/Implements) -- specifies an interface or class that will be implemented in the class in which it appears + +* [Interface](../tB/Core/Interface) -- (twinBASIC) defines a COM interface using twinBASIC syntax * [Is](../tB/Core/Is) -- compares two object references for identity @@ -79,7 +83,7 @@ These statements are built into the language itself. They are understood by the * [MidB =](../tB/Core/MidB-equals) -- byte-positioned form of **Mid =** -* [Module](../tB/Core/Module) +* [Module](../tB/Core/Module) -- defines a module: a non-instantiable container for procedures, constants, types, and module-level variables * [Name](../tB/Core/Name) -- renames a disk file, directory, or folder @@ -101,11 +105,13 @@ These statements are built into the language itself. They are understood by the * [Property](../tB/Core/Property) -- declares the **Get**, **Let**, or **Set** procedures that form the body of a property +* [Protected](../tB/Core/Protected) -- (twinBASIC) declares a class member accessible within the class and its derived classes + * [Public](../tB/Core/Public) -- declares module-level variables accessible to all procedures in all modules * [Put](../tB/Core/Put) -- writes data from a variable to a disk file -* [RaiseEvent](../tB/Core/RaiseEvent) +* [RaiseEvent](../tB/Core/RaiseEvent) -- fires an event declared at the module level within a class, form, or document * [Randomize](../tB/Modules/Math/Randomize) -- initializes the random-number generator From 581c4a0df842de1015e3bddd0cdc4701009feff2 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 19:00:02 +0200 Subject: [PATCH 08/10] Document Unload, SavePicture, Load, Deftype, and the preprocessor. --- docs/Reference/Core/Deftype.md | 80 +++++++++++++++++++++ docs/Reference/Core/Load.md | 47 ++++++++++++ docs/Reference/Core/SavePicture.md | 40 +++++++++++ docs/Reference/Core/Topic-Preprocessor.md | 87 +++++++++++++++++++++++ docs/Reference/Core/Unload.md | 45 ++++++++++++ docs/Reference/Core/todo.md | 11 +-- docs/Reference/Statements.md | 10 ++- 7 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 docs/Reference/Core/Deftype.md create mode 100644 docs/Reference/Core/Load.md create mode 100644 docs/Reference/Core/SavePicture.md create mode 100644 docs/Reference/Core/Topic-Preprocessor.md create mode 100644 docs/Reference/Core/Unload.md diff --git a/docs/Reference/Core/Deftype.md b/docs/Reference/Core/Deftype.md new file mode 100644 index 0000000..6439cbf --- /dev/null +++ b/docs/Reference/Core/Deftype.md @@ -0,0 +1,80 @@ +--- +title: Deftype +parent: Statements +permalink: /tB/Core/Deftype +--- +# DefBool, DefByte, DefInt, DefLng, DefLngLng, DefLngPtr, DefCur, DefSng, DefDbl, DefDec, DefDate, DefStr, DefObj, DefVar +{: .no_toc } + +Used at the module level to set the default data type for variables, arguments passed to procedures, and the return type for **Function** and **Property Get** procedures whose names start with the specified characters. + +> [!WARNING] +> The **Def**_type_ family of statements is deprecated. They are supported for compatibility with legacy code, but new code should declare every variable, argument, and return type explicitly with **As** *type*. Combined with [**Option Explicit**](Option#Explicit), explicit declarations make code far easier to read and maintain. + +Syntax: + +- > **DefBool** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefByte** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefInt** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefLng** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefLngLng** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefLngPtr** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefCur** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefSng** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefDbl** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefDec** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefDate** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefStr** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefObj** *letterrange* [ **,** *letterrange* ] **. . .** +- > **DefVar** *letterrange* [ **,** *letterrange* ] **. . .** + +*letterrange* +: A single letter, or a hyphenated range *letter1*-*letter2*. The letters specify the leading character of names that adopt the default type. Case is not significant. + +The statement name determines the data type: + +| Statement | Data type | +|:----------|:----------| +| **DefBool** | **Boolean** | +| **DefByte** | **Byte** | +| **DefInt** | **Integer** | +| **DefLng** | **Long** | +| **DefLngLng** | **LongLong** | +| **DefLngPtr** | **LongPtr** | +| **DefCur** | **Currency** | +| **DefSng** | **Single** | +| **DefDbl** | **Double** | +| **DefDec** | **Decimal** (not currently supported) | +| **DefDate** | **Date** | +| **DefStr** | **String** | +| **DefObj** | **Object** | +| **DefVar** | **Variant** | + +For example, in the following fragment, `Message` is a **String** variable: + +```tb +DefStr A-Q +. . . +Message = "Out of stack space." +``` + +A **Def**_type_ statement affects only the module where it is used. The default data type for variables, arguments, and return types of items not declared explicitly and not covered by a **Def**_type_ statement is **Variant**. + +When you specify a letter range, it usually defines the data type for variables that begin with letters in the first 128 characters of the character set. However, when you specify the range A-Z, you set the default to the specified data type for *all* names, including those starting with characters from the extended part of the character set (128-255). + +After the range A-Z has been specified, you can't further redefine subranges by using **Def**_type_ statements. Once a range has been specified, including a previously defined letter in another **Def**_type_ statement is an error. You can, however, explicitly specify the data type of any variable — defined or not — by using a [**Dim**](Dim) statement with an **As** *type* clause: + +```tb +DefInt A-Z +Dim TaxRate As Double ' explicit declaration overrides the default +``` + +**Def**_type_ statements don't affect elements of user-defined types — those must be explicitly declared. + +### See Also + +- [**Dim** statement](Dim) +- [**Option** statement](Option) (for **Option Explicit**) +- [**Type** statement](Type) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Load.md b/docs/Reference/Core/Load.md new file mode 100644 index 0000000..7891f68 --- /dev/null +++ b/docs/Reference/Core/Load.md @@ -0,0 +1,47 @@ +--- +title: Load +parent: Statements +permalink: /tB/Core/Load +--- +# Load +{: .no_toc } + +Loads an object — typically a form — into memory but does not show it. + +Syntax: +> **Load** *object* + +*object* +: An object expression that evaluates to a loadable object (commonly a form or a control array element). + +When an object is loaded, it is placed in memory but is not visible. Use the **Show** method to make it visible. Until an object is visible, the user can't interact with it; the object can be manipulated programmatically inside its **Initialize** event handler. + +Use [**Unload**](Unload) to remove the object from memory once you are done with it. + +### Example + +In the following example, `UserForm2` is loaded during `UserForm1`'s **Initialize** event. Subsequent clicking on `UserForm2` reveals `UserForm1`. + +```tb +' This is the Initialize event procedure for UserForm1. +Private Sub UserForm_Initialize() + Load UserForm2 + UserForm2.Show +End Sub + +' This is the Click event of UserForm2. +Private Sub UserForm_Click() + UserForm2.Hide +End Sub + +' This is the Click event for UserForm1. +Private Sub UserForm_Click() + UserForm2.Show +End Sub +``` + +### See Also + +- [**Unload** statement](Unload) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/SavePicture.md b/docs/Reference/Core/SavePicture.md new file mode 100644 index 0000000..2d8d4e9 --- /dev/null +++ b/docs/Reference/Core/SavePicture.md @@ -0,0 +1,40 @@ +--- +title: SavePicture +parent: Statements +permalink: /tB/Core/SavePicture +--- +# SavePicture +{: .no_toc } + +Saves a graphic from a `Picture` or `Image` to a file. + +Syntax: +> **SavePicture** *picture* **,** *stringexpression* + +*picture* +: A `Picture` (e.g. `stdole.StdPicture`) or `Image` from which the graphic will be saved. Typically the `Picture` property of a control such as a **PictureBox** or **Image**, or the result of a **LoadPicture** call. + +*stringexpression* +: String expression specifying the path of the file to write. + +If the *picture* originally came from a file (loaded with **LoadPicture**), the file is written in the same format as the original. If *picture* was created or modified at runtime (for example, by drawing into a **PictureBox**), the file is saved as a bitmap (`.bmp`). + +> [!NOTE] +> **SavePicture** is a legacy VB6/VBx statement preserved in twinBASIC for source compatibility. New code that needs more control over output format (PNG, JPEG, format options) should use the platform's imaging APIs directly or convert via [**PictureToByteArray**](../Modules/HiddenModule/PictureToByteArray) and write the bytes through standard file I/O. + +### Example + +```tb +' Save the graphic currently displayed in Picture1 to disk. +SavePicture Picture1.Picture, "C:\Temp\Snapshot.bmp" + +' Round-trip an image through a Picture object. +Dim P As StdPicture +Set P = LoadPicture("C:\Temp\Original.png") +SavePicture P, "C:\Temp\Copy.bmp" ' Always saved as BMP if not loaded from file. +``` + +### See Also + +- [**PictureToByteArray** function](../Modules/HiddenModule/PictureToByteArray) +- [**CreateStdPictureFromHandle** function](../Modules/HiddenModule/CreateStdPictureFromHandle) diff --git a/docs/Reference/Core/Topic-Preprocessor.md b/docs/Reference/Core/Topic-Preprocessor.md new file mode 100644 index 0000000..f923b70 --- /dev/null +++ b/docs/Reference/Core/Topic-Preprocessor.md @@ -0,0 +1,87 @@ +--- +title: '#If, #Const' +parent: Statements +permalink: /tB/Core/Topic-Preprocessor +--- +# #If...Then...#Else, #Const directives +{: .no_toc } + +Compiler directives that conditionally include or exclude blocks of code at *compile time*, and define the constants those conditions are tested against. Unlike runtime [**If...Then...Else**](If-Then-Else) and [**Const**](Const), the directives operate during compilation: code in an inactive branch is omitted entirely from the compiled output and contributes no size or runtime cost. + +## #If...Then...#Else directive + +Syntax: + +> **#If** *expression* **Then** +>     *statements* +> [ **#ElseIf** *expression-n* **Then** +>     [ *elseifstatements* ] ] ... +> [ **#Else** +>     [ *elsestatements* ] ] +> **#End If** + +*expression*, *expression-n* +: An expression composed exclusively of conditional compiler constants, literals, and operators, evaluating to **True** or **False**. + +*statements*, *elseifstatements*, *elsestatements* +: Source lines or further compiler directives included when the corresponding *expression* is **True**. + +The directive's behaviour mirrors the runtime [**If...Then...Else**](If-Then-Else) statement, with the following differences: + +- There is no single-line form — `#If`, `#ElseIf`, `#Else`, and `#End If` must each appear on their own line. +- All *expressions* are evaluated regardless of which branch is selected, so every constant they reference must be defined. Undefined conditional compiler constants evaluate as **Empty** (i.e. zero), which is treated as **False**. +- Code in unselected branches is *removed* from the compilation rather than skipped at runtime. In twinBASIC, inactive code is not even checked for errors. The IDE greys out inactive blocks based on the current build configuration. + +> [!NOTE] +> The [**Option Compare**](Option) statement does not affect expressions in `#If`/`#ElseIf`. They are always evaluated as if **Option Compare Text** were in effect. + +## #Const directive + +Syntax: + +> **#Const** *constname* **=** *expression* + +*constname* +: Name of the conditional compiler constant; follows standard variable naming conventions. + +*expression* +: A literal, another conditional compiler constant, or any combination using arithmetic or logical operators (except [**Is**](Is)). Standard runtime constants (declared with [**Const**](Const)) are *not* allowed here. + +Conditional compiler constants declared with `#Const` are private to the module in which they appear. Project-wide conditional constants must be defined in the project's compilation settings — `#Const` cannot create them. + +Conditional compiler constants are always evaluated at the module level regardless of where they appear in code; they can only be used in `#If`/`#ElseIf` expressions. + +## Predefined compiler constants + +twinBASIC provides a set of built-in compiler constants — `Win64`, `Win32`, `TWINBASIC`, `TWINBASIC_BUILD`, `VBA7`, etc. See the dedicated [Compiler Constants](../../Reference/Compiler-Constants) page for the full list and what each one means. + +### Example + +This example uses the `Win64` predefined constant to select platform-specific imports, and a project-defined `DEBUG_BUILD` constant to enable extra logging only in debug builds. + +```tb +#Const DEBUG_BUILD = 1 + +#If Win64 Then + ' 64-bit-only declarations. + Import Library "/Miscellaneous/sqlite3_64.obj" As SQLITE3 +#Else + ' 32-bit fallback. + Import Library "/Miscellaneous/sqlite3_32.obj" As SQLITE3 +#End If + +Public Sub DoWork() +#If DEBUG_BUILD Then + Debug.Print "Entering DoWork at "; Now +#End If + ' ... +End Sub +``` + +### See Also + +- [**If...Then...Else** statement](If-Then-Else) — the runtime counterpart. +- [**Const** statement](Const) — the runtime counterpart of **#Const**. +- [Compiler Constants](../../Reference/Compiler-Constants) — the full list of built-in conditional constants. + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/Unload.md b/docs/Reference/Core/Unload.md new file mode 100644 index 0000000..c37115e --- /dev/null +++ b/docs/Reference/Core/Unload.md @@ -0,0 +1,45 @@ +--- +title: Unload +parent: Statements +permalink: /tB/Core/Unload +--- +# Unload +{: .no_toc } + +Removes an object — typically a form — from memory. + +Syntax: +> **Unload** *object* + +*object* +: An object expression that evaluates to an unloadable object (commonly a form or a control array element). + +When an object is unloaded, it is removed from memory and all memory associated with the object is reclaimed. Until it is placed in memory again with the [**Load**](Load) statement, the user can't interact with the object, and the object can't be manipulated programmatically. + +### Example + +The following example assumes two `UserForm`s in a program. In `UserForm1`'s **Initialize** event, `UserForm2` is loaded and shown. When the user clicks `UserForm2`, it is unloaded and `UserForm1` appears. When `UserForm1` is clicked, it is unloaded in turn. + +```tb +' This is the Initialize event procedure for UserForm1. +Private Sub UserForm_Initialize() + Load UserForm2 + UserForm2.Show +End Sub + +' This is the Click event for UserForm2. +Private Sub UserForm_Click() + Unload UserForm2 +End Sub + +' This is the Click event for UserForm1. +Private Sub UserForm_Click() + Unload UserForm1 +End Sub +``` + +### See Also + +- [**Load** statement](Load) + +{% include VBA-Attribution.md %} diff --git a/docs/Reference/Core/todo.md b/docs/Reference/Core/todo.md index 3510fc6..a1e5c9c 100644 --- a/docs/Reference/Core/todo.md +++ b/docs/Reference/Core/todo.md @@ -1,15 +1,8 @@ --- title: TODO List for /tb/Core nav_exclude: true -redirect_from: - - /tB/Core/Topic-Preprocessor - - /tB/Core/Deftype - - - /tB/Core/Load - - /tB/Core/SavePicture - - /tB/Core/Unload --- -> [!WARNING] +> [!NOTE] > -> Pardon, we have not documented this statement or procedure yet. \ No newline at end of file +> All entries in `/tB/Core/` are now documented; this stub is retained as a place to add new entries. \ No newline at end of file diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index 7445cf7..f611585 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -75,6 +75,8 @@ These statements are built into the language itself. They are understood by the * [Line Input #](../tB/Core/Line-Input) -- reads a single line from an open sequential file into a string variable +* [Load](../tB/Core/Load) -- loads an object (typically a form) into memory without showing it + * [Lock](../tB/Core/Lock), [Unlock](../tB/Core/Unlock) -- control access by other processes to all or part of an open file * [LSet](../tB/Core/LSet) -- left-aligns a string within a string variable, or copies one user-defined-type variable into another @@ -123,6 +125,8 @@ These statements are built into the language itself. They are understood by the * [RSet](../tB/Core/RSet) -- right-aligns a string within a string variable +* [SavePicture](../tB/Core/SavePicture) -- saves a graphic from a **Picture** or **Image** to a file + * [Seek](../tB/Core/Seek) * [Select Case](../tB/Core/Select-Case) -- executes one of several groups of statements, depending on the value of an expression @@ -137,18 +141,20 @@ These statements are built into the language itself. They are understood by the * [Type](../tB/Core/Type) -- defines a user-defined data type containing one or more elements +* [Unload](../tB/Core/Unload) -- removes an object (typically a form) from memory + * [While ... Wend](../tB/Core/While-Wend) -- executes a series of statements as long as a given condition is **True** * [With](../tB/Core/With) -- executes a series of statements on a single object or a user-defined type * [Write #](../tB/Core/Write) -- writes raw, delimited data to a sequential file (paired with [**Input #**](../tB/Core/Input)) -* [#If ... Then ... Else](../tB/Core/Topic-Preprocessor) +* [#If ... Then ... Else](../tB/Core/Topic-Preprocessor), [#Const](../tB/Core/Topic-Preprocessor) -- compiler directives that conditionally include code blocks at compile time --- ## Deprecated -* [DefBool through DefVar](../tB/Core/Deftype) +* [DefBool through DefVar](../tB/Core/Deftype) -- set the default data type for variables whose names start with given letters; superseded by explicit **As** *type* declarations * [Error](../tB/Core/Error) \ No newline at end of file From 845be65e785de4999ee8452c2d862638fbfcc1a0 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 19:03:09 +0200 Subject: [PATCH 09/10] Add a blurb for the Seek statement. Remove duplicate Error statement. --- docs/Reference/Statements.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index f611585..96517fa 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -10,9 +10,6 @@ permalink: /Reference/Statements These statements are built into the language itself. They are understood by the compiler, and are not explicitly declared nor defined in the visible runtime library. -> [!WARNING] -> Work in Progress - ## Alphabetical List * [Call](../tB/Core/Call) -- transfer control to a procedure @@ -127,7 +124,7 @@ These statements are built into the language itself. They are understood by the * [SavePicture](../tB/Core/SavePicture) -- saves a graphic from a **Picture** or **Image** to a file -* [Seek](../tB/Core/Seek) +* [Seek](../tB/Core/Seek) -- sets the read/write position within a file opened by using the **Open** statement * [Select Case](../tB/Core/Select-Case) -- executes one of several groups of statements, depending on the value of an expression @@ -155,6 +152,4 @@ These statements are built into the language itself. They are understood by the ## Deprecated -* [DefBool through DefVar](../tB/Core/Deftype) -- set the default data type for variables whose names start with given letters; superseded by explicit **As** *type* declarations - -* [Error](../tB/Core/Error) \ No newline at end of file +* [DefBool through DefVar](../tB/Core/Deftype) -- set the default data type for variables whose names start with given letters; superseded by explicit **As** *type* declarations \ No newline at end of file From 00d44fc1b33241c8040fe24e2244ec77c1e300e7 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 9 May 2026 19:06:26 +0200 Subject: [PATCH 10/10] Categorize remaining statements. --- docs/Reference/Categories.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/Reference/Categories.md b/docs/Reference/Categories.md index 8987984..bcebb18 100644 --- a/docs/Reference/Categories.md +++ b/docs/Reference/Categories.md @@ -16,13 +16,16 @@ This chapter lists the global statements and procedures that form the core of th * [Option](../tB/Core/Option) - configure a compiler option * [#If ... Then ... Else](../tB/Core/Topic-Preprocessor) - enable or disable compilation of enclosed code +* [#Const](../tB/Core/Topic-Preprocessor) - define a module-private conditional compiler constant ## Declarations and Definitions * [Class](../tB/Core/Class), [Module](../tB/Core/Module) - define a class or module +* [Interface](../tB/Core/Interface), [CoClass](../tB/Core/CoClass) - (twinBASIC) define a COM interface or coclass using twinBASIC syntax * [Sub](../tB/Core/Sub) - define a procedure * [Function](../tB/Core/Function) - define a function * [Property](../tB/Core/Property) - define a property +* [ParamArray](../tB/Core/ParamArray) - declare a procedure's final parameter as a variadic argument list * [Enum](../tB/Core/Enum) - define an enumeration type with associated constants * [Type](../tB/Core/Type) - declare a user-defined data type (UDT)/a structure * [Declare](../tB/Core/Declare) - declare an external/library procedure or function @@ -39,6 +42,7 @@ Statements: * [If ... Then ... Else](../tB/Core/If-Then-Else) - execute code conditionally * [Continue](../tB/Core/Continue) - skip to the next iteration of the loop * [Exit](../tB/Core/Exit) - exit a loop, procedure, function or property +* [Return](../tB/Core/Return) - return from a **GoSub** subroutine, or (twinBASIC) return a value and exit from a **Function** or **Property Get** * [Select Case](../tB/Core/Select-Case) - execute a code block selected by an expression * [With](../tB/Core/With) - bring a variable or expression into scope * [Goto](../tB/Core/GoTo), [GoSub ... Return](../tB/Core/GoSub-Return) - transfer execution to another location @@ -81,6 +85,7 @@ Statements: * [Const](../tB/Core/Const) - declare a constant * [Public](../tB/Core/Public) - declare a public variable in a class or module * [Private](../tB/Core/Private) - declare a private variable in a class or module +* [Protected](../tB/Core/Protected) - (twinBASIC) declare a class member accessible within the class and its derived classes * [Static](../tB/Core/Static) - declare a a variable of static duration ## Variable Assignment and Modification @@ -89,9 +94,15 @@ Statements: - [Let](../tB/Core/Let) - sets the value of a variable - [Set](../tB/Core/Set) - changes the object referred by the variable +- [New](../tB/Core/New) - create a new instance of a class - [LSet](../tB/Core/LSet) - assigns a user-defined type, or left-aligns a string - [RSet](../tB/Core/RSet) - right-aligns a string +Operators: + +- [Is](../tB/Core/Is) - compares two object references for identity +- [IsNot](../tB/Core/IsNot) - (twinBASIC) the logical inverse of **Is** + ## Arrays Statements: