{"id":13746432,"url":"https://github.com/haifenghuang/monkey","last_synced_at":"2025-05-09T07:30:41.173Z","repository":{"id":79707674,"uuid":"107351323","full_name":"haifenghuang/monkey","owner":"haifenghuang","description":"Interpreter with support for class, linq, sql, net, http, fmt, json and A realtime syntax highlighting REPL.","archived":true,"fork":false,"pushed_at":"2018-12-20T10:36:29.000Z","size":10711,"stargazers_count":389,"open_issues_count":1,"forks_count":21,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-11-15T19:37:29.792Z","etag":null,"topics":["interpreted-languages","interpreter","language","object-oriented","pratt-parser","programming-language","scripting-language","scripting-languages"],"latest_commit_sha":null,"homepage":"https://github.com/haifenghuang/monkey","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/haifenghuang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-18T02:54:47.000Z","updated_at":"2024-09-20T02:07:55.000Z","dependencies_parsed_at":"2023-05-14T22:30:43.782Z","dependency_job_id":null,"html_url":"https://github.com/haifenghuang/monkey","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haifenghuang%2Fmonkey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haifenghuang%2Fmonkey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haifenghuang%2Fmonkey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haifenghuang%2Fmonkey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/haifenghuang","download_url":"https://codeload.github.com/haifenghuang/monkey/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253209248,"owners_count":21871618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["interpreted-languages","interpreter","language","object-oriented","pratt-parser","programming-language","scripting-language","scripting-languages"],"created_at":"2024-08-03T06:00:53.527Z","updated_at":"2025-05-09T07:30:40.428Z","avatar_url":"https://github.com/haifenghuang.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Deprecation Notice\nThis project is no longer being maintained. All the contents have been\nmoved to [magpie](https://github.com/haifenghuang/magpie).\n\n\n# Monkey Programming Language\n\nChinese version: [中文](README_cn.md)\n\nTable of Contents\n=================\n\n* [Monkey Programming Language](#monkey-programming-language)\n  * [Summary](#summary)\n  * [Overview](#overview)\n  * [Installation](#installation)\n  * [Basic use](#basic-use)\n  * [Language Tour](#language-tour)\n    * [Comments](#comments)\n    * [Data Types](#data-types)\n    * [Constants(Literal)](#constantsliteral)\n    * [Variables](#variables)\n    * [Reserved keywords](#reserved-keywords)\n    * [Type conversion](#type-conversion)\n    * [qw(Quote word) keyword](#qwquote-word-keyword)\n    * [enum keyword](#enum-keyword)\n    * [Meta\\-Operators](#meta-operators)\n    * [Control flow](#control-flow)\n    * [using statement](#using-statement)\n    * [User Defined Operator](#user-defined-operator)\n    * [Integer](#integer)\n    * [Float](#float)\n    * [Decimal](#decimal)\n    * [Array](#array)\n    * [String](#string)\n    * [Hash](#hash)\n    * [Tuple](#tuple)\n    * [class](#class)\n      * [inheritance and polymorphism](#inheritance-and-polymorphism)\n      * [operator overloading](#operator-overloading)\n      * [property(like c\\#)](#propertylike-c)\n      * [indexer](#indexer)\n      * [static members/methods/properties](#static-membersmethodsproperties)\n      * [Class Category](#class-category)\n      * [Annotations](#annotations)\n    * [Standard input/output/error](#standard-inputoutputerror)\n    * [Error Handling of standard library](#error-handling-of-standard-library)\n    * [About defer keyword](#about-defer-keyword)\n    * [Concatenation of different types](#concatenation-of-different-types)\n    * [Comprehensions](#comprehensions)\n    * [grep and map](#grep-and-map)\n    * [Function](#function)\n    * [Pipe Operator](#pipe-operator)\n    * [Spawn and channel](#spawn-and-channel)\n  * [Use go language modules](#use-go-language-modules)\n  * [Standard module introduction](#standard-module-introduction)\n      * [fmt module](#fmt-module)\n      * [time module](#time-module)\n      * [logger module](#logger-module)\n      * [flag module(for handling of command line options)](#flag-modulefor-handling-of-command-line-options)\n      * [json module(for json marshal \u0026amp; unmarshal)](#json-modulefor-json-marshal--unmarshal)\n      * [net module](#net-module)\n      * [linq module](#linq-module)\n      * [Linq for file](#linq-for-file)\n      * [csv module](#csv-module)\n      * [template module](#template-module)\n      * [sql module](#sql-module)\n  * [About regular expression](#about-regular-expression)\n  * [Useful Utilities](#useful-utilities)\n  * [Document generator](#document-generator)\n  * [Syntax Highlight](#syntax-highlight)\n  * [Future Plans](#future-plans)\n  * [License](#license)\n\n## Summary\n\nMonkey is a toy language interpreter, written in Go. It has C-style syntax, and is largely inspired by Ruby, Python, Perl and c#\nIt support the normal control flow, functional programming and object oriented programming.\nIt also has a REPL with realtime syntax highlighter.\n\nThis is a sample program using monkey language:\n\n```swift\n\n//Declare annotation class\n//Note: In the body, you must use property, not method.\nclass @MinMaxValidator {\n  property MinLength\n  property MaxLength default 10 //Same as 'property MaxLength = 10'\n}\n\n//This is a marker annotation\nclass @NoSpaceValidator {}\n\nclass @DepartmentValidator {\n  property Department\n}\n\n//The 'Request' class\nclass Request {\n  @MinMaxValidator(MinLength=1)\n  property FirstName; //getter and setter are implicit. It is equal to 'property FirstName { get; set; }'\n\n  @NoSpaceValidator\n  property LastName;\n\n  @DepartmentValidator(Department=[\"Department of Education\", \"Department of Labors\"])\n  property Dept;\n}\n\n//This class is responsible for processing the annotation.\nclass RequestHandler {\n  static fn handle(o) {\n    props = o.getProperties()\n    for p in props {\n      annos = p.getAnnotations()\n      for anno in annos {\n        if anno.instanceOf(MinMaxValidator) {\n          //p.value is the property real value.\n          if len(p.value) \u003e anno.MaxLength || len(p.value) \u003c anno.MinLength {\n            printf(\"Property '%s' is not valid!\\n\", p.name)\n          }\n        } elseif anno.instanceOf(NoSpaceValidator) {\n          for c in p.value {\n            if c == \" \" || c == \"\\t\" {\n              printf(\"Property '%s' is not valid!\\n\", p.name)\n              break\n            }\n          }\n        } elseif anno.instanceOf(DepartmentValidator) {\n          found = false\n          for d in anno.Department {\n            if p.value == d {\n              found = true\n            }\n          }\n          if !found {\n            printf(\"Property '%s' is not valid!\\n\", p.name)\n          }\n        }\n      }\n    }\n  }\n}\n\nclass RequestMain {\n  static fn main() {\n    request = new Request()\n    request.FirstName = \"Haifeng123456789\"\n    request.LastName = \"Huang     \"\n    request.Dept = \"Department of Labors\"\n    RequestHandler.handle(request)\n  }\n}\n\nRequestMain.main()\n```\n\nBelow is the result：\n\n```\nProperty 'FirstName' not valid!\nProperty 'LastName' not valid!\n```\n\n\nBelow is the REPL with real time syntax highlight:\n\n![REPL](REPL.gif)\n\nBelow is the html document generated using the `mdoc` tool:\n\n![HTML DOC](doc.png)\n\n## Overview\n\nThis project is based on mayoms's project [monkey](https://github.com/mayoms/monkey) with some bug fixes and a lot of new features including:\n\n* Added simple class support(Indexer, operator overloading, property, static method/property/field and class annotation)\n* Modified string module(which can correctly handle utf8 character encoding)\n* Added `file` module(with some new methods).\n* Added `math`，`time`, `sort`, `os`, `log`, `net`, `http`, `filepath`, `fmt`, `sync`, `list`, `csv`, `regexp`, `template`, etc...\n* `sql`(db) module(which can correctly handing null values)\n* `flag` module(for handling command line options)\n* `json` module(for json marshaling and unmarshaling)\n* `linq` module(Code come from [linq](https://github.com/ahmetb/go-linq) with some modifications)\n* `decimal` module(Code come from [decimal](https://github.com/shopspring/decimal) with some minor modifications)\n* Regular expression literal support(partially like perls)\n* channel support(like golang's channel)\n* more operator support(\u0026\u0026, ||, \u0026, |, ^, +=, -=, ?:, ??, etc.)\n* utf8 support(e.g. you could use utf8 character as variable name)\n* more flow control support(e.g. try/catch/finally, for-in, case, c-like for loop)\n* defer support\n* spawn support(goroutine)\n* enum support\n* `using` support(like C#'s `using`)\n* pipe operator support(see demo for help)\n* function with default value and variadic parameters\n* list comprehension and hash comprehension support\n* user defined operator support\n* Using method of Go Package(`RegisterFunctions` and `RegisterVars`)\n\nThere are a number of tasks to complete, as well as a number of bugs. The purpose of this project was to dive deeper into Go, as well as get a better understanding of how programming languages work. It has been successful in those goals. There may or may not be continued work - I do plan on untangling a few messy spots, and there are a few features I'd like to see implemented. This will happen as time and interest allows.\n\n## Installation\n\nJust download the repository and run `./run.sh`\n\n## Basic use\n\nTo access the REPL, simply run the following:\n\n```sh\n~ » monkey\nMonkey programming language REPL\n\n\u003e\u003e\n```\n\nor, to run a program:\n\n```sh\nmonkey path/to/file\n```\n\n## Language Tour\n\n### Comments\n\nMonkey support two kinds of single line comment and also block comment.\n\n```swift\n// this is a single line comment\n# this is another single line comment\n\n/* This is a \n   block comment.\n*/\n```\n\n### Data Types\n\nMonkey supports 9 basic data types: `String`, `Int`, `UInt`, `Float`, `Bool`, `Array`, `Hash`, `Tuple` and `Nil`\n\n```swift\ns1 = \"hello, 黄\"       # strings are UTF-8 encoded\ns2 = `hello, \"world\"`  # raw string\ni = 10                 # int\nu = 10u                # uint\nf = 10.0               # float\nb = true               # bool\na = [1, \"2\"]           # array\nh = {\"a\": 1, \"b\": 2}   # hash\nt = (1,2,3)            # tuple\nn = nil\n```\n\n### Constants(Literal)\n\nIn monkey, there are mainly eleven types of constants(Literals).\n\n* Integer\n* UInteger\n* Float\n* String\n* Regular expression\n* Array\n* Hash\n* Tuple\n* Nil\n* Boolean\n* Function\n\n```swift\n// Integer literals\ni1 = 10\ni2 = 20_000_000     //for more readable\ni3 = 0x80           // hex\ni4 = 0b10101        // binary\ni5 = 0o127          // octal\n\n// Unsigned Integer literals\nui1 = 10u\nui2 = 20_000_000u     //for more readable\nui3 = 0x80u           // hex\nui4 = 0b10101u        // binary\nui5 = 0o127u          // octal\n\n// Float literals\nf1 = 10.25\nf2 = 1.02E3\nf3 = 123_456.789_012 //for more readable\n\n// String literals\ns1 = \"123\"\ns2 = \"Hello world\"\n\n// Regular expression literals\nr = /\\d+/.match(\"12\")\nif (r) { prinln(\"regex matched!\") }\n\n// Array literals\na = [1+2, 3, 4, \"5\", 3]\n\n// Hash literals\nh = { \"a\": 1, \"b\": 2, \"c\": 2}\n\n//Tuple literals\nt = (1, 2+3, \"Hello\", 5)\n\n// Nil literal\nn = nil\n\n// Boolean literals\nt = true\nf = false\n\n// Function literals\nlet f1 = add(x, y) { return x + y }\nprintln(f1(1,2))\n\n//fat-arrow function literals\nlet f2 = (x, y) =\u003e x + y\nprintln(f2(1,2))\n```\n\n### Variables\n\nVariables in Monkey could start with the keyword `let`, or nothing with the\nform `variable=value`.\n\n```swift\nlet a, b, c = 1, \"hello world\", [1,2,3]\nd = 4\ne = 5\n姓 = \"黄\"\n```\n\nYou can also use `Destructuring assignment`.\nNote, the left-hand side must be included using the '()'.\n\n```swift\n//righ-hand side is an array\nlet (d,e,f) = [1,5,8]\n//d=1, e=5, f=8\n\n//right-hand side is a tuple\nlet (g, h, i) = (10, 20, \"hhf\")\n//g=10, h=20, i=hhf\n\n//righ-hand side is a hash\nlet (j, k, l) = {\"j\": 50, \"l\": \"good\"}\n//j=50, k=nil, l=good\n\n```\n\nNote however, if you do not use the keyword `let`, you could not do multiple variable assignments.\nBelow code is not correct：\n\n```swift\n//Error, multiple variable assignments must be use `let` keyword\na, b, c = 1, \"hello world\", [1,2,3]\n```\n\nNote：Starting from Monkey 5.0，when the decalared variable already exists, it's value will be overwritten:\n\n```swift\nlet x, y = 10, 20;\nlet x, y = y, x //Swap the value of x and y\nprintf(\"x=%v, y=%v\\n\", x, y)  //result: x=20, y=10\n```\n`let` also support the placeholder(_), when assigned a value, it will just ignore it.\n\n```swift\nlet x, _, y = 10, 20, 30\nprintf(\"x=%d, y=%d\\n\", x, y) //result: x=10, y=30\n```\n\n### Reserved keywords\n\nKeywords are predefined, reserved identifiers that have special meanings to the compiler. They cannot be used as identifiers. Below is a list of reserved keywords\n\n* fn\n* let\n* true false nil\n* if elsif elseif elif else\n* unless\n* return\n* include\n* and or\n* enum\n* struct # reserved, not used\n* do while for break continue where\n* grep map\n* case is in\n* try catch finally throw\n* defer\n* spawn\n* qw\n* using\n* class new property set get static default\n* interface public private protected # reserved, not used\n\n### Type conversion\n\nYou can use the builtin `int()`, `uint()`, `float()`, `str()`, `array()`, `tuple()`, `hash`, `decimal` functions for type conversion.\n\n```swift\nlet i = 0xa\nlet u = uint(i)                 // result: 10\nlet s = str(i)                  // result: \"10\"\nlet f = float(i)                // result: 10\nlet a = array(i)                // result: [10]\nlet t = tuple(i)                // result:(10,)\nlet h = hash((\"key\", \"value\"))  // result: {\"key\": \"value}\nlet d = decimal(\"123.45634567\") // result: 123.45634567\n```\n\nYou could create a tuple from an array:\n\n```swift\nlet t = tuple([10, 20])   //result:(10,20)\n```\n\nSimilarly, you could also create an array from a tuple:\n\n```swift\nlet arr = array((10,20))  //result:[10,20]\n```\n\nYou could only create a hash from an array or a tuple:\n\n```swift\n//create an empty hash\nlet h1 = hash()  //same as h1 = {}\n\n//create a hash from an array\nlet h1 = hash([10, 20])     //result: {10 : 20}\nlet h2 = hash([10,20,30])   //result: {10 : 20, 30 : nil}\n\n//create a hash from a tuple\nlet h3 = hash((10, 20))     //result: {10 : 20}\nlet h4 = hash((10,20,30))   //result: {10 : 20, 30 : nil}\n```\n\n### `qw`(Quote word) keyword\n\nThe `qw` keyword is like perl's `qw` keyword. When you want to use a lot of quoted strings,\nthe `qw` keyword can make it a lot easier for those strings.\n\n```swift\nfor str in qw\u003cabc, def, ghi, jkl, mno\u003e { //allowed 'qw' pair is '{}', '\u003c\u003e', '()'\n  println('str={str}')\n}\n\nnewArr = qw(1,2,3.5) //array with string values, not number values.\nfmt.printf(\"newArr=%v\\n\", newArr)\n```\n\n### `enum` keyword\n\nIn mokey, you can use enum to define constants.\n\n```swift\nLogOption = enum {\n    Ldate         = 1 \u003c\u003c 0,\n    Ltime         = 1 \u003c\u003c 1,\n    Lmicroseconds = 1 \u003c\u003c 2,\n    Llongfile     = 1 \u003c\u003c 3,\n    Lshortfile    = 1 \u003c\u003c 4,\n    LUTC          = 1 \u003c\u003c 5,\n    LstdFlags     = 1 \u003c\u003c 4 | 1 \u003c\u003c 5\n}\n\nopt = LogOption.LstdFlags\nprintln(opt)\n\n//get all names of the `enum`\nfor s in LogOption.getNames() { //not ordered\n    println(s)\n}\n\n//get all values of the `enum`\nfor s in LogOption.getValues() { //not ordered\n    println(s)\n}\n\n// get a specific name of the `enum`\nprintln(LogOption.getName(LogOption.Lshortfile))\n```\n\n### Meta-Operators\nMonkey has some meta-operators borrowed from perl6.\nThere are strict rules for meta-operators:\n\n* Meta-operators can only operator on arrays.\n* Each array's element must be number type(uint, int, float) or string type.\n* If the meat-operators serve as an infix operator, and if the left and right are all arrays, they must have the same number of elements.\n\n```swift\nlet arr1 = [1,2,3] ~* [4,5,6]\nlet arr2 = [1,2,3] ~* 4\nlet arr3 = [1,2,\"HELLO\"] ~* 2\nlet value1 = ~*[10,2,2]\nlet value2 = ~+[2,\"HELLO\",2]\n\nprintln(arr1)   //result: [4, 10, 18]\nprintln(arr2)   //result: [4, 8, 12]\nprintln(arr3)   //result: [2,4,\"HELLOHELLO\"]\nprintln(value1) //result: 40\nprintln(value2) //result: 2HELLO2\n```\n\nAt the moment, Monkey has six meta-operators：\n* \u003cp\u003e~+\u003c/p\u003e\n* \u003cp\u003e~-\u003c/p\u003e\n* \u003cp\u003e~*\u003c/p\u003e\n* \u003cp\u003e~/\u003c/p\u003e\n* \u003cp\u003e~%\u003c/p\u003e\n* \u003cp\u003e~^\u003c/p\u003e\n\nThe six meta-operators could be served as either infix expression or prefix expression.\n\nThe meta-operator for infix expression will return an array.\nThe meta-operator for prefix expression will return a value(uint, int, float, string).\n\nBelow talbe give an example of meta-operator and their meanings:(only `~+` is showed):\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eMeta-Operator\u003c/td\u003e\n    \u003cth\u003eExpression\u003c/td\u003e\n    \u003cth\u003eExample\u003c/td\u003e\n    \u003cth\u003eResult\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e~+\u003c/td\u003e\n    \u003ctd\u003eInfix Expression\u003c/td\u003e\n    \u003ctd\u003e[x1, y1, z1] ~+ [x2, y2, z2]\u003c/td\u003e\n    \u003ctd\u003e[x1+x2, y1+y2, z1+z2] (Array)\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e~+\u003c/td\u003e\n    \u003ctd\u003eInfix Expression\u003c/td\u003e\n    \u003ctd\u003e[x1, y1, z1] ~+ 4\u003c/td\u003e\n    \u003ctd\u003e[x1+4, y1+4, z1+4] (Array)\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e~+\u003c/td\u003e\n    \u003ctd\u003ePrefix Expression\u003c/td\u003e\n    \u003ctd\u003e~+[x1, y1, z1]\u003c/td\u003e\n    \u003ctd\u003ex1+y1+z1 (Note: a value, not an array)\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Control flow\n\n* if/if-else/if-elif-else/if-elsif-else/if-elseif-else/if-else if-else\n* unless/unless-else\n* for/for-in\n* while\n* do\n* try-catch-finally\n* case-in/case-is\n\n```swift\n// if-else\nlet a, b = 10, 5\nif (a \u003e b) {\n    println(\"a \u003e b\")\n}\nelseif a == b { // could also use 'elsif', 'elseif' or 'elif'\n    println(\"a = b\")\n}\nelse {\n    println(\"a \u003c b\")\n}\n\n//unless-else\nunless b \u003e a {\n    println(\"a \u003e= b\")\n} else {\n    println(\"b \u003e a\")\n}\n\n// for\ni = 9\nfor { // forever loop\n    i = i + 2\n    if (i \u003e 20) { break }\n    println('i = {i}')\n}\n\ni = 0\nfor (i = 0; i \u003c 5; i++) {  // c-like for, '()' is a must\n    if (i \u003e 4) { break }\n    if (i == 2) { continue }\n    println('i is {i}')\n}\n\ni = 0\nfor (; i \u003c 5; i++) {  // no initialization statement.\n    if (i \u003e 4) { break }\n    if (i == 2) { continue }\n    println('i is {i}')\n}\n\ni = 0\nfor (; i \u003c 5;;) {  // no updater statement.\n    if (i \u003e 4) { break }\n    if (i == 2) { continue }\n    println('i is {i}')\n    i++ // Updater statement\n}\n\ni = 0\nfor (;;;) {  // same as 'for { block }'\n    if (i \u003e 4) { break }\n    println('i is {i}')\n    i++ //update the 'i'\n}\n\nfor i in range(10) {\n    println('i = {i}')\n}\n\na = [1,2,3,4]\nfor i in a where i % 2 != 0 {\n    println(i)\n}\n\n\nhs = {\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4, \"e\": 5, \"f\": 6, \"g\": 7}\nfor k, v in hs where v % 2 == 0 {\n    println('{k} : {v}')\n}\n\n\nfor i in 1..5 {\n    println('i={i}')\n}\n\nfor item in 10..20 where $_ % 2 == 0 { // $_ is the index\n    printf(\"idx=%d, item=%d\\n\", $_, item)\n}\n\n\nfor c in \"m\"..\"a\" {\n    println('c={c}')\n}\n\n\nfor idx, v in \"abcd\" {\n    printf(\"idx=%d, v=%s\\n\", idx, v)\n}\n\n\nfor idx, v in [\"a\", \"b\", \"c\", \"d\"] {\n    printf(\"idx=%d, v=%s\\n\", idx, v)\n}\n\nfor item in [\"a\", \"b\", \"c\", \"d\"] where $_ % 2 == 0 { // $_ is the index\n    printf(\"idx=%d, item=%s\\n\", $_, v)\n}\n\n\n//for loop is an expression, not statement, so it could be assigned to a variable\nlet plus_one = for i in [1,2,3,4] { i + 1 }\nfmt.println(plus_one)\n\n// while\ni = 10\nwhile (i\u003e3) {\n    i--\n    println('i={i}')\n}\n\n// do\ni = 10\ndo {\n    i--\n    if (i==3) { break }\n}\n\n// try-catch-finally(only support string type)\nlet exceptStr = \"SUMERROR\"\ntry {\n    let th = 1 + 2\n    if (th == 3) { throw exceptStr }\n}\ncatch \"OTHERERROR\" {\n    println(\"Catched OTHERERROR\")\n}\ncatch exceptStr {\n    println(\"Catched is SUMERROR\")\n}\ncatch {\n    println(\"Catched ALL\")\n}\nfinally {\n    println(\"finally running\")\n}\n\n// case-in/case-is\nlet testStr = \"123\"\ncase testStr in { // in(exact/partial match), is(only exact match)\n    \"abc\", \"mno\" { println(\"testStr is 'abc' or 'mno'\") }\n    \"def\"        { println(\"testStr is 'def'\") }\n    `\\d+`        { println(\"testStr contains digit\") }\n    else         { println(\"testStr not matched\") }\n}\n\nlet i = [{\"a\": 1, \"b\": 2}, 10]\nlet x = [{\"a\": 1, \"b\": 2},10]\ncase i in {\n    1, 2 { println(\"i matched 1, 2\") }\n    3    { println(\"i matched 3\") }\n    x    { println(\"i matched x\") }\n    else { println(\"i not matched anything\")}\n}\n\n```\n### using statement\nIn monkey, if you have some resources you want to release/free/close, e.g. close opended file, close network connection etc，\nyou can use the `using` statement just like `c#`.\n\n```swift\n// Here we use 'using' statement, so we do not need to call infile.close().\n// When finished running 'using' statement, it will automatically call infile.close().\nusing (infile = newFile(\"./file.demo\", \"r\")) {\n    if (infile == nil) {\n        println(\"opening 'file.demo' for reading failed, error:\", infile.message())\n        os.exit(1)\n    }\n\n    let line;\n    let num = 0\n    //Read file by using extraction operator(\"\u003e\u003e\")\n    while (infile\u003e\u003eline != nil) {\n        num++\n        printf(\"%d\t%s\\n\", num, line)\n    }\n}\n```\n\n### User Defined Operator\nIn monkey, you are free to define some operators, but you cannot\noverwrite predefined operators.\n\n\u003e Note: Not all operators could be user defined.\n\nBelow is an example for showing how to write User Defined Operators:\n\n```swift\n//infix operator '=@' which accept two parameters.\nfn =@(x, y) {\n    return x + y * y\n}\n\n//prefix operator '=^' which accept only one parameter.\nfn =^(x) {\n    return -x\n}\n\nlet pp = 10 =@ 5 // Use the '=@' user defined infix operator\nprintf(\"pp=%d\\n\", pp) // result: pp=35\n\nlet hh = =^10 // Use the '=^' prefix operator\nprintf(\"hh=%d\\n\", hh) // result: hh=-10\n```\n\n```swift\nfn .^(x, y) {\n    arr = []\n    while x \u003c= y {\n        arr += x\n        x += 2\n    }\n    return arr\n}\n\nlet pp = 10.^20\nprintf(\"pp=%v\\n\", pp) // result: pp=[10, 12, 14, 16, 18, 20]\n```\n\nBelow is a list of predefined operators and user defined operators:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003ePredefined Operators\u003c/td\u003e\n    \u003cth\u003eUser Defined Operators\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e==\u003cbr/\u003e=~\u003cbr/\u003e=\u003e\u003c/td\u003e\n    \u003ctd\u003e=X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e++\u003cbr/\u003e+=\u003c/td\u003e\n    \u003ctd\u003e+X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e--\u003cbr/\u003e-=\u003cbr/\u003e-\u003e\u003c/td\u003e\n    \u003ctd\u003e-X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u0026gt;=\u003cbr/\u003e\u0026lt;\u0026gt;\u003c/td\u003e\n    \u003ctd\u003e\u0026gt;X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u0026lt;=\u003cbr/\u003e\u0026lt;\u0026lt;\u003c/td\u003e\n    \u003ctd\u003e\u0026lt;X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e!=\u003cbr/\u003e!~\u003c/td\u003e\n    \u003ctd\u003e!X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e*=\u003cbr/\u003e**\u003c/td\u003e\n    \u003ctd\u003e*X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e..\u003cbr/\u003e..\u003c/td\u003e\n    \u003ctd\u003e.X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u0026amp;=\u003cbr/\u003e\u0026amp;\u0026amp;\u003c/td\u003e\n    \u003ctd\u003e\u0026amp;X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e|=\u003cbr/\u003e||\u003c/td\u003e\n    \u003ctd\u003e|X\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e^=\u003c/td\u003e\n    \u003ctd\u003e^X\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003e In the table above, `X` could be `.=+-*/%\u0026,|^~\u003c,\u003e},!?@#$`\n\n### Integer\n\nIn monkey, integer is treated as an object, so you could call it's methods.\nPlease see below examples:\n\n```swift\nx = (-1).next()\nprintln(x) //0\n\nx = -1.next() //equals 'x = -(1.next())\nprintln(x) //-2\n\nx = (-1).prev()\nprintln(x) //-2\n\nx = -1.prev() //equals 'x = -(1.prev())\nprintln(x) //0\n\nx = [i for i in 10.upto(15)]\nprintln(x) //[10, 11, 12, 13, 14, 15]\n\nfor i in 10.downto(5) {\n    print(i, \"\") //10 9 8 7 6 5\n\n}\nprintln()\n\nif 10.isEven() {\n    println(\"10 is even\")\n}\n\nif 9.isOdd() {\n    println(\"9 is odd\")\n}\n```\n\n### Float\n\nIn monkey, float is also treated as an object, so you could call it's methods.\nPlease see below examples:\n\n```swift\nf0 = 15.20\nprintln(f0)\n\nf1 = 15.20.ceil()\nprintln(f1)\n\nf2 = 15.20.floor()\nprintln(f2)\n```\n\n### Decimal\n\nIn monkey, decimal is Arbitrary-precision fixed-point decimal numbers.\nAnd the code mainly based on [decimal](https://github.com/shopspring/decimal).\n\nPlease see below examples:\n\n```swift\nd1 = decimal.fromString(\"123.45678901234567\")  //create decimal from string\nd2 = decimal.fromFloat(3)  //create decimal from float\n\n//set decimal division precision.\n//Note: this will affect all other code that follows\ndecimal.setDivisionPrecision(50)\n\nfmt.println(\"123.45678901234567/3 = \", d1.div(d2))  //print d1/d2\nfmt.println(d1.div(d2)) //same as above\n\nfmt.println(decimal.fromString(\"123.456\").trunc(2)) //truncate decimal\n\n//convert string to decimal\nd3=decimal(\"123.45678901234567\")\nfmt.println(d3)\nfmt.println(\"123.45678901234567/3 = \", d3.div(d2))\n```\n\n### Array\n\nIn monkey, you could use [] to initialize an empty array:\n\n```swift\nemptyArr = []\nemptyArr[3] = 3 //will auto expand the array\nprintln(emptyArr)\n```\n\nYou can create an array with the given size(or length) using below two ways:\n\n```swift\n//create an array with 10 elements initialized to nil.\n//Note: this only support integer literal.\nlet arr = []10\nprintln(arr)\n\n//use the builtin 'newArray' method.\nlet anotherArr = newArray(len(arr))\nprintln(anotherArr) //result: [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]\n\nlet arr1 = [\"1\",\"a5\",\"5\", \"5b\",\"4\",\"cc\", \"7\", \"dd\", \"9\"]\nlet arr2 = newArray(6, arr1, 10, 11, 12) //first parameter is the size\nprintln(arr2) //result: [\"1\", \"a5\", \"5\", \"5b\", \"4\", \"cc\", \"7\", \"dd\", \"9\", 10, 11, 12]\n\nlet arr3 = newArray(20, arr1, 10, 11, 12)\nprintln(arr3) //result : [\"1\", \"a5\", \"5\", \"5b\", \"4\", \"cc\", \"7\", \"dd\", \"9\", 10, 11, 12, nil, nil, nil, nil, nil, nil, nil, nil]\n```\n\nArray could contain any number of different data types.\nNote: the last comma before the closing ']' is optional.\n\n```swift\nmixedArr = [1, 2.5, \"Hello\", [\"Another\", \"Array\"], {\"Name\": \"HHF\", \"SEX\": \"Male\"}]\n```\n\nYou could use index to access array element.\n\n```swift\nprintln('mixedArr[2]={mixedArr[2]}')\nprintln([\"a\", \"b\", \"c\", \"d\"][2])\n```\n\nBecause array is an object, so you could use the object's method to operate on it.\n\n```swift\nif ([].empty()) {\n    println(\"array is empty\")\n}\n\nemptyArr.push(\"Hello\")\nprintln(emptyArr)\n\n//you could also use 'addition' to add an item to an array\nemptyArr += 2\nprintln(emptyArr)\n\n//You could also use `\u003c\u003c(insertion operator)` to add item(s) to an array, the insertion operator support chain operation.\nemptyArr \u003c\u003c 2 \u003c\u003c 3\nprintln(emptyArr)\n```\n\nArray could be iterated using `for` loop\n\n```swift\nnumArr = [1,3,5,2,4,6,7,8,9]\nfor item in numArr where item % 2 == 0 {\n    println(item)\n}\n\nlet strArr = [\"1\",\"a5\",\"5\", \"5b\",\"4\",\"cc\", \"7\", \"dd\", \"9\"]\nfor item in strArr where /^\\d+/.match(item) {\n    println(item)\n}\n\nfor item in [\"a\", \"b\", \"c\", \"d\"] where $_ % 2 == 0 {  //$_ is the index\n    printf(\"idx=%d, v=%s\\n\", $_, item)\n}\n```\n\nYou could also use the builtin `reverse` function to reverse array element:\n\n```swift\nlet arr = [1,3,5,2,4,6,7,8,9]\nprintln(\"Source Array =\", arr)\n\nrevArr = reverse(arr)\nprintln(\"Reverse Array =\", revArr)\n```\n\n\nArray also support `array multiplication operator`(*):\n\n```swift\nlet arr = [3,4] * 3\nprintln(arr) // result: [3,4,3,4,3,4]\n```\n\n### String\n\nIn monkey, there are three types of `string`:\n\n* Raw string\n* Double quoted string(Could not contains newline)\n* Single quoted string(Interpolated String)\n\nRaw string literals are character sequences between back quotes, as in `foo`. Within the quotes, any character may appear except back quote.\n\nSee below for some examples:\n\n```swift\nnormalStr = \"Hello \" + \"world!\"\nprintln(normalStr)\n\nprintln(\"123456\"[2])\n\nrawStr = `Welcome to\nvisit us!`\nprintln(rawStr)\n\n//when you use single quoted string, and want variable to be interpolated,\n//you just put the variable into '{}'. see below:\nstr = \"Hello world\"\nprintln('str={str}') //output: \"Hello world\"\nstr[6]=\"W\"\nprintln('str={str}') //output: \"Hello World\"\n\n```\n\nIn monkey, strings are utf8-encoded, you could use utf-8 encoded name as a variable name.\n\n```swift\n三 = 3\n五 = 5\nprintln(三 + 五) //output : 8\n```\n\nstrings are also object, so you could use some of the methods provided by `strings` module.\n\n```swift\nupperStr = \"hello world\".upper()\nprintln(upperStr) //output : HELLO WORLD\n```\n\nstring could also be iterated:\n\n```swift\nfor idx, v in \"abcd\" {\n    printf(\"idx=%d, v=%s\\n\", idx, v)\n}\n\nfor v in \"Hello World\" {\n    printf(\"idx=%d, v=%s\\n\", $_, v) //$_ is the index\n}\n```\n\nYou could concatenate an object to a string:\n\n```swift\njoinedStr = \"Hello \" + \"World\"\njoinedStr += \"!\"\nprintln(joinedStr)\n```\n\nYou could also can use the builtin `reverse` function to reverse character of the string:\n\n```swift\nlet str = \"Hello world!\"\nprintln(\"Source Str =\", str)\nrevStr = reverse(str)\nprintln(\"Reverse str =\", revStr)\n```\n\nIf you hava a string, you want to convert it to number, you could add a \"+\" prefix before the string.\n\n```swift\na = +\"121314\" // a is an int\nprintln(a) // result: 121314\n\n// Integer also support \"0x\"(hex), \"0b\"(binary), \"0o\"(octal) prefix\na = +\"0x10\" // a is an int\nprintln(a) // result: 16\n\na = +\"121314.6789\" // a is a float\nprintln(a) // result: 121314.6789\n```\n\n### Hash\nIn monkey, the builtin hash will keep the order of keys when they are added to the hash, just like python's orderedDict.\n\nYou could use {} to initialize an empty hash:\n\n```swift\nemptyHash = {}\nemptyHash[\"key1\"] = \"value1\"\nprintln(emptyHash)\n```\n\nHash's key could be string, int, boolean:\n\n```swift\nhashObj = {\n    12     : \"twelve\",\n    true   : 1,\n    \"Name\" : \"HHF\"\n}\nprintln(hashObj)\n```\n\nNote: the last comma before the closing '}' is optional.\n\nYou could use '+' or '-' to add or remove an item from a hash:\n\n```swift\nhashObj += {\"key1\" : \"value1\"}\nhashObj += {\"key2\" : \"value2\"}\nhashObj += {5 : \"five\"}\nhashObj -= \"key2\"\nhashObj -= 5\nprintln(hash)\n```\n\nIn monkey, Hash is also an object, so you could use them to operate on hash object:\n\n```swift\n\nhashObj.push(15, \"fifteen\") //first parameter is the key, second is the value\nhashObj.pop(15)\n\nkeys = hashObj.keys()\nprintln(keys)\n\nvalues = hashObj.values()\nprintln(values)\n```\n\nYou could also use the builtin `reverse` function to reverse hash's key and value:\n\n```swift\nlet hs = {\"key1\": 12, \"key2\": \"HHF\", \"key3\": false}\nprintln(\"Source Hash =\", hs)\nrevHash = reverse(hs)\nprintln(\"Reverse Hash =\", revHash)\n```\n\n### Tuple\n\nIn monkey, `tuple` is just like array, but it could not be changed once it has been created.\n\nTuples are constructed using parenthesized list notation:\n\n```swift\n//Create an empty tuple\nlet t1 = tuple()\n\n//Same as above.\nlet t2 = ()\n\n// Create a one element tuple.\n// Note: the trailing comma is necessary to distinguish it from the\n//       parenthesized expression (1).\n// 1-tuples are seldom used.\nlet t3 = (1,)\n\n//Create a two elements tuple\nlet t4 = (2,3)\n```\n\nAny object may be converted to a tuple by using the built-in `tuple` function.\n\n```swift\nlet t = tuple(\"hello\")\nprintln(t)  // result: (\"hello\")\n```\n\nLike arrays, tuples are indexed sequences, so they may be indexed and sliced.\nThe index expression tuple[i] returns the tuple element at index i, and the slice\nexpression tuple[i:j] returns a subsequence of a tuple.\n\n```swift\nlet t = (1,2,3)[2]\nprint(t) // result:3\n```\n\nTuples are iterable sequences, so they may be used as the operand of a for-loop,\na list comprehension, or various built-in functions.\n\n```swift\n//for-loop\nfor i in (1,2,3) {\n    println(i)\n}\n\n//tuple comprehension\nlet t1 =  [x+1 for x in (2,4,6)]\nprintln(t1) //result: [3, 5, 7]. Note: Result is array, not tuple\n```\n\nUnlike arrays, tuples cannot be modified. However, the mutable elements of a tuple may be modified.\n\n```swift\narr1 = [1,2,3]\nt = (0, arr1, 5, 6)\nprintln(t)    // result: (0, [1, 2, 3], 5, 6)\narr1.push(4)\nprintln(t)    //result:  (0, [1, 2, 3, 4], 5, 6)\n```\n\nTuples are hashable (assuming their elements are hashable), so they may be used as keys of a hash.\n\n```swift\nkey1=(1,2,3)\nkey2=(2,3,4)\nlet ht = {key1 : 10, key2 : 20}\nprintln(ht[key1]) // result: 10\nprintln(ht[key2]) // result: 20\n\n//Below is not supported(will issue a syntax error):\nlet ht = {(1,2,3) : 10, (2,3,4) : 20} //error!\nprintln(ht[(1,2,3)])  //error!\nprintln(ht[(2,3,4)])  //error!\n```\n\nTuples may be concatenated using the + operator, it will create a new tuple.\n\n```swift\nlet t = (1, 2) + (3, 4)\nprintln(t) // result: (1, 2, 3, 4)\n```\n\nA tuple used in a Boolean context is considered true if it is non-empty.\n\n```swift\nlet t = (1,)\nif t {\n    println(\"t is not empty!\")\n} else {\n    println(\"t is empty!\")\n}\n\n//result : \"t is not empty!\"\n```\n\nTuple's json marshaling and unmarshaling will be treated as array:\n\n```swift\nlet tupleJson = (\"key1\",\"key2\")\nlet tupleStr = json.marshal(tupleJson)\n//Result:[\n//        \"key1\"，\n//        \"key2\"，\n//       ]\nprintln(json.indent(tupleStr, \"  \"))\n\nlet tupleJson1 = json.unmarshal(tupleStr)\nprintln(tupleJson1) //result: [\"key1\", \"key2\"]\n```\n\nTuple plus an array will return an new array, not a tuple\n\n```swift\nt2 = (1,2,3) + [4,5,6]\nprintln(t2) // result: [(1, 2, 3), 4, 5, 6]\n```\n\nYou could also use the builtin `reverse` function to reverse tuples's elements:\n\n```swift\nlet tp = (1,3,5,2,4,6,7,8,9)\nprintln(tp) //result: (1, 3, 5, 2, 4, 6, 7, 8, 9)\n\nrevTuple = reverse(tp)\nprintln(revTuple) //result: (9, 8, 7, 6, 4, 2, 5, 3, 1)\n```\n\n### class\n\nMonkey has limited support for the oop concept, below is a list of features:\n\n* inheritance and polymorphism\n* operator overloading\n* property(with getter or setter or both)\n* static member/method/property\n* indexer\n* class category\n* class annotations(limited support)\n* constructor method and normal methods support default value and variadic parameters\n\nThe monkey parser could parse `public`, `private`, `protected`, but it has no effect in the evaluation phase.\nThat means monkey do not support access modifiers at present.\n\nYou use `class` keyword to declare a class and use `new class(xxx)` to create an instance of a `class`.\n\n```swift\nclass Animal {\n    let name = \"\"\n    fn init(name) {    //'init' is the constructor\n        //do somthing\n    }\n}\n```\n\nIn monkey, all class is inherited from the root class `object`. \n`object` class include some common method like `toString()`, `instanceOf()`, `is_a()`, `classOf()`, `hashCode`.\n\nAbove code is same as:\n\n```swift\nclass Animal : object {\n    let name = \"\"\n    fn init(name) {    //'init' is the constructor\n        //do somthing\n    }\n}\n```\n\n#### inheritance and polymorphism\n\nYou can inherit a class using `:`:\n\n```swift\nclass Dog : Animal { //Dog inherits from Animal\n}\n```\n\nIn the child class, you can use the `parent` to access parent class's members or methods.\n\nplease see below for an example：\n\n```swift\nclass Animal {\n    let Name;\n\n    fn MakeNoise()\n    {\n        println(\"generic noise\")\n    }\n    fn ToString()\n    {\n        return \"oooooooo\"\n    }\n}\n\nclass Cat : Animal {\n    fn init(name)\n    {\n        this.Name = name\n    }\n\n    fn MakeNoise()\n    {\n        println(\"Meow\")\n    }\n\n    fn ToString()\n    {\n        return Name + \" cat\"\n    }\n}\n\nclass Dog : Animal {\n    fn init(name)\n    {\n        this.Name = name\n    }\n\n    fn MakeNoise()\n    {\n        println(\"Woof!\")\n    }\n\n    fn ToString()\n    {\n        return Name + \" dog\"\n    }\n\n    fn OnlyDogMethod()\n    {\n        println(\"secret dog only method\")\n    }\n}\n\n\ncat = new Cat(\"pearl\")\ndog = new Dog(\"cole\")\nrandomAnimal = new Animal()\n\nanimals = [cat, dog, randomAnimal]\n\nfor animal in animals\n{\n    println(\"Animal name: \" + animal.Name)\n    animal.MakeNoise()\n    println(animal.ToString())\n    if is_a(animal, \"Dog\") {\n        animal.OnlyDogMethod()\n    }\n}\n```\n\nThe result is:\n\n```\nAnimal name: pearl\nMeow\npearl cat\nAnimal name: cole\nWoof!\ncole dog\nsecret dog only method\nAnimal name: nil\ngeneric noise\noooooooo\n```\n\n#### operator overloading\n\n```swift\nclass Vector {\n    let x = 0;\n    let y = 0;\n\n    // constructor\n    fn init (a, b, c) {\n        if (!a) { a = 0;}\n        if (!b) {b = 0;}\n        x = a; y = b\n    }\n\n    fn +(v) { //overloading '+'\n        if (type(v) == \"INTEGER\" {\n            return new Vector(x + v, y + v);\n        } elseif v.is_a(Vector) {\n            return new Vector(x + v.x, y + v.y);\n        }\n        return nil;\n    }\n\n    fn String() {\n        return fmt.sprintf(\"(%v),(%v)\", this.x, this.y);\n    }\n}\n\nfn Vectormain() {\n    v1 = new Vector(1,2);\n    v2 = new Vector(4,5);\n    \n    // call + function in the vector object\n    v3 = v1 + v2 //same as 'v3 = v1.+(v2)'\n    // returns string \"(5),(7)\"\n    println(v3.String());\n    \n    v4 = v1 + 10 //same as v4 = v1.+(10);\n    //returns string \"(11),(12)\"\n    println(v4.String());\n}\n\nVectormain()\n```\n\n#### property(like c#)\n\n```swift\nclass Date {\n    let month = 7;  // Backing store\n    property Month\n    {\n        get { return month }\n        set {\n            if ((value \u003e 0) \u0026\u0026 (value \u003c 13))\n            {\n                month = value\n            } else {\n               println(\"BAD, month is invalid\")\n            }\n        }\n    }\n\n    property Year; // same as 'property Year { get; set;}'\n\n    property Day { get; }\n\n    property OtherInfo1 { get; }\n    property OtherInfo2 { set; }\n\n    fn init(year, month, day) {\n        this.Year = year\n        this.Month = month\n        this.Day = day\n    }\n\n    fn getDateInfo() {\n        printf(\"Year:%v, Month:%v, Day:%v\\n\", this.Year, this.Month, this.Day) //note here, you need to use 'this.Property', not 'Property'\n    }\n}\n\ndateObj = new Date(2000, 5, 11)\n//printf(\"Calling Date's getter, month=%d\\n\", dateObj.Month)\ndateObj.getDateInfo()\n\nprintln()\ndateObj.Month = 10\nprintf(\"dateObj.Month=%d\\n\", dateObj.Month)\n\ndateObj.Year = 2018\nprintln()\ndateObj.getDateInfo()\n\n//Below code will raise an execution error! Because OtherInfo1 is a READONLY property.\n//dateObj.OtherInfo1 = \"Other Date Info\"\n//println(dateObj.OtherInfo1)\n\n//Below code will raise an execution error! Because OtherInfo2 is a WRITEONLY property.\n//dateObj.OtherInfo2 = \"Other Date Info2\"\n//println(dateObj.OtherInfo2)\n\n//Below code will raise an execution error! Because Day is a READONLY property.\n//dateObj.Day = 18\n```\n\n#### indexer\n\nMonkey has support for class `indexer`(like c#). \nAn indexer is a member that enables an object to be indexed in the same way as an array.\n\nYou declare an Indexer using `property this[parameter]`.\n\n```swift\nproperty this[index] {\n    get { xxx }\n    set { xxx }\n}\n```\n\nPlease see the example code:\n\n```swift\nclass IndexedNames\n{\n    let namelist = []\n    let size = 10\n    fn init()\n    {\n        let i = 0\n        for (i = 0; i \u003c size; i++)\n        {\n            namelist[i] = \"N. A.\"\n        }\n    }\n\n    fn getNameList() {\n        println(namelist)\n    }\n\n    property this[index]\n    {\n        get\n        {\n            let tmp;\n            if ( index \u003e= 0 \u0026\u0026 index \u003c= size - 1 )\n            {\n               tmp = namelist[index]\n            }\n            else\n            {\n               tmp = \"\"\n            }\n     \n            return tmp\n         }\n         set\n         {\n             if ( index \u003e= 0 \u0026\u0026 index \u003c= size-1 )\n             {\n                 namelist[index] = value\n             }\n         }\n    }\n}\n\nfn Main()\n{\n    namesObj = new IndexedNames()\n\n    //Below code will call Indexer's setter function\n    namesObj[0] = \"Zara\"\n    namesObj[1] = \"Riz\"\n    namesObj[2] = \"Nuha\"\n    namesObj[3] = \"Asif\"\n    namesObj[4] = \"Davinder\"\n    namesObj[5] = \"Sunil\"\n    namesObj[6] = \"Rubic\"\n\n    namesObj.getNameList()\n\n    for (i = 0; i \u003c namesObj.size; i++)\n    {\n        println(namesObj[i]) //Calling Indexer's getter function\n    }\n}\n\nMain()\n```\n\n#### static members/methods/properties\n\n```swift\nclass Test\n{\n   static let x = 0;\n   static let y = 5;\n\n   static fn Main()\n   {\n      println(Test.x);\n      println(Test.y);\n\n      Test.x = 99;\n      println(Test.x);\n   }\n}\n\nTest.Main()\n```\n\nNote：Non-static variable/method/property could access static variable/method/property.\n      On the other hand, static variable/method/property cannot access Non-static variable/method/property.\n\n#### Class Category\n\nMonkey also support class Category like objective-c（C# is called 'extension methods'）.\n\n```swift\nclass Animal {\n    fn Walk() {\n        println(\"Animal Walk!\")\n    }\n}\n\n//Class category like objective-c\nclass Animal (Run) { //Create an 'Run' category of Animal class.\n    fn Run() {\n        println(\"Animal Run!\")\n        this.Walk() //can call Walk() method of Animal class.\n    }\n}\n\nanimal = new Animal()\nanimal.Walk()\n\nprintln()\nanimal.Run()\n```\n\n#### Annotations\n\nMonkey also has very simple annotation support like java：\n\n* Only mehotd and property of class can have annotations(not class itself, or other simple functions)\n* In the body of `Annotation` class, only support property, do not support methods.\n* When use annotations, you must create an object.\n\n\nYou could use `class @annotationName {}` to declare an annotation class.\nMonkey also include some builtin annotations:\n\n* @Override annotation(just like java's @Override).\n* @NotNull\n* @NotEmpty\n\nPlease see below example：\n\n```swift\n\n//Declare annotation class\n//Note: In the body, you must use property, not method.\nclass @MinMaxValidator {\n  property MinLength\n  property MaxLength default 10 //Same as 'property MaxLength = 10'\n}\n\n//This is a marker annotation\nclass @NoSpaceValidator {}\n\nclass @DepartmentValidator {\n  property Department\n}\n\n//The 'Request' class\nclass Request {\n  @MinMaxValidator(MinLength=1)\n  property FirstName; // getter and setter is implicit\n\n  @NoSpaceValidator\n  property LastName;\n\n  @DepartmentValidator(Department=[\"Department of Education\", \"Department of Labors\", \"Department of Justice\"])\n  property Dept;\n}\n\n//This class is responsible for processing the annotation.\nclass RequestHandler {\n  static fn handle(o) {\n    props = o.getProperties()\n    for p in props {\n      annos = p.getAnnotations()\n      for anno in annos {\n        if anno.instanceOf(MinMaxValidator) {\n          //p.value is the property real value.\n          if len(p.value) \u003e anno.MaxLength || len(p.value) \u003c anno.MinLength {\n            printf(\"Property '%s' is not valid!\\n\", p.name)\n          }\n        } elseif anno.instanceOf(NoSpaceValidator) {\n          for c in p.value {\n            if c == \" \" || c == \"\\t\" {\n              printf(\"Property '%s' is not valid!\\n\", p.name)\n              break\n            }\n          }\n        } elseif anno.instanceOf(DepartmentValidator) {\n          found = false\n          for d in anno.Department {\n            if p.value == d {\n              found = true\n            }\n          }\n          if !found {\n            printf(\"Property '%s' is not valid!\\n\", p.name)\n          }\n        }\n      }\n    }\n  }\n}\n\nclass RequestMain {\n  static fn main() {\n    request = new Request()\n    request.FirstName = \"Haifeng123456789\"\n    request.LastName = \"Huang     \"\n    request.Dept = \"Department of Justice\"\n    RequestHandler.handle(request)\n  }\n}\n\nRequestMain.main()\n```\n\nBelow is the result：\n\n```\nProperty 'FirstName' is not valid!\nProperty 'LastName' is not valid!\n```\n\n\n### Standard input/output/error\n\nThere are three predefined object for representing standard input, standard output, standard error.\nThey are `stdin`, `stdout`, `stderr`.\n\n```swift\nstdout.writeLine(\"Hello world\")\n//same as above\nfmt.fprintf(stdout, \"Hello world\\n\")\n\nprint(\"Please type your name:\")\nname = stdin.read(1024)  //read up to 1024 bytes from stdin\nprintln(\"Your name is \" + name)\n```\n\nYou can also using Insertion operator (`\u003c\u003c`) and Extraction operator(`\u003e\u003e`) just like c++ to operate stdin/stdout/stderr.\n\n```swift\n// Output to stdout by using insertion operator(\"\u003c\u003c\")\n// 'endl' is a predefined object, which is \"\\n\".\nstdout \u003c\u003c \"hello \" \u003c\u003c \"world!\" \u003c\u003c \" How are you?\" \u003c\u003c endl;\n\n// Read from stdin by using extraction operator(\"\u003e\u003e\")\nlet name;\nstdout \u003c\u003c \"Your name please: \";\nstdin \u003e\u003e name;\nprintf(\"Welcome, name=%v\\n\", name)\n```\n\nInsertion operator (`\u003c\u003c`) and Extraction operator(`\u003e\u003e`) can also be used for operating file object。\n\n```swift\n//Read file by using extraction operator(\"\u003e\u003e\")\ninfile = newFile(\"./file.demo\", \"r\")\nif (infile == nil) {\n    println(\"opening 'file.demo' for reading failed, error:\", infile.message())\n    os.exit(1)\n}\nlet line;\nlet num = 0\nwhile ( infile\u003e\u003eline != nil) {\n    num++\n    printf(\"%d\t%s\\n\", num, line)\n}\ninfile.close()\n\n\n//Writing to file by using inserttion operator(\"\u003c\u003c\")\noutfile = newFile(\"./outfile.demo\", \"w\")\nif (outfile == nil) {\n    println(\"opening 'outfile.demo' for writing failed, error:\", outfile.message())\n    os.exit(1)\n}\noutfile \u003c\u003c \"Hello\" \u003c\u003c endl\noutfile \u003c\u003c \"world\" \u003c\u003c endl\noutfile.close()\n```\n\n### Error Handling of standard library\n\nWhen a standard library function returns `nil` or `false`, you can use the return value's message() function for the error message:\n\n```swift\nfile = newFile(filename, \"r\")\nif (file == nil) {\n    println(\"opening \", filename, \"for reading failed, error:\", file.message())\n}\n//do something with the file\n\n//close the file\nfile.close()\n\n\nlet ret = http.listenAndServe(\"127.0.0.1:9090\")\nif (ret == false) {\n    println(\"listenAndServe failed, error:\", ret.message())\n}\n\n```\n\nMaybe you are curious about why `nil` or `false` have message() function? Because in monkey, `nil` and `false`\nboth are objects, so they have method to operate on it.\n\n### About `defer` keyword\n\nA defer statement defers the execution of a function until the surrounding function returns.\n\nThe deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.\n\n```swift\nlet add  =  fn(x,y){\n    defer println(\"I'm defer1\")\n    println(\"I'm in add\")\n    defer println(\"I'm defer2\")\n    return x + y\n}\nprintln(add(2,2))\n```\n\nThe result is as below:\n\n```sh\nI'm in add\nI'm defer2\nI'm defer1\n4\n```\n\n```swift\nfile = newFile(filename, \"r\")\nif (file == nil) {\n    println(\"opening \", filename, \"for reading failed, error:\", file.message())\n    return false\n}\ndefer file.close()\n//do other file related stuff, and not need to worry about the file close.\n//when any file operation error occurs, it will close the file before it returns.\n\n```\n\n### Concatenation of different types\n\nIn monkey, you could concatenate of different types. See below for examples:\n\n```swift\n// Number plus assignment\nnum = 10\nnum += 10 + 15.6\nnum += 20\nprintln(num)\n\n// String plus assignment\nstr = \"Hello \"\nstr += \"world! \"\nstr += [1, 2, 3]\nprintln(str)\n\n// Array plus assignment\narr = []\narr += 1\narr += 10.5\narr += [1, 2, 3]\narr += {\"key\": \"value\"}\nprintln(arr)\n\n// Array compare\narr1 = [1, 10.5, [1, 2, 3], {\"key\" : \"value\"}]\nprintln(arr1)\nif arr == arr1 { //support ARRAY compare\n    println(\"arr1 = arr\")\n} else {\n    println(\"arr1 != arr\")\n}\n\n// Hash assignment(\"+=\", \"-=\")\nhash = {}\nhash += {\"key1\" : \"value1\"}\nhash += {\"key2\" : \"value2\"}\nhash += {5 : \"five\"}\nprintln(hash)\nhash -= \"key2\"\nhash -= 5\nprintln(hash)\n```\n\n### Comprehensions\n\nMonkey support list(array,string, range, tuple) comprehensions.\nlist comprehension will return an array.\nplease see following examples:\n\n```swift\n//array comprehension\nx = [[word.upper(), word.lower(), word.title()] for word in [\"hello\", \"world\", \"good\", \"morning\"]]\nprintln(x) //result: [[\"HELLO\", \"hello\", \"Hello\"], [\"WORLD\", \"world\", \"World\"], [\"GOOD\", \"good\", \"Good\"], [\"MORNING\", \"morning\", \"Morning\"]]\n\n//string comprehension (here string is treated like an array)\ny = [ c.upper() for c in \"huanghaifeng\" where $_ % 2 != 0] //$_ is the index\nprintln(y) //result: [\"U\", \"N\", \"H\", \"I\", \"E\", \"G\"]\n\n//range comprehension\nw = [i + 1 for i in 2..10]\nprintln(w) //result: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n\n//tuple comprehension\nv = [x+1 for x in (12,34,56)]\nprintln(v) //result: [13, 35, 57]\n\n//hash comprehension\nz = [v * 10 for k,v in {\"key1\": 10, \"key2\": 20, \"key3\": 30}]\nprintln(z) //result: [100, 200, 300]\n```\n\nMonkey also support hash comprehension.\nhash comprehension will return a hash.\nplease see following examples:\n\n```swift\n//hash comprehension (from hash)\nz1 = { v:k for k,v in {\"key1\": 10, \"key2\": 20, \"key3\": 30}} //reverse key-value pair\nprintln(z1) // result: {10 : \"key1\", 20 : \"key2\", 30 : \"key3\"}. Order may differ\n\n//hash comprehension (from array)\nz2 = {x:x**2 for x in [1,2,3]}\nprintln(z2) // result: {1 : 1, 2 : 4, 3 : 9}. Order may differ\n\n//hash comprehension (from .. range)\nz3 = {x:x**2 for x in 5..7}\nprintln(z3) // result: {5 : 25, 6 : 36, 7 : 49}. Order may differ\n\n//hash comprehension (from string)\nz4 = {x:x.upper() for x in \"hi\"}\nprintln(z4) // result: {\"h\" : \"H\", \"i\" : \"I\"}. Order may differ\n\n//hash comprehension (from tuple)\nz5 = {x+1:x+2 for x in (1,2,3)}\nprintln(z5) // result: {4 : 5, 2 : 3, 3 : 4}. Order may differ\n```\n\n### grep and map\n\nThe `grep` and `map` operators are just like perl's `grep` and `map`.\n\nThe grep operator takes a list of values and a \"testing expression.\" For each item in the list of values,\nthe item is placed temporarily into the $_ variable, and the testing expression is evaluated. If the\nexpression results in a true value, the item is considered selected.\n\nThe map operator has a very similar syntax to the grep operator and shares a lot of the same operational steps.\nFor example, items from a list of values are temporarily placed into $_ one at a time. However,\nthe testing expression becomes a mapping expression.\n\n```swift\nlet sourceArr = [2,4,6,8,10,12]\n\nlet m = grep  $_ \u003e 5, sourceArr\nprintln('m is {m}')\n\nlet cp = map $_ * 2 , sourceArr\nprintln('cp is {cp}')\n\n//a little bit more complex example\nlet fields = {\n                \"animal\"   : \"dog\",\n                \"building\" : \"house\",\n                \"colour\"   : \"red\",\n                \"fruit\"    : \"apple\"\n             }\nlet pattern = `animal|fruit`\n// =~(match), !~(unmatch)\nlet values = map { fields[$_] } grep { $_ =~ pattern } fields.keys()\nprintln(values)\n```\n\n### Function\n\nFunction in monkey is a first-class object. This means the language supports passing functions as arguments to\nother functions, returning them as the values from other functions, and assigning them to variables or storing\nthem in data structures.\n\nFunction also could have default parameters and variadic parameters.\n\n```swift\n//define a function\nlet add = fn() { [5,6] }\nlet n = [1, 2] + [3, 4] + add()\nprintln(n)\n\n\nlet complex = {\n   \"add\" : fn(x, y) { return fn(z) {x + y + z } }, //function with closure\n   \"sub\" : fn(x, y) { x - y },\n   \"other\" : [1,2,3,4]\n}\nprintln(complex[\"add\"](1, 2)(3))\nprintln(complex[\"sub\"](10, 2))\nprintln(complex[\"other\"][2])\n\n\nlet warr = [1+1, 3, fn(x) { x + 1}(2),\"abc\",\"def\"]\nprintln(warr)\n\n\nprintln(\"\\nfor i in 5..1 where i \u003e 2 :\")\nfor i in fn(x){ x+1 }(4)..fn(x){ x+1 }(0) where i \u003e 2 {\n  if (i == 3) { continue }\n  println('i={i}')\n}\n\n\n// default parameter and variadic parameters\nadd = fn (x, y=5, z=7, args...) {\n    w = x + y + z\n    for i in args {\n        w += i\n    }\n    return w\n}\n\nw = add(2,3,4,5,6,7)\nprintln(w)\n```\n\nYou could also declare named function like below:\n\n```swift\nfn sub(x,y=2) {\n    return x - y\n}\nprintln(sub(10)) //output : 8\n```\n\nYou could also create a function using the `fat arrow` syntax:\n\n```swift\nlet x = () =\u003e 5 + 5\nprintln(x())  //result: 10\n\nlet y = (x) =\u003e x * 5\nprintln(y(2)) //result: 10\n\nlet z = (x,y) =\u003e x * y + 5\nprintln(z(3,4)) //result :17\n\n\nlet add = fn (x, factor) {\n  x + factor(x)\n}\nresult = add(5, (x) =\u003e x * 2)\nprintln(result)  //result : 15\n```\n\nIf the function has no parameter, then you could omit the parentheses. e.g.\n\n```swift\nprintln(\"hhf\".upper)  //result: \"HHF\"\n//Same as above\nprintln(\"hhf\".upper())\n```\n\nBefore ver5.0, Monkey do not support multiple return values, But there are many ways to do it.\n\nBelow suggest a way of doing it:\n\n```swift\nfn div(x, y) {\n    if y == 0 {\n        return [nil, \"y could not be zero\"]\n    }\n    return [x/y, \"\"]\n}\n\nret = div(10,5)\nif ret[1] != \"\" {\n    println(ret[1])\n} else {\n    println(ret[0])\n}\n```\n\nStarting from ver5.0, Monkey support multiple return values using 'let'.\nThe returned values are wrapped as a tuple.\n\n```swift\nfn testReturn(a, b, c, d=40) {\n    return a, b, c, d\n}\n\nlet (x, y, c, d) = testReturn(10, 20, 30)\n// let x, y, c, d = testReturn(10, 20, 30)  same as above\n\nprintf(\"x=%v, y=%v, c=%v, d=%v\\n\", x, y, c, d)\n//Result: x=10, y=20, c=30, d=40\n```\n\nNote：You must use `let` to support multiple return values, below statement will issue a compile error.\n\n```swift\n(x, y, c, d) = testReturn(10, 20, 30) // no 'let', compile error\nx, y, c, d = testReturn(10, 20, 30)   // no 'let', compile error\n```\n\n### Pipe Operator\n\nThe pipe operator, inspired by [Elixir](https://elixir-lang.org/).\nAnd thanks for the project [Aria](https://github.com/fadion/aria), I got the idea and some code from this project.\n\nSee below for examples:\n\n```swift\n# Test pipe operator(|\u003e)\nx = [\"hello\", \"world\"] |\u003e strings.join(\" \") |\u003e strings.upper() |\u003e strings.lower() |\u003e strings.title()\nprintf(\"x=\u003c%s\u003e\\n\", x)\n\nlet add = fn(x,y) { return x + y }\nlet pow = fn(x) { return x ** 2}\nlet subtract = fn(x) { return x - 1}\n\nlet mm = add(1,2) |\u003e pow() |\u003e subtract()\nprintf(\"mm=%d\\n\", mm)\n\n\"Hello %s!\\n\" |\u003e fmt.printf(\"world\")\n```\n\n### Spawn and channel\n\nYou can use `spawn` to create a new thread, and `chan` to communicate with the thread.\n\n```swift\nlet aChan = chan()\nspawn fn() {\n    let message = aChan.recv()\n    println('channel received message=\u003c{message}\u003e')\n}()\n\n//send message to thread\naChan.send(\"Hello Channel!\")\n```\nYou could use channel and spawn togeter to support lazy evaluation:\n\n```swift\n// XRange is an iterator over all the numbers from 0 to the limit.\nfn XRange(limit) {\n    ch = chan()\n    spawn fn() {\n        //for (i = 0; i \u003c= limit; i++)  // Warning: Never use this kind of for loop, or else you will get weird results.\n        for i in 0..limit {\n            ch.send(i)\n        }\n\n        // Ensure that at the end of the loop we close the channel!\n        ch.close()\n    }()\n    return ch\n}\n\nfor i in XRange(10) {\n    fmt.println(i)\n}\n```\n\n## Use `go` language modules\nMonkey has experimental support for working with `go` modules.\n\nIf you need to use `go`s language package fucntion, you first need to use `RegisterFunctions' or `RegisterVars` to\nregister `go` language functions or types into monkey language.\n\nBelow is an example of `main.go`(extracted):\n\n```swift\n// Because in monkey we already have built in module `fmt`, here we use `gfmt` for package name.\neval.RegisterFunctions(\"gfmt\", []interface{}{\n    fmt.Errorf,\n    fmt.Println, fmt.Print, fmt.Printf,\n    fmt.Fprint, fmt.Fprint, fmt.Fprintln, fmt.Fscan, fmt.Fscanf, fmt.Fscanln,\n    fmt.Scan, fmt.Scanf, fmt.Scanln,\n    fmt.Sscan, fmt.Sscanf, fmt.Sscanln,\n    fmt.Sprint, fmt.Sprintf, fmt.Sprintln,\n})\n\neval.RegisterFunctions(\"io/ioutil\", []interface{}{\n    ioutil.WriteFile, ioutil.ReadFile, ioutil.TempDir, ioutil.TempFile,\n    ioutil.ReadAll, ioutil.ReadDir, ioutil.NopCloser,\n})\n\neval.Eval(program, scope)\n```\nNow, in your monkey file, you could use it like below:\n\n```swift\ngfmt.Printf(\"Hello %s!\\n\", \"go function\");\n\n//Note Here: we use 'io_ioutil', not 'io/ioutil'.\nlet files, err = io_ioutil.ReadDir(\".\")\nif err != nil {\n    gfmt.Println(err)\n}\nfor file in files {\n    if file.Name() == \".git\" {\n        continue\n    }\n    gfmt.Printf(\"Name=%s, Size=%d\\n\", file.Name(), file.Size())\n}\n```\n\nFor more detailed examples, please see `goObj.my`.\n\n## Standard module introduction\n\nIn monkey, there are some standard modules provided for you. e.g. json, sql, sort, fmt, os, logger, time, flag, net, http, etc...\nThis is a brief introduction of some of the monkey standard modules, don't expect it to be thorough.\nIf you are curious, please see the source code.\n\n#### fmt module\n\n```swift\nlet i, f, b, s, aArr, aHash = 108, 25.383, true, \"Hello, world\",\n    [1, 2, 3, 4, \"a\", \"b\"],\n    { \"key1\" : 1, \"key2\" : 2, \"key3\" : \"abc\"}\n\n// Use '%v (value)' to print variable value, '%_' to print the variable's type\nfmt.printf(\"i=[%05d, %X], b=[%t], f=[%.5f], s=[%-15s], aArr=%v, aHash=%v\\n\", i, i, b, f, s, aArr, aHash)\nfmt.printf(\"i=[%_], b=[%t], f=[%f], aArr=%_, aHash=%_, s=[%s] \\n\", i, b, f, aArr, aHash, s)\n\nsp = fmt.sprintf(\"i=[%05d, %X], b=[%t], f=[%.5f], s=[%-15s]\\n\", i, i, b, f, s)\nfmt.printf(\"sp=%s\", sp)\n\nfmt.fprintf(stdout, \"Hello %s\\n\", \"world\")\n```\n\n#### time module\n\n```swift\nt1 = newTime()\nformat = t1.strftime(\"%F %R\")\nprintln(t1.toStr(format))\nEpoch = t1.toEpoch()\nprintln(Epoch)\n\nt2 = t1.fromEpoch(Epoch)\nprintln(t2.toStr(format))\n```\n\n#### logger module\n\n```swift\n#Log to stdout\nlog = newLogger(stdout, \"LOGGER-\", logger.LSTDFLAGS | logger.LMICROSECONDS)\n\nlog.printf(\"Hello, %s\\n\", \"logger\")\nfmt.printf(\"Logger: flags =\u003c%d\u003e, prefix=\u003c%s\u003e\\n\", log.flags(), log.prefix())\n\n#Log to file\nfile = newFile(\"./logger.log\", \"a+\")\nlog.setOutput(file)\nfor i in 1..5 {\n    log.printf(\"This is \u003c%d\u003e\\n\", i)\n}\nfile.close() //do not forget to close the file\n```\n\n#### flag module(for handling of command line options)\n\n```swift\nlet verV = flag.bool(\"version\", false, \"0.1\")\nlet ageV = flag.int(\"age\", 40, \"an int\")\nlet heightV = flag.float(\"height\", 120.5, \"a float\")\nlet nameV = flag.string(\"name\", \"HuangHaiFeng\", \"a string\")\nlet hobbiesV = flag.string(\"hobbies\", \"1,2,3\", \"a comma-delimited string\")\nflag.parse()\n\nprintln(\"verV = \", verV)\nprintln(\"ageV = \", ageV)\nprintln(\"heightV = \", heightV)\nprintln(\"nameV = \", nameV)\nprintln(\"hobbies = \", hobbiesV.split(\",\"))\n\nif (flag.isSet(\"age\")) {\n    println(\"age is set\")\n} else {\n    println(\"age is not set\")\n}\n```\n\n#### json module(for json marshal \u0026 unmarshal)\n\n```swift\nlet hsJson = {\"key1\" : 10,\n              \"key2\" : \"Hello Json %s %s Module\",\n              \"key3\" : 15.8912,\n              \"key4\" : [1,2,3.5, \"Hello\"],\n              \"key5\" : true,\n              \"key6\" : {\"subkey1\": 12, \"subkey2\": \"Json\"},\n              \"key7\" : fn(x,y){x+y}(1,2)\n}\nlet hashStr = json.marshal(hsJson) //same as `json.toJson(hsJson)`\nprintln(json.indent(hashStr, \"  \"))\n\nlet hsJson1 = json.unmarshal(hashStr)\nprintln(hsJson1)\n\n\nlet arrJson = [1,2.3,\"HHF\",[],{ \"key\" : 10, \"key1\" : 11}]\nlet arrStr = json.marshal(arrJson)\nprintln(json.indent(arrStr))\nlet arr1Json = json.unmarshal(arrStr)  //same as `json.fromJson(arrStr)`\nprintln(arr1Json)\n```\n\n#### net module\n\n```swift\n//A simple tcp client\nlet conn = dialTCP(\"tcp\", \"127.0.0.1:9090\")\nif (conn == nil) {\n    println(\"dailTCP failed, error:\", conn.message())\n    os.exit(1)\n}\n\nlet n = conn.write(\"Hello server, I'm client\")\nif (n == nil) {\n    println(\"conn write failed, error:\", n.message())\n    os.exit(1)\n}\n\nlet ret = conn.close()\nif (ret == false) {\n    println(\"Server close failed, error:\", ret.message())\n}\n\n//A simple tcp server\nlet ln = listenTCP(\"tcp\", \":9090\")\nfor {\n    let conn = ln.acceptTCP()\n    if (conn == nil) {\n        println(conn.message())\n    } else {\n        printf(\"Accepted client, Address=%s\\n\", conn.addr())\n    }\n    spawn fn(conn) { //spawn a thread to handle the connection\n        println(conn.read())\n    }(conn)\n\n} //end for\n\nlet ret = ln.close()\nif (ret == false) {\n    println(\"Server close failed, error:\", ret.message())\n}\n```\n\n#### linq module\n\nIn monkey, the `linq` module support seven types of object:\n\n* File object (create using `newFile` builtin function)\n* Csv reader object (created using `newCsvReader` builtin function)\n* String object\n* Array object\n* Tuple object\n* Hash object\n* Channel object (created using `chan` builtin function)\n\n```swift\nlet mm = [1,2,3,4,5,6,7,8,9,10]\nprintln('before mm={mm}')\n\nresult = linq.from(mm).where(fn(x) {\n    x % 2 == 0\n}).select(fn(x) {\n    x = x + 2\n}).toSlice()\nprintln('after result={result}')\n\nresult = linq.from(mm).where(fn(x) {\n    x % 2 == 0\n}).select(fn(x) {\n    x = x + 2\n}).last()\nprintln('after result={result}')\n\nlet sortArr = [1,2,3,4,5,6,7,8,9,10]\nresult = linq.from(sortArr).sort(fn(x,y){\n    return x \u003e y\n})\nprintln('[1,2,3,4,5,6,7,8,9,10] sort(x\u003ey)={result}')\n\nresult = linq.from(sortArr).sort(fn(x,y){\n    return x \u003c y\n})\nprintln('[1,2,3,4,5,6,7,8,9,10] sort(x\u003cy)={result}')\n\nthenByDescendingArr = [\n    {\"Owner\" : \"Google\",    \"Name\" : \"Chrome\"},\n    {\"Owner\" : \"Microsoft\", \"Name\" : \"Windows\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GMail\"},\n    {\"Owner\" : \"Microsoft\", \"Name\" : \"VisualStudio\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GMail\"},\n    {\"Owner\" : \"Microsoft\", \"Name\" : \"XBox\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GMail\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"AppEngine\"},\n    {\"Owner\" : \"Intel\",     \"Name\" : \"ParallelStudio\"},\n    {\"Owner\" : \"Intel\",     \"Name\" : \"VTune\"},\n    {\"Owner\" : \"Microsoft\", \"Name\" : \"Office\"},\n    {\"Owner\" : \"Intel\",     \"Name\" : \"Edison\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GMail\"},\n    {\"Owner\" : \"Microsoft\", \"Name\" : \"PowerShell\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GMail\"},\n    {\"Owner\" : \"Google\",    \"Name\" : \"GDrive\"}\n]\n\nresult = linq.from(thenByDescendingArr).orderBy(fn(x) {\n    return x[\"Owner\"]\n}).thenByDescending(fn(x){\n    return x[\"Name\"]\n}).toOrderedSlice()    //Note: You need to use toOrderedSlice\n\n//use json.indent() for formatting the output\nlet thenByDescendingArrStr = json.marshal(result)\nprintln(json.indent(thenByDescendingArrStr, \"  \"))\n\n//test 'selectManyByIndexed'\nprintln()\nlet selectManyByIndexedArr1 = [[1, 2, 3], [4, 5, 6, 7]]\nresult = linq.from(selectManyByIndexedArr1).selectManyByIndexed(\nfn(idx, x){\n    if idx == 0 { return linq.from([10, 20, 30]) }\n    return linq.from(x)\n}, fn(x,y){\n    return x + 1\n})\nprintln('[[1, 2, 3], [4, 5, 6, 7]] selectManyByIndexed() = {result}')\n\nlet selectManyByIndexedArr2 = [\"st\", \"ng\"]\nresult = linq.from(selectManyByIndexedArr2).selectManyByIndexed(\nfn(idx,x){\n    if idx == 0 { return linq.from(x + \"r\") }\n    return linq.from(\"i\" + x)\n},fn(x,y){\n    return x + \"_\"\n})\nprintln('[\"st\", \"ng\"] selectManyByIndexed() = {result}')\n```\n\n#### Linq for file\n\nNow, monkey has a powerful `linq for file` support. it can be used to operate\nfiles a little bit like awk. See below for example:\n\n```swift\n//test: linq for \"file\"\nfile = newFile(\"./examples/linqSample.csv\", \"r\") //open linqSample.csv file for reading\nresult = linq.from(file,\",\",fn(line){ //the second parameter is field separator, the third is a selector function\n    if line.trim().hasPrefix(\"#\") { //if line start '#'\n        return true // return 'true' means we ignore this line\n    } else {\n        return false\n    }\n}).where(fn(fields) {\n    //The 'fields' is an array of hashes, like below:\n    //  fields = [\n    //      {\"line\": LineNo1, \"nf\": line1's number of fields, 0: line1, 1: field1, 2: field2, ...},\n    //      {\"line\": LineNo2, \"nf\": line2's number of fields, 0: line2, 1: field1, 2: field2, ...}\n    //  ]\n\n    int(fields[1]) \u003e 300000 //only 1st Field's Value \u003e 300000\n}).sort(fn(field1,field2){\n    return int(field1[1]) \u003e int(field2[1]) //sort with first field(descending)\n}).select(fn(fields) {\n    fields[5]  //only output the fifth field\n})\nprintln(result)\nfile.close() //do not forget to close the file\n\n//another test: linq for \"file\"\nfile = newFile(\"./examples/linqSample.csv\", \"r\") //open linqSample.csv file for reading\nresult = linq.from(file,\",\",fn(line){ //the second parameter is field separator, the third is a selector function\n    if line.trim().hasPrefix(\"#\") { //if line start '#'\n        return true //return 'true' means we ignore this line\n    } else {\n        return false\n    }\n}).where(fn(fields) {\n    int(fields[1]) \u003e 300000 //only 1st Field's Value \u003e 300000\n}).sort(fn(field1,field2){\n    return int(field1[1]) \u003e int(field2[1]) //sort with first field(descending)\n}).selectMany(fn(fields) {\n    row = [[fields[0]]] //fields[0] is the whole line, we need two \"[]\"s, otherwise selectMany() will flatten the output.\n    linq.from(row)  //output the whole records\n})\nprintln(result)\nfile.close() //do not forget to close the file\n\n\n//test: linq for \"csv\"\nr = newCsvReader(\"./examples/test.csv\") //open test.csv file for reading\nr.setOptions({\"Comma\": \";\", \"Comment\": \"#\"})\nresult = linq.from(r).where(fn(x) {\n    //The 'x' is an array of hashes, like below:\n    //  x = [\n    //      {\"nf\" : line1's number of fields, 1: field1, 2: field2, ...},\n    //      {\"nf\" : line2's number of fields, 1: field1, 2: field2, ...}\n    //  ]\n    x[2] == \"Pike\"//only 2nd Field = \"Pike\"\n}).sort(fn(x,y){\n    return len(x[1]) \u003e len(y[1]) //sort with length of first field\n})\nprintln(result)\nr.close() //do not forget to close the reader\n```\n\n#### csv module\n\n```swift\n//test csv reader\nlet r = newCsvReader(\"./examples/test.csv\")\nif r == nil {\n    printf(\"newCsv returns err, message:%s\\n\", r.message())\n}\n\nr.setOptions({\"Comma\": \";\", \"Comment\": \"#\"})\n\nra = r.readAll()\nif (ra == nil) {\n    printf(\"readAll returns err, message:%s\\n\", ra.message())\n}\n\nfor line in ra {\n    println(line)\n    for record in line {\n        println(\"\t\", record)\n    }\n}\nr.close() //do not forget to close the reader\n\n//test csv writer\nlet ofile = newFile(\"./examples/demo.csv\", \"a+\")\nlet w = newCsvWriter(ofile)\nw.setOptions({\"Comma\": \"\t\"})\nw.write([\"1\", \"2\", \"3\"])\nw.writeAll([[\"4\", \"5\", \"6\"],[\"7\", \"8\", \"9\"],[\"10\", \"11\", \"12\"]])\nw.flush()\nofile.close() //do not forget to close the file\n```\n\n#### template module\n\nThe `template` module contains 'text' and 'html' template handling.\n\nUse `newText(...)` or `parseTextFiles(...)` to create a new 'text' template.\n\nUse `newHtml(...)` or `parseHtmlFiles(...)` to create a new 'html' template.\n\n```swift\narr = [\n    { \"key\" : \"key1\", \"value\" : \"value1\" },\n    { \"key\" : \"key2\", \"value\" : \"value2\" },\n    { \"key\" : \"key3\", \"value\" : \"value3\" }\n]\n\n//use parseTextFiles(), write to a string\ntemplate.parseTextFiles(\"./examples/looping.tmpl\").execute(resultValue, arr)\nprintln('{resultValue}')\n\n//use parseTextFiles(), write to a file\nfile = newFile(\"./examples/outTemplate.log\", \"a+\")\ntemplate.parseTextFiles(\"./examples/looping.tmpl\").execute(file, arr)\nfile.close() //do not to forget to close the file\n\n//use parse()\n//Note here: we need to use \"{{-\" and \"-}}\" to remove the newline from the output\ntemplate.newText(\"array\").parse(`Looping\n{{- range . }}\n        key={{ .key }}, value={{ .value -}}\n{{- end }}\n`).execute(resultValue, arr)\nprintln('{resultValue}')\n```\n\n#### sql module\n\nThe `sql` module provides a lower abstraction layer for working with database.\n\nIt should correctly handle database null values, though not throughly tested.\n\nFor testing `sql` module, you need to do following:\n\n1. Download sql driver source.\n\n2. Include the package in 'sql.go' like below:\n\n```go\n    _ \"github.com/mattn/go-sqlite3\"\n```\n\n3. Recompile monkey source.\n\nBelow is a complete source of the `examples/db.my`:\n\n```swift\nlet dbOp = fn() {\n    os.remove(\"./foo.db\") //delete `foo.db` file\n    let db = dbOpen(\"sqlite3\", \"./foo.db\")\n    if (db == nil) {\n        println(\"DB open failed, error:\", db.message())\n        return false\n    }\n    defer db.close()\n    let sqlStmt = `create table foo (id integer not null primary key, name text);delete from foo;`\n    let exec_ret = db.exec(sqlStmt)\n    if (exec_ret == nil) {\n        println(\"DB exec failed! error:\", exec_ret.message())\n        return false\n    }\n\n    let tx = db.begin()\n    if (tx == nil) {\n        println(\"db.Begin failed!, error:\", tx.message())\n        return false\n    }\n\n    let stmt = tx.prepare(`insert into foo(id, name) values(?, ?)`)\n    if (stmt == nil) {\n        println(\"tx.Prepare failed!, error:\", stmt.message())\n        return false\n    }\n\n    defer stmt.close()\n    let i = 0\n    for (i = 0; i \u003c 105; i++) {\n        let name = \"您好\" + i\n        if (i\u003e100) {\n            //insert `null` value. There are seven predefined values:INT_NULL,UINT_NULL,FLOAT_NULL,STRING_NULL,BOOL_NULL,TIME_NULL, DECIMAL_NULL.\n            let rs = stmt.exec(i, sql.STRING_NULL)\n        } else {\n            let rs = stmt.exec(i, name)\n        }\n\n        if (rs == nil) {\n            println(\"statement exec failed, error:\", rs.message())\n            return false\n        }\n    } //end for\n\n    tx.commit()\n\n    let id, name = 0, \"\"\n    let rows = db.query(\"select id, name from foo\")\n    if (rows == nil) {\n        println(\"db queue failed, error:\", rows.message())\n        return false\n    }\n    defer rows.close()\n    while (rows.next()) {\n        rows.scan(id, name)\n        if (name.valid()) { //check if it's `null`\n            println(id, \"|\", name)\n        } else {\n            println(id, \"|\", \"null\")\n        }\n    }\n    return true\n}\n\nlet ret = dbOp()\nif (ret == nil) {\n    os.exit(1)\n}\n\nos.exit()\n```\n\n## About regular expression\n\nIn monkey, regard to regular expression, you could use:\n\n* Regular expression literal\n* 'regexp' module\n* =\u0026#126; and !\u0026#126; operators(like perl's)\n\n```swift\n//Use regular expression literal( /pattern/.match(str) )\nlet regex = /\\d+\\t/.match(\"abc 123\tmnj\")\nif (regex) { println(\"regex matched using regular expression literal\") }\n\n//Use 'regexp' module\nif regexp.compile(`\\d+\\t`).match(\"abc 123\tmnj\") {\n    println(\"regex matched using 'regexp' module\")\n}\n\n//Use '=~'(str =~ pattern)\nif \"abc 123\tmnj\" =~ `\\d+\\t` {\n    println(\"regex matched using '=~'\")\n}else {\n    println(\"regex not matched using '=~'\")\n}\n\n```\n\n```sh\nNote: For detailed explanation of 'Regular Expression' pattern matching, you could see golang's regexp module for reference.\n```\n\n## Useful Utilities\n\nIncluded has some useful utilities like `formatter` and `highlighter`.\n\nThe formatter utility can format the monkey language.\nThe highlighter utility can highlight the monkey language to console or html.\n\nYou could also combine the two utilities:\n\n```sh\n./fmt xx.my | ./highlight  //output to console(console highlight not support windows)\n```\n\n## Document generator\n\nIncluded also has a tool(`mdoc`) for generating documentation in markdown format or html format\n\nThe tool only support below statement for document generator:\n\n* let statement\n* enum statement\n* function statement\n* class statement\n  * let statement\n  * function statement\n  * property statement\n\n```sh\n//generate markdown file, the generated file is named 'doc.md'\n./mdoc examples/doc.my\n\n//generate html file, the generated file is named 'doc.html'\n./mdoc -html examples/doc.my\n\n//generate html file, also generate source code of classes and functions. the generated file is named 'doc.html'\n./mdoc -html -showsource examples/doc.my\n\n//Use the some builtin css types for styling the generated html\n//    0 - GitHub\n//    1 - Zenburn\n//    2 - Lake\n//    3 - Sea Side\n//    4 - Kimbie Light\n//    5 - Light Blue\n//    6 - Atom Dark\n//    7 - Forgotten Light\n\n./mdoc -html -showsource -css 1 examples/doc.my\n\n//Using external css file for styling the generated html file.\n//The '-cssfile' option has higher priority than the '-css' option.\n//If the supplied css file does not exists, then the '-css' option will be used.\n./mdoc -html -showsource -css 1 -cssfile ./examples/github-markdown.css examples/doc.my\n\n//processing all the '.my' files in examples directory, generate html.\n./mdoc -html examples\n```\nThe generating of HTML document is base on github REST API，so you must have network connection to make it work.\nYou may also need to set proxy if you behind a firewall(Environ variable:HTTP_PROXY).\n\nLook at below to see how documentation generated by mdoc looks like.\n\n* [markdown.md](examples/markdown.md)\n* [markdown.html](examples/markdown.html)\n\nBecause github can not render html directly, you could use(http://htmlpreview.github.io/) to review the generated html.\n\n## Syntax Highlight\n\nCurrently there are below kinds of syntax highlight for editors:\n\n1. vim\n\n    [vim](misc/vim)\n\n2. emeditor\n\n    [emeditor](misc/emeditor)\n\n3. notepad++\n\n    [notepad++](misc/notepad%2B%2B)\n\n4. Visual Studio Code\n\n    [VSC](misc/vscode)\n\n5. Sublime Text 3\n\n    [Sublime Text 3](misc/SublimeText3)\n\n## Future Plans\n\nThere are some other things i plan to do:\n\n* Improve the Standard Library with more functions.\n* Write more tests!\n* Improve this document with more explanation of the language.\n* Rewrite the demo program for better understanding of the language.\n* Rewrite the 'include' module logic.\n* ~~Add support for if-elseif-else expression~~.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaifenghuang%2Fmonkey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhaifenghuang%2Fmonkey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaifenghuang%2Fmonkey/lists"}