diff options
69 files changed, 924 insertions, 900 deletions
@@ -104,11 +104,12 @@ clean: %: %.md pandoc --lua-filter=docs/.pandoc/bold-code.lua -s $< -t man -o $@ -examples: examples/base64/base64 examples/ini/ini examples/game/game examples/http-server/http-server \ - examples/tomodeps/tomodeps examples/tomo-install/tomo-install examples/wrap/wrap examples/colorful/colorful - ./build/tomo -qIL examples/patterns examples/time examples/commands examples/shell examples/base64 examples/log \ - examples/ini examples/vectors examples/http \ +examples: + ./build/tomo -qIL examples/patterns examples/time examples/commands examples/shell examples/random \ + examples/base64 examples/log examples/ini examples/vectors examples/http \ examples/wrap examples/pthreads examples/colorful examples/core + ./build/tomo -e examples/base64/base64.tm examples/ini/ini.tm examples/game/game.tm examples/http-server/http-server.tm \ + examples/tomodeps/tomodeps.tm examples/tomo-install/tomo-install.tm examples/wrap/wrap.tm examples/colorful/colorful.tm ./build/tomo examples/learnxiny.tm deps: check-gcc @@ -7,7 +7,7 @@ the language design decisions of the future. ``` func greeting(name:Text, add_exclamation:Bool -> Text): message := "hello $name" - message = " ":join([w:title() for w in message:split_any(" ")]) + message = " ".join([w.title() for w in message.split_any(" ")]) if add_exclamation: message ++= "!!!" return message diff --git a/docs/arrays.md b/docs/arrays.md index 8ea51f01..ec114442 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -29,16 +29,16 @@ Arrays can also use comprehensions, where you specify how to dynamically create all the elements by iteration instead of manually specifying each: ```tomo ->> [i*10 for i in 3:to(8)] +>> [i*10 for i in (3).to(8)] = [30, 40, 50, 60, 70, 80] ->> [i*10 for i in 3:to(8) if i != 4] +>> [i*10 for i in (3).to(8) if i != 4] = [30, 50, 60, 70, 80] ``` Comprehensions can be combined with regular items or other comprehensions: ```tomo ->> [-1, i*10 for i in 3:to(8), i for i in 3] +>> [-1, i*10 for i in (3).to(8), i for i in 3] = [-1, 30, 40, 50, 60, 70, 80, 1, 2, 3] ``` @@ -122,7 +122,7 @@ in bytes between each element in the array. The reason this is mentioned is that it is possible to create immutable slices of arrays in constant time by creating a new struct that points to the appropriate starting place for the array items and has the appropriate stride. The upshot is that a method like -`array:reversed()` does not actually copy the array, it simply returns a struct +`array.reversed()` does not actually copy the array, it simply returns a struct that points to the back of the array with a negative stride. Arrays adhere to copy-on-write semantics, so we can cheaply create many read-only references to the same data, and only need to do copying if we plan to modify data. After @@ -146,13 +146,13 @@ explicitly perform an assignment operation on the variable or call a method on the variable. Because it would be tedious to require users to write all array operations as -pure functions like `array = array:with_value_at_index(value=x, index=i)`, Tomo +pure functions like `array = array.with_value_at_index(value=x, index=i)`, Tomo provides the familiar imperative syntax for modifying arrays, but keeps the semantics of the pure functional style. Writing `array[i] = x` is -_semantically_ equivalent to `array = array:with_value_at_index(value=x, +_semantically_ equivalent to `array = array.with_value_at_index(value=x, index=i)`, but much more readable and easy to write. Similarly, -`array:insert(x)` is semantically equivalent to `array = -array:with_value_inserted(x)`. We implement these mutating methods as functions +`array.insert(x)` is semantically equivalent to `array = +array.with_value_inserted(x)`. We implement these mutating methods as functions that take a pointer to an array variable, which then either mutate the array's data in-place (if this is the only thing referencing that data) or construct a new array and store its value in the memory where the array variable is stored. @@ -210,7 +210,7 @@ behavior that you get in Python when you create a `list`: nums := @[10, 20, 30] tmp := nums -nums:insert(40) +nums.insert(40) >> tmp = @[10, 20, 30, 40] ``` @@ -278,13 +278,13 @@ place where it would be found if it were inserted and the array were sorted. **Example:** ```tomo ->> [1, 3, 5, 7, 9]:binary_search(5) +>> [1, 3, 5, 7, 9].binary_search(5) = 3 ->> [1, 3, 5, 7, 9]:binary_search(-999) +>> [1, 3, 5, 7, 9].binary_search(-999) = 1 ->> [1, 3, 5, 7, 9]:binary_search(999) +>> [1, 3, 5, 7, 9].binary_search(999) = 6 ``` @@ -305,7 +305,7 @@ A new array with every `step`-th element from the original array. **Example:** ```tomo ->> [1, 2, 3, 4, 5, 6]:by(2) +>> [1, 2, 3, 4, 5, 6].by(2) = [1, 3, 5] ``` @@ -325,7 +325,7 @@ Nothing. **Example:** ```tomo ->> my_array:clear() +>> my_array.clear() ``` --- @@ -344,7 +344,7 @@ A table mapping each element to its count. **Example:** ```tomo ->> [10, 20, 30, 30, 30]:counts() +>> [10, 20, 30, 30, 30].counts() = {10=1, 20=1, 30=3} ``` @@ -365,10 +365,10 @@ The index of the first occurrence or `!Int` if not found. **Example:** ```tomo ->> [10, 20, 30, 40, 50]:find(20) +>> [10, 20, 30, 40, 50].find(20) = 2 : Int? ->> [10, 20, 30, 40, 50]:find(9999) +>> [10, 20, 30, 40, 50].find(9999) = none : Int? ``` @@ -391,9 +391,9 @@ item matches. **Example:** ```tomo ->> [4, 5, 6]:find(func(i:&Int): i:is_prime()) +>> [4, 5, 6].find(func(i:&Int): i.is_prime()) = 5 : Int? ->> [4, 6, 8]:find(func(i:&Int): i:is_prime()) +>> [4, 6, 8].find(func(i:&Int): i.is_prime()) = none : Int? ``` @@ -414,7 +414,7 @@ A new array starting from the specified index. **Example:** ```tomo ->> [10, 20, 30, 40, 50]:from(3) +>> [10, 20, 30, 40, 50].from(3) = [30, 40, 50] ``` @@ -434,7 +434,7 @@ func has(arr: [T] -> Bool) **Example:** ```tomo ->> [10, 20, 30]:has(20) +>> [10, 20, 30].has(20) = yes ``` @@ -458,8 +458,8 @@ The removed top element of the heap or `none` if the array is empty. **Example:** ```tomo >> my_heap := [30, 10, 20] ->> my_heap:heapify() ->> my_heap:heap_pop() +>> my_heap.heapify() +>> my_heap.heap_pop() = 10 ``` @@ -483,7 +483,7 @@ Nothing. **Example:** ```tomo ->> my_heap:heap_push(10) +>> my_heap.heap_push(10) ``` --- @@ -505,7 +505,7 @@ Nothing. **Example:** ```tomo >> my_heap := [30, 10, 20] ->> my_heap:heapify() +>> my_heap.heapify() ``` --- @@ -529,11 +529,11 @@ Nothing. **Example:** ```tomo >> arr := [10, 20] ->> arr:insert(30) +>> arr.insert(30) >> arr = [10, 20, 30] ->> arr:insert(999, at=2) +>> arr.insert(999, at=2) >> arr = [10, 999, 20, 30] ``` @@ -559,11 +559,11 @@ Nothing. **Example:** ```tomo arr := [10, 20] -arr:insert_all([30, 40]) +arr.insert_all([30, 40]) >> arr = [10, 20, 30, 40] -arr:insert_all([99, 100], at=2) +arr.insert_all([99, 100], at=2) >> arr = [10, 99, 100, 20, 30, 40] ``` @@ -590,12 +590,12 @@ otherwise the item at the given index. ```tomo >> arr := [10, 20, 30, 40] ->> arr:pop() +>> arr.pop() = 40 >> arr = &[10, 20, 30] ->> arr:pop(index=2) +>> arr.pop(index=2) = 20 >> arr = &[10, 30] @@ -620,7 +620,7 @@ A random element from the array. **Example:** ```tomo ->> [10, 20, 30]:random() +>> [10, 20, 30].random() = 20 ``` @@ -643,11 +643,11 @@ Nothing. **Example:** ```tomo arr := [10, 20, 30, 40, 50] -arr:remove_at(2) +arr.remove_at(2) >> arr = [10, 30, 40, 50] -arr:remove_at(2, count=2) +arr.remove_at(2, count=2) >> arr = [10, 50] ``` @@ -671,11 +671,11 @@ Nothing. **Example:** ```tomo arr := [10, 20, 10, 20, 30] -arr:remove_item(10) +arr.remove_item(10) >> arr = [20, 20, 30] -arr:remove_item(20, max_count=1) +arr.remove_item(20, max_count=1) >> arr = [20, 30] ``` @@ -696,7 +696,7 @@ A slice of the array with elements in reverse order. **Example:** ```tomo ->> [10, 20, 30]:reversed() +>> [10, 20, 30].reversed() = [30, 20, 10] ``` @@ -734,7 +734,7 @@ A list of sampled elements from the array. **Example:** ```tomo ->> [10, 20, 30]:sample(2, weights=[90%, 5%, 5%]) +>> [10, 20, 30].sample(2, weights=[90%, 5%, 5%]) = [10, 10] ``` @@ -757,7 +757,7 @@ Nothing. **Example:** ```tomo ->> arr:shuffle() +>> arr.shuffle() ``` --- @@ -779,7 +779,7 @@ A new array with shuffled elements. **Example:** ```tomo ->> [10, 20, 30, 40]:shuffled() +>> [10, 20, 30, 40].shuffled() = [40, 10, 30, 20] ``` @@ -803,10 +803,10 @@ second-to-last, and so on. **Example:** ```tomo ->> [10, 20, 30, 40, 50]:slice(2, 4) +>> [10, 20, 30, 40, 50].slice(2, 4) = [20, 30, 40] ->> [10, 20, 30, 40, 50]:slice(-3, -2) +>> [10, 20, 30, 40, 50].slice(-3, -2) = [30, 40] ``` @@ -829,11 +829,11 @@ Nothing. **Example:** ```tomo arr := [40, 10, -30, 20] -arr:sort() +arr.sort() >> arr = [-30, 10, 20, 40] -arr:sort(func(a,b:&Int): a:abs() <> b:abs()) +arr.sort(func(a,b:&Int): a.abs() <> b.abs()) >> arr = [10, 20, -30, 40] ``` @@ -856,10 +856,10 @@ A new array with sorted elements. **Example:** ```tomo ->> [40, 10, -30, 20]:sorted() +>> [40, 10, -30, 20].sorted() = [-30, 10, 20, 40] ->> [40, 10, -30, 20]:sorted(func(a,b:&Int): a:abs() <> b:abs()) +>> [40, 10, -30, 20].sorted(func(a,b:&Int): a.abs() <> b.abs()) = [10, 20, -30, 40] ``` @@ -880,10 +880,10 @@ A new array containing elements from the start up to the specified index. **Example:** ```tomo ->> [10, 20, 30, 40, 50]:to(3) +>> [10, 20, 30, 40, 50].to(3) = [10, 20, 30] ->> [10, 20, 30, 40, 50]:to(-2) +>> [10, 20, 30, 40, 50].to(-2) = [10, 20, 30, 40] ``` @@ -903,6 +903,6 @@ A set containing only unique elements from the array. **Example:** ```tomo ->> [10, 20, 10, 10, 30]:unique() +>> [10, 20, 10, 10, 30].unique() = {10, 20, 30} ``` diff --git a/docs/enums.md b/docs/enums.md index 72e4f3be..45321d25 100644 --- a/docs/enums.md +++ b/docs/enums.md @@ -85,4 +85,4 @@ enum VariousThings(AnInteger(i:Int), TwoWords(word1, word2:Text), Nothing): ``` Functions defined in an enum's namespace can be invoked as methods with `:` if -the first argument is the enum's type or a pointer to one (`vt:doop()`). +the first argument is the enum's type or a pointer to one (`vt.doop()`). diff --git a/docs/functions.md b/docs/functions.md index 9f95277f..ba5e86b3 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -35,7 +35,7 @@ callsite: ``` **Note:** Default arguments are re-evaluated at the callsite for each function -call, so if your default argument is `func foo(x=random:int(1,10) -> Int)`, then +call, so if your default argument is `func foo(x=random.int(1,10) -> Int)`, then each time you call the function without an `x` argument, it will give you a new random number. diff --git a/docs/integers.md b/docs/integers.md index 5681388c..bdef827d 100644 --- a/docs/integers.md +++ b/docs/integers.md @@ -120,7 +120,7 @@ rounding _towards zero_, and modulus never gives negative results: Each integer type has its own version of the following functions. Functions can be called either on the type itself: `Int.sqrt(x)` or as a method call: -`x:sqrt()`. Method call syntax is preferred. +`x.sqrt()`. Method call syntax is preferred. - [`func abs(x: Int -> Int)`](#abs) - [`func choose(n: Int, k: Int -> Int)`](#choose) @@ -151,7 +151,7 @@ The absolute value of `x`. **Example:** ```tomo ->> -10:abs() +>> (-10).abs() = 10 ``` @@ -159,8 +159,8 @@ The absolute value of `x`. ### `choose` Computes the binomial coefficient of the given numbers (the equivalent of `n` -choose `k` in combinatorics). This is equal to `n:factorial()/(k:factorial() * -(n-k):factorial())`. +choose `k` in combinatorics). This is equal to `n.factorial()/(k.factorial() * +(n-k).factorial())`. ```tomo func choose(n: Int, k: Int -> Int) @@ -175,7 +175,7 @@ The binomial coefficient, equivalent to the number of ways to uniquely choose **Example:** ```tomo ->> 4:choose(2) +>> (4).choose(2) = 6 ``` @@ -198,7 +198,7 @@ The first argument clamped between the other two arguments. **Example:** ```tomo ->> 2:clamped(5, 10) +>> (2).clamped(5, 10) = 5 ``` @@ -218,7 +218,7 @@ The factorial of the given integer. **Example:** ```tomo ->> 10:factorial() +>> (10).factorial() = 3628800 ``` @@ -239,7 +239,7 @@ A string representation of the integer, padded to the specified number of digits **Example:** ```tomo ->> 42:format(digits=5) +>> (42).format(digits=5) = "00042" ``` @@ -262,7 +262,7 @@ The hexadecimal string representation of the integer. **Example:** ```tomo ->> 255:hex(digits=4, uppercase=yes, prefix=yes) +>> (255).hex(digits=4, uppercase=yes, prefix=yes) = "0x00FF" ``` @@ -289,9 +289,9 @@ func is_prime(x: Int, reps: Int = 50 -> Bool) **Example:** ```tomo ->> 7:is_prime() +>> (7).is_prime() = yes ->> 6:is_prime() +>> (6).is_prime() = no ``` @@ -317,7 +317,7 @@ The next prime number greater than `x`. **Example:** ```tomo ->> 11:next_prime() +>> (11).next_prime() = 13 ``` @@ -339,7 +339,7 @@ The octal string representation of the integer. **Example:** ```tomo ->> 64:octal(digits=4, prefix=yes) +>> (64).octal(digits=4, prefix=yes) = "0o0100" ``` @@ -362,8 +362,8 @@ An iterator function that counts onward from the starting integer. **Example:** ```tomo nums : &[Int] = &[] -for i in 5:onward(): - nums:insert(i) +for i in (5).onward(): + nums.insert(i) stop if i == 10 >> nums[] = [5, 6, 7, 8, 9, 10] @@ -425,7 +425,7 @@ The previous prime number less than `x`. **Example:** ```tomo ->> 11:prev_prime() +>> (11).prev_prime() = 7 ``` @@ -445,9 +445,9 @@ The integer part of the square root of `x`. **Example:** ```tomo ->> 16:sqrt() +>> (16).sqrt() = 4 ->> 17:sqrt() +>> (17).sqrt() = 4 ``` @@ -470,13 +470,13 @@ An iterator function that returns each integer in the given range (inclusive). **Example:** ```tomo ->> 2:to(5) +>> (2).to(5) = func(->Int?) ->> [x for x in 2:to(5)] +>> [x for x in (2).to(5)] = [2, 3, 4, 5] ->> [x for x in 5:to(2)] +>> [x for x in (5).to(2)] = [5, 4, 3, 2] ->> [x for x in 2:to(5, step=2)] +>> [x for x in (2).to(5, step=2)] = [2, 4] ``` diff --git a/docs/iterators.md b/docs/iterators.md index ff7ed138..8d585ccb 100644 --- a/docs/iterators.md +++ b/docs/iterators.md @@ -8,13 +8,13 @@ For example, the `Path.each_line()` API method returns a function that successively gets one line from a file at a time until the file is exhausted: ```tomo -(./test.txt):write(" +(./test.txt).write(" line one line two line three ") ->> iter := (./test.txt):each_line() +>> iter := (./test.txt).each_line() >> iter() = "line one" : Text? >> iter() @@ -24,7 +24,7 @@ successively gets one line from a file at a time until the file is exhausted: >> iter() = none : Text? -for line in (./test.txt):each_line(): +for line in (./test.txt).each_line(): pass ``` @@ -38,7 +38,7 @@ func primes_up_to(limit:Int): if n > limit: return !Int - while not n:is_prime(): + while not n.is_prime(): n += 1 n += 1 diff --git a/docs/langs.md b/docs/langs.md index d7225ae5..aec9cfa9 100644 --- a/docs/langs.md +++ b/docs/langs.md @@ -11,7 +11,7 @@ where a different type of string is needed. ```tomo lang HTML: convert(t:Text -> HTML): - t = t:translate({ + t = t.translate({ "&" = "&", "<" = "<", ">" = ">", @@ -75,14 +75,14 @@ instead of building a global function called `execute()` that takes a ```tomo lang Sh: convert(text:Text -> Sh): - return Sh.from_text("'" ++ text:replace("'", "''") ++ "'") + return Sh.from_text("'" ++ text.replace("'", "''") ++ "'") func execute(sh:Sh -> Text): ... dir := ask("List which dir? ") cmd := $Sh@(ls -l @dir) -result := cmd:execute() +result := cmd.execute() ``` ## Conversions @@ -94,12 +94,12 @@ another type's block or at the top level. ```tomo lang Sh: convert(text:Text -> Sh): - return Sh.from_text("'" ++ text:replace("'", "''") ++ "'") + return Sh.from_text("'" ++ text.replace("'", "''") ++ "'") struct Foo(x,y:Int): convert(f:Foo -> Sh): return Sh.from_text("$(f.x),$(f.y)") convert(texts:[Text] -> Sh): - return $Sh" ":join([Sh(t) for t in texts]) + return $Sh" ".join([Sh(t) for t in texts]) ``` diff --git a/docs/nums.md b/docs/nums.md index 02859d28..36dedcac 100644 --- a/docs/nums.md +++ b/docs/nums.md @@ -115,7 +115,7 @@ provided are not NaN. Each Num type has its own version of the following functions. Functions can be called either on the type itself: `Num.sqrt(x)` or as a method call: -`x:sqrt()`. Method call syntax is preferred. +`x.sqrt()`. Method call syntax is preferred. --- @@ -184,7 +184,7 @@ The absolute value of `n`. **Example:** ```tomo ->> -3.5:abs() +>> (-3.5).abs() = 3.5 ``` @@ -204,7 +204,7 @@ The arc cosine of `x` in radians. **Example:** ```tomo ->> 0.0:acos() // -> (π/2) +>> (0.0).acos() // -> (π/2) = 1.5708 ``` @@ -224,7 +224,7 @@ The inverse hyperbolic cosine of `x`. **Example:** ```tomo ->> 1.0:acosh() +>> (1.0).acosh() = 0 ``` @@ -244,7 +244,7 @@ The arc sine of `x` in radians. **Example:** ```tomo ->> 0.5:asin() // -> (π/6) +>> (0.5).asin() // -> (π/6) = 0.5236 ``` @@ -264,7 +264,7 @@ The inverse hyperbolic sine of `x`. **Example:** ```tomo ->> 0.0:asinh() +>> (0.0).asinh() = 0 ``` @@ -284,7 +284,7 @@ The arc tangent of `x` in radians. **Example:** ```tomo ->> 1.0:atan() // -> (π/4) +>> (1.0).atan() // -> (π/4) = 0.7854 ``` @@ -325,7 +325,7 @@ The inverse hyperbolic tangent of `x`. **Example:** ```tomo ->> 0.5:atanh() +>> (0.5).atanh() = 0.5493 ``` @@ -345,7 +345,7 @@ The cube root of `x`. **Example:** ```tomo ->> 27.0:cbrt() +>> (27.0).cbrt() = 3 ``` @@ -365,7 +365,7 @@ The smallest integer greater than or equal to `x`. **Example:** ```tomo ->> 3.2:ceil() +>> (3.2).ceil() = 4 ``` @@ -388,7 +388,7 @@ The first argument clamped between the other two arguments. **Example:** ```tomo ->> 2.5:clamped(5.5, 10.5) +>> (2.5).clamped(5.5, 10.5) = 5.5 ``` @@ -409,7 +409,7 @@ A number with the magnitude of `x` and the sign of `y`. **Example:** ```tomo ->> 3.0:copysign(-1) +>> (3.0).copysign(-1) = -3 ``` @@ -429,7 +429,7 @@ The cosine of `x`. **Example:** ```tomo ->> 0.0:cos() +>> (0.0).cos() = 1 ``` @@ -449,7 +449,7 @@ The hyperbolic cosine of `x`. **Example:** ```tomo ->> 0.0:cosh() +>> (0.0).cosh() = 1 ``` @@ -469,7 +469,7 @@ The error function of `x`. **Example:** ```tomo ->> 0.0:erf() +>> (0.0).erf() = 0 ``` @@ -489,7 +489,7 @@ The complementary error function of `x`. **Example:** ```tomo ->> 0.0:erfc() +>> (0.0).erfc() = 1 ``` @@ -509,7 +509,7 @@ The value of \( e^x \). **Example:** ```tomo ->> 1.0:exp() +>> (1.0).exp() = 2.7183 ``` @@ -529,7 +529,7 @@ The value of \( 2^x \). **Example:** ```tomo ->> 3.0:exp2() +>> (3.0).exp2() = 8 ``` @@ -549,7 +549,7 @@ The value of \( e^x - 1 \). **Example:** ```tomo ->> 1.0:expm1() +>> (1.0).expm1() = 1.7183 ``` @@ -572,7 +572,7 @@ The positive difference \( \max(0, x - y) \). ```tomo fd ->> 5.0:fdim(3) +>> (5.0).fdim(3) = 2 ``` @@ -592,7 +592,7 @@ The largest integer less than or equal to `x`. **Example:** ```tomo ->> 3.7:floor() +>> (3.7).floor() = 3 ``` @@ -613,7 +613,7 @@ A text representation of the number with the specified precision. **Example:** ```tomo ->> 3.14159:format(precision=2) +>> (3.14159).format(precision=2) = "3.14" ``` @@ -654,9 +654,9 @@ func isfinite(n: Num -> Bool) **Example:** ```tomo ->> 1.0:isfinite() +>> (1.0).isfinite() = yes ->> Num.INF:isfinite() +>> Num.INF.isfinite() = no ``` @@ -676,9 +676,9 @@ func isinf(n: Num -> Bool) **Example:** ```tomo ->> Num.INF:isinf() +>> Num.INF.isinf() = yes ->> 1.0:isinf() +>> (1.0).isinf() = no ``` @@ -698,7 +698,7 @@ The Bessel function of the first kind of order 0 of `x`. **Example:** ```tomo ->> 0.0:j0() +>> (0.0).j0() = 1 ``` @@ -718,7 +718,7 @@ The Bessel function of the first kind of order 1 of `x`. **Example:** ```tomo ->> 0.0:j1() +>> (0.0).j1() = 0 ``` @@ -738,7 +738,7 @@ The natural logarithm of `x`. **Example:** ```tomo ->> Num.E:log() +>> Num.E.log() = 1 ``` @@ -758,7 +758,7 @@ The base-10 logarithm of `x`. **Example:** ```tomo ->> 100.0:log10() +>> (100.0).log10() = 2 ``` @@ -778,7 +778,7 @@ The value of \( \log(1 + x) \). **Example:** ```tomo ->> 1.0:log1p() +>> (1.0).log1p() = 0.6931 ``` @@ -798,7 +798,7 @@ The base-2 logarithm of `x`. **Example:** ```tomo ->> 8.0:log2() +>> (8.0).log2() = 3 ``` @@ -818,7 +818,7 @@ The binary exponent of `x`. **Example:** ```tomo ->> 8.0:logb() +>> (8.0).logb() = 3 ``` @@ -840,9 +840,9 @@ The interpolated number between `x` and `y` based on `amount`. **Example:** ```tomo ->> 0.5:mix(10, 20) +>> (0.5).mix(10, 20) = 15 ->> 0.25:mix(10, 20) +>> (0.25).mix(10, 20) = 12.5 ``` @@ -867,13 +867,13 @@ func near(x: Num, y: Num, ratio: Num = 1e-9, min_epsilon: Num = 1e-9 -> Bool) **Example:** ```tomo ->> 1.0:near(1.000000001) +>> (1.0).near(1.000000001) = yes ->> 100.0:near(110, ratio=0.1) +>> (100.0).near(110, ratio=0.1) = yes ->> 5.0:near(5.1, min_epsilon=0.1) +>> (5.0).near(5.1, min_epsilon=0.1) = yes ``` @@ -894,7 +894,7 @@ The next representable value after `x` in the direction of `y`. **Example:** ```tomo ->> 1.0:nextafter(1.1) +>> (1.0).nextafter(1.1) = 1.0000000000000002 ``` @@ -938,9 +938,9 @@ A text representation of the number as a percentage with a percent sign. **Example:** ```tomo ->> 0.5:percent() +>> (0.5).percent() = "50%" ->> (1./3.):percent(2) +>> (1./3.).percent(2) = "33.33%" ``` @@ -960,9 +960,9 @@ The nearest integer value of `x`. **Example:** ```tomo ->> 3.5:rint() +>> (3.5).rint() = 4 ->> 2.5:rint() +>> (2.5).rint() = 2 ``` @@ -982,9 +982,9 @@ The nearest integer value of `x`. **Example:** ```tomo ->> 2.3:round() +>> (2.3).round() = 2 ->> 2.7:round() +>> (2.7).round() = 3 ``` @@ -1005,7 +1005,7 @@ A text representation of the number in scientific notation with the specified pr **Example:** ```tomo ->> 12345.6789:scientific(precision=2) +>> (12345.6789).scientific(precision=2) = "1.23e+04" ``` @@ -1025,7 +1025,7 @@ The significand of `x`. **Example:** ```tomo ->> 1234.567:significand() +>> (1234.567).significand() = 0.1234567 ``` @@ -1045,7 +1045,7 @@ The sine of `x`. **Example:** ```tomo ->> 0.0:sin() +>> (0.0).sin() = 0 ``` @@ -1065,7 +1065,7 @@ The hyperbolic sine of `x`. **Example:** ```tomo ->> 0.0:sinh() +>> (0.0).sinh() = 0 ``` @@ -1085,7 +1085,7 @@ The square root of `x`. **Example:** ```tomo ->> 16.0:sqrt() +>> (16.0).sqrt() = 4 ``` @@ -1105,7 +1105,7 @@ The tangent of `x`. **Example:** ```tomo ->> 0.0:tan() +>> (0.0).tan() = 0 ``` @@ -1125,7 +1125,7 @@ The hyperbolic tangent of `x`. **Example:** ```tomo ->> 0.0:tanh() +>> (0.0).tanh() = 0 ``` @@ -1145,7 +1145,7 @@ The gamma function of `x`. **Example:** ```tomo ->> 1.0:tgamma() +>> (1.0).tgamma() = 1 ``` @@ -1165,9 +1165,9 @@ The integer part of `x` towards zero. **Example:** ```tomo ->> 3.7:trunc() +>> (3.7).trunc() = 3 ->> (-3.7):trunc() +>> (-3.7).trunc() = -3 ``` @@ -1187,7 +1187,7 @@ The Bessel function of the second kind of order 0 of `x`. **Example:** ```tomo ->> 1.0:y0() +>> (1.0).y0() = -0.7652 ``` @@ -1207,6 +1207,6 @@ The Bessel function of the second kind of order 1 of `x`. **Example:** ```tomo ->> 1.0:y1() +>> (1.0).y1() = 0.4401 ``` diff --git a/docs/operators.md b/docs/operators.md index a304cf35..548891e7 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -37,7 +37,7 @@ It's particularly handy for using the array `sort()` method, which takes a function that returns a signed integer: ```tomo ->> foos:sort(func(a,b:&Foo): a.length <> b.length) +>> foos.sort(func(a,b:&Foo): a.length <> b.length) ``` ## Reducers @@ -110,12 +110,12 @@ struct Foo(x,y:Int): >> (+: f.x for f in foos) = -9 ->> (or):is_even() foos +>> (or).is_even() foos = yes // Shorthand for: ->> (or) f:is_even() for f in foos +>> (or) f.is_even() for f in foos ->> (+.x:abs(): foos) +>> (+.x.abs(): foos) = 11 ``` @@ -144,7 +144,7 @@ Here's some examples: ```tomo // Get the largest absolute value number: ->> 3 _max_:abs() -15 +>> 3 _max_.abs() -15 = -15 struct Person(name:Text, age:Int) @@ -164,7 +164,7 @@ struct Person(name:Text, age:Int) The keyed comparison can chain together multiple field accesses, array index operations, method calls, etc. If you wanted to, for example, get the item -whose `x` field has the highest absolute value, you could use `_max_.x:abs()`. +whose `x` field has the highest absolute value, you could use `_max_.x.abs()`. ### Working with Reducers @@ -177,7 +177,7 @@ object using them: >> (_max_: nums) = 30 ->> (_max_:abs(): nums) +>> (_max_.abs(): nums) = -40 ``` @@ -221,7 +221,7 @@ func plus(T, T)->T ``` In an addition expression `a + b` between two objects of the same type, the -method `a:plus(b)` will be invoked, which returns a new value of the same type. +method `a.plus(b)` will be invoked, which returns a new value of the same type. #### Subtraction @@ -230,7 +230,7 @@ func minus(T, T)->T ``` In a subtraction expression `a - b` between two objects of the same type, the -method `a:minus(b)` will be invoked, which returns a new value of the same type. +method `a.minus(b)` will be invoked, which returns a new value of the same type. #### Multiplication @@ -239,9 +239,9 @@ func times(T, T)->T func scaled_by(T, N)->T ``` -The multiplication expression `a * b` invokes either the `a:times(b)` method, -if `a` and `b` are the same non-numeric type, or `a:scaled_by(b)` if `a` is -non-numeric and `b` is numeric, or `b:scaled_by(a)` if `b` is non-numeric and +The multiplication expression `a * b` invokes either the `a.times(b)` method, +if `a` and `b` are the same non-numeric type, or `a.scaled_by(b)` if `a` is +non-numeric and `b` is numeric, or `b.scaled_by(a)` if `b` is non-numeric and `a` is numeric. In all cases, a new value of the non-numeric type is returned. #### Division @@ -250,7 +250,7 @@ non-numeric and `b` is numeric, or `b:scaled_by(a)` if `b` is non-numeric and func divided_by(T, N)->T ``` -In a division expression `a / b` the method `a:divided_by(b)` will be invoked +In a division expression `a / b` the method `a.divided_by(b)` will be invoked if `a` has type `T` and `b` has a numeric type `N`. #### Exponentiation @@ -260,7 +260,7 @@ func power(T, N)->T ``` In an exponentiation expression, `a ^ b`, if `a` has type `T` and `b` has a -numeric type `N`, then the method `a:power(b)` will be invoked. +numeric type `N`, then the method `a.power(b)` will be invoked. #### Modulus diff --git a/docs/optionals.md b/docs/optionals.md index ff4252d1..7b100dc7 100644 --- a/docs/optionals.md +++ b/docs/optionals.md @@ -119,7 +119,7 @@ func do_stuff(matches:[Text]): pass for line in lines: - matches := line:matches($/{..},{..}/) or skip + matches := line.matches($/{..},{..}/) or skip # The `or skip` above means that if we're here, `matches` is non-none: do_stuff(matches) ``` diff --git a/docs/paths.md b/docs/paths.md index ac60d864..af179ede 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -92,9 +92,9 @@ accessed, or `none` if no such file or directory exists. **Example:** ```tomo ->> (./file.txt):accessed() +>> (./file.txt).accessed() = 1704221100? ->> (./not-a-file):accessed() +>> (./not-a-file).accessed() = none ``` @@ -117,7 +117,7 @@ Nothing. **Example:** ```tomo -(./log.txt):append("extra line$(\n)") +(./log.txt).append("extra line$(\n)") ``` --- @@ -139,7 +139,7 @@ Nothing. **Example:** ```tomo -(./log.txt):append_bytes([104[B], 105[B]]) +(./log.txt).append_bytes([104[B], 105[B]]) ``` --- @@ -158,7 +158,7 @@ The base name of the file or directory. **Example:** ```tomo ->> (./path/to/file.txt):base_name() +>> (./path/to/file.txt).base_name() = "file.txt" ``` @@ -181,15 +181,15 @@ value if the file couldn't be read. **Example:** ```tomo # Safely handle file not being readable: -if lines := (./file.txt):by_line(): +if lines := (./file.txt).by_line(): for line in lines: - say(line:upper()) + say(line.upper()) else: say("Couldn't read file!") # Assume the file is readable and error if that's not the case: -for line in (/dev/stdin):by_line()!: - say(line:upper()) +for line in (/dev/stdin).by_line()!: + say(line.upper()) ``` --- @@ -208,11 +208,11 @@ func can_execute(path: Path -> Bool) **Example:** ```tomo ->> (/bin/sh):can_execute() +>> (/bin/sh).can_execute() = yes ->> (/usr/include/stdlib.h):can_execute() +>> (/usr/include/stdlib.h).can_execute() = no ->> (/non/existant/file):can_execute() +>> (/non/existant/file).can_execute() = no ``` @@ -232,11 +232,11 @@ func can_read(path: Path -> Bool) **Example:** ```tomo ->> (/usr/include/stdlib.h):can_read() +>> (/usr/include/stdlib.h).can_read() = yes ->> (/etc/shadow):can_read() +>> (/etc/shadow).can_read() = no ->> (/non/existant/file):can_read() +>> (/non/existant/file).can_read() = no ``` @@ -256,11 +256,11 @@ func can_write(path: Path -> Bool) **Example:** ```tomo ->> (/tmp):can_write() +>> (/tmp).can_write() = yes ->> (/etc/passwd):can_write() +>> (/etc/passwd).can_write() = no ->> (/non/existant/file):can_write() +>> (/non/existant/file).can_write() = no ``` @@ -286,9 +286,9 @@ changed, or `none` if no such file or directory exists. **Example:** ```tomo ->> (./file.txt):changed() +>> (./file.txt).changed() = 1704221100? ->> (./not-a-file):changed() +>> (./not-a-file).changed() = none ``` @@ -309,7 +309,7 @@ A new path representing the child. **Example:** ```tomo ->> (./directory):child("file.txt") +>> (./directory).child("file.txt") = (./directory/file.txt) ``` @@ -330,7 +330,7 @@ A list of paths for the children. **Example:** ```tomo ->> (./directory):children(include_hidden=yes) +>> (./directory).children(include_hidden=yes) = [".git", "foo.txt"] ``` @@ -352,7 +352,7 @@ Nothing. **Example:** ```tomo -(./new_directory):create_directory() +(./new_directory).create_directory() ``` --- @@ -371,7 +371,7 @@ func exists(path: Path -> Bool) **Example:** ```tomo ->> (/):exists() +>> (/).exists() = yes ``` @@ -393,9 +393,9 @@ replace the `~` with an absolute path to the user's home directory. **Example:** ```tomo ->> (~/foo):expand_home() # Assume current user is 'user' +>> (~/foo).expand_home() # Assume current user is 'user' = /home/user/foo ->> (/foo):expand_home() # No change +>> (/foo).expand_home() # No change = /foo ``` @@ -418,13 +418,13 @@ no file extension. **Example:** ```tomo ->> (./file.tar.gz):extension() +>> (./file.tar.gz).extension() = "tar.gz" ->> (./file.tar.gz):extension(full=no) +>> (./file.tar.gz).extension(full=no) = "gz" ->> (/foo):extension() +>> (/foo).extension() = "" ->> (./.git):extension() +>> (./.git).extension() = "" ``` @@ -445,7 +445,7 @@ A list of file paths. **Example:** ```tomo ->> (./directory):files(include_hidden=yes) +>> (./directory).files(include_hidden=yes) = [(./directory/file1.txt), (./directory/file2.txt)] ``` @@ -498,20 +498,20 @@ A list of file paths that match the glob. **Example:** ```tomo # Current directory includes: foo.txt, baz.txt, qux.jpg, .hidden ->> (./*):glob() +>> (./*).glob() = [(./foo.txt), (./baz.txt), (./qux.jpg)] ->> (./*.txt):glob() +>> (./*.txt).glob() = [(./foo.txt), (./baz.txt)] ->> (./*.{txt,jpg}):glob() +>> (./*.{txt,jpg}).glob() = [(./foo.txt), (./baz.txt), (./qux.jpg)] ->> (./.*):glob() +>> (./.*).glob() = [(./.hidden)] # Globs with no matches return an empty array: ->> (./*.xxx):glob() +>> (./*.xxx).glob() = [] ``` @@ -532,9 +532,9 @@ The name of the group which owns the file or directory, or `none` if the path do **Example:** ```tomo ->> (/bin):group() +>> (/bin).group() = "root" ->> (/non/existent/file):group() +>> (/non/existent/file).group() = none ``` @@ -555,10 +555,10 @@ func is_directory(path: Path, follow_symlinks=yes -> Bool) **Example:** ```tomo ->> (./directory/):is_directory() +>> (./directory/).is_directory() = yes ->> (./file.txt):is_directory() +>> (./file.txt).is_directory() = no ``` @@ -579,10 +579,10 @@ func is_file(path: Path, follow_symlinks=yes -> Bool) **Example:** ```tomo ->> (./file.txt):is_file() +>> (./file.txt).is_file() = yes ->> (./directory/):is_file() +>> (./directory/).is_file() = no ``` @@ -603,7 +603,7 @@ func is_socket(path: Path, follow_symlinks=yes -> Bool) **Example:** ```tomo ->> (./socket):is_socket() +>> (./socket).is_socket() = yes ``` @@ -623,7 +623,7 @@ func is_symlink(path: Path -> Bool) **Example:** ```tomo ->> (./link):is_symlink() +>> (./link).is_symlink() = yes ``` @@ -645,9 +645,9 @@ modified, or `none` if no such file or directory exists. **Example:** ```tomo ->> (./file.txt):modified() +>> (./file.txt).modified() = 1704221100? ->> (./not-a-file):modified() +>> (./not-a-file).modified() = none ``` @@ -668,9 +668,9 @@ The name of the user who owns the file or directory, or `none` if the path does **Example:** ```tomo ->> (/bin):owner() +>> (/bin).owner() = "root" ->> (/non/existent/file):owner() +>> (/non/existent/file).owner() = none ``` @@ -690,7 +690,7 @@ The path of the parent directory. **Example:** ```tomo ->> (./path/to/file.txt):parent() +>> (./path/to/file.txt).parent() = (./path/to/) ``` @@ -713,10 +713,10 @@ raised. **Example:** ```tomo ->> (./hello.txt):read() +>> (./hello.txt).read() = "Hello"? ->> (./nosuchfile.xxx):read() +>> (./nosuchfile.xxx).read() = none ``` --- @@ -738,10 +738,10 @@ returned. **Example:** ```tomo ->> (./hello.txt):read() +>> (./hello.txt).read() = [72[B], 101[B], 108[B], 108[B], 111[B]]? ->> (./nosuchfile.xxx):read() +>> (./nosuchfile.xxx).read() = none ``` @@ -762,7 +762,7 @@ The relative path. **Example:** ```tomo ->> (./path/to/file.txt):relative(relative_to=(./path)) +>> (./path/to/file.txt).relative(relative_to=(./path)) = (./to/file.txt) ``` @@ -783,7 +783,7 @@ Nothing. **Example:** ```tomo -(./file.txt):remove() +(./file.txt).remove() ``` --- @@ -803,10 +803,10 @@ The resolved absolute path. **Example:** ```tomo ->> (~/foo):resolved() +>> (~/foo).resolved() = (/home/user/foo) ->> (./path/to/file.txt):resolved(relative_to=(/foo)) +>> (./path/to/file.txt).resolved(relative_to=(/foo)) = (/foo/path/to/file.txt) ``` @@ -829,7 +829,7 @@ Nothing. If a path does not exist, a failure will be raised. **Example:** ```tomo -(./file.txt):set_owner(owner="root", group="wheel") +(./file.txt).set_owner(owner="root", group="wheel") ``` --- @@ -849,10 +849,10 @@ A list of subdirectory paths. **Example:** ```tomo ->> (./directory):subdirectories() +>> (./directory).subdirectories() = [(./directory/subdir1), (./directory/subdir2)] ->> (./directory):subdirectories(include_hidden=yes) +>> (./directory).subdirectories(include_hidden=yes) = [(./directory/.git), (./directory/subdir1), (./directory/subdir2)] ``` @@ -873,11 +873,11 @@ A unique directory path after creating the directory. **Example:** ``` ->> created := (/tmp/my-dir.XXXXXX):unique_directory() +>> created := (/tmp/my-dir.XXXXXX).unique_directory() = (/tmp/my-dir-AwoxbM/) ->> created:is_directory() +>> created.is_directory() = yes -created:remove() +created.remove() ``` --- @@ -900,7 +900,7 @@ Nothing. **Example:** ```tomo -(./file.txt):write("Hello, world!") +(./file.txt).write("Hello, world!") ``` --- @@ -923,7 +923,7 @@ Nothing. **Example:** ```tomo -(./file.txt):write_bytes([104[B], 105[B]]) +(./file.txt).write_bytes([104[B], 105[B]]) ``` --- @@ -946,11 +946,11 @@ The path of the newly created unique file. **Example:** ```tomo ->> created := (./file-XXXXXX.txt):write_unique("Hello, world!") +>> created := (./file-XXXXXX.txt).write_unique("Hello, world!") = (./file-27QHtq.txt) ->> created:read() +>> created.read() = "Hello, world!" -created:remove() +created.remove() ``` --- @@ -973,9 +973,9 @@ The path of the newly created unique file. **Example:** ```tomo ->> created := (./file-XXXXXX.txt):write_unique_bytes([1[B], 2[B], 3[B]]) +>> created := (./file-XXXXXX.txt).write_unique_bytes([1[B], 2[B], 3[B]]) = (./file-27QHtq.txt) ->> created:read() +>> created.read() = [1[B], 2[B], 3[B]] -created:remove() +created.remove() ``` diff --git a/docs/pointers.md b/docs/pointers.md index f1bd1b5a..36cab2c1 100644 --- a/docs/pointers.md +++ b/docs/pointers.md @@ -94,12 +94,12 @@ For convenience, most operations that work on values can work with pointers to values implicitly. For example, if you have a struct type with a `.foo` field, you can use `ptr.foo` on a pointer to that struct type as well, without needing to use `ptr[].foo`. The same is true for array accesses like `ptr[i]` and method -calls like `ptr:reversed()`. +calls like `ptr.reversed()`. # Read-Only Views -In a small number of API methods (`array:first()`, `array:binary_search()`, -`array:sort()`, `array:sorted()`, and `array:heapify()`), the methods allow you +In a small number of API methods (`array.first()`, `array.binary_search()`, +`array.sort()`, `array.sorted()`, and `array.heapify()`), the methods allow you to provide custom comparison functions. However, for safety, we don't actually want the comparison methods to be able mutate the values inside of immutable array values. For implementation reasons, we can't pass the values themselves @@ -112,7 +112,7 @@ inside of any datastructures as elements or members. ```tomo nums := @[10, 20, 30] ->> nums:first(func(x:&Int): x / 2 == 10) +>> nums.first(func(x:&Int): x / 2 == 10) = 2 : Int? ``` diff --git a/docs/reductions.md b/docs/reductions.md index 959d9701..7aadd30b 100644 --- a/docs/reductions.md +++ b/docs/reductions.md @@ -80,7 +80,7 @@ maximum value _according to some feature_. = "aaaaa" # Get the number with the biggest absolute value: ->> (_max_:abs(): [1, -2, 3, -4])! +>> (_max_.abs(): [1, -2, 3, -4])! = -4 ``` @@ -96,6 +96,6 @@ while filtering out values or while applying a transformation: = 6 # Sum the primes between 1-100: ->> (+: i for i in 100 if i:is_prime())! +>> (+: i for i in 100 if i.is_prime())! = 1060 ``` diff --git a/docs/serialization.md b/docs/serialization.md index a1a38fdb..d63cb168 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -10,12 +10,12 @@ original value. ## Serializing -To serialize data, simply call the method `:serialized()` on any value and it +To serialize data, simply call the method `.serialized()` on any value and it will return an array of bytes that encode the value's data: ```tomo value := Int64(5) ->> serialized := value:serialized() +>> serialized := value.serialized() = [0x0A] : [Byte] ``` @@ -30,7 +30,7 @@ To deserialize data, you must provide its type explicitly using the syntax ```tomo i := 123 -bytes := i:serialized() +bytes := i.serialized() roundtripped := deserialize(bytes -> Int) >> roundtripped @@ -58,7 +58,7 @@ c := @Cycle("A") c.next = @Cycle("B", next=c) >> c = @Cycle(name="A", next=@Cycle(name="B", next=@~1)) ->> serialized := c:serialized() +>> serialized := c.serialized() = [0x02, 0x02, 0x41, 0x01, 0x04, 0x02, 0x42, 0x01, 0x02] : [Byte] >> roundtrip := DESERIALIZE(serialized):@Cycle = @Cycle(name="A", next=@Cycle(name="B", next=@~1)) : @Cycle diff --git a/docs/sets.md b/docs/sets.md index 023547dd..62351ed5 100644 --- a/docs/sets.md +++ b/docs/sets.md @@ -6,7 +6,7 @@ implemented using hash tables. ```tomo a := {10, 20, 30} b := {20, 30} ->> a:overlap(b) +>> a.overlap(b) = {20} ``` @@ -103,7 +103,7 @@ Nothing. **Example:** ```tomo ->> nums:add(42) +>> nums.add(42) ``` --- @@ -123,7 +123,7 @@ Nothing. **Example:** ```tomo ->> nums:add_all([1, 2, 3]) +>> nums.add_all([1, 2, 3]) ``` --- @@ -142,7 +142,7 @@ Nothing. **Example:** ```tomo ->> nums:clear() +>> nums.clear() ``` --- @@ -162,7 +162,7 @@ func has(set:{T}, item:T -> Bool) **Example:** ```tomo ->> {10, 20}:has(20) +>> {10, 20}.has(20) = yes ``` @@ -184,7 +184,7 @@ func (set: {T}, other: {T}, strict: Bool = no -> Bool) **Example:** ```tomo ->> {1, 2}:is_subset_of({1, 2, 3}) +>> {1, 2}.is_subset_of({1, 2, 3}) = yes ``` @@ -206,7 +206,7 @@ func is_superset_of(set:{T}, other: {T}, strict: Bool = no -> Bool) **Example:** ```tomo ->> {1, 2, 3}:is_superset_of({1, 2}) +>> {1, 2, 3}.is_superset_of({1, 2}) = yes ``` ### `overlap` @@ -224,7 +224,7 @@ A new set containing only items present in both sets. **Example:** ```tomo ->> {1, 2}:overlap({2, 3}) +>> {1, 2}.overlap({2, 3}) = {2} ``` @@ -245,7 +245,7 @@ Nothing. **Example:** ```tomo ->> nums:remove(42) +>> nums.remove(42) ``` --- @@ -265,7 +265,7 @@ Nothing. **Example:** ```tomo ->> nums:remove_all([1, 2, 3]) +>> nums.remove_all([1, 2, 3]) ``` --- @@ -285,7 +285,7 @@ A new set containing all items from both sets. **Example:** ```tomo ->> {1, 2}:with({2, 3}) +>> {1, 2}.with({2, 3}) = {1, 2, 3} ``` @@ -306,6 +306,6 @@ A new set containing items from the original set excluding those in the other se **Example:** ```tomo ->> {1, 2}:without({2, 3}) +>> {1, 2}.without({2, 3}) = {1} ``` diff --git a/docs/structs.md b/docs/structs.md index ce4f6ff5..f075f037 100644 --- a/docs/structs.md +++ b/docs/structs.md @@ -31,8 +31,8 @@ struct Foo(name:Text, age:Int): f.age += 1 ... my_foo := @Foo("Alice", 28) -my_foo:greet() -my_foo:get_older() +my_foo.greet() +my_foo.get_older() ``` Method calls work when the first argument is the struct type or a pointer to diff --git a/docs/tables.md b/docs/tables.md index 93ee3eb9..7a50c9dd 100644 --- a/docs/tables.md +++ b/docs/tables.md @@ -102,7 +102,7 @@ is non-optional (because a value will always be present). ## Setting Values You can assign a new key/value mapping or overwrite an existing one using -`:set(key, value)` or an `=` assignment statement: +`.set(key, value)` or an `=` assignment statement: ```tomo t := {"A"=1, "B"=2} @@ -177,8 +177,8 @@ Nothing. **Example:** ```tomo >> t := {"A"=1} -t:bump("A") -t:bump("B", 10) +t.bump("A") +t.bump("B", 10) >> t = {"A"=2, "B"=10} ``` @@ -199,7 +199,7 @@ Nothing. **Example:** ```tomo ->> t:clear() +>> t.clear() ``` --- @@ -221,16 +221,16 @@ The value associated with the key or `none` if the key is not found. **Example:** ```tomo >> t := {"A"=1, "B"=2} ->> t:get("A") +>> t.get("A") = 1? ->> t:get("????") +>> t.get("????") = none ->> t:get("A")! +>> t.get("A")! = 1 ->> t:get("????") or 0 +>> t.get("????") or 0 = 0 ``` @@ -251,9 +251,9 @@ func has(t:{K=V}, key: K -> Bool) **Example:** ```tomo ->> {"A"=1, "B"=2}:has("A") +>> {"A"=1, "B"=2}.has("A") = yes ->> {"A"=1, "B"=2}:has("xxx") +>> {"A"=1, "B"=2}.has("xxx") = no ``` @@ -275,7 +275,7 @@ Nothing. **Example:** ```tomo t := {"A"=1, "B"=2} -t:remove("A") +t.remove("A") >> t = {"B"=2} ``` @@ -299,7 +299,7 @@ Nothing. **Example:** ```tomo t := {"A"=1, "B"=2} -t:set("C", 3) +t.set("C", 3) >> t = {"A"=1, "B"=2, "C"=3} ``` diff --git a/docs/text.md b/docs/text.md index fd79fdbd..f0665762 100644 --- a/docs/text.md +++ b/docs/text.md @@ -245,7 +245,7 @@ several joining modifiers attached to it, that text has a length of 1. Iteration is *not* supported for text. It is rarely ever the case that you will need to iterate over text, but if you do, you can iterate over the length of the text and retrieve 1-wide slices. Alternatively, you can split the text into -its constituent grapheme clusters with `text:split()` and iterate over those. +its constituent grapheme clusters with `text.split()` and iterate over those. ### Equality, Comparison, and Hashing @@ -319,7 +319,7 @@ A C-style string (`CString`) representing the text. **Example:** ```tomo ->> "Hello":as_c_string() +>> "Hello".as_c_string() = CString("Hello") ``` @@ -343,7 +343,7 @@ indices are counted from the back of the text, so `-1` means the last cluster, **Example:** ```tomo ->> "Amélie":at(3) +>> "Amélie".at(3) = "é" ``` @@ -362,7 +362,7 @@ func by_line(text: Text -> func(->Text?)) **Returns:** An iterator function that returns one line at a time, until it runs out and returns `none`. **Note:** this function ignores a trailing newline if there is -one. If you don't want this behavior, use `text:by_split($/{1 nl}/)` instead. +one. If you don't want this behavior, use `text.by_split($/{1 nl}/)` instead. **Example:** ```tomo @@ -370,7 +370,7 @@ text := " line one line two " -for line in text:by_line(): +for line in text.by_line(): # Prints: "line one" then "line two": say(line) ``` @@ -398,7 +398,7 @@ delimiter (the default) will iterate over single grapheme clusters in the text. **Example:** ```tomo text := "one,two,three" -for chunk in text:by_split(","): +for chunk in text.by_split(","): # Prints: "one" then "two" then "three": say(chunk) ``` @@ -425,7 +425,7 @@ given delimiter characters, until it runs out and returns `none`. **Example:** ```tomo text := "one,two,;,three" -for chunk in text:by_split_any(",;"): +for chunk in text.by_split_any(",;"): # Prints: "one" then "two" then "three": say(chunk) ``` @@ -447,7 +447,7 @@ An array of bytes (`[Byte]`) representing the text in UTF8 encoding. **Example:** ```tomo ->> "Amélie":bytes() +>> "Amélie".bytes() = [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte] ``` @@ -470,11 +470,11 @@ func caseless_equals(a: Text, b:Text, language:Text = "C" -> Bool) **Example:** ```tomo ->> "A":caseless_equals("a") +>> "A".caseless_equals("a") = yes # Turkish lowercase "I" is "ı" (dotless I), not "i" ->> "I":caseless_equals("i", language="tr_TR") +>> "I".caseless_equals("i", language="tr_TR") = no ``` @@ -494,7 +494,7 @@ An array of codepoint names (`[Text]`). **Example:** ```tomo ->> "Amélie":codepoint_names() +>> "Amélie".codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] ``` @@ -515,7 +515,7 @@ func ends_with(text: Text, suffix: Text -> Bool) **Example:** ```tomo ->> "hello world":ends_with("world") +>> "hello world".ends_with("world") = yes ``` @@ -539,10 +539,10 @@ the length of the text. **Example:** ```tomo ->> "hello":from(2) +>> "hello".from(2) = "ello" ->> "hello":from(-2) +>> "hello".from(-2) = "lo" ``` @@ -655,9 +655,9 @@ func has(text: Text, target: Text -> Bool) **Example:** ```tomo ->> "hello world":has("wo") +>> "hello world".has("wo") = yes ->> "hello world":has("xxx") +>> "hello world".has("xxx") = no ``` @@ -678,7 +678,7 @@ A single `Text` value with the pieces joined by the glue. **Example:** ```tomo ->> ", ":join(["one", "two", "three"]) +>> ", ".join(["one", "two", "three"]) = "one, two, three" ``` @@ -703,9 +703,9 @@ reach the exact desired length. **Example:** ```tomo ->> "x":middle_pad(6) +>> "x".middle_pad(6) = " x " ->> "x":middle_pad(10, "ABC") +>> "x".middle_pad(10, "ABC") = "ABCAxABCAB" ``` @@ -730,9 +730,9 @@ exact desired length. **Example:** ```tomo ->> "x":left_pad(5) +>> "x".left_pad(5) = " x" ->> "x":left_pad(5, "ABC") +>> "x".left_pad(5, "ABC") = "ABCAx" ``` @@ -753,15 +753,15 @@ An array of substrings resulting from the split. **Example:** ```tomo ->> "one$(\n)two$(\n)three":lines() +>> "one$(\n)two$(\n)three".lines() = ["one", "two", "three"] ->> "one$(\n)two$(\n)three$(\n)":lines() +>> "one$(\n)two$(\n)three$(\n)".lines() = ["one", "two", "three"] ->> "one$(\n)two$(\n)three$(\n\n)":lines() +>> "one$(\n)two$(\n)three$(\n\n)".lines() = ["one", "two", "three", ""] ->> "one$(\r\n)two$(\r\n)three$(\r\n)":lines() +>> "one$(\r\n)two$(\r\n)three$(\r\n)".lines() = ["one", "two", "three"] ->> "":lines() +>> "".lines() = [] ``` @@ -782,10 +782,10 @@ The lowercase version of the text. **Example:** ```tomo ->> "AMÉLIE":lower() +>> "AMÉLIE".lower() = "amélie" ->> "I":lower(language="tr_TR") +>> "I".lower(language="tr_TR") >> "ı" ``` @@ -807,7 +807,7 @@ The text formatted as a quoted text. **Example:** ```tomo ->> "one$(\n)two":quoted() +>> "one$(\n)two".quoted() = "\"one\\ntwo\"" ``` @@ -828,7 +828,7 @@ The text repeated the given number of times. **Example:** ```tomo ->> "Abc":repeat(3) +>> "Abc".repeat(3) = "AbcAbcAbc" ``` @@ -850,7 +850,7 @@ The text with occurrences of the target replaced. **Example:** ```tomo ->> "Hello world":replace("world", "there") +>> "Hello world".replace("world", "there") = "Hello there" ``` @@ -870,7 +870,7 @@ A reversed version of the text. **Example:** ```tomo ->> "Abc":reversed() +>> "Abc".reversed() = "cbA" ``` @@ -895,9 +895,9 @@ exact desired length. **Example:** ```tomo ->> "x":right_pad(5) +>> "x".right_pad(5) = "x " ->> "x":right_pad(5, "ABC") +>> "x".right_pad(5, "ABC") = "xABCA" ``` @@ -922,13 +922,13 @@ the text. **Example:** ```tomo ->> "hello":slice(2, 3) +>> "hello".slice(2, 3) = "el" ->> "hello":slice(to=-2) +>> "hello".slice(to=-2) = "hell" ->> "hello":slice(from=2) +>> "hello".slice(from=2) = "ello" ``` @@ -951,10 +951,10 @@ An array of subtexts resulting from the split. **Example:** ```tomo ->> "one,two,,three":split(",") +>> "one,two,,three".split(",") = ["one", "two", "", "three"] ->> "abc":split() +>> "abc".split() = ["a", "b", "c"] ``` @@ -978,7 +978,7 @@ An array of subtexts resulting from the split. **Example:** ```tomo ->> "one, two,,three":split_any(", ") +>> "one, two,,three".split_any(", ") = ["one", "two", "three"] ``` @@ -999,7 +999,7 @@ func starts_with(text: Text, prefix: Text -> Bool) **Example:** ```tomo ->> "hello world":starts_with("hello") +>> "hello world".starts_with("hello") = yes ``` @@ -1020,11 +1020,11 @@ The text in title case. **Example:** ```tomo ->> "amélie":title() +>> "amélie".title() = "Amélie" # In Turkish, uppercase "i" is "İ" ->> "i":title(language="tr_TR") +>> "i".title(language="tr_TR") = "İ" ``` @@ -1048,10 +1048,10 @@ the text. **Example:** ```tomo ->> "goodbye":to(3) +>> "goodbye".to(3) = "goo" ->> "goodbye":to(-2) +>> "goodbye".to(-2) = "goodby" ``` @@ -1077,7 +1077,7 @@ replacement text. **Example:** ```tomo ->> "A <tag> & an amperand":translate({ +>> "A <tag> & an amperand".translate({ "&" = "&", "<" = "<", ">" = ">", @@ -1106,13 +1106,13 @@ The text without the trim characters at either end. **Example:** ```tomo ->> " x y z $(\n)":trim() +>> " x y z $(\n)".trim() = "x y z" ->> "one,":trim(",") +>> "one,".trim(",") = "one" ->> " xyz ":trim(right=no) +>> " xyz ".trim(right=no) = "xyz " ``` @@ -1133,11 +1133,11 @@ The uppercase version of the text. **Example:** ```tomo ->> "amélie":upper() +>> "amélie".upper() = "AMÉLIE" # In Turkish, uppercase "i" is "İ" ->> "i":upper(language="tr_TR") +>> "i".upper(language="tr_TR") = "İ" ``` @@ -1157,7 +1157,7 @@ An array of 32-bit integer Unicode code points (`[Int32]`). **Example:** ```tomo ->> "Amélie":utf32_codepoints() +>> "Amélie".utf32_codepoints() = [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32] ``` @@ -1182,9 +1182,9 @@ An integer representing the display width of the text. **Example:** ```tomo ->> "Amélie":width() +>> "Amélie".width() = 6 ->> "🤠":width() +>> "🤠".width() = 2 ``` @@ -1206,9 +1206,9 @@ prefix is not present. **Example:** ```tomo ->> "foo:baz":without_prefix("foo:") +>> "foo:baz".without_prefix("foo:") = "baz" ->> "qux":without_prefix("foo:") +>> "qux".without_prefix("foo:") = "qux" ``` @@ -1230,8 +1230,8 @@ suffix is not present. **Example:** ```tomo ->> "baz.foo":without_suffix(".foo") +>> "baz.foo".without_suffix(".foo") = "baz" ->> "qux":without_suffix(".foo") +>> "qux".without_suffix(".foo") = "qux" ``` diff --git a/examples/base64/base64.tm b/examples/base64/base64.tm index e2f5ea19..714e223b 100644 --- a/examples/base64/base64.tm +++ b/examples/base64/base64.tm @@ -1,6 +1,6 @@ # Base 64 encoding and decoding -_enc := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/":bytes() +_enc := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".bytes() _EQUAL_BYTE := Byte(0x3D) @@ -25,7 +25,7 @@ _dec : [Byte] = [ lang Base64: func parse(text:Text -> Base64?): - return Base64.from_bytes(text:bytes()) + return Base64.from_bytes(text.bytes()) func from_bytes(bytes:[Byte] -> Base64?): output := &[Byte(0) for _ in bytes.length * 4 / 3 + 4] @@ -61,10 +61,10 @@ lang Base64: return Base64.from_text(Text.from_bytes(output[]) or return none) func decode_text(b64:Base64 -> Text?): - return Text.from_bytes(b64:decode_bytes() or return none) + return Text.from_bytes(b64.decode_bytes() or return none) func decode_bytes(b64:Base64 -> [Byte]?): - bytes := b64.text:bytes() + bytes := b64.text.bytes() output := &[Byte(0) for _ in bytes.length/4 * 3] src := Int64(1) dest := Int64(1) @@ -83,14 +83,14 @@ lang Base64: dest += 3 while output[-1] == 0xFF: - output[] = output:to(-2) + output[] = output.to(-2) return output[] func main(input=(/dev/stdin), decode=no): if decode: - b := Base64.from_text(input:read()!) - say(b:decode_text()!) + b := Base64.from_text(input.read()!) + say(b.decode_text()!) else: - text := input:read()! + text := input.read()! say(Base64.parse(text)!.text) diff --git a/examples/colorful/README.md b/examples/colorful/README.md index b504a730..c03ace24 100644 --- a/examples/colorful/README.md +++ b/examples/colorful/README.md @@ -60,5 +60,5 @@ use colorful $Colorful" @(blue:Welcome to the @(bold:party)!) We have @(green,bold:colors)! -":print() +".print() ``` diff --git a/examples/colorful/colorful.tm b/examples/colorful/colorful.tm index 57a2a3d6..6769841d 100644 --- a/examples/colorful/colorful.tm +++ b/examples/colorful/colorful.tm @@ -11,7 +11,7 @@ use patterns lang Colorful: convert(text:Text -> Colorful): - text = text:translate({"@"="@(at)", "("="@(lparen)", ")"="@(rparen)"}) + text = text.translate({"@"="@(at)", "("="@(lparen)", ")"="@(rparen)"}) return Colorful.from_text(text) convert(i:Int -> Colorful): return Colorful.from_text("$i") @@ -21,13 +21,13 @@ lang Colorful: return CSI ++ "m" ++ _for_terminal(c, _TermState()) func print(c:Colorful, newline=yes): - say(c:for_terminal(), newline=newline) + say(c.for_terminal(), newline=newline) func main(texts:[Text], files:[Path]=[], by_line=no): for i,text in texts: colorful := Colorful.from_text(text) - colorful:print(newline=no) + colorful.print(newline=no) if i == texts.length: say("") else: say(" ", newline=no) @@ -36,27 +36,27 @@ func main(texts:[Text], files:[Path]=[], by_line=no): for file in files: if by_line: - for line in file:by_line() or exit("Could not read file: $file"): + for line in file.by_line() or exit("Could not read file: $file"): colorful := Colorful.from_text(line) - colorful:print() + colorful.print() else: - colorful := Colorful.from_text(file:read() or exit("Could not read file: $file")) - colorful:print(newline=no) + colorful := Colorful.from_text(file.read() or exit("Could not read file: $file")) + colorful.print(newline=no) func _for_terminal(c:Colorful, state:_TermState -> Text): - return c.text:map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], state)) + return c.text.map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], state)) enum _Color(Default, Bright(color:Int16), Color8Bit(color:Int16), Color24Bit(color:Int32)): func from_text(text:Text -> _Color?): - if text:matches_pattern($Pat/#{3-6 hex}/): - hex := text:from(2) + if text.matches_pattern($Pat/#{3-6 hex}/): + hex := text.from(2) return none unless hex.length == 3 or hex.length == 6 if hex.length == 3: hex = hex[1]++hex[1]++hex[2]++hex[2]++hex[3]++hex[3] n := Int32.parse("0x" ++ hex) or return none return Color24Bit(n) - else if text:matches_pattern($Pat/{1-3 digit}/): + else if text.matches_pattern($Pat/{1-3 digit}/): n := Int16.parse(text) or return none if n >= 0 and n <= 255: return Color8Bit(n) else if text == "black": return _Color.Color8Bit(0) @@ -118,20 +118,20 @@ enum _Color(Default, Bright(color:Int16), Color8Bit(color:Int16), Color24Bit(col func _toggle(sequences:&[Text], cur,new:Bool, apply,unapply:Text; inline): if new and not cur: - sequences:insert(apply) + sequences.insert(apply) else if cur and not new: - sequences:insert(unapply) + sequences.insert(unapply) func _toggle2(sequences:&[Text], cur1,cur2,new1,new2:Bool, apply1,apply2,unapply:Text; inline): return if new1 == cur1 and new2 == cur2 if (cur1 and not new1) or (cur2 and not new2): # Gotta wipe at least one - sequences:insert(unapply) + sequences.insert(unapply) cur1, cur2 = no, no # Wiped out if new1 and not cur1: - sequences:insert(apply1) + sequences.insert(apply1) if new2 and not cur2: - sequences:insert(apply2) + sequences.insert(apply2) struct _TermState( bold=no, dim=no, italic=no, underline=no, blink=no, @@ -154,35 +154,35 @@ struct _TermState( _toggle2(sequences, old.subscript, old.subscript, new.superscript, new.superscript, "73", "74", "75") if new.bg != old.bg: - sequences:insert(new.bg:bg()) + sequences.insert(new.bg.bg()) if new.fg != old.fg: - sequences:insert(new.fg:fg()) + sequences.insert(new.fg.fg()) if new.underline_color != old.underline_color: - sequences:insert(new.underline_color:underline()) + sequences.insert(new.underline_color.underline()) if sequences.length == 0: return "" - return CSI ++ ";":join(sequences) ++ "m" + return CSI ++ ";".join(sequences) ++ "m" func _add_ansi_sequences(text:Text, prev_state:_TermState -> Text): if text == "lparen": return "(" else if text == "rparen": return ")" else if text == "@" or text == "at": return "@" parts := ( - text:pattern_captures($Pat/{0+..}:{0+..}/) or + text.pattern_captures($Pat/{0+..}:{0+..}/) or return "@("++_for_terminal(Colorful.from_text(text), prev_state)++")" ) - attributes := parts[1]:split_pattern($Pat/{0+space},{0+space}/) + attributes := parts[1].split_pattern($Pat/{0+space},{0+space}/) new_state := prev_state for attr in attributes: - if attr:starts_with("fg="): - new_state.fg = _Color.from_text(attr:from(4))! - else if attr:starts_with("bg="): - new_state.bg = _Color.from_text(attr:from(4))! - else if attr:starts_with("ul="): - new_state.underline_color = _Color.from_text(attr:from(4))! + if attr.starts_with("fg="): + new_state.fg = _Color.from_text(attr.from(4))! + else if attr.starts_with("bg="): + new_state.bg = _Color.from_text(attr.from(4))! + else if attr.starts_with("ul="): + new_state.underline_color = _Color.from_text(attr.from(4))! else if color := _Color.from_text(attr): new_state.fg = color else if attr == "b" or attr == "bold": @@ -212,7 +212,7 @@ func _add_ansi_sequences(text:Text, prev_state:_TermState -> Text): else: fail("Invalid attribute: '$attr'") - result := prev_state:apply(new_state) - result ++= parts[2]:map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], new_state)) - result ++= new_state:apply(prev_state) + result := prev_state.apply(new_state) + result ++= parts[2].map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], new_state)) + result ++= new_state.apply(prev_state) return result diff --git a/examples/commands/commands.tm b/examples/commands/commands.tm index 170e45bc..6817fca1 100644 --- a/examples/commands/commands.tm +++ b/examples/commands/commands.tm @@ -3,8 +3,8 @@ use ./commands.c use -lunistring -extern run_command:func(exe:Text, args:[Text], env:{Text=Text}, input:[Byte]?, output:&[Byte]?, error:&[Byte]? -> Int32) -extern command_by_line:func(exe:Text, args:[Text], env:{Text=Text} -> func(->Text?)?) +extern run_command : func(exe:Text, args:[Text], env:{Text=Text}, input:[Byte]?, output:&[Byte]?, error:&[Byte]? -> Int32) +extern command_by_line : func(exe:Text, args:[Text], env:{Text=Text} -> func(->Text?)?) enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed): func succeeded(e:ExitType -> Bool): @@ -12,7 +12,7 @@ enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed): else: return no func or_fail(e:ExitType, message:Text?=none): - if not e:succeeded(): + if not e.succeeded(): fail(message or "Program failed: $e") struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType): @@ -28,7 +28,7 @@ struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType): if status == 0: if text := Text.from_bytes(r.stdout): if trim_newline: - text = text:without_suffix(\n) + text = text.without_suffix(\n) return text else: return none return none @@ -52,7 +52,7 @@ struct Command(command:Text, args:[Text]=[], env:{Text=Text}={}): func result(command:Command, input="", input_bytes:[Byte]=[] -> ProgramResult): if input.length > 0: - (&input_bytes):insert_all(input:bytes()) + (&input_bytes).insert_all(input.bytes()) stdout : [Byte] = [] stderr : [Byte] = [] @@ -78,10 +78,10 @@ struct Command(command:Text, args:[Text]=[], env:{Text=Text}={}): return ExitType.Failed func get_output(command:Command, input="", trim_newline=yes -> Text?): - return command:result(input=input):output_text(trim_newline=trim_newline) + return command.result(input=input).output_text(trim_newline=trim_newline) func get_output_bytes(command:Command, input="", input_bytes:[Byte]=[] -> [Byte]?): - result := command:result(input=input, input_bytes=input_bytes) + result := command.result(input=input, input_bytes=input_bytes) when result.exit_type is Exited(status): if status == 0: return result.stdout return none diff --git a/examples/game/game.tm b/examples/game/game.tm index 36ef14ff..ce08c329 100644 --- a/examples/game/game.tm +++ b/examples/game/game.tm @@ -5,24 +5,24 @@ use ./world.tm func main(map=(./map.txt)): InitWindow(1600, 900, CString("raylib [core] example - 2d camera")) - map_contents := map:read() or exit("Could not find the game map: $map") + map_contents := map.read() or exit("Could not find the game map: $map") world := @World( player=@Player(Vector2(0,0), Vector2(0,0)), goal=@Box(Vector2(0,0), Vector2(50,50), color=Color(0x10,0xa0,0x10)), boxes=@[], ) - world:load_map(map_contents) + world.load_map(map_contents) SetTargetFPS(60) while not WindowShouldClose(): dt := GetFrameTime() - world:update(dt) + world.update(dt) BeginDrawing() ClearBackground(Color(0xCC, 0xCC, 0xCC, 0xFF)) - world:draw() + world.draw() EndDrawing() CloseWindow() diff --git a/examples/game/player.tm b/examples/game/player.tm index f73dcf6a..7f14f51e 100644 --- a/examples/game/player.tm +++ b/examples/game/player.tm @@ -16,11 +16,11 @@ struct Player(pos,prev_pos:Vector2): target_y := inline C:Num32 { (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) } - target_vel := Vector2(target_x, target_y):norm() * Player.WALK_SPEED + target_vel := Vector2(target_x, target_y).norm() * Player.WALK_SPEED vel := (p.pos - p.prev_pos)/World.DT vel *= Player.FRICTION - vel = vel:mix(target_vel, Player.ACCEL) + vel = vel.mix(target_vel, Player.ACCEL) p.prev_pos, p.pos = p.pos, p.pos + World.DT*vel diff --git a/examples/game/raylib.tm b/examples/game/raylib.tm index ad248e4f..5e58e996 100644 --- a/examples/game/raylib.tm +++ b/examples/game/raylib.tm @@ -27,37 +27,37 @@ struct Vector2(x,y:Num32; extern): func divided_by(v:Vector2, divisor:Num32->Vector2; inline): return Vector2(v.x/divisor, v.y/divisor) func length(v:Vector2->Num32; inline): - return (v.x*v.x + v.y*v.y):sqrt() + return (v.x*v.x + v.y*v.y).sqrt() func dist(a,b:Vector2->Num32; inline): - return a:minus(b):length() + return a.minus(b).length() func angle(v:Vector2->Num32; inline): return Num32.atan2(v.y, v.x) func norm(v:Vector2->Vector2; inline): if v.x == 0 and v.y == 0: return v - len := v:length() + len := v.length() return Vector2(v.x/len, v.y/len) func rotated(v:Vector2, radians:Num32 -> Vector2): - cos := radians:cos() or return v - sin := radians:sin() or return v + cos := radians.cos() or return v + sin := radians.sin() or return v return Vector2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) func mix(a,b:Vector2, amount:Num32 -> Vector2): return Vector2( - amount:mix(a.x, b.x), - amount:mix(a.y, b.y), + amount.mix(a.x, b.x), + amount.mix(a.y, b.y), ) -extern InitWindow:func(width:Int32, height:Int32, title:CString) -extern SetTargetFPS:func(fps:Int32) -extern WindowShouldClose:func(->Bool) -extern GetFrameTime:func(->Num32) -extern BeginDrawing:func() -extern EndDrawing:func() -extern CloseWindow:func() -extern ClearBackground:func(color:Color) -extern DrawRectangle:func(x,y,width,height:Int32, color:Color) -extern DrawRectangleRec:func(rec:Rectangle, color:Color) -extern DrawRectangleV:func(pos:Vector2, size:Vector2, color:Color) -extern DrawText:func(text:CString, x,y:Int32, text_height:Int32, color:Color) -extern GetScreenWidth:func(->Int32) -extern GetScreenHeight:func(->Int32) +extern InitWindow : func(width:Int32, height:Int32, title:CString) +extern SetTargetFPS : func(fps:Int32) +extern WindowShouldClose : func(->Bool) +extern GetFrameTime : func(->Num32) +extern BeginDrawing : func() +extern EndDrawing : func() +extern CloseWindow : func() +extern ClearBackground : func(color:Color) +extern DrawRectangle : func(x,y,width,height:Int32, color:Color) +extern DrawRectangleRec : func(rec:Rectangle, color:Color) +extern DrawRectangleV : func(pos:Vector2, size:Vector2, color:Color) +extern DrawText : func(text:CString, x,y:Int32, text_height:Int32, color:Color) +extern GetScreenWidth : func(->Int32) +extern GetScreenHeight : func(->Int32) diff --git a/examples/game/world.tm b/examples/game/world.tm index 76acac6b..e8255ab8 100644 --- a/examples/game/world.tm +++ b/examples/game/world.tm @@ -44,11 +44,11 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won= func update(w:@World, dt:Num32): w.dt_accum += dt while w.dt_accum > 0: - w:update_once() + w.update_once() w.dt_accum -= World.DT func update_once(w:@World): - w.player:update() + w.player.update() if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vector2(0,0): w.won = yes @@ -60,24 +60,24 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won= func draw(w:@World): for b in w.boxes: - b:draw() - w.goal:draw() - w.player:draw() + b.draw() + w.goal.draw() + w.player.draw() if w.won: DrawText(CString("WINNER"), GetScreenWidth()/Int32(2)-Int32(48*3), GetScreenHeight()/Int32(2)-Int32(24), 48, Color(0,0,0)) func load_map(w:@World, map:Text): - if map:has("[]"): - map = map:translate({"[]"="#", "@ "="@", " "=" "}) + if map.has("[]"): + map = map.translate({"[]"="#", "@ "="@", " "=" "}) w.boxes = @[] box_size := Vector2(50., 50.) - for y,line in map:lines(): - for x,cell in line:split(): + for y,line in map.lines(): + for x,cell in line.split(): if cell == "#": pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) box := @Box(pos, size=box_size, color=Color(0x80,0x80,0x80)) - w.boxes:insert(box) + w.boxes.insert(box) else if cell == "@": pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) pos += box_size/Num32(2) - Player.SIZE/Num32(2) diff --git a/examples/http-server/connection-queue.tm b/examples/http-server/connection-queue.tm index 84f92362..3c8058d5 100644 --- a/examples/http-server/connection-queue.tm +++ b/examples/http-server/connection-queue.tm @@ -5,21 +5,21 @@ func _assert_success(name:Text, val:Int32; inline): struct ConnectionQueue(_connections:@[Int32]=@[], _mutex=pthread_mutex_t.new(), _cond=pthread_cond_t.new()): func enqueue(queue:ConnectionQueue, connection:Int32): - queue._mutex:lock() - queue._connections:insert(connection) - queue._mutex:unlock() - queue._cond:signal() + queue._mutex.lock() + queue._connections.insert(connection) + queue._mutex.unlock() + queue._cond.signal() func dequeue(queue:ConnectionQueue -> Int32): conn : Int32? = none - queue._mutex:lock() + queue._mutex.lock() while queue._connections.length == 0: - queue._cond:wait(queue._mutex) + queue._cond.wait(queue._mutex) - conn = queue._connections:pop(1) - queue._mutex:unlock() - queue._cond:signal() + conn = queue._connections.pop(1) + queue._mutex.unlock() + queue._cond.signal() return conn! diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm index 1a09b601..649a9e12 100644 --- a/examples/http-server/http-server.tm +++ b/examples/http-server/http-server.tm @@ -19,9 +19,9 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th connections := ConnectionQueue() workers : &[@pthread_t] = &[] for i in num_threads: - workers:insert(pthread_t.new(func(): + workers.insert(pthread_t.new(func(): repeat: - connection := connections:dequeue() + connection := connections.dequeue() request_text := inline C : Text { Text_t request = EMPTY_TEXT; char buf[1024] = {}; @@ -35,7 +35,7 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th } request := HTTPRequest.from_text(request_text) or skip - response := handler(request):bytes() + response := handler(request).bytes() inline C { if (_$response.stride != 1) Array$compact(&_$response, 1); @@ -65,26 +65,26 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th repeat: conn := inline C : Int32 { accept(_$sock, NULL, NULL) } stop if conn < 0 - connections:enqueue(conn) + connections.enqueue(conn) say("Shutting down...") for w in workers: - w:cancel() + w.cancel() struct HTTPRequest(method:Text, path:Text, version:Text, headers:[Text], body:Text): func from_text(text:Text -> HTTPRequest?): - m := text:pattern_captures($Pat'{word} {..} HTTP/{..}{crlf}{..}') or return none + m := text.pattern_captures($Pat'{word} {..} HTTP/{..}{crlf}{..}') or return none method := m[1] - path := m[2]:replace_pattern($Pat'{2+ /}', '/') + path := m[2].replace_pattern($Pat'{2+ /}', '/') version := m[3] - rest := m[-1]:pattern_captures($Pat/{..}{2 crlf}{0+ ..}/) or return none - headers := rest[1]:split_pattern($Pat/{crlf}/) + rest := m[-1].pattern_captures($Pat/{..}{2 crlf}{0+ ..}/) or return none + headers := rest[1].split_pattern($Pat/{crlf}/) body := rest[-1] return HTTPRequest(method, path, version, headers, body) struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{Text=Text}={}): func bytes(r:HTTPResponse -> [Byte]): - body_bytes := r.body:bytes() + body_bytes := r.body.bytes() extra_headers := (++: "$k: $v$(\r\n)" for k,v in r.headers) or "" return " HTTP/1.1 $(r.status) OK$\r @@ -93,10 +93,10 @@ struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{T Connection: close$\r $extra_headers $\r$\n - ":bytes() ++ body_bytes + ".bytes() ++ body_bytes func _content_type(file:Path -> Text): - when file:extension() is "html": return "text/html" + when file.extension() is "html": return "text/html" is "tm": return "text/html" is "js": return "text/javascript" is "css": return "text/css" @@ -105,30 +105,30 @@ func _content_type(file:Path -> Text): enum RouteEntry(ServeFile(file:Path), Redirect(destination:Text)): func respond(entry:RouteEntry, request:HTTPRequest -> HTTPResponse): when entry is ServeFile(file): - body := if file:can_execute(): - Command(Text(file)):get_output()! + body := if file.can_execute(): + Command(Text(file)).get_output()! else: - file:read()! + file.read()! return HTTPResponse(body, content_type=_content_type(file)) is Redirect(destination): return HTTPResponse("Found", 302, headers={"Location"=destination}) func load_routes(directory:Path -> {Text=RouteEntry}): routes : &{Text=RouteEntry} = &{} - for file in (directory ++ (./*)):glob(): - skip unless file:is_file() - contents := file:read() or skip - server_path := "/" ++ "/":join(file:relative_to(directory).components) - if file:base_name() == "index.html": - canonical := server_path:without_suffix("index.html") + for file in (directory ++ (./*)).glob(): + skip unless file.is_file() + contents := file.read() or skip + server_path := "/" ++ "/".join(file.relative_to(directory).components) + if file.base_name() == "index.html": + canonical := server_path.without_suffix("index.html") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) - else if file:extension() == "html": - canonical := server_path:without_suffix(".html") + else if file.extension() == "html": + canonical := server_path.without_suffix(".html") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) - else if file:extension() == "tm": - canonical := server_path:without_suffix(".tm") + else if file.extension() == "tm": + canonical := server_path.without_suffix(".tm") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) else: @@ -142,7 +142,7 @@ func main(directory:Path, port=Int32(8080)): serve(port, func(request:HTTPRequest): if handler := routes[request.path]: - return handler:respond(request) + return handler.respond(request) else: return HTTPResponse("Not found!", 404) ) diff --git a/examples/http-server/sample-site/random.tm b/examples/http-server/sample-site/random.tm index 502618e8..29b93be7 100755 --- a/examples/http-server/sample-site/random.tm +++ b/examples/http-server/sample-site/random.tm @@ -11,7 +11,7 @@ func main(): </head> <body> <h1>Random Number</h1> - Your random number is: $(random:int(1,100)) + Your random number is: $(random.int(1,100)) </body> </html> ") diff --git a/examples/http/http.tm b/examples/http/http.tm index 12d9978e..048ffaab 100644 --- a/examples/http/http.tm +++ b/examples/http/http.tm @@ -10,7 +10,7 @@ enum _Method(GET, POST, PUT, PATCH, DELETE) func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPResponse): chunks : @[Text] = @[] save_chunk := func(chunk:CString, size:Int64, n:Int64): - chunks:insert(inline C:Text { + chunks.insert(inline C:Text { Text$format("%.*s", _$size*_$n, _$chunk) }) return n*size @@ -79,7 +79,7 @@ func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPRespon curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_$code); } - return HTTPResponse(Int(code), "":join(chunks)) + return HTTPResponse(Int(code), "".join(chunks)) func get(url:Text, headers:[Text]=[] -> HTTPResponse): return _send(GET, url, none, headers) @@ -99,7 +99,7 @@ func delete(url:Text, data:Text?=none, headers=["Content-Type: application/json" func main(): say("GET:") say(get("https://httpbin.org/get").body) - say("Waiting 1sec... + say("Waiting 1sec...") sleep(1) say("POST:") say(post("https://httpbin.org/post", `{"key": "value"}`).body) diff --git a/examples/ini/ini.tm b/examples/ini/ini.tm index cee33f3c..3fa4c834 100644 --- a/examples/ini/ini.tm +++ b/examples/ini/ini.tm @@ -10,29 +10,29 @@ _HELP := " " func parse_ini(path:Path -> {Text={Text=Text}}): - text := path:read() or exit("Could not read INI file: $\[31;1]$(path)$\[]") + text := path.read() or exit("Could not read INI file: $\[31;1]$(path)$\[]") sections : @{Text=@{Text=Text}} = @{} current_section : @{Text=Text} = @{} # Line wraps: - text = text:replace_pattern($Pat/\{1 nl}{0+space}/, " ") + text = text.replace_pattern($Pat/\{1 nl}{0+space}/, " ") - for line in text:lines(): - line = line:trim() - skip if line:starts_with(";") or line:starts_with("#") - if line:matches_pattern($Pat/[?]/): - section_name := line:replace($Pat/[?]/, "\1"):trim():lower() + for line in text.lines(): + line = line.trim() + skip if line.starts_with(";") or line.starts_with("#") + if line.matches_pattern($Pat/[?]/): + section_name := line.replace($Pat/[?]/, "\1").trim().lower() current_section = @{} sections[section_name] = current_section - else if line:matches_pattern($Pat/{..}={..}/): - key := line:replace_pattern($Pat/{..}={..}/, "\1"):trim():lower() - value := line:replace_pattern($Pat/{..}={..}/, "\2"):trim() + else if line.matches_pattern($Pat/{..}={..}/): + key := line.replace_pattern($Pat/{..}={..}/, "\1").trim().lower() + value := line.replace_pattern($Pat/{..}={..}/, "\2").trim() current_section[key] = value return {k=v[] for k,v in sections[]} func main(path:Path, key:Text?): - keys := (key or ""):split($|/|) + keys := (key or "").split($|/|) if keys.length > 2: exit(" Too many arguments! @@ -44,18 +44,18 @@ func main(path:Path, key:Text?): say("$data") return - section := keys[1]:lower() + section := keys[1].lower() section_data := data[section] or exit(" Invalid section name: $\[31;1]$section$\[] - Valid names: $\[1]$(", ":join([k:quoted() for k in data.keys]))$\[] + Valid names: $\[1]$(", ".join([k.quoted() for k in data.keys]))$\[] ") if keys.length < 2 or keys[2] == '*': say("$section_data") return - section_key := keys[2]:lower() + section_key := keys[2].lower() value := section_data[section_key] or exit(" Invalid key: $\[31;1]$section_key$\[] - Valid keys: $\[1]$(", ":join([s:quoted() for s in section_data.keys]))$\[] + Valid keys: $\[1]$(", ".join([s.quoted() for s in section_data.keys]))$\[] ") say(value) diff --git a/examples/learnxiny.tm b/examples/learnxiny.tm index ecb1140f..6a3b7e21 100644 --- a/examples/learnxiny.tm +++ b/examples/learnxiny.tm @@ -160,18 +160,18 @@ func main(): # Sets are similar to tables, but they represent an unordered collection of # unique values: set := {10, 20, 30} - >> set:has(20) + >> set.has(20) = yes - >> set:has(999) + >> set.has(999) = no # You can do some operations on sets: other_set := {30, 40, 50} - >> set:with(other_set) + >> set.with(other_set) = {10, 20, 30, 40, 50} - >> set:without(other_set) + >> set.without(other_set) = {10, 20} - >> set:overlap(other_set) + >> set.overlap(other_set) = {30} # So far, the datastructures that have been discussed are all *immutable*, @@ -184,7 +184,7 @@ func main(): = @[999, 20, 30] # To call a method, you must use ":" and the name of the method: - my_arr:sort() + my_arr.sort() >> my_arr = @[20, 30, 999] @@ -197,7 +197,7 @@ func main(): # location. Later, a new value might end up there, but the photograph will # remain unchanged. snapshot := my_arr[] - my_arr:insert(1000) + my_arr.insert(1000) >> my_arr = @[20, 30, 999, 1000] >> snapshot @@ -275,7 +275,7 @@ func demo_structs(): = 30 # Calling methods: - alice:say_age() + alice.say_age() # You can call static methods by using the class name and ".": >> Person.get_cool_name() diff --git a/examples/log/log.tm b/examples/log/log.tm index 3aa45e7a..7375d5f6 100644 --- a/examples/log/log.tm +++ b/examples/log/log.tm @@ -13,33 +13,33 @@ func _timestamp(->Text): strftime(str, 20, "%F %T", tm_info); str } - return c_str:as_text() + return c_str.as_text() func info(text:Text, newline=yes): say("$\[2]⚫ $text$\[]", newline) for file in logfiles: - file:append("$(_timestamp()) [info] $text$\n") + file.append("$(_timestamp()) [info] $text$\n") func debug(text:Text, newline=yes): say("$\[32]🟢 $text$\[]", newline) for file in logfiles: - file:append("$(_timestamp()) [debug] $text$\n") + file.append("$(_timestamp()) [debug] $text$\n") func warn(text:Text, newline=yes): say("$\[33;1]🟡 $text$\[]", newline) for file in logfiles: - file:append("$(_timestamp()) [warn] $text$\n") + file.append("$(_timestamp()) [warn] $text$\n") func error(text:Text, newline=yes): say("$\[31;1]🔴 $text$\[]", newline) for file in logfiles: - file:append("$(_timestamp()) [error] $text$\n") + file.append("$(_timestamp()) [error] $text$\n") func add_logfile(file:Path): - logfiles:add(file) + logfiles.add(file) func remove_logfile(file:Path): - logfiles:remove(file) + logfiles.remove(file) func main(): add_logfile((./log.txt)) diff --git a/examples/patterns/README.md b/examples/patterns/README.md index 9e9d8601..8e2a1ff8 100644 --- a/examples/patterns/README.md +++ b/examples/patterns/README.md @@ -123,7 +123,7 @@ be automatically escaped using the `{1 ?}` rule described above: # This is: `{ 1{ }` (one open brace) followed by the literal text "..xxx}" # No error: ->> some_text:find($/$user_input/) +>> some_text.find($/$user_input/) = 0 ``` @@ -168,7 +168,7 @@ An iterator function that yields `PatternMatch` objects one at a time. **Example:** ```tomo text := "one, two, three" -for word in text:by_pattern($Pat"{id}"): +for word in text.by_pattern($Pat"{id}"): say(word.text) ``` @@ -190,7 +190,7 @@ An iterator function that yields text segments. **Example:** ```tomo text := "one two three" -for word in text:by_pattern_split($Pat"{whitespace}"): +for word in text.by_pattern_split($Pat"{whitespace}"): say(word.text) ``` @@ -211,7 +211,7 @@ func each_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch), recursive=yes **Example:** ```tomo text := "one two three" -text:each_pattern($Pat"{id}", func(m:PatternMatch): +text.each_pattern($Pat"{id}", func(m:PatternMatch): say(m.txt) ) ``` @@ -234,7 +234,7 @@ An array of `PatternMatch` objects. **Example:** ```tomo text := "one! two three!" ->> text:find_patterns($Pat"{id}!") +>> text.find_patterns($Pat"{id}!") = [PatternMatch(text="one!", index=1, captures=["one"]), PatternMatch(text="three!", index=10, captures=["three"])] ``` @@ -256,7 +256,7 @@ func has_pattern(text:Text, pattern:Pat -> Bool) **Example:** ```tomo text := "...okay..." ->> text:has_pattern($Pat"{id}") +>> text.has_pattern($Pat"{id}") = yes ``` @@ -281,7 +281,7 @@ A new text with the transformed matches. ```tomo text := "I have #apples and #oranges and #plums" fruits := {"apples"=4, "oranges"=5} ->> text:map_pattern($Pat'#{id}', func(match:PatternMatch): +>> text.map_pattern($Pat'#{id}', func(match:PatternMatch): fruit := match.captures[1] "$(fruits[fruit] or 0) $fruit" ) @@ -305,9 +305,9 @@ func matches_pattern(text:Text, pattern:Pat -> Bool) **Example:** ```tomo ->> "Hello!!!":matches_pattern($Pat"{id}") +>> "Hello!!!".matches_pattern($Pat"{id}") = no ->> "Hello":matches_pattern($Pat"{id}") +>> "Hello".matches_pattern($Pat"{id}") = yes ``` @@ -329,9 +329,9 @@ not match the pattern. **Example:** ```tomo ->> "123 boxes":pattern_captures($Pat"{int} {id}") +>> "123 boxes".pattern_captures($Pat"{int} {id}") = ["123", "boxes"]? ->> "xxx":pattern_captures($Pat"{int} {id}") +>> "xxx".pattern_captures($Pat"{int} {id}") = none ``` @@ -355,19 +355,19 @@ A new text with replacements applied. **Example:** ```tomo ->> "I have 123 apples and 456 oranges":replace_pattern($Pat"{int}", "some") +>> "I have 123 apples and 456 oranges".replace_pattern($Pat"{int}", "some") = "I have some apples and some oranges" ->> "I have 123 apples and 456 oranges":replace_pattern($Pat"{int}", "(@1)") +>> "I have 123 apples and 456 oranges".replace_pattern($Pat"{int}", "(@1)") = "I have (123) apples and (456) oranges" ->> "I have 123 apples and 456 oranges":replace_pattern($Pat"{int}", "(?1)", backref="?") +>> "I have 123 apples and 456 oranges".replace_pattern($Pat"{int}", "(?1)", backref="?") = "I have (123) apples and (456) oranges" ->> "bad(fn(), bad(notbad))":replace_pattern($Pat"bad(?)", "good(@1)") +>> "bad(fn(), bad(notbad))".replace_pattern($Pat"bad(?)", "good(@1)") = "good(fn(), good(notbad))" ->> "bad(fn(), bad(notbad))":replace_pattern($Pat"bad(?)", "good(@1)", recursive=no) +>> "bad(fn(), bad(notbad))".replace_pattern($Pat"bad(?)", "good(@1)", recursive=no) = "good(fn(), bad(notbad))" ``` @@ -388,7 +388,7 @@ An array of text segments. **Example:** ```tomo ->> "one two three":split_pattern($Pat"{whitespace}") +>> "one two three".split_pattern($Pat"{whitespace}") = ["one", "two", "three"] ``` @@ -412,7 +412,7 @@ A new text with all specified replacements applied. **Example:** ```tomo >> text := "foo(x, baz(1))" ->> text:translate_patterns({ +>> text.translate_patterns({ $Pat"{id}(?)"="call(fn('@1'), @2)", $Pat"{id}"="var('@1')", $Pat"{int}"="int(@1)", @@ -439,6 +439,6 @@ The trimmed text. **Example:** ```tomo ->> "123abc456":trim_pattern($Pat"{digit}") +>> "123abc456".trim_pattern($Pat"{digit}") = "abc" ``` diff --git a/examples/pthreads/pthreads.tm b/examples/pthreads/pthreads.tm index fb79e822..3be052b4 100644 --- a/examples/pthreads/pthreads.tm +++ b/examples/pthreads/pthreads.tm @@ -69,18 +69,18 @@ struct IntQueue(_queue:@[Int], _mutex:@pthread_mutex_t, _cond:@pthread_cond_t): return IntQueue(@initial, pthread_mutex_t.new(), pthread_cond_t.new()) func give(q:IntQueue, n:Int): - do: q._mutex:lock() - q._queue:insert(n) - q._mutex:unlock() - q._cond:signal() + do: q._mutex.lock() + q._queue.insert(n) + q._mutex.unlock() + q._cond.signal() func take(q:IntQueue -> Int): - do: q._mutex:lock() - n := q._queue:pop(1) + do: q._mutex.lock() + n := q._queue.pop(1) while not n: - q._cond:wait(q._mutex) - n = q._queue:pop(1) - q._mutex:unlock() + q._cond.wait(q._mutex) + n = q._queue.pop(1) + q._mutex.unlock() return n! fail("Unreachable") @@ -90,29 +90,29 @@ func main(): say_mutex := pthread_mutex_t.new() announce := func(speaker:Text, text:Text): - do: say_mutex:lock() + do: say_mutex.lock() say("$\033[2m[$speaker]$\033[m $text") - say_mutex:unlock() + say_mutex.unlock() worker := pthread_t.new(func(): say("I'm in the thread!") repeat: announce("worker", "waiting for job") - job := jobs:take() + job := jobs.take() result := job * 10 announce("worker", "Jobbing $job into $result") - results:give(result) + results.give(result) announce("worker", "Signaled $result") ) for i in 10: announce("boss", "Pushing job $i") - jobs:give(i) + jobs.give(i) announce("boss", "Gave job $i") for i in 10: announce("boss", "Getting result...") - result := results:take() + result := results.take() announce("boss", "Got result $result") - >> worker:cancel() + >> worker.cancel() diff --git a/examples/random/README.md b/examples/random/README.md index 6233c1ba..d9983ab7 100644 --- a/examples/random/README.md +++ b/examples/random/README.md @@ -17,14 +17,14 @@ a Tomo program launches. This documentation provides details on RNG functions available in the API. Arrays also have some methods which use RNG values: -`array:shuffle()`, `array:shuffled()`, `array:random()`, and `array:sample()`. +`array.shuffle()`, `array.shuffled()`, `array.random()`, and `array.sample()`. - [`func bool(rng: RNG, p: Num = 0.5 -> Bool)`](#bool) - [`func byte(rng: RNG -> Byte)`](#byte) - [`func bytes(rng: RNG, count: Int -> [Byte])`](#bytes) - [`func copy(rng: RNG -> RNG)`](#copy) - [`func int(rng: RNG, min: Int, max: Int -> Int)`](#int`, `int64`, `int32`, `int16`, `int8) -- [`func new(seed: [Byte] = (/dev/urandom):read_bytes(40)! -> RNG)`](#new) +- [`func new(seed: [Byte] = (/dev/urandom).read_bytes(40)! -> RNG)`](#new) - [`func num(rng: RNG, min: Num = 0.0, max: Num = 1.0 -> Num)`](#num`, `num32) ------------- @@ -46,9 +46,9 @@ func bool(rng: RNG, p: Num = 0.5 -> Bool) **Example:** ```tomo ->> random:bool() +>> random.bool() = no ->> random:bool(1.0) +>> random.bool(1.0) = yes ``` @@ -68,7 +68,7 @@ A random byte (0-255). **Example:** ```tomo ->> random:byte() +>> random.byte() = 103[B] ``` @@ -89,7 +89,7 @@ An array of length `count` random bytes with uniform random distribution (0-255) **Example:** ```tomo ->> random:bytes(4) +>> random.bytes(4) = [135[B], 169[B], 103[B], 212[B]] ``` @@ -111,13 +111,13 @@ A copy of the given RNG. **Example:** ```tomo >> rng := RNG.new([]) ->> copy := rng:copy() +>> copy := rng.copy() ->> rng:bytes(10) +>> rng.bytes(10) = [224[B], 102[B], 190[B], 59[B], 251[B], 50[B], 217[B], 170[B], 15[B], 221[B]] # The copy runs in parallel to the original RNG: ->> copy:bytes(10) +>> copy.bytes(10) = [224[B], 102[B], 190[B], 59[B], 251[B], 50[B], 217[B], 170[B], 15[B], 221[B]] ``` @@ -144,7 +144,7 @@ is greater than `max`, an error will be raised. **Example:** ```tomo ->> random:int(1, 10) +>> random.int(1, 10) = 8 ``` @@ -154,7 +154,7 @@ is greater than `max`, an error will be raised. Return a new random number generator. ```tomo -func new(seed: [Byte] = (/dev/urandom):read_bytes(40)! -> RNG) +func new(seed: [Byte] = (/dev/urandom).read_bytes(40)! -> RNG) ``` - `seed`: The seed use for the random number generator. A seed length of 40 @@ -167,7 +167,7 @@ A new random number generator. **Example:** ```tomo >> my_rng := RNG.new([1[B], 2[B], 3[B], 4[B]]) ->> my_rng:bool() +>> my_rng.bool() = yes ``` @@ -191,6 +191,6 @@ A floating point number uniformly chosen from the range `[min, max]` **Example:** ```tomo ->> random:num(1, 10) +>> random.num(1, 10) = 9.512830439975572 ``` diff --git a/examples/random/random.tm b/examples/random/random.tm index 95d2f9fe..1f60aff0 100644 --- a/examples/random/random.tm +++ b/examples/random/random.tm @@ -79,9 +79,9 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret func bool(rng:&RandomNumberGenerator, probability=0.5 -> Bool): if probability == 0.5: - return rng:byte() < 0x80 + return rng.byte() < 0x80 else: - return rng:num(0., 1.) < 0.5 + return rng.num(0., 1.) < 0.5 func int64(rng:&RandomNumberGenerator, min=Int64.min, max=Int64.max -> Int64): fail("Random minimum value $min is larger than the maximum value $max") if min > max @@ -188,7 +188,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret } func num32(rng:&RandomNumberGenerator, min=Num32(0.), max=Num32(1.) -> Num32): - return Num32(rng:num(Num(min), Num(max))) + return Num32(rng.num(Num(min), Num(max))) func int(rng:&RandomNumberGenerator, min:Int, max:Int -> Int): return inline C : Int { @@ -228,13 +228,13 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret func main(): >> rng := RandomNumberGenerator.new() - >> rng:num() - >> rng:num() - >> rng:num() - >> rng:num(0, 100) - >> rng:byte() - >> rng:bytes(20) - # >> rng:int(1, 100) - # >> rng:int(1, 100) - # >> rng:int(1, 100) - # >> rng:int(1, 100) + >> rng.num() + >> rng.num() + >> rng.num() + >> rng.num(0, 100) + >> rng.byte() + >> rng.bytes(20) + # >> rng.int(1, 100) + # >> rng.int(1, 100) + # >> rng.int(1, 100) + # >> rng.int(1, 100) diff --git a/examples/shell/shell.tm b/examples/shell/shell.tm index dd26428f..816d63a2 100644 --- a/examples/shell/shell.tm +++ b/examples/shell/shell.tm @@ -2,16 +2,16 @@ use commands lang Shell: convert(text:Text -> Shell): - return Shell.from_text("'" ++ text:replace($/'/, `'"'"'`) ++ "'") + return Shell.from_text("'" ++ text.replace($/'/, `'"'"'`) ++ "'") convert(texts:[Text] -> Shell): - return Shell.from_text(" ":join([Shell(t).text for t in texts])) + return Shell.from_text(" ".join([Shell(t).text for t in texts])) convert(path:Path -> Shell): - return Shell(Text(path:expand_home())) + return Shell(Text(path.expand_home())) convert(paths:[Path] -> Shell): - return Shell.from_text(" ":join([Shell(Text(p)).text for p in paths])) + return Shell.from_text(" ".join([Shell(Text(p)).text for p in paths])) convert(n:Int -> Shell): return Shell.from_text(Text(n)) convert(n:Int64 -> Shell): return Shell.from_text(Text(n)) @@ -25,20 +25,20 @@ lang Shell: return Command("sh", ["-c", shell.text]) func result(shell:Shell, input="", input_bytes:[Byte]=[] -> ProgramResult): - return shell:command():result(input=input, input_bytes=input_bytes) + return shell.command().result(input=input, input_bytes=input_bytes) func run(shell:Shell -> ExitType): - return shell:command():run() + return shell.command().run() func get_output(shell:Shell, input="", trim_newline=yes -> Text?): - return shell:command():get_output(input=input, trim_newline=trim_newline) + return shell.command().get_output(input=input, trim_newline=trim_newline) func get_output_bytes(shell:Shell, input="", input_bytes:[Byte]=[] -> [Byte]?): - return shell:command():get_output_bytes(input=input, input_bytes=input_bytes) + return shell.command().get_output_bytes(input=input, input_bytes=input_bytes) func by_line(shell:Shell -> func(->Text?)?): - return shell:command():by_line() + return shell.command().by_line() func main(command:Shell): - for line in command:by_line()!: + for line in command.by_line()!: >> line diff --git a/examples/time/time.tm b/examples/time/time.tm index 8664f2e4..4d686ac7 100644 --- a/examples/time/time.tm +++ b/examples/time/time.tm @@ -77,10 +77,10 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): return seconds func minutes_till(t:Time, target:Time -> Num): - return t:seconds_till(target)/60. + return t.seconds_till(target)/60. func hours_till(t:Time, target:Time -> Num): - return t:seconds_till(target)/3600. + return t.seconds_till(target)/3600. func relative(t:Time, relative_to=Time.now(), timezone=Time.local_timezone() -> Text): inline C { @@ -116,17 +116,17 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): func time(t:Time, seconds=no, am_pm=yes, timezone=Time.local_timezone() -> Text): time := if seconds and am_pm: - t:format("%l:%M:%S%P") + t.format("%l:%M:%S%P") else if seconds and not am_pm: - t:format("%T") + t.format("%T") else if not seconds and am_pm: - t:format("%l:%M%P") + t.format("%l:%M%P") else: - t:format("%H:%M") - return time:trim() + t.format("%H:%M") + return time.trim() func date(t:Time, timezone=Time.local_timezone() -> Text): - return t:format("%F") + return t.format("%F") func info(t:Time, timezone=Time.local_timezone() -> TimeInfo): return inline C : TimeInfo { @@ -185,23 +185,23 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): } func _run_tests(): - >> Time.now():format() + >> Time.now().format() >> Time.set_local_timezone("Europe/Paris") - >> Time.now():format() + >> Time.now().format() >> Time.set_local_timezone("America/New_York") - >> Time.now():format() - # >> Time.now():format(timezone="Europe/Paris") - # >> Time.now():format() - # >> Time.now():format("%Y-%m-%d") - # >> Time.new(2023, 11, 5):format() + >> Time.now().format() + # >> Time.now().format(timezone="Europe/Paris") + # >> Time.now().format() + # >> Time.now().format("%Y-%m-%d") + # >> Time.new(2023, 11, 5).format() # >> Time.local_timezone() - # >> Time.new(2023, 11, 5):seconds_till(Time.now()) - # >> Time.new(2023, 11, 5):relative() + # >> Time.new(2023, 11, 5).seconds_till(Time.now()) + # >> Time.new(2023, 11, 5).relative() - # >> Time.now():info() - # >> Time.now():time() - # >> Time.now():date() + # >> Time.now().info() + # >> Time.now().time() + # >> Time.now().date() # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M") # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M", timezone="Europe/Paris") diff --git a/examples/tomo-install/tomo-install.tm b/examples/tomo-install/tomo-install.tm index 0be05619..0a255987 100644 --- a/examples/tomo-install/tomo-install.tm +++ b/examples/tomo-install/tomo-install.tm @@ -12,13 +12,13 @@ _HELP := " func find_urls(path:Path -> [Text]): urls : @[Text] = @[] - if path:is_directory(): - for f in path:children(): - urls:insert_all(find_urls(f)) - else if path:is_file() and path:extension() == ".tm": - for line in path:by_line()!: - if captures := line:pattern_captures($Pat/use{space}{url}/) or line:pattern_captures($Pat/{id}{space}:={space}use{space}{url}/): - urls:insert(captures[-1]) + if path.is_directory(): + for f in path.children(): + urls.insert_all(find_urls(f)) + else if path.is_file() and path.extension() == ".tm": + for line in path.by_line()!: + if captures := line.pattern_captures($Pat/use{space}{url}/) or line.pattern_captures($Pat/{id}{space}:={space}use{space}{url}/): + urls.insert(captures[-1]) return urls func main(paths:[Path]): @@ -27,27 +27,27 @@ func main(paths:[Path]): urls := (++: find_urls(p) for p in paths) or [] - github_token := (~/.config/tomo/github-token):read() + github_token := (~/.config/tomo/github-token).read() - (~/.local/share/tomo/installed):create_directory() - (~/.local/share/tomo/lib):create_directory() + (~/.local/share/tomo/installed).create_directory() + (~/.local/share/tomo/lib).create_directory() for url in urls: original_url := url - url_without_protocol := url:trim_pattern($Pat"http{0-1 s}://", right=no) - hash := $Shell@(echo -n @url_without_protocol | sha256sum):get_output()!:slice(to=32) - if (~/.local/share/tomo/installed/$hash):is_directory(): + url_without_protocol := url.trim_pattern($Pat"http{0-1 s}://", right=no) + hash := $Shell@(echo -n @url_without_protocol | sha256sum).get_output()!.slice(to=32) + if (~/.local/share/tomo/installed/$hash).is_directory(): say("Already installed: $url") skip alias : Text? = none curl_flags := ["-L"] - if github := url_without_protocol:pattern_captures($Pat"github.com/{!/}/{!/}#{..}"): + if github := url_without_protocol.pattern_captures($Pat"github.com/{!/}/{!/}#{..}"): user := github[1] repo := github[2] tag := github[3] url = "https://api.github.com/repos/$user/$repo/tarball/$tag" - alias = "$(repo:without_prefix("tomo-")).$(tag).$(user)" + alias = "$(repo.without_prefix("tomo-")).$(tag).$(user)" if github_token: curl_flags ++= ["-H", "Authorization: Bearer $github_token"] curl_flags ++= [ @@ -55,7 +55,7 @@ func main(paths:[Path]): "-H", "X-GitHub-Api-Version: 2022-11-28", ] - (~/.local/share/tomo/downloads/$hash):create_directory() + (~/.local/share/tomo/downloads/$hash).create_directory() say($Shell@` set -euo pipefail cd ~/.local/share/tomo/downloads/@hash @@ -67,7 +67,7 @@ func main(paths:[Path]): else ln -f -s ../installed/@hash/lib@hash.so ~/.local/share/tomo/lib/lib@hash.so fi - `:get_output()!) + `.get_output()!) if alias: say($Shell( @@ -78,7 +78,7 @@ func main(paths:[Path]): else ln -f -s lib@hash.so ~/.local/share/tomo/lib/lib@alias.so fi - ):get_output()!) + ).get_output()!) say("$\[1]Installed $url!$\[]") diff --git a/examples/tomodeps/tomodeps.tm b/examples/tomodeps/tomodeps.tm index c6c2171e..dd7bec10 100644 --- a/examples/tomodeps/tomodeps.tm +++ b/examples/tomodeps/tomodeps.tm @@ -12,44 +12,44 @@ _HELP := " enum Dependency(File(path:Path), Module(name:Text)) func _get_file_dependencies(file:Path -> {Dependency}): - if not file:is_file(): + if not file.is_file(): say("Could not read file: $file") - return {} + return {/} - deps : @{Dependency} = @{} - if lines := file:by_line(): + deps : @{Dependency} = @{/} + if lines := file.by_line(): for line in lines: - if line:matches_pattern($Pat/use {..}.tm/): - file_import := Path.from_text(line:replace_pattern($Pat/use {..}/, "\1")):resolved(relative_to=file) - deps:add(Dependency.File(file_import)) - else if line:matches_pattern($Pat/use {id}/): - module_name := line:replace_pattern($Pat/use {..}/, "\1") - deps:add(Dependency.Module(module_name)) + if line.matches_pattern($Pat/use {..}.tm/): + file_import := Path.from_text(line.replace_pattern($Pat/use {..}/, "\1")).resolved(relative_to=file) + deps.add(Dependency.File(file_import)) + else if line.matches_pattern($Pat/use {id}/): + module_name := line.replace_pattern($Pat/use {..}/, "\1") + deps.add(Dependency.Module(module_name)) return deps[] func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency={Dependency}}): - return if dependencies:has(dep) + return if dependencies.has(dep) - dependencies[dep] = {} # Placeholder + dependencies[dep] = {/} # Placeholder dep_deps := when dep is File(path): _get_file_dependencies(path) is Module(module): dir := (~/.local/share/tomo/installed/$module) - module_deps : @{Dependency} = @{} - visited : @{Path} = @{} - unvisited := @{f:resolved() for f in dir:files() if f:extension() == ".tm"} + module_deps : @{Dependency} = @{/} + visited : @{Path} = @{/} + unvisited := @{f.resolved() for f in dir.files() if f.extension() == ".tm"} while unvisited.length > 0: file := unvisited.items[-1] - unvisited:remove(file) - visited:add(file) + unvisited.remove(file) + visited.add(file) for file_dep in _get_file_dependencies(file): when file_dep is File(f): - if not visited:has(f): - unvisited:add(f) + if not visited.has(f): + unvisited.add(f) is Module(m): - module_deps:add(file_dep) + module_deps.add(file_dep) module_deps[] dependencies[dep] = dep_deps @@ -66,19 +66,19 @@ func _printable_name(dep:Dependency -> Text): when dep is Module(module): return "$(\x1b)[34;1m$module$(\x1b)[m" is File(f): - f = f:relative_to((.)) - if f:exists(): + f = f.relative_to((.)) + if f.exists(): return Text(f) else: return "$(\x1b)[31;1m$(f) (not found)$(\x1b)[m" func _draw_tree(dep:Dependency, dependencies:{Dependency={Dependency}}, already_printed:@{Dependency}, prefix="", is_last=yes): - if already_printed:has(dep): + if already_printed.has(dep): say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep) ++ " $\x1b[2m(recursive)$\x1b[m") return say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep)) - already_printed:add(dep) + already_printed.add(dep) child_prefix := prefix ++ (if is_last: " " else: "│ ") @@ -88,9 +88,9 @@ func _draw_tree(dep:Dependency, dependencies:{Dependency={Dependency}}, already_ _draw_tree(child, dependencies, already_printed, child_prefix, is_child_last) func draw_tree(dep:Dependency, dependencies:{Dependency={Dependency}}): - printed : @{Dependency} = @{} + printed : @{Dependency} = @{/} say(_printable_name(dep)) - printed:add(dep) + printed.add(dep) deps := dependencies[dep] or {/} for i,child in deps.items: is_child_last := (i == deps.length) @@ -104,11 +104,11 @@ func main(files:[Text]): ") for arg in files: - if arg:matches_pattern($Pat/{..}.tm/): - path := Path.from_text(arg):resolved() + if arg.matches_pattern($Pat/{..}.tm/): + path := Path.from_text(arg).resolved() dependencies := get_dependency_graph(File(path)) draw_tree(File(path), dependencies) - else if arg:matches_pattern($Pat/{id}/): + else if arg.matches_pattern($Pat/{id}/): dependencies := get_dependency_graph(Module(arg)) draw_tree(Module(arg), dependencies) else: diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm index f978d3d1..42ada614 100644 --- a/examples/vectors/vectors.tm +++ b/examples/vectors/vectors.tm @@ -19,24 +19,24 @@ struct Vec2(x,y:Num): func divided_by(v:Vec2, divisor:Num->Vec2; inline): return Vec2(v.x/divisor, v.y/divisor) func length(v:Vec2->Num; inline): - return (v.x*v.x + v.y*v.y):sqrt() + return (v.x*v.x + v.y*v.y).sqrt() func dist(a,b:Vec2->Num; inline): - return a:minus(b):length() + return a.minus(b).length() func angle(v:Vec2->Num; inline): return Num.atan2(v.y, v.x) func norm(v:Vec2->Vec2; inline): if v.x == 0 and v.y == 0: return v - len := v:length() + len := v.length() return Vec2(v.x/len, v.y/len) func rotated(v:Vec2, radians:Num -> Vec2): - cos := radians:cos() or return v - sin := radians:sin() or return v + cos := radians.cos() or return v + sin := radians.sin() or return v return Vec2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) func mix(a,b:Vec2, amount:Num -> Vec2): return Vec2( - amount:mix(a.x, b.x), - amount:mix(a.y, b.y), + amount.mix(a.x, b.x), + amount.mix(a.y, b.y), ) struct Vec3(x,y,z:Num): @@ -58,19 +58,19 @@ struct Vec3(x,y,z:Num): func divided_by(v:Vec3, divisor:Num->Vec3; inline): return Vec3(v.x/divisor, v.y/divisor, v.z/divisor) func length(v:Vec3->Num; inline): - return (v.x*v.x + v.y*v.y + v.z*v.z):sqrt() + return (v.x*v.x + v.y*v.y + v.z*v.z).sqrt() func dist(a,b:Vec3->Num; inline): - return a:minus(b):length() + return a.minus(b).length() func norm(v:Vec3->Vec3; inline): if v.x == 0 and v.y == 0 and v.z == 0: return v - len := v:length() + len := v.length() return Vec3(v.x/len, v.y/len, v.z/len) func mix(a,b:Vec3, amount:Num -> Vec3): return Vec3( - amount:mix(a.x, b.x), - amount:mix(a.y, b.y), - amount:mix(a.z, b.z), + amount.mix(a.x, b.x), + amount.mix(a.y, b.y), + amount.mix(a.z, b.z), ) @@ -97,7 +97,7 @@ struct IVec2(x,y:Int): y := Num(v.y) return Num.sqrt(x*x + y*y) func dist(a,b:IVec2->Num; inline): - return a:minus(b):length() + return a.minus(b).length() func angle(v:IVec2->Num; inline): return Num.atan2(Num(v.y), Num(v.x)) @@ -125,7 +125,7 @@ struct IVec3(x,y,z:Int): z := Num(v.z) return Num.sqrt(x*x + y*y + z*z) func dist(a,b:IVec3->Num; inline): - return a:minus(b):length() + return a.minus(b).length() func main(): >> Vec2(10, 20) diff --git a/examples/wrap/wrap.tm b/examples/wrap/wrap.tm index 61ca5821..448ab813 100644 --- a/examples/wrap/wrap.tm +++ b/examples/wrap/wrap.tm @@ -15,11 +15,11 @@ UNICODE_HYPHEN := \{hyphen} func unwrap(text:Text, preserve_paragraphs=yes, hyphen=UNICODE_HYPHEN -> Text): if preserve_paragraphs: - paragraphs := text:split($/{2+ nl}/) + paragraphs := text.split($/{2+ nl}/) if paragraphs.length > 1: - return \n\n:join([unwrap(p, hyphen=hyphen, preserve_paragraphs=no) for p in paragraphs]) + return \n\n.join([unwrap(p, hyphen=hyphen, preserve_paragraphs=no) for p in paragraphs]) - return text:replace($/$(hyphen)$(\n)/, "") + return text.replace($/$(hyphen)$(\n)/, "") func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): if width <= 0: @@ -35,8 +35,8 @@ func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): lines : @[Text] = @[] line := "" - for word in text:split($/{whitespace}/): - letters := word:split() + for word in text.split($/{whitespace}/): + letters := word.split() skip if letters.length == 0 while not _can_fit_word(line, letters, width): @@ -49,17 +49,17 @@ func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): split = split _max_ min_split split = split _min_ (letters.length - min_split) if line != "": line ++= " " - line ++= ((++: letters:to(split)) or "") ++ hyphen - letters = letters:from(split + 1) + line ++= ((++: letters.to(split)) or "") ++ hyphen + letters = letters.from(split + 1) else if line == "": # Force split word without hyphenation: if line != "": line ++= " " - line ++= (++: letters:to(line_space)) or "" - letters = letters:from(line_space + 1) + line ++= (++: letters.to(line_space)) or "" + letters = letters.from(line_space + 1) else: pass # Move to next line - lines:insert(line) + lines.insert(line) line = "" if letters.length > 0: @@ -67,9 +67,9 @@ func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): line ++= (++: letters) or "" if line != "": - lines:insert(line) + lines.insert(line) - return \n:join(lines) + return \n.join(lines) func _can_fit_word(line:Text, letters:[Text], width:Int -> Bool; inline): if line == "": @@ -82,21 +82,21 @@ func main(files:[Path], width=80, inplace=no, min_split=3, rewrap=yes, hyphen=UN files = [(/dev/stdin)] for file in files: - text := file:read() or exit("Could not read file: $file") + text := file.read() or exit("Could not read file: $file") if rewrap: text = unwrap(text) - out := if file:is_file() and inplace: + out := if file.is_file() and inplace: file else: (/dev/stdout) first := yes wrapped_paragraphs : @[Text] = @[] - for paragraph in text:split($/{2+ nl}/): - wrapped_paragraphs:insert( + for paragraph in text.split($/{2+ nl}/): + wrapped_paragraphs.insert( wrap(paragraph, width=width, min_split=min_split, hyphen=hyphen) ) - out:write(\n\n:join(wrapped_paragraphs[]) ++ \n) + out.write(\n\n.join(wrapped_paragraphs[]) ++ \n) diff --git a/src/compile.c b/src/compile.c index a4f5b3ec..46a0e607 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1468,7 +1468,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) // If we're iterating over a comprehension, that's actually just doing // one loop, we don't need to compile the comprehension as an array // comprehension. This is a common case for reducers like `(+: i*2 for i in 5)` - // or `(and) x:is_good() for x in xs` + // or `(and) x.is_good() for x in xs` if (for_->iter->tag == Comprehension) { auto comp = Match(for_->iter, Comprehension); ast_t *body = for_->body; @@ -1552,7 +1552,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) stop); } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") && get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { - // Special case for Int:onward() + // Special case for Int.onward() arg_ast_t *args = Match(for_->iter, MethodCall)->args; arg_t *arg_spec = new(arg_t, .name="step", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"), .next=NULL); CORD step = compile_arguments(env, for_->iter, arg_spec, args); @@ -1933,7 +1933,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool ++depth; // Passing a literal value won't trigger an incref, because it's ephemeral, - // e.g. [10, 20]:reversed() + // e.g. [10, 20].reversed() if (t->tag != PointerType && needs_incref && !can_be_mutated(env, ast)) needs_incref = false; @@ -2001,7 +2001,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) // should be from what we know the specific type of the return value is, // but it requires a bit of special logic. // For example: - // x : [Int?] = [none]:sorted() + // x : [Int?] = [none].sorted() // Here, we know that `[none]` is `[Int?]`, but we need to thread that // information through the compiler using an `ExplicitlyTyped` node. if (ast->tag == MethodCall) { @@ -2924,7 +2924,7 @@ CORD compile(env_t *env, ast_t *ast) if (streq(call->name, "serialized")) { if (call->args) - code_err(ast, ":serialized() doesn't take any arguments"); + code_err(ast, ".serialized() doesn't take any arguments"); return CORD_all("generic_serialize((", compile_declaration(self_t, "[1]"), "){", compile(env, call->self), "}, ", compile_type_info(self_t), ")"); } @@ -2934,6 +2934,18 @@ CORD compile(env_t *env, ast_t *ast) for (; self_value_t->tag == PointerType; self_value_t = Match(self_value_t, PointerType)->pointed) pointer_depth += 1; + if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) { + return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), + .args=call->args)); + } + + type_t *field_type = get_field_type(self_value_t, call->name); + if (field_type && field_type->tag == ClosureType) + field_type = Match(field_type, ClosureType)->fn; + if (field_type && field_type->tag == FunctionType) + return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), + .args=call->args)); + CORD self = compile(env, call->self); #define EXPECT_POINTER(article, name) do { \ diff --git a/src/parse.c b/src/parse.c index fa021e28..d686bb6d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -847,8 +847,8 @@ PARSER(parse_reduction) { ast_t *new_term; progress = (false || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) || (new_term=parse_method_call_suffix(ctx, key)) + || (new_term=parse_field_suffix(ctx, key)) || (new_term=parse_fncall_suffix(ctx, key)) || (new_term=parse_optional_suffix(ctx, key)) || (new_term=parse_non_optional_suffix(ctx, key)) @@ -1095,6 +1095,7 @@ PARSER(parse_heap_alloc) { ast_t *new_term; if ((new_term=parse_index_suffix(ctx, val)) || (new_term=parse_fncall_suffix(ctx, val)) + || (new_term=parse_method_call_suffix(ctx, val)) || (new_term=parse_field_suffix(ctx, val))) { val = new_term; } else break; @@ -1121,6 +1122,7 @@ PARSER(parse_stack_reference) { ast_t *new_term; if ((new_term=parse_index_suffix(ctx, val)) || (new_term=parse_fncall_suffix(ctx, val)) + || (new_term=parse_method_call_suffix(ctx, val)) || (new_term=parse_field_suffix(ctx, val))) { val = new_term; } else break; @@ -1462,8 +1464,8 @@ PARSER(parse_term) { ast_t *new_term; progress = (false || (new_term=parse_index_suffix(ctx, term)) - || (new_term=parse_field_suffix(ctx, term)) || (new_term=parse_method_call_suffix(ctx, term)) + || (new_term=parse_field_suffix(ctx, term)) || (new_term=parse_fncall_suffix(ctx, term)) || (new_term=parse_optional_suffix(ctx, term)) || (new_term=parse_non_optional_suffix(ctx, term)) @@ -1479,7 +1481,7 @@ ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) { const char *start = self->start; const char *pos = self->end; - if (!match(&pos, ":")) return NULL; + if (!match(&pos, ".")) return NULL; if (*pos == ' ') return NULL; const char *fn = get_id(&pos); if (!fn) return NULL; @@ -1622,8 +1624,8 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn ast_t *new_term; progress = (false || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) || (new_term=parse_method_call_suffix(ctx, key)) + || (new_term=parse_field_suffix(ctx, key)) || (new_term=parse_fncall_suffix(ctx, key)) || (new_term=parse_optional_suffix(ctx, key)) || (new_term=parse_non_optional_suffix(ctx, key)) diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index dfa35236..58c1b2e4 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -446,10 +446,10 @@ PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const Type // Table comparison rules: // - If two tables have different keys, then compare as if comparing a // sorted array of the keys of the two tables: - // `x.keys:sorted() <> y.keys:sorted()` + // `x.keys.sorted() <> y.keys.sorted()` // - Otherwise, compare as if comparing arrays of values for the sorted key // arrays: - // `[x[k] for k in x.keys:sorted()] <> [y[k] for k in y.keys:sorted()]` + // `[x[k] for k in x.keys.sorted()] <> [y[k] for k in y.keys.sorted()]` // // We can do this in _linear_ time if we find the smallest `k` such that // `x[k] != y[k]`, as well as the largest key in `x` and `y`. @@ -637,7 +637,7 @@ public Table_t Table$from_entries(Array_t entries, const TypeInfo_t *type) // Overlap is "set intersection" in formal terms public Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that a:has(k), b:has(k), and a[k]==b[k] + // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), and a[k]==b[k] Table_t result = {}; const size_t offset = value_offset(type); for (int64_t i = 0; i < Table$length(a); i++) { @@ -659,7 +659,7 @@ public Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type) // With is "set union" in formal terms public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) { - // return a table such that t[k]==b[k] for all k such that b:has(k), and t[k]==a[k] for all k such that a:has(k) and not b:has(k) + // return a table such that t[k]==b[k] for all k such that b.has(k), and t[k]==a[k] for all k such that a.has(k) and not b.has(k) Table_t result = {}; const size_t offset = value_offset(type); for (int64_t i = 0; i < Table$length(a); i++) { @@ -684,7 +684,7 @@ public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) // Without is "set difference" in formal terms public Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that not b:has(k) or b[k] != a[k] + // Return a table such that t[k]==a[k] for all k such that not b.has(k) or b[k] != a[k] Table_t result = {}; const size_t offset = value_offset(type); for (int64_t i = 0; i < Table$length(a); i++) { @@ -241,8 +241,6 @@ int main(int argc, char *argv[]) if (files.length < 1) print_err("No file specified!"); - else if (files.length != 1) - print_err("Too many files specified!"); quiet = !verbose; diff --git a/src/typecheck.c b/src/typecheck.c index c5243195..21e02460 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -550,7 +550,7 @@ type_t *get_method_type(env_t *env, ast_t *self, const char *name) { binding_t *b = get_namespace_binding(env, self, name); if (!b || !b->type) - code_err(self, "No such method: ", type_to_str(get_type(env, self)), ":", name, "(...)"); + code_err(self, "No such method: ", type_to_str(get_type(env, self)), ".", name, "(...)"); return b->type; } @@ -895,6 +895,12 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *self_value_t = get_type(env, call->self); if (!self_value_t) code_err(call->self, "Couldn't get the type of this value"); self_value_t = value_type(self_value_t); + + if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) { + return get_type(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), + .args=call->args)); + } + switch (self_value_t->tag) { case ArrayType: { type_t *item_type = Match(self_value_t, ArrayType)->item_type; @@ -952,6 +958,11 @@ type_t *get_type(env_t *env, ast_t *ast) code_err(ast, "There is no '", call->name, "' method for ", type_to_str(self_value_t), " tables"); } default: { + type_t *field_type = get_field_type(self_value_t, call->name); + if (field_type && field_type->tag == ClosureType) + field_type = Match(field_type, ClosureType)->fn; + if (field_type && field_type->tag == FunctionType) + return Match(field_type, FunctionType)->ret; type_t *fn_type_t = get_method_type(env, call->self, call->name); if (!fn_type_t) code_err(ast, "No such method!"); diff --git a/test/arrays.tm b/test/arrays.tm index 4834f0ab..c60085ca 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -60,7 +60,7 @@ func main(): do: >> arr := @[10, 20] >> copy := arr[] - >> arr:insert(30) + >> arr.insert(30) >> arr = @[10, 20, 30] >> copy @@ -72,7 +72,7 @@ func main(): do: >> arr := &[10, 20, 30] - >> reversed := arr:reversed() + >> reversed := arr.reversed() = [30, 20, 10] # Ensure the copy-on-write behavior triggers: >> arr[1] = 999 @@ -82,105 +82,105 @@ func main(): do: >> nums := @[10, -20, 30] # Sorted function doesn't mutate original: - >> nums:sorted() + >> nums.sorted() = [-20, 10, 30] >> nums = @[10, -20, 30] # Sort function does mutate in place: - >> nums:sort() + >> nums.sort() >> nums = @[-20, 10, 30] # Custom sort functions: - >> nums:sort(func(x,y:&Int): x:abs() <> y:abs()) + >> nums.sort(func(x,y:&Int): x.abs() <> y.abs()) >> nums = @[10, -20, 30] - >> nums:sort(func(x,y:&Int): y[] <> x[]) + >> nums.sort(func(x,y:&Int): y[] <> x[]) >> nums = @[30, 10, -20] - >> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0]) + >> ["A", "B", "C"].sample(10, [1.0, 0.5, 0.0]) do: >> heap := @[(i * 1337) mod 37 for i in 10] - >> heap:heapify() + >> heap.heapify() >> heap heap_order : @[Int] = @[] repeat: - heap_order:insert(heap:heap_pop() or stop) - >> heap_order[] == heap_order:sorted() + heap_order.insert(heap.heap_pop() or stop) + >> heap_order[] == heap_order.sorted() = yes heap_order[] = [] for i in 10: - heap:heap_push((i*13337) mod 37) + heap.heap_push((i*13337) mod 37) >> heap repeat: - heap_order:insert(heap:heap_pop() or stop) - >> heap_order[] == heap_order:sorted() + heap_order.insert(heap.heap_pop() or stop) + >> heap_order[] == heap_order.sorted() = yes do: - >> [i*10 for i in 5]:from(3) + >> [i*10 for i in 5].from(3) = [30, 40, 50] - >> [i*10 for i in 5]:to(3) + >> [i*10 for i in 5].to(3) = [10, 20, 30] - >> [i*10 for i in 5]:to(-2) + >> [i*10 for i in 5].to(-2) = [10, 20, 30, 40] - >> [i*10 for i in 5]:from(-2) + >> [i*10 for i in 5].from(-2) = [40, 50] - >> [i*10 for i in 5]:by(2) + >> [i*10 for i in 5].by(2) = [10, 30, 50] - >> [i*10 for i in 5]:by(-1) + >> [i*10 for i in 5].by(-1) = [50, 40, 30, 20, 10] - >> [10, 20, 30, 40]:by(2) + >> [10, 20, 30, 40].by(2) = [10, 30] - >> [10, 20, 30, 40]:by(-2) + >> [10, 20, 30, 40].by(-2) = [40, 20] - >> [i*10 for i in 10]:by(2):by(2) + >> [i*10 for i in 10].by(2).by(2) = [10, 50, 90] - >> [i*10 for i in 10]:by(2):by(-1) + >> [i*10 for i in 10].by(2).by(-1) = [90, 70, 50, 30, 10] - # Test iterating over array:from() and array:to() + # Test iterating over array.from() and array.to() xs := ["A", "B", "C", "D"] - for i,x in xs:to(-2): - for y in xs:from(i+1): + for i,x in xs.to(-2): + for y in xs.from(i+1): say("$(x)$(y)") do: >> nums := @[-7, -4, -1, 2, 5] - >> nums:sort() - >> [nums:binary_search(i) for i in nums[]] + >> nums.sort() + >> [nums.binary_search(i) for i in nums[]] = [1, 2, 3, 4, 5] - >> nums:sort(func(a,b:&Int): a:abs() <> b:abs()) - >> [nums:binary_search(i, func(a,b:&Int): a:abs() <> b:abs()) for i in nums[]] + >> nums.sort(func(a,b:&Int): a.abs() <> b.abs()) + >> [nums.binary_search(i, func(a,b:&Int): a.abs() <> b.abs()) for i in nums[]] = [1, 2, 3, 4, 5] - >> ["a", "b", "c"]:find("b") + >> ["a", "b", "c"].find("b") = 2? - >> ["a", "b", "c"]:find("XXX") + >> ["a", "b", "c"].find("XXX") = none - >> [10, 20]:first(func(i:&Int): i:is_prime()) + >> [10, 20].first(func(i:&Int): i.is_prime()) = none - >> [4, 5, 6]:first(func(i:&Int): i:is_prime()) + >> [4, 5, 6].first(func(i:&Int): i.is_prime()) = 2? do: >> nums := &[10, 20, 30, 40, 50] - >> nums:pop() + >> nums.pop() = 50? >> nums = &[10, 20, 30, 40] - >> nums:pop(2) + >> nums.pop(2) = 20? >> nums = &[10, 30, 40] - >> nums:clear() + >> nums.clear() >> nums = &[] - >> nums:pop() + >> nums.pop() = none diff --git a/test/bytes.tm b/test/bytes.tm index a62acd10..862d2674 100644 --- a/test/bytes.tm +++ b/test/bytes.tm @@ -8,9 +8,9 @@ func main(): = Byte(0xFF) >> b := Byte(0x0F) - >> b:hex() + >> b.hex() = "0F" - >> b:hex(prefix=yes) + >> b.hex(prefix=yes) = "0x0F" - >> b:hex(uppercase=no) + >> b.hex(uppercase=no) = "0f" diff --git a/test/defer.tm b/test/defer.tm index 6657bdc5..4053f0c1 100644 --- a/test/defer.tm +++ b/test/defer.tm @@ -3,7 +3,7 @@ func main(): nums : @[Int] = @[] do: defer: - nums:insert(x) + nums.insert(x) x = 999 >> nums diff --git a/test/enums.tm b/test/enums.tm index 58bc412c..19590353 100644 --- a/test/enums.tm +++ b/test/enums.tm @@ -43,9 +43,9 @@ func main(): >> x := Foo.One(123) >> t := {x} - >> t:has(x) + >> t.has(x) = yes - >> t:has(Foo.Zero) + >> t.has(Foo.Zero) = no >> choose_text(Foo.Zero) diff --git a/test/extern.tm b/test/extern.tm index b1f3c7f9..17c39948 100644 --- a/test/extern.tm +++ b/test/extern.tm @@ -1,4 +1,4 @@ -extern sqrt:func(n:Num->Num) +extern sqrt : func(n:Num->Num) func main(): >> sqrt(4) diff --git a/test/integers.tm b/test/integers.tm index 76e55ca1..508a38ae 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -43,11 +43,11 @@ func main(): = "1,2,3,4,5," >> x := Int64(123) - >> x:format(digits=5) + >> x.format(digits=5) = "00123" - >> x:hex() + >> x.hex() = "0x7B" - >> x:octal() + >> x.octal() = "0o173" >> Int64.min @@ -56,11 +56,11 @@ func main(): = Int64(9223372036854775807) - >> Int32(123):hex() + >> Int32(123).hex() = "0x7B" - >> Int16(123):hex() + >> Int16(123).hex() = "0x7B" - >> Int8(123):hex() + >> Int8(123).hex() = "0x7B" >> Int(2.1, truncate=yes) @@ -92,13 +92,13 @@ func main(): >> (n/d)*d + (n mod d) == n = yes - >> 0:next_prime() + >> (0).next_prime() = 2 - >> 7:next_prime() + >> (7).next_prime() = 11 - #>> 11:prev_prime() + #>> (11).prev_prime() #= 7 - >> (and: p:is_prime() for p in [ + >> (and: p.is_prime() for p in [ 2, 3, 5, 7, 137372146048179869781170214707, 811418847921670560768224995279, @@ -113,7 +113,7 @@ func main(): ])! = yes - >> (or: p:is_prime() for p in [ + >> (or: p.is_prime() for p in [ -1, 0, 1, 4, 6, 137372146048179869781170214707*2, 811418847921670560768224995279*3, @@ -131,8 +131,8 @@ func main(): >> Int64(no) = Int64(0) - >> 4:choose(2) + >> (4).choose(2) = 6 - >> 4:factorial() + >> (4).factorial() = 24 diff --git a/test/iterators.tm b/test/iterators.tm index a8316aba..64d21ea3 100644 --- a/test/iterators.tm +++ b/test/iterators.tm @@ -27,7 +27,7 @@ func main(): do: result : @[Text] = @[] for foo in pairwise(values): - result:insert("$(foo.x)$(foo.y)") + result.insert("$(foo.x)$(foo.y)") >> result[] = ["AB", "BC", "CD"] diff --git a/test/lambdas.tm b/test/lambdas.tm index 3d536086..6e261e0f 100644 --- a/test/lambdas.tm +++ b/test/lambdas.tm @@ -12,7 +12,7 @@ func main(): >> add_one(10) = 11 - >> shout := func(msg:Text): say("$(msg:upper())!") + >> shout := func(msg:Text): say("$(msg.upper())!") >> shout("hello") >> asdf := add_one @@ -23,7 +23,7 @@ func main(): >> add_100(5) = 105 - >> shout2 := suffix_fn(func(t:Text): t:upper(), "!") + >> shout2 := suffix_fn(func(t:Text): t.upper(), "!") >> shout2("hello") = "HELLO!" diff --git a/test/lang.tm b/test/lang.tm index dba096d6..6d0a94ea 100644 --- a/test/lang.tm +++ b/test/lang.tm @@ -1,7 +1,7 @@ lang HTML: HEADER := $HTML"<!DOCTYPE HTML>" convert(t:Text->HTML): - t = t:translate({ + t = t.translate({ "&"="&", "<"="<", ">"=">", @@ -43,7 +43,7 @@ func main(): >> $HTML"$(Int8(3))" = $HTML"3" - >> html:paragraph() + >> html.paragraph() = $HTML"<p>Hello I <3 hax!</p>" >> Text(html) diff --git a/test/metamethods.tm b/test/metamethods.tm index 5566b4e9..aac37c4b 100644 --- a/test/metamethods.tm +++ b/test/metamethods.tm @@ -53,7 +53,7 @@ func main(): = Vec2(x=-90, y=-180) >> x * y = Vec2(x=1000, y=4000) - >> x:dot(y) + >> x.dot(y) = 5000 >> x * -1 = Vec2(x=-10, y=-20) diff --git a/test/minmax.tm b/test/minmax.tm index f28aec91..a0f0640f 100644 --- a/test/minmax.tm +++ b/test/minmax.tm @@ -18,7 +18,7 @@ func main(): >> Foo(999, 1) _min_.y Foo(1, 10) = Foo(x=999, y=1) - >> Foo(-999, -999) _max_:len() Foo(10, 10) + >> Foo(-999, -999) _max_.len() Foo(10, 10) = Foo(x=-999, y=-999) diff --git a/test/nums.tm b/test/nums.tm index 285614d9..e5de1e22 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -14,12 +14,12 @@ func main(): >> Num.PI = 3.141592653589793 - >> Num.PI:format(precision=10) + >> Num.PI.format(precision=10) = "3.1415926536" >> Num.INF = Num.INF - >> Num.INF:isinf() + >> Num.INF.isinf() = yes >> none_num : Num? = none @@ -53,24 +53,24 @@ func main(): # >> 0./0. # = none - >> Num.PI:cos()!:near(-1) + >> Num.PI.cos()!.near(-1) = yes - >> Num.PI:sin()!:near(0) + >> Num.PI.sin()!.near(0) = yes - >> Num.INF:near(-Num.INF) + >> Num.INF.near(-Num.INF) = no >> Num32.sqrt(16) = Num32(4)? - >> 0.25:mix(10, 20) + >> (0.25).mix(10, 20) = 12.5 - >> 2.0:mix(10, 20) + >> (2.0).mix(10, 20) = 30. >> Num(5) = 5. - >> 0.5:percent() + >> (0.5).percent() = "50%" diff --git a/test/optionals.tm b/test/optionals.tm index 316b2e71..bf3e1633 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -57,7 +57,7 @@ func maybe_lambda(should_i:Bool-> func()?): func maybe_c_string(should_i:Bool->CString?): if should_i: - return ("hi":as_c_string())? + return ("hi".as_c_string())? else: return none @@ -248,7 +248,7 @@ func main(): >> nones : {Int?} = {none, none} >> also_nones : {Int?} = {none} >> nones == also_nones - >> [5?, none, none, 6?]:sorted() + >> [5?, none, none, 6?].sorted() = [none, none, 5, 6] do: diff --git a/test/paths.tm b/test/paths.tm index e448dee1..85f9b191 100644 --- a/test/paths.tm +++ b/test/paths.tm @@ -1,8 +1,8 @@ # Tests for file paths func main(): - >> (/):exists() + >> (/).exists() = yes - >> (~/):exists() + >> (~/).exists() = yes >> (~/Downloads/file(1).txt) @@ -12,73 +12,73 @@ func main(): = (/half\)paren) >> filename := "example.txt" - >> (~):child(filename) + >> (~).child(filename) = (~/example.txt) - >> tmpdir := (/tmp/tomo-test-path-XXXXXX):unique_directory() - >> (/tmp):subdirectories():has(tmpdir) + >> tmpdir := (/tmp/tomo-test-path-XXXXXX).unique_directory() + >> (/tmp).subdirectories().has(tmpdir) = yes >> tmpfile := (tmpdir++(./one.txt)) - >> tmpfile:write("Hello world") - >> tmpfile:append("!") - >> tmpfile:read() + >> tmpfile.write("Hello world") + >> tmpfile.append("!") + >> tmpfile.read() = "Hello world!"? - >> tmpfile:read_bytes()! + >> tmpfile.read_bytes()! = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21] - >> tmpdir:files():has(tmpfile) + >> tmpdir.files().has(tmpfile) = yes - if tmp_lines := tmpfile:by_line(): + if tmp_lines := tmpfile.by_line(): >> [line for line in tmp_lines] = ["Hello world!"] else: fail("Couldn't read lines in $tmpfile") - >> (./does-not-exist.xxx):read() + >> (./does-not-exist.xxx).read() = none - >> (./does-not-exist.xxx):read_bytes() + >> (./does-not-exist.xxx).read_bytes() = none - if lines := (./does-not-exist.xxx):by_line(): + if lines := (./does-not-exist.xxx).by_line(): fail("I could read lines in a nonexistent file") else: pass - >> tmpfile:remove() + >> tmpfile.remove() - >> tmpdir:files():has(tmpfile) + >> tmpdir.files().has(tmpfile) = no - >> tmpdir:remove() + >> tmpdir.remove() >> p := (/foo/baz.x/qux.tar.gz) - >> p:base_name() + >> p.base_name() = "qux.tar.gz" - >> p:parent() + >> p.parent() = (/foo/baz.x) - >> p:extension() + >> p.extension() = "tar.gz" - >> p:extension(full=no) + >> p.extension(full=no) = "gz" - >> (~/.foo):extension() + >> (~/.foo).extension() = "" - >> (~/foo):extension() + >> (~/foo).extension() = "" - >> (~/.foo.baz.qux):extension() + >> (~/.foo.baz.qux).extension() = "baz.qux" - >> (/):parent() + >> (/).parent() = (/) - >> (~/x/.):parent() + >> (~/x/.).parent() = (~) - >> (~/x):parent() + >> (~/x).parent() = (~) - >> (.):parent() + >> (.).parent() = (..) - >> (..):parent() + >> (..).parent() = (../..) - >> (../foo):parent() + >> (../foo).parent() = (..) # Concatenation tests: @@ -127,4 +127,4 @@ func main(): = (/foo/bar/quux) say("Globbing:") - >> (./*.tm):glob() + >> (./*.tm).glob() diff --git a/test/reductions.tm b/test/reductions.tm index f2fa4b9c..bd74f306 100644 --- a/test/reductions.tm +++ b/test/reductions.tm @@ -17,14 +17,14 @@ func main(): >> (_max_: [3, 5, 2, 1, 4]) = 5? - >> (_max_:abs(): [1, -10, 5]) + >> (_max_.abs(): [1, -10, 5]) = -10? >> (_max_: [Foo(0, 0), Foo(1, 0), Foo(0, 10)])! = Foo(x=1, y=0) >> (_max_.y: [Foo(0, 0), Foo(1, 0), Foo(0, 10)])! = Foo(x=0, y=10) - >> (_max_.y:abs(): [Foo(0, 0), Foo(1, 0), Foo(0, 10), Foo(0, -999)])! + >> (_max_.y.abs(): [Foo(0, 0), Foo(1, 0), Foo(0, 10), Foo(0, -999)])! = Foo(x=0, y=-999) say("(or) and (and) have early out behavior:") diff --git a/test/serialization.tm b/test/serialization.tm index 9e3ac368..b3ccc67a 100644 --- a/test/serialization.tm +++ b/test/serialization.tm @@ -6,44 +6,44 @@ enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text)) func main(): do: >> obj := Int64(123) - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Int64) == obj = yes do: >> obj := 5 - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Int) == obj = yes do: >> obj := 9999999999999999999999999999999999999999999999999999 - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Int) == obj = yes do: >> obj := "Héllo" - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Text) >> deserialize(bytes -> Text) == obj = yes do: - >> obj := [Int64(10), Int64(20), Int64(30)]:reversed() - >> bytes := obj:serialized() + >> obj := [Int64(10), Int64(20), Int64(30)].reversed() + >> bytes := obj.serialized() >> deserialize(bytes -> [Int64]) == obj = yes do: >> obj := yes - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Bool) == obj = yes do: >> obj := @[10, 20] - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> roundtrip := deserialize(bytes -> @[Int]) >> roundtrip == obj = no @@ -52,44 +52,44 @@ func main(): do: >> obj := {"A"=10, "B"=20; fallback={"C"=30}} - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> {Text=Int}) == obj = yes do: >> obj := @Foo("root") >> obj.next = @Foo("abcdef", next=obj) - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> @Foo) # = @Foo(name="root", next=@Foo(name="abcdef", next=@~1)) do: >> obj := MyEnum.Two(123, "OKAY") - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> MyEnum) == obj = yes do: >> obj := "Hello"? - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Text?) == obj = yes do: >> obj := {10, 20, 30} - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> {Int}) == obj = yes do: >> obj : Num? = none - >> bytes := obj:serialized() + >> bytes := obj.serialized() >> deserialize(bytes -> Num?) == obj = yes do: cases := [0, -1, 1, 10, 100000, 999999999999999999999999999] for i in cases: - >> bytes := i:serialized() + >> bytes := i.serialized() >> deserialize(bytes -> Int) == i = yes diff --git a/test/sets.tm b/test/sets.tm index 7b285d60..5179947e 100644 --- a/test/sets.tm +++ b/test/sets.tm @@ -2,36 +2,36 @@ func main(): >> t1 := @{10, 20, 30, 10} = @{10, 20, 30} - >> t1:has(10) + >> t1.has(10) = yes - >> t1:has(-999) + >> t1.has(-999) = no >> t2 := {30, 40} - >> t1:with(t2) + >> t1.with(t2) >> {10, 20, 30, 40} - >> t1:without(t2) + >> t1.without(t2) >> {10, 20} - >> t1:overlap(t2) + >> t1.overlap(t2) >> {30} - >> {1,2}:is_subset_of({2,3}) + >> {1,2}.is_subset_of({2,3}) = no - >> {1,2}:is_subset_of({1,2,3}) + >> {1,2}.is_subset_of({1,2,3}) = yes - >> {1,2}:is_subset_of({1,2}) + >> {1,2}.is_subset_of({1,2}) = yes - >> {1,2}:is_subset_of({1,2}, strict=yes) + >> {1,2}.is_subset_of({1,2}, strict=yes) = no - >> t1:add_all(t2) + >> t1.add_all(t2) >> t1 = @{10, 20, 30, 40} - >> t1:remove_all(t2) + >> t1.remove_all(t2) >> t1 = @{10, 20} diff --git a/test/structs.tm b/test/structs.tm index c1d2f7b0..cf7b6a1c 100644 --- a/test/structs.tm +++ b/test/structs.tm @@ -33,9 +33,9 @@ func test_metamethods(): >> x < Pair(11, 20) = yes >> set := {x} - >> set:has(x) + >> set.has(x) = yes - >> set:has(y) + >> set.has(y) = no func test_mixed(): @@ -50,9 +50,9 @@ func test_mixed(): >> x < Mixed(11, "Hello") = yes >> set := {x} - >> set:has(x) + >> set.has(x) = yes - >> set:has(y) + >> set.has(y) = no func test_text(): diff --git a/test/tables.tm b/test/tables.tm index 20d76269..a5f51520 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -58,7 +58,7 @@ func main(): = {2=20, 3=30, 4=40} >> t3 := @{1=10, 2=20, 3=30} - >> t3:remove(3) + >> t3.remove(3) >> t3 = @{1=10, 2=20} @@ -70,13 +70,13 @@ func main(): = 20 >> plain[456] or -999 = -999 - >> plain:has(2) + >> plain.has(2) = yes - >> plain:has(456) + >> plain.has(456) = no >> fallback := {4=40; fallback=plain} - >> fallback:has(1) + >> fallback.has(1) = yes >> fallback[1] or -999 = 10 @@ -96,10 +96,10 @@ func main(): >> {1=1, 2=2} <> {2=2, 1=1} = Int32(0) - >> ints : [{Int=Int}] = [{}, {0=0}, {99=99}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 2=-99, 3=3}, {1=1, 99=-99, 3=4}]:sorted() + >> ints : [{Int=Int}] = [{}, {0=0}, {99=99}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 2=-99, 3=3}, {1=1, 99=-99, 3=4}].sorted() = [{}, {0=0}, {1=1, 2=-99, 3=3}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 99=-99, 3=4}, {99=99}] - >> other_ints : [{Int}] = [{/}, {1}, {2}, {99}, {0, 3}, {1, 2}, {99}]:sorted() + >> other_ints : [{Int}] = [{/}, {1}, {2}, {99}, {0, 3}, {1, 2}, {99}].sorted() = [{/}, {0, 3}, {1}, {1, 2}, {2}, {99}, {99}] do: @@ -109,9 +109,9 @@ func main(): = 10 >> counter["y"] = 0 - >> counter:has("x") + >> counter.has("x") = yes - >> counter:has("y") + >> counter.has("y") = no >> counter["y"] += 1 diff --git a/test/text.tm b/test/text.tm index df48470e..b353aa26 100644 --- a/test/text.tm +++ b/test/text.tm @@ -2,33 +2,33 @@ func main(): str := "Hello Amélie!" say("Testing strings like $str") - >> str:upper() + >> str.upper() = "HELLO AMÉLIE!" - >> str:lower() + >> str.lower() = "hello amélie!" - >> str:lower():title() + >> str.lower().title() = "Hello Amélie!" >> str[1] = "H" - >> "I":lower() + >> "I".lower() = "i" - >> "I":lower(language="tr_TR") + >> "I".lower(language="tr_TR") = "ı" - >> "i":upper() + >> "i".upper() = "I" - >> "i":upper(language="tr_TR") + >> "i".upper(language="tr_TR") = "İ" - >> "ian":title() + >> "ian".title() = "Ian" - >> "ian":title(language="tr_TR") + >> "ian".title(language="tr_TR") = "İan" - >> "I":caseless_equals("ı") + >> "I".caseless_equals("ı") = no - >> "I":caseless_equals("ı", language="tr_TR") + >> "I".caseless_equals("ı", language="tr_TR") = yes >> str[9] @@ -40,7 +40,7 @@ func main(): >> \U65\U301 = "é" - >> \{Penguin}:codepoint_names() + >> \{Penguin}.codepoint_names() = ["PENGUIN"] >> \[31;1] @@ -50,11 +50,11 @@ func main(): = yes amelie := "Am$(\UE9)lie" - >> amelie:split() + >> amelie.split() = ["A", "m", "é", "l", "i", "e"] - >> amelie:utf32_codepoints() + >> amelie.utf32_codepoints() = [65, 109, 233, 108, 105, 101] - >> amelie:bytes() + >> amelie.bytes() = [0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65] >> Text.from_bytes([0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])! = "Amélie" @@ -62,36 +62,36 @@ func main(): = none amelie2 := "Am$(\U65\U301)lie" - >> amelie2:split() + >> amelie2.split() = ["A", "m", "é", "l", "i", "e"] - >> amelie2:utf32_codepoints() + >> amelie2.utf32_codepoints() = [65, 109, 233, 108, 105, 101] - >> amelie2:bytes() + >> amelie2.bytes() = [0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65] - >> amelie:codepoint_names() + >> amelie.codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] - >> amelie2:codepoint_names() + >> amelie2.codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] - >> "Hello":replace("e", "X") + >> "Hello".replace("e", "X") = "HXllo" - >> "Hello":has("l") + >> "Hello".has("l") = yes - >> "Hello":has("x") + >> "Hello".has("x") = no - >> "Hello":replace("l", "") + >> "Hello".replace("l", "") = "Heo" - >> "xxxx":replace("x", "") + >> "xxxx".replace("x", "") = "" - >> "xxxx":replace("y", "") + >> "xxxx".replace("y", "") = "xxxx" - >> "One two three four five six":replace("e ", "") + >> "One two three four five six".replace("e ", "") = "Ontwo threfour fivsix" - >> amelie:has(amelie2) + >> amelie.has(amelie2) = yes >> multiline := " @@ -118,135 +118,135 @@ func main(): = "one {nested} two 3" c := "É̩" - >> c:codepoint_names() + >> c.codepoint_names() = ["LATIN CAPITAL LETTER E WITH ACUTE", "COMBINING VERTICAL LINE BELOW"] - >> c == Text.from_codepoint_names(c:codepoint_names())! + >> c == Text.from_codepoint_names(c.codepoint_names())! = yes - >> c == Text.from_codepoints(c:utf32_codepoints()) + >> c == Text.from_codepoints(c.utf32_codepoints()) = yes - >> c == Text.from_bytes(c:bytes())! + >> c == Text.from_bytes(c.bytes())! = yes - >> "one$(\n)two$(\n)three":lines() + >> "one$(\n)two$(\n)three".lines() = ["one", "two", "three"] - >> "one$(\n)two$(\n)three$(\n)":lines() + >> "one$(\n)two$(\n)three$(\n)".lines() = ["one", "two", "three"] - >> "one$(\n)two$(\n)three$(\n\n)":lines() + >> "one$(\n)two$(\n)three$(\n\n)".lines() = ["one", "two", "three", ""] - >> "one$(\r\n)two$(\r\n)three$(\r\n)":lines() + >> "one$(\r\n)two$(\r\n)three$(\r\n)".lines() = ["one", "two", "three"] - >> "":lines() + >> "".lines() = [] say("Test splitting and joining text:") - >> "one,, two,three":split(",") + >> "one,, two,three".split(",") = ["one", "", " two", "three"] - >> [t for t in "one,, two,three":by_split(",")] + >> [t for t in "one,, two,three".by_split(",")] = ["one", "", " two", "three"] - >> "one,, two,three":split_any(", ") + >> "one,, two,three".split_any(", ") = ["one", "two", "three"] - >> [t for t in "one,, two,three":by_split_any(", ")] + >> [t for t in "one,, two,three".by_split_any(", ")] = ["one", "two", "three"] - >> ",one,, two,three,":split(",") + >> ",one,, two,three,".split(",") = ["", "one", "", " two", "three", ""] - >> [t for t in ",one,, two,three,":by_split(",")] + >> [t for t in ",one,, two,three,".by_split(",")] = ["", "one", "", " two", "three", ""] - >> ",one,, two,three,":split_any(", ") + >> ",one,, two,three,".split_any(", ") = ["", "one", "two", "three", ""] - >> [t for t in ",one,, two,three,":by_split_any(", ")] + >> [t for t in ",one,, two,three,".by_split_any(", ")] = ["", "one", "two", "three", ""] - >> "abc":split() + >> "abc".split() = ["a", "b", "c"] - >> "one two three":split_any() + >> "one two three".split_any() = ["one", "two", "three"] - >> ", ":join(["one", "two", "three"]) + >> ", ".join(["one", "two", "three"]) = "one, two, three" - >> "":join(["one", "two", "three"]) + >> "".join(["one", "two", "three"]) = "onetwothree" - >> "+":join(["one"]) + >> "+".join(["one"]) = "one" - >> "+":join([]) + >> "+".join([]) = "" - >> "":split() + >> "".split() = [] say("Test text slicing:") - >> "abcdef":slice() + >> "abcdef".slice() = "abcdef" - >> "abcdef":slice(from=3) + >> "abcdef".slice(from=3) = "cdef" - >> "abcdef":slice(to=-2) + >> "abcdef".slice(to=-2) = "abcde" - >> "abcdef":slice(from=2, to=4) + >> "abcdef".slice(from=2, to=4) = "bcd" - >> "abcdef":slice(from=5, to=1) + >> "abcdef".slice(from=5, to=1) = "" >> house := "家" = "家" >> house.length = 1 - >> house:codepoint_names() + >> house.codepoint_names() = ["CJK Unified Ideographs-5BB6"] - >> house:utf32_codepoints() + >> house.utf32_codepoints() = [23478] - >> "🐧":codepoint_names() + >> "🐧".codepoint_names() = ["PENGUIN"] >> Text.from_codepoint_names(["not a valid name here buddy"]) = none - >> "Hello":replace("ello", "i") + >> "Hello".replace("ello", "i") = "Hi" - >> "<tag>":translate({"<"="<", ">"=">"}) + >> "<tag>".translate({"<"="<", ">"=">"}) = "<tag>" - >> "Abc":repeat(3) + >> "Abc".repeat(3) = "AbcAbcAbc" - >> "abcde":starts_with("ab") + >> "abcde".starts_with("ab") = yes - >> "abcde":starts_with("bc") + >> "abcde".starts_with("bc") = no - >> "abcde":ends_with("de") + >> "abcde".ends_with("de") = yes - >> "abcde":starts_with("cd") + >> "abcde".starts_with("cd") = no - >> "abcde":without_prefix("ab") + >> "abcde".without_prefix("ab") = "cde" - >> "abcde":without_suffix("ab") + >> "abcde".without_suffix("ab") = "abcde" - >> "abcde":without_prefix("de") + >> "abcde".without_prefix("de") = "abcde" - >> "abcde":without_suffix("de") + >> "abcde".without_suffix("de") = "abc" - >> ("hello" ++ " " ++ "Amélie"):reversed() + >> ("hello" ++ " " ++ "Amélie").reversed() = "eilémA olleh" do: say("Testing concatenation-stability:") ab := Text.from_codepoint_names(["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"])! - >> ab:codepoint_names() + >> ab.codepoint_names() = ["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"] >> ab.length = 1 a := Text.from_codepoint_names(["LATIN SMALL LETTER E"])! b := Text.from_codepoint_names(["COMBINING VERTICAL LINE BELOW"])! - >> (a++b):codepoint_names() + >> (a++b).codepoint_names() = ["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"] >> (a++b) == ab = yes @@ -279,38 +279,38 @@ func main(): >> concat4 == final = yes - >> "x":left_pad(5) + >> "x".left_pad(5) = " x" - >> "x":right_pad(5) + >> "x".right_pad(5) = "x " - >> "x":middle_pad(5) + >> "x".middle_pad(5) = " x " - >> "1234":left_pad(8, "XYZ") + >> "1234".left_pad(8, "XYZ") = "XYZX1234" - >> "1234":right_pad(8, "XYZ") + >> "1234".right_pad(8, "XYZ") = "1234XYZX" - >> "1234":middle_pad(9, "XYZ") + >> "1234".middle_pad(9, "XYZ") = "XY1234XYZ" - >> amelie:width() + >> amelie.width() = 6 cowboy := "🤠" - >> cowboy:width() + >> cowboy.width() = 2 - >> cowboy:left_pad(4) + >> cowboy.left_pad(4) = " 🤠" - >> cowboy:right_pad(4) + >> cowboy.right_pad(4) = "🤠 " - >> cowboy:middle_pad(4) + >> cowboy.middle_pad(4) = " 🤠 " - >> " one, ":trim(" ,") + >> " one, ".trim(" ,") = "one" - >> " one, ":trim(" ,", left=no) + >> " one, ".trim(" ,", left=no) = " one" - >> " one, ":trim(" ,", right=no) + >> " one, ".trim(" ,", right=no) = "one, " - >> " ":trim(" ,") + >> " ".trim(" ,") = "" - >> " ":trim(" ,", left=no) + >> " ".trim(" ,", left=no) = "" |
