04. Values
This time, I will explain the values that can be handled by LuneScript.
value type
Here is a correspondence table between the value types that can be handled by LuneScript and the Lua values after the transcompiler.
LuneScript | Lua | go | Usage | How to define in LuneScript | |
---|---|---|---|---|---|
nil, null | nil | nil | nil | nil | |
int | numerical value | LnsInt | integer | 0 1 2 3 ?A 0x10 -100 | |
real | numerical value | LnsReal | real number | 0.0 1.0 0.001 | |
str | string | string | string, binary data | "abc" 'def' ```hij``` | |
bool | boolean | bool | boolean | true false | |
List | table | LnsList | list | [1, 2, 3 ] | |
Map | table | LnsMap | map | { "A":1, "BC": 100 } | |
Set | table | LnsSet | set | (@ 1, 2, 3) | |
Tuple | table | []LnsAny | tuple | (= 1, 2, 3) | |
class | table | struct | class | class Test {} | |
interface | table | interface | interface | interface Test {} | |
fn | function | func | function | fn func() {} | |
enum | number, string | const | enum | enum Test { } | |
alge | table | struct | algebraic data | alge Test { } | |
Luaval | Lua value | Lns_luaValue | Lua value itself | ||
stem | value | LnsAny | A type that can hold any non-nil value |
2019/3 Set added. 2020/10 Luaval added 2023/1 Tuple added
As mentioned above, LuneScript subdivides Lua values and treats each as a separate type.
The intention of the subdivision is to improve the following specifications of Lua.
- Numerical values in Lua are all real numbers, and if you are not used to it, problems due to the numerical values being real numbers will occur. For example, the Lua calculation result for
10/3
is 3.3333 instead of 3. -
Lua tables are divided into sequences whose keys are all natural numbers and non-sequence whose keys are not natural numbers.
-
Due to this difference, the functions for enumerating data in the table are divided into ipairs and pairs, and it is necessary to use them properly.
- Although it can be said that there is no problem if only pairs are used. . .
- The
#
operator to get the size of a table is confusing because it returns the size of sequences and not the size of non-sequences.
-
nil
nil is the same as nil in Lua.
Null is also available in LuneScript.
null is an alias for nil.
Null support allows you to work with JSON as-is in LuneScript.
integer, real
LuneScript treats integers and real numbers separately.
So 10/3 becomes 3 and 10/3.0 becomes 3.3333…
The model names are as follows.
// @lnsFront: ok
let val:int = 1; // 整数 int
let val2:real = 1.5; // 実数 real
numeric literal
C89-like numeric literals are used.
- Integers support decimal and hexadecimal representations
- A real number is a decimal number and exponential representation by e.
letter
Using ?
allows LuneScript to treat the code of the character following ?
as an immediate value of type int .
// @lnsFront: ok
print( ?a ); // 97 (0x61)
Note that to get the code of a ' or " character, you need to escape it with \ like ?\' .
Only one byte of code is available this way. For example, if the character following ? is a multi-byte code such as UTF-8, the first byte is acquired and the second and subsequent bytes are parsed as LuneScript code, resulting in a parse error.
Four arithmetic operations
The four arithmetic operations of numerical values adopt the same ones as Lua.
The result of a binary operation changes type as follows:
- The operation result of int and int is int.
- The operation result of real and real becomes real.
- The result of an operation between int and real is real.
However, if the operation result of int and int is outside the range of int, the internal value at runtime will be real, but the type in LuneScript will remain int. To round the result of an operation to an int, you need to cast with @@int
.
When transcompiled to go, it's still an int internally.
bit operation
Supports bitwise operations. Not available in Lua5.1.
The bit length is 32bit in Lua5.2. The bit length of Lua5.3 depends on the environment.
- Logical AND (&)
// @lnsFront: ok
print( 1 & 3 == 1 );
- Logical OR (|)
// @lnsFront: ok
print( 1 | 2 == 3 );
- Exclusive OR (~)
// @lnsFront: ok
print( 1 ~ 3 == 2 );
- Logical shift left (|<<)
// @lnsFront: ok
print( 1 |<< 2 == 4 );
- Logical shift right (|>>)
// @lnsFront: ok
print( 0x10 |>> 2 == 4 );
- Bit reversal (~)
// @lnsFront: ok
print( ~2 == 0xfffffffd );
string
If you surround it with " or ', it becomes a string. You can use ' inside "", and you can use " inside ''.
Note that "", '' cannot contain newlines. Use " " to include line breaks.
Use ``` to define a multi-line string without using . The characters in `````` are converted to strings as they are instead of newlines.
Use [N] to get the character at a specific position in the string. The N specified here indicates 1 at the beginning of the string.
// @lnsFront: ok
let txt = "abc";
print( txt[ 2 ] ); // 98
The behavior is undefined if N exceeds the string length.
Get the string length with #.
// @lnsFront: ok
print( #"abc" ); // 3
The type name is str as follows.
// @lnsFront: ok
let val:str = "abc"; // 文字列 str
Linking
String concatenation is done with ..
.
// @lnsFront: ok
print( "abc" .. "efg" ); // abcdefg
format string
You can generate a string by specifying the format below.
// @lnsFront: ok
print( "%s %d %d" ("abc", 1, 2) ); // abc 1 2
Specify the value with () immediately after the string literal.
See Lua's string.format
API for information on formatting.
boolean
Has true and false.
The type name is bool as follows:
// @lnsFront: ok
let val:bool = true; // bool
list
A list is a type that can add and remove values.
// @lnsFront: ok
let mut list:List<int> = [];
list.insert( 1 ); // [ 1 ]
list.insert( 2 ); // [ 1, 2 ]
list.insert( 3 ); // [ 1, 2, 3 ]
list.remove(); // [ 1, 2 ]
print( list[1] ); // 1
Elements of the list are accessed by [index]. The index at the beginning of the list is 1. The behavior is undefined if you access outside the scope of the list.
Original LuneScript was developed as a lua transcompiler, so//I focused on compatibility with lua and set the index from 1,//I now believe this was a failure. .
It's #
to get the length of the list. For example, #list
gets the length of the list type variable list.
Add value is insert()
same as Lua, delete remove()
.
The type name is List<T> as follows: where T indicates the type of elements the list holds.
// @lnsFront: ok
let val:List<int> = [1,2];
Map
Map literals are an extension of the JSON format.
You can handle the JSON format like this:
// @lnsFront: ok
let map = {
"val1": 1,
"val2": 2,
"val3": 3
};
print( map.val1, map.val2, map.val3 ); // 1 2 3
It differs from JSON in the following ways:
- All non-nil values are allowed for keys and values
// @lnsFront: ok
let mut test:Map<int,int> = {};
let map = {
1: "val1",
2.0: "val2",
test: "val3"
};
print( map[ 1 ], map[ 2.0 ], map[ test ] ); // val1 val2 val3
Also, since null is aliased to nil, JSON itself can be handled.
// @lnsFront: ok
let mut map:Map<str,int> = {};
map[ "abc" ] = 1;
map.xyz = 10;
Elements of the Map are accessed by [key]. If key is of type str , it can also be accessed as .key .
For example, [ "abc" ] and .abc access the same element, so print in the following example prints true .
// @lnsFront: skip
print( map[ "abc" ] == map.abc ); // true
Note that you cannot use the #
operator on maps.
The type name of Map is Map<K,V> as follows: where K is the key type and V is the value type associated with the key.
// @lnsFront: ok
let val:Map<str,int> = { "abc":123 };
Delete value
As mentioned earlier, a Map cannot have nil values. By using this and setting the value to nil , we can remove it from the Map .
For example, the following will set abc to nil for val with 123 registered for key "abc": This will remove abc from val.
// @lnsFront: ok
let val:Map<str,int> = { "abc":123 };
val.abc = nil;
let mut total = 0;
foreach _ in val {
total = total + 1;
}
print( total ); // 1
a set of nilables
Assigning non-nil nilable values to Map is discouraged. It will be an error in the future.
// @lnsFront: ok
let val:Map<str,int> = { "abc":123 };
fn func( work:int! ) {
val.abc = work; // warrning
}
func( 1 );
This is because setting a Map to nilable makes it ambiguous whether it is a set of values or a deletion.
Note that setting immediate to nil itself will still be supported.
Notes on Maps
Note the following when working with Maps:
Map keys cannot distinguish between int and real .
Specifically, it is undefined what map[1]
and map[1.0]
return in the example below.
// @lnsFront: ok
let map = {
1: "val1",
1.0: "val2",
};
print( map[ 1 ], map[ 1.0 ] );
This is a limitation from the Lua specification.
Note that int and real are distinguished when transcompiled to go.
Key equivalence judgment when using keys other than int, real, str
In the following example, both list1 and list2 are lists with int 1 as an element. Using this list1 as a key, register "aaa" in map.
And if you get values from map with list1, list2 as keys, the result will be aaa and nil .
// @lnsFront: ok
let mut map:Map<&List<int>,str> = {};
let list1 = [ 1 ];
let list2 = [ 1 ];
map[ list1 ] = "aaa";
print( map[ list1 ], map[ list2 ] ); // aaa nil
This is because list1 and list2 are determined as different keys.
If you use anything other than int, real, or str as a map key, the keys must be the same object for equality.
nilable
As mentioned earlier, accessing the elements of a Map can be done like this:
// @lnsFront: ok
let map = {
"val1": 1,
"val2": 2,
"val3": 3
};
print( map.val1, map.val2, map.val3 ); // 1 2 3
Here, map.val1 becomes nilable and cannot be treated as 1 of int which is original data as it is.
In other words, you cannot run map.val1 + 1
like this:
print( map.val1 +1 )
For nilable see below.
Set
Tuple
generics
List, Array and Map support generics.
For example, declare each of the following:
// @lnsFront: ok
let list:List<int> = []; // int を要素に持つリスト
let array:Array<real> = [@]; // real を要素に持つ配列
let map:Map<str,int> = {}; // str をキー、int を値に持つマップ
collection type
// @lnsFront: ok
let list = [ 1, 2, 3 ];
let map = { "A": 10, "B": 11, "C": 12 };
Collections such as lists and maps can declare literals as shown above. The type of list and map generated at this time is determined by the configured value.
If the values used in a collection's constructor are all of the same type, the collection's type is that value's type.
For example, [ 1, 2, 3 ]
in the above sample becomes List<int>
.
If the values used in the collection's constructor are different, then the collection's type is stem .
Specifically:
// @lnsFront: ok
let list1 = [ 1, 2, 3 ]; // List<int>
let list2 = [ 'a', 'b', 'c' ]; // List<str>
let list3 = [ 'a', 1, 'c' ]; // List<stem>
let map1 = { "A": 10, "B": 11, "C": 12 }; // Map<str,int>
let map2 = { "A": 10, "B": 11, "C": 12 }; // Map<str,int>
let map3 = { "a": 'z', "b": 'y', "c": 'x' }; // Map<str,str>
let map4 = { "a": 1, "b": 'Z' }; // Map<str,stem>
Constructor of a collection containing multiple classes with inheritance relationships
Constructors of collections that include multiple classes with inheritance relationships may cause an error due to inability to resolve type inference.
If so, please specify the type.
Here is a sample:
// @lnsFront: error
class Test {
}
class Sub extend Test {
}
{
let mut val1 = [ [ new Test() ], [ new Sub() ] ]; // error
let mut val2:List<List<Test>> = [ [ new Test() ], [ new Sub() ] ]; // ok
let mut val3 = [ [ new Test() ], [ new Test() ] ]; // ok
let mut val4 = [ [ new Sub() ], [ new Sub() ] ]; // ok
}
Here, val1 using type inference is an error. On the other hand, val2 with an explicit type is OK. Since val3 and val4 do not mix classes, they can be type inferred.
enum
Luaval
LuneScript can execute Lua code. Execution results of Lua code convert int, real, bool and str internally, but other values are treated as Lua values without conversion.
Luaval holds the value of that Lua.
See below for details.
stem
A stem is a type that can hold any non-nil value.
LuneScript is a statically typed language, and if you give a value different from the expected type, you will get a compilation error.
On the other hand, the stem type is a type that can handle all types other than nil, so no compilation error will occur even if any value other than nil is given.
stem! is a type that can handle all values including nil. There is no problem in thinking that it is a Lua variable itself.
Note that once converted to the stem type, a cast is required to convert back to the original type.
Please refer to the following link for casting.
form
form is a type that handles function objects.
We'll talk about functions later.