A Golang implementation of the Lox language from the book Crafting Interpreters with my own features
Usage: lox [OPTIONS] [FILE]
OPTIONS:
-c <code>
Execute Lox code from command line argument
-i
Drop into REPL mode after running Lox code
--disable-loxcode, -dl
Disable execution of all Lox files that are bundled inside this interpreter executable
--unsafe
Enable unsafe mode, allowing access to functions that can potentially crash this interpreter
-h, --help
Print this usage message and exit
-v, --version
Print version information and exit
First, install Go if it's not installed already. Then run the following commands to build this interpreter:
git clone https://github.com/AlanLuu/lox-go.git
cd lox-go
go build
This will create an executable binary called lox on Linux/macOS and lox.exe on Windows that can be run directly.
- No error is reported when calling a function with 255 arguments or more
- The last statement in a Lox file does not need to end with a semicolon
- Multi-line comments (
/**/) are supported in this implementation of Lox - Concatenating a string with another data type will convert that type into a string and concatenate them together
- Integers and floats are distinct types in this implementation of Lox
- Integers are signed 64-bit values and floats are double-precision floating-point values
- A binary operation on an integer and float converts the integer into a float before performing the specified operation
- The
/operation on two integers converts both operands to floats before performing the division. If the result of the division can be represented as an integer, the final result is an integer, otherwise it is a float - The
**operation on two integers converts both operands to floats before performing the exponentiation. After the operation, the result of the exponentiation is converted into an integer, which is used as the final result
- The following additional operations are supported in this implementation of Lox:
a % b, which returns the remainder of two numbersaandba ** b, which returnsaraised to the power ofb, whereaandbare numbers- The exponent operator has higher precedence than any unary operators on the left, so
-a ** bis equivalent to-(a ** b).
- The exponent operator has higher precedence than any unary operators on the left, so
a << banda >> b, which returns a number representing the numberashifted bybbits to the left and right respectively.- If
aorbare floats, they are converted into integers before the shift operation
- If
~a, which returns the bitwise NOT of the numbera- If
ais a float, it is converted into an integer before the bitwise operation
- If
a & b,a | b, anda ^ b, which returns the bitwise AND, OR, and XOR of two numbersaandbrespectively.- If
aorbare floats, they are converted into integers before the bitwise operation - Unlike in C, the precedence of the bitwise operators is higher than the precedence of the comparison operators, so
a & b == valueis equivalent to(a & b) == value
- If
- The ternary operator
a ? b : c, which evaluates tobifais a truthy value andcotherwise
- Division by 0 results in
Infinity, which uses Golang'smath.Inf()under the hoodInfinityliterals are supported using the identifier "Infinity"
- Performing a binary operation that isn't supported between two types results in
NaN, which stands for "not-a-number", using Golang'smath.NaN()under the hoodNaNliterals are supported using the identifier "NaN"
- Booleans and
nilare treated as integers when performing arithmetic operations on them, withtrueandfalsebeing treated as1and0respectively, andnilbeing treated as0 - Besides
falseandnil, the values0,0.0,0n,0.0n,NaN,"",[],{},Set(),Buffer(), and any value with a length of 0 are also falsy values - The
&&and||operators are supported for the logical AND and OR operations respectively - Binary, hexadecimal, and octal integer literals are supported in this implementation of Lox
- Binary literals start with the prefix
0b - Hexadecimal literals start with the prefix
0x - Octal literals start with the prefixes
0oor0 - All letters in prefixes are case-insensitive
- Binary literals start with the prefix
- Scientific notation number literals are supported in this implementation of Lox
- Examples:
1e5,1e+5,1e-5,1.5e5,1.5e+5,1.5e-5 - All scientific notation number literals are floats
- Examples:
- Number literals support the following features:
- An underscore character can be used to group digits, such as
1_000_000, which is equivalent to1000000- Underscore characters are also allowed in binary, hexadecimal, and octal literals, except for octal literals starting with a
0
- Underscore characters are also allowed in binary, hexadecimal, and octal literals, except for octal literals starting with a
- An underscore character can be used to group digits, such as
- Arbitrary-precision integers and floats are supported in this implementation of Lox, which are called bigints and bigfloats respectively
- Examples:
1n,1.5n - bigints and bigfloats support the same operations as integers and floats, with the following notes:
- bigfloats do not support the
**operator - Dividing a bigint by
0or0nthrows a runtime error
- bigfloats do not support the
- Examples:
- Class properties that share the same spelling as a reserved keyword are supported in this implementation of Lox
class A {} A.if = 0; print A.if;
- A new statement called
putprints an expression without a newline character at the end- Syntax:
put <expression>;
- Syntax:
- New statements called
printerrandputerrprint an expression to standard error instead of standard output- Syntax:
printerr <expression>,puterr <expression>
- Syntax:
breakandcontinuestatements are supported in this implementation of Lox- Empty statements (statements that are standalone semicolons) are supported in this implementation of Lox
- For loops are implemented with their own AST node instead of being desugared into while loop nodes
- This makes it easier to implement the
continuestatement inside for loops
- This makes it easier to implement the
- Variables declared in the initialization part of a for loop are locally scoped to that loop and do not become global variables
- Do while loops are supported in this implementation of Lox
var i = 1; do { print i; i = i + 1; } while (i <= 10);
- Foreach loops are supported in this implementation of Lox
var iterable = [1, 2, 3, 4, 5]; foreach (var element in iterable) { print element; }
- The target of a foreach loop must be an iterable type. Iterable types are the following:
- String
- For each iteration,
elementis each character of the string iterated in order
- For each iteration,
- List
- For each iteration,
elementis each element of the list iterated in order
- For each iteration,
- Buffer
- For each iteration,
elementis each element of the buffer iterated in order
- For each iteration,
- Dictionary
- For each iteration,
elementis a list with two elements, with the first element being a dictionary key and the second element being the dictionary value corresponding to that key
- For each iteration,
- Range
- For each iteration,
elementis each generated integer from the range object
- For each iteration,
- Set
- For each iteration,
elementis each element of the set
- For each iteration,
- Queue
- For each iteration,
elementis each element of the queue
- For each iteration,
- Deque
- For each iteration,
elementis each element of the deque
- For each iteration,
- File
- For each iteration,
elementis each line from the file as a string
- For each iteration,
- CSV Reader
- For each iteration,
elementis a list of strings of the values that are separated by the delimiter for each line in the CSV file
- For each iteration,
- Logger
- For each iteration,
elementis each saved log line from the logger object as a string
- For each iteration,
- gzip reader
- For each iteration,
elementis each byte of decompressed gzip data from the gzip reader object as an integer
- For each iteration,
- HTML tokenizer
- For each iteration,
elementis each HTML token from the HTML tokenizer object as an HTML token object
- For each iteration,
- IP address
- For each iteration,
elementis each byte from the IP address instance as an integer
- For each iteration,
- Ring
- For each iteration,
elementis each element of the ring
- For each iteration,
- Bitfield
- For each iteration,
elementis an integer that is either1or0
- For each iteration,
- Dotenv
- For each iteration,
elementis a list with two elements, with the first element being an env key as a string and the second element being the value of that env key as a string
- For each iteration,
- RB tree
- For each iteration,
elementis each element of the RB tree
- For each iteration,
- Priority queue
- For each iteration,
elementis a list with two elements, with the first element being an element from the priority queue and the second element being that element's priority value as an integer
- For each iteration,
- URL values
- For each iteration,
elementis a list with two elements, with the first element being a key as a string and the second element being the first value associated with that key as a string
- For each iteration,
- String
- Note: when iterating over dictionaries or sets using a foreach loop, the iteration order is random since dictionaries and sets are unordered
- The target of a foreach loop must be an iterable type. Iterable types are the following:
- Repeat statements are supported in this implementation of Lox, which repeatedly executes a statement for a certain number of times according to the expression
//Syntax: repeat (<expression>) <statement> //Prints "Hello world!" 10 times repeat (10) { print "Hello world!"; }
- The expression in a repeat statement must evaluate to an integer or bigint or else a runtime error is thrown
- If the expression evaluates to a negative value, it is the same as specifying
0as the repeat expression - Booleans and
nilare treated as integers when performing arithmetic operations on them, withtrueandfalsebeing treated as1and0respectively, andnilbeing treated as0
- If the expression evaluates to a negative value, it is the same as specifying
breakandcontinuestatements are supported inside repeat statements
- The expression in a repeat statement must evaluate to an integer or bigint or else a runtime error is thrown
- Loop statements are supported in this implementation of Lox, which repeatedly executes a block continuously without stopping
//Syntax: loop <block> //Prints "Hello world!" continuously without stopping loop { print "Hello world!"; } print "here"; //This line is never reached
breakandcontinuestatements are supported inside loop statementsloop { print "Hello world!"; break; } print "here"; //This line is reached
- Switch statements are supported in this implementation of Lox
var x = 2; //Prints 2, then 3 switch (x) { case 1: print 1; case 2: print 2; case 3: print 3; break; default: print "default"; }
- Switch statements fall through by default and have their own local scope
- Try-catch-finally statements are supported in this implementation of Lox
try { print i; } catch (e) { print "caught error"; } finally { print "done"; }
- A
trystatement must be followed by acatchorfinallystatement or both - If the exception variable is not needed, it may be omitted from the
catchstatementtry { print i; } catch { print "caught error"; }
- Along with try-catch-finally statements,
throwstatements are supported in this implementation of Lox- Syntax:
throw <expression>; throwstatements throw a runtime error using the provided expression as the error message. If the provided expression is an error object, the object itself is thrown. Otherwise, if the provided expression is not a string, the string representation of the expression is used as the error message
- Syntax:
- A
- Assert statements are supported in this implementation of Lox
assert 1 == 1; assert 1 == 2; //Throws a runtime error
- If the specified expression is false, a runtime error is thrown, otherwise the statement does nothing
- Anonymous function expressions are supported in this implementation of Lox. There are two forms supported:
fun(param1, paramN) {<statements>}, which is a traditional anonymous function expression that contains a block with statementsfun(param1, paramN) => <expression>, which is an arrow function expression that implicitly returns the given expression when called- The parser will attempt to parse anonymous function expressions that appear on their own line as function declarations, throwing a parser error as a result. This is expected behavior; to force the parser to parse them as expressions, wrap the function expression inside parentheses, like
(fun() {})(). In this case, this creates an anonymous function expression that is called immediately
- Variadic functions are supported in this implementation of Lox
- An indefinite amount of arguments can be accepted as a list
fun foo(...a) { //"a" is a list of arguments print a; } fun bar(a, b, ...c) { //"c" is a list of arguments print c; } foo(1, 2, 3, 4, 5); //Prints [1, 2, 3, 4, 5] bar(1, 2, 3, 4, 5); //Prints [3, 4, 5]
- The spread operator
...is supported in this implementation of Lox- Examples:
function(a, ...iterable, b), which passes all elements in the iterable as arguments to the specified functionvar arr = [1, 2, 3]; var arr2 = [1, 2, ...arr, 4]; print arr2; //[1, 2, 1, 2, 3, 4]
var dict = { 1: 2, 3: 4 }; var dict2 = { "key": "value", ...dict, "key2": "value2" }; print dict2; //{"key": "value", 1: 2, 3: 4, "key2": "value2"}
- Examples:
- Static class fields and methods are supported in this implementation of Lox
- Classes also support initializing instance fields to an initial value directly in the class body without the need for a constructor
class A { static x = 10; static y() { return 20; } z = 30; } print A.x; //Prints "10" print A.y(); //Prints "20" var a = A(); print a.z; //Prints "30"
- Static classes are supported in this implementation of Lox, which are classes that cannot be instantiated, and attempting to do so will throw a runtime error
static class A {} var a = A(); //Throws a runtime error
- Various mathematical methods and constants are defined under a built-in class called
Math, which is documented here - Various bigint and bigfloat mathematical methods are defined under a built-in class called
bigmath, which is documented here - Various methods and fields to work with HTML are defined under a built-in class called
HTML, which is documented here - Various methods to work with JSON strings are defined under a built-in class called
JSON, which is documented here - Various methods and fields to work with URL strings are defined under a built-in-class called
URL, which id documented here - Various methods and fields to work with operating system functionality are defined under a built-in class called
os, which is documented here - Various methods to work with HTTP requests are defined under a built-in class called
http, which is documented here - Various methods to work with cryptographic functionality are defined under a built-in class called
crypto, which is documented here - Various methods to work with CSV files are defined under a built-in class called
csv, which is documented here - Various methods to work with dotenv functionality are defined under a built-in class called
dotenv, which is documented here - Various methods and fields to work with file paths are defined under a built-in class called
filepath, which is documented here - Various methods and fields to work with gzip files are defined under a built-in class called
gzip, which is documented here - Various methods and fields to work with logging are defined under a built-in class called
log, which is documented here - Various methods and fields to work with network requests are defined under a built-in class called
net, which is documented here - Various methods for priority queues are defined under a built-in class called
pqueue, which is documented here - Various methods to work with regular expressions are defined under a built-in class called
regex, which is documented here - Various methods and fields to work with dates are defined under a built-in class called
Date, which is documented here - Various methods and fields to work with durations are defined under a built-in class called
Duration, which is documented here - Various methods to work with processes are defined under a built-in class called
process, which is documented here - Various methods to work with generating cryptographically secure strings are defined under a class called
secrets, which is documented here - Various methods for RB trees are defined under a built-in-class called
rbtree, which is documented here - Various methods to work with data from standard input are defined under a class called
stdin, which is documented here - Various methods and fields to work with tar files are defined under a built-in class called
tar, which is documented here - If unsafe mode is enabled, various methods to work with unsafe functionality are defined under a built-in class called
unsafe, which is documented here - Various methods and fields to work with UUID objects are defined under a class called
UUID, which is documented here - Various methods to work with opening web browsers are defined under a built-in class called
webbrowser, which is documented here - Various methods and fields to work with Windows-specific functionality are defined under a built-in class called
windows, which is documented here- This class does not exist on non-Windows systems
- Various methods and fields to work with zip files are defined under a built-in class called
zip, which is documented here - Various methods to work with base64 strings are defined under a built-in class called
base64, where the following methods are defined:base64.decode(string), which decodes the specified base64-encoded string into a decoded string and returns that string- A runtime error is thrown if the specified string is not properly encoded as base64
base64.decodeToBuf(string), which decodes the specified base64-encoded string into a buffer and returns that buffer- A runtime error is thrown if the specified string is not properly encoded as base64
base64.decodeURLSafe(string), which decodes the specified URL-safe base64-encoded string into a decoded string and returns that string- A runtime error is thrown if the specified string is not properly encoded as URL-safe base64
base64.decodeURLSafeToBuf(string), which decodes the specified URL-safe base64-encoded string into a buffer and returns that buffer- A runtime error is thrown if the specified string is not properly encoded as URL-safe base64
base64.encode(arg), which encodes the specified argument, which is either a string or a buffer, into a base64 string and returns that encoded stringbase64.encodeURLSafe(arg), which encodes the specified argument, which is either a string or a buffer, into a URL-safe base64 string and returns that encoded string
- Various methods to work with base32 strings are defined under a built-in class called
base32, where the following methods are defined:base32.decode(string), which decodes the specified base32-encoded string into a decoded string and returns that string- A runtime error is thrown if the specified string is not properly encoded as base32
base32.decodeToBuf(string), which decodes the specified base32-encoded string into a buffer and returns that buffer- A runtime error is thrown if the specified string is not properly encoded as base32
base32.encode(arg), which encodes the specified argument, which is either a string or a buffer, into a base32 string and returns that encoded string
- Various methods to work with hexadecimal strings are defined under a built-in class called
hexstr, where the following methods are defined:hexstr.decode(hexStr), which decodes the specified hexadecimal string into a buffer and returns that bufferhexstr.decodeToStr(hexStr)which decodes the specified hexadecimal string into a decoded string and returns that stringhexstr.dump(arg), which returns a string containing the hex dump of the specified argument, which is either a string or a bufferhexstr.encode(arg), which encodes the specified argument, which is either a string or a buffer, into a hexadecimal string and returns that encoded stringhexstr.tobigint(hexStr), which returns the integer representation of the specified hexadecimal string as a bigint
- Various methods and fields to work with the state of this interpreter are defined under a built-in class called
lox, where the following methods and fields are defined:lox.gc([num]), which invokes the garbage collector and blocks until the garbage collection process completes- If
numis specified, wherenumis an integer, this method invokes the garbage collector a total ofnumtimes instead of invoking it only once
- If
lox.globals(), which returns a dictionary containing all global variable names as string keys and their values as dictionary valueslox.locals(), which returns a dictionary containing all local variable names as string keys and their values as dictionary values- If this method is called in global scope, an empty dictionary is returned
lox.ranloxcode, which is a boolean that istrueif the--disable-loxcodeflag was passed andfalseotherwiselox.unsafe, which is a boolean that istrueif unsafe mode is enabled for this interpreter andfalseotherwise
- Various methods and fields to work with integers are defined under a built-in class called
Integer, where the following methods and fields are defined:Integer.MAX, which is the maximum value that an integer can storeInteger.MAX8, which is the maximum value that an 8-bit integer can storeInteger.MAX16, which is the maximum value that a 16-bit integer can storeInteger.MAX32, which is the maximum value that a 32-bit integer can storeInteger.MAXU8, which is the maximum value that an unsigned 8-bit integer can storeInteger.MAXU16, which is the maximum value that an unsigned 16-bit integer can storeInteger.MAXU32, which is the maximum value that an unsigned 32-bit integer can storeInteger.MIN, which is the minimum value that an integer can storeInteger.MIN8, which is the minimum value that an 8-bit integer can storeInteger.MIN16, which is the minimum value that a 16-bit integer can storeInteger.MIN32, which is the minimum value that a 32-bit integer can storeInteger.parseInt(string), which attempts to convert the specified string argument into an integer and returns that integer if successful, otherwise a runtime error is thrownInteger.tobigint(integer), which converts the specified integer argument into a bigint and returns that bigintInteger.toFloat(integer), which converts the specified integer argument into a float and returns that floatInteger.toHexStr(integer), which returns the hexadecimal representation of the specified integer as a string without the prefix "0x"Integer.toString(integer), which returns the string representation of the specified integer argument
- Various methods and fields to work with floats are defined under a built-in class called
Float, where the following methods and fields are defined:Float.MAX, which is the maximum value that a float can storeFloat.MAX32, which is the maximum value that a 32-bit float can storeFloat.MIN, which is the minimum value that a float can storeFloat.MIN32, which is the minimum value that a 32-bit float can storeFloat.parseFloat(string), which attempts to convert the specified string argument into a float and returns that float if successful, otherwise a runtime error is thrownFloat.tobigfloat(float), which converts the specified float argument into a bigfloat and returns that bigfloatFloat.toInt(float), which converts the specified float argument into an integer and returns that integerFloat.toString(float), which returns the string representation of the specified float argument
- Various methods to work with bigints and bigfloats are defined under built-in classes called
bigintandbigfloatrespectively, which are documented here - Various methods and fields that correspond to string constants and utility operations are defined under a built-in class called
String, where the following methods and fields are defined:String.digits, which is the string"0123456789"String.hexDigits, which is the string"0123456789abcdefABCDEF"String.hexDigitsLower, which is the string"0123456789abcdef"String.hexDigitsUpper, which is the string"0123456789ABCDEF"String.letters, which is the string"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"String.lowercase, which is the string"abcdefghijklmnopqrstuvwxyz"String.octDigits, which is the string"01234567"String.punctuation, which is the string"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"String.qwertyLower, which is the string"qwertyuiopasdfghjklzxcvbnm"String.qwertyUpper, which is the string"QWERTYUIOPASDFGHJKLZXCVBNM"String.builder(), which returns a stringbuilder object, with methods documented hereString.toString(arg), which returns the string representation of the specified argumentString.uppercase, which is the string"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- Methods to work with random number generation are defined under a built-in-class called
Rand, which is documented here - Strings have some additional features associated with them:
- Strings can be represented using single quotes as well
- Strings can be indexed by an integer, which will return a new string with only the character at the specified index:
string[index] - Get a new string with all characters from indexes
starttoendexclusive, wherestartandendare integers andstart < end:string[start:end]- If
start >= end, a new empty string is returned startorendcan be omitted, in which case the starting index will have a value of0ifstartis omitted and the ending index will have a value oflen(string)ifendis omitted
- If
- Negative integers are supported for string indexes, where a negative index
iis equivalent to the indexi + len(string). For example,string[-1]refers to the last character in the string - It is a runtime error to use a negative index value whose absolute value is greater than the length of the string or a positive index value greater than or equal to the length of the string to index into that string
- Get a new string that is the original string repeated
ntimes, wherenis an integer:string * n - Escape characters in strings are supported:
\': single quote\": double quote\\: backslash\a: bell\n: newline\r: carriage return\t: horizontal tab\b: backspace\f: form feed\v: vertical tab
- Besides these features, strings also have some methods associated with them:
string.caesar(shift), which returns a new string that is the original string encoded by a caesar cipher of the specified shift amount, which is an integerstring.capitalize(), which returns a new string with the first character from the original string capitalized if possible and the rest of the characters in lowercase if possiblestring.center(length, [fillChar]), which returns a new string where the original string is padded withfillCharon the left and right until the new string is of the specified integer length, wherefillCharis a string that contains a single Unicode character (if unspecified, a single space character is used as the padding character)- If
length <= len(string), this method returns the original string - Padding begins on the left if the original string has an even length, otherwise padding begins on the right
- If
string.compare(string2), which lexicographically comparesstringandstring2and returns0ifstring == string2,-1ifstring < string2, and1ifstring > string2string.contains(substr), which returnstrueifsubstris contained withinstringandfalseotherwisestring.count(string2), which returns an integer representing the number of timesstring2appears instringin a non-overlapping manner- If
string2is an empty string, this method returns 1 plus the number of Unicode characters instring
- If
string.cut(separator), which returns a list of two strings where the first string is the text before the specified separator string in the original string and the second string is the text after the specified separator string in the original string- If the separator is not in the original string, the returned list's string elements are a copy of the original string and an empty string
string.cutPrefix(prefix), which returns a new string where the prefix from the original string specified by the prefix string is removed if the prefix exists, otherwise a copy of the original string is returnedstring.cutSuffix(suffix), which returns a new string where the suffix from the original string specified by the suffix string is removed if the suffix exists, otherwise a copy of the original string is returnedstring.endsWith(suffix), which returnstrueifstringends withsuffixandfalseotherwisestring.equalsIgnoreCase(string2), which returnstrueifstringequalsstring2, ignoring letter case, andfalseotherwisestring.fields(), which returns a list containing all substrings that are separated by one or more consecutive whitespace characters- If the string only contains whitespace characters, this method returns an empty list
string.filter(callback), which returns a new string containing only the characters from the original string where the callback function returns a truthy value for themstring.index(string2), which returns an integer representing the index value of the location ofstring2instring, or-1ifstring2is not instringstring.indexFrom(string2, fromIndex), which returns an integer representing the index value of the location ofstring2instring, starting at the indexfromIndex, which is an integer, or-1ifstring2is not instringstarting at the indexfromIndex- If
fromIndex <= 0, this method returns the same value asstring.index(string2) - If
fromIndex > len(string), this method returns-1
- If
string.isAlnum(), which returnstrueif all characters in the string are alphanumeric andfalseotherwise- Alias:
string.isalnum
- Alias:
string.isAlpha(), which returnstrueif all characters in the string are letters according to Unicode andfalseotherwise- Alias:
string.isalpha
- Alias:
string.isAscii(), which returnstrueif all characters in the string are ASCII andfalseotherwise- Alias:
string.isascii - This method returns
truefor an empty string
- Alias:
string.isDigit(), which returnstrueif all characters in the string are digits andfalseotherwise- Alias:
string.isdigit
- Alias:
string.isEmpty(), which returnstrueif the length of the string is 0 andfalseotherwisestring.isLower(), which returnstrueif all characters in the string are lowercase letters andfalseotherwise- Alias:
string.islower
- Alias:
string.isNumeric(), which returnstrueif all characters in the string are numeric characters andfalseotherwise- Alias:
string.isnumeric
- Alias:
string.isPrintable(), which returnstrueif all characters in the string are printable characters andfalseotherwise- Alias:
string.isprintable - This method returns
truefor an empty string
- Alias:
string.isSpace(), which returnstrueif all characters in the string are whitespace characters andfalseotherwise- Alias:
string.isspace
- Alias:
string.isUpper(), which returnstrueif all characters in the string are uppercase letters andfalseotherwise- Alias:
string.isupper
- Alias:
string.join(iterable), which returns a string that is the concatenation of the string representations of the elements from the specified iterable, where the separator between the string representations of those elements is the original stringstring.lastIndex(string2), which returns an integer representing the index value of the last occurrence ofstring2instring, or-1ifstring2is not instringstring.lastIndexFrom(string2, fromIndex), which returns an integer representing the index value of the last occurrence ofstring2instring, starting at the indexfromIndex, which is an integer, or-1ifstring2is not instringstarting at the indexfromIndex- If
fromIndex >= len(string), this method returns the same value asstring.lastIndex(string2) - If
fromIndex < 0, this method returns-1
- If
string.ljust(length, [fillChar]), which returns a new string where the original string is padded withfillCharon the right, causing a left justification of the original string, until the new string is of the specified integer length, wherefillCharis a string that contains a single Unicode character (if unspecified, a single space character is used as the padding character)- If
length <= len(string), this method returns the original string
- If
string.lower(), which returns a new string with all lowercase lettersstring.lstrip([chars]), which returns a new string with all leading characters fromcharsremoved. Ifcharsis omitted, this method returns a new string with all leading whitespace, newlines, and tabs removedstring.padEnd(length, padStr), which pads the contents ofpadStrto the end ofstringuntil the new string is of lengthlengthstring.padStart(length, padStr), which pads the contents ofpadStrto the beginning ofstringuntil the new string is of lengthlengthstring.quote(), which returns a new string with double quotes at the beginning and end of the stringstring.replace(oldStr, newStr), which returns a new string where all occurrences ofoldStrin the original string are replaced withnewStrstring.reversed(), which returns a new string that is the original string in reversed orderstring.reversedWords([delimiter]), which returns a new string where each word from the original string is in backwards order, where each word is taken to be each string in a list of strings after splitting the original string by the specified delimiter string. Ifdelimiteris omitted, the delimiter is a string with a single space- Example:
"hello world".reversedWords() == "world hello"
- Example:
string.rjust(length, [fillChar]), which returns a new string where the original string is padded withfillCharon the left, causing a right justification of the original string, until the new string is of the specified integer length, wherefillCharis a string that contains a single Unicode character (if unspecified, a single space character is used as the padding character)- If
length <= len(string), this method returns the original string
- If
string.rot13(), which returns a new string that is the ROT13 encoding of the original stringstring.rot18(), which returns a new string that is the ROT18 encoding of the original string- ROT18 is a variation of ROT13 that combines ROT13 with ROT5, which shifts numerical digits by a factor of 5
string.rot47(), which returns a new string that is the ROT47 encoding of the original stringstring.rstrip([chars]), which returns a new string with all trailing characters fromcharsremoved. Ifcharsis omitted, this method returns a new string with all trailing whitespace, newlines, and tabs removedstring.shuffled(), which returns a new string that is a shuffled version of the original stringstring.split(delimiter), which returns a list containing all substrings that are separated bydelimiterstring.splitAfter(delimiter), which returns a list containing all substrings that are after each instance ofdelimiterstring.startsWith(prefix), which returnstrueifstringbegins withprefixandfalseotherwisestring.strip([chars]), which returns a new string with all leading and trailing characters fromcharsremoved. Ifcharsis omitted, this method returns a new string with all leading and trailing whitespace, newlines, and tabs removedstring.swapcase(), which returns a new string with all lowercase characters converted to uppercase and vice-versastring.title(), which returns a new string where each word starts with a capital letter if possible and the remaining characters in each word are in lowercase if possiblestring.toBuffer(), which convertsstringinto a buffer with the raw UTF-8 byte representation of each character in the string as the buffer elements and returns that bufferstring.toList(), which convertsstringinto a list with each character in the string as the list elements and returns that liststring.toNum([base]), which attempts to convertstringinto an integer or float and returns that value if successful andNaNotherwise. Ifbaseis specified, then this method will attempt to convertstringthat is represented as the specified base into an integer or float and returns that value if the conversion was successful andNaNotherwisestring.toSet(), which convertsstringinto a set with each unique character in the string as the set elements and returns that setstring.unquote(), which returns a new string with the ending single or double matching quote pair removed from the string if there is one, otherwise this method returns the original stringstring.upper(), which returns a new string with all uppercase lettersstring.zfill(length), which returns a new string where the character'0'is padded to the left until the new string is of lengthlength. If a leading'+'or'-'sign is part of the original string, the'0'padding is inserted after the leading sign instead of before
- Lists are supported in this implementation of Lox
- Create a list and assign it to a variable:
var list = [1, 2, 3]; - Get an element from a list by index, where
indexis an integer:list[index] - Get a new list with all elements from indexes
starttoendexclusive, wherestartandendare integers andstart < end:list[start:end]- If
start >= end, a new empty list is returned startorendcan be omitted, in which case the starting index will have a value of0ifstartis omitted and the ending index will have a value oflen(list)ifendis omitted
- If
- Set an element:
list[index] = value; - Negative integers are supported for list indexes, where a negative index
iis equivalent to the indexi + len(list). For example,list[-1]refers to the last element in the list- Negative indexes are also supported in list methods that accept integer values for list indexes as a parameter
- It is a runtime error to use a negative index value whose absolute value is greater than the length of the list or a positive index value greater than or equal to the length of the list to get or set
- Concatenate two lists together into a new list:
list + list2 - Get a new list with all elements from the original list repeated
ntimes, wherenis an integer:list * n - Besides these operations, lists also have some methods associated with them:
list.add(element), which is an alias forlist.appendlist.all(callback), which returnstrueif the callback function returnstruefor all elements in the list andfalseotherwiselist.any(callback)which returnstrueif the callback function returnstruefor any element in the list andfalseotherwiselist.append(element), which appends an element to the end of the listlist.clear(), which removes all elements from the listlist.contains(element), which returnstrueifelementis contained in the list andfalseotherwiselist.copy(), which returns a shallow copy of the original list as a new listlist.count(element), which returns the number of timeselementappears in the listlist.countFunc(callback), which returns the number of elements in the list where the callback function returns a truthy value for themlist.extend(list2), which appends every element fromlist2to the end of the listlist.filter(callback), which returns a new list containing only the elements from the original list where the callback function returns a truthy value for themlist.find(callback), which returns the first element in the list where the callback function returnstrue, ornilif the callback returnsfalsefor every element in the listlist.findIndex(callback), which returns the index of the first element in the list where the callback function returnstrue, or-1if the callback returnsfalsefor every element in the listlist.findLast(callback), which returns the first element in the list starting from the last element where the callback function returnstrue, ornilif the callback returnsfalsefor every element in the listlist.findLastIndex(callback), which returns the index of the first element in the list starting from the last element where the callback function returnstrue, or-1if the callback returnsfalsefor every element in the listlist.first(), which returns the first element in the list. If the list is empty, a runtime error is thrownlist.flatMap(callback), which returns a new list with the results of calling a callback function on each element of the original list followed by flattening the new list by one levellist.flatMapDepth(depth, callback), which returns a new list with the results of calling a callback function on each element of the original list followed by flattening the new list by the specified depth level, wheredepthis an integer orInfinityor-Infinity- If
depthisInfinity, after the map operation, this method flattens all elements contained within nested lists without checking for the existence of self-referential lists - If
depthis0or a negative integer or-Infinity, after the map operation, this method simply returns a new list that is a copy of the original list - This method does not check for the existence of self-referential lists
- If
list.flatten(), which returns a new list where all elements contained within nested lists are flattened into a list without any nested listslist.flattenDepth(depth), which returns a new list where all elements contained within nested lists of the specified depth level are flattened into a list without any nested lists, wheredepthis an integer orInfinityor-Infinity- If
depthisInfinity, this method does the same thing aslist.flattenexcept that the existence of self-referential lists isn't checked - If
depthis0or a negative integer or-Infinity, this method simply returns a new list that is a copy of the original list - This method does not check for the existence of self-referential lists
- If
list.forEach(callback), which executes the callback function for each element in the listlist.index(element), which returns the index value of the element's position in the list, or-1if the element is not in the listlist.indexFrom(element, fromIndex), which returns the index value of the element's position in the list, starting at the indexfromIndex, which is an integer, or-1if the element is not in the list starting at the indexfromIndex- If
fromIndex <= 0, this method returns the same value aslist.index(element) - If
fromIndex > len(list), this method returns-1
- If
list.insert(index, element), which inserts an element into the list at the specified indexlist.isEmpty(), which returnstrueif the list contains no elements andfalseotherwiselist.join(separator), which concatenates all elements in the list into a string where each element is separated by a separator stringlist.last(), which returns the last element in the list. If the list is empty, a runtime error is thrownlist.lastIndex(element), which returns the index value of the last occurrence of the element in the list, or-1if the element is not in the listlist.lastIndexFrom(element, fromIndex), which returns the index value of the last occurrence of the element in the list, starting at the indexfromIndex, which is an integer, or-1if the element is not in the list starting at the indexfromIndex- If
fromIndex >= len(list), this method returns the same value aslist.lastIndex(element) - If
fromIndex < 0, this method returns-1
- If
list.len, which is an alias forlist.lengthlist.length, which is the number of elements in the list as an integerlist.map(callback), which returns a new list with the results of calling a callback function on each element of the original listlist.pop([index]), which removes and returns the element at the specified index from the list. Ifindexis omitted, this method removes and returns the last element from the listlist.reduce(callback, [initialValue]), which applies a reducer callback function on every element in the list from left to right and returns a single valuelist.reduceRight(callback, [initialValue]), which applies a reducer callback function on every element in the list from right to left and returns a single valuelist.remove(element), which removes the first occurrence ofelementfrom the list. Returnstrueif the list containedelementandfalseotherwiselist.removeAll(element1, element2, ..., elementN), which removes all occurrences of each element passed into this method from the list. Returnstrueif an element was removed andfalseotherwiselist.removeAllList(list2), which removes all occurrences of each element that are contained in the specified list argument from the list. Iflist == list2, removes all elements from the list. Returnstrueif an element was removed andfalseotherwiselist.reverse(), which reverses all elements in the list in placelist.reversed(), which returns a new list with all elements from the original list in reversed orderlist.shuffle(), which shuffles all elements in the list in placelist.shuffled(), which returns a new list with all elements from the original list in shuffled orderlist.sort(callback), which sorts all elements in the list in place based on the results of the callback function- The callback function is called with two arguments
aandb, which are the first and second elements from the list to compare respectively - The callback function should return an integer or float where
- A negative value means that
agoes beforeb - A positive value means that
agoes afterb 0or0.0means thataandbremain at the same places
- A negative value means that
- If the callback function does not return an integer or float, it is equivalent to returning
0from the function
- The callback function is called with two arguments
list.sorted(callback), which returns a new list with all elements from the original list in sorted order based on the results of the callback function, which has the same behavior as the function described inlist.sortlist.sum(), which attempts to return an integer, float, bigint, or bigfloat that is the sum of all the elements from the list. If an element from the list cannot be used as an element to sum, a runtime error is thrownlist.toBuffer(), which attempts to return a new buffer with the elements from the list. If the list contains an element that cannot belong in a buffer, a runtime error is thrownlist.toDict(), which returns a dictionary with the keys being list indices and values being corresponding list elementslist.toSet(), which attempts to return a new set with the elements from the list. If the list contains an element that cannot belong in a set, a runtime error is thrownlist.with(index, element), which returns a new list that is a copy of the original list with the original element at the specified index replaced with the new element
- Two lists are compared based on whether they are the same length and for every index
i, the element from the first list at indexiis equal to the element from the second list at indexi - Attempting to use an index value larger than the length of the list will cause a runtime error
- Create a list and assign it to a variable:
- A buffer type is supported in this implementation of Lox, mainly for use in manipulating binary files
- Buffers are similar to lists, except they can only contain integers between 0 and 255 inclusive, and attempting to set a buffer element to an invalid value will throw a runtime error
- Buffers share the same syntax in regards to getting and setting elements, concatenating two buffers into a new buffer, and getting a new buffer with all elements from the original buffer repeated a number of times
- Buffers share the same methods as lists, except that the usual element restrictions are in place in terms of adding and setting elements, and any shared methods that normally return lists return buffers instead
- Notably, the
mapmethod on buffers throws a runtime error if its callback function ever returns a value that is not an integer or is an integer less than 0 or greater than 255
- Notably, the
- Besides the methods shared with lists, buffers also have the following methods associated with them:
buffer.addChar(c), which is an alias forbuffer.appendCharbuffer.addCharNew(c), which is an alias forbuffer.appendCharNewbuffer.appendChar(c), which appends the byte or bytes representation of the specified single Unicode character string argument to the bufferbuffer.appendCharNew(c), which returns a new buffer of the original buffer's contents with the byte or bytes representation of the single Unicode character string argument appended at the end of that new bufferbuffer.memfrob([num]), which applies the XOR operation to each buffer element with the number 42, changing the original buffer as a result. If an integernumis specified, onlynumbuffer elements starting with the first element are changedbuffer.memfrobCopy([num]), which returns a new buffer with the original buffer elements XORed with 42. If an integernumis specified, onlynumbuffer elements starting with the first element are changedbuffer.memfrobRange(start, [stop]), which applies the XOR operation to each buffer element with the number 42 starting from indexstartand stopping at indexstopexclusive, which are both integers, and changing the original buffer as a result. Ifstopis omitted, the length of the buffer is used as the stop valuebuffer.memfrobRangeCopy(start, [stop]), which returns a new buffer with the original buffer elements remaining the same and the elements from integer indexesstarttostopexclusive XORed with 42. Ifstopis omitted, the length of the buffer is used as the stop valuebuffer.tobigint(), which returns a bigint that is the integer representation of the bytes stored in the original buffer in big-endian order- This method throws a runtime error if the original buffer is empty
buffer.toList(), which returns a new list with the elements from the bufferbuffer.toString(), which attempts to convert the elements from the buffer into a string. If a portion of the buffer cannot be converted into a string, a runtime error is thrown, with the error message specifying the portion of the buffer that cannot be converted into a string
- Dictionaries are supported in this implementation of Lox
- Create a dictionary and assign it to a variable:
var dict = {"key": "value"}; - Get an element from a dictionary by key:
dict[key]- It is a runtime error to attempt to get an element using a key that is not in the dictionary
- Set an element:
dict[key] = value; - Merge two dictionaries together:
dict | dict2- If a key exists in both
dictanddict2, the key in the merged dictionary becomes associated with the value fromdict2
- If a key exists in both
- The following cannot be used as dictionary keys: buffer, deque, dictionary, list, queue, set
- Besides these operations, dictionaries also have some methods associated with them:
dictionary.clear(), which removes all keys from the dictionarydictionary.containsKey(key), which returnstrueif the specified key exists in the dictionary andfalseotherwisedictionary.copy(), which returns a shallow copy of the original dictionary as a new dictionarydictionary.get(key, [defaultValue]), which returns the value associated with the specified key from the dictionary, ordefaultValueif the key doesn't exist in the dictionary anddefaultValueis provided, ornilotherwisedictionary.isEmpty(), which returnstrueif the dictionary contains no keys andfalseotherwisedictionary.keys(), which returns a list of all the keys in the dictionary in no particular orderdictionary.removeKey(key), which removes the specified key from the dictionary and returns the value originally associated with the key ornilif the key doesn't exist in the dictionary. Note that a return value ofnilcan also mean that the specified key had a value ofnildictionary.values(), which returns a list of all the values in the dictionary in no particular order
- Create a dictionary and assign it to a variable:
- Sets are supported in this implementation of Lox
- Create a set and assign it to a variable:
var set = Set(element1, element2);- The
Setfunction takes in a variable number of arguments and uses them as the set elements:Set(element1, element2, ..., elementN)
- The
- Operations on sets, where
aandbare sets:- Union:
a | b - Intersection:
a & b - Difference:
a - b - Symmetric difference:
a ^ b - Proper subset test:
a < b - Subset test:
a <= b - Proper superset test:
a > b - Superset test:
a >= b
- Union:
- The following cannot be used as set elements: buffer, deque, dictionary, list, queue, set
- Besides these operations, sets also have some methods associated with them:
set.add(element), which adds an element to the set if it is not already in the set. This method returnstrueif the element was successfully added,falseif it was not, and throws a runtime error if the element is an object that cannot be a set elementset.clear(), which removes all elements from the setset.contains(element), which returnstrueif the specified element is in the set,falseif it is not, and throws a runtime error if the element is an object that cannot be a set elementset.copy(), which returns a shallow copy of the original set as a new setset.isDisjoint(set2), which returnstrueifsetandset2are disjoint, meaning they have no elements in common, andfalseotherwiseset.isEmpty(), which returnstrueif the set contains no elements andfalseotherwiseset.remove(element), which removes the specified element from the set. Returnstrueif the set containedelement, false if it didn't, and throws a runtime error if the element is an object that cannot be a set elementset.toList(), which returns a list of all the elements in the set in no particular order
- Create a set and assign it to a variable:
- Queues are supported in this implementation of Lox
- Create a queue and assign it to a variable:
var queue = Queue(element1, element2);- The
Queuefunction takes in a variable number of arguments and uses them as the queue elements:Queue(element1, element2, ..., elementN)
- The
- Besides these operations, queues also have some methods associated with them:
queue.add(element), which adds an element to the back of the queue and is an alias forqueue.enqueuequeue.back(), which returns the element at the back of the queue- This method returns
nilif the queue is empty
- This method returns
queue.clear(), which removes all elements from the queuequeue.contains(element), which returnstrueif the queue contains the specified element andfalseotherwisequeue.dequeue(), which removes and returns the element at the front of the queue- This method throws a runtime error if the queue is empty
queue.enqueue(element), which adds an element to the back of the queuequeue.isEmpty(), which returnstrueif the queue contains no elements andfalseotherwisequeue.peek(), which returns the element at the front of the queue without removing it from the queue- This method returns
nilif the queue is empty
- This method returns
queue.rear(), which returns the element at the back of the queue and is an alias forqueue.back- This method returns
nilif the queue is empty
- This method returns
queue.remove(), which removes and returns the element at the front of the queue and is an alias forqueue.dequeue- This method throws a runtime error if the queue is empty
queue.toList(), which returns a new list with the elements from the queue
- Create a queue and assign it to a variable:
- Deques are supported in this implementation of Lox
- Create a deque and assign it to a variable:
var deque = Deque(element1, element2);- The
Dequefunction takes in a variable number of arguments and uses them as the deque elements:Deque(element1, element2, ..., elementN)
- The
- Besides these operations, deques also have some methods associated with them:
deque.back(), which returns the element at the back of the deque without removing it from the deque- This method returns
nilif the deque is empty
- This method returns
deque.clear(), which removes all elements from the dequedeque.contains(element), which returnstrueif the deque contains the specified element andfalseotherwisedeque.front(), which returns the element at the front of the deque without removing it from the deque- This method returns
nilif the deque is empty
- This method returns
deque.isEmpty(), which returnstrueif the deque contains no elements andfalseotherwisedeque.pushBack(element), which adds an element to the back of the dequedeque.pushFront(element)which adds an element to the front of the dequedeque.rear(), which returns the element at the back of the deque without removing it from the deque and is an alias fordeque.back- This method returns
nilif the deque is empty
- This method returns
deque.removeBack(), which removes and returns the element at the back of the deque- This method throws a runtime error if the deque is empty
deque.removeFront(), which removes and returns the element at the front of the deque- This method throws a runtime error if the deque is empty
deque.toList(), which returns a new list with the elements from the dequedeque.toListReversed(), which returns a new list with the elements from the deque in reversed order starting from the back of the deque
- Create a deque and assign it to a variable:
- A range type is supported in this implementation of Lox
- A range is a sequence of integers generated on demand, starting from a start value, stopping at but not including the stop value, and updating the current value using the step value
- Examples of creating range objects and assigning them to variables:
var x = range(5);var x = range(1, 6);var x = range(6, 1, -1);var x = range(2, 12, 2);var x = range(12, 2, -2);
- Get an integer from a range object by index, where
indexis an integer:range[index] - Get a new range object by slicing the original range object from indexes
starttoend, wherestartandendare integers:range[start:end] - Ranges are iterables and can be iterated over, yielding the generated integers. For example:
range(5)yields the integers [0, 1, 2, 3, 4]range(1, 6)yields the integers [1, 2, 3, 4, 5]range(6, 1, -1)yields the integers [6, 5, 4, 3, 2]range(2, 12, 2)yields the integers [2, 4, 6, 8, 10]range(12, 2, -2)yields the integers [12, 10, 8, 6, 4]
- Unlike lists of integers, ranges always take up the same amount of memory no matter what the start, stop, and step values of the range are
- Ranges have the following fields and methods associated with them:
range.all(callback), which returnstrueif the callback function returnstruefor all generated integers in the range andfalseotherwiserange.any(callback)which returnstrueif the callback function returnstruefor any generated integer in the range andfalseotherwiserange.contains(num), which returnstrueifnumis in the range based on the start, stop, and step values andfalseotherwiserange.filter(callback), which returns a list containing only the generated integers from the range where the callback function returns a truthy value for themrange.forEach(callback), which executes the callback function for each generated integer in the rangerange.index(num), which returns the index value ofnumin the range or-1ifnumis not in the rangerange.map(callback), which returns a list with the results of calling a callback function on each generated integer from the rangerange.reduce(callback, [initialValue]), which applies a reducer callback function on every generated integer from the range from start to stop based on the step value and returns a single valuerange.start, which is the range object's start value as an integerrange.step, which is the range object's step value as an integerrange.stop, which is the range object's stop value as an integerrange.sum(), which returns the sum of all generated integers from the range as an integerrange.toBuffer(), which attempts to return a buffer with all generated integers from the range as buffer elements, throwing an error if an integer generated from the range is an invalid buffer valuerange.toList(), which returns a list with all generated integers from the range as list elementsrange.toSet(), which returns a set with all generated integers from the range as set elements
- A bigrange type is supported in this implementation of Lox
- Bigranges are like ranges except their start, stop, and step values are bigints instead of integers
- They are created using the
bigrangefunction, which takes the same integer arguments asrangeas well as bigint arguments - They share the same methods as ranges
- Enums are supported in this implementation of Lox
enum Token { ADD, SUBTRACT, MULTIPLY, DIVIDE } var a = Token.ADD; var b = Token.ADD; var c = Token.SUBTRACT; print a; //Prints "Token.ADD" print type(a); //Prints "Token" print a == b; //Prints "true" print a == c; //Prints "false"
- The ability to import built-in Lox files as modules is supported in this implementation of Lox
- Syntax:
import module_name; import module_name as alias;
- The specified module file is executed and all variable, function, and class declarations declared globally in the module file are brought into a class of the same name of the module name that is defined in the global environment of the current file
- If an alias is specified, the globally-defined class will be defined with the alias name instead of the module name
- If the specified module file doesn't exist or if the file exists but an error occurred while it was being executed, a runtime error is thrown
- Lox module files are located in the directory
ast/include- Example:
import hello;will search for a file calledhello.loxin that directory, execute it, and all variable, function, and class declarations declared globally in that file are brought into a class of the same name of the module name that is defined in the global environment of the current file
- Example:
- Syntax:
- The ability to include other Lox files is supported in this implementation of Lox
- Syntax:
include "file-name"; include "file-name" as alias;
- The specified include file is executed and all variable, function, and class declarations declared globally in the included file are brought into the global environment of the current file
- If the specified include file doesn't exist or if the file exists but an error occurred while it was being executed, a runtime error is thrown
includestatements can also have an optional alias specified, in which case only the alias name is brought into the global environment of the current file and all global variable, function, and class declarations from the included file become properties of the alias and can be accessed using the following notation:alias.variable
- Syntax:
- A few other native functions are defined:
arity(callable), which takes in a callable, which is either a function or class, and returns an integer that represents the number of arguments the specified callable expects to receive- If the callable can receive a variable number of arguments,
-1is returned
- If the callable can receive a variable number of arguments,
bfloat(arg), which attempts to convert the specified argument into a bigfloat and returns that bigfloat if successful, otherwise a runtime error is thrown- Valid arguments to
bfloatare the following types: nil, bool, integer, float, bigint, bigfloat, string
- Valid arguments to
biglen(arg), which returns the length of the specified element as a bigint, whereargcan be any type that thelenfunction already acceptsbigrange(stop), which takes in an integer or bigint and returns a bigrange object with a start value of0n, a stop value ofstop, and a step value of1nbigrange(start, stop, [step]), which takes instart,stop, andstepas integers or bigints and returns a bigrange object with the specified parameters. Ifstepis omitted, the resulting bigrange object will have a step value of1nbin(num), which converts the specified integernuminto its binary representation as a string prefixed with "0b"bint(arg), which attempts to convert the specified argument into a bigint and returns that bigint if successful, otherwise a runtime error is thrown- Valid arguments to
bintare the following types: nil, bool, integer, float, bigint, bigfloat, string
- Valid arguments to
bool(arg), which returnstrueif the specified argument is a truthy value andfalseotherwiseBuffer(element1, element2, ..., elementN), which takes in a variable number of arguments and returns a buffer with the arguments as buffer elements. If an argument is not an integer or is an integer less than 0 or greater than 255, a runtime error is thrownBufferCap(capacity), which returns a new buffer of the specified capacity, which is the number of elements the buffer can store before having to internally resize the underlying array that stores the buffer elements when a new element is addedBufferZero(length), which returns a new buffer of the specified length, where each initial element is0cap(item), which returns the capacity of a buffer, list, or stringbuilder, which is the number of elements the item can store before having to internally resize the underlying array that stores the elements when a new element is addedchr(i), which returns a string with a single character that is the Unicode character value of the code pointi, whereiis an integerDeque(element1, element2, ..., elementN), which takes in a variable number of arguments and returns a deque with the arguments as deque elementsDequeIterable(iterable), which takes in an iterable and returns a deque with the iterable elements as deque elementsDictIterable(iterable), which takes in an iterable and returns a dictionary with the keys being integers starting from0and the values being elements from the iterable, with the key that is associated with a value being incremented for each iterable element there is- If the iterable argument is a dictionary, this function returns a new dictionary that is a shallow copy of the original dictionary
eval(argument), which evaluates the string argument as Lox code and returns the result of the final expression in the evaluated code. If the argument is not a string, it is simply returned directly- Warning:
evalis a dangerous function to use, as it can execute arbitrary Lox code and must be used with caution
- Warning:
float(arg), which attempts to convert the specified argument into a float and returns that float if successful, otherwise a runtime error is thrown- Valid arguments to
floatare the following types: nil, bool, integer, float, bigint, bigfloat, string
- Valid arguments to
hex(num), which converts the specified integernuminto its hexadecimal representation as a string prefixed with "0x"input([prompt]), which writes the value ofpromptto standard output if it is provided and reads a line from standard input as a string without a trailing newline and returns that string- Pressing Ctrl+C will throw a keyboard interrupt runtime error, and pressing Ctrl+D will cause this function to return
nil
- Pressing Ctrl+C will throw a keyboard interrupt runtime error, and pressing Ctrl+D will cause this function to return
int(arg), which attempts to convert the specified argument into an integer and returns that integer if successful, otherwise a runtime error is thrown- Valid arguments to
intare the following types: nil, bool, integer, float, bigint, bigfloat, string
- Valid arguments to
isinstance(element, class), which returnstrueif the specified element is an instance of the specified class andfalseotherwiseiterator(iterable), which returns an iterator object from the specified iterable type and throws a runtime error if the argument is not an iterable typelen(element), which returns the length of the specified element, which can be any of the following:- Bigranges: the length is the number of bigints in the bigrange object based on its start, stop, and step values
- Bitfields: the length is the number
8 - Buffers: the length is the number of elements in the buffer
- Deques: the length is the number of elements in the deque
- Dictionaries: the length is the number of keys in the dictionary
- IP address instances: the length is the number of bytes in the IP address instance
- Lists: the length is the number of elements in the list
- Queues: the length is the number of elements in the queue
- Ranges: the length is the number of integers in the range object based on its start, stop, and step values
- RB trees: the length is the number of elements in the RB tree
- Rings: the length is the number of elements in the ring
- Sets: the length is the number of elements in the set
- Strings: the length is the number of characters in the string
- Stringbuilders: the length is the number of characters in the stringbuilder
- Stringreaders: the length is the number of unread bytes in the stringreader
- URL values objects: the length is the number of keys in the URL values object
List(length), which returns a new list of the specified length, where each initial element isnilListCap(capacity), which returns a new list of the specified capacity, which is the number of elements the list can store before having to internally resize the underlying array that stores the list elements when a new element is addedListIterable(iterable), which takes in an iterable and returns a list with the iterable elements as list elementsListZero(length), which returns a new list of the specified length, where each initial element is0oct(num), which converts the specified integernuminto its octal representation as a string prefixed with "0o"ord(c), which returns an integer that represents the Unicode code point of the characterc, wherecis a string that contains a single Unicode characterQueue(element1, element2, ..., elementN), which takes in a variable number of arguments and returns a queue with the arguments as queue elementsQueueIterable(iterable), which takes in an iterable and returns a queue with the iterable elements as queue elementsrange(stop), which takes in an integer and returns a range object with a start value of0, a stop value ofstop, and a step value of1range(start, stop, [step]), which takes instart,stop, andstepas integers and returns a range object with the specified parameters. Ifstepis omitted, the resulting range object will have a step value of1repeatFunc(integer, callback), which takes in an integer and callback function and repeatedly invokes the callback function for the specified integer number of times- If the specified integer argument is negative, it is the same as specifying
0as the integer argument
- If the specified integer argument is negative, it is the same as specifying
Set(element1, element2, ..., elementN), which takes in a variable number of arguments and returns a set with the arguments as set elements with all duplicate elements removed. If an argument cannot be stored in a set, a runtime error is thrownSetIterable(iterable), which takes in an iterable and returns a set with the iterable elements as set elements. If an element from the iterable cannot be stored in a set, a runtime error is thrownsleep(seconds), which pauses the program for the specified number of secondsstr(arg), which converts the specified argument into its string representation and returns that stringsum(iterable), which takes in an iterable and attempts to return an integer, float, bigint, or bigfloat that is the sum of all the elements from the iterable. If an element from the iterable cannot be used as an element to sum, a runtime error is thrown<unsafe> threadFunc(numThreads, callback), which takes in an integernumThreadsand a callback function and spins upnumThreadsthreads that execute the callback function concurrently- If this function is called in non-unsafe mode, a runtime error is thrown
- If
numThreadsis negative, it is the same as specifying0for that argument - The call to
threadFuncblocks until all threads have finishing running - If a runtime error is thrown in a thread, the error is printed to standard error but this doesn't affect the remaining threads
<unsafe> threadFuncs(num, callback1, [callback2, ..., callbackN]), which takes in an integernumand at least one callback function and spins upnum * callbackCountthreads that execute all provided callback functions concurrently, wherecallbackCountis the number of callback functions provided as arguments tothreadFuncs- If this function is called in non-unsafe mode, a runtime error is thrown
- If
numis negative, it is the same as specifying0for that argument - The call to
threadFuncsblocks until all threads have finishing running - If a runtime error is thrown in a thread, the error is printed to standard error but this doesn't affect the remaining threads
type(element), which returns a string representing the type of the element
- This Lox REPL supports typing in block statements with multiple lines
- Expressions such as
1 + 1that are typed into the REPL are evaluated and their results are displayed, with no need for semicolons at the end- Assignment expressions still require semicolons when typed into the REPL as standalone expressions, like
x = 0;,object.property = value;, andlist[index] = value;
- Assignment expressions still require semicolons when typed into the REPL as standalone expressions, like
- Lox files can be included in the
loxcodedirectory, which will cause them to be embedded in the final interpreter executable and executed every time the interpreter starts- If a file from this directory cannot be opened due to an error, it is skipped and a warning message is printed to standard error
- If a parser or runtime error occurs when executing a file from this directory, the interpreter immediately exits with a status code of 1
- If a file from this directory is altered, the interpreter must be rebuilt to include the altered file
See knownbugs.md
- Chapter 4 - Scanning (Complete)
- Chapter 5 - Representing Code (Complete)
- Chapter 6 - Parsing Expressions (Complete)
- Chapter 7 - Evaluating Expressions (Complete)
- Chapter 8 - Statements and State (Complete)
- Chapter 9 - Control Flow (Complete)
- Chapter 10 - Functions (Complete)
- Chapter 11 - Resolving and Binding (Complete)
- Chapter 12 - Classes (Complete)
- Chapter 13 - Inheritance (Complete)
This implementation of Lox is distributed under the terms of the MIT License.