ALA7 language tutorial

Introduction

Program code is devided to units. A unit is a function, a procedures on main module block of instructions.

i=1
x=2

The above code form main module direct instructions.

proc p1() #here starts procedure program unit

x=2

proc p2()

x=4

i=1 #here starts main code
x=2

All not global variables belong to their program units.
The fallowing examples are included in files tut1-5.ala.

To see results of our programming we will use printNL procedure that prints new line on the console screen.

Integer operations

i=1
i=i+2
x=y-z
x=a*(a+b)
x=x1/x2; y=reminder 	#reminder from last / (divide) operation
x=y >> 2 		#shift 2 bits right
x=x & y 		#bit and  1FFF FFFF & FFFF FFF1 -> 1FFF FFF1
x=x | y 		#bit or 0000 0001 |  1000 0001 -> 1000 0001

String operations

text_1="Alice " 	#str variable text_1 is declared and initialized to ”Alice” 

text_3=text_1 		#copy all chars not reference

text_1="012345678" ;index=2
el_2=text_1[index]	#indexes from 0

sub=text_1[1:4]	# take from 1 th,2,3 but not 4 

Lists

list_1=[]	#untyped list

i=6; append(list_1,i)

To untyped list we can append any value: int, str or object

list_2=[1,2,3]		# equals to list_2=[]; append(list_2,1);...

i=0
for item in list_2	#iteration through all elements of list_2
	i=item

Variable item has special type all to hold values of all types, because our list is untyped. The variable name must be unique in program unit, for each for .. in instruction.

for  e in list_1	#e type is all
	printNL(e)

Above instructions are an example of dynamic (late) procedure binding. The compiler first tries to find procedure printNL(all x). If procedure is not found the compiler tries to find procedures where name is printNL and have one parameter. If at least one is found than dynamic binding is coded: during run time program tries to find function address for a current actual parameter type.

for e_1 in list_1	#e_1 not e because in: for var in list, var must be unique 
	i=e_1
	printNL(i)	#static binding

Typed lists

list_int=[int]		#typed list of ints
list_class_1=[Class_1]	#typed list of Class_1

for e_i in list_int
	printNL(e_i)	#static binding to printNL(int i) because compiler knows list_int element type

for e_cl_1 in list_3
	printNL(e_cl_1)	#late binding

	class_1=e_cl_1; printNL(class_1)	#early binding

List variable contains reference to list object. When we assign one list var to another we simply copy reference, so the new variable is pointing to the same list. Objects reference counting is done automaticly. If we want a variable pointing to new list, new operator should be used.

list_1_a=list_1 	#list_1_a referes to the same list as list_1

list_1_a=new list 	#list_1_a referes now to new empty list

When list copy is required use copy function : new list object is created and all elements are copied.

list_1_copy=copy(list_1)

List element and subsets are similar to strings. Indexes also start from 0.

index=0
i_0=list_1[index] 		#take list element at index=0

list_1_sub=list_1[1:3] 	#take 1-th,2, but not 3 

Pop operation takes off last list element.

i=pop(list_1)

index=1
delete(list_1,index)	#delete element at index=1

To change list element replace function is to be used. There are two posibilities: first when second argument is a list index and second when for in variable is used.

index=0
new_value=44
replace(list_1,index,new_value)

for e_rep in list_1
	i=e_rep
	if i >10 
		replace(list_1,e_rep,new_value)

Classes

Example class definition:

class Class_1 
	attr_int=0
	attr_str="Ala"
	attr_list=[]		#untyped list
	attr_list_1=[int]	#typed list 

Its easy to notice that there are no methods, instead we have function overloading:

proc printNL(Class_1 class_1)
text="Class_1 :attr_int=" + toStr(class_1.attr_int)
text=text + ", attr_str=" + class_1.attr_str
printNL(text)
proc printNL(Class_2 class_2)

To use objects first declaration is required:

Class_1 class_1

To assign values to attributes and to use them full stop notation is used:

class_1.attr_int=66
class_1.attr_str="xxxx 666"
i=class_1.attr_int

When we assign one object variable to another object reference is copied, not object and afterwards two variables are pointing to the same object. Object reference counting will be described later. When we want a variable pointing to new or copied object the new clause and copy function are to be used respectively.

class_1=new Class_1	#we want class_1 to refere to new Class_1 object
class_1_1=copy(class_1)

Program control

To change program flow if ..eilif else, while, for and goto instructions are used.

x=1

if x=1 :printNL("equal")

if x=1
	printNL("equal")

If subsequent instructions are to be in the same line colon ( : ) must be used.

if x=1
	printNL("1")
elif x=2
	printNL("2")
else
	print("others")

While isnstruction

x=0
while x < 4
	printNL(x);x=x+1

Result :0 1 2 3 .
For loops

for i=0 to 4
	printNL(i)

Result :0 1 2 3 4

for i=4 to 0 step -1
	printNL(i)

Result: 4,3,2,1,0
For e in l loop was described when lists were concerned.

Goto:

x=1
goto et_1

x=2

et_1:	#label name followed by colon 

Functions and procedures

An example function definition:

int sum(int a, int b)=	# = this is important !!!!!!!!! 
	c=a+b
	return c

Equal sign must be used in function definition, this form is not popular but designing the language I had no better idea.

c=sum(a,b)

This assignment may serve as variable c declaration because the compiler knows sum type.

sum1(int a, int b)=	
	c=a+b
	return c

Here function type is resolved from return expression type.
Functiuon names may be overloaded:

int sum(int a, int b, int c)=
	d=a+b+c
	return d

Defining functions recursion may be used.

int factorial(int i)=
	x=1
	if i>0 : x=factorial(i-1);x=x*i
	return x

Procedures are similar:

proc printNL(Class_1 class_1)	#equal sign my be used but is not necsessairy
	text="Class_1 :attr_int=" + toStr(class_1.attr_int)
	text=text + ", attr_str=" + class_1.attr_str
	printNL(text)

Global variables

Global variables are declared using global instruction, all must be same type.

proc incr_g1()
	global g1; int g1
	g1=g1+1

	printNL("global g1 is="+toStr(g1))

global g1

g1=12

incr_g1()

# now g1 is 13

Keyboard and console

keyboard_text=getLine() 

When you press enter key this ends and getLine returns entered chars as str.
As semi debug utility bp() procedure may be used that waits till you press enter key.

File io

To use file io procedures we can use following schema:

file_handler=OpenFile(file_name,type) 
ok=writeLines(file_handler, list_of_strings)
or read_lines_output=readLines(file_handler)
closeFile(file_handler)

Where openFile type is OF_READ, OF_WRITE or OF_CREATE_OVERWRITE

lines=[]

file_name="c:\\fsp_2007\\tut3_lines.txt" 
#in strings \ before special kode so here two \\ are required

file_handler=openFile("c:\\fsp_2007\\tut4_lines.txt", OF_CREATE_OVERWRITE)

ok=writeLines(file_name,lines)
if ok=1 :printNL("write ok")
else:et=getLastError();printNL("write error "+et)

Than read it back from file


OkAndData read_lines_output

class OkAndData
	ok=0
	data=[]

read_lines_output=readLines(file_handler)

if read_lines_output.ok
	printNL("read lines ok")
	lines_read=read_lines_output.data
	print(lines_read)
else:et=getLastError();printNL("read error "+et)

More advanced example

class CirclePoint
	x=0;y=0;ok=0

proc printCircle(int x0, int y0, int r)=
	maxX=80; maxY=40;col_no=0
	if x0<0 or y0 < 0 or r <0 : printNL("wrong input data"); 			return
	if x0+r>maxX or y0+r > maxY :print("wrong input data"); return
	screen=[]
	line=[CirclePoint]

	#prepare list o lists containing points

	for line_no=0 to maxY

		line=new list	#we need new list object

		for col_no=0 to maxX
			point=new CirclePoint	#new point for each line,column

			point.x=col_no; point.y=line_no
			x=point.x; y=point.y*3/2; y00=y0*3/2
			d2=(x-x0)*(x-x0)+(y-y00)*(y-y00)

			if d2 <= r*r: point.ok=1 # ok=1 if point belongs to circle 

			append(line,point)
		append(screen,line)

	printNL()

	for l in screen
		line=l 	

		#necsesary because compiler knows l as list not list of CirclePoint

		for p in line
			if p.ok=1:print("*") 
			else:print(" ")
		printNL() 

Using assembler code

To move data between int, byte variables and processors registers mov instruction is used. We can also define auxiliary procedures and call them directly using call instruction. To see registers printRegs auxiliary procedure may be used. When we have more assembler instruction $asm directive is used to start and end assembler code area.

i=12
mov eax,i
asm mov ebx,eax
asm mov ecx,eax
asm add ecx,12
call printRegs

mov i,ecx
printNL(i)
i=12
mov eax,i
$asm #start assembler code
mov ebx,eax
mov ecx,eax
add ecx,12
call printRegs
$asm #assembler code end

mov i,ecx
printNL(i)

Result is:

0000000C 0000000C 00000018 00DB6690 	: eax, ebx, ecx,edx
00DB6690 00DB0020 00E9F65C 0012FFA4	: ebp, esi, edi, esp
24

Auxiliary assembler procedure:

aux asm_proc()		
	$asm
	add eax,ebx
	inc eax
asm mov eax,1; asm mov ebx,2
call asm_proc 		

C language integration

In Ala we have some additional types to make C language integration easier. First is cstr – null ended text. Next are cstruct and cunion. To call function from external dll use call dll_name.function_name instruction. If function uses Pascal parameter passing convention (like win32 function do) it is possible to write dll_name.fun_name(parameters). Here is an example where actual parameters are put on stack by hand:

asm push 0
asm mov eax,ebp; asm sub eax,4; asm push eax #address of dummy variable
asm mov eax,ebx;call lenCstr;asm push eax #length
asm push ebx#address
asm push F5h #-11
call KERNEL32.GetStdHandle 
asm push eax
call KERNEL32.WriteFile  

En example of parameters list:

call kernel32.ReadFile(hFile, lpBuffer, numberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped) 

Class typeId

Class typeId is an integer number :

CirclePoint circlePoint
i1=CirclePoint.typeId	#class_name.typeId

i2=typeId(circlePoint)	#typeId is function returning variable typeId
i3=typeId(i1)	#integer variable type id is 2, str is 3 , list 4

Addr, ref functions

Addr function returns address of a variable , ref functions returns address of an object referenced by parameter variable.

addr_value=addr(i1)
addr_value=addr(circlePoint)
ref_value=ref(circlePoint)

Microsoft Win32 GUI programming

At the beginning I am going to describe program from window1.ala file. Resulting program creates window with one button. First we need to load necessary gui classes from module gui.

import gui from gui_win32.ala

Than we design class containing object window based on Window class and one control button_new based on Button class. Window component gives us access to window properties and methods.

class MainWindow
	Window window
	Button button_new

At the beginning of MainWindow constructor Window constructor is called. Coordinates x, y refer to left, upper corner of the screen, w is window width and h is window height. Next we have window style, mode that will be described later, parentId where 0 means no parent window and window title at he end. After constructor call window id contains handler (window handle) to the window. GetClientRect returns Rect (x,y,w,h) object where x,y coordinates refer to upper left corner of window client area. It is required because button will be located at the bottom of the window.
Button constructor Button(int x,int y,int w,int h,int style, str title, int parentId), as parentId we use window.id.

MainWindow MainWindow(int x, int y, int w, int h, str title)=
	MainWindow main_window
	
	main_window.window=Window(x,y,w,h,0,0,0,title)

	client_rect=getClientRect(main_window.window)

	button_h=20;button_w=100
	button_x=10;button_y=client_rect.h-button_h-10

	main_window.button_new=Button(button_x, button_y, button_w, button_h, 0, "new", main_window.window.id)

	return main_window

Now I will describe main program, first line sets two global variables hInstance and guiFont.

initGui() #!!!!!!!!!!!!!!!!!!

Next line calls MainWindow constructor, CW_USEDEFAULT constant means that we use operating system default values.

main_window=MainWindow(CW_USEDEFAULT,0,CW_USEDEFAULT,0, "window Ala")

Now we must show the window:

show(main_window.window)

At the end typical window message loop:

eventLoop()

As window style all win32 window styles may be used. As far as the mode is concerned 0 means WINDOW_MODE_PARALLEL (modeless window : does not disable his parent window) , 1 WINDOW_MODE_EXCLUSIVE alone (other windows are disabled).

In window2.ala example we draw simple figures.
First in window constructor we bind WM_PAINT message with windowPaint procedure.

MainWindow MainWindow(int x, int y, int w, int h, str title)=
	bind main_window.window, WM_PAINT,windowPaint(main_window)

Then at the begin of windowPaint procedure user32.BeginPaint procedure is called to start drawing, this procedure returns handle to drawing device context. At the end user32.EndPaint is called.

proc windowPaint(MainWindow main_window)=
	PAINTSTRUCT ps; hdc=0
	call user32.BeginPaint(main_window.window.id,ps); mov hdc,eax

	drawHouse(hdc,100,100) #!!!!!!!!!

	call user32.EndPaint(main_window.window.id,ps)

proc drawHouse(int hdc, int x0,int y0)=

	dd=100
	#draw main wall
	x1=x0+dd;y1=y0+dd;w=dd+dd;h=w; color=GREEN
	drawRect(hdc,x1,y1,w,h,color,color)

	ddX=w/7;ddY=h/6
	x2=x1+ddX; y2=y1+2*ddY; w2=2*ddX;h2=2*ddY ;color=WHITE;colorBorder=BLACK
	drawRect(hdc,x2,y2,w2,h2,color,colorBorder)

	x=x2+ddX; drawVerLine(hdc,x,y2,h2,colorBorder)
	y=y2+ddY; drawHorLine(hdc,x2,y,w2,colorBorder)

	x3=x1+4*ddX; y3=y2; w3=2*ddX;h3=4*ddY;color=BLUE
	drawRect(hdc,x3,y3,w3,h3,color,color)

	#draw the roof
	xr=x1;yr=y0;wr=2*dd;hr=dd;color=RED
	drawRect(hdc,xr,yr,wr,hr,color,color)

	for dy=1 to hr-1
		dx=dy*2/3
		x=xr-dx; y=yr+dy
		drawHorLine(hdc,x,y,dx,color)
	xr=xr+wr
	for dy=1 to hr-1
		dx=dy*2/3
		x=xr; y=yr+dy
		drawHorLine(hdc,x,y,dx,color)

	#draw a text on main wall
	x=x1+10;y=y1+10
	drawText(hdc,x,y,"Alice has a cat",color)

File window3.ala contains two important lines. First line binds button, mouse left button click event and whenButtonNewClicked procedure.

MainWindow MainWindow(int x, int y, int w, int h, str title)=

	bind main_window.button_new, WM_LBUTTONDOWN,
		whenButtonNewClicked(main_window)  

Second line sends WM_PAINT message to the window through draw() procedure.

proc whenButtonNewClicked(MainWindow main_window)

	draw(main_window.window)  

Now window controls and window4.ala example.

	control_x=20;control_y=20;control_w=200;control_h=20
	main_window.text_box=TextBox(control_x, control_y, control_w, control_h,0, "" ,main_window.window.id)

Control x,y referes to left,upper corner of window client area, and main_window.window.id is a parent window id.

For ListBox and ComboBox :

	addItem(main_window.list_box,item_text)

For RadioButtons to start new group – seting one element resets the others – WS_GROUP style must be used.

main_window.radio_button_1=RadioButton(control_x, control_y, control_w,control_h, WS_GROUP,"radio button 1",main_window.window.id)

As far as the |TreeList is concerned wen we add element, new element handle is returned, and this handle is to be used to add subelements:

	master_item_id=addItem(main_window.tree_list,item_text)

	subitem_id=addItem(main_window.tree_list, master_item_id, item_text) 

To read control data:

MainWindow MainWindow(int x, int y, int w, int h, str title)=
	MainWindow main_window
	
	main_window.window=Window(x,y,w,h,0,0,0,title)

	client_rect=getClientRect(main_window.window)

	#textBox
	control_x=20;control_y=20;control_w=200;control_h=20
	main_window.text_box=TextBox(control_x, control_y, control_w,control_h,0,"",main_window.window.id)

	#ListBox
	control_x=300;control_y=20;control_w=200;control_h=300
	main_window.list_box=ListBox(control_x, control_y, control_w,control_h, 0,"",main_window.window.id)

	for i= 1 to 30
		item_text="Alice "+toStr(i)
		addItem(main_window.list_box,item_text)


	#combobox
	control_x=550;control_y=20;control_w=200;control_h=200
	main_window.combo_box=ComboBox(control_x, control_y,control_w, control_h, 0,"",main_window.window.id)

	for i= 1 to 5
		item_text="Alice has a cat "+toStr(i)
		addItem(main_window.combo_box,item_text)


	#checkBox

	control_x=20;control_y=300;control_w=200;control_h=20
	main_window.check_box=CheckBox(control_x, control_y,control_w, control_h,0,"check box",main_window.window.id)

	setCheck(main_window.radio_button_1,1) #!!!!!!!!!!!!!!!

	#static
	control_x=200;control_y=300;control_w=100;control_h=20
	main_window.static=Static(control_x, control_y, control_w,control_h,0,"Static 1",main_window.window.id)


	#group of radio buttons 
	control_x=20;control_y=350;control_w=200;control_h=20
	main_window.radio_button_1=RadioButton(control_x, control_y,control_w, control_h,WS_GROUP, "radio button 1",main_window.window.id)


	control_x=20;control_y=350+20;control_w=200;control_h=20
	main_window.radio_button_2=RadioButton(control_x, control_y,control_w, control_h,0,"radio button 2",main_window.window.id)


	control_x=20;control_y=350+40;control_w=200;control_h=20
	main_window.radio_button_3=RadioButton(control_x, control_y,control_w, control_h,0,"radio button 3",main_window.window.id)


	#next group of radio buttons style=WS_GROUP starts new group

	control_x=20;control_y=420;control_w=200;control_h=20
	main_window.radio_button_a=RadioButton(control_x, control_y,control_w, control_h,WS_GROUP,"radio button a",main_window.window.id)


	control_x=20;control_y=420+20;control_w=200;control_h=20
	main_window.radio_button_b=RadioButton(control_x, control_y,control_w, control_h,0,"radio button b",main_window.window.id)


	control_x=20;control_y=420+40;control_w=200;control_h=20
	main_window.radio_button_c=RadioButton(control_x, control_y,control_w, control_h,0,"radio button c",main_window.window.id)

	#Tree list
	control_x=300;control_y=350;control_w=200;control_h=300
	main_window.tree_list=TreeList(control_x, control_y,control_w,control_h,0,"",main_window.window.id)

	for i= 1 to 30
		item_text="Alice in tree "+toStr(i)
		
		master_item_id=addItem(main_window.tree_list,item_text)
		item_text="leaf in tree "+toStr(i)
		subitem_id=addItem(main_window.tree_list,master_item_id,item_text) 

	#push button

	button_h=20;button_w=100
	button_x=10;button_y=client_rect.h-button_h-10

	main_window.button_new=Button(button_x, button_y, button_w,button_h,0,"new",main_window.window.id)

	bind main_window.button_new, WM_LBUTTONDOWN,whenButtonNewClicked(main_window)  


	return main_window


proc whenButtonNewClicked(MainWindow main_window)

	check_box_state=getCheck(main_window.check_box)
	printNL("button clicked: checkBox check is " + 
		toStr(check_box_state))

	radio_state=getCheck(main_window.radio_button_1)
	printNL("radio button 1 state is "+toStr(radio_state))
	
	radio_state=getCheck(main_window.radio_button_a)

	printNL("radio button a state is "+toStr(radio_state))

	printNL("text box text is:"+getText(main_window.text_box))

	printNL("list box selected item index="+
		toStr( getCurSel(main_window.list_box)))

	printNL("combo box text is:"+getText(main_window.combo_box))

	setCheck(main_window.check_box,0)

And here is the result:

Window5.ala is an example where we will build advanced gui element and where virtual functions will be used.

Our component DbGrid will consist of two controls:

class DbGrid
		Control dataWnd
		Control header

First is a window where rows of data are displeyed and the second is a header. When data rows are to be painted, getField function is called.

proc paintData(DbGrid dbg)=

			text=getField(dbg,lineNo,colNo)

This function is declared as virtual:

str getField(DbGrid dbGrid, int row, int col)=virtual

When component is used we must bind virtual function with real function that will supply data.

str getFieldHere(int row, int col)=
	res="row="+toStr(row)+" col="+toStr(col)
	return res

MainWindow MainWindow(int x, int y, int w, int h, str title)=


	virtual getField(main_window.data_grid, int, int) = 
		getFieldHere( int, int)

The result :

Very similar result using two DataGrid components from gui module(window6.ala).

RDBMS interface

Nowadays relational databases are very important. As an example posgresql in tut6.ala is concerned.

import postSql from post_sql.ala

conn_str = "hostaddr=169.254.64.211 dbname = test user=postgres password=postgres"

conn=connectDB(conn_str) 

In the following code table table_1 with two columns, will be used. First column id is int4 postgresql type and second column is text (text type). The following sql command may be used to create table:

create table table_1( id int4, text text)

Now code to connect to database test, execute insert command and execute select query.

id=0; text=""
if isConnectionOk(conn)
	sql="insert into table_1(id,text) values(4,'Alice has a cat')"
	execSqlCmd(conn,sql) #execute sql command

	sql="select id,text from table_1"
	query_res=execSql(conn,sql) #execute sql query
	rows=countSqlRows(query_res)

	checkIfEquivalent(typeId(id),query_res,0) 	#is id field int
	checkIfEquivalent(typeId(text),query_res,1)	#is text field str

	for i=0 to rows-1
		printNL("row "+toStr(i));print(" ")
		id=getIntField(query_res,0)		#get field 0
		print(id);print(" ")
		text=getStrField(query_res,1)		#get field 1
		print(text)
		nextQueryResultRow(query_res)

	clearQueryResult(query_res)

	closeDBConnection(conn) #!!!!!!!!!!!!!!!!!
	
else
	s=connectionError(conn);printNL(s)

Modules

There are two reasons to use modules in Ala. First is code reuse. The example is gui module that contains classes and functions needed to create user interface.

import gui from gui_win32.ala

To use classes and procedures from imported module we do not need any prefix. The commpiler first tries to find class/function in current module, then in imported modules and in base module at the end. Secondly we use modules when program code become to large to handdle in one file espetially when compile time is concerned. When we first time compile after ide startup, base and imported modules are compiled and result is hold in memory. When we compile next time only modules from current file are compiled and base and imported modules executable code is added from memory.

Reference counting

There are two methods to handle objects: reference counting nad garbage collectors. Each method has its lights and shadows. One of the main aims to develop new programming language was performance, so I decided to use reference counting. Direct recursion in class designs is not allowed:

class Class_1
	Class_2 attr_x

class Class_2
	Class_1 attr_x

However when we need recursion in class definition we can use all type:

class Class_1
	all attr_all  #here we can store Class_2 objects.

class Class_2
	Class_1 attr_x

Concurrent programming

When we have two or more processors( or cores) its possible to fork program execution. When done properly performance gain is very big and scales nearly in linear way when processors (cores) are added. Now two cores are common and in near future four and eight are expected. Current impementation of Ala uses one or two cores. To use second processor core first we mast set ide option mt to 1. Ater this exectutable code file name will change to xxx_mt.exe. Then in main code we must start second program thread:

initMT()

An example from tut7.ala:

for e in mainList
	ok1=e
	if isOdd(ind)

		setLen(ok1) con#!!!!!!!!!!!!
			
	else

		setLen(ok1)#!!!!!!!!!!!!

		waitThreadReady()

	ind=ind+1

The idea is to first call setLen example procedure concurently on second core , than for another element from list mainList to call example procedure on main core. When main core procedure is finished than we wait untill concurrent procedure is over.
The output is:

elapsed time when concurrent 4.234 sec

elapsed time on one core 7.704 sec

InitMT starts new thread. Calling function/procedure concurrently(con attribute) function address and parameters are passed to this thread. Compiler responsibility is to add alock and unlock operations when objects are created, global variables are used and print function is called.

Example code files

tut1.ala: int operations
tut2.ala: list operations
tut3.ala: if,while,for,functons and procedures, globals
tut4.ala: console and file io
tut5.ala: more advanced function example and assembler code integration
tut6.ala: postgresql programing
tut7.ala: concurrent code

window1.ala: simple window
window2.ala: drawing
window3.ala: binding events
window4.ala: controls
window5.ala: making your own components
window6.ala: using DataGrid component