xgo/docs/tutorial.md
2019-01-24 19:31:17 -08:00

5.4 KiB

Tengo Syntax

Tengo's syntax is designed to be familiar to Go developers while being a bit simpler and more streamlined.

Comments

Tengo supports line comments (//...) and block comments (/* ... */).

/* 
  multi-line block comments 
*/

a := 5 // line comments

Run in Playground

Types and Assignment

Tengo is a dynamically typed language, and, you can initialize the variables using := operator.

a := 1984 		// int
b := "aomame"		// string
c := -9.22		// float
d := true		// bool
e := '九'		// char
f := [1, false, "foo"]	// array
g := {			// map
    h: 439,
    i: 12.34,
    j: [0, 9, false]
}
k := func(l, m) {	// function
    return l + m
}

Run in Playground

After the variable is initialized, it can be re-assigned different value using = operator.

a := 1928		// int
a = "foo"		// string
f := func() {
    a := false		// 'a' is defined in the function scope
    a = [1, 2, 3]	// and thus does not affect 'a' in global scope.
}
a == "foo" 		// still "foo"

Run in Playground

Type is not directly specified, but, you can use type-coercion functions to convert between types.

s1 := string(1984)  // "1984"
i2 := int("-999")   // -999
f3 := float(-51)    // -51.0
b4 := bool(1)       // true
c5 := char("X")     // 'X'

Run in Playground

See Variable Types for more details on the variable types.

Indexing

You can use the dot selector (.) and indexer ([]) operator to read or write elements of arrays, strings, or maps.

["one", "two", "three"][1]	// == "two"

m := {
    a: 1,
    b: [2, 3, 4],
    c: func() { return 10 }
}
m.a				// == 1
m["b"][1]			// == 3
m.c()				// == 10
m.x = 5				// add 'x' to map 'm'
//m.b[5] = 0			// but this is an error: index out of bounds

Run in Playground

For sequence types (string, bytes, array), you can use slice operator ([:]) too.

a := [1, 2, 3, 4, 5][1:3]	// == [2, 3]
b := [1, 2, 3, 4, 5][3:]	// == [4, 5]
c := [1, 2, 3, 4, 5][:3]	// == [1, 2, 3]
d := "hello world"[2:10]	// == "llo worl"

Run in Playground

Functions

In Tengo, functions are first-class citizen, and, it also supports closures, functions that captures variables in outer scopes. In the following example, the function returned from adder is capturing base variable.

adder := func(base) {
    return func(x) { return base + x }	// capturing 'base'
}
add5 := adder(5)
nine := add5(4)		// == 9

Run in Playground

Flow Control

For flow control, Tengo currently supports if-else, for, for-in statements.

// IF-ELSE
if a < 0 {
    // ...
} else if a == 0 {
    // ...
} else {
    // ...
}

// IF with init statement
if a := 0; a < 10 {
    // ...
} else {
    // ...
}

// FOR
for a:=0; a<10; a++ {
    // ...
}

// FOR condition-only (like WHILE in other languages)
for a < 10 {
    // ...
}

// FOR-IN
for x in [1, 2, 3] {		// array: element
    // ...
}
for i, x in [1, 2, 3] {		// array: index and element
    // ...
} 
for k, v in {k1: 1, k2: 2} {	// map: key and value
    // ...
}

Immutable Values

A value can be marked as immutable using immutable expression.

a := immutable([1, 2, 3]) // 'a' is immutable
b = a[1]                  // b == 2
a[0] = 5                  // runtime error

c := immutable({d: [1, 2, 3]})
c.d[1] = 10               // runtime error as 'c.d' is also immutable

e := {f: a}               // 'a' is immutable but 'e' is not
e.g = 20                  // valid; e == {f: a, g: 20}
e.a[1] = 5                // runtime error as 'e.a' is immutable

Errors

An error object is created using error expression. An error can contain value of any types, and, the underlying value can be read using .value selector.

err1 := error("oops")   // error with string value
err2 := error(1+2+3)    // error with int value
if is_error(err1) {     // 'is_error' builtin function
    err_val := err1.value   // get underlying value 
}  

Run in Playground

Modules

You can load other scripts as modules using import expression.

Main script:

mod1 := import("./mod1") // assuming mod1.tengo file exists in the current directory 
                         // same as 'import("./mod1.tengo")' or 'import("mod1")'
mod1.func1(a)            // module function 
a += mod1.foo            // module variable
//mod1.foo = 5           // error: module variables are read-only

mod1.tengo file:

func1 := func(x) { print(x) }
foo := 2

Basically, import expression returns all the global variables defined in the module as a Map-like value. One can access the functions or variables defined in the module using . selector or ["key"] indexer, but, module variables are immutable.

Also, you can use import to load the Standard Library.

math := import("math")
a := math.abs(-19.84) // == 19.84