tech

[English] [Japanese]

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 ]
Array table array (fixed length) [@ 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.

../nilable

Set

Handles a set of values.

See the following article for details.

../set/

Tuple

Handles value combinations.

See the following article for details.

../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

LuneScript supports enums.

See the article linked below for details.

../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.

../lua/

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.

../cast/

form

form is a type that handles function objects.

We'll talk about functions later.