|
LanguageRules
A description of the Kitsch programming language
IntroductionThis page introduces Kitsch, and explains how to program in it.
CommentsKitsch currently only supports a single-line comment. Any text after a '#' symbol on the current line will be ignored VariablesAs an imperative programming language, variables are the heart of Kitsch. This section explains how to use them. TypesKitsch has three types of variables
Note that strings are treated as a primitive type. Kitsch's ints are arbitrary precision integers, and bools are, as one would expect, either True or False. Kitsch also has an array type for each of the three primitive types. No implicit conversions are performed between types. Casting must be used to change a variables type. Scope RulesVariables belong to the scope they are first assigned in. Functions can only access their parameters. DeclarationKitsch is a strongly typed language with type inference. Variable types are not explicitly declared - they are inferred by the Compiler. Assignment<identifier> = <expression> identifier is the name of this variable; this name must be unique in the current scope. The type of expression is inferred unless explicitly cast (see the section on Casting for details). If a variable is assigned a new value, the identifier's type will be changed to the type of the new expression. In other words, the most recent assignment to the identifier will be that identifier's current type. Examplex = 42 y = 96 / 17 s = "This is a string!" # Case matters!, must be 'True' or 'False' b = True x = "foobar" # x is now a string, not an int CastingSince Kitsch does not perform implicit conversions it provides an explicit casting mechanism to change a variable's type. Available types include int, string, and bool. Examplei = 42 x = (int) "123" s = (string) i # s will contain the value "42" b = (bool) i # Will be True b = (bool) 0 # Will be False Value Conversions
StringsConcatenationKitsch overloads the addition + operator for concatenating two strings a = "foo" b = "bar" c = a + b # String c will contain "foobar" MultiplicationKitsch overloads the multiplication * operator between a string and an integer. It is commutative - string * int is the same as int * string a = "foo" i = 5 print a*3 # prints 'foofoofoo' print a*i # prints 'foofoofoofoofoo' print 3*a # prints 'foofoofoo' print i*a # prints 'foofoofoofoofoo' Substrings / String-slicingCharacters in a kitsch string can be indexed like elements of an array. The index is 0-based (the first character is index 0). a = "foobar" print a[3] # prints 'b' Slicing allows a range to be specified rather than a single digit. The last index is not included in the substring. So the range [0:3] would include characters at indexes 0, 1, and 2. Because of this, if a range is specified with both the starting index and the ending index being the same, the result is an empty string (because it does not include the character at the last index) a = "foobar" print a[0:3] # prints 'foo' print a[3:3] # prints an empty string, '' Either element in the range can be left blank. If the first element in the range is left blank, it means to start from the beginning of the string and is equivalent to start with a 0. If the second element of the range is left blank, it means to go until the end of the string is reached Finally if neither is included, the result is the original string a = "foobar" print a[3:] # prints 'foo' print a[:3] # prints 'bar' print a[:] # prints 'foobar' Indexes can also be negative! This means it wraps around the string. So for example, the character at index -1 is the last character in the string, at index -2 is the 2nd to the last character in the string. a = "foobar" print a[-1] # prints 'r' print a[:-1] # also prints 'r' print a[:-3] # prints 'bar' Expressions and OperatorsKitsch has a standard C-like operator precedence. Bitwise operators are identical to C. Boolean operators are verbose rather than symbolic, like Python or VB. Substring and range operators are similar to Python, with slicing supported. The following are listed from highest priority to lowest priority.
Expressions using ints give int results, and can contain identifiers and numeric constants and can be assigned to integers. Expressions using comparison operators and logical operators (and, or, not) are only valid within an if or while test, or as part of a boolean expression (ie, assignment to a bool variable). Overloaded Operators+ is supported between int types for addition + is supported between string types for string concatenation + is supported between array types for concatenation. The array types must be the same. For example, int[] and int[] can be added together, but string[] and int[] cannot. * is supported between int types for integer multiplication * is supported between int and string for duplication of strings. All other arithmetic and bit-wise operators are only supported by int variables. Conditional Control StructureSo far the language only supports an "if/elif/else" form of conditional control. Rather than using curly {} braces for blocks, the if statement has a matching fi statement to close the block. A colon is used to separate the expressions in the condition from the statements in the block. Multiple tests can be created by using the elif keyword and a condition, and a final default block with the else keyword. NOTE: Conditionals must be either boolean variables, comparisons (like <, ==, etc), or logical combinations thereof (using not, and, and or). Formif <some comparison> : <some statements> elif <another condition> : <more statements> else : <more statements> fi Valid Examplesif x < y : x = y elif x > y : y = x else : x = z y = z fi if True:
<statements>
fi if boolvar1 and boolvar2 :
<statements>
fiInvalid ExamplesConditionals may not have just strings or integers if "foobar":
<statements>
fi if 42:
<statements>
fiIterationAt the moment the language only supports a while loop for iteration. An iterator based 'for' loop is planned for the future. Formwhile <some condition> : <statements> elihw Examplewhile x < 100:
sum = sum + x
x = x +1
elihwInput/OutputI/O is accomplished through the use of print and read statements. print _expr_ prints the result of the expression followed by a newline to standard out. read <ident> reads the next line from standard in. The endline is not included in the string. All values read in become strings. An explicit cast must be made to make it into another type. Reading blocks indefinitely until a newline is received(\n). The text can be empty, but must still be followed by a newline. Exampleread x # this will set "x" equal to the next line of text entered via stdin print x # Print out the string + \n to stdout FunctionsFunction DeclarationFunctions are declared with the following syntax. def functionName(<required_parameters>*,<optional_parameters>*):
<function statements>
<return type> return <expression>
fedFunction CallingFunctions are called/invoked with the following syntax. Arguments are positional, the first argument passed into the function will be the first parameter to the function. All function arguments are passed by value. functionName(<argument1>,<argument2>,...,<argument n>) Function Rules
Examples
# A simple function with no arguments and no return statement, this will return 0
def simple():
print "Hello World!"
fed
# Call the simple function
simple()
# Adds two numbers, x and y, and returns the result
def add(x,y):
int return x+y
fed
# Call the function with arguments
add(10,20)
# Or assign its result to a variable
sum = add(10,20)
# Or use it an expression
sum = add(10,20)+30
print "Sum is: " + (string) sum
# Adds two numbers, x and y, and returns the result.
# y is optional and defaults to a value of 42 if not specified by the caller
def addDefault(x,y=42):
int return x+y
fed
# Call it with both parameters specified
addDefault(10,20)
# Call it with just the required parameter, y will be 42 inside the function
addDefault(10)
Invalid Examples# A simple function meant to concatenate 2 strings
def concat(s1,s2="bar"):
string return s1+s2
fed
# We can call it first with 2 string arguments - this is valid
s = concat("foo","bar")
# Now try to call it again with different arguments - this is INVALID
# 'concat' has already had the 's1' argument inferred as a string, an integer is a type error
s = concat(42,"bar")
# Now try to call it with a default value whose value is of a different type - INVALID
s = concat("foo",42)
# This function is invalid because it has a required argument after a default parameter
def invalidDefault(y=42,z):
int return x+y+z
fedArraysArrays are dynamically allocated on the heap at run time. They do not need to be explicitly declared. An arrays must contain homogeneous primitive elements. Every element in the array must be of the same type. Arrays of arrays are not supported yet. Creating ArraysAn array is created by either being explicitly initialized, or via an index assignment. Arrays can be initialized with the following syntax: <identifier> = [expression1, expression2, ..., expressionN] Example: a = [100,200,300,400,500] Arrays can also be created simply by assigning to an index of the identifier. If the identifier does not already exist, an array is created. All of the other values are initialized to either 0, an empty string, or False, for int, str, bool, types respectively. # This creates an array of 6 elements, the other elements are initialized to 0
b[5] = 42 If the array already exists, but an index is used that exceeds the currently allocated space for the array, then Kitsch automatically reallocates a new array and updates the pointer value. There is no such thing as an index out of bounds error for a positive integer. Array AssignmentArray elements are assigned using the following syntax <identifier>[expression] = <expression> The index can be any integer expression. A negative index results in a run-time error. 0 is the index of the first element in the array. Example: a[0] = 0
a[1] = 42
a[2*4] = 100
a[a[1]] = 200
x = 0
while x < 5:
a[x] = x**2
x = x + 1
elihwArrays in memoryThe identifier associated with an array is a pointer to the beginning of the array structure on the heap. The array structure contains the actual length of the array, the amount of memory allocated for the array, and all of the elements of the array (starting with the value at index 0). Arrays as parametersThe identifier is a pointer (reference) to the array. Therefore a function that modifies the elements of the array will modify the original array in the caller's scope. Arrays are not copied on the stack when passed into functions. The pointer itself is copied by value, so if a new array is created (by assigning to an element larger than the current length, or adding two arrays) then the identifier in the caller will not point to the new array. Example of modifying elements: a [1,2,3]
print a
def f(array):
i = 0
while i < len(array):
array[i] = array[i] + 1
i = i + 1
elihw
fed
f(a)
print aThe pointer to the array 'a' is passed by value, however simply modifying the individual elements of the array does not cause a relocation of the array, so the reference to 'a' after the function call points to the modified array Output: [1, 2, 3] [2, 3, 4] Now consider an example where a new list is created a = [1,2,3]
def f(array):
array = array + array + array
fed
print a
f(a)
print aOutput: [1, 2, 3] [1, 2, 3] A new array was created because array+array+array could not fit in the original array's allocated memory. Therefore 'a' contains a reference to the original [1, 2, 3], not [1, 2, 3, 1, 2, 3, 1, 2, 3]. Note that the internal allocation of arrays reserves more space than is required anytime an array is realloced, so the array pointer may not always be moved if elements are assigned to the array larger than its original len(), but this behavior should not be relied upon. An array pointer should be returned if you want to expand an array inside a function. Example: a = [1,2,3]
def f(array):
int[] return array + array + array
fed
print a
a = f(a)
print aOutput: [1, 2, 3] [1, 2, 3, 1, 2, 3, 1, 2, 3] If you need to return both an updated pointer reference and another value, then you must create an array containing a reference to the modified array and your desired return value(s) Array OperationsThe only operator supported between arrays is addition. It concatenates the elements of the arrays Example: a = [1,2]
b = [10,11,12]
c = a + b
# c = [1,2,10,11,12]Built-in FunctionsKitsch contains a library of functions that get compiled into all programs (currently the Compiler does not intelligently link functions into the target-code based on whether or not the are used). These functions are used for things like allocating memory on the heap, copying arrays, initializing arrays, and printing arrays. Most of these built-ins are called directly by the compiler and are not meant to be used by user-code (but they could if they wanted to). len()The len() function is a global built-in function that returns the length of an array. The length of the array is determined by the largest index that has been assigned to in the array. If an array is created, or resized, by assigning to an index, then the length will be 1 greater than that index - not the number of elements that have been explicitly assigned. len() only works on arrays, it will not work for a string. a = [2,4,6] size = len(a) # size will be an integer, 3 b[5] = 0 size = len(b) # size will be 6, not 1 |