Merge: Introduce crapto modules for cryptographic attacks.
authorJean Privat <jean@pryen.org>
Fri, 13 May 2016 17:18:37 +0000 (13:18 -0400)
committerJean Privat <jean@pryen.org>
Fri, 13 May 2016 17:18:37 +0000 (13:18 -0400)
Introduce a frequency analysis service for the english language.

Pull-Request: #2063
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

31 files changed:
.gitignore
examples/calculator/Makefile
examples/calculator/README.md
examples/calculator/doc/android-scientific.png [new file with mode: 0644]
examples/calculator/doc/ios-scientific.png [new file with mode: 0644]
examples/calculator/doc/linux-scientific.png [new file with mode: 0644]
examples/calculator/src/android_calculator.nit [new file with mode: 0644]
examples/calculator/src/calculator.nit
examples/calculator/src/calculator_logic.nit
examples/calculator/src/calculator_test.nit
examples/calculator/src/ios_calculator.nit [new file with mode: 0644]
examples/calculator/src/scientific_calculator.nit [new file with mode: 0644]
lib/cocoa/foundation.nit
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/crypto.nit
lib/gtk/v3_4/gtk_assistant.nit
lib/gtk/v3_4/gtk_core.nit
lib/gtk/v3_4/gtk_dialogs.nit
lib/gtk/v3_4/gtk_widgets_ext.nit
lib/json/static.nit
lib/json/string_parser.nit
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
tests/base_is_optional.nit [new file with mode: 0644]
tests/sav/base_is_optional.res [new file with mode: 0644]
tests/sav/base_is_optional_alt1.res [new file with mode: 0644]
tests/sav/test_flatbuf_from.res [new file with mode: 0644]
tests/sav/test_flatbuf_from_alt1.res [new file with mode: 0644]
tests/sav/test_flatbuf_from_alt2.res [new file with mode: 0644]
tests/test_flatbuf_from.nit [new file with mode: 0644]

index edf0d66..80ef46d 100644 (file)
@@ -55,3 +55,4 @@ nitunit.xml
 
 .metadata/*
 .github_data
+.DS_Store
index c97809c..8265899 100644 (file)
@@ -7,18 +7,22 @@ bin/calculator: $(shell ${NITLS} -M src/calculator.nit linux) ${NITC}
        mkdir -p bin
        ${NITC} -o $@ src/calculator.nit -m linux
 
+bin/scientific_calculator: $(shell ${NITLS} -M src/scientific_calculator.nit linux) ${NITC}
+       mkdir -p bin
+       ${NITC} -o $@ src/scientific_calculator.nit -m linux
+
 # ---
 # Android
 
 android: bin/calculator.apk
 
-bin/calculator.apk: $(shell ${NITLS} -M src/calculator.nit android) ${NITC} android/res/
+bin/calculator.apk: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
        mkdir -p bin
-       ${NITC} -o $@ src/calculator.nit -m ../../lib/android/ui/ -D debug
+       ${NITC} -o $@ src/scientific_calculator.nit -m src/android_calculator.nit -D debug
 
-android-release: $(shell ${NITLS} -M src/calculator.nit android) ${NITC} android/res/
+android-release: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
        mkdir -p bin
-       ${NITC} -o bin/calculator.apk src/calculator.nit -m ../../lib/android/ui/ --release
+       ${NITC} -o bin/calculator.apk src/scientific_calculator.nit -m src/android_calculator.nit --release
 
 android/res/: art/icon.svg ../../contrib/inkscape_tools/bin/svg_to_icons
        mkdir -p android/res
@@ -33,9 +37,9 @@ android-install: bin/calculator.apk
 # ---
 # iOS
 
-bin/calculator.app: $(shell ${NITLS} -M src/calculator.nit ios) ${NITC} ios/AppIcon.appiconset/Contents.json
+bin/calculator.app: $(shell ${NITLS} -M src/scientific_calculator.nit src/ios_calculator.nit) ${NITC} ios/AppIcon.appiconset/Contents.json
        mkdir -p bin
-       ${NITC} -o $@ src/calculator.nit -m ios -D debug
+       ${NITC} -o $@ src/scientific_calculator.nit -m src/ios_calculator.nit -D debug
 
 ios/AppIcon.appiconset/Contents.json: art/icon-ios.svg
        mkdir -p ios
index 0073c26..aa93fed 100644 (file)
@@ -5,6 +5,9 @@ Portable calculator built using _app.nit_
 * `calculator_logic` defines `CalculatorContext` with all the business logic of a calculator.
   It takes as input operations and numbers, and outputs the text to display.
 * `calculator` implements the portable graphical interface using the _app.nit_ framework
+* `scientific_calculator` refines `calculator` to add scientific operations.
+* `android_calculator` refines `calculator` to get a nicer aesthetic on Android.
+* `ios_calculator` refines `calculator` to get a nicer aesthetic on iOS.
 * `calculator_test` test `CalculatorContext` as a black box.
 
 # Compilation
@@ -29,3 +32,11 @@ Portable calculator built using _app.nit_
        make bin/android.app
        ios-sim launch bin/calculator.app
        ~~~
+
+# Screenshots
+
+![Scientific calculator on Linux with GTK+](doc/linux-scientific.png)
+
+![Scientific calculator on Android](doc/android-scientific.png)
+
+![Scientific calculator on iOS](doc/ios-scientific.png)
diff --git a/examples/calculator/doc/android-scientific.png b/examples/calculator/doc/android-scientific.png
new file mode 100644 (file)
index 0000000..f23d675
Binary files /dev/null and b/examples/calculator/doc/android-scientific.png differ
diff --git a/examples/calculator/doc/ios-scientific.png b/examples/calculator/doc/ios-scientific.png
new file mode 100644 (file)
index 0000000..8505c72
Binary files /dev/null and b/examples/calculator/doc/ios-scientific.png differ
diff --git a/examples/calculator/doc/linux-scientific.png b/examples/calculator/doc/linux-scientific.png
new file mode 100644 (file)
index 0000000..58b653e
Binary files /dev/null and b/examples/calculator/doc/linux-scientific.png differ
diff --git a/examples/calculator/src/android_calculator.nit b/examples/calculator/src/android_calculator.nit
new file mode 100644 (file)
index 0000000..7fe192c
--- /dev/null
@@ -0,0 +1,36 @@
+# 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.
+
+# Aesthetic adaptations for Android
+module android_calculator
+
+import calculator
+import android::ui
+
+redef class Button
+       init do set_android_style(native, (text or else "?").is_int)
+
+       private fun set_android_style(java_button: NativeButton, is_number: Bool)
+       in "Java" `{
+               // Flatten the background and use a different color for digit buttons
+               int color = is_number? android.graphics.Color.DKGRAY: android.graphics.Color.TRANSPARENT;
+               java_button.setBackgroundColor(color);
+
+               // Center the label on both horizontal and vertical axes
+               java_button.setGravity(android.view.Gravity.CENTER);
+
+               // Set lowercase text to correctly display constants like e and π
+               java_button.setAllCaps(false);
+       `}
+end
index 76c3a0a..4068f34 100644 (file)
@@ -51,13 +51,13 @@ class CalculatorWindow
        private var context = new CalculatorContext
 
        # Main window layout
-       private var layout = new VerticalLayout(parent=self)
+       var layout = new VerticalLayout(parent=self)
 
        # Main display, at the top of the screen
        private var display = new TextInput(parent=layout)
 
        # Maps operators as `String` to their `Button`
-       private var buttons = new HashMap[String, Button]
+       var buttons = new HashMap[String, Button]
 
        init
        do
@@ -66,8 +66,8 @@ class CalculatorWindow
                # All the button labels, row by row
                var rows = [["7", "8", "9", "+"],
                            ["4", "5", "6", "-"],
-                           ["1", "2", "3", "*"],
-                           ["0", ".", "C", "/"],
+                           ["1", "2", "3", "×"],
+                           ["0", ".", "C", "÷"],
                            ["="]]
 
                for row in rows do
@@ -94,9 +94,9 @@ class CalculatorWindow
                        else if op.is_numeric then
                                var n = op.to_i
                                context.push_digit n
-                       else
+                       else if op != null then
                                buttons["."].enabled = true
-                               context.push_op op.chars.first
+                               context.push_op op
                        end
 
                        display.text = context.display_text
index e5e492b..840ed84 100644 (file)
@@ -24,33 +24,35 @@ class CalculatorContext
        # Result of the last operation
        var result: nullable Numeric = null
 
-       # Last operation pushed with `push_op`, to be executed on the next push
-       var last_op: nullable Char = null
+       # Last operation pushed with `push_op`
+       var last_op: nullable Text = null
+
+       # Is `last_op` an unary operation or a '='?
+       var last_op_was_unary = false
 
        # Value currently being entered
-       var current: nullable FlatBuffer = null
+       var current: nullable String = null
 
        # Text to display on screen
        fun display_text: String
        do
-               var result = result
-               var last_op = last_op
-               var current = current
-
                var buf = new FlatBuffer
 
-               if result != null and (current == null or last_op != '=') then
-                       if last_op == '=' then buf.append "= "
+               var last_op = last_op
+               var result = result
+               if result != null then
+                       if last_op_was_unary then buf.append "{last_op or else "?"} "
 
                        buf.append result.to_s
                        buf.add ' '
                end
 
-               if last_op != null and last_op != '=' then
-                       buf.add last_op
+               if last_op != null and not last_op_was_unary then
+                       buf.append last_op
                        buf.add ' '
                end
 
+               var current = current
                if current != null then
                        buf.append current.to_s
                        buf.add ' '
@@ -60,40 +62,95 @@ class CalculatorContext
        end
 
        # Push operation `op`, will usually execute the last operation
-       fun push_op(op: Char)
+       fun push_op(op: Text)
        do
+               # Constants
+               # TODO Protect constants to preserve full precision and to forbid appending extra digits
+               if op == "π" then
+                       if last_op_was_unary then clear
+                       current = pi.to_s
+                       return
+               else if op == "e" then
+                       if last_op_was_unary then clear
+                       current = 2.718282.to_s
+                       return
+
+               # Clear screen
+               else if op == "C" then
+                       clear
+                       return
+
+               # Unary -
+               else if op == "-" then
+                       if current == null then
+                               if last_op_was_unary then clear
+                               current = "-"
+                               return
+                       else if current == "-" then
+                               current = null
+                               return
+                       end
+               end
+
+               # For all operators, apply pending operators
                apply_last_op_if_any
-               if op == 'C' then
-                       self.result = null
-                       last_op = null
+
+               var result = self.result or else 0
+
+               last_op = op
+               last_op_was_unary = true
+
+               # Unary operators
+               if op == "√" then
+                       self.result = result.to_f.sqrt
+               else if op == "x²" then
+                       self.result = result.to_f.pow(2.0)
+               else if op == "x!" then
+                       self.result = result.to_i.factorial
+               else if op == "sin" then
+                       self.result = result.to_f.sin
+               else if op == "cos" then
+                       self.result = result.to_f.cos
+               else if op == "tan" then
+                       self.result = result.to_f.tan
+
+               # =
+               else if op == "=" then
+                       current = null
+
+               # Binary operators
                else
-                       last_op = op # store for next push_op
+                       self.result = result # Set as same or 0
+                       last_op_was_unary = false
+                       current = null
                end
+       end
 
-               # prepare next current
-               self.current = null
+       # Clear all state
+       private fun clear
+       do
+               result = null
+               last_op = null
+               current = null
        end
 
        # Push a digit
        fun push_digit(digit: Int)
        do
+               if last_op_was_unary then clear
+
                var current = current
-               if current == null then current = new FlatBuffer
-               current.add digit.to_s.chars.first
+               if current == null then current = ""
+               current += digit.to_s
                self.current = current
-
-               if last_op == '=' then
-                       self.result = null
-                       last_op = null
-               end
        end
 
        # Switch entry mode from integer to decimal
        fun switch_to_decimals
        do
                var current = current
-               if current == null then current = new FlatBuffer.from("0")
-               if not current.chars.has('.') then current.add '.'
+               if current == null then current = "0"
+               if not current.chars.has('.') then current += "."
                self.current = current
        end
 
@@ -110,14 +167,20 @@ class CalculatorContext
                if op == null then
                        result = current.to_n
                else if result != null then
-                       if op == '+' then
+                       if op == "+" then
                                result = result.add(current.to_n)
-                       else if op == '-' then
+                       else if op == "-" then
                                result = result.sub(current.to_n)
-                       else if op == '/' then
+                       else if op == "/" or op == "÷" then
                                result = result.div(current.to_n)
-                       else if op == '*' then
+                       else if op == "*" or op == "×" then
                                result = result.mul(current.to_n)
+                       else if op == "%" then
+                               result = result.to_i % current.to_i
+                       else if op == "xⁿ" then
+                               result = result.to_f.pow(current.to_f)
+                       else if op == "log" then
+                               result = result.to_f.log_base(current.to_f)
                        end
                end
 
index 270854c..610b7a9 100644 (file)
@@ -23,11 +23,11 @@ import calculator_logic
 var context = new CalculatorContext
 context.push_digit( 1 )
 context.push_digit( 2 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 3 )
-context.push_op( '*' )
+context.push_op( "*" )
 context.push_digit( 2 )
-context.push_op( '=' )
+context.push_op( "=" )
 var r = context.result
 assert r == 30 else print r or else "-"
 
@@ -36,16 +36,16 @@ context.push_digit( 1 )
 context.push_digit( 4 )
 context.switch_to_decimals
 context.push_digit( 1 )
-context.push_op( '*' )
+context.push_op( "*" )
 context.push_digit( 3 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 42.3 else print r or else "-"
 
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
 context.push_digit( 1 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 53.3 else print r or else "-"
 
@@ -54,9 +54,9 @@ context.push_digit( 4 )
 context.push_digit( 2 )
 context.switch_to_decimals
 context.push_digit( 3 )
-context.push_op( '/' )
+context.push_op( "/" )
 context.push_digit( 3 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 14.1 else print r or else "-"
 
@@ -68,20 +68,20 @@ context.switch_to_decimals
 context.push_digit( 1 )
 context.push_digit( 2 )
 context.push_digit( 3 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 51.123 else print r or else "-"
 
-#test 'C' button
+#test "C" button
 context = new CalculatorContext
 context.push_digit( 1 )
 context.push_digit( 0 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
 context.push_digit( 0 )
-context.push_op( '=' )
-context.push_op( 'C' )
+context.push_op( "=" )
+context.push_op( "C" )
 r = context.result
 assert r == null else print r
diff --git a/examples/calculator/src/ios_calculator.nit b/examples/calculator/src/ios_calculator.nit
new file mode 100644 (file)
index 0000000..23fba4c
--- /dev/null
@@ -0,0 +1,32 @@
+# 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.
+
+# Aesthetic adaptations for iOS
+module ios_calculator
+
+import calculator
+import ios
+
+redef class CalculatorWindow
+       init do title = "app.nit Calculator"
+end
+
+redef class TextInput
+       init do set_ios_style(native)
+
+       private fun set_ios_style(objc_text_field: UITextField)
+       in "ObjC" `{
+               objc_text_field.textAlignment = NSTextAlignmentCenter;
+       `}
+end
diff --git a/examples/calculator/src/scientific_calculator.nit b/examples/calculator/src/scientific_calculator.nit
new file mode 100644 (file)
index 0000000..c22e44e
--- /dev/null
@@ -0,0 +1,40 @@
+# 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.
+
+# Extends the portable calculator app with scientific operations
+module scientific_calculator
+
+import calculator
+
+redef class CalculatorWindow
+       init
+       do
+               # All the button labels, row by row
+               var rows = [["√",  "x²", "xⁿ", "e"  ],
+                           ["log","ln", "%",  "x!" ],
+                           ["π",  "sin","cos","tan"]]
+
+               for row in rows do
+                       var row_layout = new HorizontalLayout(parent=layout)
+
+                       for op in row do
+                               var but = new Button(parent=row_layout, text=op)
+                               but.observers.add self
+                               buttons[op] = but
+                       end
+               end
+
+               super
+       end
+end
index 33114e4..3a6fa98 100644 (file)
@@ -37,7 +37,7 @@ extern class NSString in "ObjC" `{ NSString * `}
        # Get an UTF8 encoded `char*` copy of `self`
        fun utf8_string: NativeString in "ObjC" `{ return (char *)[self UTF8String]; `}
 
-       redef fun to_s do return utf8_string.to_s
+       redef fun to_s do return utf8_string.to_s_with_copy
 end
 
 redef class NativeString
index c15901e..c9d1279 100644 (file)
@@ -1514,6 +1514,42 @@ abstract class Buffer
        # In Buffers, the internal sequence of character is mutable
        # Thus, `chars` can be used to modify the buffer.
        redef fun chars: Sequence[Char] is abstract
+
+       # Appends `length` chars from `s` starting at index `from`
+       #
+       # ~~~nit
+       #       var b = new Buffer
+       #       b.append_substring("abcde", 1, 2)
+       #       assert b == "bc"
+       #       b.append_substring("vwxyz", 2, 3)
+       #       assert b == "bcxyz"
+       #       b.append_substring("ABCDE", 4, 300)
+       #       assert b == "bcxyzE"
+       #       b.append_substring("VWXYZ", 400, 1)
+       #       assert b == "bcxyzE"
+       # ~~~
+       fun append_substring(s: Text, from, length: Int) do
+               if from < 0 then
+                       length += from
+                       from = 0
+               end
+               var ln = s.length
+               if (length + from) > ln then length = ln - from
+               if length <= 0 then return
+               append_substring_impl(s, from, length)
+       end
+
+       # Unsafe version of `append_substring` for performance
+       #
+       # NOTE: Use only if sure about `from` and `length`, no checks
+       # or bound recalculation is done
+       fun append_substring_impl(s: Text, from, length: Int) do
+               var pos = from
+               for i in [0 .. length[ do
+                       self.add s[pos]
+                       pos += 1
+               end
+       end
 end
 
 # View for chars on Buffer objects, extends Sequence
@@ -1755,6 +1791,18 @@ redef class Char
                return cp >= 0xD800 and cp <= 0xDFFF
        end
 
+       # Is `self` a UTF-16 high surrogate ?
+       fun is_hi_surrogate: Bool do
+               var cp = code_point
+               return cp >= 0xD800 and cp <= 0xDBFF
+       end
+
+       # Is `self` a UTF-16 low surrogate ?
+       fun is_lo_surrogate: Bool do
+               var cp = code_point
+               return cp >= 0xDC00 and cp <= 0xDFFF
+       end
+
        # Length of `self` in a UTF-8 String
        fun u8char_len: Int do
                var c = self.code_point
index 45ad144..b7e8e61 100644 (file)
@@ -411,7 +411,7 @@ abstract class FlatString
 
                if from < 0 then
                        count += from
-                       if count < 0 then return ""
+                       if count <= 0 then return ""
                        from = 0
                end
 
@@ -924,7 +924,10 @@ class FlatBuffer
                is_dirty = true
                _bytelen = 0
                _length = 0
-               if written then reset
+               if written then
+                       _capacity = 16
+                       reset
+               end
        end
 
        redef fun empty do return new Buffer
@@ -991,15 +994,10 @@ class FlatBuffer
        init from(s: Text)
        do
                _items = new NativeString(s.bytelen)
-               if s isa FlatText then
-                       _items = s._items
-               else
-                       for i in substrings do i.as(FlatString)._items.copy_to(_items, i._bytelen, 0, 0)
-               end
+               for i in s.substrings do i._items.copy_to(_items, i._bytelen, first_byte, 0)
                _bytelen = s.bytelen
                _length = s.length
                _capacity = _bytelen
-               written = true
        end
 
        # Create a new empty string with a given capacity.
@@ -1054,6 +1052,21 @@ class FlatBuffer
                return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
        end
 
+       redef fun append_substring_impl(s, from, length) do
+               if length <= 0 then return
+               if not s isa FlatText then
+                       super
+                       return
+               end
+               var bytest = s.char_to_byte_index(from)
+               var bytend = s.char_to_byte_index(from + length - 1)
+               var btln = bytend - bytest + 1
+               enlarge(btln + _bytelen)
+               s._items.copy_to(_items, btln, bytest, _bytelen)
+               _bytelen += btln
+               _length += length
+       end
+
        redef fun reverse
        do
                written = false
@@ -1356,37 +1369,26 @@ 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
+               var cp = c.code_point
+               if cp < 128 then
+                       self[pos] = cp.to_b
                        return
                end
                var ln = c.u8char_len
-               native_set_char(pos, c, ln)
-       end
-
-       private fun native_set_char(pos: Int, c: Char, ln: Int) `{
-               char* dst = self + pos;
-               switch(ln){
-                       case 1:
-                               dst[0] = c;
-                               break;
-                       case 2:
-                               dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
-                               dst[1] = 0x80 | (c & 0x3F);
-                               break;
-                       case 3:
-                               dst[0] = 0xE0 | ((c & 0xF000) >> 12);
-                               dst[1] = 0x80 | ((c & 0xFC0) >> 6);
-                               dst[2] = 0x80 | (c & 0x3F);
-                               break;
-                       case 4:
-                               dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
-                               dst[1] = 0x80 | ((c & 0x3F000) >> 12);
-                               dst[2] = 0x80 | ((c & 0xFC0) >> 6);
-                               dst[3] = 0x80 | (c & 0x3F);
-                               break;
-               }
-       `}
+               if ln == 2 then
+                       self[pos] = (0xC0 | ((cp & 0x7C0) >> 6)).to_b
+                       self[pos + 1] = (0x80 | (cp & 0x3F)).to_b
+               else if ln == 3 then
+                       self[pos] = (0xE0 | ((cp & 0xF000) >> 12)).to_b
+                       self[pos + 1] = (0x80 | ((cp & 0xFC0) >> 6)).to_b
+                       self[pos + 2] = (0x80 | (cp & 0x3F)).to_b
+               else if ln == 4 then
+                       self[pos] = (0xF0 | ((cp & 0x1C0000) >> 18)).to_b
+                       self[pos + 1] = (0x80 | ((cp & 0x3F000) >> 12)).to_b
+                       self[pos + 2] = (0x80 | ((cp & 0xFC0) >> 6)).to_b
+                       self[pos + 3] = (0x80 | (cp & 0x3F)).to_b
+               end
+       end
 end
 
 redef class Int
index 357eb99..2abe5d7 100644 (file)
@@ -154,32 +154,23 @@ redef class Text
                end
                return arr.to_s
        end
+end
+
+redef class Bytes
 
        # Returns `self` xored with `key`
        #
-       # The shortest of the two is cycled through until the longest has been
-       # completely xored.
+       # The key is cycled through until the `self` has been completely xored.
        #
-       #     assert "goodmorning".xor(" ".to_bytes) == "GOODMORNING"
-       fun xor(key: SequenceRead[Byte]): Text do
-               var xored = new Bytes.with_capacity(bytelen.max(key.length))
-
-               var shortest: SequenceRead[Byte]
-               var longest: SequenceRead[Byte]
-
-               if key.length > self.length then
-                       shortest = self.to_bytes
-                       longest = key
-               else
-                       shortest = key
-                       longest = self.to_bytes
-               end
+       #     assert "goodmorning".to_bytes.xorcipher(" ".to_bytes) == "GOODMORNING".bytes
+       fun xorcipher(key: Bytes): Bytes do
+               var xored = new Bytes.with_capacity(self.length)
 
-               for i in longest.length.times do
-                       xored.add(longest[i] ^ shortest[i % shortest.length])
+               for i in self.length.times do
+                       xored.add(self[i] ^ key[i % key.length])
                end
 
-               return xored.to_s
+               return xored
        end
 end
 
index 0d43784..e472143 100644 (file)
@@ -72,8 +72,8 @@ extern class GtkAssistant `{GtkAssistant *`}
                gtk_assistant_set_page_type(self, page, t);
        `}
 
-       fun get_page_title(page: GtkWidget): String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_assistant_get_page_title(self, page));
+       fun get_page_title(page: GtkWidget): String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_assistant_get_page_title(self, page));
        `}
 
        fun set_page_title(page: GtkWidget, title: String) import String.to_cstring `{
index ae6b915..b6fec7c 100644 (file)
@@ -331,8 +331,8 @@ extern class GtkFrame `{GtkFrame *`}
                return (GtkFrame *)gtk_frame_new(String_to_cstring(lbl));
        `}
 
-       fun frame_label: String `{
-               return NativeString_to_s((char *)gtk_frame_get_label(self));
+       fun frame_label: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_frame_get_label(self));
        `}
 
        fun frame_label=(lbl: String) import String.to_cstring `{
@@ -682,8 +682,8 @@ extern class GtkLabel `{GtkLabel *`}
        `}
 
        # Returns the text of the label
-       fun text: String import NativeString.to_s `{
-               return NativeString_to_s((char*)gtk_label_get_text(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char*)gtk_label_get_text(self));
        `}
 
        # Sets the angle of rotation for the label.
@@ -803,8 +803,8 @@ extern class GtkButton `{GtkButton *`}
                return (GtkButton *)gtk_button_new_from_stock(String_to_cstring(stock_id));
        `}
 
-       fun text: String `{
-               return NativeString_to_s((char *)gtk_button_get_label(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_button_get_label(self));
        `}
 
        fun text=(value: String) import String.to_cstring `{
@@ -876,8 +876,8 @@ extern class GtkExpander `{GtkExpander *`}
                gtk_expander_set_spacing(self, pixels);
        `}
 
-       fun label_text: String `{
-               return NativeString_to_s((char *)gtk_expander_get_label(self));
+       fun label_text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_expander_get_label(self));
        `}
 
        fun label_text=(lbl: String) import String.to_cstring `{
@@ -995,8 +995,8 @@ extern class GtkComboBox `{GtkComboBox *`}
                gtk_combo_box_set_id_column(self, id_column);
        `}
 
-       fun active_id: String `{
-               return NativeString_to_s((char *)gtk_combo_box_get_active_id(self));
+       fun active_id: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_combo_box_get_active_id(self));
        `}
 
        fun active_id=(id_active: String) import String.to_cstring `{
@@ -1019,8 +1019,8 @@ extern class GtkComboBox `{GtkComboBox *`}
                gtk_combo_box_popdown(self);
        `}
 
-       fun title: String `{
-               return NativeString_to_s((char *)gtk_combo_box_get_title(self));
+       fun title: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_combo_box_get_title(self));
        `}
 
        fun title=(t: String) import String.to_cstring `{
index d4ff95f..f28a91b 100644 (file)
@@ -52,40 +52,40 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
                return (GtkAboutDialog *)gtk_about_dialog_new();
        `}
 
-       fun program_name: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_program_name(self));
+       fun program_name: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_program_name(self));
        `}
 
        fun program_name=(name: String) import String.to_cstring `{
                gtk_about_dialog_set_program_name(self, String_to_cstring(name));
        `}
 
-       fun version: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_version(self));
+       fun version: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_version(self));
        `}
 
        fun version=(v: String) import String.to_cstring `{
                gtk_about_dialog_set_version(self, String_to_cstring(v));
        `}
 
-       fun copyright: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_copyright(self));
+       fun copyright: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_copyright(self));
        `}
 
        fun copyright=(c: String) import String.to_cstring `{
                gtk_about_dialog_set_copyright(self, String_to_cstring(c));
        `}
 
-       fun comments: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_comments(self));
+       fun comments: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_comments(self));
        `}
 
        fun comments=(com: String) import String.to_cstring `{
                gtk_about_dialog_set_comments(self, String_to_cstring(com));
        `}
 
-       fun license: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_license(self));
+       fun license: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_license(self));
        `}
 
        fun license=(li: String) import String.to_cstring `{
@@ -94,16 +94,16 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
 
        # license_type
 
-       fun website: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_website(self));
+       fun website: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_website(self));
        `}
 
        fun website=(link: String) import String.to_cstring `{
                gtk_about_dialog_set_website(self, String_to_cstring(link));
        `}
 
-       fun website_label: String import NativeString.to_s `{
-               return NativeString_to_s((char *) gtk_about_dialog_get_website_label(self));
+       fun website_label: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *) gtk_about_dialog_get_website_label(self));
        `}
 
        fun website_label=(link_label: String) import String.to_cstring `{
@@ -111,8 +111,8 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
        `}
 
        # TODO
-       # fun authors: String`{
-       #               return NativeString_to_s(gtk_about_dialog_get_authors(self));
+       # fun authors: String  import NativeString.to_s_with_copy `{
+       #               return NativeString_to_s_with_copy(gtk_about_dialog_get_authors(self));
        # `}
 
        # TODO
@@ -144,8 +144,8 @@ extern class GtkAppChooserDialog `{GtkAppChooserDialog *`}
 
        fun widget: GtkWidget `{ return gtk_app_chooser_dialog_get_widget(self); `}
 
-       fun heading: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_app_chooser_dialog_get_heading(self));
+       fun heading: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_app_chooser_dialog_get_heading(self));
        `}
 
        fun heading=(text: String) import String.to_cstring `{
index 25265c4..1cf98ee 100644 (file)
@@ -131,8 +131,8 @@ extern class GtkProgressBar `{GtkProgressBar *`}
                gtk_progress_bar_set_show_text(self, show);
        `}
 
-       fun text: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_progress_bar_get_text(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_progress_bar_get_text(self));
        `}
 
        fun text=(value: String) import String.to_cstring `{
index 7c0e2cf..565da1f 100644 (file)
@@ -439,6 +439,11 @@ redef class JsonParseError
                                "\"position\":{position.to_json}," +
                                "\"message\":{message.to_json}\}"
        end
+
+       redef fun pretty_json_visit(buf, indents) do
+               buf.clear
+               buf.append(to_json)
+       end
 end
 
 redef class Position
index d429697..51d302e 100644 (file)
@@ -231,32 +231,103 @@ class JSONStringParser
                return val
        end
 
+       private var parse_str_buf = new FlatBuffer
+
        # Parses and returns a Nit string from a JSON String
        fun parse_json_string: Jsonable do
+               var src = src
                var ln = src.length
                var p = pos
                p += 1
                if p > ln then return make_parse_error("Malformed JSON String")
                var c = src[p]
-               var st = p
+               var ret = parse_str_buf
+               var chunk_st = p
                while c != '"' do
-                       if c == '\\' then
-                               if p + 1 >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
+                       if c != '\\' then
                                p += 1
+                               if p >= ln then return make_parse_error("Malformed JSON string")
                                c = src[p]
-                               if c == 'u' then
+                               continue
+                       end
+                       ret.append_substring_impl(src, chunk_st, p - chunk_st)
+                       p += 1
+                       if p >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
+                       c = src[p]
+                       if c == 'r' then
+                               ret.add '\r'
+                               p += 1
+                       else if c == 'n' then
+                               ret.add '\n'
+                               p += 1
+                       else if c == 't' then
+                               ret.add '\t'
+                               p += 1
+                       else if c == 'u' then
+                               var cp = 0
+                               p += 1
+                               for i in [0 .. 4[ do
+                                       cp <<= 4
+                                       if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       c = src[p]
+                                       if c >= '0' and c <= '9' then
+                                               cp += c.code_point - '0'.code_point
+                                       else if c >= 'a' and c <= 'f' then
+                                               cp += c.code_point - 'a'.code_point + 10
+                                       else if c >= 'A' and c <= 'F' then
+                                               cp += c.code_point - 'A'.code_point + 10
+                                       else
+                                               make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       end
                                        p += 1
-                                       if p + 3 >= ln then return make_parse_error("Bad Unicode escape sequence in string")
-                                       for i in [0 .. 4[ do if not src[p + i].is_hexdigit then return make_parse_error("Bad Unicode escape sequence in string")
-                                       p += 3
                                end
+                               c = cp.code_point
+                               if cp >= 0xD800 and cp <= 0xDBFF then
+                                       if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       c = src[p]
+                                       if c != '\\' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       p += 1
+                                       c = src[p]
+                                       if c != 'u' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       var locp = 0
+                                       p += 1
+                                       for i in [0 .. 4[ do
+                                               locp <<= 4
+                                               if p > ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                               c = src[p]
+                                               if c >= '0' and c <= '9' then
+                                                       locp += c.code_point - '0'.code_point
+                                               else if c >= 'a' and c <= 'f' then
+                                                       locp += c.code_point - 'a'.code_point + 10
+                                               else if c >= 'A' and c <= 'F' then
+                                                       locp += c.code_point - 'A'.code_point + 10
+                                               else
+                                                       make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                               end
+                                               p += 1
+                                       end
+                                       c = (((locp & 0x3FF) | ((cp & 0x3FF) << 10)) + 0x10000).code_point
+                               end
+                               ret.add c
+                       else if c == 'b' then
+                               ret.add 8.code_point
+                               p += 1
+                       else if c == 'f' then
+                               ret.add '\f'
+                               p += 1
+                       else
+                               p += 1
+                               ret.add c
                        end
-                       p += 1
-                       if p >= ln then return make_parse_error("Malformed JSON String")
+                       chunk_st = p
                        c = src[p]
                end
                pos = p + 1
-               return src.substring(st, p - st).unescape_json
+               if ret.is_empty then return src.substring(chunk_st, p - chunk_st)
+               ret.append_substring_impl(src, chunk_st, p - chunk_st)
+               var rets = ret.to_s
+               ret.clear
+               return rets
        end
 
        # Ignores any character until a JSON separator is encountered
index 9982dd1..9811228 100644 (file)
@@ -3162,7 +3162,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
index 99f2356..539fb89 100644 (file)
@@ -1514,7 +1514,7 @@ redef class AAttrPropdef
        # Evaluate and set the default value of the attribute in `recv`
        private fun init_expr(v: NaiveInterpreter, recv: Instance)
        do
-               if is_lazy then return
+               if is_lazy or is_optional then return
                if has_value then
                        var f = v.new_frame(self, mreadpropdef.as(not null), [recv])
                        evaluate_expr(v, recv, f)
diff --git a/tests/base_is_optional.nit b/tests/base_is_optional.nit
new file mode 100644 (file)
index 0000000..ea13e28
--- /dev/null
@@ -0,0 +1,68 @@
+# 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
+
+fun foo(i: Int): Int
+do
+       'f'.output
+       i.output
+       return i
+end
+
+class A
+       # needed on the new
+       var i: Int
+
+       # initialized by the allocation
+       var j: Int = foo(2)
+
+       # optional in the new, default value evaluated if `null` is given
+       var k: Int = foo(3) is optional
+
+       # the `init` will initialize it
+       var l: Int is noautoinit
+       init do l = foo(4)
+
+       # initialized if needed on the first `read`
+       var m: Int = foo(5) is lazy
+
+       fun set
+       do
+               i = 10
+               j = 20
+               k = 30
+               l = 40
+               m = 50
+       end
+
+       fun test
+       do
+               #alt1#set
+               i.output
+               j.output
+               k.output
+               l.output
+               m.output
+               '\n'.output
+       end
+end
+
+var a
+a = new A(foo(100))
+a.test
+a = new A(foo(100), null)
+a.test
+a = new A(foo(100), foo(300))
+a.test
diff --git a/tests/sav/base_is_optional.res b/tests/sav/base_is_optional.res
new file mode 100644 (file)
index 0000000..3f89579
--- /dev/null
@@ -0,0 +1,33 @@
+f2
+f100
+f3
+f4
+100
+2
+3
+4
+f5
+5
+
+f2
+f100
+f3
+f4
+100
+2
+3
+4
+f5
+5
+
+f2
+f100
+f300
+f4
+100
+2
+300
+4
+f5
+5
+
diff --git a/tests/sav/base_is_optional_alt1.res b/tests/sav/base_is_optional_alt1.res
new file mode 100644 (file)
index 0000000..12d1806
--- /dev/null
@@ -0,0 +1,30 @@
+f2
+f100
+f3
+f4
+10
+20
+30
+40
+50
+
+f2
+f100
+f3
+f4
+10
+20
+30
+40
+50
+
+f2
+f100
+f300
+f4
+10
+20
+30
+40
+50
+
diff --git a/tests/sav/test_flatbuf_from.res b/tests/sav/test_flatbuf_from.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_flatbuf_from_alt1.res b/tests/sav/test_flatbuf_from_alt1.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_flatbuf_from_alt2.res b/tests/sav/test_flatbuf_from_alt2.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_flatbuf_from.nit b/tests/test_flatbuf_from.nit
new file mode 100644 (file)
index 0000000..a91883f
--- /dev/null
@@ -0,0 +1,25 @@
+# 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.
+
+#alt2 intrude import core::text::ropes
+
+var a = "String"
+
+var s: Text = a
+#alt1 s = new FlatBuffer
+#alt1 s.append(a)
+#alt2 var b = a
+#alt2 s = new Concat(a, b)
+
+var buf = new FlatBuffer.from(s)