Anyone here made their own programming language?

Off-topic talk on music, art, literature, games and forum games.
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

My professor tells me my seminar is good enough to be published in some computer science journal, if not an international one (for which I should spend time translating my work to English, and have a higher risk my paper will be rejected), than at least in Osječki Matematički List. So, I've re-edited my seminar to make it better. It's available on the same address.
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

Some "dragitz" on GitHub will collaborate with me on that compiler, he claims to be able to implement matrix operations into it. I am a bit skeptical this will end up well (primarily because they will need to edit the parser code, which is rather dirty), but it's good to have a company.
https://github.com/dragitz/ArithmeticEx ... ler/pull/1
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

Anyway, I've updated the AEC.
First, and perhaps most important, I didn't realize before that JavaScript had a standard way (using ArrayBuffer) to convert decimal numbers to IEEE754 hexadecimals. Not only in modern browsers, it works all the way back to Internet Explorer 10-era browsers. That opens a door towards making an AEC compiler running in NodeJS as powerful (if not more) than the one that runs on Duktape. It also opens a door to targetting GNU Assembler as well as FlatAssembler. In case you didn't know, GNU Assembler has been supporting Intel Syntax for quite some time now (though it's not as feature-full as FlatAssembler is).
Second, today, I implemented the conditional "?:" operator in my compiler. You can also see that in the browser. It can really make the code shorter sometimes. On the university, we are taught not to use it, but I strongly disagree with that.
I also changed the UI of the web-based version of the compiler, though I am not sure I made it better.
Peppesq
Newbie
Posts: 17
Joined: Fri May 29, 2020 10:08 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by Peppesq »

Here is the “Hello, World!” application in my own language:

string := import (“string@mylanguage.com”)
printf := import (“printf@mylanguage.com”)
hello :~ printf (“Hello, World!\n”)
main: func {int} (args: collection(string) ) ~ { hello() 0}
Some features:

Head-initial syntax (declaration of ‘hello’ starts with the word ‘hello’) to make it easier to read the code
The ‘:’ operator for declaration can be followed by the ‘=’ operator for assignment to allow quick “declare and assign” as in script languages, while preserving the safe, mandatory declaration
dynamic linkage. Here, the ‘import’ call can be resolved in link time if the linker is intelligent enough, but you don’t have to hardcode the repository name. The association with the local symbol ‘string’ is also dynamic
‘~’ operator for delayed invocation, as opposed to ‘=’, in this example used to define the function ‘hello’ as opposed to evaluating the expression ‘printf(…)’ immediately
A rudimentary function definiton like ‘hello’ does not need ‘{}’ around the body, nor does it need the ‘func’ keyword. You can supply those details for clarrity if you want, of course. But otherwise the compiler sorts it out.
‘{hello() 0}’ means a call to the ‘hello’ function concatenated with the number 0. Since the ‘hello’ function returns void, in this case the return value of ‘main’ is simply 0.
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

Peppesq wrote: Sat May 30, 2020 11:16 pm Here is the “Hello, World!” application in my own language:

string := import (“string@mylanguage.com”)
printf := import (“printf@mylanguage.com”)
hello :~ printf (“Hello, World!\n”)
main: func {int} (args: collection(string) ) ~ { hello() 0}
Some features:

Head-initial syntax (declaration of ‘hello’ starts with the word ‘hello’) to make it easier to read the code
The ‘:’ operator for declaration can be followed by the ‘=’ operator for assignment to allow quick “declare and assign” as in script languages, while preserving the safe, mandatory declaration
dynamic linkage. Here, the ‘import’ call can be resolved in link time if the linker is intelligent enough, but you don’t have to hardcode the repository name. The association with the local symbol ‘string’ is also dynamic
‘~’ operator for delayed invocation, as opposed to ‘=’, in this example used to define the function ‘hello’ as opposed to evaluating the expression ‘printf(…)’ immediately
A rudimentary function definiton like ‘hello’ does not need ‘{}’ around the body, nor does it need the ‘func’ keyword. You can supply those details for clarrity if you want, of course. But otherwise the compiler sorts it out.
‘{hello() 0}’ means a call to the ‘hello’ function concatenated with the number 0. Since the ‘hello’ function returns void, in this case the return value of ‘main’ is simply 0.
Is it a compiled or an interpreted language, I didn't understand? Can you link to the compiler/interpreter you've made?
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

Yesterday evening, I made a program in my programming language that can run on DOS. It's an analog clock again, similar to the previous one.

Code: Select all

Syntax GAS
;This is the same program as in the "analogClock.aec" file, just modified to
;run on DOS instead of Linux. It also compiles using GNU Assembler.
;Namely, GCC 9.3.0 and GNU Assembler 2.34, although they are released in
;2019, still feature the ability to compile for DOS. You don't need to 
;run them on DOS for that, in fact, I doubt they even can be run on DOS.
;If you manage to compile them to run on DOS, they will probably run out of 
;RAM even for the simplest programs (DOS can't use more than 64MB of RAM,
;which is far too little to run a modern compiler). You can run them on
;Linux and they will produce a DOS executable which you then can run in an
;emulator. That's called cross-compiling. Now, it's not possible to do with
;the stripped-down version of GNU Compiler Collection (GCC) you get with
;Linux, you need to build it from source to get all the features (among 
;other things, cross-compilation to many OS-es). It's not too hard, but it
;does take hours to compile full version of GCC even on a super-modern
;computer. For some reason that escapes me, this particular executable
;causes DosBox to crash, even though it works on FreeDOS in VirtualBox.
;Now, I hope this goes without saying, but if some modern program runs on 
;DOS, that's probably a coincidence, and you can't count on it working 
;flawlessly. Developers have long stopped testing whether their app works
;under DOS. So, while the C library that comes with GCC 9.3.0 can compile
;for DOS, attempts to actually link with it lead to countless linker errors.
;GCC will by default attempt to link to the C library, even if your code
;doesn't use any of the functions present in it. So, you need to compile
;the assembly code ArithmeticExpressionCompiler produces with:
;   djgpp-gcc -o analogClockForDOS.exe -ffreestanding -nostdlib analogClockForDOS.s
;For that reason, I wasn't able to compile Duktape to run on DOS.
;Why use GNU Assembler instead of FlatAssembler? Well, first of all, I
;already have tons of inline assembly compatible with GNU Assembler (from
;"analogClock.aec" which runs on Linux). Second, when you work in
;GNU Assembler, you don't have to write the complicated code for putting
;the processor in the 32-bit mode (DOS programs automatically start in
;16-bit mode), GNU Assembler does that for you.
;Now, in order for 32-bit apps to be able to run on DOS, you need to have
;a driver called DPMI (DOS Protected Mode Interface). It comes pre-installed
;in FreeDOS, but not on MS-DOS. It also comes with Windows 3.x.
;FlatAssembler for DOS is also a 32-bit app and it won't run on DOS without
;a DPMI installed and run.
AsmStart ;So, the following code is generated by GCC 9.3.0, plus some inline assembly I put in the C program.
    .file	"analogClock.c"
	.section .text
/APP
	.intel_syntax noprefix
call _main #I hope this goes without saying, but when you are developing...
#...for a system without a C library, there is no guarantee "main" will...
#...be called first (or even at all before your program crashes),
#you need to take care of that yourself.
.att_syntax

/NO_APP
	.globl	_putchar
_putchar:
LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$4, %esp
	movl	8(%ebp), %eax
	movb	%al, -4(%ebp)
/APP
# 9 "analogClock.c" 1
	movb -4(%ebp),%dl
movb $0x02,%ah
int $0x21

# 0 "" 2
/NO_APP
	nop
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE0:
    .comm   result,4
    .comm   i,4
    .comm   x,4
    .comm   y,4
    .comm   currentSign,4
    .comm   centerX,4
    .comm   centerY,4
    .comm   distance,4
    .comm   clockRadius,4
    .comm   output,7360
    .comm   hour,4
    .comm   minute,4
    .comm   second,4
    .comm   angle,4
    .comm   endOfTheHandX,4
    .comm   endOfTheHandY,4
    .comm   coefficientOfTheDirection,4
    .comm   windowWidth,4
    .comm   windowHeight,4
    .comm   lowerBoundX,4
    .comm   upperBoundX,4
    .comm   lowerBoundY,4
    .comm   upperBoundY,4
    .comm   isXWithinBounds,4
    .comm   isYWithinBounds,4
    .comm   expectedY,4
    .comm   expectedX,4
    .comm   j,4
    .comm   ASCIIofSpaceAsFloat32,4
    .comm   ASCIIofDigit0AsFloat32,4
    .comm   ASCIIofColonAsFloat32,4
    .comm   ASCIIofNewLineAsFloat32,4
	.globl	_main
_main:
LFB1:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$24, %esp
/APP
# 18 "analogClock.c" 1
.ifdef debugForDOS #When you don't have a good debugger (like when working on DOS), you need to find some clever ways to debug. You know, like printing "Hello world!" step by step.
    .intel_syntax noprefix
    mov dl,'H'
    mov ax,0x200
    int 0x21
.att_syntax
.endif
.intel_syntax noprefix #Get current time. As we have no access to the standard C library here, we need to look up a way to do that in DOS API.
mov ax,0x2C00
int 0x21
mov byte ptr hour,ch
fild dword ptr hour
fstp dword ptr hour
mov byte ptr minute,cl
fild dword ptr minute
fstp dword ptr minute
mov byte ptr second,dh
fild dword ptr second
fstp dword ptr second #Eh, now I understand why some assembly-language programmers prefer att_syntax to intel_syntax (no need to write "dword ptr" there).
#Let's also set the graphic card to text-mode, in case it isn't in it (though I don't know if it's possible to invoke my program from some other mode without crashing DOS before my program even begins then).
mov ax,0x0003
int 0x10
.att_syntax
AsmEnd ;And now finally follows a program written in AEC.
windowWidth:=80
windowHeight:=23
ASCIIofSpace<=" \0\0\0" ;As integer. We know we are dealing with a...
ASCIIofNewLine<="\n\0\0\0" ;32-bit little-endian machine.
ASCIIofStar<="*\0\0\0"
i:=0
While i<windowWidth*windowHeight ;First, fill the window with spaces and newlines.
    If mod(i,windowWidth)=windowWidth-1
        AsmStart
            .intel_syntax noprefix
            fild dword ptr ASCIIofSpace #Not need for a new line, DOS will do that automatically.
            fstp dword ptr currentSign
            .att_syntax
        AsmEnd
    Else
        AsmStart
            .intel_syntax noprefix
            fild dword ptr ASCIIofSpace
            fstp dword ptr currentSign
            fld dword ptr currentSign
            fstp dword ptr ASCIIofSpaceAsFloat32
            .att_syntax
        AsmEnd
    EndIf
    output[i]:=currentSign
    i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'e'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
centerX:=windowWidth/2-mod(windowWidth/2,1)
centerY:=windowHeight/2-mod(windowHeight/2,1)
clockRadius:=(centerX<centerY)?(centerX):(centerY)-1
i:=0
While i<windowWidth*windowHeight ;Next, draw the circle which represents the clock.
    y:=i/windowWidth-mod(i/windowWidth,1) ;When I didn't put "floor" into my programming language...
    x:=mod(i,windowWidth)
    distance:=sqrt((x-centerX)*(x-centerX)+(y-centerY)*(y-centerY)) ;Pythagorean Theorem.
    If abs(distance-clockRadius)<3/4
        AsmStart
            .intel_syntax noprefix
            fild dword ptr ASCIIofStar
            fstp dword ptr currentSign
            .att_syntax
        AsmEnd
        output[i]:=currentSign
    EndIf
    i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'l'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
AsmStart
    .intel_syntax noprefix
    jmp ASCIIofDigitsAsInt32Array$
    ASCIIofDigitsAsInt32Array:
    .macro writeDigits startingWith=0
        .byte '0'+\startingWith,0,0,0 #".byte" is to GNU Assembler about the same as "db" is to FlatAssembler.
        .if \startingWith < 9
            writeDigits \startingWith+1
        .endif
    .endm
    writeDigits #The goal is to make Assembler output the ASCII of "0\0\0\01\0\0\02\0\0\0...9\0\0\0" inside the executable (if the instruction pointer points to it, it will, of course, be an invalid instruction).
    ASCIIofDigitsAsInt32Array$:
    .att_syntax
AsmEnd
;Label of "12"...
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+2*4] #The ASCII of '2'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX+1]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+6*4] #The ASCII of '6'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
output[(centerY+clockRadius-1)*windowWidth+centerX]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+3*4] #The ASCII of '3'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
output[centerY*windowWidth+centerX+clockRadius-1]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+9*4] #The ASCII of '9'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
output[centerY*windowWidth+centerX-clockRadius+1]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(360/12)*(clockRadius-1)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+2*4] #The ASCII of '2'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(2*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(2*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+4*4] #The ASCII of '4'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(4*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(4*360/12)*(clockRadius-1)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+5*4] #The ASCII of '5'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(5*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(5*360/12)*(clockRadius-1)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+7*4] #The ASCII of '7'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(7*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(7*360/12)*(clockRadius-1)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+8*4] #The ASCII of '8'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(8*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(8*360/12)*(clockRadius-1)]:=currentSign
;Label "10"...
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+0*4] #The ASCII of '0'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)+1]:=currentSign
;Label "11"...
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
    .intel_syntax noprefix
    fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)+1] := currentSign
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'o'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
j:=0
While j<3
    If j=0
        angle:=(mod(hour+minute/60,12))*(360/12)
    ElseIf j=1
        angle:=minute*(360/60)
    Else
        angle:=second*(360/60)
    EndIf
    endOfTheHandX:=centerX+sin(angle)*clockRadius/(j=0?2:j=1?3/2:4/3) ;Hour hand will be the shortest, and the hand that shows the seconds will be the longest.
    endOfTheHandY:=centerY-cos(angle)*clockRadius/(j=0?2:j=1?3/2:4/3)
    coefficientOfTheDirection:=(endOfTheHandY-centerY)/(endOfTheHandX-centerX)
    debugString <= "Drawing line between (%d,%d) and (%d,%d).\n\0"
    AsmStart
        .intel_syntax noprefix
        .ifdef DEBUG #Conditional assembly, this will only be assembled if you tell GNU Assembler (by modifying the file or using command line) that you want to enable debugging.
            fld dword ptr endOfTheHandY
            fistp dword ptr result
            push dword ptr result #This (pushing a "dword" onto the system stack) breaks the compatibility with 64-bit Linux (but you can still enable it by disabling debugging)!
            fld dword ptr endOfTheHandX
            fistp dword ptr result
            push dword ptr result
            fld dword ptr centerY
            fistp dword ptr result
            push dword ptr result
            fld dword ptr centerX
            fistp dword ptr result
            push dword ptr result
            lea ebx,debugString
            push ebx
            call printf #I hope this goes without saying, but, unless you link with a C library, this won't work under DOS.
        .endif #End of the conditional assembly.
        .att_syntax
    AsmEnd
    i:=0
    While i<windowWidth*windowHeight
        lowerBoundX:=(endOfTheHandX<centerX)?(endOfTheHandX):(centerX)
        upperBoundX:=(endOfTheHandX>centerX)?(endOfTheHandX):(centerX)
        lowerBoundY:=(endOfTheHandY<centerY)?(endOfTheHandY):(centerY)
        upperBoundY:=(endOfTheHandY>centerY)?(endOfTheHandY):(centerY)
        y:=i/windowWidth-mod(i/windowWidth,1)
        x:=mod(i,windowWidth)
        isXWithinBounds:=(x>lowerBoundX | x=lowerBoundX) & (x<upperBoundX | x=upperBoundX) ;Damn... Now I understand why almost every programming language supports the "<=" and ">=" operators, no matter how much harder they make the language to tokenize.
        isYWithinBounds:=(y>lowerBoundY | y=lowerBoundY) & (y<upperBoundY | y=upperBoundY)
        If isXWithinBounds=1 & isYWithinBounds=1
            expectedY:=(x-centerX)*coefficientOfTheDirection+centerY
            expectedX:=(y-centerY)*(1/coefficientOfTheDirection)+centerX
            debugString1 <= "The point (%d,%d) is within bounds, expectedY is %d and expectedX is %d.\n\0"
            AsmStart
                .intel_syntax noprefix
                .ifdef DEBUG
                    fld dword ptr expectedX
                    fistp dword ptr result
                    push dword ptr result
                    fld dword ptr expectedY
                    fistp dword ptr result
                    push dword ptr result
                    fld dword ptr y
                    fistp dword ptr result
                    push dword ptr result
                    fld dword ptr x
                    fistp dword ptr result
                    push dword ptr result
                    lea ebx,debugString1
                    push ebx
                    call printf
                .endif
                .att_syntax
            AsmEnd
            ASCIIofLetterH<="h\0\0\0"
            ASCIIofLetterM<="m\0\0\0"
            ASCIIofLetterS<="s\0\0\0"
            If j=0
                AsmStart
                    .intel_syntax noprefix
                    fild dword ptr ASCIIofLetterH
                    fstp dword ptr currentSign
                    .att_syntax
                AsmEnd
            ElseIf j=1
                AsmStart
                    .intel_syntax noprefix
                    fild dword ptr ASCIIofLetterM
                    fstp dword ptr currentSign
                    .att_syntax
                AsmEnd
            Else
                AsmStart
                    .intel_syntax noprefix
                    fild dword ptr ASCIIofLetterS
                    fstp dword ptr currentSign
                    .att_syntax
                AsmEnd
            EndIf
            If (upperBoundX=lowerBoundX | upperBoundY=lowerBoundY) & output[i]=ASCIIofSpaceAsFloat32
                output[i]:=currentSign
            EndIf
            If (abs(expectedY-y)<3/4 | abs(expectedX-x)<3/4) & output[i]=ASCIIofSpaceAsFloat32
                output[i]:=currentSign
            EndIf
        EndIf
        i:=i+1
    EndWhile
    j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,' '
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
;Draw some ornament...
ASCIIofLetterX<="x\0\0\0"
AsmStart
    .intel_syntax noprefix
    fild dword ptr ASCIIofLetterX
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
i:=0
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'w'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
While i<windowWidth*windowHeight
    y:=i/windowWidth-mod(i/windowWidth,1)
    x:=mod(i,windowWidth)
    If abs(windowHeight-2*ln(1+abs((x-centerX)/2))-y)<1-abs(x-centerX)/(centerX*95/112) & x>1/2*centerX & x<3/2*centerX & output[i]=ASCIIofSpaceAsFloat32 ;The logarithmic curve looks somewhat like a lemma of a flower.
        output[i]:=currentSign
    EndIf
    i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'o'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
AsmStart
    .intel_syntax noprefix
    fild dword ptr ASCIIofLetterX
    fstp dword ptr currentSign
    .att_syntax
AsmEnd
;Let's try to make it look like the bottom of the lemma isn't floating in the air.
j:=0
While j<3
    i:=windowWidth*(windowHeight-1) ;So, move to the beginning of the last line.
    While i<windowWidth*windowHeight
        If j<2 & (output[i-windowWidth]=currentSign & (output[i+1]=currentSign | output[i-1]=currentSign))
            output[i]:=currentSign
        ElseIf j=2 & (output[i+1]=ASCIIofSpaceAsFloat32 & output[i-windowWidth]=currentSign)
            output[i]:=ASCIIofSpaceAsFloat32
        EndIf
        i:=i+1
    EndWhile
    j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'r'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
;Let's make a digital clock in the corner...
AsmStart
    .intel_syntax noprefix
    fild dword ptr ASCIIofDigitsAsInt32Array #So, load "0\0\0\0" (the first 32 bits of the array "ASCIIofDigitsAsInt32Array") into the st0 register and convert it to Float32.
    fstp dword ptr ASCIIofDigit0AsFloat32
    .att_syntax
AsmEnd
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'l'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
ASCIIofColon<=":\0\0\0"
AsmStart
    .intel_syntax
    fild dword ptr ASCIIofColon
    fstp dword ptr ASCIIofColonAsFloat32
    .att_syntax
AsmEnd
output[windowWidth*windowHeight-2]:=ASCIIofDigit0AsFloat32+mod(second,10)
output[windowWidth*windowHeight-3]:=ASCIIofDigit0AsFloat32+second/10-mod(second/10,1)
output[windowWidth*windowHeight-4]:=ASCIIofColonAsFloat32
output[windowWidth*windowHeight-5]:=ASCIIofDigit0AsFloat32+mod(minute,10)
output[windowWidth*windowHeight-6]:=ASCIIofDigit0AsFloat32+minute/10-mod(minute/10,1)
output[windowWidth*windowHeight-7]:=ASCIIofColonAsFloat32
output[windowWidth*windowHeight-8]:=ASCIIofDigit0AsFloat32+mod(hour,10)
output[windowWidth*windowHeight-9]:=ASCIIofDigit0AsFloat32+hour/10-mod(hour/10,1)
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'d'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
signature<="Analog Clock for DOS\nMade in AEC by\nTeo Samarzija\0"
currentSign:=signature[0]
i:=windowWidth*(windowHeight-3)
j:=0
While not(currentSign=0) ;That is, as long as it's not the '\0' sign.
    AsmStart
        .intel_syntax noprefix
        fld dword ptr j
        fistp dword ptr result
        mov ebx, dword ptr result
        movzx eax, byte ptr [signature+ebx] #I hope it goes without saying something like this (using post-Pentium instructions in inline assembly) won't work on a machine with an archaic processor. I am writing this program for a machine with a modern processor which happens to run DOS.
        mov dword ptr result, eax
        fild dword ptr result
        fstp dword ptr currentSign
        fild dword ptr ASCIIofNewLine
        fstp dword ptr ASCIIofNewLineAsFloat32
        .att_syntax
    AsmEnd
    If currentSign=ASCIIofNewLineAsFloat32
        i:=(i/windowWidth-mod(i/windowWidth,1)+1)*windowWidth
    ElseIf not(currentSign=0)
        output[i]:=currentSign
        i:=i+1
    Else
        output[i]:=ASCIIofSpaceAsFloat32
    EndIf
    j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
    mov dl,'!'
    mov ax,0x200
    int 0x21
.endif
.att_syntax
AsmEnd
AsmStart ;And this is, according to GCC 9.3.0, how you convert a Float32Array with ASCII codes and print it under DOS.
# 0 "" 2
/NO_APP
	movl	$0, -4(%ebp)
	jmp	L3
L4:
	movl	-4(%ebp), %eax
	flds	output(,%eax,4)
	fnstcw	-18(%ebp)
	movw	-18(%ebp), %ax
	orb	$12, %ah
	movw	%ax, -20(%ebp)
	fldcw	-20(%ebp)
	fistps	-22(%ebp)
	fldcw	-18(%ebp)
	movb	-22(%ebp), %al
	movsbl	%al, %eax
	pushl	%eax
	call	_putchar
	addl	$4, %esp
	incl	-4(%ebp)
L3:
	cmpl	$1839, -4(%ebp)
	jle	L4
/APP
# 21 "analogClock.c" 1
	.intel_syntax noprefix
mov al,0 #And I hope this also goes without saying, but when there is...
mov ah,0x4C #...no C library, returning 0 from "main" crashes your program...
int 0x21 #...and you need to use OS-specific code to end it properly.
.att_syntax

# 0 "" 2
/NO_APP
	movl	$0, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1:
	.ident	"GCC: (GNU) 9.3.0"
AsmEnd
Here is what it looks like in QEMU:
Image
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

I've tried to make my program for sorting decimal numbers faster for nearly-sorted or nearly-reverse-sorted arrays by implementing the algorithm recursively, checking the sortedness of the array each iteration. I've implemented it in ArithmeticExpressionCompiler:

Code: Select all

Syntax GAS ;Neka ArithmeticExpressionCompiler ispisuje asemblerski kod kompatibilan s GNU Assemblerom, da bude kompatibilan s GCC-om. Po defaultu ispisuje kod kompatibilan s FlatAssemblerom (a FlatAssembler na Linuxu ne radi bas najbolje).
verboseMode ON ;Neka ArithmeticExpressionCompiler ispisuje vise komentara u asemblerski kod koji ispisuje (da bude laksi za citanje i debuggiranje).
AsmStart ;Neka GNU Assembler obavijesti linkera da je "hybrid_sort" naziv potprograma...
    .global hybrid_sort
    hybrid_sort:
AsmEnd
If gornja_granica-donja_granica<2 ;Ako je niz duljine manje od 2 (0 ili 1), znaci da je vec poredan, pa prekidamo izvodenje ovog potprograma.
    AsmStart ;Kako radimo izvan sekcija, mozemo jednostavno prekinuti izvodenje potprograma asemblerskom naredbom "ret" (inace bismo, da radimo u sekcijama, morali znati vrti li se program na 32-bitnom ili 64-bitnom Linuxu).
        ret
    AsmEnd 
EndIf
razvrstanost:=0
i:=donja_granica
While i < gornja_granica - 1
    razvrstanost:=razvrstanost+(originalni_niz[i]<originalni_niz[i+1])
    i:=i+1
EndWhile
razvrstanost:=razvrstanost/((gornja_granica-donja_granica-1)/2)-1
i:=2
While i<7 | i=7 
    razvrstanost_na_potenciju[i] := pow(abs(razvrstanost), i) ;"pow(x,y)" je u AEC-u samo sintaksni secer za "exp(ln(x)*y)", i to vraca NaN za x=0 ili x<0. Nema ocitog nacina da se "pow(x,y)" prevede na asemblerski.
    razvrstanost_na_potenciju[i] := (razvrstanost=0) ? 0 : (mod(i,2)=1 & razvrstanost<0) ? (-razvrstanost_na_potenciju[i]) : razvrstanost_na_potenciju[i] ;C-ov i JavaScriptin uvjetni operator nekad zna znatno skratiti kod, zato sam ga ugradio i u svoj jezik.
    i:=i+1
EndWhile
;Formula koju je ispisao genetski algoritam za predvidanje koliko ce usporedbi QuickSort napraviti: https://github.com/FlatAssembler/ArithmeticExpressionCompiler/tree/master/QuickSort/Genetic_algorithm_for_deriving_the_formula
polinom_pod_apsolutnom := 2.38854*razvrstanost_na_potenciju[7] - 0.284258*razvrstanost_na_potenciju[6] - 1.87104*razvrstanost_na_potenciju[5] + 0.372637*razvrstanost_na_potenciju[4] + 0.167242*razvrstanost_na_potenciju[3] - 0.0884977*razvrstanost_na_potenciju[2] + 0.315119*razvrstanost
Eulerov_broj_na_koju_potenciju := (ln(gornja_granica - donja_granica) + ln(ln(gornja_granica - donja_granica))) * 1.05 + (ln(gornja_granica - donja_granica) - ln(ln(gornja_granica - donja_granica)) - ln(2)) * 0.9163 * abs(polinom_pod_apsolutnom)
koliko_usporedbi_ocekujemo_od_QuickSorta := exp(Eulerov_broj_na_koju_potenciju)
koliko_usporedbi_ocekujemo_od_MergeSorta := 2 * (gornja_granica - donja_granica) * ln(gornja_granica - donja_granica) / ln(2)
If razvrstanost=1 ;Ako je niz vec poredan.
    broj_vec_poredanih_podniza := broj_vec_poredanih_podniza + 1
    AsmStart
        ret
    AsmEnd
ElseIf razvrstanost = -1 ;Ako je niz obrnuto poredan...
    broj_obrnuto_poredanih_podniza := broj_obrnuto_poredanih_podniza + 1
    i:=donja_granica
    j:=gornja_granica-1
    While i<gornja_granica
        pomocni_niz[i] := originalni_niz[j]
        j := j - 1
        i := i + 1
    EndWhile
    i := donja_granica
    While i < gornja_granica
        originalni_niz[i] := pomocni_niz[i]
        i := i + 1
    EndWhile
    AsmStart
        ret
    AsmEnd
ElseIf koliko_usporedbi_ocekujemo_od_MergeSorta < koliko_usporedbi_ocekujemo_od_QuickSorta ;MergeSort algoritam (priblizno poredani podnizovi, za koje je MergeSort efikasniji od QuickSorta)...
    broj_pokretanja_MergeSorta := broj_pokretanja_MergeSorta + 1
    sredina_niza:=(gornja_granica+donja_granica)/2
    sredina_niza:=sredina_niza-mod(sredina_niza,1)
    vrh_stoga:=vrh_stoga+1 ;Zauzmi mjesta na stogu za rekurziju. Ne koristimo sistemski stog, kao sto koristi C++, nego koristimo vise globalnih polja kao stogove. Da koristimo sistemski stog, morali bismo znati pokrecemo li se na 32-bitnom Linuxu ili 64-bitnom Linuxu, jer oni nisu kompatibilni u tom pogledu.
    stog_s_donjim_granicama[vrh_stoga]:=donja_granica
    stog_s_gornjim_granicama[vrh_stoga]:=gornja_granica
    stog_sa_sredinama_niza[vrh_stoga]:=sredina_niza
    gornja_granica:=sredina_niza
    AsmStart
        call hybrid_sort
    AsmEnd
    donja_granica:=stog_s_donjim_granicama[vrh_stoga] ;Sad je rekurzija gotovo sigurno izmijenila sve globalne varijable koje nam trebaju ("donja_granica", "gornja_granica" i "sredina_niza"), ali zato imamo njihove stare vrijednosti na stogovima.
    gornja_granica:=stog_s_gornjim_granicama[vrh_stoga]
    sredina_niza:=stog_sa_sredinama_niza[vrh_stoga]
    donja_granica:=sredina_niza
    AsmStart
        call hybrid_sort
    AsmEnd
    donja_granica:=stog_s_donjim_granicama[vrh_stoga]
    gornja_granica:=stog_s_gornjim_granicama[vrh_stoga]
    sredina_niza:=stog_sa_sredinama_niza[vrh_stoga]
    ;Spajanje nizova originalni_niz[donja_granica..sredina_niza] i originalni_niz[sredina_niza..gornja_granica] u jedan niz...
    i:=donja_granica
    gdje_smo_u_prvom_nizu:=donja_granica
    gdje_smo_u_drugom_nizu:=sredina_niza
    While i<gornja_granica
        If (gdje_smo_u_prvom_nizu=sredina_niza | originalni_niz[gdje_smo_u_drugom_nizu]<originalni_niz[gdje_smo_u_prvom_nizu]) & gdje_smo_u_drugom_nizu<gornja_granica
            pomocni_niz[i]:=originalni_niz[gdje_smo_u_drugom_nizu]
            gdje_smo_u_drugom_nizu:=gdje_smo_u_drugom_nizu+1
        Else
            pomocni_niz[i]:=originalni_niz[gdje_smo_u_prvom_nizu]
            gdje_smo_u_prvom_nizu:=gdje_smo_u_prvom_nizu+1
        EndIf
        i:=i+1
    EndWhile
    i:=donja_granica
    While i<gornja_granica
        originalni_niz[i]:=pomocni_niz[i]
        i:=i+1
    EndWhile
    vrh_stoga:=vrh_stoga-1 ;Oslobodi mjesto na stogovima.
    AsmStart
        ret
    AsmEnd
Else ;QuickSort algoritam (nasumicno ispremjestani podnizovi)...
    broj_pokretanja_QuickSorta := broj_pokretanja_QuickSorta + 1
    ;Daljnji kod je priblizno prepisan s https://www.geeksforgeeks.org/quick-sort/
    pivot := originalni_niz[gornja_granica - 1]
    i := donja_granica - 1
    j := donja_granica
    While j < gornja_granica - 1
        If originalni_niz[j] < pivot
            i := i + 1
            pomocna_varijabla_za_zamijenu := originalni_niz[i]
            originalni_niz[i] := originalni_niz [j]
            originalni_niz[j] := pomocna_varijabla_za_zamijenu
        EndIf
        j:=j+1
    EndWhile
    pomocna_varijabla_za_zamijenu := originalni_niz[i + 1]
    originalni_niz[i + 1] := originalni_niz[gornja_granica - 1]
    originalni_niz[gornja_granica - 1] := pomocna_varijabla_za_zamijenu
    gdje_je_pivot := i + 1
    vrh_stoga := vrh_stoga + 1 ;Zauzmi mjesta na stogu za rekurziju (ne koristimo sistemski stog, kao sto koristi C++, nego koristimo vise globalnih polja kao stogove).
    stog_s_donjim_granicama[vrh_stoga] := donja_granica
    stog_s_gornjim_granicama[vrh_stoga] := gornja_granica
    stog_sa_sredinama_niza[vrh_stoga] := gdje_je_pivot
    gornja_granica := gdje_je_pivot
    AsmStart
        call hybrid_sort
    AsmEnd
    donja_granica := stog_s_donjim_granicama[vrh_stoga]
    gornja_granica := stog_s_gornjim_granicama[vrh_stoga]
    gdje_je_pivot := stog_sa_sredinama_niza[vrh_stoga]
    donja_granica := gdje_je_pivot
    AsmStart
        call hybrid_sort
    AsmEnd
    vrh_stoga := vrh_stoga - 1 ;Oslobodi mjesto na stogovima.
    AsmStart
        ret
    AsmEnd
EndIf
AsmStart ;Ovdje tok programa ne smije doci. Ako dode, pozovi debugger.
    call abort
AsmEnd
This time, I didn't make an executable program using FlatAssembler, but a static library that can be compiled using GNU Assembler and linked with, for instance, a C++ program that will test it. So, I can get more precise measurements of its performance. Now it works better than last time for nearly-sorted arrays (it's as fast as C++ "sort" directive), but it's around 50 times slower than C++ "sort" is if the array is randomly shuffled, and around 2 times slower than my last program was. Oddly, I thought I implemented QuickSort (running when the sortedness is near 0) much more efficiently now.
Image
Implementing it as a static linking library rather than as an executable also makes it easier to see what's going on inside:
Image
There also appears to be some surge in the number of QuickSorts executed when the sortedness is around 0.9 (when MergeSort will almost certainly do a better job), I can't figure out exactly why (yet alone that there is an easy fix).
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

Today, I've opened a Reddit thread about my programming language. The responses seem very positive.
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

I've written some specification for my programming language. A programming language isn't really a programming language without a specification.
teo123
Master of the Forum
Posts: 1393
Joined: Tue Oct 27, 2015 3:46 pm
Diet: Vegan

Re: Anyone here made their own programming language?

Post by teo123 »

I've added some basic support for data structures into my programming language.
Post Reply