src/Makefile is already executed at the begin of the tests, so a double execution is mostly useless and time-expensive.
Pull-Request: #1888
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
do
super
- maximum_fps = 50
+ maximum_fps = 50.0
end
redef fun frame_core(display)
-all: nitiwiki
+all: nitiwiki bin/nitiwiki_server
nitiwiki:
mkdir -p bin
../../bin/nitc src/nitiwiki.nit -o bin/nitiwiki
+bin/nitiwiki_server: $(shell ../../bin/nitls -M src/wiki_edit.nit)
+ ../../bin/nitc -o $@ src/wiki_edit.nit
+
check: nitiwiki
cd tests; make
* `SUBTITLE`: Wiki description
* `LOGO`: Wiki logo image path
-Additionnal macros can be used in specialized templates.
+Additional macros can be used in specialized templates.
### Main template
Be sure to set `wiki.rsync_dir` in order to correctly push your changes.
When using `--rsync`, keep in mind that the rendered output must be configured
to work on the web server.
+
+### Serve and edit with nitiwiki_server
+
+nitiwiki_server is a lightweight web server to publish the generated files
+and accept modifications from a web form.
+
+The binary available in `bin/nitiwiki_server` is configured for simple usage or demo.
+The source of the server, at `src/wiki_edit`, can be tweaked for more advanced use.
+It is also possible to import the source and add an instance of `EditAction` to a custom nitcorn server.
+
+To launch the server, change directory to the root of the wiki and run `nitiwiki_server`.
+It uses `config.ini` from the local directory and listen on localhost:8080.
+The template should define the macro `%EDIT%` and `config.ini` should define `wiki.edit=/edit/`.
+To limit who can edit the wiki, list the md5 sum of accepted passwords (one per line) in the local file `passwords`.
--- /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.
+
+# Web server to server generated files and modify the wiki from a web form
+module wiki_edit
+
+import nitcorn
+import markdown
+import md5
+
+intrude import wiki_html
+
+# Page for editing markdown source
+class WikiEditForm
+ super WikiArticle
+
+ # Part of the title before the name of the page
+ var title_prefix: String
+
+ # Markdown content, for previews
+ redef var md
+
+ # Custom HTML code, for forms and links
+ var html: String
+
+ init do content = (md or else "").md_to_html.to_s + html
+
+ redef fun dir_href do return "edit" / href
+
+ redef fun tpl_article
+ do
+ var s = super
+ s.title = title_prefix + title
+ return s
+ end
+
+ # Fill and return a new `HttpResponse` with this page content
+ fun to_http_response: HttpResponse
+ do
+ var resp = new HttpResponse(200)
+ resp.body = tpl_page.write_to_string
+ return resp
+ end
+end
+
+# Action to serve edit forms, show previews and apply changes
+class EditAction
+ super Action
+
+ # Full public URL for the root of this wiki
+ var root_url: String
+
+ # Path to the wiki config
+ var config_file_path: String
+
+ # Configuration of the Wiki, loaded once
+ var wiki_config = new WikiConfig(config_file_path) is lazy
+
+ # Path to the root of the wiki
+ private var wiki_root: String = config_file_path.dirname is lazy
+
+ # Path to the source files
+ private var source_dir: String = (wiki_root / wiki_config.source_dir).simplify_path + "/" is lazy
+
+ # List of acceptable password to apply modifications
+ #
+ # If `null`, no password checks are applied and all modifications are accepted.
+ var passwords: nullable Collection[String]
+
+ # Reload the wiki instance with the latest changes
+ fun wiki: Nitiwiki
+ do
+ var wiki = new Nitiwiki(wiki_config)
+ wiki.parse
+ return wiki
+ end
+
+ redef fun answer(http_request, turi)
+ do
+ var action = http_request.string_arg("action")
+ var markdown = http_request.post_args.get_or_default("content", "")
+
+ var file_path = turi.strip_leading_slash
+ file_path = wiki_root / file_path
+
+ var abs_file_path = file_path.to_absolute_path
+ var abs_source_dir = source_dir.to_absolute_path
+
+ if not abs_file_path.has_prefix(abs_source_dir) then
+ # Attempting to access a file outside the source directory
+ var entity = new WikiEditForm(wiki, turi.strip_leading_slash,
+ "Access denied: ", "", "<p>Target outside of the source directory</p>")
+ return entity.to_http_response
+ end
+
+ if action == "Submit" then
+ var passwords = passwords
+ var password = http_request.post_args.get_or_null("password")
+ if passwords != null and (password == null or not passwords.has(password.md5)) then
+ # Deny modification
+ var entity = new WikiEditForm(wiki, turi.strip_leading_slash,
+ "Changes rejected: ", "", "<p>Password invalid</p>")
+ return entity.to_http_response
+ end
+
+ # Save markdown source
+ markdown = markdown.replace('\r', "")
+ markdown.write_to_file file_path
+
+ # Update HTML files
+ var wiki = wiki
+ wiki.render
+
+ var link
+ if turi.has_prefix("/pages/") then
+ link = root_url / turi.substring_from(7)
+ else link = root_url / turi
+ link = link.strip_extension(".md") + ".html"
+
+ # Show confirmation
+ var body = """
+<p>Your edits were recorded and the file is updated: <a href="{{{link}}}">{{{link}}}</a></p>
+"""
+ var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Changes saved: ", "", body)
+ return entity.to_http_response
+ else
+ # Show edit form, and preview when requested
+
+ # When not in a preview, use the local content of the file
+ if action != "Preview" then markdown = file_path.to_path.read_all
+
+ var form = """
+<form method="POST" action="/edit{{{turi}}}">
+ You may edit the file. When you are done, click on "Submit".<br/>
+ <textarea name="content" rows="30" cols="80">{{{markdown.html_escape}}}</textarea><br/>
+"""
+ if passwords != null then form += """
+ Password: <input type="password" name="password"><br/>
+"""
+ form += """
+ <input type="submit" name="action" value="Preview">
+ <input type="submit" name="action" value="Submit">
+</form>
+"""
+
+ # Show processed markdown only on preview
+ if action != "Preview" then markdown = ""
+
+ var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Edit source: ", markdown, form)
+ return entity.to_http_response
+ end
+ end
+end
+
+redef class String
+ private fun strip_leading_slash: String
+ do
+ if has_prefix("/") then return substring_from(1)
+ return self
+ end
+
+ private fun to_absolute_path: String
+ do
+ return (getcwd / self).simplify_path
+ end
+end
+
+var config_file_path = "config.ini"
+var iface = "localhost:8080"
+var password_file_path = "passwords"
+
+# Load passwords for file
+var passwords = if password_file_path.file_exists then
+ password_file_path.to_path.read_lines
+ else null
+
+var vh = new VirtualHost(iface)
+
+# Serve Markdown editing form
+var action = new EditAction("http://" + iface, config_file_path, passwords)
+vh.routes.add new Route("/edit", action)
+
+# Serve the static (and generated) content
+var path_to_public_files = config_file_path.dirname / action.wiki_config.out_dir
+vh.routes.add new Route(null, new FileServer(path_to_public_files))
+
+var factory = new HttpFactory.and_libevent
+factory.config.virtual_hosts.add vh
+factory.run
# Load `self` from saved data.
init from_json(game: Game, period: String, owner: GameEntity, json: JsonObject) do
+ init(game, period, owner)
var values = json.get_or_null("values")
if not values isa JsonObject then return
for k, v in values do self[k] = v.as(Int)
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="http://xymus.net/ens/">Enseignement</a></li>
- <li class="active"><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
+ <li><a href="http://xymus.net/opportunity/">Opportunité</a></li>
<li><a href="http://tnitter.xymus.net/">Tnitter</a></li>
+ <li class="active"><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
<li><a href="http://benitlux.xymus.net/">Benitlux</a></li>
- <li><a href="http://xymus.net/opportunity/">Opportunité</a></li>
- <li><a href="http://nitlanguage.org/">Nit</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
# * `max`: max amount refunded for this `Care` in a reclamation sheet.
# * `month_max`: max amount refunded by month.
init with_vals(id: Int, cover: Float, max, month_max: nullable Dollar) do
- self.id = id
+ init(id)
self.cover = cover
self.max = max
self.month_max = month_max
# * `max`: max amount refunded for this `Care` in a reclamation sheet.
# * `month_max`: max amount refunded by month.
init with_vals(id: Range[Int], cover: Float, max, month_max: nullable Dollar) do
- self.id = id
+ init(id)
self.cover = cover
self.max = max
self.month_max = month_max
redef fun on_create
do
super
- maximum_fps = 60
+ maximum_fps = 60.0
assets.assign_images_to_story context.game.story
end
return new LocalServerContext
else
print "Connecting to:{address}:{port}"
- maximum_fps = 0
+ maximum_fps = 0.0
# Args are: tinks server_address {port}
#var address = "riph" # args[0]
gyroscope.enabled = true
light.enabled = true
proximity.enabled = true
- maximum_fps = 50
+ maximum_fps = 50.0
sensors_support_enabled = true
super
end
redef fun run
do
- maximum_fps = 50
+ maximum_fps = 50.0
super
end
do
super
- maximum_fps = 80
+ maximum_fps = 80.0
var display = display
assert display != null
private init native(res: NativeResources, app_package: String)
do
- self.android_resources = res.new_global_ref
- self.app_package = app_package
+ init(res.new_global_ref, app_package)
end
# Get a color from resources
+++ /dev/null
-[package]
-name=buffered_ropes
-tags=algo,text,lib
-maintainer=Lucas Bajolet <r4pass@hotmail.com>
-license=Apache-2.0
-[upstream]
-browse=https://github.com/nitlang/nit/tree/master/lib/buffered_ropes.nit
-git=https://github.com/nitlang/nit.git
-git.directory=lib/buffered_ropes.nit
-homepage=http://nitlanguage.org
-issues=https://github.com/nitlang/nit/issues
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Ropes with a special kind of Leaves that act similar to a `Buffer`
-#
-# When using this module, re-allocations are limited by the introduction
-# of a larger-than-necessary buffered area for the native part of a `String`
-# in an append-only fashion.
-#
-# Concretely, when concatenating two small strings of length `n` + `m` < `maxlen`
-# What happens is that a `maxlen` byte buffer is allocated, ready to receive more
-# bytes a posteriori without necessarily reallocating a new byte array.
-#
-# Theoretically, this should lower the number of concatenations
-# and reallocations when concatenating `String` objects.
-module buffered_ropes
-
-intrude import core::text::ropes
-
-# Hidden buffer, used to simulate a `FlatBuffer` on a short string.
-#
-# This is to be used by low-level APIs because of its lack of
-# safety, if you use it, make sure you know what you are doing !
-#
-# Practically, it is the underlying representation of a `Leaf` in
-# the `Rope` block, its advantage is that it saves a bit more space
-# for future concatenations, without risking to overwrite previously
-# used space, making it suitable for Strings.
-#
-# Note for future use : Should there be parallel capacity in Nit at
-# some point, this is NOT thread safe !
-private class ManualBuffer
- var ns: NativeString is noinit
- # Current position in the `NativeString`
- #
- # It is used by the clients of `ManualBuffer` as a guard
- # to detect if the concatenation in the `ManualBuffer`
- # is safe or not.
- #
- # i.e. :
- # Say we have two strings `x` and `y` referencing the
- # same `ManualBuffer` `b`, `y` is the concatenation of
- # `x` and another string.
- #
- # If we try to concatenate a `String` `z` to `x`, a new
- # `ManualBuffer` will be created since `pos` and `x.length`
- # do not match.
- #
- # However, if we concatenate the same `String` to `y`,
- # the contents of `z` will be copied to the `ManualBuffer`.
- var pos = 0
-
- init do ns = new NativeString(maxlen)
-
- fun [](i: Int): Byte do return ns[i]
-end
-
-# Simple implementation of the iterator on Substrings for `Leaf`
-#
-# Basically just returns `self` encapsulated in a `FlatString`.
-private class LeafSubstrings
- super IndexedIterator[FlatText]
-
- var leaf: Leaf
- var str: FlatString is noinit
- var avail = true
-
- init do
- str = new FlatString.with_infos(leaf.buf.ns, leaf.length, 0, leaf.length - 1)
- end
-
- redef fun is_ok do return avail
-
- redef fun next do avail = false
-
- redef fun index do return 0
-
- redef fun item do return str
-end
-
-# Leaf of a `Rope`, used as a buffered area for speedy concatenation.
-private class Leaf
- super String
- super Rope
-
- var buf: ManualBuffer
- var bns: NativeString is noinit
- redef var length is noinit
-
- # Unsafe, but since it is an experiment, don't mind
- redef fun bytelen do return length
-
- redef fun empty do return new Leaf(new ManualBuffer)
-
- redef fun to_cstring do
- var len = length
- var ns = new NativeString(len + 1)
- ns[len] = 0u8
- buf.ns.copy_to(ns, len, 0, 0)
- return ns
- end
-
- redef fun substrings do return new LeafSubstrings(self)
-
- redef fun [](i) do return buf[i].to_i.code_point
-
- init do
- bns = buf.ns
- length = buf.pos
- end
-
- redef fun output do new FlatString.with_infos(buf.ns, length, 0, length - 1).output
-
- redef fun to_upper do
- var x = new FlatBuffer
- for i in chars do x.add(i.to_upper)
- return x.to_s
- end
-
- redef fun to_lower do
- var x = new FlatBuffer
- for i in chars do x.add(i.to_lower)
- return x.to_s
- end
-
- redef fun reversed do
- var x = new ManualBuffer
- var nns = x.ns
- var ns = bns
- var mlen = length
- var j = mlen - 1
- for i in [0 .. mlen[ do
- nns[j] = ns[i]
- j -= 1
- end
- x.pos = mlen - 1
- return new Leaf(x)
- end
-
- redef fun substring(from, len) do
- return new FlatString.with_infos(buf.ns, len, from, from + len - 1)
- end
-
- redef fun insert_at(s, pos) do
- var l = substring(0, pos)
- var r = substring_from(pos)
- return l + s + r
- end
-
- redef fun +(o) do
- var s = o.to_s
- var slen = s.bytelen
- var mlen = bytelen
- if slen == 0 then return self
- if mlen == 0 then return s
- var nlen = mlen + slen
- if nlen > maxlen then return new Concat(self, s)
- if s isa FlatString then
- var bpos = buf.pos
- var sits = s.items
- if bpos == mlen then
- sits.copy_to(buf.ns, slen, s.first_byte, bpos)
- buf.pos = bpos + slen
- return new Leaf(buf)
- else
- var b = new ManualBuffer
- var nbns = b.ns
- bns.copy_to(nbns, mlen, 0, 0)
- sits.copy_to(nbns, slen, s.first_byte, mlen)
- b.pos = nlen
- return new Leaf(b)
- end
- else if s isa Leaf then
- var bpos = buf.pos
- var sbns = s.bns
- if bpos == mlen then
- sbns.copy_to(bns, slen, 0, bpos)
- buf.pos += slen
- return new Leaf(buf)
- else
- var b = new ManualBuffer
- var nbns = b.ns
- bns.copy_to(nbns, mlen, 0, 0)
- sbns.copy_to(nbns, slen, 0, mlen)
- b.pos = nlen
- return new Leaf(b)
- end
- else if s isa Concat then
- if not s.left isa Concat then
- return new Concat(self + s.left, s.right)
- end
- return new Concat(self, s)
- else
- var bpos = buf.pos
- var b = buf
- if bpos != mlen then
- b = new ManualBuffer
- bns.copy_to(b.ns, mlen, 0, 0)
- end
- for i in s.bytes do
- bns[bpos] = i
- bpos += 1
- end
- return new Leaf(b)
- end
- end
-end
-
-redef class Concat
- redef fun to_cstring do
- var len = length
- var ns = new NativeString(len + 1)
- ns[len] = 0u8
- var off = 0
- for i in substrings do
- var ilen = i.length
- if i isa FlatString then
- i.items.copy_to(ns, ilen, i.first_byte, off)
- else if i isa Leaf then
- i.buf.ns.copy_to(ns, ilen, 0, off)
- else
- abort
- end
- off += ilen
- end
- return ns
- end
-
- redef fun +(o) do
- var s = o.to_s
- var slen = s.length
- if s isa FlatString then
- var r = right
- var rlen = r.length
- if rlen + slen > maxlen then return new Concat(left, new Concat(r, s))
- return new Concat(left, r + s)
- else if s isa Concat then
- return new Concat(self, s)
- else if s isa Leaf then
- var r = right
- var rlen = r.length
- if rlen + slen > maxlen then return new Concat(left, new Concat(r, s))
- return new Concat(left, r + s)
- else
- abort
- end
- end
-end
-
-redef class FlatString
- redef fun +(o) do
- var s = o.to_s
- var slen = s.length
- var mlen = length
- if slen == 0 then return self
- if mlen == 0 then return s
- if s isa FlatString then
- if slen + mlen > maxlen then return new Concat(self, s)
- var mits = items
- var sifrom = s.first_byte
- var mifrom = first_byte
- var sits = s.items
- var b = new ManualBuffer
- var bns = b.ns
- mits.copy_to(bns, mlen, mifrom, 0)
- sits.copy_to(bns, slen, sifrom, mlen)
- b.pos = mlen + slen
- return new Leaf(b)
- else if s isa Concat then
- var sl = s.left
- var sllen = sl.length
- if sllen + mlen > maxlen then return new Concat(self, s)
- return new Concat(sl + self, s.right)
- else if s isa Leaf then
- if slen + mlen > maxlen then return new Concat(self, s)
- var mifrom = first_byte
- var sb = s.buf
- var b = new ManualBuffer
- var bns = b.ns
- items.copy_to(bns, mlen, mifrom, 0)
- sb.ns.copy_to(bns, slen, 0, mlen)
- b.pos = mlen + slen
- return new Leaf(b)
- else
- abort
- end
- end
-end
-
-redef class Array[E]
-
- # Fast implementation
- redef fun to_s do
- var l = length
- if l == 0 then return ""
- if l == 1 then if self[0] == null then return "" else return self[0].to_s
- var its = _items
- var na = new NativeArray[String](l)
- var i = 0
- var sl = 0
- var mypos = 0
- while i < l do
- var itsi = its[i]
- if itsi == null then
- i += 1
- continue
- end
- var tmp = itsi.to_s
- sl += tmp.length
- na[mypos] = tmp
- i += 1
- mypos += 1
- end
- var ns = new NativeString(sl + 1)
- ns[sl] = 0u8
- i = 0
- var off = 0
- while i < mypos do
- var tmp = na[i]
- var tpl = tmp.length
- if tmp isa FlatString then
- tmp.items.copy_to(ns, tpl, tmp.first_byte, off)
- off += tpl
- else
- for j in tmp.substrings do
- var slen = j.length
- if j isa FlatString then
- j.items.copy_to(ns, slen, j.first_byte, off)
- else if j isa Leaf then
- j.buf.ns.copy_to(ns, slen, 0, off)
- end
- off += slen
- end
- end
- i += 1
- end
- return ns.to_s_with_length(sl)
- end
-end
end
# Return the canonicalized absolute pathname (see POSIX function `realpath`)
+ #
+ # Require: `file_exists`
fun realpath: String do
var cs = to_cstring.file_realpath
+ assert file_exists
var res = cs.to_s_with_copy
- # cs.free_malloc # FIXME memory leak
+ cs.free
return res
end
# `i` bits shift fo the left
#
# assert 5u8 << 1 == 10u8
- fun <<(i: Int): Byte `{ return self << i; `}
+ fun <<(i: Int): Byte is intern do return lsh(i)
+
+ private fun lsh(i: Int): Byte `{ return self << i; `}
# `i` bits shift fo the right
#
# assert 5u8 >> 1 == 2u8
- fun >>(i: Int): Byte `{ return self >> i; `}
+ fun >>(i: Int): Byte is intern do return rsh(i)
+
+ private fun rsh(i: Int): Byte `{ return self >> i; `}
# Returns the character equivalent of `self`
#
# REQUIRE: `self <= 127u8`
- fun ascii: Char `{ return (uint32_t)self; `}
+ fun ascii: Char is intern do return ffi_ascii
+
+ private fun ffi_ascii: Char `{ return (uint32_t)self; `}
redef fun to_i is intern
redef fun to_f is intern
# `i` bits shift fo the left
#
# assert 5 << 1 == 10
- fun <<(i: Int): Int `{ return self << i; `}
+ fun <<(i: Int): Int is intern do return lsh(i)
+
+ private fun lsh(i: Int): Int `{ return self << i; `}
# `i` bits shift fo the right
#
# assert 5 >> 1 == 2
- fun >>(i: Int): Int `{ return self >> i; `}
+ fun >>(i: Int): Int is intern do return rsh(i)
+
+ private fun rsh(i: Int): Int `{ return self >> i; `}
redef fun to_i do return self
redef fun to_f is intern
# assert 65.code_point == 'A'
# assert 10.code_point == '\n'
# assert 0x220B.code_point == '∋'
- fun code_point: Char `{ return (uint32_t)self; `}
+ fun code_point: Char is intern do return cp
+
+ private fun cp: Char `{ return (uint32_t)self; `}
# Number of digits of an integer in base `b` (plus one if negative)
#
# assert (-10).abs == 10
# assert 10.abs == 10
# assert 0.abs == 0
- fun abs: Int
- do
- if self >= 0
- then
- return self
- else
- return -1 * self
- end
- end
+ fun abs: Int do return if self >= 0 then self else -self
end
# Native characters.
# assert 'A'.code_point == 65
# assert '\n'.code_point == 10
# assert '∋'.code_point == 0x220B
- fun code_point: Int `{ return (long)self; `}
+ fun code_point: Int is intern do return cp
+
+ private fun cp: Int `{ return (long)self; `}
# Is `self` an ASCII character ?
#
# Returns the result of a binary AND operation on `self` and `i`
#
# assert 0x10 & 0x01 == 0
- fun &(i: Int): Int `{ return self & i; `}
+ fun &(i: Int): Int is intern do return band(i)
+
+ private fun band(i: Int): Int `{ return self & i; `}
# Returns the result of a binary OR operation on `self` and `i`
#
# assert 0x10 | 0x01 == 0x11
- fun |(i: Int): Int `{ return self | i; `}
+ fun |(i: Int): Int is intern do return bor(i)
+
+ private fun bor(i: Int): Int `{ return self | i; `}
# Returns the result of a binary XOR operation on `self` and `i`
#
# Returns the result of a binary AND operation on `self` and `i`
#
# assert 0x10u8 & 0x01u8 == 0u8
- fun &(i: Byte): Byte `{ return self & i; `}
+ fun &(i: Byte): Byte is intern do return band(i)
+
+ private fun band(i: Byte): Byte `{ return self & i; `}
# Returns the result of a binary OR operation on `self` and `i`
#
return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
end
+ # Is `self` an hexadecimal digit ?
+ #
+ # assert 'A'.is_hexdigit
+ # assert not 'G'.is_hexdigit
+ # assert 'a'.is_hexdigit
+ # assert not 'g'.is_hexdigit
+ # assert '5'.is_hexdigit
+ fun is_hexdigit: Bool do return (self >= '0' and self <= '9') or (self >= 'A' and self <= 'F') or
+ (self >= 'a' and self <= 'f')
+
# Returns true if the char is an alpha or a numeric digit
#
# assert 'a'.is_alphanumeric
`}
end
+redef class Float
+ # Sleep approximately `self` seconds
+ fun sleep `{
+ time_t s = self;
+ long ns = (self-s) * 1000000000.0;
+ const struct timespec req = {s, ns};
+ nanosleep(&req, NULL);
+ `}
+end
+
# Time since epoch
extern class TimeT `{time_t`}
# Create a new writer with the specified format.
init with_format(ostream:Writer, format: CsvFormat) do
- self.ostream = ostream
+ init(ostream)
self.format = format
end
# Create a new reader with the specified format.
init with_format(istream:Reader, format: CsvFormat) do
- self.istream = istream
+ init(istream)
self.format = format
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.
+
+# Frame-rate control for applications
+module limit_fps
+
+import gamnit
+private import realtime
+
+redef class App
+ # Limit the frame-rate to a given frequency
+ #
+ # This basically limits how much `frame_core` is called per second.
+ # Zero (or a negative value) means no limit.
+ #
+ # Applications can modify this value even during the main-loop.
+ var maximum_fps = 60.0 is writable
+
+ # Current frame-rate
+ #
+ # Updated each 5 seconds.
+ var current_fps = 0.0
+
+ redef fun frame_full
+ do
+ super
+ limit_fps
+ end
+
+ # The clock for limit_fps
+ private var clock = new Clock
+
+ # Number of frames since the last deadline
+ #
+ # Used to compute `current_fps`.
+ private var frame_count = 0
+
+ # Deadline used to compute `current_fps`
+ private var frame_count_deadline = 0
+
+ # Check and sleep to maintain a frame-rate bellow `maximum_fps`
+ #
+ # Also periodically update `current_fps`
+ # Is automatically called at the end of `full_frame`.
+ fun limit_fps
+ do
+ var t = clock.total.sec
+ if t >= frame_count_deadline then
+ var cfps = frame_count.to_f / 5.0
+ self.current_fps = cfps
+ frame_count = 0
+ frame_count_deadline = t + 5
+ end
+ frame_count += 1
+
+ var mfps = maximum_fps
+ if mfps <= 0.0 then return
+ var lapse = clock.lapse
+ var dt = lapse.to_f
+ var target_dt = 1.0 / mfps
+ if dt < target_dt then
+ var sleep_t = target_dt - dt
+ sleep_t.sleep
+ clock.lapse
+ end
+ end
+end
# assert box.right == 4 and box.top == 4
init around(boxed: Boxed3d[N]...)
do
+ super
+
assert not boxed.is_empty
var left: nullable N = null
# Init `self` from a `json` object.
init from_json(api: GithubAPI, repo: Repo, json: JsonObject) do
- self.api = api
- self.repo = repo
+ init(api, repo)
self.json = json
end
end
# Init `self` from a `json` object.
init from_json(api: GithubAPI, json: JsonObject) do
- self.api = api
- self.json = json
+ init(api, json)
end
# User these statistics are about.
# Init `self` from a `json` object.
init from_json(api: GithubAPI, json: JsonObject) do
- self.api = api
+ init(api)
self.json = json
end
# Creates a link with a `title` attribute.
init with_title(href: String, text: Writable, title: nullable String) do
- self.href = href
- self.text = text
+ init(href, text)
self.title = title
end
# Creates a link with a `title` attribute.
init with_subtext(level: Int, text: Writable, subtext: String) do
- self.level = level
- self.text = text
+ init(level, text)
self.subtext = subtext
end
# Create a HTML elements with the specifed type and attributes.
init with_attrs(tag: String, attrs: Map[String, String]) do
- self.tag = tag
+ init(tag)
self.attrs = attrs
end
var c = input[i]
if c == '\n' then
eol = true
+ else if c == '\r' then
else if c == '\t' then
var np = pos + (4 - (pos & 3))
while pos < np do
# Create a link with a title.
init with_title(link: String, title: nullable String) do
- self.link = link
+ init(link)
self.title = title
end
end
# Zero (or a negative value) means no limit.
#
# Applications can modify this value even during the main-loop.
- var maximum_fps = 60 is writable
+ var maximum_fps = 60.0 is writable
# Current frame-rate
# Updated each 5 seconds.
do
var t = clock.total.sec
if t >= frame_count_deadline then
- var cfps = frame_count_deadline.to_f / 5.0
+ var cfps = frame_count.to_f / 5.0
self.current_fps = cfps
frame_count = 0
frame_count_deadline = t + 5
frame_count += 1
var mfps = maximum_fps
- if mfps <= 0 then return
- var dt = clock.lapse
- var target_dt = 1000000000 / mfps
- var sec = dt.sec
- var nanosec = dt.nanosec
- if sec == 0 and nanosec < target_dt then
- var sleep_t = target_dt - nanosec
- sys.nanosleep(0, sleep_t)
- dt = clock.lapse
+ if mfps <= 0.0 then return
+ var lapse = clock.lapse
+ var dt = lapse.to_f
+ var target_dt = 1.0 / mfps
+ if dt < target_dt then
+ var sleep_t = target_dt - dt
+ sleep_t.sleep
+ clock.lapse
end
end
end
import tnitter
import benitlux::benitlux_controller
import opportunity::opportunity_controller
+import nitiwiki::wiki_edit
# Header for the whole site
class MasterHeader
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li{{{actives.get_or_default("ens", "")}}}><a href="http://xymus.net/ens/">Enseignement</a></li>
- <li><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
+ <li{{{actives.get_or_default("opportunity", "")}}}><a href="http://xymus.net/opportunity/">Opportunité</a></li>
<li{{{actives.get_or_default("tnitter", "")}}}><a href="http://tnitter.xymus.net/">Tnitter</a></li>
+ <li><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
<li{{{actives.get_or_default("benitlux", "")}}}><a href="http://benitlux.xymus.net/">Benitlux</a></li>
- <li{{{actives.get_or_default("opportunity", "")}}}><a href="http://xymus.net/opportunity/">Opportunité</a></li>
- <li><a href="http://nitlanguage.org/">Nit</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
tnitter_vh.routes.add new Route("/push/", new TnitterPush)
tnitter_vh.routes.add new Route(null, tnitter)
-
# Pep/8 Analysis is only a file server. It is available at `pep8.xymus.net`
# and through the global/default file server at `xymus.net/pep8/`
#
benitlux_vh.routes.add new Route("/rest/", benitlux_rest)
benitlux_vh.routes.add new Route(null, benitlux_sub)
+# Opportunity service
var opportunity = new OpportunityWelcome
var opportunity_rest = new OpportunityRESTAction
default_vh.routes.add new Route("/opportunity/rest/", opportunity_rest)
default_vh.routes.add new Route("/opportunity/", opportunity)
+# Nitiwiki modification form
+var passwords = "nitiwiki_passwords".to_path.read_lines
+assert passwords.not_empty
+default_vh.routes.add new Route("/edit", new EditAction("http://xymus.net/", "/home/xymus/projects/wiki/config.ini", passwords))
+
# We use a special file server for the path `xymus.net/ens` only to display
# a different header.
var file_server_ens = new FileServer("/var/www/ens/")
else if pname == "to_b" then
v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "code_point" then
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+ return true
+ else if pname == "&" then
+ v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "|" then
+ v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == ">>" then
+ v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "<<" then
+ v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+ return true
end
else if cname == "Char" then
if pname == "object_id" then
else if pname == "to_i" then
v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
return true
+ else if pname == "code_point" then
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "Byte" then
if pname == "output" then
else if pname == ">=" then
v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
return true
+ else if pname == ">>" then
+ v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "<<" then
+ v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+ return true
+ else if pname == "&" then
+ v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+ return true
else if pname == "to_i" then
v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
return true
else if pname == "to_u32" then
v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
return true
+ else if pname == "ascii" then
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+ return true
end
else if cname == "Bool" then
if pname == "output" then
var pot_path = locale_dir / module_name
var arr = vi.strings.values.to_a
- var po = new POFile.with_strings(arr)
+ var po = new POFile(arr)
po.write_template(pot_path)
if lang != null then
# Read from a PO file
var strings: Array[PObject]
- # Creates a PO file with strings built-in
- init with_strings(sm: Array[PObject])do
- strings = new Array[PObject].with_capacity(sm.length)
- strings.add_all sm
- end
-
redef fun write_to_file(path) do
if not path.has_suffix(".po") then path += ".po"
super path
if nexpr != null then
if nexpr isa ANewExpr then
mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true)
+ else if nexpr isa AAsCastExpr then
+ mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true)
else if nexpr isa AIntegerExpr then
var cla: nullable MClass = null
if nexpr.value isa Int then
class APublicVisibility
super AVisibility
# The `public` keyword, if any
- var n_kwpublic: nullable TKwpublic is writable
+ var n_kwpublic: nullable TKwpublic = null is writable
end
# An explicit private visibility modifier
class APrivateVisibility
# Still here? So it means that we must determine what super inits need to be automatically invoked
# The code that follow is required to deal with complex cases with old-style and new-style inits
- # Look for old-style super constructors
var auto_super_inits = new Array[CallSite]
- for msupertype in mclassdef.supertypes do
- # FIXME: the order is quite arbitrary
- if not msupertype.mclass.kind.need_init then continue
- msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
- var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
- if candidate == null then
- candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, "init")
- end
- if candidate == null then
- modelbuilder.error(self, "Error: cannot do an implicit constructor call in `{mpropdef}`; there is no constructor named `{mpropdef.mproperty.name}` in `{msupertype}`.")
- return
- end
- assert candidate isa MMethod
-
- # Skip new-style init
- if candidate.is_root_init then continue
-
- var candidatedefs = candidate.lookup_definitions(mmodule, anchor)
- if candidatedefs.is_empty then
- # skip broken
- is_broken = true
- return
- end
- var candidatedef = candidatedefs.first
- # TODO, we drop the others propdefs in the callsite, that is not great :(
-
- var msignature = candidatedef.new_msignature or else candidatedef.msignature
- msignature = msignature.resolve_for(recvtype, anchor, mmodule, true)
-
- var callsite = new CallSite(hot_location, recvtype, mmodule, anchor, true, candidate, candidatedef, msignature, false)
- auto_super_inits.add(callsite)
- modelbuilder.toolcontext.info("Old-style auto-super init for {mpropdef} to {candidate.full_name}", 4)
- end
- # No old style? The look for new-style super constructors (called from a old style constructor)
+ # The look for new-style super constructors (called from a old style constructor)
var the_root_init_mmethod = modelbuilder.the_root_init_mmethod
if the_root_init_mmethod != null and auto_super_inits.is_empty then
var candidatedefs = the_root_init_mmethod.lookup_definitions(mmodule, anchor)
return
end
- # Search the longest-one and checks for conflict
var candidatedef = candidatedefs.first
if candidatedefs.length > 1 then
- # Check for conflict in the order of initializers
- # Each initializer list must me a prefix of the longest list
- # part 1. find the longest list
- for spd in candidatedefs do
- if spd.initializers.length > candidatedef.initializers.length then candidatedef = spd
- end
- # compare
- for spd in candidatedefs do
- var i = 0
- for p in spd.initializers do
- if p != candidatedef.initializers[i] then
- modelbuilder.error(self, "Error: cannot do an implicit constructor call to conflicting inherited inits `{spd}({spd.initializers.join(", ")}`) and `{candidatedef}({candidatedef.initializers.join(", ")}`). NOTE: Do not mix old-style and new-style init!")
- return
- end
- i += 1
- end
- end
+ var cd2 = candidatedefs[1]
+ modelbuilder.error(self, "Error: cannot do an implicit constructor call to conflicting inherited inits `{cd2}({cd2.initializers.join(", ")}`) and `{candidatedef}({candidatedef.initializers.join(", ")}`). NOTE: Do not mix old-style and new-style init!")
+ is_broken = true
+ return
end
var msignature = candidatedef.new_msignature or else candidatedef.msignature
msignature = msignature.resolve_for(recvtype, anchor, mmodule, true)
+ if msignature.arity > 0 then
+ modelbuilder.error(self, "Error: cannot do an implicit constructor call to `{candidatedef}{msignature}`. Expected at least `{msignature.arity}` arguments.")
+ is_broken = true
+ return
+ end
+
var callsite = new CallSite(hot_location, recvtype, mmodule, anchor, true, the_root_init_mmethod, candidatedef, msignature, false)
auto_super_inits.add(callsite)
modelbuilder.toolcontext.info("Auto-super init for {mpropdef} to {the_root_init_mmethod.full_name}", 4)
return
end
- # Can the super-constructors be called?
- for auto_super_init in auto_super_inits do
- var auto_super_init_def = auto_super_init.mpropdef
- var msig = mpropdef.msignature.as(not null)
- var supermsig = auto_super_init.msignature
- if supermsig.arity > msig.arity then
- modelbuilder.error(self, "Error: cannot do an implicit constructor call to `{auto_super_init_def}{supermsig}`. Expected at least `{supermsig.arity}` arguments, got `{msig.arity}`.")
- continue
- end
- var i = 0
- for sp in supermsig.mparameters do
- var p = msig.mparameters[i]
- var sub = p.mtype
- var sup = sp.mtype
- if not sub.is_subtype(mmodule, anchor, sup) then
- modelbuilder.error(self, "Error: cannot do an implicit constructor call to `{auto_super_init_def}{supermsig}`. Expected argument #{i} of type `{sp.mtype}`, got implicit argument `{p.name}` of type `{p.mtype}`.")
- break
- end
- i += 1
- end
- end
self.auto_super_inits = auto_super_inits
end
var y: Int = -2 #alt2#
#alt2#var y: Int
#alt3#init (xx: Int) do x = xx
- #alt4#init foo(xx: Int) do x = xx
+ #alt4#init foo(xx: Int) do init(xx)
fun work
do
x.output
super B
var i: Int
- init(i: Int) do
- self.i = i
+ init do
i.output
end
end
'2'.output
' '.output
end
- init init_par(c: Char)
+ redef init init_par(c: Char)
do
'C'.output
'3'.output
c.output
' '.output
end
- init init_par3(c: Char)
+ redef init init_par3(c: Char)
do
'C'.output
'4'.output
#alt1#redef init do '1'.output
init do '1'.output #alt1#
#alt1#redef init init2 do '2'.output
- init init2 do '2'.output #alt1#
+ redef init init2 do '2'.output #alt1#
#alt1#redef init init3 do '3'.output
init init3 do '3'.output #alt1#
end
class C3
super A
init(j: Int) do
+ super(j)#alt6#
j.output
end
end
class D3
super A
init(j: Int) do
+ super(j)#alt6#
j.output
end
end
class F2
super A
init(j: Int, k: Bool) do
+ super(j)#alt6#
j.output
end
end
-1
-2
-1
-2
-3
-1
-2
-1
-2
-5
-1
-2
-6
+alt/base_init_combine_alt1.nit:59,9--11: Error: expected 2 argument(s) for `init(i: Int, z: Int)`; got 1. See introduction at `core::Object::init`.
11
true
12
-12
-13
13
14
true
15
15
16
-16
-alt/base_init_super_call2_alt3.nit:38,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt3#A#init(i: Int)`. Expected at least `1` arguments, got `0`.
+alt/base_init_super_call2_alt3.nit:38,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt3#A#init(i: Int)`. Expected at least `1` arguments.
-alt/base_init_super_call2_alt6.nit:105,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt6#A#init(i: Int)`. Expected argument #0 of type `Int`, got implicit argument `j` of type `Bool`.
+alt/base_init_super_call2_alt6.nit:91,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt6#A#init(i: Int)`. Expected at least `1` arguments.
+alt/base_init_super_call2_alt6.nit:99,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt6#A#init(i: Int)`. Expected at least `1` arguments.
+alt/base_init_super_call2_alt6.nit:107,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt6#A#init(i: Int)`. Expected at least `1` arguments.
+alt/base_init_super_call2_alt6.nit:123,2--5: Error: cannot do an implicit constructor call to `base_init_super_call2_alt6#A#init(i: Int)`. Expected at least `1` arguments.
-alt/base_init_super_call_alt3.nit:38,2--5: Error: cannot do an implicit constructor call to `base_init_super_call_alt3#A#init(i: Int)`. Expected at least `1` arguments, got `0`.
+1
+2
+3
+4
+6
+6
+7
+7
+8
+true
+9
+9
+10
+10
+11
+true
+12
+13
+14
+true
+15
+15
+16
-alt/base_init_super_call_alt6.nit:105,2--5: Error: cannot do an implicit constructor call to `base_init_super_call_alt6#A#init(i: Int)`. Expected argument #0 of type `Int`, got implicit argument `j` of type `Bool`.
+1
+2
+3
+4
+5
+6
+6
+7
+7
+8
+true
+9
+9
+10
+10
+11
+true
+12
+13
+true
+15
+15
+16
../lib/core/kernel.nit:431,1--486,3: Error: `kernel#Numeric` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
../lib/core/kernel.nit:492,1--515,3: Error: `kernel#Bool` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
../lib/core/kernel.nit:517,1--599,3: Error: `kernel#Float` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-../lib/core/kernel.nit:601,1--702,3: Error: `kernel#Byte` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-../lib/core/kernel.nit:704,1--890,3: Error: `kernel#Int` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-../lib/core/kernel.nit:892,1--1060,3: Error: `kernel#Char` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-../lib/core/kernel.nit:1062,1--1069,3: Error: `kernel#Pointer` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-../lib/core/kernel.nit:1071,1--1080,3: Error: `kernel#Task` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
+../lib/core/kernel.nit:601,1--708,3: Error: `kernel#Byte` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
+../lib/core/kernel.nit:710,1--894,3: Error: `kernel#Int` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
+../lib/core/kernel.nit:896,1--1066,3: Error: `kernel#Char` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
+../lib/core/kernel.nit:1068,1--1075,3: Error: `kernel#Pointer` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
+../lib/core/kernel.nit:1077,1--1086,3: Error: `kernel#Task` does not specialize `module_0#Object`. Possible duplication of the root class `Object`?
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:731)
11
21
31
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:731)
11
21
31
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:731)
11
21
31
Numeric -> Float [dir=back arrowtail=open style=dashed];
Byte [
- label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l+ \>\>(i: Int): Byte\l+ ascii(): Char\l}"
+ label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l- lsh(i: Int): Byte\l+ \>\>(i: Int): Byte\l- rsh(i: Int): Byte\l+ ascii(): Char\l- ffi_ascii(): Char\l}"
]
Discrete -> Byte [dir=back arrowtail=open style=dashed];
Numeric -> Byte [dir=back arrowtail=open style=dashed];
Int [
- label = "{Int||+ %(i: Int): Int\l+ \<\<(i: Int): Int\l+ \>\>(i: Int): Int\l+ code_point(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
+ label = "{Int||+ %(i: Int): Int\l+ \<\<(i: Int): Int\l- lsh(i: Int): Int\l+ \>\>(i: Int): Int\l- rsh(i: Int): Int\l+ code_point(): Char\l- cp(): Char\l+ digit_count(b: Int): Int\l+ digit_count_base_10(): Int\l+ to_c(): Char\l+ abs(): Int\l}"
]
Discrete -> Int [dir=back arrowtail=open style=dashed];
Numeric -> Int [dir=back arrowtail=open style=dashed];
Char [
- label = "{Char||+ to_i(): Int\l+ ascii(): Byte\l+ code_point(): Int\l+ is_ascii(): Bool\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l+ is_whitespace(): Bool\l}"
+ label = "{Char||+ to_i(): Int\l+ ascii(): Byte\l+ code_point(): Int\l- cp(): Int\l+ is_ascii(): Bool\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l+ is_whitespace(): Bool\l}"
]
Discrete -> Char [dir=back arrowtail=open style=dashed];
`}
class A
- var a: Int
+ var a: Int is noautoinit
init do a = 1234
init alt(i: Int) do a = i
fun to_i: Int do return a
# limitations under the License.
#alt1 import core
-#alt1 import buffered_ropes
var st = "quick brown fox over the lazy dog"
class A
var s : String
- init a( s : String ) do self.s = s
+ init a( s : String ) do init(s)
end
class B
var i : Int
- init b( i : Int ) do self.i = i
+ init b( i : Int ) do init(i)
end
class C
# limitations under the License.
#alt2 import core
-#alt2 import buffered_ropes
var str = "Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six."
var spaces = " "