Merge: Better Vim documentation on classes (with special attention to the doc of...
authorJean Privat <jean@pryen.org>
Sun, 15 Mar 2015 02:30:43 +0000 (09:30 +0700)
committerJean Privat <jean@pryen.org>
Sun, 15 Mar 2015 02:30:43 +0000 (09:30 +0700)
Calling Nitdoc() or Ctrl-D on the word `Container` will now display the following in the preview window.

~~~
# standard::Container[standard::Container::E]

A collection that contains only one item.

Used to pass arguments by reference.

Also used when one want to give a single element when a full
collection is expected

## Class hierarchy
* Direct super classes: Collection
* All super classes: Collection, Object
* Direct sub classes: ListNode
* All sub classes: ListNode

## Properties
+ count(item: E): Int  # How many occurrences of `item` are in the collection?
+ first: E  # Return the first item of the collection
+ has(item: E): Bool  # Is `item` in the collection ?
+ has_all(other: Collection[E]): Bool  # Does the collection contain at least each element of `other`?
+ has_exactly(other: Collection[E]): Bool  # Does the collection contain exactly all the elements of `other`?
+ has_only(item: E): Bool  # Is the collection contain only `item`?
+ is_empty: Bool  # Is there no item in the collection?
+ item: E  # The stored item
+ item=(item: E)  # The stored item
+ iterator: Iterator[E]  # Get a new iterator on the collection.
+ join(sep: Text): String  # Concatenate and separate each elements with `sep`.
+ length: Int  # Number of items in the collection.
+ rand: E  # Return a random element form the collection
+ to_a: Array[E]  # Build a new array from a collection
~~~

Pull-Request: #1197
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

23 files changed:
examples/rosettacode/perlin_noise.nit [new file with mode: 0644]
lib/mnit/numbers.nit
lib/mnit/tileset.nit
lib/noise.nit [new file with mode: 0644]
lib/standard/math.nit
lib/standard/stream.nit
lib/standard/string.nit
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/modelize/modelize_property.nit
src/rapid_type_analysis.nit
src/semantize/typing.nit
tests/base_attr_abstract.nit [new file with mode: 0644]
tests/bench_strfib.nit [new file with mode: 0644]
tests/sav/base_attr5_alt17.res
tests/sav/base_attr_abstract.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt1.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt2.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt3.res [new file with mode: 0644]
tests/sav/base_attr_abstract_alt4.res [new file with mode: 0644]
tests/sav/bench_strfib.res [new file with mode: 0644]
tests/sav/error_kern_attr_int.res
tests/sav/perlin_noise.res [new file with mode: 0644]

diff --git a/examples/rosettacode/perlin_noise.nit b/examples/rosettacode/perlin_noise.nit
new file mode 100644 (file)
index 0000000..a3352da
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Perlin noise
+#
+# See: <http://rosettacode.org/wiki/Perlin_noise>
+module perlin_noise
+
+redef universal Float
+       # Smoothened `self`
+       fun fade: Float do return self*self*self*(self*(self*6.0-15.0)+10.0)
+end
+
+# Improved noise
+class ImprovedNoise
+       # Permutations
+       var p: Array[Int] = [151,160,137,91,90,15,
+               131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
+               190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+               88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+               77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+               102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+               135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+               5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+               223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+               129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+               251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+               49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+               138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
+
+       # Noise value in [-1..1] at 3d coordinates `x, y, z`
+       fun noise(x, y, z: Float): Float
+       do
+               var xx = x.to_i.bin_and(255)
+               var yy = y.to_i.bin_and(255)
+               var zz = z.to_i.bin_and(255)
+
+               x -= x.floor
+               y -= y.floor
+               z -= z.floor
+
+               var u = x.fade
+               var v = y.fade
+               var w = z.fade
+
+               var a  = p[xx  ] + yy
+               var aa = p[a   ] + zz
+               var ab = p[a+1 ] + zz
+               var b  = p[xx+1] + yy
+               var ba = p[b   ] + zz
+               var bb = p[b+1 ] + zz
+
+               return w.lerp(v.lerp(u.lerp(grad(p[aa  ], x,     y,     z    ),
+                                           grad(p[ba  ], x-1.0, y,     z    )),
+                                    u.lerp(grad(p[ab  ], x,     y-1.0, z    ),
+                                           grad(p[bb  ], x-1.0, y-1.0, z    ))),
+                      v.lerp(u.lerp(grad(p[aa+1], x,     y,     z-1.0),
+                                           grad(p[ba+1], x-1.0, y,     z-1.0)),
+                                    u.lerp(grad(p[ab+1], x,     y-1.0, z-1.0),
+                                           grad(p[bb+1], x-1.0, y-1.0, z-1.0))))
+       end
+
+       # Value at a corner of the grid
+       fun grad(hash: Int, x, y, z: Float): Float
+       do
+               var h = hash.bin_and(15)
+               var u = if h < 8 then x else y
+               var v = if h < 4 then y else if h == 12 or h == 14 then x else z
+               return (if h.is_even then u else -u) + (if h.bin_and(2) == 0 then v else -v)
+       end
+end
+
+var map = new ImprovedNoise
+print map.noise(3.14, 42.0, 7.0).to_precision(17)
index e7fb184..1b7a017 100644 (file)
@@ -24,8 +24,6 @@ class NumberImages
 
        # Images from 0 to 9
        var imgs: Array[Image]
-
-       private init(imgs: Array[Image]) do self.imgs = imgs
 end
 
 redef class App
index 5bd5d4d..c9dbb16 100644 (file)
@@ -17,7 +17,7 @@ module tileset
 
 import mnit_display
 
-# Efficienly retrieve tiles in a big image
+# Efficiently retrieve tiles in a big image
 class TileSet
        # The image containing the tileset
        var image: Image
@@ -63,20 +63,20 @@ end
 class TileSetFont
        super TileSet
 
-       # Each caracter in the image
+       # Each character in the image
        # in left->right, then top->bottom order
        # Use space (' ') for holes in the tileset
        var chars: String
 
        # Additional space to insert horizontally between characters
-       # A negave value will display tile overlaped
+       # A negative value will display tile overlapped
        var hspace: Numeric = 0.0 is writable
 
        # Additional space to insert vertically between characters
-       # A negave value will display tile overlaped
+       # A negative value will display tile overlapped
        var vspace: Numeric = 0.0 is writable
 
-       # The glyph (tile) associated to the caracter `c` according to `chars`
+       # The glyph (tile) associated to the character `c` according to `chars`
        # Returns null if `c` is not in `chars`
        fun char(c: Char): nullable Image
        do
@@ -84,6 +84,30 @@ class TileSetFont
                if i == -1 then return null
                return subimages[i]
        end
+
+       # Distance between the beginning of a letter tile and the beginning of the next letter tile
+       fun advance: Numeric do return width.add(hspace)
+
+       # Distance between the beginning and the end of the longest line of `text`
+       fun text_width(text: String): Numeric
+       do
+               var lines = text.split('\n')
+               if lines.is_empty then return 0
+
+               var longest = 0
+               for line in lines do longest = longest.max(line.length)
+
+               return longest.mul(advance)
+       end
+
+       # Distance between the top of the first line to the bottom of the last line in `text`
+       fun text_height(text: Text): Numeric
+       do
+               if text.is_empty then return 0
+
+               var n_lines = text.chars.count('\n')
+               return (n_lines+1).mul(height.add(vspace)).sub(vspace)
+       end
 end
 
 redef class Display
diff --git a/lib/noise.nit b/lib/noise.nit
new file mode 100644 (file)
index 0000000..0754d4b
--- /dev/null
@@ -0,0 +1,337 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Provides the noise generators `PerlinNoise` and `InterpolatedNoise`
+module noise
+
+# 2D noise generator
+abstract class Noise
+
+       # Get the noise value at `x`, `y`
+       #
+       # The coordinates `x`, `y` can be floats of any size.
+       #
+       # Returns a value between or equal to `min` and `max`.
+       fun [](x, y: Float): Float is abstract
+
+       # Lowest possible value returned by `[]`
+       #
+       # Default at `0.0`.
+       #
+       # Require: `min < max`
+       var min = 0.0 is writable
+
+       # Highest possible value returned by `[]`
+       #
+       # Default at `1.0`.
+       #
+       # Require: `min < max`
+       var max = 1.0 is writable
+
+       # Distance between reference points of the noise
+       #
+       # Higher values will result in smoother noise and
+       # lower values will result in steeper curves.
+       #
+       # Default at `1.0`.
+       var period = 1.0 is writable
+
+       # Amplitude of the values returned by `[]`
+       fun amplitude: Float do return max - min
+
+       # Set the desired amplitude of the values returned by `[]`
+       #
+       # Will only modify `max`, `min` stays the same.
+       fun amplitude=(value: Float) do max = min + value
+
+       # Frequency of this noise
+       fun frequency: Float do return 1.0/period
+
+       # Set the frequency if this noise
+       fun frequency=(value: Float) do period = 1.0/value
+
+       # Seed to the random number generator `gradient_vector`
+       #
+       # By default, `seed` has a random value created with `Int::rand`.
+       var seed: Int = 19559.rand is lazy, writable
+end
+
+# 2D Perlin noise generator using layered `InterpolatedNoise`
+#
+# Get values at any coordinates with `[]`.
+# The behavior of this generator can be customized using its attributes `min`,
+# `max`, `period` and `seed`.
+#
+# This noise is more realistic and less smooth than the `InterpolatedNoise`.
+#
+# Due to implementation logic, the full amplitude cannot be reached.
+# In practice, only `amplitude * (1.0 - 1.0 / n_levels)` is covered.
+#
+# This implementation uses a custom deterministic pseudo random number
+# generator to set `InterpolatedNoise::seed` of the `layers`.
+# It is seeded with the local `seed` and can be further customized by
+# redefining `pseudo_random`.
+# This process do not require any state, so this class only holds the
+# attributes of the generator and does not keep any generated data.
+#
+# ## Usage example
+#
+# ~~~
+# var map = new PerlinNoise
+# map.min = 0.0
+# map.max = 16.0
+# map.period = 20.0
+# map.seed = 0
+#
+# var max = 0.0
+# var min = 100.0
+# for y in 30.times do
+#     for x in 70.times do
+#         # Get a value at x, y
+#         var val = map[x.to_f, y.to_f]
+#         printn val.to_i.to_hex
+#
+#         max = max.max(val)
+#         min = min.min(val)
+#     end
+#     print ""
+# end
+# assert max <= map.max
+# assert min >= map.min
+# ~~~
+#
+# ## Result at seed == 0
+#
+# ~~~raw
+# 76666555444322234567789abbcbbaabbaa98777766665665566667888987655444444
+# 776665554443322234567789abbbbbbbbba98777766666665556666788998654444444
+# 777766544443322234566789abbbbbbbbaa99877777776665556666788888655444444
+# 777776444443322244556679abbbccbbbaa99877777776655556666688888655444444
+# 777766444444332244555678abbbccbbbaa99887787877655556666678888654444444
+# 8887654344443333444456789abcccbbaa999877888886555555666688777654444455
+# 8887654344443333444456789abbcdcbaa999887889887655555566677777654444456
+# 7876654434444444444456778abbcccaaa999888899888655555566677777654444556
+# 78765544344445544444567789bbccca99999888899988765555566666667654445566
+# 77765444344455554445567889bbccba99999998999988765555566555666654445667
+# 7765444334555665445556788abbbba988998999999988765555566545556554456677
+# 87654444334556655455567899bbbba998888899999887766555566544556555456777
+# 87655444334566665555567899bbbbba98888899988888776555566544556555556777
+# 97655544334566665555567899abbbba98888899988888776555655544456555667777
+# 97655544444566665556667899aaaaba98888999877777776555555444456666667777
+# 866555444456666666566789999aaaaa98889998877777766556544443456667777777
+# 976555445556776666666789aa99aaaa98889998876777666555544444456677887777
+# 9765554556667777776667899999aaaa98889988876676666555443444446678888888
+# 87655555666777788766678999899aaa99889988776666666554433344446789998888
+# 876555566777788888766889998899a999889987776666666543333334456899a99899
+# 766556677877889998877888888889a99998888777666666653222233345799aaa999a
+# 6665556777777899998878988888899999999887777656666543222233446899aa999a
+# 6655456777777899999888988888889999a988887776566666532222233457899a999a
+# 665555677777789999998998888878899aa9888887765666655322222234578899aa9a
+# 665555677777789999a98888888877899aa9888887766666655322222234467899aa9a
+# 65666677667778999aaa988878877789aaa9888887776676654322222344467889aa9a
+# 55566677767788899aaa987777777789aaa9888887776666654322222344567889aaa9
+# 5566767777788889aaaa987777777789aaaa988887777666555432122344556899aaa9
+# 5567777777788889aaaa977777777789aaaa99888777766555543212234555689aaaaa
+# 5667877777889989aaa9876677777889aaaa99888777765554443212334555689aaaaa
+# ~~~
+class PerlinNoise
+       super Noise
+
+       # Desired number of `layers`
+       #
+       # This attribute must be assigned before any call to `layers` or `[]`.
+       #
+       # By default, it is the highest integer under the logarithm base 2
+       # of `amplitude`, or 4, whichever is the highest.
+       var n_layers: Int = 4.max(amplitude.abs.log_base(2.0).to_i) is lazy, writable
+
+       # Layers of `InterpolatedNoise` composing `self`
+       var layers: Array[InterpolatedNoise] is lazy do
+               var layers = new Array[InterpolatedNoise]
+
+               var max = max
+               var min = min
+               var period = period
+               var seed = seed
+               for l in n_layers.times do
+                       min = min / 2.0
+                       max = max / 2.0
+                       seed = pseudo_random(seed)
+
+                       var layer = new InterpolatedNoise
+                       layer.min = min
+                       layer.max = max
+                       layer.period = period
+                       layer.seed = seed
+                       layers.add layer
+
+                       period = period / 2.0
+               end
+               return layers
+       end
+
+       redef fun [](x, y)
+       do
+               var val = 0.0
+               for layer in layers do
+                       val += layer[x, y]
+               end
+               return val
+       end
+
+       # Deterministic pseudo random number generator
+       #
+       # Used to get seeds for layers from the previous layers or `seed`.
+       protected fun pseudo_random(value: Int): Int
+       do
+               return value + 2935391 % 954847
+       end
+end
+
+# Simple interpolated noise
+#
+# Generates smoother noise than `PerlinNoise`.
+#
+# Each coordinates at a multiple of `period` defines a random vector and
+# values in between are interpolated from these vectors.
+#
+# This implementation uses a custom deterministic pseudo random number
+# generator seeded with `seed`.
+# It can be further customized by redefining `gradient_vector`.
+# This process do not require any state, so this class only holds the
+# attributes of the generator and does not keep any generated data.
+#
+# ## Usage example
+#
+# ~~~
+# var map = new InterpolatedNoise
+# map.min = 0.0
+# map.max = 16.0
+# map.period = 20.0
+# map.seed = 0
+#
+# var max = 0.0
+# var min = 100.0
+# for y in 30.times do
+#     for x in 70.times do
+#         # Get a value at x, y
+#         var val = map[x.to_f, y.to_f]
+#         printn val.to_i.to_hex
+#
+#         max = max.max(val)
+#         min = min.min(val)
+#     end
+#     print ""
+# end
+# assert max <= map.max
+# assert min >= map.min
+# ~~~
+#
+# ## Result at seed == 0
+#
+# ~~~raw
+# 89abcddeeeeeeeddcba9877666555555555666778766555544444555566789abcddeee
+# 789abcddeeeeeeddccba887766655555555566677766555544444555566779abcddeee
+# 689abcddeeeeeeeddcba988776655555555555667666555554455555566778abccdeee
+# 678abccdeeeeeeeedccba988766655555555555666655555555555556666789abcddee
+# 5789abcddeeeeeeeddcba998776655544444555666655555555555556666789abcddee
+# 5689abcddeeeeeeeedccba98776655544444455566555555555555566666789abccdde
+# 4679abccdeeeffeeeddcba98776655444444445565555555555555666666789abbcddd
+# 4678abccdeeeffeeeedcba98876555444444444555555555566666666666689aabccdd
+# 46789abcdeeeeffeeedccb988765544443344445555566666666666666666789abccdd
+# 45789abcddeeeffeeeddcb987765544433334445555666666666666666666789abbccd
+# 45789abcddeeeeeeeeddcb987665444333333445556666666777777777766789aabccc
+# 45789abcddeeeeeeeeddca987655443333333445566666777777777777776789aabbcc
+# 45789abcddeeeeeeeedcca9876544333333333455666777777788877777767899aabbc
+# 46789abcddeeeeeeeddcba9876544333222333455667777888888888877767899aabbb
+# 46789abcdddeeeeedddcba87655433222223334566777888889998888877778899aabb
+# 5678aabcdddeeeedddccb987654332222222334566778889999999998887778899aaab
+# 5689abbcddddeedddccba9865443222222223345677889999aaaa99998877788999aaa
+# 6789abbcddddddddccbba8765432221111223345678899aaaaaaaaaa9988778889999a
+# 6789abccdddddddccbba9865433221111122344577899aabbbbbbbaaa9987788889999
+# 789abbccddddddccbba9876543211111111234567899aabbbccccbbbaa987788888899
+# 889abbccdddddccbba9886543211000001123456889abbcccccccccbba988888888888
+# 899abbcccddddcccbaa9875432211000011223457899abbcccccccccbba98888888888
+# 899abbccccddccccbba9876533211000001123456789aabccccddcccbbaa9998888888
+# 899abbccccccccccbbaa9765432111000011223456899abbcccdddcccbba9999988888
+# 899abbbcccccccccbbaa9865432211000011123456789abbccdddddcccbba999988888
+# 899aabbcccccccccbbaa9875433211100001122346789abbccddddddcccbaa99988888
+# 899aabbbcccccccbbbbaa876543211100001122345689aabccdddddddccbaaa9988887
+# 899aabbbbbbccbbbbbbaa876543221110001112335679aabccddddddddcbbaa9988877
+# 899aaabbbbbbbbbbbbbaa9765433211111111123356789abccddddddddccbaa9988777
+# 8999aaaabbbbbbbbbbaaa9765433221111111122356789abccdddeedddccbaa9988777
+# ~~~
+class InterpolatedNoise
+       super Noise
+
+       redef fun [](x, y)
+       do
+               x = x/period
+               y = y/period
+
+               # Get grid coordinates
+               var x0 = if x > 0.0 then x.to_i else x.to_i - 1
+               var x1 = x0 + 1
+               var y0 = if y > 0.0 then y.to_i else y.to_i - 1
+               var y1 = y0 + 1
+
+               # Position in grid
+               var sx = x - x0.to_f
+               var sy = y - y0.to_f
+
+               # Interpolate
+               var n0 = gradient_dot_product(x0, y0, x, y)
+               var n1 = gradient_dot_product(x1, y0, x, y)
+               var ix0 = sx.lerp(n0, n1)
+               n0 = gradient_dot_product(x0, y1, x, y)
+               n1 = gradient_dot_product(x1, y1, x, y)
+               var ix1 = sx.lerp(n0, n1)
+               var val = sy.lerp(ix0, ix1)
+
+               # Return value in [min...max] from val in [-0.5...0.5]
+               val += 0.5
+               return val.lerp(min, max)
+       end
+
+       # Get the component `w` of the gradient unit vector at `x`, `y`
+       #
+       # `w` at 0 targets the X axis, at 1 the Y axis.
+       #
+       # Returns a value between -1.0 and 1.0.
+       #
+       # Require: `w == 0 or w == 1`
+       protected fun gradient_vector(x, y, w: Int): Float
+       do
+               assert w == 0 or w == 1
+
+               # Use our own deterministic pseudo random number generator
+               #
+               # These magic prime numbers were determined good enough by
+               # non-emperical experimentation. They may need to be changed/improved.
+               var i = 17957*seed + 45127*x + 22613*y
+               var mod = 19031
+
+               var angle = (i%mod).to_f*2.0*pi/mod.to_f
+               if w == 0 then return angle.cos
+               return angle.sin
+       end
+
+       private fun gradient_dot_product(ix, iy: Int, x, y: Float): Float
+       do
+               var dx = x - ix.to_f
+               var dy = y - iy.to_f
+
+               return dx*gradient_vector(ix, iy, 0) + dy*gradient_vector(ix, iy, 1)
+       end
+end
index 4b6ed35..2b55dca 100644 (file)
@@ -167,12 +167,18 @@ redef class Float
        #     #assert 0.0.pow(9.0) == 0.0
        fun pow(e: Float): Float is extern "kernel_Float_Float_pow_1"
 
-       # Returns the logarithm of `self`.
+       # Natural logarithm of `self`.
        #
        #     assert 0.0.log.is_inf == -1
        #     #assert 1.0.log == 0.0
        fun log: Float is extern "kernel_Float_Float_log_0"
 
+       # Logarithm of `self` to base `base`.
+       #
+       #     assert 100.0.log_base(10.0) == 2.0
+       #     assert 256.0.log_base(2.0) == 8.0
+       fun log_base(base: Float): Float do return log/base.log
+
        # Returns *e* raised to `self`.
        fun exp: Float is extern "kernel_Float_Float_exp_0"
 
@@ -195,7 +201,7 @@ redef class Float
        #     assert -1.34.round == -1.0
        #     assert -1.67.round == -2.0
        fun round: Float is extern "round"
-       
+
        # Returns a random `Float` in `[0.0 .. self[`.
        fun rand: Float is extern "kernel_Float_Float_rand_0"
 
@@ -220,6 +226,16 @@ redef class Float
        end
 
        private fun is_inf_extern: Bool is extern "isinf"
+
+       # Linear interpolation between `a` and `b` using `self` as weight
+       #
+       # ~~~
+       # assert  0.0.lerp(0.0, 128.0) == 0.0
+       # assert  0.5.lerp(0.0, 128.0) == 64.0
+       # assert  1.0.lerp(0.0, 128.0) == 128.0
+       # assert -0.5.lerp(0.0, 128.0) == -64.0
+       # ~~~
+       fun lerp(a, b: Float): Float do return (1.0 - self) * a + self * b
 end
 
 redef class Collection[ E ]
@@ -238,6 +254,15 @@ redef class Collection[ E ]
        end
 end
 
+redef class SequenceRead[E]
+       # Optimized for large collections using `[]`
+       redef fun rand
+       do
+               assert not is_empty
+               return self[length.rand]
+       end
+end
+
 redef class Sys
        init
        do
index 1e4ba18..7df1bd1 100644 (file)
@@ -404,6 +404,7 @@ abstract class BufferedReader
                if _buffer_pos + i >= _buffer.length then
                        var from = _buffer_pos
                        _buffer_pos = _buffer.length
+                       if from == 0 then return _buffer.to_s
                        return _buffer.substring_from(from).to_s
                end
                _buffer_pos += i
index b99fc99..e78f655 100644 (file)
@@ -1180,10 +1180,14 @@ class FlatString
        #              String Specific Methods           #
        ##################################################
 
-       private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
+       # Low-level creation of a new string with given data.
+       #
+       # `items` will be used as is, without copy, to retrieve the characters of the string.
+       # Aliasing issues is the responsibility of the caller.
+       private init with_infos(items: NativeString, length: Int, from: Int, to: Int)
        do
                self.items = items
-               length = len
+               self.length = length
                index_from = from
                index_to = to
        end
@@ -1627,6 +1631,20 @@ class FlatBuffer
        # Create a new empty string.
        init do end
 
+       # Low-level creation a new buffer with given data.
+       #
+       # `items` will be used as is, without copy, to store the characters of the buffer.
+       # Aliasing issues is the responsibility of the caller.
+       #
+       # If `items` is shared, `written` should be set to true after the creation
+       # so that a modification will do a copy-on-write.
+       private init with_infos(items: NativeString, capacity, length: Int)
+       do
+               self.items = items
+               self.length = length
+               self.capacity = capacity
+       end
+
        # Create a new string copied from `s`.
        init from(s: Text)
        do
@@ -1651,7 +1669,6 @@ class FlatBuffer
        init with_capacity(cap: Int)
        do
                assert cap >= 0
-               # _items = new NativeString.calloc(cap)
                items = new NativeString(cap+1)
                capacity = cap
                length = 0
@@ -1695,11 +1712,10 @@ class FlatBuffer
                if from < 0 then from = 0
                if count > length then count = length
                if from < count then
-                       var r = new FlatBuffer.with_capacity(count - from)
-                       while from < count do
-                               r.chars.push(items[from])
-                               from += 1
-                       end
+                       var len = count - from
+                       var r_items = new NativeString(len)
+                       items.copy_to(r_items, len, from, 0)
+                       var r = new FlatBuffer.with_infos(r_items, len, len)
                        return r
                else
                        return new FlatBuffer
index 23927b6..aa57850 100644 (file)
@@ -1834,6 +1834,15 @@ redef class MMethodDef
                var modelbuilder = v.compiler.modelbuilder
                var val = constant_value
                var node = modelbuilder.mpropdef2node(self)
+
+               if is_abstract then
+                       var cn = v.class_name_string(arguments.first)
+                       v.current_node = node
+                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+                       v.add_raw_abort
+                       return null
+               end
+
                if node isa APropdef then
                        var oldnode = v.current_node
                        v.current_node = node
@@ -1893,13 +1902,6 @@ end
 redef class AMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               if mpropdef.is_abstract then
-                       var cn = v.class_name_string(arguments.first)
-                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
-                       v.add_raw_abort
-                       return
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
index bb0b3b3..dd61428 100644 (file)
@@ -413,6 +413,14 @@ class NaiveInterpreter
                var val = mpropdef.constant_value
 
                var node = modelbuilder.mpropdef2node(mpropdef)
+               if mpropdef.is_abstract then
+                       if node != null then
+                               self.frames.unshift new Frame(node, mpropdef, args)
+                       end
+                       fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
+                       abort
+               end
+
                if node isa APropdef then
                        self.parameter_check(node, mpropdef, args)
                        return node.call(self, mpropdef, args)
@@ -725,11 +733,6 @@ redef class AMethPropdef
                        v.write_variable(variable, arguments[i+1])
                end
 
-               if mpropdef.is_abstract then
-                       v.fatal("Abstract method `{mpropdef.mproperty.name}` called on `{arguments.first.mtype}`")
-                       abort
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
@@ -1163,7 +1166,9 @@ redef class AAttrPropdef
                        evaluate_expr(v, recv)
                        return
                end
-               var mtype = self.mpropdef.static_mtype.as(not null)
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return
+               var mtype = mpropdef.static_mtype.as(not null)
                mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
                if mtype isa MNullableType then
                        v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
index 35a50c3..a4d1141 100644 (file)
@@ -955,32 +955,31 @@ redef class AAttrPropdef
        redef fun build_property(modelbuilder, mclassdef)
        do
                var mclass = mclassdef.mclass
+               var nid2 = n_id2
+               var name = nid2.text
+
+               var atabstract = self.get_single_annotation("abstract", modelbuilder)
+               if atabstract == null then
+                       if mclass.kind == interface_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
+                       else if mclass.kind == enum_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
+                       else if mclass.kind == extern_kind then
+                               modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+                       end
 
-               var name: String
-               name = self.n_id2.text
-
-               if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
-               else if mclass.kind == enum_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
-               else if mclass.kind == extern_kind then
-                       modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+                       var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
+                       var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
+                       self.mpropdef = mpropdef
+                       modelbuilder.mpropdef2npropdef[mpropdef] = self
                end
 
-               # New attribute style
-               var nid2 = self.n_id2
-               var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
-               var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
-               self.mpropdef = mpropdef
-               modelbuilder.mpropdef2npropdef[mpropdef] = self
-
                var readname = name
                var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
                if mreadprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
                        mreadprop = new MMethod(mclassdef, readname, mvisibility)
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
-                       mreadprop.deprecation = mprop.deprecation
                else
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
@@ -991,10 +990,16 @@ redef class AAttrPropdef
                self.mreadpropdef = mreadpropdef
                modelbuilder.mpropdef2npropdef[mreadpropdef] = self
                set_doc(mreadpropdef, modelbuilder)
-               mpropdef.mdoc = mreadpropdef.mdoc
+               if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc
+               if atabstract != null then mreadpropdef.is_abstract = true
 
                has_value = n_expr != null or n_block != null
 
+               if atabstract != null and has_value then
+                       modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value")
+                       return
+               end
+
                var atnoinit = self.get_single_annotation("noinit", modelbuilder)
                if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder)
                if atnoinit != null then
@@ -1003,6 +1008,10 @@ redef class AAttrPropdef
                                modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value")
                                return
                        end
+                       if atabstract != null then
+                               modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract")
+                               return
+                       end
                end
 
                var atlazy = self.get_single_annotation("lazy", modelbuilder)
@@ -1054,7 +1063,7 @@ redef class AAttrPropdef
                        end
                        mwriteprop = new MMethod(mclassdef, writename, mvisibility)
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
-                       mwriteprop.deprecation = mprop.deprecation
+                       mwriteprop.deprecation = mreadprop.deprecation
                else
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
                        if atwritable != null then
@@ -1066,18 +1075,19 @@ redef class AAttrPropdef
                var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
                self.mwritepropdef = mwritepropdef
                modelbuilder.mpropdef2npropdef[mwritepropdef] = self
-               mwritepropdef.mdoc = mpropdef.mdoc
+               mwritepropdef.mdoc = mreadpropdef.mdoc
+               if atabstract != null then mwritepropdef.is_abstract = true
        end
 
        redef fun build_signature(modelbuilder)
        do
+               var mreadpropdef = self.mreadpropdef
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skipped
-               var mclassdef = mpropdef.mclassdef
+               if mreadpropdef == null then return # Error thus skipped
+               var mclassdef = mreadpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var mtype: nullable MType = null
 
-               var mreadpropdef = self.mreadpropdef
 
                var ntype = self.n_type
                if ntype != null then
@@ -1087,7 +1097,7 @@ redef class AAttrPropdef
 
                var inherited_type: nullable MType = null
                # Inherit the type from the getter (usually an abstract getter)
-               if mreadpropdef != null and not mreadpropdef.is_intro then
+               if not mreadpropdef.is_intro then
                        var msignature = mreadpropdef.mproperty.intro.msignature
                        if msignature == null then return # Error, thus skipped
                        inherited_type = msignature.return_mtype
@@ -1122,7 +1132,7 @@ redef class AAttrPropdef
                                        var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
                                        if cla != null then mtype = cla.mclass_type
                                else
-                                       modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
+                                       modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}. Implicit typing allowed only for literals and new.")
                                end
 
                                if mtype == null then return
@@ -1137,13 +1147,15 @@ redef class AAttrPropdef
                end
 
                if mtype == null then
-                       modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
+                       modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}")
                        return
                end
 
-               mpropdef.static_mtype = mtype
+               if mpropdef != null then
+                       mpropdef.static_mtype = mtype
+               end
 
-               if mreadpropdef != null then
+               do
                        var msignature = new MSignature(new Array[MParameter], mtype)
                        mreadpropdef.msignature = msignature
                end
index f5c1dc2..a6cd32e 100644 (file)
@@ -389,7 +389,6 @@ class RapidTypeAnalysis
                if mproperty.mpropdefs.length <= 1 then return
                # If all definitions of a method are live, we can remove the definition of the totry set
                for d in mproperty.mpropdefs do
-                       if d.is_abstract then continue
                        if not live_methoddefs.has(d) then return
                end
                #print "full property: {mpropdef.mproperty} for {mpropdef.mproperty.mpropdefs.length} definitions"
index 343edbd..6a6d943 100644 (file)
@@ -597,6 +597,8 @@ end
 redef class AAttrPropdef
        redef fun do_typing(modelbuilder: ModelBuilder)
        do
+               if not has_value then return
+
                var mpropdef = self.mpropdef.as(not null)
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
diff --git a/tests/base_attr_abstract.nit b/tests/base_attr_abstract.nit
new file mode 100644 (file)
index 0000000..34c1e4c
--- /dev/null
@@ -0,0 +1,50 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+interface Foo
+       var a: Object is abstract
+       #alt1#var b = 1 is abstract
+       #alt2#var b is abstract, noautoinit
+end
+
+class Bar
+       super Foo
+       redef var a
+end
+
+class Baz
+       super Foo
+       redef fun a do return 100
+       redef fun a=(x) do (101).output
+end
+
+class FooBar
+       super Foo
+end
+
+var f: Foo = new Bar(1)
+f.a.output
+f.a = 2
+f.a.output
+
+f = new Baz
+f.a.output
+f.a = 3
+f.a.output
+
+f = new FooBar
+#alt3#f.a.output
+#alt4#f.a = 4
diff --git a/tests/bench_strfib.nit b/tests/bench_strfib.nit
new file mode 100644 (file)
index 0000000..2357686
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Function that recursively allocate and concatenate strings.
+#
+# It is related to the Fibonacci sequence since:
+#
+# * `strfib(0).length == 0`
+# * `strfib(1).length == 2`
+# * `strfib(n).length = strfib(n-1).length + strfib(n-2).length + 2`
+fun strfib(i: Int): String
+do
+       if i == 0 then return ""
+       if i == 1 then return "()"
+       return "({strfib(i-2)}{strfib(i-1)})"
+end
+
+var quiet = false
+if args.has("-q") then
+       quiet = true
+       args.remove("-q")
+end
+
+var n = 5
+if args.length > 0 then n = args.first.to_i
+var res = strfib(n)
+
+if quiet then
+       print res.length
+else
+       print res
+end
index 03ea677..86f9e7a 100644 (file)
@@ -1,2 +1 @@
 alt/base_attr5_alt17.nit:47,12--14: Error: No property B::bar is inherited. Remove the redef keyword to define a new property.
-alt/base_attr5_alt17.nit:47,12--14: Error: Untyped attribute base_attr5_alt17#B#_bar
diff --git a/tests/sav/base_attr_abstract.res b/tests/sav/base_attr_abstract.res
new file mode 100644 (file)
index 0000000..36f0f1a
--- /dev/null
@@ -0,0 +1,5 @@
+1
+2
+100
+101
+100
diff --git a/tests/sav/base_attr_abstract_alt1.res b/tests/sav/base_attr_abstract_alt1.res
new file mode 100644 (file)
index 0000000..6971473
--- /dev/null
@@ -0,0 +1 @@
+alt/base_attr_abstract_alt1.nit:19,15--22: Error: `abstract` attributes cannot have an initial value
diff --git a/tests/sav/base_attr_abstract_alt2.res b/tests/sav/base_attr_abstract_alt2.res
new file mode 100644 (file)
index 0000000..1ed3535
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_attr_abstract_alt2.nit:20,6: Error: Untyped attribute base_attr_abstract_alt2#Foo#b
+alt/base_attr_abstract_alt2.nit:20,21--30: Error: `noautoinit` attributes cannot be abstract
diff --git a/tests/sav/base_attr_abstract_alt3.res b/tests/sav/base_attr_abstract_alt3.res
new file mode 100644 (file)
index 0000000..4052598
--- /dev/null
@@ -0,0 +1,6 @@
+Runtime error: Abstract method `a` called on `FooBar` (alt/base_attr_abstract_alt3.nit:18)
+1
+2
+100
+101
+100
diff --git a/tests/sav/base_attr_abstract_alt4.res b/tests/sav/base_attr_abstract_alt4.res
new file mode 100644 (file)
index 0000000..09d65f9
--- /dev/null
@@ -0,0 +1,6 @@
+Runtime error: Abstract method `a=` called on `FooBar` (alt/base_attr_abstract_alt4.nit:18)
+1
+2
+100
+101
+100
diff --git a/tests/sav/bench_strfib.res b/tests/sav/bench_strfib.res
new file mode 100644 (file)
index 0000000..4e58388
--- /dev/null
@@ -0,0 +1 @@
+((()(()))((())(()(()))))
index f870a60..004eee0 100644 (file)
@@ -1 +1 @@
-error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the interface Int.
+error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the enum class Int.
diff --git a/tests/sav/perlin_noise.res b/tests/sav/perlin_noise.res
new file mode 100644 (file)
index 0000000..58a5cf2
--- /dev/null
@@ -0,0 +1 @@
+0.13691995878400010