-
Notifications
You must be signed in to change notification settings - Fork 0
Home
An Islang program consists of 4 main parts:
The order of these parts is unimportant, and in fact is just a suggestion. They are all optional. This means a blank file is technically valid, although a bit unhelpful. Whitespace at the start of the line is important, as it indicates blocks and scopes. Consecutive lines indented the same amount are part of the same block, and probably the same scope as well. Statements end with a newline, rather than a semicolon or any other symbol. This means you may not put newlines anywhere you want, as that will change the meaning of your program. There is an exception to this rule when declaring a list or an enum, as newlines between the delimiters (square brackets or brackets, respectively) will be ignored, for convenience.
Line comments start with a #, and block comments start with /* and end with */. Additionally, block comments can be nested.
# This is a line comment
/* A block comment*/ # And then a line comment
/* /* This is perfectly */ fine */
Islang is a typed language. You declare a variable like so:
typename variablename -> value
There are 6 different types:
-
num: Number. A floating point number, used for all numbers. -
str: String. A string. -
bool: Boolean. A boolean value. -
list: List. A list (Like an array) of values of the same type. -
enum: Enumeration. An enumeration. -
val: Enumeration value. Represents an enumerator value.
Numbers are pretty easy. They are written like you'd write them in python, for example. 1, -27.2761 and 888888 are all possible values for numbers.
num hours_spent -> 100
Strings are delimited by "" quotes. There's no other way to indicate a string. You may escape special characters with \, like \" if you want to show a quote, or \{ if you want to open a bracket. Additionally, \n is a newline (Which will probably mess with the program's output) and \t is a tab.
Strings can also contain arbitrary values in them, which will be converted to string format when printed onto the screen. To do this, simply write the value in curly brackets inside the string, like so:
str some_string -> "String string string {some_variable} string string string {100003}"
some_variable will be evaluated when the string has to be printed, and so will the number 100003. If some_variable was, say, 1, the string in the end will be String string string 1 string string string 100003.
Booleans hold a true/false value, or a probability. If they hold a probability, it will be evaluated once when needed.
true and always are keywords that signify a value of 1, or true. On the other hand, false and never mean 0, or false. These keywords always evaluate to the same.
Probabilities exist in 2 main forms, percentages and ...not percentages.
Percentages are written, as would be expected, in the form X%, for example, 50% or 0% or 8.3762%. They will evaluate to true X percent of the time, that is, X times out of 100.
Non-percentages are written in the form X^, for example 50^, 2^ or 1.004^. They will evaluate to true 100/X percent of the time, that is, 1 out of X times they're evaluated.
Additionally, every value in a program can evaluate to either true or false. The number zero, an empty string, the boolean value false and the empty list evaluate to false. Everything else evaluates to true.
bool warm -> 80%
Lists hold several values of the same type at once. To make a list, simply open square brackets, write your values separated by commas, and then close square brackets, like so:
list colors -> ["Red", "Blue", "Yellow", "Green"]
Ta-dah. Lists can hold other lists as well, as long as the lists are of the same type:
list list_of_lists -> [[0,1,2,3], [4,5,6,7]]
You might be tempted to declare an empty list with [], but that won't work (Since there's absolutely no reason for empty lists). Don't try it, you'll make the interpreter sad.
To access a list element, index it by writing the element number between brackets after the list, like colors[0]. Lists are 0-indexed, of course (As it should be). There's other ways to access elements discussed here.
List elements may also have a probability assigned to them, for use with the pick operators. To assign a probability to an element of a list, write ~ probability after it, like so:
list colors_biased -> ["Red" ~ 5%, "Blue" ~ 85%, "Yellow" ~ 5%, "Green" ~ 5%]
Probabilities count towards the list type (That is, you cannot mix elements with and without probabilities in a list, or have a list of lists, some with probabilities, some without)
Enumerations are simply a convenient way of storing arbitrary values that are related. To make one, simply open brackets, write your enumeration elements separated by commas, then close brackets, like so:
enum colors -> {RED, BLUE, YELLOW, GREEN}
Then you can access any of the values like so: enum_name.enum_value, for example, colors.RED.
Enumerations can be made into lists with a (TODO: THINK OF NAME !important) expression. You do that like so: enum_name{prob1, prob2 ... probn}, where n is the amount of values the enum holds.This makes a list where the first value of enum_name has prob1, the second value has prob2, etc. This is convenient if you want to pick.
list colors? -> colors{5%, 85%, 5%, 5%}
Enumeration values hold, well, enumeration values. They may only be assigned values from an enumeration. Pretty easy, right?
val color -> colors.BLUE
The main part of an Islang program takes part in blocks. They contain all the "logic" to your program. There's 3 types of blocks, which all work exactly the same, and are only different in name and representation (As each will show its own type name on the console). These are: location blocks, event blocks and contains blocks.
To declare a block, you may do it like so:
block_type block_name (appear_chance):
Appear chance may be ommitted, and if the block is empty, the colon at the end may also be ommited.
A block spans all consecutive statements that have the same indentation, which also has to be more indented than the block declaration line.
All variables declared inside a block belong to that block, and to access them you need to prepend the variable name with the block name and a dot, like this: block_name.variable_name. If the block belongs to another block, then you prepend that with the parent block, and so forth. As an exception, variables in parent blocks can be accessed without any prefixes, as long as no variable with the same name exists in a block before it.
location blah:
num pi -> 3.14
location blah_blah:
num pi -> 3.2
location here:
str pi_str -> "{pi}" # Will find blah.blah_blah.pi and will become "3.2"
str pi_str_2 -> "{blah.pi}" # Will find blah.pi and will become "3.14"
num pi -> 4
str pi_str_3 -> "{pi}" # Will find blah.blah_blah.here.pi and will become "4"
location there:
# str other_str -> pi_str # This is an error
str other_str -> blah.blah_blah.here.pi_str # This works fine
Blocks also have internal properties that affect how they are shown in the console. They can be modified using the block keywords. These are:
- Name: The name the block will show in the console, if not set, the block's name will be the name you gave it in the program.
- Appear chance: The chance the block and blocks belonging to this block will appear when showing the program. As mentioned, it can also be specified when declaring the block, in parenthesis:
location name(50%):. The default is100%, or to always appear. - Color: The color the block and the associated strings will show on the console. The default is the console's default color.
Islang has a bunch of operators, so for convenience I'll divide them in 3 categories: Boolean operators (which return a boolean), List operators (which operate on list) and other operators (Whose function is different from the others)
x and y represent arbitrary values.
-
and: And. The logical and, used like so:x and y. It will return true if bothxandyevaluate to true, and false otherwise. -
or: Or. The logical or, used like so:x or y. It will return true if eitherxoryevaluates to true, and false otherwise. -
not: Not. The logical negator, used like so:not x. It will return true ifxevaluates to false, and false ifxevaluates to true. -
=: Equals. The equality check, used like so:x = y. It will return true ifxandyare both the same type and evaluate to the same value, and false otherwise. List are deep-compared element by element. -
~=: Not equals. The inequality check, used like so:x ~= y. It will return true ifxandyare either different types or do not evaluate to the same value, and false otherwise. List are deep-compared element by element.
x and y represent arbitrary values. l represents an arbitrary list.
-
*: Pick. The biased pick, used like so:l*xorx*lor*lorl*. It will pickxelements froml, according tol's element's probabilities if they have any, or randomly if they don't. Ifxis 1, it will return the picked value, if it's greater than 1 it will return a list with all chosen values. Ifxis not present, only 1 element will be picked. -/: Xpick. The biased, exclusive pick, used like so:l/xorx/lor/lorl/. It works exactly like the pick operator, except if it were to take more than one element it'll never pick the same element twice. As a consequence, you may never xpick more elements from a list than the size of the list. -
**: Upick. The unbiased pick, used like so:l**xorx**lor**lorl**. It functions exactly like the pick operator, except it will ignore probabilities and always pick random elements. -
//: Uxpick. The unbiased exclusive pick, used like so:l//xorx//lor//lorl//. Does the exact same thing as the xpick operator, except it will ignore probabilities.
-
::: The special scope, or magic operator. It lets you access color names and other language built-ins. Use it like so:::builtin_name. -
->: Is. Used to assign a value to a variable. Normally only used when declaring variables, as they need to be initialized immediately. -
~: If. Context sensitive. Inside a list it assigns a probability to a list element. Outside of lists it can be used in cojunction with thereprkeyword to make it conditional.
Global keywords may be used in the outermost part of the program, as their functionality does not depend on being inside a block. There's only 2 global keywords.
The import keyword lets you call upon other files, to "import" their variables and blocks. It is used like so: import name as alias or import "filename" as alias, where name is the name of the file you want to import, without extension (it has to be in the same directory as the file that's being run), filename is the full name of a file to import (Can be a path, relative or absolute), and alias is the name with which to import the content, as if it were a block. If as alias is ommited, the content will be imported under the filename, without extension and without the path.
/* pi.isl */
num pi -> 3.14
/* colors.isl */
list colors -> ["Red", "Blue", "Yellow", "Green"]
/* main.isl */
import "pi.isl"
import colors as color_list
num re_pi -> pi.pi
list re_colors -> color_list.colors
The generate keyword is placed at the very end of a program, and it tells the program which block is the main block when it needs to print the program to the console. It is used like so: generate block_name, where block_name is the name of a block.
-
name: To set the name of the block. Used likename "Name of the block". Setting the name more than once per block is not allowed -
color: To set the color of the block. Used likecolor ::color_name. Setting the color more than once per block is not allowed. -
appear: To set the appearance chance of the block, if not already specified. Used likeappear chance, where chance is a probability or a not-probability. Setting it more than once per block is not allowed. -
use: To "import" variables or blocks into the current block. It can be used like so:use var_or_block as alias, wherevar_or_blockis the name of a variable or a block, andaliasis the name you want to use it as. If the alias is not present, it will be accessible by its original name, as if it were in the current scope.location main: location sub: num phi -> 7 location sib: use main.sub.phi as phi use main.sub as sibling num phi_1 -> main.sub.phi # Can do num phi_2 -> pho # Can do too, using the aliased version num phi_3 -> sibling.phi # Also allowed -
repr: Thereprkeyword (Stands for "representation") is used to show text on the console whenever the block it belongs to is printed. It's used like sorepr "Some string" ~ if_expr, whereif_expris a value that evaluates to a boolean. If~ if_expris not present, the string will always be printed to screen, otherwise it will only be printed ifif_exprevaluates to true. Repr accepts any expression as arguments. -
opts: The opts keyword (Stands for "options") works similarly torepr, except it takes a list (Of which it will print one as if picked by thepickoperator). It is used like so:opts ["Option 1", "Option 2", "Option 3"] -
else: Always used in conjunction withreproropts, and always used right after a conditional repr or opts statement. Used like soelse repr "Some string" ~ if_expr. An else repr or opts statement will only be evaluated if the previous repr or opts statement'sif_exprwas evaluated, and it evaluated to false. If the previous repr or opts statmenent's condition was not evaluated (Which can happen if the previous statement is also an else repr or opts statement) it won't be evaluated. This means that, for a group of consecutive else repr or opts statements, plus the preceding single repr or opts statement, only one will ever be printed to string.repr "12 + 144 + 20" ~ 10% # Only one else repr "+ 3 * sqrt(4)" ~ 20% # of these else repr "/ 7 + 5 * 11" ~ 30% # will be else repr "9² + 0" ~ 40% # eventually printed else repr "How's that for poetry?" # to console
-
run: Will run the interpreted program once -
exit: Will exit the program