AWL is a minimalist interpreted programming language written in TypeScript.
Introduction • Getting Started • Syntax Overview • Syntax Highlighting • Future Plans
AWL is my second semester personal project its an interpreted programming language written entirely in Typescript, it features a hand rolled recursive descend parser , a lexer and a treeWalk AST interepreter as of now it supports basic programming constructs a mini standard library and basic data constructs like structs and arrays with plans to extend it further in the future
AWL was made for fun and as a learning experience,it rather allowed me to explore different ways of implementing common features awl is distinct in its syntax and semantics and it tries to be diffrent for the sake of it its not meant for production use or real world applications (at least as of now)
awl can be used for quick prototyping of algorithms and basic scripting and automation tasks other area where awl can be used is in educational purposes to teach programming concepts
you can try out awl in the online playground here
thanks arnav for making this, do checkout the repo awl-playground
awl has a binary executable in the repo , its just named awl you can check the releases to get this executable download the file and run it in your terminal give it a file that contains awl code
./awl yourfile.awlits that easy youll see the output , ( might not work on windows or mac {not tested})
tho ugh its recommended to use the binary executable you can also run awl from source
-
make sure you have nodejs or bun runtime installed
-
clone the repo
git clone https://github.com/atoms19/AWL.git
cd AWL- install dependencies
npm install
# or
bun install- run awl interpreter
node main.ts yourfile.awl
# or
bun main.ts yourfile.awlyou can also build the project as a binary using bun or pkg using
for compiling with bun in linux machines build.sh script is provided
bash ./build.shyou can run awl in debug mode using the --debug flag this will print the tokens generated by the lexer
the AST generated by the parser and the runtime errors with stack trace
./awl yourfile.awl --debugflag should be given after the filename
some of the awl syntax feels less in line with other languages and thats intentional to make it stand out in a wierd way and some of it me just being wacky ```awl printOut("Hello World") printOut prints every line in a new line , to avoid this behaviour you can use printInline
printInline("Hello ")
printInline("World")this will be printed in the same line as "hello world" printInline is more primitvie than printOut and can only print one value at a time
variables are declared using the let keyword
let x = 10
let name = "vish"variables are dynamically typed , you can reassign them as well
you can convert between types using the following functions
toInt(x) // converts x to integer
toString(x) // converts x to string
toFloat(x) // converts x to float
toArray(x) // converts x to array
toChar(x) // converts x to character (x : charcter code)
toCharCode(x) // converts x to character code (x : character)basic datatypes in awl are Numeric , String , Array
comments in awl are done with # for single line comments and
### ### for multi line comments
# this is a single line comment
###
this is a multi line comment
###- Numeric : represents both integers and floating point numbers
- String : represents a sequence of characters or a single character
- Array : represents a collection of values, can hold mixed datatypes including other arrays
- Boolean : represents true or false values
- Null : represents a null value
these are some of the builtin functions in awl
printOut(x): prints x to the consoleprintInline(x): prints x to the console without a new linegetInput(x): takes input from the user and returns it as a string, x is the prompt messagearrayLength(x): returns the length of x arrayarrayInsert(x, value): inserts value to the end of array xarrayInsertAt(index, value, x): inserts value at index of array xarrayRemoveAt(index, x): removes the element at index from array x string operations are expected to be done by converting them to array of charactersgetTime(): returns the current time in milliseconds since epoch
functions are declared using fun keyword
once defined a function can be called using its name() just like in other languages
fun add(a,b) {
return a + b
}
let result = add(20, 10)
printOut(result) // prints 30functions support return statement to return a value from the function
statements after return are not executed
only positional paramerters are supported as of now
control flow in awl is done using if ,while and for keywords
all control statements can be followed by a block of code enclosed in {}
or a single statement without {}
let x = 10
if (x > 5) {
printOut("x is greater than 5")
}else if (x == 5) {
printOut("x is equal to 5")
}
else{
printOut("x is less than 5")
}let i = 0
while(i < 10) {
printOut(i)
i = i + 1
}if(condition) printOut("single statement if")
break can be used to exit a loop , its behaviour for nested loops might be broken
due to furstration of writing while loops i finally took my time to write for loops for loops in awl is super simple they support ranges
for (i in 0 -> 10 ){
printOut(i)
}the above loop will print numbers from 0 to 9
ranges can also be descending
for (i in 10 -> 0 ){
printOut(i)
}the above loop will print numbers from 10 to 1
ranges can also be return values of functions
for( i in 0 -> arrayLength(arr)){
printOut(arrayGet(i, arr))
}you can also specify step in ranges using by keyword after the end value of the range
for( i in 0 -> 10 by 2){
printOut(i)
} ranges can be negative as well
for( i in 10 -> 0 by -2){
printOut(i)
} floating point steps are supported as well
for( i in 0 -> 1 by 0.1){
printOut(i)
}you can also iterate over arrays using for loops
let arr = [1,2,3,4,5]
for( item in arr) {
printOut(item)
}string iterations are easier now
let str = "hello"
for( char in toArray(str)) {
printOut(char)
}break and continue statements are supported in for loops as well
awl uses functions for bitewise operations unlike other c based languages we reserve most symbols for syntactic meaning rather than bitewise as you wont be dealing with bitewise often
AND(a,b)is bitewise andOR(a,b)is bitewise orXOR(a,b)is bitewise xorNOT(a)is bitewise notL_SHIFT(a,b)is left shiftR_SHIFT(a,b)is right shift
arrays are declared using square brackets
let arr = [1,2,3,4,5]
let strArr = ["hello", "world"]arrays can hold mixed datatypes as well ,including other arrays
let mixedArr = [1, "hello", 2.5, [1,2,3]]arrays are dynamically sized , you can add or remove elements from them
you can access the elements from an array at any index using <array-name>[index]
let arr = [1,2,3,4,5]
printOut(arr[0]) #prints 1you can also modify the elements at any index the same way by assigning to an index
let arr = [1,2,3,4,5]
arr[0] = 10
printOut(arr[0]) #prints 10arrays also support negative indexing just like in python
let arr = [1,2,3,4,5]
printOut(arr[-1]) #prints 5arrays can be sliced using the same syntax we used in ranges
let arr = [1,2,3,4,5]
let slicedArr = arr[1 -> 4] #slicedArr = [2,3]you can also specify step in slicing
let arr = [1,2,3,4,5]
let slicedArr = arr[0 -> 5 by 2] #slicedArr = [1,3,5]<~ operator is used to push to an array
let arr = [1,2,3]
arr <~ 4 # arr = [1,2,3,4]you can also insert to a specific index using <~ operator
let arr = [1,2,3]
arr[1] <~ 1.5 # arr = [1,1.5,2,3]for removing elements the suggested way is to use the builtin function arrayRemoveAt(index, array)
let arr = [1,2,3,4,5]
arrayRemoveAt(2, arr) #arr = [1,2,4,5]builtin functions for inserting still exist the operators are just syntactic sugar over them
for removing at any index using operators you can use ~> operator
let arr = [1,2,3,4,5]
arr ~> 2 #arr = [1,2,4,5]this will remove the element at index 2 from array but will not return it , as its a standalone statment and not to be confused with expressions or to be used in them
the sugged way is to store the value to be removed in a variable first and then remove it
let arr = [1,2,3,4,5]
let valueToRemove = arr[2]
arr ~> 2
printOut(valueToRemove) #prints 3or you could just use the builin function arrayRemoveAt which returns the removed value
let arr = [1,2,3,4,5]
let removedValue = arrayRemoveAt(2, arr)
printOut(removedValue) #prints 3enhancements in this area is expected in the next update
strings are declared using double quotes
let str = "hello world"strings can be concatenated using + operator
let str1 = "hello"
let str2 = "world"
let str3 = str1 + " " + str2 #str3 = "hello worldstrings can be accessed indexing just like arrays
let str = "hello"
printOut(str[0]) #prints hstrings are immutable , you cannot modify a character at an index
you can convert a string to an array of characters using toArray() function
strings can be sliced using the same syntax we used in ranges
let str = "hello world"
let slicedStr = str[0 -> 5] #slicedStr = "hello"you can also specify step in slicing
let str = "hello world"
let slicedStr = str[0 -> 11 by 2] #slicedStr = "hlowrd"negative steps are supported for both array slicing as well as string slicing but awl does not have negative indexing
The string module provides essential helper functions for string manipulation and inspection.
You must first include the module to use its functions:
include "stdlib/string.awl"Returns the total number of characters in a string.
let name = "AWL"
let len = stringLen(name)
printOut(len) # Prints 3
let empty = ""
printOut(stringLen(empty)) # Prints 0Returns a new string by concatenating the original str to itself count times.
let s = "xo"
let repeated = stringRepeat(s, 3)
printOut(repeated) # Prints "xoxoxo"
printOut(stringRepeat("=", 10)) # Prints "=========="Removes all whitespace characters from the Left (beginning) of a string.
let s = " hello world"
let trimmed = stringLTrim(s)
printOut(trimmed) # Prints "hello world"Removes all whitespace characters from the Right (end) of a string.
let s = "hello world "
let trimmed = stringRTrim(s)
printOut(trimmed) # Prints "hello world"Removes all whitespace characters from both the beginning and the end of a string. This is a combination of stringLTrim and stringRTrim.
let s = " hello world "
let trimmed = stringTrim(s)
printOut(trimmed) # Prints "hello world"Returns a new string with all alphabetic characters converted to their uppercase equivalent.
let s = "Hello World 123!"
let upper = stringToUpper(s)
printOut(upper) # Prints "HELLO WORLD 123!"Returns a new string with all alphabetic characters converted to their lowercase equivalent.
let s = "Hello World 123!"
let lower = stringToLower(s)
printOut(lower) # Prints "hello world 123!"+: addition-: subtraction*: multiplication/: division%: modulus^: exponentiation
==: equal to!=: not equal to>: greater than<: less than>=: greater than or equal to<=: less than or equal to
&&: logical and||: logical or!: logical not
structs are datastructures that can hold multiple values of different types
they are declared using the struct keyword
struct Person {
let name = ""
let age = 0
let isStudent =false
}you can create an instance of a struct using the new keyword
let person1 = new Person
person1["name"] = "vish"
person1["age"] = 20
person1["isStudent"] = true
printOut(person1["name"]) #prints vishas of now struct definitions are not local scopped they are global only a more easier way to access struct properties is planned in the future as well as support for methods in structs
awl supports basic file operations using builtin functions
fileWrite(filename, content): writes content to a file with name filenamefileRead(filename): reads content from a file with name filename and returns it as a stringfileAppend(filename, content): appends content to a file with name filenamefileExists(filename): checks if a file with name filename exists and returns true or false
you can run shell commands using the runCommand(command) function
it takes a string command as an argument and returns the output of the command as a string
let output = runCommand("ls -la")
printOut(output)you can access the command line arguments as an array using the getArguments() function
this function returns an array of strings containing the arguments passed to the script during execution
you can use it like this
let args = getArguments()
let firstArg =args[0]
printOut(firstArg)the name of the script is not included in the arguments array just the arguments passed to it
you can use the exit(code) function to exit the program with a specific exit code
you can include other awl files using the include keyword
this is very similar to #include in C/C++
include "otherfile.awl"this is how we can have awl files with reusable code and import them in other files pretty much everything from the other file is imported into the current file so make sure that there are no name conflicts
- will be working on a proper module system once classLoader is implemented
# math.awl
fun add(a,b) {
return a + b
}include "math.awl"
let result = add(10, 20) # assuming add is defined in math.awl
printOut(result) #prints 30there is no standard library as of now so you have to write your own utility functions might add a standard library in the future
The math module provides common mathematical constants and functions. Some functions (sin, cos, ln) are assumed to be fast, native functions provided by the interpreter, while others are derived from them and written in pure AWL.
You must first include the module to use its functions:
include "stdlib/math.awl"
The constant
The constant
Returns the smaller of the two numbers a and b.
Returns the larger of the two numbers a and b.
Returns the absolute (non-negative) value of a.
Returns the "ceiling" of a, which is the smallest integer greater than or equal to a. It rounds numbers up.
Important Note: This function truncates the decimal (rounds toward zero). This matches floor for positive numbers, but not for negative ones.
(e.g., The true mathematical floor of
Returns the value of a rounded to the nearest integer. Numbers ending in .5 are rounded away from zero.
Returns the square root of a by calculating
Returns the base-10 logarithm of a.
Returns the natural logarithm (base a.
this is a native function meaning u can use it without including the math module
sin(a) and cos(a) are provided as native functions and that a is in radians.
other trigonomic functions expect a to be in radians as well
The complex module provides functions and a data structure for performing complex number arithmetic. This is essential for advanced mathematical, scientific, or engineering calculations.
You must first include the module to use its functions:
include "stdlib/complex.awl"
This is the core data structure used by the module to store a complex number. It contains two fields:
real: The real part of the number.img: The imaginary part of the number.
You should not typically create this struct directly. Use the complex() constructor function instead.
This is the "constructor" function. It creates and returns a new ComplexNo struct populated with the given real and imaginary components.
let c1 = complex(3, 4)This creates a complex number representing
Returns a new ComplexNo struct that is the sum of c1 and c2.
let a = complex(1, 2)
let b = complex(3, 4)
let sum = complexAdd(a, b)'sum' is now a ComplexNo struct { real: 4, img: 6 }
Returns a new ComplexNo struct that is the difference of c1 and c2 (
let a = complex(5, 5)
let b = complex(1, 2)
let diff = complexSub(a, b)
# 'diff' is now a ComplexNo struct { real: 4, img: 3 }Returns a new ComplexNo struct that is the product of c1 and c2.
let a = complex(2, 3)
let b = complex(4, 5)
let product = complexMul(a, b)
# 'product' is now a ComplexNo struct { real: -7, img: 22 }Returns a new ComplexNo struct that is the quotient of c1 and c2 (0 + 0i if a division by zero is attempted.
let a = complex(-7, 22)
let b = complex(2, 3)
let quotient = complexDiv(a, b)
# 'quotient' is now a ComplexNo struct { real: 4, img: 5 }Returns a Numeric (not a complex number) representing the magnitude squared (complexMag as it avoids a square root operation.
This is the preferred function for escape-time fractals (like Mandelbrot).
let c = complex(3, 4)
let magSqr = complexMagSqr(c)
printOut(magSqr) # Prints 25Returns a Numeric representing the magnitude (
let c = complex(3, 4)
let mag = complexMag(c)
printOut(mag) # Prints 5Returns a new ComplexNo struct that is the conjugate of
let c = complex(3, 4)
let conj = complexConj(c)
# 'conj' is now a ComplexNo struct { real: 3, img: -4 }Returns a new ComplexNo struct that is the negation of
let c = complex(3, 4)
let neg = complexNeg(c)
# 'neg' is now a ComplexNo struct { real: -3, img: -4 }if you want syntax highlighting for awl in the best editor in the world
you can use the file named awl.vim in the repo put it in your ~/.nvim/syntax/ folder
or $VIMRUNTIME/syntax/ folder
and then run the following command to enable it
:set syntax = awlawl is meant to be used in any scripting environment desired, to configure awl for your own use case you might need to modify certain parts of the interpreter
example if you want this to run in a server you might want to mask certain functions like runCommand for security reasons
you can do this easily bu modifying standardFunctions.ts file in the repo
you dont even have to remove any function just comment out the exporting line of the function
export const standardFunctions = {
"printOut": executeOutput,
"printInline": executeInlineOutPut,
"getTime": clock,
"getInput": executeInput,
// "runCommand" : runCommand,
"toInt": convertToInteger,
"toFloat": convertToFloat,
"arrayRemoveAt": arrayRemoveAt,
"arrayLength": arrayLength,
"toCharCode": toCharCode,
....}you can also rename functions here and add your own custom functions as well
you can also make your own standard libraries by making awl files in the stdlib folder
here are V0.2 plans for awl
- add closures
- dot notation for struct property access
- more slight optimizations
- constant folding
- memory refrence optimizations
- better error reporting accurate line numbers
contributions are welcome feel free to open issues and PRs, make sure you read the CONTRIBUTING.md file before making a PR
