221V d4b70ca64b upd docs 1 month ago
..
readme.md d4b70ca64b upd docs 1 month ago

readme.md

Description (My bad opinionated opinions :D)

0. Reason - Why? - Because!

Because I need a more user-friendly tool, that's why!

0.1. less f*ck brains with parentheses

todo (about problems in languages with C-like syntax)

0.2. less f*ck brains with whitespaces

todo (about problems in languages with indentation syntax - Off-side rule - YAML, Python, Nim)

0.3. less key presses

Erlang is most laconic high-level language that I ever seen -
laconic and expressive, very simple (not overcomplicated) and is easy to learn.

0.4. target language

The only imperfection of Erlang is .. VM -
so if you write any crypto miner in Erlang - it won't impress with its performance...
(Erlang is very well designed for IO - network, parallel tasks etc,
but can show not better results for "number crusher" - CPU heavy tasks)
.
so as target needs system programming language that compiles to native single executable binary file.
.
Haskell was rejected cause - sometimes headache with io-pure-impure functions, non-optional GC, too many ways to make the same (so read other people's code may be too nontrivial);
Go was rejected cause - that was not more easy that Haskell for me (Haskell was more easy in use), also non-optional GC;
Rust was rejected cause - overcomplicated style of writing code - you do not thinking about your app architecture but have f*ck your brains with compiler - borrow checker etc (also is not so "memory safe" in reality as in advertising);
C++ was rejected cause - super overcomplicated, not need so headache ;) ;
C was rejected cause - I think for 2025 year C is a bit outdated (not so user friendly as we want), also don't want to write function declarations twice, and other cucumbers;
OCaml was rejected cause - has not user friendly compiler (non-informative errors messages), non-optional GC;
Nim was rejected cause - too less performance comparing to Dlang and C;
Zig was rejected cause - unstabled - in active development phase (will check - compare in future);
Jai was rejected cause - still in closed beta (will check - compare in future);
Forth, Common Lisp, Red, Ada, Lua, Mojo and Swift - have not been considered for this task;
D - Dlang - compiles to native single executable binary file, rich of features - not less possibilities than in C++, has optional GC, stable - mature etc.
so Dlang has been selected as target language, and compiler v1 will be in Dlang (v2 will be seft-hosted).

1. Naming

Compiler dopp for doppelganger
(also dopp command),

Language DEF-lang for
[based on] Dlang [and influenced by / inspiration from] Erlang [new] Fine language.

File extension - *.de (because de shorter than def).

2. Syntax and Ideas

2.1 toml file for config keys-values

[lexer]
indent_type = 0
indent_matter = 4
indent_out = 2

indent_type = 0 for whitespaces, 1 for tabs;
indent_matter = 4 means we have basic indent (Off-side rule) as 4 whitespaces,
so we do not got error when next line have 5, 6 or 3, 2 whitespaces etc -
needs to have more (+- 4) whitespaces for change indent (Off-side rule) for next level.
indent_out = 2 means we got 2 whitespaces indents in resu;t files.

2.2 do not use any reserved words (keywords) for function definition (like in Erlang)

todo add examples

2.3 Pattern Matching that transforms into if .. else if .. else .. or switch .. case

https://dlang.org/spec/statement.html#SwitchStatement
https://dlang.org/spec/statement.html#case-range
https://tour.dlang.org/tour/uk/basics/controlling-flow
https://dlang.org/spec/expression.html#IsExpression
todo add examples

2.4 Pattern Matching at functions too (like in Erlang)

Function overloading in dlang allows to define the same function but with different versions for different data types:

int my_func(int x){
    return x;
}

int my_func(string x){
    return x.length; // or other operation(s) with argument
}

that similar to erlang's functions clauses:

my_func(X) when is_integer(X) -> X;
my_func(Y) when is_binary(Y) orelse is_list(Y) -> string:length(Y). %% is_list for list string (list chars codes)

other example

my_func(1) -> 1;
my_func(5) -> 5 * 2;
my_func(9) -> 9 * 3;
my_func(X) -> X * 4.

->

int my_func(int x){
    if(x == 1){
        return 1;
    }else if(x == 5){
        return x * 2;
    }else if(x == 9){
        return x * 3;
    }else{ // other cases
        return x * 4;
    }
}

// or

int my_func(int x){
    switch (x){
        case 1:
            return 1; // if x == 1
        case 5:
            return x * 2; // if x == 5
        case 9:
            return x * 3; // if x == 9
        default:
            return x * 4; // other cases
    }
}

note that compiler can optimise few comparison conditions for switch better than that in if .. else.

2.4.1 pattern matching on strings

There are Pattern Matching on strings/binaries in Erlang, that looks like:

my_func(<<"test", Rest/binary>>) -> true;
my_func(_) -> false.

% or
my_func("test" ++ Rest) -> true;
my_func(_) -> false.

similar code in D - dlang:

bool my_func(string str){
    if(str.length >= 4 && str[0..4] == "test"){
        return true;
    }
    return false;
}

// also we can get rest

import std.stdio;

bool my_func(string str, out string rest){
    if(str.length >= 4 && str[0..4] == "test"){
        rest = str[4..$];
        return true;
    }
    rest = str;
    return false;
}

void main(){
    string rest;
    writeln(my_func("test123", rest)); // true, rest = "123"
    writeln(rest); // "123"
    writeln(my_func("example", rest)); // false, rest = "example"
}

// also we can to match from string ending
bool has_end_test(string str){
    if(str.length >= 4){
        if(str[$-4 .. $] == "test"){
            return true;
        }
    }
    return false;
}

In My Humble Opinion be better to make this syntax similar (laconic) as in Erlang.
upd. whitespaces count between function name and first argument do not matter (>=1 e.g. " " = [␣]{1, }),
between first argiment and second (and next) must be at least comma and 1 whitespase (", " = [␣]{0,}[,]{1}[␣]{1,} ).

2.4.2 pattern matching examples

// left = function type, right = arguments type
bool = string str
has_begin_test "test" ++ _ = true
has_begin_test _ = false

->

bool has_begin_test(string str){
    if(str.length >= 4 && str[0..4] == "test"){
        return true;
    }
    return false;
}
// left = function type, right = arguments type
bool = string str, out string rest
has_begin_test    "test" ++ rest = true
has_begin_test    _ = false

->

// import std.stdio;

bool has_begin_test(string str, out string rest){
    if(str.length >= 4 && str[0..4] == "test"){
        rest = str[4..$];
        return true;
    }
    rest = str;
    return false;
}

/*
void main(){
    string rest;
    
    writeln( has_begin_test("test123", rest) ); // true, rest = "123"
    writeln(rest); // "123"
    
    writeln( has_begin_test("test", rest) ); // true, rest = ""
    writeln(rest); // ""
    
    writeln( has_begin_test("example", rest) ); // false, rest = "example"
    writeln(rest); // "example"
}
*/
// left = function type, right = arguments type
bool = string str
has_end_test _ ++ "test" = true
has_end_test _ = false

->

// match from string ending
bool has_end_test(string str){
    if(str.length >= 4){
        if(str[$-4 .. $] == "test"){
            return true;
        }
    }
    return false;
}
// left = function type, right = arguments type
bool = string str, out string rest
has_end_test rest ++ "test" = true
has_end_test _ = false

->

// import std.stdio;

bool has_end_test(string str, out string rest){
    if(str.length >= 4 && str[$-4 .. $] == "test"){
        rest = str[0 .. $-4];
        return true;
    }
    rest = str;
    return false;
}

/*
void main(){
    string rest;
    
    writeln( has_end_test("hellotest", rest) ); // true, rest = "hello"
    writeln(rest); // "hello"
    
    writeln( has_end_test("example", rest) ); // false, rest = "example"
    writeln(rest); // "example"
    
    writeln( has_end_test("test", rest) ); // true, rest = ""
    writeln(rest); // ""
}
*/
// left = function type, right = arguments type
int = int x
my_func 1 = 1
my_func 5 = 5 * 2
my_func 9 = 9 * 3
my_func X = X * 4

->

int my_func(int x){
    if(x == 1){
        return 1;
    }else if(x == 5){
        return 5 * 2; // return x * 2;
    }else if(x == 9){
        return 9 * 3; // return x * 3;
    }else{ // other cases
        return x * 4;
    }
}
// left = function type, right = arguments type
// "_sw" in function name' end for use switch (no "_sw" in dlang function name)
int = int x
my_func_sw 1 = 1
my_func_sw 5 = 5 * 2
my_func_sw 9 = 9 * 3
my_func_sw X = X * 4

->

int my_func(int x){
    switch(x){
        case 1:
            return 1; // if x == 1
        case 5:
            return 5 * 2; // return x * 2; // if x == 5
        case 9:
            return 9 * 3; // return x * 3; // if x == 9
        default:
            return x * 4; // other cases
    }
}
// ptn = pattern = for pattern matching like `case X of` in erlang
y = ptn x
  1 = 1
  5 = 5 * 2
  9 = 9 * 3
  _ = x * 4

->

if (x == 1){
    y = 1;
}else if(x == 5){
    y = 5 * 2; // return x * 2;
}else if(x == 9){
    y = 9 * 3; // return x * 3;
}else{ // other cases
    y = x * 4;
}
// ptns = use switch in dlang for this pattern matching
y = ptns x
  1 = 1
  5 = 5 * 2
  9 = 9 * 3
  _ = x * 4

->

switch(x){
    case 1:
        y = 1; // if x == 1
    case 5:
        y = 5 * 2; // y = x * 2; // if x == 5
    case 9:
        y = 9 * 3; // y = x * 3; // if x == 9
    default:
        y = x * 4; // other cases
}

todo more examples -- with switch case dlang

2.5 aliases for imports, externs etc

use core.stdc.stdio [ printf scanf ]
    std.conv [ to ]

->

import core.stdc.stdio : printf, scanf;
import std.conv : to;
// definition at the beginning of the file
use as use1
    core.stdc.stdio [ printf scanf ]
    std.conv [ to ]
    std.stdio
    std.array
    std.string
    std.algorithm


use1 // use elsewhere in the file

->
import core.stdc.stdio : printf, scanf;
import std.conv : to;
import std.stdio;
import std.array;
import std.string;
import std.algorithm;

todo add more examples

2.6 main, wasm import from js (wasm export to js = as usual function + return)

main

->

void main()
{
...
smain

->

extern(C) void main()
{
...
// for wasm
wmain

->

void _start(){  }
// wasm import from js
void = int a
print

->

void print(int a);

2.7 data types aliases

// signed
i8   ->   byte     //                  -128 — 127
i16  ->   short    //                -32768 — 32767
i32  ->   int      //           -2147483648 — 2147483647
i64  ->   long     //  -9223372036854775808 — 9223372036854775807

// unsigned
u8   ->   ubyte    //  0 — 255
u16  ->   ushort   //  0 — 65535
u32  ->   uint     //  0 — 4294967295
u64  ->   ulong    //  0 — 18446744073709551615

// 128 not exists  //  0 — 340282366920938463463374607431768211455

f32  ->   float
f64  ->   double
f128 ->   real     // up to 128 bit
// todo mv to code
alias i8 = char;
alias i16 = short;
alias i32 = int;
alias i64 = long;
alias u8 = uchar;
alias u16 = ushort;
alias u32 = uint;
alias u64 = ulong;
alias f32 = float;
alias f64 = double;
alias f128 = real;

2.8 code between dlang reserved words translates into target .d file without changes

...

dlang

// common dlang code here

dlang

...

2.9 reserved words (keywords) list

including https://dlang.org/spec/lex.html#keywords

abstract alias align asm assert auto 
bool break byte 
case cast catch char class const continue 
dchar debug default delegate deprecated do double 
else enum export extern 
false final finally float for foreach foreach_reverse function 
goto  
if immutable import in inout int interface invariant is 
lazy long 
macro mixin module 
new nothrow null 
out override 
package pragma private protected public pure 
real ref return 
scope shared static struct super switch synchronized 
template this throw true try typeid typeof 
ubyte uint ulong union unittest ushort 
version void 
wchar while with 

__FILE__
__FILE_FULL_PATH__
__FUNCTION__
__LINE__
__MODULE__
__PRETTY_FUNCTION__

__gshared
__parameters
__rvalue
__traits
__vector

__DATE__
__EOF__
__TIME__
__TIMESTAMP__
__VENDOR__
__VERSION__


[deprecated keywords] body cdouble cent cfloat creal delete idouble ifloat ireal ucent


use
as
ptn
ptns

_
++

*_sw (function name end)

main
smain
wmain

dlang (dlang code without changes)

todo