Code Examples

1 - Comments

2 - Basics

    2.1 - Operators

    2.2 - Variables

        2.2.1 - Numbers

        2.2.2 - Strings

        2.2.3 - Table

        2.2.4 - Enums

        2.2.5 - Functions

        2.2.6 - Userdata

3 - Premade userdatas

    3.1 - Interop

    3.2 - Socket and Packet

4 - Looping

    4.1 - While

    4.2 - For

    4.3 - Foreach

5 - Goto

6 - Precompiler


1 - Comments

// single line
/* multi line comments */

2.1 - Operators

// All the basic operators such as +, -, *, /, <=, >=, ==, !=, &&, ||

// %: then we have the modulo, used on numbers this returns the remainer.
local var = 1 % 2 // will return 1

// When used on a string and a table, they become a formater.
local var = "%b%" % {"a", "c"} // will return "abc"

// !: short equals false
local var = !boolvar // false, will compile as "local var = false == boolvar"

// ++ and --
var++
var--
++var
--var

// #: The counter. Use this infront of a string, table or userdata to get the number of letters/objects inside of it.
local var = #"hello" // returns 5
local var = #{1, "a", "b", "c", 131} // returns 5

2.2 - Variables

// There are 2 types of variables, Locals, and Globals.
// Locals are bound to their own scope (and childs), and are fast to use.
// Globals are stored in the BromScript::Instance, and using them will be somewhat more expensive
// so try to make as much variables local as you can to keep speed!

local localvar = "local"
globalvar = "global"

// Because globalvar does not have local infront of it, it will check if globalvar has been defined as a local before
// if not, this will redirect the call to the globals inside of the BromScript::Instance


// You can also use typesafe variables
local number somenumber = 15

// now the compiler knows it's an number, and it can take that into account while optimizing

2.2.1 - Numbers

// All numeric values are doubles, 8 byte floating point numbers.

print(10 + 10) // 20
print(10 * 10) // 100
print(10 - 10) // 0
print(10 / 10) // 1
print(10 % 10) // 0
print(10 ^ 10) // 10000000000

2.2.2 - Strings

// Strings are NULL indexed, so putting a null character in it would be a bad idea.

print("a" + 1) // a1
print(1 + "a") // 1a
print("a" + "b") // ab
print("a" * 2) // aa
print("aa" / 2) // a
print(#"hello") // 5

2.2.3 - Table

// Tables are null indexes, which means the first element is at [0]
// All indexes are considered strings. Meaning that ["0"] will be the exact same this as [0]
// and it will be a bit faster due it doesn't need to convert the value afterwards

local tbl = {1, 2, 3, 4, 5}
print(tbl[0]) // 1
print(#tbl) // 5

2.2.4 - Enums

// an Enum works pretty much like a table, but defining them works a bit different.
// Every key refers to his index in that table, which can be modified by adding an asignment to it

local peopleToMakeDisappear = enum {
	Bill, // indexing starts at 0, and as it's Bill's turn tonight, no need to change it!
	Henk = 7, // but henk's turn is next week
	Joe, // but Henk and Joe are both tiny, so they fit together, so the day after Henk, the 8th day, it's his turn.
	NotFailcake = Math.Pi / 0 // the asignment gets parsed, so you can put anything in there that is a number
}

// Just to be sure, all names used are fictional, and using those names would ruin the surprise for them.

2.2.5 - Functions

// you can define a function on 2 ways. Being named or being anonymous.
// both of them can be localized by putting "local" infront of them.

// named
function example() {
	return 42
}

// anonymous
example = function() {
	return 42
}

// arguments can be supplied too ofcourse
function argfunc(argA, argB) {

}

// you can also make your function typesafe by putting a type infront of it
// now you're sure that everything that gets trough will be a number
function typesafefunc(number a, number b) {
	return a * b
}

// strings, bools and numbers get copied as arguments, rather than references, as that's what you'd expect
// all others will be sent as references
// if you don't want to copy them, that's okay. you can tell the compiler to stop it's evil cloning process
// and pass it along as reference anyway by using the & sign infront of the name
function test(number &a, number b) {
	a++
	b++
}

local numa = 0
local numb = 0
test(numa, numb)
print(numa, numb)
// output will be 1<tab>0

2.2.6 - Userdata

// When userdata is registered, we can use the new operator to call the constructor function
local data = new RawData()

// now we can call registered functions on this object
data.WriteInt(42)

// and if the class has registered members or operators, you can also use them.
// CVector is a custom class as used in the implementation docs
local vec = new CVector()
vec.x = 15

vec = new CVector(40, 40, 40) + new CVector(2, 2, 2)

3.1 - Interop

// Interop userdata.
// Here's a SDL2 pong game.
// NOTE: Interop only works on windows currently, not on linux.

// config
local windowWidth = 512
local windowHeight = 512
local paddleHeight = 70
local paddleWidth = 5
local ballSize = 10

// first we'll have to load in the dll (.exe, .so, etc)
local iop = new Interop("SDL2.dll")

// then we'll load methond, providing the name, the return type, and a table with all the arguments it expects
// the userdata has the Call operator overriden, so it'll behave as any other function
local im_SDL_Window = iop.GetMethod("SDL_CreateWindow", "int", {"string", "int", "int", "int", "int", "int"})
local im_SDL_Init = iop.GetMethod("SDL_Init", "void", {"int"})
local im_SDL_Delay = iop.GetMethod("SDL_Delay", "void", {"int"})
local im_SDL_Destroy = iop.GetMethod("SDL_DestroyWindow", "void", {"int"})
local im_SDL_GetWindowSurface = iop.GetMethod("SDL_GetWindowSurface", "int", {"int"})
local im_SDL_FillRect = iop.GetMethod("SDL_FillRect", "void", {"int", "int", "int"})
local im_SDL_UpdateWindowSurface = iop.GetMethod("SDL_UpdateWindowSurface", "void", {"int"})
local im_SDL_Quit = iop.GetMethod("SDL_Quit", "void", {})
local im_SDL_PollEvent = iop.GetMethod("SDL_PollEvent", "int", {"int"})
local im_SDL_SetWindowTitle = iop.GetMethod("SDL_SetWindowTitle", "void", {"int", "string"})

local function CreateRect(x, y, w, h){
	local ret = new RawData(16) // x y w h ints
	
	ret.SetInt(0, x)
	ret.SetInt(4, y)
	ret.SetInt(8, w)
	ret.SetInt(12, h)
	
	return ret
}

local windowRect = CreateRect(0, 0, windowWidth, windowHeight)

local scoreA = 0
local scoreB = 0

local event = new RawData(58) // size of sdl event
local ball = CreateRect(windowWidth / 2 - ballSize / 2, windowWidth / 2 - ballSize / 2, ballSize, ballSize)

local playerA = CreateRect(5, 5, paddleWidth, paddleHeight)
local playerB = CreateRect(windowWidth - 5 - paddleWidth, 5, paddleWidth, paddleHeight)

if (im_SDL_Init(0x00000020) < 0){
	error("could not init SDL D:")
}

local whandle = im_SDL_Window("BromScript pong", 100, 100, windowWidth, windowHeight, 0x00000002)
if (whandle == 0){
	error("could not make window D:")
}

local surface = im_SDL_GetWindowSurface(whandle);
if (surce == 0){
	error("could not load surface! D:")
}


local inkeys = {}
local ballVelocity = {1, -1}

while(true){
	while(im_SDL_PollEvent(event.GetReference()) != 0){
		local etype = event.GetInt(0)
		
		if (etype == 256){
			im_SDL_Destroy(whandle)
			im_SDL_Quit()
			return
		}
		
		if (etype == 768){ // key in
			inkeys[event.GetInt(16)] = true
		}elseif (etype == 769){ // key out
			inkeys[event.GetInt(16)] = false
		}
	}
	
	if (inkeys[82]){
		local plyY = playerA.GetInt(4)
		if ((plyY - 4) >= 0){
			playerA.SetInt(4, plyY - 4)
		}
	}
	
	if (inkeys[81]){
		local plyY = playerA.GetInt(4)
		if ((plyY + 4) <= (windowHeight - paddleHeight)){
			playerA.SetInt(4, plyY + 4)
		}
	}
	
	local bx = ball.GetInt(0) + ballVelocity[0]
	local by = ball.GetInt(4) + ballVelocity[1]
	
	if (bx <= 0){
		scoreB++;
		bx = windowWidth / 2
		by = windowHeight / 2
		
		im_SDL_SetWindowTitle(whandle, "BromScript pong: A: %, B: %" % {scoreA, scoreB});
	}elseif (bx >= (windowWidth - ballSize)){
		scoreA++;
		bx = windowWidth / 2
		by = windowHeight / 2
		
		im_SDL_SetWindowTitle(whandle, "BromScript pong: A: %, B: %" % {scoreA, scoreB});
	}
	
	local plyAY = playerA.GetInt(4)
	if (bx < (5 + paddleWidth)){
		if ((by + ballSize) > plyAY){
			if (by <= (plyAY + paddleHeight)){
				ballVelocity[0] *= -1
				
				local rem = (plyAY + paddleHeight / 2) - by
				rem /= paddleHeight / 2
				ballVelocity[1] = rem
			}
		}
	}
	
	local plyBY = playerB.GetInt(4)
	if (bx + ballSize >= (windowWidth - 5 - paddleWidth)){
		if((by + ballSize) > plyBY){
			if (by <= (plyBY + paddleHeight)){
				ballVelocity[0] *= -1
				
				local rem = (plyBY + paddleHeight / 2) - by
				rem /= paddleHeight / 2
				ballVelocity[1] = rem
			}
		}
	}
	
	if (by <= 0) ballVelocity[1] *= -1
	if (by >= (windowHeight - ballSize)) ballVelocity[1] *= -1
	
	
	ball.SetInt(0, bx)
	ball.SetInt(4, by)
	
	// playerB is AI, cheat :D
	local nby = by - paddleHeight / 2
	if (nby < 0) nby = 0
	if (nby > (windowHeight - paddleHeight)) nby = windowHeight - paddleHeight
	playerB.SetInt(4, nby)
	
	im_SDL_FillRect(surface, windowRect.GetReference(), 0x000000)
	im_SDL_FillRect(surface, ball.GetReference(), 0xFFFFFF)
	im_SDL_FillRect(surface, playerA.GetReference(), 0xFF0000)
	im_SDL_FillRect(surface, playerB.GetReference(), 0x0000FF)
	
	im_SDL_UpdateWindowSurface(whandle)
	
	GC.Run()
	sleep(1)
}

3.2 - Socket and Packet

// The socket userdata
// You'll have full acces to any networking thing you'd want to do with this.

// Example HTTP connection
local sock = new Socket()
sock.Connect("google.com", 80)

local p = new Packet()
p.WriteLine("GET / HTTP/1.1")
p.WriteLine("Host: www.google.com")
p.WriteLine("")

sock.Send(p)

while(true){
	p = sock.Receive(1024)
	
	if (p){
		print(p.ToString())
	}
}


// Example webserver
local s = new Socket()
s.Host(80)
s.SetBlocking(true)

local filetypes = {
	txt = "text/html",
	html = "text/html",
	css = "text/html",
	gif = "image/gif",
	png = "image/png"
}

while(true){
	local c = s.Accept()

	if (c){
		c.SetBlocking(true)
	
		local p = c.Receive(100)
		local path = p.ToString()
		path = String.Sub(path, String.IndexOf(path, "/"))
		path = String.Trim(String.Sub(path, 1, String.IndexOf(path, " ")))
		
		if (path == "")
			path = "index.html"
		
		path = "www/" + path
		local ext = "txt"
		if (String.IndexOf(path, ".") > -1)
			ext = String.Sub(path, String.IndexOf(path, ".") + 1)
			
		if (!filetypes[ext])
			ext = "txt"
		
		print("%:%" % {c.GetIP(), c.GetPort()}, path, ext)
		
		local file = new IO()
		if (file.Open(path, "rb")){
			p = new Packet()
			p.WriteLine("HTTP/1.0 200 OK")
			p.WriteLine("Date: Fri, 31 Dec 1999 23:59:59 GMT")
			p.WriteLine("Content-Type: " + filetypes[ext])
			p.WriteLine("Content-Length: " + file.GetLength())
			p.WriteLine("")
			p.WriteBytes(file.ReadToEnd())
			c.Send(p)
			file.Close()
		}else{
			p = new Packet()
			p.WriteLine("HTTP/1.0 404 FILE NOT FOUND")
			p.WriteLine("Date: Fri, 31 Dec 1999 23:59:59 GMT")
			p.WriteLine("Content-Type: text/html")
			p.WriteLine("Content-Length: 15")
			p.WriteLine("")
			p.WriteLine("File not found!")
			c.Send(p)
		}
	}
}

4.1 - While

local i = 0

while(true) {
	if (i == 50) continue
	if (i++ > 100) break

	print("hello") // prints this 99 times
}

4.2 - For

// single statment for loop
for(local i = 0, i < 100, i++) {
	if (i == 10) continue
	if (i++ > 50) break

	print("hello") // prints this 50 times
}

// multi statment for loop
for(local i = 0; local i2 = 0, i < 100, i++; i2++) {
	if (i == 10) continue
	if (i++ > 50) break

	print("hello", i2) // prints this 50 times
}

4.3 - Foreach

// foreach works like so:
local tbl = {1, 2, 3}
foreach (k, v, tbl) {
	print(k, v)
	
	// prints:
	// 1 1
	// 2 2
	// 3 3
}

// The arguments are keyname, valuename and the iteration object
// if you have a custom class and want to support foreach, then all you need to do is add a method
// called "NextKey", and return the indexing key for the next entry.
// After "NextKey" is called, the get index operator [] is called with the returned key.
// return null if the end of the object has been reached

5 - Goto

// goto is a magical keyword, and it can be very usefull
// but also can cause undefined behaviour while executing when used wrong (crashes, memory corruption, etc)

// in general it is recommened to use goto only to jump out of nested loops, when break just doesn't do the trick.
// but for the sake of functionalty it can jump anywhere you desire to.

local str = "hello"
function unsafe_funcjump(){
	unsafe_label:
	str += " planet"
	
	// if the function were to exit here, it would return to the location of the calling line
	// which, in this case is after start_the_fun()
}

function start_the_fun(){
	goto unsafe_label
}

start_the_fun()
print(str) // prints hello planet

6 - Precompiler

// BromScript has the same style precompiller as C and C++ do.

// we can set pre-compiller flags
#set shouldprint totaly

// and we can define strings to replace
#define our_string "hello"

#if shouldprint == totaly
	print(our_string)
#end

#set shouldprint no
#if shouldprint == totaly
	error("won't happen")
#else
	print(our_string)
#end

#set shouldprint no
#if shouldprint == totaly
	error("won't happen")
#else
	print(our_string)
#end

// this will output "hello hello hello"
// you can also unset the value
#unset shouldprint