Merge: app.nit UI: Intro an attribute to toggle password entry on TextFields
authorJean Privat <jean@pryen.org>
Thu, 10 Mar 2016 04:55:45 +0000 (23:55 -0500)
committerJean Privat <jean@pryen.org>
Thu, 10 Mar 2016 04:55:45 +0000 (23:55 -0500)
The attribute `TextField::is_password` toggles hiding the content of the field using platform specific services. So on Android, not only is the text hidden but it also notifies the soft keyboard to behave accordingly.

In the future, we could add alternative modes for email entry and more.

Pull-Request: #1978
Reviewed-by: Jean Privat <jean@pryen.org>

25 files changed:
benchmarks/markdown/engines/nitmd/nitmd.nit
lib/core/file.nit
lib/core/math.nit
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/markdown/markdown.nit
lib/markdown/test_markdown.nit
lib/pthreads/concurrent_collections.nit
src/compiler/abstract_compiler.nit
src/compiler/compiler_ffi/compiler_ffi.nit
src/doc/doc_phases/doc_console.nit
src/frontend/check_annotation.nit
src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit
src/interpreter/naive_interpreter.nit
src/modelize/modelize_property.nit
src/nitni/nitni_utilities.nit
src/semantize/typing.nit
tests/base_attr_optional.nit [new file with mode: 0644]
tests/error_arg.nit [new file with mode: 0644]
tests/sav/base_attr_optional.res [new file with mode: 0644]
tests/sav/base_meth_call_alt1.res
tests/sav/error_arg.res [new file with mode: 0644]
tests/sav/error_arg_alt1.res [new file with mode: 0644]
tests/sav/nitcg/test_text_stat.res
tests/sav/test_text_stat.res

index 2db91b2..09c7d07 100644 (file)
@@ -19,6 +19,7 @@ var n = args[1].to_i
 
 var str = file.to_path.read_all
 var parser = new MarkdownProcessor
+parser.no_location = true
 for i in [1..n] do
        print parser.process(str)
 end
index 66b81aa..0454f1d 100644 (file)
@@ -1145,11 +1145,16 @@ redef class String
 
        # Create a directory (and all intermediate directories if needed)
        #
+       # The optional `mode` parameter specifies the permissions of the directory,
+       # the default value is `0o777`.
+       #
        # Return an error object in case of error.
        #
        #    assert "/etc/".mkdir != null
-       fun mkdir: nullable Error
+       fun mkdir(mode: nullable Int): nullable Error
        do
+               mode = mode or else 0o777
+
                var dirs = self.split_with("/")
                var path = new FlatBuffer
                if dirs.is_empty then return null
@@ -1162,7 +1167,7 @@ redef class String
                        if d.is_empty then continue
                        path.append(d)
                        path.add('/')
-                       var res = path.to_s.to_cstring.file_mkdir
+                       var res = path.to_s.to_cstring.file_mkdir(mode)
                        if not res and error == null then
                                error = new IOError("Cannot create directory `{path}`: {sys.errno.strerror}")
                        end
@@ -1326,7 +1331,7 @@ redef class NativeString
                return stat_element;
        `}
 
-       private fun file_mkdir: Bool `{ return !mkdir(self, 0777); `}
+       private fun file_mkdir(mode: Int): Bool `{ return !mkdir(self, mode); `}
 
        private fun rmdir: Bool `{ return !rmdir(self); `}
 
index 36cf3fe..0f8325f 100644 (file)
@@ -128,7 +128,7 @@ redef class Int
        #
        # assert 3.is_prime
        # assert not 1.is_prime
-       # assert not 12.is_prime
+       # assert not 15.is_prime
        fun is_prime: Bool
        do
                if self == 2 then
@@ -136,7 +136,7 @@ redef class Int
                else if self <= 1 or self.is_even then
                        return false
                end
-               for i in [3..self.sqrt[ do
+               for i in [3..self.sqrt] do
                        if self % i == 0 then return false
                end
                return true
index 0d2848c..859808a 100644 (file)
@@ -615,7 +615,7 @@ abstract class Text
                                b.append("\\\\")
                        else if c.code_point < 32 then
                                b.add('\\')
-                               var oct = c.code_point.to_base(8, false)
+                               var oct = c.code_point.to_base(8)
                                # Force 3 octal digits since it is the
                                # maximum allowed in the C specification
                                if oct.length == 1 then
@@ -689,7 +689,7 @@ abstract class Text
                                b.add('\\')
                                b.add(c)
                        else if c.code_point < 32 or c == ';' or c == '|' or c == '\\' or c == '=' then
-                               b.append("?{c.code_point.to_base(16, false)}")
+                               b.append("?{c.code_point.to_base(16)}")
                        else
                                b.add(c)
                        end
@@ -1569,9 +1569,9 @@ redef class Int
        # Returns a string describing error number
        fun strerror: String do return strerror_ext.to_s
 
-       # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
+       # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if negative).
        # assume < to_c max const of char
-       private fun fill_buffer(s: Buffer, base: Int, signed: Bool)
+       private fun fill_buffer(s: Buffer, base: Int)
        do
                var n: Int
                # Sign
@@ -1603,14 +1603,30 @@ redef class Int
                snprintf(nstr, strlen, "%ld", self);
        `}
 
-       # return displayable int in base base and signed
-       fun to_base(base: Int, signed: Bool): String is abstract
+       # String representation of `self` in the given `base`
+       #
+       # ~~~
+       # assert 15.to_base(10) == "15"
+       # assert 15.to_base(16) == "f"
+       # assert 15.to_base(2) == "1111"
+       # assert (-10).to_base(3) == "-101"
+       # ~~~
+       fun to_base(base: Int): String
+       do
+               var l = digit_count(base)
+               var s = new Buffer
+               s.enlarge(l)
+               for x in [0..l[ do s.add(' ')
+               fill_buffer(s, base)
+               return s.to_s
+       end
+
 
        # return displayable int in hexadecimal
        #
        #     assert 1.to_hex  == "1"
        #     assert (-255).to_hex  == "-ff"
-       fun to_hex: String do return to_base(16,false)
+       fun to_hex: String do return to_base(16)
 end
 
 redef class Float
@@ -1887,7 +1903,11 @@ redef class Collection[E]
        #     assert [1, 2, 3].join(":")    == "1:2:3"
        #     assert [1..3].join(":")       == "1:2:3"
        #     assert [1..3].join            == "123"
-       fun join(separator: nullable Text): String
+       #
+       # if `last_separator` is given, then it is used to separate the last element.
+       #
+       #     assert [1, 2, 3, 4].join(", ", " and ")    == "1, 2, 3 and 4"
+       fun join(separator: nullable Text, last_separator: nullable Text): String
        do
                if is_empty then return ""
 
@@ -1898,13 +1918,19 @@ redef class Collection[E]
                var e = i.item
                if e != null then s.append(e.to_s)
 
+               if last_separator == null then last_separator = separator
+
                # Concat other items
                i.next
                while i.is_ok do
-                       if separator != null then s.append(separator)
                        e = i.item
-                       if e != null then s.append(e.to_s)
                        i.next
+                       if i.is_ok then
+                               if separator != null then s.append(separator)
+                       else
+                               if last_separator != null then s.append(last_separator)
+                       end
+                       if e != null then s.append(e.to_s)
                end
                return s.to_s
        end
index 47b8608..18fdabe 100644 (file)
@@ -52,20 +52,25 @@ redef class FlatText
        fun char_to_byte_index(index: Int): Int do
                var dpos = index - _position
                var b = _bytepos
+               var its = _items
 
-               if dpos == 0 then return b
                if dpos == 1 then
-                       b += _items.length_of_char_at(b)
+                       if its[b] & 0x80u8 == 0x00u8 then
+                               b += 1
+                       else
+                               b += its.length_of_char_at(b)
+                       end
                        _bytepos = b
                        _position = index
                        return b
                end
                if dpos == -1 then
-                       b = _items.find_beginning_of_char_at(b - 1)
+                       b = its.find_beginning_of_char_at(b - 1)
                        _bytepos = b
                        _position = index
                        return b
                end
+               if dpos == 0 then return b
 
                var ln = _length
                var pos = _position
@@ -74,7 +79,6 @@ redef class FlatText
                var delta_end = (ln - 1) - index
                var delta_cache = (pos - index).abs
                var min = delta_begin
-               var its = _items
 
                if delta_cache < min then min = delta_cache
                if delta_end < min then min = delta_end
@@ -292,7 +296,49 @@ redef class FlatText
        end
 
        redef fun [](index) do
-               assert index >= 0 and index < _length
+               var len = _length
+
+               # Statistically:
+               # * ~70% want the next char
+               # * ~23% want the previous
+               # * ~7% want the same char
+               #
+               # So it makes sense to shortcut early. And early is here.
+               var dpos = index - _position
+               var b = _bytepos
+               if dpos == 1 and index < len - 1 then
+                       var its = _items
+                       var c = its[b]
+                       if c & 0x80u8 == 0x00u8 then
+                               # We want the next, and current is easy.
+                               # So next is easy to find!
+                               b += 1
+                               _position = index
+                               _bytepos = b
+                               # The rest will be done by `dpos==0` bellow.
+                               dpos = 0
+                       end
+               else if dpos == -1 and index > 1 then
+                       var its = _items
+                       var c = its[b-1]
+                       if c & 0x80u8 == 0x00u8 then
+                               # We want the previous, and it is easy.
+                               b -= 1
+                               dpos = 0
+                               _position = index
+                               _bytepos = b
+                               return c.ascii
+                       end
+               end
+               if dpos == 0 then
+                       # We know what we want (+0 or +1) just get it now!
+                       var its = _items
+                       var c = its[b]
+                       if c & 0x80u8 == 0x00u8 then return c.ascii
+                       return items.char_at(b)
+               end
+
+               assert index >= 0 and index < len
                return fetch_char_at(index)
        end
 
@@ -1250,6 +1296,10 @@ redef class NativeString
        #
        # Very unsafe, make sure to have room for this char prior to calling this function.
        private fun set_char_at(pos: Int, c: Char) do
+               if c.code_point < 128 then
+                       self[pos] = c.code_point.to_b
+                       return
+               end
                var ln = c.u8char_len
                native_set_char(pos, c, ln)
        end
@@ -1280,14 +1330,6 @@ redef class NativeString
 end
 
 redef class Int
-       redef fun to_base(base, signed)
-       do
-               var l = digit_count(base)
-               var s = new FlatBuffer.from(" " * l)
-               fill_buffer(s, base, signed)
-               return s.to_s
-       end
-
        # return displayable int in base 10 and signed
        #
        #     assert 1.to_s            == "1"
index 4c2428f..d63f3e4 100644 (file)
@@ -133,6 +133,14 @@ class MarkdownProcessor
        # ~~~
        var ext_mode = true
 
+       # Disable attaching MDLocation to Tokens
+       #
+       # Locations are useful for some tools but they may
+       # cause an important time and space overhead.
+       #
+       # Default = `false`
+       var no_location = false is writable
+
        init do self.emitter = new MarkdownEmitter(self)
 
        # Process the mardown `input` string and return the processed output.
@@ -397,11 +405,16 @@ class MarkdownProcessor
                        c2 = ' '
                end
 
-               var loc = new MDLocation(
-                       current_loc.line_start,
-                       current_loc.column_start + pos,
-                       current_loc.line_start,
-                       current_loc.column_start + pos)
+               var loc
+               if no_location then
+                       loc = null
+               else
+                       loc = new MDLocation(
+                               current_loc.line_start,
+                               current_loc.column_start + pos,
+                               current_loc.line_start,
+                               current_loc.column_start + pos)
+               end
 
                if c == '*' then
                        if c1 == '*' then
@@ -610,10 +623,12 @@ class MarkdownEmitter
        end
 
        # Append `c` to current buffer.
-       fun addc(c: Char) do add c.to_s
+       fun addc(c: Char) do
+               current_buffer.add c
+       end
 
        # Append a "\n" line break.
-       fun addn do add "\n"
+       fun addn do addc '\n'
 end
 
 # A Link Reference.
@@ -1938,7 +1953,7 @@ end
 abstract class Token
 
        # Location of `self` in the original input.
-       var location: MDLocation
+       var location: nullable MDLocation
 
        # Position of `self` in input independant from lines.
        var pos: Int
@@ -2330,18 +2345,11 @@ redef class Text
                        if c == '\\' and pos + 1 < length then
                                pos = escape(out, self[pos + 1], pos)
                        else
-                               var end_reached = false
-                               for n in nend do
-                                       if c == n then
-                                               end_reached = true
-                                               break
-                                       end
-                               end
-                               if end_reached then break
+                               for n in nend do if c == n then break label
                                out.add c
                        end
                        pos += 1
-               end
+               end label
                if pos == length then return -1
                return pos
        end
index 53c355f..8a2c7a0 100644 (file)
@@ -2830,7 +2830,7 @@ class TestTokenProcessor
        redef fun token_at(input, pos) do
                var token = super
                if token isa TokenNone then return token
-               var res = "{token.class_name} at {token.location}"
+               var res = "{token.class_name} at {token.location or else "?"}"
                var exp = test_stack.shift
                print ""
                print "EXP {exp}"
index d55bad0..e0c53af 100644 (file)
@@ -161,10 +161,10 @@ abstract class ConcurrentCollection[E]
                return r
        end
 
-       redef fun join(sep)
+       redef fun join(sep, last_sep)
        do
                mutex.lock
-               var r = real_collection.join(sep)
+               var r = real_collection.join(sep, last_sep)
                mutex.unlock
                return r
        end
index 347aa11..1fad40d 100644 (file)
@@ -2046,6 +2046,8 @@ redef class MMethodDef
                else if node isa AClassdef then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
+               else if node == null then
+                       return true
                else
                        abort
                end
@@ -3094,7 +3096,18 @@ redef class AAttrPropdef
                        v.assign(v.frame.returnvar.as(not null), res)
                else if mpropdef == mwritepropdef then
                        assert arguments.length == 2
-                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+                       var recv = arguments.first
+                       var arg = arguments[1]
+                       if is_optional then
+                               var value = v.new_var(self.mpropdef.static_mtype.as(not null))
+                               v.add("if ({arg} == NULL) \{")
+                               v.assign(value, evaluate_expr(v, recv))
+                               v.add("\} else \{")
+                               v.assign(value, arg)
+                               v.add("\}")
+                               arg = value
+                       end
+                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
                        if is_lazy then
                                var ret = self.mtype
                                var useiset = not ret.is_c_primitive and not ret isa MNullableType
index 79c2f14..7a536b1 100644 (file)
@@ -262,18 +262,20 @@ redef class MExplicitCast
                #
 
                # In nitni files, declare internal function as extern
-               var full_friendly_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind})"
+               var full_friendly_csignature = "int {v.compiler.mainmodule.c_name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind})"
                ccu.header_decl.add("extern {full_friendly_csignature};\n")
 
                # In nitni files, #define friendly as extern
-               ccu.header_decl.add("#define {check_cname} {v.compiler.mainmodule.name}___{check_cname}\n")
+               ccu.header_decl.add "#ifndef {check_cname}\n"
+               ccu.header_decl.add "#define {check_cname} {v.compiler.mainmodule.c_name}___{check_cname}\n"
+               ccu.header_decl.add "#endif\n"
 
                if compile_implementation_too then
                        # Internally, implement internal function
                        var nitni_visitor = v.compiler.new_visitor
                        nitni_visitor.frame = v.frame
 
-                       var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
+                       var full_internal_csignature = "int {v.compiler.mainmodule.c_name }___{from.mangled_cname}_is_a_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
 
                        nitni_visitor.add_decl("/* nitni check for {from} to {to} */")
                        nitni_visitor.add_decl("{full_internal_csignature} \{")
@@ -289,7 +291,9 @@ redef class MExplicitCast
                # special checks
                if from == to.as_nullable then
                        # format A_is_null
-                       ccu.header_decl.add("#define {from.mangled_cname}_is_null !{from.mangled_cname}_is_a_{to.mangled_cname}\n")
+                       ccu.header_decl.add "#ifndef {from.mangled_cname}_is_null\n"
+                       ccu.header_decl.add "#define {from.mangled_cname}_is_null !{from.mangled_cname}_is_a_{to.mangled_cname}\n"
+                       ccu.header_decl.add "#endif\n"
                end
 
                #
@@ -297,18 +301,20 @@ redef class MExplicitCast
                #
 
                # In nitni files, declare internal function as extern
-               full_friendly_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})"
+               full_friendly_csignature = "{to.cname_blind} {v.compiler.mainmodule.c_name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})"
                ccu.header_decl.add("extern {full_friendly_csignature};\n")
 
                # In nitni files, #define friendly as extern
-               ccu.header_decl.add("#define {cast_cname} {v.compiler.mainmodule.name}___{cast_cname}\n")
+               ccu.header_decl.add "#ifndef {cast_cname}\n"
+               ccu.header_decl.add "#define {cast_cname} {v.compiler.mainmodule.c_name}___{cast_cname}\n"
+               ccu.header_decl.add "#endif\n"
 
                if compile_implementation_too then
                        # Internally, implement internal function
                        var nitni_visitor = v.compiler.new_visitor
                        nitni_visitor.frame = v.frame
 
-                       var full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
+                       var full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.c_name }___{from.mangled_cname}_as_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
                        nitni_visitor.add_decl("/* nitni cast for {from} to {to} */")
                        nitni_visitor.add_decl("{full_internal_csignature} \{")
 
@@ -333,12 +339,16 @@ redef class MExplicitCast
                # special casts
                if from.as_nullable == to then
                        # format A_as_nullable
-                       ccu.header_decl.add("#define {from.mangled_cname}_as_nullable {from.mangled_cname}_as_{to.mangled_cname}\n")
+                       ccu.header_decl.add "#ifndef {from.mangled_cname}_as_nullable\n"
+                       ccu.header_decl.add "#define {from.mangled_cname}_as_nullable {from.mangled_cname}_as_{to.mangled_cname}\n"
+                       ccu.header_decl.add "#endif\n"
                end
 
                if from == to.as_nullable then
                        # format A_as_nullable
-                       ccu.header_decl.add("#define {to.mangled_cname}_as_not_nullable {from.mangled_cname}_as_{to.mangled_cname}\n")
+                       ccu.header_decl.add "#ifndef {to.mangled_cname}_as_not_nullable\n"
+                       ccu.header_decl.add "#define {to.mangled_cname}_as_not_nullable {from.mangled_cname}_as_{to.mangled_cname}\n"
+                       ccu.header_decl.add "#endif\n"
                end
        end
 end
index 070b86b..56fbc74 100644 (file)
@@ -551,7 +551,7 @@ private class Pager
                        else if c == '`' then
                                b.append("'")
                        else if c.code_point < 32 then
-                               b.append("\\{c.code_point.to_base(8, false)}")
+                               b.append("\\{c.code_point.to_base(8)}")
                        else
                                b.add(c)
                        end
index d640ac2..5adcabd 100644 (file)
@@ -84,6 +84,7 @@ lazy
 noinit
 readonly
 writable
+optional
 autoinit
 noautoinit
 lateinit
index 85deec6..ab9e845 100644 (file)
@@ -20,6 +20,15 @@ import c_tools
 import nitni
 import ffi
 import naive_interpreter
+import debugger_socket # To linearize `ToolContext::init`
+
+redef class ToolContext
+
+       # --compile-dir
+       var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
+
+       init do option_context.add_option opt_compile_dir
+end
 
 redef class AMethPropdef
        # Does this method definition use the FFI and is it supported by the interpreter?
@@ -45,10 +54,25 @@ redef class AMethPropdef
 end
 
 redef class NaiveInterpreter
+       redef fun start(mainmodule)
+       do
+               super
+
+               # Delete temporary files
+               var compile_dir = compile_dir
+               if compile_dir.file_exists then compile_dir.rmdir
+       end
+
        # Where to store generated C and extracted code
-       #
-       # TODO make customizable and delete when execution completes
-       private var compile_dir = "nit_compile"
+       private var compile_dir: String is lazy do
+               # Prioritize the user supplied directory
+               var opt = modelbuilder.toolcontext.opt_compile_dir.value
+               if opt != null then return opt
+               return "/tmp/niti_ffi_{process_id}"
+       end
+
+       # Identifier for this process, unique between running interpreters
+       private fun process_id: Int `{ return getpid(); `}
 
        # Path of the compiled foreign code library
        #
@@ -75,7 +99,7 @@ redef class AModule
                var compile_dir = v.compile_dir
                var foreign_code_lib_path = v.foreign_code_lib_path(mmodule)
 
-               if not compile_dir.file_exists then compile_dir.mkdir
+               if not compile_dir.file_exists then compile_dir.mkdir(0o700)
 
                # Compile the common FFI part
                ensure_compile_ffi_wrapper
index bbe07d1..23d3ba1 100644 (file)
@@ -1497,7 +1497,12 @@ redef class AAttrPropdef
                        return evaluate_expr(v, recv, f)
                else if mpropdef == mwritepropdef then
                        assert args.length == 2
-                       v.write_attribute(attr, recv, args[1])
+                       var arg = args[1]
+                       if is_optional and arg.mtype isa MNullType then
+                               var f = v.new_frame(self, mpropdef, args)
+                               arg = evaluate_expr(v, recv, f)
+                       end
+                       v.write_attribute(attr, recv, arg)
                        return null
                else
                        abort
index 03d9951..6588268 100644 (file)
@@ -203,19 +203,21 @@ redef class ModelBuilder
                                        mreadpropdef.mproperty.is_autoinit = true
                                        continue
                                end
-                               if npropdef.has_value then continue
-                               var paramname = mreadpropdef.mproperty.name
-                               var ret_type = msignature.return_mtype
-                               if ret_type == null then return
-                               var mparameter = new MParameter(paramname, ret_type, false)
-                               mparameters.add(mparameter)
+                               if npropdef.has_value and not npropdef.is_optional then continue
                                var msetter = npropdef.mwritepropdef
                                if msetter == null then
                                        # No setter, it is a readonly attribute, so just add it
+                                       var paramname = mreadpropdef.mproperty.name
+                                       var ret_type = msignature.return_mtype
+                                       if ret_type == null then return
+                                       var mparameter = new MParameter(paramname, ret_type, false)
+                                       mparameters.add(mparameter)
+
                                        initializers.add(npropdef.mpropdef.mproperty)
                                        npropdef.mpropdef.mproperty.is_autoinit = true
                                else
                                        # Add the setter to the list
+                                       mparameters.add_all msetter.msignature.mparameters
                                        initializers.add(msetter.mproperty)
                                        msetter.mproperty.is_autoinit = true
                                end
@@ -1154,6 +1156,9 @@ redef class AAttrPropdef
        # Is the node tagged lazy?
        var is_lazy = false
 
+       # Is the node tagged optional?
+       var is_optional = false
+
        # Has the node a default value?
        # Could be through `n_expr` or `n_block`
        var has_value = false
@@ -1250,6 +1255,14 @@ redef class AAttrPropdef
                        self.mlazypropdef = mlazypropdef
                end
 
+               var atoptional = self.get_single_annotation("optional", modelbuilder)
+               if atoptional != null then
+                       if not has_value then
+                               modelbuilder.error(atoptional, "Error: `optional` attributes need a default value.")
+                       end
+                       is_optional = true
+               end
+
                var atreadonly = self.get_single_annotation("readonly", modelbuilder)
                if atreadonly != null then
                        if not has_value then
@@ -1421,9 +1434,13 @@ redef class AAttrPropdef
 
                var mwritepropdef = self.mwritepropdef
                if mwritepropdef != null then
+                       var mwritetype = mtype
+                       if is_optional then
+                               mwritetype = mwritetype.as_nullable
+                       end
                        var name: String
                        name = n_id2.text
-                       var mparameter = new MParameter(name, mtype, false)
+                       var mparameter = new MParameter(name, mwritetype, false)
                        var msignature = new MSignature([mparameter], null)
                        mwritepropdef.msignature = msignature
                end
index c0a8a15..910877b 100644 (file)
@@ -19,7 +19,7 @@ import nitni_base
 
 redef class MMethod
        # Build a C function name for the FFI implementation (uses friendly naming).
-       # * On a specific static receiver mype `recv_mtype` 
+       # * On a specific static receiver type `recv_mtype`
        # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
        # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
        # * With a specified length indicating whether it uses the sort name or the long name with
@@ -39,13 +39,13 @@ redef class MMethod
 
                if suffix != null then cname = "{cname}{suffix}"
 
-               if length.long then cname = "{from_mmodule.name}___{cname}"
+               if length.long then cname = "{from_mmodule.c_name}___{cname}"
 
                return cname
        end
 
        # Build a C function signature for the FFI implementation (uses friendly naming).
-       # * On a specific static receiver mype `recv_mtype` 
+       # * On a specific static receiver type `recv_mtype`
        # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
        # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
        # * With a specified length indicating whether it uses the sort name or the long name with
@@ -83,7 +83,7 @@ redef class MMethod
        end
 
        # Build a C function call for the FFI implementation (uses friendly naming).
-       # * On a specific static receiver mype `recv_mtype` 
+       # * On a specific static receiver type `recv_mtype`
        # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
        # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
        # * With a specified length indicating whether it uses the sort name or the long name with
index aeb3b7e..ddc87e2 100644 (file)
@@ -152,7 +152,11 @@ private class TypeVisitor
                        end
                        return null # forward error
                end
-               self.error(nexpr, "Error: expected an expression.")
+               var more_message = null
+               var p = nexpr.parent
+               if p != null then more_message = p.bad_expr_message(nexpr)
+               if more_message == null then more_message = "" else more_message = " " + more_message
+               self.error(nexpr, "Error: expected an expression{more_message}.")
                return null
        end
 
@@ -802,6 +806,12 @@ end
 
 redef class ANode
        private fun accept_post_typing(v: TypeVisitor) do end
+
+       # An additional information message to explain the role of a child expression.
+       #
+       # The point of the method is to allow some kind of double dispatch so the parent
+       # choose how to describe its children.
+       private fun bad_expr_message(child: AExpr): nullable String do return null
 end
 
 redef class AAttrPropdef
@@ -1710,6 +1720,14 @@ redef class ASendExpr
        # The property invoked by the send.
        var callsite: nullable CallSite
 
+       redef fun bad_expr_message(child)
+       do
+               if child == self.n_expr then
+                       return "to be the receiver of `{self.property_name}`"
+               end
+               return null
+       end
+
        redef fun accept_typing(v)
        do
                var nrecv = self.n_expr
diff --git a/tests/base_attr_optional.nit b/tests/base_attr_optional.nit
new file mode 100644 (file)
index 0000000..4417afe
--- /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.
+
+import kernel
+
+class A
+       var i: Int = 99 is optional
+
+       var o: Object = 999 is optional
+end
+
+var a = new A
+a.i.output
+a.o.output
+
+a.i = 1
+a.o = 10
+a.i.output
+a.o.output
+
+a.i = null
+a.o = null
+a.i.output
+a.o.output
+
+a = new A(2)
+a.i.output
+a.o.output
+
+a = new A(3, true)
+a.i.output
+a.o.output
diff --git a/tests/error_arg.nit b/tests/error_arg.nit
new file mode 100644 (file)
index 0000000..c550d9f
--- /dev/null
@@ -0,0 +1,39 @@
+# 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 core::kernel
+
+redef class Int
+       fun a: Int do return 10*self+self.abs
+end
+
+fun foo(a: Int): Int do
+       a.output
+       return a
+end
+fun bar(a: Int) do a.output
+
+foo 1.a
+foo(1.a)
+foo (1).a
+foo(1).a
+foo((1).a)
+
+'\n'.output
+
+bar 1.a
+bar(1.a)
+#alt1#bar (1).a
+#alt1#bar(1).a
+bar((1).a)
diff --git a/tests/sav/base_attr_optional.res b/tests/sav/base_attr_optional.res
new file mode 100644 (file)
index 0000000..c879e94
--- /dev/null
@@ -0,0 +1,10 @@
+99
+999
+1
+10
+99
+999
+2
+999
+3
+true
index 5b14b4e..b2c999e 100644 (file)
@@ -1 +1 @@
-alt/base_meth_call_alt1.nit:36,1--6: Error: expected an expression.
+alt/base_meth_call_alt1.nit:36,1--6: Error: expected an expression to be the receiver of `output`.
diff --git a/tests/sav/error_arg.res b/tests/sav/error_arg.res
new file mode 100644 (file)
index 0000000..1aa7d08
--- /dev/null
@@ -0,0 +1,9 @@
+11
+11
+1
+1
+11
+
+11
+11
+11
diff --git a/tests/sav/error_arg_alt1.res b/tests/sav/error_arg_alt1.res
new file mode 100644 (file)
index 0000000..efe82e4
--- /dev/null
@@ -0,0 +1,2 @@
+alt/error_arg_alt1.nit:37,1--7: Error: expected an expression to be the receiver of `a`.
+alt/error_arg_alt1.nit:38,1--6: Error: expected an expression to be the receiver of `a`.
index 8fd2394..e28137f 100644 (file)
@@ -9,37 +9,37 @@ Allocations, by type:
        -RopeBuffer = 0
 
 Calls to length, by type:
-       FlatString = 23 (cache misses 5, 21.73%)
+       FlatString = 18 (cache misses 5, 27.77%)
 Indexed accesses, by type:
-       FlatString = 13
+       FlatString = 8
 Calls to bytelen for each type:
        FlatString = 61
 Calls to position for each type:
-       FlatString = 27
+       FlatString = 17
 Calls to bytepos for each type:
-       FlatString = 14
-Calls to first_byte on FlatString 216
+       FlatString = 9
+Calls to first_byte on FlatString 191
 Calls to last_byte on FlatString 19
 FlatStrings allocated with length 78 (86.813%)
 Length of travel for index distribution:
-* null = 16 => occurences 80.0%, cumulative 80.0% 
-* 1 = 14 => occurences 35.0%, cumulative 75.0% 
+* null = 11 => occurences 73.333%, cumulative 73.333% 
+* 1 = 8 => occurences 27.586%, cumulative 65.517% 
 Byte length of the FlatStrings created:
 * null = 6 => occurences 4.444%, cumulative 4.444% 
-* 1 = 21 => occurences 14.094%, cumulative 18.121% 
-* 2 = 33 => occurences 20.245%, cumulative 36.81% 
+* 1 = 24 => occurences 16.107%, cumulative 20.134% 
+* 2 = 30 => occurences 18.405%, cumulative 36.81% 
 * 3 = 29 => occurences 16.292%, cumulative 50.0% 
-* 4 = 9 => occurences 4.663%, cumulative 50.777% 
-* 5 = 20 => occurences 9.615%, cumulative 56.731% 
-* 6 = 21 => occurences 9.417%, cumulative 62.332% 
+* 4 = 5 => occurences 2.591%, cumulative 48.705% 
+* 5 = 20 => occurences 9.615%, cumulative 54.808% 
+* 6 = 25 => occurences 11.211%, cumulative 62.332% 
 * 9 = 1 => occurences 0.42%, cumulative 58.824% 
 * 10 = 9 => occurences 3.557%, cumulative 58.893% 
 * 11 = 2 => occurences 0.746%, cumulative 56.343% 
 * 12 = 1 => occurences 0.355%, cumulative 53.901% 
 * 13 = 1 => occurences 0.339%, cumulative 51.864% 
 * 14 = 1 => occurences 0.325%, cumulative 50.0% 
-* 15 = 5 => occurences 1.558%, cumulative 49.533% 
-* 16 = 7 => occurences 2.083%, cumulative 49.405% 
+* 15 = 7 => occurences 2.181%, cumulative 50.156% 
+* 16 = 5 => occurences 1.488%, cumulative 49.405% 
 * 17 = 1 => occurences 0.285%, cumulative 47.578% 
 * 25 = 2 => occurences 0.549%, cumulative 46.429% 
 * 26 = 1 => occurences 0.265%, cumulative 44.974% 
@@ -53,5 +53,5 @@ Byte length of the FlatStrings created:
 * 40 = 1 => occurences 0.207%, cumulative 37.19% 
 * 43 = 1 => occurences 0.201%, cumulative 36.419% 
 * 46 = 1 => occurences 0.196%, cumulative 35.686% 
-* 48 = 1 => occurences 0.191%, cumulative 34.99% 
-* 51 = 21 => occurences 3.918%, cumulative 38.06% 
+* 51 = 20 => occurences 3.824%, cumulative 38.623% 
+* 55 = 1 => occurences 0.186%, cumulative 37.732% 
index e320033..702e2b2 100644 (file)
@@ -9,48 +9,49 @@ Allocations, by type:
        -RopeBuffer = 0
 
 Calls to length, by type:
-       FlatString = 23 (cache misses 5, 21.73%)
+       FlatString = 18 (cache misses 5, 27.77%)
 Indexed accesses, by type:
-       FlatString = 13
+       FlatString = 8
 Calls to bytelen for each type:
        FlatString = 61
 Calls to position for each type:
-       FlatString = 27
+       FlatString = 17
 Calls to bytepos for each type:
-       FlatString = 14
-Calls to first_byte on FlatString 216
+       FlatString = 9
+Calls to first_byte on FlatString 191
 Calls to last_byte on FlatString 19
 FlatStrings allocated with length 78 (86.813%)
 Length of travel for index distribution:
-* 0 = 16 => occurences 80.0%, cumulative 80.0% 
-* 1 = 14 => occurences 35.0%, cumulative 75.0% 
+* 0 = 11 => occurences 73.333%, cumulative 73.333% 
+* 1 = 8 => occurences 27.586%, cumulative 65.517% 
 Byte length of the FlatStrings created:
 * 0 = 6 => occurences 4.478%, cumulative 4.478% 
-* 1 = 21 => occurences 14.189%, cumulative 18.243% 
-* 2 = 33 => occurences 20.37%, cumulative 37.037% 
+* 1 = 24 => occurences 16.216%, cumulative 20.27% 
+* 2 = 30 => occurences 18.519%, cumulative 37.037% 
 * 3 = 29 => occurences 16.384%, cumulative 50.282% 
-* 4 = 7 => occurences 3.646%, cumulative 50.0% 
-* 5 = 20 => occurences 9.662%, cumulative 56.039% 
-* 6 = 21 => occurences 9.459%, cumulative 61.712% 
-* 9 = 1 => occurences 0.422%, cumulative 58.228% 
-* 10 = 9 => occurences 3.571%, cumulative 58.333% 
-* 11 = 2 => occurences 0.749%, cumulative 55.805% 
-* 12 = 1 => occurences 0.356%, cumulative 53.381% 
-* 13 = 1 => occurences 0.34%, cumulative 51.361% 
-* 14 = 1 => occurences 0.326%, cumulative 49.511% 
-* 15 = 5 => occurences 1.563%, cumulative 49.063% 
-* 16 = 7 => occurences 2.09%, cumulative 48.955% 
-* 17 = 1 => occurences 0.286%, cumulative 47.143% 
-* 25 = 2 => occurences 0.551%, cumulative 46.006% 
-* 26 = 1 => occurences 0.265%, cumulative 44.562% 
-* 31 = 2 => occurences 0.513%, cumulative 43.59% 
-* 32 = 1 => occurences 0.248%, cumulative 42.327% 
-* 33 = 1 => occurences 0.24%, cumulative 41.247% 
-* 34 = 2 => occurences 0.465%, cumulative 40.465% 
-* 35 = 1 => occurences 0.225%, cumulative 39.414% 
-* 37 = 1 => occurences 0.219%, cumulative 38.512% 
-* 39 = 1 => occurences 0.213%, cumulative 37.66% 
-* 40 = 1 => occurences 0.207%, cumulative 36.853% 
-* 43 = 1 => occurences 0.202%, cumulative 36.089% 
-* 46 = 1 => occurences 0.196%, cumulative 35.363% 
-* 48 = 3 => occurences 0.575%, cumulative 35.057% 
+* 4 = 3 => occurences 1.563%, cumulative 47.917% 
+* 5 = 20 => occurences 9.662%, cumulative 54.106% 
+* 6 = 26 => occurences 11.712%, cumulative 62.162% 
+* 9 = 1 => occurences 0.422%, cumulative 58.65% 
+* 10 = 9 => occurences 3.571%, cumulative 58.73% 
+* 11 = 2 => occurences 0.749%, cumulative 56.18% 
+* 12 = 1 => occurences 0.356%, cumulative 53.737% 
+* 13 = 1 => occurences 0.34%, cumulative 51.701% 
+* 14 = 1 => occurences 0.326%, cumulative 49.837% 
+* 15 = 7 => occurences 2.188%, cumulative 50.0% 
+* 16 = 5 => occurences 1.493%, cumulative 49.254% 
+* 17 = 1 => occurences 0.286%, cumulative 47.429% 
+* 25 = 2 => occurences 0.551%, cumulative 46.281% 
+* 26 = 1 => occurences 0.265%, cumulative 44.828% 
+* 31 = 2 => occurences 0.513%, cumulative 43.846% 
+* 32 = 1 => occurences 0.248%, cumulative 42.574% 
+* 33 = 1 => occurences 0.24%, cumulative 41.487% 
+* 34 = 2 => occurences 0.465%, cumulative 40.698% 
+* 35 = 1 => occurences 0.225%, cumulative 39.64% 
+* 37 = 1 => occurences 0.219%, cumulative 38.731% 
+* 39 = 1 => occurences 0.213%, cumulative 37.872% 
+* 40 = 1 => occurences 0.207%, cumulative 37.06% 
+* 43 = 1 => occurences 0.202%, cumulative 36.29% 
+* 46 = 1 => occurences 0.196%, cumulative 35.56% 
+* 51 = 14 => occurences 2.682%, cumulative 37.356% 
+* 52 = 5 => occurences 0.931%, cumulative 37.244%