diff --git a/.gitignore b/.gitignore index 454e70f0..2115a54b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ *~ dist/ asteroid_lang.egg-info/ +action-test68.png diff --git a/asteroid/frontend.py b/asteroid/frontend.py index 1e55a13b..a8b4a1da 100644 --- a/asteroid/frontend.py +++ b/asteroid/frontend.py @@ -778,14 +778,14 @@ def call_or_index(self): # | FALSE # | NONE # | ID - # | '*' ID // "dereference" a variable during pattern matching + # | '*' call_or_index /* pattern dereferencing */ # | NOT call_or_index # | MINUS call_or_index # | PLUS call_or_index # | ESCAPE STRING # | EVAL exp - # | '(' tuple_stuff ')' // tuple/parenthesized expr - # | '[' list_stuff ']' // list or list access + # | '(' tuple_stuff ')' /* tuple/parenthesized expr */ + # | '[' list_stuff ']' /* list or list access */ # | function_const # | TYPEMATCH // TYPEMATCH == '%' def primary(self): @@ -823,8 +823,8 @@ def primary(self): elif tt == 'TIMES': self.lexer.match('TIMES') - id_tok = self.lexer.match('ID') - return ('deref', ('id', id_tok.value)) + v = self.call_or_index() + return ('deref', v) elif tt == 'NOT': self.lexer.match('NOT') diff --git a/asteroid/grammar.txt b/asteroid/grammar.txt index cf0431c2..6308fde6 100644 --- a/asteroid/grammar.txt +++ b/asteroid/grammar.txt @@ -123,7 +123,7 @@ //////////////////////////////////////////////////////////////////////////////////////// // primary expressions/patterns - + primary : INTEGER | REAL @@ -132,7 +132,7 @@ | FALSE | NONE | ID - | '*' ID /* "dereference" a variable during pattern matching */ + | '*' call_or_index /* pattern dereferencing */ | NOT call_or_index | MINUS call_or_index | PLUS call_or_index diff --git a/asteroid/test-suites/regression-tests/test128.ast b/asteroid/test-suites/regression-tests/test128.ast new file mode 100644 index 00000000..5d528e0b --- /dev/null +++ b/asteroid/test-suites/regression-tests/test128.ast @@ -0,0 +1,9 @@ +-- testing function calls within patterns + +function goo with none do + return pattern x. +end + +let *goo() = 4. +assert (x==4). + diff --git a/asteroid/walk.py b/asteroid/walk.py index 5c5e8ebf..5d167430 100644 --- a/asteroid/walk.py +++ b/asteroid/walk.py @@ -391,9 +391,10 @@ def unify(term, pattern, unifying = True ): check_repeated_symbols(unifier) #Ensure we have no non-linear patterns return unifier - elif pattern[0] == 'deref': # ('deref', ('id', sym)) - (ID, sym) = pattern[1] - p = state.symbol_table.lookup_sym(sym) + elif pattern[0] == 'deref': # ('deref', v) + # v can be an AST representing any computation + # that produces a pattern. + p = walk(pattern[1]) #lhh #print("unifying \nterm:{}\npattern:{}\n".format(term,p)) @@ -1150,7 +1151,7 @@ def if_stmt(node): assert_match(LIST, 'list') for i in range(0,len(if_list),2): - + lineinfo = if_list[ i ] process_lineinfo(lineinfo) @@ -1462,7 +1463,7 @@ def to_list_exp(node): # Get the stride_val stride_val = int(stride_val) - # Change the direction of the stride value based on the + # Change the direction of the stride value based on the # ends of the range. I.e. 5->1 has an implicit direction # of -1, 1->5 has a direction of +1 direction = (1 if ix < ex else -1) @@ -1470,7 +1471,7 @@ def to_list_exp(node): # We need to modify the ending index to acccount for python # ranges. For example, for 1->10 we want range(1, 10 + 1). - # Or, for the opposite, we want range(10, 1 - 1) to give + # Or, for the opposite, we want range(10, 1 - 1) to give # us the full inclusive range. Thus, we can just add our # direction new_ex = ex + direction diff --git a/docs/Asteroid in Action.rst b/docs/Asteroid in Action.rst index 5ed42e19..4d380375 100644 --- a/docs/Asteroid in Action.rst +++ b/docs/Asteroid in Action.rst @@ -10,11 +10,10 @@ - - .. *** DO NOT EDIT; MACHINE GENERATED *** + .. highlight:: none Asteroid in Action @@ -32,10 +31,12 @@ Challenge: Hello, World! The canonical ``Hello, World!`` program. The easiest way to write this in Asteroid is, :: + load system io. io @println "Hello, World!". + Output:: Hello, World! @@ -49,6 +50,7 @@ Challenge: Greet a person Here is our first solution using a separate function for each of the steps, :: + load system io. io @print ("Enter your name: "). @@ -56,19 +58,24 @@ Here is our first solution using a separate function for each of the steps, io @print ("Hello, "+name+"!"). + Letting the function ``input`` do the prompting, :: + load system io. let name = io @input("Enter your name: "). io @println ("Hello, "+name+"!"). + Doing everything in one step, :: + load system io. io @println ("Hello, "+io @input("Enter your name: ")+"!"). + Challenge: String length ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,11 +83,13 @@ Challenge: String length In order to print the length of a string we can use the function ``len`` available in the ``util`` module, :: + load system io. load "util". io @println (len("Hello!")). + Output:: 6 @@ -88,10 +97,12 @@ Output:: We can also use the string member function ``length`` in order to compute the length of the string, :: + load system io. io @println ("Hello!" @length()). + Output:: 6 @@ -105,6 +116,7 @@ Challenge: Unique digits In order to accomplish this we take advantage of the string ``explode`` function and the ``sort`` function on lists. Finally we use the ``reduce`` function to map a list with repeated digits to a list with unique digits, :: + load system io. function unique with (x,y) do @@ -121,6 +133,7 @@ Finally we use the ``reduce`` function to map a list with repeated digits to a l io @println digits. assert(digits == ["1","2","3"]). + Output:: [1,2,3] @@ -138,6 +151,7 @@ Challenge: Reverse a string We use the ``explode`` function to turn a string into a list of characters. Then, we reverse the list and turn it back into a string using the ``join`` function, :: + load system io. let str = "Hello, World!" @explode() @@ -146,6 +160,7 @@ We use the ``explode`` function to turn a string into a list of characters. Then io @println str. assert(str == "!dlroW ,olleH"). + Output:: !dlroW ,olleH @@ -157,12 +172,14 @@ Challenge: Removing blanks from a string > Remove leading, trailing, and double spaces from a given string. :: + load system io. let str = " Hello , World ! " @trim() @replace(" ",""). io @println str. assert(str == "Hello, World!"). + Output:: Hello, World! @@ -177,6 +194,7 @@ In this task, we will form the ``CamelCase`` variable for names from a given phr Names created in this style are built of several words, each of which starts with a capital letter. :: + load system io. function title with w do @@ -199,6 +217,7 @@ with a capital letter. io @println camel_str. assert(camel_str == "OnceUponATime"). + Output:: OnceUponATime @@ -209,6 +228,7 @@ Challenge: Incrementing filenames > Generate a list of filenames like file1.txt, file2.txt, etc. :: + load system io. let root = "file". @@ -218,6 +238,7 @@ Challenge: Incrementing filenames io @println (root+i+ext). end + Output:: file1.txt @@ -234,6 +255,7 @@ Challenge: Random passwords In our solution we take advantage of Asteroid's ``Pick`` object. The ``Pick`` object maintains a list of items that we can randomly select from using the ``pick`` member function. As input to the ``Pick`` object, we compute a bunch of lists of characters that are useful for password construction. The function ``achar`` converts a decimal ASCII code to a single character string. :: + load system io. load system type. load system util. @@ -257,6 +279,7 @@ In our solution we take advantage of Asteroid's ``Pick`` object. The ``Pick`` o assert (pwd == "e3zvshdbS43brt#") + Output:: e3zvshdbS43brt# @@ -277,6 +300,7 @@ to RNA happens according to the following table: We will solve this programming problem using Asteroid's first-class patterns. We could have solved this with just testing equality on DNA characters. However, using first-class patterns is more general and can be applied to problems with a more structured mapping relationship. :: + load system io. let dna2rna_table = @@ -304,6 +328,7 @@ We will solve this programming problem using Asteroid's first-class patterns. We assert(rna_seq == "UGGUAGUCAG"). + Output:: UGGUAGUCAG @@ -321,6 +346,7 @@ For example, if N is 4, then the letter e becomes a, f is transformed to b, etc. The alphabet is looped so that z becomes v, and letters a to d become w to z. :: + load system io. load system util. @@ -362,6 +388,7 @@ w to z. assert (decoded_msg == "hello, world!") + Output:: dahhk, sknhz! @@ -382,12 +409,14 @@ In program outputs, it is often required to print some number followed by a noun If there is only one file, then the phrase should be ``1 file found`` instead. :: + load system io. for n in 0 to 5 do io @println (n+" file"+("s " if n>1 or n==0 else " ")+"found"). end + Output:: 0 files found @@ -406,6 +435,7 @@ Challenge: The most frequent word In our solution we use a hash table to count the number of word occurences. :: + load system io. load system util. load system hash. @@ -464,6 +494,7 @@ In our solution we use a hash table to count the number of word occurences. assert (most_frequent_word == "sed"). + Output:: sed @@ -480,6 +511,7 @@ There are two loops (see also Task 17, The longest palindrome) over the first string (``stra``). These use the index method to search for the substring in the second string (``strb``). :: + load system io. let stra = "the quick brown fox jumps over the lazy dog". @@ -503,6 +535,7 @@ second string (``strb``). assert (common == " fox "). + Output:: The longest common substring is ' fox '. @@ -515,6 +548,7 @@ Challenge: Anagram test An anagram is a word, phrase, or name formed by rearranging the letters of another, such as ``cinema``, formed from ``iceman``. :: + load system io. let str1 = "cinema". @@ -534,6 +568,7 @@ An anagram is a word, phrase, or name formed by rearranging the letters of anoth assert (normalize(str1) == normalize(str2)). + Output:: Anagrams @@ -547,6 +582,7 @@ Challenge: Palindrome test A palindrome is a string that can be read from both ends: left to right or right to left. :: + load system io. let str = "Was it a rat I saw?". @@ -568,6 +604,7 @@ to left. assert (clean_str == clean_str @flip()). + Output:: Palindromic @@ -586,6 +623,7 @@ Now, extract the substring and do the check similar to the solution of Task without taking into account the non-letter characters, but saving the result as part of the original string. :: + load system io. let str = "Hello, World!". @@ -619,6 +657,7 @@ part of the original string. io @println longest_palindrome. + Output:: o, Wo @@ -631,6 +670,7 @@ Challenge: Finding duplicate texts We do this by finding and hashing N-grams after the appropriate preprocessing. We will use ``N=3``. :: + load system io. load system hash. @@ -748,6 +788,7 @@ We do this by finding and hashing N-grams after the appropriate preprocessing. io @println (n_gram+": "+cnt). end + Output:: lorem ipsum dolor: 2 @@ -773,17 +814,20 @@ Challenge: Pi > Print the value of pi. :: + load system io. load system math. -- definition of pi io @println (math @pi). + Output:: 3.141592653589793 Other constants are also available. :: + load system io. load system math. @@ -792,6 +836,7 @@ Other constants are also available. assert (math @tau == 2 * math @pi) + Output:: 2.718281828459045 @@ -805,6 +850,7 @@ Challenge: Factorial! By definition, the factorial of a positive integer number N is a product of all the integers numbering from 1 to N, including N. Our first solution is based on the direct implementation of the definition above using the list ``reduce`` function. :: + load system io. let n = 3. @@ -812,6 +858,7 @@ By definition, the factorial of a positive integer number N is a product of all io @println fact. assert (fact == 6). + Output:: 6 @@ -828,6 +875,7 @@ where :math:`x \in Int`. Here, each case specifies what value the function should return if the predicate applied to the input is true. The last case is of some interest because it states that the function is undefined for negative integers. :: + load system io. let POS_INT = pattern with (x:%integer) if x > 0. @@ -845,6 +893,7 @@ the predicate applied to the input is true. The last case is of some interest b io @println ("The factorial of 3 is: " + fact (3)). assert (fact(3) == 6). + Output:: The factorial of 3 is: 6 @@ -864,6 +913,7 @@ You can assign two values at a time (**Challenge: Swap two values**). You can us Here we give an iterative solutions. It is clear that there exists a trivial recursive solution by implementing the above formula. :: + load system io. let n = 10. -- compute the 10th Fib number @@ -876,6 +926,7 @@ Here we give an iterative solutions. It is clear that there exists a trivial re io @println f_1. assert (f_1 == 55) + Output:: 55 @@ -888,6 +939,7 @@ Challenge: Print squares Of course this is straightforward, with a ``for-loop`` over a list. Here we show another solution using the list ``map`` function. :: + load system io. let sq = [1 to 10] @map(lambda with x do return x*x). @@ -896,6 +948,7 @@ Of course this is straightforward, with a ``for-loop`` over a list. Here we sho assert (sq == [1,4,9,16,25,36,49,64,81,100]) + Output:: [1,4,9,16,25,36,49,64,81,100] @@ -908,6 +961,7 @@ Challenge: Powers of two Just as in the previous challenge, we skip the naive loop solution and give a solution using the ``map`` function. :: + load system io. load system math. @@ -917,6 +971,7 @@ Just as in the previous challenge, we skip the naive loop solution and give a so assert (p2 == [1,2,4,8,16,32,64,128,256,512]) + Output:: [1,2,4,8,16,32,64,128,256,512] @@ -929,6 +984,7 @@ Challenge: Odd and even numbers We start with printing the first ten odd numbers, :: + load system io. load system math. @@ -940,6 +996,7 @@ We start with printing the first ten odd numbers, io @println odd. assert(odd == [1,3,5,7,9]) + Output:: [1,3,5,7,9] @@ -947,6 +1004,7 @@ Output:: Now the even numbers, :: + load system io. load system math. @@ -958,6 +1016,7 @@ Now the even numbers, io @println even. assert(even == [2,4,6,8,10]) + Output: :: @@ -971,6 +1030,7 @@ Challenge: Compare numbers approximately Comparing non-integer numbers (which are represented as floating-point numbers) is often a task that requires approximate comparison. In Asteroid this can be accomplished with the ``isclose`` function availabel in the ``math`` module. :: + load system io. load system math. @@ -981,6 +1041,7 @@ Comparing non-integer numbers (which are represented as floating-point numbers) assert (math @isclose(2.0,2.00001,0.0001)). + Challenge: Prime numbers ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -988,6 +1049,7 @@ Challenge: Prime numbers Prime numbers are those that can be divided only by 1, and by themselves. :: + load system io. load system math. @@ -1010,6 +1072,7 @@ Prime numbers are those that can be divided only by 1, and by themselves. assert (isprime(17)). assert (not isprime(15)). + Output: :: @@ -1022,6 +1085,7 @@ Challenge: List of prime numbers > Print the list of the first ten prime numbers. :: + load system io. load system math. @@ -1047,6 +1111,7 @@ Challenge: List of prime numbers end end + Output: :: @@ -1069,6 +1134,7 @@ Challenge: Prime factors Prime factors are the prime numbers that divide the given integer number exactly. :: + load system io. load system math. @@ -1111,6 +1177,7 @@ Prime factors are the prime numbers that divide the given integer number exactly assert (factors == [3,5,11]) + Output: :: @@ -1124,6 +1191,7 @@ Challenge: Reducing a fraction 5/15 and 16/280 are examples of fractions that can be reduced. The final results of this task are 1/3 and 2/35. Generally, the algorithm of reducing a fraction requires searching for the greatest common divisor, and then dividing both numerator and denominator by that number. For our solution we use the function ``gcd`` available in the ``math`` module. :: + load system io. load system math. @@ -1141,6 +1209,7 @@ Challenge: Reducing a fraction -- show that original and reduced fraction are the same value assert (a/b == numerator/denominator). + Output: :: @@ -1155,6 +1224,7 @@ Challenge: Divide by zero Asteroid is an eager language, that is, expressions are evaluated as early as possible. We can trap division-by-zero errors using a try-catch block. :: + load system io. try @@ -1164,6 +1234,7 @@ Asteroid is an eager language, that is, expressions are evaluated as early as po end io @println "We are still alive...". + Output: :: @@ -1181,6 +1252,7 @@ Challenge: Generating random numbers Asteroid has two random number generation functions: ``random()`` generates a random real value in the interval $[0.0,1.0)$ and ``randint(a,b)`` that generates a random value in the interval $[a,b]$. The type of the random value generated depends on the type of the values a and b specifying the interval. :: + load system io. load system random. load system util. @@ -1199,6 +1271,7 @@ Asteroid has two random number generation functions: ``random()`` generates a ra io @println (randint(0.0,type @toreal(n))). io @println (randint(0,n)). + Output: :: @@ -1224,6 +1297,7 @@ This algorithm is a simple method of generating short sequences of four-digit ra To illustrate it with an example, let’s take the number 1234 as the seed. On step 2, it becomes 1522756; after step 3, 01522756. Finally, step 4 extracts the number 5227. :: + load system io. load system util. load system type. @@ -1242,6 +1316,7 @@ To illustrate it with an example, let’s take the number 1234 as the seed. On s assert (rval == 5227) + Output: :: @@ -1257,6 +1332,7 @@ The quality of the built-in generator of random numbers fully depends on the alg In our solution, we generate 10 random integers between 0 and 9. We then count how many times each of the integers have been generated. If it is a decent random number generator, all numbers should have been generated roughly an equal number of times. :: + load system io. load system random. @@ -1269,6 +1345,7 @@ In our solution, we generate 10 random integers between 0 and 9. We then count h io @println hist. + Output: :: @@ -1286,6 +1363,7 @@ Challenge: Distance between two points There are two points on a surface, each with their own coordinates, x and y. The task is to find the distance between these two points. A straightforward solution would be to use the Pythagorean theorem: :: + load system io. load system math. @@ -1296,6 +1374,7 @@ A straightforward solution would be to use the Pythagorean theorem: assert (d == 2.23606797749979) + Output: :: @@ -1310,6 +1389,7 @@ Another approach is using the math identity, where ``.`` represents the dot product. In our case ``a`` would be the distance vector between points ``x`` and ``y``, :: + load system io. load system math. load system vector. @@ -1322,6 +1402,7 @@ where ``.`` represents the dot product. In our case ``a`` would be the distance assert (d == 2.23606797749979) + Output: :: @@ -1343,6 +1424,7 @@ Standard deviation is a statistical term that shows how compact data distributio where :math:`n` is the number of elements in the array :math:`x`; :math:`\bar{x}` is its average value (**Challenge: Average on an array**). :: + load system io. load system math. @@ -1357,6 +1439,7 @@ where :math:`n` is the number of elements in the array :math:`x`; :math:`\bar{x} assert (sigma == 420.96248961952256) + Output: :: @@ -1379,6 +1462,7 @@ The conversion formulae between the Cartesian and polar systems, which is valid These expressions can be implemented as-is in the code: :: + load system io. load system math. @@ -1409,6 +1493,7 @@ These expressions can be implemented as-is in the code: -- we started with assert (math @isclose(1,x,0.0001) and math @isclose(2,y,0.0001)). + Output: :: @@ -1419,6 +1504,7 @@ For the **negative** ``x`` and ``y``, the Cartesian-to-polar conversion is a bit or smaller than ``pi``. When ``x`` is zero, it is either ``-pi/2`` or ``pi/2``. All these variants can be implemented by using ``with`` clauses and conditional matching, as demonstrated below: :: + load system io. load system math. load system util. @@ -1467,6 +1553,7 @@ All these variants can be implemented by using ``with`` clauses and conditional -- we started with assert (math @isclose(-3,x,0.0001) and math @isclose(5,y,0.0001)). + Output: :: @@ -1482,6 +1569,7 @@ The Monte Carlo method is a statistical method of calculating data whose formula To calculate the area of a circle with a radius of 1, pairs of random numbers between −1 and 1 are generated. These pairs represent the points in the square in the center of coordinates with sides of length 2. The area of the square is thus 4. If the distance between the random point and the center of the square is less than 1, then this point is located inside the circle of that radius. Counting the number of points that landed inside the circle and the number of points outside the circle gives the approximate value of the area of the circle, as soon as the area of the square is known. Here is the program. :: + load system io. load system math. load system random. @@ -1505,6 +1593,7 @@ To calculate the area of a circle with a radius of 1, pairs of random numbers be assert (area == 3.1392). + Output: :: @@ -1519,6 +1608,7 @@ Challenge: Guess the number First, a random number needs to be generated. Then the program must ask for the initial guess and enter the loop, which compares the guess with the generated number. :: + load system io. load system random. load system util. @@ -1538,6 +1628,7 @@ ask for the initial guess and enter the loop, which compares the guess with the end io @println "Yes, this is it!". + Challenge: Binary to integer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1545,6 +1636,7 @@ Challenge: Binary to integer In Asteroid this is straightforward using the built-in ``tointeger`` function, passing it a string representation of the binary number and the base. :: + load system io. load system type. @@ -1554,6 +1646,7 @@ In Asteroid this is straightforward using the built-in ``tointeger`` function, p assert (int == 45). + Output: :: @@ -1567,6 +1660,7 @@ Challenge: Integer as binary, octal, and hex In Asteroid this is easily done with the ``tobase`` function. :: + load system io. load system type. @@ -1584,6 +1678,7 @@ In Asteroid this is easily done with the ``tobase`` function. assert (tointeger(tobase(val,8),8) == val). assert (tointeger(tobase(val,16),16) == val). + Output: :: @@ -1599,6 +1694,7 @@ Challenge: Sum of digits Pretty straightforward using string and list manipulation. :: + load system io. load system type. @@ -1612,6 +1708,7 @@ Pretty straightforward using string and list manipulation. assert (s == 49). + Output: :: @@ -1625,6 +1722,7 @@ Challenge: Bit counter If we remove all the zeros from a binary number, then we are left with only ``1`` characters which we can then count. :: + load system io. let bits = "1010101" @replace("0","") @@ -1633,6 +1731,7 @@ If we remove all the zeros from a binary number, then we are left with only ``1` assert (bits == 4). + Output: :: @@ -1646,6 +1745,7 @@ Challenge: Compose the largest number The easiest way to achieve that is to treat the numbers as strings, sort them alphabetically in descending order, concatenate the pieces to a single string, and get the resulting integer. :: + load system io. load system type. @@ -1654,6 +1754,7 @@ The easiest way to achieve that is to treat the numbers as strings, sort them al assert (a == 8675451). + Output: :: @@ -1671,6 +1772,7 @@ Let’s use the algorithm that keeps the table of pre-calculated sequences of Ro In the program below, there are four such sequences: for thousands, hundreds, tens, and ones. The program iterates over the digits of the number in the decimal representation and chooses one of the values from the array of lists stored in the ``roman_hash`` table. :: + load system io. load system math. load system util. @@ -1698,6 +1800,7 @@ In the program below, there are four such sequences: for thousands, hundreds, te assert (roman == "MMXVIII") + Output: :: @@ -1724,6 +1827,7 @@ For the larger numbers (21 to 99), there are two cases. If the number is dividab The zero name appears only in the case when the given number is zero. :: + load system io. load system math. @@ -1758,6 +1862,7 @@ The zero name appears only in the case when the given number is zero. io @println (spell_number 987654). io @println (spell_number 1001). + Output: :: @@ -1782,6 +1887,7 @@ In Asteroid, there is no need to use temporary variables to swap the values of t Consider the complete program: :: + load system io. let (a,b) = (10,20). @@ -1790,6 +1896,7 @@ Consider the complete program: assert ((a,b) is (20,10)). + Output: :: @@ -1803,6 +1910,7 @@ This program prints the swapped values: This approach also works with elements of an array: :: + load system io. let a = [3,5,7,4]. @@ -1811,6 +1919,7 @@ This approach also works with elements of an array: assert (a is [3,5,4,7]). + Output: :: @@ -1822,6 +1931,7 @@ Challenge: Reverse a list > Print the given list in reverse order. :: + load system io. let a = [10, 20, 30, 40, 50]. @@ -1829,6 +1939,7 @@ Challenge: Reverse a list assert(a == [50,40,30,20,10]). + Output: :: @@ -1842,6 +1953,7 @@ Challenge: Rotate a list Asteroid does not have a built-in ``rotate`` function. However, such a function is easily constructed through slicing lists (see ``vix`` below). :: + load system io. load system math. @@ -1861,6 +1973,7 @@ Asteroid does not have a built-in ``rotate`` function. However, such a function assert(b == [7,9,11,13,15,1,3,5] and c == [11,13,15,1,3,5,7,9]). + Output: :: @@ -1876,6 +1989,7 @@ Challenge: Randomize an array This is easily accomplished with the built-in ``shuffle``. :: + load system io. load system random. @@ -1885,6 +1999,7 @@ This is easily accomplished with the built-in ``shuffle``. assert(b == [20,6,15,5,10,14,16,19,7,13,18,11,2,12,3,17,8,9,1,4]). + Output: :: @@ -1898,6 +2013,7 @@ Challenge: Incrementing array elements For this we use Asteroid's ``vector`` module, which can handle incrementing a vector with a scalar. :: + load system io. load system vector. @@ -1907,6 +2023,7 @@ For this we use Asteroid's ``vector`` module, which can handle incrementing a ve assert(b == [2,3,4,5,6,7,8,9,10,11]). + Output: :: @@ -1920,6 +2037,7 @@ Challenge: Adding up two arrays Again, here we take advantage of Asteroid's ``vector`` module. Note that the two vectors have to be of the same length in order to add them together. :: + load system io. load system vector. @@ -1930,6 +2048,7 @@ Again, here we take advantage of Asteroid's ``vector`` module. Note that the tw assert(c == [40,42,44,46,48,50,52,54,56,58,60]). + Output: :: @@ -1938,6 +2057,7 @@ Output: The vector module defines a function called ``op`` that allows you to combine two vectors using any arbitrary binary function. Rewriting the above program using ``op``, :: + load system io. load system vector. @@ -1948,6 +2068,7 @@ The vector module defines a function called ``op`` that allows you to combine tw assert(c == [40,42,44,46,48,50,52,54,56,58,60]). + Output: :: @@ -1956,6 +2077,7 @@ Output: As we said above, any arbitrary binary function. Consider the relational operator ``<`` expressed as a lambda function, :: + load system io. load system vector. load system random. @@ -1969,6 +2091,7 @@ As we said above, any arbitrary binary function. Consider the relational operato assert(c == [false,true,false,false,false,true,false,false,true,true]). + Output: :: @@ -1983,6 +2106,7 @@ appear in the second one. Here we use Asteroid's ``set`` module. :: + load system io. load system set. @@ -1993,6 +2117,7 @@ Here we use Asteroid's ``set`` module. assert(c @sort() == [1,2,3,4]). + Output: :: @@ -2007,6 +2132,7 @@ Challenge: Sum of the elements of an array > Find the sum of the elements of an array of integers. :: + load system io. let a = [4, 6, 8, 1, 0, 58, 1, 34, 7, 4, 2]. @@ -2015,6 +2141,7 @@ Challenge: Sum of the elements of an array assert (s == 125). + Output: :: @@ -2023,6 +2150,7 @@ Output: If summing up elements that are greater than 10, :: + load system io. let a = [4, 6, 8, 1, 0, 58, 1, 34, 7, 4, 2]. @@ -2032,6 +2160,7 @@ If summing up elements that are greater than 10, assert (s == 92). + Output: :: @@ -2043,6 +2172,7 @@ Challenge: Average of an array > Find the average value of the given array of numbers. :: + load system io. let a = [7, 11, 34, 50, 200]. @@ -2051,6 +2181,7 @@ Challenge: Average of an array assert (avg == 60). + Output: :: @@ -2062,12 +2193,14 @@ Challenge: Is an element in a list? > Tell if the given value is in the list. :: + load system io. let array = [10, 14, 0, 15, 17, 20, 30, 35]. let x = 17. io @println ((x+" is in the list") if array @member(x) else (x+" is not in the list")). + Output: :: @@ -2076,6 +2209,7 @@ Output: We can also use a reduction function to solve this, :: + load system io. let array = [10, 14, 0, 15, 17, 20, 30, 35]. @@ -2087,6 +2221,7 @@ We can also use a reduction function to solve this, io @println (x+" is not in the list"). end + Output: :: @@ -2100,6 +2235,7 @@ Challenge: First odd number The easiest way to do this is with a reduction, :: + load system io. load system math. load system util. @@ -2111,6 +2247,7 @@ The easiest way to do this is with a reduction, let odd = array @reduce(lambda with (acc,i) do return i if type @isnone(acc) and mod(i,2) else acc,none). io @println odd. + Output: :: @@ -2122,6 +2259,7 @@ Challenge: Take every second element > Form a new array by picking every second element from the original array. :: + load system io. load system math. @@ -2130,6 +2268,7 @@ Challenge: Take every second element assert (array == [21,23,25,27,29]). + Output: :: @@ -2138,6 +2277,7 @@ Output: We can use an index vector to accomplish the same thing, :: + load system io. load system math. @@ -2147,6 +2287,7 @@ We can use an index vector to accomplish the same thing, assert (array == [21,23,25,27,29]). + Output: :: @@ -2158,6 +2299,7 @@ Challenge: Number of occurrences in an array > Count how many times a particular element appears in the array. :: + load system io. load system math. @@ -2177,6 +2319,7 @@ Challenge: Number of occurrences in an array assert (cnt == 2). + Output: :: @@ -2190,6 +2333,7 @@ Challenge: Finding unique elements Converting a list to a set will remove all duplicate elements in the list. :: + load system io. load system set. @@ -2203,6 +2347,7 @@ Converting a list to a set will remove all duplicate elements in the list. assert (a == [2,3,4,5,6,7,10]) + Output: :: @@ -2214,6 +2359,7 @@ Challenge: Minimum and maximum > Find the minimum and the maximum numbers in the given list of integers. :: + load system io. function max with lst:%list do @@ -2234,6 +2380,7 @@ Challenge: Minimum and maximum assert (a == 15 and b == 2). + Output: :: @@ -2246,6 +2393,7 @@ Challenge: Increasing sequences > Check if the given array contains increasing (or decreasing) numbers. :: + load system io. load system type. @@ -2256,6 +2404,7 @@ Challenge: Increasing sequences assert (b). + Output: :: @@ -2296,6 +2445,7 @@ with its transpose, The procedure: :: + load system io. function transpose with m do @@ -2356,6 +2506,7 @@ The procedure: assert(mt == [[1,3,5],[2,4,6]]). + Output: :: @@ -2389,6 +2540,7 @@ Challenge: Sort hashes by parameter This task is commonly performed to sort items where the sortable parameter is one of the values in the hash. For example, sorting a list of people by age. :: + load system io. load system hash. load system sort. @@ -2420,6 +2572,7 @@ This task is commonly performed to sort items where the sortable parameter is on assert (sort @sort(pairs,lst) == [("Pete",20),("Joe",23),("Billie",40),("Brandi",43)]) + Output: :: @@ -2433,6 +2586,7 @@ Challenge: Count hash values For example, a hash is a collection mapping a car’s license plate to the colour of the car or a passport number to the name of the street where the person lives. In the first example, the task is to count how many cars of each colour there are. In the second example, we have to say how many people live on each street. But let’s simply count the colours of fruit. :: + load system io. load system hash. load system sort. @@ -2467,6 +2621,7 @@ For example, a hash is a collection mapping a car’s license plate to the colou io @println (sort @sort(pairs,color_lst)). + Output: :: @@ -2480,6 +2635,7 @@ Challenge: Product table We will do this with an outer loop and a ``map`` function. :: + load system io. load system type. @@ -2493,6 +2649,7 @@ We will do this with an outer loop and a ``map`` function. io @println ([1 to 10] @map(lambda with x do return format(i*x)) @join(" ")). end + Output: :: @@ -2555,6 +2712,7 @@ We then add them together, The only thing that is left to do is to iterate appropiately and format the output. :: + load system io. load system vector. load system util. @@ -2576,6 +2734,7 @@ The only thing that is left to do is to iterate appropiately and format the outp io @println (r @map(lambda with v do return type @tostring v) @join(" ")). end + Output: :: @@ -2590,3 +2749,4 @@ Output: The program prints the first seven rows of the Pascal triangle. The rows are not centred, and are aligned to the left side. As an extra exercise, modify the program so that it prints the triangle as it is shown at the beginning of this task. For example, you can first generate rows and keep them in a separate array and then, knowing the length of the longest string, add some spaces in front of the rows before printing them. + diff --git a/docs/Reference Guide.rst b/docs/Reference Guide.rst index 999084d3..f44d4a54 100644 --- a/docs/Reference Guide.rst +++ b/docs/Reference Guide.rst @@ -2,19 +2,10 @@ - - - - - - - - - - .. *** DO NOT EDIT; MACHINE GENERATED *** + .. highlight:: none Asteroid Reference Guide @@ -32,6 +23,7 @@ where ``*`` means zero or more occurrences of the syntactic unit are written in quotes. :: + //////////////////////////////////////////////////////////////////////////////////////// // statements @@ -135,7 +127,7 @@ are written in quotes. //////////////////////////////////////////////////////////////////////////////////////// // primary expressions/patterns - + primary : INTEGER | REAL @@ -144,7 +136,7 @@ are written in quotes. | FALSE | NONE | ID - | '*' ID + | '*' call_or_index | NOT call_or_index | MINUS call_or_index | PLUS call_or_index @@ -167,6 +159,7 @@ are written in quotes. : LAMBDA body_defs + Builtin Functions ----------------- @@ -389,12 +382,14 @@ The `math 0. let NEG_INT = pattern (x:%integer) if x < 0. @@ -59,6 +63,7 @@ in order to ensure that the domain of the function is not violated, throw Error("undefined for "+n). end + As you can see, the program first creates patterns and stores them in the variables ``POS_INT`` and ``NEG_INT`` and it uses those patterns later in the code by dereferencing those variables with the ``*`` operator. First-class patterns have @@ -87,8 +92,10 @@ Asteroid arranges these data types in a **type hierarchy**, Type hierarchies facilitate automatic type promotion. Here is an example where automatic type promotion is used to put together a string from different data types, :: + let x:%string = "value: " + 1. + Here we associate the string ``"value: 1"`` with the variable ``x`` by first promoting the integer value ``1`` to the string ``"1"`` using the fact that ``integer`` < ``string`` according to our type hierarchy and then interpreting the ``+`` operator as a string concatenation operator. Asteroid supports two more data types: @@ -99,13 +106,17 @@ Asteroid supports two more data types: These are **structured data types** in that they can contain entities of other data types. Both of these data types have the probably familiar constructors which are possibly empty squences of comma separated values enclosed by square brackets for lists, e.g. ``[1,2,3]``, and enclosed by parentheses for tuples, e.g. ``(x,y)``. For tuples we have the caveat that the 1-tuple is represented by a value followed by a comma to distinguish it from parenthesized expressions, e.g.``(3,)``. Here are some examples, :: + let a = [1,2,3]. -- this is a list let c = (1,2,3). -- this is a tuple + As we said above, in order to distinguish it from a parenthesized value the single element in a 1-tuple has to be followed by a comma, like so, :: + let one_tuple = (1,). -- this is a 1-tuple + Lists and tuples themselves are also embedded in type hierarchies, although very simple ones: * ``list`` < ``string`` @@ -113,16 +124,20 @@ Lists and tuples themselves are also embedded in type hierarchies, although very That is, any list or tuple can be viewed as a string. This is very convenient for printing lists and tuples, :: + load system io. io @println ("this is my list: " + [1,2,3]). + Finally, Asteroid supports one more type, namely the ``none`` type. The ``none`` type has only one member: A constant named conveniently ``none``. The null-tuple belongs to this type (rather than the tuple type discussed earlier) and therefore the constant ``()`` can often be used as a convenient short hand for the constant ``none``. That is, the following ``let`` statements will succeed, :: + let none = (). let () = none. + meaning that the constants ``()`` and ``none`` are equivalent and pattern-match each other. The ``none`` data type itself does not belong to any type hierarchy. @@ -136,23 +151,27 @@ Lists In Asteroid the ``list`` is a fundamental, built-in data structure. A trait it shares with programming languages such as Lisp, Python, ML, and Prolog. Below is the list reversal example from above as an executable Asteroid program. So go ahead and experiment! :: + load system io. -- load the io module so we can print let a = [1,2,3]. -- construct list a let b = a @[2,1,0]. -- reverse list a io @println b. + The output is: ``[3,2,1]``. In Asteroid lists are considered objects with member functions that can manipulate the list object, e.g. ``[1,2,3] @ reverse()``. We could rewrite the above example as, :: + load system io. let a = [1,2,3]. let b = a @reverse(). io @println b. + For a full list of available member functions for Asteroid lists please see the reference guide. As we have seen, the ``@`` operator allows you to access either individual elements, slices, or member functions of a list. @@ -161,6 +180,7 @@ Besides using the default constructor for lists which consists of the square brackets enclosing a list of elements we can use **list comprehensions** to construct lists. In Asteroid a list comprehension consist of a range specifier together with a stride specifier allowying you to generate integer values within that range, :: + load system io. -- build a list of odd values @@ -172,6 +192,7 @@ a stride specifier allowying you to generate integer values within that range, let b = a @slice. io @println ("reversed list: " + b). + The output is, :: @@ -182,6 +203,7 @@ Asteroid's simple list comprehensions in conjunction with the ``map`` function f construct virtually any kind of list. For example, the following program constructs a list of alternating 1 and -1, :: + load system io. load system math. @@ -190,6 +212,7 @@ a list of alternating 1 and -1, io @println a. + where the output is, :: @@ -197,6 +220,7 @@ where the output is, Higher dimensional arrays can easily be simulated with lists of lists, :: + load system io. -- build a 2-D array @@ -208,6 +232,7 @@ Higher dimensional arrays can easily be simulated with lists of lists, let b @1 @1 = 0. io @println b. + The output is: ``[[1,2,3],[4,0,6],[7,8,9]]`` **NOTE**: At this point slicing is not supported on the left side of a ``let`` statement. @@ -219,13 +244,16 @@ As we saw earlier, the ``tuple`` is another fundamental, built-in data structure Below is an example of a tuple declaration and access. :: + load system io. -- load the io module so we can print let a = (1,2,3). -- construct tuple a let b = a @1. -- access the second element in tuple a io @println b. -- print the element to the console + Like ``lists``, ``tuples`` may also be nested, :: + load system io. -- build a 2-D array let b = (("a","b","c"), @@ -234,8 +262,10 @@ Like ``lists``, ``tuples`` may also be nested, -- Access an element in the nested structure. io @println(b @1 @1). + Unlike lists, tuples are immutable. This means that their contents cannot be changed once they have been declared. Should we want to change the contents of an already declared tuple, we would need to abandon the original and declare a new ``tuple``. The following code block demonstrates this, :: + load system io. -- build a tuple let b = ("a","b","c"). @@ -246,6 +276,7 @@ Unlike lists, tuples are immutable. This means that their contents cannot be cha io @println(kind+": "+s). end. + Which will print out the following message: :: @@ -264,6 +295,7 @@ a *default constructor* for a structure. That constructor copies the arguments data member fields of the structure in the order that the data members appear in the structure definition and as they appear in the parameter list of the constructor. Here is a simple example, :: + load system io. structure Person with @@ -284,6 +316,7 @@ structure definition and as they appear in the parameter list of the constructor let Person(name,age,gender) = people @1. -- pattern match against the structure io @println (name + " is " + age + " years old and is " + gender + "."). + The output is, :: @@ -308,6 +341,7 @@ the ``=`` operator are completely legal and highlight the fact that ``let`` stat Here is an example where we do some computations on the right side of a ``let`` statement and then match the result against a pattern on the left, :: + load system io. -- note 1+1 evaluates to 2 and is then matched @@ -316,13 +350,16 @@ Here is an example where we do some computations on the right side of a ``let`` let [x,2,y] = [1+0,1+1,1+2]. io @println (x,y). + The output is: ``(1,3)`` Asteroid supports special patterns called **type patterns** that match any value of a given type. For instance, the ``%integer`` pattern matches any integer value. Here is a simple example, :: + let %integer = 1. + This ``let`` statement succeeds because the value ``1`` can be pattern matched against the type pattern ``%integer`` @@ -331,11 +368,13 @@ supports something called a **named pattern** were a (sub)pattern on the left si of a ``let`` statement (or any pattern as it appears in Asteroid) can be given a name and that name will be instantiated with a term during pattern matching. For example, :: + load system io. let t:(1,2) = (1,2). -- using a named pattern on lhs io @println t. + Here, the construct ``t:(1,2)`` is called a named pattern and the variable ``t`` will be unified with the term ``(1,2)``, or more generally, the variable will be unified with term that matches the pattern on the right of the colon. The program will print, :: @@ -346,6 +385,7 @@ We can combine type patterns and named patterns to give us something that looks like a variable declaration in other languages. In Asteroid, though, it is still just all about pattern matching. Consider, :: + load system io. load system math. load system type. @@ -353,6 +393,7 @@ about pattern matching. Consider, let x:%real = math @pi. io @println (type @tostring(x,type @stringformat(4,2))). + The left side of the ``let`` statement is a named type pattern that matches any real value, and if that match is successful then the value is bound to the variable ``x``. Note that even though this looks like a declaration, it is in fact a pattern matching @@ -365,12 +406,14 @@ Control structure implementation in Asteroid is along the lines of any of the mo As we said, in terms of flow of control statements there are really not a lot of surprises. This is because Asteroid supports loops and conditionals in a very similar way to many of the other modern programming languages in use today. For example, here is a short program with a ``for`` loop that prints out the first six even positive integers, :: + load system io. for i in 0 to 10 stride 2 do io @println i. end + The output is, :: @@ -383,6 +426,7 @@ The output is, Here is another example that iterates over lists, :: + load system io. load system util @@ -390,6 +434,7 @@ Here is another example that iterates over lists, io @println ("the "+ix+" bird is a "+bird). end + The output is, :: @@ -402,6 +447,7 @@ iterate pattern matching on each of the pairs on the list with the pattern ``(ix The following is a short program that demonstrates an ``if`` statement, :: + load system io. load system type. @@ -418,6 +464,7 @@ The following is a short program that demonstrates an ``if`` statement, io @println("More"). end + Even though Asteroid's flow of control statements look so familiar, they support pattern matching to a degree not found in other programming languages and which we will take a look at below. Functions @@ -431,6 +478,7 @@ instantiating the formal arguments. Let's start with something simple. Here is a function definition for ``revdouble`` that reverses a list of integers then doubles each value before returning the result, :: + load system io. function revdouble @@ -440,6 +488,7 @@ then doubles each value before returning the result, io @println (revdouble [1,2,3]). + The output is ``[6,4,2]``. Notice how we used type patterns to make sure that this function is only applied to lists of integers. @@ -447,6 +496,7 @@ In order to demonstrate functional programming like multi-dispatch, the followin Asteroid. Each ``with`` clause introduces a new function body with its corresponding pattern, :: + load system io. function qsort @@ -472,6 +522,7 @@ corresponding pattern, -- print the sorted list io @println (qsort [3,2,1,0]) + The output is as expected, :: @@ -487,10 +538,12 @@ As you have seen in a couple of occasions already in the document, Asteroid also functions except that you declare them on-the-fly and they are declared without a name. Here is an example using a ``lambda`` function, :: + load system io. io @println ((lambda with n do return n+1) 1). + The output is ``2``. Here, the lambda function is a function that takes a value and increments it by one. We then apply the value ``1`` to the function and the print function prints out the value ``2``. @@ -506,6 +559,7 @@ Pattern Matching in Expressions: The ``is`` Predicate Consider the following example of this predicate among some patterns, :: + load system io. let p = (1,2). @@ -518,6 +572,7 @@ Consider the following example of this predicate among some patterns, io @println "it's something else". end + Here we use patterns to determine if ``p`` is a triple, a pair, or something else. Pattern matching is embedded in the expressions of the ``if`` statement. The output of this program is, :: @@ -540,6 +595,7 @@ We can also employ pattern matching in loops. In the following program we use the ``is`` predicate to test whether the list is empty or not while looping, :: + load system io. let list = [1,2,3]. @@ -550,6 +606,7 @@ while looping, let list = tail. until list is []. + The output is, :: @@ -573,6 +630,7 @@ Here is an example that uses pattern matching on function arguments using custom Here ``x`` and ``y`` are variables, ``0`` represents the natural number with value zero, and ``s`` is the successor function. In Peano arithmetic any natural number can be represented by the appropriate number of applications of the successor function to the natural number ``0``. Here is the program where we replaced the ``+`` operator with the ``add`` symbol, :: + -- implements Peano addition on terms load system io. @@ -597,6 +655,7 @@ Here ``x`` and ``y`` are variables, ``0`` represents the natural number with val -- add 2 3 io @println (reduce(add(s(s(0)),s(s(s(0)))))). + Our program defines the structure ``s`` to represent the successor function and the structure ``add`` to represent Peano addition. Next, it defines a function that uses pattern matching to identify the left sides of the two axioms. If either pattern matches the input to the ``reduce`` function, it will activate the corresponding function body and rewrite the term recursively in an appropriate manner. We have one additional pattern which matches if neither one of the Peano axiom patterns matches and terminates the recursion. Finally, on the last line, we use our ``reduce`` function to compute the Peano term for the addition of 2 + 3. As expected, the output of this program is, :: @@ -613,6 +672,7 @@ input values to function bodies. Consider the following definition of the ``factorial`` function where we use conditional pattern matching to control the kind of values that are being passed to a particular function body, :: + load system io. function factorial @@ -626,6 +686,7 @@ the kind of values that are being passed to a particular function body, io @println ("The factorial of 3 is: " + factorial 3). + Here we see that first, we make sure that we are being passed integers and second, that the integers are positive using the appropriate conditions on the input values. If we are being passed a negative integer, then we throw an error. @@ -639,6 +700,7 @@ example. This combines structural matching with regular expression matching in ``for`` loops that selects certain items from a list. Suppose we want to print out the names of persons that contain a lower case 'p', :: + load system io. structure Person with @@ -658,6 +720,7 @@ that selects certain items from a list. Suppose we want to print out the names o io @println name. end + Here we pattern match the ``Person`` object in the ``for`` loop and then use a regular expression to see if the name of that person matches our requirement that it contains a lower case 'p'. We can tag the pattern with a variable name, a named pattern, so that we can print out the name if the regular expression matches. The output is ``Sophie``. Pattern Matching in ``try-catch`` Statements @@ -665,6 +728,7 @@ Pattern Matching in ``try-catch`` Statements Exception handling in Asteroid is very similar to exception handling in many of the other modern programming languages available today. The example below shows an Asteroid program that throws one of two exceptions depending on the randomly generated value ``i``, :: + load system io. load system random. load system type. @@ -690,6 +754,7 @@ Exception handling in Asteroid is very similar to exception handling in many of io @println("you loose with "+type @tostring(v,type @stringformat(4,2))). end + The ``Head`` and ``Tail`` exceptions are handled by their corresponding ``catch`` statements, respectively. In both cases the exception object is unpacked using pattern matching and the unpacked value is used in the appropriate message printed to the screen. It is worth noting that even though Asteroid has builtin exception objects such as ``Error``, @@ -720,6 +785,7 @@ The right side of the ``let`` statement invokes the default constructor for the structure in order to create an instance stored in the variable ``scarlett``. We can access members of the instance, :: + load system io. structure Person with @@ -732,12 +798,14 @@ can access members of the instance, -- access the name field of the structure instance io @println (scarlett @name). + Asteroid allows you to attach functions to structures. In member functions the object identity of the instance is available through the ``this`` keyword. For example, we can extend our ``Person`` structure with the ``hello`` function that uses the ``name`` field of the instance, :: + load system io. structure Person with @@ -754,6 +822,7 @@ of the instance, -- call the member function scarlett @hello(). + This program will print out, :: @@ -767,6 +836,7 @@ The only thing you need to keep in mind is that you **cannot** pattern match on function field. From a pattern matching perspective, a structure consists only of data fields. So even if we declare a structure like this, :: + load system io. structure Person with @@ -784,6 +854,7 @@ data fields. So even if we declare a structure like this, let Person(name,age,_) = Person("Scarlett",28,"F"). io @println (name+" is "+age+" years old"). + where the function ``hello`` is defined in the middle of the data fields, pattern matching simply ignores the function definition and pattern matches only on the data fields. The output of the program is, @@ -799,6 +870,7 @@ dog instance by calling the ``add_trick`` function. Rather than using the defau constructor, we define a constructor for our instances with the ``__init__`` function. Here is the program listing for the example in Asteroid, :: + load system io. load system type. @@ -833,6 +905,7 @@ Here is the program listing for the example in Asteroid, io @println (name+" knows how to fetch"). end + After declaring the structure we instantiate two dogs, Fido and Buddy, and add tricks to their respective trick repertoires. The last couple of lines of the program consist of a ``for`` loop over a list of our dogs. @@ -932,6 +1005,7 @@ in many spots in a program. If patterns are not first-class citizens the develo will have to retype the same patterns over and over again in the various different spots where the patterns occurs. Consider the following program snippet, :: + function fact with 0 do return 1 @@ -950,10 +1024,12 @@ spots where the patterns occurs. Consider the following program snippet, return -1. end + In order to write these two functions we had to repeat the almost identical pattern four times. First-class patterns allow us to write the same two functions in a much more elegant way, :: + let POS_INT = pattern (x:%integer) if x > 0. let NEG_INT = pattern (x:%integer) if x < 0. @@ -975,6 +1051,7 @@ much more elegant way, return -1. end + The relevant patterns are now stored in the variables ``POS_INT`` and ``NEG_INT`` which are then used in the function definitions. @@ -992,6 +1069,7 @@ In order to use a pattern as a constructor we apply the ``eval`` function to it turns the pattern into a value from Asteroid's point of view which can then be used in computations. For example, :: + load system io. let P = pattern ([a],[b]). let a = 1. @@ -999,6 +1077,7 @@ in computations. For example, let v = eval P. -- use pattern to construct a value io @println v. + The output of the program is, :: @@ -1009,6 +1088,7 @@ the variables ``a`` and ``b``, and the first-class pattern ``P``. Of course, first-class patterns can be used to destructure the constructed value, :: + load system io. let P = pattern ([a],[b]). let v = ([1],[2]). @@ -1016,6 +1096,7 @@ to destructure the constructed value, io @println a. io @println b. + As expected, the output is, :: @@ -1031,6 +1112,7 @@ uses the first-class pattern ``FP`` to both construct an object network represen a family and, since it is a pattern, can also be used to destructure a family object network. Here is the program listing, :: + load system io. ----------------------------- @@ -1104,6 +1186,7 @@ network. Here is the program listing, io @println (destructure_family (FP,f1)). io @println (destructure_family (FP,f2)). + The function ``construct_family`` constructs a family evaluating the pattern using the ``eval`` function. The formal parameters of the function provide values for the free variables in the pattern. Since we are dealing with first-class @@ -1149,6 +1232,7 @@ into the current environment. The most common use of constraint patterns is the prevention of non-linear patterns in functions. Consider the following program, :: + load system io. let POS_INT = pattern %[v if (v is %integer) and v > 0]%. @@ -1180,6 +1264,7 @@ In order to demonstrate this type of multi-dispatch, we show the example program `multi-dispatch Wikipedia page `_ written in Asteroid, :: + load system io. load system type. @@ -1227,6 +1312,7 @@ written in Asteroid, io @println (collide(Asteroid(10), Spaceship(10))). io @println (collide(Spaceship(101), Spaceship(10))). + Each ``with`` clause in the function ``collide_with`` introduces a new function body with its corresponding pattern. The function bodies in this case are simple ``return`` statements @@ -1271,6 +1357,7 @@ The ``kind`` field will be populated by Asteroid with one of the following strin In addition to the ``kind`` field, the ``value`` field holds a string with some further details on the exception. Specific exceptions can be caught by pattern matching on the ``kind`` field of the ``Exception`` object. For example, :: + load system io. try @@ -1279,6 +1366,7 @@ example, io @println s. end + The output is, :: @@ -1287,6 +1375,7 @@ The output is, Asteroid also provides a predefined ``Error`` object for user level exceptions, :: + load system io. try @@ -1295,9 +1384,11 @@ Asteroid also provides a predefined ``Error`` object for user level exceptions, io @println s. end + Of course the user can also use the ``Exception`` object for their own exceptions by defining a ``kind`` that does not interfere with the predefined ``kind`` strings above, :: + load system io. try @@ -1306,6 +1397,7 @@ by defining a ``kind`` that does not interfere with the predefined ``kind`` stri io @println s. end + The output here is, :: @@ -1314,6 +1406,7 @@ The output here is, In addition to the Asteroid defined exceptions, the user is allowed to construct user level exceptions with any kind of object including tuples and lists. Here is an example that constructs a tuple as an exception object, :: + load system io. try @@ -1322,11 +1415,13 @@ the user is allowed to construct user level exceptions with any kind of object i io @println v. end + The output of this program is ``42``. Now, if you don't care what kind of exception you catch, you need to use a ``wildcard`` or a variable because exception handlers are activated via pattern matching on the exception object itself. Here is an example using a ``wildcard``, :: + load system io. try @@ -1335,8 +1430,10 @@ exception object itself. Here is an example using a ``wildcard``, io @println "something happened". end + Here is an example using a variable, :: + load system io. load system type. @@ -1346,6 +1443,7 @@ Here is an example using a variable, io @println ("something happened: "+type @tostring(e)). end + In this last example we simply convert the caught exception object into a string and print it, :: @@ -1358,12 +1456,14 @@ Basic Asteroid I/O I/O functions are defined as member functions of the ``io`` module. The ``println`` function prints its argument in a readable form to the terminal. Recall that the ``+`` operator also implements string concatenation. This allows us to construct nicely formatted output strings, :: + load system io. let a = 1. let b = 2. io @println ("a + b = " + (a + b)). + The output is :: @@ -1379,6 +1479,7 @@ The width specifier tells the ``tostring`` function how many characters to reser Here is a program that exercises some of the string formatting options, :: + load system io. load system type. load system math. @@ -1400,6 +1501,7 @@ Here is a program that exercises some of the string formatting options, let r = type @tostring(math @pi,type @stringformat(6,3)). io @println r. + The output of the program is, :: @@ -1415,11 +1517,13 @@ except that it does not terminate print with a newline. Another useful function defined in the ``io`` module is the ``input`` function that, given an optional prompt string, will prompt the user at the terminal and return the input value as a string. Here is a small example, :: + load system io. let name = io @input("What is your name? "). io @println ("Hello " + name + "!"). + The output is, :: @@ -1430,6 +1534,7 @@ The output is, We can use the type casting functions such as ``tointeger`` or ``toreal`` defined in the ``type`` module to convert the string returned from ``input`` into a numeric value, :: + load system io. load system type. @@ -1439,6 +1544,7 @@ We can use the type casting functions such as ``tointeger`` or ``toreal`` define io @println k. end + The output is, :: @@ -1476,11 +1582,14 @@ rather than in user directories. Say that you wanted to load the ``math`` module so you could execute a certain trigonometric function. The following Asteroid program loads the ``math`` module as well as the ``io`` module. Only after loading them would you be able to complete the sine function below, :: + load system io. load system math. let x = math @sin( math @pi / 2 ). io @println("The sine of pi / 2 is " + x + "."). + Both the function ``sin`` and the constant value ``pi`` are defined in the ``math`` module. In addition, the ``io`` module is where all input/output functions in Asteroid (such as ``println``) come from. +