`nitx` is now part of the `doc` group using the same architecture as `nitdoc`.
This PR also add two new features:
* locate calls to a MProperty
* search pieces of code
Fixes #262
Pull-Request: #1369
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
module benitlux_controller
import nitcorn
-import json_serialization
+private import json::serialization
import benitlux_model
import benitlux_db
# List markdown source files from a directory.
fun list_md_files(dir: String): Array[String] do
var files = new Array[String]
- var pipe = new ProcessReader("find", dir, "-name", "*.md")
+ var pipe = new ProcessReader("find", dir, "-name", "*.{config.md_ext}")
while not pipe.eof do
var file = pipe.read_line
if file == "" then break # last line
- var name = file.basename(".md")
+ var name = file.basename(".{config.md_ext}")
if name == "header" or name == "footer" or name == "menu" then continue
files.add file
end
#
# REQUIRE: `has_template`
fun load_template(name: String): TemplateString do
- assert has_template(name)
+ if not has_template(name) then
+ message("Error: can't load template `{name}`", 0)
+ exit 1
+ end
var file = expand_path(config.root_dir, config.templates_dir, name)
var tpl = new TemplateString.from_file(file)
if tpl.has_macro("ROOT_URL") then
# Create a new article using a markdown source file.
init from_source(wiki: Nitiwiki, md_file: String) do
src_full_path = md_file
- init(wiki, md_file.basename(".md"))
+ init(wiki, md_file.basename(".{wiki.config.md_ext}"))
content = md
end
# * default: `http://localhost/`
var root_url: String is lazy do return value_or_default("wiki.root_url", "http://localhost/")
+ # Markdown extension recognized by this wiki.
+ #
+ # We allow only one kind of extension per wiki.
+ # Files with other markdown extensions will be treated as resources.
+ #
+ # * key: `wiki.md_ext`
+ # * default: `md`
+ var md_ext: String is lazy do return value_or_default("wiki.md_ext", "md")
# Root directory of the wiki.
#
module wiki_html
import wiki_links
+import markdown::decorators
redef class Nitiwiki
sitemap.is_dirty = true
return sitemap
end
+
+ # Markdown processor used for inline element such as titles in TOC.
+ private var inline_processor: MarkdownProcessor is lazy do
+ var proc = new MarkdownProcessor
+ proc.emitter.decorator = new InlineDecorator
+ return proc
+ end
+
+ # Inline markdown (remove h1, p, ... elements).
+ private fun inline_md(md: Writable): Writable do
+ return inline_processor.process(md.write_to_string)
+ end
end
redef class WikiEntry
while iter.is_ok do
var hl = iter.item
# parse title as markdown
- var title = hl.title.md_to_html.to_s
- title = title.substring(3, title.length - 8)
+ var title = wiki.inline_md(hl.title)
tpl.add "<li><a href=\"#{hl.id}\">{title}</a>"
iter.next
if iter.is_ok then
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
--- /dev/null
+Render section out
--- /dev/null
+nitiWiki
+name: wiki3
+config: wiki3/config.ini
+url: http://localhost/
+
+There is modified files:
+ + pages
+ + /pages/contact.mdwn
+ + /pages/index.mdwn
+ + /pages/other_page.mdwn
+
+Use nitiwiki --render to render modified files
--- /dev/null
+wiki.name=wiki3
+wiki.root_dir=wiki3
+wiki.md_ext=mdwn
--- /dev/null
+# Other Page
--- /dev/null
+<div class="row footer">
+ <div class="container-fluid">
+ <div class="well well-sm">
+ <p><strong>%TITLE% © %YEAR%</strong></p>
+ <p class="text-muted"><em>last modification %GEN_TIME%</em></p>
+ <p class="text-muted">Proudly powered by
+ <a href="http://nitlanguage.org">nit</a>!</p>
+ </div>
+ </div>
+</div>
--- /dev/null
+<div class="container-fluid header">
+ <div class="container">
+ <div class="header">
+ <a href="http://uqam.ca"><img src="%ROOT_URL%/%LOGO%" alt="logo" /></a>
+ <h2>%SUBTITLE%</h2>
+ <h1>%TITLE%</h1>
+ </div>
+ </div>
+</div>
--- /dev/null
+<nav class="menu" role="navigation">
+ <div class="container">
+ <!-- Brand and toggle get grouped for better mobile display -->
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="%ROOT_URL%index.html">%TITLE%</a>
+ </div>
+ <!-- Collect the nav links, forms, and other content for toggling -->
+ <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+ <ul class="nav navbar-nav">
+ %MENUS%
+ </ul>
+ </div><!-- /.navbar-collapse -->
+ </div>
+</nav>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>%TITLE%</title>
+
+ <link href="%ROOT_URL%/assets/vendors/bootstrap/bootstrap-3.2.0-dist/css/bootstrap.min.css" rel="stylesheet">
+ <link href="%ROOT_URL%/assets/css/main.css" rel="stylesheet">
+
+ <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ %HEADER%
+ %TOP_MENU%
+ <div class="container">
+ <div class="row">
+ %BODY%
+ </div>
+ %FOOTER%
+ </div>
+
+ <script src="%ROOT_URL%/vendors/jquery/jquery-1.11.1.min.js"></script>
+ <script src="%ROOT_URL%/vendors/bootstrap/bootstrap-3.2.0-dist/js/bootstrap.min.js"></script>
+ </body>
+</html>
--- /dev/null
+../bin/nitiwiki --config wiki3/config.ini --clean --render -v
--- /dev/null
+../bin/nitiwiki --config wiki3/config.ini --clean --status
self.path = path
var file = sys.files[path]
prepare_buffer(file.length)
- _buffer.append(file)
+ path.copy_to_native(_buffer, file.length, 0, 0)
end
redef fun close
redef fun fill_buffer
do
- _buffer.clear
+ buffer_reset
end_reached = true
end
redef fun on_save_state
do
- app.data_store["context"] = context.to_json
+ app.data_store["context"] = context
super
end
do
super
- var save = app.data_store["context"]
- if save == null then return
- assert save isa String
+ var context = app.data_store["context"]
+ if not context isa CalculatorContext then return
- self.context = new CalculatorContext.from_json(save)
+ self.context = context
display.text = context.display_text
end
end
# Business logic of a calculator
module calculator_logic
-import json::dynamic
+import serialization
# Hold the state of the calculator and its services
class CalculatorContext
+ auto_serializable
+
# Result of the last operation
var result: nullable Numeric = null
self.result = result
self.current = null
end
-
- # Serialize calculator state to Json
- fun to_json: String
- do
- # Do not save NaN nor inf
- var result = self.result
- if result != null and (result.to_f.is_nan or result.to_f.is_inf != 0) then result = null
-
- var self_last_op = self.last_op
- var last_op
- if self_last_op == null then
- last_op = "null"
- else last_op = "\"{self_last_op}\""
-
- var self_current = self.current
- var current
- if self_current == null then
- current = "null"
- else current = "\"{self_current}\""
-
- return """
-{
- "result": {{{result or else "null"}}},
- "last_op": {{{last_op}}},
- "current": {{{current}}}
-}"""
- end
-
- # Load calculator state from Json
- init from_json(json_string: String)
- do
- var json = json_string.to_json_value
- if json.is_error then
- print "Loading state failed: {json.to_error}"
- return
- end
-
- var result = json["result"]
- if result.is_numeric then self.result = result.to_numeric
-
- var last_op = json["last_op"]
- if last_op.is_string then self.last_op = last_op.to_s.chars.first
-
- var current = json["current"]
- if current.is_string then self.current = new FlatBuffer.from(current.to_s)
- end
end
redef universal Float
# ~~~
module a_star
+import serialization
+
# General graph node
class Node
+ super Serializable
+
# Type of the others nodes in the `graph`
type N: Node
end
end
end
+
+ # We customize the serialization process to avoid problems with recursive
+ # serialization engines. These engines, such as `JsonSerializer`,
+ # are at danger to serialize the graph as a very deep tree.
+ # With a large graph it can cause a stack overflow.
+ #
+ # Instead, we serialize the nodes first and then the links.
+ redef fun core_serialize_to(serializer: Serializer)
+ do
+ serializer.serialize_attribute("graph", graph)
+ end
+
+ redef init from_deserializer(deserializer)
+ do
+ deserializer.notify_of_creation self
+
+ var graph = deserializer.deserialize_attribute("graph")
+ assert graph isa Graph[N, Link]
+ self.graph = graph
+ end
end
# Link between two nodes and associated to a graph
class Link
+ auto_serializable
+
# Type of the nodes in `graph`
type N: Node
# General graph
class Graph[N: Node, L: Link]
+ super Serializable
+
# Nodes in this graph
var nodes: Set[N] = new HashSet[N]
# Used to check if nodes have been searched in one pathfinding
private var pathfinding_current_evocation: Int = 0
+
+ redef fun core_serialize_to(serializer: Serializer)
+ do
+ serializer.serialize_attribute("nodes", nodes)
+ serializer.serialize_attribute("links", links)
+ end
+
+ redef init from_deserializer(deserializer)
+ do
+ deserializer.notify_of_creation self
+
+ var nodes = deserializer.deserialize_attribute("nodes")
+ assert nodes isa HashSet[N]
+ self.nodes = nodes
+
+ var links = deserializer.deserialize_attribute("links")
+ assert links isa HashSet[L]
+ for link in links do add_link link
+ end
end
# Result from path finding and a walkable path
class AStarPath[N]
+ auto_serializable
- # The total cost of this path
+ # Total cost of this path
var total_cost: Int
- # The list of nodes composing this path
+ # Nodes composing this path
var nodes = new List[N]
private var at: Int = 0
# Context related to an evocation of pathfinding
class PathContext
+ auto_serializable
+
# Type of the nodes in `graph`
type N: Node
# Warning: A* is not optimize for such a case
class ConstantPathContext
super PathContext
+ auto_serializable
redef fun worst_cost do return 1
redef fun cost(l) do return 1
# A `PathContext` for graphs with `WeightedLink`
class WeightedPathContext
super PathContext
+ auto_serializable
redef type L: WeightedLink
# A `Link` with a `weight`
class WeightedLink
super Link
+ auto_serializable
# The `weight`, or cost, of this link
var weight: Int
# Advanced path conditions with customizable accept states
class TargetCondition[N: Node]
+ auto_serializable
+
# Should the pathfinding accept `node` as a goal?
fun accept(node: N): Bool is abstract
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;
# =============== Bitmap header ================
for x in [0..13] do
- bitmap_header[x] = fileReader.read(1)[0].ascii
+ var b = fileReader.read_byte
+ if b == null then
+ return
+ end
+ bitmap_header[x] = b
end
self.file_size = get_value(bitmap_header.subarray(2, 4))
self.data_offset = get_value(bitmap_header.subarray(10, 4))
# =============== DIB header ================
for x in [0..39] do
- dib_header[x] = fileReader.read(1)[0].ascii
+ var b = fileReader.read_byte
+ if b == null then return
+ dib_header[x] = b
end
var dib_size = get_value(dib_header.subarray(0, 4))
# only support BITMAPINFOHEADER
var row = new Array[Int].with_capacity(self.width)
for y in [0..self.width[
do
- var red = fileReader.read(1)[0].ascii * 256 * 256
- var green = fileReader.read(1)[0].ascii * 256
- var blue = fileReader.read(1)[0].ascii
+ var bts = fileReader.read_bytes(3)
+ if bts.length != 3 then return
+ var red = bts[0] << 16
+ var green = bts[1] << 8
+ var blue = bts[2]
row.add(red + green + blue)
end
self.data.add(row)
fileReader.close
end #end of load_from_file method
- # Reads in a series of bytes from the FileReader when loading a Bitmap from a file
- # FileReader.read(1) is used due to https://github.com/privat/nit/issues/1264
- private fun read_chars(fr: FileReader, howMany: Int): String
- do
- var s = ""
- for x in [1..howMany]
- do
- s += fr.read(1)
- end
- return s
- end
-
# Converts the value contained in two or four bytes into an Int. Since Nit
# does not have a byte type, Int is used
private fun get_value(array: Array[Int]): Int
# 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
+
+# Decorator for span elements.
+#
+# InlineDecorator does not decorate things like paragraphs or headers.
+class InlineDecorator
+ super HTMLDecorator
+
+ redef fun add_paragraph(v, block) do v.emit_in block
+ redef fun add_headline(v, block) do v.emit_in block
+
+ redef fun add_code(v, block) do
+ v.add "<code>"
+ v.emit_in block
+ v.add "</code>"
+ end
+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
# Highly specific, but useful, collections-related classes.
module more_collections
+import serialization
+
# Simple way to store an `HashMap[K, Array[V]]`
#
# Unlike standard HashMap, MultiHashMap provides a new
# assert m["four"] == ['i', 'i', 'i', 'i']
# assert m["zzz"] == new Array[Char]
class MultiHashMap[K, V]
+ auto_serializable
super HashMap[K, Array[V]]
# Add `v` to the array associated with `k`.
# assert hm2[2, "not-two"] == null
# ~~~~
class HashMap2[K1, K2, V]
+ auto_serializable
+
private var level1 = new HashMap[K1, HashMap[K2, V]]
# Return the value associated to the keys `k1` and `k2`.
# assert hm3[2, "not-two", 22] == null
# ~~~~
class HashMap3[K1, K2, K3, V]
+ auto_serializable
+
private var level1 = new HashMap[K1, HashMap2[K2, K3, V]]
# Return the value associated to the keys `k1`, `k2`, and `k3`.
# assert dma.default == [65]
# ~~~~
class DefaultMap[K, V]
+ auto_serializable
super HashMap[K, V]
# The default value.
import c
intrude import standard::string
import serialization
-private import json_serialization
+private import json::serialization
in "C Header" `{
#include <mpi.h>
# fill_buffer now checks for a message in the message queue which is filled by user inputs.
redef fun fill_buffer
do
- _buffer.clear
_buffer_pos = 0
- _buffer.append check_message.to_s
+ var nns = check_message
+ var nslen = nns.cstring_length
+ _buffer_length = nslen
+ nns.copy_to(buffer, nslen, 0, 0)
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.
+
+# 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
redef class String super DirectSerializable end
redef class SimpleCollection[E] super Serializable end
redef class Map[K, V] super Serializable end
+
+redef class Couple[F, S]
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+ var first = v.deserialize_attribute("first")
+ var second = v.deserialize_attribute("second")
+ init(first, second)
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("first", first)
+ v.serialize_attribute("second", second)
+ end
+end
+
+redef class Container[E]
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+ var item = v.deserialize_attribute("item")
+ init item
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("item", first)
+ end
+end
# Creates a socket connection to host `host` on port `port`
init connect(host: String, port: Int)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null)
# Creates a client socket, this is meant to be used by accept only
private init server_side(h: SocketAcceptResult)
do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
socket = h.socket
addrin = h.addr_in
# timeout : Time in milliseconds before stopping to wait for events
fun ready_to_read(timeout: Int): Bool
do
- if _buffer_pos < _buffer.length then return true
+ if _buffer_pos < _buffer_length then return true
if end_reached then return false
var events = [new NativeSocketPollValues.pollin]
return pollin(events, timeout).length != 0
socket.write_byte value
end
+ redef fun write_bytes(s) do
+ if closed then return
+ socket.write(s.to_s)
+ end
+
fun write_ln(msg: Text)
do
if end_reached then return
redef fun fill_buffer
do
- _buffer.clear
+ _buffer_length = 0
_buffer_pos = 0
if not connected then return
var read = socket.read
close
end_reached = true
end
- _buffer.append(read)
+ enlarge(_buffer_capacity + read.length)
+ read.copy_to_native(_buffer, read.length, 0, 0)
+ _buffer_length = read.length
+ end
+
+ fun enlarge(len: Int) do
+ if _buffer_capacity >= len then return
+ while _buffer_capacity < len do _buffer_capacity *= 2
+ var ns = new NativeString(_buffer_capacity)
+ _buffer.copy_to(ns, _buffer_length - _buffer_pos, _buffer_pos, 0)
+ _buffer = ns
end
redef fun close
return write(*recv, &value, 1);
`}
- fun read: String import NativeString.to_s_with_length `{
+ fun read: String import NativeString.to_s_with_length, NativeString.to_s_with_copy `{
static char c[1024];
- int n = read(*recv, c, 1024);
+ int n = read(*recv, c, 1023);
if(n < 0) {
return NativeString_to_s_with_length("",0);
}
- char* ret = malloc(n + 1);
- memcpy(ret, c, n);
- ret[n] = '\0';
- return NativeString_to_s_with_length(ret, n);
+ c[n] = 0;
+ return NativeString_to_s_with_copy(c);
`}
# Sets an option for the socket
return
end
end_reached = false
- _buffer_pos = 0
- _buffer.clear
+ buffer_reset
end
redef fun close
do
super
- _buffer.clear
+ buffer_reset
end_reached = true
end
redef fun fill_buffer
do
- var nb = _file.io_read(_buffer.items, _buffer.capacity)
+ var nb = _file.io_read(_buffer, _buffer_capacity)
if nb <= 0 then
end_reached = true
nb = 0
end
- _buffer.length = nb
+ _buffer_length = nb
_buffer_pos = 0
end
super FileStream
super Writer
+ redef fun write_bytes(s) do
+ if last_error != null then return
+ if not _is_writable then
+ last_error = new IOError("cannot write to non-writable stream")
+ return
+ end
+ write_native(s.items, s.length)
+ end
+
redef fun write(s)
do
if last_error != null then return
if not _is_writable then
- last_error = new IOError("Cannot write to non-writable stream")
+ last_error = new IOError("cannot write to non-writable stream")
return
end
- if s isa FlatText then
- write_native(s.to_cstring, s.length)
- else
- for i in s.substrings do write_native(i.to_cstring, i.length)
- end
+ for i in s.substrings do write_native(i.to_cstring, i.length)
end
redef fun write_byte(value)
# ~~~
#
# See `Reader::read_all` for details.
- fun read_all: String
+ fun read_all: String do return read_all_bytes.to_s
+
+ fun read_all_bytes: Bytes
do
var s = open_ro
- var res = s.read_all
+ var res = s.read_all_bytes
s.close
return res
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
intrude import ropes
import error
+intrude import bytes
in "C" `{
#include <unistd.h>
# Reads a byte. Returns `null` on EOF or timeout
fun read_byte: nullable Int is abstract
+ # Reads a String of at most `i` length
+ fun read(i: Int): String do return read_bytes(i).to_s
+
# Read at most i bytes
- fun read(i: Int): String
+ fun read_bytes(i: Int): Bytes
do
- if last_error != null then return ""
- var s = new FlatBuffer.with_capacity(i)
+ if last_error != null then return new Bytes.empty
+ var s = new NativeString(i)
+ var buf = new Bytes(s, 0, 0)
while i > 0 and not eof do
- var c = read_char
+ var c = read_byte
if c != null then
- s.add(c)
+ buf.add c
i -= 1
end
end
- return s.to_s
+ return buf
end
# Read a string until the end of the line.
# Read all the stream until the eof.
#
- # The content of the file is returned verbatim.
+ # The content of the file is returned as a String.
#
# ~~~
# var txt = "Hello\n\nWorld\n"
# var i = new StringReader(txt)
# assert i.read_all == txt
# ~~~
- fun read_all: String
+ fun read_all: String do return read_all_bytes.to_s
+
+ # Read all the stream until the eof.
+ #
+ # The content of the file is returned verbatim.
+ fun read_all_bytes: Bytes
do
- if last_error != null then return ""
- var s = new FlatBuffer
+ if last_error != null then return new Bytes.empty
+ var s = new Bytes.empty
while not eof do
- var c = read_char
+ var c = read_byte
if c != null then s.add(c)
end
- return s.to_s
+ return s
end
# Read a string until the end of the line and append it to `s`.
# A `Stream` that can be written to
abstract class Writer
super Stream
+
+ # Writes bytes from `s`
+ fun write_bytes(s: Bytes) is abstract
+
# write a string
fun write(s: Text) is abstract
# Like `write_to` but return a new String (may be quite large)
#
- # This funtionnality is anectodical, since the point
+ # This funtionality is anectodical, since the point
# of streamable object to to be efficienlty written to a
# stream without having to allocate and concatenate strings
fun write_to_string: String
return c
end
- # Peeks up to `n` bytes in the buffer, returns an empty string on EOF
+ fun buffer_reset do
+ _buffer_length = 0
+ _buffer_pos = 0
+ end
+
+ # Peeks up to `n` bytes in the buffer
#
# The operation does not consume the buffer
#
# ~~~nitish
- # var x = new FileReader("File.txt")
- # assert x.peek(5) == x.read(5)
+ # var x = new FileReader.open("File.txt")
+ # assert x.peek(5) == x.read(5)
# ~~~
- fun peek(i: Int): String do
- if eof then return ""
- var b = new FlatBuffer.with_capacity(i)
- while i > 0 and not eof do
- b.add _buffer[_buffer_pos]
- _buffer_pos += 1
- i -= 1
+ fun peek(i: Int): Bytes do
+ if eof then return new Bytes.empty
+ var remsp = _buffer_length - _buffer_pos
+ if i <= remsp then
+ var bf = new Bytes.with_capacity(i)
+ bf.append_ns_from(_buffer, i, _buffer_pos)
+ return bf
end
- var nbuflen = b.length + (_buffer.length - _buffer_pos)
- var nbuf = new FlatBuffer.with_capacity(nbuflen)
- nbuf.append(b)
- while _buffer_pos < _buffer.length do
- nbuf.add(_buffer[_buffer_pos])
- _buffer_pos += 1
+ var bf = new Bytes.with_capacity(i)
+ bf.append_ns_from(_buffer, remsp, _buffer_pos)
+ _buffer_pos = _buffer_length
+ read_intern(i - bf.length, bf)
+ remsp = _buffer_length - _buffer_pos
+ var full_len = bf.length + remsp
+ if full_len > _buffer_capacity then
+ var c = _buffer_capacity
+ while c < full_len do c = c * 2 + 2
+ _buffer_capacity = c
end
+ var nns = new NativeString(_buffer_capacity)
+ bf.items.copy_to(nns, bf.length, 0, 0)
+ _buffer.copy_to(nns, remsp, _buffer_pos, bf.length)
+ _buffer = nns
_buffer_pos = 0
- _buffer = nbuf
- return b.to_s
+ _buffer_length = full_len
+ return bf
end
- redef fun read(i)
+ redef fun read_bytes(i)
do
- if last_error != null then return ""
- if eof then return ""
+ if last_error != null then return new Bytes.empty
+ var buf = new Bytes.with_capacity(i)
+ read_intern(i, buf)
+ return buf
+ end
+
+ # Fills `buf` with at most `i` bytes read from `self`
+ private fun read_intern(i: Int, buf: Bytes): Int do
+ if eof then return 0
var p = _buffer_pos
- var bufsp = _buffer.length - p
+ var bufsp = _buffer_length - p
if bufsp >= i then
_buffer_pos += i
- return _buffer.substring(p, i).to_s
+ buf.append_ns_from(_buffer, i, p)
+ return i
end
- _buffer_pos = _buffer.length
- var readln = _buffer.length - p
- var s = _buffer.substring(p, readln).to_s
- fill_buffer
- return s + read(i - readln)
+ _buffer_pos = _buffer_length
+ var readln = _buffer_length - p
+ buf.append_ns_from(_buffer, readln, p)
+ var rd = read_intern(i - readln, buf)
+ return rd + readln
end
- redef fun read_all
+ redef fun read_all_bytes
do
- if last_error != null then return ""
- var s = new FlatBuffer
+ if last_error != null then return new Bytes.empty
+ var s = new Bytes.with_capacity(10)
while not eof do
var j = _buffer_pos
- var k = _buffer.length
+ var k = _buffer_length
while j < k do
- s.add(_buffer[j])
+ s.add(_buffer[j].ascii)
j += 1
end
_buffer_pos = j
fill_buffer
end
- return s.to_s
+ return s
end
redef fun append_line_to(s)
loop
# First phase: look for a '\n'
var i = _buffer_pos
- while i < _buffer.length and _buffer[i] != '\n' do i += 1
+ while i < _buffer_length and _buffer[i] != '\n' do
+ i += 1
+ end
var eol
- if i < _buffer.length then
+ if i < _buffer_length then
assert _buffer[i] == '\n'
i += 1
eol = true
redef fun eof
do
- if _buffer_pos < _buffer.length then return false
+ if _buffer_pos < _buffer_length then return false
if end_reached then return true
fill_buffer
- return _buffer_pos >= _buffer.length and end_reached
+ return _buffer_pos >= _buffer_length and end_reached
end
# The buffer
- private var buffer: nullable FlatBuffer = null
+ private var buffer: NativeString = new NativeString(0)
# The current position in the buffer
- private var buffer_pos: Int = 0
+ private var buffer_pos = 0
+
+ # Length of the current buffer (i.e. nuber of bytes in the buffer)
+ private var buffer_length = 0
+
+ # Capacity of the buffer
+ private var buffer_capacity = 0
# Fill the buffer
protected fun fill_buffer is abstract
- # Is the last fill_buffer reach the end
+ # Has the last fill_buffer reached the end
protected fun end_reached: Bool is abstract
# Allocate a `_buffer` for a given `capacity`.
protected fun prepare_buffer(capacity: Int)
do
- _buffer = new FlatBuffer.with_capacity(capacity)
+ _buffer = new NativeString(capacity)
_buffer_pos = 0 # need to read
+ _buffer_length = 0
+ _buffer_capacity = capacity
end
end
private var content = new Array[String]
redef fun to_s do return content.to_s
redef fun is_writable do return not closed
+
+ redef fun write_bytes(b) do
+ content.add(b.to_s)
+ end
+
redef fun write(str)
do
assert not closed
source = ""
end
- redef fun read_all do
- var c = cursor
- cursor = source.length
- if c == 0 then return source
- return source.substring_from(c)
+ redef fun read_all_bytes do
+ var nslen = source.length - cursor
+ var nns = new NativeString(nslen)
+ source.copy_to_native(nns, nslen, cursor, 0)
+ return new Bytes(nns, nslen, nslen)
end
redef fun eof do return cursor >= source.length
import base64
intrude import standard::stream
+intrude import standard::bytes
# Websocket compatible listener
#
super TCPStream
init do
- _buffer = new FlatBuffer
+ _buffer = new NativeString(1024)
_buffer_pos = 0
+ _buffer_capacity = 1024
+ _buffer_length = 0
var headers = parse_handshake
var resp = handshake_response(headers)
end
# Frames a text message to be sent to a client
- private fun frame_message(msg: String): String
+ private fun frame_message(msg: String): Bytes
do
- var ans_buffer = new FlatBuffer
+ var ans_buffer = new Bytes.with_capacity(msg.length)
# Flag for final frame set to 1
# opcode set to 1 (for text)
- ans_buffer.add(129.ascii)
+ ans_buffer.add(129)
if msg.length < 126 then
- ans_buffer.add(msg.length.ascii)
+ ans_buffer.add(msg.length)
end
if msg.length >= 126 and msg.length <= 65535 then
- ans_buffer.add(126.ascii)
- ans_buffer.add(msg.length.rshift(8).ascii)
- ans_buffer.add(msg.length.ascii)
+ ans_buffer.add(126)
+ ans_buffer.add(msg.length.rshift(8))
+ ans_buffer.add(msg.length)
end
- ans_buffer.append(msg)
- return ans_buffer.to_s
+ if msg isa FlatString then
+ ans_buffer.append_ns_from(msg.items, msg.length, msg.index_from)
+ else
+ for i in msg.substrings do
+ ans_buffer.append_ns_from(i.as(FlatString).items, i.length, i.as(FlatString).index_from)
+ end
+ end
+ return ans_buffer
end
# Reads an HTTP frame
# Gets the message from the client, unpads it and reconstitutes the message
private fun unpad_message do
var fin = false
+ var bf = new Bytes.empty
while not fin do
var fst_byte = client.read_byte
var snd_byte = client.read_byte
if fin_flag != 0 then fin = true
var opcode = fst_byte.bin_and(15)
if opcode == 9 then
- _buffer.add(138.ascii)
- _buffer.add('\0')
- client.write(_buffer.to_s)
- _buffer_pos += 2
+ bf.add(138)
+ bf.add(0)
+ client.write(bf.to_s)
+ _buffer_pos = _buffer_length
return
end
if opcode == 8 then
var len = snd_byte.bin_and(127)
var payload_ext_len = 0
if len == 126 then
- var tmp = client.read(2)
+ var tmp = client.read_bytes(2)
if tmp.length != 2 then
last_error = new IOError("Error: received interrupted frame")
client.close
return
end
- payload_ext_len = tmp[1].ascii + tmp[0].ascii.lshift(8)
+ payload_ext_len = tmp[1] + tmp[0].lshift(8)
else if len == 127 then
# 64 bits for length are not supported,
# only the last 32 will be interpreted as a Nit Integer
- var tmp = client.read(8)
+ var tmp = client.read_bytes(8)
if tmp.length != 8 then
last_error = new IOError("Error: received interrupted frame")
client.close
return
end
for pos in [0 .. tmp.length[ do
- var i = tmp[pos].ascii
+ var i = tmp[pos]
payload_ext_len += i.lshift(8 * (7 - pos))
end
end
if mask_flag != 0 then
+ var mask = client.read_bytes(4).items
if payload_ext_len != 0 then
- var msg = client.read(payload_ext_len+4)
- var mask = msg.substring(0,4)
- _buffer.append(unmask_message(mask, msg.substring(4, payload_ext_len)))
- else
- if len == 0 then
- return
- end
- var msg = client.read(len+4)
- var mask = msg.substring(0,4)
- _buffer.append(unmask_message(mask, msg.substring(4, len)))
+ len = payload_ext_len
end
+ var msg = client.read_bytes(len).items
+ bf.append_ns(unmask_message(mask, msg, len), len)
end
end
+ _buffer = bf.items
+ _buffer_length = bf.length
end
# Unmasks a message sent by a client
- private fun unmask_message(key: String, message: String): String
+ private fun unmask_message(key: NativeString, message: NativeString, len: Int): NativeString
do
- var return_message = new FlatBuffer.with_capacity(message.length)
- var msg_iter = message.chars.iterator
+ var return_message = new NativeString(len)
- while msg_iter.is_ok do
- return_message.chars[msg_iter.index] = msg_iter.item.ascii.bin_xor(key.chars[msg_iter.index%4].ascii).ascii
- msg_iter.next
+ for i in [0 .. len[ do
+ return_message[i] = message[i].ascii.bin_xor(key[i%4].ascii).ascii
end
- return return_message.to_s
+ return return_message
end
# Checks if a connection to a client is available
redef fun connected do return client.connected
- redef fun write(msg)
- do
- client.write(frame_message(msg.to_s))
- end
+ redef fun write_bytes(s) do client.write_bytes(frame_message(s.to_s))
+
+ redef fun write(msg) do client.write(frame_message(msg.to_s).to_s)
redef fun is_writable do return client.connected
redef fun fill_buffer
do
- _buffer.clear
- _buffer_pos = 0
+ buffer_reset
unpad_message
end
- redef fun end_reached do return client._buffer_pos >= client._buffer.length and client.end_reached
+ redef fun end_reached do return client._buffer_pos >= client._buffer_length and client.end_reached
# Is there some data available to be read ?
fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout)
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)
var total_sends: Int = 0
var nullable_sends: Int = 0
+ var nullable_eq_sends: Int = 0
var buggy_sends: Int = 0
# Get a new visitor on a classef to add type count in `typecount`.
end
t = t.anchor_to(self.nclassdef.mclassdef.mmodule, self.nclassdef.mclassdef.bound_mtype)
if t isa MNullableType then
- self.nullable_sends += 1
+ var name = n.callsite.mproperty.name
+ if name == "==" or name == "!=" or name == "is_same_instance" then
+ self.nullable_eq_sends += 1
+ else
+ self.nullable_sends += 1
+ end
else if t isa MClassType then
# Nothing
else
print "--- Sends on Nullable Receiver ---"
var total_sends = 0
var nullable_sends = 0
+ var nullable_eq_sends = 0
var buggy_sends = 0
# Visit all the source code to collect data
visitor.enter_visit(nclassdef)
total_sends += visitor.total_sends
nullable_sends += visitor.nullable_sends
+ nullable_eq_sends += visitor.nullable_eq_sends
buggy_sends += visitor.buggy_sends
end
end
print "Total number of sends: {total_sends}"
- print "Number of sends on a nullable receiver: {nullable_sends} ({div(nullable_sends*100,total_sends)}%)"
+ print "Number of sends on a unsafe nullable receiver: {nullable_sends} ({div(nullable_sends*100,total_sends)}%)"
+ print "Number of sends on a safe nullable receiver: {nullable_eq_sends} ({div(nullable_eq_sends*100,total_sends)}%)"
print "Number of buggy sends (cannot determine the type of the receiver): {buggy_sends} ({div(buggy_sends*100,total_sends)}%)"
end
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"
sum: 0
--- Sends on Nullable Receiver ---
Total number of sends: 19
-Number of sends on a nullable receiver: 0 (0.00%)
+Number of sends on a unsafe nullable receiver: 0 (0.00%)
+Number of sends on a safe nullable receiver: 0 (0.00%)
Number of buggy sends (cannot determine the type of the receiver): 0 (0.00%)
# RTA metrics
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)
+ if name == "Couple[Serializable, Int]" then return new Couple[Serializable, Int].from_deserializer(self)
return super
end
end
+++ /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"