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>
.metadata/*
.github_data
+.DS_Store
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
# ---
# 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
* `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
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)
--- /dev/null
+# 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
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
# 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
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
# 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 ' '
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
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
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 "-"
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 "-"
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 "-"
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
--- /dev/null
+# 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
--- /dev/null
+# 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
# 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
# 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
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
if from < 0 then
count += from
- if count < 0 then return ""
+ if count <= 0 then return ""
from = 0
end
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
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.
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
#
# 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
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
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 `{
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 `{
`}
# 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.
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 `{
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 `{
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 `{
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 `{
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 `{
# 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 `{
`}
# 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
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 `{
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 `{
"\"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
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
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
# 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)
--- /dev/null
+# 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
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+# 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)