Monkey Programming Language (v4.0 release)
The new features are as follows
- Add “unsigned Int “base datatype (
let u = 10u
) - Added simple object oriented (class) support
- Inheritance and polymorphism
- Indexer (supports multiple indexers and indexers support multiple parameters)
- Operator overloading
- Properties (getters and setters)
- Static methods/members/properties
- Enhanced REPL(REPL supports real-time syntax highlighting)
- If the function has no arguments, the parentheses can be omitted when the function is called
- Add arbitrary precision floating point operation type (Decimal)
- Fixed bug where return statements do not work in loops
The home page
monkey
An overview of the
Monkey is a parser written in the GO language. Syntax borrowed from C, Ruby, Python, Perl, and C#. Support common control flow, functional programming and object-oriented programming. It also includes a REPL with real-time syntax highlighting.
Here is an example program using the Monkey language:
// A pseudo-class using function with closure
fn Person(name, age) {
self = {} // Create an empty hash, you can also use 'self = hash()'
self.name = name
self.age = age
// You can also use 'self.getName = fn() {return self.name}'
self.getName = () -> return self.name
self.getAge = () -> return self.age
self.message = () -> return self.name + ", aged " + str(self.age)
self.sets = fn(newName, newAge) {
self.name = newName
self.age = newAge
}
return self
}
p = Person("Mike".40)
printf("1 - info = %v\n", p.message())
printf("1 - name = %v\n", p.getName())
printf("1 - age = %v\n", p.getAge())
printf("\n=========================\n\n")
p.sets("HHF".42)
printf("2 - info = %v\n", p.message())
printf("2 - name = %v\n", p.getName())
printf("2 - age = %v\n", p.getAge())
Copy the code
The overview
This project is based on the Mayoms project Monkey, which fixes some of the bugs and adds many language features:
- To change the
string
Module (can handle UTF8 character encodings correctly) - To modify the
file
Module (contains some new methods). - increased
math
The module - increased
sql(db)
Module (can handle correctlynull
Value) - increased
time
The module - increased
sort
The module - increased
os
The module - increased
log
The module - increased
net
The module - increased
http
The module - increased
filepath
The module - increased
flag
Module (for processing command-line arguments) - increased
json
Modules (JSON serialization and deserialization) - increased
fmt
The module - increased
sync
The module - increased
list
The module - increased
linq
Module (code fromlinqAnd changed accordingly) - increased
csv
The module - increased
template
The module - increased
decimal
Module (code fromdecimalAnd made minor changes accordingly) - Regular expression support (partially similar to Perl)
- Channels (Channels based on the GO language)
- More operator support (&&, | |, &, |, ^, + =, – =? , etc.)
- Utf8 support (for example, you can use utF8 characters as variable names)
- More flow control support (e.g., try/catch/finally, for-in, case-in, c-like for loops)
- Defer to support
- Spawn support (goroutine)
- Enum support and
- Pipe operator support
- Functions that support variable and default arguments
- Support for List Comprehension and Hash Comprehension
- Simple object Oriented (OOP) support
The main objectives of this project are as follows:
- Teach yourself go
- Understand how the parser works
However, the speed of the parser is not a factor in this project
The installation
Download this project and run. /run.sh
Basic usage
You can use the REPL as follows:
~ » monkey
Monkey programming language REPL
>>
Copy the code
REPL
Or run a monkey file:
monkey path/to/file
Copy the code
Language tour
annotation
Monkey only supports single-line comments.
// an inline comment
# another inline comment
Copy the code
The data type
Monkey supports nine basic types: String, Int, UInt, Float, Bool, Array, Hash, Tuple, and Nil
s1 = "Hello, huang" # strings are UTF-8 encoded
s2 = `hello, "world"` # raw string
i = 10 # int
u = 10u # uint
f = 10.0 # float
b = true # bool
a = [1."2"] # array
h = { "a"= >1."b"= >2} # hash
t = (1.2.3) # tuple
n = nil
Copy the code
Constants (literals)
There are 11 main types of constants (literals) in Monkey.
- Integer
- UInteger
- Float
- String
- Regular expression
- Array
- Hash
- Tuple
- Nil
- Boolean
- Function
// Integer literals
i1 = 10
i2 = 20 _000_000
i3 = 0x80 // hex
i4 = 0b10101 // binary
i5 = 0c127 // octal
// Unsigned Integer literals
ui1 = 10u
ui2 = 20_000_000u //for more readable
ui3 = 0x80u // hex
ui4 = 0b10101u // binary
ui5 = 0c127u // octal
// Float literals
f1 = 10.25
f2 = 1.02 e3
f3 = 123 _456. 789 _012
// String literals
s1 = "123"
s2 = "Hello world"
// Regular expression literals
r = /\d+/.match("12")
if (r) { prinln("regex matched!")}// Array literals
a = [1+2.3.4."5".3]
// Hash literals
h = { "a"= >1."b"= >2."c"= >2}
//Tuple literals
t = (1.2+3."Hello".5)
// Nil literal
n = nil
// Boolean literals
t = true
f = false
// Function literals
let f1 = add(x, y) { return x + y }
println(f1(1.2))
//short-arrow function literals
let f2 = (x, y) -> x + y
println(f2(1.2))
Copy the code
variable
You can declare a variable using let, or you can declare and assign a variable directly using assignment :variable=value.
let a, b, c = 1."hello world"[1.2.3]
d = 4
e = 5Name ="Yellow"
Copy the code
If you do not use let to assign to variables, you will not be able to use multivariable assignment. The following statement is incorrect:
// Error, multivariable assignment must use let keyword
a, b, c = 1."hello world"[1.2.3]
Copy the code
Reserved words
Here are the reserved words for the Monkey language:
- fn
- let
- true false nil
- if elsif elseif elif else
- unless
- return
- include
- and or
- enum
- Struct # reserved
- do while for break continue where
- grep map
- case is in
- try catch finally throw
- defer
- spawn
- yield #not used
- qw
- class new property set get static
- Interface public private protected #
Type conversion
You can convert between different types using the built-in methods: int(), uint(), float(), STR (), array(), tuple, hash, decimal.
let i = 0xa
let u = uint(i) // result: 10
let s = str(i) // result: "10"
let f = float(i) // result: 10
let a = array(i) // result: [10]
let t = tuple(i) // result: (10,)
let h = hash(("key"."value")) // result: {"key"=>"value}
let d = decimal("123.45634567") / / result: 123.45634567
Copy the code
You can create a tuple from an array:
let t = tuple([10.20]) / / the result: (10, 20)
Copy the code
Similarly, you can create an array from a tuple:
let arr = array((10.20)) / / the result: [10, 20]
Copy the code
You can only create a hash from arrays or tuples:
// Create an empty hash
let h1 = hash() //same as h1 = {}
// Create hash from array
let h1 = hash([10.20]) //result: {10 => 20}
let h2 = hash([10.20.30]) //result: {10 => 20, 30 => nil}
// Create hash from tuple
let h3 = hash((10.20)) //result: {10 => 20}
let h4 = hash((10.20.30)) //result: {10 => 20, 30 => nil}
Copy the code
qw
(Quote word) keyword
The qW keyword is similar to the Perl QW keyword. Qw is a great help when you want to use a lot of double-quoted strings.
for str in qw<abc, def, ghi, jkl, mno> { // Pair operators allowed: '{}', '<>', '()'
println('str={str}')
}
newArr = qw(1.2.3.5) // newArr is a string array, not an integer array.
fmt.printf("newArr=%v\n", newArr)
Copy the code
enum
The keyword
In Mokey, you can use enum to define constants.
LogOption = enum {
Ldate = 1 << 0.Ltime = 1 << 1.Lmicroseconds = 1 << 2.Llongfile = 1 << 3.Lshortfile = 1 << 4.LUTC = 1 << 5.LstdFlags = 1 << 4 | 1 << 5
}
opt = LogOption.LstdFlags
println(opt)
// Get all names of 'enum'
for s in LogOption.getNames() { // Non-ordered
println(s)
}
// Get all values of 'enum'
for s in LogOption.getValues() { // Non-ordered
println(s)
}
// Get a specific name for 'enum'
println(LogOption.getName(LogOption.Lshortfile))
Copy the code
Control process
- if/if-else/if-elif-else/if-elsif-else/if-elseif-else
- unless/unless-else
- for/for-in
- while
- do
- try-catch-finally
- case-in/case-is
// if-else
let a, b = 10.5
if (a > b) { // '()' is optional, but '{}' is required
println("a > b")
}
elseif a == b { // 'elsif', 'elseif' and 'elif' can also be used
println("a = b")}else {
println("a < b")}//unless-else
unless b > a {
println("a >= b")}else {
println("b > a")}// for
i = 9
for { // Infinite loop
i = i + 2
if (i > 20) { break }
println('i = {i}')
}
i = 0
for (i = 0; i < 5; i++) { // Similar to the C for loop, '()' must have
if (i > 4) { break }
if (i == 2) { continue }
println('i is {i}')
}
for i in range(10) {
println('i = {i}')
}
a = [1.2.3.4]
for i in a where i % 2! =0 {
println(i)
}
hs = {"a"= >1."b"= >2."c"= >3."d"= >4."e"= >5."f"= >6."g"= >7}
for k, v in hs where v % 2= =0 {
println('{k} : {v}')
}
for i in 1.5 {
println('i={i}')
}
for item in 10.20 where $_ % 2= =0 { // $_ is the index
printf("idx=%d, item=%d\n", $_, item)
}
for c in "m"."a" {
println('c= {c}')
}
for idx, v in "abcd" {
printf("idx=%d, v=%s\n", idx, v)
}
for idx, v in ["a"."b"."c"."d"] {
printf("idx=%d, v=%s\n", idx, v)
}
for item in ["a"."b"."c"."d"] where $_ % 2= =0 { // $_ is the index
printf("idx=%d, item=%s\n", $_, v)
}
// The for loop is an expression, not a statement, so it can be assigned to a variable
let plus_one = for i in [1.2.3.4] { i + 1 }
fmt.println(plus_one)
// while
i = 10
while (i>3) {
i--
println('i={i}')
}
// do
i = 10
do {
i--
if (i==3) { break}}// try-catch-finally(throw a string variable only)
let exceptStr = "SUMERROR"
try {
let th = 1 + 2
if (th == 3) { throw exceptStr }
}
catch "OTHERERROR" {
println("Catched OTHERERROR")}catch exceptStr {
println("Catched is SUMERROR")}catch {
println("Catched ALL")
}
finally {
println("finally running")}// case-in/case-is
let testStr = "123"
case testStr in { // in(complete or partial match), is(complete match)
"abc"."mno" { println("testStr is 'abc' or 'mno'")}"def" { println("testStr is 'def'") }
`\d+` { println("testStr contains digit")}else { println("testStr not matched")}}let i = [{"a"= >1."b"= >2}, 10]
let x = [{"a"= >1."b"= >2},10]
case i in {
1.2 { println("i matched 1, 2")}3 { println("i matched 3") }
x { println("i matched x")}else { println("i not matched anything")}}Copy the code
Integer (Integer)
In Monkey, an integer is also an object. Therefore, you can call methods on this object. Look at the following example:
x = (-1).next()
println(x) / / 0
x = -1.next() //equals 'x = -(1.next())
println(x) / / - 2
x = (-1).prev()
println(x) / / - 2
x = -1.prev() //equals 'x = -(1.prev())
println(x) / / 0
x = [i for i in 10.upto(15)]
println(x) //[10, 11, 12, 13, 14, 15]
for i in 10.downto(5) {
print(i, "") // 5
}
println(a)if 10.isEven() {
println("10 is even")}if 9.isOdd() {
println("9 is odd")}Copy the code
Floating point (Float)
In Monkey, floating point is also an object. Therefore, you can call methods on this object. Look at the following example:
f0 = 15.20
println(f0)
f1 = 15.20.ceil()
println(f1)
f2 = 15.20.floor()
println(f2)
Copy the code
A Decimal type
In the Monkey, the Decimal type represents a fixed-point Decimal number with Arbitrary precision. This type of code is primarily based on Decimal.
Look at the following example:
d1 = decimal.fromString("123.45678901234567") // Creates a Decimal type from a string
d2 = decimal.fromFloat(3) // Create a Decimal type from a floating-point type
// Set the division precision.
// Note: This operation affects all subsequent operations on Decimal type
decimal.setDivisionPrecision(50)
fmt.println("123.45678901234567/3 =", d1.div(d2)) / / print d1 / d2
fmt.println(d1.div(d2)) // same as above
fmt.println(decimal.fromString("123.456").trunc(2))
// Converts the string to decimal
d3=decimal("123.45678901234567")
fmt.println(d3)
fmt.println("123.45678901234567/3 =", d3.div(d2))
Copy the code
Array (Array)
In Monkey, you can initialize an empty array with [] :
emptyArr = []
emptyArr[3] = 3 // The capacity will be automatically expanded
println(emptyArr)
Copy the code
Arrays can contain elements of any data type.
mixedArr = [1.2.5."Hello"["Another"."Array"] and {"Name"= >"HHF"."SEX"= >"Male"}]
Copy the code
Note: The comma (‘, ‘) before closing the square bracket (‘]’) can be omitted.
You can use indexes to access array elements.
println('mixedArr[2]={mixedArr[2]} ')println(["a"."b"."c"."d"] [2])
Copy the code
Because an array is an object, you can manipulate it using object methods.
if ([].empty()) {
println("array is empty")
}
emptyArr.push("Hello")
println(emptyArr)
// You can add an element to the array using 'add (+=)' :
emptyArr += 2
println(emptyArr)
Copy the code
You can use a for loop to iterate over an array.
numArr = [1.3.5.2.4.6.7.8.9]
for item in numArr where item % 2= =0 {
println(item)
}
let strArr = ["1"."a5"."5"."5b"."4"."cc"."Seven"."dd"."9"]
for item in strArr where /^\d+/.match(item) {
println(item)
}
for item in ["a"."b"."c"."d"] where $_ % 2= =0 { / / $_ is indexed
printf("idx=%d, v=%s\n", $_, item)
}
Copy the code
You can use the built-in function reverse to reverse array elements:
let arr = [1.3.5.2.4.6.7.8.9]
println("Source Array =", arr)
revArr = reverse(arr)
println("Reverse Array =", revArr)
Copy the code
String (String)
There are three types of strings in monkey:
- Native string (can contain
\n
) - Double quoted string (not included
\n
) - Single quoted strings (parsed strings)
A native string is a sequence of characters. Use backquotes (‘ ‘). In native strings, you can use any character except backquotes.
Look at the following example:
normalStr = "Hello " + "world!"
println(normalStr)
println("123456"[2])
rawStr = `Welcometo visit us! `println(rawStr)
// You can use single quotes when you want a variable to be parsed in a string.
// The string to be parsed is placed in curly braces ('{}') :
str = "Hello world"
println('str={str}') // Output: "Hello world"
str[6] ="W"
println('str={str}') // Output: "Hello World"
Copy the code
In monkey, strings are encoded at UTF8, which means you can use utF8-encoded characters as variable names:
3 =35 =5
println(三 + 五) // Output: 8
Copy the code
Strings are also objects, and you can manipulate strings using methods in the Strings module:
upperStr = "hello world".upper()
println(upperStr) // Output: HELLO WORLD
Copy the code
Strings can also be traversed:
for idx, v in "abcd" {
printf("idx=%d, v=%s\n", idx, v)
}
for v in "Hello World" {
printf("idx=%d, v=%s\n", $_, v) / / $_ is indexed
}
Copy the code
You can concatenate an object to a string:
joinedStr = "Hello " + "World"
joinedStr += "!"
println(joinedStr)
Copy the code
You can also use the built-in function reverse to reverse a string:
let str = "Hello world!"
println("Source Str =", str)
revStr = reverse(str)
println("Reverse str =", revStr)
Copy the code
Hash (Hash)
In monkey, use {} to create an empty hash:
emptyHash = {}
emptyHash["key1"] = "value1"
println(emptyHash)
Copy the code
A hash key can be a string, an int, or a Boolean:
hashObj = {
12= >"twelve".true= >1."Name"= >"HHF"
}
println(hashObj)
Copy the code
Note: The comma (‘, ‘) before closing the curly bracket (‘}’) can be omitted.
You can also use ‘+’ or ‘-‘ to add or remove an element from a hash:
hashObj += {"key1"= >"value1"}
hashObj += {"key2"= >"value2"}
hashObj += {5= >"five"}
hashObj -= "key2"
hashObj -= 5
println(hash)
Copy the code
A hash is also an object, and you can manipulate it using methods in the Hash module:
hashObj.push(15."fifteen") // The first argument is the key, and the second argument is the value
hashObj.pop(15)
keys = hashObj.keys()
println(keys)
values = hashObj.values()
println(values)
Copy the code
You can also use the built-in reverse function to reverse the hash key and value:
let hs = {"key1"= >12."key2"= >"HHF"."key3"= >false}
println("Source Hash =", hs)
revHash = reverse(hs)
println("Reverse Hash =", revHash)
Copy the code
Tuples (a Tuple)
In Monkey, tuples are very similar to arrays, but once you create a meta-ancestor, you can’t change it.
Tuples are created using parentheses:
// Create an empty ancestor
let t1 = tuple()
// same as above
let t2 = ()
// Create a progenitor with only one element.
// Note: the trailing "," is required, otherwise it will be resolved to (1) instead of the ancestor
let t3 = (1.)// Create a meta-ancestor with two elements
let t4 = (2.3)
Copy the code
You can use the built-in function tuple to render any type of object as a primitive.
let t = tuple("hello")
println(t) // Result: ("hello")
Copy the code
Like arrays, primitives can be indexed, or sliced. The index expression tuple[I] returns the primitive element at the ith index position, and the slice expression tuple[I :j] returns a child primitive.
let t = (1.2.3) [2]
print(t) // result:3
Copy the code
Primitives can also be iterated over (like arrays), so primitives can be used in for loops and list derivations.
/ / a for loop
for i in (1.2.3) {
println(i)
}
// Comprehension
let t1 = [x+1 for x in (2.4.6)]
println(t1) //result: [3, 5, 7]
Copy the code
Unlike arrays, primitives cannot be modified. But the mutable elements inside a meta-ancestor can be modified.
arr1 = [1.2.3]
t = (0, arr1, 5.6)
println(t) // Result: (0, [1, 2, 3], 5, 6)
arr1.push(4)
println(t) // Result: (0, [1, 2, 3, 4], 5, 6)
Copy the code
The ancestor can also be used as a key in a hash.
key1=(1.2.3)
key2=(2.3.4)
let ht = {key1 => 10, key2 =>20}
println(ht[key1]) // result: 10
println(ht[key2]) // result: 20
Copy the code
A meta-ancestor can be connected using +, which creates a new meta-ancestor.
let t = (1.2) + (3.4)
println(t) // Result: 1, 2, 3, 4
Copy the code
If a primitive is used in a Boolean environment, then true is returned if the number of elements in the primitive is greater than zero.
let t = (1.)if t {
println("t is not empty!")}else {
println("t is empty!")}// Result: "t is not empty!"
Copy the code
Json serialization (deserialization) of primitives results in arrays, not primitives
let tupleJson = ("key1"."key2")
let tupleStr = json.marshal(tupleJson)
// Result: [
/ / "key1",
/ / "key2",
/ /]
println(json.indent(tupleStr, ""))
let tupleJson1 = json.unmarshal(tupleStr)
println(tupleJson1) // Result: ["key1", "key2"]
Copy the code
Adding a primitive to an array returns an array, not a primitive.
t2 = (1.2.3) + [4.5.6]
println(t2) // Result: [(1, 2, 3), 4, 5, 6]
Copy the code
You can also use the built-in function reverse to reverse elements in a primitive:
let tp = (1.3.5.2.4.6.7.8.9)
println(tp) Result: (1, 3, 5, 2, 4, 6, 7, 8, 9)
revTuple = reverse(tp)
println(revTuple) // Result: (9, 8, 7, 6, 4, 2, 5, 3, 1)
Copy the code
class
Monkey supports simple object-oriented programming, and the features Mokey supports are listed below:
- Inheritance and polymorphism
- Operator overloading
- Properties (getters and setters)
- Static variables/methods/properties
- The indexer
The Monkey parser correctly handles the keywords public, private, and protected, but the interpreter (evaluator) ignores these. That said, monkey does not support access restrictions for now.
You can declare a class using the class keyword and create an instance of the class using new Class(XXX).
class Animal {
let name = ""
fn init(naem) { //'init' is the constructor
//do somthing}}Copy the code
In the Monkey, all classes inherit from the object root class. The Object root class contains several methods common to all classes. Examples include toString(), instanceOf(), is_a(), classOf(), hashCode. InstanceOf () is equivalent to is_a()
The following code is equivalent to the above:
class Animal : object {
let name = ""
fn init(naem) { //'init' is the constructor
//do somthing}}Copy the code
Inheritance and polymorphism
You use: to indicate inheritance:
class Dog : Animal { //Dog is descended from Animal
}
Copy the code
In subclasses, you can use parent to access the methods and fields of the base class.
Look at the following example:
class Animal {
let Name;
fn MakeNoise()
{
println("generic noise")
}
fn ToString()
{
return "oooooooo"}}class Cat : Animal {
fn init(name)
{
this.Name = name
}
fn MakeNoise()
{
println("Meow")
}
fn ToString()
{
return Name + " cat"}}class Dog : Animal {
fn init(name)
{
this.Name = name
}
fn MakeNoise()
{
println("Woof!")
}
fn ToString()
{
return Name + " dog"
}
fn OnlyDogMethod()
{
println("secret dog only method")
}
}
cat = new Cat("pearl")
dog = new Dog("cole")
randomAnimal = new Animal()
animals = [cat, dog, randomAnimal]
for animal in animals
{
println("Animal name: " + animal.Name)
animal.MakeNoise(a)println(animal.ToString())
if is_a(animal, "Dog") {
animal.OnlyDogMethod()}}Copy the code
The running results are as follows:
Animal name: pearl
Meow
pearl cat
Animal name: cole
Woof!
cole dog
secret dog only method
Animal name: nil
generic noise
oooooooo
Copy the code
Operator overloading
class Vector {
let x = 0;
let y = 0;
// constructor
fn init (a, b, c) {
if(! a) { a =0; }if(! b) {b =0; } x = a; y = b } fn +(v) {/ / reloading the '+'
if (type(v) == "INTEGER" {
return new Vector(x + v, y + v);
} elseif v.is_a(Vector) {
return new Vector(x + v.x, y + v.y);
}
return nil;
}
fn String() {
return fmt.sprintf("(%v),(%v)", this.x, this.y);
}
}
fn Vectormain() {
v1 = new Vector(1.2);
v2 = new Vector(4.5);
// The following code calls the '+' method on the Vector
v3 = v1 + v2 V3 = v1.+(v2)
/ / return "(5), (7)"
println(v3.String());
v4 = v1 + 10 // equivalent to v4 = v1.+(10);
/ / return "(11), (12)"
println(v4.String());
}
Vectormain(a)Copy the code
Properties (similar to C#)
class Date {
let month = 7; // Backing store
property Month
{
get { return month }
set {
if ((value > 0) && (value < 13))
{
month = value
} else {
println("BAD, month is invalid")
}
}
}
property Year { get; set; } propertyDay { get; }
property OtherInfo1 { get; }
property OtherInfo2 { set; }
fn init(year, month, day) {
this.Year = year
this.Month = month
this.Day = day
}
fn getDateInfo() {
printf("Year:%v, Month:%v, Day=%v\n", this.Year, this.Month, this.Day) //note here, you need to use 'this.Property', not 'Property'
}
}
dateObj = new Date(2000.5.11)
//printf("Calling Date's getter, month=%d\n", dateObj.Month)
dateObj.getDateInfo()
println()
dateObj.Month = 10
printf("dateObj.Month=%d\n", dateObj.Month)
dateObj.Year = 2018
println()
dateObj.getDateInfo()
// The following code will report an error because OtherInfo1 is read-only
//dateObj.OtherInfo1 = "Other Date Info"
//println(dateObj.OtherInfo1)
// The following code will report an error because OtherInfo2 is a write-only attribute
//dateObj.OtherInfo2 = "Other Date Info2"
//println(dateObj.OtherInfo2)
// The following code will report an error because the Day attribute is read-only
//dateObj.Day = 18
Copy the code
Monkey also supports an Indexer like C#. Indexers allow you to access objects as if they were arrays.
Indexers are declared as follows:
Declare an indexer using the property this[parameter] method.
property this[index] {
get { xxx }
set { xxx }
}
Copy the code
Take a look at the following code:
class IndexedNames
{
let namelist = []
let size = 10
fn init()
{
let i = 0
for (i = 0; i < size; i++)
{
namelist[i] = "N. A."
}
}
fn getNameList() {
println(namelist)
}
property this[index]
{
get
{
let tmp;
if ( index >= 0 && index <= size - 1 )
{
tmp = namelist[index]
}
else
{
tmp = ""
}
return tmp
}
set
{
if ( index >= 0 && index <= size-1 )
{
namelist[index] = value
}
}
}
}
fn Main()
{
namesObj = new IndexedNames(a)// The following code calls setter methods of the indexer
namesObj[0] = "Zara"
namesObj[1] = "Riz"
namesObj[2] = "Nuha"
namesObj[3] = "Asif"
namesObj[4] = "Davinder"
namesObj[5] = "Sunil"
namesObj[6] = "Rubic"
namesObj.getNameList()
for (i = 0; i < namesObj.size; i++)
{
println(namesObj[i]) // Call the indexer's getter}}Main(a)Copy the code
Static variables/methods/properties
class Test
{
static let x = 0;
static let y = 5;
static fn Main()
{
println(Test.x);
println(Test.y);
Test.x = 99;
println(Test.x); }}Test.Main(a)Copy the code
Standard input/output/error
The following three objects are predefined in Monkey: stdin, stdout, and stderr. They represent standard input, standard output, and standard error
stdout.writeLine("Hello world")
// Same effect as above
fmt.fprintf(stdout, "Hello world\n")
print("Please type your name:")
name = stdin.read(1024) // Read up to 1024 bytes from standard input
println("Your name is " + name)
Copy the code
Error handling in the standard library
When library functions return nil or false, you can use their message() method class to get an error message:
file = newFile(filename, "r")
if (file == nil) {
println("opening ", filename, "for reading failed, error:", file.message())
}
// Manipulate files
/ /...
// Close the file
file.close()
let ret = http.listenAndServe("127.0.0.1:9090")
if (ret == false) {
println("listenAndServe failed, error:", ret.message())
}
Copy the code
You might wonder, why does nil or false have a message() method? Because in the monkey, nil and false are both objects, so they both have methods.
aboutdefer
The keyword
The defer statement delays the execution of a function until it returns.
let add = fn(x,y){
defer println("I'm defer1")
println("I'm in add")
defer println("I'm defer2")
return x + y
}
println(add(2.2))
Copy the code
The results are as follows:
I'm in add
I'm defer2
I'm defer1
4
Copy the code
Different types of joins
In Monkey, you can join different types. Look at the following example:
// Number plus assignment
num = 10
num += 10 + 15.6
num += 20
println(num)
// String plus assignment
str = "Hello "
str += "world! "
str += [1.2.3]
println(str)
// Array plus assignment
arr = []
arr += 1
arr += 10.5
arr += [1.2.3]
arr += {"key"= >"value"}
println(arr)
// Array compare
arr1 = [1.10.5[1.2.3] and {"key"= >"value"}]
println(arr1)
if arr == arr1 { //support ARRAY compare
println("arr1 = arr")}else {
println("arr1 ! = arr")}// Hash assignment("+=", "-=")
hash = {}
hash += {"key1"= >"value1"}
hash += {"key2"= >"value2"}
hash += {5= >"five"}
println(hash)
hash -= "key2"
hash -= 5
println(hash)
Copy the code
Comprehensions are Comprehensions.
Monkey supports list inference (lists can be arrays, strings, ranges, tuples, hashes). List derivations return arrays. Look at the following example:
/ / array
x = [[word.upper(), word.lower(), word.title()] for word in ["hello"."world"."good"."morning"]]
println(x) / / the result: [["HELLO", "hello", "Hello"], ["WORLD", "world", "World"], ["GOOD", "good", "Good"], ["MORNING", "morning", "Morning"]]
/ / string
y = [ c.upper() for c in "huanghaifeng" where $_ % 2! =0] //$_ is the index
println(y) // Result: ["U", "N", "H", "I", "E", "G"]
/ / range
w = [i + 1 for i in 1.10]
println(w) // Result: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
//tuple
v = [x+1 for x in (12.34.56)]
println(v) // Results: [13, 35, 57]
/ / hash
z = [v * 10 for k,v in {"key1"= >10."key2"= >20."key3"= >30}]
println(z) // Result: [100, 200, 300]
Copy the code
Monkey also supports hash derivation. The return value of the hash derivation is hash. Look at the following example:
// From hash
z1 = { v:k for k,v in {"key1"= >10."key2"= >20."key3"= >30}} //reverse key-value pair
println(z1) / / results: {10 = > "key1", 20 = > "key2", 30 = > "key3"}, order may be different
// hash from array
z2 = {x:x**2 for x in [1.2.3]}
println(z2) {1 => 1, 2 => 4, 3 => 9
// hash derivation (from.. range)
z3 = {x:x**2 for x in 5.7}
println(z3) // Result: {5 => 25, 6 => 36, 7 => 49}
// Hash derivation (from string)
z4 = {x:x.upper() for x in "hi"}
println(z4) // result: {"h" => "h", "I" => "I"}
// from tuple
z5 = {x+1:x+2 for x in (1.2.3)}
println(z5) // Result: {4 => 5, 2 => 3, 3 => 4}
Copy the code
The Grep and map
Grep and map are similar to perl grep and map.
let sourceArr = [2.4.6.8.10.12]
//$_ represents the value obtained for each loop
let m = grep $_ > 5, sourceArr // For each element in sourceArr, only elements ">5" are returned
println('m is {m}')
let cp = map $_ * 2 , sourceArr // Multiply each element by 2
println('cp is {cp}')
// A more complicated example
let fields = {
"animal"= >"dog"."building"= >"house"."colour"= >"red"."fruit"= >"apple"
}
let pattern = `animal|fruit`
// =~(match),! ~(mismatch)
let values = map { fields[$_] } grep { $_ =~ pattern } fields.keys()
println(values)
Copy the code
function
In the Monkey, functions, like any other basic type, can be used as arguments to functions and return values to functions
Functions can also have default and variable arguments.
//define a function
let add = fn() { [5.6]}let n = [1.2] + [3.4] + add()
println(n)
let complex = {
"add" => fn(x, y) { return fn(z) {x + y + z } }, //function with closure
"sub" => fn(x, y) { x - y },
"other"= > [1.2.3.4]}println(complex["add"] (1.2) (3))
println(complex["sub"] (10.2))
println(complex["other"] [2])
let warr = [1+1.3, fn(x) { x + 1} (2),"abc"."def"]
println(warr)
println("\nfor i in 5.. 1 where i > 2 :")
for i in fn(x){ x+1} (4).. fn(x){ x+1} (0) where i > 2 {
if (i == 3) { continue }
println('i={i}')
}
// Default and variable parameters
add = fn (x, y=5, z=7, args...) {
w = x + y + z
for i in args {
w += i
}
return w
}
w = add(2.3.4.5.6.7)
println(w)
Copy the code
You can also create a named function like this:
fn sub(x,y=2) {
return x - y
}
println(sub(10)) // Result: 8
Copy the code
You can also create an anonymous function using the short arraw syntax:
let x = () -> 5 + 5
println(x()) // Result: 10
let y = (x) -> x * 5
println(y(2)) // Result: 10
let z = (x,y) -> x * y + 5
println(z(3.4)) / / results: 17
let add = fn (x, factor) {
x + factor(x)
}
result = add(5, (x) -> x * 2)
println(result) // Result: 15
Copy the code
If the function has no arguments, you can omit (). For example,
println("hhf".upper) // Result: "HHF"
// Same result as above
println("hhf".upper())
Copy the code
The Monkey does not support multiple return values, but there are many ways to do it.
Here’s one way to do it:
fn div(x, y) {
if y == 0 {
return [nil."y could not be zero"]}return [x/y, ""]
}
ret = div(10.5)
if ret[1] != "" {
println(ret[1])}else {
println(ret[0])}Copy the code
The Pipe operator
The PIPE operator comes from Elixir.
# Test pipe operator(|>)
x = ["hello"."world"] |> strings.join("") |> strings.upper() |> strings.lower() |> strings.title()
printf("x=<%s>\n", x)
let add = fn(x,y) { return x + y }
let pow = fn(x) { return x ** 2}
let subtract = fn(x) { return x - 1}
let mm = add(1.2) |> pow() |> subtract()
printf("mm=%d\n", mm)
"Hello %s! \n" |> fmt.printf("world")
Copy the code
Spawn and channel
You can use spawn to create a new thread and chan to interact with the thread.
let aChan = chan()
spawn fn() {
let message = aChan.recv()
println('channel received message=<{message}>')
}()
// Send information to the thread
aChan.send("Hello Channel!")
Copy the code
Introduction to standard Modules
Monkey has predefined standard modules such as JSON, SQL, sort, FMT, OS, Logger, Time, Flag, NET, HTTP, etc.
Below is a brief description of the monkey’s standard modules.
FMT module
let i, f, b, s, aArr, aHash = 108.25.383.true."Hello, world"[1.2.3.4."a"."b"] and {"key1"= >1."key2"= >2."key3"= >"abc"}
// Use '%v (value)' to print the variable value and '%_' to print the variable type
fmt.printf("i=[%05d, %X], b=[%t], f=[%.5f], s=[%-15s], aArr=%v, aHash=%v\n", i, i, b, f, s, aArr, aHash)
fmt.printf("i=[%_], b=[%t], f=[%f], aArr=%_, aHash=%_, s=[%s] \n", i, b, f, aArr, aHash, s)
sp = fmt.sprintf("i=[%05d, %X], b=[%t], f=[%.5f], s=[%-15s]\n", i, i, b, f, s)
fmt.printf("sp=%s", sp)
fmt.fprintf(stdout, "Hello %s\n"."world")
Copy the code
Time module
t1 = newTime()
format = t1.strftime("%F %R")
println(t1.toStr(format))
Epoch = t1.toEpoch()
println(Epoch)
t2 = t1.fromEpoch(Epoch)
println(t2.toStr(format))
Copy the code
The logger module
Log = newLogger(stdout,"LOGGER-", logger.LSTDFLAGS | logger.LMICROSECONDS)
log.printf("Hello, %s\n"."logger")
fmt.printf("Logger: flags =<%d>, prefix=<%s>\n", log.flags(), log.prefix())
// Output to a file
file = newFile("./logger.log"."a+")
log.setOutput(file)
for i in 1.5 {
log.printf("This is <%d>\n", i)
}
file.close() // Don't forget to close the file
Copy the code
Flag module (handles command line options)
let verV = flag.bool("version".false."0.1")
let ageV = flag.int("age".40."an int")
let heightV = flag.float("height".120.5."a float")
let nameV = flag.string("name"."HuangHaiFeng"."a string")
let hobbiesV = flag.string("hobbies"."1, 2, 3"."a comma-delimited string")
flag.parse()
println("verV = ", verV)
println("ageV = ", ageV)
println("heightV = ", heightV)
println("nameV = ", nameV)
println("hobbies = ", hobbiesV.split(","))
if (flag.isSet("age")) {
println("age is set")}else {
println("age is not set")}Copy the code
Json modules (JSON serialization (Marshal) and deserialization (unmarshal))
let hsJson = {"key1"= >10."key2"= >"Hello Json %s %s Module"."key3"= >15.8912."key4"= > [1.2.3.5."Hello"]."key5"= >true."key6"= > {"subkey1"= >12."subkey2"= >"Json"},
"key7" => fn(x,y){x+y}(1.2)}let hashStr = json.marshal(hsJson) // 'json.tojson (hsJson)' can also be used
println(json.indent(hashStr, ""))
let hsJson1 = json.unmarshal(hashStr)
println(hsJson1)
let arrJson = [1.2.3."HHF", [] and {"key"= >10."key1"= >11}]
let arrStr = json.marshal(arrJson)
println(json.indent(arrStr))
let arr1Json = json.unmarshal(arrStr) // 'json.fromjson (arrStr)' can also be used
println(arr1Json)
Copy the code
Net module
// Simple TCP client
let conn = dialTCP("tcp"."127.0.0.1:9090")
if (conn == nil) {
println("dailTCP failed, error:", conn.message())
os.exit(1)}let n = conn.write("Hello server, I'm client")
if (n == nil) {
println("conn write failed, error:", n.message())
os.exit(1)}let ret = conn.close()
if (ret == false) {
println("Server close failed, error:", ret.message())
}
// A simple TCP server
let ln = listenTCP("tcp".": 9090")
for {
let conn = ln.acceptTCP()
if (conn == nil) {
println(conn.message())
} else {
printf("Accepted client, Address=%s\n", conn.addr())
}
spawn fn(conn) { //spawn a thread to handle the connection
println(conn.read())
}(conn)
} //end for
let ret = ln.close()
if (ret == false) {
println("Server close failed, error:", ret.message())
}
Copy the code
Linq module
In the Monkey, the LINQ module supports the following types of objects:
- File object (using built-in functions
newFile
Create) - Csv Reader objects (using built-in functions
newCsvReader
Create) - String object
- The Array object
- The Tuple objects
- The Hash object
- Channel object (using built-in functions
chan
Create)
let mm = [1.2.3.4.5.6.7.8.9.10]
println('before mm={mm}')
result = linq.from(mm).where(fn(x) {
x % 2= =0
}).select(fn(x) {
x = x + 2
}).toSlice()
println('after result={result}')
result = linq.from(mm).where(fn(x) {
x % 2= =0
}).select(fn(x) {
x = x + 2
}).last()
println('after result={result}')
let sortArr = [1.2.3.4.5.6.7.8.9.10]
result = linq.from(sortArr).sort(fn(x,y){
return x > y
})
println(' [1.2.3.4.5.6.7.8.9.10] sort(x>y)={result}')
result = linq.from(sortArr).sort(fn(x,y){
return x < y
})
println(' [1.2.3.4.5.6.7.8.9.10] sort(x<y)={result}')
thenByDescendingArr = [
{"Owner"= >"Google"."Name"= >"Chrome"},
{"Owner"= >"Microsoft"."Name"= >"Windows"},
{"Owner"= >"Google"."Name"= >"GMail"},
{"Owner"= >"Microsoft"."Name"= >"VisualStudio"},
{"Owner"= >"Google"."Name"= >"GMail"},
{"Owner"= >"Microsoft"."Name"= >"XBox"},
{"Owner"= >"Google"."Name"= >"GMail"},
{"Owner"= >"Google"."Name"= >"AppEngine"},
{"Owner"= >"Intel"."Name"= >"ParallelStudio"},
{"Owner"= >"Intel"."Name"= >"VTune"},
{"Owner"= >"Microsoft"."Name"= >"Office"},
{"Owner"= >"Intel"."Name"= >"Edison"},
{"Owner"= >"Google"."Name"= >"GMail"},
{"Owner"= >"Microsoft"."Name"= >"PowerShell"},
{"Owner"= >"Google"."Name"= >"GMail"},
{"Owner"= >"Google"."Name"= >"GDrive"}
]
result = linq.from(thenByDescendingArr).orderBy(fn(x) {
return x["Owner"]
}).thenByDescending(fn(x){
return x["Name"]
}).toOrderedSlice() //Note: You need to use toOrderedSlice
//use json.indent() for formatting the output
let thenByDescendingArrStr = json.marshal(result)
println(json.indent(thenByDescendingArrStr, ""))
/ / test 'selectManyByIndexed'
println(a)let selectManyByIndexedArr1 = [[1.2.3], [4.5.6.7]]
result = linq.from(selectManyByIndexedArr1).selectManyByIndexed(
fn(idx, x){
if idx == 0 { return linq.from([10.20.30])}return linq.from(x)
}, fn(x,y){
return x + 1
})
println(' [[1.2.3], [4.5.6.7]] selectManyByIndexed() = {result}')
let selectManyByIndexedArr2 = ["st"."ng"]
result = linq.from(selectManyByIndexedArr2).selectManyByIndexed(
fn(idx,x){
if idx == 0 { return linq.from(x + "r")}return linq.from("i" + x)
},fn(x,y){
return x + "_"
})
println(' ["st"."ng"] selectManyByIndexed() = {result}')
Copy the code
Linq for file support
Monkey now has support for LINq for file. This functionality is similar to AWK. Take a look at the following code:
//test: linq for "file"
file = newFile("./examples/linqSample.csv"."r") // Open linqsample.csv in read mode
result = linq.from(file,",",fn(line){ // The second argument is the field delimiter, and the third argument is the comment function.
if line.trim().hasPrefix("#") { // If the line begins with '#'
return true // Return 'true' to ignore this line
} else {
return false}}).where(fn(fields) {
//'fields' is a hash array:
// fields = [
// {"line" =>LineNo1, "nf" =>line1's number of fields, 0 => line1, 1 => field1, 2 =>field2, ... },
// {"line" =>LineNo2, "nf" =>line2's number of fields, 0 => line2, 1 => field1, 2 =>field2, ... }
/ /]
int(fields[1) >300000 // Select only the value of the first field > 300000
}).sort(fn(field1,field2){
return int(field1[1]) > int(field2[1]) // The first field is sorted in descending order
}).select(fn(fields) {
fields[5] // Output only the fifth field
})
println(result)
file.close() // Don't forget to close the file
//another test: linq for "file"
file = newFile("./examples/linqSample.csv"."r") // Open linqsample.csv in read mode
result = linq.from(file,",",fn(line){ // The second argument is the field delimiter, and the third argument is the comment function.
if line.trim().hasPrefix("#") { // If the line begins with '#'
return true // Return 'true' to ignore this line
} else {
return false}}).where(fn(fields) {
int(fields[1) >300000 // Select only the value of the first field > 300000
}).sort(fn(field1,field2){
return int(field1[1]) > int(field2[1]) // The first field is sorted in descending order
}).selectMany(fn(fields) {
row = [[fields[0]]] // Fields [0] is a full line of data. Note: We need to use both [], otherwise selectMany() will flatten the output
linq.from(row) // Output the entire line of data
})
println(result)
file.close() // Don't forget to close the file
//test: linq for "csv"
r = newCsvReader("./examples/test.csv") // Open test.csv in read mode
r.setOptions({"Comma"= >";"."Comment"= >"#"})
result = linq.from(r).where(fn(x) {
//The 'x' is an array of hashes, like below:
// x = [
// {"nf" =>line1's number of fields, 1 => field1, 2 =>field2, ... },
// {"nf" =>line2's number of fields, 1 => field1, 2 =>field2, ... }
/ /]
x[2] = ="Pike"// Select only the second field = "Pike"
}).sort(fn(x,y){
return len(x[1]) > len(y[1]) // Sort by the length of the first field
})
println(result)
r.close() // Don't forget to close Reader
Copy the code
The CSV module
// Test CSV reader
let r = newCsvReader("./examples/test.csv")
if r == nil {
printf("newCsv returns err, message:%s\n", r.message())
}
r.setOptions({"Comma"= >";"."Comment"= >"#"})
ra = r.readAll()
if (ra == nil) {
printf("readAll returns err, message:%s\n", ra.message())
}
for line in ra {
println(line)
for record in line {
println("", record)
}
}
r.close() //do not to forget to close the reader
// Test the CSV writer
let ofile = newFile("./examples/demo.csv"."a+")
let w = newCsvWriter(ofile)
w.setOptions({"Comma"= >""})
w.write(["1"."2"."3"])
w.writeAll([["4"."5"."6"], ["Seven"."8"."9"], ["10"."11"."12"]])
w.flush()
ofile.close() //do not to forget to close the file
Copy the code
The template module
The template module contains ‘text’ and ‘HTML’ template processing.
Using newText (…). Or parseTextFiles (…). Create a new ‘text’ template.
Using newHtml (…). Or parseHtmlFiles (…). To create a new ‘HTML’ template.
arr = [
{ "key"= >"key1"."value"= >"value1" },
{ "key"= >"key2"."value"= >"value2" },
{ "key"= >"key3"."value"= >"value3"}]// Use parseTextFiles() to write a string
template.parseTextFiles("./examples/looping.tmpl").execute(resultValue, arr)
println('{resultValue}')
// Use parseTextFiles() to write a file
file = newFile("./examples/outTemplate.log"."a+")
template.parseTextFiles("./examples/looping.tmpl").execute(file, arr)
file.close() //do not to forget to close the file
/ / use the parse ()
// We need to use "{{-" and "-}}" to remove newline from output.
template.newText("array").parse(`Looping
{{- range . }}
key={{ .key }}, value={{ .value -}}
{{- end }}
`).execute(resultValue, arr)
println('{resultValue}')
Copy the code
SQL module
SQL modules provide a low-level wrapper to manipulate the database.
It handles null values in the database correctly, although it has not been fully tested.
To test the SQL module, you need to do the following steps:
-
Download the SQL Driver code.
-
Include the drive package in the ‘sql.go’ file:
_ "github.com/mattn/go-sqlite3"
Copy the code
- Recompile the monkey source code.
Here is a complete example of using a database (examples/db.my):
let dbOp = fn() {
os.remove("./foo.db") //delete `foo.db` file
let db = dbOpen("sqlite3"."./foo.db")
if (db == nil) {
println("DB open failed, error:", db.message())
return false
}
defer db.close()
letsqlStmt = `create table foo (id integer not null primary key, name text); delete from foo; `let exec_ret = db.exec(sqlStmt)
if (exec_ret == nil) {
println("DB exec failed! error:", exec_ret.message())
return false
}
let tx = db.begin()
if (tx == nil) {
println("db.Begin failed! , error:", tx.message())
return false
}
letstmt = tx.prepare(`insert into foo(id, name) values(? ,?) `)if (stmt == nil) {
println("tx.Prepare failed! , error:", stmt.message())
return false
}
defer stmt.close()
let i = 0
for (i = 0; i < 105; i++) {
let name = "Hello" + i
if (i>100) {
// Insert 'null' value. There are seven predefined NULL constants: INT_NULL,UINT_NULL,FLOAT_NULL,STRING_NULL,BOOL_NULL,TIME_NULL, DECIMAL_NULL.
let rs = stmt.exec(i, sql.STRING_NULL)}else {
let rs = stmt.exec(i, name)
}
if (rs == nil) {
println("statement exec failed, error:", rs.message())
return false}}//end for
tx.commit()
let id, name = 0.""
let rows = db.query("select id, name from foo")
if (rows == nil) {
println("db queue failed, error:", rows.message())
return false
}
defer rows.close()
while (rows.next()) {
rows.scan(id, name)
if (name.valid()) { // Check for 'null'
println(id, "|", name)
} else {
println(id, "|"."null")}}return true
}
let ret = dbOp()
if (ret == nil) {
os.exit(1)
}
os.exit()
Copy the code
utility
The project also includes some tools to use: Formatter and highlighter.
The Formatter tool formats the Monkey language. The highlighter tool syntactically highlights the Monkey language (providing two outputs: command line and HTML).
You can also use them together:
. / FMT xx. My |. / highlight / / output to the screen (command line highlight not only the Windows)Copy the code
Syntax highlighting
Currently, Monkey supports syntax highlighting for three editors:
-
vim
vim
-
emeditor
emeditor
-
notepad++
notepad++
-
Visual Studio Code
VSC
-
Sublime Text 3
Sublime Text 3
Future plans
The following is a description of future plans for the project:
- Improve the standard library and add more functions.
- Write more test code!
license
MIT
note
If you like this project, please click the link below, Duoduo Star, fork. Thank you very much! monkey