SCiPL
SCiPL (SCi Programming Language)
The SCi programming language is a high-level, interpreted, dynamically typed programming language created by User:Alx.
SCiPL - Data Types
NULL |
Initialized at runtime under names (nil, NULL). This is the NULL object. |
bool |
Represented in code as 'true' or 'false'. |
string |
Represented in code as single or double-quoted literals. |
int |
Represented in code as either a hexadecimal literal, or base-10. |
map |
Spawned via the 'map' function. Allows for key-value tables, aka the map. |
table |
Represented in code with opening curly braces and closing curly braces. |
function |
Type for function protos. |
float |
32-bit floating-point numbers. |
extern |
External types, like char-arrays. |
SCiPL - Syntax
The SCiPL syntax follows the Lua (5.1) syntax, however functions very differently from Lua.
Differences
1. SCiPL has only one scope, being the global scope. Local assignments have no effect on the scope.
2. locally assigning in SCiPL, assigns x-amount of immediate variables to a singular value, whereas normal assignment usually requires the same amount of values to pair the variables. It is also worth noting that if there is no value given for the local assignment, they will all be assigned to the value in the metavariable called '__setslot', which is initialized as NULL. i.e;
local a, b, c -- a, b, and c are all assigned to the contents of the metavariable __setslot local a, b, c = 1 -- a, b and c are all assigned to 1. a, b, c = 1 -- invalid - there must be more values to match the sequence.
3. Function proto parameter names are set as global variables, once called. i.e;
function a_function(a, b) return a + b end a_function(1, 2)
'a' and 'b' are both now global variables, assigned to 1 and 2.
4. Non-builtin function calls (if a value is returned explicitly in the function body) will return the values inside a table. However, if there is no return statement, the function will not return a table, but instead return NULL. If a function body has a return statement, there must be a value to return. i.e;
function ex() -- valid syntax result = 1 + 1 return result end function ex() -- invalid syntax result = 1 + 1 return end
5. If statements cannot have elseif-else clauses. i.e;
if conditions then print(NULL, 1) end
is the only accepted form of if statement.
6. Table indexes start at 0, not 1. Tables also do not support key-value pairing. i.e;
a_table = { -- valid table 1, 2, 3, 4 } a_table = { -- invalid table ["key"] = 1, ["key2"] = 2, }
7. Key-value paired tables (aka the map type) can be spawned using builtin 'map' function, which takes tables paired with key-values as key and value pairs. i.e;
my_map = map( {"key1", 555}, {2, "value"} )
8. Function call argument unpacking is very different. In order to call a function with variable-sized arguments, you cannot use the 'unpack' function, you must comment --[[UNPACK]] before the function call, and the last argument will be unpacked for the function call, which is assumed to be a table. i.e;
--[[UNPACK]]vararg_call(1, {2, 3, 4})
9. f-strings are supported. f-strings are almost pythonic, but are quite different to python's f-strings, as they are just function calls. You can make one look pythonic by a non-parenthesized function call, i.e;
formatted_string = f{"{} + {} is {}", 1, 1, 2} print(NULL, formatted_string)
10. There is no 'break' keyword to break out of loops.
More... There are also many more differences from Lua to SCiPL, such as builtin libraries, builtin functions, and said data types. A list of builtin functions, and their descriptions are below.
Name | Type | Description | Parameter Type(s) |
---|---|---|---|
__include |
Builtin | Includes a SCi module file (compiled .(csci), or plain (.sci)) into the current namespace. | string; |
__discard |
Special Builtin | Discard the topmost item on the stack. | |
__delref |
Special Builtin | Deletes a reference or variable by name. | string; |
__getenv |
Special Builtin | Returns the current namespace. | |
__base |
Builtin | Returns a list of all builtins. | |
__incrref |
Special Builtin | Increments a variable if variable is an int by 1. Used for optimization. | string; |
__decrref |
Special Builtin | Decrements a variable if variable is an int by 1. Used for optimization. | string; |
map |
Builtin | The function that allows spawning key-value tables, aka the map type. | *table; |
print |
Builtin | Prints an object. First argument is an end specifier, if NULL, the end is a newline. | ...; *... |
type |
Builtin | Returns the type of the argument as it's name, as a string. | ... |
math |
Library | A map library of mathematical functions. | |
string |
Library | A map library for string operations. | |
table |
Library | A map library for table operations. | |
iter |
Builtin | Spawns an iterator, usually used for 'for' iterations in an iterable. | (table, map); |
input |
Builtin | Captures stdin with a prompt as the first argument. No prompt if empty. | string; bool; |
stoi |
Builtin | Typecasts a string into an int. | string; |
tostring |
Builtin | Typecasts an object into its string representation. | ...; |
bit32 |
Library | A map library for bitwise operations. (32-bit) | |
range |
Builtin | A numerical-range iterator, usually used in for-loops. | int; int; int; |
f |
Builtin | Returns a formatted string. | table; |
b |
Builtin | Encodes a string into a byte-string, or char array. | string; |
unpack |
Special Builtin | Unpacks table items onto the stack. | table; |
io |
Library | A map library for file operations. | |
dump |
Builtin | Compiles and returns the bytecode of provided code in string representation. | string; |
fpairs |
Builtin | Calls a function on every value inside the provided table, and returns an equivalent table with the new items. | function; table; |
return_call |
Builtin | Calls a function with a table of arguments, and returns the first item in the return table. | function; table; |
SCiPL - Internals
The current SCiPL interpreter is a stack-based virtual machine. There are currently (as of SCiPL version v0-m6 b) 40 opcodes. The instruction notation is explained below:
The instructions are a fixed size of 40 bits / 5 bytes.
Instruction Type A
First Bit (0) Operand A: 32 bits Opcode: 6 bits Opmode: 2 bits Last Bit (40)
Instruction Type B
First Bit (0) Operand A: 8 bits Operand B: 8 bits Operand C: 16 bits Opcode: 6 bits Opmode: 2 bits Last Bit (40)
The current instruction set is described in the following table.
Opcode | Description |
---|---|
LOAD_GBL | Loads a global variable onto the stack. |
LOAD_KST | Loads a constant from the constants pool onto the stack. |
LOAD_UL | Loads an unsigned-long integer onto the stack. |
IJMP | Incremental absolute jump. |
DJMP | Decremental absolute jump. |
MKTBL | Creates a table consuming x-amount of items from the stack. |
MKMAP | Creates a key-value pair map consuming x-amount of items from the stack. Obsolete opcode. |
SUBSCR | Performs subscription on a table. |
SET_GBL | Pops an item off the stack, and sets it under a global variable. |
SET_SUBSCR | Subscripts a table or map, and assigns the key/index as a value. |
ADD | Pops two items off the stack, and performs addition. |
MUL | Pops two items off the stack, and performs multiplication. |
DIV | Pops two items off the stack, and performs division. |
POW | Pops two items off the stack, and performs exponentiation. |
MOD | Pops two items off the stack, and performs modulo. |
SUB | Pops two items off the stack, and performs subtraction. |
DISCARD | Pops the topmost stack item off the stack. |
CALL | Consumes x-amount of items off the stack as arguments, +1 extra as the callable, and calls. |
NADD | Equivalent to ADD, except with variables. Used for optimization. |
NMUL | Equivalent to MUL, except with variables. Used for optimization. |
NDIV | Equivalent to DIV, except with variables. Used for optimization. |
NPOW | Equivalent to POW, except with variables. Used for optimization. |
NMOD | Equivalent to MOD, except with variables. Used for optimization. |
NSUB | Equivalent to SUB, except with variables. Used for optimization. |
MKFUNC | Creates a callable function from TOS, which is assumed to be the function proto. |
IJMP_FALSE | Conditional incremental jump. |
DJMP_FALSE | Conditional decremental jump. |
NOT | Performs a logical NOT on the topmost stack item. |
LAND | Pops two items off the stack and performs a logical AND. |
LOR | Pops two items off the stack and performs a logical OR. |
RETURN_NUL | Returns NULL. This is at the end of every code body. |
RETURN | Returns x-amount of items from the stack, in a table. |
CMP | Pops two items off the stack, and performs one of the following actions: <, >, <=, >=, ~=, ==. |
LOAD_NUL | Loads NULL onto the stack. |
LOAD_BOOL | Loads a boolean onto the stack, either being true or false. |
CONCAT | Pops two items off the stack which are typecasted to strings, and concatenates them together. |
GET_LEN | Gets the length of TOS, which is assumed to be either a string, table map or other iterable. |
FORJMP | For-iterator conditional jump. Jumps to offset x once iterator is exhausted. |
NEG | Inverts the topmost stack item. |
LD_FLOAT32 | Loads a float onto the stack. |
SCiPL - Extras
Example Programs
1 - Guess the Number Game
__include("librandom") min = stoi(input("Pick a number: ")) max = stoi(input("Pick another: ")) n = unpack(randint(min, max)) guesses = 1 print(NULL, f{"I'm thinking of a number between {} and {}. Good luck!", min, max}) while true do check = stoi(input("Your guess: ")) if (check == n) then return print(NULL, f{"You guessed my number in {} tries!", guesses}) end if (check < n) then print(NULL, "Too low!") end if (check > n) then print(NULL, "Too high!") end guesses = guesses + 1 end
STDOUT:
Pick a number: 1 Pick another: 10 I'm thinking of a number between 1 and 10. Good luck! Your guess: 1 Too low! Your guess: 4 Too low! Your guess: 7 Too high! Your guess: 6 You guessed my number in 4 tries!
2 - Calculator App
play = true function add() a = stoi(input("Number A > ")) b = stoi(input("Number B > ")) print(NULL, "The result of A + B is:", a + b) end function multiply() a = stoi(input("Number A > ")) b = stoi(input("Number B > ")) print(NULL, "The result of A * B is:", a * b) end function sub() a = stoi(input("Number A > ")) b = stoi(input("Number B > ")) print(NULL, "The result of A - B is:", a - b) end function get_input(a) print(NULL, [[ [A]dd [M]ultiply [S]ub [Q]uit ]]) test = input(" > ") test = string.upper(string.subscript(test, 0, 1)) if (test == "A") then return add() end if (test == "M") then return multiply() end if (test == "S") then return sub() end if (test == "Q") then play = false end end print(NULL, "Welcome to the Calculator:") while play do get_input() end
STDOUT:
Welcome to the Calculator: [A]dd [M]ultiply [S]ub [Q]uit > m Number A > 10 Number B > 5 The result of A * B is: 50 [A]dd [M]ultiply [S]ub [Q]uit > q
3 - XOR cipher message
__include("libcodec") function xor_cipher(plaintext, key) r = "" for i=0, #plaintext do key_byte = string.byte(key, (i % #key)) plaintxt_byte = string.byte(plaintext, i) r = r .. string.char(bit32.bxor(plaintxt_byte, key_byte)) end return r end text, key = "here is a message i need ciphered", "and a key" ciphertext = unpack(xor_cipher(text, key)) print(NULL, tostring(encode(b(ciphertext), "hex"), 1)) plaintext = unpack(xor_cipher(ciphertext, key)) print(NULL, plaintext)
STDOUT:
090b164541491845184103015312410c0059084e0a4504444b0610110601520444 here is a message i need ciphered
4 - Adler32 Checksum Algorithm
function adler32(stream) local ctr, B = 0 A = 1 while (ctr < #stream) do c = string.byte(stream, ctr) A = (A + c) % 0xFFF1 B = (A + B) % 0xFFF1 ctr = ctr + 1 end result = bit32.bor(bit32.bshl(B, 16), A) return result end data = {"a very long string, indeed", "data1", "data2", "data", "dtaa" } for i in iter(data) do v = unpack(adler32(i)) print(NULL, v) __discard() end
STDOUT:
2140998020 97255884 97321421 67109275 68354459
Update
Regarding the `vararg call` in SCiPL Syntax section 8, I mean unpack, not vararg