summaryrefslogtreecommitdiffhomepage
path: root/sing.html.markdown
diff options
context:
space:
mode:
authormdegirolami <mdegirolami@gmail.com>2021-11-02 13:26:19 +0100
committerGitHub <noreply@github.com>2021-11-02 13:26:19 +0100
commit74ccc905616e648b443f68fe1e96c5a64449290c (patch)
tree78001a90aa12d53eb63a2da3b46f31c9a32455c2 /sing.html.markdown
parent9552f2a1fee55280b3bdda18624058aa14c1a369 (diff)
[sing/en] Added sing language (#4256)
* Added sing language * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Update sing.html.markdown Co-authored-by: Andre Polykanine <ap@oire.me> * Fixed a comment Co-authored-by: De Girolami <maurizio.degirolami@datalogic.com> Co-authored-by: Andre Polykanine <ap@oire.me>
Diffstat (limited to 'sing.html.markdown')
-rw-r--r--sing.html.markdown446
1 files changed, 446 insertions, 0 deletions
diff --git a/sing.html.markdown b/sing.html.markdown
new file mode 100644
index 00000000..2e439800
--- /dev/null
+++ b/sing.html.markdown
@@ -0,0 +1,446 @@
+---
+name: Sing
+category: language
+language: Sing
+filename: learnsing.sing
+contributors:
+ - ["Maurizio De Girolami", "https://github.com/mdegirolami"]
+---
+
+The purpose of sing is to provide a simple, safe, fast language that
+can be a good replacement for c++ for high performance applications.
+
+Sing is an easy choice because it compiles to human-quality readable c++.
+
+Because of that, if you work for a while with Sing and, at any time, you discover you don't like Sing anymore, you lose nothing of your work
+because you are left with nice and clean c++ code.
+
+In some way you can also think Sing as a tool to write c++ in a way that enforces some best practices.
+
+```go
+/* Multi- line comment.
+ /* It can be nested */
+ Use it to remark-out part of the code.
+ It leaves no trace in the intermediate c++ code.
+ (sing translates into nice human readable c++)
+*/
+
+// Single line comment, can be placed only before a statement or declaration...
+// ...or at the right of the first line of a statement or declaration.
+// single line comments are kept into c++.
+//
+// here we declare if we need to use public declarations from other files.
+// (in this case from files 'sio', 'sys')
+requires "sio";
+requires "sys";
+
+//
+// A sing function declaration.
+// All the declarations can be made public with the 'public' keyword.
+// All the declarations start with a keyword specifying the type of declaration
+// (in this case fn for function) then follows the name, the arguments and the
+// return type.
+//
+// Each argument starts with a direction qualifyer (in, out, io) which tells if
+// the argument is an input, an output or both...
+// ...then follows the argument name and the type.
+public fn singmain(in argv [*]string) i32
+{
+ // print is from the sio file and sends a string to the console
+ sio.print("Hello World\n");
+
+ // type conversions are allowed in the form of <newtype>(expression).
+ sio.print(string(sum(5, 10)) + "\n");
+
+ // For clarity you can specify after an argument its name separated by ':'.
+ var result i32;
+ recursive_power(10:base, 3:exponent, result);
+
+ // referred here to avoid a 'not used' error.
+ learnTypes();
+
+ // functions can only return a single value of some basic type.
+ return(0);
+}
+
+// You can have as many arguments as you want, comma separated.
+// You can also omit the 'in' direction qualifyer (it is the default).
+fn sum(arg1 i32, arg2 i32) i32
+{
+ // as 'fn' declares a function, 'let' declares a constant.
+ // With constants, if you place an initializer, you can omit the type.
+ let the_sum = arg1 + arg2;
+
+ return(the_sum);
+}
+
+// Arguments are passed by reference, which means that in the function body you
+// use the argument names to refer to the passed variables.
+// Example: all the functions in the recursion stack access the same 'result'
+// variable, supplied by the singmain function.
+fn recursive_power(base i32, exponent i32, out result i32) void
+{
+ if (exponent == 0) {
+ result = 1;
+ } else {
+ recursive_power(base, exponent - 1, result);
+ result *= base;
+ }
+}
+
+//**********************************************************
+//
+// TYPES
+//
+//**********************************************************
+fn learnTypes() void
+{
+ // the var keyword declares mutable variables
+ // in this case an UTF-8 encoded string
+ var my_name string;
+
+ // ints of 8..64 bits size
+ var int0 i8;
+ var int1 i16;
+ var int2 i32;
+ var int3 i64;
+
+ // uints
+ var uint0 u8;
+ var uint1 u16;
+ var uint2 u32;
+ var uint3 u64;
+
+ // floats
+ var float0 f32;
+ var float1 f64;
+
+ // complex
+ var cmplx0 c64;
+ var cmplx1 c128;
+
+ cmplx0 = 0;
+ cmplx1 = 0;
+
+ // and of course...
+ var bool0 bool;
+
+ // type inference: by default constants are i32, f32, c64
+ let an_int32 = 15;
+ let a_float32 = 15.0;
+ let a_complex = 15.0 + 3i;
+ let a_string = "Hello !";
+ let a_bool = false;
+
+ // To create constant of different types use a conversion-like syntax:
+ // NOTE: this is NOT a conversion. Just a type specification
+ let a_float64 = f64(5.6);
+
+ // in a type definition [] reads as "array of"
+ // in the example []i32 => array of i32.
+ var intarray []i32 = {1, 2, 3};
+
+ // You can specify a length, else the length is given by the initializer
+ // the last initializer is replicated on the extra items
+ var sizedarray [10]i32 = {1, 2, 3};
+
+ // Specify * as the size to get a dynamic array (can change its length)
+ var dyna_array [*]i32;
+
+ // you can append items to a vector invoking a method-like function on it.
+ dyna_array.push_back(an_int32);
+
+ // getting the size of the array. sys.validate() is like assert in c
+ sys.validate(dyna_array.size() == 1);
+
+ // a map that associates a number to a string.
+ // "map(x)..." reads "map with key of type x and vaue of type..."
+ var a_map map(string)i32;
+
+ a_map.insert("one", 1);
+ a_map.insert("two", 2);
+ a_map.insert("three", 3);
+ let key = "two";
+
+ // note: the second argument of get_safe is the value to be returned
+ // when the key is not found.
+ sio.print("\nAnd the value is...: " + string(a_map.get_safe(key, -1)));
+
+ // string concatenation
+ my_name = "a" + "b";
+}
+
+// an enum type can only have a value from a discrete set.
+// can't be converted to/from int !
+enum Stages {first, second, last}
+
+// you can refer to enum values (to assign/compare them)
+// specifying both the typename and tagname separated with the '.' operator
+var current_stage = Stages.first;
+
+
+//**********************************************************
+//
+// POINTERS
+//
+//**********************************************************
+
+// This is a factory for a dynamic vector.
+// In a type declaration '*' reads 'pointer to..'
+// so the return type is 'pointer to a vector of i32'
+fn vectorFactory(first i32, last i32) *[*]i32
+{
+ var buffer [*]i32;
+
+ // fill
+ for (value in first : last) {
+ buffer.push_back(value);
+ }
+
+ // The & operator returns the address of the buffer.
+ // You can only use & on local variables
+ // As you use & on a variable, that variable is allocated on the HEAP.
+ return(&buffer);
+}
+
+fn usePointers() void
+{
+ var bufferptr = vectorFactory(0, 100);
+
+ // you don't need to use the factory pattern to use pointers.
+ var another_buffer [*]i32;
+ var another_bufferptr = &another_buffer;
+
+ // you can dereference a pointer with the * operator
+ // sys.validate is an assertion (causes a signal if the argument is false)
+ sys.validate((*bufferptr)[0] == 0);
+
+ /*
+ // as all the pointers to a variable exit their scope the variable is
+ // no more accessible and is deleted (freed)
+ */
+}
+
+//**********************************************************
+//
+// CLASSES
+//
+//**********************************************************
+
+// This is a Class. The member variables can be directly initialized here
+class AClass {
+public:
+ var public_var = 100; // same as any other variable declaration
+ fn is_ready() bool; // same as any other function declaration
+ fn mut finalize() void; // destructor (called on object deletion)
+private:
+ var private_var string;
+
+ // Changes the member variables and must be marked as 'mut' (mutable)
+ fn mut private_fun(errmsg string) void;
+}
+
+// How to declare a member function
+fn AClass.is_ready() bool
+{
+ // inside a member function, members can be accessed thrugh the
+ // this keyword and the field selector '.'
+ return(this.public_var > 10);
+}
+
+fn AClass.private_fun(errmsg string) void
+{
+ this.private_var = errmsg;
+}
+
+// using a class
+fn useAClass() void
+{
+ // in this way you create a variable of type AClass.
+ var instance AClass;
+
+ // then you can access its members through the '.' operator.
+ if (instance.is_ready()) {
+ instance.public_var = 0;
+ }
+}
+
+//**********************************************************
+//
+// INTERFACES
+//
+//**********************************************************
+
+// You can use polymorphism in sing defining an interface...
+interface ExampleInterface {
+ fn mut eraseAll() void;
+ fn identify_myself() void;
+}
+
+// and then creating classes which implement the interface
+// NOTE: you don't need (and cannot) re-declare the interface functions
+class Implementer1 : ExampleInterface {
+private:
+ var to_be_erased i32 = 3;
+public:
+ var only_on_impl1 = 0;
+}
+
+class Implementer2 : ExampleInterface {
+private:
+ var to_be_erased f32 = 3;
+}
+
+fn Implementer1.eraseAll() void
+{
+ this.to_be_erased = 0;
+}
+
+fn Implementer1.identify_myself() void
+{
+ sio.print("\nI'm the terrible int eraser !!\n");
+}
+
+fn Implementer2.eraseAll() void
+{
+ this.to_be_erased = 0;
+}
+
+fn Implementer2.identify_myself() void
+{
+ sio.print("\nI'm the terrible float eraser !!\n");
+}
+
+fn interface_casting() i32
+{
+ // upcasting is automatic (es: *Implementer1 to *ExampleInterface)
+ var concrete Implementer1;
+ var if_ptr *ExampleInterface = &concrete;
+
+ // you can access interface members with (guess what ?) '.'
+ if_ptr.identify_myself();
+
+ // downcasting requires a special construct
+ // (see also below the conditional structures)
+ typeswitch(ref = if_ptr) {
+ case *Implementer1: return(ref.only_on_impl1);
+ case *Implementer2: {}
+ default: return(0);
+ }
+
+ return(1);
+}
+
+// All the loop types
+fn loops() void
+{
+ // while: the condition must be strictly of boolean type
+ var idx = 0;
+ while (idx < 10) {
+ ++idx;
+ }
+
+ // for in an integer range. The last value is excluded
+ // 'it' is local to the loop and must not be previously declared
+ for (it in 0 : 10) {
+ }
+
+ // reverse direction
+ for (it in 10 : 0) {
+ }
+
+ // configurable step. The loop stops when it's >= the final value
+ for (it in 0 : 100 step 3) {
+ }
+
+ // with an auxiliary counter.
+ // The counter start always at 0 and increments by one at each iteration
+ for (counter, it in 3450 : 100 step -22) {
+ }
+
+ // value assumes in turn all the values from array
+ var array [*]i32 = {0, 10, 100, 1000};
+ for (value in array) {
+ }
+
+ // as before with auxiliary counter
+ for (counter, value in array) {
+ }
+}
+
+// All the conditional structures
+interface intface {}
+class c0_test : intface {public: fn c0stuff() void;}
+class delegating : intface {}
+
+fn conditionals(in object intface, in objptr *intface) void
+{
+ let condition1 = true;
+ let condition2 = true;
+ let condition3 = true;
+ var value = 30;
+
+ // condition1 must be a boolean.
+ if (condition1) {
+ ++value; // conditioned statement
+ }
+
+ // you can chain conditions with else if
+ if (condition1) {
+ ++value;
+ } else if (condition2) {
+ --value;
+ }
+
+ // a final else runs if any other condition is false
+ if (condition1) {
+ ++value;
+ } else if (condition2) {
+ --value;
+ } else {
+ value = 0;
+ }
+
+ // based on the switch value selects a case statement
+ switch (value) {
+ case 0: sio.print("value is zero"); // a single statement !
+ case 1: {} // do nothing
+ case 2: // falls through
+ case 3: sio.print("value is more than one");
+ case 4: { // a block is a single statement !
+ value = 0;
+ sio.print("how big !!");
+ }
+ default: return; // if no one else matches
+ }
+
+ // similar to a switch but selects a case based on argument type.
+ // - object must be a function argument of type interface.
+ // - the case types must be classes implementing the object interface.
+ // - in each case statement, ref assumes the class type of that case.
+ typeswitch(ref = object) {
+ case c0_test: ref.c0stuff();
+ case delegating: {}
+ default: return;
+ }
+
+ // - object must be an interface pointer.
+ // - the case types must be pointers to classes implementing the objptr interface.
+ // - in each case statement, ref assumes the class pointer type of that case.
+ typeswitch(ref = objptr) {
+ case *c0_test: {
+ ref.c0stuff();
+ return;
+ }
+ case *delegating: {}
+ default: sio.print("unknown pointer type !!");
+ }
+}
+```
+
+## Further Reading
+
+[official Sing web site](https://mdegirolami.wixsite.com/singlang).
+
+If you want to play with sing you are recommended to download the vscode plugin. Please
+follow the instructions at [Getting Started](https://mdegirolami.wixsite.com/singlang/copy-of-interfacing-sing-and-c-2)