In reaction of recent updates on #394 I wanted to have some up-to-date numbers.
`nitmetrics --nullable` can count the sends on nullable receivers but safe and unsafe calls where merged. Note: safe calls on nullable are things like `x == null`, that is safe even if `x` is a nullable thing.
So this PR distinguishes safe and unsafe calls.
Some numbers:
~~~
nitmetrics --nullables ../lib ../src/nit*.nit
Total number of sends: 60262
Number of sends on a unsafe nullable receiver: 1750 (2.90%)
Number of sends on a safe nullable receiver: 2639 (4.37%)
~~~
Pull-Request: #1375
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
module benitlux_controller
import nitcorn
-import json_serialization
+private import json::serialization
import benitlux_model
import benitlux_db
module wiki_links
import wiki_base
-intrude import markdown
+import markdown::wikilinks
redef class Nitiwiki
# Looks up a WikiEntry by its `name`.
emitter = new MarkdownEmitter(self)
emitter.decorator = new NitiwikiDecorator(wiki, context)
end
-
- redef fun token_at(text, pos) do
- var token = super
- if not token isa TokenLink then return token
- if pos + 1 < text.length then
- var c = text[pos + 1]
- if c == '[' then return new TokenWikiLink(pos, c)
- end
- return token
- end
end
private class NitiwikiDecorator
# Article used to contextualize links.
var context: WikiArticle
- fun add_wikilink(v: MarkdownEmitter, link: Text, name, comment: nullable Text) do
+ redef fun add_wikilink(v, link, name, comment) do
var wiki = v.processor.as(NitiwikiMdProcessor).wiki
var target: nullable WikiEntry = null
var anchor: nullable String = null
v.add "</a>"
end
end
-
-# A NitiWiki link token.
-#
-# Something of the form `[[foo]]`.
-#
-# Allowed formats:
-#
-# * `[[Wikilink]]`
-# * `[[Wikilink/Bar]]`
-# * `[[Wikilink#foo]]`
-# * `[[Wikilink/Bar#foo]]`
-# * `[[title|Wikilink]]`
-# * `[[title|Wikilink/Bar]]`
-# * `[[title|Wikilink/Bar#foo]]`
-class TokenWikiLink
- super TokenLink
-
- redef fun emit_hyper(v) do
- v.decorator.as(NitiwikiDecorator).add_wikilink(v, link.as(not null), name, comment)
- end
-
- redef fun check_link(v, out, start, token) do
- var md = v.current_text
- var pos = start + 2
- var tmp = new FlatBuffer
- pos = md.read_md_link_id(tmp, pos)
- if pos < start then return -1
- var name = tmp.write_to_string
- if name.has("|") then
- var parts = name.split_once_on("|")
- self.name = parts.first
- self.link = parts[1]
- else
- self.name = null
- self.link = name
- end
- pos += 1
- pos = md.skip_spaces(pos)
- if pos < start then return -1
- pos += 1
- return pos
- end
-end
module bundle
import serialization
-import json_serialization
+import json::serialization
import platform
import activities
import dalvik
import android::bundle
import serialization
-private import json_serialization
+private import json::serialization
in "Java" `{
import android.content.Intent;
import dalvik
import serialization
-private import json_serialization
+private import json::serialization
in "Java" `{
import android.content.SharedPreferences;
- import android.content.Context;
+ import android.content.Context;
import android.app.Activity;
import java.util.Map;
import java.util.Iterator;
# See the License for the specific language governing permissions and
# limitations under the License.
-# Handles serialization and deserialization of objects to/from Json.
-module json_serialization
+# Handles serialization and deserialization of objects to/from JSON
+#
+# ## Nity JSON
+#
+# `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
+# and `JsonDeserializer` can read them. They both use meta-data added to the
+# generated JSON to recreate the Nit instances with the exact original type.
+#
+# For more information on Nit serialization, see: ../serialization/README.md
+#
+# ## Plain JSON
+#
+# The attribute `JsonSerializer::plain_json` triggers generating plain and
+# clean JSON. This format is easier to read for an human and a non-Nit program,
+# but it cannot be fully deserialized. It can still be read by services from
+# `json::static` and `json::dynamic`.
+#
+# A shortcut to this service is provided by `Serializable::to_plain_json`.
+#
+# ### Usage Example
+#
+# ~~~nitish
+# import json::serialization
+#
+# class Person
+# auto_serializable
+#
+# var name: String
+# var year_of_birth: Int
+# var next_of_kin: nullable Person
+# end
+#
+# var bob = new Person("Bob", 1986)
+# var alice = new Person("Alice", 1978, bob)
+#
+# assert bob.to_plain_json == """
+# {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
+#
+# assert alice.to_plain_json == """
+# {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
+# ~~~
+module serialization
-import serialization
-import json::static
+import ::serialization
+private import ::serialization::engine_tools
+private import static
# Serializer of Nit objects to Json string.
class JsonSerializer
# Target writing stream
var stream: Writer
+ # Write plain JSON? easier to read but does not support Nit deserialization
+ #
+ # If `false`, the default, serialize to support deserialization:
+ #
+ # * Write meta-data, including the types of the serialized objects so they can
+ # be deserialized to their original form using `JsonDeserializer`.
+ # * Use references when an object has already been serialized so to not duplicate it.
+ # * Support cycles in references.
+ # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
+ # * The generated JSON is standard and can be read by non-Nit programs.
+ # However, some Nit types are not represented by the simplest possible JSON representation.
+ # With the added meta-data, it can be complex to read.
+ #
+ # If `true`, serialize for other programs:
+ #
+ # * Nit objects are serialized to pure and standard JSON so they can
+ # be easily read by non-Nit programs and humans.
+ # * Nit objects are serialized for every references, so they can be duplicated.
+ # It is easier to read but it creates a larger output.
+ # * Does not support cycles, will replace the problematic references by `null`.
+ # * Does not serialize the meta-data needed to deserialize the objects
+ # back to regular Nit objects.
+ # * Keys of Nit `HashMap` are converted to their string reprensentation using `to_s`.
+ var plain_json = false is writable
+
+ # List of the current open objects, the first is the main target of the serialization
+ #
+ # Used only when `plain_json == true` to detect cycles in serialization.
+ private var open_objects = new Array[Object]
+
+ # Has the first attribute of the current object already been serialized?
+ #
+ # Used only when `plain_json == true`.
+ private var first_attribute = false
+
redef fun serialize(object)
do
if object == null then
stream.write "null"
- else object.serialize_to_json(self)
+ else
+ if plain_json then
+ for o in open_objects do
+ if object.is_same_serialized(o) then
+ # Cycle detected
+ stream.write "null"
+ return
+ end
+ end
+
+ open_objects.add object
+ end
+
+ first_attribute = true
+ object.serialize_to_json self
+ first_attribute = false
+
+ if plain_json then open_objects.pop
+ end
end
redef fun serialize_attribute(name, value)
do
- stream.write ", \"{name}\": "
+ if not plain_json or not first_attribute then
+ stream.write ", "
+ first_attribute = false
+ end
+
+ stream.write "\""
+ stream.write name
+ stream.write "\": "
super
end
redef fun serialize_reference(object)
do
- if refs_map.has_key(object) then
+ if not plain_json and refs_map.has_key(object) then
# if already serialized, add local reference
var id = ref_id_for(object)
- stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}"
+ stream.write "\{\"__kind\": \"ref\", \"__id\": "
+ stream.write id.to_s
+ stream.write "\}"
else
# serialize here
serialize object
private var text: Text
# Root json object parsed from input text.
- var root: nullable Jsonable is noinit
+ private var root: nullable Jsonable is noinit
# Depth-first path in the serialized object tree.
- var path = new Array[JsonObject]
+ private var path = new Array[JsonObject]
# Map of references to already deserialized objects.
private var id_to_object = new StrictHashMap[Int, Object]
private fun serialize_to_json(v: JsonSerializer)
do
var id = v.ref_id_for(self)
- v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
+ v.stream.write "\{"
+ if not v.plain_json then
+ v.stream.write "\"__kind\": \"obj\", \"__id\": "
+ v.stream.write id.to_s
+ v.stream.write ", \"__class\": \""
+ v.stream.write class_name
+ v.stream.write "\""
+ end
core_serialize_to(v)
v.stream.write "\}"
end
+
+ # Serialize this object to plain JSON
+ #
+ # This is a shortcut using `JsonSerializer::plain_json`,
+ # see its documentation for more information.
+ fun to_plain_json: String
+ do
+ var stream = new StringWriter
+ var serializer = new JsonSerializer(stream)
+ serializer.plain_json = true
+ serializer.serialize self
+ stream.close
+ return stream.to_s
+ end
end
redef class Int
end
redef class Char
- redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
+ redef fun serialize_to_json(v)
+ do
+ if v.plain_json then
+ v.stream.write to_s.to_json
+ else
+ v.stream.write "\{\"__kind\": \"char\", \"__val\": "
+ v.stream.write to_s.to_json
+ v.stream.write "\}"
+ end
+ end
end
redef class String
redef fun serialize_to_json(v)
do
# Register as pseudo object
- var id = v.ref_id_for(self)
- v.stream.write """{"__kind": "obj", "__id": """
- v.stream.write id.to_s
- v.stream.write """, "__class": """"
- v.stream.write class_name
- v.stream.write """", "__length": """
- v.stream.write length.to_s
- v.stream.write """, "__items": """
+ if not v.plain_json then
+ var id = v.ref_id_for(self)
+ v.stream.write """{"__kind": "obj", "__id": """
+ v.stream.write id.to_s
+ v.stream.write """, "__class": """"
+ v.stream.write class_name
+ v.stream.write """", "__length": """
+ v.stream.write length.to_s
+ v.stream.write """, "__items": """
+ end
+
serialize_to_pure_json v
- v.stream.write "\}"
+
+ if not v.plain_json then
+ v.stream.write "\}"
+ end
end
redef init from_deserializer(v: Deserializer)
redef class Array[E]
redef fun serialize_to_json(v)
do
- if class_name == "Array[nullable Serializable]" then
+ if v.plain_json or class_name == "Array[nullable Serializable]" then
# Using class_name to get the exact type,
# we do not want Array[Int] or anything else here.
# Register as pseudo object
var id = v.ref_id_for(self)
- v.stream.write """{"__kind": "obj", "__id": """
- v.stream.write id.to_s
- v.stream.write """, "__class": """"
- v.stream.write class_name
- v.stream.write """", "__length": """
- v.stream.write length.to_s
- v.stream.write """, "__keys": """
+ if v.plain_json then
+ v.stream.write "\{"
+ var first = true
+ for key, val in self do
+ if not first then
+ v.stream.write ", "
+ else first = false
+
+ if key == null then key = "null"
+
+ v.stream.write key.to_s.to_json
+ v.stream.write ": "
+ if not v.try_to_serialize(val) then
+ v.warn("element of type {val.class_name} is not serializable.")
+ v.stream.write "null"
+ end
+ end
+ v.stream.write "\}"
+ else
+ v.stream.write """{"__kind": "obj", "__id": """
+ v.stream.write id.to_s
+ v.stream.write """, "__class": """"
+ v.stream.write class_name
+ v.stream.write """", "__length": """
+ v.stream.write length.to_s
- keys.serialize_to_pure_json v
+ v.stream.write """, "__keys": """
+ keys.serialize_to_pure_json v
- v.stream.write """, "__values": """
- values.serialize_to_pure_json v
- v.stream.write "\}"
+ v.stream.write """, "__values": """
+ values.serialize_to_pure_json v
+
+ v.stream.write "\}"
+ end
end
# Instantiate a new `Array` from its serialized representation.
end
end
end
-
-# Maps instances to a value, uses `is_same_instance`
-#
-# Warning: This class does not implement all the services from `Map`.
-private class StrictHashMap[K, V]
- super Map[K, V]
-
- # private
- var map = new HashMap[K, Array[Couple[K, V]]]
-
- redef var length = 0
-
- redef fun is_empty do return length == 0
-
- # private
- fun node_at(key: K): nullable Couple[K, V]
- do
- if not map.keys.has(key) then return null
-
- var arr = map[key]
- for couple in arr do
- if couple.first.is_same_serialized(key) then
- return couple
- end
- end
-
- return null
- end
-
- redef fun [](key)
- do
- var node = node_at(key)
- assert node != null
- return node.second
- end
-
- redef fun []=(key, value)
- do
- var node = node_at(key)
- if node != null then
- node.second = value
- return
- end
-
- var arr
- if not map.keys.has(key) then
- arr = new Array[Couple[K, V]]
- map[key] = arr
- else arr = map[key]
-
- arr.add new Couple[K, V](key, value)
- self.length += 1
- end
-
- redef fun has_key(key) do return node_at(key) != null
-end
import app::data_store
private import xdg_basedir
private import sqlite3
-private import json_serialization
+private import json::serialization
redef class App
redef var data_store = new LinuxStore
--- /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.
+
+# Decorators for `markdown` parsing.
+module decorators
+
+import markdown
+
+# `Decorator` that outputs markdown.
+class MdDecorator
+ super Decorator
+
+ redef var headlines = new ArrayMap[String, HeadLine]
+
+ redef fun add_ruler(v, block) do v.add "***\n"
+
+ redef fun add_headline(v, block) do
+ # save headline
+ var txt = block.block.first_line.value
+ var id = strip_id(txt)
+ var lvl = block.depth
+ headlines[id] = new HeadLine(id, txt, lvl)
+ v.add "{"#" * lvl} "
+ v.emit_in block
+ v.addn
+ end
+
+ redef fun add_paragraph(v, block) do
+ v.emit_in block
+ v.addn
+ end
+
+ redef fun add_code(v, block) do
+ if block isa BlockFence and block.meta != null then
+ v.add "~~~{block.meta.to_s}"
+ else
+ v.add "~~~"
+ end
+ v.addn
+ v.emit_in block
+ v.add "~~~"
+ v.addn
+ end
+
+ redef fun add_blockquote(v, block) do
+ v.add "> "
+ v.emit_in block
+ v.addn
+ end
+
+ redef fun add_unorderedlist(v, block) do
+ in_unorderedlist = true
+ v.emit_in block
+ in_unorderedlist = false
+ end
+ private var in_unorderedlist = false
+
+ redef fun add_orderedlist(v, block) do
+ in_orderedlist = true
+ current_li = 0
+ v.emit_in block
+ in_unorderedlist = false
+ end
+ private var in_orderedlist = false
+ private var current_li = 0
+
+ redef fun add_listitem(v, block) do
+ if in_unorderedlist then
+ v.add "* "
+ else if in_orderedlist then
+ current_li += 1
+ v.add "{current_li} "
+ end
+ v.emit_in block
+ v.addn
+ end
+
+ redef fun add_em(v, text) do
+ v.add "*"
+ v.add text
+ v.add "*"
+ end
+
+ redef fun add_strong(v, text) do
+ v.add "**"
+ v.add text
+ v.add "**"
+ end
+
+ redef fun add_strike(v, text) do
+ v.add "~~"
+ v.add text
+ v.add "~~"
+ end
+
+ redef fun add_image(v, link, name, comment) do
+ v.add "!["
+ v.add name
+ v.add "]("
+ append_value(v, link)
+ if comment != null and not comment.is_empty then
+ v.add " "
+ append_value(v, comment)
+ end
+ v.add ")"
+ end
+
+ redef fun add_link(v, link, name, comment) do
+ v.add "["
+ v.add name
+ v.add "]("
+ append_value(v, link)
+ if comment != null and not comment.is_empty then
+ v.add " "
+ append_value(v, comment)
+ end
+ v.add ")"
+ end
+
+ redef fun add_abbr(v, name, comment) do
+ v.add "<abbr title=\""
+ append_value(v, comment)
+ v.add "\">"
+ v.emit_text(name)
+ v.add "</abbr>"
+ end
+
+ redef fun add_span_code(v, text, from, to) do
+ v.add "`"
+ append_code(v, text, from, to)
+ v.add "`"
+ end
+
+ redef fun add_line_break(v) do
+ v.add "\n"
+ end
+
+ redef fun append_value(v, text) do for c in text do escape_char(v, c)
+
+ redef fun escape_char(v, c) do v.addc(c)
+
+ redef fun append_code(v, buffer, from, to) do
+ for i in [from..to[ do
+ v.addc buffer[i]
+ end
+ end
+
+ redef fun strip_id(txt) do
+ # strip id
+ var b = new FlatBuffer
+ for c in txt do
+ if c == ' ' then
+ b.add '_'
+ else
+ if not c.is_letter and
+ not c.is_digit and
+ not allowed_id_chars.has(c) then continue
+ b.add c
+ end
+ end
+ var res = b.to_s
+ var key = res
+ # check for multiple id definitions
+ if headlines.has_key(key) then
+ var i = 1
+ key = "{res}_{i}"
+ while headlines.has_key(key) do
+ i += 1
+ key = "{res}_{i}"
+ end
+ end
+ return key
+ end
+
+ private var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
+end
# The emitter use a `Decorator` to select the output format.
class MarkdownEmitter
+ # Kind of processor used for parsing.
+ type PROCESSOR: MarkdownProcessor
+
# Processor containing link refs.
- var processor: MarkdownProcessor
+ var processor: PROCESSOR
+
+ # Kind of decorator used for decoration.
+ type DECORATOR: Decorator
# Decorator used for output.
# Default is `HTMLDecorator`
- var decorator: Decorator = new HTMLDecorator is writable
+ var decorator: DECORATOR is writable, lazy do
+ return new HTMLDecorator
+ end
# Create a new `MarkdownEmitter` using a custom `decorator`.
- init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do
+ init with_decorator(processor: PROCESSOR, decorator: DECORATOR) do
init processor
self.decorator = decorator
end
fun emit_in(block: Block) do block.emit_in(self)
# Transform and emit mardown text
- fun emit_text(text: Text) do
- emit_text_until(text, 0, null)
- end
+ fun emit_text(text: Text) do emit_text_until(text, 0, null)
# Transform and emit mardown text starting at `from` and
# until a token with the same type as `token` is found.
end
# Append `c` to current buffer.
- fun addc(c: Char) do current_buffer.add c
+ fun addc(c: Char) do add c.to_s
# Append a "\n" line break.
- fun addn do current_buffer.add '\n'
+ fun addn do add "\n"
end
# A Link Reference.
# Default decorator used is `HTMLDecorator`.
interface Decorator
+ # Kind of emitter used for decoration.
+ type EMITTER: MarkdownEmitter
+
# Render a ruler block.
- fun add_ruler(v: MarkdownEmitter, block: BlockRuler) is abstract
+ fun add_ruler(v: EMITTER, block: BlockRuler) is abstract
# Render a headline block with corresponding level.
- fun add_headline(v: MarkdownEmitter, block: BlockHeadline) is abstract
+ fun add_headline(v: EMITTER, block: BlockHeadline) is abstract
# Render a paragraph block.
- fun add_paragraph(v: MarkdownEmitter, block: BlockParagraph) is abstract
+ fun add_paragraph(v: EMITTER, block: BlockParagraph) is abstract
# Render a code or fence block.
- fun add_code(v: MarkdownEmitter, block: BlockCode) is abstract
+ fun add_code(v: EMITTER, block: BlockCode) is abstract
# Render a blockquote.
- fun add_blockquote(v: MarkdownEmitter, block: BlockQuote) is abstract
+ fun add_blockquote(v: EMITTER, block: BlockQuote) is abstract
# Render an unordered list.
- fun add_unorderedlist(v: MarkdownEmitter, block: BlockUnorderedList) is abstract
+ fun add_unorderedlist(v: EMITTER, block: BlockUnorderedList) is abstract
# Render an ordered list.
- fun add_orderedlist(v: MarkdownEmitter, block: BlockOrderedList) is abstract
+ fun add_orderedlist(v: EMITTER, block: BlockOrderedList) is abstract
# Render a list item.
- fun add_listitem(v: MarkdownEmitter, block: BlockListItem) is abstract
+ fun add_listitem(v: EMITTER, block: BlockListItem) is abstract
# Render an emphasis text.
- fun add_em(v: MarkdownEmitter, text: Text) is abstract
+ fun add_em(v: EMITTER, text: Text) is abstract
# Render a strong text.
- fun add_strong(v: MarkdownEmitter, text: Text) is abstract
+ fun add_strong(v: EMITTER, text: Text) is abstract
# Render a strike text.
#
# Extended mode only (see `MarkdownProcessor::ext_mode`)
- fun add_strike(v: MarkdownEmitter, text: Text) is abstract
+ fun add_strike(v: EMITTER, text: Text) is abstract
# Render a link.
- fun add_link(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+ fun add_link(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
# Render an image.
- fun add_image(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+ fun add_image(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
# Render an abbreviation.
- fun add_abbr(v: MarkdownEmitter, name: Text, comment: Text) is abstract
+ fun add_abbr(v: EMITTER, name: Text, comment: Text) is abstract
# Render a code span reading from a buffer.
- fun add_span_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+ fun add_span_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
# Render a text and escape it.
- fun append_value(v: MarkdownEmitter, value: Text) is abstract
+ fun append_value(v: EMITTER, value: Text) is abstract
# Render code text from buffer and escape it.
- fun append_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+ fun append_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
# Render a character escape.
- fun escape_char(v: MarkdownEmitter, char: Char) is abstract
+ fun escape_char(v: EMITTER, char: Char) is abstract
# Render a line break
- fun add_line_break(v: MarkdownEmitter) is abstract
+ fun add_line_break(v: EMITTER) is abstract
# Generate a new html valid id from a `String`.
fun strip_id(txt: String): String is abstract
--- /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.
+
+# Wikilinks handling.
+#
+# Wikilinks are on the form `[[link]]`.
+# They can also contain a custom title with the syntax `[[title|link]]`.
+#
+# By importing this module, you enable the `MarkdownProcessor` to recognize
+# `TokenWikiLink` but nothing will happen until you define a
+# `Decorator::add_wikilink` customized to your applciation domain.
+module wikilinks
+
+intrude import markdown
+
+# `MarkdownProcessor` is now able to parse wikilinks.
+redef class MarkdownProcessor
+
+ redef fun token_at(text, pos) do
+ var token = super
+ if not token isa TokenLink then return token
+ if pos + 1 < text.length then
+ var c = text[pos + 1]
+ if c == '[' then return new TokenWikiLink(pos, c)
+ end
+ return token
+ end
+end
+
+redef class Decorator
+
+ # Renders a `[[wikilink]]` item.
+ fun add_wikilink(v: EMITTER, link: Text, name, comment: nullable Text) do
+ if name != null then
+ v.add "[[{name}|{link}]]"
+ else
+ v.add "[[{link}]]"
+ end
+ end
+end
+
+# A NitiWiki link token.
+#
+# Something of the form `[[foo]]`.
+#
+# Allowed formats:
+#
+# * `[[Wikilink]]`
+# * `[[Wikilink/Bar]]`
+# * `[[Wikilink#foo]]`
+# * `[[Wikilink/Bar#foo]]`
+# * `[[title|Wikilink]]`
+# * `[[title|Wikilink/Bar]]`
+# * `[[title|Wikilink/Bar#foo]]`
+class TokenWikiLink
+ super TokenLink
+
+ redef fun emit_hyper(v) do
+ v.decorator.add_wikilink(v, link.as(not null), name, comment)
+ end
+
+ redef fun check_link(v, out, start, token) do
+ var md = v.current_text
+ var pos = start + 2
+ var tmp = new FlatBuffer
+ pos = md.read_md_link_id(tmp, pos)
+ if pos < start then return -1
+ var name = tmp.write_to_string
+ if name.has("|") then
+ var parts = name.split_once_on("|")
+ self.name = parts.first
+ self.link = parts[1]
+ else
+ self.name = null
+ self.link = name
+ end
+ pos += 1
+ pos = md.skip_spaces(pos)
+ if pos < start then return -1
+ pos += 1
+ return pos
+ end
+end
import c
intrude import standard::string
import serialization
-private import json_serialization
+private import json::serialization
in "C Header" `{
#include <mpi.h>
--- /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.
+
+# Advanced services for serialization engines
+module engine_tools
+
+import serialization
+
+# Maps instances to a value, uses `is_same_instance`
+#
+# Warning: This class does not implement all the services from `Map`.
+class StrictHashMap[K, V]
+ super Map[K, V]
+
+ # private
+ var map = new HashMap[K, Array[Couple[K, V]]]
+
+ redef var length = 0
+
+ redef fun is_empty do return length == 0
+
+ # private
+ fun node_at(key: K): nullable Couple[K, V]
+ do
+ if not map.keys.has(key) then return null
+
+ var arr = map[key]
+ for couple in arr do
+ if couple.first.is_same_serialized(key) then
+ return couple
+ end
+ end
+
+ return null
+ end
+
+ redef fun [](key)
+ do
+ var node = node_at(key)
+ assert node != null
+ return node.second
+ end
+
+ redef fun []=(key, value)
+ do
+ var node = node_at(key)
+ if node != null then
+ node.second = value
+ return
+ end
+
+ var arr
+ if not map.keys.has(key) then
+ arr = new Array[Couple[K, V]]
+ map[key] = arr
+ else arr = map[key]
+
+ arr.add new Couple[K, V](key, value)
+ self.length += 1
+ end
+
+ redef fun has_key(key) do return node_at(key) != null
+end
# assert files.is_empty
#
# TODO find a better way to handle errors and to give them back to the user.
- fun files: Array[String] is extern import Array[String], Array[String].add, NativeString.to_s, String.to_cstring `{
- char *dir_path;
- DIR *dir;
-
- dir_path = String_to_cstring( recv );
- if ((dir = opendir(dir_path)) == NULL)
- {
- //perror( dir_path );
- //exit( 1 );
- Array_of_String results;
- results = new_Array_of_String();
- return results;
- }
- else
- {
- Array_of_String results;
- String file_name;
- struct dirent *de;
-
- results = new_Array_of_String();
-
- while ( ( de = readdir( dir ) ) != NULL )
- if ( strcmp( de->d_name, ".." ) != 0 &&
- strcmp( de->d_name, "." ) != 0 )
- {
- file_name = NativeString_to_s( strdup( de->d_name ) );
- Array_of_String_add( results, file_name );
- }
+ fun files: Array[String]
+ do
+ var res = new Array[String]
+ var d = new NativeDir.opendir(to_cstring)
+ if d.address_is_null then return res
+
+ loop
+ var de = d.readdir
+ if de.address_is_null then break
+ var name = de.to_s_with_copy
+ if name == "." or name == ".." then continue
+ res.add name
+ end
+ d.closedir
- closedir( dir );
- return results;
- }
- `}
+ return res
+ end
end
redef class NativeString
new native_stderr is extern "file_NativeFileCapable_NativeFileCapable_native_stderr_0"
end
+# Standard `DIR*` pointer
+private extern class NativeDir `{ DIR* `}
+
+ # Open a directory
+ new opendir(path: NativeString) `{ return opendir(path); `}
+
+ # Close a directory
+ fun closedir `{ closedir(recv); `}
+
+ # Read the next directory entry
+ fun readdir: NativeString `{
+ struct dirent *de;
+ de = readdir(recv);
+ if (!de) return NULL;
+ return de->d_name;
+ `}
+end
+
redef class Sys
# Standard input
do
var ret_type = mmodule.native_array_type(elttype)
ret_type = anchor(ret_type).as(MClassType)
+ length = autobox(length, compiler.mainmodule.int_type)
return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
end
self.require_declaration("NEW_{mtype.mclass.c_name}")
assert mtype isa MGenericType
var compiler = self.compiler
+ length = autobox(length, compiler.mainmodule.int_type)
if mtype.need_anchor then
hardening_live_open_type(mtype)
link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
redef fun c_name do return "null"
redef fun as_nullable do return self
- # Aborts on `null`
- redef fun as_notnull do abort # sorry...
+ redef var as_notnull = new MBottomType(model) is lazy
+ redef fun need_anchor do return false
+ redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
+ redef fun can_resolve_for(mtype, anchor, mmodule) do return true
+
+ redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef]
+
+ redef fun collect_mclasses(mmodule) do return new HashSet[MClass]
+
+ redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
+end
+
+# The special universal most specific type.
+#
+# This type is intended to be only used internally for type computation or analysis and should not be exposed to the user.
+# The bottom type can de used to denote things that are absurd, dead, or the absence of knowledge.
+#
+# Semantically it is the singleton `null.as_notnull`.
+class MBottomType
+ super MType
+ redef var model: Model
+ redef fun to_s do return "bottom"
+ redef fun full_name do return "bottom"
+ redef fun c_name do return "bottom"
+ redef fun as_nullable do return model.null_type
+ redef fun as_notnull do return self
redef fun need_anchor do return false
redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
redef fun can_resolve_for(mtype, anchor, mmodule) do return true
+++ /dev/null
-Runtime error: Aborted (../lib/serialization/serialization.nit:109)
-# Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}
-
-# Back in Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
-
-# Back in Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
-
-# Back in Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111 f"\r\/> true>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": {"__kind": "ref", "__id": 0}}
-
-# Back in Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111 f"\r\/> true>
-
-Error: doesn't know how to deserialize class "Array"
if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
if name == "Array[String]" then return new Array[String].from_deserializer(self)
- if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
if name == "HashMap[Serializable, Array[Couple[Serializable, Int]]]" then return new HashMap[Serializable, Array[Couple[Serializable, Int]]].from_deserializer(self)
if name == "Array[Couple[Serializable, Int]]" then return new Array[Couple[Serializable, Int]].from_deserializer(self)
return super
+++ /dev/null
-Runtime error: Aborted (../lib/serialization/serialization.nit:109)
-# Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}
-
-# Back in Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
-
-# Back in Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
-
-# Back in Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111 f"\r\/> true>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": {"__kind": "ref", "__id": 0}}
-
-# Back in Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111 f"\r\/> true>
-
-Error: doesn't know how to deserialize class "Array[Object]"
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false>
+
+# Json:
+{"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
+
+# Json:
+{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}}
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false> 1111 f"\r\/> true>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": null}
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{"a": ["hello", 1234, 123.4], "b": ["hella", 2345, 234.5]}
+
+# Nit:
+<E: 2222>
+
+# Json:
+{"n": 2222}
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{"n": 33.33}
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"hs": [-1, 0], "s": ["one", "two"], "hm": {"one": 1, "two": 2}, "am": {"three": "3", "four": "4"}}
+
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false>
+
+# Json:
+{"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
+
+# Json:
+{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}}
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false> 1111 f"\r\/> true>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "\tf\"\r\\\/", "d": null}
+
# limitations under the License.
import serialization
-import json_serialization
# Simple class
class A
var a = new Array[Object].with_items("hello", 1234, 123.4)
var b = new Array[nullable Serializable].with_items("hella", 2345, 234.5)
- init do end
redef fun to_s do return "<E: a: {a.join(", ")}; b: {b.join(", ")}>"
end
auto_serializable
var n: N
- init(n: N) do self.n = n
redef fun to_s do return "<E: {n}>"
end
"hm: {hm.join(", ", ". ")}; am: {am.join(", ", ". ")}>"
end
-var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
-var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
-var c = new C(a, b)
-var d = new D(false, 'b', 123.123, 2345, "new line ->\n<-", null, 1111, "\t\f\"\r\\/")
-d.d = d
-var e = new E
-var fi = new F[Int](2222)
-var ff = new F[Float](33.33)
-var g = new G
-
-# Default works only with Nit serial
-var tests = [a, b, c, d, e, fi, ff, g: Serializable]
-
-# Alt1 should work without nitserial
-#alt1# tests = new Array[Serializable].with_items(a, b, c, d)
-
-for o in tests do
- var stream = new StringWriter
- var serializer = new JsonSerializer(stream)
- serializer.serialize(o)
-
- var deserializer = new JsonDeserializer(stream.to_s)
- var deserialized = deserializer.deserialize
-
- print "# Nit:\n{o}\n"
- print "# Json:\n{stream}\n"
- print "# Back in Nit:\n{deserialized or else "null"}\n"
+class TestEntities
+ var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
+ var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
+ var c = new C(a, b)
+ var d = new D(false, 'b', 123.123, 2345, "new line ->\n<-", null, 1111, "\t\f\"\r\\/")
+ init do d.d = d
+ var e = new E
+ var fi = new F[Int](2222)
+ var ff = new F[Float](33.33)
+ var g = new G
+
+ # should work without nitserial
+ var without_generics: Array[Serializable] = [a, b, c, d: Serializable]
+
+ # Default works only with Nit serial
+ var with_generics: Array[Serializable] = [a, b, c, d, e, fi, ff, g: Serializable]
end
+
+# We instanciate it here so that `nitserial` detects generic types as being alive
+var entities = new TestEntities
if name == "ArrayMap[String, String]" then return new ArrayMap[String, String].from_deserializer(self)
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
if name == "Array[String]" then return new Array[String].from_deserializer(self)
- if name == "HashMap[Serializable, Int]" then return new HashMap[Serializable, Int].from_deserializer(self)
- if name == "Array[JsonObject]" then return new Array[JsonObject].from_deserializer(self)
- if name == "HashMap[Int, Object]" then return new HashMap[Int, Object].from_deserializer(self)
- if name == "Array[Node]" then return new Array[Node].from_deserializer(self)
- if name == "Array[LRState]" then return new Array[LRState].from_deserializer(self)
if name == "Array[Couple[String, String]]" then return new Array[Couple[String, String]].from_deserializer(self)
- if name == "Array[nullable Jsonable]" then return new Array[nullable Jsonable].from_deserializer(self)
return super
end
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.
+
+import test_deserialization
+import json::serialization
+#alt1# import test_deserialization_serial
+
+var entities = new TestEntities
+
+var tests = entities.without_generics#alt1##alt2#
+#alt1#var tests = entities.with_generics
+#alt2#var tests = entities.with_generics
+
+for o in tests do
+ var stream = new StringWriter
+ var serializer = new JsonSerializer(stream)
+ #alt2#serializer.plain_json = true
+ serializer.serialize(o)
+
+ var deserializer = new JsonDeserializer(stream.to_s)#alt2#
+ var deserialized = deserializer.deserialize#alt2#
+
+ print "# Nit:\n{o}\n"
+ print "# Json:\n{stream}\n"
+ print "# Back in Nit:\n{deserialized or else "null"}\n"#alt2#
+end
print a[0]
print a.to_a.join(",")
+var i
+i = 3
+a = new NativeArray[Int](i)
+i = 1
+a[i] = i
+print a[i]
+print a[1]
# limitations under the License.
import serialization
-import json_serialization
+import json::serialization
# Simple class
class A
for o in new Array[Serializable].with_items(a, b, c, d) do
var stream = new StringWriter
var serializer = new JsonSerializer(stream)
+ #alt1#serializer.plain_json = true
serializer.serialize(o)
print "# Nit:\n{o}\n"