Skip to content

Commit 8acd595

Browse files
committed
[[ LocalVariableInit ]] Make compiler responsible for initializing locals
This patch moves the 'defaulting' action on local variables from an implicit behavior to an explicit one by adding a 'reset' bytecode op and adds defaulting of out parameters. The compiler generates 'reset' opcodes for all variable definitions at the point of definition.
1 parent bcd919b commit 8acd595

File tree

12 files changed

+192
-94
lines changed

12 files changed

+192
-94
lines changed

docs/guides/LiveCode Builder Language Reference.md

Lines changed: 75 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,37 @@ group: reference
55
# LiveCode Builder Language Reference
66

77
## Introduction
8-
LiveCode Builder is a variant of the current LiveCode scripting language
9-
(LiveCode Script) which has been designed for 'systems' building. It is
10-
statically compiled with optional static typing and direct foreign code
8+
LiveCode Builder is a variant of the current LiveCode scripting language
9+
(LiveCode Script) which has been designed for 'systems' building. It is
10+
statically compiled with optional static typing and direct foreign code
1111
interconnect (allowing easy access to APIs written in other languages).
1212

13-
Unlike most languages, LiveCode Builder has been designed around the
14-
idea of extensible syntax. Indeed, the core language is very small -
15-
comprising declarations and control structures - with the majority of
13+
Unlike most languages, LiveCode Builder has been designed around the
14+
idea of extensible syntax. Indeed, the core language is very small -
15+
comprising declarations and control structures - with the majority of
1616
the language syntax and functionality being defined in modules.
1717

18-
> **Note:** It is an eventual aim that control structures will also be
18+
> **Note:** It is an eventual aim that control structures will also be
1919
> extensible, however this is not the case in the current incarnation).
2020
21-
The syntax will be familiar to anyone familiar with LiveCode Script,
22-
however LiveCode Builder is a great deal more strict - the reason being
23-
it is intended that it will eventually be compilable to machine code
24-
with the performance and efficiency you'd expect from any 'traditional'
25-
programming language. Indeed, over time we hope to move the majority of
26-
implementation of the whole LiveCode system over to being written in
21+
The syntax will be familiar to anyone familiar with LiveCode Script,
22+
however LiveCode Builder is a great deal more strict - the reason being
23+
it is intended that it will eventually be compilable to machine code
24+
with the performance and efficiency you'd expect from any 'traditional'
25+
programming language. Indeed, over time we hope to move the majority of
26+
implementation of the whole LiveCode system over to being written in
2727
LiveCode Builder.
2828

29-
> **Note:** One of the principal differences is that type conversion is
30-
> strict - there is no automatic conversion between different types such
31-
> as between number and string. Such conversion must be explicitly
32-
> specified using syntax (currently this is using things like
29+
> **Note:** One of the principal differences is that type conversion is
30+
> strict - there is no automatic conversion between different types such
31+
> as between number and string. Such conversion must be explicitly
32+
> specified using syntax (currently this is using things like
3333
> *... parsed as number* and *... formatted as string*.
3434
3535
## Tokens
3636

37-
The structure of tokens is similar to LiveCode Script, but again a
38-
little stricter. The regular expressions describing the tokens are as
37+
The structure of tokens is similar to LiveCode Script, but again a
38+
little stricter. The regular expressions describing the tokens are as
3939
follows:
4040

4141
- **Identifier**: [A-Za-z_][A-Za-z0-9_.]*
@@ -53,27 +53,27 @@ Strings use backslash ('\') as an escape - the following are understood:
5353
- **\u{X...X}: character with unicode codepoint U+X...X - any number of nibbles may be specified, but any values greater than 0x10FFFF will be replaced by U+FFFD.
5454
- **\\**: backslash '\'
5555

56-
> **Note:** The presence of '.' in identifiers are used as a namespace
56+
> **Note:** The presence of '.' in identifiers are used as a namespace
5757
> scope delimiter.
5858
5959
> **Note:** Source files are presumed to be in UTF-8 encoding.
6060
6161
### Case-Sensitivity
6262

63-
At the moment, due to the nature of the parser being used, keywords are
64-
all case-sensitive and reserved. The result of this is that, using all
65-
lower-case identifiers for names of definitions should be avoided.
66-
However, identifiers *are* case-insensitive - so a variable with name
63+
At the moment, due to the nature of the parser being used, keywords are
64+
all case-sensitive and reserved. The result of this is that, using all
65+
lower-case identifiers for names of definitions should be avoided.
66+
However, identifiers *are* case-insensitive - so a variable with name
6767
pFoo can also be referenced as PFOO, PfOO, pfoO etc.
6868

69-
> **Aside:** The current parser and syntax rules for LiveCode Builder
70-
> are constructed at build-time of the LiveCode Builder compiler and
71-
> uses *bison* (a standard parser generator tool) to build the parser.
72-
> Unfortunately, this means that any keywords have to be reserved as the
73-
> parser cannot distinguish the use of an identifier in context (whether
69+
> **Aside:** The current parser and syntax rules for LiveCode Builder
70+
> are constructed at build-time of the LiveCode Builder compiler and
71+
> uses *bison* (a standard parser generator tool) to build the parser.
72+
> Unfortunately, this means that any keywords have to be reserved as the
73+
> parser cannot distinguish the use of an identifier in context (whether
7474
> it is a keyword at a particular point, or a name of a definition).
7575
76-
It is highly recommended that the following naming conventions be used
76+
It is highly recommended that the following naming conventions be used
7777
for identifiers:
7878

7979
- **tVar** - for local variables
@@ -85,20 +85,20 @@ for identifiers:
8585
- **kConstant** - for constants
8686
- Use identifiers starting with an uppercase letter for handler and type names.
8787

88-
By following this convention, there will not be any ambiguity between
88+
By following this convention, there will not be any ambiguity between
8989
identifiers and keywords. (All keywords are all lower-case).
9090

91-
> **Note:** When we have a better parsing technology we will be
92-
> evaluating whether to make keywords case-insensitive as well. At the
93-
> very least, at that point, we expect to be able to make all keywords
91+
> **Note:** When we have a better parsing technology we will be
92+
> evaluating whether to make keywords case-insensitive as well. At the
93+
> very least, at that point, we expect to be able to make all keywords
9494
> unreserved.
9595
9696
## Typing
9797

98-
LiveCode Builder is a typed language, although typing is completely
99-
optional in most places (the only exception being in foreign handler
100-
declarations). If a type annotation is not specified it is simply taken
101-
to be the most general type *optional any* (meaning any value, including
98+
LiveCode Builder is a typed language, although typing is completely
99+
optional in most places (the only exception being in foreign handler
100+
declarations). If a type annotation is not specified it is simply taken
101+
to be the most general type *optional any* (meaning any value, including
102102
nothing).
103103

104104
The range of core types is relatively small, comprising the following:
@@ -210,7 +210,7 @@ definitions can only be used within the module.
210210

211211
> **Note**: Properties and events are, by their nature, always public as
212212
> they define things which only make sense to access from outside.
213-
>
213+
>
214214
> **Note**: When writing a library module, all public handlers are added
215215
> to bottom of the message path in LiveCode Script.
216216
@@ -273,7 +273,7 @@ The remaining types are as follows:
273273
- **Pointer**: a low-level pointer (this is used with foreign code interconnect and shouldn't be generally used).
274274

275275
> **Note:** *Integer* and *Real* are currently the same as *Number*.
276-
276+
277277
> **Note:** In a subsequent update you will be able to specify lists and
278278
> arrays of fixed types. For example, *List of String*.
279279
@@ -313,6 +313,24 @@ The type specification for the variable is optional, if it is not
313313
specified the type of the variable is *optional any* meaning that it can
314314
hold any value, including being nothing.
315315

316+
Variables whose type has a default value are initialized to that value at the
317+
point of definition. The default values for the standard types are:
318+
319+
- **optional**: nothing
320+
- **Boolean**: false
321+
- **Integer**: 0
322+
- **Real**: 0.0
323+
- **Number**: 0
324+
- **String**: the empty string
325+
- **Data**: the empty data
326+
- **Array**: the empty array
327+
- **List**: the empty list
328+
- **nothing**: nothing
329+
330+
Variables whose type do not have a default value will remain unassigned and it
331+
is a checked runtime error to fetch from such variables until they are assigned
332+
a value.
333+
316334
### Handlers
317335

318336
HandlerDefinition
@@ -350,8 +368,10 @@ return.
350368

351369
> **Note:** It is a checked runtime error to return from a handler
352370
> without ensuring all non-optional 'out' parameters have been assigned
353-
> a value.
354-
>
371+
> a value. However, this will only occur for typed variables whose type does
372+
> not have a default value as those which do will be default initialized at the
373+
> start of the handler.
374+
355375
An inout parameter means that the value from the caller is copied to the
356376
parameter variable in the callee handler on entry, and copied back out
357377
again on exit.
@@ -385,8 +405,8 @@ low-level types to be specified making it easier to interoperate.
385405

386406
Foreign types map to high-level types as follows:
387407

388-
- bool maps to boolean
389-
- int and uint map to integer (number)
408+
- bool maps to boolean
409+
- int and uint map to integer (number)
390410
- float and double map to real (number)
391411

392412
This mapping means that a foreign handler with a bool parameter say,
@@ -524,15 +544,14 @@ can be used after the variable statement, but not before.
524544
> **Note:** Variables are currently not block-scoped, they are defined
525545
> from the point of declaration to the end of the handler - this might
526546
> change in a subsequent revision.
527-
528-
Variables are initially undefined and thus cannot be fetched without a
529-
runtime error occurring until a value is placed into them. If a variable
530-
has been annotated with an optional type, its initial value will be
531-
nothing.
532-
533-
> **Note:** It is a checked runtime error to attempt to use a
534-
> non-optionally typed variable before it has a value.
535-
547+
548+
Variables whose type have a default value are initialized with that value at
549+
the point of definition in the handler. See the main Variables section for the
550+
defaults of the standard types.
551+
552+
> **Note:** It is a checked runtime error to attempt to use a variable whose
553+
> type has no default before it is assigned a value.
554+
536555
The type specification for the variable is optional, if it is not
537556
specified the type of the variable is *optional any* meaning that it can
538557
hold any value, including being nothing.
@@ -583,7 +602,7 @@ evaluates to a negative integer, it is taken to be zero.
583602

584603
> **Note:** It is a checked runtime error to use an expression not
585604
> evaluating to a number as the Count.
586-
605+
587606
The **repeat while** form repeats the body until the Condition
588607
expression evaluates to false.
589608

@@ -595,7 +614,7 @@ expression evaluates to true.
595614

596615
> **Note:** It is a checked runtime error to use an expression not
597616
> evaluating to a boolean as the Condition.
598-
617+
599618
The **repeat with** form repeats the body until the Counter variable
600619
reaches or crosses (depending on iteration direction) the value of the
601620
Finish expression. The counter variable is adjusted by the value of the
@@ -606,7 +625,7 @@ statement.
606625

607626
> **Note:** It is a checked runtime error to use expressions not
608627
> evaluating to a number as Start, Finish or Step.
609-
628+
610629
The **repeat for each** form evaluates the Container expression, and
611630
then iterates through it in a custom manner depending on the Iterator
612631
syntax. For example:

docs/lcb/notes/17526.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# LiveCode Builder Language
2+
## Syntax
3+
4+
* handler-local variables are now lexically scoped within
5+
statement blocks.
6+
7+
# [17526] Make variables lexically scoped in statement blocks.
8+

docs/lcb/notes/17809.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# LiveCode Builder Language
2+
## Variables
3+
4+
* Out parameters are now initialized by default to a suitable empty value at the
5+
start of the handler. For example:
6+
7+
public handler GetMyValue(out rValue as Integer)
8+
end handler
9+
10+
Will result in rValue begin 0.
11+
12+
# [17809] Initialize out parameters to default.

libscript/include/libscript/script.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ void MCScriptContinueInvokeInModule(MCScriptModuleBuilderRef builder, uindex_t a
366366
void MCScriptEndInvokeInModule(MCScriptModuleBuilderRef builder);
367367
void MCScriptEmitFetchInModule(MCScriptModuleBuilderRef builder, uindex_t dst_reg, uindex_t index, uindex_t level);
368368
void MCScriptEmitStoreInModule(MCScriptModuleBuilderRef builder, uindex_t src_reg, uindex_t index, uindex_t level);
369+
void MCScriptEmitResetInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
369370

370371
void MCScriptEmitPositionInModule(MCScriptModuleBuilderRef builder, MCNameRef file, uindex_t line);
371372

libscript/src/script-builder.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,14 @@ void MCScriptEmitStoreInModule(MCScriptModuleBuilderRef self, uindex_t p_src_reg
15321532
__emit_instruction(self, kMCScriptBytecodeOpStore, 3, p_src_reg, p_index, p_level - 1);
15331533
}
15341534

1535+
void MCScriptEmitResetInModule(MCScriptModuleBuilderRef self, uindex_t p_reg)
1536+
{
1537+
if (self == nil || !self -> valid)
1538+
return;
1539+
1540+
__emit_instruction(self, kMCScriptBytecodeOpReset, 1, p_reg);
1541+
}
1542+
15351543
void MCScriptEmitPositionInModule(MCScriptModuleBuilderRef self, MCNameRef p_file, uindex_t p_line)
15361544
{
15371545
if (self == nil || !self -> valid)

libscript/src/script-instance.cpp

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -943,37 +943,6 @@ MCScriptFrameInitializeParametersImmediate(MCScriptFrame *self,
943943
return true;
944944
}
945945

946-
static bool
947-
MCScriptFrameInitializeSlots(MCScriptFrame *self)
948-
{
949-
MCTypeInfoRef t_signature = MCScriptGetSignatureForFrame(self);
950-
uindex_t t_param_count = MCHandlerTypeInfoGetParameterCount(t_signature);
951-
952-
for (uindex_t i = t_param_count; i < self->handler->slot_count; ++i)
953-
{
954-
MCTypeInfoRef t_slot_type = nil;
955-
if (i < t_param_count + self->handler->local_type_count)
956-
{
957-
t_slot_type = self->instance->module->types[self->handler->local_types[i - t_param_count]]->typeinfo;
958-
}
959-
960-
MCValueRef t_default = nil;
961-
if (nil != t_slot_type)
962-
{
963-
t_default = MCTypeInfoGetDefault(t_slot_type);
964-
}
965-
966-
if (nil == t_default)
967-
{
968-
continue;
969-
}
970-
971-
MCValueAssign(self->slots[i], t_default);
972-
}
973-
974-
return true;
975-
}
976-
977946
static MCScriptFrame *MCScriptDestroyFrame(MCScriptFrame *self)
978947
{
979948
MCScriptFrame *t_caller;
@@ -1078,7 +1047,11 @@ static inline void MCScriptStoreToRegisterInFrame(MCScriptFrame *p_frame, uindex
10781047
if (p_frame -> slots[p_register] != p_value)
10791048
{
10801049
MCValueRelease(p_frame -> slots[p_register]);
1081-
p_frame -> slots[p_register] = MCValueRetain(p_value);
1050+
1051+
if (p_value != nil)
1052+
MCValueRetain(p_value);
1053+
1054+
p_frame -> slots[p_register] = p_value;
10821055
}
10831056
}
10841057

@@ -1159,8 +1132,7 @@ static bool MCScriptPerformScriptInvoke(MCScriptFrame*& x_frame, byte_t*& x_next
11591132
return false;
11601133

11611134
if (!MCScriptFrameInitializeParametersCaller(t_callee,
1162-
p_arguments, p_arity) ||
1163-
!MCScriptFrameInitializeSlots(t_callee))
1135+
p_arguments, p_arity))
11641136
{
11651137
MCScriptDestroyFrame(t_callee);
11661138
return false;
@@ -2075,8 +2047,7 @@ bool MCScriptCallHandlerOfInstanceInternal(MCScriptInstanceRef self, MCScriptHan
20752047
if (!MCScriptCreateFrame(nil, self, p_handler, t_frame))
20762048
return false;
20772049

2078-
if (!MCScriptFrameInitializeParametersImmediate(t_frame, p_arguments, p_argument_count) ||
2079-
!MCScriptFrameInitializeSlots(t_frame))
2050+
if (!MCScriptFrameInitializeParametersImmediate(t_frame, p_arguments, p_argument_count))
20802051
{
20812052
MCScriptDestroyFrame(t_frame);
20822053
return false;
@@ -2586,6 +2557,30 @@ bool MCScriptCallHandlerOfInstanceInternal(MCScriptInstanceRef self, MCScriptHan
25862557
}
25872558
}
25882559
break;
2560+
case kMCScriptBytecodeOpReset:
2561+
{
2562+
for(uindex_t t_arg = 0; t_success && t_arg < t_arity; t_arg += 1)
2563+
{
2564+
MCTypeInfoRef t_type =
2565+
MCScriptGetRegisterTypeInFrame(t_frame, t_arguments[t_arg]);
2566+
2567+
// If there is no type attached to the slot, then the creation
2568+
// makes it unassigned.
2569+
MCValueRef t_default_value = nil;
2570+
if (t_type != nil)
2571+
{
2572+
t_default_value = MCTypeInfoGetDefault(t_type);
2573+
}
2574+
2575+
// Assign to the slot. Notice that we don't do a 'checked'
2576+
// store, this is because we might be making the slot unassigned
2577+
// and we already know the type conforms.
2578+
MCScriptStoreToRegisterInFrame(t_frame,
2579+
t_arguments[t_arg],
2580+
t_default_value);
2581+
}
2582+
}
2583+
break;
25892584
}
25902585

25912586
// If we failed, then make sure the frame address is up to date.

0 commit comments

Comments
 (0)