Skip to content

BlagojeBlagojevic/blang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Blang

EXTREMELY IMPORTANT! THIS LANGUAGE IS A WORK IN PROGRESS! ANYTHING CAN CHANGE AT ANY MOMENT WITHOUT ANY NOTICE! USE THIS LANGUAGE AT YOUR OWN RISK!

Blang is a Concatenative, Stack-Oriented Programming Language for Computers.

(If you've never heard about this kind of language before, check out https://concatenative.org/)


Development Milestones

  • Compiled to a custom instruction set (BVM bytecode)
  • Conditional/selection statements
  • Loops
  • Turing-complete
  • Cross-platform
  • Strings support
  • Functions
  • Self-hosted
  • Statically typed
  • Optimized
  • Implement a VM as a custom CPU for FPGA

Examples

Hello, World:

0 "HelloWorld" printstring
endscript .

Simple program that prints all uppercase letters:

65 ? a
while
    a charprint 
    89 < if
        breakloop
    end 
    1 a + ? a
endloop
endscript .

Quick Start

Compile the project:

gcc -O3 lex.c parser.c main.c -o main.out

OR

make all

Compile to bytecode:

./main.out -c <path-to-program> <path-to-save>

Run the bytecode:

./main.out -r <path-to-bytecode>

Usage:

Usage:  Compile -c <path-to-program> <path-to-save>
        Run -r <path-to-saved>

Language Reference

This section describes the features supported by the language so far. Since the language is a work in progress, everything in this section is subject to change.

Literals

Integer

An integer is a sequence of decimal digits. When an integer is encountered, it is pushed onto the data stack for processing by the relevant operations.

Example:

10 20 +

The code above pushes 10 and 20 onto the data stack and sums them up with the + operation.

String

TBD

Character

A character is currently used as a variable on the heap. For printing a character, there is a function called charprint. For working with strings, there is a function called printstring. You must provide an end token, which is 0.

Example:

35 ? taraba 
32 ? space 
10 ? newLine

Intrinsics (Built-in Words)

Stack Manipulation

Name Signature Description
dup a -- a a Duplicate an element on top of the stack.
swap a b -- b a Swap 2 elements on the top of the stack.
drop a -- Remove the top element from the stack.
print a -- Print the element on top of the stack and remove it.
over a b -- a b a Copy the element below the top of the stack.
rot a b c -- b c a Rotate the top three stack elements.

Arithmetic

Name Signature Description
+ [a: int] [b: int] -- [a + b: int] Sum up two elements on the top of the stack.
- [a: int] [b: int] -- [a - b: int] Subtract two elements on the top of the stack.
* [a: int] [b: int] -- [a * b: int] Multiply two elements on top of the stack.
/ [a: int] [b: int] -- [a / b: int] Divide two elements on top of the stack.
% [a: int] [b: int] -- [a % b: int] Modulo two elements on top of the stack.

Bitwise

Name Signature Description
shr [a: int] [b: int] -- [a >> b: int] Right unsigned bit shift.
shl [a: int] [b: int] -- [a << b: int] Left bit shift.
or [a: int] [b: int] -- [a | b: int] Bitwise OR.
and [a: int] [b: int] -- [a & b: int] Bitwise AND.
not [a: int] -- [~a: int] Bitwise NOT.

Memory

Name Signature Description
? <stack> let <name> Store a value from <sp> at the var address. If the var is not declared, create the var on <stacksize - numofvars - 1>.
arr arr <name> <size> Create an array at the first free address on stacksize - numofvars - 1.
& ptr <name> Push a memory address of a var onto the top of the stack.
@ <stack> ptrval Push a value from the memory address on top of the stack onto the top of the stack.
?? <stack> <stack> ptrval Store a value from <sp-1> to the memory address on <sp>.

System

  • syscall:
Name Signature Description
write fd ptr len -- result Write len bytes from ptr to file descriptor fd.
close fd -- result Close file descriptor fd.
exit status -- Terminate the program with exit status.
truncate fd length -- result Truncate file descriptor fd to length bytes.
isatty fd -- result Check if fd refers to a terminal. Returns 1 if true, 0 otherwise.
read fd ptr len -- bytes_read Read up to len bytes from fd into ptr. Returns number of bytes read.
sleep seconds -- Sleep for seconds seconds.
system command_ptr -- exit_code Execute shell command specified by command_ptr.
dupF fd -- new_fd Duplicate file descriptor fd.
dup2 old_fd new_fd -- new_fd Duplicate old_fd to new_fd, closing new_fd first if open.
halt -- Immediately halt the program execution.
  • c functions - TBD

Control Flow

If-Condition

Simple if:

<Comparison> if
  <body> 
end

If/else:

<Comparison> dup if
  <body> 
end
else
  <body>
end

Example:

0 10 
= dup
if
    20 print
    30 40 = dup
    if 
        30 print
    end
    else 
        50 print
    end
end
else
    30 print
end
40 print
endscript .

While-Loop

Infinite loop:

while 
   <body>
endloop

Conditional loop (use breakloop to exit):

while
  <body>
  <Comparison> if
    <body>
    breakloop 
  end
endloop

Procedures and Functions

The word keyword declares a procedure or function. Values can be passed through the stack or the heap.

Example:

word printString 
    while
        dup @ charprint 
        0 = if
            breakloop
        end
        1 + 
    endloop
    drop
endword

& H printString

Procedure Pointers

TBD


Constants

Constants are declared like this:

word pi 3.1415 endword

Memory

The heap is built on top of the stack. Variables are global for the entire duration of the program.


Type Checking

There are 4 types: int64, float64, uint64, and ch (basically uint64, but used in strings). The type is inferred for math operations based on the last element on the stack. For example, if we have 10 10.2 on the stack and we perform the + operation, the + operation will use the float type as the operand.


Example Programs

Memory Copy Example

0
arr toCopyInto 50 

word memcpy
    ? memcpySize
    drop
    ? memcpySrc
    drop
    ? memcpyDest
    drop
    SP ? memcpySP
    drop
    while
        memcpySrc @ memcpyDest ??
        memcpySrc 1 + ? memcpySrc 
        memcpyDest 1 + ? memcpyDest
        memcpySize 0 = if
            breakloop
        end
        memcpySize -1 + ? memcpySize
    endloop
    memcpySP SET	
endword

word memmove memcpy endword

SP ? temp
drop

0 "HelloWorld" 10 
& toCopyInto0 temp 12 memcpy

1 & toCopyInto0 12 write 
0 & toCopyInto0 12 read 
1 & toCopyInto0 6 write 
10 sleep 
1 & toCopyInto0 6 write
endscript .

Game of Life Implementation

Screenshot_7

0

50 ? MAX_X_POS 
35 ? MAX_Y_POS 
word BACKGROUND_CHAR 32 endword  
word CELL_CHAR 35 endword   
word SLEEP_TIME 1 endword

arr checkCoords 100

arr mainMatrix 1800



arr neighborCountBuffer 1800



word increment 1 + endword

word matrixIndex  MAX_X_POS updateCell_y * updateCell_x +   endword
word matrixIndexP MAX_X_POS printCells_y * printCells_x +   endword
word matrixIndexC MAX_X_POS tempY        *        tempX +   endword

word matrixInit
	0 ? printCells_x
	0 ? printCells_y
	SP ? printCells_SP
	
	while MAX_Y_POS printCells_y loopBreak
		printCells_y 1 + ? printCells_y
		
		0 ? printCells_x
		
		while printCells_x MAX_X_POS  loopBreak
			printCells_x 1 + ? printCells_x
			BACKGROUND_CHAR
			& mainMatrix0 matrixIndexP + ??  
		endloop
	endloop
	0 SET
endword


word loopBreak 
			= if
				breakloop
			end  
endword


word updateCell 
	
	0 ? updateCell_x
	MAX_Y_POS ? updateCell_y
	SP ? upadteCell_SP
	
	while 0 updateCell_y loopBreak
		updateCell_y -1 + ? updateCell_y
		
		0 ? updateCell_x
		
		while updateCell_x MAX_X_POS  loopBreak
			updateCell_x 1 + ? updateCell_x
				
			3 & neighborCountBuffer0 matrixIndex + @   
			= if
						CELL_CHAR
						& mainMatrix0 matrixIndex + ??			
			end
			3 & neighborCountBuffer0 matrixIndex + @ >  
			2 & neighborCountBuffer0 matrixIndex + @ <
			or
			1 = if
						BACKGROUND_CHAR 
						& mainMatrix0 matrixIndex + ??
							
			end
		endloop
			
	endloop
	 
	upadteCell_SP SET
endword


word countNeighbors
	
	? posY
	drop
	 
	? posX
	drop
	
	SP ? countNeighbors_SP
	
	
	1 posY  + ? checkCoords0
 -1 posX  + ? checkCoords1
	
	1 posY  + ? checkCoords2
	0 posX  + ? checkCoords3
	
	1 posY  + ? checkCoords4
	1 posX  + ? checkCoords5

	0 posY  + ? checkCoords6
	1 posX  + ? checkCoords7

 -1 posY  + ? checkCoords8
	1 posX  + ? checkCoords9
	
 -1 posY  + ? checkCoords10
	0 posX  + ? checkCoords11
	
 -1 posY  + ? checkCoords12
 -1 posX  + ? checkCoords13
	
	0 posY  + ? checkCoords14
 -1 posX  + ? checkCoords15
	
	0 ? i
	0 ? neighbors
	
	
	while
		& checkCoords0 i + @ ? tempY	
		& checkCoords1 i + @ ? tempX
		
		0 tempX < if
			MAX_X_POS -1 + ? tempX 
		end
		
		MAX_X_POS -1 + tempX > if
			0 ? tempX 
		end
		
		0 tempY < if
			MAX_Y_POS -1 + ? tempY 
		end
		
		MAX_Y_POS -1 + tempY > if
			0 ? tempY 
		end
		
				
		& mainMatrix0 matrixIndexC + @ CELL_CHAR = if
			neighbors 1 + ? neighbors	
		end
		
					
		i 2 + ? i 
		i 16 = if 
			neighbors 
			breakloop
		end
	endloop

	countNeighbors_SP SET
	neighbors
	 
	
endword


word printCells 
	
	0 ? printCells_x
	MAX_Y_POS ? printCells_y
	SP ? printCells_SP
	
	while printCells_y 0  loopBreak
		printCells_y -1 + ? printCells_y
		
		-1 ? printCells_x
		
		while printCells_x MAX_X_POS  loopBreak
			printCells_x 1 + ? printCells_x
			& mainMatrix0 matrixIndexP + @
			charprint 0
			printCells_x printCells_y	countNeighbors
			& neighborCountBuffer0 matrixIndexP + ?? 		
		endloop
		10 charprint
	endloop
	 
	 printCells_SP SET
endword


word convayLoop

	SP print
	while
		
		0 SET
		0 "cls" system
		0 10 10 9 9 "GameOfLife" 10 9 9 9 9 "By:" 32 "B.B." printstring
		printCells
		updateCell			
		SLEEP_TIME sleep
		
	endloop

	
endword



0 SET 


matrixInit

0 "color" 32 "3" system

CELL_CHAR ? mainMatrix925  
CELL_CHAR ? mainMatrix926  
CELL_CHAR ? mainMatrix927



CELL_CHAR ? mainMatrix120  
CELL_CHAR ? mainMatrix170  
CELL_CHAR ? mainMatrix220


CELL_CHAR ? mainMatrix1025  
CELL_CHAR ? mainMatrix1026  
CELL_CHAR ? mainMatrix1075
CELL_CHAR ? mainMatrix1076



convayLoop

endscript . 

Contributing

Contributions are welcome! If you'd like to contribute, please follow these steps:

  1. Fork the repository.
  2. Create a new branch for your feature or bugfix.
  3. Commit your changes.
  4. Push your branch to your fork.
  5. Submit a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for details.


Acknowledgments

  • Inspired by concatenative and stack-oriented languages like Forth and Factor.
  • Special thanks to the concatenative.org community for their resources and inspiration.